fix(simulator): underscore-separated component ids for SPICE safety

The user reported the default editor canvas — Arduino Uno + LED +
220Ω resistor — was correctly powered (1.84 V at the LED anode,
14 mA through the diode) but the LED visual stayed dark. Only the
built-in pin-13 LED on the wokwi-arduino-uno element lit up.

Root cause: ngspice's WASM build truncates branch-current vector
keys at the first hyphen. A sense source named V_led-builtin_sense
ends up exposed under a key like v_led#branch rather than the
expected v_led-builtin_sense#branch. CircuitSimulationService and
BasicParts.ts both look up the FULL key, miss, and the LED's
brightness update treats raw as undefined → digital-fallback path
runs but the SPICE memo timestamp is fresh so HOLD keeps zero
brightness. Visible symptom: a perfectly conducting LED that never
lights.

Fix in two places:
  - Default canvas (useSimulatorStore.ts): rename 'led-builtin' /
    'r-builtin' to 'led_builtin' / 'r_builtin' (and the matching
    wire ids).
  - DynamicComponent.tsx makeNewComponent: the id template was
    'metadata.id-timestamp-rand' producing hyphens for every
    user-added component too. Switched to underscores, AND replace
    any hyphens already in metadata.id (e.g. 'led-bar-graph') so
    the prefix doesn't reintroduce the bug.

Existing saved projects whose ids contain hyphens are not migrated
here — those will keep the visual bug until either the operator
edits the components or we add a sanitisation step inside
componentToSpice + BasicParts. The next follow-up commit can add
that if you confirm this default-canvas fix works.
This commit is contained in:
David Montero Crespo 2026-05-18 10:01:38 -03:00
parent ca2b76f1f8
commit 32f5b407af
2 changed files with 23 additions and 10 deletions

View File

@ -552,8 +552,15 @@ export function createComponentFromMetadata(
y: number;
properties: Record<string, any>;
} {
// Underscore separators (not '-') so the resulting id is safe to embed
// in SPICE component / source names. ngspice's WASM build truncates
// vector keys at '-', which broke branch-current lookups for any LED /
// ammeter wired up by the user (visible symptom: correct node voltage,
// dark LED). Also strip '-' from metadata.id (e.g. 'led-bar-graph') so
// the prefix doesn't reintroduce a hyphen.
const safePrefix = metadata.id.replace(/-/g, '_');
return {
id: `${metadata.id}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
id: `${safePrefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
metadataId: metadata.id,
x,
y,

View File

@ -1894,16 +1894,22 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
// short forward-biased between 5V and GND — real hardware blows the
// diode, and the ngspice solver returns an indeterminate / NaN branch
// current so the visual LED never lights up on the canvas either.
// NOTE: component ids must NOT contain hyphens. ngspice (WASM build)
// truncates branch-current vector names at '-', so a sense source
// named V_led-builtin_sense yields the wrong key in branchCurrents
// and the LED's update() loop never sees the diode current — the
// node voltage is correct (the user sees ~1.84V on the wire) but the
// visual brightness stays at zero. Underscore is safe.
components: [
{
id: 'led-builtin',
id: 'led_builtin',
metadataId: 'led',
x: 380,
y: 100,
properties: { color: 'red' },
},
{
id: 'r-builtin',
id: 'r_builtin',
metadataId: 'resistor',
x: 240,
y: 130,
@ -1914,24 +1920,24 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
wires: [
// Pin 13 → resistor pin 1 (current-limiting side).
{
id: 'wire-builtin-pin13',
id: 'wire_builtin_pin13',
start: { componentId: 'arduino-uno', pinName: '13', x: 0, y: 0 },
end: { componentId: 'r-builtin', pinName: '1', x: 0, y: 0 },
end: { componentId: 'r_builtin', pinName: '1', x: 0, y: 0 },
waypoints: [],
color: '#22c55e',
},
// Resistor pin 2 → LED anode.
{
id: 'wire-builtin-anode',
start: { componentId: 'r-builtin', pinName: '2', x: 0, y: 0 },
end: { componentId: 'led-builtin', pinName: 'A', x: 0, y: 0 },
id: 'wire_builtin_anode',
start: { componentId: 'r_builtin', pinName: '2', x: 0, y: 0 },
end: { componentId: 'led_builtin', pinName: 'A', x: 0, y: 0 },
waypoints: [],
color: '#22c55e',
},
// LED cathode → GND.
{
id: 'wire-builtin-cathode',
start: { componentId: 'led-builtin', pinName: 'C', x: 0, y: 0 },
id: 'wire_builtin_cathode',
start: { componentId: 'led_builtin', pinName: 'C', x: 0, y: 0 },
end: { componentId: 'arduino-uno', pinName: 'GND.1', x: 0, y: 0 },
waypoints: [],
color: '#000000',