From 9f368c19059389a269b540cb60967890271742f8 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Feb 2026 20:13:36 -0800 Subject: [PATCH 1/5] Track thread terminal running state across UI and store - add `terminalRunning` to thread state and hydration defaults - apply terminal events in reducer to sync running/open state (close drawer on exit) - wire drawer running-state callbacks and show a Terminal status pill in sidebar - extend store and persistence tests for terminal running/exited behavior --- apps/web/src/App.tsx | 11 ++++ apps/web/src/components/Sidebar.tsx | 28 ++++++++- apps/web/src/persistenceSchema.test.ts | 1 + apps/web/src/persistenceSchema.ts | 1 + apps/web/src/store.test.ts | 79 +++++++++++++++++++++++++- apps/web/src/store.ts | 46 ++++++++++++++- apps/web/src/types.ts | 1 + 7 files changed, 164 insertions(+), 3 deletions(-) diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index a982c1fc6e..41c7ff36fc 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -27,6 +27,16 @@ function EventRouter() { }); }, [api, dispatch]); + useEffect(() => { + if (!api) return; + return api.terminal.onEvent((event) => { + dispatch({ + type: "APPLY_TERMINAL_EVENT", + event, + }); + }); + }, [api, dispatch]); + return null; } @@ -82,6 +92,7 @@ function AutoProjectBootstrap() { terminalOpen: false, terminalHeight: DEFAULT_THREAD_TERMINAL_HEIGHT, terminalIds: [DEFAULT_THREAD_TERMINAL_ID], + runningTerminalIds: [], activeTerminalId: DEFAULT_THREAD_TERMINAL_ID, terminalGroups: [ { diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 32a832a491..858e438b50 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -32,7 +32,7 @@ function inferProjectName(cwd: string): string { } interface ThreadStatusPill { - label: "Working" | "Connecting" | "Completed" | "Awaiting response"; + label: "Working" | "Connecting" | "Completed" | "Awaiting response" | "Terminal"; colorClass: string; dotClass: string; pulse: boolean; @@ -89,6 +89,18 @@ function threadStatusPill(thread: Thread, hasPendingApprovals: boolean): ThreadS return null; } +function terminalStatusPill(thread: Thread): ThreadStatusPill | null { + if (thread.runningTerminalIds.length === 0) { + return null; + } + return { + label: "Terminal", + colorClass: "text-teal-600 dark:text-teal-300/90", + dotClass: "bg-teal-500 dark:bg-teal-300/90", + pulse: true, + }; +} + export default function Sidebar() { const { state, dispatch } = useStore(); const api = useNativeApi(); @@ -118,6 +130,7 @@ export default function Sidebar() { terminalOpen: false, terminalHeight: DEFAULT_THREAD_TERMINAL_HEIGHT, terminalIds: [DEFAULT_THREAD_TERMINAL_ID], + runningTerminalIds: [], activeTerminalId: DEFAULT_THREAD_TERMINAL_ID, terminalGroups: [ { @@ -388,6 +401,7 @@ export default function Sidebar() { thread, pendingApprovalByThreadId.get(thread.id) === true, ); + const terminalStatus = terminalStatusPill(thread); return (