diff --git a/frontend/src/simulation/parts/BasicParts.ts b/frontend/src/simulation/parts/BasicParts.ts index 76428465..3d428ea7 100644 --- a/frontend/src/simulation/parts/BasicParts.ts +++ b/frontend/src/simulation/parts/BasicParts.ts @@ -1,5 +1,6 @@ import { PartSimulationRegistry } from './PartSimulationRegistry'; import { useElectricalStore } from '../../store/useElectricalStore'; +import { useSimulatorStore } from '../../store/useSimulatorStore'; import { emitPropertyChange } from './partUtils'; /** @@ -171,6 +172,14 @@ const LED_BURNOUT_A = 0.1; /** Surface a circuit fault for an LED — console + a UI event the toolbar shows. */ function reportLedFault(componentId: string, kind: string, message: string): void { console.warn(`[led] ${componentId}: ${message}`); + // Mark it destroyed in the shared burnt set (P4): the canvas renders it + // charred + a smoke badge, and the solver opens it — same treatment as a + // burnt resistor/capacitor. (The LED also goes dark via its own update.) + try { + useSimulatorStore.getState().markComponentBurnt?.(componentId); + } catch { + /* store unavailable (test env) — ignore */ + } if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function') { try { window.dispatchEvent( diff --git a/frontend/src/simulation/spice/CircuitSimulationService.ts b/frontend/src/simulation/spice/CircuitSimulationService.ts index 2972e07c..f388937b 100644 --- a/frontend/src/simulation/spice/CircuitSimulationService.ts +++ b/frontend/src/simulation/spice/CircuitSimulationService.ts @@ -40,6 +40,9 @@ export interface SimulatorStorePort { end: { componentId: string; pinName: string }; }>; boards: Array<{ id: string; boardKind: string; pinStates?: Record }>; + /** Components destroyed at runtime (P4) — excluded from the netlist so a + * burnt part actually goes open. Optional for non-store ports. */ + burntComponents?: Set; }; subscribe(listener: (state: unknown, prev: unknown) => void): () => void; } @@ -279,8 +282,14 @@ export class CircuitSimulationService { private async runSolve(): Promise { const state = this.simStore.getState(); + // P4: a runtime-destroyed part is excluded from the netlist so it actually + // goes open — its current stops and anything it fed loses power (cascading + // failure), the way real hardware behaves once a component burns out. + const burnt = state.burntComponents; + const liveComponents = + burnt && burnt.size > 0 ? state.components.filter((c) => !burnt.has(c.id)) : state.components; const snap = { - components: state.components, + components: liveComponents, wires: state.wires, boards: state.boards.map((b) => ({ id: b.id,