nyancad 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
nyancad-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: nyancad
3
+ Version: 0.1.0
4
+ Summary: NyanCAD Python library for schematic editor with anywidget integration
5
+ Author-email: Pepijn de Vos <me@pepijndevos.nl>
6
+ License: MPL-2.0
7
+ Project-URL: Homepage, https://github.com/NyanCAD/Mosaic
8
+ Project-URL: Repository, https://github.com/NyanCAD/Mosaic
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: anywidget>=0.9.0
21
+ Requires-Dist: traitlets
22
+
23
+ # NyanCAD Python Library
24
+
25
+ A Python library for the [NyanCAD schematic editor](https://github.com/NyanCAD/Mosaic) with anywidget integration. Provides live access to schematic data from marimo notebooks.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install nyancad
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Basic Usage
36
+
37
+ ```python
38
+ import marimo as mo
39
+ from nyancad import SchematicBridge
40
+
41
+ # Create a schematic bridge widget
42
+ bridge = SchematicBridge()
43
+
44
+ # Display the widget (shows connection status)
45
+ bridge
46
+
47
+ # Access live schematic data
48
+ print(bridge.schematic_data)
49
+
50
+ # Send simulation data back to the editor
51
+ bridge.simulation_data = {
52
+ "results": [1.2, 3.4, 5.6],
53
+ "timestamp": "2024-01-01T00:00:00Z"
54
+ }
55
+ ```
56
+
57
+ ### Integration with Marimo
58
+
59
+ The SchematicBridge widget automatically connects to the NyanCAD editor running in the same marimo session. Any changes made in the schematic editor will be immediately reflected in the Python widget.
60
+
61
+ ```python
62
+ # In a marimo cell
63
+ bridge = SchematicBridge()
64
+ bridge # This will show the connection status
65
+
66
+ # In another marimo cell - access the live data
67
+ mo.md(f"""
68
+ ## Schematic Analysis
69
+ Raw schematic data: {len(bridge.schematic_data)} items
70
+ """)
71
+
72
+ # View the actual schematic data structure
73
+ bridge.schematic_data
74
+ ```
75
+
76
+ ## Features
77
+
78
+ - **Live Sync**: Real-time synchronization with NyanCAD schematic editor via PouchDB
79
+ - **Bidirectional Communication**: Send simulation data back to the editor from Python
80
+ - **Zero Configuration**: Automatically detects and connects to the active schematic
81
+ - **Raw Data Access**: Direct access to the complete schematic data structure
82
+ - **Marimo Integration**: Seamless integration with marimo notebooks
83
+
84
+ ## API Reference
85
+
86
+ ### SchematicBridge
87
+
88
+ The main widget class that provides bidirectional communication with the Mosaic editor.
89
+
90
+ #### Properties
91
+
92
+ - `schematic_data` (dict): Raw schematic data from the Mosaic editor, automatically synced
93
+ - `simulation_data` (dict): Simulation data to send to the Mosaic editor. Setting this will store the data with a timestamp in the editor's database
94
+
95
+ ## Development
96
+
97
+ This package is part of the [NyanCAD](https://github.com/NyanCAD/Mosaic) project. The anywidget integration uses a ClojureScript bridge that compiles to an ESM module, allowing seamless data sharing between the schematic editor and Python environment.
98
+
99
+ ## License
100
+
101
+ This project is licensed under the Mozilla Public License 2.0 - see the [LICENSE](../../LICENSE) file for details.
@@ -0,0 +1,79 @@
1
+ # NyanCAD Python Library
2
+
3
+ A Python library for the [NyanCAD schematic editor](https://github.com/NyanCAD/Mosaic) with anywidget integration. Provides live access to schematic data from marimo notebooks.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install nyancad
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Usage
14
+
15
+ ```python
16
+ import marimo as mo
17
+ from nyancad import SchematicBridge
18
+
19
+ # Create a schematic bridge widget
20
+ bridge = SchematicBridge()
21
+
22
+ # Display the widget (shows connection status)
23
+ bridge
24
+
25
+ # Access live schematic data
26
+ print(bridge.schematic_data)
27
+
28
+ # Send simulation data back to the editor
29
+ bridge.simulation_data = {
30
+ "results": [1.2, 3.4, 5.6],
31
+ "timestamp": "2024-01-01T00:00:00Z"
32
+ }
33
+ ```
34
+
35
+ ### Integration with Marimo
36
+
37
+ The SchematicBridge widget automatically connects to the NyanCAD editor running in the same marimo session. Any changes made in the schematic editor will be immediately reflected in the Python widget.
38
+
39
+ ```python
40
+ # In a marimo cell
41
+ bridge = SchematicBridge()
42
+ bridge # This will show the connection status
43
+
44
+ # In another marimo cell - access the live data
45
+ mo.md(f"""
46
+ ## Schematic Analysis
47
+ Raw schematic data: {len(bridge.schematic_data)} items
48
+ """)
49
+
50
+ # View the actual schematic data structure
51
+ bridge.schematic_data
52
+ ```
53
+
54
+ ## Features
55
+
56
+ - **Live Sync**: Real-time synchronization with NyanCAD schematic editor via PouchDB
57
+ - **Bidirectional Communication**: Send simulation data back to the editor from Python
58
+ - **Zero Configuration**: Automatically detects and connects to the active schematic
59
+ - **Raw Data Access**: Direct access to the complete schematic data structure
60
+ - **Marimo Integration**: Seamless integration with marimo notebooks
61
+
62
+ ## API Reference
63
+
64
+ ### SchematicBridge
65
+
66
+ The main widget class that provides bidirectional communication with the Mosaic editor.
67
+
68
+ #### Properties
69
+
70
+ - `schematic_data` (dict): Raw schematic data from the Mosaic editor, automatically synced
71
+ - `simulation_data` (dict): Simulation data to send to the Mosaic editor. Setting this will store the data with a timestamp in the editor's database
72
+
73
+ ## Development
74
+
75
+ This package is part of the [NyanCAD](https://github.com/NyanCAD/Mosaic) project. The anywidget integration uses a ClojureScript bridge that compiles to an ESM module, allowing seamless data sharing between the schematic editor and Python environment.
76
+
77
+ ## License
78
+
79
+ This project is licensed under the Mozilla Public License 2.0 - see the [LICENSE](../../LICENSE) file for details.
@@ -0,0 +1,9 @@
1
+ """
2
+ NyanCAD Python Library
3
+
4
+ A Python library for the NyanCAD schematic editor with anywidget integration.
5
+ Provides live access to schematic data from marimo notebooks.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+ __all__ = []
@@ -0,0 +1,50 @@
1
+ """
2
+ Anywidget integration for Mosaic schematic editor.
3
+
4
+ Provides a live bridge to schematic data from the Mosaic editor via PouchDB.
5
+ """
6
+
7
+ import anywidget
8
+ import traitlets
9
+ import marimo as mo
10
+
11
+
12
+ class SchematicBridge(anywidget.AnyWidget):
13
+ """
14
+ An anywidget that provides live bidirectional communication with the Mosaic editor.
15
+
16
+ The widget displays a simple status indicator and automatically syncs schematic
17
+ data from the Mosaic editor's PouchDB storage into the Python model, while also
18
+ enabling Python to send simulation data back to the editor.
19
+
20
+ Attributes:
21
+ schematic_data (dict): Live schematic data from the Mosaic editor
22
+ simulation_data (dict): Simulation data to send to the Mosaic editor.
23
+ Setting this will store the data with a timestamp in the editor's database.
24
+ """
25
+
26
+ # ESM shim that dynamically imports the Shadow CLJS compiled anywidget module
27
+ # Uses window.location.origin to resolve the full URL since ESM runs from data: URL
28
+ _esm = """
29
+ const { render } = await import(`${window.location.origin}/js/anywidget.js`);
30
+ export default { render };
31
+ """
32
+
33
+ # Schematic data that gets synced from the ClojureScript side
34
+ schematic_data = traitlets.Dict().tag(sync=True)
35
+
36
+ # Simulation data that gets synced to the ClojureScript side
37
+ simulation_data = traitlets.Dict().tag(sync=True)
38
+
39
+ @property
40
+ def name(self):
41
+ return mo.query_params()["schem"]
42
+
43
+ def schematic_bridge():
44
+ """
45
+ Create a SchematicBridge widget for use in Marimo notebooks.
46
+
47
+ Returns:
48
+ SchematicBridge: An instance of the SchematicBridge widget.
49
+ """
50
+ return mo.ui.anywidget(SchematicBridge())
@@ -0,0 +1,332 @@
1
+ # SPDX-FileCopyrightText: 2022 Pepijn de Vos
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ """
5
+ This module communicates with CouchDB to fetch schematics, and generate SPICE netlists out of them.
6
+
7
+ """
8
+
9
+ from collections import deque, namedtuple
10
+
11
+ class SchemId(namedtuple("SchemId", ["cell", "model", "device"])):
12
+ @classmethod
13
+ def from_string(cls, id):
14
+ schem, dev, *_= id.split(':') + [None]
15
+ cell, model = schem.split('$')
16
+ return cls(cell, model, dev)
17
+
18
+ @property
19
+ def schem(self):
20
+ return f"{self.cell}${self.model}"
21
+
22
+ def shape_ports(shape):
23
+ for y, s in enumerate(shape):
24
+ for x, c in enumerate(s):
25
+ if c != ' ':
26
+ yield x, y, c
27
+
28
+
29
+ mosfet_shape = list(shape_ports([
30
+ " D ",
31
+ "GB ",
32
+ " S ",
33
+ ]))
34
+
35
+ bjt_shape = list(shape_ports([
36
+ " C ",
37
+ "B ",
38
+ " E ",
39
+ ]))
40
+
41
+
42
+ twoport_shape = list(shape_ports([
43
+ " P ",
44
+ " ",
45
+ " N ",
46
+ ]))
47
+
48
+
49
+ def rotate(shape, transform, devx, devy):
50
+ a, b, c, d, e, f = transform
51
+ width = max(max(x, y) for x, y, _ in shape)+1
52
+ mid = width/2-0.5
53
+ res = {}
54
+ for px, py, p in shape:
55
+ x = px-mid
56
+ y = py-mid
57
+ nx = a*x+c*y+e
58
+ ny = b*x+d*y+f
59
+ res[round(devx+nx+mid), round(devy+ny+mid)] = p
60
+ return res
61
+
62
+
63
+ def getports(doc, models):
64
+ cell = doc['cell']
65
+ x = doc['x']
66
+ y = doc['y']
67
+ tr = doc.get('transform', [1, 0, 0, 1, 0, 0])
68
+ if cell == 'wire':
69
+ rx = doc['rx']
70
+ ry = doc['ry']
71
+ return {(x, y): None,
72
+ (x+rx, y+ry): None}
73
+ elif cell == 'text':
74
+ return {}
75
+ elif cell == 'port':
76
+ return {(x, y): doc['name']}
77
+ elif cell in {'nmos', 'pmos'}:
78
+ return rotate(mosfet_shape, tr, x, y)
79
+ elif cell in {'npn', 'pnp'}:
80
+ return rotate(bjt_shape, tr, x, y)
81
+ elif cell in {'resistor', 'capacitor', 'inductor', 'vsource', 'isource', 'diode'}:
82
+ return rotate(twoport_shape, tr, x, y)
83
+ else:
84
+ return rotate(models[f"models:{cell}"]['conn'], tr, x, y)
85
+
86
+
87
+ def port_index(docs, models):
88
+ wire_index = {}
89
+ device_index = {}
90
+ for doc in docs.values():
91
+ cell = doc['cell']
92
+ for (x, y), p in getports(doc, models).items():
93
+ if cell in {'wire', 'port'}:
94
+ wire_index.setdefault((x, y), []).append(doc)
95
+ else:
96
+ device_index.setdefault((x, y), []).append((p, doc))
97
+ # add a dummy net so two devices can connect directly
98
+ wire_index.setdefault((x, y), []).append({"cell": "wire", "x": x, "y": y, "rx": 0, "ry": 0})
99
+ return device_index, wire_index
100
+
101
+
102
+ def wire_net(wireid, docs, models):
103
+ device_index, wire_index = port_index(docs, models)
104
+ netname = None
105
+ net = deque([docs[wireid]]) # all the wires on this net
106
+ while net:
107
+ doc = net.popleft() # take a wire from the net
108
+ cell = doc['cell']
109
+ if cell == 'wire':
110
+ wirename = doc.get('name')
111
+ if netname == None and wirename != None:
112
+ netname = wirename
113
+ for ploc in getports(doc, models).keys(): # get the wire ends
114
+ # if the wire connects to another wire,
115
+ # that we have not seen, add it to the net
116
+ if ploc in wire_index:
117
+ net.extend(wire_index.pop(ploc))
118
+ elif cell == 'port':
119
+ netname = doc.get('name')
120
+ else:
121
+ raise ValueError(cell)
122
+ return netname
123
+
124
+ def netlist(docs, models):
125
+ """
126
+ Turn a collection of documents as returned by `get_docs` into a netlist structure.
127
+ Returns a dictionary of device ID: {port: net}
128
+ Usage:
129
+ ```
130
+ async with SchematicService("http://localhost:5984/offline") as service:
131
+ name = "top$top"
132
+ seq, docs = await service.get_all_schem_docs(name)
133
+ print(netlist(docs[name], models))
134
+ ```
135
+ """
136
+ device_index, wire_index = port_index(docs, models)
137
+ nl = {}
138
+ netnum = 0
139
+ while wire_index: # while there are wires left
140
+ loc, locwires = wire_index.popitem() # take one
141
+ netname = None
142
+ net = deque(locwires) # all the wires on this net
143
+ netdevs = {} # all the devices on this net
144
+ while net:
145
+ doc = net.popleft() # take a wire from the net
146
+ cell = doc['cell']
147
+ if cell == 'wire':
148
+ wirename = doc.get('name')
149
+ if netname == None and wirename != None:
150
+ netname = wirename
151
+ for ploc in getports(doc, models).keys(): # get the wire ends
152
+ # if the wire connects to another wire,
153
+ # that we have not seen, add it to the net
154
+ if ploc in wire_index:
155
+ net.extend(wire_index.pop(ploc))
156
+ # if the wire connect to a device, add its port to netdevs
157
+ if ploc in device_index:
158
+ for p, dev in device_index[ploc]:
159
+ netdevs.setdefault(dev['_id'], []).append(p)
160
+ elif cell == 'port':
161
+ netname = doc.get('name')
162
+ else:
163
+ raise ValueError(cell)
164
+ if netname == None:
165
+ netname = f"net{netnum}"
166
+ netnum += 1
167
+ for k, v in netdevs.items():
168
+ nl.setdefault(netname, {}).setdefault(k, []).extend(v)
169
+ inl = {}
170
+ for net, devs in nl.items():
171
+ for dev, pts in devs.items():
172
+ for port in pts:
173
+ inl.setdefault(dev, {})[port] = net
174
+ return inl
175
+
176
+
177
+ def print_props(props):
178
+ prs = []
179
+ for k, v in props.items():
180
+ if k == "model":
181
+ prs.insert(0, v)
182
+ elif k == "spice":
183
+ prs.append(v)
184
+ else:
185
+ prs.append(f"{k}={v}")
186
+ return " ".join(prs)
187
+
188
+
189
+ def circuit_spice(docs, models, declarations, corner, sim):
190
+ nl = netlist(docs, models)
191
+ cir = []
192
+ for id, ports in nl.items():
193
+ dev = docs[id]
194
+ cell = dev['cell']
195
+ mname = dev.get('props', {}).get('model', '')
196
+ name = dev.get('name') or id
197
+ # print(ports)
198
+ def p(p): return ports[p]
199
+ propstr = print_props(dev.get('props', {}))
200
+ if cell == "resistor":
201
+ ports = ' '.join(p(c) for c in ['P', 'N'])
202
+ templ = "R{name} {ports} {properties}"
203
+ elif cell == "capacitor":
204
+ ports = ' '.join(p(c) for c in ['P', 'N'])
205
+ templ = "C{name} {ports} {properties}"
206
+ elif cell == "inductor":
207
+ ports = ' '.join(p(c) for c in ['P', 'N'])
208
+ templ = "L{name} {ports} {properties}"
209
+ elif cell == "diode":
210
+ ports = ' '.join(p(c) for c in ['P', 'N'])
211
+ templ = "D{name} {ports} {properties}"
212
+ elif cell == "vsource":
213
+ ports = ' '.join(p(c) for c in ['P', 'N'])
214
+ templ = "V{name} {ports} {properties}"
215
+ elif cell == "isource":
216
+ ports = ' '.join(p(c) for c in ['P', 'N'])
217
+ templ = "I{name} {ports} {properties}"
218
+ elif cell in {"pmos", "nmos"}:
219
+ ports = ' '.join(p(c) for c in ['D', 'G', 'S', 'B'])
220
+ templ = "M{name} {ports} {properties}"
221
+ elif cell in {"npn", "pnp"}:
222
+ ports = ' '.join(p(c) for c in ['C', 'B', 'E'])
223
+ templ = "Q{name} {ports} {properties}"
224
+ else: # subcircuit
225
+ m = models[f"models:{cell}"]
226
+ ports = ' '.join(p(c[2]) for c in m['conn'])
227
+ templ = "X{name} {ports} {properties}"
228
+
229
+ # a spice type model can overwrite its reference
230
+ # for example if the mosfet is really a subcircuit
231
+ try:
232
+ m = models[f"models:{cell}"]["models"][mname][sim]
233
+ templ = m['reftempl']
234
+ declarations.add(m['decltempl'].format(corner=corner))
235
+ except KeyError:
236
+ pass
237
+
238
+ cir.append(templ.format(name=name, ports=ports, properties=propstr))
239
+ return '\n'.join(cir)
240
+
241
+
242
+ def spice_netlist(name, schem, extra="", corner='tt', temp=None, sim="NgSpice", **params):
243
+ """
244
+ Generate a spice netlist, taking a dictionary of schematic documents, and the name of the top level schematic.
245
+ It is possible to pass extra SPICE code and specify the simulation corner.
246
+ """
247
+ models = schem["models"]
248
+ declarations = set()
249
+ for subname, docs in schem.items():
250
+ if subname in {name, "models"}: continue
251
+ _id = SchemId.from_string(subname)
252
+ mod = models[f"models:{_id.cell}"]
253
+ ports = ' '.join(c[2] for c in mod['conn'])
254
+ body = circuit_spice(docs, models, declarations, corner, sim)
255
+ declarations.add(f".subckt {_id.model} {ports}\n{body}\n.ends {_id.model}") # parameters??
256
+
257
+ body = circuit_spice(schem[name], models, declarations, corner, sim)
258
+ ckt = []
259
+ ckt.append(f"* {name}")
260
+ ckt.extend(declarations)
261
+ ckt.append(body)
262
+ ckt.append(extra)
263
+ ckt.append(".end\n")
264
+
265
+ return "\n".join(ckt)
266
+
267
+ default_device_vectors = {
268
+ 'resistor': ['i'],
269
+ 'capacitor': ['i'],
270
+ 'inductor': ['i'],
271
+ 'vsource': ['i'],
272
+ 'isource': [],
273
+ 'diode': [],
274
+ 'nmos': ['gm', 'id', 'vdsat'],
275
+ 'pmos': ['gm', 'id', 'vdsat'],
276
+ 'npn': ['gm', 'ic', 'ib'],
277
+ 'pnp': ['gm', 'ic', 'ib'],
278
+
279
+ }
280
+ device_prefix = {
281
+ 'resistor': 'r',
282
+ 'capacitor': 'c',
283
+ 'inductor': 'l',
284
+ 'vsource': 'v',
285
+ 'isource': 'i',
286
+ 'diode': 'd',
287
+ 'nmos': 'm',
288
+ 'pmos': 'm',
289
+ 'npn': 'q',
290
+ 'pnp': 'q',
291
+
292
+ }
293
+ # @m.xx1.xmc1.msky130_fd_pr__nfet_01v8[gm]
294
+ def ngspice_vectors(name, schem, path=()):
295
+ """
296
+ Extract all the relevant vectors from the schematic,
297
+ and format them in NgSpice syntax.
298
+ Saves label/port net names, and vectors indicated on spice models.
299
+ """
300
+ models = schem["models"]
301
+ vectors = []
302
+ for id, elem in schem[name].items():
303
+ if elem['cell'] == 'port' and elem['name'].lower() != 'gnd':
304
+ vectors.append(('.'.join(path + (elem['name'],))).lower())
305
+ continue
306
+ m = models.get("models:"+elem['cell'], {})
307
+ n = m.get('models', {}).get(elem.get('props', {}).get('model'), {})
308
+ if n.get('type') == 'spice':
309
+ vex = n.get('NgSpice', {}).get('vectors', [])
310
+ comp = n.get('NgSpice', {}).get('component')
311
+ reftempl = n.get('NgSpice', {}).get('reftempl')
312
+ typ = (comp or reftempl or 'X')[0]
313
+ dtyp = (reftempl or 'X')[0]
314
+ if comp:
315
+ full = typ + '.' + '.'.join(path + (dtyp+elem['name'], comp))
316
+ elif path:
317
+ full = typ + '.' + '.'.join(path + (dtyp+elem['name'],))
318
+ else:
319
+ full = typ+elem['name']
320
+ vectors.extend(f"@{full}[{v}]".lower() for v in vex)
321
+ elif n.get('type') == 'schematic':
322
+ name = elem['cell']+"$"+elem['props']['model']
323
+ vectors.extend(ngspice_vectors(name, schem, path+("X"+elem['name'],)))
324
+ elif elem['cell'] in default_device_vectors: # no model specified
325
+ vex = default_device_vectors[elem['cell']]
326
+ typ = device_prefix.get(elem['cell'], 'x')
327
+ if path:
328
+ full = typ + '.' + '.'.join(path + (typ+elem['name'],))
329
+ else:
330
+ full = typ+elem['name']
331
+ vectors.extend(f"@{full}[{v}]".lower() for v in vex)
332
+ return vectors
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: nyancad
3
+ Version: 0.1.0
4
+ Summary: NyanCAD Python library for schematic editor with anywidget integration
5
+ Author-email: Pepijn de Vos <me@pepijndevos.nl>
6
+ License: MPL-2.0
7
+ Project-URL: Homepage, https://github.com/NyanCAD/Mosaic
8
+ Project-URL: Repository, https://github.com/NyanCAD/Mosaic
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: anywidget>=0.9.0
21
+ Requires-Dist: traitlets
22
+
23
+ # NyanCAD Python Library
24
+
25
+ A Python library for the [NyanCAD schematic editor](https://github.com/NyanCAD/Mosaic) with anywidget integration. Provides live access to schematic data from marimo notebooks.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install nyancad
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Basic Usage
36
+
37
+ ```python
38
+ import marimo as mo
39
+ from nyancad import SchematicBridge
40
+
41
+ # Create a schematic bridge widget
42
+ bridge = SchematicBridge()
43
+
44
+ # Display the widget (shows connection status)
45
+ bridge
46
+
47
+ # Access live schematic data
48
+ print(bridge.schematic_data)
49
+
50
+ # Send simulation data back to the editor
51
+ bridge.simulation_data = {
52
+ "results": [1.2, 3.4, 5.6],
53
+ "timestamp": "2024-01-01T00:00:00Z"
54
+ }
55
+ ```
56
+
57
+ ### Integration with Marimo
58
+
59
+ The SchematicBridge widget automatically connects to the NyanCAD editor running in the same marimo session. Any changes made in the schematic editor will be immediately reflected in the Python widget.
60
+
61
+ ```python
62
+ # In a marimo cell
63
+ bridge = SchematicBridge()
64
+ bridge # This will show the connection status
65
+
66
+ # In another marimo cell - access the live data
67
+ mo.md(f"""
68
+ ## Schematic Analysis
69
+ Raw schematic data: {len(bridge.schematic_data)} items
70
+ """)
71
+
72
+ # View the actual schematic data structure
73
+ bridge.schematic_data
74
+ ```
75
+
76
+ ## Features
77
+
78
+ - **Live Sync**: Real-time synchronization with NyanCAD schematic editor via PouchDB
79
+ - **Bidirectional Communication**: Send simulation data back to the editor from Python
80
+ - **Zero Configuration**: Automatically detects and connects to the active schematic
81
+ - **Raw Data Access**: Direct access to the complete schematic data structure
82
+ - **Marimo Integration**: Seamless integration with marimo notebooks
83
+
84
+ ## API Reference
85
+
86
+ ### SchematicBridge
87
+
88
+ The main widget class that provides bidirectional communication with the Mosaic editor.
89
+
90
+ #### Properties
91
+
92
+ - `schematic_data` (dict): Raw schematic data from the Mosaic editor, automatically synced
93
+ - `simulation_data` (dict): Simulation data to send to the Mosaic editor. Setting this will store the data with a timestamp in the editor's database
94
+
95
+ ## Development
96
+
97
+ This package is part of the [NyanCAD](https://github.com/NyanCAD/Mosaic) project. The anywidget integration uses a ClojureScript bridge that compiles to an ESM module, allowing seamless data sharing between the schematic editor and Python environment.
98
+
99
+ ## License
100
+
101
+ This project is licensed under the Mozilla Public License 2.0 - see the [LICENSE](../../LICENSE) file for details.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ nyancad/__init__.py
4
+ nyancad/anywidget.py
5
+ nyancad/netlist.py
6
+ nyancad.egg-info/PKG-INFO
7
+ nyancad.egg-info/SOURCES.txt
8
+ nyancad.egg-info/dependency_links.txt
9
+ nyancad.egg-info/requires.txt
10
+ nyancad.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ anywidget>=0.9.0
2
+ traitlets
@@ -0,0 +1 @@
1
+ nyancad
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nyancad"
7
+ version = "0.1.0"
8
+ description = "NyanCAD Python library for schematic editor with anywidget integration"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = {text = "MPL-2.0"}
12
+ authors = [
13
+ {name = "Pepijn de Vos", email = "me@pepijndevos.nl"},
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ ]
26
+ dependencies = [
27
+ "anywidget>=0.9.0",
28
+ "traitlets",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/NyanCAD/Mosaic"
33
+ Repository = "https://github.com/NyanCAD/Mosaic"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["."]
37
+ include = ["nyancad*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+