Repository navigation
Updated Circuit Editor to V2 and version to Alpha v0.3.0 - #12
Merged
Merged
Conversation
New greenfield module at src/renderer/src/circuit/ per docs/circuit-view-tech-spec.md: - core/model: circuit.json v2 schema, parse/serialize, v1+Wokwi migration, unknown-key preservation (B3) - core/routing: wireRouting port with Wokwi '*' journey semantics (B2), parametric junction math - core/nets: DSU nets across both views — buses (B8 seam), junction-by-identity (B9), net labels, implicit-connection seam; canonical keys (B10) - core/commands + core/store: pure Commands, undo/redo with gesture merging, duplicate-wire guard (B5), delete cascades with junction repair - core/geometry + core/refdes: pin transforms, pin-grid snapping, spatial hash, refdes assignment - views/CircuitView: read-only M0 preview mounted in the Circuit tab behind localStorage flag tinystudio.circuitV2 (legacy DiagramEditor untouched) - 35 core tests (npm run test:circuit, esbuild + node:test); typecheck clean Docs: architecture comparison (circuit-architecture-and-roadmap.md), tech spec with resolved decisions (circuit-view-tech-spec.md), agent progress log (circuit-editor-progress.md).
…ey, pending-junction typing - core/clipboard.ts: buildClipboard (fixpoint filtering of junction riders), parseClipboard (shape-detected JSON), materializePaste (fresh refdes ids, endpoint rewrite, placement offset) - commands.composite() gains an optional mergeKey so multi-part drag frames collapse into one undo step like single-part gestures - model: PendingJunctionEnd + isPendingJunction for v1-migrated free-point endpoints awaiting geometric resolution - 18 new tests (53 total)
- parts/svg.ts: namespaceSvgIds / svgNs / stripSvgSize / escapeXml — pure string transforms, unit-tested; full sanitizer arrives with M2 packs - views/exportImage.ts: composeSceneSvg (namespaced per part instance), exportSvg / exportPng @2x with watermark - index.ts re-exports clipboard + svg helpers
- views/partsAdapter.ts: legacy-partsLibrary geometry glue (pin transforms,
endpoint resolution incl. junction chains, frozen-bend reroutes,
snap-by-first-pin per spec §6.3) — swapped for the M2 registry later
- views/canvas/Canvas.tsx: camera (wheel zoom, pan, fit), selection +
marquee, wire draw (pin→pin/junction tap via {wire,t} tAtPoint, Shift
straight, Esc), selected-wire handles (segment/vertex/endpoint;
endpoint re-targets to pin or junction, reverts on empty space —
v2 has no free endpoints), part drag single+multi with frozen bends
(between-two-moved wires translate), label drag, R/context rotate,
arrow nudge (Shift=5x), Del cascade, Ctrl+Z/Y undo/redo,
Ctrl+C/X/V/D clipboard via core/clipboard, pending-junction resolution
on first render (closes the M0 migration gap)
- views/palette/Palette.tsx, views/inspector/Inspector.tsx: legacy rail +
inspector ported; inspector renames refdes (validated), edits attrs,
moves/rotates with reroutes
- views/CircuitView.tsx: shell — store owner, debounced (250ms) save via
onChange with external-edit echo/skip guard, toolbar, zoom cluster,
status pills, PartsEditor modal
…s log EditorPanel v2 branch: CircuitV2View migrates diagram.json → circuit.json (+ diagram.json.bak) on first open, then mounts the editor on the circuit.json hidden buffer with updateFileContent as the debounced sink. diagram.json intentionally left in place until M4 (legacy editor still default-on behind the flag).
Fritzing part roots carry x=0px/y=0px + width/height; injecting placement attributes duplicated them, making the composed SVG invalid XML — inline DOM rendering tolerated it, the standalone .svg and the PNG rasterizer's <img> load did not. prepareSvgForEmbed strips prolog/doctype and root geometry attrs (viewBox kept). Also: #id selectors in <style> blocks are now namespaced, and var(--token) colors are inlined at export time so builtin board art survives outside the app stylesheet.
- parts/breadboard.ts: parametric generator (mini 17 / half+ 30 / full+ 63 cols), every hole on the GRID_BB pitch so snap-by-first-pin placement makes seating an exact coordinate match; per-column bank buses + rail buses via breadboardBuses(); crisp generated SVG (body, channel, rail stripes, row letters, column numbers) - partsAdapter: implicitSeats() derives pin-in-hole connections (never stored, spec §4/§7.3) via SpatialHash; seatedPartsOn() for sticky boards; holeAt() nearest-hole lookup - CircuitView: registers breadboards as builtins pre-parse; net model now runs with busesFor + implicit seats - Canvas: breadboards skip per-pin hit divs (>60 pins) — press-without- displacement on a board is a hole interaction (arm/finalize wires on holes), drag moves the board WITH its seated parts, hover shows a hole highlight + tooltip (hole name · net members), green seat marks render for every derived connection; wire preview snaps to hovered holes - 4 new test groups (60 total): hole grid alignment, pin counts/naming, bus shapes, bus+implicit net integration
…odel - parts/symbols.ts: generated IC-box symbols so schematic never blocks on missing art (authored art preferred, cached, pins on the major grid) - partsAdapter goes view-generic (ViewId param, bb defaults + aliases); ratsnest() bridges nets connected globally but unrouted in the active view (bb treats breadboard seating as routed) - Canvas: view prop — per-view placements/wires/snap grids, ink orthogonal schematic wires, flip (F/Inspector, sch), dashed ratsnest layer, bb-only seating UI; remounts per view so gesture state can't leak - Shell: Breadboard|Schematic toggle, unplaced-parts tray (click to place), routed-here status counter, per-view part placement; Inspector edits the active view's placement incl. mirror - 63/63 tests, typecheck clean
…id; net-label rotation (R key + inspector)
…IndexedDB, restored on mount
…tDef conversion, drop-to-place + persist
…mulate panel, sim sources; progress log
…ce toggles, readable DC table
…CSV export; boards render under everything; resistor bands follow value
…rcuitEditor Circuit View v2 is now the only editor mounted by EditorPanel's CircuitView - no more tinystudio.circuitV2 flag or CircuitV1View branch. diagram.json projects still auto-migrate to circuit.json on open (unchanged). Removes src/renderer/src/components/DiagramEditor.tsx (2009 lines, legacy breadboard editor) and the already-dead CircuitEditor.tsx static viewer (B21). npm run typecheck (node+web) clean; test:circuit 95/95.
SimPanel gets an auto-rerun checkbox: once on, doc edits re-run the active analysis after a 400ms debounce (single in-flight run, guarded by a ref). Breadboard hole tooltips now append the net's solved DC (.op) voltage alongside the existing 'hole · net members' text, via a new simVoltageForNet(netIdx) lookup threaded CircuitView -> Canvas. npm run typecheck (node+web) clean; test:circuit 95/95.
generateNetlist now records elementOfPart (lowercased SPICE device names per part id, e.g. R1 -> ['rr1']) alongside the existing nodeOfNet. New pure mapSimIssue/mapSimIssues (core/netlist.ts) scan raw ngspice error/warning lines for those device names and node names (word-boundary, case-insensitive, ground '0' excluded) and return the implicated part ids + net indices. SimPanel's error box now renders clickable part/net chips (+ a 'select all') under any run failure; CircuitView wires them to canvas selection via a new selectSimIssue, expanding net hits to their member parts. +6 tests (elementOfPart, device-name match, node-name match incl. ground skip, n1/n10 word-boundary, multi-line de-dup). npm run typecheck (node+web) clean; test:circuit 100/100.
Closes the last M2/M4-leftover item on sim.probes (spec Sec10.3): three
placeable builtin probes, CircuitLab-flag style, seatable on breadboards
like any part:
- sim-probe-v 1 pin - voltage probe (ngspice already reports every
node, so this needs no SPICE element)
- sim-probe-vdiff 2 pins - differential voltage; core/probes.ts computes
a synthetic vdiff(<id>) = v(+) - v(-) vector
(real and imaginary, for AC) after each run
- sim-probe-i 2 pins - current probe, wired in series; emits a real
0V series voltage source (V<id> DC 0) so
ngspice reports i(v<id>) without disturbing
the circuit
core/netlist.ts: EMITTERS regexes for the three types (anchored with \b, not
$, since the match key has a trailing ' <family>'); elementOfPart already
built into the last commit's error-mapping change. core/probes.ts adds
probesIn/diffProbeVector(s)/probeLabelFor, all pure and tested. SimPanel/Plot
now merge diff-probe vectors into results and prefer a probe's attrs.label
over the raw v(node)/i(vXX)/vdiff(id) name in the DC table and plot legend.
refdes.ts gets an explicit probe->'P' prefix entry (matches the prior
fallback, now documented).
+9 unit tests (probes.test.ts) +1 real-engine integration test (sim.test.ts:
verifies i(vamm1) against the analytic divider current). npm run typecheck
(node+web) clean; test:circuit 107/107.
…-connect implicitSeats() previously seated each of a part's pins independently against the nearest hole within SEAT_RADIUS, so a part with one leg near a hole and another off-grid would report a single spurious connection while the rest of its pins silently dangled unconnected. Now a part seats as a rigid body — ALL of its pins must land within SEAT_RADIUS at the part's current placement for ANY of them to seat; otherwise none do (matches Fritzing: a component either fully plugs in or just rests on top). Pairs naturally with the existing snap-by-first-pin grid snap, so well-formed grid-pitched parts still seat on the first touch. Single computation point (CircuitView.tsx memo), consumed by net building, hole seat marks, sticky-board drag, and rigid board rotation — all get the fix for free. npm run typecheck (node+web) clean; test:circuit 107/107 (implicitSeats depends on the legacy partsLibrary registry like the rest of partsAdapter.ts, so it isn't unit-tested in isolation, consistent with the rest of that module).
Closes the last M2-scope gap noted since M1 ('leg pins resolve to the drawn
leg end; real rubber-band legs are a Phase 2 renderer feature').
Import (both importers, mirrored):
- scripts/fritzing-import.mjs and circuit/parts/fzpz.ts now read each
connector's legId and tag the resolved pin name into a new
PartView.legs: string[] (lib/partsLibrary.ts). Geometry itself needed no
change — core/geometry.pinWorld and partsAdapter.pinWorldOf were already
leg-offset aware (Placement.legs existed in the model since M0/M1 but
nothing ever produced or consumed it).
Canvas (bb view only, per the model's 'legs bb only' contract):
- A pin tagged as a leg can be dragged independent of the part body
(beginLegDrag in views/canvas/Canvas.tsx): live-dispatches merged
placePart commands per move (same pattern as the existing label-offset
drag), so wires re-anchor to the tip in real time via the normal
doc -> resolve()/wireGeometry() recompute — no separate drag-override
path needed. A real (>=3px) drag suppresses the trailing click so it
doesn't also arm/complete a wire from that pin; a plain click still
works normally. Double-click a bent leg to reset it to rest.
- Visual: a simple ink line from the rest pin to the dragged tip, overlaid
on the existing static Fritzing leg art (which stays at rest position) —
real per-instance SVG leg-path warping is future work, noted in code.
- implicitSeats() (M2's rigid seat rule, previous commit) already reads
pin world position through the leg-aware pinWorldOf, so a stretched leg
can now actually reach an adjacent hole and seat, matching Fritzing.
Not covered: Inspector UI for leg offsets (canvas drag + reset covers the
interaction), and this specific gesture code has no automated test (Canvas.tsx
render/gesture code isn't unit-tested anywhere in this suite; verified by
typecheck + code review only — the importer half touches DOM-only code paths
the existing fzpz.test.ts also intentionally leaves untested).
npm run typecheck (node+web) clean; test:circuit 107/107 (unaffected).
Closes the last M2-scope item: GitHub-distributed parts packs (spec §5.4, Arduino Boards-Manager pattern), targeting the app's actual current pipeline (legacy PartDef v1 + lib/userParts.ts persistence) rather than the unbuilt v2 registry the spec assumes. - circuit/parts/packs.ts: fetchIndex/fetchManifest (shape-validated, relative URLs resolved against their own manifest/index), installPack (per-part fetch + saveUserPart, continues past individual failures, marks the pack installed only if >=1 part landed), index-URL list + installed-version settings in localStorage. DEFAULT_INDEX_URL points at the tinyparts repo the M2 decision log already named. - circuit/views/packs/PackManager.tsx: modal (Components rail -> new package icon) — add/remove index URLs, per-pack Install/Update with progress and toast summaries. - scripts/make-pack-index.mjs: wraps a scripts/fritzing-import.mjs output directory into pack.json + index.json — re-shapes the existing bulk importer's manifest, no new conversion logic. - docs/tinyparts-pack-setup.md: full guide for Geoff to create/host the tinyparts repo by hand (layout, exact JSON schemas, the generator command, local testing, ATTRIBUTION requirement per the M0 licensing decision, and an explicit 'not done' list — no hash verification, no launch-time update check, no zip hosting). Verified two ways: 7 unit tests (packs.test.ts — index/manifest validation, relative-URL resolution, partial-failure install, settings) against mocked fetch/localStorage, AND a real end-to-end smoke test (not part of the suite): ran make-pack-index.mjs against the app's actual bundled 29-part catalogue, served the output over a plain local HTTP server, and installed it through the real packs.ts client — 29/29 installed, 0 failures. npm run typecheck (node+web) clean; test:circuit 114/114.
Both milestones now fully closed per the tech spec's Section 15 table. Only M5 (KiCad/Wokwi interop) remains untouched. Full rundown of all seven commits, verification method (sandbox esbuild-linux workaround for test:circuit), and known gaps for the next agent.
- Default theme to light mode (main.tsx) - Top/bottom bar recolored blue -> green via new --bar-accent token, kept independent of --brand so buttons/selection/focus rings are untouched (base.css, Header.tsx, StatusBar.tsx) - Docs panel tabs are now controlled via editorSlice.docsTab; opening a folder or example (activateWorkspace in fileCommands.ts) switches off Examples onto Docs automatically - Per-tab accent colors: Examples yellow, Docs unchanged blue, Studio AI grape (#9c6c9c) (DocsPanel.tsx) - Examples tab: removed redundant "Examples" header, added generous spacing/padding around cards (ExamplesContent.tsx) - AI panel toolbar: dropped "Studio AI" label, shows just the model name
…-deps Fixes electron binary not being fetched on postinstall.
✅ Deploy Preview for tinystudios ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Thanks for your interest in tinyStudio! 🙏 This project is in early alpha and is not accepting external contributions right now, Contributions will open up as the project stabilizes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.