diff --git a/frontend/src/components/DynamicComponent.tsx b/frontend/src/components/DynamicComponent.tsx index a33a8c9c..0e045b3d 100644 --- a/frontend/src/components/DynamicComponent.tsx +++ b/frontend/src/components/DynamicComponent.tsx @@ -204,28 +204,50 @@ export const DynamicComponent: React.FC = ({ let cleanupSimulationEvents: (() => void) | undefined; if (logic && logic.attachEvents && simulator) { - // Helper to find Arduino pin connected to a component pin + // Helper to find Arduino pin connected to a component pin. + // Traces through passive components (resistors) so that a circuit like + // LED-cathode → resistor → GND returns -1 (GND) instead of null. const getArduinoPin = (componentPinName: string): number | null => { const state = useSimulatorStore.getState(); - const wires = state.wires.filter( - w => (w.start.componentId === id && w.start.pinName === componentPinName) || - (w.end.componentId === id && w.end.pinName === componentPinName) - ); - for (const w of wires) { - // Find which endpoint connects to a board component - const boardEndpoint = isBoardComponent(w.start.componentId) ? w.start : - isBoardComponent(w.end.componentId) ? w.end : null; - if (boardEndpoint) { - // Use the board's actual kind for pin mapping (instance ID may differ from kind, - // e.g. board ID 'arduino-uno' after switching to 'raspberry-pi-pico') - const boardKind = state.boards.find((b) => b.id === boardEndpoint.componentId)?.boardKind - ?? boardEndpoint.componentId; - const pin = boardPinToNumber(boardKind, boardEndpoint.pinName); - if (pin !== null) return pin; + // Passive component metadataIds that are electrically transparent. + const PASSIVE = new Set(['resistor', 'resistor-us']); + + // Depth-limited BFS: trace from (fromId, fromPin) through wires, + // traversing through passive components to reach a board pin. + const trace = (fromId: string, fromPin: string, depth: number): number | null => { + if (depth > 6) return null; + + const wires = state.wires.filter( + w => (w.start.componentId === fromId && w.start.pinName === fromPin) || + (w.end.componentId === fromId && w.end.pinName === fromPin) + ); + + for (const w of wires) { + const selfEp = (w.start.componentId === fromId && w.start.pinName === fromPin) ? w.start : w.end; + const otherEp = selfEp === w.start ? w.end : w.start; + + if (isBoardComponent(otherEp.componentId)) { + // Direct board connection + const boardKind = state.boards.find((b) => b.id === otherEp.componentId)?.boardKind + ?? otherEp.componentId; + const pin = boardPinToNumber(boardKind, otherEp.pinName); + if (pin !== null) return pin; + } else { + // Intermediate passive component — traverse through it + const comp = state.components.find(c => c.id === otherEp.componentId); + if (comp && PASSIVE.has(comp.metadataId)) { + // Resistors have two pins; find the other one to continue tracing + const otherPin = otherEp.pinName === '1' ? '2' : '1'; + const result = trace(otherEp.componentId, otherPin, depth + 1); + if (result !== null) return result; + } + } } - } - return null; + return null; + }; + + return trace(id, componentPinName, 0); }; cleanupSimulationEvents = logic.attachEvents(el, simulator, getArduinoPin, id); diff --git a/frontend/src/simulation/MicroPythonLoader.ts b/frontend/src/simulation/MicroPythonLoader.ts index d8b9d4dd..737e1be0 100644 --- a/frontend/src/simulation/MicroPythonLoader.ts +++ b/frontend/src/simulation/MicroPythonLoader.ts @@ -8,6 +8,11 @@ import { get as idbGet, set as idbSet } from 'idb-keyval'; import createLittleFS from 'littlefs'; +// ?url tells Vite to return the correct asset URL for the WASM binary +// (without this, Emscripten fetches 'littlefs.wasm' relative to the bundle +// which resolves to the SPA index.html — causing the "expected magic word" error) +// @ts-ignore — Vite ?url import, no type declaration needed +import littlefsWasmUrl from 'littlefs/dist/littlefs.wasm?url'; // Flash geometry (matches rp2040js and MicroPython defaults) const FLASH_START_ADDRESS = 0x10000000; @@ -68,8 +73,9 @@ export async function loadUserFiles( // Create a backing buffer for the LittleFS filesystem const fsBuffer = new Uint8Array(MICROPYTHON_FS_BLOCK_COUNT * MICROPYTHON_FS_BLOCK_SIZE); - // Initialize the littlefs WASM module - const lfs = await createLittleFS({}); + // Initialize the littlefs WASM module. + // locateFile redirects Emscripten's internal fetch to the Vite-resolved asset URL. + const lfs = await createLittleFS({ locateFile: () => littlefsWasmUrl }); // Register flash read/write callbacks for the WASM module const flashRead = lfs.addFunction( diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 42881531..8d34bd91 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -20,6 +20,7 @@ export default defineConfig({ }, }, }, + assetsInclude: ['**/*.wasm'], optimizeDeps: { include: ['avr8js', 'rp2040js', '@wokwi/elements', 'littlefs'], },