feat(compile): expose compile logs via Zustand store + UI slot

Two minimal hooks so the velxio-pro agent overlay can offer a 'Diagnose
this compile failure with AI' affordance without touching upstream
component internals:

  - New store/useCompileLogsStore: holds the editor's compile output as
    Zustand state instead of local React useState in EditorPage. The
    setter accepts both a value and an updater fn so the EditorToolbar
    callers that used setCompileLogs(prev => [...prev, log]) keep
    working without changes.

  - CompilationConsole header now renders a
    <div data-velxio-slot='compile-console-actions' /> when errorCount
    > 0. The pro overlay mounts a 'Diagnose with AI' button into this
    slot via slotMounter. Empty in the OSS image — no behaviour change.

EditorPage replaces its local useState<CompilationLog[]> with the store
selector. The downstream prop-drilled setCompileLogs callers (toolbar,
sub-toolbars) keep their signature.

Companion commit lands the button + diagnostic prompt builder in the
velxio-prod overlay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
davidmonterocrespo24 2026-05-14 16:44:05 +02:00
parent 30004bb82b
commit 729c8785ba
3 changed files with 54 additions and 1 deletions

View File

@ -75,6 +75,12 @@ export const CompilationConsole: React.FC<CompilationConsoleProps> = ({
{warningCount}
</span>
)}
{/* Pro overlay mounts a "Diagnose with AI" button here when
errorCount > 0. Empty in the OSS image slotMounter
only fires when the pro tree is present. */}
{errorCount > 0 && (
<div data-velxio-slot="compile-console-actions" />
)}
</div>
</div>
<div style={styles.headerRight}>

View File

@ -27,6 +27,7 @@ import { LoginPromptModal } from '../components/layout/LoginPromptModal';
import { GitHubStarBanner } from '../components/layout/GitHubStarBanner';
import { useSimulatorStore, DEFAULT_BOARD_POSITION } from '../store/useSimulatorStore';
import { useEditorStore } from '../store/useEditorStore';
import { useCompileLogsStore } from '../store/useCompileLogsStore';
import { useOscilloscopeStore } from '../store/useOscilloscopeStore';
import { useAuthStore } from '../store/useAuthStore';
import { useProjectStore } from '../store/useProjectStore';
@ -81,7 +82,11 @@ export const EditorPage: React.FC = () => {
const isRaspberryPi3 = activeBoardKind === 'raspberry-pi-3';
const oscilloscopeOpen = useOscilloscopeStore((s) => s.open);
const [consoleOpen, setConsoleOpen] = useState(false);
const [compileLogs, setCompileLogs] = useState<CompilationLog[]>([]);
// compileLogs live in a Zustand store so the velxio-pro agent overlay
// (mounted in a separate React tree via slotMounter) can subscribe and
// build a "diagnose this failure" prompt without prop-drilling.
const compileLogs = useCompileLogsStore((s) => s.logs);
const setCompileLogs = useCompileLogsStore((s) => s.setLogs);
const [bottomPanelHeight, setBottomPanelHeight] = useState(BOTTOM_PANEL_DEFAULT);
const [saveModalOpen, setSaveModalOpen] = useState(false);
const [loginPromptOpen, setLoginPromptOpen] = useState(false);

View File

@ -0,0 +1,42 @@
/**
* Zustand store for the editor's compile output.
*
* Lives outside <EditorPage> so other components (notably the velxio-pro
* agent overlay, mounted into a different React tree via slotMounter) can
* subscribe without prop-drilling. The overlay reads `logs` to build a
* "diagnose this compile failure with AI" prompt; without the store it
* would have no way to reach the upstream component state. Board target
* is read from `useEditorStore` by the overlay independently.
*/
import { create } from 'zustand';
import type { CompilationLog } from '../utils/compilationLogger';
/** React-style setter accepts either a new value OR an updater fn so
* callers that used `useState`'s `setX(prev => ...)` form can be
* swapped in without rewriting the call sites. */
type LogsSetter = (
next: CompilationLog[] | ((prev: CompilationLog[]) => CompilationLog[]),
) => void;
interface CompileLogsState {
logs: CompilationLog[];
setLogs: LogsSetter;
appendLogs: (logs: CompilationLog[]) => void;
clear: () => void;
}
export const useCompileLogsStore = create<CompileLogsState>((set, get) => ({
logs: [],
setLogs: (next) => {
if (typeof next === 'function') {
set({ logs: (next as (prev: CompilationLog[]) => CompilationLog[])(get().logs) });
} else {
set({ logs: next });
}
},
appendLogs: (entries) =>
set((s) => ({ logs: [...s.logs, ...entries] })),
clear: () => set({ logs: [] }),
}));