WEBVTT 00:00.000 --> 00:06.400 Thanks, everyone. 00:06.400 --> 00:09.520 So yeah, like Seth said, I've been working on KeyCAD for a long time. 00:09.520 --> 00:14.720 If you're new to the world of open hardware and haven't heard of KeyCAD yet, welcome. 00:14.720 --> 00:19.040 It's a CAD tool, mostly for designing electronics for printed circuit board design, but 00:19.040 --> 00:21.400 also has a few other applications. 00:21.400 --> 00:25.880 So I'm going to be talking about one of the new features, which is coming in KeyCAD 9. 00:25.880 --> 00:29.920 If you want to hear about the other 500 features, stick around for Wayne's talk a little 00:29.920 --> 00:33.600 later, and KeyCAD 9 will be out shortly. 00:33.600 --> 00:37.600 It was going to be out the end of this week, but it's going to be next week because 00:37.600 --> 00:41.320 fuzz them and things stick around. 00:41.320 --> 00:46.880 So to talk about what I did here, I have to start by talking about why I did it. 00:46.880 --> 00:51.920 If you're familiar with KeyCAD, you probably know that it already has a plug-in system. 00:51.920 --> 00:56.440 KeyCAD has had the ability to add Python plug-ins for quite a long time, and if you're 00:56.480 --> 01:01.120 really into KeyCAD, maybe you've even tried to write some of your own plug-ins. 01:01.120 --> 01:06.000 So why did I make an API if we already have an API? 01:06.000 --> 01:11.720 So the current Python plug-in system is mostly around a tool called Swig, which is a 01:11.720 --> 01:16.840 clever tool that can analyze mostly C++ code, and now they also support C. 01:16.840 --> 01:21.400 And it automatically generates wrappers in a variety of different languages, and allows 01:21.400 --> 01:26.640 you to, for example, call C++ functions directly from Python. 01:26.640 --> 01:33.480 So in the case of KeyCAD, a subset of our C++ application is wrapped using Swig into a Python 01:33.480 --> 01:40.200 module, and plug-ins can then just import that Python module and call into KeyCAD code. 01:40.200 --> 01:41.120 So this is pretty cool. 01:41.120 --> 01:43.800 We don't have to do that much work. 01:43.800 --> 01:46.040 Code is mostly generated for us. 01:46.040 --> 01:49.760 We have some extra code that we had to write, but not that much. 01:49.760 --> 01:54.600 And now Python-based tools can do a lot of different things with KeyCAD, which has been 01:54.600 --> 02:00.400 really great for the ecosystem, because there is now a whole range of tooling that adds 02:00.400 --> 02:07.200 on to KeyCAD does specific things or things that we hadn't yet supported in the main C++ 02:07.200 --> 02:10.680 code base, which is pretty good, right? 02:10.680 --> 02:16.600 Well, maybe this is a bit obvious in hindsight, but this really wasn't the best choice 02:16.600 --> 02:22.840 to deliver an API that works the way that people expect when they hear the phrase API, 02:22.840 --> 02:28.880 like not having the functions you call change every version, or not being able to crash 02:28.880 --> 02:32.680 KeyCAD, if you call functions in the wrong order. 02:32.680 --> 02:38.920 So the main challenge is with Swig, our cover, like a couple of different places. 02:38.920 --> 02:44.800 So first of all, from the point of view of us, the KeyCAD development team, like I said, 02:44.800 --> 02:46.720 the interface is really fragile. 02:46.720 --> 02:50.520 There's no API layer within the KeyCAD code base. 02:50.520 --> 02:55.280 It was just directly, let's take the existing things that KeyCAD is doing, let's make it 02:55.280 --> 02:57.680 so that you can call them from Python. 02:57.680 --> 03:02.040 And this means that we are constantly dealing with challenges when we want to refactor 03:02.040 --> 03:08.000 that code, when we want to deprecate something, or add a new way of doing things, or just 03:08.000 --> 03:12.080 change how we do things internally in order to improve KeyCAD itself. 03:12.080 --> 03:16.840 We have to decide, are we going to just break these plugins that are expecting this 03:16.840 --> 03:22.560 old interface, which we sometimes have to do, unfortunately, or are we going to add some 03:22.560 --> 03:27.120 way of preserving backwards compatibility, which sometimes is pretty easy and sometimes 03:27.120 --> 03:33.280 is impossible and often is forgotten about until later, which is annoying for all of the 03:33.280 --> 03:35.200 Python developers. 03:35.200 --> 03:40.360 In addition to that, which is a big pain for a lot of people, Swig is also a little bit 03:40.360 --> 03:44.720 annoying for us on the development team, because we've been moving to use more and more 03:44.720 --> 03:52.040 modern C++ APIs and types, and Swig is slowly getting better at this, but it's still 03:52.040 --> 03:57.120 not, you know, it's not keeping pace with how we adopt C++ features. 03:57.120 --> 04:00.600 And it also slows down the build process for us, which limits how fast we can iterate 04:00.600 --> 04:02.600 on things. 04:02.600 --> 04:07.640 And like I said, Python plugins are directly reaching into KeyCAD as it is running the 04:07.880 --> 04:11.880 same process, they have access to all that memory. 04:11.880 --> 04:16.480 If they do things that don't match the expected contract, that we were thinking about when 04:16.480 --> 04:21.680 we wrote the code, they can just completely break things in very weird ways and cause 04:21.680 --> 04:25.000 an interesting bug reports. 04:25.000 --> 04:27.080 But it doesn't stop there. 04:27.080 --> 04:31.640 The current Python plugin system basically lets plugins assume that they Python bindings 04:31.640 --> 04:36.120 to our core framework library, WSWGets, are available. 04:36.120 --> 04:42.360 And sometimes this has been interesting for packaging, because that binding WX Python 04:42.360 --> 04:48.040 is sometimes out of sync with WX widgets in different platforms, and we've had to patch 04:48.040 --> 04:53.440 around that, and basically spend a bunch of energy maintaining things on the windows and 04:53.440 --> 04:55.840 macOS side, especially. 04:55.840 --> 05:01.360 We also have to bundle our own Python interpreter on those platforms, because we need to ensure 05:01.360 --> 05:06.440 for these plugins that they have, all of these Python modules available with appropriate 05:06.440 --> 05:11.840 versions, and we can't rely on that being installable externally. 05:11.840 --> 05:17.480 So we have to now ship a Python environment, and plugins have to use that environment, 05:17.480 --> 05:23.520 which is challenging for them if they want to then add external Python dependencies, and 05:23.520 --> 05:27.760 sometimes it's just not possible for them to use certain Python dependencies. 05:27.760 --> 05:32.000 There's one Python environment shared by everything, which if you're a Python developer, 05:32.000 --> 05:36.160 you know, is just not a great idea. 05:36.160 --> 05:42.200 If that wasn't enough, it's also not great as a developer if you're trying to build plugins, 05:42.200 --> 05:50.080 because using keycads to run your Python code, the way that it runs is a little bit different 05:50.080 --> 05:55.320 than the way that you would typically run a Python program, and so if you're trying to test 05:55.360 --> 06:00.560 your code, if you're trying to debug in an IDE or anything like that, it's sometimes 06:00.560 --> 06:05.240 possible, but it's very hard to get exactly the same environment and run it in exactly 06:05.240 --> 06:06.440 the same way. 06:06.440 --> 06:10.400 So sometimes you just have to test things the brute force way. 06:10.400 --> 06:15.120 We have a built-in Python console that lets you have a Python repel and just test things 06:15.120 --> 06:19.800 out, but if you're used to developing Python, maybe you're used to a little bit nicer 06:19.800 --> 06:26.240 tooling, and this built-in console is something that the keycad team now has to maintain, 06:26.240 --> 06:32.200 and it's another challenge and another thing that makes developing for keycad a little 06:32.200 --> 06:37.560 bit bespoke compared to other kinds of Python development. 06:37.560 --> 06:42.200 So that's where we are, and it's taken a long time to develop something better along the 06:42.200 --> 06:46.800 way we considered a few options that we then didn't do, which I think are an interesting 06:46.800 --> 06:48.200 dimension. 06:48.200 --> 06:52.240 So first of all, we could have just made a real API layer in C++, and then pointed 06:52.240 --> 06:55.040 to Swiget that instead of at the internals. 06:55.040 --> 06:58.720 This would have been relatively straightforward, and it would have probably totally solved 06:58.720 --> 07:03.480 those fragility problems, where you know, function names are changing every keycad version. 07:03.480 --> 07:07.000 The issue is that most of those other problems would have remained. 07:07.000 --> 07:12.240 So we have known for a while that we wanted to move away from Swig, and we also spend a little 07:12.240 --> 07:17.760 while looking at a library called Pi by 11, which is kind of serving the same kind of function 07:17.760 --> 07:23.480 as Swig, but in a more manual way where you have more control over how it works. 07:23.480 --> 07:28.480 And this might have solved a few more of the problems, but definitely not all of them. 07:28.480 --> 07:33.760 So about two years ago, I wrote an RFC for the team that basically said, what if we went 07:33.760 --> 07:35.200 in a different direction here? 07:35.200 --> 07:39.360 What if we could completely remove Python from the core of keycad and build a different 07:39.360 --> 07:41.000 kind of API? 07:41.000 --> 07:45.520 So over that window, I built a proof of concept, and it seemed to be working pretty well, 07:45.520 --> 07:50.160 some time passed, and here we are now about to ship the real version of this with keycad 07:50.160 --> 07:51.160 9. 07:51.160 --> 07:54.240 So let's talk about the IPC API. 07:54.240 --> 07:57.400 What I wanted in this API can basically be summed up like this. 07:57.400 --> 08:02.200 First of all, we wanted to be able to change any internals in keycad and hide that change 08:02.200 --> 08:04.000 from all the API users. 08:04.000 --> 08:08.560 We should be able to do normal things that people expect, like have a sane deprecation 08:08.560 --> 08:13.360 policy, and have forwards and backwards compatibility wherever possible. 08:13.360 --> 08:18.640 Second of all, API users should no longer have direct access to keycad internal state. 08:18.640 --> 08:23.880 We want to run them in external processes and keep them from doing, let's say, accidental 08:23.880 --> 08:25.480 evil. 08:25.480 --> 08:29.920 And third, the developer experience is important, both for us the keycad team and for all 08:29.920 --> 08:34.960 of the API users, if we want this thing to be adopted and useful, it has to be better 08:34.960 --> 08:39.720 than what exists out there right now, not just different. 08:39.720 --> 08:41.520 So here's how it works at a high level. 08:41.520 --> 08:46.480 The API is using protocol buffers to define messages, messages that are passed back and 08:46.480 --> 08:52.160 forth between keycad and an external application over unic sockets or Windows name pipes. 08:52.160 --> 08:56.120 And we use a library called nanomessage next generation, which I'll just call NNG from 08:56.120 --> 09:02.920 now on because it's shorter as an abstraction layer over those sockets and pipes. 09:02.920 --> 09:04.120 So let's start at the bottom. 09:04.120 --> 09:05.120 Why NNG? 09:05.120 --> 09:06.720 It's a question I get sometimes. 09:06.720 --> 09:10.600 I looked at a number of different options that could solve the problem I had where I want 09:10.600 --> 09:15.680 to send bytes back and forth between keycad and other things on the same machine. 09:15.680 --> 09:20.840 And basically, NNG came out on top because it's pretty simple to use and very few lines 09:20.840 --> 09:21.840 of code. 09:21.840 --> 09:27.200 And those very few lines of code are using the native platform IPC mechanisms. 09:27.200 --> 09:31.520 The same code does the right thing on Windows or on non-Windows and you don't have to 09:31.520 --> 09:34.640 care that much about the differences there. 09:34.640 --> 09:39.840 And it has all the availability we needed it to be in terms of what platforms it supported 09:39.840 --> 09:42.360 and what languages it supported. 09:42.360 --> 09:45.320 And basically, I didn't find a good reason not to use it. 09:45.320 --> 09:49.640 And I also looked at a lot of other options so you can see some of them down here. 09:49.640 --> 09:55.400 And all of these other options came out as either more complicated or they had some drawback 09:55.400 --> 10:00.800 or in some way they were trying to solve a different set of problems than my problems. 10:00.800 --> 10:07.880 So I think there are great solutions for different problems but they weren't the one for me. 10:07.880 --> 10:10.640 So then one layer up why a protobuff. 10:10.640 --> 10:15.520 So if you're not familiar with protobuff, basically is a project from Google where you define 10:15.520 --> 10:21.840 messages using a domain-specific language and then use a tool that compiles that DSL into 10:21.840 --> 10:27.400 various different wrappers essentially and a bunch of different target languages that 10:27.400 --> 10:29.280 implement those messages. 10:29.280 --> 10:35.720 And we use this because we can then define most of the keycad API in terms of those DSL messages 10:35.720 --> 10:38.760 which are pretty easy to parse but look at some in a bit. 10:38.760 --> 10:43.880 And it means it's really easy to keep ccad synced up with the third party language bindings. 10:43.880 --> 10:49.400 So protobuff is not perfect and in fact it has some downsides that are important to be aware 10:49.400 --> 10:52.200 of if you want to use it for your project. 10:52.200 --> 10:56.880 But ultimately I did look at a number of alternatives to protobuff and they either didn't 10:56.880 --> 11:02.160 have all the features we need or they weren't nearly as popular and I decided after 11:02.160 --> 11:07.600 looking at what was available and all of the resources I could find people posting about 11:07.600 --> 11:11.320 challenges they had had and how they had overcome those challenges. 11:11.320 --> 11:16.360 I wanted to go with the thing that was the most widely used because I could see how these 11:16.360 --> 11:20.640 people were doing similar things and how they had solved these problems and some of the alternatives 11:20.640 --> 11:21.640 I looked at. 11:21.640 --> 11:25.320 There just wasn't that much information about people solving similar problems and I think 11:25.320 --> 11:28.040 that kind of thing is important. 11:28.040 --> 11:31.480 So here's some example protobuff messages from the keycad API. 11:31.480 --> 11:36.720 You can think of them kind of like structures if you know a language like cc++. 11:36.720 --> 11:42.520 You can see some of these have primitive types like these in 64s and then a lot of them 11:42.520 --> 11:47.160 are composed out of other messages internally. 11:47.160 --> 11:53.720 So here's an example of a command that an API client might want to send to get the bounding 11:53.720 --> 11:56.280 box or extends from a text object. 11:56.280 --> 12:01.200 So we're going to have this certain other message that specifies what the text is and we're 12:01.200 --> 12:05.920 going to get back that box too we just saw with the bounding box. 12:05.920 --> 12:09.600 So to actually send and receive these messages we have these what are called envelope 12:09.600 --> 12:15.640 messages that have a request and response inside as an any type which is a protobuff feature 12:15.640 --> 12:20.640 that basically lets you do runtime type introspection so that we can dynamically dispatch 12:20.640 --> 12:24.880 a handler depending on what kind of message we receive coming in. 12:24.880 --> 12:30.320 Protobuff defines a binary format that these messages get serialized into and that serialized 12:30.320 --> 12:33.560 format is what we send back and forth over the wire. 12:33.560 --> 12:39.360 And one note is that protobuff can also be serialized as text that is basically JSON. 12:39.360 --> 12:43.280 So we have a debugging feature where you can actually dump every message going back and 12:43.280 --> 12:48.000 forth to a text log and see what's going on which is really handy. 12:48.000 --> 12:52.040 So we have these messages how do we actually hook them up to keycad? 12:52.040 --> 12:56.000 At a super high level keycad is an event driven application one of our other devs e-in 12:56.000 --> 12:59.640 who's up there did a deep dive into this on our developer meetup on Friday and you 12:59.640 --> 13:03.880 can watch that on our YouTube channel if you want the full details but at a really high 13:03.880 --> 13:08.240 level events are coming in from various sources maybe from an input device maybe from 13:08.240 --> 13:13.240 the operating system and they get routed to various event handlers by it's spasher. 13:13.240 --> 13:18.120 This is all happening in a single thread so all these handlers are designed to be non-blocking 13:18.120 --> 13:22.320 and sometimes they have their own internal event loops and all of this is going on with 13:22.320 --> 13:27.480 various core routine magic that lets us have somewhat sane code but I'm going to skip 13:27.480 --> 13:29.320 over that today. 13:29.320 --> 13:34.480 So to hook into this I basically added a new type of event and this is called whenever 13:34.480 --> 13:38.720 any kind of API message comes in and that is routed to a new kind of handler which 13:38.720 --> 13:44.760 is handling the API messages and this handler is unpacking that envelope message and 13:44.760 --> 13:47.560 figuring out okay what should I do with this? 13:47.560 --> 13:53.240 It has its own that of handlers that are all supporting the actual API functions. 13:53.240 --> 13:58.400 Some of those functions might be able to return some calculation immediately some may be 13:58.400 --> 14:03.400 able to create their own other events and post those to the event loop that will then 14:03.400 --> 14:08.120 get handle that some later time so all of these have to be non-blocking essentially return 14:08.120 --> 14:13.280 to the client immediately so if you want to call an API command that is going to take 14:13.280 --> 14:18.720 some time to do you are just going to immediately get a reply that hey this command started 14:18.720 --> 14:22.600 and you're going to need to check back to see when it finished. 14:22.600 --> 14:26.200 Some of the details of this by the way can be kind of hidden from the actual users of 14:26.200 --> 14:31.640 the API through the client libraries to make it a little bit less complicated. 14:31.640 --> 14:35.960 And one other thing I should mention is that these API handlers are dynamically registered. 14:35.960 --> 14:41.080 So for example if you open a keypad project manager you might only have a few handlers 14:41.080 --> 14:45.560 but then when you open up the PCB editor it will register a PCB handler because now you 14:45.560 --> 14:50.200 can work with a board and now you can call get board and refill zones and things like that 14:50.200 --> 14:55.160 and if you close that PCB editor it will get de-registered and then if you say refill zones 14:55.160 --> 15:00.760 you would instead get back a message saying hey there's not a handler for that right now. 15:00.760 --> 15:05.000 So because of this design you know the API events are synchronous and are getting sequenced 15:05.000 --> 15:08.680 in with all of the user input so that's something you have to keep in mind as a plugin 15:08.680 --> 15:14.280 developer is that you're not kind of operating in parallel with the user but with the user. 15:14.280 --> 15:19.240 So you have to keep in mind how you want your plugin actions to get sequenced in 15:19.240 --> 15:24.760 as undue steps and sometimes the user will be doing something that we don't want to allow 15:24.760 --> 15:28.760 interruption like if you're in the middle of routing tracks we don't want your API plugin to be 15:28.760 --> 15:33.880 able to just delete the board. So sometimes the API will say hey I'm busy you need to try that again 15:33.880 --> 15:38.840 later. So that's an overview of how it's implemented but what about using it? 15:40.200 --> 15:45.160 Well we're releasing Python bindings for the API along with Kika 9 and you can actually install it right 15:45.160 --> 15:50.120 now if you have a nightly and want to try it out so it's there in the Python package index 15:50.120 --> 15:55.560 and that's the source code for it just make sure that you go into your Kika preferences and turn 15:55.560 --> 16:01.720 on the API because it's not on by default right now. So here's a simple example of creating a 16:01.720 --> 16:06.120 zone on whatever board is open we start by importing some things and then getting a handle to the 16:06.120 --> 16:11.960 open Kika instance and then the board and then we construct a zone object that has certain properties 16:11.960 --> 16:16.840 which is most of the code you see there and finally we put it on the board. By the way we could be 16:16.840 --> 16:23.000 developing this from any IDE or from a repel outside of Kika and it would work the same way as if it was 16:23.000 --> 16:27.960 running as a plugin from Kika so there's no longer any difference there you can use whatever kind of 16:27.960 --> 16:34.200 Python environment is most comfortable for you. One thing I want to point out is that these two lines 16:34.200 --> 16:40.360 are the only lines that are communicating with Kika using the API so the rest of it is building up 16:40.440 --> 16:44.920 objects in Python that are representing protobuff messages that will then be sent. 16:47.160 --> 16:52.440 You could also take away that a lot of steps to define an outline for a zone and this is true 16:52.440 --> 16:59.160 and I guess my point here is that even if we make the API really easy to use and Pythonic and all that 16:59.160 --> 17:05.080 good stuff you're still going to need to kind of understand Kika had some some places to understand 17:05.800 --> 17:10.360 the zone is complicated because it can actually have a arcs in its border and it can have internal 17:10.360 --> 17:16.440 cut out some things like this and so the API describes all of that so that just is on us to make 17:16.440 --> 17:20.360 sure that all of these things are documented well and have good examples for you to work from. 17:22.040 --> 17:25.960 One thing that I'm not going to have time for too much detail on is that there is a new 17:25.960 --> 17:31.320 Python plugin launching system coming with Kika at 9 which is using virtual environments per plugin 17:31.320 --> 17:38.200 and automatic dependency installation that mostly works and this you know I'm sure there's a few 17:38.200 --> 17:43.240 edge cases because that's Python where it doesn't work but for a few people who've been testing it 17:43.240 --> 17:47.640 it seems to work which is really awesome because now plugins can use whatever UI to locate they want 17:48.280 --> 17:54.520 and as a bonus it can launch any executable file so you don't necessarily need to use Python if you don't 17:54.520 --> 18:01.720 want to so Kika at 9 is coming from with the first public version of this API but I'm definitely 18:01.720 --> 18:05.240 not done there are a lot of features that are important to add that will be coming in future 18:05.240 --> 18:10.280 versions and probably some things I don't even know I should add so I'm definitely looking for 18:10.280 --> 18:15.400 feedback from early users of this you know up next I'm going to be working on integration with 18:15.400 --> 18:20.680 the footprint editor and replacing the footprint wizard systems so that we can have shared code 18:20.680 --> 18:27.240 base between our library team and Kika itself you know called back to the CAD query talk which 18:27.240 --> 18:32.680 has just happened we also want to support headless operations and since I know a lot of people are 18:32.680 --> 18:39.160 wondering yes we are bringing this to schematic in symbols no it's not in 9 basically we just ran 18:39.160 --> 18:44.440 out of time to do the necessary internal changes to the schematic editor to make that possible 18:45.480 --> 18:50.040 so if you want to learn more you can find a little bit more detail than I was able to cover today 18:50.040 --> 18:55.000 in the dev dox link here as well as more information about creating new API plugins 18:56.840 --> 19:02.600 Kika Python is up on Python package index so you can check that out and there's some examples 19:02.600 --> 19:11.640 basic plugins and also just some quick scripts in that repo so with that any questions 19:21.000 --> 19:35.480 Hi I was just wondering how does that work when you have multiple type of instance running 19:35.480 --> 19:41.480 like audio select one or stuff like that great question so the question was if you have multiple 19:41.480 --> 19:47.960 different instances how do you know which one to talk to so there will be a default path that the 19:47.960 --> 19:53.240 first instance will grab and then after that if it's already taken you know you will have a 19:53.240 --> 19:58.440 different way to communicate that will have a different you know process ID in the in the path 19:58.440 --> 20:03.640 essentially when you launch a plugin from P cad it passes the information through environment 20:03.640 --> 20:09.080 variables as to where you should communicate with this key cad that just launched it but if you 20:09.080 --> 20:14.280 are running from a debugger you would have to manually override that if you didn't want to use the 20:14.280 --> 20:18.920 default path so if you're if you're developing a Python plugin I recommend using just one 20:18.920 --> 20:28.680 instance at a time can you go a bit into can you go a bit into how the virtual environment 20:28.680 --> 20:36.920 installation works on a like a managed type installation yes so the virtual environment is today 20:37.480 --> 20:44.040 pretty basic and assumes one particular workflow which has so far been working in my tests but I 20:44.040 --> 20:49.800 know it won't cover all cases so basically if you're plugin in your plugin definition file 20:49.800 --> 20:55.560 you know says it's a Python plugin then we will look for a requirements text file and we will attempt 20:55.560 --> 21:02.440 to install so we will use Python basically you can configure which Python interpreter to use and then 21:02.440 --> 21:08.600 we will assume that we have access to the virtual environment module so if if you somehow don't have 21:08.680 --> 21:13.480 that it won't work and then we will attempt to install pip and then we will attempt to use pip to 21:13.480 --> 21:19.400 install your dependencies and if you can't install pip or if your dependencies don't have binary 21:19.400 --> 21:24.040 wheels it won't work and you'll have to do something manual but that's something that I'm sure 21:24.040 --> 21:34.440 could be improved in the future well we have more questions if our pod if you could start getting 21:35.240 --> 21:43.160 set up one question if my plugin is doing something both with schematics and PCB editor is 21:43.160 --> 21:48.840 a way that I can launch any application you mentioned that it has to be running in order to 21:48.840 --> 21:58.600 do understand to come on so if you want to have a plugin that launches kick out no I'm if my plugin 21:58.680 --> 22:09.640 it's like printing schematics and generating gorillas for example it has to deal with schematics 22:09.640 --> 22:18.760 and if one of them is not running once we happen you mentioned that schematics has to be running 22:18.760 --> 22:26.520 in order to understand commands for schematics yeah so today you can only with the with the current 22:26.520 --> 22:33.720 plugin system it's intended for the use case of the the user launches it from the editor so 22:34.520 --> 22:38.840 your plugin cannot open the editor the user has to open the editor and then launch the plugin 22:39.400 --> 22:44.280 in the future we won't have that restriction one of the things I put on the roadmap was a headless 22:44.280 --> 22:50.200 mode so right now you actually can't do plotting and exporting with this that's well supported by 22:50.200 --> 22:55.400 keycud CLI so that's our recommendation right now but in the future you will be able to do things 22:55.400 --> 23:01.080 like that through a special mode of keycud CLI where you can have your plugin say now I want to 23:01.080 --> 23:13.240 open this schematics file and work with it. Any further questions? Okay thank you John