Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 181x 181x 181x 181x 2x 181x 2x 2x 2x 2x 2x 2x 181x 158x 158x 65x 65x 93x 3x 3x 1x 1x 1x 3x 181x 164x 7x 1x 1x 1x 181x | import { useEffect, useRef, useCallback } from "react";
import type { IncomingArduinoMessage } from "@/types/websocket";
interface UseSimulationLifecycleOptions {
code: string;
simulationStatus: "idle" | "running" | "compiling" | "queued" | "paused";
setSimulationStatus: (s: "idle" | "running" | "compiling" | "queued" | "paused") => void;
sendMessage: (msg: IncomingArduinoMessage) => void;
resetPinUI: (opts?: { keepDetected?: boolean }) => void;
clearOutputs?: () => void;
handlePause?: () => void;
handleResume?: () => void;
handleReset?: () => void;
hasCompilationErrors?: boolean;
}
export function useSimulationLifecycle({
code,
simulationStatus,
setSimulationStatus,
sendMessage,
resetPinUI,
clearOutputs,
handlePause,
handleResume,
handleReset,
hasCompilationErrors = false,
}: UseSimulationLifecycleOptions) {
// Trace state transitions for tests/debugging
useEffect(() => { /* status changes handled by lifecycle */ }, [simulationStatus]);
// Temporary suppression flag (used when inserting editor suggestions)
const skipAutoStopRef = useRef(false);
// Remember last code to detect *edits* (not initial mount)
const prevCodeRef = useRef<string | null>(null);
const suppressAutoStopOnce = useCallback(() => {
skipAutoStopRef.current = true;
}, []);
const stopSimulation = useCallback(() => {
try {
sendMessage({ type: "stop_simulation" });
} catch {}
try {
setSimulationStatus("idle");
} catch {}
try {
resetPinUI();
} catch {}
}, [sendMessage, setSimulationStatus, resetPinUI]);
// Watch for code edits and stop running/paused simulation (unless suppressed)
useEffect(() => {
const prev = prevCodeRef.current;
if (prev === null) {
prevCodeRef.current = code;
return;
}
if (prev === code) return;
prevCodeRef.current = code;
if ((simulationStatus === "running" || simulationStatus === "paused") && !skipAutoStopRef.current) {
stopSimulation();
// preserve detected pin modes when stopping due to edit
try {
resetPinUI({ keepDetected: true });
} catch {}
}
skipAutoStopRef.current = false;
}, [code, simulationStatus, stopSimulation, resetPinUI]);
// Stop simulation automatically when compiler reports errors
useEffect(() => {
if (!hasCompilationErrors) return;
if (simulationStatus === "running" || simulationStatus === "paused") {
stopSimulation();
// keep UI state consistent when compiler fails
try {
clearOutputs?.();
} catch {}
}
}, [hasCompilationErrors, simulationStatus, stopSimulation, clearOutputs]);
return {
suppressAutoStopOnce,
stopSimulation,
pauseSimulation: handlePause ?? (() => {}),
resumeSimulation: handleResume ?? (() => {}),
resetSimulation: handleReset ?? (() => {}),
} as const;
}
|