From 55dd25eeba1112dbd5ea32fa999ff61547bd4de3 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Tue, 28 Jul 2026 14:36:13 +0200 Subject: [PATCH] feat(pi-family): guestSetup seam + board-branded workspace texts Overlay QEMU-Linux boards are not Raspberry Pis: ProBoardDef.guestSetup lets a board send one shell line at the boot prompt (hostname/PS1/clear) to de-brand the generic image, sent before piBooted flips so uploads cannot interleave; the workspace start button and power-on title carry the board's own label for non raspberry-pi kinds; the compile console line uses the board label instead of 'Raspberry Pi 3B'. --- .../src/components/editor/EditorToolbar.tsx | 4 ++-- .../raspberry-pi/RaspberryPiWorkspace.tsx | 15 +++++++++++--- frontend/src/lib/proBoardRegistry.ts | 4 ++++ frontend/src/store/useSimulatorStore.ts | 20 +++++++++++++++---- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx index d1d6a11a..b8c16198 100644 --- a/frontend/src/components/editor/EditorToolbar.tsx +++ b/frontend/src/components/editor/EditorToolbar.tsx @@ -502,9 +502,9 @@ export const EditorToolbar = ({ const blog = (type: CompilationLog['type'], message: string) => addLog({ timestamp: new Date(), type, message, target: boardTarget }); - // Raspberry Pi 3B doesn't need arduino-cli compilation + // QEMU-Linux boards don't need arduino-cli compilation if (isPiBoardKind(kind)) { - blog('info', 'Raspberry Pi 3B: no compilation needed — run Python scripts directly.'); + blog('info', `${boardLabel}: no compilation needed — run Python scripts directly.`); setMessage({ type: 'success', text: 'Ready (no compilation needed)' }); setCompiling(false); return; diff --git a/frontend/src/components/raspberry-pi/RaspberryPiWorkspace.tsx b/frontend/src/components/raspberry-pi/RaspberryPiWorkspace.tsx index 8561bd9c..8e3aadd7 100644 --- a/frontend/src/components/raspberry-pi/RaspberryPiWorkspace.tsx +++ b/frontend/src/components/raspberry-pi/RaspberryPiWorkspace.tsx @@ -75,6 +75,15 @@ export const RaspberryPiWorkspace: React.FC = ({ boar // Display the active board's real name (Pi 3B / 4B / 5 / Zero / …) instead of // a hardcoded "Raspberry Pi 3B" — the workspace serves the whole Pi family. const boardLabel = board ? boardDisplayName(board) : 'Raspberry Pi'; + // Overlay QEMU-Linux boards (piFamily kinds) are not Raspberry Pis — their + // start button / power title must carry the board's own name. + const isRaspberry = !board || board.boardKind.startsWith('raspberry-pi'); + const startLabel = isRaspberry + ? t('editor.pi.startPi') + : t('editor.pi.startBoard', { board: boardLabel, defaultValue: 'Start {{board}}' }); + const powerOnTitle = isRaspberry + ? t('editor.pi.powerOnTitle') + : t('editor.pi.powerOnBoardTitle', { board: boardLabel, defaultValue: 'Power on {{board}}' }); // Three display states: offline (!running) → booting (running, guest Linux // still coming up) → ready (running + booted shell). `running` flips on click @@ -193,9 +202,9 @@ export const RaspberryPiWorkspace: React.FC = ({ boar ) : ( <> @@ -268,7 +277,7 @@ export const RaspberryPiWorkspace: React.FC = ({ boar
{t('editor.pi.offlineTitle', { board: boardLabel })}
{t('editor.pi.offlineSubtitle')}
{t('editor.pi.offlineNote1')} diff --git a/frontend/src/lib/proBoardRegistry.ts b/frontend/src/lib/proBoardRegistry.ts index a844a532..8d986c9a 100644 --- a/frontend/src/lib/proBoardRegistry.ts +++ b/frontend/src/lib/proBoardRegistry.ts @@ -59,6 +59,10 @@ export interface ProBoardDef { * bridge (backend qemu WebSocket, VFS panel, boot terminal). The overlay * must also register a matching backend profile for the kind. */ piFamily?: boolean; + /** One shell line sent to the guest right after it reaches the boot + * prompt (piFamily boards only). Lets a board de-brand the generic + * image, e.g. set its own hostname/PS1 and clear the stock motd. */ + guestSetup?: string; /** Canvas renderer. Receives the placed board's props; return a React node. * When omitted, the canvas renders ``. */ render?: (props: { id: string; x: number; y: number; running: boolean }) => React.ReactNode; diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index 9b1520f3..390f37a1 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -1244,11 +1244,23 @@ export const useSimulatorStore = create((set, get) => { }; // Guest Linux finished booting (shell prompt reached). Flip piBooted so // the workspace swaps the "Booting…" overlay for the live terminal and - // uploads know the shell is ready. + // uploads know the shell is ready. Overlay boards may declare a + // guestSetup line (hostname/PS1/clear) to de-brand the generic image; + // it runs before piBooted flips so the VFS upload (gated on piBooted) + // cannot interleave with it. bridge.onBooted = () => { - set((s) => ({ - boards: s.boards.map((b) => (b.id === id ? { ...b, piBooted: true } : b)), - })); + const setup = getProBoard(boardKind)?.guestSetup; + const flip = () => + set((s) => ({ + boards: s.boards.map((b) => (b.id === id ? { ...b, piBooted: true } : b)), + })); + if (setup) { + void bridge + .sendAndWaitForPrompt(setup.endsWith('\n') ? setup : setup + '\n') + .then(flip); + } else { + flip(); + } }; bridge.onDisconnected = () => { set((s) => {