From 145710561a8839a77d65ddbecd4175196acaaab4 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Tue, 26 May 2026 22:06:44 -0300 Subject: [PATCH] fix: 5 user-reported issues (#208 #209 #210 #211 #212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #208 — stale binary executes after compile error EditorToolbar.handleCompile: on failed compile, clear the active board's compiledProgram so a subsequent Run can't silently execute the previous successful build (which doesn't match the editor any more). The Run gate already short-circuits on !compiledProgram and forces a fresh compile. #209 — compile terminal kept stale messages across runs EditorToolbar.handleCompile: setCompileLogs([]) at the top of the handler. Previously logs from the prior compile lingered, making it hard to tell new errors / warnings apart from old ones. #210 — desktop File > New Project did nothing desktop/menu.ts: the menu action used to dispatch a CustomEvent nobody listened to. Replaced with a real `newProject()` function that stops the running simulation, removes every board (also drops the bridges + wires touching them), clears components / wires, loads the default Blink sketch into the editor, clears project metadata, and wipes the compile output. Confirms first if there's unsaved work on the canvas. #211 — deleting the only board made every other component unresponsive (wires still worked) SimulatorCanvas.tsx::interactionRunning: the old expression treated boards.length === 0 as "boardless electrical mode is running" — which suppressed the property dialog on click and made non-sensor components look frozen. Fixed by also requiring useElectricalStore.submittedNetlist !== '' before flipping to the boardless-running branch. SPICE has to have actually solved at least once for the mode to engage. #212 — ESP32 Support 404 with no actionable message desktop/Esp32QemuPrompt.tsx: catch the raw "download HTTP 404" / "not found" upstream error and reword it to "ESP32 support is not yet available for your platform. The Velxio team is preparing this build - try again in a few days, or use Arduino/RP2040 boards in the meantime." The real fix is server-side (the velxio team needs to publish a qemu-xtensa.tar.gz for the user's platform into the asset bucket and update esp32-qemu/latest.json). Tracked in project/desktop-agent-v040/ follow-ups. All five fixes verified with `tsc --noEmit` clean and the existing 25-test vitest suite green. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/editor/EditorToolbar.tsx | 11 +++ .../components/simulator/SimulatorCanvas.tsx | 11 ++- frontend/src/desktop/Esp32QemuPrompt.tsx | 17 ++++- frontend/src/desktop/menu.ts | 73 +++++++++++++++++++ 4 files changed, 109 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx index 52c7f01e..11a785ac 100644 --- a/frontend/src/components/editor/EditorToolbar.tsx +++ b/frontend/src/components/editor/EditorToolbar.tsx @@ -163,6 +163,10 @@ export const EditorToolbar = ({ setCompiling(true); setMessage(null); setConsoleOpen(true); + // Wipe the previous build's output before we append anything new. + // Issue #209: lingering logs from prior compiles made it impossible + // to tell the latest errors / warnings apart from stale ones. + setCompileLogs([]); trackCompileCode(); // ── Chip-program path ─────────────────────────────────────────────── @@ -380,6 +384,13 @@ export const EditorToolbar = ({ } else { const errText = result.error || result.stderr || 'Compile failed'; setMessage({ type: 'error', text: errText }); + // Issue #208: drop the previous successful program from this + // board so a subsequent Run cannot silently execute stale code + // that doesn't match the editor any more. The Run button gates + // on `!compiledProgram` and will refuse + force a re-compile. + if (activeBoardId) { + updateBoard(activeBoardId, { compiledProgram: null }); + } // Detect missing library errors — common patterns: // "No such file or directory" for #include, "fatal error: XXX.h" const looksLikeMissingLib = diff --git a/frontend/src/components/simulator/SimulatorCanvas.tsx b/frontend/src/components/simulator/SimulatorCanvas.tsx index 5b8d0321..7b064749 100644 --- a/frontend/src/components/simulator/SimulatorCanvas.tsx +++ b/frontend/src/components/simulator/SimulatorCanvas.tsx @@ -259,9 +259,16 @@ export const SimulatorCanvas = ({ headerSlot }: SimulatorCanvasProps = {}) => { // their own state on click instead of opening the property dialog. // We treat board-less + un-paused as "interaction-running" so the same // gating logic that suppresses the dialog for MCU-running mode also - // covers board-less circuits. + // covers board-less circuits — BUT only when SPICE has actually + // engaged (submittedNetlist != ''). Without that extra check, deleting + // the only board on a normal Arduino+LED circuit dropped boards to 0 + // and silently flipped every remaining component to "running" mode, + // which suppresses the property dialog and makes them appear + // unresponsive to clicks (issue #211). const electricalPaused = useElectricalStore((s) => s.paused); - const interactionRunning = running || (boards.length === 0 && !electricalPaused); + const electricalEngaged = useElectricalStore((s) => s.submittedNetlist !== ''); + const interactionRunning = + running || (boards.length === 0 && electricalEngaged && !electricalPaused); // Refs that mirror state/props for use inside touch event closures // (touch listeners are added imperatively and can't access current React state) diff --git a/frontend/src/desktop/Esp32QemuPrompt.tsx b/frontend/src/desktop/Esp32QemuPrompt.tsx index fe746099..ece53183 100644 --- a/frontend/src/desktop/Esp32QemuPrompt.tsx +++ b/frontend/src/desktop/Esp32QemuPrompt.tsx @@ -104,7 +104,22 @@ export const Esp32QemuPrompt = () => { setStatus(fresh); if (fresh.installed) setOpen(false); } catch (e) { - setErr(e instanceof Error ? e.message : String(e)); + // Issue #212: the backend returns 404 when the velxio team + // hasn't published an ESP32 QEMU build for the user's platform + // yet (Windows / Linux x86_64 / macOS aarch64). The raw + // "download HTTP 404" string is opaque - reword to something + // the user can actually act on (or at least understand isn't + // their fault). + const raw = e instanceof Error ? e.message : String(e); + if (/HTTP\s*404|not found/i.test(raw)) { + setErr( + 'ESP32 support is not yet available for your platform. ' + + 'The Velxio team is preparing this build - try again in a ' + + 'few days, or use Arduino/RP2040 boards in the meantime.', + ); + } else { + setErr(raw); + } } finally { setInstalling(false); setProgress(null); diff --git a/frontend/src/desktop/menu.ts b/frontend/src/desktop/menu.ts index cb54f49a..4916e73e 100644 --- a/frontend/src/desktop/menu.ts +++ b/frontend/src/desktop/menu.ts @@ -26,6 +26,9 @@ import { listen } from './tauriBridge'; import { dlog } from './log'; import { triggerDownloadVlx, importVlxFile } from '../utils/vlxFile'; import { useSimulatorStore } from '../store/useSimulatorStore'; +import { useEditorStore } from '../store/useEditorStore'; +import { useProjectStore } from '../store/useProjectStore'; +import { useCompileLogsStore } from '../store/useCompileLogsStore'; import { switchLocale } from '../i18n/path'; import { LOCALES, type Locale } from '../i18n/config'; @@ -74,6 +77,8 @@ async function handle(action: MenuAction, payload?: MenuEventPayload): Promise 0 || + sim.components.length > 0 || + sim.wires.length > 0 || + editor.files.some((f) => f.modified) || + project.currentProject !== null; + + if (hasWork) { + // eslint-disable-next-line no-alert + const ok = window.confirm( + 'Start a new project? Any unsaved changes will be lost.', + ); + if (!ok) return; + } + + // Stop any running simulation first so workers / bridges shut down + // cleanly. Idempotent — no-op if nothing is running. + if (sim.running) { + sim.stopSimulation(); + } + + // Drop every board (also disconnects its bridges + removes wires + // touching it). Iterate over a snapshot copy since removeBoard + // mutates the array. + for (const board of [...sim.boards]) { + sim.removeBoard(board.id); + } + + // Any non-board components + wires that weren't connected to a + // board still need to go. + sim.setComponents([]); + sim.setWires([]); + + // Reset the editor to the default Blink sketch. loadFiles takes + // a {name, content}[] and rebuilds the file list, picking the + // first .ino as active. + editor.loadFiles([ + { + name: 'sketch.ino', + content: + '// Arduino Blink Example\nvoid setup() {\n pinMode(LED_BUILTIN, OUTPUT);\n}\n\nvoid loop() {\n digitalWrite(LED_BUILTIN, HIGH);\n delay(1000);\n digitalWrite(LED_BUILTIN, LOW);\n delay(1000);\n}\n', + }, + ]); + + // Drop project metadata so the next Save .vlx doesn't reuse the + // previous project's slug / name. + project.clearCurrentProject(); + + // Clear the compile output panel so old build logs don't carry over. + compileLogs.clear(); +} + async function checkForUpdates(): Promise { try { // eslint-disable-next-line @typescript-eslint/no-explicit-any