Three additions to support interactive/notebook-style harness building: - Graph cache invalidation: _invalidate_graph() called from all mutating methods so svg/png output reflects latest state after mutations - bom_list_dicts(): JSON-serializable BOM export as list of dicts - parse(harness=, populate_bom=): append YAML fragments to existing harness for cell-by-cell building with deferred BOM population Templates persist on the Harness object across parse() calls so component definitions in one fragment are available to connections in later fragments. Includes 24 new tests covering all three features plus full incremental workflow simulation. All 122 tests pass.
115 lines
3.9 KiB
Markdown
115 lines
3.9 KiB
Markdown
# Message 001
|
|
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| From | notebook-api-agent |
|
|
| To | web-ui-agent |
|
|
| Date | 2026-02-13T00:00:00Z |
|
|
| Re | Notebook-Ready API additions to WireViz core |
|
|
|
|
---
|
|
|
|
Three new capabilities have been added to the WireViz core library on `testing/web-ui-features` to support interactive/notebook-style harness building. All changes are backward-compatible.
|
|
|
|
## 1. Graph Cache Invalidation (automatic)
|
|
|
|
The `Harness._graph` cache is now invalidated automatically whenever any mutating method is called (`add_connector`, `add_cable`, `add_additional_bom_item`, `add_mate_pin`, `add_mate_component`, `connect`).
|
|
|
|
**No action needed on your side.** After any mutation, accessing `harness.svg` or `harness.png` will regenerate the diagram. Previously, calling `.svg` after adding new components would return stale output.
|
|
|
|
```python
|
|
h.add_connector("X1", pins=[1, 2])
|
|
svg1 = h.svg # renders X1
|
|
|
|
h.add_connector("X2", pins=[1, 2])
|
|
svg2 = h.svg # automatically re-renders, now includes X2
|
|
```
|
|
|
|
## 2. Structured BOM Export via `bom_list_dicts()`
|
|
|
|
New function in `wireviz.wv_bom`:
|
|
|
|
```python
|
|
from wireviz.wv_bom import bom_list_dicts
|
|
|
|
dicts = bom_list_dicts(harness.bom)
|
|
# Returns: [{"#": 1, "Qty": 2, "Description": "...", ...}, ...]
|
|
```
|
|
|
|
- Returns `List[Dict]` (JSON-serializable)
|
|
- Each dict maps column header to cell value
|
|
- Empty BOM returns `[]`
|
|
- Safe for `json.dumps(dicts, default=str)`
|
|
- Keys vary based on which columns have data (empty columns like P/N are omitted, matching `bom_list()` behavior)
|
|
|
|
## 3. YAML Fragment Merging via `parse(harness=...)`
|
|
|
|
The `parse()` function now accepts two new optional parameters:
|
|
|
|
```python
|
|
from wireviz.wireviz import parse
|
|
|
|
parse(
|
|
fragment, # YAML string, dict, or file path
|
|
return_types="harness",
|
|
harness=existing_harness, # append to this harness
|
|
populate_bom=False, # skip BOM for intermediate fragments
|
|
)
|
|
```
|
|
|
|
### Cell-by-cell building pattern:
|
|
|
|
```python
|
|
from wireviz.wv_harness import Harness
|
|
from wireviz.wv_dataclasses import Metadata, Options, Tweak
|
|
from wireviz.wireviz import parse
|
|
from wireviz.wv_bom import bom_list_dicts
|
|
|
|
# Create empty harness
|
|
h = Harness(metadata=Metadata({}), options=Options(), tweak=Tweak())
|
|
|
|
# Cell 1: Define connectors
|
|
parse({"connectors": {"X1": {"pins": [1,2]}, "X2": {"pins": [1,2]}}},
|
|
return_types="harness", harness=h, populate_bom=False)
|
|
|
|
# Cell 2: Define cable
|
|
parse({"cables": {"W1": {"wirecount": 2, "colors": ["BK", "RD"]}}},
|
|
return_types="harness", harness=h, populate_bom=False)
|
|
|
|
# Cell 3: Connect and render
|
|
parse({"connections": [[{"X1": [1,2]}, {"W1": [1,2]}, {"X2": [1,2]}]]},
|
|
return_types="harness", harness=h, populate_bom=True)
|
|
|
|
# Now get outputs
|
|
svg_data = h.svg # rendered diagram
|
|
bom_data = bom_list_dicts(h.bom) # JSON-serializable BOM
|
|
```
|
|
|
|
### Mixed programmatic + YAML pattern:
|
|
|
|
```python
|
|
h = Harness(metadata=Metadata({}), options=Options(), tweak=Tweak())
|
|
h.add_connector("X1", pins=[1, 2]) # programmatic
|
|
|
|
# YAML fragment references existing X1
|
|
parse({"connectors": {"X2": {"pins": [1,2]}},
|
|
"cables": {"W1": {"wirecount": 2, "colors": ["BK","RD"]}},
|
|
"connections": [[{"X1": [1,2]}, {"W1": [1,2]}, {"X2": [1,2]}]]},
|
|
return_types="harness", harness=h, populate_bom=True)
|
|
```
|
|
|
|
### Key behaviors:
|
|
|
|
- When `harness` is provided, the fragment's `metadata`/`options`/`tweak` sections are ignored
|
|
- Templates defined in earlier fragments are available to later fragments (persisted on the harness)
|
|
- `populate_bom=False` skips BOM computation; call `h.populate_bom()` explicitly when ready
|
|
- Existing components can be re-referenced across fragments without re-definition
|
|
|
|
---
|
|
|
|
**Next steps for recipient:**
|
|
- [ ] Integrate `harness.svg` for live preview in notebook cells
|
|
- [ ] Use `bom_list_dicts()` for the BOM panel/table
|
|
- [ ] Implement cell-by-cell YAML parsing using `parse(harness=h, populate_bom=False)`
|
|
- [ ] Call `populate_bom=True` on final render or when BOM display is requested
|