fix: 5 user-reported issues (#208 #209 #210 #211 #212)

#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) <noreply@anthropic.com>
This commit is contained in:
David Montero Crespo 2026-05-26 22:06:44 -03:00
parent d64eebc200
commit 145710561a
4 changed files with 109 additions and 3 deletions

View File

@ -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 =

View File

@ -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)

View File

@ -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);

View File

@ -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<v
useSimulatorStore.getState().toggleSerialMonitor();
return;
case 'new-project':
newProject();
return;
case 'find-in-editor':
case 'toggle-file-explorer':
window.dispatchEvent(new CustomEvent(`velxio:menu:${action}`));
@ -152,6 +157,74 @@ function pickAndImportVlx(): void {
input.click();
}
/**
* Wipe the current workspace and start from the default Blink sketch +
* empty canvas. Issue #210: the menu action used to just dispatch a
* CustomEvent that nobody listened to.
*
* Confirms first if the user has unsaved changes (any modified file
* or any component on the canvas). The Tauri-side menu can't show a
* confirm dialog cheaply, so we use the browser-native `confirm()`
* here fine for the desktop bundle where it renders as a modal.
*/
function newProject(): void {
const sim = useSimulatorStore.getState();
const editor = useEditorStore.getState();
const project = useProjectStore.getState();
const compileLogs = useCompileLogsStore.getState();
const hasWork =
sim.boards.length > 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<void> {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any