diff --git a/apps/renderer/src/components/ChatView.tsx b/apps/renderer/src/components/ChatView.tsx index 985f7aefd3db7d65897ea7c7fb799f355dc45370..96483ea70e142f6caea464a2259ce6579a034dd5 100644 --- a/apps/renderer/src/components/ChatView.tsx +++ b/apps/renderer/src/components/ChatView.tsx @@ -113,6 +113,7 @@ export default function ChatView() { const [isModelMenuOpen, setIsModelMenuOpen] = useState(false); const [selectedEffort, setSelectedEffort] = useState(DEFAULT_REASONING); + const [isSwitchingRuntimeMode, setIsSwitchingRuntimeMode] = useState(false); const [respondingRequestIds, setRespondingRequestIds] = useState( [], ); @@ -210,6 +211,35 @@ export default function ChatView() { sandboxMode: "workspace-write", } as const); + const handleRuntimeModeChange = async ( + mode: "approval-required" | "full-access", + ) => { + if (mode === state.runtimeMode) return; + dispatch({ type: "SET_RUNTIME_MODE", mode }); + if (!api) return; + + const sessionIds = state.threads + .map((t) => t.session) + .filter( + (s): s is NonNullable => + s !== null && s.status !== "closed", + ) + .map((s) => s.sessionId); + + if (sessionIds.length === 0) return; + + setIsSwitchingRuntimeMode(true); + try { + await Promise.all( + sessionIds.map((id) => + api.providers.stopSession({ sessionId: id }).catch(() => undefined), + ), + ); + } finally { + setIsSwitchingRuntimeMode(false); + } + }; + // Auto-scroll on new messages const messageCount = activeThread?.messages.length ?? 0; const workLogCount = workLogEntries.length; @@ -775,6 +805,83 @@ export default function ChatView() { /> + + {/* Divider */} +
+ + {/* Runtime mode toggle */} +
{/* Right side: send / stop button */} diff --git a/apps/renderer/src/components/Sidebar.tsx b/apps/renderer/src/components/Sidebar.tsx index 0cdaa14b4a5b6a2869fb647d309f2f58fe029c68..c00da63e544efa41ae221903d6d1a3d995248359 100644 --- a/apps/renderer/src/components/Sidebar.tsx +++ b/apps/renderer/src/components/Sidebar.tsx @@ -4,6 +4,7 @@ import { readNativeApi } from "../session-logic"; import { useStore } from "../store"; import type { Project } from "../types"; + function formatRelativeTime(iso: string): string { const diff = Date.now() - new Date(iso).getTime(); const minutes = Math.floor(diff / 60_000); @@ -28,7 +29,6 @@ export default function Sidebar() { const [addingProject, setAddingProject] = useState(false); const [newCwd, setNewCwd] = useState(""); const [isPickingFolder, setIsPickingFolder] = useState(false); - const [isSwitchingRuntimeMode, setIsSwitchingRuntimeMode] = useState(false); const handleAddProject = () => { const cwd = newCwd.trim(); @@ -80,43 +80,6 @@ export default function Sidebar() { } }; - const handleRuntimeModeChange = useCallback( - async (mode: "approval-required" | "full-access") => { - if (mode === state.runtimeMode) return; - - dispatch({ - type: "SET_RUNTIME_MODE", - mode, - }); - - if (!api) return; - - const sessionIds = state.threads - .map((thread) => thread.session) - .filter( - (session): session is NonNullable => - session !== null && session.status !== "closed", - ) - .map((session) => session.sessionId); - - if (sessionIds.length === 0) { - return; - } - - setIsSwitchingRuntimeMode(true); - try { - await Promise.all( - sessionIds.map((sessionId) => - api.providers.stopSession({ sessionId }).catch(() => undefined), - ), - ); - } finally { - setIsSwitchingRuntimeMode(false); - } - }, - [api, dispatch, state.runtimeMode, state.threads], - ); - useEffect(() => { const onWindowKeyDown = (event: KeyboardEvent) => { const isNewThreadShortcut = @@ -189,43 +152,6 @@ export default function Sidebar() { -
-
-

- Runtime mode -

-

- Switching restarts live sessions. -

-
- - -
-
-
- {/* Project list */}