feat: auto-compile when pressing Play if code changed or no hex loaded
Closes #81 - Add `codeChangedSinceLastCompile` dirty flag to useEditorStore - Play button now triggers compilation automatically when no compiled program exists or code has changed since last compile - Show compilation progress during auto-compile before run - If compilation fails, show errors instead of running stale code - Keep separate Compile button for manual compile-only workflow - Play button always enabled (disabled only while running/compiling) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>pull/82/head
parent
e99ded70b5
commit
67f9bc70c6
|
|
@ -44,7 +44,7 @@ const BOARD_PILL_COLOR: Record<BoardKind, string> = {
|
|||
};
|
||||
|
||||
export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compileLogs, setCompileLogs }: EditorToolbarProps) => {
|
||||
const { files } = useEditorStore();
|
||||
const { files, codeChangedSinceLastCompile, markCompiled } = useEditorStore();
|
||||
const {
|
||||
boards,
|
||||
activeBoardId,
|
||||
|
|
@ -137,6 +137,7 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
|
|||
compileBoardProgram(activeBoardId, program);
|
||||
}
|
||||
setMessage({ type: 'success', text: 'Compiled successfully' });
|
||||
markCompiled();
|
||||
setMissingLibHint(false);
|
||||
} else {
|
||||
const errText = result.error || result.stderr || 'Compile failed';
|
||||
|
|
@ -155,24 +156,62 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
|
|||
}
|
||||
};
|
||||
|
||||
const handleRun = () => {
|
||||
// Track whether we should auto-run after compilation completes
|
||||
const autoRunAfterCompile = useRef(false);
|
||||
|
||||
const handleRun = async () => {
|
||||
if (activeBoardId) {
|
||||
const board = boards.find((b) => b.id === activeBoardId);
|
||||
const isQemuBoard = board?.boardKind === 'raspberry-pi-3' || board?.boardKind === 'esp32' || board?.boardKind === 'esp32-s3';
|
||||
if (isQemuBoard || board?.compiledProgram) {
|
||||
|
||||
// QEMU boards don't need compilation
|
||||
if (isQemuBoard) {
|
||||
trackRunSimulation(board?.boardKind);
|
||||
startBoard(activeBoardId);
|
||||
setMessage(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto-compile if no program or code changed since last compile
|
||||
if (!board?.compiledProgram || codeChangedSinceLastCompile) {
|
||||
autoRunAfterCompile.current = true;
|
||||
await handleCompile();
|
||||
// After compile, check if it succeeded and run
|
||||
const updatedBoard = useSimulatorStore.getState().boards.find((b) => b.id === activeBoardId);
|
||||
if (autoRunAfterCompile.current && updatedBoard?.compiledProgram) {
|
||||
autoRunAfterCompile.current = false;
|
||||
trackRunSimulation(updatedBoard.boardKind);
|
||||
startBoard(activeBoardId);
|
||||
setMessage(null);
|
||||
} else {
|
||||
autoRunAfterCompile.current = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
trackRunSimulation(board?.boardKind);
|
||||
startBoard(activeBoardId);
|
||||
setMessage(null);
|
||||
return;
|
||||
}
|
||||
// legacy fallback
|
||||
if (compiledHex) {
|
||||
|
||||
// Legacy fallback
|
||||
if (!compiledHex || codeChangedSinceLastCompile) {
|
||||
autoRunAfterCompile.current = true;
|
||||
await handleCompile();
|
||||
const hex = useSimulatorStore.getState().compiledHex;
|
||||
if (autoRunAfterCompile.current && hex) {
|
||||
autoRunAfterCompile.current = false;
|
||||
trackRunSimulation();
|
||||
startSimulation();
|
||||
setMessage(null);
|
||||
} else {
|
||||
autoRunAfterCompile.current = false;
|
||||
}
|
||||
} else {
|
||||
trackRunSimulation();
|
||||
startSimulation();
|
||||
setMessage(null);
|
||||
} else {
|
||||
setMessage({ type: 'error', text: 'Compile first' });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -337,9 +376,9 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
|
|||
{/* Run */}
|
||||
<button
|
||||
onClick={handleRun}
|
||||
disabled={running || (!['raspberry-pi-3','esp32','esp32-s3'].includes(activeBoard?.boardKind ?? '') && !compiledHex && !activeBoard?.compiledProgram)}
|
||||
disabled={running || compiling}
|
||||
className="tb-btn tb-btn-run"
|
||||
title="Run"
|
||||
title="Run (auto-compiles if needed)"
|
||||
>
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" stroke="none">
|
||||
<polygon points="5,3 19,12 5,21" />
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ interface EditorState {
|
|||
setTheme: (theme: 'vs-dark' | 'light') => void;
|
||||
setFontSize: (size: number) => void;
|
||||
|
||||
// Dirty flag — tracks whether code changed since last compilation
|
||||
codeChangedSinceLastCompile: boolean;
|
||||
markCompiled: () => void;
|
||||
|
||||
// Legacy compat — sets content of the active file
|
||||
setCode: (code: string) => void;
|
||||
}
|
||||
|
|
@ -108,6 +112,9 @@ export const useEditorStore = create<EditorState>((set, get) => ({
|
|||
activeGroupFileId: { [DEFAULT_GROUP_ID]: MAIN_ID },
|
||||
openGroupFileIds: { [DEFAULT_GROUP_ID]: [MAIN_ID] },
|
||||
|
||||
codeChangedSinceLastCompile: true,
|
||||
markCompiled: () => set({ codeChangedSinceLastCompile: false }),
|
||||
|
||||
// ── File operations (legacy API — operate on active group) ──────────────
|
||||
|
||||
createFile: (name: string) => {
|
||||
|
|
@ -178,6 +185,7 @@ export const useEditorStore = create<EditorState>((set, get) => ({
|
|||
return {
|
||||
files: s.files.map(mapper),
|
||||
fileGroups: { ...s.fileGroups, [groupId]: (s.fileGroups[groupId] ?? []).map(mapper) },
|
||||
codeChangedSinceLastCompile: true,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue