@wrongstack/webui 0.1.10 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/chime.ts","../src/main.tsx","../src/lib/utils.ts","../src/components/Toaster.tsx","../src/hooks/useWebSocket.ts","../src/lib/favicon.ts","../src/lib/notify.ts","../src/lib/ws-client.ts","../src/stores/index.ts","../src/App.tsx","../src/components/ChatView.tsx","../src/components/ChatInput.tsx","../src/components/CommandPalette.tsx","../src/components/FilePicker.tsx","../src/components/ui/button.tsx","../src/components/ConnectionChip.tsx","../src/components/CostChip.tsx","../src/lib/tool-summary.ts","../src/components/MessageBubble.tsx","../src/components/DiffView.tsx","../src/components/ToolResult.tsx","../src/components/ModePicker.tsx","../src/components/SearchOverlay.tsx","../src/components/ToolGroup.tsx","../src/components/WelcomeScreen.tsx","../src/components/ui/scroll-area.tsx","../src/components/ConfirmDialog.tsx","../src/components/ui/dialog.tsx","../src/components/ConnectionBanner.tsx","../src/components/QuickModelSwitcher.tsx","../src/components/SettingsPanel.tsx","../src/components/ThemeProvider.tsx","../src/components/ui/input.tsx","../src/components/ui/tabs.tsx","../src/components/ShortcutsOverlay.tsx","../src/components/Sidebar.tsx"],"sourcesContent":["/**\n * Tiny soft chime played when a long-running agent run completes. We don't\n * ship an audio asset — the sound is synthesized with Web Audio so it's\n * zero-bytes-shipped and trivially tweakable.\n *\n * Tuned to be unobtrusive: two notes ascending, ~250ms total, ~-12dB\n * envelope. Quiet enough to not startle on a still office, audible enough\n * to cut through music. Browsers require a prior user interaction before\n * any audio plays — by the time the agent has actually finished a turn the\n * user has already typed, so the autoplay policy is satisfied.\n */\n\nlet ctx: AudioContext | null = null;\n\nfunction audio(): AudioContext | null {\n if (typeof window === 'undefined') return null;\n if (ctx) return ctx;\n // SSR/Test guard plus older Safari (webkit prefix).\n const Cls =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Cls) return null;\n try {\n ctx = new Cls();\n } catch {\n return null;\n }\n return ctx;\n}\n\nfunction tone(freq: number, startAt: number, durSec: number): void {\n const ac = audio();\n if (!ac) return;\n const t = ac.currentTime + startAt;\n const osc = ac.createOscillator();\n const gain = ac.createGain();\n osc.type = 'sine';\n osc.frequency.value = freq;\n // Quick attack, exponential decay — sounds like a soft \"bing\".\n gain.gain.setValueAtTime(0, t);\n gain.gain.linearRampToValueAtTime(0.18, t + 0.01);\n gain.gain.exponentialRampToValueAtTime(0.0001, t + durSec);\n osc.connect(gain).connect(ac.destination);\n osc.start(t);\n osc.stop(t + durSec + 0.02);\n}\n\nexport function playCompletionChime(): void {\n // Two-note arpeggio: E5 → A5 (a perfect fourth, pleasant + non-alarming).\n tone(659.25, 0, 0.18);\n tone(880, 0.12, 0.24);\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { App } from './App';\nimport './index.css';\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n);\n","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { cn } from '@/lib/utils';\nimport { AlertTriangle, CheckCircle2, Info, X, XCircle } from 'lucide-react';\nimport { useEffect } from 'react';\nimport { create } from 'zustand';\n\n/**\n * Tiny toast store + portal. We resisted pulling in shadcn-ui's full\n * toast/sonner since it brings a tree of providers — this is one store,\n * one component, ~80 lines total. The store is exposed via `toast.success(...)`\n * etc. so non-React modules (ws-client handlers) can fire toasts without\n * the hook.\n */\n\nexport type ToastVariant = 'success' | 'error' | 'warn' | 'info';\n\ninterface ToastEntry {\n id: string;\n message: string;\n variant: ToastVariant;\n ttl: number;\n}\n\ninterface ToastState {\n toasts: ToastEntry[];\n push: (t: Omit<ToastEntry, 'id'>) => string;\n dismiss: (id: string) => void;\n}\n\nconst useToastStore = create<ToastState>((set) => ({\n toasts: [],\n push: (t) => {\n const id = `toast_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n set((state) => ({ toasts: [...state.toasts, { ...t, id }] }));\n return id;\n },\n dismiss: (id) => set((state) => ({ toasts: state.toasts.filter((x) => x.id !== id) })),\n}));\n\n/** Imperative API. Pass plain strings or arrays of strings for multi-line. */\nexport const toast = {\n success: (msg: string, ttl = 3500) =>\n useToastStore.getState().push({ message: msg, variant: 'success', ttl }),\n error: (msg: string, ttl = 6000) =>\n useToastStore.getState().push({ message: msg, variant: 'error', ttl }),\n warn: (msg: string, ttl = 4500) =>\n useToastStore.getState().push({ message: msg, variant: 'warn', ttl }),\n info: (msg: string, ttl = 3500) =>\n useToastStore.getState().push({ message: msg, variant: 'info', ttl }),\n dismiss: (id: string) => useToastStore.getState().dismiss(id),\n};\n\nfunction Icon({ variant }: { variant: ToastVariant }) {\n if (variant === 'success') return <CheckCircle2 className=\"h-4 w-4 text-green-500\" />;\n if (variant === 'error') return <XCircle className=\"h-4 w-4 text-destructive\" />;\n if (variant === 'warn') return <AlertTriangle className=\"h-4 w-4 text-amber-500\" />;\n return <Info className=\"h-4 w-4 text-blue-500\" />;\n}\n\nfunction ToastItem({ entry }: { entry: ToastEntry }) {\n const dismiss = useToastStore((s) => s.dismiss);\n useEffect(() => {\n const t = setTimeout(() => dismiss(entry.id), entry.ttl);\n return () => clearTimeout(t);\n }, [entry.id, entry.ttl, dismiss]);\n return (\n <div\n className={cn(\n 'flex items-start gap-2 rounded-lg border bg-popover shadow-lg px-3 py-2 text-sm max-w-sm',\n 'animate-message',\n entry.variant === 'error' && 'border-destructive/40',\n entry.variant === 'warn' && 'border-amber-500/40',\n entry.variant === 'success' && 'border-green-500/40',\n )}\n >\n <Icon variant={entry.variant} />\n <div className=\"flex-1 min-w-0 whitespace-pre-wrap break-words leading-snug\">\n {entry.message}\n </div>\n <button\n type=\"button\"\n onClick={() => dismiss(entry.id)}\n className=\"text-muted-foreground hover:text-foreground\"\n title=\"Dismiss\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n );\n}\n\nexport function Toaster() {\n const toasts = useToastStore((s) => s.toasts);\n if (toasts.length === 0) return null;\n return (\n <div className=\"fixed bottom-4 right-4 z-[60] flex flex-col gap-2 pointer-events-auto\">\n {toasts.map((t) => (\n <ToastItem key={t.id} entry={t} />\n ))}\n </div>\n );\n}\n","import { toast } from '@/components/Toaster';\nimport { playCompletionChime } from '@/lib/chime';\nimport { installFaviconVisibilityReset, setFaviconStatus } from '@/lib/favicon';\nimport { ensureNotificationPermission, notifyIfHidden } from '@/lib/notify';\nimport { type WrongStackWebSocketClient, getWSClient } from '@/lib/ws-client';\nimport {\n type SessionHistoryEntry,\n useChatStore,\n useConfigStore,\n useHistoryStore,\n useSessionStore,\n useUIStore,\n} from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport { useCallback, useEffect, useRef } from 'react';\n\n/**\n * One-shot WebSocket handler installation.\n *\n * Critical: this is called by `useWebSocketBootstrap` from App.tsx EXACTLY\n * ONCE per page. Every other component that needs to talk to the backend uses\n * `useWebSocket()` (below) which only returns action methods — it does NOT\n * register handlers.\n *\n * The earlier design had every component that imported `useWebSocket()`\n * register its own copy of the handlers via `ws.on(type, handler)`. With\n * ChatInput + ConfirmDialog + SettingsPanel all using the hook, every\n * incoming WS message was processed three times — three identical tool\n * bubbles, three appends of the same text_delta, three clearMessages on\n * session.start. That's the \"duplicate tool bubble / repeated text\" bug\n * the user kept hitting. Singleton install fixes it at the root.\n */\nfunction installHandlers(ws: WrongStackWebSocketClient): () => void {\n const offs: Array<() => void> = [];\n\n const on = (type: string, fn: (msg: WSServerMessage) => void) => {\n offs.push(ws.on(type, fn));\n };\n\n on('session.start', (msg) => {\n const payload = msg.payload as {\n sessionId: string;\n model: string;\n provider: string;\n maxContext?: number;\n projectName?: string;\n cwd?: string;\n mode?: string;\n inputCost?: number;\n outputCost?: number;\n cacheReadCost?: number;\n /** Backend tells us \"the whole context was wiped on my side, mirror\n * that in the UI\". Sent by context.clear so the chat empties even\n * though the sessionId is unchanged. */\n reset?: boolean;\n };\n const prev = useSessionStore.getState().session?.id;\n const isNew = !prev || prev !== payload.sessionId;\n useSessionStore.getState().startSession({\n id: payload.sessionId,\n startedAt: Date.now(),\n model: payload.model,\n provider: payload.provider,\n });\n useSessionStore.getState().setEnv({\n maxContext: payload.maxContext,\n projectName: payload.projectName,\n mode: payload.mode,\n inputCost: payload.inputCost,\n outputCost: payload.outputCost,\n cacheReadCost: payload.cacheReadCost,\n });\n useConfigStore.getState().setConfig({\n provider: payload.provider,\n model: payload.model,\n });\n if (isNew || payload.reset) useChatStore.getState().clearMessages();\n\n // Resume hydration: rebuild the chat from the on-disk transcript so the\n // user can pick up exactly where they left off. We translate each\n // Message into the simpler ChatMessage shape the UI store expects.\n const replay = (payload as { replayMessages?: Array<{ role: string; content: unknown }> })\n .replayMessages;\n if (replay && replay.length > 0) {\n const chat = useChatStore.getState();\n for (const m of replay) {\n if (m.role === 'user' || m.role === 'assistant' || m.role === 'system') {\n let text = '';\n if (typeof m.content === 'string') {\n text = m.content;\n } else if (Array.isArray(m.content)) {\n for (const b of m.content as Array<Record<string, unknown>>) {\n if (b.type === 'text' && typeof b.text === 'string') {\n text += (text ? '\\n' : '') + b.text;\n } else if (b.type === 'tool_use') {\n chat.addMessage({\n role: 'tool',\n content: '',\n toolName: String(b.name ?? 'tool'),\n toolInput: b.input,\n toolUseId: String(b.id ?? ''),\n });\n text = '';\n } else if (b.type === 'tool_result') {\n const all = useChatStore.getState().messages;\n let last: { id: string } | undefined;\n for (let i = all.length - 1; i >= 0; i--) {\n if (all[i]!.toolUseId === String(b.tool_use_id ?? '')) {\n last = all[i]!;\n break;\n }\n }\n if (last) {\n chat.setToolResult(\n last.id,\n typeof b.content === 'string' ? b.content : JSON.stringify(b.content),\n !b.is_error,\n );\n }\n }\n }\n }\n if (text) {\n chat.addMessage({ role: m.role as 'user' | 'assistant', content: text });\n }\n }\n }\n }\n });\n\n on('context.debug', (msg) => {\n const p = msg.payload as {\n total: number;\n systemPrompt: number;\n tools: {\n total: number;\n count: number;\n breakdown: Array<{ name: string; tokens: number }>;\n };\n messages: {\n total: number;\n count: number;\n breakdown: Array<{ index: number; role: string; tokens: number; preview: string }>;\n };\n };\n const fmt = (n: number) => n.toLocaleString();\n // Sort tools+messages by size descending so the top consumers float up.\n const topTools = [...p.tools.breakdown].sort((a, b) => b.tokens - a.tokens).slice(0, 8);\n const topMsgs = [...p.messages.breakdown].sort((a, b) => b.tokens - a.tokens).slice(0, 8);\n const lines = [\n `📊 **Context breakdown** (heuristic — 4 chars/token)`,\n ``,\n `**Total estimate:** ${fmt(p.total)} tokens`,\n `• System prompt: ${fmt(p.systemPrompt)}`,\n `• Tool schemas: ${fmt(p.tools.total)} (${p.tools.count} tools)`,\n `• Messages: ${fmt(p.messages.total)} (${p.messages.count} messages)`,\n ``,\n `**Top tool schemas:**`,\n ...topTools.map((t) => ` · ${t.name}: ${fmt(t.tokens)}`),\n ``,\n `**Top messages:**`,\n ...topMsgs.map(\n (m) => ` · #${m.index} ${m.role}: ${fmt(m.tokens)} — ${m.preview || '(empty)'}`,\n ),\n ];\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: lines.join('\\n'),\n });\n });\n\n on('key.operation_result', (msg) => {\n // Provider/key/model.switch operations report back here. Toast is the\n // right surface — these are transient acks/errors, not chat content.\n const p = msg.payload as { success: boolean; message: string };\n if (p.success) toast.success(p.message);\n else toast.error(p.message);\n });\n\n on('context.compacted', (msg) => {\n const payload = msg.payload as {\n before: number;\n after: number;\n saved: number;\n reductions: Array<{ phase: string; saved: number }>;\n };\n // Inline notice in the chat — the model just shed ~N tokens of history,\n // user should see what happened so the next reply context isn't a\n // surprise. Not an error; rendered as a subdued assistant note.\n const summary = payload.reductions.length\n ? payload.reductions.map((r) => `${r.phase}: ${r.saved}`).join(', ')\n : 'no-op';\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: `🗜️ Context compacted: ${payload.before} → ${payload.after} tokens (saved ~${payload.saved}). ${summary}`,\n });\n // The new context size is the de-facto next input — reflect it in the\n // topbar so the ctx % chip updates immediately.\n useSessionStore.setState({ lastInputTokens: payload.after });\n });\n\n on('session.end', () => {\n useConfigStore.getState().setWsConnected(false);\n });\n\n on('iteration.started', (msg) => {\n const payload = msg.payload as { index: number; maxIterations?: number };\n useSessionStore.getState().setIteration({\n index: payload.index,\n max: payload.maxIterations ?? 0,\n });\n // Defensive: a new iteration means the agent is actively working.\n // Make sure the running indicator stays visible even if some earlier\n // event dropped isLoading prematurely.\n useChatStore.getState().setLoading(true);\n if (typeof document !== 'undefined' && document.hidden) {\n setFaviconStatus('running');\n }\n // First iteration of a fresh run — snapshot start time + cost so\n // run.result can compute a per-turn summary (duration, cost delta).\n // Subsequent iterations in the same loop preserve the original start.\n if (useChatStore.getState().runStart === null) {\n useChatStore.getState().setRunStart({\n at: Date.now(),\n cost: useSessionStore.getState().cost,\n });\n }\n // Don't pre-create an empty assistant bubble — text_delta lazy-creates\n // one when the model actually writes something.\n useChatStore.getState().setCurrentAssistantMessage(null);\n });\n\n on('provider.text_delta', (msg) => {\n const payload = msg.payload as { text: string; messageId: string };\n // Model has moved from internal reasoning to user-facing output — the\n // transient thinking buffer is no longer current.\n useChatStore.getState().clearThinking();\n let id = useChatStore.getState().currentAssistantMessageId;\n if (!id) {\n id = useChatStore.getState().addMessage({ role: 'assistant', content: '', streaming: true });\n useChatStore.getState().setCurrentAssistantMessage(id);\n }\n useChatStore.getState().appendToMessage(id, payload.text);\n });\n\n on('provider.thinking_delta', (msg) => {\n const payload = msg.payload as { text: string };\n if (!payload.text) return;\n useChatStore.getState().appendThinking(payload.text);\n });\n\n on('tool.started', (msg) => {\n const payload = msg.payload as {\n id: string;\n name: string;\n input?: unknown;\n messageId: string;\n };\n // Guard against duplicate tool.started for the same backend id. Could\n // happen if the agent retries / re-emits, and we definitely don't want a\n // second bubble for the same tool_use.\n const existing = useChatStore.getState().messages.find((m) => m.toolUseId === payload.id);\n if (existing) {\n useChatStore.getState().setCurrentToolId(existing.id);\n return;\n }\n // Model is acting, not still reasoning — drop the transient thinking.\n useChatStore.getState().clearThinking();\n useChatStore.getState().setCurrentAssistantMessage(null);\n const id = useChatStore.getState().addMessage({\n role: 'tool',\n content: '',\n toolName: payload.name,\n toolInput: payload.input,\n toolUseId: payload.id,\n });\n useChatStore.getState().setCurrentToolId(id);\n useChatStore.getState().addExecution({\n id: payload.id,\n name: payload.name,\n input: payload.input,\n ok: true,\n startedAt: Date.now(),\n });\n });\n\n on('tool.progress', (msg) => {\n // Live progress feed for an in-flight tool. We route each event onto\n // the tool bubble matched by backend tool_use id (set by tool.started).\n // Skip pure 'metric' events with no text — they're useful to logs but\n // would clutter the chat with empty rows.\n const payload = msg.payload as {\n id: string;\n name: string;\n eventType: 'log' | 'warning' | 'metric' | 'file_changed' | 'partial_output';\n text?: string;\n };\n const text = (payload.text ?? '').trim();\n if (!text) return;\n const messages = useChatStore.getState().messages;\n const owner = messages.find((m) => m.toolUseId === payload.id);\n if (!owner) return;\n // Prefix warnings so they're visually distinct; everything else flows in\n // as-is. partial_output (bash stdout etc.) is the common case.\n const prefix = payload.eventType === 'warning' ? '⚠ ' : '';\n useChatStore.getState().appendToolProgress(owner.id, prefix + text);\n });\n\n on('tool.executed', (msg) => {\n const payload = msg.payload as {\n id?: string;\n name: string;\n durationMs: number;\n ok: boolean;\n input?: unknown;\n output?: string;\n };\n const { messages, currentToolId } = useChatStore.getState();\n // Prefer matching on backend tool_use id (works for parallel tools).\n // Fall back to currentToolId only when id is missing (legacy emitters).\n const owner = payload.id\n ? messages.find((m) => m.toolUseId === payload.id)\n : currentToolId\n ? messages.find((m) => m.id === currentToolId)\n : undefined;\n if (owner) {\n useChatStore.getState().setToolResult(owner.id, payload.output ?? '', payload.ok);\n useChatStore.getState().updateMessage(owner.id, { toolDurationMs: payload.durationMs });\n }\n if (payload.id) {\n useChatStore.getState().updateExecution(payload.id, {\n completedAt: Date.now(),\n durationMs: payload.durationMs,\n output: payload.output,\n ok: payload.ok,\n });\n }\n if (currentToolId && owner && owner.id === currentToolId) {\n useChatStore.getState().setCurrentToolId(null);\n }\n });\n\n on('provider.response', (msg) => {\n const payload = msg.payload as {\n usage: {\n input: number;\n output: number;\n cacheRead?: number;\n cacheWrite?: number;\n };\n stopReason: string;\n messageId: string;\n };\n useSessionStore.getState().updateUsage(payload.usage);\n const { inputCost, outputCost, cacheReadCost } = useSessionStore.getState();\n const dCost =\n (payload.usage.input * inputCost +\n payload.usage.output * outputCost +\n (payload.usage.cacheRead ?? 0) * cacheReadCost) /\n 1_000_000;\n if (dCost > 0) useSessionStore.getState().addCost(dCost);\n // Run is NOT done if the provider stopped to use tools — the agent will\n // execute them and loop. Keep isLoading true so the Thinking/Running\n // indicator stays visible between iterations. The terminal flip happens\n // in run.result.\n if (payload.stopReason !== 'tool_use' && payload.stopReason !== 'tool_call') {\n useChatStore.getState().setLoading(false);\n }\n // Close out the current streaming bubble either way — finalize the text\n // (collapse model-emitted duplicate paragraphs) and drop the streaming\n // flag so a fresh iteration starts a new bubble.\n const id = useChatStore.getState().currentAssistantMessageId;\n if (id) {\n useChatStore.getState().finalizeMessage(id);\n // Attribute the run's usage to this bubble so the user can see what\n // each answer cost. Iterations that loop on tool_use don't get an\n // attribution here — `usage` from a mid-loop response covers tool\n // arguments, not user-visible content. Only the terminal response\n // (or any with real output) gets the badge.\n if (payload.usage.output > 0) {\n useChatStore.getState().updateMessage(id, { usage: payload.usage });\n }\n }\n useChatStore.getState().setCurrentAssistantMessage(null);\n // Belt-and-suspenders: response landed → no more thinking for this turn.\n useChatStore.getState().clearThinking();\n });\n\n on('tool.confirm_needed', (msg) => {\n const payload = msg.payload as {\n id: string;\n toolName: string;\n input: unknown;\n suggestedPattern: string;\n };\n useUIStore.getState().showConfirm({\n id: payload.id,\n toolName: payload.toolName,\n input: payload.input,\n suggestedPattern: payload.suggestedPattern,\n });\n });\n\n on('run.result', (msg) => {\n const payload = msg.payload as {\n status: string;\n iterations: number;\n finalText?: string;\n error?: { code: string; message: string; recoverable: boolean };\n };\n useSessionStore.getState().setIteration(null);\n useChatStore.getState().setLoading(false);\n useChatStore.getState().setCurrentAssistantMessage(null);\n useChatStore.getState().clearThinking();\n // Compute a per-turn summary and attach it to the last assistant\n // message of this run, then clear runStart for the next turn. Tools\n // are counted by walking the chat backwards for tool bubbles whose\n // timestamp is after runStart.at.\n const runStart = useChatStore.getState().runStart;\n if (runStart && payload.status === 'done') {\n const all = useChatStore.getState().messages;\n let lastAssistantIdx = -1;\n let toolCount = 0;\n for (let i = all.length - 1; i >= 0; i--) {\n const m = all[i]!;\n if (m.role === 'assistant' && lastAssistantIdx === -1 && m.content) {\n lastAssistantIdx = i;\n }\n if (m.role === 'tool' && m.timestamp >= runStart.at) {\n toolCount += 1;\n }\n // Stop walking when we cross the user message that started this run.\n if (m.role === 'user' && m.timestamp <= runStart.at) break;\n }\n if (lastAssistantIdx !== -1) {\n const sessionCost = useSessionStore.getState().cost;\n useChatStore.getState().updateMessage(all[lastAssistantIdx]!.id, {\n runSummary: {\n iterations: payload.iterations,\n tools: toolCount,\n durationMs: Date.now() - runStart.at,\n costDelta: Math.max(0, sessionCost - runStart.cost),\n },\n });\n }\n }\n useChatStore.getState().setRunStart(null);\n if (payload.status !== 'done' && payload.error) {\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: `Error: ${payload.error.message}`,\n isError: true,\n });\n toast.error(`Run ended: ${payload.error.message}`);\n notifyIfHidden('WrongStack run failed', payload.error.message);\n if (typeof document !== 'undefined' && document.hidden) {\n setFaviconStatus('error');\n }\n } else if (payload.status === 'done') {\n // Two-pronged \"you can come back now\" signal:\n // • Toast: lands in-page when the user returns, no permission needed.\n // • OS notification: only when the tab is actually hidden AND the\n // user previously granted permission. Cheap if neither applies.\n if (typeof document !== 'undefined' && document.hidden) {\n toast.success(\n `Run completed in ${payload.iterations} iteration${payload.iterations === 1 ? '' : 's'}`,\n );\n notifyIfHidden(\n 'WrongStack run finished',\n `Completed in ${payload.iterations} iteration${payload.iterations === 1 ? '' : 's'}.`,\n );\n setFaviconStatus('ready');\n }\n // Lazy permission ask — only after the first successful run, so a\n // user who never sticks around long enough for one doesn't see a\n // permission prompt on mount.\n void ensureNotificationPermission();\n // Optional chime — fires regardless of tab visibility because users\n // often want the audible cue specifically when their attention is\n // elsewhere. Synthesized via Web Audio, see lib/chime.ts. Off by\n // default; user opts in via Command Palette.\n if (useConfigStore.getState().soundOnComplete) {\n try {\n playCompletionChime();\n } catch {\n /* audio policy may block */\n }\n }\n }\n // Drain a queued follow-up if the user typed while we were running.\n // We pull one message at a time so the queue doesn't all fire as a\n // single mega-prompt — each one starts its own iteration loop.\n const next = useChatStore.getState().dequeue();\n if (next) {\n const client = getWSClient(useConfigStore.getState().wsUrl);\n useChatStore.getState().addMessage({ role: 'user', content: next });\n useChatStore.getState().setLoading(true);\n client.sendMessage(next);\n }\n });\n\n on('tools.list', (msg) => {\n const p = msg.payload as {\n tools: Array<{ name: string; description: string; params: string[] }>;\n };\n const lines = [\n `🛠️ **Registered tools** (${p.tools.length})`,\n '',\n ...p.tools.map(\n (t) =>\n `• \\`${t.name}\\`${t.params.length ? ` (${t.params.join(', ')})` : ''} — ${t.description || '_no description_'}`,\n ),\n ];\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('memory.list', (msg) => {\n const p = msg.payload as { text: string; error?: string };\n const body = p.text?.trim();\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: p.error\n ? `Memory read failed: ${p.error}`\n : body\n ? `🧠 **Memory** \\n\\n${body}`\n : '🧠 **Memory** \\n\\n_empty — nothing remembered yet_',\n });\n });\n\n on('skills.list', (msg) => {\n const p = msg.payload as {\n enabled: boolean;\n error?: string;\n skills: Array<{\n name: string;\n description: string;\n version: string;\n source: string;\n path: string;\n trigger: string;\n scope: string[];\n }>;\n };\n if (!p.enabled) {\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: '🎯 **Skills** \\n\\n_disabled (config.features.skills = false)_',\n });\n return;\n }\n const lines = [\n `🎯 **Skills** (${p.skills.length})`,\n '',\n ...(p.skills.length === 0\n ? ['_none registered_']\n : p.skills.map(\n (s) =>\n `• \\`${s.name}\\`${s.version ? ` v${s.version}` : ''} _(${s.source})_ — ${s.description || s.trigger || '_no description_'}`,\n )),\n ];\n if (p.error) lines.push('', `⚠ ${p.error}`);\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('diag.get', (msg) => {\n const p = msg.payload as {\n provider: string;\n model: string;\n cwd: string;\n sessionId: string;\n tools: { count: number; names: string[] };\n features: { memory: boolean; skills: boolean; modelsRegistry: boolean };\n mode: string;\n usage: { input: number; output: number; cacheRead?: number };\n messages: number;\n todos: number;\n };\n const lines = [\n '🩺 **Runtime diagnostics**',\n '',\n `**Provider:** \\`${p.provider}\\` / \\`${p.model}\\``,\n `**Mode:** \\`${p.mode}\\``,\n `**Session:** \\`${p.sessionId}\\``,\n `**CWD:** \\`${p.cwd}\\``,\n '',\n `**Tools:** ${p.tools.count}`,\n `**Messages:** ${p.messages} · **Todos:** ${p.todos}`,\n `**Usage:** ${p.usage.input.toLocaleString()} in · ${p.usage.output.toLocaleString()} out${p.usage.cacheRead ? ` · ${p.usage.cacheRead.toLocaleString()} cache` : ''}`,\n '',\n `**Features:** memory=${p.features.memory ? '✓' : '✗'} · skills=${p.features.skills ? '✓' : '✗'} · modelsRegistry=${p.features.modelsRegistry ? '✓' : '✗'}`,\n ];\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('stats.get', (msg) => {\n const p = msg.payload as {\n sessionId: string;\n provider: string;\n model: string;\n usage: { input: number; output: number; cacheRead?: number; cacheWrite?: number };\n cache: { readTokens: number; writeTokens: number; hitRatio: number } | null;\n cost: number;\n messages: number;\n readFiles: number;\n tools: number;\n elapsedMs: number;\n };\n const elapsedSec = Math.floor(p.elapsedMs / 1000);\n const elapsed =\n elapsedSec < 60\n ? `${elapsedSec}s`\n : elapsedSec < 3600\n ? `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`\n : `${Math.floor(elapsedSec / 3600)}h ${Math.floor((elapsedSec % 3600) / 60)}m`;\n const lines = [\n '📈 **Session stats**',\n '',\n `**Session:** \\`${p.sessionId}\\``,\n `**Provider/Model:** \\`${p.provider}\\` / \\`${p.model}\\``,\n `**Elapsed:** ${elapsed}`,\n '',\n `**Usage:** ${p.usage.input.toLocaleString()} in · ${p.usage.output.toLocaleString()} out`,\n ...(p.cache && p.cache.readTokens > 0\n ? [\n `**Cache:** ${p.cache.readTokens.toLocaleString()} read · ${p.cache.writeTokens.toLocaleString()} write · hit ratio ${(p.cache.hitRatio * 100).toFixed(1)}%`,\n ]\n : []),\n `**Cost:** $${p.cost.toFixed(4)}`,\n '',\n `**Messages:** ${p.messages} · **Files read:** ${p.readFiles} · **Tools available:** ${p.tools}`,\n ];\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('todos.updated', (msg) => {\n const p = msg.payload as {\n todos: Array<{\n id: string;\n content: string;\n status: 'pending' | 'in_progress' | 'completed';\n activeForm?: string;\n }>;\n };\n useSessionStore.getState().setTodos(p.todos ?? []);\n });\n\n on('modes.list', (msg) => {\n const p = msg.payload as {\n modes: Array<{ id: string; name: string; description: string; isActive: boolean }>;\n activeId: string;\n };\n useSessionStore\n .getState()\n .setModes(p.modes.map((m) => ({ id: m.id, name: m.name, description: m.description })));\n useSessionStore.getState().setEnv({ mode: p.activeId });\n });\n\n on('sessions.list', (msg) => {\n const payload = msg.payload as {\n sessions: SessionHistoryEntry[];\n error?: string;\n };\n useHistoryStore.getState().setEntries(payload.sessions ?? [], payload.error ?? null);\n });\n\n on('error', (msg) => {\n const payload = msg.payload as { phase: string; message: string };\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: `[${payload.phase}] ${payload.message}`,\n isError: true,\n });\n useChatStore.getState().setLoading(false);\n });\n\n return () => {\n for (const off of offs) off();\n };\n}\n\n/**\n * Mounts the WebSocket connection and installs event handlers EXACTLY ONCE.\n * Call this from App.tsx (top of the tree) and nowhere else.\n */\nexport function useWebSocketBootstrap(): void {\n const { autoConnect, wsUrl } = useConfigStore();\n const setWsStatus = useConfigStore((s) => s.setWsStatus);\n const installed = useRef(false);\n\n useEffect(() => {\n if (!autoConnect) return;\n installFaviconVisibilityReset();\n const ws = getWSClient(wsUrl);\n\n // Subscribe to fine-grained status — the topbar uses this to flip\n // between \"Connecting…\", \"Connected\", \"Reconnecting (attempt 3, retrying in 8s)\",\n // and the terminal \"Disconnected\" with the last error.\n const offStatus = ws.onStatus((s) => setWsStatus(s));\n\n ws.connect().catch((err) => {\n // eslint-disable-next-line no-console\n console.error('[WS] Connection failed:', err);\n });\n\n // installed.current guards against React StrictMode's double-mount in dev.\n if (installed.current) {\n return () => {\n offStatus();\n };\n }\n installed.current = true;\n const off = installHandlers(ws);\n return () => {\n off();\n offStatus();\n installed.current = false;\n };\n }, [autoConnect, wsUrl, setWsStatus]);\n}\n\n/**\n * Cheap accessor for the singleton WS client and its imperative action\n * methods. Components call this freely; it does NOT register handlers.\n */\nexport function useWebSocket() {\n const { wsUrl } = useConfigStore();\n const client = getWSClient(wsUrl);\n\n const sendMessage = useCallback(\n (content: string) => {\n if (client.isConnected) return client.sendMessage(content);\n return null;\n },\n [client],\n );\n\n const sendAbort = useCallback(() => client.sendAbort(), [client]);\n\n const { hideConfirm } = useUIStore();\n const sendConfirm = useCallback(\n (id: string, decision: 'yes' | 'no' | 'always' | 'deny') => {\n client.sendConfirm(id, decision);\n hideConfirm();\n },\n [client, hideConfirm],\n );\n\n const switchModel = useCallback(\n (provider: string, model: string) => client.switchModel(provider, model),\n [client],\n );\n\n const listProviders = useCallback(() => client.listProviders(), [client]);\n const listProviderModels = useCallback(\n (providerId: string) => client.listProviderModels(providerId),\n [client],\n );\n const listSavedProviders = useCallback(() => client.listSavedProviders(), [client]);\n const addKey = useCallback(\n (providerId: string, label: string, apiKey: string) => client.addKey(providerId, label, apiKey),\n [client],\n );\n const updateKey = useCallback(\n (providerId: string, label: string, apiKey: string) =>\n client.updateKey(providerId, label, apiKey),\n [client],\n );\n const deleteKey = useCallback(\n (providerId: string, label: string) => client.deleteKey(providerId, label),\n [client],\n );\n const setActiveKey = useCallback(\n (providerId: string, label: string) => client.setActiveKey(providerId, label),\n [client],\n );\n const addProvider = useCallback(\n (id: string, family: string, baseUrl?: string, apiKey?: string) =>\n client.addProvider(id, family, baseUrl, apiKey),\n [client],\n );\n const removeProvider = useCallback(\n (providerId: string) => client.removeProvider(providerId),\n [client],\n );\n\n const listSessions = useCallback(\n (limit?: number) => {\n useHistoryStore.getState().setLoading(true);\n client.listSessions(limit);\n },\n [client],\n );\n const deleteSession = useCallback(\n (id: string) => {\n useHistoryStore.getState().removeEntry(id);\n client.deleteSession(id);\n },\n [client],\n );\n const resumeSession = useCallback((id: string) => client.resumeSessionById(id), [client]);\n const saveSession = useCallback(() => client.saveSession(), [client]);\n const listTools = useCallback(() => client.listTools(), [client]);\n const listMemory = useCallback(() => client.listMemory(), [client]);\n const listSkills = useCallback(() => client.listSkills(), [client]);\n const getDiag = useCallback(() => client.getDiag(), [client]);\n const getStats = useCallback(() => client.getStats(), [client]);\n const listModes = useCallback(() => client.listModes(), [client]);\n const switchMode = useCallback((id: string) => client.switchMode(id), [client]);\n\n return {\n client,\n sendMessage,\n sendAbort,\n sendConfirm,\n switchModel,\n listProviders,\n listProviderModels,\n listSavedProviders,\n addKey,\n updateKey,\n deleteKey,\n setActiveKey,\n addProvider,\n removeProvider,\n listSessions,\n deleteSession,\n resumeSession,\n saveSession,\n listTools,\n listMemory,\n listSkills,\n getDiag,\n getStats,\n listModes,\n switchMode,\n };\n}\n","/**\n * Dynamic favicon helper. We don't ship a `.ico` file — the icon is built at\n * runtime as an inline SVG data URL so it can be re-rendered with or without\n * a status badge.\n *\n * setFaviconStatus('idle') → plain \"W\" mark\n * setFaviconStatus('running') → \"W\" + amber pulse dot (running indicator)\n * setFaviconStatus('ready') → \"W\" + green dot (run finished, tab hidden)\n * setFaviconStatus('error') → \"W\" + red dot (run failed, tab hidden)\n *\n * The status auto-resets to 'idle' on the next visibilitychange where the\n * tab becomes visible — so the badge only persists while the user is away.\n */\n\nexport type FaviconStatus = 'idle' | 'running' | 'ready' | 'error';\n\nconst BASE_BG = '#4f46e5'; // indigo-600, matches the topbar Zap mark.\n\nfunction buildSvg(status: FaviconStatus): string {\n const badge = (() => {\n if (status === 'ready')\n return '<circle cx=\"50\" cy=\"14\" r=\"14\" fill=\"#22c55e\" stroke=\"#fff\" stroke-width=\"3\" />';\n if (status === 'error')\n return '<circle cx=\"50\" cy=\"14\" r=\"14\" fill=\"#ef4444\" stroke=\"#fff\" stroke-width=\"3\" />';\n if (status === 'running')\n return '<circle cx=\"50\" cy=\"14\" r=\"14\" fill=\"#f59e0b\" stroke=\"#fff\" stroke-width=\"3\" />';\n return '';\n })();\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\">\n <rect x=\"2\" y=\"2\" width=\"60\" height=\"60\" rx=\"14\" fill=\"${BASE_BG}\" />\n <text x=\"32\" y=\"44\" text-anchor=\"middle\" font-family=\"-apple-system,Segoe UI,Roboto,sans-serif\" font-size=\"38\" font-weight=\"700\" fill=\"#fff\">W</text>\n ${badge}\n </svg>`;\n}\n\nfunction svgToDataUrl(svg: string): string {\n // encodeURIComponent keeps the URL valid across browsers; btoa would\n // also work but breaks on non-ASCII (not a concern here, but cleaner).\n return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;\n}\n\nfunction ensureLink(): HTMLLinkElement | null {\n if (typeof document === 'undefined') return null;\n let link = document.querySelector<HTMLLinkElement>('link[rel=\"icon\"]');\n if (!link) {\n link = document.createElement('link');\n link.rel = 'icon';\n link.type = 'image/svg+xml';\n document.head.appendChild(link);\n }\n return link;\n}\n\nlet currentStatus: FaviconStatus = 'idle';\n\nexport function setFaviconStatus(status: FaviconStatus): void {\n currentStatus = status;\n const link = ensureLink();\n if (!link) return;\n link.href = svgToDataUrl(buildSvg(status));\n}\n\nlet visibilityHookInstalled = false;\n\n/** Install the visibilitychange listener once. When the user comes back to\n * the tab we clear any \"ready/error\" badge so they don't see a stale\n * notification dot after they've already returned. */\nexport function installFaviconVisibilityReset(): void {\n if (visibilityHookInstalled || typeof document === 'undefined') return;\n visibilityHookInstalled = true;\n document.addEventListener('visibilitychange', () => {\n if (!document.hidden && (currentStatus === 'ready' || currentStatus === 'error')) {\n setFaviconStatus('idle');\n }\n });\n}\n","/**\n * Browser Notification API wrapper. We never pop a notification when the\n * tab is foreground — the user already sees the chat update, so an OS\n * popup would just be noise. The flow:\n *\n * 1. requestPermission() is called lazily on first run completion;\n * browsers require a user gesture for the prompt to be safe but\n * Chrome / Firefox accept it from any code path with relaxed rules.\n * 2. notifyIfHidden() short-circuits unless the page is `document.hidden`.\n * 3. Clicking the notification focuses the page (best-effort — some\n * browsers won't focus across tab groups).\n */\n\nlet permissionState: NotificationPermission | 'unsupported' = 'default';\n\nif (typeof window !== 'undefined' && 'Notification' in window) {\n permissionState = Notification.permission;\n} else {\n permissionState = 'unsupported';\n}\n\nexport async function ensureNotificationPermission(): Promise<\n NotificationPermission | 'unsupported'\n> {\n if (\n permissionState === 'unsupported' ||\n permissionState === 'granted' ||\n permissionState === 'denied'\n ) {\n return permissionState;\n }\n try {\n const result = await Notification.requestPermission();\n permissionState = result;\n return result;\n } catch {\n return 'denied';\n }\n}\n\nexport function notifyIfHidden(title: string, body?: string): void {\n if (typeof document === 'undefined' || !document.hidden) return;\n if (permissionState !== 'granted') return;\n try {\n const n = new Notification(title, {\n body,\n icon: '/favicon.ico',\n // Tag-collapse: if multiple notifications stack while the tab is\n // hidden, only the latest \"WrongStack run\" shows up so we don't\n // litter the OS notification center.\n tag: 'wrongstack-run',\n // Auto-dismiss as soon as the user focuses the tab.\n });\n n.onclick = () => {\n window.focus();\n n.close();\n };\n } catch {\n // Some browsers (e.g. iOS Safari) throw — silently swallow.\n }\n}\n","import type { WSClientMessage, WSServerMessage } from '../types';\n\ntype EventHandler = (msg: WSServerMessage) => void;\n\ninterface PendingConfirm {\n resolve: (decision: 'yes' | 'no' | 'always' | 'deny') => void;\n}\n\n/** Internal connection lifecycle states the UI subscribes to. */\nexport type WsStatus =\n | { state: 'connecting' }\n | { state: 'open' }\n | { state: 'closed'; error?: string }\n | { state: 'reconnecting'; attempt: number; nextRetryAt: number; lastError?: string };\n\nexport class WrongStackWebSocketClient {\n private ws: WebSocket | null = null;\n private url: string;\n private handlers: Map<string, Set<EventHandler>> = new Map();\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 10;\n private reconnectDelay = 1000;\n private shouldReconnect = true;\n private messageQueue: WSClientMessage[] = [];\n private pendingConfirms: Map<string, PendingConfirm> = new Map();\n private sessionId: string | null = null;\n /** Stored last close reason / error message so the UI can show \"what\n * went wrong\" while reconnecting instead of a generic spinner. */\n private lastErrorText: string | undefined;\n private statusListeners = new Set<(s: WsStatus) => void>();\n private currentStatus: WsStatus = { state: 'connecting' };\n\n onStatus(fn: (s: WsStatus) => void): () => void {\n this.statusListeners.add(fn);\n fn(this.currentStatus);\n return () => this.statusListeners.delete(fn);\n }\n\n get status(): WsStatus {\n return this.currentStatus;\n }\n\n private setStatus(s: WsStatus) {\n this.currentStatus = s;\n for (const fn of this.statusListeners) {\n try {\n fn(s);\n } catch {\n /* listener errors must not break the socket */\n }\n }\n }\n\n constructor(url?: string) {\n this.url = url ?? defaultWsUrl();\n }\n\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n resolve();\n return;\n }\n\n this.setStatus({ state: 'connecting' });\n\n try {\n this.ws = new WebSocket(this.url);\n this.ws.binaryType = 'arraybuffer';\n\n const connectTimeout = setTimeout(() => {\n reject(new Error('Connection timeout'));\n }, 10000);\n\n this.ws.onopen = () => {\n clearTimeout(connectTimeout);\n console.log('[WS Client] Connected');\n this.reconnectAttempts = 0;\n this.lastErrorText = undefined;\n this.setStatus({ state: 'open' });\n this.flushMessageQueue();\n resolve();\n };\n\n this.ws.onmessage = (event) => {\n try {\n const msg = JSON.parse(event.data) as WSServerMessage;\n this.handleMessage(msg);\n } catch (err) {\n console.error('[WS Client] Failed to parse message', err);\n }\n };\n\n this.ws.onerror = (error) => {\n console.error('[WS Client] Error', error);\n // ErrorEvent in browsers is intentionally opaque — Chrome won't\n // expose the underlying reason for security. We stash a generic\n // hint so the UI has something to display.\n this.lastErrorText = 'Connection error (see browser devtools)';\n };\n\n this.ws.onclose = (ev) => {\n console.log('[WS Client] Disconnected', ev.code, ev.reason);\n if (ev.reason && !this.lastErrorText) {\n this.lastErrorText = `${ev.reason} (code ${ev.code})`;\n } else if (!this.lastErrorText && ev.code !== 1000) {\n this.lastErrorText = `Closed with code ${ev.code}`;\n }\n this.attemptReconnect();\n };\n } catch (err) {\n clearTimeout((globalThis as Record<string, unknown>).connectTimeout as number);\n this.lastErrorText = err instanceof Error ? err.message : String(err);\n this.setStatus({ state: 'closed', error: this.lastErrorText });\n reject(err);\n }\n });\n }\n\n private attemptReconnect() {\n if (!this.shouldReconnect || this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.log('[WS Client] Not reconnecting');\n this.setStatus({ state: 'closed', error: this.lastErrorText ?? 'Disconnected' });\n return;\n }\n\n this.reconnectAttempts++;\n const delay = Math.min(this.reconnectDelay * 2 ** (this.reconnectAttempts - 1), 30000);\n const nextRetryAt = Date.now() + delay;\n console.log(`[WS Client] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n this.setStatus({\n state: 'reconnecting',\n attempt: this.reconnectAttempts,\n nextRetryAt,\n lastError: this.lastErrorText,\n });\n\n setTimeout(async () => {\n if (this.shouldReconnect) {\n try {\n await this.connect();\n } catch (err) {\n console.error('[WS Client] Reconnect failed', err);\n }\n }\n }, delay);\n }\n\n /** Force an immediate reconnect attempt, bypassing the backoff timer. */\n retryNow(): void {\n if (this.currentStatus.state === 'open') return;\n this.reconnectAttempts = 0;\n void this.connect().catch(() => undefined);\n }\n\n private flushMessageQueue() {\n while (this.messageQueue.length > 0) {\n const msg = this.messageQueue.shift();\n if (msg) this.send(msg);\n }\n }\n\n private handleMessage(msg: WSServerMessage) {\n if (msg.type === 'tool.confirm_needed') {\n const payload = msg.payload as unknown as {\n id: string;\n toolName: string;\n input: unknown;\n suggestedPattern: string;\n resolve: (d: 'yes' | 'no' | 'always' | 'deny') => void;\n };\n\n this.pendingConfirms.set(payload.id, {\n resolve: payload.resolve,\n });\n\n const msgForHandler = {\n ...msg,\n payload: {\n ...payload,\n resolve: () => {},\n },\n };\n\n this.emit(msgForHandler as WSServerMessage);\n return;\n }\n\n if (msg.type === 'session.start') {\n const payload = msg.payload as { sessionId: string };\n this.sessionId = payload.sessionId;\n }\n\n this.emit(msg);\n }\n\n private emit(msg: WSServerMessage) {\n const handlers = this.handlers.get(msg.type);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(msg);\n } catch (err) {\n console.error(`[WS Client] Handler error for ${msg.type}`, err);\n }\n }\n }\n }\n\n send(message: WSClientMessage) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(message));\n } else {\n this.messageQueue.push(message);\n }\n }\n\n on(eventType: string, handler: EventHandler): () => void {\n let handlers = this.handlers.get(eventType);\n if (!handlers) {\n handlers = new Set();\n this.handlers.set(eventType, handlers);\n }\n handlers.add(handler);\n return () => handlers?.delete(handler);\n }\n\n off(eventType: string, handler: EventHandler) {\n this.handlers.get(eventType)?.delete(handler);\n }\n\n sendMessage(content: string): string {\n const id = `msg_${Date.now()}_${crypto.randomUUID().slice(0, 8)}`;\n this.send({\n type: 'user_message',\n payload: {\n id,\n content,\n timestamp: Date.now(),\n },\n });\n return id;\n }\n\n sendAbort() {\n this.send({\n type: 'abort',\n payload: {},\n } as unknown as WSClientMessage);\n }\n\n sendConfirm(id: string, decision: 'yes' | 'no' | 'always' | 'deny') {\n const pending = this.pendingConfirms.get(id);\n if (pending) {\n pending.resolve(decision);\n this.pendingConfirms.delete(id);\n }\n this.send({\n type: 'tool.confirm_result',\n payload: { id, decision },\n });\n }\n\n switchModel(provider: string, model: string) {\n this.send({\n type: 'model.switch',\n payload: { provider, model },\n });\n }\n\n // ---- Provider/Model/Key management (mirrors TUI/CLI auth-menu) ----\n\n listProviders() {\n this.send({ type: 'providers.list' });\n }\n\n listProviderModels(providerId: string) {\n this.send({ type: 'provider.models', payload: { providerId } });\n }\n\n listSavedProviders() {\n this.send({ type: 'providers.saved' });\n }\n\n addKey(providerId: string, label: string, apiKey: string) {\n this.send({ type: 'key.add', payload: { providerId, label, apiKey } });\n }\n\n updateKey(providerId: string, label: string, apiKey: string) {\n this.send({ type: 'key.update', payload: { providerId, label, apiKey } });\n }\n\n deleteKey(providerId: string, label: string) {\n this.send({ type: 'key.delete', payload: { providerId, label } });\n }\n\n setActiveKey(providerId: string, label: string) {\n this.send({ type: 'key.set_active', payload: { providerId, label } });\n }\n\n addProvider(id: string, family: string, baseUrl?: string, apiKey?: string) {\n this.send({ type: 'provider.add', payload: { id, family, baseUrl, apiKey } });\n }\n\n removeProvider(providerId: string) {\n this.send({ type: 'provider.remove', payload: { providerId } });\n }\n\n newSession() {\n this.send({ type: 'session.new' });\n }\n\n clearContext() {\n this.send({ type: 'context.clear' });\n }\n\n compactContext(aggressive = false) {\n this.send({ type: 'context.compact', payload: { aggressive } });\n }\n\n debugContext() {\n this.send({ type: 'context.debug' });\n }\n\n // ---- Inspect commands (mirror TUI/CLI's /tools /memory /skill /diag /stats) ----\n\n listTools() {\n this.send({ type: 'tools.list' });\n }\n\n listMemory() {\n this.send({ type: 'memory.list' });\n }\n\n remember(text: string, scope?: 'project-agents' | 'project-memory' | 'user-memory') {\n this.send({ type: 'memory.remember', payload: { text, scope } });\n }\n\n forget(text: string, scope?: 'project-agents' | 'project-memory' | 'user-memory') {\n this.send({ type: 'memory.forget', payload: { text, scope } });\n }\n\n listSkills() {\n this.send({ type: 'skills.list' });\n }\n\n getDiag() {\n this.send({ type: 'diag.get' });\n }\n\n getStats() {\n this.send({ type: 'stats.get' });\n }\n\n saveSession() {\n this.send({ type: 'session.save' });\n }\n\n resumeSessionById(id: string) {\n this.send({ type: 'session.resume', payload: { id } });\n }\n\n listModes() {\n this.send({ type: 'modes.list' });\n }\n\n switchMode(id: string) {\n this.send({ type: 'mode.switch', payload: { id } });\n }\n\n listFiles(query?: string, limit?: number) {\n this.send({ type: 'files.list', payload: { query, limit } });\n }\n\n getTodos() {\n this.send({ type: 'todos.get' });\n }\n\n clearTodos() {\n this.send({ type: 'todos.clear' });\n }\n\n listSessions(limit = 50) {\n this.send({ type: 'sessions.list', payload: { limit } });\n }\n\n deleteSession(id: string) {\n this.send({ type: 'session.delete', payload: { id } });\n }\n\n resumeSession(sessionId: string) {\n this.send({\n type: 'session.resume',\n payload: { id: sessionId },\n });\n }\n\n ping() {\n this.send({ type: 'ping' });\n }\n\n disconnect() {\n this.shouldReconnect = false;\n this.ws?.close();\n this.ws = null;\n }\n\n get isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n get currentSessionId(): string | null {\n return this.sessionId;\n }\n}\n\nlet client: WrongStackWebSocketClient | null = null;\n\n/**\n * Default WS URL derived from the page's host.\n *\n * Subtle gotcha on Windows: when the page is loaded from `http://localhost:3456`,\n * the browser resolves `localhost` *itself* and on Windows it tries IPv6 `[::1]`\n * before IPv4 `127.0.0.1`. If the backend listens only on `127.0.0.1`, every\n * connection attempt to `ws://localhost:3457` first hits the IPv6 socket\n * (refused) and then either gives up or flaps — symptom: \"ws disconnect hep\".\n *\n * Fix: when the page is on a loopback host (`localhost` / `127.0.0.1` / `::1`),\n * force the WS URL to use the literal IPv4 loopback address. That bypasses the\n * DNS dance entirely. For any other hostname (LAN IP, custom WS_HOST override)\n * we keep the page's hostname so things still \"just work\".\n */\nfunction defaultWsUrl(): string {\n const port = 3457;\n if (typeof window === 'undefined' || !window.location?.hostname) {\n return `ws://127.0.0.1:${port}`;\n }\n const host = window.location.hostname.toLowerCase();\n if (host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host === '::1') {\n return `ws://127.0.0.1:${port}`;\n }\n return `ws://${window.location.hostname}:${port}`;\n}\n\nexport function getWSClient(url?: string): WrongStackWebSocketClient {\n if (!client) {\n client = new WrongStackWebSocketClient(url);\n }\n return client;\n}\n","import type { Usage } from '@wrongstack/core';\nimport type { ContentBlock } from '@wrongstack/core';\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\n/**\n * Strip immediately-repeated paragraphs/lines from an assistant reply.\n * MiniMax-M2.7 (and other smaller open models) sometimes emit the same\n * paragraph twice in one stream — we don't want that to land in the chat.\n * We only collapse *consecutive* duplicates so legitimate repetition\n * elsewhere in the message is preserved.\n */\nfunction dedupeRepeatedBlocks(text: string): string {\n if (!text) return text;\n // Pass 1: paragraph-level (split on blank lines).\n const paraSplit = text.split(/\\n{2,}/);\n const paras: string[] = [];\n for (const p of paraSplit) {\n if (paras.length > 0 && paras[paras.length - 1]!.trim() === p.trim()) continue;\n paras.push(p);\n }\n // Pass 2: line-level within each paragraph (handles models that emit the\n // same sentence twice without a blank line between).\n const cleaned = paras.map((p) => {\n const lines = p.split('\\n');\n const out: string[] = [];\n for (const line of lines) {\n if (out.length > 0 && line.trim().length > 0 && out[out.length - 1]!.trim() === line.trim()) {\n continue;\n }\n out.push(line);\n }\n return out.join('\\n');\n });\n return cleaned.join('\\n\\n');\n}\n\n// ============================================\n// Types\n// ============================================\n\nexport interface MessageContent {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string | ContentBlock[];\n}\n\nexport interface ToolExecution {\n id: string;\n name: string;\n input?: unknown;\n output?: string;\n durationMs?: number;\n ok: boolean;\n startedAt: number;\n completedAt?: number;\n}\n\nexport interface ChatMessage {\n id: string;\n content: string;\n role: 'user' | 'assistant' | 'system' | 'tool';\n toolName?: string;\n toolInput?: unknown;\n toolResult?: string;\n /** Wall-clock ms reported by the backend in tool.executed; rendered next\n * to the tool name so the user can spot slow tools at a glance. */\n toolDurationMs?: number;\n /** Backend's tool_use id (e.g. \"toolu_...\" from Anthropic). Used to map\n * tool.executed events back to the right bubble when the model fires\n * multiple tools in parallel — currentToolId alone only points at the\n * most recent start and would leave earlier ones stuck on \"Running...\". */\n toolUseId?: string;\n isError?: boolean;\n timestamp: number;\n usage?: Usage;\n streaming?: boolean;\n parentId?: string;\n /** Live progress lines for an in-flight tool, populated from\n * tool.progress WS events. Each line is shown in chronological order\n * inside the tool bubble while it's still running, and cleared once the\n * final tool.executed lands (toolResult takes over). Capped to the last\n * ~30 lines so a chatty bash command can't grow this unbounded. */\n progressLines?: string[];\n /** End-of-run summary attached to the last assistant message of a turn\n * after run.result lands. Populated by the run.result handler in\n * useWebSocket — gives the user a single-line readout of what just\n * happened (iterations, tool calls, elapsed time, cost). */\n runSummary?: {\n iterations: number;\n tools: number;\n durationMs: number;\n costDelta: number;\n };\n}\n\nexport interface SessionInfo {\n id: string;\n startedAt: number;\n provider: string;\n model: string;\n title?: string;\n}\n\n// ============================================\n// Chat Store\n// ============================================\n\ninterface ChatState {\n messages: ChatMessage[];\n currentAssistantMessageId: string | null;\n currentToolId: string | null;\n isLoading: boolean;\n abortController: AbortController | null;\n executions: Map<string, ToolExecution>;\n /** Messages typed while the agent was running. Drained one-at-a-time\n * after run.result lands so the user can stack up follow-ups without\n * waiting for each turn to finish. */\n queue: string[];\n /** Snapshot taken at the start of the current run (first iteration.started\n * after idle). Used by run.result to compute the per-turn summary —\n * duration is now-at minus this `at`, cost delta is the difference\n * between the session's current cost and the cost captured here. Null\n * while idle. */\n runStart: { at: number; cost: number } | null;\n /** Transient extended-thinking buffer. Populated by provider.thinking_delta\n * events and shown as a soft, ephemeral bubble below the chat tail while\n * the model is reasoning. Cleared the moment the model produces user-\n * facing output (text_delta) or starts a tool — and at provider.response /\n * run.result. Never persisted into `messages`, so refresh wipes it. */\n thinkingBuffer: string;\n /** Wall-clock ms when the current thinking burst started, for the chip's\n * elapsed timer. Reset alongside `thinkingBuffer`. */\n thinkingStartedAt: number | null;\n\n // Actions\n addMessage: (msg: Omit<ChatMessage, 'id' | 'timestamp'>) => string;\n updateMessage: (id: string, updates: Partial<ChatMessage>) => void;\n appendToMessage: (id: string, text: string) => void;\n /** Clean up an assistant bubble after its provider.response arrived:\n * collapse model-emitted duplicate paragraphs / consecutive duplicate\n * lines, flip the streaming flag off. Some models (notably MiniMax-M2.7)\n * emit the same paragraph twice in one stream — this strips that noise\n * at the bubble boundary so the persisted content matches what the user\n * expects to see. */\n finalizeMessage: (id: string) => void;\n setToolResult: (id: string, result: string, ok: boolean) => void;\n /** Append a single progress line to the tool bubble identified by its\n * ChatMessage id. Capped at 30 lines (oldest dropped) so chatty tools\n * don't bloat memory or rerender too aggressively. */\n appendToolProgress: (id: string, line: string) => void;\n setLoading: (loading: boolean) => void;\n setAbortController: (ctrl: AbortController | null) => void;\n clearMessages: () => void;\n setCurrentAssistantMessage: (id: string | null) => void;\n setCurrentToolId: (id: string | null) => void;\n /** Remove the message identified by `id` and every message after it.\n * Used by the \"edit + regenerate\" action on user bubbles — the user\n * clicks the pencil, types a corrected prompt, and we want everything\n * downstream of that point to disappear so the new send looks like a\n * fresh branch from this question. */\n truncateAfter: (id: string) => void;\n addExecution: (exec: ToolExecution) => void;\n updateExecution: (id: string, updates: Partial<ToolExecution>) => void;\n enqueue: (text: string) => void;\n dequeue: () => string | null;\n removeQueued: (idx: number) => void;\n clearQueue: () => void;\n setRunStart: (s: { at: number; cost: number } | null) => void;\n /** Append a thinking chunk. Lazy-starts the elapsed timer on the first\n * chunk after a clear. */\n appendThinking: (text: string) => void;\n /** Wipe the thinking buffer + timer. Called by the events that indicate\n * the model has moved past reasoning (text/tool/response/run end). */\n clearThinking: () => void;\n}\n\nexport const useChatStore = create<ChatState>()(\n persist(\n (set, get) => ({\n messages: [],\n currentAssistantMessageId: null,\n currentToolId: null,\n isLoading: false,\n abortController: null,\n executions: new Map(),\n queue: [],\n runStart: null,\n thinkingBuffer: '',\n thinkingStartedAt: null,\n\n addMessage: (msg) => {\n const id = `msg_${Date.now()}_${crypto.randomUUID().slice(0, 8)}`;\n const fullMsg: ChatMessage = { ...msg, id, timestamp: Date.now() };\n set((state) => ({\n messages: [...state.messages, fullMsg],\n currentAssistantMessageId:\n msg.role === 'assistant' ? id : state.currentAssistantMessageId,\n }));\n return id;\n },\n\n updateMessage: (id, updates) => {\n set((state) => ({\n messages: state.messages.map((m) => (m.id === id ? { ...m, ...updates } : m)),\n }));\n },\n\n appendToMessage: (id, text) => {\n set((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, content: m.content + text } : m,\n ),\n }));\n },\n\n finalizeMessage: (id) => {\n set((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, content: dedupeRepeatedBlocks(m.content), streaming: false } : m,\n ),\n }));\n },\n\n setToolResult: (id, result, ok) => {\n set((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, toolResult: result, isError: !ok, progressLines: undefined } : m,\n ),\n }));\n },\n\n appendToolProgress: (id, line) => {\n set((state) => ({\n messages: state.messages.map((m) => {\n if (m.id !== id) return m;\n const prev = m.progressLines ?? [];\n const next = [...prev, line];\n // Bounded buffer: keep the most recent 30 lines.\n const trimmed = next.length > 30 ? next.slice(next.length - 30) : next;\n return { ...m, progressLines: trimmed };\n }),\n }));\n },\n\n setLoading: (loading) => set({ isLoading: loading }),\n setAbortController: (ctrl) => set({ abortController: ctrl }),\n\n clearMessages: () =>\n set({\n messages: [],\n currentAssistantMessageId: null,\n currentToolId: null,\n executions: new Map(),\n }),\n\n setCurrentAssistantMessage: (id) => set({ currentAssistantMessageId: id }),\n\n setCurrentToolId: (id) => set({ currentToolId: id }),\n\n truncateAfter: (id) =>\n set((state) => {\n const idx = state.messages.findIndex((m) => m.id === id);\n if (idx === -1) return state;\n return {\n messages: state.messages.slice(0, idx),\n currentAssistantMessageId: null,\n currentToolId: null,\n };\n }),\n\n addExecution: (exec) => {\n set((state) => {\n const newExecutions = new Map(state.executions);\n newExecutions.set(exec.id, exec);\n return { executions: newExecutions };\n });\n },\n\n updateExecution: (id, updates) => {\n set((state) => {\n const newExecutions = new Map(state.executions);\n const existing = newExecutions.get(id);\n if (existing) {\n newExecutions.set(id, { ...existing, ...updates });\n }\n return { executions: newExecutions };\n });\n },\n\n enqueue: (text) => set((state) => ({ queue: [...state.queue, text] })),\n dequeue: () => {\n const q = get().queue;\n if (q.length === 0) return null;\n const [next, ...rest] = q;\n set({ queue: rest });\n return next!;\n },\n removeQueued: (idx) => set((state) => ({ queue: state.queue.filter((_, i) => i !== idx) })),\n clearQueue: () => set({ queue: [] }),\n setRunStart: (s) => set({ runStart: s }),\n appendThinking: (text) =>\n set((state) => ({\n thinkingBuffer: state.thinkingBuffer + text,\n thinkingStartedAt: state.thinkingStartedAt ?? Date.now(),\n })),\n clearThinking: () => set({ thinkingBuffer: '', thinkingStartedAt: null }),\n }),\n {\n name: 'wrongstack-chat',\n // Intentionally persist nothing. Messages are bound to a backend session;\n // restoring them on reload would resurrect a stale conversation that the\n // backend no longer has context for (the next session.start clearMessages\n // anyway). Keep theme/wsUrl in useConfigStore, transcripts ephemeral.\n partialize: () => ({}),\n },\n ),\n);\n\n// ============================================\n// Config Store\n// ============================================\n\ninterface ConfigState {\n provider: string;\n model: string;\n baseUrl?: string;\n apiKey?: string;\n wsUrl: string;\n wsConnected: boolean;\n /** Fine-grained connection state from the WS client. Drives the topbar's\n * reconnect indicator. */\n wsStatus:\n | { state: 'connecting' }\n | { state: 'open' }\n | { state: 'closed'; error?: string }\n | { state: 'reconnecting'; attempt: number; nextRetryAt: number; lastError?: string };\n theme: 'light' | 'dark' | 'system';\n autoConnect: boolean;\n /** Play a soft synthesized chime when run.result lands with status=done.\n * Off by default — opt-in via the Command Palette. Persisted so the\n * preference survives reloads. Actual playback only fires after the\n * user has interacted with the page (Web Audio autoplay policy). */\n soundOnComplete: boolean;\n\n setProvider: (provider: string) => void;\n setModel: (model: string) => void;\n setConfig: (\n config: Partial<Omit<ConfigState, 'setProvider' | 'setModel' | 'setConfig' | 'setTheme'>>,\n ) => void;\n setTheme: (theme: 'light' | 'dark' | 'system') => void;\n setWsConnected: (connected: boolean) => void;\n setWsStatus: (s: ConfigState['wsStatus']) => void;\n setSoundOnComplete: (on: boolean) => void;\n}\n\nexport const useConfigStore = create<ConfigState>()(\n persist(\n (set) => ({\n provider: 'anthropic',\n model: 'claude-sonnet-4-20250514',\n // Default WS URL tracks the page's hostname so loading from 127.0.0.1,\n // localhost, or a LAN IP all just work. For `localhost` we force the\n // literal IPv4 address — see ws-client.ts `defaultWsUrl()` for the\n // Windows IPv6/IPv4 resolution gotcha this avoids.\n wsUrl: (() => {\n if (typeof window === 'undefined' || !window.location?.hostname) {\n return 'ws://127.0.0.1:3457';\n }\n const h = window.location.hostname.toLowerCase();\n if (h === 'localhost' || h === '127.0.0.1' || h === '[::1]' || h === '::1') {\n return 'ws://127.0.0.1:3457';\n }\n return `ws://${window.location.hostname}:3457`;\n })(),\n wsConnected: false,\n wsStatus: { state: 'connecting' },\n theme: 'system',\n autoConnect: true,\n soundOnComplete: false,\n setProvider: (provider) => set({ provider }),\n setModel: (model) => set({ model }),\n setConfig: (config) => set(config),\n setTheme: (theme) => set({ theme }),\n setWsConnected: (connected) => set({ wsConnected: connected }),\n setWsStatus: (wsStatus) => set({ wsStatus, wsConnected: wsStatus.state === 'open' }),\n setSoundOnComplete: (on) => set({ soundOnComplete: on }),\n }),\n {\n name: 'wrongstack-config',\n },\n ),\n);\n\n// ============================================\n// Session Store\n// ============================================\n\ninterface SessionState {\n session: SessionInfo | null;\n totalTokens: Usage;\n /** Input tokens of the LAST provider response — used as the \"live context\n * size\" indicator in the topbar (matches what TUI's ContextChip shows). */\n lastInputTokens: number;\n cost: number;\n startTime: number | null;\n /** Model max context window, from models.dev catalog. 0 = unknown. */\n maxContext: number;\n /** USD per 1M tokens — used to compute cost deltas on every provider.response. */\n inputCost: number;\n outputCost: number;\n cacheReadCost: number;\n /** basename(projectRoot) for the topbar. */\n projectName: string;\n /** Active mode id (default | code | …). */\n mode: string;\n /** All modes the backend knows about, populated by modes.list. The\n * topbar mode chip uses this to render a picker; empty until the\n * backend responds. */\n modes: Array<{ id: string; name: string; description: string }>;\n /** Iteration progress while the agent is running. Resets on run.result. */\n iteration: { index: number; max: number } | null;\n /** Live snapshot of context.todos — backend broadcasts on every\n * tool.executed, and the sidebar/overlay reads from here. */\n todos: Array<{\n id: string;\n content: string;\n status: 'pending' | 'in_progress' | 'completed';\n activeForm?: string;\n }>;\n\n setSession: (session: SessionInfo | null) => void;\n updateUsage: (usage: Usage) => void;\n addCost: (cost: number) => void;\n startSession: (session: SessionInfo) => void;\n endSession: () => void;\n setEnv: (env: {\n maxContext?: number;\n projectName?: string;\n mode?: string;\n inputCost?: number;\n outputCost?: number;\n cacheReadCost?: number;\n }) => void;\n setIteration: (it: { index: number; max: number } | null) => void;\n setModes: (modes: Array<{ id: string; name: string; description: string }>) => void;\n setTodos: (todos: SessionState['todos']) => void;\n}\n\nexport const useSessionStore = create<SessionState>()(\n persist(\n (set) => ({\n session: null,\n totalTokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n lastInputTokens: 0,\n cost: 0,\n startTime: null,\n maxContext: 0,\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n projectName: '',\n mode: 'default',\n modes: [],\n iteration: null,\n todos: [],\n\n setSession: (session) => set({ session }),\n\n updateUsage: (usage) =>\n set((state) => {\n // True total input this turn = fresh + cached subsets, since\n // Usage is now disjoint across providers (see core's Usage doc).\n // Without summing, prompt-cached turns under-report the ctx chip.\n const totalInput =\n (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);\n return {\n totalTokens: {\n input: state.totalTokens.input + usage.input,\n output: state.totalTokens.output + usage.output,\n cacheRead: (state.totalTokens.cacheRead ?? 0) + (usage.cacheRead ?? 0),\n cacheWrite: (state.totalTokens.cacheWrite ?? 0) + (usage.cacheWrite ?? 0),\n },\n lastInputTokens: totalInput || state.lastInputTokens,\n };\n }),\n\n addCost: (cost) => set((state) => ({ cost: state.cost + cost })),\n\n startSession: (session) =>\n // Full reset on every session boundary. Without this, /new and\n // /clear would keep the previous session's token totals + cost in\n // the status bar — confusing because the chat looks empty but the\n // header insists there were 50k tokens already used.\n set({\n session,\n startTime: Date.now(),\n iteration: null,\n lastInputTokens: 0,\n totalTokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n cost: 0,\n }),\n\n endSession: () =>\n set({\n session: null,\n startTime: null,\n iteration: null,\n }),\n\n setEnv: (env) =>\n set((state) => ({\n maxContext: env.maxContext ?? state.maxContext,\n projectName: env.projectName ?? state.projectName,\n mode: env.mode ?? state.mode,\n inputCost: env.inputCost ?? state.inputCost,\n outputCost: env.outputCost ?? state.outputCost,\n cacheReadCost: env.cacheReadCost ?? state.cacheReadCost,\n })),\n\n setIteration: (iteration) => set({ iteration }),\n setModes: (modes) => set({ modes }),\n setTodos: (todos) => set({ todos }),\n }),\n {\n name: 'wrongstack-session',\n partialize: () => ({}),\n },\n ),\n);\n\n// ============================================\n// UI Store\n// ============================================\n\ninterface UIState {\n sidebarOpen: boolean;\n settingsOpen: boolean;\n currentView: 'chat' | 'history' | 'settings';\n showConfirmDialog: boolean;\n confirmInfo: {\n id: string;\n toolName: string;\n input: unknown;\n suggestedPattern: string;\n } | null;\n /** ⌘K palette is mounted globally; this flag controls its visibility. */\n paletteOpen: boolean;\n /** \"?\" shortcuts overlay visibility. */\n shortcutsOpen: boolean;\n /** Ctrl+F chat-content search. */\n searchOpen: boolean;\n searchQuery: string;\n /** Rolling list of recently sent user prompts so ↑ in an empty input can\n * recall them like a terminal. Capped to ~50 to keep storage bounded. */\n promptHistory: string[];\n /** Sidebar width in pixels. User can drag the right edge to resize.\n * Clamped to [200, 480] in the drag handler. Persisted. */\n sidebarWidth: number;\n /** Assistant message ids the user pinned. Persisted across reloads so a\n * user who pins a long debugging answer doesn't lose the bookmark on a\n * refresh. Note: messages themselves aren't persisted, so a pin's only\n * useful within the same in-memory session — the sidebar Pinned panel\n * prunes ids that no longer correspond to a live message. */\n pinnedIds: string[];\n /** \"Compact mode\" — denser spacing throughout the chat. Off by default;\n * power users with long sessions like the tighter layout. Toggle via\n * Ctrl+Shift+D globally. */\n compactMode: boolean;\n /** Open state for the Quick Model Switcher overlay. Lives in the store\n * so the topbar's model chip can open it imperatively without smuggling\n * synthetic keyboard events through the DOM. Ctrl+M toggles this too. */\n modelSwitcherOpen: boolean;\n /** Session ids the user starred in the history list. Persisted across\n * reloads. Starred sessions float to the top of the history sidebar\n * regardless of date bucket so a long-running project session stays\n * reachable without scrolling. */\n favoriteSessionIds: string[];\n /** Local UI nicknames for sessions, keyed by session id. The backend\n * session.title is auto-derived from the first user message and isn't\n * user-editable yet; this lets a user rename a session in the WebUI\n * (\"Auth refactor exploration\") without a backend round-trip. Used by\n * the History list, recent-sessions cards, and the tab title. */\n sessionNicknames: Record<string, string>;\n\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n setSettingsOpen: (open: boolean) => void;\n setCurrentView: (view: 'chat' | 'history' | 'settings') => void;\n showConfirm: (info: UIState['confirmInfo']) => void;\n hideConfirm: () => void;\n setPaletteOpen: (open: boolean) => void;\n setShortcutsOpen: (open: boolean) => void;\n setSearchOpen: (open: boolean) => void;\n setSearchQuery: (q: string) => void;\n pushPrompt: (text: string) => void;\n setSidebarWidth: (px: number) => void;\n togglePin: (id: string) => void;\n unpinAll: () => void;\n toggleCompactMode: () => void;\n setModelSwitcherOpen: (open: boolean) => void;\n toggleFavoriteSession: (id: string) => void;\n setSessionNickname: (id: string, nickname: string) => void;\n}\n\nexport const useUIStore = create<UIState>()(\n persist(\n (set) => ({\n sidebarOpen: true,\n settingsOpen: false,\n currentView: 'chat',\n showConfirmDialog: false,\n confirmInfo: null,\n paletteOpen: false,\n shortcutsOpen: false,\n searchOpen: false,\n searchQuery: '',\n promptHistory: [],\n sidebarWidth: 288,\n pinnedIds: [],\n compactMode: false,\n modelSwitcherOpen: false,\n favoriteSessionIds: [],\n sessionNicknames: {},\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n setSidebarOpen: (open) => set({ sidebarOpen: open }),\n setSettingsOpen: (open) => set({ settingsOpen: open }),\n setCurrentView: (view) => set({ currentView: view }),\n showConfirm: (info) => set({ showConfirmDialog: true, confirmInfo: info }),\n hideConfirm: () => set({ showConfirmDialog: false, confirmInfo: null }),\n setPaletteOpen: (open) => set({ paletteOpen: open }),\n setShortcutsOpen: (open) => set({ shortcutsOpen: open }),\n setSearchOpen: (open) => set({ searchOpen: open, searchQuery: open ? '' : '' }),\n setSearchQuery: (q) => set({ searchQuery: q }),\n pushPrompt: (text) =>\n set((state) => {\n const trimmed = text.trim();\n if (!trimmed) return state;\n // Dedupe consecutive duplicates and cap the buffer.\n const filtered = state.promptHistory.filter((p) => p !== trimmed);\n return { promptHistory: [trimmed, ...filtered].slice(0, 50) };\n }),\n setSidebarWidth: (px) => set({ sidebarWidth: Math.max(200, Math.min(480, Math.round(px))) }),\n togglePin: (id) =>\n set((state) => {\n const has = state.pinnedIds.includes(id);\n return {\n pinnedIds: has ? state.pinnedIds.filter((p) => p !== id) : [...state.pinnedIds, id],\n };\n }),\n unpinAll: () => set({ pinnedIds: [] }),\n toggleCompactMode: () => set((s) => ({ compactMode: !s.compactMode })),\n setModelSwitcherOpen: (open) => set({ modelSwitcherOpen: open }),\n toggleFavoriteSession: (id) =>\n set((state) => {\n const has = state.favoriteSessionIds.includes(id);\n return {\n favoriteSessionIds: has\n ? state.favoriteSessionIds.filter((s) => s !== id)\n : [...state.favoriteSessionIds, id],\n };\n }),\n setSessionNickname: (id, nickname) =>\n set((state) => {\n const trimmed = nickname.trim();\n const next = { ...state.sessionNicknames };\n if (trimmed) next[id] = trimmed;\n else delete next[id];\n return { sessionNicknames: next };\n }),\n }),\n {\n name: 'wrongstack-ui',\n // Persist only what's useful across reloads — sidebar state and the\n // prompt history. Modal flags (palette/shortcuts/search) reset on\n // load so the user doesn't reopen the app into an open dialog.\n partialize: (s) => ({\n sidebarOpen: s.sidebarOpen,\n sidebarWidth: s.sidebarWidth,\n promptHistory: s.promptHistory,\n pinnedIds: s.pinnedIds,\n compactMode: s.compactMode,\n favoriteSessionIds: s.favoriteSessionIds,\n sessionNicknames: s.sessionNicknames,\n }),\n },\n ),\n);\n\n// ============================================\n// History Store\n// ============================================\n\n/** A row in the sidebar's History tab. Mirrors core's SessionSummary +\n * isCurrent so the active session can be highlighted. Timestamps are\n * ISO-8601 strings as stored on disk; the UI parses them lazily. */\nexport interface SessionHistoryEntry {\n id: string;\n title: string;\n startedAt: string;\n model: string;\n provider: string;\n tokenTotal: number;\n isCurrent: boolean;\n}\n\ninterface HistoryState {\n entries: SessionHistoryEntry[];\n loading: boolean;\n error: string | null;\n setEntries: (entries: SessionHistoryEntry[], error?: string | null) => void;\n setLoading: (loading: boolean) => void;\n removeEntry: (id: string) => void;\n clearHistory: () => void;\n}\n\nexport const useHistoryStore = create<HistoryState>()((set) => ({\n entries: [],\n loading: false,\n error: null,\n setEntries: (entries, error = null) => set({ entries, error, loading: false }),\n setLoading: (loading) => set({ loading }),\n removeEntry: (id) =>\n set((state) => ({\n entries: state.entries.filter((e) => e.id !== id),\n })),\n clearHistory: () => set({ entries: [] }),\n}));\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { useWebSocketBootstrap } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport { useEffect } from 'react';\nimport { ChatView } from './components/ChatView';\nimport { CommandPalette, downloadChatAsMarkdown } from './components/CommandPalette';\nimport { ConfirmDialog } from './components/ConfirmDialog';\nimport { ConnectionBanner } from './components/ConnectionBanner';\nimport { QuickModelSwitcher } from './components/QuickModelSwitcher';\nimport { SettingsPanel } from './components/SettingsPanel';\nimport { ShortcutsOverlay } from './components/ShortcutsOverlay';\nimport { Sidebar } from './components/Sidebar';\nimport { ThemeProvider, useTheme } from './components/ThemeProvider';\nimport { Toaster } from './components/Toaster';\n\nfunction AppInner() {\n const { theme } = useTheme();\n const { currentView, sidebarOpen, toggleSidebar, setSearchOpen, setSidebarOpen } = useUIStore();\n const isLoading = useChatStore((s) => s.isLoading);\n const iteration = useSessionStore((s) => s.iteration);\n const projectName = useSessionStore((s) => s.projectName);\n const sessionTitle = useSessionStore((s) => s.session?.title);\n const sessionId = useSessionStore((s) => s.session?.id);\n // User-set local nickname for the current session — takes precedence\n // over the backend title in the tab strip and topbar.\n const nickname = useUIStore((s) => (sessionId ? s.sessionNicknames[sessionId] : undefined));\n const ws = useWebSocket();\n\n // Mobile-friendly: collapse the sidebar automatically below the md\n // breakpoint (768px). Tracks viewport changes so a window resize behaves\n // the same as a fresh load. We only AUTO-close — re-opening (or keeping\n // it open) on small screens stays a user decision, so we never call\n // setSidebarOpen(true) here.\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mq = window.matchMedia('(max-width: 768px)');\n const apply = () => {\n if (mq.matches && useUIStore.getState().sidebarOpen) {\n setSidebarOpen(false);\n }\n };\n apply();\n mq.addEventListener('change', apply);\n return () => mq.removeEventListener('change', apply);\n }, [setSidebarOpen]);\n // Install WS handlers exactly once for the whole app. Every other consumer\n // (ChatInput, ConfirmDialog, SettingsPanel) uses the cheap `useWebSocket()`\n // hook which returns action methods only — see hooks/useWebSocket.ts for\n // the duplicate-handler trap this avoids.\n useWebSocketBootstrap();\n\n // Reflect the agent's run state + session identity in the browser tab\n // title. Pinned/grouped tab strips become readable at a glance — the\n // project name surfaces first so multiple WrongStack windows on the same\n // bar can still be distinguished, then the session title (if any), then\n // the running indicator. Falls back gracefully when fields are missing.\n useEffect(() => {\n const parts: string[] = [];\n if (isLoading) {\n const it = iteration\n ? ` iter ${iteration.index}${iteration.max ? `/${iteration.max}` : ''}`\n : '';\n parts.push(`●${it}`);\n }\n const sessionLabel = nickname?.trim() || sessionTitle?.trim();\n const projectLabel = projectName?.trim();\n if (sessionLabel) parts.push(sessionLabel);\n if (projectLabel) parts.push(projectLabel);\n parts.push('WrongStack');\n const title = parts.filter(Boolean).join(' · ');\n document.title = title;\n return () => {\n document.title = 'WrongStack';\n };\n }, [isLoading, iteration, projectName, sessionTitle, nickname]);\n\n // Global keyboard shortcuts for the actions that don't have a dedicated\n // owner (palette/shortcuts handle their own). Bound here so they fire\n // anywhere except inside text inputs (where Ctrl+F should still search\n // the chat, but Ctrl+L would otherwise be a browser address-bar focus).\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const t = e.target as HTMLElement | null;\n const tag = t?.tagName?.toLowerCase();\n const inField = tag === 'input' || tag === 'textarea' || t?.isContentEditable;\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.key === '\\\\') {\n e.preventDefault();\n toggleSidebar();\n return;\n }\n if (mod && e.key.toLowerCase() === 'f') {\n e.preventDefault();\n setSearchOpen(true);\n return;\n }\n if (mod && e.key.toLowerCase() === '/') {\n // Focus the chat textarea so the user can start typing without\n // hunting for it. Useful after closing palette/settings.\n e.preventDefault();\n const ta = document.querySelector('textarea');\n ta?.focus();\n return;\n }\n // The Ctrl-letter shortcuts skip when the user is typing in any\n // input — otherwise Ctrl+L wipes the chat while they're composing.\n if (mod && !inField) {\n if (e.key.toLowerCase() === 'l') {\n e.preventDefault();\n useChatStore.getState().clearMessages();\n ws.client?.clearContext?.();\n } else if (e.key.toLowerCase() === 'n') {\n e.preventDefault();\n ws.client?.newSession?.();\n } else if (e.key.toLowerCase() === 'e') {\n e.preventDefault();\n downloadChatAsMarkdown();\n }\n }\n // Ctrl+Shift+D toggles compact UI density. Distinct from Ctrl+D\n // (which is reserved as the browser bookmark accelerator).\n if (mod && e.shiftKey && e.key.toLowerCase() === 'd') {\n e.preventDefault();\n useUIStore.getState().toggleCompactMode();\n }\n // Vim-style chat navigation: j/k step between bubbles, g goes to the\n // first message and G to the last. Skipped while typing so j/k inside\n // the textarea still inserts those letters. No modifier required —\n // this is the chat surface's primary input mode for keyboard users.\n if (!inField && !mod && !e.altKey) {\n const bubbles = Array.from(document.querySelectorAll<HTMLElement>('[data-message-id]'));\n if (bubbles.length === 0) return;\n const current = document.querySelector<HTMLElement>(\n '[data-message-id][data-focused=\"true\"]',\n );\n const idx = current ? bubbles.indexOf(current) : -1;\n const focusBubble = (target: HTMLElement) => {\n for (const b of bubbles) b.removeAttribute('data-focused');\n target.setAttribute('data-focused', 'true');\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n };\n if (e.key === 'j' || e.key === 'ArrowDown') {\n // ArrowDown only intercepts when nothing else has focus AND the\n // user is not in a scrollable list context — the textarea check\n // above covers the only place arrows have meaningful default\n // behaviour for this app.\n const next = bubbles[Math.min(bubbles.length - 1, Math.max(0, idx + 1))];\n if (next) {\n e.preventDefault();\n focusBubble(next);\n }\n return;\n }\n if (e.key === 'k' || e.key === 'ArrowUp') {\n const prev = bubbles[Math.max(0, idx <= 0 ? 0 : idx - 1)];\n if (prev) {\n e.preventDefault();\n focusBubble(prev);\n }\n return;\n }\n if (e.key === 'g' && !e.shiftKey) {\n e.preventDefault();\n focusBubble(bubbles[0]!);\n return;\n }\n if (e.key === 'G' || (e.key === 'g' && e.shiftKey)) {\n e.preventDefault();\n focusBubble(bubbles[bubbles.length - 1]!);\n return;\n }\n if (e.key === 'Escape' && current) {\n e.preventDefault();\n current.removeAttribute('data-focused');\n return;\n }\n // `c` while a bubble is focused: copy its visible text. Useful\n // pairing with the j/k flow so power users can step + copy without\n // hunting for the in-bubble copy button.\n if (e.key === 'c' && current) {\n const text =\n current.querySelector<HTMLElement>('.markdown-content')?.innerText ?? current.innerText;\n if (text) {\n void navigator.clipboard?.writeText(text).catch(() => {});\n e.preventDefault();\n }\n return;\n }\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [toggleSidebar, setSearchOpen, ws]);\n\n return (\n <div className={cn('flex h-screen', theme)}>\n {sidebarOpen && <Sidebar />}\n <main className=\"flex-1 flex flex-col overflow-hidden\">\n <ConnectionBanner />\n {currentView === 'chat' && <ChatView />}\n {currentView === 'settings' && <SettingsPanel />}\n </main>\n\n {/* Global overlays */}\n <ConfirmDialog />\n <CommandPalette />\n <ShortcutsOverlay />\n <QuickModelSwitcher />\n <Toaster />\n </div>\n );\n}\n\nexport function App() {\n return (\n <ThemeProvider defaultTheme=\"system\">\n <AppInner />\n </ThemeProvider>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport type { ChatMessage } from '@/stores';\nimport { useConfigStore } from '@/stores';\nimport {\n Activity,\n ArrowDown,\n ArrowUp,\n Bot,\n Brain,\n Command,\n Cpu,\n FolderOpen,\n Monitor,\n Moon,\n PanelLeftOpen,\n Settings,\n Sun,\n Wifi,\n WifiOff,\n Zap,\n} from 'lucide-react';\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from 'react';\nimport { ChatInput } from './ChatInput';\nimport { ConnectionChip } from './ConnectionChip';\nimport { CostChip } from './CostChip';\nimport { MessageBubble } from './MessageBubble';\nimport { ModePicker } from './ModePicker';\nimport { SearchOverlay } from './SearchOverlay';\nimport { ToolGroup } from './ToolGroup';\nimport { WelcomeScreen } from './WelcomeScreen';\nimport { Button } from './ui/button';\nimport { ScrollArea } from './ui/scroll-area';\n\nfunction fmtTok(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1000) return `${(n / 1000).toFixed(n >= 10_000 ? 0 : 1)}k`;\n return String(n);\n}\n\n/**\n * Soft, ephemeral chip rendered while the model is mid-reasoning. Reads the\n * thinking buffer straight from the chat store so it stays in sync with the\n * stream without re-rendering the full message list. Mounted only while the\n * buffer is non-empty — the WS handler clears it on text/tool/response/run\n * boundaries, so this naturally appears at the start of a turn and\n * disappears the moment the model commits to user-visible output.\n */\nfunction ThinkingBubble() {\n const buf = useChatStore((s) => s.thinkingBuffer);\n if (!buf) return null;\n // Show only the last ~6 lines so the chip stays bounded while the model\n // rambles. Whole buffer is in the store if we ever want a \"show all\"\n // affordance, but for the ephemeral chip the tail is what feels live.\n const tailLines = buf.split('\\n').slice(-6);\n const tail = tailLines.join('\\n').trim();\n return (\n <div className=\"flex gap-3 animate-message\">\n <div className=\"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-violet-500/10 text-violet-600 dark:text-violet-400 ring-2 ring-offset-2 ring-offset-background ring-violet-500/20\">\n <Brain className=\"h-4 w-4 animate-pulse\" />\n </div>\n <div className=\"flex flex-col gap-1 max-w-[85%] min-w-0\">\n <span className=\"text-xs font-medium text-violet-600 dark:text-violet-400 px-1\">\n Thinking…\n </span>\n <div className=\"rounded-2xl rounded-bl-md px-3 py-2 bg-violet-500/[0.04] border border-violet-500/20 text-foreground/80\">\n <pre className=\"whitespace-pre-wrap break-words font-sans text-xs leading-relaxed italic max-h-32 overflow-hidden\">\n {tail || '…'}\n </pre>\n </div>\n </div>\n </div>\n );\n}\n\nexport function ChatView() {\n const { messages, isLoading } = useChatStore();\n const setPaletteOpen = useUIStore((s) => s.setPaletteOpen);\n const setShortcutsOpen = useUIStore((s) => s.setShortcutsOpen);\n const sidebarOpen = useUIStore((s) => s.sidebarOpen);\n const toggleSidebar = useUIStore((s) => s.toggleSidebar);\n const compactMode = useUIStore((s) => s.compactMode);\n const setTheme = useConfigStore((s) => s.setTheme);\n const theme = useConfigStore((s) => s.theme);\n const { totalTokens, cost, startTime, lastInputTokens, maxContext, projectName, iteration } =\n useSessionStore();\n const { wsConnected, wsStatus, provider, model } = useConfigStore();\n const { setCurrentView } = useUIStore();\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Context window usage (mirrors TUI's ContextChip semantics: lastInputTokens\n // is the most recent provider call's input size — the de-facto live context).\n const ctxPct =\n maxContext > 0 && lastInputTokens > 0\n ? Math.min(100, Math.round((lastInputTokens / maxContext) * 100))\n : 0;\n const ctxTone =\n ctxPct >= 85\n ? 'bg-red-500/15 text-red-600 dark:text-red-400'\n : ctxPct >= 70\n ? 'bg-amber-500/15 text-amber-600 dark:text-amber-400'\n : 'bg-muted text-muted-foreground';\n\n // Auto-scroll with \"user is reading older messages\" lock. We watch the\n // Radix ScrollArea viewport's scroll position; if the user is within\n // ~120px of the bottom we keep pinning new messages to the bottom. The\n // moment they scroll up, we let go — new content appends invisibly and a\n // floating \"↓ new messages\" button shows up so they can rejoin the live\n // tail when they're ready.\n const [pinnedToBottom, setPinnedToBottom] = useState(true);\n const [unreadCount, setUnreadCount] = useState(0);\n /** True when the user has scrolled past ~1 viewport-height from the top\n * AND isn't anchored at the bottom — i.e. they're deep in mid-history.\n * Used to surface a \"back to top\" pill so navigating a 200-message\n * transcript doesn't require thumb-flicking. */\n const [scrolledDeep, setScrolledDeep] = useState(false);\n const lastSeenCount = useRef(messages.length);\n\n // Resolve the actual scrollable viewport that Radix renders inside the\n // ScrollArea root. We re-resolve every render because the ref points at\n // the root, not the viewport.\n const getViewport = useCallback((): HTMLElement | null => {\n return scrollRef.current?.querySelector('[data-radix-scroll-area-viewport]') ?? null;\n }, []);\n\n useEffect(() => {\n const viewport = getViewport();\n if (!viewport) return;\n const onScroll = () => {\n const dist = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;\n const nowPinned = dist < 120;\n setPinnedToBottom(nowPinned);\n if (nowPinned) {\n setUnreadCount(0);\n lastSeenCount.current = messages.length;\n }\n // Show \"back to top\" only when there's actually a lot of content\n // ABOVE the user (so it doesn't pop up on tiny chats) AND they're\n // not already near the top.\n const deep =\n viewport.scrollTop > viewport.clientHeight &&\n viewport.scrollHeight > viewport.clientHeight * 2.5;\n setScrolledDeep(deep);\n };\n viewport.addEventListener('scroll', onScroll, { passive: true });\n return () => viewport.removeEventListener('scroll', onScroll);\n }, [getViewport, messages.length]);\n\n useEffect(() => {\n const viewport = getViewport();\n if (!viewport) return;\n if (pinnedToBottom) {\n viewport.scrollTop = viewport.scrollHeight;\n lastSeenCount.current = messages.length;\n } else {\n const delta = messages.length - lastSeenCount.current;\n if (delta > 0) setUnreadCount(delta);\n }\n }, [messages, pinnedToBottom, getViewport]);\n\n const scrollToBottom = useCallback(() => {\n const viewport = getViewport();\n if (!viewport) return;\n viewport.scrollTo({ top: viewport.scrollHeight, behavior: 'smooth' });\n setPinnedToBottom(true);\n setUnreadCount(0);\n lastSeenCount.current = messages.length;\n }, [getViewport, messages.length]);\n\n const scrollToTop = useCallback(() => {\n const viewport = getViewport();\n if (!viewport) return;\n viewport.scrollTo({ top: 0, behavior: 'smooth' });\n }, [getViewport]);\n\n // Live \"agent is busy\" indicator. We track when the current run started\n // (rising edge of isLoading) and tick a second-resolution clock so the\n // running-status bubble shows a live elapsed timer. Reset on idle.\n const [runStartedAt, setRunStartedAt] = useState<number | null>(null);\n const [nowTick, setNowTick] = useState<number>(() => Date.now());\n /** Anchor for streaming-speed computation. We capture the assistant\n * message id, the wall-clock time, and the content length at the moment\n * streaming starts; chars-per-second is derived from the delta between\n * then and `nowTick`. Reset when the streaming bubble changes (new turn)\n * or when streaming flips off (idle). */\n const streamAnchor = useRef<{ id: string; at: number; len: number } | null>(null);\n useEffect(() => {\n if (isLoading && runStartedAt === null) setRunStartedAt(Date.now());\n if (!isLoading && runStartedAt !== null) setRunStartedAt(null);\n }, [isLoading, runStartedAt]);\n useEffect(() => {\n if (!isLoading) return;\n const t = setInterval(() => setNowTick(Date.now()), 500);\n return () => clearInterval(t);\n }, [isLoading]);\n\n const formatDuration = (start: number | null) => {\n if (!start) return '--';\n const seconds = Math.floor((Date.now() - start) / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${minutes}m ${secs}s`;\n };\n\n // Agent state derived once — used by both the chip and the indicator\n // bubble at the bottom of the chat. `streaming` while an assistant\n // bubble is mid-text, else `thinking` between turns, else `idle`.\n const agentState = (() => {\n if (!isLoading) return 'idle' as const;\n const last = messages[messages.length - 1];\n const isStreaming = last?.role === 'assistant' && !!last.content && last.streaming;\n return isStreaming ? ('streaming' as const) : ('thinking' as const);\n })();\n const stateTone =\n agentState === 'idle'\n ? 'bg-muted text-muted-foreground'\n : agentState === 'streaming'\n ? 'bg-blue-500/10 text-blue-600 dark:text-blue-400'\n : 'bg-amber-500/10 text-amber-600 dark:text-amber-400';\n\n // We show the status row (ctx / tokens / cost / elapsed) only when there's\n // *something* worth reporting — otherwise the second line is just dead\n // pixels on a brand-new empty session.\n const hasStatusContent =\n (maxContext > 0 && lastInputTokens > 0) || totalTokens.input > 0 || !!startTime;\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Header — two compact rows.\n Row 1: identity + actions (sidebar reopen, connection, project,\n model, mode, state, iteration, palette/theme/?/settings).\n Row 2 (when present): live numbers (ctx %, tokens, cache %, cost,\n elapsed) — kept off-row 1 so the action cluster never\n wraps when a session warms up. */}\n <header className=\"flex flex-col border-b bg-card shrink-0\">\n <div className=\"flex items-center justify-between gap-2 px-3 py-2\">\n <div className=\"flex items-center gap-1.5 min-w-0 flex-1\">\n {/* Sidebar reopen — only visible when the sidebar is hidden.\n Otherwise the sidebar's own toggle handles it. */}\n {!sidebarOpen && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 shrink-0\"\n onClick={toggleSidebar}\n title=\"Open sidebar (Ctrl+\\\\)\"\n >\n <PanelLeftOpen className=\"h-4 w-4\" />\n </Button>\n )}\n {/* When the sidebar is hidden we surface a tiny WrongStack mark\n so the user can still see they're in WrongStack — but inline,\n no big header text. */}\n {!sidebarOpen && (\n <div className=\"flex items-center gap-1.5 shrink-0 mr-1\">\n <div className=\"w-5 h-5 rounded bg-primary flex items-center justify-center\">\n <Zap className=\"h-3 w-3 text-primary-foreground\" />\n </div>\n </div>\n )}\n {/* Connection pill — granular status with retry button. Lives\n next to the sidebar toggle so it's always visible. Hover\n shows the last error (if any). */}\n <ConnectionChip wsStatus={wsStatus} wsConnected={wsConnected} />\n <span\n className={cn(\n 'flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 tabular-nums',\n stateTone,\n )}\n title={`Agent state: ${agentState}`}\n >\n {agentState !== 'idle' && (\n <span className=\"h-1.5 w-1.5 rounded-full bg-current animate-pulse\" />\n )}\n <span>{agentState}</span>\n </span>\n {projectName && (\n <span\n className=\"flex items-center gap-1 text-[11px] text-muted-foreground shrink-0 min-w-0\"\n title={`Project: ${projectName}`}\n >\n <FolderOpen className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate max-w-[12rem]\">{projectName}</span>\n </span>\n )}\n <button\n type=\"button\"\n onClick={() => useUIStore.getState().setModelSwitcherOpen(true)}\n className=\"group flex items-center gap-1 px-2 py-0.5 rounded-md border bg-background/50 hover:bg-accent hover:border-primary/40 transition-colors text-[11px] min-w-0 shrink-0\"\n title=\"Change provider / model (Ctrl+M)\"\n >\n <Cpu className=\"h-3 w-3 text-muted-foreground group-hover:text-foreground shrink-0\" />\n <span className=\"font-mono truncate max-w-[16rem]\">\n <span className=\"text-muted-foreground\">{provider || 'no-provider'}</span>\n <span className=\"text-muted-foreground/40 mx-0.5\">/</span>\n <span className=\"font-medium\">{model || 'no-model'}</span>\n </span>\n </button>\n <ModePicker />\n {iteration && (\n <span\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 text-primary shrink-0\"\n title=\"Agent iteration\"\n >\n <Activity className=\"h-3 w-3 animate-pulse\" />\n iter {iteration.index}\n {iteration.max > 0 ? `/${iteration.max}` : ''}\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-0.5 shrink-0\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n onClick={() => setPaletteOpen(true)}\n title=\"Command palette (Ctrl+K)\"\n >\n <Command className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n onClick={() => {\n const next = theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light';\n setTheme(next);\n }}\n title={`Theme: ${theme} (click to cycle)`}\n >\n {theme === 'light' ? (\n <Sun className=\"h-4 w-4\" />\n ) : theme === 'dark' ? (\n <Moon className=\"h-4 w-4\" />\n ) : (\n <Monitor className=\"h-4 w-4\" />\n )}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 font-mono text-xs\"\n onClick={() => setShortcutsOpen(true)}\n title=\"Keyboard shortcuts (?)\"\n >\n ?\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n onClick={() => setCurrentView('settings')}\n title=\"Settings\"\n >\n <Settings className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {hasStatusContent && (\n <div className=\"flex items-center justify-between gap-3 px-3 py-1 border-t bg-muted/20 text-[11px] text-muted-foreground\">\n <div className=\"flex items-center gap-3 min-w-0 flex-1 tabular-nums\">\n {maxContext > 0 && lastInputTokens > 0 && (\n <span\n className={cn(\n 'flex items-center gap-1 px-1.5 py-0.5 rounded-full font-medium shrink-0',\n ctxTone,\n )}\n title={`Last input: ${lastInputTokens.toLocaleString()} / ${maxContext.toLocaleString()} tokens`}\n >\n ctx {ctxPct}% · {fmtTok(lastInputTokens)}/{fmtTok(maxContext)}\n </span>\n )}\n {totalTokens.input > 0 && (\n <>\n <span className=\"flex items-center gap-1\">\n <span className=\"font-medium text-foreground\">{fmtTok(totalTokens.input)}</span>\n <span>in</span>\n </span>\n <span className=\"flex items-center gap-1\">\n <span className=\"font-medium text-foreground\">\n {fmtTok(totalTokens.output)}\n </span>\n <span>out</span>\n </span>\n {totalTokens.cacheRead &&\n totalTokens.cacheRead > 0 &&\n (() => {\n const denom = (totalTokens.cacheRead ?? 0) + totalTokens.input;\n const pct =\n denom > 0 ? Math.round(((totalTokens.cacheRead ?? 0) / denom) * 100) : 0;\n return (\n <span\n className=\"flex items-center gap-1\"\n title={`Cache hit ratio: ${pct}%`}\n >\n <span className=\"font-medium text-foreground\">\n {fmtTok(totalTokens.cacheRead)}\n </span>\n <span>cache ({pct}%)</span>\n </span>\n );\n })()}\n <CostChip />\n </>\n )}\n </div>\n {startTime && (\n <span className=\"text-muted-foreground/70 tabular-nums shrink-0\">\n {formatDuration(startTime)}\n </span>\n )}\n </div>\n )}\n </header>\n\n {/* Messages */}\n <div className=\"flex-1 relative overflow-hidden\">\n {/* Chat-local Ctrl+F overlay — pinned top-right, scrolls hits into\n view and highlights the active row in MessageBubble. */}\n <SearchOverlay />\n {/* Jump-to-latest pill — only when the user scrolled away from the\n live tail. Shows the unread count so they know how much they're\n behind without having to scroll down first. */}\n {!pinnedToBottom && (\n <button\n type=\"button\"\n onClick={scrollToBottom}\n className={cn(\n 'absolute bottom-4 left-1/2 -translate-x-1/2 z-10',\n 'flex items-center gap-2 px-4 py-2 rounded-full shadow-lg',\n 'bg-primary text-primary-foreground text-xs font-medium',\n 'hover:bg-primary/90 transition-colors animate-message',\n )}\n >\n <ArrowDown className=\"h-3.5 w-3.5\" />\n {unreadCount > 0\n ? `${unreadCount} new message${unreadCount === 1 ? '' : 's'}`\n : 'Jump to latest'}\n </button>\n )}\n {/* Back-to-top — only when the chat is genuinely long AND the user\n scrolled past one viewport. Floats top-right so it doesn't compete\n with the jump-to-latest pill at the bottom. */}\n {scrolledDeep && (\n <button\n type=\"button\"\n onClick={scrollToTop}\n title=\"Scroll to top (oldest)\"\n className={cn(\n 'absolute top-3 right-3 z-10',\n 'flex items-center gap-1 px-2.5 py-1 rounded-full shadow-md border',\n 'bg-background/90 backdrop-blur-sm text-[11px] text-muted-foreground',\n 'hover:text-foreground hover:bg-background transition-colors animate-message',\n )}\n >\n <ArrowUp className=\"h-3 w-3\" />\n <span>Top</span>\n </button>\n )}\n <ScrollArea className=\"h-full\" ref={scrollRef}>\n <div\n className={cn(\n 'mx-auto pb-8',\n compactMode ? 'max-w-5xl p-3 space-y-3' : 'max-w-5xl p-4 space-y-6',\n )}\n >\n {messages.length === 0 && !isLoading && <WelcomeScreen />}\n\n {/* Two-pass grouping.\n Pass 1 — collapse consecutive tool messages into one ToolGroup\n chip (so 8 parallel reads don't eat the viewport).\n Pass 2 — bundle every run of non-user groups (assistant text +\n tool chips) into a single \"agent turn\". Inside a turn, items\n render with tight spacing and only the first item shows the\n avatar; this stitches the text-tool-text-tool stream into one\n continuous flow instead of stacking each message as its own\n detached bubble. */}\n {(() => {\n type Group =\n | { kind: 'msg'; message: ChatMessage; isFirst: boolean }\n | { kind: 'tools'; tools: ChatMessage[]; key: string };\n const groups: Group[] = [];\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i]!;\n if (m.role === 'tool') {\n const last = groups[groups.length - 1];\n if (last && last.kind === 'tools') {\n last.tools.push(m);\n } else {\n groups.push({ kind: 'tools', tools: [m], key: m.id });\n }\n } else {\n const prev = messages[i - 1];\n groups.push({\n kind: 'msg',\n message: m,\n isFirst: !prev || prev.role !== m.role,\n });\n }\n }\n // Bundle consecutive non-user groups into agent turns. User\n // messages stay as their own standalone turn so the bubble\n // alignment switches sides naturally.\n type Turn =\n | { kind: 'user'; message: ChatMessage; key: string }\n | { kind: 'agent'; items: Group[]; key: string };\n const turns: Turn[] = [];\n for (const g of groups) {\n if (g.kind === 'msg' && g.message.role === 'user') {\n turns.push({ kind: 'user', message: g.message, key: g.message.id });\n continue;\n }\n const last = turns[turns.length - 1];\n if (last && last.kind === 'agent') {\n last.items.push(g);\n } else {\n const key = g.kind === 'msg' ? g.message.id : g.key;\n turns.push({ kind: 'agent', items: [g], key });\n }\n }\n // Track which date (local YYYY-MM-DD) the previous turn was\n // stamped with, so we can emit a soft divider between days. This\n // matters most after `session.resume` rehydrates a transcript\n // that spans yesterday → today; without dividers the user can't\n // tell where the gap is.\n let prevDay: string | null = null;\n const dayKey = (ts: number) => {\n const d = new Date(ts);\n return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;\n };\n const dayLabel = (ts: number) => {\n const d = new Date(ts);\n const today = new Date();\n const yest = new Date(Date.now() - 86_400_000);\n if (dayKey(ts) === dayKey(today.getTime())) return 'Today';\n if (dayKey(ts) === dayKey(yest.getTime())) return 'Yesterday';\n return d.toLocaleDateString(undefined, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n year: d.getFullYear() === today.getFullYear() ? undefined : 'numeric',\n });\n };\n const turnTs = (t: Turn): number => {\n if (t.kind === 'user') return t.message.timestamp;\n const first = t.items[0]!;\n return first.kind === 'msg' ? first.message.timestamp : first.tools[0]!.timestamp;\n };\n const out: ReactNode[] = [];\n for (let idx = 0; idx < turns.length; idx++) {\n const t = turns[idx]!;\n const ts = turnTs(t);\n const day = dayKey(ts);\n if (day !== prevDay) {\n out.push(\n <div\n key={`day-${day}-${idx}`}\n className=\"flex items-center gap-3 py-1 text-[11px] text-muted-foreground/70 uppercase tracking-wider font-medium\"\n >\n <div className=\"flex-1 h-px bg-border/50\" />\n <span>{dayLabel(ts)}</span>\n <div className=\"flex-1 h-px bg-border/50\" />\n </div>,\n );\n prevDay = day;\n }\n if (t.kind === 'user') {\n out.push(<MessageBubble key={t.key} message={t.message} isFirst />);\n continue;\n }\n const isLastTurn = idx === turns.length - 1;\n out.push(\n <div key={t.key} className={cn(compactMode ? 'space-y-1' : 'space-y-1.5')}>\n {t.items.map((g, gi) => {\n const continuation = gi > 0;\n if (g.kind === 'msg') {\n return (\n <MessageBubble\n key={g.message.id}\n message={g.message}\n isFirst={!continuation && g.isFirst}\n isContinuation={continuation}\n />\n );\n }\n const isLatestRunning =\n isLastTurn &&\n gi === t.items.length - 1 &&\n isLoading &&\n g.tools.some((tt) => tt.toolResult === undefined);\n return (\n <ToolGroup\n key={g.key}\n tools={g.tools}\n defaultOpen={isLatestRunning}\n isContinuation={continuation}\n />\n );\n })}\n </div>,\n );\n }\n return out;\n })()}\n\n {/* Transient extended-thinking bubble. Driven by\n provider.thinking_delta events; cleared by the first text_delta /\n tool.started / provider.response / run.result of the turn, so it\n \"appears and disappears\" alongside the model's internal reasoning\n and never persists into the transcript. */}\n <ThinkingBubble />\n\n {/* Running status bubble — always present as the last message\n while the agent is not idle. Picks a label based on what the\n agent is currently doing (composing reply / running tools /\n thinking between steps) and ticks a live elapsed timer so the\n user has visible proof of life even mid-iteration. */}\n {isLoading &&\n (() => {\n const last = messages[messages.length - 1];\n const runningTools = messages.filter(\n (m) => m.role === 'tool' && m.toolResult === undefined,\n );\n let label = 'Thinking…';\n if (runningTools.length > 0) {\n const names = Array.from(\n new Set(runningTools.map((t) => t.toolName).filter(Boolean) as string[]),\n );\n const preview = names.slice(0, 2).join(', ');\n const more = names.length > 2 ? ` +${names.length - 2}` : '';\n label =\n runningTools.length === 1\n ? `Running ${preview || 'tool'}…`\n : `Running ${runningTools.length} tools (${preview}${more})…`;\n } else if (last?.role === 'assistant' && last.content) {\n label = 'Writing reply…';\n } else if (last?.role === 'tool' && last.toolResult !== undefined) {\n label = 'Thinking about the next step…';\n }\n const elapsedSec = runStartedAt\n ? Math.max(0, Math.floor((nowTick - runStartedAt) / 1000))\n : 0;\n const elapsed =\n elapsedSec < 60\n ? `${elapsedSec}s`\n : `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`;\n // Streaming speed: derive chars/s for the currently-streaming\n // assistant bubble. Anchor on first sight of a streaming bubble;\n // tear down once it's no longer streaming so the next turn\n // starts from zero.\n let speedLabel = '';\n const streamingBubble =\n last?.role === 'assistant' && last.streaming && last.content ? last : null;\n if (streamingBubble) {\n const anchor = streamAnchor.current;\n if (!anchor || anchor.id !== streamingBubble.id) {\n streamAnchor.current = {\n id: streamingBubble.id,\n at: Date.now(),\n len: streamingBubble.content.length,\n };\n } else {\n const dt = Math.max(1, nowTick - anchor.at);\n const dl = Math.max(0, streamingBubble.content.length - anchor.len);\n // Only show after 0.5s of streaming so the first reading\n // isn't wildly inflated by the latency-to-first-chunk.\n if (dt > 500 && dl > 0) {\n const cps = (dl * 1000) / dt;\n speedLabel =\n cps >= 1000\n ? `${(cps / 1000).toFixed(1)}k ch/s`\n : `${Math.round(cps)} ch/s`;\n }\n }\n } else if (streamAnchor.current) {\n streamAnchor.current = null;\n }\n return (\n <div className=\"flex gap-3 animate-message\">\n <div className=\"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-accent text-accent-foreground ring-2 ring-offset-2 ring-offset-background ring-accent/20\">\n <Bot className=\"h-4 w-4\" />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"rounded-2xl px-4 py-3 bg-card border text-foreground\">\n <div className=\"flex items-center gap-3 text-sm\">\n <span className=\"flex gap-1\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.3s]\" />\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.15s]\" />\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce\" />\n </span>\n <span className=\"text-foreground/90\">{label}</span>\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n {elapsed}\n </span>\n {iteration && (\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n · iter {iteration.index}\n {iteration.max > 0 ? `/${iteration.max}` : ''}\n </span>\n )}\n {speedLabel && (\n <span className=\"text-xs text-muted-foreground/80 tabular-nums\">\n · {speedLabel}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n })()}\n </div>\n </ScrollArea>\n </div>\n\n {/* Input */}\n <div className=\"border-t bg-card/50 backdrop-blur supports-[backdrop-filter]:bg-card/50 p-4 shrink-0\">\n <div className=\"max-w-5xl mx-auto\">\n <ChatInput />\n <p className=\"text-xs text-center text-muted-foreground/50 mt-2\">\n Press Enter to send, Shift+Enter for new line\n </p>\n </div>\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport { Pencil, Send, Square } from 'lucide-react';\nimport type React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { downloadChatAsMarkdown } from './CommandPalette';\nimport { FilePicker } from './FilePicker';\nimport { Button } from './ui/button';\n\n/**\n * Slash command registry. Each entry knows its triggers (so /model and\n * /settings can map to the same action), a one-line description (shown in\n * the popup), and a `run` callback. Adding a new command means: append an\n * entry here, write a `run` body, done — no need to touch the popup or\n * dispatcher.\n */\ntype SlashCategory = 'Session' | 'Inspect' | 'Run' | 'App';\n\ninterface SlashCommandDef {\n /** Primary name (the one shown). */\n name: string;\n /** Optional alternative spellings — typed by the user, dispatched here. */\n aliases?: string[];\n description: string;\n /** Logical bucket the command belongs to. Drives the section headings\n * in the slash menu popup so a user scanning the list can find what\n * they want by category. */\n category: SlashCategory;\n}\n\nconst SLASH_COMMANDS: SlashCommandDef[] = [\n { name: '/help', category: 'App', description: 'Show every slash command and what it does' },\n { name: '/export', category: 'Session', description: 'Download the current chat as markdown' },\n {\n name: '/todos',\n category: 'Inspect',\n description: 'List current todos (try `/todos clear` to reset)',\n },\n {\n name: '/clear',\n category: 'Session',\n description: 'Wipe current context (keeps session id, disk record stays)',\n },\n {\n name: '/new',\n category: 'Session',\n description: 'Start a brand-new session (fresh on disk and in memory)',\n },\n {\n name: '/compact',\n category: 'Session',\n description: 'Shrink context — elide ancient tool output',\n },\n {\n name: '/debug',\n category: 'Inspect',\n aliases: ['/context'],\n description: 'Per-section context size breakdown',\n },\n {\n name: '/tools',\n category: 'Inspect',\n description: 'List every registered tool the model can call',\n },\n {\n name: '/memory',\n category: 'Inspect',\n description: 'Show all remembered notes (project + user scope)',\n },\n { name: '/skill', category: 'Inspect', aliases: ['/skills'], description: 'List active skills' },\n {\n name: '/diag',\n category: 'Inspect',\n description: 'Runtime diagnostics (provider, tools, features, mode, usage)',\n },\n {\n name: '/stats',\n category: 'Inspect',\n description: 'Session stats: tokens, cache hit ratio, cost, elapsed',\n },\n {\n name: '/save',\n category: 'Session',\n description: 'Force-flush the session (auto-saved already)',\n },\n { name: '/abort', category: 'Run', aliases: ['/stop'], description: 'Abort the current run' },\n {\n name: '/settings',\n category: 'App',\n aliases: ['/model'],\n description: 'Open settings (provider/model/keys)',\n },\n];\n\nconst SLASH_CATEGORY_ORDER: SlashCategory[] = ['Run', 'Session', 'Inspect', 'App'];\n\n/**\n * Match what the user typed against the registry. Empty query lists\n * everything; otherwise filter by primary name AND alias prefixes so\n * `/sto` finds `/stop` (alias of /abort).\n */\n/**\n * Find an open `@`-mention at the cursor. Scans backwards: if we hit\n * whitespace before an `@`, there's no mention. The `@` must be either at\n * the very start of the buffer or immediately preceded by whitespace —\n * email addresses like `a@b.com` shouldn't trigger the picker.\n */\nfunction detectAtMention(value: string, cursor: number): { start: number; query: string } | null {\n let i = cursor - 1;\n while (i >= 0) {\n const c = value[i]!;\n if (c === '@') {\n const prev = i > 0 ? value[i - 1] : '';\n if (i === 0 || /\\s/.test(prev ?? '')) {\n return { start: i, query: value.slice(i + 1, cursor) };\n }\n return null;\n }\n // Allow path chars (letters/digits/_/-./).\n if (/\\s/.test(c)) return null;\n i--;\n }\n return null;\n}\n\nfunction matchSlash(query: string): SlashCommandDef[] {\n const q = query.toLowerCase();\n if (q === '/' || q === '') return SLASH_COMMANDS;\n return SLASH_COMMANDS.filter(\n (c) => c.name.startsWith(q) || (c.aliases?.some((a) => a.startsWith(q)) ?? false),\n );\n}\n\nexport function ChatInput() {\n const { isLoading, setLoading, addMessage, clearMessages } = useChatStore();\n const queue = useChatStore((s) => s.queue);\n const enqueue = useChatStore((s) => s.enqueue);\n const removeQueued = useChatStore((s) => s.removeQueued);\n const clearQueue = useChatStore((s) => s.clearQueue);\n const { setCurrentView } = useUIStore();\n const pushPrompt = useUIStore((s) => s.pushPrompt);\n const promptHistory = useUIStore((s) => s.promptHistory);\n const ws = useWebSocket();\n const { sendMessage, sendAbort, client } = ws;\n /** Live context-budget signals — drive the token-estimate chip beside\n * the character counter. The estimate uses the universal 4-char-per-token\n * heuristic which is wrong by ±25% for natural prose but accurate enough\n * to warn the user before they paste a 100k-char file into a 200k window.\n * The chip only renders past the threshold so short drafts stay clean. */\n const lastInputTokens = useSessionStore((s) => s.lastInputTokens);\n const maxContext = useSessionStore((s) => s.maxContext);\n const [input, setInput] = useState('');\n const [slashIndex, setSlashIndex] = useState(0);\n /** Cursor into promptHistory. -1 = \"live input, not browsing history\".\n * Reset to -1 whenever the user types something that's NOT a history\n * navigation. */\n const [historyIdx, setHistoryIdx] = useState(-1);\n /** Open `@`-mention picker state. We track the starting position of the\n * `@` in the textarea so on pick we can replace the partial token\n * (`@compa`) with the chosen path. Null = closed. */\n const [atMention, setAtMention] = useState<{ start: number; query: string } | null>(null);\n /** Transient hint shown after a large paste. The user can read it for a\n * few seconds then it auto-dismisses. We only surface it for genuinely\n * big drops (>800 chars) — smaller pastes don't need a callout. */\n const [pasteHint, setPasteHint] = useState<{ chars: number; lines: number } | null>(null);\n /** True while an OS-drag is hovering over the input area. Triggers the\n * drop overlay so the user gets visual confirmation they're about to\n * attach. Browsers don't expose full filesystem paths from drops for\n * security reasons, so we seed the @-mention picker with the file's\n * basename and let the user confirm the resolved workspace path. */\n const [draggingOver, setDraggingOver] = useState(false);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const runSlashCommand = useCallback(\n (raw: string): boolean => {\n const trimmed = raw.trim();\n // Split into head (with leading slash) + the rest. Lowercase the\n // head so `/Todos` and `/TODOS` route the same; preserve case on\n // the args because the user might be inserting a content string.\n const sp = trimmed.indexOf(' ');\n const head = (sp === -1 ? trimmed : trimmed.slice(0, sp)).toLowerCase();\n const args = sp === -1 ? '' : trimmed.slice(sp + 1).trim();\n const cmd = head;\n switch (cmd) {\n case '/help': {\n // Render the registry inline as an assistant message.\n const lines = [\n '📖 **Slash commands**',\n '',\n ...SLASH_COMMANDS.map(\n (c) =>\n `• \\`${c.name}\\`${c.aliases?.length ? ` (${c.aliases.map((a) => `\\`${a}\\``).join(', ')})` : ''} — ${c.description}`,\n ),\n ];\n addMessage({ role: 'assistant', content: lines.join('\\n') });\n return true;\n }\n case '/clear':\n clearMessages();\n client?.clearContext?.();\n return true;\n case '/new':\n client?.newSession?.();\n return true;\n case '/compact':\n case '/compact!':\n client?.compactContext?.(cmd === '/compact!');\n return true;\n case '/debug':\n case '/context':\n client?.debugContext?.();\n return true;\n case '/tools':\n ws.listTools();\n return true;\n case '/memory':\n ws.listMemory();\n return true;\n case '/skill':\n case '/skills':\n ws.listSkills();\n return true;\n case '/diag':\n ws.getDiag();\n return true;\n case '/stats':\n ws.getStats();\n return true;\n case '/save':\n ws.saveSession();\n return true;\n case '/todos': {\n // Sub-commands: `/todos` (default = list), `/todos clear`. We\n // pull live state from the session store so the rendered output\n // matches what the sidebar already shows — no separate fetch.\n const sub = args.toLowerCase();\n if (sub === 'clear') {\n client?.clearTodos?.();\n return true;\n }\n const list = useSessionStore.getState().todos;\n if (list.length === 0) {\n addMessage({\n role: 'assistant',\n content:\n \"✅ **Todos** — _empty. Ask the agent to plan something and they'll show up here._\",\n });\n return true;\n }\n const lines: string[] = [\n `✅ **Todos** (${list.filter((t) => t.status === 'completed').length}/${list.length} done)`,\n '',\n ];\n for (const t of list) {\n const mark =\n t.status === 'completed' ? '[x]' : t.status === 'in_progress' ? '[~]' : '[ ]';\n const text = t.status === 'in_progress' && t.activeForm ? t.activeForm : t.content;\n lines.push(`- ${mark} ${text}`);\n }\n lines.push('', '_Use `/todos clear` to wipe the list._');\n addMessage({ role: 'assistant', content: lines.join('\\n') });\n return true;\n }\n case '/export':\n downloadChatAsMarkdown();\n addMessage({ role: 'assistant', content: '📥 Chat exported to your downloads folder.' });\n return true;\n case '/abort':\n case '/stop':\n sendAbort();\n setLoading(false);\n return true;\n case '/settings':\n case '/model':\n setCurrentView('settings');\n return true;\n default:\n return false;\n }\n },\n [addMessage, clearMessages, client, sendAbort, setLoading, setCurrentView, ws],\n );\n\n // Suggest slash commands as the user types. Only when the buffer is\n // exactly a slash command head — `/foo bar` shouldn't open the popup.\n const slashSuggestions = input.startsWith('/') && !input.includes(' ') ? matchSlash(input) : [];\n\n // Reset the highlight when the visible list changes so ↑/↓ always starts\n // from the top of the new matches.\n useEffect(() => {\n if (slashIndex >= slashSuggestions.length) setSlashIndex(0);\n }, [slashSuggestions.length, slashIndex]);\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n if (!input.trim()) return;\n\n const content = input.trim();\n\n if (content.startsWith('/') && runSlashCommand(content)) {\n pushPrompt(content);\n setInput('');\n setHistoryIdx(-1);\n return;\n }\n\n setInput('');\n setHistoryIdx(-1);\n pushPrompt(content);\n\n // If the agent is still running, queue the follow-up instead of\n // dropping it. The run.result handler in useWebSocket drains the\n // queue one message at a time. We also enable the textarea while\n // running so this code path is reachable.\n if (isLoading) {\n enqueue(content);\n return;\n }\n\n try {\n if (client?.isConnected) {\n addMessage({ role: 'user', content });\n setLoading(true);\n sendMessage(content);\n } else {\n console.error('WebSocket not connected');\n }\n } catch (err) {\n console.error('Failed to send:', err);\n setLoading(false);\n }\n },\n [\n input,\n isLoading,\n enqueue,\n client,\n sendMessage,\n setLoading,\n addMessage,\n runSlashCommand,\n pushPrompt,\n ],\n );\n\n const handleAbort = useCallback(() => {\n sendAbort();\n setLoading(false);\n }, [sendAbort, setLoading]);\n\n /** \"Stop & edit\" — abort the in-flight run, then pull the last user\n * message back into the input so the user can rewrite the prompt and\n * resend. Saves the two-step dance of clicking Abort, waiting for the\n * agent to settle, then hunting for the original prompt. */\n const handleStopAndEdit = useCallback(() => {\n sendAbort();\n setLoading(false);\n const all = useChatStore.getState().messages;\n for (let i = all.length - 1; i >= 0; i--) {\n const m = all[i]!;\n if (m.role === 'user' && m.content) {\n setInput(m.content);\n requestAnimationFrame(() => {\n const ta = textareaRef.current;\n if (ta) {\n ta.style.height = 'auto';\n ta.style.height = `${Math.min(ta.scrollHeight, 200)}px`;\n ta.focus();\n ta.setSelectionRange(m.content.length, m.content.length);\n }\n });\n return;\n }\n }\n }, [sendAbort, setLoading]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n // Terminal-style prompt history: ↑ pulls the previous user prompt,\n // ↓ steps forward. Only active when both popups are closed AND the\n // input is empty OR already showing a history entry. We keep the cursor\n // ergonomic — once the user starts editing, we drop out of history mode.\n if (slashSuggestions.length === 0 && !atMention && promptHistory.length > 0) {\n if (e.key === 'ArrowUp') {\n const ta = e.currentTarget;\n // Only steal ↑ if we're on the first line (so multi-line editing\n // can still navigate within the textarea naturally).\n const beforeCursor = ta.value.slice(0, ta.selectionStart);\n if (historyIdx >= 0 || beforeCursor.indexOf('\\n') === -1) {\n e.preventDefault();\n const next = Math.min(promptHistory.length - 1, historyIdx + 1);\n setHistoryIdx(next);\n const text = promptHistory[next] ?? '';\n setInput(text);\n requestAnimationFrame(() => {\n const el = textareaRef.current;\n if (el) {\n el.style.height = 'auto';\n el.style.height = `${Math.min(el.scrollHeight, 200)}px`;\n el.setSelectionRange(text.length, text.length);\n }\n });\n return;\n }\n }\n if (e.key === 'ArrowDown' && historyIdx >= 0) {\n e.preventDefault();\n const next = historyIdx - 1;\n if (next < 0) {\n setHistoryIdx(-1);\n setInput('');\n } else {\n setHistoryIdx(next);\n const text = promptHistory[next] ?? '';\n setInput(text);\n requestAnimationFrame(() => {\n const el = textareaRef.current;\n if (el) {\n el.style.height = 'auto';\n el.style.height = `${Math.min(el.scrollHeight, 200)}px`;\n el.setSelectionRange(text.length, text.length);\n }\n });\n }\n return;\n }\n }\n\n // Slash popup keyboard navigation: ↑/↓ to select, Tab/Enter to commit,\n // Esc to dismiss. Matches the TUI's slash menu UX one-for-one so users\n // moving between surfaces don't have to relearn anything.\n if (slashSuggestions.length > 0) {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setSlashIndex((i) => (i + 1) % slashSuggestions.length);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n setSlashIndex((i) => (i - 1 + slashSuggestions.length) % slashSuggestions.length);\n return;\n }\n if (e.key === 'Tab') {\n e.preventDefault();\n const pick = slashSuggestions[slashIndex];\n if (pick) {\n setInput(pick.name + ' ');\n setSlashIndex(0);\n }\n return;\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n // Commit the highlighted suggestion if there's an exact match below\n // the cursor (or the user hasn't typed a full name yet). Otherwise\n // fall through to normal submit.\n const pick = slashSuggestions[slashIndex];\n if (pick && pick.name !== input.toLowerCase().trim()) {\n e.preventDefault();\n setInput('');\n runSlashCommand(pick.name);\n return;\n }\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n setInput('');\n return;\n }\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit(e);\n }\n };\n\n const adjustTextareaHeight = () => {\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;\n }\n };\n\n return (\n <div className=\"flex flex-col gap-2\">\n {/* Smart paste hint — transient, auto-dismisses after 4s. We don't\n *do* anything with it (no auto-attach), just inform; users who\n pasted by accident can still Cmd+Z. */}\n {pasteHint && (\n <div className=\"rounded-md border border-amber-500/30 bg-amber-500/5 text-amber-700 dark:text-amber-300 px-2.5 py-1.5 text-xs flex items-center justify-between gap-2 animate-message\">\n <span>\n Pasted{' '}\n <span className=\"font-mono tabular-nums\">{pasteHint.chars.toLocaleString()}</span> chars\n (<span className=\"font-mono tabular-nums\">{pasteHint.lines}</span> lines) — fenced code\n blocks render best with <span className=\"font-mono\">```</span>.\n </span>\n <button\n type=\"button\"\n onClick={() => setPasteHint(null)}\n className=\"text-amber-600/70 hover:text-amber-600 dark:text-amber-300/70 dark:hover:text-amber-300 shrink-0\"\n title=\"Dismiss\"\n >\n ×\n </button>\n </div>\n )}\n {/* Queue visualization — shows messages the user stacked while the\n agent was still running. Each row has a remove button; the whole\n queue can be cleared. The hook below drains them after run.result. */}\n {queue.length > 0 && (\n <div className=\"rounded-lg border bg-muted/30 p-2 text-xs\">\n <div className=\"flex items-center justify-between mb-1.5\">\n <span className=\"text-[10px] uppercase tracking-wider text-muted-foreground font-medium\">\n Queued ({queue.length})\n </span>\n <button\n type=\"button\"\n onClick={clearQueue}\n className=\"text-muted-foreground hover:text-destructive text-xs\"\n >\n Clear all\n </button>\n </div>\n <ul className=\"space-y-1\">\n {queue.map((q, i) => (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: queue has stable order\n key={i}\n className=\"flex items-start justify-between gap-2 rounded bg-background/60 border px-2 py-1\"\n >\n <span className=\"truncate flex-1 min-w-0\">{q}</span>\n <button\n type=\"button\"\n onClick={() => removeQueued(i)}\n className=\"text-muted-foreground hover:text-destructive shrink-0\"\n title=\"Remove from queue\"\n >\n ×\n </button>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n <form\n onSubmit={handleSubmit}\n onDragEnter={(e) => {\n // Only react to drags carrying files — text/uri-list drags from\n // other parts of the page shouldn't trip the overlay.\n if (!e.dataTransfer || !Array.from(e.dataTransfer.types).includes('Files')) return;\n e.preventDefault();\n setDraggingOver(true);\n }}\n onDragOver={(e) => {\n if (!e.dataTransfer || !Array.from(e.dataTransfer.types).includes('Files')) return;\n // preventDefault on dragover is what makes the area a valid drop\n // target — without it the browser navigates to the file instead.\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n }}\n onDragLeave={(e) => {\n // dragleave fires when crossing child boundaries too; only clear if\n // the cursor genuinely left the form (relatedTarget outside or null).\n if (e.currentTarget.contains(e.relatedTarget as Node | null)) return;\n setDraggingOver(false);\n }}\n onDrop={(e) => {\n if (!e.dataTransfer) return;\n const files = Array.from(e.dataTransfer.files ?? []);\n if (files.length === 0) {\n setDraggingOver(false);\n return;\n }\n e.preventDefault();\n setDraggingOver(false);\n // Insert `@<filename>` per dropped file at the current cursor, with\n // spaces between them. Browsers strip the full path for security, so\n // we use the basename only — the FilePicker (opened by setting\n // atMention to the last inserted handle) will resolve it against\n // the workspace tree.\n const ta = textareaRef.current;\n const insertPos = ta?.selectionStart ?? input.length;\n const before = input.slice(0, insertPos);\n const after = input.slice(insertPos);\n const needsLeadingSpace = before.length > 0 && !/\\s$/.test(before);\n const lead = needsLeadingSpace ? ' ' : '';\n const tokens = files.map((f) => `@${f.name}`);\n const joined = tokens.join(' ');\n // Trailing space only when there isn't already one — keeps the\n // cursor neatly between insert and following text.\n const needsTrailingSpace = after.length === 0 || !/^\\s/.test(after);\n const trail = needsTrailingSpace ? ' ' : '';\n const insertion = `${lead}${joined}${trail}`;\n const next = before + insertion + after;\n setInput(next);\n // Open the FilePicker against the LAST dropped basename so the user\n // can replace the basename with the correctly-resolved workspace\n // path. Position the @-mention start at the `@` of the last token.\n const lastTokenStart =\n before.length +\n lead.length +\n tokens.slice(0, -1).join(' ').length +\n (tokens.length > 1 ? 1 : 0);\n const lastBasename = files[files.length - 1]!.name;\n requestAnimationFrame(() => {\n if (ta) {\n const cur = before.length + insertion.length - trail.length;\n ta.focus();\n ta.setSelectionRange(cur, cur);\n ta.style.height = 'auto';\n ta.style.height = `${Math.min(ta.scrollHeight, 200)}px`;\n }\n setAtMention({ start: lastTokenStart, query: lastBasename });\n });\n }}\n className={cn(\n 'flex items-end gap-2 relative rounded-lg transition-colors',\n draggingOver && 'ring-2 ring-primary ring-offset-2 ring-offset-background bg-primary/5',\n )}\n >\n {draggingOver && (\n <div className=\"absolute inset-0 z-20 flex items-center justify-center pointer-events-none rounded-lg bg-primary/10 text-primary text-sm font-medium\">\n Drop file{`(s)`} to attach as @-mention\n </div>\n )}\n <div className=\"relative flex-1\">\n {/* @-mention file picker — takes priority over the slash popup\n since `@` and `/` can't both be active at the cursor. */}\n {atMention && (\n <FilePicker\n query={atMention.query}\n onClose={() => setAtMention(null)}\n onPick={(p) => {\n // Replace the partial `@query` with `@<path> `, then move\n // the cursor after the inserted space so typing continues\n // naturally.\n const before = input.slice(0, atMention.start);\n const after = input.slice(atMention.start + 1 + atMention.query.length);\n const inserted = `@${p} `;\n const next = before + inserted + after;\n setInput(next);\n setAtMention(null);\n requestAnimationFrame(() => {\n const ta = textareaRef.current;\n if (ta) {\n const pos = before.length + inserted.length;\n ta.focus();\n ta.setSelectionRange(pos, pos);\n ta.style.height = 'auto';\n ta.style.height = `${Math.min(ta.scrollHeight, 200)}px`;\n }\n });\n }}\n />\n )}\n\n {/* Slash command popup — descriptions inline, ↑/↓ to select, Tab to\n autocomplete, Enter to dispatch directly. Click also works. */}\n {!atMention &&\n slashSuggestions.length > 0 &&\n (() => {\n // Bucket the suggestions by category and preserve the global\n // index across categories — the keyboard navigation (↑/↓) tracks\n // a flat index, so each rendered row needs to map back to its\n // position in the un-grouped `slashSuggestions` array.\n const byCategory: Record<string, Array<{ cmd: SlashCommandDef; idx: number }>> = {};\n slashSuggestions.forEach((cmd, idx) => {\n if (!byCategory[cmd.category]) byCategory[cmd.category] = [];\n byCategory[cmd.category]!.push({ cmd, idx });\n });\n const orderedCategories = SLASH_CATEGORY_ORDER.filter((c) => byCategory[c]?.length);\n return (\n <div className=\"absolute bottom-full left-0 right-0 mb-2 rounded-lg border bg-popover shadow-md p-1 text-sm max-h-72 overflow-auto\">\n <div className=\"px-3 py-1 text-[10px] uppercase tracking-wider text-muted-foreground border-b mb-1\">\n ↑/↓ select · Tab complete · Enter dispatch · Esc dismiss\n </div>\n {orderedCategories.map((cat) => (\n <div key={cat} className=\"mb-1\">\n <div className=\"px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-wider text-muted-foreground/70 font-semibold\">\n {cat}\n </div>\n {byCategory[cat]!.map(({ cmd, idx }) => (\n <button\n type=\"button\"\n key={cmd.name}\n onClick={() => {\n setInput('');\n runSlashCommand(cmd.name);\n }}\n onMouseEnter={() => setSlashIndex(idx)}\n className={cn(\n 'w-full text-left px-3 py-1.5 rounded transition-colors flex items-center gap-3',\n idx === slashIndex\n ? 'bg-accent text-accent-foreground'\n : 'hover:bg-accent/40',\n )}\n >\n <span className=\"font-mono shrink-0\">{cmd.name}</span>\n {cmd.aliases?.length ? (\n <span className=\"text-xs text-muted-foreground/70 font-mono shrink-0\">\n ({cmd.aliases.join(', ')})\n </span>\n ) : null}\n <span className=\"text-xs text-muted-foreground truncate\">\n — {cmd.description}\n </span>\n </button>\n ))}\n </div>\n ))}\n </div>\n );\n })()}\n <textarea\n ref={textareaRef}\n value={input}\n onChange={(e) => {\n const v = e.target.value;\n setInput(v);\n adjustTextareaHeight();\n // Manual typing drops us out of history mode so the next\n // Enter sends the user's edits, not a stale history entry.\n if (historyIdx >= 0) setHistoryIdx(-1);\n // Detect / refresh @-mention based on cursor position.\n const cur = e.target.selectionStart ?? v.length;\n setAtMention(detectAtMention(v, cur));\n }}\n onSelect={(e) => {\n const ta = e.currentTarget;\n setAtMention(detectAtMention(ta.value, ta.selectionStart));\n }}\n onKeyDown={handleKeyDown}\n onPaste={(e) => {\n // Surface a tiny hint when the user drops a chunky payload —\n // helps them realize they pasted the wrong thing (e.g. an\n // entire file when they meant a snippet). 4-second TTL.\n const text = e.clipboardData?.getData('text') ?? '';\n if (text.length > 800) {\n const lines = text.split('\\n').length;\n setPasteHint({ chars: text.length, lines });\n setTimeout(() => setPasteHint(null), 4000);\n }\n }}\n placeholder={\n !client?.isConnected\n ? 'Connect to server first…'\n : isLoading\n ? 'Agent is running — type to queue a follow-up…'\n : 'Message WrongStack… (type / for commands, @ for files)'\n }\n className={cn(\n 'flex min-h-[44px] w-full resize-none rounded-lg border border-input bg-background px-4 py-3 pr-12',\n 'text-sm ring-offset-background placeholder:text-muted-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n 'scrollbar-thin',\n )}\n rows={1}\n disabled={!client?.isConnected}\n />\n\n {input.length > 0 &&\n (() => {\n // Hide the token estimate until the draft is non-trivial — small\n // messages aren't worth a context warning, and the chip would\n // otherwise just flicker as the user types each character.\n const showTokens = input.length >= 400;\n const estTokens = Math.ceil(input.length / 4);\n // Project the next request's context usage: last sent + draft +\n // small overhead. If that crosses 85% of the configured window,\n // tint amber; past 100% turns red. Falls through to muted when\n // we don't have the window size yet (e.g. before first request).\n let tone = 'text-muted-foreground';\n let title: string | undefined;\n if (maxContext > 0 && showTokens) {\n const projected = lastInputTokens + estTokens + 64;\n const pct = (projected / maxContext) * 100;\n if (pct >= 100) {\n tone = 'text-red-600 dark:text-red-400 font-medium';\n title = `Projected ${Math.round(pct)}% of ${maxContext.toLocaleString()} ctx — will likely error or compact.`;\n } else if (pct >= 85) {\n tone = 'text-amber-600 dark:text-amber-400 font-medium';\n title = `Projected ${Math.round(pct)}% of ${maxContext.toLocaleString()} ctx — getting tight.`;\n } else {\n title = `≈ ${estTokens.toLocaleString()} tokens · projected ${Math.round(pct)}% of ${maxContext.toLocaleString()} ctx.`;\n }\n } else if (showTokens) {\n title = `≈ ${estTokens.toLocaleString()} tokens (4-char heuristic)`;\n }\n return (\n <span\n className={cn('absolute bottom-1.5 right-12 text-xs tabular-nums', tone)}\n title={title}\n >\n {input.length}\n {showTokens && (\n <span className=\"ml-1 opacity-70\">\n · ≈{estTokens >= 1000 ? `${(estTokens / 1000).toFixed(1)}k` : estTokens}t\n </span>\n )}\n </span>\n );\n })()}\n </div>\n\n <div className=\"flex gap-1\">\n {isLoading ? (\n <>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"outline\"\n onClick={handleStopAndEdit}\n className=\"h-[44px] w-[44px] rounded-lg\"\n title=\"Stop run and edit the last prompt (reuse + rewrite)\"\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"destructive\"\n onClick={handleAbort}\n className=\"h-[44px] w-[44px] rounded-lg\"\n title=\"Abort the current run\"\n >\n <Square className=\"h-4 w-4 fill-current\" />\n </Button>\n </>\n ) : (\n <Button\n type=\"submit\"\n size=\"icon\"\n disabled={!input.trim() || !client?.isConnected}\n className=\"h-[44px] w-[44px] rounded-lg\"\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n </form>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport {\n useChatStore,\n useConfigStore,\n useHistoryStore,\n useSessionStore,\n useUIStore,\n} from '@/stores';\nimport {\n ArchiveRestore,\n BarChart3,\n Brain,\n Cpu,\n Database,\n Download,\n Hash,\n History as HistoryIcon,\n type LucideIcon,\n Maximize2,\n Monitor,\n Moon,\n RotateCcw,\n Search,\n Settings as SettingsIcon,\n Sparkles,\n Stethoscope,\n Sun,\n Trash2,\n Volume2,\n VolumeX,\n Wrench,\n} from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Cross-cut search-everything overlay invoked with Ctrl/Cmd+K. Mirrors the\n * pattern from VS Code / Linear / Slack — one keyboard shortcut, one fuzzy\n * search input, one list of every action the user might want. Each entry\n * names its category (Command / Session / Theme) and an icon, plus an\n * inline \"↵\" hint on the highlighted row. Closes on Esc, Enter, or click.\n */\ninterface PaletteItem {\n id: string;\n category: 'Command' | 'Session' | 'Theme' | 'Tool';\n label: string;\n hint?: string;\n icon: LucideIcon;\n keywords?: string[];\n run: () => void;\n}\n\nexport function CommandPalette() {\n const open = useUIStore((s) => s.paletteOpen);\n const setOpen = useUIStore((s) => s.setPaletteOpen);\n const setCurrentView = useUIStore((s) => s.setCurrentView);\n const setTheme = useConfigStore((s) => s.setTheme);\n const { entries: historyEntries } = useHistoryStore();\n const { addMessage, clearMessages } = useChatStore();\n const ws = useWebSocket();\n\n const [query, setQuery] = useState('');\n const [index, setIndex] = useState(0);\n const inputRef = useRef<HTMLInputElement>(null);\n\n // Focus the search input every time we open. Defer to the next tick so\n // it actually grabs focus after the dialog has mounted.\n useEffect(() => {\n if (open) {\n setQuery('');\n setIndex(0);\n requestAnimationFrame(() => inputRef.current?.focus());\n }\n }, [open]);\n\n // Global Ctrl/Cmd+K to toggle, Esc to dismiss. Bound at body level so it\n // works from anywhere in the app, not just when the palette has focus.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {\n e.preventDefault();\n setOpen(!useUIStore.getState().paletteOpen);\n return;\n }\n if (e.key === 'Escape' && useUIStore.getState().paletteOpen) {\n e.preventDefault();\n setOpen(false);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [setOpen]);\n\n const items = useMemo<PaletteItem[]>(() => {\n const base: PaletteItem[] = [\n // Commands\n {\n id: 'help',\n category: 'Command',\n label: 'Show slash commands',\n icon: Hash,\n keywords: ['help', 'commands', '?'],\n run: () => {\n addMessage({\n role: 'assistant',\n content: 'Type `/` in the message box to see every slash command.',\n });\n },\n },\n {\n id: 'tools',\n category: 'Command',\n label: 'List tools',\n icon: Wrench,\n keywords: ['tools', 'list'],\n run: () => ws.listTools(),\n },\n {\n id: 'memory',\n category: 'Command',\n label: 'Show memory',\n icon: Brain,\n keywords: ['memory', 'remember', 'notes'],\n run: () => ws.listMemory(),\n },\n {\n id: 'skills',\n category: 'Command',\n label: 'List skills',\n icon: Sparkles,\n keywords: ['skills'],\n run: () => ws.listSkills(),\n },\n {\n id: 'diag',\n category: 'Command',\n label: 'Runtime diagnostics',\n icon: Stethoscope,\n keywords: ['diag', 'diagnostics', 'debug'],\n run: () => ws.getDiag(),\n },\n {\n id: 'stats',\n category: 'Command',\n label: 'Session stats (tokens, cache, cost)',\n icon: BarChart3,\n keywords: ['stats', 'tokens', 'cost', 'cache'],\n run: () => ws.getStats(),\n },\n // Session actions\n {\n id: 'clear',\n category: 'Session',\n label: 'Clear context',\n hint: 'Wipe in-memory context, keep session id',\n icon: Trash2,\n keywords: ['clear', 'reset', 'wipe'],\n run: () => {\n clearMessages();\n ws.client?.clearContext?.();\n },\n },\n {\n id: 'new',\n category: 'Session',\n label: 'New session',\n hint: 'Brand-new on disk + memory',\n icon: RotateCcw,\n keywords: ['new', 'fresh', 'session'],\n run: () => ws.client?.newSession?.(),\n },\n {\n id: 'compact',\n category: 'Session',\n label: 'Compact context',\n icon: Database,\n keywords: ['compact', 'shrink', 'context'],\n run: () => ws.client?.compactContext?.(),\n },\n {\n id: 'export',\n category: 'Session',\n label: 'Export chat as markdown',\n icon: Download,\n keywords: ['export', 'save', 'markdown', 'download'],\n run: () => downloadChatAsMarkdown(),\n },\n {\n id: 'export-html',\n category: 'Session',\n label: 'Export chat as HTML',\n hint: 'Self-contained, opens in any browser',\n icon: Download,\n keywords: ['export', 'html', 'download', 'archive'],\n run: () => downloadChatAsHtml(),\n },\n // Navigation\n {\n id: 'history',\n category: 'Command',\n label: 'Open history',\n icon: HistoryIcon,\n keywords: ['history', 'sessions'],\n run: () => setCurrentView('history'),\n },\n {\n id: 'settings',\n category: 'Command',\n label: 'Open settings',\n icon: SettingsIcon,\n keywords: ['settings', 'config'],\n run: () => setCurrentView('settings'),\n },\n {\n id: 'model',\n category: 'Command',\n label: 'Change provider/model',\n icon: Cpu,\n keywords: ['model', 'provider', 'change'],\n run: () => setCurrentView('settings'),\n },\n // Theme\n {\n id: 'theme-light',\n category: 'Theme',\n label: 'Theme: Light',\n icon: Sun,\n keywords: ['theme', 'light', 'mode'],\n run: () => setTheme('light'),\n },\n {\n id: 'theme-dark',\n category: 'Theme',\n label: 'Theme: Dark',\n icon: Moon,\n keywords: ['theme', 'dark', 'mode'],\n run: () => setTheme('dark'),\n },\n {\n id: 'theme-system',\n category: 'Theme',\n label: 'Theme: Follow system',\n icon: Monitor,\n keywords: ['theme', 'system', 'auto'],\n run: () => setTheme('system'),\n },\n {\n id: 'compact-toggle',\n category: 'Command',\n label: 'Toggle compact density',\n icon: Maximize2,\n hint: 'Ctrl+Shift+D',\n keywords: ['compact', 'dense', 'density', 'size'],\n run: () => useUIStore.getState().toggleCompactMode(),\n },\n {\n id: 'sound-toggle',\n category: 'Command',\n label: useConfigStore.getState().soundOnComplete\n ? 'Sound on completion: ON — turn off'\n : 'Sound on completion: OFF — turn on',\n icon: useConfigStore.getState().soundOnComplete ? Volume2 : VolumeX,\n hint: 'Chime when a run finishes',\n keywords: ['sound', 'audio', 'chime', 'notify', 'beep'],\n run: () => {\n const next = !useConfigStore.getState().soundOnComplete;\n useConfigStore.getState().setSoundOnComplete(next);\n // Play once immediately when enabling so the user hears what\n // they just signed up for (and so Web Audio gets the gesture\n // permission unlocked).\n if (next) {\n import('@/lib/chime').then((m) => m.playCompletionChime()).catch(() => {});\n }\n },\n },\n ];\n\n // Append recent sessions so the palette doubles as a \"switch to\n // session\" picker — the killer feature for multi-project use.\n for (const entry of historyEntries.slice(0, 10)) {\n if (entry.isCurrent) continue;\n base.push({\n id: `resume-${entry.id}`,\n category: 'Session',\n label: `Resume: ${entry.title || '(empty)'}`,\n hint: `${entry.provider}/${entry.model}`,\n icon: ArchiveRestore,\n keywords: ['resume', entry.title, entry.id, entry.provider, entry.model],\n run: () => ws.resumeSession(entry.id),\n });\n }\n return base;\n }, [historyEntries, ws, setCurrentView, setTheme, addMessage, clearMessages]);\n\n const filtered = useMemo(() => {\n const q = query.toLowerCase().trim();\n if (!q) return items;\n return items.filter((it) => {\n const hay = [it.label, it.hint ?? '', it.category, ...(it.keywords ?? [])]\n .join(' ')\n .toLowerCase();\n return hay.includes(q);\n });\n }, [items, query]);\n\n useEffect(() => {\n if (index >= filtered.length) setIndex(0);\n }, [filtered.length, index]);\n\n if (!open) return null;\n\n const dispatchPick = (item: PaletteItem | undefined) => {\n if (!item) return;\n setOpen(false);\n item.run();\n };\n\n return (\n <div\n className=\"fixed inset-0 z-50 bg-background/60 backdrop-blur-sm flex items-start justify-center pt-[14vh] px-4\"\n onClick={() => setOpen(false)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(false);\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n className=\"w-full max-w-2xl rounded-xl border bg-popover shadow-2xl overflow-hidden flex flex-col\"\n >\n <div className=\"flex items-center gap-2 px-4 py-3 border-b\">\n <Search className=\"h-4 w-4 text-muted-foreground\" />\n <input\n ref={inputRef}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search commands, sessions, settings…\"\n className=\"flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground\"\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setIndex((i) => (i + 1) % Math.max(1, filtered.length));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setIndex(\n (i) => (i - 1 + Math.max(1, filtered.length)) % Math.max(1, filtered.length),\n );\n } else if (e.key === 'Enter') {\n e.preventDefault();\n dispatchPick(filtered[index]);\n }\n }}\n />\n <kbd className=\"text-[10px] text-muted-foreground border rounded px-1.5 py-0.5\">Esc</kbd>\n </div>\n\n <div className=\"max-h-[60vh] overflow-y-auto\">\n {filtered.length === 0 ? (\n <div className=\"px-4 py-8 text-center text-sm text-muted-foreground\">\n No matches for \"{query}\"\n </div>\n ) : (\n renderGroupedList(filtered, index, dispatchPick, setIndex)\n )}\n </div>\n\n <div className=\"border-t px-4 py-2 text-[10px] uppercase tracking-wider text-muted-foreground flex items-center gap-3\">\n <span>↑↓ navigate</span>\n <span>↵ select</span>\n <span>Esc dismiss</span>\n </div>\n </div>\n </div>\n );\n}\n\nfunction renderGroupedList(\n filtered: PaletteItem[],\n index: number,\n dispatch: (it: PaletteItem) => void,\n setIndex: (i: number) => void,\n) {\n // Maintain global filtered-index as we walk, so the highlighted row\n // matches what arrow keys point at. Grouping is visual sugar only.\n const groups: Record<string, Array<{ item: PaletteItem; globalIdx: number }>> = {};\n filtered.forEach((it, i) => {\n if (!groups[it.category]) groups[it.category] = [];\n groups[it.category]!.push({ item: it, globalIdx: i });\n });\n return (\n <div className=\"p-1\">\n {Object.entries(groups).map(([cat, rows]) => (\n <div key={cat}>\n <div className=\"px-3 pt-2 pb-1 text-[10px] uppercase tracking-wider text-muted-foreground\">\n {cat}\n </div>\n {rows.map(({ item, globalIdx }) => {\n const Icon = item.icon;\n const active = globalIdx === index;\n return (\n <button\n type=\"button\"\n key={item.id}\n onMouseEnter={() => setIndex(globalIdx)}\n onClick={() => dispatch(item)}\n className={cn(\n 'w-full flex items-center gap-3 px-3 py-2 rounded text-left text-sm transition-colors',\n active ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/40',\n )}\n >\n <Icon className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <div className=\"truncate\">{item.label}</div>\n {item.hint && (\n <div className=\"text-xs text-muted-foreground truncate\">{item.hint}</div>\n )}\n </div>\n {active && <span className=\"text-[10px] text-muted-foreground\">↵</span>}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * Build a markdown export of the current chat and trigger a browser\n * download. Includes user/assistant turns and a compact summary of tool\n * calls inline so the transcript stays readable but you can still see\n * which tools the agent invoked.\n */\nexport function downloadChatAsMarkdown(): void {\n const messages = useChatStore.getState().messages;\n const session = useChatStore.getState();\n void session;\n const lines: string[] = [];\n const now = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');\n lines.push(`# WrongStack chat export`);\n lines.push(`*Exported: ${new Date().toISOString()}*`);\n lines.push('');\n for (const m of messages) {\n if (m.role === 'user') {\n lines.push(`## 👤 User`);\n lines.push('');\n lines.push(m.content);\n lines.push('');\n } else if (m.role === 'assistant') {\n lines.push(`## 🤖 Assistant`);\n lines.push('');\n lines.push(m.content);\n lines.push('');\n } else if (m.role === 'tool') {\n const status = m.isError ? '❌' : m.toolResult !== undefined ? '✅' : '⏳';\n lines.push(`### 🔧 Tool: \\`${m.toolName ?? 'unknown'}\\` ${status}`);\n if (m.toolInput !== undefined) {\n lines.push('```json');\n lines.push(JSON.stringify(m.toolInput, null, 2));\n lines.push('```');\n }\n if (m.toolResult) {\n lines.push('<details><summary>Output</summary>');\n lines.push('');\n lines.push('```');\n lines.push(m.toolResult);\n lines.push('```');\n lines.push('</details>');\n }\n lines.push('');\n }\n }\n const blob = new Blob([lines.join('\\n')], { type: 'text/markdown' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `wrongstack-chat-${now}.md`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Build a stand-alone HTML export of the current chat. Self-contained\n * (inline CSS, no external assets) so the file opens cleanly anywhere\n * and survives being emailed / pasted into a wiki / archived offline.\n * Code-block highlighting is intentionally not included — would require\n * shipping a syntax highlighter, and the export is already readable\n * with the basic monospace styling.\n */\nexport function downloadChatAsHtml(): void {\n const messages = useChatStore.getState().messages;\n const session = useSessionStore.getState();\n const now = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');\n const escapeHtml = (s: string) =>\n s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n\n const turns = messages.map((m) => {\n if (m.role === 'tool') {\n const status = m.isError ? '❌' : m.toolResult !== undefined ? '✅' : '⏳';\n return `\n <section class=\"bubble tool ${m.isError ? 'error' : ''}\">\n <header><span class=\"icon\">🔧</span><code>${escapeHtml(m.toolName ?? 'tool')}</code> ${status}</header>\n ${\n m.toolInput !== undefined\n ? `<details><summary>Input</summary><pre>${escapeHtml(JSON.stringify(m.toolInput, null, 2))}</pre></details>`\n : ''\n }\n ${\n m.toolResult\n ? `<details><summary>Output</summary><pre>${escapeHtml(m.toolResult)}</pre></details>`\n : ''\n }\n </section>`;\n }\n const cls = m.role === 'user' ? 'user' : 'assistant';\n const icon = m.role === 'user' ? '👤' : '🤖';\n const role = m.role === 'user' ? 'User' : 'Assistant';\n // Keep newlines but escape everything — we deliberately don't render\n // markdown here. Static HTML with preserved whitespace is faithful to\n // what the user actually saw, and dodges the whole \"rendered markdown\n // syntax-highlighting drift\" problem.\n return `\n <section class=\"bubble ${cls}\">\n <header><span class=\"icon\">${icon}</span><strong>${role}</strong></header>\n <pre class=\"content\">${escape(m.content)}</pre>\n </section>`;\n });\n\n const html = `<!doctype html>\n<html lang=\"en\"><head>\n<meta charset=\"utf-8\">\n<title>WrongStack chat — ${escape(session.session?.title || session.projectName || 'export')}</title>\n<style>\n :root { color-scheme: light dark; }\n body { font: 14px/1.55 -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; max-width: 920px; margin: 24px auto; padding: 0 16px; }\n h1 { font-size: 20px; margin-bottom: 4px; }\n .meta { color: #666; font-size: 12px; margin-bottom: 24px; }\n .bubble { margin: 12px 0; padding: 10px 14px; border-radius: 10px; border: 1px solid #ddd; }\n .bubble header { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: #666; margin-bottom: 6px; }\n .bubble header .icon { margin-right: 4px; }\n .bubble.user { background: #eef4ff; border-color: #c8d8f5; }\n .bubble.assistant { background: #fff; }\n .bubble.tool { background: #fafafa; }\n .bubble.tool.error { background: #fff5f5; border-color: #f5c8c8; }\n pre.content, .bubble pre { white-space: pre-wrap; word-break: break-word; font: 12px/1.5 ui-monospace, Menlo, Consolas, monospace; margin: 0; }\n details summary { cursor: pointer; color: #555; font-size: 12px; }\n details pre { margin-top: 6px; background: #f4f4f4; padding: 8px; border-radius: 6px; max-height: 360px; overflow: auto; }\n @media (prefers-color-scheme: dark) {\n body { background: #0d0d0f; color: #e6e6e6; }\n .bubble { border-color: #2a2a2e; }\n .bubble.user { background: #16213a; border-color: #2a3d6b; }\n .bubble.assistant { background: #161618; }\n .bubble.tool { background: #131315; }\n .bubble.tool.error { background: #2a1717; border-color: #5c2a2a; }\n details pre { background: #1a1a1c; }\n .meta, .bubble header, details summary { color: #999; }\n }\n</style>\n</head><body>\n<h1>WrongStack chat — ${escape(session.session?.title || session.projectName || 'export')}</h1>\n<div class=\"meta\">\n Exported ${new Date().toISOString()}${session.session?.provider ? ` · ${escape(session.session.provider)}/${escape(session.session.model)}` : ''} · ${messages.length} message${messages.length === 1 ? '' : 's'}\n</div>\n${turns.join('')}\n</body></html>`;\n const blob = new Blob([html], { type: 'text/html;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `wrongstack-chat-${now}.html`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore } from '@/stores';\nimport { FileText, Folder } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\ninterface FilePickerProps {\n /** Whatever the user typed after the `@` trigger (case-preserved). */\n query: string;\n /** Called when the user picks a file (Enter / Tab / click). */\n onPick: (relPath: string) => void;\n /** Called when the picker should close without inserting (Esc). */\n onClose: () => void;\n}\n\n/**\n * `@` file mention popup. Subscribes to the WS `files.list` response,\n * supports ↑/↓ Tab Enter Esc. Refetches on query change with a small\n * debounce so we don't spam the backend on every keystroke.\n */\nexport function FilePicker({ query, onPick, onClose }: FilePickerProps) {\n const ws = useWebSocket();\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const [files, setFiles] = useState<string[]>([]);\n const [index, setIndex] = useState(0);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const wantHandle = useRef<{ resolve: (paths: string[]) => void } | null>(null);\n\n // Subscribe to the single `files.list` event for the lifetime of this\n // picker. We use the raw client.on() rather than the hook because the\n // hook installs handlers at boot — but `files.list` is picker-local and\n // the data shouldn't leak into other surfaces.\n useEffect(() => {\n const client = getWSClient(wsUrl);\n const off = client.on('files.list', (msg) => {\n const p = msg.payload as { files: string[] };\n setFiles(p.files ?? []);\n setIndex(0);\n wantHandle.current?.resolve(p.files ?? []);\n wantHandle.current = null;\n });\n return () => off();\n }, [wsUrl]);\n\n // Debounced fetch on query change.\n useEffect(() => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(() => {\n ws.client.listFiles(query, 50);\n }, 80);\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n };\n }, [query, ws.client]);\n\n // Keyboard nav at window level — ChatInput owns the textarea so we\n // intercept keys there and forward intent here via the imperative API.\n // We listen too so click+keyboard mixing works.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setIndex((i) => (i + 1) % Math.max(1, files.length));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setIndex((i) => (i - 1 + Math.max(1, files.length)) % Math.max(1, files.length));\n } else if (e.key === 'Enter' || e.key === 'Tab') {\n if (files.length === 0) return;\n e.preventDefault();\n onPick(files[index]!);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [files, index, onPick, onClose]);\n\n return (\n <div className=\"absolute bottom-full left-0 right-0 mb-2 rounded-lg border bg-popover shadow-md p-1 text-sm max-h-72 overflow-auto\">\n <div className=\"px-3 py-1 text-[10px] uppercase tracking-wider text-muted-foreground border-b mb-1 flex items-center justify-between\">\n <span>@ Files {query && `· \"${query}\"`}</span>\n <span>↑/↓ select · ↵ insert · Esc dismiss</span>\n </div>\n {files.length === 0 ? (\n <div className=\"px-3 py-2 text-xs text-muted-foreground italic\">\n {query ? `No files match \"${query}\"` : 'Searching project…'}\n </div>\n ) : (\n files.map((p, i) => (\n <button\n key={p}\n type=\"button\"\n onClick={() => onPick(p)}\n onMouseEnter={() => setIndex(i)}\n className={cn(\n 'w-full text-left px-3 py-1.5 rounded transition-colors flex items-center gap-2 font-mono text-xs',\n i === index ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/40',\n )}\n >\n {p.includes('/') ? (\n <Folder className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n ) : (\n <FileText className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n )}\n <span className=\"truncate\">{p}</span>\n </button>\n ))\n )}\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { Slot } from '@radix-ui/react-slot';\nimport { type VariantProps, cva } from 'class-variance-authority';\nimport * as React from 'react';\n\nconst buttonVariants = cva(\n 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n ghost: 'hover:bg-accent hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 hover:underline',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : 'button';\n return (\n <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />\n );\n },\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n","import { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport type { WsStatus } from '@/lib/ws-client';\nimport { useConfigStore } from '@/stores';\nimport { Loader2, RotateCcw, Wifi, WifiOff } from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\ninterface Props {\n wsStatus: WsStatus;\n wsConnected: boolean;\n}\n\n/**\n * Tiny status pill for the topbar. Renders four distinct visual states:\n *\n * • open — solid green Wi-Fi\n * • connecting — pulsing yellow spinner\n * • reconnecting — orange, attempt counter + live retry countdown,\n * click to retry immediately\n * • closed — red Wi-Fi-off, click to retry, tooltip shows error\n *\n * The countdown ticks live (every 500ms while `nextRetryAt` is in the\n * future) without forcing the whole topbar to re-render — we only re-run\n * the local timer effect.\n */\nexport function ConnectionChip({ wsStatus, wsConnected }: Props) {\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const [now, setNow] = useState(Date.now());\n\n // Keep a live clock running only while we're between retries, otherwise\n // we'd burn one render every 500ms in a happy-path session.\n useEffect(() => {\n if (wsStatus.state !== 'reconnecting') return;\n const id = setInterval(() => setNow(Date.now()), 500);\n return () => clearInterval(id);\n }, [wsStatus.state]);\n\n const retry = () => getWSClient(wsUrl).retryNow();\n\n if (wsStatus.state === 'open' && wsConnected) {\n return (\n <div\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 bg-green-500/10 text-green-600 dark:text-green-400\"\n title=\"Backend connected\"\n >\n <Wifi className=\"h-3 w-3\" />\n </div>\n );\n }\n\n if (wsStatus.state === 'connecting') {\n return (\n <div\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 bg-yellow-500/10 text-yellow-600 dark:text-yellow-400\"\n title=\"Connecting to backend\"\n >\n <Loader2 className=\"h-3 w-3 animate-spin\" />\n <span>connecting</span>\n </div>\n );\n }\n\n if (wsStatus.state === 'reconnecting') {\n const remaining = Math.max(0, Math.ceil((wsStatus.nextRetryAt - now) / 1000));\n return (\n <button\n type=\"button\"\n onClick={retry}\n className={cn(\n 'flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0',\n 'bg-orange-500/10 text-orange-600 dark:text-orange-400 hover:bg-orange-500/20',\n 'transition-colors',\n )}\n title={\n wsStatus.lastError\n ? `Reconnecting — last error: ${wsStatus.lastError}. Click to retry now.`\n : 'Reconnecting — click to retry now.'\n }\n >\n <Loader2 className=\"h-3 w-3 animate-spin\" />\n <span>\n retry #{wsStatus.attempt} in {remaining}s\n </span>\n </button>\n );\n }\n\n // closed\n return (\n <button\n type=\"button\"\n onClick={retry}\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 bg-red-500/10 text-red-600 dark:text-red-400 hover:bg-red-500/20 transition-colors\"\n title={\n wsStatus.state === 'closed' && wsStatus.error\n ? `Disconnected: ${wsStatus.error}. Click to retry.`\n : 'Disconnected. Click to retry.'\n }\n >\n <WifiOff className=\"h-3 w-3\" />\n <span className=\"flex items-center gap-0.5\">\n offline\n <RotateCcw className=\"h-3 w-3 opacity-70\" />\n </span>\n </button>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore } from '@/stores';\nimport { useEffect, useRef, useState } from 'react';\n\n/**\n * Clickable cost figure in the topbar. Tap to drop a small popover with a\n * per-turn breakdown sourced from `ChatMessage.runSummary` (attached by the\n * run.result handler). Helps the cost-conscious user trace where the\n * dollars actually went without spelunking through transcripts.\n *\n * We only render the popover after the user clicks — the cost chip itself\n * is identical to the plain span it replaces, so the topbar layout stays\n * unchanged.\n */\nexport function CostChip() {\n const cost = useSessionStore((s) => s.cost);\n const inputCost = useSessionStore((s) => s.inputCost);\n const outputCost = useSessionStore((s) => s.outputCost);\n const cacheReadCost = useSessionStore((s) => s.cacheReadCost);\n const messages = useChatStore((s) => s.messages);\n const [open, setOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (!rootRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('mousedown', onClick);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('mousedown', onClick);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n /** Top expensive turns. We only consider messages with a runSummary —\n * the run.result handler attaches one per completed turn. costDelta is\n * the delta computed in that handler (session cost at end minus at\n * start of this turn). Falls back to nothing if no data yet. */\n const turns = (() => {\n const rows: Array<{\n id: string;\n preview: string;\n cost: number;\n tools: number;\n ms: number;\n ts: number;\n }> = [];\n for (const m of messages) {\n if (m.role === 'assistant' && m.runSummary && m.runSummary.costDelta > 0) {\n rows.push({\n id: m.id,\n preview: m.content.slice(0, 80).replace(/\\s+/g, ' ').trim() || '(empty)',\n cost: m.runSummary.costDelta,\n tools: m.runSummary.tools,\n ms: m.runSummary.durationMs,\n ts: m.timestamp,\n });\n }\n }\n rows.sort((a, b) => b.cost - a.cost);\n return rows.slice(0, 5);\n })();\n\n const fmt$ = (v: number) =>\n v >= 0.01\n ? `$${v.toFixed(4)}`\n : v > 0\n ? `$${v.toFixed(6).replace(/0+$/, '').replace(/\\.$/, '')}`\n : '$0';\n\n const haveRates = inputCost > 0 || outputCost > 0;\n\n return (\n <div ref={rootRef} className=\"relative inline-block\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n 'font-medium text-green-600 dark:text-green-400 hover:underline tabular-nums',\n )}\n title=\"Click for per-turn cost breakdown\"\n >\n ${cost.toFixed(4)}\n </button>\n {open && (\n <div className=\"absolute right-0 top-full mt-1 z-30 w-80 rounded-md border bg-popover shadow-lg p-3 text-foreground\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Cost breakdown\n </span>\n <span className=\"font-mono tabular-nums text-sm font-semibold text-green-600 dark:text-green-400\">\n {fmt$(cost)}\n </span>\n </div>\n {haveRates ? (\n <div className=\"text-[11px] text-muted-foreground font-mono mb-3 border-b pb-2\">\n <div className=\"flex justify-between\">\n <span>input /1M</span>\n <span>${inputCost.toFixed(2)}</span>\n </div>\n <div className=\"flex justify-between\">\n <span>output /1M</span>\n <span>${outputCost.toFixed(2)}</span>\n </div>\n {cacheReadCost > 0 && (\n <div className=\"flex justify-between\">\n <span>cache /1M</span>\n <span>${cacheReadCost.toFixed(2)}</span>\n </div>\n )}\n </div>\n ) : (\n <div className=\"text-[11px] text-muted-foreground italic mb-3 border-b pb-2\">\n No pricing for the current model — cost figures are zero.\n </div>\n )}\n {turns.length === 0 ? (\n <div className=\"text-xs text-muted-foreground italic\">\n No completed turns yet. Run a prompt to see per-turn cost here.\n </div>\n ) : (\n <>\n <div className=\"text-[10px] uppercase tracking-wider text-muted-foreground/80 mb-1\">\n Top {turns.length} expensive turn{turns.length === 1 ? '' : 's'}\n </div>\n <ul className=\"space-y-1\">\n {turns.map((t) => (\n <li key={t.id}>\n <button\n type=\"button\"\n onClick={() => {\n const el = document.querySelector(`[data-message-id=\"${t.id}\"]`);\n if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n setOpen(false);\n }}\n className=\"w-full text-left rounded px-2 py-1.5 hover:bg-accent/40 transition-colors\"\n >\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-xs truncate\">{t.preview}</span>\n <span className=\"text-xs font-mono tabular-nums text-green-600 dark:text-green-400 shrink-0\">\n {fmt$(t.cost)}\n </span>\n </div>\n <div className=\"text-[10px] text-muted-foreground font-mono mt-0.5\">\n {t.tools} tool{t.tools === 1 ? '' : 's'} · {(t.ms / 1000).toFixed(1)}s\n </div>\n </button>\n </li>\n ))}\n </ul>\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Tool-aware one-line summary for the collapsed bubble. The whole point is\n * that 8 parallel `read` calls should NOT look identical, and a TodoWrite\n * call with 8 todos shouldn't dump 800 chars of JSON when \"8 todos · 3\n * done\" tells the whole story.\n *\n * Cases that earn a custom branch:\n * - TodoWrite → \"8 todos · 3 done · 2 in-progress\"\n * - edit / write → \"edit foo.ts (3 lines → 5 lines)\" / \"write …\"\n * - bash / shell → \"$ <command snippet>\"\n * - fetch / http → \"GET https://…\"\n * - grep / glob → pattern + scope\n * - batch_tool_use → \"N sub-tools\"\n * - read → \"path:N..M\" if offset/limit present\n *\n * Everything else falls back to the head-field-or-JSON heuristic the\n * collapsed view used before.\n */\n\nconst FALLBACK_HEAD_FIELDS = [\n 'path',\n 'file_path',\n 'pattern',\n 'command',\n 'cmd',\n 'url',\n 'query',\n 'description',\n 'content',\n];\n\nexport function summarizeToolInput(toolName: string | undefined, input: unknown): string {\n if (input === null || input === undefined) return '';\n if (typeof input !== 'object') return clip(String(input), 120);\n\n const obj = input as Record<string, unknown>;\n const name = (toolName ?? '').toLowerCase();\n\n // ---- TodoWrite ----------------------------------------------------\n if (/^todo(_?write)?$|^todos$/i.test(name) || Array.isArray(obj.todos)) {\n const todos = (obj.todos ?? []) as Array<{ status?: string; content?: string }>;\n if (Array.isArray(todos)) {\n const done = todos.filter((t) => t.status === 'completed').length;\n const wip = todos.filter((t) => t.status === 'in_progress').length;\n const parts = [`${todos.length} todo${todos.length === 1 ? '' : 's'}`];\n if (done > 0) parts.push(`${done} done`);\n if (wip > 0) parts.push(`${wip} in-progress`);\n return parts.join(' · ');\n }\n }\n\n // ---- batch_tool_use / parallel_tool_use ---------------------------\n if (/batch|parallel/.test(name) || Array.isArray(obj.tool_uses) || Array.isArray(obj.calls)) {\n const list = (obj.tool_uses ?? obj.calls ?? obj.batch) as unknown[];\n if (Array.isArray(list)) {\n const subNames = new Set<string>();\n for (const item of list) {\n if (item && typeof item === 'object' && 'name' in item) {\n subNames.add(String((item as { name: unknown }).name));\n }\n }\n const preview = [...subNames].slice(0, 3).join(', ');\n const more = subNames.size > 3 ? ` +${subNames.size - 3}` : '';\n return `${list.length} sub-tool${list.length === 1 ? '' : 's'}${preview ? ` · ${preview}${more}` : ''}`;\n }\n }\n\n // ---- edit / str_replace / patch -----------------------------------\n if (/^(edit|str_replace|edit_file|patch)$/.test(name)) {\n const fp = pickPath(obj);\n const oldS = typeof obj.old_string === 'string' ? obj.old_string : '';\n const newS = typeof obj.new_string === 'string' ? obj.new_string : '';\n const oldLines = oldS ? oldS.split('\\n').length : 0;\n const newLines = newS ? newS.split('\\n').length : 0;\n return `edit ${fp || '(file)'}${oldLines || newLines ? ` (${oldLines} → ${newLines} lines)` : ''}`;\n }\n\n // ---- write / write_file / create_file -----------------------------\n if (/^(write|write_file|create_file|new_file)$/.test(name)) {\n const fp = pickPath(obj);\n const c = typeof obj.content === 'string' ? obj.content : '';\n const lines = c ? c.split('\\n').length : 0;\n return `write ${fp || '(file)'}${lines ? ` · ${lines} lines` : ''}`;\n }\n\n // ---- bash / shell / exec / run ------------------------------------\n if (/^(bash|shell|exec|run|run_command|run_shell)$/.test(name)) {\n const cmd = (obj.command ?? obj.cmd ?? obj.script) as string | undefined;\n if (typeof cmd === 'string') return `$ ${clip(cmd, 110)}`;\n }\n\n // ---- fetch / http / web -------------------------------------------\n if (/^(fetch|http|web|webfetch|curl|request)$/.test(name)) {\n const url = obj.url as string | undefined;\n if (typeof url === 'string') {\n const method = (obj.method as string | undefined) ?? 'GET';\n return `${method.toUpperCase()} ${clip(url, 100)}`;\n }\n }\n\n // ---- grep / search ------------------------------------------------\n if (/^(grep|search|ripgrep)$/.test(name)) {\n const pattern = obj.pattern as string | undefined;\n const scope = (obj.path ?? obj.glob ?? obj.type) as string | undefined;\n if (typeof pattern === 'string') {\n return scope ? `grep ${clip(pattern, 60)} in ${scope}` : `grep ${clip(pattern, 100)}`;\n }\n }\n\n // ---- glob / find --------------------------------------------------\n if (/^(glob|find)$/.test(name)) {\n const p = (obj.pattern ?? obj.glob) as string | undefined;\n if (typeof p === 'string') return `glob ${clip(p, 100)}`;\n }\n\n // ---- read with offset/limit --------------------------------------\n if (/^(read|read_file|cat)$/.test(name)) {\n const fp = pickPath(obj);\n const offset = obj.offset as number | undefined;\n const limit = obj.limit as number | undefined;\n if (fp && (typeof offset === 'number' || typeof limit === 'number')) {\n const start = offset ?? 0;\n const end = typeof limit === 'number' ? start + limit : '';\n return `read ${fp} (${start}…${end})`;\n }\n if (fp) return `read ${fp}`;\n }\n\n // ---- Fallback: pick the first non-empty \"headline\" string field --\n for (const k of FALLBACK_HEAD_FIELDS) {\n const v = obj[k];\n if (typeof v === 'string' && v.length > 0) {\n return `${k}: ${clip(v, 100)}`;\n }\n }\n // Last resort: compact JSON.\n const json = safeJson(input);\n return clip(json, 120);\n}\n\nfunction clip(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n - 1)}…` : s;\n}\n\nfunction pickPath(obj: Record<string, unknown>): string {\n const p = obj.file_path ?? obj.path ?? obj.filepath;\n return typeof p === 'string' ? p : '';\n}\n\nfunction safeJson(v: unknown): string {\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n","import { summarizeToolInput } from '@/lib/tool-summary';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport type { ChatMessage } from '@/stores';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport { useConfigStore } from '@/stores';\nimport {\n Bot,\n Check,\n CheckCircle2,\n ChevronDown,\n ChevronRight,\n Clock,\n Copy,\n Download,\n FileCode2,\n Pencil,\n Pin,\n PinOff,\n RotateCcw,\n Terminal,\n User,\n XCircle,\n} from 'lucide-react';\nimport type React from 'react';\nimport { useState } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport { DiffView, diffFromToolInput } from './DiffView';\nimport { ToolResult } from './ToolResult';\n\n/**\n * Tiny copy-to-clipboard helper used by the in-bubble copy buttons. Falls\n * back to the legacy `document.execCommand('copy')` path on insecure\n * (non-HTTPS, non-localhost) contexts where `navigator.clipboard` is\n * blocked — the WebUI is usually loaded from 127.0.0.1 over plain http so\n * we hit this fallback regularly.\n */\nasync function copyToClipboard(text: string): Promise<boolean> {\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return true;\n }\n } catch {\n /* fall through */\n }\n try {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n const ok = document.execCommand('copy');\n document.body.removeChild(ta);\n return ok;\n } catch {\n return false;\n }\n}\n\n/**\n * ReactMarkdown component overrides. Fenced code blocks render with a\n * header strip (language label + copy button) and an internally scrollable\n * body so a 200-line snippet doesn't blow up the chat. Inline `code` stays\n * styled simply. Kept at module scope so the components object reference is\n * stable across renders.\n */\nconst markdownComponents = {\n code({\n inline,\n className,\n children,\n ...props\n }: {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n }) {\n const match = /language-(\\w+)/.exec(className ?? '');\n const codeText = String(children ?? '').replace(/\\n$/, '');\n if (inline || !match) {\n return (\n <code\n className={cn('rounded bg-muted/60 px-1.5 py-0.5 text-[0.85em] font-mono', className)}\n {...props}\n >\n {children}\n </code>\n );\n }\n return (\n <div className=\"not-prose relative my-3 rounded-lg border bg-muted/30 overflow-hidden\">\n <div className=\"flex items-center justify-between px-3 py-1.5 border-b bg-muted/40 text-xs\">\n <span className=\"font-mono text-muted-foreground\">{match[1]}</span>\n <CopyButton text={codeText} label=\"\" />\n </div>\n <pre className=\"overflow-x-auto p-3 text-xs leading-relaxed font-mono max-h-[40rem]\">\n <code>{codeText}</code>\n </pre>\n </div>\n );\n },\n};\n\nfunction CopyButton({\n text,\n className,\n label = 'Copy',\n}: {\n text: string;\n className?: string;\n label?: string;\n}) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n onClick={async (ev) => {\n ev.stopPropagation();\n const ok = await copyToClipboard(text);\n if (ok) {\n setCopied(true);\n setTimeout(() => setCopied(false), 1400);\n }\n }}\n className={cn(\n 'inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors',\n className,\n )}\n title={label}\n >\n {copied ? (\n <>\n <Check className=\"h-3 w-3 text-green-500\" />\n <span>Copied</span>\n </>\n ) : (\n <>\n <Copy className=\"h-3 w-3\" />\n <span>{label}</span>\n </>\n )}\n </button>\n );\n}\n\n/**\n * Spawn a browser download for the given text content. Uses a transient\n * Blob URL so the disk hit is browser-managed and we don't have to pump\n * the bytes through a backend route. Best-effort: no-op in non-browser\n * test environments.\n */\nfunction downloadTextFile(filename: string, text: string): void {\n if (typeof document === 'undefined') return;\n const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\n/**\n * Pick a reasonable file extension for a tool output dump based on the\n * tool name. JSON-shaped tools get `.json`, Read gets `.txt` (line numbers\n * baked in mean it isn't valid source anymore), bash gets `.log`,\n * everything else falls back to `.txt`.\n */\nfunction fileExtensionFor(toolName: string | undefined): string {\n const t = (toolName ?? '').toLowerCase();\n if (/bash|shell|exec|run/.test(t)) return 'log';\n if (/grep|search|find/.test(t)) return 'txt';\n return 'txt';\n}\n\n/**\n * Heuristic for \"the assistant just dumped a stack trace at me\". Returns\n * the index where the stack begins or -1 if none. We're tolerant of the\n * three common shapes: Node V8 (` at fn (file:line:col)`), Python\n * (`File \"x\", line N`), and Java (`at pkg.Class.method(File.java:N)`).\n * False positives are cheap (user just hits \"Show stack\" anyway).\n */\nfunction detectStackBoundary(text: string): number {\n // Look for the first line that matches a stack-frame pattern.\n const lines = text.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i]!;\n if (/^\\s*at\\s+\\S+.*\\(.*:\\d+:\\d+\\)\\s*$/.test(ln)) return i;\n if (/^\\s*at\\s+\\S+\\.\\S+\\(\\S+\\.java:\\d+\\)\\s*$/.test(ln)) return i;\n if (/^\\s+File \"[^\"]+\", line \\d+/.test(ln)) return i;\n }\n return -1;\n}\n\n/**\n * Wraps an error-flavoured assistant body and offers a \"Show/hide stack\"\n * toggle when a stack-trace boundary is detected. The lead message\n * (everything before the first frame) stays visible; the frames go\n * behind a click. Reduces the visual weight of a long traceback that\n * the user usually only needs once.\n */\nfunction ErrorBodyWithStack({ text }: { text: string }) {\n const idx = detectStackBoundary(text);\n const [open, setOpen] = useState(false);\n if (idx === -1) {\n return (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs leading-relaxed\">\n {text}\n </pre>\n );\n }\n const lines = text.split('\\n');\n const head = lines.slice(0, idx).join('\\n').trim();\n const stack = lines.slice(idx).join('\\n');\n const frameCount = stack.split('\\n').filter((l) => l.trim().length > 0).length;\n return (\n <div className=\"space-y-2\">\n {head && (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs leading-relaxed\">\n {head}\n </pre>\n )}\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"inline-flex items-center gap-1 text-xs text-destructive hover:text-destructive/80 font-medium\"\n >\n {open ? '▾' : '▸'} {open ? 'Hide' : 'Show'} stack trace ({frameCount} frame\n {frameCount === 1 ? '' : 's'})\n </button>\n {open && (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-[11px] leading-snug bg-destructive/5 border border-destructive/20 rounded p-2 max-h-80 overflow-auto\">\n {stack}\n </pre>\n )}\n </div>\n );\n}\n\n/**\n * Render a tool-call's input as a structured key/value table instead of a\n * raw JSON dump. Shallow scalars land on one row each; nested values land\n * as a collapsible row that expands into pretty-printed JSON. Falls back\n * to a single JSON block when the input is not an object (e.g. a number\n * or a string), which keeps the layout sensible for tools whose schema\n * isn't `Record<string, unknown>`.\n */\nfunction ToolInputView({ input }: { input: unknown }) {\n const [openKeys, setOpenKeys] = useState<Record<string, boolean>>({});\n if (input === null || input === undefined || typeof input !== 'object' || Array.isArray(input)) {\n return (\n <pre className=\"whitespace-pre-wrap break-all text-xs font-mono\">\n {JSON.stringify(input, null, 2)}\n </pre>\n );\n }\n const entries = Object.entries(input as Record<string, unknown>);\n if (entries.length === 0) {\n return <span className=\"text-xs text-muted-foreground italic\">(no params)</span>;\n }\n return (\n <div className=\"text-xs font-mono\">\n {entries.map(([k, v]) => {\n const isPrimitive =\n v === null ||\n v === undefined ||\n typeof v === 'string' ||\n typeof v === 'number' ||\n typeof v === 'boolean';\n if (isPrimitive) {\n const display =\n v === null\n ? 'null'\n : v === undefined\n ? 'undefined'\n : typeof v === 'string'\n ? v\n : String(v);\n // Long string values get their own line so the row stays usable.\n const isLong = typeof v === 'string' && (display.length > 80 || display.includes('\\n'));\n return (\n <div\n key={k}\n className={cn(\n 'py-0.5',\n isLong ? 'flex flex-col gap-0.5' : 'flex items-baseline gap-2',\n )}\n >\n <span className=\"text-muted-foreground shrink-0\">{k}:</span>\n <span\n className={cn(\n 'text-foreground',\n isLong\n ? 'whitespace-pre-wrap break-all bg-muted/40 rounded px-1.5 py-1'\n : 'truncate',\n typeof v === 'string' ? '' : 'text-amber-600 dark:text-amber-400',\n )}\n title={typeof v === 'string' && !isLong ? display : undefined}\n >\n {display}\n </span>\n </div>\n );\n }\n const open = !!openKeys[k];\n const summary = Array.isArray(v)\n ? `[${v.length} item${v.length === 1 ? '' : 's'}]`\n : `{${Object.keys(v as object).length} key${Object.keys(v as object).length === 1 ? '' : 's'}}`;\n return (\n <div key={k} className=\"py-0.5\">\n <button\n type=\"button\"\n onClick={() => setOpenKeys((p) => ({ ...p, [k]: !p[k] }))}\n className=\"flex items-baseline gap-2 hover:bg-muted/30 rounded px-1 -mx-1\"\n >\n <span className=\"text-muted-foreground/60 text-[10px]\">{open ? '▾' : '▸'}</span>\n <span className=\"text-muted-foreground\">{k}:</span>\n <span className=\"text-violet-600 dark:text-violet-400\">{summary}</span>\n </button>\n {open && (\n <pre className=\"ml-3 mt-1 whitespace-pre-wrap break-all text-[11px] bg-muted/40 rounded px-2 py-1.5\">\n {JSON.stringify(v, null, 2)}\n </pre>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n\ninterface MessageBubbleProps {\n message: ChatMessage;\n isFirst?: boolean;\n /** Render as a continuation of the previous item in the same agent turn.\n * Hides the avatar (replaces it with a transparent spacer so content\n * stays aligned) and the role label, and tightens the top margin — used\n * by ChatView's turn-bundling so text→tool→text reads as one flow\n * instead of three detached bubbles each with its own avatar column. */\n isContinuation?: boolean;\n}\n\nfunction formatToolDuration(ms: number): string {\n if (ms < 1000) return `${ms} ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(ms < 10_000 ? 2 : 1)}s`;\n const m = Math.floor(ms / 60_000);\n const s = Math.floor((ms % 60_000) / 1000);\n return `${m}m ${s}s`;\n}\n\nexport function MessageBubble({\n message,\n isFirst = false,\n isContinuation = false,\n}: MessageBubbleProps) {\n const [expandedTools, setExpandedTools] = useState<Record<string, boolean>>({});\n const [editing, setEditing] = useState(false);\n const [editValue, setEditValue] = useState('');\n /** Per-bubble \"show raw markdown\" toggle for assistant messages. Useful\n * when the model emits weird-looking markdown and you want to verify\n * what it actually wrote (escape sequences, table cells, etc.) versus\n * how ReactMarkdown is rendering it. Hover-revealed toggle in the\n * footer; off by default so the chat stays pleasant to read. */\n const [showRaw, setShowRaw] = useState(false);\n const isUser = message.role === 'user';\n const isTool = message.role === 'tool';\n // (kept for symmetry — isAssistant referenced indirectly via !isUser && !isTool)\n void message.role;\n\n const truncateAfter = useChatStore((s) => s.truncateAfter);\n const addMessage = useChatStore((s) => s.addMessage);\n const setLoading = useChatStore((s) => s.setLoading);\n const isLoading = useChatStore((s) => s.isLoading);\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const pinnedIds = useUIStore((s) => s.pinnedIds);\n const togglePin = useUIStore((s) => s.togglePin);\n const compactMode = useUIStore((s) => s.compactMode);\n const isPinned = pinnedIds.includes(message.id);\n /** Per-token cost rates for the active provider/model, from setEnv on\n * session.start. We multiply against this message's usage to render a\n * USD figure beside the token counts — answers the \"what did this turn\n * cost?\" question without making the user open Settings. */\n const inputCost = useSessionStore((s) => s.inputCost);\n const outputCost = useSessionStore((s) => s.outputCost);\n const cacheReadCost = useSessionStore((s) => s.cacheReadCost);\n\n /** True when this is the most recent assistant message and we're not\n * in the middle of a run — eligible for the regenerate action. We\n * derive this from the store so the button only renders on one bubble\n * at a time without prop drilling. */\n const isLatestAssistant = (() => {\n if (message.role !== 'assistant' || isLoading) return false;\n const all = useChatStore.getState().messages;\n for (let i = all.length - 1; i >= 0; i--) {\n const m = all[i]!;\n if (m.role === 'assistant') return m.id === message.id;\n }\n return false;\n })();\n\n const regenerate = () => {\n // Find the user message that prompted this reply by walking backward.\n // Truncate to that user message (exclusive), then re-send its content.\n const all = useChatStore.getState().messages;\n const idx = all.findIndex((m) => m.id === message.id);\n if (idx === -1) return;\n let userIdx = -1;\n for (let i = idx - 1; i >= 0; i--) {\n if (all[i]!.role === 'user') {\n userIdx = i;\n break;\n }\n }\n if (userIdx === -1) return;\n const userMsg = all[userIdx]!;\n truncateAfter(userMsg.id);\n addMessage({ role: 'user', content: userMsg.content });\n setLoading(true);\n const client = getWSClient(wsUrl);\n client.sendMessage(userMsg.content);\n };\n\n const toggleTool = (id: string) => {\n setExpandedTools((prev) => ({ ...prev, [id]: !prev[id] }));\n };\n\n const startEdit = () => {\n setEditValue(message.content);\n setEditing(true);\n };\n const cancelEdit = () => {\n setEditing(false);\n setEditValue('');\n };\n const saveEdit = () => {\n const next = editValue.trim();\n if (!next) {\n cancelEdit();\n return;\n }\n // Wipe everything from this message forward, then send the corrected\n // prompt as a fresh user turn. The backend still has the original in\n // its server-side context.messages — that's an acceptable tradeoff for\n // a no-backend-change implementation; the user gets the rewind UX they\n // expect locally, and the model just sees a \"rephrased follow-up\".\n truncateAfter(message.id);\n addMessage({ role: 'user', content: next });\n setLoading(true);\n const client = getWSClient(wsUrl);\n client.sendMessage(next);\n setEditing(false);\n setEditValue('');\n };\n\n return (\n <div\n data-message-id={message.id}\n data-pinned={isPinned ? '1' : undefined}\n className={cn(\n 'group flex animate-message rounded-lg transition-shadow',\n compactMode ? 'gap-2' : 'gap-3',\n isUser ? 'flex-row-reverse' : 'flex-row',\n isPinned && 'ring-1 ring-amber-500/30 bg-amber-500/[0.02] px-1 -mx-1',\n )}\n >\n {/* Avatar — replaced by an invisible spacer in continuation mode so\n subsequent items in the same agent turn align under the first\n item's avatar without redrawing it. */}\n {isContinuation ? (\n <div className=\"flex-shrink-0 w-8 h-8\" aria-hidden />\n ) : (\n <div\n className={cn(\n 'flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center',\n 'ring-2 ring-offset-2 ring-offset-background',\n isUser\n ? 'bg-primary text-primary-foreground ring-primary/20'\n : isTool\n ? 'bg-secondary text-secondary-foreground ring-secondary/20'\n : 'bg-accent text-accent-foreground ring-accent/20',\n )}\n >\n {isUser ? (\n <User className=\"h-4 w-4\" />\n ) : isTool ? (\n <Terminal className=\"h-4 w-4\" />\n ) : (\n <Bot className=\"h-4 w-4\" />\n )}\n </div>\n )}\n\n {/* Content */}\n <div className={cn('flex flex-col gap-1.5 max-w-[85%]', isUser && 'items-end')}>\n {/* Role indicator for first message in a group. Suppressed for\n continuation items so the same label doesn't repeat for every\n text→tool→text segment of one agent turn. */}\n {isFirst && !isContinuation && (\n <span\n className={cn(\n 'text-xs font-medium px-1',\n isUser ? 'text-primary' : isTool ? 'text-secondary' : 'text-muted-foreground',\n )}\n >\n {isUser ? 'You' : isTool ? 'Tool' : 'Assistant'}\n </span>\n )}\n\n {/* Tool header */}\n {isTool && message.toolName && (\n <button\n type=\"button\"\n onClick={() => toggleTool(message.id)}\n className={cn(\n 'flex items-center gap-2 text-sm font-medium cursor-pointer select-none',\n 'hover:bg-muted/50 rounded-lg px-2 py-1 -mx-2 transition-colors',\n message.isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n <span className=\"text-muted-foreground/50\">\n {expandedTools[message.id] ? (\n <ChevronDown className=\"h-3 w-3\" />\n ) : (\n <ChevronRight className=\"h-3 w-3\" />\n )}\n </span>\n <Terminal className=\"h-3 w-3\" />\n <span className=\"font-mono\">{message.toolName}</span>\n {message.toolResult === undefined ? (\n // Pulsing dot while still running (matches the inline indicator below).\n <span className=\"h-1.5 w-1.5 rounded-full bg-amber-500 animate-pulse\" aria-hidden />\n ) : message.isError ? (\n <XCircle className=\"h-3 w-3 text-destructive\" />\n ) : (\n <CheckCircle2 className=\"h-3 w-3 text-green-500\" />\n )}\n {typeof message.toolDurationMs === 'number' && (\n <span className=\"text-xs text-muted-foreground tabular-nums font-normal\">\n {formatToolDuration(message.toolDurationMs)}\n </span>\n )}\n </button>\n )}\n\n {/* Message content */}\n <div\n className={cn(\n 'rounded-2xl',\n compactMode ? 'px-3 py-1.5' : 'px-4 py-3',\n isUser\n ? 'bg-primary text-primary-foreground rounded-br-md'\n : isTool\n ? message.isError\n ? 'bg-destructive/5 border border-destructive/20 text-destructive'\n : 'bg-muted/80 text-foreground'\n : 'bg-card border text-foreground',\n message.isError && !isTool && 'border-destructive/20',\n )}\n >\n {isTool ? (\n (() => {\n const expanded = !!expandedTools[message.id];\n const inputSummary =\n message.toolInput !== undefined\n ? summarizeToolInput(message.toolName, message.toolInput)\n : '';\n const lines = message.toolResult ? message.toolResult.split('\\n').length : 0;\n return (\n <div className=\"space-y-1\">\n {/* Collapsed: just a one-line input summary so parallel calls\n stay distinguishable. The output is hidden entirely —\n click the header (or \"Show details\" link) to expand. */}\n {inputSummary && !expanded && (\n <div className=\"text-xs text-muted-foreground font-mono truncate\">\n {inputSummary}\n </div>\n )}\n {/* Live progress feed while the tool is still running.\n Shown both collapsed and expanded so the user can see\n what's happening — the final result replaces this when\n tool.executed lands. */}\n {message.toolResult === undefined &&\n message.progressLines &&\n message.progressLines.length > 0 && (\n <div className=\"mt-1 rounded-md border border-amber-500/20 bg-amber-500/5 p-1.5 text-[11px] font-mono leading-snug max-h-32 overflow-auto\">\n {message.progressLines.slice(-6).map((line, i) => (\n <div\n // biome-ignore lint/suspicious/noArrayIndexKey: static progress lines\n key={i}\n className=\"truncate text-muted-foreground\"\n >\n {line}\n </div>\n ))}\n </div>\n )}\n {/* Expanded view. For the edit/write family of tools we\n replace the raw JSON dump with a real diff — the\n old_string/new_string pair (or just the new content\n for write) is the whole point of the call. Falls\n back to JSON for everything else. */}\n {expanded &&\n message.toolInput !== undefined &&\n (() => {\n const diffArgs = diffFromToolInput(message.toolName, message.toolInput);\n if (diffArgs) {\n return (\n <DiffView\n oldText={diffArgs.oldText}\n newText={diffArgs.newText}\n caption={diffArgs.caption}\n />\n );\n }\n return (\n <div className=\"p-3 bg-muted/50 rounded-lg overflow-x-auto\">\n <div className=\"flex items-center gap-1 text-muted-foreground mb-2 text-xs\">\n <Clock className=\"h-3 w-3\" />\n <span>Input</span>\n </div>\n <ToolInputView input={message.toolInput} />\n </div>\n );\n })()}\n {expanded &&\n message.toolResult !== undefined &&\n message.toolResult.length > 0 && (\n <div className=\"relative group/tool\">\n <ToolResult\n toolName={message.toolName}\n result={message.toolResult}\n isError={message.isError}\n />\n <div className=\"absolute top-1.5 right-1.5 flex items-center gap-1 opacity-0 group-hover/tool:opacity-100 transition-opacity\">\n <CopyButton\n text={message.toolResult}\n label=\"\"\n className=\"bg-background/80 border rounded px-1.5 py-0.5\"\n />\n {/* Download — only worth showing when the dump is\n big enough to actually save (>5 lines).\n Small grep hits are easier to just copy. */}\n {message.toolResult.split('\\n').length > 5 && (\n <button\n type=\"button\"\n onClick={(ev) => {\n ev.stopPropagation();\n const ext = fileExtensionFor(message.toolName);\n const base = (message.toolName ?? 'output')\n .replace(/[^a-z0-9_-]+/gi, '-')\n .toLowerCase();\n downloadTextFile(\n `${base}-${new Date().toISOString().replace(/[:.]/g, '-')}.${ext}`,\n message.toolResult ?? '',\n );\n }}\n className=\"inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground bg-background/80 border rounded px-1.5 py-0.5\"\n title=\"Download as file\"\n >\n <Download className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n </div>\n )}\n {expanded &&\n message.toolResult !== undefined &&\n message.toolResult.length === 0 && (\n <span className=\"text-xs text-muted-foreground italic\">(empty)</span>\n )}\n {/* Error case: keep the message inline even when collapsed,\n since silently hiding a failure is worse than the noise. */}\n {!expanded && message.isError && message.toolResult && (\n <div className=\"text-xs font-mono text-destructive truncate\">\n {message.toolResult.split('\\n')[0]}\n </div>\n )}\n {/* \"Show details\" toggle — only when there's anything to reveal. */}\n {((message.toolResult !== undefined && message.toolResult.length > 0) ||\n (message.toolInput !== undefined &&\n Object.keys((message.toolInput as object) ?? {}).length > 0)) && (\n <button\n type=\"button\"\n onClick={() => toggleTool(message.id)}\n className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n >\n {expanded\n ? 'Hide details'\n : `Show details${lines > 0 ? ` (${lines} line${lines === 1 ? '' : 's'})` : ''}`}\n </button>\n )}\n </div>\n );\n })()\n ) : editing && isUser ? (\n <div className=\"flex flex-col gap-2 min-w-[280px]\">\n <textarea\n value={editValue}\n onChange={(e) => setEditValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n cancelEdit();\n } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n saveEdit();\n }\n }}\n rows={Math.min(8, Math.max(2, editValue.split('\\n').length))}\n className=\"w-full resize-none rounded-md border bg-background text-foreground px-2 py-1.5 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-[10px] text-primary-foreground/60\">\n ⌘/Ctrl+Enter to save · Esc to cancel\n </span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={cancelEdit}\n className=\"text-xs px-2 py-0.5 rounded border border-primary-foreground/30 hover:bg-primary-foreground/10\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={saveEdit}\n disabled={!editValue.trim()}\n className=\"text-xs px-2 py-0.5 rounded bg-primary-foreground text-primary disabled:opacity-50\"\n >\n Save &amp; resend\n </button>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"text-sm leading-relaxed markdown-content\">\n {message.content ? (\n showRaw && message.role === 'assistant' ? (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs leading-relaxed text-foreground/90 max-h-[40rem] overflow-auto\">\n {message.content}\n </pre>\n ) : message.role === 'assistant' && message.isError ? (\n <ErrorBodyWithStack text={message.content} />\n ) : (\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>\n {message.content}\n </ReactMarkdown>\n )\n ) : message.streaming ? (\n <span className=\"inline-block animate-pulse text-muted-foreground\">Typing...</span>\n ) : (\n <span className=\"text-muted-foreground italic\">No content</span>\n )}\n </div>\n )}\n </div>\n\n {/* Footer: timestamp + copy. Copy is hover-revealed so the chat\n stays clean by default. Tool bubbles get their own copy button\n on the output box, so we skip it here for them. */}\n <div\n className={cn('flex items-center gap-2 px-1', isUser ? 'flex-row-reverse' : 'flex-row')}\n >\n <span className=\"text-xs text-muted-foreground/50\">\n {new Date(message.timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })}\n </span>\n {/* Per-message token attribution. Lands on the assistant bubble\n after provider.response with the input/output counts. Cache\n reads show up too when present — useful to see when prompt\n caching is actually saving cost. USD figure appears only when\n we know the per-token rates for the active model. */}\n {/* Run summary — attached by the run.result handler to the last\n assistant bubble of each turn. Renders as a single quiet line:\n iterations / tools / elapsed / $ delta. Hidden when nothing\n meaningful happened (e.g. plain-text reply with no tools). */}\n {message.runSummary && (\n <span\n className=\"text-[10px] text-muted-foreground/60 font-mono tabular-nums\"\n title={[\n `Iterations: ${message.runSummary.iterations}`,\n `Tool calls: ${message.runSummary.tools}`,\n `Elapsed: ${(message.runSummary.durationMs / 1000).toFixed(2)}s`,\n message.runSummary.costDelta > 0\n ? `Cost: $${message.runSummary.costDelta.toFixed(4)}`\n : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n >\n {message.runSummary.iterations} iter\n {message.runSummary.tools > 0\n ? ` · ${message.runSummary.tools} tool${message.runSummary.tools === 1 ? '' : 's'}`\n : ''}\n {' · '}\n {message.runSummary.durationMs < 60_000\n ? `${(message.runSummary.durationMs / 1000).toFixed(1)}s`\n : `${Math.floor(message.runSummary.durationMs / 60_000)}m ${Math.floor((message.runSummary.durationMs % 60_000) / 1000)}s`}\n {message.runSummary.costDelta > 0\n ? ` · $${message.runSummary.costDelta >= 0.01 ? message.runSummary.costDelta.toFixed(4) : message.runSummary.costDelta.toFixed(6).replace(/0+$/, '').replace(/\\.$/, '')}`\n : ''}\n </span>\n )}\n {message.usage &&\n (message.usage.input > 0 || message.usage.output > 0) &&\n (() => {\n const u = message.usage;\n const dollars =\n (u.input * inputCost + u.output * outputCost + (u.cacheRead ?? 0) * cacheReadCost) /\n 1_000_000;\n const haveCost = inputCost > 0 || outputCost > 0;\n const dollarStr =\n dollars >= 0.01\n ? `$${dollars.toFixed(4)}`\n : dollars > 0\n ? `$${dollars.toFixed(6).replace(/0+$/, '').replace(/\\.$/, '')}`\n : '';\n return (\n <span\n className=\"text-[10px] text-muted-foreground/60 font-mono tabular-nums\"\n title={[\n `Input: ${u.input.toLocaleString()}`,\n `Output: ${u.output.toLocaleString()}`,\n u.cacheRead ? `Cache read: ${u.cacheRead.toLocaleString()}` : '',\n haveCost ? `Cost: ${dollarStr}` : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n >\n {u.input.toLocaleString()}→{u.output.toLocaleString()}\n {u.cacheRead ? ` · ${u.cacheRead.toLocaleString()} ↺` : ''}\n {haveCost && dollarStr ? ` · ${dollarStr}` : ''}\n </span>\n );\n })()}\n {!isTool && message.content && !message.streaming && (\n <CopyButton\n text={message.content}\n label=\"\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n )}\n {/* Raw markdown toggle — assistant bubbles only. When the model\n emits literal markdown the user wants to inspect (escaped\n backticks, weird table formatting, raw HTML…), this flips the\n body from rendered → raw without leaving the chat. */}\n {message.role === 'assistant' && message.content && !message.streaming && (\n <button\n type=\"button\"\n onClick={() => setShowRaw((v) => !v)}\n title={showRaw ? 'Show rendered markdown' : 'Show raw markdown source'}\n className={cn(\n 'text-xs inline-flex items-center gap-1 transition-opacity',\n showRaw\n ? 'text-primary hover:text-primary/80 opacity-100'\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-foreground',\n )}\n >\n <FileCode2 className=\"h-3 w-3\" />\n <span>{showRaw ? 'Rendered' : 'Raw'}</span>\n </button>\n )}\n {/* Edit + regenerate — user messages only. Hidden while a run is\n in flight to avoid the textarea-vs-streaming-bubble race. */}\n {isUser && !editing && !isLoading && message.content && (\n <button\n type=\"button\"\n onClick={startEdit}\n title=\"Edit & resend this prompt\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity text-xs text-muted-foreground hover:text-foreground inline-flex items-center gap-1\"\n >\n <Pencil className=\"h-3 w-3\" />\n <span>Edit</span>\n </button>\n )}\n {/* Pin/unpin — assistant messages with real content. A pinned bubble\n gets a subtle amber ring + lands in the sidebar's Pinned panel\n so the user can jump back to it. Always visible once pinned so\n the user can tell at a glance it's bookmarked. */}\n {message.role === 'assistant' && message.content && !message.streaming && (\n <button\n type=\"button\"\n onClick={() => togglePin(message.id)}\n title={isPinned ? 'Unpin' : 'Pin this answer'}\n className={cn(\n 'text-xs inline-flex items-center gap-1 transition-opacity',\n isPinned\n ? 'text-amber-500 hover:text-amber-600 opacity-100'\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-foreground',\n )}\n >\n {isPinned ? <PinOff className=\"h-3 w-3\" /> : <Pin className=\"h-3 w-3\" />}\n <span>{isPinned ? 'Pinned' : 'Pin'}</span>\n </button>\n )}\n {/* Regenerate — only the most recent assistant reply, when the run\n has settled. Rewinds local state to the prompting user message\n and resends it, giving the user a one-click \"try again\". */}\n {isLatestAssistant && message.content && !message.streaming && (\n <button\n type=\"button\"\n onClick={regenerate}\n title=\"Regenerate this response\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity text-xs text-muted-foreground hover:text-foreground inline-flex items-center gap-1\"\n >\n <RotateCcw className=\"h-3 w-3\" />\n <span>Retry</span>\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useMemo } from 'react';\n\ninterface DiffViewProps {\n oldText: string;\n newText: string;\n /** Optional caption shown above the diff (file path, \"write\" vs \"edit\", etc.) */\n caption?: string;\n}\n\n/**\n * Tiny line-based diff renderer. Computes the longest-common-subsequence\n * between `oldText` and `newText` line arrays, then walks both sides\n * emitting `add`/`remove`/`context` rows. Context lines are de-emphasised\n * so the eye lands on the changes. Not as pretty as a Myers diff but it's\n * one short function with no deps and good enough for a chat preview.\n *\n * Limits: text > 5000 lines is shown without a diff (just a \"too large\"\n * note); that's a UI guard, not a hard limit on the underlying tool.\n */\nexport function DiffView({ oldText, newText, caption }: DiffViewProps) {\n const rows = useMemo(() => computeDiff(oldText, newText), [oldText, newText]);\n\n if (rows === null) {\n return (\n <div className=\"text-xs text-muted-foreground italic px-3 py-2\">\n Diff omitted (file too large to render inline).\n </div>\n );\n }\n\n const adds = rows.filter((r) => r.kind === 'add').length;\n const dels = rows.filter((r) => r.kind === 'del').length;\n\n return (\n <div className=\"rounded-lg border bg-background/40 overflow-hidden text-xs\">\n <div className=\"flex items-center justify-between px-3 py-1.5 border-b bg-muted/40\">\n <span className=\"font-mono text-muted-foreground truncate\">{caption ?? 'diff'}</span>\n <span className=\"font-mono shrink-0\">\n <span className=\"text-emerald-600 dark:text-emerald-400\">+{adds}</span>\n <span className=\"text-muted-foreground mx-1\">·</span>\n <span className=\"text-rose-600 dark:text-rose-400\">-{dels}</span>\n </span>\n </div>\n <div className=\"font-mono leading-relaxed max-h-96 overflow-auto\">\n {rows.map((r, i) => (\n <div\n // biome-ignore lint/suspicious/noArrayIndexKey: static diff rows\n key={i}\n className={cn(\n 'flex',\n r.kind === 'add' && 'bg-emerald-500/10',\n r.kind === 'del' && 'bg-rose-500/10',\n )}\n >\n <span\n className={cn(\n 'w-6 shrink-0 text-center select-none',\n r.kind === 'add' && 'text-emerald-600 dark:text-emerald-400',\n r.kind === 'del' && 'text-rose-600 dark:text-rose-400',\n r.kind === 'ctx' && 'text-muted-foreground/40',\n )}\n >\n {r.kind === 'add' ? '+' : r.kind === 'del' ? '-' : ' '}\n </span>\n <pre\n className={cn(\n 'whitespace-pre-wrap break-all flex-1 px-2',\n r.kind === 'ctx' && 'text-muted-foreground/70',\n )}\n >\n {r.text || ' '}\n </pre>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\ninterface DiffRow {\n kind: 'add' | 'del' | 'ctx';\n text: string;\n}\n\nconst MAX_LINES = 5000;\n\n/**\n * Returns null when either side exceeds the line cap. Otherwise walks the\n * LCS table to produce a clean diff. LCS table is O(n*m) memory — fine for\n * a file edit (usually <500 lines), prohibitive for a full file rewrite of\n * a giant generated file (hence the cap).\n */\nfunction computeDiff(oldText: string, newText: string): DiffRow[] | null {\n const a = oldText.split('\\n');\n const b = newText.split('\\n');\n if (a.length > MAX_LINES || b.length > MAX_LINES) return null;\n const n = a.length;\n const m = b.length;\n // dp[i][j] = LCS length of a[i..] and b[j..]\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n if (a[i] === b[j]) dp[i]![j] = dp[i + 1]![j + 1]! + 1;\n else dp[i]![j] = Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n const rows: DiffRow[] = [];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (a[i] === b[j]) {\n rows.push({ kind: 'ctx', text: a[i]! });\n i++;\n j++;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n rows.push({ kind: 'del', text: a[i]! });\n i++;\n } else {\n rows.push({ kind: 'add', text: b[j]! });\n j++;\n }\n }\n while (i < n) rows.push({ kind: 'del', text: a[i++]! });\n while (j < m) rows.push({ kind: 'add', text: b[j++]! });\n return rows;\n}\n\n/**\n * Recognise the WrongStack edit-family tools and pull a (oldText, newText,\n * caption) tuple out of their input. Returns null when the tool doesn't\n * carry diffable input. Caller uses this to decide whether to render the\n * DiffView at all.\n */\nexport function diffFromToolInput(\n toolName: string | undefined,\n input: unknown,\n): { oldText: string; newText: string; caption: string } | null {\n if (!toolName || typeof input !== 'object' || input === null) return null;\n const obj = input as Record<string, unknown>;\n const filePath = String(obj.file_path ?? obj.path ?? '');\n switch (toolName) {\n case 'edit':\n case 'str_replace':\n case 'edit_file': {\n const oldText = typeof obj.old_string === 'string' ? obj.old_string : '';\n const newText = typeof obj.new_string === 'string' ? obj.new_string : '';\n if (!oldText && !newText) return null;\n return { oldText, newText, caption: `edit ${filePath}` };\n }\n case 'write':\n case 'write_file':\n case 'create_file': {\n const content = typeof obj.content === 'string' ? obj.content : '';\n // For a fresh write there's no \"old\" — treat as additive so the\n // viewer shows everything green.\n return { oldText: '', newText: content, caption: `write ${filePath} (new)` };\n }\n default:\n return null;\n }\n}\n","import { cn } from '@/lib/utils';\nimport { ChevronDown, ChevronRight, ChevronsDown, ChevronsUp } from 'lucide-react';\nimport { useMemo, useState } from 'react';\n\n/** When a tool dumps hundreds of lines of output, the chat turns into a\n * scroll-wall. This threshold gates the auto-collapse: anything longer\n * shows the first ~LONG_PEEK_LINES with a \"Show all N more\" toggle. */\nconst LONG_OUTPUT_THRESHOLD = 25;\nconst LONG_PEEK_LINES = 12;\n\n/**\n * Render `text` as a monospace block, auto-collapsing when it exceeds\n * LONG_OUTPUT_THRESHOLD lines. The first LONG_PEEK_LINES stay visible;\n * the rest expand on click. Keeping the wrap class configurable lets the\n * `numbered` path use `whitespace-pre` (no wrap) and the bash/plain path\n * use `whitespace-pre-wrap break-all` (wrap and break long URLs/paths).\n */\nfunction CollapsibleText({\n text,\n isError,\n className,\n wrapClass,\n showLineNumbers,\n}: {\n text: string;\n isError?: boolean;\n className?: string;\n wrapClass: string;\n /** Whether to render a left gutter with line numbers when the output is\n * long. Off for the numbered/Read-tool path (which already prefixes its\n * own line numbers in the content) and for wrap-mode bodies where a\n * fixed gutter can't stay aligned with wrapped lines. */\n showLineNumbers?: boolean;\n}) {\n const lines = useMemo(() => text.split('\\n'), [text]);\n const isLong = lines.length > LONG_OUTPUT_THRESHOLD;\n const [expanded, setExpanded] = useState(!isLong);\n const shown = expanded ? text : lines.slice(0, LONG_PEEK_LINES).join('\\n');\n // Only emit the gutter when the body won't wrap (alignment would break\n // otherwise) AND the user actually expanded it / it's not super short.\n const renderGutter = !!showLineNumbers && isLong && expanded;\n return (\n <div className={cn('rounded-md border bg-background/40 overflow-hidden', className)}>\n {renderGutter ? (\n // Force `whitespace-pre` inside the gutter view so the line numbers\n // stay 1:1 with content rows. Long horizontal lines scroll within\n // the shared overflow container rather than wrapping (which would\n // break alignment between the gutter and the body).\n <div className=\"flex max-h-96 overflow-auto\">\n <pre\n aria-hidden\n className=\"text-xs font-mono leading-[1.4] py-2 pl-2 pr-2 text-muted-foreground/50 select-none border-r border-border/40 bg-muted/20 tabular-nums text-right whitespace-pre shrink-0\"\n >\n {lines.map((_, i) => `${i + 1}`).join('\\n')}\n </pre>\n <pre\n className={cn(\n 'text-xs font-mono leading-[1.4] py-2 px-2 flex-1 whitespace-pre',\n isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n {shown}\n </pre>\n </div>\n ) : (\n <pre\n className={cn(\n 'text-xs font-mono p-2 max-h-96 overflow-auto',\n wrapClass,\n isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n {shown}\n </pre>\n )}\n {isLong && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"w-full flex items-center justify-center gap-1 px-2 py-1 border-t bg-muted/30 text-[11px] text-muted-foreground hover:text-foreground transition-colors\"\n >\n {expanded ? (\n <>\n <ChevronsUp className=\"h-3 w-3\" />\n Collapse to first {LONG_PEEK_LINES} lines\n </>\n ) : (\n <>\n <ChevronsDown className=\"h-3 w-3\" />\n Show all {lines.length} lines ({lines.length - LONG_PEEK_LINES} more)\n </>\n )}\n </button>\n )}\n </div>\n );\n}\n\ninterface Props {\n toolName?: string;\n result: string;\n isError?: boolean;\n className?: string;\n}\n\n/**\n * Decides which renderer to use for a tool result based on its shape /\n * tool name. The goal is to make output *legible at a glance*:\n * - Read tool with leading line numbers stays line-numbered, monospace.\n * - Bash output gets a \"exit code N\" footer split from stdout.\n * - Valid JSON gets pretty-printed and collapsible by default.\n * - Everything else falls back to raw monospace.\n */\nexport function ToolResult({ toolName, result, isError, className }: Props) {\n const shape = useMemo(() => detectShape(toolName, result), [toolName, result]);\n\n if (shape.kind === 'json') {\n return <JsonResult value={shape.value} isError={isError} className={className} />;\n }\n if (shape.kind === 'numbered') {\n return (\n <CollapsibleText\n text={result}\n isError={isError}\n className={className}\n wrapClass=\"whitespace-pre\"\n />\n );\n }\n if (shape.kind === 'bash') {\n return (\n <div className={cn('rounded-md border bg-background/40 overflow-hidden', className)}>\n {shape.stdout && (\n <CollapsibleText\n text={shape.stdout}\n isError={isError}\n // Nested CollapsibleText already adds border/bg; strip them here\n // so we get one outer frame, not two.\n className=\"border-0 rounded-none bg-transparent\"\n wrapClass=\"whitespace-pre-wrap break-all\"\n showLineNumbers\n />\n )}\n {(shape.exitCode !== undefined || shape.duration) && (\n <div\n className={cn(\n 'flex items-center gap-3 text-[11px] px-2 py-1 border-t bg-muted/30 tabular-nums',\n shape.exitCode && shape.exitCode !== 0 ? 'text-destructive' : 'text-muted-foreground',\n )}\n >\n {shape.exitCode !== undefined && (\n <span>\n exit code: <span className=\"font-mono\">{shape.exitCode}</span>\n </span>\n )}\n {shape.duration && <span>{shape.duration}</span>}\n </div>\n )}\n </div>\n );\n }\n return (\n <CollapsibleText\n text={result}\n isError={isError}\n className={className}\n wrapClass=\"whitespace-pre-wrap break-all\"\n showLineNumbers\n />\n );\n}\n\ninterface Shape {\n kind: 'numbered' | 'json' | 'bash' | 'plain';\n /** For JSON results. */\n value?: unknown;\n /** For bash-shape results. */\n stdout?: string;\n exitCode?: number;\n duration?: string;\n}\n\nfunction detectShape(toolName: string | undefined, result: string): Shape {\n const trimmed = result.trim();\n // ---- Read tool: lines like \" 42→ const foo = …\" -------------\n if (/^\\s*\\d+→/m.test(result.slice(0, 200))) {\n return { kind: 'numbered' };\n }\n // ---- JSON ---------------------------------------------------------\n if (\n (trimmed.startsWith('{') && trimmed.endsWith('}')) ||\n (trimmed.startsWith('[') && trimmed.endsWith(']'))\n ) {\n try {\n const parsed = JSON.parse(trimmed);\n if (typeof parsed === 'object' && parsed !== null) {\n return { kind: 'json', value: parsed };\n }\n } catch {\n /* fall through */\n }\n }\n // ---- Bash-ish -----------------------------------------------------\n const isBashTool = !!toolName && /^(bash|shell|exec|run)/i.test(toolName);\n // Many bash wrappers emit a trailing \"exit code: N\" or \"[exit 1]\" line.\n const exitMatch = result.match(/(?:^|\\n)\\s*(?:\\[?exit(?:\\s*code)?\\]?\\s*[:=]?\\s*)(\\d+)\\s*$/i);\n const durMatch = result.match(/(?:^|\\s)(\\d+\\s*ms|\\d+\\.\\d+s)\\s*$/i);\n if (isBashTool || exitMatch) {\n let stdout = result;\n if (exitMatch) stdout = result.slice(0, exitMatch.index).trimEnd();\n return {\n kind: 'bash',\n stdout,\n exitCode: exitMatch ? Number(exitMatch[1]) : undefined,\n duration: durMatch?.[1],\n };\n }\n return { kind: 'plain' };\n}\n\nfunction JsonResult({\n value,\n isError,\n className,\n}: {\n value: unknown;\n isError?: boolean;\n className?: string;\n}) {\n const pretty = useMemo(() => {\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }, [value]);\n const lineCount = pretty.split('\\n').length;\n const [expanded, setExpanded] = useState(lineCount < 30);\n return (\n <div\n className={cn(\n 'rounded-md border bg-background/40 overflow-hidden',\n isError && 'border-destructive/40',\n className,\n )}\n >\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"w-full flex items-center justify-between px-2 py-1 border-b bg-muted/30 text-[11px] text-muted-foreground hover:text-foreground\"\n >\n <span className=\"flex items-center gap-1\">\n {expanded ? <ChevronDown className=\"h-3 w-3\" /> : <ChevronRight className=\"h-3 w-3\" />}\n <span className=\"font-mono\">JSON · {lineCount} lines</span>\n </span>\n <span>{expanded ? 'collapse' : 'expand'}</span>\n </button>\n {expanded && (\n <pre\n className={cn(\n 'text-xs font-mono whitespace-pre p-2 max-h-96 overflow-auto',\n isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n {pretty}\n </pre>\n )}\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useSessionStore } from '@/stores';\nimport { Check, ChevronDown } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\n/**\n * Pill-shaped mode chip in the topbar. Click to drop a small picker with\n * the available modes (fetched lazily on first open from the backend's\n * modes.list handler). Selecting a mode broadcasts a session.start update\n * so the chip and the system-prompt context reflect the change without a\n * page reload.\n */\nexport function ModePicker() {\n const mode = useSessionStore((s) => s.mode);\n const modes = useSessionStore((s) => s.modes);\n const { listModes, switchMode } = useWebSocket();\n const [open, setOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // Lazy-fetch the modes list the first time the user clicks the chip\n // (and refresh on every open in case the user installed a new mode\n // through some other surface).\n if (open) listModes();\n }, [open, listModes]);\n\n // Close on outside click / Escape.\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (!rootRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('mousedown', onClick);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('mousedown', onClick);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n const items =\n modes.length > 0\n ? modes\n : [{ id: 'default', name: 'Default', description: 'Standard agent behaviour' }];\n\n return (\n <div ref={rootRef} className=\"relative shrink-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n 'flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium',\n 'bg-accent/40 text-foreground hover:bg-accent transition-colors border border-transparent hover:border-primary/30',\n )}\n title=\"Active mode\"\n >\n mode: <span className=\"font-mono\">{mode || 'default'}</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" />\n </button>\n {open && (\n <div className=\"absolute top-full left-0 mt-1 w-64 rounded-md border bg-popover shadow-lg z-30 py-1\">\n <div className=\"px-3 py-1.5 text-[10px] uppercase tracking-wider text-muted-foreground border-b\">\n Mode\n </div>\n {items.map((m) => (\n <button\n key={m.id}\n type=\"button\"\n onClick={() => {\n switchMode(m.id);\n setOpen(false);\n }}\n className={cn(\n 'w-full text-left px-3 py-2 hover:bg-accent/40 flex items-start gap-2',\n m.id === mode && 'bg-accent/30',\n )}\n >\n <Check\n className={cn(\n 'h-3.5 w-3.5 mt-0.5 shrink-0',\n m.id === mode ? 'opacity-100 text-primary' : 'opacity-0',\n )}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-xs font-mono\">{m.id}</div>\n {m.description && (\n <div className=\"text-[11px] text-muted-foreground leading-snug\">\n {m.description}\n </div>\n )}\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useChatStore, useUIStore } from '@/stores';\nimport { ArrowDown, ArrowUp, Search, X } from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Ctrl+F overlay that searches the current chat transcript. Hits are\n * counted as the user types; ↑/↓ step between hits and Enter scrolls the\n * highlighted message into view. The actual highlight is handed off via a\n * DOM data attribute so we don't need to invasively rewrap message\n * rendering — see ChatView for where the active hit gets the highlight\n * class applied.\n */\nexport function SearchOverlay() {\n const open = useUIStore((s) => s.searchOpen);\n const setOpen = useUIStore((s) => s.setSearchOpen);\n const query = useUIStore((s) => s.searchQuery);\n const setQuery = useUIStore((s) => s.setSearchQuery);\n const messages = useChatStore((s) => s.messages);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const [activeHit, setActiveHit] = useState(0);\n\n useEffect(() => {\n if (open) requestAnimationFrame(() => inputRef.current?.focus());\n }, [open]);\n\n const hits = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return [] as string[];\n return messages\n .filter((m) => {\n if (m.role === 'tool') {\n return (\n (m.toolName ?? '').toLowerCase().includes(q) ||\n (m.toolResult ?? '').toLowerCase().includes(q) ||\n JSON.stringify(m.toolInput ?? '')\n .toLowerCase()\n .includes(q)\n );\n }\n return m.content.toLowerCase().includes(q);\n })\n .map((m) => m.id);\n }, [messages, query]);\n\n useEffect(() => {\n if (activeHit >= hits.length) setActiveHit(0);\n }, [hits, activeHit]);\n\n // Paint every match of the current query inside chat bubbles using the\n // CSS Custom Highlights API. We walk the text nodes under every\n // `[data-message-id]` element, build Range objects per hit, then register\n // two highlights: `chat-search` covers everything, `chat-search-active`\n // covers only the message currently navigated to (so the user can see\n // where they are in the list). The registry is cleared on unmount and on\n // every query change so stale ranges don't linger.\n useEffect(() => {\n // Feature-detect — falls back to silent no-op on older browsers; the\n // ring-flash navigation behaviour below still works.\n const win = window as unknown as {\n CSS?: { highlights?: Map<string, unknown> };\n Highlight?: new (...ranges: Range[]) => unknown;\n };\n const highlights = win.CSS?.highlights;\n const HighlightCtor = win.Highlight;\n if (!highlights || !HighlightCtor) return;\n const clear = () => {\n highlights.delete('chat-search');\n highlights.delete('chat-search-active');\n };\n const q = query.trim();\n if (!q || !open) {\n clear();\n return;\n }\n const lcQuery = q.toLowerCase();\n const allRanges: Range[] = [];\n const activeRanges: Range[] = [];\n const activeId = hits[activeHit];\n for (const el of document.querySelectorAll('[data-message-id]')) {\n const id = (el as HTMLElement).dataset.messageId;\n const isActive = id === activeId;\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let node = walker.nextNode() as Text | null;\n while (node) {\n const text = node.nodeValue ?? '';\n if (text.length > 0) {\n const lc = text.toLowerCase();\n let from = 0;\n while (from <= lc.length - lcQuery.length) {\n const at = lc.indexOf(lcQuery, from);\n if (at === -1) break;\n const range = document.createRange();\n range.setStart(node, at);\n range.setEnd(node, at + lcQuery.length);\n allRanges.push(range);\n if (isActive) activeRanges.push(range);\n from = at + lcQuery.length;\n }\n }\n node = walker.nextNode() as Text | null;\n }\n }\n if (allRanges.length > 0) {\n highlights.set('chat-search', new HighlightCtor(...allRanges) as never);\n } else {\n highlights.delete('chat-search');\n }\n if (activeRanges.length > 0) {\n highlights.set('chat-search-active', new HighlightCtor(...activeRanges) as never);\n } else {\n highlights.delete('chat-search-active');\n }\n return clear;\n }, [query, hits, activeHit, open]);\n\n useEffect(() => {\n const id = hits[activeHit];\n if (!id) return;\n const el = document.querySelector(`[data-message-id=\"${id}\"]`);\n if (el) {\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n }, [hits, activeHit]);\n\n if (!open) return null;\n\n const step = (dir: 1 | -1) => {\n if (hits.length === 0) return;\n setActiveHit((i) => (i + dir + hits.length) % hits.length);\n };\n\n return (\n <div className=\"absolute top-2 right-4 z-30 w-[28rem] max-w-[calc(100%-2rem)] rounded-lg border bg-popover shadow-xl\">\n <div className=\"flex items-center gap-2 px-3 py-2\">\n <Search className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <input\n ref={inputRef}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n setOpen(false);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n step(e.shiftKey ? -1 : 1);\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n step(1);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n step(-1);\n }\n }}\n placeholder=\"Search in chat…\"\n className=\"flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground\"\n />\n <span className=\"text-xs text-muted-foreground tabular-nums shrink-0\">\n {hits.length === 0 ? (query ? '0' : '') : `${activeHit + 1} / ${hits.length}`}\n </span>\n <button\n type=\"button\"\n onClick={() => step(-1)}\n disabled={hits.length === 0}\n className={cn(\n 'p-1 rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed',\n )}\n title=\"Previous hit\"\n >\n <ArrowUp className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => step(1)}\n disabled={hits.length === 0}\n className={cn(\n 'p-1 rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed',\n )}\n title=\"Next hit\"\n >\n <ArrowDown className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"p-1 rounded hover:bg-muted text-muted-foreground\"\n title=\"Close (Esc)\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport type { ChatMessage } from '@/stores';\nimport { CheckCircle2, ChevronDown, ChevronRight, Loader2, Terminal, XCircle } from 'lucide-react';\nimport { useState } from 'react';\nimport { MessageBubble } from './MessageBubble';\n\ninterface ToolGroupProps {\n /** A run of consecutive tool messages (>=1). Rendered as one chip while\n * collapsed, expanded into the usual MessageBubble list on click. */\n tools: ChatMessage[];\n /** Force-expand the latest group so newly-running tools are visible by\n * default (otherwise users see \"5 tool calls\" pop in and have to click to\n * understand what's running). Older groups stay collapsed. */\n defaultOpen?: boolean;\n /** Render as a continuation of the previous item in the same agent turn —\n * hides the avatar column (replaced with a transparent spacer) and the\n * group's chrome stitches into the same flow as the surrounding text /\n * tool items instead of standing alone. */\n isContinuation?: boolean;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(ms < 10_000 ? 2 : 1)}s`;\n const m = Math.floor(ms / 60_000);\n const s = Math.floor((ms % 60_000) / 1000);\n return `${m}m${s}s`;\n}\n\nexport function ToolGroup({ tools, defaultOpen = false, isContinuation = false }: ToolGroupProps) {\n const [open, setOpen] = useState(defaultOpen);\n\n // Single tool? Render as a normal bubble — grouping overhead is just noise.\n if (tools.length === 1) {\n return <MessageBubble message={tools[0]!} isFirst isContinuation={isContinuation} />;\n }\n\n const running = tools.filter((t) => t.toolResult === undefined).length;\n const errored = tools.filter((t) => t.isError).length;\n const totalMs = tools.reduce((acc, t) => acc + (t.toolDurationMs ?? 0), 0);\n\n // Show the first few tool names so the user has a hint of what's inside\n // without expanding (\"Read, Grep, Bash …\").\n const names = Array.from(new Set(tools.map((t) => t.toolName).filter(Boolean) as string[]));\n const preview = names.slice(0, 3).join(', ');\n const more = names.length > 3 ? ` +${names.length - 3}` : '';\n\n return (\n <div className=\"flex gap-3 animate-message\">\n {isContinuation ? (\n <div className=\"flex-shrink-0 w-8 h-8\" aria-hidden />\n ) : (\n <div className=\"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-secondary text-secondary-foreground ring-2 ring-offset-2 ring-offset-background ring-secondary/20\">\n <Terminal className=\"h-4 w-4\" />\n </div>\n )}\n\n <div className=\"flex flex-col gap-1.5 max-w-[85%] flex-1 min-w-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n 'flex items-center gap-2 text-sm font-medium cursor-pointer select-none',\n 'hover:bg-muted/50 rounded-lg px-2 py-1.5 -mx-2 transition-colors',\n 'border border-border/40 bg-muted/30',\n )}\n >\n {open ? (\n <ChevronDown className=\"h-3.5 w-3.5 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n )}\n <Terminal className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <span className=\"font-mono text-xs\">\n {tools.length} tool call{tools.length === 1 ? '' : 's'}\n </span>\n {running > 0 ? (\n <Loader2 className=\"h-3 w-3 animate-spin text-amber-500\" />\n ) : errored > 0 ? (\n <XCircle className=\"h-3 w-3 text-destructive\" />\n ) : (\n <CheckCircle2 className=\"h-3 w-3 text-green-500\" />\n )}\n {totalMs > 0 && (\n <span className=\"text-xs text-muted-foreground tabular-nums font-normal\">\n {formatDuration(totalMs)}\n </span>\n )}\n {preview && (\n <span className=\"text-xs text-muted-foreground/80 font-mono truncate\">\n · {preview}\n {more}\n </span>\n )}\n </button>\n\n {open && (\n <div className=\"space-y-2 pl-3 border-l-2 border-border/40 ml-2\">\n {tools.map((tool) => (\n <MessageBubble key={tool.id} message={tool} isFirst={false} />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore, useHistoryStore, useSessionStore, useUIStore } from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n ArchiveRestore,\n ArrowRight,\n Bug,\n Clock,\n KeyRound,\n Keyboard,\n Search,\n Sparkles,\n Wrench,\n Zap,\n} from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\ninterface PromptCard {\n icon: LucideIcon;\n title: string;\n hint: string;\n tone: string;\n prompts: string[];\n}\n\nconst CARDS: PromptCard[] = [\n {\n icon: Search,\n title: 'Explore',\n hint: 'Understand the code before changing it',\n tone: 'text-blue-600 dark:text-blue-400 bg-blue-500/10 border-blue-500/20',\n prompts: [\n 'Walk me through this codebase: list the top-level packages, the role of each, and how they depend on one another. Highlight any cross-cutting abstractions I should understand first.',\n \"Find every place where the WebSocket protocol is defined or consumed (server handlers, client send/receive, type contracts). Show me the message-type table and any gaps where the type isn't enforced.\",\n 'Locate the entrypoint that boots the agent for normal runs. Trace the call chain from CLI launch all the way to the first model call — what middleware, hooks, and tools are wired along the way?',\n ],\n },\n {\n icon: Wrench,\n title: 'Build',\n hint: 'Add a feature end-to-end',\n tone: 'text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 border-emerald-500/20',\n prompts: [\n \"Add a slash command `/export` that dumps the current chat (messages + tool calls + usage) as a markdown file to ~/.wrongstack/exports/ and surfaces a 'saved to X' toast. Wire backend + ws-client + slash menu entry.\",\n 'Create a notification toast system (Zustand store + portal-rendered <Toast/> component) and migrate every existing `key.operation_result` success/failure message to use it instead of dropping into chat.',\n 'Add structured JSON logging to the WebSocket server: each handler logs `{ts, level, type, payload}` to ~/.wrongstack/logs/webui.jsonl. Make it tail-friendly and respect the existing log.level config.',\n ],\n },\n {\n icon: Bug,\n title: 'Debug',\n hint: 'Track a problem to its root cause',\n tone: 'text-amber-600 dark:text-amber-400 bg-amber-500/10 border-amber-500/20',\n prompts: [\n 'Something feels off with token accounting — the cost chip and the per-message tally drift apart over a long session. Reproduce locally if you can, then propose a fix. Start by reading the TokenCounter + provider.response handler.',\n 'The WebSocket sometimes silently stops streaming text mid-response on lossy networks. Check the reconnect logic, message queue, and how we handle a half-completed text_delta stream after a reconnect.',\n 'I want to know why ctx % climbs so fast in long sessions. Use the existing /debug context breakdown to identify the largest contributors and propose three concrete pruning strategies (with token savings estimates).',\n ],\n },\n {\n icon: Sparkles,\n title: 'Refactor',\n hint: 'Clean up without breaking behavior',\n tone: 'text-violet-600 dark:text-violet-400 bg-violet-500/10 border-violet-500/20',\n prompts: [\n 'Find duplicated logic between packages/cli/src/webui-server.ts and packages/webui/src/server/index.ts. Extract the shared bits into a single source of truth (likely the webui package) and update the CLI to import it.',\n \"Look at the Zustand stores in packages/webui/src/stores/index.ts — anything that should be a derived selector instead of stored state? Anything persisted that shouldn't be? Propose a leaner shape and migration plan.\",\n \"Audit the slash command dispatcher: pull each command's run logic into its own module under packages/webui/src/commands/, make the registry data-driven, and ensure /help auto-generates from the registry (not a hardcoded list).\",\n ],\n },\n];\n\nconst SLASH_REFS: Array<{ name: string; hint: string }> = [\n { name: '/help', hint: 'list every slash command' },\n { name: '/diag', hint: 'runtime diagnostics' },\n { name: '/stats', hint: 'tokens · cache · cost · elapsed' },\n { name: '/tools', hint: 'show registered tools' },\n { name: '/memory', hint: 'show remembered notes' },\n { name: '/compact', hint: 'shrink context' },\n { name: '/clear', hint: 'wipe current context' },\n { name: '/new', hint: 'fresh session' },\n];\n\nfunction fillTextarea(text: string): void {\n const ta = document.querySelector('textarea');\n if (!ta) return;\n const setter = Object.getOwnPropertyDescriptor(\n window.HTMLTextAreaElement.prototype,\n 'value',\n )?.set;\n setter?.call(ta, text);\n ta.dispatchEvent(new Event('input', { bubbles: true }));\n ta.focus();\n}\n\nexport function WelcomeScreen() {\n const { projectName } = useSessionStore();\n const { provider, model } = useConfigStore();\n const wsConnected = useConfigStore((s) => s.wsConnected);\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const setCurrentView = useUIStore((s) => s.setCurrentView);\n /** Saved-provider count. We subscribe directly to `providers.saved`\n * because SettingsPanel is the canonical owner of that state but isn't\n * always mounted (only when the user is on the Settings tab). undefined\n * means \"not yet fetched\" — we skip the CTA in that state to avoid a\n * flash on first paint. */\n const [savedCount, setSavedCount] = useState<number | undefined>(undefined);\n useEffect(() => {\n if (!wsConnected) return;\n const client = getWSClient(wsUrl);\n const off = client.on('providers.saved', (msg: WSServerMessage) => {\n const p = msg.payload as { providers: unknown[] };\n setSavedCount(p.providers?.length ?? 0);\n });\n client.listSavedProviders();\n return () => {\n off();\n };\n }, [wsConnected, wsUrl]);\n /** Recent prompts harvested from the user's typing history. The same\n * store that powers ↑/↓ recall in the input — surfacing them here turns\n * a blank welcome screen into a useful \"pick up where you left off\"\n * surface, without any backend round-trip. Limited to 6 so it doesn't\n * dominate the page. */\n const promptHistory = useUIStore((s) => s.promptHistory);\n const recentPrompts = promptHistory.slice(0, 6);\n /** Recent sessions surfaced as one-click resume buttons. Drives the\n * \"pick back up\" workflow without sending the user to the History tab.\n * We fetch on first paint when connected; the listing is otherwise\n * populated by the History tab on demand. */\n const { listSessions, resumeSession } = useWebSocket();\n const historyEntries = useHistoryStore((s) => s.entries);\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only\n useEffect(() => {\n if (wsConnected && historyEntries.length === 0) listSessions(10);\n // Intentionally only fire on first connect — refreshing on every\n // historyEntries change would loop after the response lands.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [wsConnected]);\n const sessionNicknames = useUIStore((s) => s.sessionNicknames);\n const recentSessions = historyEntries.filter((e) => !e.isCurrent).slice(0, 4);\n\n return (\n <div className=\"flex flex-col gap-8 py-8 px-2 max-w-5xl mx-auto w-full\">\n {/* Hero */}\n <div className=\"flex flex-col items-center text-center gap-3\">\n <div className=\"relative\">\n <div className=\"w-14 h-14 rounded-2xl bg-gradient-to-br from-primary via-primary to-primary/60 flex items-center justify-center shadow-lg shadow-primary/20\">\n <Zap className=\"h-7 w-7 text-primary-foreground\" />\n </div>\n <div className=\"absolute -inset-3 bg-gradient-to-r from-transparent via-primary/10 to-transparent animate-pulse rounded-full -z-10\" />\n </div>\n <div>\n <h2 className=\"text-2xl font-semibold tracking-tight\">\n Where do you want to start\n {projectName ? (\n <>\n {' in '}\n <span className=\"text-primary\">{projectName}</span>\n </>\n ) : (\n ''\n )}\n ?\n </h2>\n <p className=\"text-sm text-muted-foreground mt-2 max-w-2xl mx-auto leading-relaxed\">\n WrongStack is connected to your project and ready to read, edit, run commands, search\n the codebase, track todos, and remember context across sessions. Pick a starting prompt\n below, write your own, or type <span className=\"font-mono text-foreground/80\">/</span>{' '}\n for the full command palette.\n </p>\n {provider && model && (\n <p className=\"text-xs text-muted-foreground/70 mt-2 font-mono\">\n {provider} / {model}\n </p>\n )}\n </div>\n </div>\n\n {/* No-keys CTA — shown only when the backend is connected and the\n providers.saved response confirmed zero registered keys. Lands\n above the prompt cards because clicking those won't work until\n the user adds a key. Quietly disappears once at least one\n provider is registered. */}\n {wsConnected && savedCount === 0 && (\n <button\n type=\"button\"\n onClick={() => setCurrentView('settings')}\n className={cn(\n 'group rounded-xl border bg-gradient-to-br from-amber-500/10 via-amber-500/5 to-transparent',\n 'border-amber-500/30 hover:border-amber-500/50 transition-colors',\n 'p-4 flex items-center gap-4 text-left',\n )}\n >\n <span className=\"flex items-center justify-center w-12 h-12 rounded-lg bg-amber-500/15 text-amber-600 dark:text-amber-400 shrink-0\">\n <KeyRound className=\"h-6 w-6\" />\n </span>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-base font-semibold mb-1\">No API key configured yet</h3>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">\n Register a provider in Settings before sending a message — otherwise the agent has\n nothing to talk to. Anthropic, OpenAI, Google, and any OpenAI-compatible endpoint all\n work.\n </p>\n </div>\n <span className=\"flex items-center gap-1 text-xs text-amber-600 dark:text-amber-400 font-medium shrink-0 group-hover:translate-x-0.5 transition-transform\">\n Open Settings <ArrowRight className=\"h-3.5 w-3.5\" />\n </span>\n </button>\n )}\n\n {/* Prompt cards */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n {CARDS.map((card) => {\n const Icon = card.icon;\n return (\n <div\n key={card.title}\n className=\"rounded-xl border bg-card/40 backdrop-blur-sm p-4 flex flex-col gap-3\"\n >\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'flex items-center justify-center w-8 h-8 rounded-lg border',\n card.tone,\n )}\n >\n <Icon className=\"h-4 w-4\" />\n </span>\n <div>\n <h3 className=\"text-sm font-semibold\">{card.title}</h3>\n <p className=\"text-xs text-muted-foreground\">{card.hint}</p>\n </div>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n {card.prompts.map((p, i) => (\n <button\n // biome-ignore lint/suspicious/noArrayIndexKey: static list\n key={i}\n type=\"button\"\n onClick={() => fillTextarea(p)}\n className=\"text-left text-xs leading-relaxed text-foreground/80 hover:text-foreground border border-transparent hover:border-border/60 rounded-lg px-3 py-2 hover:bg-muted/40 transition-colors line-clamp-3\"\n title={p}\n >\n {p}\n </button>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Recent sessions — one-click resume. We pull the most recent\n non-current sessions so the user can pick back up without leaving\n the welcome screen. Hidden when there's nothing to show (fresh\n install / first run). */}\n {recentSessions.length > 0 && (\n <div className=\"rounded-xl border bg-muted/20 p-4\">\n <div className=\"flex items-center gap-2 mb-3\">\n <ArchiveRestore className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Pick back up\n </span>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n {recentSessions.map((entry) => (\n <button\n key={entry.id}\n type=\"button\"\n onClick={() => resumeSession(entry.id)}\n className=\"text-left rounded-lg border border-border/40 bg-background/60 hover:border-primary/40 hover:bg-accent/30 px-3 py-2 transition-colors group/sess\"\n title={entry.title}\n >\n <div className=\"text-sm font-medium truncate text-foreground group-hover/sess:text-primary\">\n {sessionNicknames[entry.id] || entry.title || '(empty)'}\n </div>\n <div className=\"text-[10px] text-muted-foreground font-mono truncate mt-0.5\">\n {entry.provider}/{entry.model}\n {entry.tokenTotal > 0 && (\n <span className=\"ml-2\">· {entry.tokenTotal.toLocaleString()} tok</span>\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Recent prompts — your own past prompts as one-click refills. Slash\n commands aren't included (the quick-commands block below handles\n those). Shows nothing until you've actually typed something. */}\n {recentPrompts.length > 0 && (\n <div className=\"rounded-xl border bg-muted/20 p-4\">\n <div className=\"flex items-center gap-2 mb-3\">\n <Clock className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Recent prompts\n </span>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n {recentPrompts\n .filter((p) => !p.startsWith('/'))\n .slice(0, 5)\n .map((p, i) => (\n <button\n // biome-ignore lint/suspicious/noArrayIndexKey: static list\n key={i}\n type=\"button\"\n onClick={() => fillTextarea(p)}\n className=\"text-left text-xs leading-relaxed text-muted-foreground hover:text-foreground border border-transparent hover:border-border/60 rounded-lg px-3 py-2 hover:bg-background/60 transition-colors line-clamp-2\"\n title={p}\n >\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Slash command quick-ref */}\n <div className=\"rounded-xl border bg-muted/20 p-4\">\n <div className=\"flex items-center gap-2 mb-3\">\n <Keyboard className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Quick commands\n </span>\n </div>\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-2\">\n {SLASH_REFS.map((c) => (\n <button\n key={c.name}\n type=\"button\"\n onClick={() => fillTextarea(c.name)}\n className=\"text-left flex flex-col gap-0.5 rounded-md border border-border/40 bg-background/60 px-3 py-2 hover:border-primary/40 hover:bg-accent/40 transition-colors\"\n >\n <span className=\"font-mono text-xs text-foreground\">{c.name}</span>\n <span className=\"text-[11px] text-muted-foreground truncate\">{c.hint}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';\nimport * as React from 'react';\n\nconst ScrollArea = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n <ScrollAreaPrimitive.Root\n ref={ref}\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport className=\"h-full w-full rounded-[inherit]\">\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n));\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;\n\nconst ScrollBar = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none transition-colors',\n orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',\n orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',\n className,\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb className=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n));\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;\n\nexport { ScrollArea, ScrollBar };\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { useUIStore } from '@/stores';\nimport { AlertTriangle, FileEdit, Globe, ShieldAlert, Terminal, Wrench } from 'lucide-react';\nimport { useEffect } from 'react';\nimport { DiffView, diffFromToolInput } from './DiffView';\nimport { Button } from './ui/button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from './ui/dialog';\n\n/**\n * Pick the right hero icon for the tool being confirmed. Helps the user\n * see at a glance \"is this a file edit, a shell run, a network call?\".\n */\nfunction pickToolIcon(toolName: string) {\n if (/edit|write|create|patch/i.test(toolName)) return FileEdit;\n if (/bash|shell|exec|run|command/i.test(toolName)) return Terminal;\n if (/fetch|http|web|curl|request/i.test(toolName)) return Globe;\n return Wrench;\n}\n\n/**\n * Render the tool input intelligently. For edit/write we drop the JSON\n * dump and show a proper diff. For shell-like tools we surface the\n * command as a single mono line. Everything else falls back to JSON.\n */\nfunction SmartInputPreview({\n toolName,\n input,\n}: {\n toolName: string;\n input: unknown;\n}) {\n const diffArgs = diffFromToolInput(toolName, input);\n if (diffArgs) {\n return (\n <div className=\"rounded-lg overflow-hidden border\">\n <DiffView\n oldText={diffArgs.oldText}\n newText={diffArgs.newText}\n caption={diffArgs.caption}\n />\n </div>\n );\n }\n\n // Shell-like: pull out the command string so it shows as a real terminal\n // line instead of a JSON envelope.\n if (typeof input === 'object' && input !== null) {\n const obj = input as Record<string, unknown>;\n const cmd = (obj.command ?? obj.cmd ?? obj.script) as string | undefined;\n if (typeof cmd === 'string' && cmd.trim().length > 0) {\n return (\n <div className=\"rounded-lg border bg-background/40 overflow-hidden\">\n <div className=\"px-3 py-1.5 text-[10px] uppercase tracking-wider text-muted-foreground border-b bg-muted/40 flex items-center gap-1.5\">\n <Terminal className=\"h-3 w-3\" />\n <span>Command</span>\n </div>\n <pre className=\"px-3 py-2 text-xs font-mono whitespace-pre-wrap break-all max-h-40 overflow-auto\">\n ${cmd}\n </pre>\n </div>\n );\n }\n // url + method for fetch-like calls\n const url = obj.url as string | undefined;\n if (typeof url === 'string') {\n const method = (obj.method as string | undefined) ?? 'GET';\n return (\n <div className=\"rounded-lg border bg-background/40 px-3 py-2 text-xs font-mono\">\n <span className=\"text-muted-foreground\">{method.toUpperCase()}</span>{' '}\n <span className=\"break-all\">{url}</span>\n </div>\n );\n }\n }\n\n return (\n <div className=\"p-3 rounded-lg bg-muted/50 border text-xs font-mono\">\n <div className=\"text-muted-foreground mb-2\">Input:</div>\n <pre className=\"whitespace-pre-wrap break-all max-h-60 overflow-auto\">\n {JSON.stringify(input, null, 2)}\n </pre>\n </div>\n );\n}\n\nexport function ConfirmDialog() {\n const { showConfirmDialog, confirmInfo, hideConfirm } = useUIStore();\n const { sendConfirm } = useWebSocket();\n\n const handleConfirm = (decision: 'yes' | 'no' | 'always' | 'deny') => {\n if (confirmInfo) {\n sendConfirm(confirmInfo.id, decision);\n }\n hideConfirm();\n };\n\n // Keyboard shortcuts inside the dialog: y = yes, n = no, a = always,\n // Esc = no (dismiss). Matches the CLI permission prompt so habits\n // transfer directly.\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyboard handler; handleConfirm stable\n useEffect(() => {\n if (!showConfirmDialog) return;\n const onKey = (e: KeyboardEvent) => {\n const target = e.target as HTMLElement | null;\n const tag = target?.tagName?.toLowerCase();\n if (tag === 'input' || tag === 'textarea') return;\n if (e.key === 'y' || e.key === 'Y') {\n e.preventDefault();\n handleConfirm('yes');\n } else if (e.key === 'n' || e.key === 'N' || e.key === 'Escape') {\n e.preventDefault();\n handleConfirm('no');\n } else if (e.key === 'a' || e.key === 'A') {\n e.preventDefault();\n handleConfirm('always');\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [showConfirmDialog, confirmInfo?.id]);\n\n if (!confirmInfo) {\n return (\n <Dialog open={showConfirmDialog} onOpenChange={() => hideConfirm()}>\n <DialogContent />\n </Dialog>\n );\n }\n\n const Icon = pickToolIcon(confirmInfo.toolName);\n const isEdit = /edit|write/i.test(confirmInfo.toolName);\n\n return (\n <Dialog open={showConfirmDialog} onOpenChange={() => hideConfirm()}>\n <DialogContent className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <ShieldAlert className=\"h-5 w-5 text-yellow-500\" />\n Confirm: {confirmInfo.toolName}\n </DialogTitle>\n <DialogDescription>\n The agent wants to {isEdit ? 'modify a file' : 'run this tool'}. Review the request\n below and decide whether to proceed.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"py-2 space-y-3\">\n <div className=\"flex items-center gap-3 p-3 rounded-lg bg-muted\">\n <Icon className=\"h-5 w-5 text-muted-foreground\" />\n <div className=\"min-w-0\">\n <div className=\"font-medium font-mono truncate\">{confirmInfo.toolName}</div>\n <div className=\"text-xs text-muted-foreground\">\n {isEdit ? 'File modification' : 'Tool execution'} — preview below\n </div>\n </div>\n </div>\n\n {confirmInfo.input !== undefined && (\n <SmartInputPreview toolName={confirmInfo.toolName} input={confirmInfo.input} />\n )}\n\n {confirmInfo.suggestedPattern && (\n <div className=\"flex items-start gap-2 p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20\">\n <AlertTriangle className=\"h-4 w-4 text-yellow-600 mt-0.5 shrink-0\" />\n <div className=\"text-sm min-w-0\">\n <div className=\"font-medium text-yellow-800 dark:text-yellow-200\">\n Trust pattern suggestion\n </div>\n <div className=\"font-mono text-xs mt-1 break-all\">\n {confirmInfo.suggestedPattern}\n </div>\n <div className=\"text-xs text-muted-foreground mt-1\">\n Picking <span className=\"font-medium\">Always</span> will whitelist matching calls\n for this project.\n </div>\n </div>\n </div>\n )}\n </div>\n\n <DialogFooter className=\"gap-2 sm:gap-2 flex-wrap\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleConfirm('deny')}\n title=\"Reject this and all future calls matching the pattern\"\n >\n Deny always\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleConfirm('no')}\n title=\"Reject this single call (Esc / n)\"\n >\n No <kbd className=\"ml-1 text-[10px] border rounded px-1 bg-background\">n</kbd>\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleConfirm('always')}\n title=\"Approve and remember the pattern for the project (a)\"\n >\n Always <kbd className=\"ml-1 text-[10px] border rounded px-1 bg-background\">a</kbd>\n </Button>\n <Button\n size=\"sm\"\n onClick={() => handleConfirm('yes')}\n title=\"Approve this single call (y)\"\n >\n Yes <kbd className=\"ml-1 text-[10px] border rounded px-1 bg-background/80\">y</kbd>\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import { cn } from '@/lib/utils';\nimport * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { X } from 'lucide-react';\nimport * as React from 'react';\n\nconst Dialog = DialogPrimitive.Root;\nconst DialogTrigger = DialogPrimitive.Trigger;\nconst DialogPortal = DialogPrimitive.Portal;\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n className,\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} />\n);\nDialogHeader.displayName = 'DialogHeader';\n\nconst DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}\n {...props}\n />\n);\nDialogFooter.displayName = 'DialogFooter';\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-lg font-semibold leading-none tracking-tight', className)}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n","import { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore } from '@/stores';\nimport { Loader2, RotateCcw, WifiOff, X } from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\n/**\n * Prominent connection-lost banner. The ConnectionChip in the topbar is\n * easy to miss when the user is heads-down on the chat; this banner\n * stretches across the top, blocks the visual flow, and offers a \"retry\n * now\" button. Auto-hides as soon as the socket comes back open.\n *\n * Dismissable for the current outage (X button) — once dismissed, only\n * comes back if the socket recovers and then drops again. The chip in\n * the topbar still reflects the live state so the user isn't fully blind\n * after dismissing.\n */\nexport function ConnectionBanner() {\n const wsStatus = useConfigStore((s) => s.wsStatus);\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const [dismissed, setDismissed] = useState(false);\n const [now, setNow] = useState(Date.now());\n\n // Live retry countdown while reconnecting.\n useEffect(() => {\n if (wsStatus.state !== 'reconnecting') return;\n const id = setInterval(() => setNow(Date.now()), 500);\n return () => clearInterval(id);\n }, [wsStatus.state]);\n\n // Reset the dismissal flag every time the socket recovers — so the *next*\n // disconnection re-shows the banner. Without this the user would have to\n // refresh after dismissing to ever see the banner again.\n useEffect(() => {\n if (wsStatus.state === 'open') setDismissed(false);\n }, [wsStatus.state]);\n\n if (wsStatus.state === 'open' || wsStatus.state === 'connecting') return null;\n if (dismissed) return null;\n\n const retry = () => getWSClient(wsUrl).retryNow();\n const isReconnecting = wsStatus.state === 'reconnecting';\n const errorText =\n wsStatus.state === 'closed'\n ? wsStatus.error\n : wsStatus.state === 'reconnecting'\n ? wsStatus.lastError\n : undefined;\n const remaining = isReconnecting\n ? Math.max(0, Math.ceil((wsStatus.nextRetryAt - now) / 1000))\n : 0;\n\n return (\n <div\n className={cn(\n 'flex items-center gap-3 px-4 py-2 border-b text-sm',\n isReconnecting\n ? 'bg-orange-500/10 text-orange-700 dark:text-orange-300 border-orange-500/30'\n : 'bg-red-500/10 text-red-700 dark:text-red-300 border-red-500/30',\n )}\n >\n {isReconnecting ? (\n <Loader2 className=\"h-4 w-4 animate-spin shrink-0\" />\n ) : (\n <WifiOff className=\"h-4 w-4 shrink-0\" />\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"font-medium\">\n {isReconnecting\n ? `Reconnecting to backend (attempt ${wsStatus.attempt}) — retrying in ${remaining}s`\n : 'Disconnected from backend'}\n </div>\n {errorText && <div className=\"text-xs opacity-80 truncate\">{errorText}</div>}\n </div>\n <button\n type=\"button\"\n onClick={retry}\n className={cn(\n 'inline-flex items-center gap-1 px-2.5 py-1 rounded-md border text-xs font-medium',\n 'hover:bg-background/30 transition-colors shrink-0',\n isReconnecting ? 'border-orange-500/40' : 'border-red-500/40',\n )}\n title=\"Retry connection now\"\n >\n <RotateCcw className=\"h-3 w-3\" />\n Retry now\n </button>\n <button\n type=\"button\"\n onClick={() => setDismissed(true)}\n className=\"text-current/60 hover:text-current shrink-0\"\n title=\"Dismiss (chip in topbar still shows status)\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore, useUIStore } from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport { ArrowRight, Cpu, Search } from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\ninterface SavedProvider {\n id: string;\n apiKeys: Array<{ label: string; isActive: boolean }>;\n}\ninterface CatalogModel {\n id: string;\n name: string;\n contextWindow?: number;\n}\n\n/**\n * Ctrl/Cmd+M — flat searchable provider/model picker. Drops a 3-click\n * trip through Settings down to one shortcut. Pulls the list of *saved*\n * providers (the ones that actually have a registered key) and lazy-loads\n * each provider's models when the overlay opens. The active model is\n * highlighted; Enter switches via the existing model.switch WS handler\n * (which atomically swaps provider+model on the backend).\n */\nexport function QuickModelSwitcher() {\n const open = useUIStore((s) => s.modelSwitcherOpen);\n const setOpen = useUIStore((s) => s.setModelSwitcherOpen);\n const [query, setQuery] = useState('');\n const [selected, setSelected] = useState(0);\n const [saved, setSaved] = useState<SavedProvider[]>([]);\n const [modelsByProvider, setModelsByProvider] = useState<Record<string, CatalogModel[]>>({});\n const inputRef = useRef<HTMLInputElement>(null);\n\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const currentProvider = useConfigStore((s) => s.provider);\n const currentModel = useConfigStore((s) => s.model);\n const paletteOpen = useUIStore((s) => s.paletteOpen);\n const ws = useWebSocket();\n\n // Ctrl/Cmd+M opens. Skip when the command palette is already open so\n // the two overlays don't fight for focus.\n // biome-ignore lint/correctness/useExhaustiveDependencies: setOpen is stable\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.key.toLowerCase() === 'm' && !e.shiftKey && !e.altKey) {\n if (paletteOpen) return;\n e.preventDefault();\n setOpen(!open);\n return;\n }\n if (e.key === 'Escape' && open) {\n e.preventDefault();\n setOpen(false);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [open, paletteOpen]);\n\n // Wire up WS listeners + fetch on open. We listen unconditionally so a\n // late response (e.g. the user opened then closed before models arrived)\n // still populates state for the next open.\n useEffect(() => {\n const client = getWSClient(wsUrl);\n const offSaved = client.on('providers.saved', (msg: WSServerMessage) => {\n const p = msg.payload as { providers: SavedProvider[] };\n setSaved(p.providers ?? []);\n });\n const offModels = client.on('provider.models', (msg: WSServerMessage) => {\n const p = msg.payload as { provider: string; models: CatalogModel[] };\n setModelsByProvider((prev) => ({ ...prev, [p.provider]: p.models }));\n });\n return () => {\n offSaved();\n offModels();\n };\n }, [wsUrl]);\n\n useEffect(() => {\n if (!open) return;\n setQuery('');\n setSelected(0);\n ws.listSavedProviders();\n // Auto-focus the search input after the dialog paints. requestAnimationFrame\n // because the input ref isn't mounted on the same tick we flip `open`.\n requestAnimationFrame(() => inputRef.current?.focus());\n }, [open, ws]);\n\n // Lazy-load models per saved provider once we know what's saved.\n useEffect(() => {\n if (!open) return;\n for (const sp of saved) {\n if (!modelsByProvider[sp.id]) {\n ws.listProviderModels(sp.id);\n }\n }\n }, [open, saved, modelsByProvider, ws]);\n\n /** Flatten into a single list of {provider, model} candidates, then apply\n * the search filter. The active row floats to the top so the user can\n * see what they're currently on. */\n const candidates = useMemo(() => {\n const list: Array<{\n provider: string;\n model: string;\n modelName: string;\n contextWindow?: number;\n isCurrent: boolean;\n }> = [];\n for (const sp of saved) {\n const models = modelsByProvider[sp.id] ?? [];\n for (const m of models) {\n list.push({\n provider: sp.id,\n model: m.id,\n modelName: m.name || m.id,\n contextWindow: m.contextWindow,\n isCurrent: sp.id === currentProvider && m.id === currentModel,\n });\n }\n }\n const q = query.toLowerCase().trim();\n const filtered = q\n ? list.filter(\n (c) =>\n c.provider.toLowerCase().includes(q) ||\n c.model.toLowerCase().includes(q) ||\n c.modelName.toLowerCase().includes(q),\n )\n : list;\n return filtered.sort((a, b) => {\n if (a.isCurrent !== b.isCurrent) return a.isCurrent ? -1 : 1;\n return a.provider.localeCompare(b.provider) || a.model.localeCompare(b.model);\n });\n }, [saved, modelsByProvider, query, currentProvider, currentModel]);\n\n useEffect(() => {\n if (selected >= candidates.length) setSelected(0);\n }, [candidates.length, selected]);\n\n const commit = (idx: number) => {\n const pick = candidates[idx];\n if (!pick) return;\n ws.switchModel(pick.provider, pick.model);\n setOpen(false);\n };\n\n if (!open) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-start justify-center bg-background/60 backdrop-blur-sm pt-[15vh]\"\n onClick={(e) => {\n if (e.target === e.currentTarget) setOpen(false);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(false);\n }}\n >\n <div className=\"w-full max-w-xl rounded-xl border bg-popover shadow-2xl overflow-hidden\">\n <div className=\"flex items-center gap-2 border-b px-3 py-2\">\n <Search className=\"h-4 w-4 text-muted-foreground\" />\n <input\n ref={inputRef}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value);\n setSelected(0);\n }}\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setSelected((i) => Math.min(candidates.length - 1, i + 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setSelected((i) => Math.max(0, i - 1));\n } else if (e.key === 'Enter') {\n e.preventDefault();\n commit(selected);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setOpen(false);\n }\n }}\n placeholder=\"Filter providers and models…\"\n className=\"flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground\"\n />\n <span className=\"text-[10px] text-muted-foreground font-mono\">↑↓ · Enter · Esc</span>\n </div>\n <div className=\"max-h-[50vh] overflow-y-auto py-1\">\n {candidates.length === 0 ? (\n <div className=\"px-4 py-8 text-center text-sm text-muted-foreground\">\n {saved.length === 0\n ? 'No saved providers — register a key in Settings first.'\n : 'Loading models…'}\n </div>\n ) : (\n candidates.map((c, idx) => (\n <button\n type=\"button\"\n key={`${c.provider}:${c.model}`}\n onClick={() => commit(idx)}\n onMouseEnter={() => setSelected(idx)}\n className={cn(\n 'w-full flex items-center gap-3 px-3 py-2 text-left text-sm transition-colors',\n idx === selected ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/40',\n c.isCurrent && 'font-medium',\n )}\n >\n <Cpu\n className={cn(\n 'h-4 w-4 shrink-0',\n c.isCurrent ? 'text-primary' : 'text-muted-foreground',\n )}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate\">\n <span className=\"text-muted-foreground\">{c.provider}</span>\n <span className=\"mx-1 text-muted-foreground/40\">·</span>\n <span>{c.modelName}</span>\n </div>\n {c.contextWindow && (\n <div className=\"text-[10px] text-muted-foreground font-mono\">\n {c.model} · ctx {c.contextWindow.toLocaleString()}\n </div>\n )}\n </div>\n {c.isCurrent ? (\n <span className=\"text-[10px] uppercase tracking-wide text-primary font-semibold\">\n active\n </span>\n ) : (\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground opacity-0 group-hover:opacity-100\" />\n )}\n </button>\n ))\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useConfigStore, useUIStore } from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport {\n AlertCircle,\n CheckCircle2,\n Cpu,\n Eye,\n EyeOff,\n Globe,\n Key,\n Loader2,\n Monitor,\n Moon,\n Network,\n Palette,\n Plus,\n RefreshCw,\n Sun,\n Trash2,\n X,\n} from 'lucide-react';\nimport React, { useState, useEffect, useCallback } from 'react';\nimport { useTheme } from './ThemeProvider';\nimport { Button } from './ui/button';\nimport { Input } from './ui/input';\nimport { ScrollArea } from './ui/scroll-area';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';\n\ninterface CatalogProvider {\n id: string;\n name: string;\n family: string;\n apiBase?: string;\n envVars: string[];\n modelCount: number;\n hasApiKey: boolean;\n}\n\ninterface CatalogModel {\n id: string;\n name: string;\n releaseDate?: string;\n contextWindow?: number;\n inputCost?: number;\n outputCost?: number;\n capabilities: string[];\n}\n\ninterface SavedProvider {\n id: string;\n family?: string;\n baseUrl?: string;\n apiKeys: Array<{\n label: string;\n maskedKey: string;\n isActive: boolean;\n createdAt: string;\n }>;\n}\n\ntype ProviderTab = 'catalog' | 'saved';\n\nexport function SettingsPanel() {\n const { setCurrentView } = useUIStore();\n const { provider, model, setProvider, setModel, wsConnected } = useConfigStore();\n const { theme, setTheme } = useTheme();\n const ws = useWebSocket();\n const wsClient = ws.client;\n const listProviders = ws.listProviders;\n const listSavedProviders = ws.listSavedProviders;\n\n // Catalog data\n const [catalogProviders, setCatalogProviders] = useState<CatalogProvider[]>([]);\n const [catalogModels, setCatalogModels] = useState<Record<string, CatalogModel[]>>({});\n const [savedProviders, setSavedProviders] = useState<SavedProvider[]>([]);\n const [isLoadingCatalog, setIsLoadingCatalog] = useState(false);\n const [isLoadingModels, setIsLoadingModels] = useState(false);\n const [isLoadingSaved, setIsLoadingSaved] = useState(false);\n const [operationStatus, setOperationStatus] = useState<{\n success: boolean;\n message: string;\n } | null>(null);\n\n // Provider tab selection\n const [providerTab, setProviderTab] = useState<ProviderTab>('catalog');\n\n // New key form\n const [showAddKeyForm, setShowAddKeyForm] = useState<string | null>(null);\n const [newKeyLabel, setNewKeyLabel] = useState('');\n const [newKeyValue, setNewKeyValue] = useState('');\n const [showNewKeyValue, setShowNewKeyValue] = useState(false);\n\n // New provider form\n const [showAddProviderForm, setShowAddProviderForm] = useState(false);\n const [newProviderId, setNewProviderId] = useState('');\n const [newProviderFamily, setNewProviderFamily] = useState('openai-compatible');\n const [newProviderBaseUrl, setNewProviderBaseUrl] = useState('');\n const [newProviderApiKey, setNewProviderApiKey] = useState('');\n\n // Current provider config from catalog\n const currentCatalogProvider = catalogProviders.find((p) => p.id === provider);\n\n // Load catalog and saved providers on mount\n useEffect(() => {\n const handleProviderCatalog = (msg: WSServerMessage) => {\n if (msg.type === 'provider.catalog') {\n const payload = msg.payload as { providers: CatalogProvider[] };\n setCatalogProviders(payload.providers.sort((a, b) => a.id.localeCompare(b.id)));\n setIsLoadingCatalog(false);\n }\n };\n\n const handleProviderModels = (msg: WSServerMessage) => {\n if (msg.type === 'provider.models') {\n const payload = msg.payload as { provider: string; models: CatalogModel[] };\n setCatalogModels((prev) => ({ ...prev, [payload.provider]: payload.models }));\n setIsLoadingModels(false);\n }\n };\n\n const handleSavedProviders = (msg: WSServerMessage) => {\n if (msg.type === 'providers.saved') {\n const payload = msg.payload as { providers: SavedProvider[] };\n const next = payload.providers.sort((a, b) => a.id.localeCompare(b.id));\n setSavedProviders(next);\n setIsLoadingSaved(false);\n // If the user already has registered accounts (the common case after\n // running `wstack auth`), open the Saved tab automatically. Otherwise\n // most of their data would sit one click away on a tab they never\n // visit, behind a catalog list that doesn't include their aliases\n // (e.g. \"minimax-coding-plan\" isn't in models.dev).\n if (next.length > 0) setProviderTab('saved');\n }\n };\n\n const handleKeyOperationResult = (msg: WSServerMessage) => {\n if (msg.type === 'key.operation_result') {\n const payload = msg.payload as { success: boolean; message: string };\n setOperationStatus(payload);\n setTimeout(() => setOperationStatus(null), 3000);\n // Refresh saved providers after operation\n listSavedProviders?.();\n }\n };\n\n // Only subscribe / fetch once the underlying WS is actually connected.\n // SettingsPanel mounts before the WebSocket finishes its handshake, so\n // ws.client is null on the first render and silently dropping the\n // listProviders() call left users staring at \"Saved (0)\" forever. Keying\n // the effect on wsConnected re-runs it the moment the socket comes up.\n if (!wsConnected || !wsClient) return;\n\n const off1 = wsClient.on('provider.catalog', handleProviderCatalog);\n const off2 = wsClient.on('provider.models', handleProviderModels);\n const off3 = wsClient.on('providers.saved', handleSavedProviders);\n const off4 = wsClient.on('key.operation_result', handleKeyOperationResult);\n\n setIsLoadingCatalog(true);\n setIsLoadingSaved(true);\n listProviders?.();\n listSavedProviders?.();\n\n return () => {\n off1?.();\n off2?.();\n off3?.();\n off4?.();\n };\n }, [wsConnected, wsClient, listProviders, listSavedProviders]);\n\n // Selecting a provider just loads its model list and stages the pick locally.\n // The actual backend switch fires when the user picks a model — that's the\n // single point where (provider, model) both have meaningful values to send.\n const handleProviderSelect = useCallback(\n (providerId: string) => {\n setProvider(providerId);\n if (!catalogModels[providerId]) {\n setIsLoadingModels(true);\n ws.listProviderModels?.(providerId);\n }\n },\n [catalogModels, setProvider, ws],\n );\n\n const handleModelSelect = useCallback(\n (modelId: string) => {\n setModel(modelId);\n // Tell the backend to actually swap the agent's provider+model. Backend\n // will rebuild the provider instance, persist the choice to the config\n // file, and broadcast a fresh session.start — useWebSocket's session.start\n // handler then re-syncs our config store so the chip stays in sync.\n ws.switchModel?.(provider, modelId);\n setOperationStatus({ success: true, message: `Switching to ${provider} / ${modelId}…` });\n },\n [setModel, ws, provider],\n );\n\n const handleAddKey = useCallback(\n (providerId: string) => {\n if (!newKeyLabel.trim() || !newKeyValue.trim()) return;\n ws.addKey?.(providerId, newKeyLabel.trim(), newKeyValue.trim());\n setNewKeyLabel('');\n setNewKeyValue('');\n setShowAddKeyForm(null);\n },\n [ws, newKeyLabel, newKeyValue],\n );\n\n const handleDeleteKey = useCallback(\n (providerId: string, label: string) => {\n ws.deleteKey?.(providerId, label);\n },\n [ws],\n );\n\n const handleSetActiveKey = useCallback(\n (providerId: string, label: string) => {\n ws.setActiveKey?.(providerId, label);\n },\n [ws],\n );\n\n const handleAddProvider = useCallback(() => {\n if (!newProviderId.trim()) return;\n ws.addProvider?.(\n newProviderId.trim(),\n newProviderFamily,\n newProviderBaseUrl || undefined,\n newProviderApiKey || undefined,\n );\n setNewProviderId('');\n setNewProviderFamily('openai-compatible');\n setNewProviderBaseUrl('');\n setNewProviderApiKey('');\n setShowAddProviderForm(false);\n }, [ws, newProviderId, newProviderFamily, newProviderBaseUrl, newProviderApiKey]);\n\n const handleRemoveProvider = useCallback(\n (providerId: string) => {\n ws.removeProvider?.(providerId);\n },\n [ws],\n );\n\n // Group catalog by family, with optional text filter applied first. 115+\n // providers in the catalog made scrolling alone impractical.\n const [catalogQuery, setCatalogQuery] = useState('');\n const families = ['anthropic', 'openai', 'google', 'openai-compatible'] as const;\n const filteredCatalog = catalogQuery.trim()\n ? catalogProviders.filter((p) => {\n const q = catalogQuery.trim().toLowerCase();\n return (\n p.id.toLowerCase().includes(q) ||\n p.name.toLowerCase().includes(q) ||\n p.family.toLowerCase().includes(q)\n );\n })\n : catalogProviders;\n const catalogByFamily = filteredCatalog.reduce(\n (acc, p) => {\n if (!acc[p.family]) acc[p.family] = [];\n acc[p.family]!.push(p);\n return acc;\n },\n {} as Record<string, CatalogProvider[]>,\n );\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Header */}\n <header className=\"flex items-center justify-between px-4 py-3 border-b bg-card shrink-0\">\n <h1 className=\"text-lg font-semibold\">Settings</h1>\n <Button variant=\"ghost\" size=\"icon\" onClick={() => setCurrentView('chat')}>\n <X className=\"h-4 w-4\" />\n </Button>\n </header>\n\n {/* Content */}\n <ScrollArea className=\"flex-1\">\n <div className=\"p-6 max-w-2xl mx-auto\">\n <Tabs defaultValue=\"provider\">\n <TabsList className=\"w-full justify-start mb-6 grid grid-cols-4\">\n <TabsTrigger value=\"provider\" className=\"gap-2\">\n <Network className=\"h-4 w-4\" />\n Provider\n </TabsTrigger>\n <TabsTrigger value=\"model\" className=\"gap-2\">\n <Cpu className=\"h-4 w-4\" />\n Model\n </TabsTrigger>\n <TabsTrigger value=\"connection\" className=\"gap-2\">\n <Globe className=\"h-4 w-4\" />\n Connection\n </TabsTrigger>\n <TabsTrigger value=\"appearance\" className=\"gap-2\">\n <Palette className=\"h-4 w-4\" />\n Appearance\n </TabsTrigger>\n </TabsList>\n\n {/* Provider Tab */}\n <TabsContent value=\"provider\" className=\"space-y-4\">\n {/* Provider source toggle */}\n <div className=\"flex gap-2 mb-4\">\n <Button\n variant={providerTab === 'catalog' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setProviderTab('catalog')}\n >\n <Globe className=\"h-4 w-4 mr-1\" />\n Catalog\n </Button>\n <Button\n variant={providerTab === 'saved' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setProviderTab('saved')}\n >\n <Key className=\"h-4 w-4 mr-1\" />\n Saved ({savedProviders.length})\n </Button>\n </div>\n\n {operationStatus && (\n <div\n className={cn(\n 'p-3 rounded-lg mb-4 flex items-center gap-2',\n operationStatus.success\n ? 'bg-green-500/10 text-green-600'\n : 'bg-red-500/10 text-red-600',\n )}\n >\n {operationStatus.success ? (\n <CheckCircle2 className=\"h-4 w-4\" />\n ) : (\n <AlertCircle className=\"h-4 w-4\" />\n )}\n {operationStatus.message}\n </div>\n )}\n\n {/* Catalog View */}\n {providerTab === 'catalog' && (\n <div className=\"space-y-4\">\n {/* Search */}\n <Input\n placeholder={`Search ${catalogProviders.length} providers (name / id / family)…`}\n value={catalogQuery}\n onChange={(e) => setCatalogQuery(e.target.value)}\n className=\"text-sm\"\n />\n {isLoadingCatalog && catalogProviders.length === 0 ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-muted-foreground\">Loading catalog...</span>\n </div>\n ) : filteredCatalog.length === 0 && catalogQuery ? (\n <div className=\"text-center py-8 text-muted-foreground text-sm\">\n No providers match \"<span className=\"font-mono\">{catalogQuery}</span>\".\n </div>\n ) : (\n <>\n {families.map((family) => {\n const providers = catalogByFamily[family];\n if (!providers?.length) return null;\n return (\n <div key={family} className=\"space-y-2\">\n <h3 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">\n {family}\n </h3>\n <div className=\"grid grid-cols-1 gap-2\">\n {providers.map((p) => (\n <button\n type=\"button\"\n key={p.id}\n onClick={() => handleProviderSelect(p.id)}\n className={cn(\n 'flex flex-col items-start p-3 rounded-lg border text-left transition-all',\n provider === p.id\n ? 'border-primary bg-primary/5 ring-2 ring-primary/20'\n : 'border-border hover:bg-muted',\n )}\n >\n <div className=\"flex w-full justify-between items-start\">\n <div>\n <span className=\"font-medium\">{p.name}</span>\n <span className=\"ml-2 text-xs text-muted-foreground\">\n ({p.id})\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n {p.hasApiKey && (\n <span className=\"text-xs bg-green-500/10 text-green-600 px-2 py-0.5 rounded\">\n <Key className=\"h-3 w-3 inline mr-1\" />\n Configured\n </span>\n )}\n {p.envVars[0] && (\n <span className=\"text-xs text-muted-foreground\">\n ENV: {p.envVars[0]}\n </span>\n )}\n {provider === p.id && (\n <CheckCircle2 className=\"h-4 w-4 text-primary\" />\n )}\n </div>\n </div>\n <div className=\"text-xs text-muted-foreground mt-1\">\n {p.modelCount} models\n {p.apiBase && ` · ${p.apiBase}`}\n </div>\n </button>\n ))}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n )}\n\n {/* Saved Providers View */}\n {providerTab === 'saved' && (\n <div className=\"space-y-4\">\n <div className=\"flex justify-between items-center\">\n <p className=\"text-sm text-muted-foreground\">\n Manage your API keys and provider configurations\n </p>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setShowAddProviderForm(!showAddProviderForm)}\n >\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Provider\n </Button>\n </div>\n\n {/* Add Provider Form */}\n {showAddProviderForm && (\n <div className=\"p-4 border rounded-lg space-y-3 bg-muted/50\">\n <h4 className=\"font-medium\">Add Custom Provider</h4>\n <Input\n placeholder=\"Provider ID (e.g. my-llm-server)\"\n value={newProviderId}\n onChange={(e) => setNewProviderId(e.target.value)}\n />\n <select\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={newProviderFamily}\n onChange={(e) => setNewProviderFamily(e.target.value)}\n >\n <option value=\"anthropic\">Anthropic</option>\n <option value=\"openai\">OpenAI</option>\n <option value=\"openai-compatible\">OpenAI Compatible</option>\n <option value=\"google\">Google</option>\n </select>\n <Input\n placeholder=\"Base URL (optional, e.g. http://localhost:11434/v1)\"\n value={newProviderBaseUrl}\n onChange={(e) => setNewProviderBaseUrl(e.target.value)}\n />\n <Input\n type=\"password\"\n placeholder=\"API Key (optional)\"\n value={newProviderApiKey}\n onChange={(e) => setNewProviderApiKey(e.target.value)}\n />\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n onClick={handleAddProvider}\n disabled={!newProviderId.trim()}\n >\n Add\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setShowAddProviderForm(false)}\n >\n Cancel\n </Button>\n </div>\n </div>\n )}\n\n {isLoadingSaved ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n ) : savedProviders.length === 0 ? (\n <div className=\"text-center py-8 text-muted-foreground\">\n <Key className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No saved providers yet</p>\n <p className=\"text-sm\">Add a provider to get started</p>\n </div>\n ) : (\n savedProviders.map((sp) => (\n <div key={sp.id} className=\"border rounded-lg p-4 space-y-3\">\n <div className=\"flex justify-between items-start\">\n <div>\n <h4 className=\"font-medium\">{sp.id}</h4>\n {sp.family && (\n <span className=\"text-xs text-muted-foreground\">{sp.family}</span>\n )}\n </div>\n <div className=\"flex gap-2\">\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => handleRemoveProvider(sp.id)}\n >\n <Trash2 className=\"h-4 w-4 text-destructive\" />\n </Button>\n </div>\n </div>\n\n {sp.baseUrl && (\n <div className=\"text-xs text-muted-foreground\">\n <Globe className=\"h-3 w-3 inline mr-1\" />\n {sp.baseUrl}\n </div>\n )}\n\n {/* API Keys */}\n <div className=\"space-y-2\">\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm font-medium\">API Keys</span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() =>\n setShowAddKeyForm(showAddKeyForm === sp.id ? null : sp.id)\n }\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n Add Key\n </Button>\n </div>\n\n {sp.apiKeys.length === 0 && !showAddKeyForm && (\n <p className=\"text-xs text-muted-foreground\">No keys configured</p>\n )}\n\n {sp.apiKeys.map((key) => (\n <div\n key={key.label}\n className=\"flex items-center justify-between p-2 bg-muted/50 rounded\"\n >\n <div>\n <span className=\"text-sm font-medium\">{key.label}</span>\n {key.isActive && (\n <span className=\"ml-2 text-xs bg-green-500/10 text-green-600 px-1.5 py-0.5 rounded\">\n Active\n </span>\n )}\n <div className=\"text-xs text-muted-foreground font-mono\">\n {key.maskedKey}\n </div>\n </div>\n <div className=\"flex gap-1\">\n {!key.isActive && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => handleSetActiveKey(sp.id, key.label)}\n >\n Set Active\n </Button>\n )}\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => handleDeleteKey(sp.id, key.label)}\n >\n <Trash2 className=\"h-3 w-3 text-destructive\" />\n </Button>\n </div>\n </div>\n ))}\n\n {/* Add Key Form */}\n {showAddKeyForm === sp.id && (\n <div className=\"p-3 border rounded space-y-2 bg-background\">\n <Input\n placeholder=\"Key label (e.g. default, production)\"\n value={newKeyLabel}\n onChange={(e) => setNewKeyLabel(e.target.value)}\n />\n <div className=\"flex gap-2\">\n <Input\n type={showNewKeyValue ? 'text' : 'password'}\n placeholder=\"API key\"\n value={newKeyValue}\n onChange={(e) => setNewKeyValue(e.target.value)}\n />\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => setShowNewKeyValue(!showNewKeyValue)}\n >\n {showNewKeyValue ? (\n <EyeOff className=\"h-4 w-4\" />\n ) : (\n <Eye className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n onClick={() => handleAddKey(sp.id)}\n disabled={!newKeyLabel.trim() || !newKeyValue.trim()}\n >\n Save Key\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setShowAddKeyForm(null);\n setNewKeyLabel('');\n setNewKeyValue('');\n }}\n >\n Cancel\n </Button>\n </div>\n </div>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n )}\n </TabsContent>\n\n {/* Model Tab */}\n <TabsContent value=\"model\" className=\"space-y-4\">\n {provider ? (\n <>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {currentCatalogProvider?.name || provider}\n </p>\n <p className=\"text-xs text-muted-foreground\">{provider}</p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setIsLoadingModels(true);\n ws.listProviderModels?.(provider);\n }}\n >\n <RefreshCw className={cn('h-4 w-4', isLoadingModels && 'animate-spin')} />\n </Button>\n </div>\n\n {isLoadingModels && !catalogModels[provider] ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-muted-foreground\">Loading models...</span>\n </div>\n ) : (\n <div className=\"space-y-1\">\n {(catalogModels[provider] || []).map((m) => (\n <button\n type=\"button\"\n key={m.id}\n onClick={() => handleModelSelect(m.id)}\n className={cn(\n 'w-full flex items-center justify-between p-3 rounded-lg border text-left transition-all',\n model === m.id\n ? 'border-primary bg-primary/5 ring-2 ring-primary/20'\n : 'border-border hover:bg-muted',\n )}\n >\n <div>\n <span className=\"font-medium\">{m.name || m.id}</span>\n <div className=\"flex gap-2 mt-1\">\n {m.capabilities.map((cap) => (\n <span key={cap} className=\"text-xs bg-muted px-1.5 py-0.5 rounded\">\n {cap}\n </span>\n ))}\n </div>\n </div>\n <div className=\"text-right text-xs text-muted-foreground\">\n {m.contextWindow && <div>{m.contextWindow / 1000}k context</div>}\n {m.inputCost && m.outputCost && (\n <div>\n ${m.inputCost}/${m.outputCost}\n </div>\n )}\n {model === m.id && (\n <CheckCircle2 className=\"h-4 w-4 text-primary mt-1\" />\n )}\n </div>\n </button>\n ))}\n\n {catalogModels[provider]?.length === 0 && (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n No models found for this provider. The catalog might be empty or still\n loading.\n </p>\n )}\n </div>\n )}\n </>\n ) : (\n <div className=\"text-center py-8 text-muted-foreground\">\n <Cpu className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>Select a provider first</p>\n </div>\n )}\n </TabsContent>\n\n {/* Connection Tab */}\n <TabsContent value=\"connection\" className=\"space-y-4\">\n <div className=\"space-y-3\">\n <label\n htmlFor=\"websocket-url\"\n className=\"text-sm font-medium flex items-center gap-2\"\n >\n <Globe className=\"h-4 w-4 text-muted-foreground\" />\n WebSocket Server URL\n </label>\n <Input\n id=\"websocket-url\"\n value={useConfigStore.getState().wsUrl}\n onChange={(e) => useConfigStore.getState().setConfig({ wsUrl: e.target.value })}\n placeholder=\"ws://localhost:3457\"\n className=\"font-mono text-sm\"\n />\n <p className=\"text-xs text-muted-foreground\">\n URL of the WrongStack WebSocket server. The server runs alongside the CLI.\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border bg-muted/50\">\n <h4 className=\"text-sm font-medium mb-2\">Starting the WebSocket Server</h4>\n <p className=\"text-xs text-muted-foreground mb-3\">\n Standalone: run <code className=\"bg-muted px-1 py-0.5 rounded\">./dev.ps1</code>{' '}\n from the repo root, or set WS_HOST/WS_PORT before launching{' '}\n <code className=\"bg-muted px-1 py-0.5 rounded\">\n node packages/webui/dist/server/entry.js\n </code>\n . Or alongside the CLI:{' '}\n <code className=\"bg-muted px-1 py-0.5 rounded\">wstack --webui</code>.\n </p>\n </div>\n </TabsContent>\n\n {/* Appearance Tab */}\n <TabsContent value=\"appearance\" className=\"space-y-4\">\n <div>\n <h3 className=\"text-sm font-semibold mb-3\">Theme</h3>\n <div className=\"grid grid-cols-3 gap-2 max-w-md\">\n <Button\n variant={theme === 'light' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setTheme('light')}\n >\n <Sun className=\"h-4 w-4 mr-1\" />\n Light\n </Button>\n <Button\n variant={theme === 'dark' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setTheme('dark')}\n >\n <Moon className=\"h-4 w-4 mr-1\" />\n Dark\n </Button>\n <Button\n variant={theme === 'system' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setTheme('system')}\n >\n <Monitor className=\"h-4 w-4 mr-1\" />\n System\n </Button>\n </div>\n <p className=\"text-xs text-muted-foreground mt-2\">\n System follows your OS-level light/dark preference.\n </p>\n </div>\n\n {/* Preferences — surfaces the persisted toggles that until\n now were only reachable via Command Palette / Ctrl+Shift+D.\n Keeps them discoverable and centralizes per-user knobs in\n one obvious place. Reads & writes via the live stores so\n changes take effect immediately. */}\n <div className=\"pt-2 border-t\">\n <h3 className=\"text-sm font-semibold mb-3 mt-3\">Preferences</h3>\n <PreferenceToggle\n label=\"Compact density\"\n hint=\"Tighter spacing throughout the chat. Toggle anywhere with Ctrl+Shift+D.\"\n selector={(s) => s.compactMode}\n onChange={() => useUIStore.getState().toggleCompactMode()}\n />\n <PreferenceToggle\n label=\"Sound on completion\"\n hint=\"Play a soft chime when a run finishes — useful when working in another tab.\"\n selector={null /* config-store path, see component */}\n configKey=\"soundOnComplete\"\n />\n </div>\n </TabsContent>\n </Tabs>\n </div>\n </ScrollArea>\n </div>\n );\n}\n\n/**\n * One row in the Preferences section. Renders a label / hint pair on the\n * left and a small switch on the right. Source-of-truth selection is\n * pluggable: pass `selector` for a UI-store-backed flag, or `configKey`\n * to bind to a `useConfigStore` boolean (we only handle `soundOnComplete`\n * today but the shape leaves room for more without growing the API).\n */\nfunction PreferenceToggle({\n label,\n hint,\n selector,\n onChange,\n configKey,\n}: {\n label: string;\n hint?: string;\n selector: ((s: ReturnType<typeof useUIStore.getState>) => boolean) | null;\n onChange?: () => void;\n configKey?: 'soundOnComplete';\n}) {\n const uiVal = useUIStore((s) => (selector ? selector(s) : false));\n const cfgVal = useConfigStore((s) => (configKey ? (s[configKey] as boolean) : false));\n const on = selector ? uiVal : cfgVal;\n const handleToggle = () => {\n if (selector) onChange?.();\n else if (configKey === 'soundOnComplete') {\n const next = !useConfigStore.getState().soundOnComplete;\n useConfigStore.getState().setSoundOnComplete(next);\n // Audible \"yes you turned it on\" — same logic as the Command Palette\n // toggle so users get the gesture-permission unlock for Web Audio.\n if (next) {\n import('@/lib/chime').then((m) => m.playCompletionChime()).catch(() => {});\n }\n }\n };\n return (\n <div className=\"flex items-start justify-between gap-3 py-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm font-medium\">{label}</div>\n {hint && <div className=\"text-xs text-muted-foreground mt-0.5\">{hint}</div>}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={on}\n onClick={handleToggle}\n className={cn(\n 'shrink-0 relative inline-flex h-5 w-9 rounded-full border transition-colors',\n on ? 'bg-primary border-primary' : 'bg-muted border-input hover:bg-muted/80',\n )}\n >\n <span\n className={cn(\n 'absolute top-0.5 left-0.5 h-3.5 w-3.5 rounded-full bg-background shadow transition-transform',\n on && 'translate-x-4',\n )}\n />\n </button>\n </div>\n );\n}\n","import { useConfigStore } from '@/stores';\nimport type React from 'react';\nimport { createContext, useContext, useEffect, useState } from 'react';\n\ntype Theme = 'light' | 'dark' | 'system';\n\ninterface ThemeProviderProps {\n children: React.ReactNode;\n defaultTheme?: Theme;\n storageKey?: string;\n}\n\ninterface ThemeProviderState {\n theme: Theme;\n setTheme: (theme: Theme) => void;\n}\n\nconst ThemeProviderContext = createContext<ThemeProviderState | undefined>(undefined);\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'wrongstack-theme',\n}: ThemeProviderProps) {\n const { theme: storeTheme, setTheme: setStoreTheme } = useConfigStore();\n const [theme, setTheme] = useState<Theme>(() => {\n if (typeof window !== 'undefined') {\n return (localStorage.getItem(storageKey) as Theme) || defaultTheme;\n }\n return defaultTheme;\n });\n\n useEffect(() => {\n const root = window.document.documentElement;\n root.classList.remove('light', 'dark');\n\n if (theme === 'system') {\n const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light';\n root.classList.add(systemTheme);\n } else {\n root.classList.add(theme);\n }\n }, [theme]);\n\n const value = {\n theme,\n setTheme: (newTheme: Theme) => {\n localStorage.setItem(storageKey, newTheme);\n setTheme(newTheme);\n setStoreTheme(newTheme);\n },\n };\n\n return <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\n\nexport function useTheme() {\n const context = useContext(ThemeProviderContext);\n if (context === undefined) {\n throw new Error('useTheme must be used within a ThemeProvider');\n }\n return context;\n}\n","import { cn } from '@/lib/utils';\nimport * as React from 'react';\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nInput.displayName = 'Input';\n\nexport { Input };\n","import { cn } from '@/lib/utils';\nimport * as TabsPrimitive from '@radix-ui/react-tabs';\nimport * as React from 'react';\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n 'inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',\n className,\n )}\n {...props}\n />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n {...props}\n />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n","import { useUIStore } from '@/stores';\nimport { Keyboard, X } from 'lucide-react';\nimport { useEffect } from 'react';\n\ninterface Shortcut {\n keys: string[];\n description: string;\n}\n\nconst SHORTCUTS: Array<{ section: string; items: Shortcut[] }> = [\n {\n section: 'Global',\n items: [\n { keys: ['Ctrl', 'K'], description: 'Open command palette' },\n { keys: ['?'], description: 'Show this shortcuts overlay' },\n { keys: ['Ctrl', '\\\\'], description: 'Toggle sidebar' },\n { keys: ['Ctrl', '/'], description: 'Focus the message input' },\n ],\n },\n {\n section: 'Chat input',\n items: [\n { keys: ['Enter'], description: 'Send message' },\n { keys: ['Shift', 'Enter'], description: 'Insert a newline' },\n { keys: ['↑'], description: 'Recall previous prompt (in empty input)' },\n { keys: ['↓'], description: 'Recall next prompt' },\n { keys: ['/'], description: 'Open slash command popup' },\n { keys: ['Tab'], description: 'Autocomplete highlighted command' },\n { keys: ['Esc'], description: 'Dismiss popup / clear input' },\n ],\n },\n {\n section: 'Chat',\n items: [\n { keys: ['Ctrl', 'F'], description: 'Search within current chat' },\n { keys: ['Ctrl', 'L'], description: 'Clear context (same as /clear)' },\n { keys: ['Ctrl', 'N'], description: 'Start a new session (same as /new)' },\n { keys: ['Ctrl', 'E'], description: 'Export chat as markdown' },\n { keys: ['Ctrl', 'M'], description: 'Quick model switcher overlay' },\n { keys: ['Ctrl', 'Shift', 'D'], description: 'Toggle compact UI density' },\n { keys: ['Esc'], description: 'Abort the current run (when running)' },\n ],\n },\n {\n section: 'Chat navigation (when not typing)',\n items: [\n { keys: ['j'], description: 'Focus next message (alias: ↓)' },\n { keys: ['k'], description: 'Focus previous message (alias: ↑)' },\n { keys: ['g'], description: 'Jump to first message' },\n { keys: ['Shift', 'G'], description: 'Jump to last message' },\n { keys: ['c'], description: 'Copy focused message text' },\n { keys: ['Esc'], description: 'Clear focused message' },\n ],\n },\n];\n\nexport function ShortcutsOverlay() {\n const open = useUIStore((s) => s.shortcutsOpen);\n const setOpen = useUIStore((s) => s.setShortcutsOpen);\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n // \"?\" — but only when the user isn't typing in an input. Otherwise\n // typing a literal \"?\" into the chat would open this overlay.\n const target = e.target as HTMLElement | null;\n const tag = target?.tagName?.toLowerCase();\n const isTyping = tag === 'input' || tag === 'textarea' || target?.isContentEditable;\n if (!isTyping && e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) {\n e.preventDefault();\n setOpen(!useUIStore.getState().shortcutsOpen);\n return;\n }\n if (e.key === 'Escape' && useUIStore.getState().shortcutsOpen) {\n e.preventDefault();\n setOpen(false);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [setOpen]);\n\n if (!open) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 bg-background/60 backdrop-blur-sm flex items-center justify-center px-4\"\n onClick={() => setOpen(false)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(false);\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n className=\"w-full max-w-2xl rounded-xl border bg-popover shadow-2xl overflow-hidden flex flex-col max-h-[80vh]\"\n >\n <div className=\"flex items-center justify-between px-5 py-4 border-b\">\n <div className=\"flex items-center gap-2\">\n <Keyboard className=\"h-4 w-4 text-muted-foreground\" />\n <h2 className=\"text-sm font-semibold\">Keyboard shortcuts</h2>\n </div>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"text-muted-foreground hover:text-foreground p-1 rounded hover:bg-muted\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <div className=\"overflow-y-auto px-5 py-4 space-y-6\">\n {SHORTCUTS.map((group) => (\n <div key={group.section}>\n <div className=\"text-[10px] uppercase tracking-wider text-muted-foreground mb-2\">\n {group.section}\n </div>\n <div className=\"grid grid-cols-1 gap-1.5\">\n {group.items.map((s) => (\n <div\n key={s.description}\n className=\"flex items-center justify-between gap-3 text-sm px-2 py-1.5 rounded hover:bg-muted/40\"\n >\n <span className=\"text-foreground/80\">{s.description}</span>\n <span className=\"flex items-center gap-1 shrink-0\">\n {s.keys.map((k, ki) => (\n <span key={k} className=\"flex items-center gap-1\">\n {ki > 0 && <span className=\"text-muted-foreground/40 text-xs\">+</span>}\n <kbd className=\"font-mono text-[10px] border rounded px-1.5 py-0.5 bg-background\">\n {k}\n </kbd>\n </span>\n ))}\n </span>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n <div className=\"border-t px-5 py-3 text-xs text-muted-foreground\">\n Press{' '}\n <kbd className=\"font-mono text-[10px] border rounded px-1 py-0.5 bg-background\">?</kbd>{' '}\n any time to reopen this list.\n </div>\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport {\n useChatStore,\n useConfigStore,\n useHistoryStore,\n useSessionStore,\n useUIStore,\n} from '@/stores';\nimport {\n CheckCircle2,\n Circle,\n CircleDot,\n Database,\n History,\n ListTodo,\n Loader2,\n MessageSquare,\n PanelLeftClose,\n Pin,\n RefreshCw,\n RotateCcw,\n Search,\n Settings as SettingsIcon,\n Star,\n Trash2,\n Wifi,\n WifiOff,\n X,\n Zap,\n} from 'lucide-react';\nimport { useEffect, useState } from 'react';\nimport { Button } from './ui/button';\nimport { ScrollArea } from './ui/scroll-area';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';\n\n/**\n * Sidebar: navigation + at-a-glance session info. Settings live in the main\n * `SettingsPanel` (open via the gear in ChatView's header or the button at\n * the bottom of this sidebar) — keeping settings in two places confused\n * users who clicked the sidebar \"Settings\" tab expecting a model picker\n * and only found a theme switcher.\n */\nexport function Sidebar() {\n const { toggleSidebar, currentView, setCurrentView } = useUIStore();\n const sidebarWidth = useUIStore((s) => s.sidebarWidth);\n const setSidebarWidth = useUIStore((s) => s.setSidebarWidth);\n const { totalTokens, cost, session, todos } = useSessionStore();\n const { messages, clearMessages } = useChatStore();\n const pinnedIds = useUIStore((s) => s.pinnedIds);\n const unpinAll = useUIStore((s) => s.unpinAll);\n /** Local-only filter for the History tab. Case-insensitive substring\n * match against title, model, provider, and session id — covers the\n * ways users actually try to find a past session (\"the one where I\n * used sonnet…\"). Live as you type; clears with the X button. */\n const [historyQuery, setHistoryQuery] = useState('');\n const favoriteSessionIds = useUIStore((s) => s.favoriteSessionIds);\n const toggleFavoriteSession = useUIStore((s) => s.toggleFavoriteSession);\n const sessionNicknames = useUIStore((s) => s.sessionNicknames);\n const setSessionNickname = useUIStore((s) => s.setSessionNickname);\n /** Inline rename target — null when nothing is being edited, otherwise\n * the session id whose title is currently in edit mode. The draft text\n * is local state so Esc can cancel cleanly. */\n const [renamingId, setRenamingId] = useState<string | null>(null);\n const [renameDraft, setRenameDraft] = useState('');\n /** Pinned bubbles that still exist in the current message list. We prune\n * here (rather than auto-trimming pinnedIds in the store) so the user\n * doesn't lose pins just because we cleared and reloaded a transcript.\n * Shows a single-line preview + jumps to the bubble on click. */\n const pinnedRows = pinnedIds\n .map((id) => messages.find((m) => m.id === id))\n .filter((m): m is NonNullable<typeof m> => !!m && m.content.length > 0);\n const { wsConnected, wsUrl, provider, model } = useConfigStore();\n const {\n entries: historyEntries,\n loading: historyLoading,\n error: historyError,\n } = useHistoryStore();\n const { listSessions, deleteSession, resumeSession, client } = useWebSocket();\n // Pull the current todo snapshot once on connect so a freshly-opened\n // tab doesn't sit todo-less until the next tool runs.\n useEffect(() => {\n if (wsConnected) client?.getTodos?.();\n }, [wsConnected, client]);\n const activeSessionId = session?.id;\n\n // Refresh the history list on tab open + whenever the active session id\n // changes (a /new would push the previous session into history).\n useEffect(() => {\n void activeSessionId;\n if (currentView === 'history' && wsConnected) {\n listSessions(50);\n }\n }, [currentView, wsConnected, activeSessionId, listSessions]);\n\n const formatDuration = (start: number | null) => {\n if (!start) return '--';\n const seconds = Math.floor((Date.now() - start) / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n return `${minutes}m`;\n };\n\n const formatRelative = (iso: string): string => {\n const ts = Date.parse(iso);\n if (Number.isNaN(ts)) return '';\n const diff = Date.now() - ts;\n if (diff < 60_000) return 'just now';\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;\n if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;\n const days = Math.floor(diff / 86_400_000);\n if (days < 7) return `${days}d ago`;\n return new Date(ts).toLocaleDateString();\n };\n\n /** Bucket history rows into Today / Yesterday / This week / Earlier so the\n * list reads like a journal instead of one long undifferentiated stream.\n * Boundaries are local midnight. Entries with unparseable timestamps\n * fall into \"Earlier\" so they're not silently dropped. */\n const groupedHistory = ((): Array<{\n label: string;\n rows: typeof historyEntries;\n star?: boolean;\n }> => {\n const q = historyQuery.trim().toLowerCase();\n const filtered = q\n ? historyEntries.filter(\n (e) =>\n e.title.toLowerCase().includes(q) ||\n e.model.toLowerCase().includes(q) ||\n e.provider.toLowerCase().includes(q) ||\n e.id.toLowerCase().includes(q),\n )\n : historyEntries;\n const startOfDay = new Date();\n startOfDay.setHours(0, 0, 0, 0);\n const todayStart = startOfDay.getTime();\n const yesterdayStart = todayStart - 86_400_000;\n const weekStart = todayStart - 6 * 86_400_000;\n const buckets: {\n today: typeof historyEntries;\n yesterday: typeof historyEntries;\n week: typeof historyEntries;\n older: typeof historyEntries;\n } = {\n today: [],\n yesterday: [],\n week: [],\n older: [],\n };\n for (const e of filtered) {\n const ts = Date.parse(e.startedAt);\n if (Number.isNaN(ts)) {\n buckets.older.push(e);\n continue;\n }\n if (ts >= todayStart) buckets.today.push(e);\n else if (ts >= yesterdayStart) buckets.yesterday.push(e);\n else if (ts >= weekStart) buckets.week.push(e);\n else buckets.older.push(e);\n }\n // Favorites form their own bucket at the very top, regardless of when\n // they were started — that's the whole point of starring a session.\n const favSet = new Set(favoriteSessionIds);\n const favorites = filtered.filter((e) => favSet.has(e.id));\n const out: Array<{ label: string; rows: typeof historyEntries; star?: boolean }> = [];\n if (favorites.length) out.push({ label: 'Favorites', rows: favorites, star: true });\n // Strip favorites from the date buckets so they don't appear twice.\n const dedupe = (arr: typeof historyEntries) => arr.filter((e) => !favSet.has(e.id));\n const today = dedupe(buckets.today);\n const yesterday = dedupe(buckets.yesterday);\n const week = dedupe(buckets.week);\n const older = dedupe(buckets.older);\n if (today.length) out.push({ label: 'Today', rows: today });\n if (yesterday.length) out.push({ label: 'Yesterday', rows: yesterday });\n if (week.length) out.push({ label: 'This week', rows: week });\n if (older.length) out.push({ label: 'Earlier', rows: older });\n return out;\n })();\n\n // Drag handle: track pointer movement on a sibling element. We start the\n // drag on mousedown, update width on mousemove, drop on mouseup. Bound on\n // window so the user can drag past the handle without losing focus.\n const startDrag = (e: React.MouseEvent) => {\n e.preventDefault();\n const startX = e.clientX;\n const startWidth = sidebarWidth;\n const onMove = (ev: MouseEvent) => {\n setSidebarWidth(startWidth + (ev.clientX - startX));\n };\n const onUp = () => {\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n };\n\n return (\n <aside\n style={{ width: `${sidebarWidth}px` }}\n className=\"relative border-r bg-card flex flex-col shrink-0\"\n >\n {/* Drag handle. Hit area is wider than the visible rail so users\n don't have to be pixel-perfect; the rail itself gets a clear\n highlight + a centered grip indicator on hover so the affordance\n isn't invisible until you try. Double-click resets to default. */}\n <div\n onMouseDown={startDrag}\n onDoubleClick={() => setSidebarWidth(288)}\n className=\"group/handle absolute top-0 right-0 h-full w-2 cursor-col-resize z-10 flex items-center justify-end\"\n title=\"Drag to resize · double-click to reset\"\n >\n <div className=\"h-full w-px bg-border group-hover/handle:bg-primary/60 group-hover/handle:w-0.5 transition-all\" />\n <div className=\"absolute right-0 top-1/2 -translate-y-1/2 flex flex-col gap-0.5 opacity-0 group-hover/handle:opacity-100 transition-opacity pr-0.5\">\n <span className=\"h-1 w-1 rounded-full bg-primary/70\" />\n <span className=\"h-1 w-1 rounded-full bg-primary/70\" />\n <span className=\"h-1 w-1 rounded-full bg-primary/70\" />\n </div>\n </div>\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b\">\n <div className=\"flex items-center gap-2\">\n <div className=\"w-6 h-6 rounded bg-primary flex items-center justify-center\">\n <Zap className=\"h-4 w-4 text-primary-foreground\" />\n </div>\n <span className=\"text-sm font-semibold tracking-tight\">WrongStack</span>\n </div>\n <Button variant=\"ghost\" size=\"icon\" onClick={toggleSidebar}>\n <PanelLeftClose className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {/* Navigation — Chat | History only. Settings opens the full panel. */}\n <Tabs\n value={currentView === 'settings' ? 'chat' : currentView}\n onValueChange={(v) => setCurrentView(v as 'chat' | 'history')}\n className=\"flex-1 flex flex-col\"\n >\n <TabsList className=\"w-full rounded-none bg-transparent p-2 h-auto grid grid-cols-2\">\n <TabsTrigger\n value=\"chat\"\n className=\"flex-col gap-1.5 py-2 data-[state=active]:bg-primary/10\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n <span className=\"text-xs\">Chat</span>\n </TabsTrigger>\n <TabsTrigger\n value=\"history\"\n className=\"flex-col gap-1.5 py-2 data-[state=active]:bg-primary/10\"\n >\n <History className=\"h-4 w-4\" />\n <span className=\"text-xs\">History</span>\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"chat\" className=\"flex-1 flex flex-col m-0 overflow-hidden\">\n {/* Connection status */}\n <div className=\"px-4 py-3 border-b\">\n <div\n className={cn(\n 'flex items-center gap-2 px-3 py-2 rounded-lg text-sm',\n wsConnected\n ? 'bg-green-500/10 text-green-600 dark:text-green-400'\n : 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400',\n )}\n >\n {wsConnected ? <Wifi className=\"h-4 w-4\" /> : <WifiOff className=\"h-4 w-4\" />}\n <span className=\"font-medium\">{wsConnected ? 'Connected' : 'Disconnected'}</span>\n </div>\n <div className=\"text-xs text-muted-foreground mt-2 px-1 font-mono\">{wsUrl}</div>\n </div>\n\n {/* Active model — clickable shortcut to settings */}\n <button\n type=\"button\"\n onClick={() => setCurrentView('settings')}\n className=\"px-4 py-3 border-b text-left hover:bg-muted/40 transition-colors\"\n >\n <div className=\"text-[10px] uppercase tracking-wider text-muted-foreground mb-1\">\n Active model\n </div>\n <div className=\"font-mono text-xs truncate\">\n <span className=\"text-muted-foreground\">{provider || '—'}</span>\n <span className=\"text-muted-foreground/40 mx-1\">/</span>\n <span className=\"font-medium\">{model || '—'}</span>\n </div>\n </button>\n\n {/* Session Stats */}\n <div className=\"px-4 py-3 border-b space-y-3\">\n <h3 className=\"text-sm font-medium flex items-center gap-2\">\n <Database className=\"h-4 w-4 text-muted-foreground\" />\n Session\n </h3>\n <div className=\"grid grid-cols-2 gap-2 text-xs\">\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Messages</span>\n <span className=\"text-lg font-semibold\">{messages.length}</span>\n </div>\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Duration</span>\n <span className=\"text-lg font-semibold\">\n {formatDuration(session?.startedAt ?? null)}\n </span>\n </div>\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Input</span>\n <span className=\"text-lg font-semibold\">{totalTokens.input.toLocaleString()}</span>\n </div>\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Output</span>\n <span className=\"text-lg font-semibold\">{totalTokens.output.toLocaleString()}</span>\n </div>\n </div>\n {cost > 0 && (\n <div className=\"flex justify-between items-center p-2 rounded-lg bg-green-500/10\">\n <span className=\"text-sm text-muted-foreground\">Cost</span>\n <span className=\"text-lg font-semibold text-green-600 dark:text-green-400\">\n ${cost.toFixed(4)}\n </span>\n </div>\n )}\n </div>\n\n {/* Live TODO list — populated by the backend's todos.updated\n broadcast after every tool.executed. Empty array → hide the\n section entirely so a vanilla session keeps its existing\n vertical rhythm. */}\n {todos.length > 0 && (\n <div className=\"px-4 py-3 border-b space-y-2\">\n <h3 className=\"text-sm font-medium flex items-center justify-between\">\n <span className=\"flex items-center gap-2\">\n <ListTodo className=\"h-4 w-4 text-muted-foreground\" />\n Todos\n </span>\n <span className=\"text-[10px] text-muted-foreground tabular-nums\">\n {todos.filter((t) => t.status === 'completed').length}/{todos.length}\n </span>\n </h3>\n <ul className=\"space-y-1 max-h-48 overflow-y-auto pr-1\">\n {todos.map((t) => {\n const Icon =\n t.status === 'completed'\n ? CheckCircle2\n : t.status === 'in_progress'\n ? CircleDot\n : Circle;\n const tone =\n t.status === 'completed'\n ? 'text-green-600 dark:text-green-400 line-through opacity-70'\n : t.status === 'in_progress'\n ? 'text-amber-600 dark:text-amber-400'\n : 'text-muted-foreground';\n return (\n <li\n key={t.id}\n className={cn('flex items-start gap-2 text-xs leading-snug', tone)}\n >\n <Icon className=\"h-3.5 w-3.5 mt-0.5 shrink-0\" />\n <span className=\"break-words\">\n {t.status === 'in_progress' && t.activeForm ? t.activeForm : t.content}\n </span>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {/* Pinned answers — click to scroll the chat to the bubble. We\n briefly highlight the target via a CSS class on data-message-id\n so the user can tell which one we just snapped to. */}\n {pinnedRows.length > 0 && (\n <div className=\"px-4 py-3 border-b space-y-2\">\n <h3 className=\"text-sm font-medium flex items-center justify-between\">\n <span className=\"flex items-center gap-2\">\n <Pin className=\"h-4 w-4 text-amber-500\" />\n Pinned\n </span>\n <button\n type=\"button\"\n onClick={unpinAll}\n className=\"text-[10px] text-muted-foreground hover:text-destructive\"\n >\n Clear\n </button>\n </h3>\n <ul className=\"space-y-1 max-h-48 overflow-y-auto pr-1\">\n {pinnedRows.map((m) => {\n const preview = m.content.replace(/\\s+/g, ' ').slice(0, 80);\n return (\n <li key={m.id}>\n <button\n type=\"button\"\n onClick={() => {\n const el = document.querySelector(`[data-message-id=\"${m.id}\"]`);\n if (!el) return;\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n el.classList.add('ring-2', 'ring-amber-500/60');\n setTimeout(() => {\n el.classList.remove('ring-2', 'ring-amber-500/60');\n }, 1600);\n }}\n className=\"w-full text-left text-xs px-2 py-1.5 rounded bg-muted/40 hover:bg-muted/70 border border-amber-500/20 leading-snug\"\n title={m.content.slice(0, 400)}\n >\n {preview}\n {m.content.length > 80 ? '…' : ''}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {/* Quick Actions */}\n <div className=\"px-4 py-3 border-b space-y-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full justify-start text-destructive hover:text-destructive\"\n onClick={() => {\n // Match /clear: drop UI + backend context together so the\n // model doesn't keep replying with knowledge from messages\n // the user just told us to forget.\n clearMessages();\n client?.clearContext?.();\n }}\n >\n <Trash2 className=\"h-4 w-4 mr-2\" />\n Clear context\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={() => client?.newSession?.()}\n disabled={!wsConnected}\n >\n <RotateCcw className=\"h-4 w-4 mr-2\" />\n New session\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={() => client?.compactContext?.()}\n disabled={!wsConnected}\n >\n <Database className=\"h-4 w-4 mr-2\" />\n Compact context\n </Button>\n </div>\n\n <div className=\"flex-1\" />\n\n {/* Footer: settings entry point */}\n <div className=\"px-3 py-3 border-t\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={() => setCurrentView('settings')}\n >\n <SettingsIcon className=\"h-4 w-4 mr-2\" />\n Settings\n </Button>\n </div>\n </TabsContent>\n\n <TabsContent value=\"history\" className=\"flex-1 m-0 flex flex-col overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-2 border-b\">\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground\">\n Recent sessions\n </span>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-6 w-6\"\n onClick={() => listSessions(50)}\n disabled={!wsConnected}\n title=\"Refresh\"\n >\n {historyLoading ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <RefreshCw className=\"h-3.5 w-3.5\" />\n )}\n </Button>\n </div>\n {/* History search — only renders once we have something to filter,\n otherwise it's just empty UI clutter on a fresh install. */}\n {historyEntries.length > 3 && (\n <div className=\"px-3 py-2 border-b\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground/60\" />\n <input\n type=\"text\"\n value={historyQuery}\n onChange={(e) => setHistoryQuery(e.target.value)}\n placeholder=\"Filter title, model, provider…\"\n className=\"w-full pl-7 pr-7 py-1 text-xs rounded-md bg-muted/40 border border-transparent focus:bg-background focus:border-input focus:outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50\"\n />\n {historyQuery && (\n <button\n type=\"button\"\n onClick={() => setHistoryQuery('')}\n className=\"absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground/60 hover:text-foreground p-0.5\"\n title=\"Clear filter\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n </div>\n )}\n\n {historyError && (\n <div className=\"px-4 py-2 text-xs text-destructive bg-destructive/5 border-b\">\n {historyError}\n </div>\n )}\n\n <ScrollArea className=\"flex-1\">\n {historyEntries.length === 0 && !historyLoading ? (\n <div className=\"text-center text-muted-foreground py-8 px-4\">\n <History className=\"h-8 w-8 mx-auto mb-3 opacity-20\" />\n <p className=\"text-sm font-medium\">No history yet</p>\n <p className=\"text-xs mt-1\">Your conversations will appear here</p>\n </div>\n ) : groupedHistory.length === 0 ? (\n <div className=\"text-center text-muted-foreground py-8 px-4\">\n <Search className=\"h-8 w-8 mx-auto mb-3 opacity-20\" />\n <p className=\"text-sm font-medium\">No matches</p>\n <p className=\"text-xs mt-1\">Try a different filter</p>\n </div>\n ) : (\n <div className=\"p-2 space-y-3\">\n {groupedHistory.map((group) => (\n <div key={group.label} className=\"space-y-1\">\n <div\n className={cn(\n 'sticky top-0 z-[1] px-1 pb-1 text-[10px] uppercase tracking-wider font-semibold bg-card/90 backdrop-blur-sm flex items-center gap-1',\n group.star ? 'text-amber-500' : 'text-muted-foreground/80',\n )}\n >\n {group.star && <Star className=\"h-3 w-3 fill-current\" />}\n {group.label}{' '}\n <span className=\"text-muted-foreground/50 font-normal normal-case ml-1\">\n ({group.rows.length})\n </span>\n </div>\n {group.rows.map((entry) => (\n <div\n key={entry.id}\n className={cn(\n 'group relative rounded-md border text-sm transition-colors',\n entry.isCurrent\n ? 'bg-primary/5 border-primary/40'\n : 'bg-card border-border/60 hover:bg-muted/40 hover:border-primary/40',\n )}\n >\n <button\n type=\"button\"\n disabled={entry.isCurrent || renamingId === entry.id}\n onClick={() => resumeSession(entry.id)}\n onDoubleClick={(e) => {\n // Double-click anywhere on the row enters rename mode.\n // We stop propagation so it doesn't also fire the\n // single-click resume handler.\n e.stopPropagation();\n setRenamingId(entry.id);\n setRenameDraft(sessionNicknames[entry.id] ?? entry.title ?? '');\n }}\n className=\"block w-full rounded-md px-3 py-2 pr-16 text-left disabled:cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <div className=\"min-w-0 flex-1\">\n {renamingId === entry.id ? (\n <input\n value={renameDraft}\n onChange={(e) => setRenameDraft(e.target.value)}\n onClick={(e) => e.stopPropagation()}\n onBlur={() => {\n setSessionNickname(entry.id, renameDraft);\n setRenamingId(null);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n setSessionNickname(entry.id, renameDraft);\n setRenamingId(null);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setRenamingId(null);\n }\n }}\n placeholder={entry.title || 'Nickname'}\n className=\"w-full text-sm bg-background border border-input rounded px-1.5 py-0.5 focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n ) : (\n <div\n className=\"font-medium truncate text-foreground\"\n title={\n sessionNicknames[entry.id]\n ? `${sessionNicknames[entry.id]} — original: ${entry.title}`\n : `${entry.title}\\n\\nDouble-click to rename`\n }\n >\n {sessionNicknames[entry.id] || entry.title || '(empty)'}\n </div>\n )}\n <div className=\"text-[10px] text-muted-foreground font-mono truncate mt-0.5\">\n {entry.provider}/{entry.model}\n </div>\n <div className=\"flex items-center gap-2 text-[10px] text-muted-foreground/80 mt-0.5\">\n <span>{formatRelative(entry.startedAt)}</span>\n {entry.tokenTotal > 0 && (\n <>\n <span>·</span>\n <span className=\"tabular-nums\">\n {entry.tokenTotal.toLocaleString()} tok\n </span>\n </>\n )}\n {entry.isCurrent && (\n <>\n <span>·</span>\n <span className=\"text-primary font-medium\">active</span>\n </>\n )}\n </div>\n </div>\n </button>\n {/* Star toggle — always rendered (not hidden behind hover)\n when already favorited, so the user can tell at a\n glance which rows are starred without hovering each\n one. The Trash sits beside it but only on hover. */}\n <div className=\"absolute right-2 top-2 flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => toggleFavoriteSession(entry.id)}\n className={cn(\n 'transition-opacity hover:text-amber-500',\n favoriteSessionIds.includes(entry.id)\n ? 'opacity-100 text-amber-500'\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground',\n )}\n title={\n favoriteSessionIds.includes(entry.id)\n ? 'Unfavorite'\n : 'Mark as favorite'\n }\n >\n <Star\n className={cn(\n 'h-3.5 w-3.5',\n favoriteSessionIds.includes(entry.id) && 'fill-current',\n )}\n />\n </button>\n {!entry.isCurrent && (\n <button\n type=\"button\"\n onClick={() => {\n if (window.confirm(`Delete session \"${entry.title}\"?`)) {\n deleteSession(entry.id);\n }\n }}\n className=\"opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground hover:text-destructive\"\n title=\"Delete session\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n ))}\n </div>\n )}\n </ScrollArea>\n </TabsContent>\n </Tabs>\n </aside>\n );\n}\n"],"mappings":"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","names":["React","create","useEffect","client","useEffect","ArrowDown","ArrowUp","Bot","Brain","Cpu","Monitor","Moon","Sun","Zap","useCallback","useEffect","useRef","useState","useCallback","useEffect","useRef","useState","useEffect","useRef","jsx","jsxs","Icon","useEffect","useRef","useState","jsx","jsxs","client","jsx","jsx","jsxs","client","useState","useRef","useCallback","useEffect","tone","RotateCcw","useEffect","useState","jsx","jsxs","useEffect","useRef","useState","Fragment","jsx","jsxs","CheckCircle2","ChevronDown","ChevronRight","Download","Pencil","RotateCcw","XCircle","useState","useMemo","jsx","jsxs","i","j","useMemo","useState","Fragment","jsx","jsxs","Fragment","jsx","jsxs","useState","client","ChevronDown","ChevronRight","XCircle","CheckCircle2","Download","Pencil","RotateCcw","Check","ChevronDown","useEffect","useRef","useState","jsx","jsxs","Search","X","useEffect","useMemo","useRef","useState","jsx","jsxs","CheckCircle2","ChevronDown","ChevronRight","Loader2","Terminal","XCircle","useState","jsx","jsxs","useState","Terminal","ChevronDown","ChevronRight","Loader2","XCircle","CheckCircle2","ArchiveRestore","Clock","Search","Sparkles","Wrench","useEffect","useState","Fragment","jsx","jsxs","client","Icon","React","jsx","jsxs","Fragment","jsx","jsxs","Brain","useRef","useState","useCallback","useEffect","formatDuration","Zap","Cpu","Sun","Moon","Monitor","ArrowDown","ArrowUp","Bot","AlertTriangle","Terminal","Wrench","useEffect","X","React","jsx","jsxs","jsx","jsxs","X","jsx","jsxs","Terminal","Wrench","useEffect","Icon","AlertTriangle","Loader2","RotateCcw","WifiOff","X","useEffect","useState","jsx","jsxs","ArrowRight","Cpu","Search","useEffect","useMemo","useRef","useState","jsx","jsxs","client","CheckCircle2","Cpu","Globe","Loader2","Monitor","Moon","Sun","Trash2","X","useState","useEffect","useCallback","useEffect","useState","jsx","React","jsx","React","jsx","Fragment","jsx","jsxs","useState","useEffect","useCallback","X","Cpu","Globe","CheckCircle2","Loader2","Trash2","Sun","Moon","Monitor","Keyboard","X","useEffect","jsx","jsxs","CheckCircle2","Database","Loader2","Pin","RefreshCw","RotateCcw","Search","SettingsIcon","Trash2","Wifi","WifiOff","X","Zap","useEffect","useState","Fragment","jsx","jsxs","useState","client","useEffect","formatDuration","Zap","Wifi","WifiOff","Database","Icon","CheckCircle2","tone","Pin","Trash2","RotateCcw","SettingsIcon","Loader2","RefreshCw","Search","X","jsx","jsxs","useEffect","jsx","React"]}
1
+ {"version":3,"sources":["../src/lib/chime.ts","../src/main.tsx","../src/lib/utils.ts","../src/components/Toaster.tsx","../src/hooks/useWebSocket.ts","../src/lib/favicon.ts","../src/lib/notify.ts","../src/lib/ws-client.ts","../src/stores/index.ts","../src/App.tsx","../src/components/ChatView.tsx","../src/components/ChatInput.tsx","../src/components/CommandPalette.tsx","../src/components/FilePicker.tsx","../src/components/ui/button.tsx","../src/components/ConnectionChip.tsx","../src/components/CostChip.tsx","../src/lib/tool-summary.ts","../src/components/MessageBubble.tsx","../src/components/DiffView.tsx","../src/components/ToolResult.tsx","../src/components/ModePicker.tsx","../src/components/SearchOverlay.tsx","../src/components/ToolGroup.tsx","../src/components/WelcomeScreen.tsx","../src/components/ui/scroll-area.tsx","../src/components/ConfirmDialog.tsx","../src/components/ui/dialog.tsx","../src/components/ConnectionBanner.tsx","../src/components/ErrorBoundary.tsx","../src/components/QuickModelSwitcher.tsx","../src/components/SettingsPanel.tsx","../src/components/ThemeProvider.tsx","../src/components/ui/input.tsx","../src/components/ui/tabs.tsx","../src/components/ShortcutsOverlay.tsx","../src/components/Sidebar.tsx"],"sourcesContent":["/**\n * Tiny soft chime played when a long-running agent run completes. We don't\n * ship an audio asset — the sound is synthesized with Web Audio so it's\n * zero-bytes-shipped and trivially tweakable.\n *\n * Tuned to be unobtrusive: two notes ascending, ~250ms total, ~-12dB\n * envelope. Quiet enough to not startle on a still office, audible enough\n * to cut through music. Browsers require a prior user interaction before\n * any audio plays — by the time the agent has actually finished a turn the\n * user has already typed, so the autoplay policy is satisfied.\n */\n\nlet ctx: AudioContext | null = null;\n\nfunction audio(): AudioContext | null {\n if (typeof window === 'undefined') return null;\n if (ctx) return ctx;\n // SSR/Test guard plus older Safari (webkit prefix).\n const Cls =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Cls) return null;\n try {\n ctx = new Cls();\n } catch {\n return null;\n }\n return ctx;\n}\n\nfunction tone(freq: number, startAt: number, durSec: number): void {\n const ac = audio();\n if (!ac) return;\n const t = ac.currentTime + startAt;\n const osc = ac.createOscillator();\n const gain = ac.createGain();\n osc.type = 'sine';\n osc.frequency.value = freq;\n // Quick attack, exponential decay — sounds like a soft \"bing\".\n gain.gain.setValueAtTime(0, t);\n gain.gain.linearRampToValueAtTime(0.18, t + 0.01);\n gain.gain.exponentialRampToValueAtTime(0.0001, t + durSec);\n osc.connect(gain).connect(ac.destination);\n osc.start(t);\n osc.stop(t + durSec + 0.02);\n}\n\nexport function playCompletionChime(): void {\n // Two-note arpeggio: E5 → A5 (a perfect fourth, pleasant + non-alarming).\n tone(659.25, 0, 0.18);\n tone(880, 0.12, 0.24);\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { App } from './App';\nimport './index.css';\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n);\n","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { cn } from '@/lib/utils';\nimport { AlertTriangle, CheckCircle2, Info, X, XCircle } from 'lucide-react';\nimport { useEffect } from 'react';\nimport { create } from 'zustand';\n\n/**\n * Tiny toast store + portal. We resisted pulling in shadcn-ui's full\n * toast/sonner since it brings a tree of providers — this is one store,\n * one component, ~80 lines total. The store is exposed via `toast.success(...)`\n * etc. so non-React modules (ws-client handlers) can fire toasts without\n * the hook.\n */\n\nexport type ToastVariant = 'success' | 'error' | 'warn' | 'info';\n\ninterface ToastEntry {\n id: string;\n message: string;\n variant: ToastVariant;\n ttl: number;\n}\n\ninterface ToastState {\n toasts: ToastEntry[];\n push: (t: Omit<ToastEntry, 'id'>) => string;\n dismiss: (id: string) => void;\n}\n\nconst useToastStore = create<ToastState>((set) => ({\n toasts: [],\n push: (t) => {\n const id = `toast_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n set((state) => ({ toasts: [...state.toasts, { ...t, id }] }));\n return id;\n },\n dismiss: (id) => set((state) => ({ toasts: state.toasts.filter((x) => x.id !== id) })),\n}));\n\n/** Imperative API. Pass plain strings or arrays of strings for multi-line. */\nexport const toast = {\n success: (msg: string, ttl = 3500) =>\n useToastStore.getState().push({ message: msg, variant: 'success', ttl }),\n error: (msg: string, ttl = 6000) =>\n useToastStore.getState().push({ message: msg, variant: 'error', ttl }),\n warn: (msg: string, ttl = 4500) =>\n useToastStore.getState().push({ message: msg, variant: 'warn', ttl }),\n info: (msg: string, ttl = 3500) =>\n useToastStore.getState().push({ message: msg, variant: 'info', ttl }),\n dismiss: (id: string) => useToastStore.getState().dismiss(id),\n};\n\nfunction Icon({ variant }: { variant: ToastVariant }) {\n if (variant === 'success') return <CheckCircle2 className=\"h-4 w-4 text-green-500\" />;\n if (variant === 'error') return <XCircle className=\"h-4 w-4 text-destructive\" />;\n if (variant === 'warn') return <AlertTriangle className=\"h-4 w-4 text-amber-500\" />;\n return <Info className=\"h-4 w-4 text-blue-500\" />;\n}\n\nfunction ToastItem({ entry }: { entry: ToastEntry }) {\n const dismiss = useToastStore((s) => s.dismiss);\n useEffect(() => {\n const t = setTimeout(() => dismiss(entry.id), entry.ttl);\n return () => clearTimeout(t);\n }, [entry.id, entry.ttl, dismiss]);\n return (\n <div\n className={cn(\n 'flex items-start gap-2 rounded-lg border bg-popover shadow-lg px-3 py-2 text-sm max-w-sm',\n 'animate-message',\n entry.variant === 'error' && 'border-destructive/40',\n entry.variant === 'warn' && 'border-amber-500/40',\n entry.variant === 'success' && 'border-green-500/40',\n )}\n >\n <Icon variant={entry.variant} />\n <div className=\"flex-1 min-w-0 whitespace-pre-wrap break-words leading-snug\">\n {entry.message}\n </div>\n <button\n type=\"button\"\n onClick={() => dismiss(entry.id)}\n className=\"text-muted-foreground hover:text-foreground\"\n title=\"Dismiss\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n );\n}\n\nexport function Toaster() {\n const toasts = useToastStore((s) => s.toasts);\n if (toasts.length === 0) return null;\n return (\n <div className=\"fixed bottom-4 right-4 z-[60] flex flex-col gap-2 pointer-events-auto\">\n {toasts.map((t) => (\n <ToastItem key={t.id} entry={t} />\n ))}\n </div>\n );\n}\n","import { toast } from '@/components/Toaster';\nimport { playCompletionChime } from '@/lib/chime';\nimport { installFaviconVisibilityReset, setFaviconStatus } from '@/lib/favicon';\nimport { ensureNotificationPermission, notifyIfHidden } from '@/lib/notify';\nimport { type WrongStackWebSocketClient, getWSClient } from '@/lib/ws-client';\nimport {\n type SessionHistoryEntry,\n useChatStore,\n useConfigStore,\n useHistoryStore,\n useSessionStore,\n useUIStore,\n} from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport { useCallback, useEffect, useRef } from 'react';\n\n/**\n * One-shot WebSocket handler installation.\n *\n * Critical: this is called by `useWebSocketBootstrap` from App.tsx EXACTLY\n * ONCE per page. Every other component that needs to talk to the backend uses\n * `useWebSocket()` (below) which only returns action methods — it does NOT\n * register handlers.\n *\n * The earlier design had every component that imported `useWebSocket()`\n * register its own copy of the handlers via `ws.on(type, handler)`. With\n * ChatInput + ConfirmDialog + SettingsPanel all using the hook, every\n * incoming WS message was processed three times — three identical tool\n * bubbles, three appends of the same text_delta, three clearMessages on\n * session.start. That's the \"duplicate tool bubble / repeated text\" bug\n * the user kept hitting. Singleton install fixes it at the root.\n */\nfunction installHandlers(ws: WrongStackWebSocketClient): () => void {\n const offs: Array<() => void> = [];\n\n const on = (type: string, fn: (msg: WSServerMessage) => void) => {\n offs.push(ws.on(type, fn));\n };\n\n on('session.start', (msg) => {\n const payload = msg.payload as {\n sessionId: string;\n model: string;\n provider: string;\n maxContext?: number;\n projectName?: string;\n cwd?: string;\n mode?: string;\n inputCost?: number;\n outputCost?: number;\n cacheReadCost?: number;\n /** Backend tells us \"the whole context was wiped on my side, mirror\n * that in the UI\". Sent by context.clear so the chat empties even\n * though the sessionId is unchanged. */\n reset?: boolean;\n };\n const prev = useSessionStore.getState().session?.id;\n const isNew = !prev || prev !== payload.sessionId;\n useSessionStore.getState().startSession({\n id: payload.sessionId,\n startedAt: Date.now(),\n model: payload.model,\n provider: payload.provider,\n });\n useSessionStore.getState().setEnv({\n maxContext: payload.maxContext,\n projectName: payload.projectName,\n mode: payload.mode,\n inputCost: payload.inputCost,\n outputCost: payload.outputCost,\n cacheReadCost: payload.cacheReadCost,\n });\n useConfigStore.getState().setConfig({\n provider: payload.provider,\n model: payload.model,\n });\n if (isNew || payload.reset) useChatStore.getState().clearMessages();\n\n // Resume hydration: rebuild the chat from the on-disk transcript so the\n // user can pick up exactly where they left off. We translate each\n // Message into the simpler ChatMessage shape the UI store expects.\n const replay = (payload as { replayMessages?: Array<{ role: string; content: unknown }> })\n .replayMessages;\n if (replay && replay.length > 0) {\n const chat = useChatStore.getState();\n for (const m of replay) {\n if (m.role === 'user' || m.role === 'assistant' || m.role === 'system') {\n let text = '';\n if (typeof m.content === 'string') {\n text = m.content;\n } else if (Array.isArray(m.content)) {\n for (const b of m.content as Array<Record<string, unknown>>) {\n if (b.type === 'text' && typeof b.text === 'string') {\n text += (text ? '\\n' : '') + b.text;\n } else if (b.type === 'tool_use') {\n chat.addMessage({\n role: 'tool',\n content: '',\n toolName: String(b.name ?? 'tool'),\n toolInput: b.input,\n toolUseId: String(b.id ?? ''),\n });\n text = '';\n } else if (b.type === 'tool_result') {\n const all = useChatStore.getState().messages;\n let last: { id: string } | undefined;\n for (let i = all.length - 1; i >= 0; i--) {\n if (all[i]!.toolUseId === String(b.tool_use_id ?? '')) {\n last = all[i]!;\n break;\n }\n }\n if (last) {\n chat.setToolResult(\n last.id,\n typeof b.content === 'string' ? b.content : JSON.stringify(b.content),\n !b.is_error,\n );\n }\n }\n }\n }\n if (text) {\n chat.addMessage({ role: m.role as 'user' | 'assistant', content: text });\n }\n }\n }\n }\n });\n\n on('context.debug', (msg) => {\n const p = msg.payload as {\n total: number;\n systemPrompt: number;\n tools: {\n total: number;\n count: number;\n breakdown: Array<{ name: string; tokens: number }>;\n };\n messages: {\n total: number;\n count: number;\n breakdown: Array<{ index: number; role: string; tokens: number; preview: string }>;\n };\n };\n const fmt = (n: number) => n.toLocaleString();\n // Sort tools+messages by size descending so the top consumers float up.\n const topTools = [...p.tools.breakdown].sort((a, b) => b.tokens - a.tokens).slice(0, 8);\n const topMsgs = [...p.messages.breakdown].sort((a, b) => b.tokens - a.tokens).slice(0, 8);\n const lines = [\n `📊 **Context breakdown** (heuristic — 4 chars/token)`,\n ``,\n `**Total estimate:** ${fmt(p.total)} tokens`,\n `• System prompt: ${fmt(p.systemPrompt)}`,\n `• Tool schemas: ${fmt(p.tools.total)} (${p.tools.count} tools)`,\n `• Messages: ${fmt(p.messages.total)} (${p.messages.count} messages)`,\n ``,\n `**Top tool schemas:**`,\n ...topTools.map((t) => ` · ${t.name}: ${fmt(t.tokens)}`),\n ``,\n `**Top messages:**`,\n ...topMsgs.map(\n (m) => ` · #${m.index} ${m.role}: ${fmt(m.tokens)} — ${m.preview || '(empty)'}`,\n ),\n ];\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: lines.join('\\n'),\n });\n });\n\n on('key.operation_result', (msg) => {\n // Provider/key/model.switch operations report back here. Toast is the\n // right surface — these are transient acks/errors, not chat content.\n const p = msg.payload as { success: boolean; message: string };\n if (p.success) toast.success(p.message);\n else toast.error(p.message);\n });\n\n on('context.compacted', (msg) => {\n const payload = msg.payload as {\n before: number;\n after: number;\n saved: number;\n reductions: Array<{ phase: string; saved: number }>;\n };\n // Inline notice in the chat — the model just shed ~N tokens of history,\n // user should see what happened so the next reply context isn't a\n // surprise. Not an error; rendered as a subdued assistant note.\n const summary = payload.reductions.length\n ? payload.reductions.map((r) => `${r.phase}: ${r.saved}`).join(', ')\n : 'no-op';\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: `🗜️ Context compacted: ${payload.before} → ${payload.after} tokens (saved ~${payload.saved}). ${summary}`,\n });\n // The new context size is the de-facto next input — reflect it in the\n // topbar so the ctx % chip updates immediately.\n useSessionStore.setState({ lastInputTokens: payload.after });\n });\n\n on('session.end', () => {\n useConfigStore.getState().setWsConnected(false);\n });\n\n on('iteration.started', (msg) => {\n const payload = msg.payload as { index: number; maxIterations?: number };\n useSessionStore.getState().setIteration({\n index: payload.index,\n max: payload.maxIterations ?? 0,\n });\n // Defensive: a new iteration means the agent is actively working.\n // Make sure the running indicator stays visible even if some earlier\n // event dropped isLoading prematurely.\n useChatStore.getState().setLoading(true);\n if (typeof document !== 'undefined' && document.hidden) {\n setFaviconStatus('running');\n }\n // First iteration of a fresh run — snapshot start time + cost so\n // run.result can compute a per-turn summary (duration, cost delta).\n // Subsequent iterations in the same loop preserve the original start.\n if (useChatStore.getState().runStart === null) {\n useChatStore.getState().setRunStart({\n at: Date.now(),\n cost: useSessionStore.getState().cost,\n });\n }\n // Don't pre-create an empty assistant bubble — text_delta lazy-creates\n // one when the model actually writes something.\n useChatStore.getState().setCurrentAssistantMessage(null);\n });\n\n on('provider.text_delta', (msg) => {\n const payload = msg.payload as { text: string; messageId: string };\n // Model has moved from internal reasoning to user-facing output — the\n // transient thinking buffer is no longer current.\n useChatStore.getState().clearThinking();\n let id = useChatStore.getState().currentAssistantMessageId;\n if (!id) {\n id = useChatStore.getState().addMessage({ role: 'assistant', content: '', streaming: true });\n useChatStore.getState().setCurrentAssistantMessage(id);\n }\n useChatStore.getState().appendToMessage(id, payload.text);\n });\n\n on('provider.thinking_delta', (msg) => {\n const payload = msg.payload as { text: string };\n if (!payload.text) return;\n useChatStore.getState().appendThinking(payload.text);\n });\n\n on('tool.started', (msg) => {\n const payload = msg.payload as {\n id: string;\n name: string;\n input?: unknown;\n messageId: string;\n };\n // Guard against duplicate tool.started for the same backend id. Could\n // happen if the agent retries / re-emits, and we definitely don't want a\n // second bubble for the same tool_use.\n const existing = useChatStore.getState().messages.find((m) => m.toolUseId === payload.id);\n if (existing) {\n useChatStore.getState().setCurrentToolId(existing.id);\n return;\n }\n // Model is acting, not still reasoning — drop the transient thinking.\n useChatStore.getState().clearThinking();\n useChatStore.getState().setCurrentAssistantMessage(null);\n const id = useChatStore.getState().addMessage({\n role: 'tool',\n content: '',\n toolName: payload.name,\n toolInput: payload.input,\n toolUseId: payload.id,\n });\n useChatStore.getState().setCurrentToolId(id);\n useChatStore.getState().addExecution({\n id: payload.id,\n name: payload.name,\n input: payload.input,\n ok: true,\n startedAt: Date.now(),\n });\n });\n\n on('tool.progress', (msg) => {\n // Live progress feed for an in-flight tool. We route each event onto\n // the tool bubble matched by backend tool_use id (set by tool.started).\n // Skip pure 'metric' events with no text — they're useful to logs but\n // would clutter the chat with empty rows.\n const payload = msg.payload as {\n id: string;\n name: string;\n eventType: 'log' | 'warning' | 'metric' | 'file_changed' | 'partial_output';\n text?: string;\n };\n const text = (payload.text ?? '').trim();\n if (!text) return;\n const messages = useChatStore.getState().messages;\n const owner = messages.find((m) => m.toolUseId === payload.id);\n if (!owner) return;\n // Prefix warnings so they're visually distinct; everything else flows in\n // as-is. partial_output (bash stdout etc.) is the common case.\n const prefix = payload.eventType === 'warning' ? '⚠ ' : '';\n useChatStore.getState().appendToolProgress(owner.id, prefix + text);\n });\n\n on('tool.executed', (msg) => {\n const payload = msg.payload as {\n id?: string;\n name: string;\n durationMs: number;\n ok: boolean;\n input?: unknown;\n output?: string;\n };\n const { messages, currentToolId } = useChatStore.getState();\n // Prefer matching on backend tool_use id (works for parallel tools).\n // Fall back to currentToolId only when id is missing (legacy emitters).\n const owner = payload.id\n ? messages.find((m) => m.toolUseId === payload.id)\n : currentToolId\n ? messages.find((m) => m.id === currentToolId)\n : undefined;\n if (owner) {\n useChatStore.getState().setToolResult(owner.id, payload.output ?? '', payload.ok);\n useChatStore.getState().updateMessage(owner.id, { toolDurationMs: payload.durationMs });\n }\n if (payload.id) {\n useChatStore.getState().updateExecution(payload.id, {\n completedAt: Date.now(),\n durationMs: payload.durationMs,\n output: payload.output,\n ok: payload.ok,\n });\n }\n if (currentToolId && owner && owner.id === currentToolId) {\n useChatStore.getState().setCurrentToolId(null);\n }\n });\n\n on('provider.response', (msg) => {\n const payload = msg.payload as {\n usage: {\n input: number;\n output: number;\n cacheRead?: number;\n cacheWrite?: number;\n };\n stopReason: string;\n messageId: string;\n };\n useSessionStore.getState().updateUsage(payload.usage);\n const { inputCost, outputCost, cacheReadCost } = useSessionStore.getState();\n const dCost =\n (payload.usage.input * inputCost +\n payload.usage.output * outputCost +\n (payload.usage.cacheRead ?? 0) * cacheReadCost) /\n 1_000_000;\n if (dCost > 0) useSessionStore.getState().addCost(dCost);\n // Run is NOT done if the provider stopped to use tools — the agent will\n // execute them and loop. Keep isLoading true so the Thinking/Running\n // indicator stays visible between iterations. The terminal flip happens\n // in run.result.\n if (payload.stopReason !== 'tool_use' && payload.stopReason !== 'tool_call') {\n useChatStore.getState().setLoading(false);\n }\n // Close out the current streaming bubble either way — finalize the text\n // (collapse model-emitted duplicate paragraphs) and drop the streaming\n // flag so a fresh iteration starts a new bubble.\n const id = useChatStore.getState().currentAssistantMessageId;\n if (id) {\n useChatStore.getState().finalizeMessage(id);\n // Attribute the run's usage to this bubble so the user can see what\n // each answer cost. Iterations that loop on tool_use don't get an\n // attribution here — `usage` from a mid-loop response covers tool\n // arguments, not user-visible content. Only the terminal response\n // (or any with real output) gets the badge.\n if (payload.usage.output > 0) {\n useChatStore.getState().updateMessage(id, { usage: payload.usage });\n }\n }\n useChatStore.getState().setCurrentAssistantMessage(null);\n // Belt-and-suspenders: response landed → no more thinking for this turn.\n useChatStore.getState().clearThinking();\n });\n\n on('tool.confirm_needed', (msg) => {\n const payload = msg.payload as {\n id: string;\n toolName: string;\n input: unknown;\n suggestedPattern: string;\n };\n useUIStore.getState().showConfirm({\n id: payload.id,\n toolName: payload.toolName,\n input: payload.input,\n suggestedPattern: payload.suggestedPattern,\n });\n });\n\n on('run.result', (msg) => {\n const payload = msg.payload as {\n status: string;\n iterations: number;\n finalText?: string;\n error?: { code: string; message: string; recoverable: boolean };\n };\n useSessionStore.getState().setIteration(null);\n useChatStore.getState().setLoading(false);\n useChatStore.getState().setCurrentAssistantMessage(null);\n useChatStore.getState().clearThinking();\n // Compute a per-turn summary and attach it to the last assistant\n // message of this run, then clear runStart for the next turn. Tools\n // are counted by walking the chat backwards for tool bubbles whose\n // timestamp is after runStart.at.\n const runStart = useChatStore.getState().runStart;\n if (runStart && payload.status === 'done') {\n const all = useChatStore.getState().messages;\n let lastAssistantIdx = -1;\n let toolCount = 0;\n for (let i = all.length - 1; i >= 0; i--) {\n const m = all[i]!;\n if (m.role === 'assistant' && lastAssistantIdx === -1 && m.content) {\n lastAssistantIdx = i;\n }\n if (m.role === 'tool' && m.timestamp >= runStart.at) {\n toolCount += 1;\n }\n // Stop walking when we cross the user message that started this run.\n if (m.role === 'user' && m.timestamp <= runStart.at) break;\n }\n if (lastAssistantIdx !== -1) {\n const sessionCost = useSessionStore.getState().cost;\n useChatStore.getState().updateMessage(all[lastAssistantIdx]!.id, {\n runSummary: {\n iterations: payload.iterations,\n tools: toolCount,\n durationMs: Date.now() - runStart.at,\n costDelta: Math.max(0, sessionCost - runStart.cost),\n },\n });\n }\n }\n useChatStore.getState().setRunStart(null);\n if (payload.status !== 'done' && payload.error) {\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: `Error: ${payload.error.message}`,\n isError: true,\n });\n toast.error(`Run ended: ${payload.error.message}`);\n notifyIfHidden('WrongStack run failed', payload.error.message);\n if (typeof document !== 'undefined' && document.hidden) {\n setFaviconStatus('error');\n }\n } else if (payload.status === 'done') {\n // Two-pronged \"you can come back now\" signal:\n // • Toast: lands in-page when the user returns, no permission needed.\n // • OS notification: only when the tab is actually hidden AND the\n // user previously granted permission. Cheap if neither applies.\n if (typeof document !== 'undefined' && document.hidden) {\n toast.success(\n `Run completed in ${payload.iterations} iteration${payload.iterations === 1 ? '' : 's'}`,\n );\n notifyIfHidden(\n 'WrongStack run finished',\n `Completed in ${payload.iterations} iteration${payload.iterations === 1 ? '' : 's'}.`,\n );\n setFaviconStatus('ready');\n }\n // Lazy permission ask — only after the first successful run, so a\n // user who never sticks around long enough for one doesn't see a\n // permission prompt on mount.\n void ensureNotificationPermission();\n // Optional chime — fires regardless of tab visibility because users\n // often want the audible cue specifically when their attention is\n // elsewhere. Synthesized via Web Audio, see lib/chime.ts. Off by\n // default; user opts in via Command Palette.\n if (useConfigStore.getState().soundOnComplete) {\n try {\n playCompletionChime();\n } catch {\n /* audio policy may block */\n }\n }\n }\n // Drain a queued follow-up if the user typed while we were running.\n // We pull one message at a time so the queue doesn't all fire as a\n // single mega-prompt — each one starts its own iteration loop.\n const next = useChatStore.getState().dequeue();\n if (next) {\n const client = getWSClient(useConfigStore.getState().wsUrl);\n useChatStore.getState().addMessage({ role: 'user', content: next });\n useChatStore.getState().setLoading(true);\n client.sendMessage(next);\n }\n });\n\n on('tools.list', (msg) => {\n const p = msg.payload as {\n tools: Array<{ name: string; description: string; params: string[] }>;\n };\n const lines = [\n `🛠️ **Registered tools** (${p.tools.length})`,\n '',\n ...p.tools.map(\n (t) =>\n `• \\`${t.name}\\`${t.params.length ? ` (${t.params.join(', ')})` : ''} — ${t.description || '_no description_'}`,\n ),\n ];\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('memory.list', (msg) => {\n const p = msg.payload as { text: string; error?: string };\n const body = p.text?.trim();\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: p.error\n ? `Memory read failed: ${p.error}`\n : body\n ? `🧠 **Memory** \\n\\n${body}`\n : '🧠 **Memory** \\n\\n_empty — nothing remembered yet_',\n });\n });\n\n on('skills.list', (msg) => {\n const p = msg.payload as {\n enabled: boolean;\n error?: string;\n skills: Array<{\n name: string;\n description: string;\n version: string;\n source: string;\n path: string;\n trigger: string;\n scope: string[];\n }>;\n };\n if (!p.enabled) {\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: '🎯 **Skills** \\n\\n_disabled (config.features.skills = false)_',\n });\n return;\n }\n const lines = [\n `🎯 **Skills** (${p.skills.length})`,\n '',\n ...(p.skills.length === 0\n ? ['_none registered_']\n : p.skills.map(\n (s) =>\n `• \\`${s.name}\\`${s.version ? ` v${s.version}` : ''} _(${s.source})_ — ${s.description || s.trigger || '_no description_'}`,\n )),\n ];\n if (p.error) lines.push('', `⚠ ${p.error}`);\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('diag.get', (msg) => {\n const p = msg.payload as {\n provider: string;\n model: string;\n cwd: string;\n sessionId: string;\n tools: { count: number; names: string[] };\n features: { memory: boolean; skills: boolean; modelsRegistry: boolean };\n mode: string;\n usage: { input: number; output: number; cacheRead?: number };\n messages: number;\n todos: number;\n };\n const lines = [\n '🩺 **Runtime diagnostics**',\n '',\n `**Provider:** \\`${p.provider}\\` / \\`${p.model}\\``,\n `**Mode:** \\`${p.mode}\\``,\n `**Session:** \\`${p.sessionId}\\``,\n `**CWD:** \\`${p.cwd}\\``,\n '',\n `**Tools:** ${p.tools.count}`,\n `**Messages:** ${p.messages} · **Todos:** ${p.todos}`,\n `**Usage:** ${p.usage.input.toLocaleString()} in · ${p.usage.output.toLocaleString()} out${p.usage.cacheRead ? ` · ${p.usage.cacheRead.toLocaleString()} cache` : ''}`,\n '',\n `**Features:** memory=${p.features.memory ? '✓' : '✗'} · skills=${p.features.skills ? '✓' : '✗'} · modelsRegistry=${p.features.modelsRegistry ? '✓' : '✗'}`,\n ];\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('stats.get', (msg) => {\n const p = msg.payload as {\n sessionId: string;\n provider: string;\n model: string;\n usage: { input: number; output: number; cacheRead?: number; cacheWrite?: number };\n cache: { readTokens: number; writeTokens: number; hitRatio: number } | null;\n cost: number;\n messages: number;\n readFiles: number;\n tools: number;\n elapsedMs: number;\n };\n const elapsedSec = Math.floor(p.elapsedMs / 1000);\n const elapsed =\n elapsedSec < 60\n ? `${elapsedSec}s`\n : elapsedSec < 3600\n ? `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`\n : `${Math.floor(elapsedSec / 3600)}h ${Math.floor((elapsedSec % 3600) / 60)}m`;\n const lines = [\n '📈 **Session stats**',\n '',\n `**Session:** \\`${p.sessionId}\\``,\n `**Provider/Model:** \\`${p.provider}\\` / \\`${p.model}\\``,\n `**Elapsed:** ${elapsed}`,\n '',\n `**Usage:** ${p.usage.input.toLocaleString()} in · ${p.usage.output.toLocaleString()} out`,\n ...(p.cache && p.cache.readTokens > 0\n ? [\n `**Cache:** ${p.cache.readTokens.toLocaleString()} read · ${p.cache.writeTokens.toLocaleString()} write · hit ratio ${(p.cache.hitRatio * 100).toFixed(1)}%`,\n ]\n : []),\n `**Cost:** $${p.cost.toFixed(4)}`,\n '',\n `**Messages:** ${p.messages} · **Files read:** ${p.readFiles} · **Tools available:** ${p.tools}`,\n ];\n useChatStore.getState().addMessage({ role: 'assistant', content: lines.join('\\n') });\n });\n\n on('todos.updated', (msg) => {\n const p = msg.payload as {\n todos: Array<{\n id: string;\n content: string;\n status: 'pending' | 'in_progress' | 'completed';\n activeForm?: string;\n }>;\n };\n useSessionStore.getState().setTodos(p.todos ?? []);\n });\n\n on('modes.list', (msg) => {\n const p = msg.payload as {\n modes: Array<{ id: string; name: string; description: string; isActive: boolean }>;\n activeId: string;\n };\n useSessionStore\n .getState()\n .setModes(p.modes.map((m) => ({ id: m.id, name: m.name, description: m.description })));\n useSessionStore.getState().setEnv({ mode: p.activeId });\n });\n\n on('sessions.list', (msg) => {\n const payload = msg.payload as {\n sessions: SessionHistoryEntry[];\n error?: string;\n };\n useHistoryStore.getState().setEntries(payload.sessions ?? [], payload.error ?? null);\n });\n\n on('error', (msg) => {\n const payload = msg.payload as { phase: string; message: string };\n useChatStore.getState().addMessage({\n role: 'assistant',\n content: `[${payload.phase}] ${payload.message}`,\n isError: true,\n });\n useChatStore.getState().setLoading(false);\n });\n\n return () => {\n for (const off of offs) off();\n };\n}\n\n/**\n * Mounts the WebSocket connection and installs event handlers EXACTLY ONCE.\n * Call this from App.tsx (top of the tree) and nowhere else.\n */\nexport function useWebSocketBootstrap(): void {\n const { autoConnect, wsUrl } = useConfigStore();\n const setWsStatus = useConfigStore((s) => s.setWsStatus);\n const installed = useRef(false);\n\n useEffect(() => {\n if (!autoConnect) return;\n installFaviconVisibilityReset();\n const ws = getWSClient(wsUrl);\n\n // Subscribe to fine-grained status — the topbar uses this to flip\n // between \"Connecting…\", \"Connected\", \"Reconnecting (attempt 3, retrying in 8s)\",\n // and the terminal \"Disconnected\" with the last error.\n const offStatus = ws.onStatus((s) => setWsStatus(s));\n\n ws.connect().catch((err) => {\n // eslint-disable-next-line no-console\n console.error('[WS] Connection failed:', err);\n });\n\n // installed.current guards against React StrictMode's double-mount in dev.\n if (installed.current) {\n return () => {\n offStatus();\n };\n }\n installed.current = true;\n const off = installHandlers(ws);\n return () => {\n off();\n offStatus();\n installed.current = false;\n };\n }, [autoConnect, wsUrl, setWsStatus]);\n}\n\n/**\n * Cheap accessor for the singleton WS client and its imperative action\n * methods. Components call this freely; it does NOT register handlers.\n */\nexport function useWebSocket() {\n const { wsUrl } = useConfigStore();\n const client = getWSClient(wsUrl);\n\n const sendMessage = useCallback(\n (content: string) => {\n if (client.isConnected) return client.sendMessage(content);\n return null;\n },\n [client],\n );\n\n const sendAbort = useCallback(() => client.sendAbort(), [client]);\n\n const { hideConfirm } = useUIStore();\n const sendConfirm = useCallback(\n (id: string, decision: 'yes' | 'no' | 'always' | 'deny') => {\n client.sendConfirm(id, decision);\n hideConfirm();\n },\n [client, hideConfirm],\n );\n\n const switchModel = useCallback(\n (provider: string, model: string) => client.switchModel(provider, model),\n [client],\n );\n\n const listProviders = useCallback(() => client.listProviders(), [client]);\n const listProviderModels = useCallback(\n (providerId: string) => client.listProviderModels(providerId),\n [client],\n );\n const listSavedProviders = useCallback(() => client.listSavedProviders(), [client]);\n const addKey = useCallback(\n (providerId: string, label: string, apiKey: string) => client.addKey(providerId, label, apiKey),\n [client],\n );\n const updateKey = useCallback(\n (providerId: string, label: string, apiKey: string) =>\n client.updateKey(providerId, label, apiKey),\n [client],\n );\n const deleteKey = useCallback(\n (providerId: string, label: string) => client.deleteKey(providerId, label),\n [client],\n );\n const setActiveKey = useCallback(\n (providerId: string, label: string) => client.setActiveKey(providerId, label),\n [client],\n );\n const addProvider = useCallback(\n (id: string, family: string, baseUrl?: string, apiKey?: string) =>\n client.addProvider(id, family, baseUrl, apiKey),\n [client],\n );\n const removeProvider = useCallback(\n (providerId: string) => client.removeProvider(providerId),\n [client],\n );\n\n const listSessions = useCallback(\n (limit?: number) => {\n useHistoryStore.getState().setLoading(true);\n client.listSessions(limit);\n },\n [client],\n );\n const deleteSession = useCallback(\n (id: string) => {\n useHistoryStore.getState().removeEntry(id);\n client.deleteSession(id);\n },\n [client],\n );\n const resumeSession = useCallback((id: string) => client.resumeSessionById(id), [client]);\n const saveSession = useCallback(() => client.saveSession(), [client]);\n const listTools = useCallback(() => client.listTools(), [client]);\n const listMemory = useCallback(() => client.listMemory(), [client]);\n const listSkills = useCallback(() => client.listSkills(), [client]);\n const getDiag = useCallback(() => client.getDiag(), [client]);\n const getStats = useCallback(() => client.getStats(), [client]);\n const listModes = useCallback(() => client.listModes(), [client]);\n const switchMode = useCallback((id: string) => client.switchMode(id), [client]);\n\n return {\n client,\n sendMessage,\n sendAbort,\n sendConfirm,\n switchModel,\n listProviders,\n listProviderModels,\n listSavedProviders,\n addKey,\n updateKey,\n deleteKey,\n setActiveKey,\n addProvider,\n removeProvider,\n listSessions,\n deleteSession,\n resumeSession,\n saveSession,\n listTools,\n listMemory,\n listSkills,\n getDiag,\n getStats,\n listModes,\n switchMode,\n };\n}\n","/**\n * Dynamic favicon helper. We don't ship a `.ico` file — the icon is built at\n * runtime as an inline SVG data URL so it can be re-rendered with or without\n * a status badge.\n *\n * setFaviconStatus('idle') → plain \"W\" mark\n * setFaviconStatus('running') → \"W\" + amber pulse dot (running indicator)\n * setFaviconStatus('ready') → \"W\" + green dot (run finished, tab hidden)\n * setFaviconStatus('error') → \"W\" + red dot (run failed, tab hidden)\n *\n * The status auto-resets to 'idle' on the next visibilitychange where the\n * tab becomes visible — so the badge only persists while the user is away.\n */\n\nexport type FaviconStatus = 'idle' | 'running' | 'ready' | 'error';\n\nconst BASE_BG = '#4f46e5'; // indigo-600, matches the topbar Zap mark.\n\nfunction buildSvg(status: FaviconStatus): string {\n const badge = (() => {\n if (status === 'ready')\n return '<circle cx=\"50\" cy=\"14\" r=\"14\" fill=\"#22c55e\" stroke=\"#fff\" stroke-width=\"3\" />';\n if (status === 'error')\n return '<circle cx=\"50\" cy=\"14\" r=\"14\" fill=\"#ef4444\" stroke=\"#fff\" stroke-width=\"3\" />';\n if (status === 'running')\n return '<circle cx=\"50\" cy=\"14\" r=\"14\" fill=\"#f59e0b\" stroke=\"#fff\" stroke-width=\"3\" />';\n return '';\n })();\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\">\n <rect x=\"2\" y=\"2\" width=\"60\" height=\"60\" rx=\"14\" fill=\"${BASE_BG}\" />\n <text x=\"32\" y=\"44\" text-anchor=\"middle\" font-family=\"-apple-system,Segoe UI,Roboto,sans-serif\" font-size=\"38\" font-weight=\"700\" fill=\"#fff\">W</text>\n ${badge}\n </svg>`;\n}\n\nfunction svgToDataUrl(svg: string): string {\n // encodeURIComponent keeps the URL valid across browsers; btoa would\n // also work but breaks on non-ASCII (not a concern here, but cleaner).\n return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;\n}\n\nfunction ensureLink(): HTMLLinkElement | null {\n if (typeof document === 'undefined') return null;\n let link = document.querySelector<HTMLLinkElement>('link[rel=\"icon\"]');\n if (!link) {\n link = document.createElement('link');\n link.rel = 'icon';\n link.type = 'image/svg+xml';\n document.head.appendChild(link);\n }\n return link;\n}\n\nlet currentStatus: FaviconStatus = 'idle';\n\nexport function setFaviconStatus(status: FaviconStatus): void {\n currentStatus = status;\n const link = ensureLink();\n if (!link) return;\n link.href = svgToDataUrl(buildSvg(status));\n}\n\nlet visibilityHookInstalled = false;\n\n/** Install the visibilitychange listener once. When the user comes back to\n * the tab we clear any \"ready/error\" badge so they don't see a stale\n * notification dot after they've already returned. */\nexport function installFaviconVisibilityReset(): void {\n if (visibilityHookInstalled || typeof document === 'undefined') return;\n visibilityHookInstalled = true;\n document.addEventListener('visibilitychange', () => {\n if (!document.hidden && (currentStatus === 'ready' || currentStatus === 'error')) {\n setFaviconStatus('idle');\n }\n });\n}\n","/**\n * Browser Notification API wrapper. We never pop a notification when the\n * tab is foreground — the user already sees the chat update, so an OS\n * popup would just be noise. The flow:\n *\n * 1. requestPermission() is called lazily on first run completion;\n * browsers require a user gesture for the prompt to be safe but\n * Chrome / Firefox accept it from any code path with relaxed rules.\n * 2. notifyIfHidden() short-circuits unless the page is `document.hidden`.\n * 3. Clicking the notification focuses the page (best-effort — some\n * browsers won't focus across tab groups).\n */\n\nlet permissionState: NotificationPermission | 'unsupported' = 'default';\n\nif (typeof window !== 'undefined' && 'Notification' in window) {\n permissionState = Notification.permission;\n} else {\n permissionState = 'unsupported';\n}\n\nexport async function ensureNotificationPermission(): Promise<\n NotificationPermission | 'unsupported'\n> {\n if (\n permissionState === 'unsupported' ||\n permissionState === 'granted' ||\n permissionState === 'denied'\n ) {\n return permissionState;\n }\n try {\n const result = await Notification.requestPermission();\n permissionState = result;\n return result;\n } catch {\n return 'denied';\n }\n}\n\nexport function notifyIfHidden(title: string, body?: string): void {\n if (typeof document === 'undefined' || !document.hidden) return;\n if (permissionState !== 'granted') return;\n try {\n const n = new Notification(title, {\n body,\n icon: '/favicon.ico',\n // Tag-collapse: if multiple notifications stack while the tab is\n // hidden, only the latest \"WrongStack run\" shows up so we don't\n // litter the OS notification center.\n tag: 'wrongstack-run',\n // Auto-dismiss as soon as the user focuses the tab.\n });\n n.onclick = () => {\n window.focus();\n n.close();\n };\n } catch {\n // Some browsers (e.g. iOS Safari) throw — silently swallow.\n }\n}\n","import type { WSClientMessage, WSServerMessage } from '../types';\n\ntype EventHandler = (msg: WSServerMessage) => void;\n\ninterface PendingConfirm {\n resolve: (decision: 'yes' | 'no' | 'always' | 'deny') => void;\n}\n\n/** Internal connection lifecycle states the UI subscribes to. */\nexport type WsStatus =\n | { state: 'connecting' }\n | { state: 'open' }\n | { state: 'closed'; error?: string }\n | { state: 'reconnecting'; attempt: number; nextRetryAt: number; lastError?: string };\n\nexport class WrongStackWebSocketClient {\n private ws: WebSocket | null = null;\n private url: string;\n private handlers: Map<string, Set<EventHandler>> = new Map();\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 10;\n private reconnectDelay = 1000;\n private shouldReconnect = true;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private messageQueue: WSClientMessage[] = [];\n private pendingConfirms: Map<string, PendingConfirm> = new Map();\n private sessionId: string | null = null;\n /** Stored last close reason / error message so the UI can show \"what\n * went wrong\" while reconnecting instead of a generic spinner. */\n private lastErrorText: string | undefined;\n private statusListeners = new Set<(s: WsStatus) => void>();\n private currentStatus: WsStatus = { state: 'connecting' };\n\n onStatus(fn: (s: WsStatus) => void): () => void {\n this.statusListeners.add(fn);\n fn(this.currentStatus);\n return () => this.statusListeners.delete(fn);\n }\n\n get status(): WsStatus {\n return this.currentStatus;\n }\n\n private setStatus(s: WsStatus) {\n this.currentStatus = s;\n for (const fn of this.statusListeners) {\n try {\n fn(s);\n } catch {\n /* listener errors must not break the socket */\n }\n }\n }\n\n constructor(url?: string) {\n this.url = url ?? defaultWsUrl();\n }\n\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n resolve();\n return;\n }\n\n this.setStatus({ state: 'connecting' });\n\n try {\n this.ws = new WebSocket(this.url);\n this.ws.binaryType = 'arraybuffer';\n\n const connectTimeout = setTimeout(() => {\n reject(new Error('Connection timeout'));\n }, 10000);\n\n // Track whether the connection was ever established so onerror and\n // onclose know whether to reject the promise or just attempt a\n // reconnect. Without this, a connection failure leaves callers\n // awaiting connect() hanging forever.\n let established = false;\n\n this.ws.onopen = () => {\n clearTimeout(connectTimeout);\n established = true;\n console.log('[WS Client] Connected');\n this.reconnectAttempts = 0;\n this.lastErrorText = undefined;\n this.setStatus({ state: 'open' });\n this.flushMessageQueue();\n resolve();\n };\n\n this.ws.onmessage = (event) => {\n try {\n const msg = JSON.parse(event.data) as WSServerMessage;\n this.handleMessage(msg);\n } catch (err) {\n console.error('[WS Client] Failed to parse message', err);\n }\n };\n\n this.ws.onerror = (error) => {\n console.error('[WS Client] Error', error);\n // ErrorEvent in browsers is intentionally opaque — Chrome won't\n // expose the underlying reason for security. We stash a generic\n // hint so the UI has something to display.\n this.lastErrorText = 'Connection error (see browser devtools)';\n if (!established) {\n clearTimeout(connectTimeout);\n reject(new Error(this.lastErrorText));\n }\n };\n\n this.ws.onclose = (ev) => {\n console.log('[WS Client] Disconnected', ev.code, ev.reason);\n if (!established) {\n clearTimeout(connectTimeout);\n const reason = ev.reason || `Closed with code ${ev.code}`;\n this.lastErrorText = reason;\n reject(new Error(reason));\n return;\n }\n if (ev.reason && !this.lastErrorText) {\n this.lastErrorText = `${ev.reason} (code ${ev.code})`;\n } else if (!this.lastErrorText && ev.code !== 1000) {\n this.lastErrorText = `Closed with code ${ev.code}`;\n }\n this.attemptReconnect();\n };\n } catch (err) {\n this.lastErrorText = err instanceof Error ? err.message : String(err);\n this.setStatus({ state: 'closed', error: this.lastErrorText });\n reject(err);\n }\n });\n }\n\n private attemptReconnect() {\n if (!this.shouldReconnect || this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.log('[WS Client] Not reconnecting');\n this.reconnectTimer = null;\n this.setStatus({ state: 'closed', error: this.lastErrorText ?? 'Disconnected' });\n return;\n }\n\n this.reconnectAttempts++;\n const delay = Math.min(this.reconnectDelay * 2 ** (this.reconnectAttempts - 1), 30000);\n const nextRetryAt = Date.now() + delay;\n console.log(`[WS Client] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n this.setStatus({\n state: 'reconnecting',\n attempt: this.reconnectAttempts,\n nextRetryAt,\n lastError: this.lastErrorText,\n });\n\n this.reconnectTimer = setTimeout(async () => {\n if (this.shouldReconnect) {\n try {\n await this.connect();\n } catch (err) {\n console.error('[WS Client] Reconnect failed', err);\n }\n }\n }, delay);\n }\n\n /** Force an immediate reconnect attempt, bypassing the backoff timer. */\n retryNow(): void {\n if (this.currentStatus.state === 'open') return;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.reconnectAttempts = 0;\n void this.connect().catch(() => undefined);\n }\n\n private flushMessageQueue() {\n while (this.messageQueue.length > 0) {\n const msg = this.messageQueue.shift();\n if (msg) this.send(msg);\n }\n }\n\n private handleMessage(msg: WSServerMessage) {\n if (msg.type === 'tool.confirm_needed') {\n const payload = msg.payload as unknown as {\n id: string;\n toolName: string;\n input: unknown;\n suggestedPattern: string;\n resolve: (d: 'yes' | 'no' | 'always' | 'deny') => void;\n };\n\n this.pendingConfirms.set(payload.id, {\n resolve: payload.resolve,\n });\n\n const msgForHandler = {\n ...msg,\n payload: {\n ...payload,\n resolve: () => {},\n },\n };\n\n this.emit(msgForHandler as WSServerMessage);\n return;\n }\n\n if (msg.type === 'session.start') {\n const payload = msg.payload as { sessionId: string };\n this.sessionId = payload.sessionId;\n }\n\n this.emit(msg);\n }\n\n private emit(msg: WSServerMessage) {\n const handlers = this.handlers.get(msg.type);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(msg);\n } catch (err) {\n console.error(`[WS Client] Handler error for ${msg.type}`, err);\n }\n }\n }\n }\n\n send(message: WSClientMessage) {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(message));\n } else {\n this.messageQueue.push(message);\n }\n }\n\n on(eventType: string, handler: EventHandler): () => void {\n let handlers = this.handlers.get(eventType);\n if (!handlers) {\n handlers = new Set();\n this.handlers.set(eventType, handlers);\n }\n handlers.add(handler);\n return () => handlers?.delete(handler);\n }\n\n off(eventType: string, handler: EventHandler) {\n this.handlers.get(eventType)?.delete(handler);\n }\n\n sendMessage(content: string): string {\n const id = `msg_${Date.now()}_${crypto.randomUUID().slice(0, 8)}`;\n this.send({\n type: 'user_message',\n payload: {\n id,\n content,\n timestamp: Date.now(),\n },\n });\n return id;\n }\n\n sendAbort() {\n this.send({\n type: 'abort',\n payload: {},\n } as unknown as WSClientMessage);\n }\n\n sendConfirm(id: string, decision: 'yes' | 'no' | 'always' | 'deny') {\n const pending = this.pendingConfirms.get(id);\n if (pending) {\n pending.resolve(decision);\n this.pendingConfirms.delete(id);\n }\n this.send({\n type: 'tool.confirm_result',\n payload: { id, decision },\n });\n }\n\n switchModel(provider: string, model: string) {\n this.send({\n type: 'model.switch',\n payload: { provider, model },\n });\n }\n\n // ---- Provider/Model/Key management (mirrors TUI/CLI auth-menu) ----\n\n listProviders() {\n this.send({ type: 'providers.list' });\n }\n\n listProviderModels(providerId: string) {\n this.send({ type: 'provider.models', payload: { providerId } });\n }\n\n listSavedProviders() {\n this.send({ type: 'providers.saved' });\n }\n\n addKey(providerId: string, label: string, apiKey: string) {\n this.send({ type: 'key.add', payload: { providerId, label, apiKey } });\n }\n\n updateKey(providerId: string, label: string, apiKey: string) {\n this.send({ type: 'key.update', payload: { providerId, label, apiKey } });\n }\n\n deleteKey(providerId: string, label: string) {\n this.send({ type: 'key.delete', payload: { providerId, label } });\n }\n\n setActiveKey(providerId: string, label: string) {\n this.send({ type: 'key.set_active', payload: { providerId, label } });\n }\n\n addProvider(id: string, family: string, baseUrl?: string, apiKey?: string) {\n this.send({ type: 'provider.add', payload: { id, family, baseUrl, apiKey } });\n }\n\n removeProvider(providerId: string) {\n this.send({ type: 'provider.remove', payload: { providerId } });\n }\n\n newSession() {\n this.send({ type: 'session.new' });\n }\n\n clearContext() {\n this.send({ type: 'context.clear' });\n }\n\n compactContext(aggressive = false) {\n this.send({ type: 'context.compact', payload: { aggressive } });\n }\n\n debugContext() {\n this.send({ type: 'context.debug' });\n }\n\n // ---- Inspect commands (mirror TUI/CLI's /tools /memory /skill /diag /stats) ----\n\n listTools() {\n this.send({ type: 'tools.list' });\n }\n\n listMemory() {\n this.send({ type: 'memory.list' });\n }\n\n remember(text: string, scope?: 'project-agents' | 'project-memory' | 'user-memory') {\n this.send({ type: 'memory.remember', payload: { text, scope } });\n }\n\n forget(text: string, scope?: 'project-agents' | 'project-memory' | 'user-memory') {\n this.send({ type: 'memory.forget', payload: { text, scope } });\n }\n\n listSkills() {\n this.send({ type: 'skills.list' });\n }\n\n getDiag() {\n this.send({ type: 'diag.get' });\n }\n\n getStats() {\n this.send({ type: 'stats.get' });\n }\n\n saveSession() {\n this.send({ type: 'session.save' });\n }\n\n resumeSessionById(id: string) {\n this.send({ type: 'session.resume', payload: { id } });\n }\n\n listModes() {\n this.send({ type: 'modes.list' });\n }\n\n switchMode(id: string) {\n this.send({ type: 'mode.switch', payload: { id } });\n }\n\n listFiles(query?: string, limit?: number) {\n this.send({ type: 'files.list', payload: { query, limit } });\n }\n\n getTodos() {\n this.send({ type: 'todos.get' });\n }\n\n clearTodos() {\n this.send({ type: 'todos.clear' });\n }\n\n listSessions(limit = 50) {\n this.send({ type: 'sessions.list', payload: { limit } });\n }\n\n deleteSession(id: string) {\n this.send({ type: 'session.delete', payload: { id } });\n }\n\n resumeSession(sessionId: string) {\n this.send({\n type: 'session.resume',\n payload: { id: sessionId },\n });\n }\n\n ping() {\n this.send({ type: 'ping' });\n }\n\n disconnect() {\n this.shouldReconnect = false;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.ws?.close();\n this.ws = null;\n }\n\n get isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n get currentSessionId(): string | null {\n return this.sessionId;\n }\n}\n\nlet client: WrongStackWebSocketClient | null = null;\n\n/**\n * Default WS URL derived from the page's host.\n *\n * Subtle gotcha on Windows: when the page is loaded from `http://localhost:3456`,\n * the browser resolves `localhost` *itself* and on Windows it tries IPv6 `[::1]`\n * before IPv4 `127.0.0.1`. If the backend listens only on `127.0.0.1`, every\n * connection attempt to `ws://localhost:3457` first hits the IPv6 socket\n * (refused) and then either gives up or flaps — symptom: \"ws disconnect hep\".\n *\n * Fix: when the page is on a loopback host (`localhost` / `127.0.0.1` / `::1`),\n * force the WS URL to use the literal IPv4 loopback address. That bypasses the\n * DNS dance entirely. For any other hostname (LAN IP, custom WS_HOST override)\n * we keep the page's hostname so things still \"just work\".\n */\nfunction defaultWsUrl(): string {\n const port = 3457;\n if (typeof window === 'undefined' || !window.location?.hostname) {\n return `ws://127.0.0.1:${port}`;\n }\n const host = window.location.hostname.toLowerCase();\n if (host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host === '::1') {\n return `ws://127.0.0.1:${port}`;\n }\n return `ws://${window.location.hostname}:${port}`;\n}\n\nexport function getWSClient(url?: string): WrongStackWebSocketClient {\n if (!client) {\n client = new WrongStackWebSocketClient(url);\n }\n return client;\n}\n","import type { Usage } from '@wrongstack/core';\nimport type { ContentBlock } from '@wrongstack/core';\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\n\n/**\n * Strip immediately-repeated paragraphs/lines from an assistant reply.\n * MiniMax-M2.7 (and other smaller open models) sometimes emit the same\n * paragraph twice in one stream — we don't want that to land in the chat.\n * We only collapse *consecutive* duplicates so legitimate repetition\n * elsewhere in the message is preserved.\n */\nfunction dedupeRepeatedBlocks(text: string): string {\n if (!text) return text;\n // Pass 1: paragraph-level (split on blank lines).\n const paraSplit = text.split(/\\n{2,}/);\n const paras: string[] = [];\n for (const p of paraSplit) {\n if (paras.length > 0 && paras[paras.length - 1]!.trim() === p.trim()) continue;\n paras.push(p);\n }\n // Pass 2: line-level within each paragraph (handles models that emit the\n // same sentence twice without a blank line between).\n const cleaned = paras.map((p) => {\n const lines = p.split('\\n');\n const out: string[] = [];\n for (const line of lines) {\n if (out.length > 0 && line.trim().length > 0 && out[out.length - 1]!.trim() === line.trim()) {\n continue;\n }\n out.push(line);\n }\n return out.join('\\n');\n });\n return cleaned.join('\\n\\n');\n}\n\n// ============================================\n// Types\n// ============================================\n\nexport interface MessageContent {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string | ContentBlock[];\n}\n\nexport interface ToolExecution {\n id: string;\n name: string;\n input?: unknown;\n output?: string;\n durationMs?: number;\n ok: boolean;\n startedAt: number;\n completedAt?: number;\n}\n\nexport interface ChatMessage {\n id: string;\n content: string;\n role: 'user' | 'assistant' | 'system' | 'tool';\n toolName?: string;\n toolInput?: unknown;\n toolResult?: string;\n /** Wall-clock ms reported by the backend in tool.executed; rendered next\n * to the tool name so the user can spot slow tools at a glance. */\n toolDurationMs?: number;\n /** Backend's tool_use id (e.g. \"toolu_...\" from Anthropic). Used to map\n * tool.executed events back to the right bubble when the model fires\n * multiple tools in parallel — currentToolId alone only points at the\n * most recent start and would leave earlier ones stuck on \"Running...\". */\n toolUseId?: string;\n isError?: boolean;\n timestamp: number;\n usage?: Usage;\n streaming?: boolean;\n parentId?: string;\n /** Live progress lines for an in-flight tool, populated from\n * tool.progress WS events. Each line is shown in chronological order\n * inside the tool bubble while it's still running, and cleared once the\n * final tool.executed lands (toolResult takes over). Capped to the last\n * ~30 lines so a chatty bash command can't grow this unbounded. */\n progressLines?: string[];\n /** End-of-run summary attached to the last assistant message of a turn\n * after run.result lands. Populated by the run.result handler in\n * useWebSocket — gives the user a single-line readout of what just\n * happened (iterations, tool calls, elapsed time, cost). */\n runSummary?: {\n iterations: number;\n tools: number;\n durationMs: number;\n costDelta: number;\n };\n}\n\nexport interface SessionInfo {\n id: string;\n startedAt: number;\n provider: string;\n model: string;\n title?: string;\n}\n\n// ============================================\n// Chat Store\n// ============================================\n\ninterface ChatState {\n messages: ChatMessage[];\n currentAssistantMessageId: string | null;\n currentToolId: string | null;\n isLoading: boolean;\n abortController: AbortController | null;\n executions: Map<string, ToolExecution>;\n /** Messages typed while the agent was running. Drained one-at-a-time\n * after run.result lands so the user can stack up follow-ups without\n * waiting for each turn to finish. */\n queue: string[];\n /** Snapshot taken at the start of the current run (first iteration.started\n * after idle). Used by run.result to compute the per-turn summary —\n * duration is now-at minus this `at`, cost delta is the difference\n * between the session's current cost and the cost captured here. Null\n * while idle. */\n runStart: { at: number; cost: number } | null;\n /** Transient extended-thinking buffer. Populated by provider.thinking_delta\n * events and shown as a soft, ephemeral bubble below the chat tail while\n * the model is reasoning. Cleared the moment the model produces user-\n * facing output (text_delta) or starts a tool — and at provider.response /\n * run.result. Never persisted into `messages`, so refresh wipes it. */\n thinkingBuffer: string;\n /** Wall-clock ms when the current thinking burst started, for the chip's\n * elapsed timer. Reset alongside `thinkingBuffer`. */\n thinkingStartedAt: number | null;\n\n // Actions\n addMessage: (msg: Omit<ChatMessage, 'id' | 'timestamp'>) => string;\n updateMessage: (id: string, updates: Partial<ChatMessage>) => void;\n appendToMessage: (id: string, text: string) => void;\n /** Clean up an assistant bubble after its provider.response arrived:\n * collapse model-emitted duplicate paragraphs / consecutive duplicate\n * lines, flip the streaming flag off. Some models (notably MiniMax-M2.7)\n * emit the same paragraph twice in one stream — this strips that noise\n * at the bubble boundary so the persisted content matches what the user\n * expects to see. */\n finalizeMessage: (id: string) => void;\n setToolResult: (id: string, result: string, ok: boolean) => void;\n /** Append a single progress line to the tool bubble identified by its\n * ChatMessage id. Capped at 30 lines (oldest dropped) so chatty tools\n * don't bloat memory or rerender too aggressively. */\n appendToolProgress: (id: string, line: string) => void;\n setLoading: (loading: boolean) => void;\n setAbortController: (ctrl: AbortController | null) => void;\n clearMessages: () => void;\n setCurrentAssistantMessage: (id: string | null) => void;\n setCurrentToolId: (id: string | null) => void;\n /** Remove the message identified by `id` and every message after it.\n * Used by the \"edit + regenerate\" action on user bubbles — the user\n * clicks the pencil, types a corrected prompt, and we want everything\n * downstream of that point to disappear so the new send looks like a\n * fresh branch from this question. */\n truncateAfter: (id: string) => void;\n addExecution: (exec: ToolExecution) => void;\n updateExecution: (id: string, updates: Partial<ToolExecution>) => void;\n enqueue: (text: string) => void;\n dequeue: () => string | null;\n removeQueued: (idx: number) => void;\n clearQueue: () => void;\n setRunStart: (s: { at: number; cost: number } | null) => void;\n /** Append a thinking chunk. Lazy-starts the elapsed timer on the first\n * chunk after a clear. */\n appendThinking: (text: string) => void;\n /** Wipe the thinking buffer + timer. Called by the events that indicate\n * the model has moved past reasoning (text/tool/response/run end). */\n clearThinking: () => void;\n}\n\nexport const useChatStore = create<ChatState>()(\n persist(\n (set, get) => ({\n messages: [],\n currentAssistantMessageId: null,\n currentToolId: null,\n isLoading: false,\n abortController: null,\n executions: new Map(),\n queue: [],\n runStart: null,\n thinkingBuffer: '',\n thinkingStartedAt: null,\n\n addMessage: (msg) => {\n const id = `msg_${Date.now()}_${crypto.randomUUID().slice(0, 8)}`;\n const fullMsg: ChatMessage = { ...msg, id, timestamp: Date.now() };\n set((state) => ({\n messages: [...state.messages, fullMsg],\n currentAssistantMessageId:\n msg.role === 'assistant' ? id : state.currentAssistantMessageId,\n }));\n return id;\n },\n\n updateMessage: (id, updates) => {\n set((state) => ({\n messages: state.messages.map((m) => (m.id === id ? { ...m, ...updates } : m)),\n }));\n },\n\n appendToMessage: (id, text) => {\n set((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, content: m.content + text } : m,\n ),\n }));\n },\n\n finalizeMessage: (id) => {\n set((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, content: dedupeRepeatedBlocks(m.content), streaming: false } : m,\n ),\n }));\n },\n\n setToolResult: (id, result, ok) => {\n set((state) => ({\n messages: state.messages.map((m) =>\n m.id === id ? { ...m, toolResult: result, isError: !ok, progressLines: undefined } : m,\n ),\n }));\n },\n\n appendToolProgress: (id, line) => {\n set((state) => ({\n messages: state.messages.map((m) => {\n if (m.id !== id) return m;\n const prev = m.progressLines ?? [];\n const next = [...prev, line];\n // Bounded buffer: keep the most recent 30 lines.\n const trimmed = next.length > 30 ? next.slice(next.length - 30) : next;\n return { ...m, progressLines: trimmed };\n }),\n }));\n },\n\n setLoading: (loading) => set({ isLoading: loading }),\n setAbortController: (ctrl) => set({ abortController: ctrl }),\n\n clearMessages: () =>\n set({\n messages: [],\n currentAssistantMessageId: null,\n currentToolId: null,\n executions: new Map(),\n }),\n\n setCurrentAssistantMessage: (id) => set({ currentAssistantMessageId: id }),\n\n setCurrentToolId: (id) => set({ currentToolId: id }),\n\n truncateAfter: (id) =>\n set((state) => {\n const idx = state.messages.findIndex((m) => m.id === id);\n if (idx === -1) return state;\n return {\n messages: state.messages.slice(0, idx),\n currentAssistantMessageId: null,\n currentToolId: null,\n };\n }),\n\n addExecution: (exec) => {\n set((state) => {\n const newExecutions = new Map(state.executions);\n newExecutions.set(exec.id, exec);\n return { executions: newExecutions };\n });\n },\n\n updateExecution: (id, updates) => {\n set((state) => {\n const newExecutions = new Map(state.executions);\n const existing = newExecutions.get(id);\n if (existing) {\n newExecutions.set(id, { ...existing, ...updates });\n }\n return { executions: newExecutions };\n });\n },\n\n enqueue: (text) => set((state) => ({ queue: [...state.queue, text] })),\n dequeue: () => {\n const q = get().queue;\n if (q.length === 0) return null;\n const [next, ...rest] = q;\n set({ queue: rest });\n return next!;\n },\n removeQueued: (idx) => set((state) => ({ queue: state.queue.filter((_, i) => i !== idx) })),\n clearQueue: () => set({ queue: [] }),\n setRunStart: (s) => set({ runStart: s }),\n appendThinking: (text) =>\n set((state) => ({\n thinkingBuffer: state.thinkingBuffer + text,\n thinkingStartedAt: state.thinkingStartedAt ?? Date.now(),\n })),\n clearThinking: () => set({ thinkingBuffer: '', thinkingStartedAt: null }),\n }),\n {\n name: 'wrongstack-chat',\n // Intentionally persist nothing. Messages are bound to a backend session;\n // restoring them on reload would resurrect a stale conversation that the\n // backend no longer has context for (the next session.start clearMessages\n // anyway). Keep theme/wsUrl in useConfigStore, transcripts ephemeral.\n partialize: () => ({}),\n },\n ),\n);\n\n// ============================================\n// Config Store\n// ============================================\n\ninterface ConfigState {\n provider: string;\n model: string;\n baseUrl?: string;\n apiKey?: string;\n wsUrl: string;\n wsConnected: boolean;\n /** Fine-grained connection state from the WS client. Drives the topbar's\n * reconnect indicator. */\n wsStatus:\n | { state: 'connecting' }\n | { state: 'open' }\n | { state: 'closed'; error?: string }\n | { state: 'reconnecting'; attempt: number; nextRetryAt: number; lastError?: string };\n theme: 'light' | 'dark' | 'system';\n autoConnect: boolean;\n /** Play a soft synthesized chime when run.result lands with status=done.\n * Off by default — opt-in via the Command Palette. Persisted so the\n * preference survives reloads. Actual playback only fires after the\n * user has interacted with the page (Web Audio autoplay policy). */\n soundOnComplete: boolean;\n\n setProvider: (provider: string) => void;\n setModel: (model: string) => void;\n setConfig: (\n config: Partial<Omit<ConfigState, 'setProvider' | 'setModel' | 'setConfig' | 'setTheme'>>,\n ) => void;\n setTheme: (theme: 'light' | 'dark' | 'system') => void;\n setWsConnected: (connected: boolean) => void;\n setWsStatus: (s: ConfigState['wsStatus']) => void;\n setSoundOnComplete: (on: boolean) => void;\n}\n\nexport const useConfigStore = create<ConfigState>()(\n persist(\n (set) => ({\n provider: 'anthropic',\n model: 'claude-sonnet-4-20250514',\n // Default WS URL tracks the page's hostname so loading from 127.0.0.1,\n // localhost, or a LAN IP all just work. For `localhost` we force the\n // literal IPv4 address — see ws-client.ts `defaultWsUrl()` for the\n // Windows IPv6/IPv4 resolution gotcha this avoids.\n wsUrl: (() => {\n if (typeof window === 'undefined' || !window.location?.hostname) {\n return 'ws://127.0.0.1:3457';\n }\n const h = window.location.hostname.toLowerCase();\n if (h === 'localhost' || h === '127.0.0.1' || h === '[::1]' || h === '::1') {\n return 'ws://127.0.0.1:3457';\n }\n return `ws://${window.location.hostname}:3457`;\n })(),\n wsConnected: false,\n wsStatus: { state: 'connecting' },\n theme: 'system',\n autoConnect: true,\n soundOnComplete: false,\n setProvider: (provider) => set({ provider }),\n setModel: (model) => set({ model }),\n setConfig: (config) => set(config),\n setTheme: (theme) => set({ theme }),\n setWsConnected: (connected) => set({ wsConnected: connected }),\n setWsStatus: (wsStatus) => set({ wsStatus, wsConnected: wsStatus.state === 'open' }),\n setSoundOnComplete: (on) => set({ soundOnComplete: on }),\n }),\n {\n name: 'wrongstack-config',\n },\n ),\n);\n\n// ============================================\n// Session Store\n// ============================================\n\ninterface SessionState {\n session: SessionInfo | null;\n totalTokens: Usage;\n /** Input tokens of the LAST provider response — used as the \"live context\n * size\" indicator in the topbar (matches what TUI's ContextChip shows). */\n lastInputTokens: number;\n cost: number;\n startTime: number | null;\n /** Model max context window, from models.dev catalog. 0 = unknown. */\n maxContext: number;\n /** USD per 1M tokens — used to compute cost deltas on every provider.response. */\n inputCost: number;\n outputCost: number;\n cacheReadCost: number;\n /** basename(projectRoot) for the topbar. */\n projectName: string;\n /** Active mode id (default | code | …). */\n mode: string;\n /** All modes the backend knows about, populated by modes.list. The\n * topbar mode chip uses this to render a picker; empty until the\n * backend responds. */\n modes: Array<{ id: string; name: string; description: string }>;\n /** Iteration progress while the agent is running. Resets on run.result. */\n iteration: { index: number; max: number } | null;\n /** Live snapshot of context.todos — backend broadcasts on every\n * tool.executed, and the sidebar/overlay reads from here. */\n todos: Array<{\n id: string;\n content: string;\n status: 'pending' | 'in_progress' | 'completed';\n activeForm?: string;\n }>;\n\n setSession: (session: SessionInfo | null) => void;\n updateUsage: (usage: Usage) => void;\n addCost: (cost: number) => void;\n startSession: (session: SessionInfo) => void;\n endSession: () => void;\n setEnv: (env: {\n maxContext?: number;\n projectName?: string;\n mode?: string;\n inputCost?: number;\n outputCost?: number;\n cacheReadCost?: number;\n }) => void;\n setIteration: (it: { index: number; max: number } | null) => void;\n setModes: (modes: Array<{ id: string; name: string; description: string }>) => void;\n setTodos: (todos: SessionState['todos']) => void;\n}\n\nexport const useSessionStore = create<SessionState>()(\n persist(\n (set) => ({\n session: null,\n totalTokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n lastInputTokens: 0,\n cost: 0,\n startTime: null,\n maxContext: 0,\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n projectName: '',\n mode: 'default',\n modes: [],\n iteration: null,\n todos: [],\n\n setSession: (session) => set({ session }),\n\n updateUsage: (usage) =>\n set((state) => {\n // True total input this turn = fresh + cached subsets, since\n // Usage is now disjoint across providers (see core's Usage doc).\n // Without summing, prompt-cached turns under-report the ctx chip.\n const totalInput = (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);\n return {\n totalTokens: {\n input: state.totalTokens.input + usage.input,\n output: state.totalTokens.output + usage.output,\n cacheRead: (state.totalTokens.cacheRead ?? 0) + (usage.cacheRead ?? 0),\n cacheWrite: (state.totalTokens.cacheWrite ?? 0) + (usage.cacheWrite ?? 0),\n },\n lastInputTokens: totalInput || state.lastInputTokens,\n };\n }),\n\n addCost: (cost) => set((state) => ({ cost: state.cost + cost })),\n\n startSession: (session) =>\n // Full reset on every session boundary. Without this, /new and\n // /clear would keep the previous session's token totals + cost in\n // the status bar — confusing because the chat looks empty but the\n // header insists there were 50k tokens already used.\n set({\n session,\n startTime: Date.now(),\n iteration: null,\n lastInputTokens: 0,\n totalTokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n cost: 0,\n }),\n\n endSession: () =>\n set({\n session: null,\n startTime: null,\n iteration: null,\n }),\n\n setEnv: (env) =>\n set((state) => ({\n maxContext: env.maxContext ?? state.maxContext,\n projectName: env.projectName ?? state.projectName,\n mode: env.mode ?? state.mode,\n inputCost: env.inputCost ?? state.inputCost,\n outputCost: env.outputCost ?? state.outputCost,\n cacheReadCost: env.cacheReadCost ?? state.cacheReadCost,\n })),\n\n setIteration: (iteration) => set({ iteration }),\n setModes: (modes) => set({ modes }),\n setTodos: (todos) => set({ todos }),\n }),\n {\n name: 'wrongstack-session',\n partialize: () => ({}),\n },\n ),\n);\n\n// ============================================\n// UI Store\n// ============================================\n\ninterface UIState {\n sidebarOpen: boolean;\n settingsOpen: boolean;\n currentView: 'chat' | 'history' | 'settings';\n showConfirmDialog: boolean;\n confirmInfo: {\n id: string;\n toolName: string;\n input: unknown;\n suggestedPattern: string;\n } | null;\n /** ⌘K palette is mounted globally; this flag controls its visibility. */\n paletteOpen: boolean;\n /** \"?\" shortcuts overlay visibility. */\n shortcutsOpen: boolean;\n /** Ctrl+F chat-content search. */\n searchOpen: boolean;\n searchQuery: string;\n /** Rolling list of recently sent user prompts so ↑ in an empty input can\n * recall them like a terminal. Capped to ~50 to keep storage bounded. */\n promptHistory: string[];\n /** Sidebar width in pixels. User can drag the right edge to resize.\n * Clamped to [200, 480] in the drag handler. Persisted. */\n sidebarWidth: number;\n /** Assistant message ids the user pinned. Persisted across reloads so a\n * user who pins a long debugging answer doesn't lose the bookmark on a\n * refresh. Note: messages themselves aren't persisted, so a pin's only\n * useful within the same in-memory session — the sidebar Pinned panel\n * prunes ids that no longer correspond to a live message. */\n pinnedIds: string[];\n /** \"Compact mode\" — denser spacing throughout the chat. Off by default;\n * power users with long sessions like the tighter layout. Toggle via\n * Ctrl+Shift+D globally. */\n compactMode: boolean;\n /** Open state for the Quick Model Switcher overlay. Lives in the store\n * so the topbar's model chip can open it imperatively without smuggling\n * synthetic keyboard events through the DOM. Ctrl+M toggles this too. */\n modelSwitcherOpen: boolean;\n /** Session ids the user starred in the history list. Persisted across\n * reloads. Starred sessions float to the top of the history sidebar\n * regardless of date bucket so a long-running project session stays\n * reachable without scrolling. */\n favoriteSessionIds: string[];\n /** Local UI nicknames for sessions, keyed by session id. The backend\n * session.title is auto-derived from the first user message and isn't\n * user-editable yet; this lets a user rename a session in the WebUI\n * (\"Auth refactor exploration\") without a backend round-trip. Used by\n * the History list, recent-sessions cards, and the tab title. */\n sessionNicknames: Record<string, string>;\n\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n setSettingsOpen: (open: boolean) => void;\n setCurrentView: (view: 'chat' | 'history' | 'settings') => void;\n showConfirm: (info: UIState['confirmInfo']) => void;\n hideConfirm: () => void;\n setPaletteOpen: (open: boolean) => void;\n setShortcutsOpen: (open: boolean) => void;\n setSearchOpen: (open: boolean) => void;\n setSearchQuery: (q: string) => void;\n pushPrompt: (text: string) => void;\n setSidebarWidth: (px: number) => void;\n togglePin: (id: string) => void;\n unpinAll: () => void;\n toggleCompactMode: () => void;\n setModelSwitcherOpen: (open: boolean) => void;\n toggleFavoriteSession: (id: string) => void;\n setSessionNickname: (id: string, nickname: string) => void;\n}\n\nexport const useUIStore = create<UIState>()(\n persist(\n (set) => ({\n sidebarOpen: true,\n settingsOpen: false,\n currentView: 'chat',\n showConfirmDialog: false,\n confirmInfo: null,\n paletteOpen: false,\n shortcutsOpen: false,\n searchOpen: false,\n searchQuery: '',\n promptHistory: [],\n sidebarWidth: 288,\n pinnedIds: [],\n compactMode: false,\n modelSwitcherOpen: false,\n favoriteSessionIds: [],\n sessionNicknames: {},\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n setSidebarOpen: (open) => set({ sidebarOpen: open }),\n setSettingsOpen: (open) => set({ settingsOpen: open }),\n setCurrentView: (view) => set({ currentView: view }),\n showConfirm: (info) => set({ showConfirmDialog: true, confirmInfo: info }),\n hideConfirm: () => set({ showConfirmDialog: false, confirmInfo: null }),\n setPaletteOpen: (open) => set({ paletteOpen: open }),\n setShortcutsOpen: (open) => set({ shortcutsOpen: open }),\n setSearchOpen: (open) => set({ searchOpen: open, searchQuery: open ? '' : '' }),\n setSearchQuery: (q) => set({ searchQuery: q }),\n pushPrompt: (text) =>\n set((state) => {\n const trimmed = text.trim();\n if (!trimmed) return state;\n // Dedupe consecutive duplicates and cap the buffer.\n const filtered = state.promptHistory.filter((p) => p !== trimmed);\n return { promptHistory: [trimmed, ...filtered].slice(0, 50) };\n }),\n setSidebarWidth: (px) => set({ sidebarWidth: Math.max(200, Math.min(480, Math.round(px))) }),\n togglePin: (id) =>\n set((state) => {\n const has = state.pinnedIds.includes(id);\n return {\n pinnedIds: has ? state.pinnedIds.filter((p) => p !== id) : [...state.pinnedIds, id],\n };\n }),\n unpinAll: () => set({ pinnedIds: [] }),\n toggleCompactMode: () => set((s) => ({ compactMode: !s.compactMode })),\n setModelSwitcherOpen: (open) => set({ modelSwitcherOpen: open }),\n toggleFavoriteSession: (id) =>\n set((state) => {\n const has = state.favoriteSessionIds.includes(id);\n return {\n favoriteSessionIds: has\n ? state.favoriteSessionIds.filter((s) => s !== id)\n : [...state.favoriteSessionIds, id],\n };\n }),\n setSessionNickname: (id, nickname) =>\n set((state) => {\n const trimmed = nickname.trim();\n const next = { ...state.sessionNicknames };\n if (trimmed) next[id] = trimmed;\n else delete next[id];\n return { sessionNicknames: next };\n }),\n }),\n {\n name: 'wrongstack-ui',\n // Persist only what's useful across reloads — sidebar state and the\n // prompt history. Modal flags (palette/shortcuts/search) reset on\n // load so the user doesn't reopen the app into an open dialog.\n partialize: (s) => ({\n sidebarOpen: s.sidebarOpen,\n sidebarWidth: s.sidebarWidth,\n promptHistory: s.promptHistory,\n pinnedIds: s.pinnedIds,\n compactMode: s.compactMode,\n favoriteSessionIds: s.favoriteSessionIds,\n sessionNicknames: s.sessionNicknames,\n }),\n },\n ),\n);\n\n// ============================================\n// History Store\n// ============================================\n\n/** A row in the sidebar's History tab. Mirrors core's SessionSummary +\n * isCurrent so the active session can be highlighted. Timestamps are\n * ISO-8601 strings as stored on disk; the UI parses them lazily. */\nexport interface SessionHistoryEntry {\n id: string;\n title: string;\n startedAt: string;\n model: string;\n provider: string;\n tokenTotal: number;\n isCurrent: boolean;\n}\n\ninterface HistoryState {\n entries: SessionHistoryEntry[];\n loading: boolean;\n error: string | null;\n setEntries: (entries: SessionHistoryEntry[], error?: string | null) => void;\n setLoading: (loading: boolean) => void;\n removeEntry: (id: string) => void;\n clearHistory: () => void;\n}\n\nexport const useHistoryStore = create<HistoryState>()((set) => ({\n entries: [],\n loading: false,\n error: null,\n setEntries: (entries, error = null) => set({ entries, error, loading: false }),\n setLoading: (loading) => set({ loading }),\n removeEntry: (id) =>\n set((state) => ({\n entries: state.entries.filter((e) => e.id !== id),\n })),\n clearHistory: () => set({ entries: [] }),\n}));\n","import { useWebSocketBootstrap } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useChatStore, useConfigStore, useSessionStore, useUIStore } from '@/stores';\nimport { useEffect } from 'react';\nimport { ChatView } from './components/ChatView';\nimport { CommandPalette, downloadChatAsMarkdown } from './components/CommandPalette';\nimport { ConfirmDialog } from './components/ConfirmDialog';\nimport { ConnectionBanner } from './components/ConnectionBanner';\nimport { ErrorBoundary } from './components/ErrorBoundary';\nimport { QuickModelSwitcher } from './components/QuickModelSwitcher';\nimport { SettingsPanel } from './components/SettingsPanel';\nimport { ShortcutsOverlay } from './components/ShortcutsOverlay';\nimport { Sidebar } from './components/Sidebar';\nimport { ThemeProvider, useTheme } from './components/ThemeProvider';\nimport { Toaster } from './components/Toaster';\n\nfunction AppInner() {\n const { theme } = useTheme();\n const { currentView, sidebarOpen, toggleSidebar, setSearchOpen, setSidebarOpen } = useUIStore();\n const isLoading = useChatStore((s) => s.isLoading);\n const iteration = useSessionStore((s) => s.iteration);\n const projectName = useSessionStore((s) => s.projectName);\n const sessionTitle = useSessionStore((s) => s.session?.title);\n const sessionId = useSessionStore((s) => s.session?.id);\n // User-set local nickname for the current session — takes precedence\n // over the backend title in the tab strip and topbar.\n const nickname = useUIStore((s) => (sessionId ? s.sessionNicknames[sessionId] : undefined));\n\n // Mobile-friendly: collapse the sidebar automatically below the md\n // breakpoint (768px). Tracks viewport changes so a window resize behaves\n // the same as a fresh load. We only AUTO-close — re-opening (or keeping\n // it open) on small screens stays a user decision, so we never call\n // setSidebarOpen(true) here.\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const mq = window.matchMedia('(max-width: 768px)');\n const apply = () => {\n if (mq.matches && useUIStore.getState().sidebarOpen) {\n setSidebarOpen(false);\n }\n };\n apply();\n mq.addEventListener('change', apply);\n return () => mq.removeEventListener('change', apply);\n }, [setSidebarOpen]);\n // Install WS handlers exactly once for the whole app. Every other consumer\n // (ChatInput, ConfirmDialog, SettingsPanel) uses the cheap `useWebSocket()`\n // hook which returns action methods only — see hooks/useWebSocket.ts for\n // the duplicate-handler trap this avoids.\n useWebSocketBootstrap();\n\n // Reflect the agent's run state + session identity in the browser tab\n // title. Pinned/grouped tab strips become readable at a glance — the\n // project name surfaces first so multiple WrongStack windows on the same\n // bar can still be distinguished, then the session title (if any), then\n // the running indicator. Falls back gracefully when fields are missing.\n useEffect(() => {\n const parts: string[] = [];\n if (isLoading) {\n const it = iteration\n ? ` iter ${iteration.index}${iteration.max ? `/${iteration.max}` : ''}`\n : '';\n parts.push(`●${it}`);\n }\n const sessionLabel = nickname?.trim() || sessionTitle?.trim();\n const projectLabel = projectName?.trim();\n if (sessionLabel) parts.push(sessionLabel);\n if (projectLabel) parts.push(projectLabel);\n parts.push('WrongStack');\n const title = parts.filter(Boolean).join(' · ');\n document.title = title;\n return () => {\n document.title = 'WrongStack';\n };\n }, [isLoading, iteration, projectName, sessionTitle, nickname]);\n\n // Global keyboard shortcuts for the actions that don't have a dedicated\n // owner (palette/shortcuts handle their own). Bound here so they fire\n // anywhere except inside text inputs (where Ctrl+F should still search\n // the chat, but Ctrl+L would otherwise be a browser address-bar focus).\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const t = e.target as HTMLElement | null;\n const tag = t?.tagName?.toLowerCase();\n const inField = tag === 'input' || tag === 'textarea' || t?.isContentEditable;\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.key === '\\\\') {\n e.preventDefault();\n toggleSidebar();\n return;\n }\n if (mod && e.key.toLowerCase() === 'f') {\n e.preventDefault();\n setSearchOpen(true);\n return;\n }\n if (mod && e.key.toLowerCase() === '/') {\n // Focus the chat textarea so the user can start typing without\n // hunting for it. Useful after closing palette/settings.\n e.preventDefault();\n const ta = document.querySelector('textarea');\n ta?.focus();\n return;\n }\n // The Ctrl-letter shortcuts skip when the user is typing in any\n // input — otherwise Ctrl+L wipes the chat while they're composing.\n // Access the WS client via the Zustand store instead of the `ws`\n // hook return value so we don't re-register this effect on every\n // render (useWebSocket() returns a fresh object each time).\n if (mod && !inField) {\n if (e.key.toLowerCase() === 'l') {\n e.preventDefault();\n useChatStore.getState().clearMessages();\n getWSClient(useConfigStore.getState().wsUrl)?.clearContext?.();\n } else if (e.key.toLowerCase() === 'n') {\n e.preventDefault();\n getWSClient(useConfigStore.getState().wsUrl)?.newSession?.();\n } else if (e.key.toLowerCase() === 'e') {\n e.preventDefault();\n downloadChatAsMarkdown();\n }\n }\n // Ctrl+Shift+D toggles compact UI density. Distinct from Ctrl+D\n // (which is reserved as the browser bookmark accelerator).\n if (mod && e.shiftKey && e.key.toLowerCase() === 'd') {\n e.preventDefault();\n useUIStore.getState().toggleCompactMode();\n }\n // Vim-style chat navigation: j/k step between bubbles, g goes to the\n // first message and G to the last. Skipped while typing so j/k inside\n // the textarea still inserts those letters. No modifier required —\n // this is the chat surface's primary input mode for keyboard users.\n if (!inField && !mod && !e.altKey) {\n const bubbles = Array.from(document.querySelectorAll<HTMLElement>('[data-message-id]'));\n if (bubbles.length === 0) return;\n const current = document.querySelector<HTMLElement>(\n '[data-message-id][data-focused=\"true\"]',\n );\n const idx = current ? bubbles.indexOf(current) : -1;\n const focusBubble = (target: HTMLElement) => {\n for (const b of bubbles) b.removeAttribute('data-focused');\n target.setAttribute('data-focused', 'true');\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n };\n if (e.key === 'j' || e.key === 'ArrowDown') {\n // ArrowDown only intercepts when nothing else has focus AND the\n // user is not in a scrollable list context — the textarea check\n // above covers the only place arrows have meaningful default\n // behaviour for this app.\n const next = bubbles[Math.min(bubbles.length - 1, Math.max(0, idx + 1))];\n if (next) {\n e.preventDefault();\n focusBubble(next);\n }\n return;\n }\n if (e.key === 'k' || e.key === 'ArrowUp') {\n const prev = bubbles[Math.max(0, idx <= 0 ? 0 : idx - 1)];\n if (prev) {\n e.preventDefault();\n focusBubble(prev);\n }\n return;\n }\n if (e.key === 'g' && !e.shiftKey) {\n e.preventDefault();\n focusBubble(bubbles[0]!);\n return;\n }\n if (e.key === 'G' || (e.key === 'g' && e.shiftKey)) {\n e.preventDefault();\n focusBubble(bubbles[bubbles.length - 1]!);\n return;\n }\n if (e.key === 'Escape' && current) {\n e.preventDefault();\n current.removeAttribute('data-focused');\n return;\n }\n // `c` while a bubble is focused: copy its visible text. Useful\n // pairing with the j/k flow so power users can step + copy without\n // hunting for the in-bubble copy button.\n if (e.key === 'c' && current) {\n const text =\n current.querySelector<HTMLElement>('.markdown-content')?.innerText ?? current.innerText;\n if (text) {\n void navigator.clipboard?.writeText(text).catch(() => {});\n e.preventDefault();\n }\n return;\n }\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [toggleSidebar, setSearchOpen]);\n\n return (\n <div className={cn('flex h-screen', theme)}>\n {sidebarOpen && <Sidebar />}\n <main className=\"flex-1 flex flex-col overflow-hidden\">\n <ConnectionBanner />\n {currentView === 'chat' && <ChatView />}\n {currentView === 'settings' && <SettingsPanel />}\n </main>\n\n {/* Global overlays */}\n <ConfirmDialog />\n <CommandPalette />\n <ShortcutsOverlay />\n <QuickModelSwitcher />\n <Toaster />\n </div>\n );\n}\n\nexport function App() {\n return (\n <ErrorBoundary>\n <ThemeProvider defaultTheme=\"system\">\n <AppInner />\n </ThemeProvider>\n </ErrorBoundary>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport type { ChatMessage } from '@/stores';\nimport { useConfigStore } from '@/stores';\nimport {\n Activity,\n ArrowDown,\n ArrowUp,\n Bot,\n Brain,\n Command,\n Cpu,\n FolderOpen,\n Monitor,\n Moon,\n PanelLeftOpen,\n Settings,\n Sun,\n Wifi,\n WifiOff,\n Zap,\n} from 'lucide-react';\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from 'react';\nimport { ChatInput } from './ChatInput';\nimport { ConnectionChip } from './ConnectionChip';\nimport { CostChip } from './CostChip';\nimport { MessageBubble } from './MessageBubble';\nimport { ModePicker } from './ModePicker';\nimport { SearchOverlay } from './SearchOverlay';\nimport { ToolGroup } from './ToolGroup';\nimport { WelcomeScreen } from './WelcomeScreen';\nimport { Button } from './ui/button';\nimport { ScrollArea } from './ui/scroll-area';\n\nfunction fmtTok(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1000) return `${(n / 1000).toFixed(n >= 10_000 ? 0 : 1)}k`;\n return String(n);\n}\n\n/**\n * Soft, ephemeral chip rendered while the model is mid-reasoning. Reads the\n * thinking buffer straight from the chat store so it stays in sync with the\n * stream without re-rendering the full message list. Mounted only while the\n * buffer is non-empty — the WS handler clears it on text/tool/response/run\n * boundaries, so this naturally appears at the start of a turn and\n * disappears the moment the model commits to user-visible output.\n */\nfunction ThinkingBubble() {\n const buf = useChatStore((s) => s.thinkingBuffer);\n if (!buf) return null;\n // Show only the last ~6 lines so the chip stays bounded while the model\n // rambles. Whole buffer is in the store if we ever want a \"show all\"\n // affordance, but for the ephemeral chip the tail is what feels live.\n const tailLines = buf.split('\\n').slice(-6);\n const tail = tailLines.join('\\n').trim();\n return (\n <div className=\"flex gap-3 animate-message\">\n <div className=\"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-violet-500/10 text-violet-600 dark:text-violet-400 ring-2 ring-offset-2 ring-offset-background ring-violet-500/20\">\n <Brain className=\"h-4 w-4 animate-pulse\" />\n </div>\n <div className=\"flex flex-col gap-1 max-w-[85%] min-w-0\">\n <span className=\"text-xs font-medium text-violet-600 dark:text-violet-400 px-1\">\n Thinking…\n </span>\n <div className=\"rounded-2xl rounded-bl-md px-3 py-2 bg-violet-500/[0.04] border border-violet-500/20 text-foreground/80\">\n <pre className=\"whitespace-pre-wrap break-words font-sans text-xs leading-relaxed italic max-h-32 overflow-hidden\">\n {tail || '…'}\n </pre>\n </div>\n </div>\n </div>\n );\n}\n\nexport function ChatView() {\n const { messages, isLoading } = useChatStore();\n const setPaletteOpen = useUIStore((s) => s.setPaletteOpen);\n const setShortcutsOpen = useUIStore((s) => s.setShortcutsOpen);\n const sidebarOpen = useUIStore((s) => s.sidebarOpen);\n const toggleSidebar = useUIStore((s) => s.toggleSidebar);\n const compactMode = useUIStore((s) => s.compactMode);\n const setTheme = useConfigStore((s) => s.setTheme);\n const theme = useConfigStore((s) => s.theme);\n const { totalTokens, cost, startTime, lastInputTokens, maxContext, projectName, iteration } =\n useSessionStore();\n const { wsConnected, wsStatus, provider, model } = useConfigStore();\n const { setCurrentView } = useUIStore();\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Context window usage (mirrors TUI's ContextChip semantics: lastInputTokens\n // is the most recent provider call's input size — the de-facto live context).\n const ctxPct =\n maxContext > 0 && lastInputTokens > 0\n ? Math.min(100, Math.round((lastInputTokens / maxContext) * 100))\n : 0;\n const ctxTone =\n ctxPct >= 85\n ? 'bg-red-500/15 text-red-600 dark:text-red-400'\n : ctxPct >= 70\n ? 'bg-amber-500/15 text-amber-600 dark:text-amber-400'\n : 'bg-muted text-muted-foreground';\n\n // Auto-scroll with \"user is reading older messages\" lock. We watch the\n // Radix ScrollArea viewport's scroll position; if the user is within\n // ~120px of the bottom we keep pinning new messages to the bottom. The\n // moment they scroll up, we let go — new content appends invisibly and a\n // floating \"↓ new messages\" button shows up so they can rejoin the live\n // tail when they're ready.\n const [pinnedToBottom, setPinnedToBottom] = useState(true);\n const [unreadCount, setUnreadCount] = useState(0);\n /** True when the user has scrolled past ~1 viewport-height from the top\n * AND isn't anchored at the bottom — i.e. they're deep in mid-history.\n * Used to surface a \"back to top\" pill so navigating a 200-message\n * transcript doesn't require thumb-flicking. */\n const [scrolledDeep, setScrolledDeep] = useState(false);\n const lastSeenCount = useRef(messages.length);\n\n // Resolve the actual scrollable viewport that Radix renders inside the\n // ScrollArea root. We re-resolve every render because the ref points at\n // the root, not the viewport.\n const getViewport = useCallback((): HTMLElement | null => {\n return scrollRef.current?.querySelector('[data-radix-scroll-area-viewport]') ?? null;\n }, []);\n\n useEffect(() => {\n const viewport = getViewport();\n if (!viewport) return;\n const onScroll = () => {\n const dist = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;\n const nowPinned = dist < 120;\n setPinnedToBottom(nowPinned);\n if (nowPinned) {\n setUnreadCount(0);\n lastSeenCount.current = messages.length;\n }\n // Show \"back to top\" only when there's actually a lot of content\n // ABOVE the user (so it doesn't pop up on tiny chats) AND they're\n // not already near the top.\n const deep =\n viewport.scrollTop > viewport.clientHeight &&\n viewport.scrollHeight > viewport.clientHeight * 2.5;\n setScrolledDeep(deep);\n };\n viewport.addEventListener('scroll', onScroll, { passive: true });\n return () => viewport.removeEventListener('scroll', onScroll);\n }, [getViewport, messages.length]);\n\n useEffect(() => {\n const viewport = getViewport();\n if (!viewport) return;\n if (pinnedToBottom) {\n viewport.scrollTop = viewport.scrollHeight;\n lastSeenCount.current = messages.length;\n } else {\n const delta = messages.length - lastSeenCount.current;\n if (delta > 0) setUnreadCount(delta);\n }\n }, [messages, pinnedToBottom, getViewport]);\n\n const scrollToBottom = useCallback(() => {\n const viewport = getViewport();\n if (!viewport) return;\n viewport.scrollTo({ top: viewport.scrollHeight, behavior: 'smooth' });\n setPinnedToBottom(true);\n setUnreadCount(0);\n lastSeenCount.current = messages.length;\n }, [getViewport, messages.length]);\n\n const scrollToTop = useCallback(() => {\n const viewport = getViewport();\n if (!viewport) return;\n viewport.scrollTo({ top: 0, behavior: 'smooth' });\n }, [getViewport]);\n\n // Live \"agent is busy\" indicator. We track when the current run started\n // (rising edge of isLoading) and tick a second-resolution clock so the\n // running-status bubble shows a live elapsed timer. Reset on idle.\n const [runStartedAt, setRunStartedAt] = useState<number | null>(null);\n const [nowTick, setNowTick] = useState<number>(() => Date.now());\n /** Anchor for streaming-speed computation. We capture the assistant\n * message id, the wall-clock time, and the content length at the moment\n * streaming starts; chars-per-second is derived from the delta between\n * then and `nowTick`. Reset when the streaming bubble changes (new turn)\n * or when streaming flips off (idle). */\n const streamAnchor = useRef<{ id: string; at: number; len: number } | null>(null);\n useEffect(() => {\n if (isLoading && runStartedAt === null) setRunStartedAt(Date.now());\n if (!isLoading && runStartedAt !== null) setRunStartedAt(null);\n }, [isLoading, runStartedAt]);\n useEffect(() => {\n if (!isLoading) return;\n const t = setInterval(() => setNowTick(Date.now()), 500);\n return () => clearInterval(t);\n }, [isLoading]);\n\n const formatDuration = (start: number | null) => {\n if (!start) return '--';\n const seconds = Math.floor((Date.now() - start) / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${minutes}m ${secs}s`;\n };\n\n // Agent state derived once — used by both the chip and the indicator\n // bubble at the bottom of the chat. `streaming` while an assistant\n // bubble is mid-text, else `thinking` between turns, else `idle`.\n const agentState = (() => {\n if (!isLoading) return 'idle' as const;\n const last = messages[messages.length - 1];\n const isStreaming = last?.role === 'assistant' && !!last.content && last.streaming;\n return isStreaming ? ('streaming' as const) : ('thinking' as const);\n })();\n const stateTone =\n agentState === 'idle'\n ? 'bg-muted text-muted-foreground'\n : agentState === 'streaming'\n ? 'bg-blue-500/10 text-blue-600 dark:text-blue-400'\n : 'bg-amber-500/10 text-amber-600 dark:text-amber-400';\n\n // We show the status row (ctx / tokens / cost / elapsed) only when there's\n // *something* worth reporting — otherwise the second line is just dead\n // pixels on a brand-new empty session.\n const hasStatusContent =\n (maxContext > 0 && lastInputTokens > 0) || totalTokens.input > 0 || !!startTime;\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Header — two compact rows.\n Row 1: identity + actions (sidebar reopen, connection, project,\n model, mode, state, iteration, palette/theme/?/settings).\n Row 2 (when present): live numbers (ctx %, tokens, cache %, cost,\n elapsed) — kept off-row 1 so the action cluster never\n wraps when a session warms up. */}\n <header className=\"flex flex-col border-b bg-card shrink-0\">\n <div className=\"flex items-center justify-between gap-2 px-3 py-2\">\n <div className=\"flex items-center gap-1.5 min-w-0 flex-1\">\n {/* Sidebar reopen — only visible when the sidebar is hidden.\n Otherwise the sidebar's own toggle handles it. */}\n {!sidebarOpen && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 shrink-0\"\n onClick={toggleSidebar}\n title=\"Open sidebar (Ctrl+\\\\)\"\n >\n <PanelLeftOpen className=\"h-4 w-4\" />\n </Button>\n )}\n {/* When the sidebar is hidden we surface a tiny WrongStack mark\n so the user can still see they're in WrongStack — but inline,\n no big header text. */}\n {!sidebarOpen && (\n <div className=\"flex items-center gap-1.5 shrink-0 mr-1\">\n <div className=\"w-5 h-5 rounded bg-primary flex items-center justify-center\">\n <Zap className=\"h-3 w-3 text-primary-foreground\" />\n </div>\n </div>\n )}\n {/* Connection pill — granular status with retry button. Lives\n next to the sidebar toggle so it's always visible. Hover\n shows the last error (if any). */}\n <ConnectionChip wsStatus={wsStatus} wsConnected={wsConnected} />\n <span\n className={cn(\n 'flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 tabular-nums',\n stateTone,\n )}\n title={`Agent state: ${agentState}`}\n >\n {agentState !== 'idle' && (\n <span className=\"h-1.5 w-1.5 rounded-full bg-current animate-pulse\" />\n )}\n <span>{agentState}</span>\n </span>\n {projectName && (\n <span\n className=\"flex items-center gap-1 text-[11px] text-muted-foreground shrink-0 min-w-0\"\n title={`Project: ${projectName}`}\n >\n <FolderOpen className=\"h-3 w-3 shrink-0\" />\n <span className=\"truncate max-w-[12rem]\">{projectName}</span>\n </span>\n )}\n <button\n type=\"button\"\n onClick={() => useUIStore.getState().setModelSwitcherOpen(true)}\n className=\"group flex items-center gap-1 px-2 py-0.5 rounded-md border bg-background/50 hover:bg-accent hover:border-primary/40 transition-colors text-[11px] min-w-0 shrink-0\"\n title=\"Change provider / model (Ctrl+M)\"\n >\n <Cpu className=\"h-3 w-3 text-muted-foreground group-hover:text-foreground shrink-0\" />\n <span className=\"font-mono truncate max-w-[16rem]\">\n <span className=\"text-muted-foreground\">{provider || 'no-provider'}</span>\n <span className=\"text-muted-foreground/40 mx-0.5\">/</span>\n <span className=\"font-medium\">{model || 'no-model'}</span>\n </span>\n </button>\n <ModePicker />\n {iteration && (\n <span\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 text-primary shrink-0\"\n title=\"Agent iteration\"\n >\n <Activity className=\"h-3 w-3 animate-pulse\" />\n iter {iteration.index}\n {iteration.max > 0 ? `/${iteration.max}` : ''}\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-0.5 shrink-0\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n onClick={() => setPaletteOpen(true)}\n title=\"Command palette (Ctrl+K)\"\n >\n <Command className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n onClick={() => {\n const next = theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light';\n setTheme(next);\n }}\n title={`Theme: ${theme} (click to cycle)`}\n >\n {theme === 'light' ? (\n <Sun className=\"h-4 w-4\" />\n ) : theme === 'dark' ? (\n <Moon className=\"h-4 w-4\" />\n ) : (\n <Monitor className=\"h-4 w-4\" />\n )}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 font-mono text-xs\"\n onClick={() => setShortcutsOpen(true)}\n title=\"Keyboard shortcuts (?)\"\n >\n ?\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n onClick={() => setCurrentView('settings')}\n title=\"Settings\"\n >\n <Settings className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {hasStatusContent && (\n <div className=\"flex items-center justify-between gap-3 px-3 py-1 border-t bg-muted/20 text-[11px] text-muted-foreground\">\n <div className=\"flex items-center gap-3 min-w-0 flex-1 tabular-nums\">\n {maxContext > 0 && lastInputTokens > 0 && (\n <span\n className={cn(\n 'flex items-center gap-1 px-1.5 py-0.5 rounded-full font-medium shrink-0',\n ctxTone,\n )}\n title={`Last input: ${lastInputTokens.toLocaleString()} / ${maxContext.toLocaleString()} tokens`}\n >\n ctx {ctxPct}% · {fmtTok(lastInputTokens)}/{fmtTok(maxContext)}\n </span>\n )}\n {totalTokens.input > 0 && (\n <>\n <span className=\"flex items-center gap-1\">\n <span className=\"font-medium text-foreground\">{fmtTok(totalTokens.input)}</span>\n <span>in</span>\n </span>\n <span className=\"flex items-center gap-1\">\n <span className=\"font-medium text-foreground\">\n {fmtTok(totalTokens.output)}\n </span>\n <span>out</span>\n </span>\n {totalTokens.cacheRead &&\n totalTokens.cacheRead > 0 &&\n (() => {\n const denom = (totalTokens.cacheRead ?? 0) + totalTokens.input;\n const pct =\n denom > 0 ? Math.round(((totalTokens.cacheRead ?? 0) / denom) * 100) : 0;\n return (\n <span\n className=\"flex items-center gap-1\"\n title={`Cache hit ratio: ${pct}%`}\n >\n <span className=\"font-medium text-foreground\">\n {fmtTok(totalTokens.cacheRead)}\n </span>\n <span>cache ({pct}%)</span>\n </span>\n );\n })()}\n <CostChip />\n </>\n )}\n </div>\n {startTime && (\n <span className=\"text-muted-foreground/70 tabular-nums shrink-0\">\n {formatDuration(startTime)}\n </span>\n )}\n </div>\n )}\n </header>\n\n {/* Messages */}\n <div className=\"flex-1 relative overflow-hidden\">\n {/* Chat-local Ctrl+F overlay — pinned top-right, scrolls hits into\n view and highlights the active row in MessageBubble. */}\n <SearchOverlay />\n {/* Jump-to-latest pill — only when the user scrolled away from the\n live tail. Shows the unread count so they know how much they're\n behind without having to scroll down first. */}\n {!pinnedToBottom && (\n <button\n type=\"button\"\n onClick={scrollToBottom}\n className={cn(\n 'absolute bottom-4 left-1/2 -translate-x-1/2 z-10',\n 'flex items-center gap-2 px-4 py-2 rounded-full shadow-lg',\n 'bg-primary text-primary-foreground text-xs font-medium',\n 'hover:bg-primary/90 transition-colors animate-message',\n )}\n >\n <ArrowDown className=\"h-3.5 w-3.5\" />\n {unreadCount > 0\n ? `${unreadCount} new message${unreadCount === 1 ? '' : 's'}`\n : 'Jump to latest'}\n </button>\n )}\n {/* Back-to-top — only when the chat is genuinely long AND the user\n scrolled past one viewport. Floats top-right so it doesn't compete\n with the jump-to-latest pill at the bottom. */}\n {scrolledDeep && (\n <button\n type=\"button\"\n onClick={scrollToTop}\n title=\"Scroll to top (oldest)\"\n className={cn(\n 'absolute top-3 right-3 z-10',\n 'flex items-center gap-1 px-2.5 py-1 rounded-full shadow-md border',\n 'bg-background/90 backdrop-blur-sm text-[11px] text-muted-foreground',\n 'hover:text-foreground hover:bg-background transition-colors animate-message',\n )}\n >\n <ArrowUp className=\"h-3 w-3\" />\n <span>Top</span>\n </button>\n )}\n <ScrollArea className=\"h-full\" ref={scrollRef}>\n <div\n className={cn(\n 'mx-auto pb-8',\n compactMode ? 'max-w-5xl p-3 space-y-3' : 'max-w-5xl p-4 space-y-6',\n )}\n >\n {messages.length === 0 && !isLoading && <WelcomeScreen />}\n\n {/* Two-pass grouping.\n Pass 1 — collapse consecutive tool messages into one ToolGroup\n chip (so 8 parallel reads don't eat the viewport).\n Pass 2 — bundle every run of non-user groups (assistant text +\n tool chips) into a single \"agent turn\". Inside a turn, items\n render with tight spacing and only the first item shows the\n avatar; this stitches the text-tool-text-tool stream into one\n continuous flow instead of stacking each message as its own\n detached bubble. */}\n {(() => {\n type Group =\n | { kind: 'msg'; message: ChatMessage; isFirst: boolean }\n | { kind: 'tools'; tools: ChatMessage[]; key: string };\n const groups: Group[] = [];\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i]!;\n if (m.role === 'tool') {\n const last = groups[groups.length - 1];\n if (last && last.kind === 'tools') {\n last.tools.push(m);\n } else {\n groups.push({ kind: 'tools', tools: [m], key: m.id });\n }\n } else {\n const prev = messages[i - 1];\n groups.push({\n kind: 'msg',\n message: m,\n isFirst: !prev || prev.role !== m.role,\n });\n }\n }\n // Bundle consecutive non-user groups into agent turns. User\n // messages stay as their own standalone turn so the bubble\n // alignment switches sides naturally.\n type Turn =\n | { kind: 'user'; message: ChatMessage; key: string }\n | { kind: 'agent'; items: Group[]; key: string };\n const turns: Turn[] = [];\n for (const g of groups) {\n if (g.kind === 'msg' && g.message.role === 'user') {\n turns.push({ kind: 'user', message: g.message, key: g.message.id });\n continue;\n }\n const last = turns[turns.length - 1];\n if (last && last.kind === 'agent') {\n last.items.push(g);\n } else {\n const key = g.kind === 'msg' ? g.message.id : g.key;\n turns.push({ kind: 'agent', items: [g], key });\n }\n }\n // Track which date (local YYYY-MM-DD) the previous turn was\n // stamped with, so we can emit a soft divider between days. This\n // matters most after `session.resume` rehydrates a transcript\n // that spans yesterday → today; without dividers the user can't\n // tell where the gap is.\n let prevDay: string | null = null;\n const dayKey = (ts: number) => {\n const d = new Date(ts);\n return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;\n };\n const dayLabel = (ts: number) => {\n const d = new Date(ts);\n const today = new Date();\n const yest = new Date(Date.now() - 86_400_000);\n if (dayKey(ts) === dayKey(today.getTime())) return 'Today';\n if (dayKey(ts) === dayKey(yest.getTime())) return 'Yesterday';\n return d.toLocaleDateString(undefined, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n year: d.getFullYear() === today.getFullYear() ? undefined : 'numeric',\n });\n };\n const turnTs = (t: Turn): number => {\n if (t.kind === 'user') return t.message.timestamp;\n const first = t.items[0]!;\n return first.kind === 'msg' ? first.message.timestamp : first.tools[0]!.timestamp;\n };\n const out: ReactNode[] = [];\n for (let idx = 0; idx < turns.length; idx++) {\n const t = turns[idx]!;\n const ts = turnTs(t);\n const day = dayKey(ts);\n if (day !== prevDay) {\n out.push(\n <div\n key={`day-${day}-${idx}`}\n className=\"flex items-center gap-3 py-1 text-[11px] text-muted-foreground/70 uppercase tracking-wider font-medium\"\n >\n <div className=\"flex-1 h-px bg-border/50\" />\n <span>{dayLabel(ts)}</span>\n <div className=\"flex-1 h-px bg-border/50\" />\n </div>,\n );\n prevDay = day;\n }\n if (t.kind === 'user') {\n out.push(<MessageBubble key={t.key} message={t.message} isFirst />);\n continue;\n }\n const isLastTurn = idx === turns.length - 1;\n out.push(\n <div key={t.key} className={cn(compactMode ? 'space-y-1' : 'space-y-1.5')}>\n {t.items.map((g, gi) => {\n const continuation = gi > 0;\n if (g.kind === 'msg') {\n return (\n <MessageBubble\n key={g.message.id}\n message={g.message}\n isFirst={!continuation && g.isFirst}\n isContinuation={continuation}\n />\n );\n }\n const isLatestRunning =\n isLastTurn &&\n gi === t.items.length - 1 &&\n isLoading &&\n g.tools.some((tt) => tt.toolResult === undefined);\n return (\n <ToolGroup\n key={g.key}\n tools={g.tools}\n defaultOpen={isLatestRunning}\n isContinuation={continuation}\n />\n );\n })}\n </div>,\n );\n }\n return out;\n })()}\n\n {/* Transient extended-thinking bubble. Driven by\n provider.thinking_delta events; cleared by the first text_delta /\n tool.started / provider.response / run.result of the turn, so it\n \"appears and disappears\" alongside the model's internal reasoning\n and never persists into the transcript. */}\n <ThinkingBubble />\n\n {/* Running status bubble — always present as the last message\n while the agent is not idle. Picks a label based on what the\n agent is currently doing (composing reply / running tools /\n thinking between steps) and ticks a live elapsed timer so the\n user has visible proof of life even mid-iteration. */}\n {isLoading &&\n (() => {\n const last = messages[messages.length - 1];\n const runningTools = messages.filter(\n (m) => m.role === 'tool' && m.toolResult === undefined,\n );\n let label = 'Thinking…';\n if (runningTools.length > 0) {\n const names = Array.from(\n new Set(runningTools.map((t) => t.toolName).filter(Boolean) as string[]),\n );\n const preview = names.slice(0, 2).join(', ');\n const more = names.length > 2 ? ` +${names.length - 2}` : '';\n label =\n runningTools.length === 1\n ? `Running ${preview || 'tool'}…`\n : `Running ${runningTools.length} tools (${preview}${more})…`;\n } else if (last?.role === 'assistant' && last.content) {\n label = 'Writing reply…';\n } else if (last?.role === 'tool' && last.toolResult !== undefined) {\n label = 'Thinking about the next step…';\n }\n const elapsedSec = runStartedAt\n ? Math.max(0, Math.floor((nowTick - runStartedAt) / 1000))\n : 0;\n const elapsed =\n elapsedSec < 60\n ? `${elapsedSec}s`\n : `${Math.floor(elapsedSec / 60)}m ${elapsedSec % 60}s`;\n // Streaming speed: derive chars/s for the currently-streaming\n // assistant bubble. Anchor on first sight of a streaming bubble;\n // tear down once it's no longer streaming so the next turn\n // starts from zero.\n let speedLabel = '';\n const streamingBubble =\n last?.role === 'assistant' && last.streaming && last.content ? last : null;\n if (streamingBubble) {\n const anchor = streamAnchor.current;\n if (!anchor || anchor.id !== streamingBubble.id) {\n streamAnchor.current = {\n id: streamingBubble.id,\n at: Date.now(),\n len: streamingBubble.content.length,\n };\n } else {\n const dt = Math.max(1, nowTick - anchor.at);\n const dl = Math.max(0, streamingBubble.content.length - anchor.len);\n // Only show after 0.5s of streaming so the first reading\n // isn't wildly inflated by the latency-to-first-chunk.\n if (dt > 500 && dl > 0) {\n const cps = (dl * 1000) / dt;\n speedLabel =\n cps >= 1000\n ? `${(cps / 1000).toFixed(1)}k ch/s`\n : `${Math.round(cps)} ch/s`;\n }\n }\n } else if (streamAnchor.current) {\n streamAnchor.current = null;\n }\n return (\n <div className=\"flex gap-3 animate-message\">\n <div className=\"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-accent text-accent-foreground ring-2 ring-offset-2 ring-offset-background ring-accent/20\">\n <Bot className=\"h-4 w-4\" />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"rounded-2xl px-4 py-3 bg-card border text-foreground\">\n <div className=\"flex items-center gap-3 text-sm\">\n <span className=\"flex gap-1\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.3s]\" />\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce [animation-delay:-0.15s]\" />\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary/70 animate-bounce\" />\n </span>\n <span className=\"text-foreground/90\">{label}</span>\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n {elapsed}\n </span>\n {iteration && (\n <span className=\"text-xs text-muted-foreground tabular-nums\">\n · iter {iteration.index}\n {iteration.max > 0 ? `/${iteration.max}` : ''}\n </span>\n )}\n {speedLabel && (\n <span className=\"text-xs text-muted-foreground/80 tabular-nums\">\n · {speedLabel}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n })()}\n </div>\n </ScrollArea>\n </div>\n\n {/* Input */}\n <div className=\"border-t bg-card/50 backdrop-blur supports-[backdrop-filter]:bg-card/50 p-4 shrink-0\">\n <div className=\"max-w-5xl mx-auto\">\n <ChatInput />\n <p className=\"text-xs text-center text-muted-foreground/50 mt-2\">\n Press Enter to send, Shift+Enter for new line\n </p>\n </div>\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport { Pencil, Send, Square } from 'lucide-react';\nimport type React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { downloadChatAsMarkdown } from './CommandPalette';\nimport { FilePicker } from './FilePicker';\nimport { Button } from './ui/button';\n\n/**\n * Slash command registry. Each entry knows its triggers (so /model and\n * /settings can map to the same action), a one-line description (shown in\n * the popup), and a `run` callback. Adding a new command means: append an\n * entry here, write a `run` body, done — no need to touch the popup or\n * dispatcher.\n */\ntype SlashCategory = 'Session' | 'Inspect' | 'Run' | 'App';\n\ninterface SlashCommandDef {\n /** Primary name (the one shown). */\n name: string;\n /** Optional alternative spellings — typed by the user, dispatched here. */\n aliases?: string[];\n description: string;\n /** Logical bucket the command belongs to. Drives the section headings\n * in the slash menu popup so a user scanning the list can find what\n * they want by category. */\n category: SlashCategory;\n}\n\nconst SLASH_COMMANDS: SlashCommandDef[] = [\n { name: '/help', category: 'App', description: 'Show every slash command and what it does' },\n { name: '/export', category: 'Session', description: 'Download the current chat as markdown' },\n {\n name: '/todos',\n category: 'Inspect',\n description: 'List current todos (try `/todos clear` to reset)',\n },\n {\n name: '/clear',\n category: 'Session',\n description: 'Wipe current context (keeps session id, disk record stays)',\n },\n {\n name: '/new',\n category: 'Session',\n description: 'Start a brand-new session (fresh on disk and in memory)',\n },\n {\n name: '/compact',\n category: 'Session',\n description: 'Shrink context — elide ancient tool output',\n },\n {\n name: '/debug',\n category: 'Inspect',\n aliases: ['/context'],\n description: 'Per-section context size breakdown',\n },\n {\n name: '/tools',\n category: 'Inspect',\n description: 'List every registered tool the model can call',\n },\n {\n name: '/memory',\n category: 'Inspect',\n description: 'Show all remembered notes (project + user scope)',\n },\n { name: '/skill', category: 'Inspect', aliases: ['/skills'], description: 'List active skills' },\n {\n name: '/diag',\n category: 'Inspect',\n description: 'Runtime diagnostics (provider, tools, features, mode, usage)',\n },\n {\n name: '/stats',\n category: 'Inspect',\n description: 'Session stats: tokens, cache hit ratio, cost, elapsed',\n },\n {\n name: '/save',\n category: 'Session',\n description: 'Force-flush the session (auto-saved already)',\n },\n { name: '/abort', category: 'Run', aliases: ['/stop'], description: 'Abort the current run' },\n {\n name: '/settings',\n category: 'App',\n aliases: ['/model'],\n description: 'Open settings (provider/model/keys)',\n },\n];\n\nconst SLASH_CATEGORY_ORDER: SlashCategory[] = ['Run', 'Session', 'Inspect', 'App'];\n\n/**\n * Match what the user typed against the registry. Empty query lists\n * everything; otherwise filter by primary name AND alias prefixes so\n * `/sto` finds `/stop` (alias of /abort).\n */\n/**\n * Find an open `@`-mention at the cursor. Scans backwards: if we hit\n * whitespace before an `@`, there's no mention. The `@` must be either at\n * the very start of the buffer or immediately preceded by whitespace —\n * email addresses like `a@b.com` shouldn't trigger the picker.\n */\nfunction detectAtMention(value: string, cursor: number): { start: number; query: string } | null {\n let i = cursor - 1;\n while (i >= 0) {\n const c = value[i]!;\n if (c === '@') {\n const prev = i > 0 ? value[i - 1] : '';\n if (i === 0 || /\\s/.test(prev ?? '')) {\n return { start: i, query: value.slice(i + 1, cursor) };\n }\n return null;\n }\n // Allow path chars (letters/digits/_/-./).\n if (/\\s/.test(c)) return null;\n i--;\n }\n return null;\n}\n\nfunction matchSlash(query: string): SlashCommandDef[] {\n const q = query.toLowerCase();\n if (q === '/' || q === '') return SLASH_COMMANDS;\n return SLASH_COMMANDS.filter(\n (c) => c.name.startsWith(q) || (c.aliases?.some((a) => a.startsWith(q)) ?? false),\n );\n}\n\nexport function ChatInput() {\n const { isLoading, setLoading, addMessage, clearMessages } = useChatStore();\n const queue = useChatStore((s) => s.queue);\n const enqueue = useChatStore((s) => s.enqueue);\n const removeQueued = useChatStore((s) => s.removeQueued);\n const clearQueue = useChatStore((s) => s.clearQueue);\n const { setCurrentView } = useUIStore();\n const pushPrompt = useUIStore((s) => s.pushPrompt);\n const promptHistory = useUIStore((s) => s.promptHistory);\n const ws = useWebSocket();\n const { sendMessage, sendAbort, client } = ws;\n /** Live context-budget signals — drive the token-estimate chip beside\n * the character counter. The estimate uses the universal 4-char-per-token\n * heuristic which is wrong by ±25% for natural prose but accurate enough\n * to warn the user before they paste a 100k-char file into a 200k window.\n * The chip only renders past the threshold so short drafts stay clean. */\n const lastInputTokens = useSessionStore((s) => s.lastInputTokens);\n const maxContext = useSessionStore((s) => s.maxContext);\n const [input, setInput] = useState('');\n const [slashIndex, setSlashIndex] = useState(0);\n /** Cursor into promptHistory. -1 = \"live input, not browsing history\".\n * Reset to -1 whenever the user types something that's NOT a history\n * navigation. */\n const [historyIdx, setHistoryIdx] = useState(-1);\n /** Open `@`-mention picker state. We track the starting position of the\n * `@` in the textarea so on pick we can replace the partial token\n * (`@compa`) with the chosen path. Null = closed. */\n const [atMention, setAtMention] = useState<{ start: number; query: string } | null>(null);\n /** Transient hint shown after a large paste. The user can read it for a\n * few seconds then it auto-dismisses. We only surface it for genuinely\n * big drops (>800 chars) — smaller pastes don't need a callout. */\n const [pasteHint, setPasteHint] = useState<{ chars: number; lines: number } | null>(null);\n /** True while an OS-drag is hovering over the input area. Triggers the\n * drop overlay so the user gets visual confirmation they're about to\n * attach. Browsers don't expose full filesystem paths from drops for\n * security reasons, so we seed the @-mention picker with the file's\n * basename and let the user confirm the resolved workspace path. */\n const [draggingOver, setDraggingOver] = useState(false);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const runSlashCommand = useCallback(\n (raw: string): boolean => {\n const trimmed = raw.trim();\n // Split into head (with leading slash) + the rest. Lowercase the\n // head so `/Todos` and `/TODOS` route the same; preserve case on\n // the args because the user might be inserting a content string.\n const sp = trimmed.indexOf(' ');\n const head = (sp === -1 ? trimmed : trimmed.slice(0, sp)).toLowerCase();\n const args = sp === -1 ? '' : trimmed.slice(sp + 1).trim();\n const cmd = head;\n switch (cmd) {\n case '/help': {\n // Render the registry inline as an assistant message.\n const lines = [\n '📖 **Slash commands**',\n '',\n ...SLASH_COMMANDS.map(\n (c) =>\n `• \\`${c.name}\\`${c.aliases?.length ? ` (${c.aliases.map((a) => `\\`${a}\\``).join(', ')})` : ''} — ${c.description}`,\n ),\n ];\n addMessage({ role: 'assistant', content: lines.join('\\n') });\n return true;\n }\n case '/clear':\n clearMessages();\n client?.clearContext?.();\n return true;\n case '/new':\n client?.newSession?.();\n return true;\n case '/compact':\n case '/compact!':\n client?.compactContext?.(cmd === '/compact!');\n return true;\n case '/debug':\n case '/context':\n client?.debugContext?.();\n return true;\n case '/tools':\n ws.listTools();\n return true;\n case '/memory':\n ws.listMemory();\n return true;\n case '/skill':\n case '/skills':\n ws.listSkills();\n return true;\n case '/diag':\n ws.getDiag();\n return true;\n case '/stats':\n ws.getStats();\n return true;\n case '/save':\n ws.saveSession();\n return true;\n case '/todos': {\n // Sub-commands: `/todos` (default = list), `/todos clear`. We\n // pull live state from the session store so the rendered output\n // matches what the sidebar already shows — no separate fetch.\n const sub = args.toLowerCase();\n if (sub === 'clear') {\n client?.clearTodos?.();\n return true;\n }\n const list = useSessionStore.getState().todos;\n if (list.length === 0) {\n addMessage({\n role: 'assistant',\n content:\n \"✅ **Todos** — _empty. Ask the agent to plan something and they'll show up here._\",\n });\n return true;\n }\n const lines: string[] = [\n `✅ **Todos** (${list.filter((t) => t.status === 'completed').length}/${list.length} done)`,\n '',\n ];\n for (const t of list) {\n const mark =\n t.status === 'completed' ? '[x]' : t.status === 'in_progress' ? '[~]' : '[ ]';\n const text = t.status === 'in_progress' && t.activeForm ? t.activeForm : t.content;\n lines.push(`- ${mark} ${text}`);\n }\n lines.push('', '_Use `/todos clear` to wipe the list._');\n addMessage({ role: 'assistant', content: lines.join('\\n') });\n return true;\n }\n case '/export':\n downloadChatAsMarkdown();\n addMessage({ role: 'assistant', content: '📥 Chat exported to your downloads folder.' });\n return true;\n case '/abort':\n case '/stop':\n sendAbort();\n setLoading(false);\n return true;\n case '/settings':\n case '/model':\n setCurrentView('settings');\n return true;\n default:\n return false;\n }\n },\n [addMessage, clearMessages, client, sendAbort, setLoading, setCurrentView, ws],\n );\n\n // Suggest slash commands as the user types. Only when the buffer is\n // exactly a slash command head — `/foo bar` shouldn't open the popup.\n const slashSuggestions = input.startsWith('/') && !input.includes(' ') ? matchSlash(input) : [];\n\n // Reset the highlight when the visible list changes so ↑/↓ always starts\n // from the top of the new matches.\n useEffect(() => {\n if (slashIndex >= slashSuggestions.length) setSlashIndex(0);\n }, [slashSuggestions.length, slashIndex]);\n\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n if (!input.trim()) return;\n\n const content = input.trim();\n\n if (content.startsWith('/') && runSlashCommand(content)) {\n pushPrompt(content);\n setInput('');\n setHistoryIdx(-1);\n return;\n }\n\n setInput('');\n setHistoryIdx(-1);\n pushPrompt(content);\n\n // If the agent is still running, queue the follow-up instead of\n // dropping it. The run.result handler in useWebSocket drains the\n // queue one message at a time. We also enable the textarea while\n // running so this code path is reachable.\n if (isLoading) {\n enqueue(content);\n return;\n }\n\n try {\n if (client?.isConnected) {\n addMessage({ role: 'user', content });\n setLoading(true);\n sendMessage(content);\n } else {\n console.error('WebSocket not connected');\n }\n } catch (err) {\n console.error('Failed to send:', err);\n setLoading(false);\n }\n },\n [\n input,\n isLoading,\n enqueue,\n client,\n sendMessage,\n setLoading,\n addMessage,\n runSlashCommand,\n pushPrompt,\n ],\n );\n\n const handleAbort = useCallback(() => {\n sendAbort();\n setLoading(false);\n }, [sendAbort, setLoading]);\n\n /** \"Stop & edit\" — abort the in-flight run, then pull the last user\n * message back into the input so the user can rewrite the prompt and\n * resend. Saves the two-step dance of clicking Abort, waiting for the\n * agent to settle, then hunting for the original prompt. */\n const handleStopAndEdit = useCallback(() => {\n sendAbort();\n setLoading(false);\n const all = useChatStore.getState().messages;\n for (let i = all.length - 1; i >= 0; i--) {\n const m = all[i]!;\n if (m.role === 'user' && m.content) {\n setInput(m.content);\n requestAnimationFrame(() => {\n const ta = textareaRef.current;\n if (ta) {\n ta.style.height = 'auto';\n ta.style.height = `${Math.min(ta.scrollHeight, 200)}px`;\n ta.focus();\n ta.setSelectionRange(m.content.length, m.content.length);\n }\n });\n return;\n }\n }\n }, [sendAbort, setLoading]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n // Terminal-style prompt history: ↑ pulls the previous user prompt,\n // ↓ steps forward. Only active when both popups are closed AND the\n // input is empty OR already showing a history entry. We keep the cursor\n // ergonomic — once the user starts editing, we drop out of history mode.\n if (slashSuggestions.length === 0 && !atMention && promptHistory.length > 0) {\n if (e.key === 'ArrowUp') {\n const ta = e.currentTarget;\n // Only steal ↑ if we're on the first line (so multi-line editing\n // can still navigate within the textarea naturally).\n const beforeCursor = ta.value.slice(0, ta.selectionStart);\n if (historyIdx >= 0 || beforeCursor.indexOf('\\n') === -1) {\n e.preventDefault();\n const next = Math.min(promptHistory.length - 1, historyIdx + 1);\n setHistoryIdx(next);\n const text = promptHistory[next] ?? '';\n setInput(text);\n requestAnimationFrame(() => {\n const el = textareaRef.current;\n if (el) {\n el.style.height = 'auto';\n el.style.height = `${Math.min(el.scrollHeight, 200)}px`;\n el.setSelectionRange(text.length, text.length);\n }\n });\n return;\n }\n }\n if (e.key === 'ArrowDown' && historyIdx >= 0) {\n e.preventDefault();\n const next = historyIdx - 1;\n if (next < 0) {\n setHistoryIdx(-1);\n setInput('');\n } else {\n setHistoryIdx(next);\n const text = promptHistory[next] ?? '';\n setInput(text);\n requestAnimationFrame(() => {\n const el = textareaRef.current;\n if (el) {\n el.style.height = 'auto';\n el.style.height = `${Math.min(el.scrollHeight, 200)}px`;\n el.setSelectionRange(text.length, text.length);\n }\n });\n }\n return;\n }\n }\n\n // Slash popup keyboard navigation: ↑/↓ to select, Tab/Enter to commit,\n // Esc to dismiss. Matches the TUI's slash menu UX one-for-one so users\n // moving between surfaces don't have to relearn anything.\n if (slashSuggestions.length > 0) {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setSlashIndex((i) => (i + 1) % slashSuggestions.length);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n setSlashIndex((i) => (i - 1 + slashSuggestions.length) % slashSuggestions.length);\n return;\n }\n if (e.key === 'Tab') {\n e.preventDefault();\n const pick = slashSuggestions[slashIndex];\n if (pick) {\n setInput(pick.name + ' ');\n setSlashIndex(0);\n }\n return;\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n // Commit the highlighted suggestion if there's an exact match below\n // the cursor (or the user hasn't typed a full name yet). Otherwise\n // fall through to normal submit.\n const pick = slashSuggestions[slashIndex];\n if (pick && pick.name !== input.toLowerCase().trim()) {\n e.preventDefault();\n setInput('');\n runSlashCommand(pick.name);\n return;\n }\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n setInput('');\n return;\n }\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit(e);\n }\n };\n\n const adjustTextareaHeight = () => {\n const textarea = textareaRef.current;\n if (textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;\n }\n };\n\n return (\n <div className=\"flex flex-col gap-2\">\n {/* Smart paste hint — transient, auto-dismisses after 4s. We don't\n *do* anything with it (no auto-attach), just inform; users who\n pasted by accident can still Cmd+Z. */}\n {pasteHint && (\n <div className=\"rounded-md border border-amber-500/30 bg-amber-500/5 text-amber-700 dark:text-amber-300 px-2.5 py-1.5 text-xs flex items-center justify-between gap-2 animate-message\">\n <span>\n Pasted{' '}\n <span className=\"font-mono tabular-nums\">{pasteHint.chars.toLocaleString()}</span> chars\n (<span className=\"font-mono tabular-nums\">{pasteHint.lines}</span> lines) — fenced code\n blocks render best with <span className=\"font-mono\">```</span>.\n </span>\n <button\n type=\"button\"\n onClick={() => setPasteHint(null)}\n className=\"text-amber-600/70 hover:text-amber-600 dark:text-amber-300/70 dark:hover:text-amber-300 shrink-0\"\n title=\"Dismiss\"\n >\n ×\n </button>\n </div>\n )}\n {/* Queue visualization — shows messages the user stacked while the\n agent was still running. Each row has a remove button; the whole\n queue can be cleared. The hook below drains them after run.result. */}\n {queue.length > 0 && (\n <div className=\"rounded-lg border bg-muted/30 p-2 text-xs\">\n <div className=\"flex items-center justify-between mb-1.5\">\n <span className=\"text-[10px] uppercase tracking-wider text-muted-foreground font-medium\">\n Queued ({queue.length})\n </span>\n <button\n type=\"button\"\n onClick={clearQueue}\n className=\"text-muted-foreground hover:text-destructive text-xs\"\n >\n Clear all\n </button>\n </div>\n <ul className=\"space-y-1\">\n {queue.map((q, i) => (\n <li\n // biome-ignore lint/suspicious/noArrayIndexKey: queue has stable order\n key={i}\n className=\"flex items-start justify-between gap-2 rounded bg-background/60 border px-2 py-1\"\n >\n <span className=\"truncate flex-1 min-w-0\">{q}</span>\n <button\n type=\"button\"\n onClick={() => removeQueued(i)}\n className=\"text-muted-foreground hover:text-destructive shrink-0\"\n title=\"Remove from queue\"\n >\n ×\n </button>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n <form\n onSubmit={handleSubmit}\n onDragEnter={(e) => {\n // Only react to drags carrying files — text/uri-list drags from\n // other parts of the page shouldn't trip the overlay.\n if (!e.dataTransfer || !Array.from(e.dataTransfer.types).includes('Files')) return;\n e.preventDefault();\n setDraggingOver(true);\n }}\n onDragOver={(e) => {\n if (!e.dataTransfer || !Array.from(e.dataTransfer.types).includes('Files')) return;\n // preventDefault on dragover is what makes the area a valid drop\n // target — without it the browser navigates to the file instead.\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n }}\n onDragLeave={(e) => {\n // dragleave fires when crossing child boundaries too; only clear if\n // the cursor genuinely left the form (relatedTarget outside or null).\n if (e.currentTarget.contains(e.relatedTarget as Node | null)) return;\n setDraggingOver(false);\n }}\n onDrop={(e) => {\n if (!e.dataTransfer) return;\n const files = Array.from(e.dataTransfer.files ?? []);\n if (files.length === 0) {\n setDraggingOver(false);\n return;\n }\n e.preventDefault();\n setDraggingOver(false);\n // Insert `@<filename>` per dropped file at the current cursor, with\n // spaces between them. Browsers strip the full path for security, so\n // we use the basename only — the FilePicker (opened by setting\n // atMention to the last inserted handle) will resolve it against\n // the workspace tree.\n const ta = textareaRef.current;\n const insertPos = ta?.selectionStart ?? input.length;\n const before = input.slice(0, insertPos);\n const after = input.slice(insertPos);\n const needsLeadingSpace = before.length > 0 && !/\\s$/.test(before);\n const lead = needsLeadingSpace ? ' ' : '';\n const tokens = files.map((f) => `@${f.name}`);\n const joined = tokens.join(' ');\n // Trailing space only when there isn't already one — keeps the\n // cursor neatly between insert and following text.\n const needsTrailingSpace = after.length === 0 || !/^\\s/.test(after);\n const trail = needsTrailingSpace ? ' ' : '';\n const insertion = `${lead}${joined}${trail}`;\n const next = before + insertion + after;\n setInput(next);\n // Open the FilePicker against the LAST dropped basename so the user\n // can replace the basename with the correctly-resolved workspace\n // path. Position the @-mention start at the `@` of the last token.\n const lastTokenStart =\n before.length +\n lead.length +\n tokens.slice(0, -1).join(' ').length +\n (tokens.length > 1 ? 1 : 0);\n const lastBasename = files[files.length - 1]!.name;\n requestAnimationFrame(() => {\n if (ta) {\n const cur = before.length + insertion.length - trail.length;\n ta.focus();\n ta.setSelectionRange(cur, cur);\n ta.style.height = 'auto';\n ta.style.height = `${Math.min(ta.scrollHeight, 200)}px`;\n }\n setAtMention({ start: lastTokenStart, query: lastBasename });\n });\n }}\n className={cn(\n 'flex items-end gap-2 relative rounded-lg transition-colors',\n draggingOver && 'ring-2 ring-primary ring-offset-2 ring-offset-background bg-primary/5',\n )}\n >\n {draggingOver && (\n <div className=\"absolute inset-0 z-20 flex items-center justify-center pointer-events-none rounded-lg bg-primary/10 text-primary text-sm font-medium\">\n Drop file{`(s)`} to attach as @-mention\n </div>\n )}\n <div className=\"relative flex-1\">\n {/* @-mention file picker — takes priority over the slash popup\n since `@` and `/` can't both be active at the cursor. */}\n {atMention && (\n <FilePicker\n query={atMention.query}\n onClose={() => setAtMention(null)}\n onPick={(p) => {\n // Replace the partial `@query` with `@<path> `, then move\n // the cursor after the inserted space so typing continues\n // naturally.\n const before = input.slice(0, atMention.start);\n const after = input.slice(atMention.start + 1 + atMention.query.length);\n const inserted = `@${p} `;\n const next = before + inserted + after;\n setInput(next);\n setAtMention(null);\n requestAnimationFrame(() => {\n const ta = textareaRef.current;\n if (ta) {\n const pos = before.length + inserted.length;\n ta.focus();\n ta.setSelectionRange(pos, pos);\n ta.style.height = 'auto';\n ta.style.height = `${Math.min(ta.scrollHeight, 200)}px`;\n }\n });\n }}\n />\n )}\n\n {/* Slash command popup — descriptions inline, ↑/↓ to select, Tab to\n autocomplete, Enter to dispatch directly. Click also works. */}\n {!atMention &&\n slashSuggestions.length > 0 &&\n (() => {\n // Bucket the suggestions by category and preserve the global\n // index across categories — the keyboard navigation (↑/↓) tracks\n // a flat index, so each rendered row needs to map back to its\n // position in the un-grouped `slashSuggestions` array.\n const byCategory: Record<string, Array<{ cmd: SlashCommandDef; idx: number }>> = {};\n slashSuggestions.forEach((cmd, idx) => {\n if (!byCategory[cmd.category]) byCategory[cmd.category] = [];\n byCategory[cmd.category]!.push({ cmd, idx });\n });\n const orderedCategories = SLASH_CATEGORY_ORDER.filter((c) => byCategory[c]?.length);\n return (\n <div className=\"absolute bottom-full left-0 right-0 mb-2 rounded-lg border bg-popover shadow-md p-1 text-sm max-h-72 overflow-auto\">\n <div className=\"px-3 py-1 text-[10px] uppercase tracking-wider text-muted-foreground border-b mb-1\">\n ↑/↓ select · Tab complete · Enter dispatch · Esc dismiss\n </div>\n {orderedCategories.map((cat) => (\n <div key={cat} className=\"mb-1\">\n <div className=\"px-3 pt-1 pb-0.5 text-[10px] uppercase tracking-wider text-muted-foreground/70 font-semibold\">\n {cat}\n </div>\n {byCategory[cat]!.map(({ cmd, idx }) => (\n <button\n type=\"button\"\n key={cmd.name}\n onClick={() => {\n setInput('');\n runSlashCommand(cmd.name);\n }}\n onMouseEnter={() => setSlashIndex(idx)}\n className={cn(\n 'w-full text-left px-3 py-1.5 rounded transition-colors flex items-center gap-3',\n idx === slashIndex\n ? 'bg-accent text-accent-foreground'\n : 'hover:bg-accent/40',\n )}\n >\n <span className=\"font-mono shrink-0\">{cmd.name}</span>\n {cmd.aliases?.length ? (\n <span className=\"text-xs text-muted-foreground/70 font-mono shrink-0\">\n ({cmd.aliases.join(', ')})\n </span>\n ) : null}\n <span className=\"text-xs text-muted-foreground truncate\">\n — {cmd.description}\n </span>\n </button>\n ))}\n </div>\n ))}\n </div>\n );\n })()}\n <textarea\n ref={textareaRef}\n value={input}\n onChange={(e) => {\n const v = e.target.value;\n setInput(v);\n adjustTextareaHeight();\n // Manual typing drops us out of history mode so the next\n // Enter sends the user's edits, not a stale history entry.\n if (historyIdx >= 0) setHistoryIdx(-1);\n // Detect / refresh @-mention based on cursor position.\n const cur = e.target.selectionStart ?? v.length;\n setAtMention(detectAtMention(v, cur));\n }}\n onSelect={(e) => {\n const ta = e.currentTarget;\n setAtMention(detectAtMention(ta.value, ta.selectionStart));\n }}\n onKeyDown={handleKeyDown}\n onPaste={(e) => {\n // Surface a tiny hint when the user drops a chunky payload —\n // helps them realize they pasted the wrong thing (e.g. an\n // entire file when they meant a snippet). 4-second TTL.\n const text = e.clipboardData?.getData('text') ?? '';\n if (text.length > 800) {\n const lines = text.split('\\n').length;\n setPasteHint({ chars: text.length, lines });\n setTimeout(() => setPasteHint(null), 4000);\n }\n }}\n placeholder={\n !client?.isConnected\n ? 'Connect to server first…'\n : isLoading\n ? 'Agent is running — type to queue a follow-up…'\n : 'Message WrongStack… (type / for commands, @ for files)'\n }\n className={cn(\n 'flex min-h-[44px] w-full resize-none rounded-lg border border-input bg-background px-4 py-3 pr-12',\n 'text-sm ring-offset-background placeholder:text-muted-foreground',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n 'scrollbar-thin',\n )}\n rows={1}\n disabled={!client?.isConnected}\n />\n\n {input.length > 0 &&\n (() => {\n // Hide the token estimate until the draft is non-trivial — small\n // messages aren't worth a context warning, and the chip would\n // otherwise just flicker as the user types each character.\n const showTokens = input.length >= 400;\n const estTokens = Math.ceil(input.length / 4);\n // Project the next request's context usage: last sent + draft +\n // small overhead. If that crosses 85% of the configured window,\n // tint amber; past 100% turns red. Falls through to muted when\n // we don't have the window size yet (e.g. before first request).\n let tone = 'text-muted-foreground';\n let title: string | undefined;\n if (maxContext > 0 && showTokens) {\n const projected = lastInputTokens + estTokens + 64;\n const pct = (projected / maxContext) * 100;\n if (pct >= 100) {\n tone = 'text-red-600 dark:text-red-400 font-medium';\n title = `Projected ${Math.round(pct)}% of ${maxContext.toLocaleString()} ctx — will likely error or compact.`;\n } else if (pct >= 85) {\n tone = 'text-amber-600 dark:text-amber-400 font-medium';\n title = `Projected ${Math.round(pct)}% of ${maxContext.toLocaleString()} ctx — getting tight.`;\n } else {\n title = `≈ ${estTokens.toLocaleString()} tokens · projected ${Math.round(pct)}% of ${maxContext.toLocaleString()} ctx.`;\n }\n } else if (showTokens) {\n title = `≈ ${estTokens.toLocaleString()} tokens (4-char heuristic)`;\n }\n return (\n <span\n className={cn('absolute bottom-1.5 right-12 text-xs tabular-nums', tone)}\n title={title}\n >\n {input.length}\n {showTokens && (\n <span className=\"ml-1 opacity-70\">\n · ≈{estTokens >= 1000 ? `${(estTokens / 1000).toFixed(1)}k` : estTokens}t\n </span>\n )}\n </span>\n );\n })()}\n </div>\n\n <div className=\"flex gap-1\">\n {isLoading ? (\n <>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"outline\"\n onClick={handleStopAndEdit}\n className=\"h-[44px] w-[44px] rounded-lg\"\n title=\"Stop run and edit the last prompt (reuse + rewrite)\"\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"destructive\"\n onClick={handleAbort}\n className=\"h-[44px] w-[44px] rounded-lg\"\n title=\"Abort the current run\"\n >\n <Square className=\"h-4 w-4 fill-current\" />\n </Button>\n </>\n ) : (\n <Button\n type=\"submit\"\n size=\"icon\"\n disabled={!input.trim() || !client?.isConnected}\n className=\"h-[44px] w-[44px] rounded-lg\"\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n </form>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport {\n useChatStore,\n useConfigStore,\n useHistoryStore,\n useSessionStore,\n useUIStore,\n} from '@/stores';\nimport {\n ArchiveRestore,\n BarChart3,\n Brain,\n Cpu,\n Database,\n Download,\n Hash,\n History as HistoryIcon,\n type LucideIcon,\n Maximize2,\n Monitor,\n Moon,\n RotateCcw,\n Search,\n Settings as SettingsIcon,\n Sparkles,\n Stethoscope,\n Sun,\n Trash2,\n Volume2,\n VolumeX,\n Wrench,\n} from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Cross-cut search-everything overlay invoked with Ctrl/Cmd+K. Mirrors the\n * pattern from VS Code / Linear / Slack — one keyboard shortcut, one fuzzy\n * search input, one list of every action the user might want. Each entry\n * names its category (Command / Session / Theme) and an icon, plus an\n * inline \"↵\" hint on the highlighted row. Closes on Esc, Enter, or click.\n */\ninterface PaletteItem {\n id: string;\n category: 'Command' | 'Session' | 'Theme' | 'Tool';\n label: string;\n hint?: string;\n icon: LucideIcon;\n keywords?: string[];\n run: () => void;\n}\n\nexport function CommandPalette() {\n const open = useUIStore((s) => s.paletteOpen);\n const setOpen = useUIStore((s) => s.setPaletteOpen);\n const setCurrentView = useUIStore((s) => s.setCurrentView);\n const setTheme = useConfigStore((s) => s.setTheme);\n const { entries: historyEntries } = useHistoryStore();\n const { addMessage, clearMessages } = useChatStore();\n const ws = useWebSocket();\n\n const [query, setQuery] = useState('');\n const [index, setIndex] = useState(0);\n const inputRef = useRef<HTMLInputElement>(null);\n\n // Focus the search input every time we open. Defer to the next tick so\n // it actually grabs focus after the dialog has mounted.\n useEffect(() => {\n if (open) {\n setQuery('');\n setIndex(0);\n requestAnimationFrame(() => inputRef.current?.focus());\n }\n }, [open]);\n\n // Global Ctrl/Cmd+K to toggle, Esc to dismiss. Bound at body level so it\n // works from anywhere in the app, not just when the palette has focus.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {\n e.preventDefault();\n setOpen(!useUIStore.getState().paletteOpen);\n return;\n }\n if (e.key === 'Escape' && useUIStore.getState().paletteOpen) {\n e.preventDefault();\n setOpen(false);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [setOpen]);\n\n const items = useMemo<PaletteItem[]>(() => {\n const base: PaletteItem[] = [\n // Commands\n {\n id: 'help',\n category: 'Command',\n label: 'Show slash commands',\n icon: Hash,\n keywords: ['help', 'commands', '?'],\n run: () => {\n addMessage({\n role: 'assistant',\n content: 'Type `/` in the message box to see every slash command.',\n });\n },\n },\n {\n id: 'tools',\n category: 'Command',\n label: 'List tools',\n icon: Wrench,\n keywords: ['tools', 'list'],\n run: () => ws.listTools(),\n },\n {\n id: 'memory',\n category: 'Command',\n label: 'Show memory',\n icon: Brain,\n keywords: ['memory', 'remember', 'notes'],\n run: () => ws.listMemory(),\n },\n {\n id: 'skills',\n category: 'Command',\n label: 'List skills',\n icon: Sparkles,\n keywords: ['skills'],\n run: () => ws.listSkills(),\n },\n {\n id: 'diag',\n category: 'Command',\n label: 'Runtime diagnostics',\n icon: Stethoscope,\n keywords: ['diag', 'diagnostics', 'debug'],\n run: () => ws.getDiag(),\n },\n {\n id: 'stats',\n category: 'Command',\n label: 'Session stats (tokens, cache, cost)',\n icon: BarChart3,\n keywords: ['stats', 'tokens', 'cost', 'cache'],\n run: () => ws.getStats(),\n },\n // Session actions\n {\n id: 'clear',\n category: 'Session',\n label: 'Clear context',\n hint: 'Wipe in-memory context, keep session id',\n icon: Trash2,\n keywords: ['clear', 'reset', 'wipe'],\n run: () => {\n clearMessages();\n ws.client?.clearContext?.();\n },\n },\n {\n id: 'new',\n category: 'Session',\n label: 'New session',\n hint: 'Brand-new on disk + memory',\n icon: RotateCcw,\n keywords: ['new', 'fresh', 'session'],\n run: () => ws.client?.newSession?.(),\n },\n {\n id: 'compact',\n category: 'Session',\n label: 'Compact context',\n icon: Database,\n keywords: ['compact', 'shrink', 'context'],\n run: () => ws.client?.compactContext?.(),\n },\n {\n id: 'export',\n category: 'Session',\n label: 'Export chat as markdown',\n icon: Download,\n keywords: ['export', 'save', 'markdown', 'download'],\n run: () => downloadChatAsMarkdown(),\n },\n {\n id: 'export-html',\n category: 'Session',\n label: 'Export chat as HTML',\n hint: 'Self-contained, opens in any browser',\n icon: Download,\n keywords: ['export', 'html', 'download', 'archive'],\n run: () => downloadChatAsHtml(),\n },\n // Navigation\n {\n id: 'history',\n category: 'Command',\n label: 'Open history',\n icon: HistoryIcon,\n keywords: ['history', 'sessions'],\n run: () => setCurrentView('history'),\n },\n {\n id: 'settings',\n category: 'Command',\n label: 'Open settings',\n icon: SettingsIcon,\n keywords: ['settings', 'config'],\n run: () => setCurrentView('settings'),\n },\n {\n id: 'model',\n category: 'Command',\n label: 'Change provider/model',\n icon: Cpu,\n keywords: ['model', 'provider', 'change'],\n run: () => setCurrentView('settings'),\n },\n // Theme\n {\n id: 'theme-light',\n category: 'Theme',\n label: 'Theme: Light',\n icon: Sun,\n keywords: ['theme', 'light', 'mode'],\n run: () => setTheme('light'),\n },\n {\n id: 'theme-dark',\n category: 'Theme',\n label: 'Theme: Dark',\n icon: Moon,\n keywords: ['theme', 'dark', 'mode'],\n run: () => setTheme('dark'),\n },\n {\n id: 'theme-system',\n category: 'Theme',\n label: 'Theme: Follow system',\n icon: Monitor,\n keywords: ['theme', 'system', 'auto'],\n run: () => setTheme('system'),\n },\n {\n id: 'compact-toggle',\n category: 'Command',\n label: 'Toggle compact density',\n icon: Maximize2,\n hint: 'Ctrl+Shift+D',\n keywords: ['compact', 'dense', 'density', 'size'],\n run: () => useUIStore.getState().toggleCompactMode(),\n },\n {\n id: 'sound-toggle',\n category: 'Command',\n label: useConfigStore.getState().soundOnComplete\n ? 'Sound on completion: ON — turn off'\n : 'Sound on completion: OFF — turn on',\n icon: useConfigStore.getState().soundOnComplete ? Volume2 : VolumeX,\n hint: 'Chime when a run finishes',\n keywords: ['sound', 'audio', 'chime', 'notify', 'beep'],\n run: () => {\n const next = !useConfigStore.getState().soundOnComplete;\n useConfigStore.getState().setSoundOnComplete(next);\n // Play once immediately when enabling so the user hears what\n // they just signed up for (and so Web Audio gets the gesture\n // permission unlocked).\n if (next) {\n import('@/lib/chime').then((m) => m.playCompletionChime()).catch(() => {});\n }\n },\n },\n ];\n\n // Append recent sessions so the palette doubles as a \"switch to\n // session\" picker — the killer feature for multi-project use.\n for (const entry of historyEntries.slice(0, 10)) {\n if (entry.isCurrent) continue;\n base.push({\n id: `resume-${entry.id}`,\n category: 'Session',\n label: `Resume: ${entry.title || '(empty)'}`,\n hint: `${entry.provider}/${entry.model}`,\n icon: ArchiveRestore,\n keywords: ['resume', entry.title, entry.id, entry.provider, entry.model],\n run: () => ws.resumeSession(entry.id),\n });\n }\n return base;\n }, [historyEntries, ws, setCurrentView, setTheme, addMessage, clearMessages]);\n\n const filtered = useMemo(() => {\n const q = query.toLowerCase().trim();\n if (!q) return items;\n return items.filter((it) => {\n const hay = [it.label, it.hint ?? '', it.category, ...(it.keywords ?? [])]\n .join(' ')\n .toLowerCase();\n return hay.includes(q);\n });\n }, [items, query]);\n\n useEffect(() => {\n if (index >= filtered.length) setIndex(0);\n }, [filtered.length, index]);\n\n if (!open) return null;\n\n const dispatchPick = (item: PaletteItem | undefined) => {\n if (!item) return;\n setOpen(false);\n item.run();\n };\n\n return (\n <div\n className=\"fixed inset-0 z-50 bg-background/60 backdrop-blur-sm flex items-start justify-center pt-[14vh] px-4\"\n onClick={() => setOpen(false)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(false);\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n className=\"w-full max-w-2xl rounded-xl border bg-popover shadow-2xl overflow-hidden flex flex-col\"\n >\n <div className=\"flex items-center gap-2 px-4 py-3 border-b\">\n <Search className=\"h-4 w-4 text-muted-foreground\" />\n <input\n ref={inputRef}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search commands, sessions, settings…\"\n className=\"flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground\"\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setIndex((i) => (i + 1) % Math.max(1, filtered.length));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setIndex(\n (i) => (i - 1 + Math.max(1, filtered.length)) % Math.max(1, filtered.length),\n );\n } else if (e.key === 'Enter') {\n e.preventDefault();\n dispatchPick(filtered[index]);\n }\n }}\n />\n <kbd className=\"text-[10px] text-muted-foreground border rounded px-1.5 py-0.5\">Esc</kbd>\n </div>\n\n <div className=\"max-h-[60vh] overflow-y-auto\">\n {filtered.length === 0 ? (\n <div className=\"px-4 py-8 text-center text-sm text-muted-foreground\">\n No matches for \"{query}\"\n </div>\n ) : (\n renderGroupedList(filtered, index, dispatchPick, setIndex)\n )}\n </div>\n\n <div className=\"border-t px-4 py-2 text-[10px] uppercase tracking-wider text-muted-foreground flex items-center gap-3\">\n <span>↑↓ navigate</span>\n <span>↵ select</span>\n <span>Esc dismiss</span>\n </div>\n </div>\n </div>\n );\n}\n\nfunction renderGroupedList(\n filtered: PaletteItem[],\n index: number,\n dispatch: (it: PaletteItem) => void,\n setIndex: (i: number) => void,\n) {\n // Maintain global filtered-index as we walk, so the highlighted row\n // matches what arrow keys point at. Grouping is visual sugar only.\n const groups: Record<string, Array<{ item: PaletteItem; globalIdx: number }>> = {};\n filtered.forEach((it, i) => {\n if (!groups[it.category]) groups[it.category] = [];\n groups[it.category]!.push({ item: it, globalIdx: i });\n });\n return (\n <div className=\"p-1\">\n {Object.entries(groups).map(([cat, rows]) => (\n <div key={cat}>\n <div className=\"px-3 pt-2 pb-1 text-[10px] uppercase tracking-wider text-muted-foreground\">\n {cat}\n </div>\n {rows.map(({ item, globalIdx }) => {\n const Icon = item.icon;\n const active = globalIdx === index;\n return (\n <button\n type=\"button\"\n key={item.id}\n onMouseEnter={() => setIndex(globalIdx)}\n onClick={() => dispatch(item)}\n className={cn(\n 'w-full flex items-center gap-3 px-3 py-2 rounded text-left text-sm transition-colors',\n active ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/40',\n )}\n >\n <Icon className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <div className=\"truncate\">{item.label}</div>\n {item.hint && (\n <div className=\"text-xs text-muted-foreground truncate\">{item.hint}</div>\n )}\n </div>\n {active && <span className=\"text-[10px] text-muted-foreground\">↵</span>}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * Build a markdown export of the current chat and trigger a browser\n * download. Includes user/assistant turns and a compact summary of tool\n * calls inline so the transcript stays readable but you can still see\n * which tools the agent invoked.\n */\nexport function downloadChatAsMarkdown(): void {\n const messages = useChatStore.getState().messages;\n const session = useChatStore.getState();\n void session;\n const lines: string[] = [];\n const now = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');\n lines.push(`# WrongStack chat export`);\n lines.push(`*Exported: ${new Date().toISOString()}*`);\n lines.push('');\n for (const m of messages) {\n if (m.role === 'user') {\n lines.push(`## 👤 User`);\n lines.push('');\n lines.push(m.content);\n lines.push('');\n } else if (m.role === 'assistant') {\n lines.push(`## 🤖 Assistant`);\n lines.push('');\n lines.push(m.content);\n lines.push('');\n } else if (m.role === 'tool') {\n const status = m.isError ? '❌' : m.toolResult !== undefined ? '✅' : '⏳';\n lines.push(`### 🔧 Tool: \\`${m.toolName ?? 'unknown'}\\` ${status}`);\n if (m.toolInput !== undefined) {\n lines.push('```json');\n lines.push(JSON.stringify(m.toolInput, null, 2));\n lines.push('```');\n }\n if (m.toolResult) {\n lines.push('<details><summary>Output</summary>');\n lines.push('');\n lines.push('```');\n lines.push(m.toolResult);\n lines.push('```');\n lines.push('</details>');\n }\n lines.push('');\n }\n }\n const blob = new Blob([lines.join('\\n')], { type: 'text/markdown' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `wrongstack-chat-${now}.md`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Build a stand-alone HTML export of the current chat. Self-contained\n * (inline CSS, no external assets) so the file opens cleanly anywhere\n * and survives being emailed / pasted into a wiki / archived offline.\n * Code-block highlighting is intentionally not included — would require\n * shipping a syntax highlighter, and the export is already readable\n * with the basic monospace styling.\n */\nexport function downloadChatAsHtml(): void {\n const messages = useChatStore.getState().messages;\n const session = useSessionStore.getState();\n const now = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');\n const escapeHtml = (s: string) =>\n s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n\n const turns = messages.map((m) => {\n if (m.role === 'tool') {\n const status = m.isError ? '❌' : m.toolResult !== undefined ? '✅' : '⏳';\n return `\n <section class=\"bubble tool ${m.isError ? 'error' : ''}\">\n <header><span class=\"icon\">🔧</span><code>${escapeHtml(m.toolName ?? 'tool')}</code> ${status}</header>\n ${\n m.toolInput !== undefined\n ? `<details><summary>Input</summary><pre>${escapeHtml(JSON.stringify(m.toolInput, null, 2))}</pre></details>`\n : ''\n }\n ${\n m.toolResult\n ? `<details><summary>Output</summary><pre>${escapeHtml(m.toolResult)}</pre></details>`\n : ''\n }\n </section>`;\n }\n const cls = m.role === 'user' ? 'user' : 'assistant';\n const icon = m.role === 'user' ? '👤' : '🤖';\n const role = m.role === 'user' ? 'User' : 'Assistant';\n // Keep newlines but escape everything — we deliberately don't render\n // markdown here. Static HTML with preserved whitespace is faithful to\n // what the user actually saw, and dodges the whole \"rendered markdown\n // syntax-highlighting drift\" problem.\n return `\n <section class=\"bubble ${cls}\">\n <header><span class=\"icon\">${icon}</span><strong>${role}</strong></header>\n <pre class=\"content\">${escape(m.content)}</pre>\n </section>`;\n });\n\n const html = `<!doctype html>\n<html lang=\"en\"><head>\n<meta charset=\"utf-8\">\n<title>WrongStack chat — ${escape(session.session?.title || session.projectName || 'export')}</title>\n<style>\n :root { color-scheme: light dark; }\n body { font: 14px/1.55 -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; max-width: 920px; margin: 24px auto; padding: 0 16px; }\n h1 { font-size: 20px; margin-bottom: 4px; }\n .meta { color: #666; font-size: 12px; margin-bottom: 24px; }\n .bubble { margin: 12px 0; padding: 10px 14px; border-radius: 10px; border: 1px solid #ddd; }\n .bubble header { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: #666; margin-bottom: 6px; }\n .bubble header .icon { margin-right: 4px; }\n .bubble.user { background: #eef4ff; border-color: #c8d8f5; }\n .bubble.assistant { background: #fff; }\n .bubble.tool { background: #fafafa; }\n .bubble.tool.error { background: #fff5f5; border-color: #f5c8c8; }\n pre.content, .bubble pre { white-space: pre-wrap; word-break: break-word; font: 12px/1.5 ui-monospace, Menlo, Consolas, monospace; margin: 0; }\n details summary { cursor: pointer; color: #555; font-size: 12px; }\n details pre { margin-top: 6px; background: #f4f4f4; padding: 8px; border-radius: 6px; max-height: 360px; overflow: auto; }\n @media (prefers-color-scheme: dark) {\n body { background: #0d0d0f; color: #e6e6e6; }\n .bubble { border-color: #2a2a2e; }\n .bubble.user { background: #16213a; border-color: #2a3d6b; }\n .bubble.assistant { background: #161618; }\n .bubble.tool { background: #131315; }\n .bubble.tool.error { background: #2a1717; border-color: #5c2a2a; }\n details pre { background: #1a1a1c; }\n .meta, .bubble header, details summary { color: #999; }\n }\n</style>\n</head><body>\n<h1>WrongStack chat — ${escape(session.session?.title || session.projectName || 'export')}</h1>\n<div class=\"meta\">\n Exported ${new Date().toISOString()}${session.session?.provider ? ` · ${escape(session.session.provider)}/${escape(session.session.model)}` : ''} · ${messages.length} message${messages.length === 1 ? '' : 's'}\n</div>\n${turns.join('')}\n</body></html>`;\n const blob = new Blob([html], { type: 'text/html;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `wrongstack-chat-${now}.html`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore } from '@/stores';\nimport { FileText, Folder } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\ninterface FilePickerProps {\n /** Whatever the user typed after the `@` trigger (case-preserved). */\n query: string;\n /** Called when the user picks a file (Enter / Tab / click). */\n onPick: (relPath: string) => void;\n /** Called when the picker should close without inserting (Esc). */\n onClose: () => void;\n}\n\n/**\n * `@` file mention popup. Subscribes to the WS `files.list` response,\n * supports ↑/↓ Tab Enter Esc. Refetches on query change with a small\n * debounce so we don't spam the backend on every keystroke.\n */\nexport function FilePicker({ query, onPick, onClose }: FilePickerProps) {\n const ws = useWebSocket();\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const [files, setFiles] = useState<string[]>([]);\n const [index, setIndex] = useState(0);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const wantHandle = useRef<{ resolve: (paths: string[]) => void } | null>(null);\n\n // Subscribe to the single `files.list` event for the lifetime of this\n // picker. We use the raw client.on() rather than the hook because the\n // hook installs handlers at boot — but `files.list` is picker-local and\n // the data shouldn't leak into other surfaces.\n useEffect(() => {\n const client = getWSClient(wsUrl);\n const off = client.on('files.list', (msg) => {\n const p = msg.payload as { files: string[] };\n setFiles(p.files ?? []);\n setIndex(0);\n wantHandle.current?.resolve(p.files ?? []);\n wantHandle.current = null;\n });\n return () => off();\n }, [wsUrl]);\n\n // Debounced fetch on query change.\n useEffect(() => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(() => {\n ws.client.listFiles(query, 50);\n }, 80);\n return () => {\n if (debounceRef.current) clearTimeout(debounceRef.current);\n };\n }, [query, ws.client]);\n\n // Keyboard nav at window level — ChatInput owns the textarea so we\n // intercept keys there and forward intent here via the imperative API.\n // We listen too so click+keyboard mixing works.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setIndex((i) => (i + 1) % Math.max(1, files.length));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setIndex((i) => (i - 1 + Math.max(1, files.length)) % Math.max(1, files.length));\n } else if (e.key === 'Enter' || e.key === 'Tab') {\n if (files.length === 0) return;\n e.preventDefault();\n onPick(files[index]!);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [files, index, onPick, onClose]);\n\n return (\n <div className=\"absolute bottom-full left-0 right-0 mb-2 rounded-lg border bg-popover shadow-md p-1 text-sm max-h-72 overflow-auto\">\n <div className=\"px-3 py-1 text-[10px] uppercase tracking-wider text-muted-foreground border-b mb-1 flex items-center justify-between\">\n <span>@ Files {query && `· \"${query}\"`}</span>\n <span>↑/↓ select · ↵ insert · Esc dismiss</span>\n </div>\n {files.length === 0 ? (\n <div className=\"px-3 py-2 text-xs text-muted-foreground italic\">\n {query ? `No files match \"${query}\"` : 'Searching project…'}\n </div>\n ) : (\n files.map((p, i) => (\n <button\n key={p}\n type=\"button\"\n onClick={() => onPick(p)}\n onMouseEnter={() => setIndex(i)}\n className={cn(\n 'w-full text-left px-3 py-1.5 rounded transition-colors flex items-center gap-2 font-mono text-xs',\n i === index ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/40',\n )}\n >\n {p.includes('/') ? (\n <Folder className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n ) : (\n <FileText className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n )}\n <span className=\"truncate\">{p}</span>\n </button>\n ))\n )}\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { Slot } from '@radix-ui/react-slot';\nimport { type VariantProps, cva } from 'class-variance-authority';\nimport * as React from 'react';\n\nconst buttonVariants = cva(\n 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n ghost: 'hover:bg-accent hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 hover:underline',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : 'button';\n return (\n <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />\n );\n },\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n","import { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport type { WsStatus } from '@/lib/ws-client';\nimport { useConfigStore } from '@/stores';\nimport { Loader2, RotateCcw, Wifi, WifiOff } from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\ninterface Props {\n wsStatus: WsStatus;\n wsConnected: boolean;\n}\n\n/**\n * Tiny status pill for the topbar. Renders four distinct visual states:\n *\n * • open — solid green Wi-Fi\n * • connecting — pulsing yellow spinner\n * • reconnecting — orange, attempt counter + live retry countdown,\n * click to retry immediately\n * • closed — red Wi-Fi-off, click to retry, tooltip shows error\n *\n * The countdown ticks live (every 500ms while `nextRetryAt` is in the\n * future) without forcing the whole topbar to re-render — we only re-run\n * the local timer effect.\n */\nexport function ConnectionChip({ wsStatus, wsConnected }: Props) {\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const [now, setNow] = useState(Date.now());\n\n // Keep a live clock running only while we're between retries, otherwise\n // we'd burn one render every 500ms in a happy-path session.\n useEffect(() => {\n if (wsStatus.state !== 'reconnecting') return;\n const id = setInterval(() => setNow(Date.now()), 500);\n return () => clearInterval(id);\n }, [wsStatus.state]);\n\n const retry = () => getWSClient(wsUrl).retryNow();\n\n if (wsStatus.state === 'open' && wsConnected) {\n return (\n <div\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 bg-green-500/10 text-green-600 dark:text-green-400\"\n title=\"Backend connected\"\n >\n <Wifi className=\"h-3 w-3\" />\n </div>\n );\n }\n\n if (wsStatus.state === 'connecting') {\n return (\n <div\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 bg-yellow-500/10 text-yellow-600 dark:text-yellow-400\"\n title=\"Connecting to backend\"\n >\n <Loader2 className=\"h-3 w-3 animate-spin\" />\n <span>connecting</span>\n </div>\n );\n }\n\n if (wsStatus.state === 'reconnecting') {\n const remaining = Math.max(0, Math.ceil((wsStatus.nextRetryAt - now) / 1000));\n return (\n <button\n type=\"button\"\n onClick={retry}\n className={cn(\n 'flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0',\n 'bg-orange-500/10 text-orange-600 dark:text-orange-400 hover:bg-orange-500/20',\n 'transition-colors',\n )}\n title={\n wsStatus.lastError\n ? `Reconnecting — last error: ${wsStatus.lastError}. Click to retry now.`\n : 'Reconnecting — click to retry now.'\n }\n >\n <Loader2 className=\"h-3 w-3 animate-spin\" />\n <span>\n retry #{wsStatus.attempt} in {remaining}s\n </span>\n </button>\n );\n }\n\n // closed\n return (\n <button\n type=\"button\"\n onClick={retry}\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium shrink-0 bg-red-500/10 text-red-600 dark:text-red-400 hover:bg-red-500/20 transition-colors\"\n title={\n wsStatus.state === 'closed' && wsStatus.error\n ? `Disconnected: ${wsStatus.error}. Click to retry.`\n : 'Disconnected. Click to retry.'\n }\n >\n <WifiOff className=\"h-3 w-3\" />\n <span className=\"flex items-center gap-0.5\">\n offline\n <RotateCcw className=\"h-3 w-3 opacity-70\" />\n </span>\n </button>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useChatStore, useSessionStore } from '@/stores';\nimport { useEffect, useRef, useState } from 'react';\n\n/**\n * Clickable cost figure in the topbar. Tap to drop a small popover with a\n * per-turn breakdown sourced from `ChatMessage.runSummary` (attached by the\n * run.result handler). Helps the cost-conscious user trace where the\n * dollars actually went without spelunking through transcripts.\n *\n * We only render the popover after the user clicks — the cost chip itself\n * is identical to the plain span it replaces, so the topbar layout stays\n * unchanged.\n */\nexport function CostChip() {\n const cost = useSessionStore((s) => s.cost);\n const inputCost = useSessionStore((s) => s.inputCost);\n const outputCost = useSessionStore((s) => s.outputCost);\n const cacheReadCost = useSessionStore((s) => s.cacheReadCost);\n const messages = useChatStore((s) => s.messages);\n const [open, setOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (!rootRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('mousedown', onClick);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('mousedown', onClick);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n /** Top expensive turns. We only consider messages with a runSummary —\n * the run.result handler attaches one per completed turn. costDelta is\n * the delta computed in that handler (session cost at end minus at\n * start of this turn). Falls back to nothing if no data yet. */\n const turns = (() => {\n const rows: Array<{\n id: string;\n preview: string;\n cost: number;\n tools: number;\n ms: number;\n ts: number;\n }> = [];\n for (const m of messages) {\n if (m.role === 'assistant' && m.runSummary && m.runSummary.costDelta > 0) {\n rows.push({\n id: m.id,\n preview: m.content.slice(0, 80).replace(/\\s+/g, ' ').trim() || '(empty)',\n cost: m.runSummary.costDelta,\n tools: m.runSummary.tools,\n ms: m.runSummary.durationMs,\n ts: m.timestamp,\n });\n }\n }\n rows.sort((a, b) => b.cost - a.cost);\n return rows.slice(0, 5);\n })();\n\n const fmt$ = (v: number) =>\n v >= 0.01\n ? `$${v.toFixed(4)}`\n : v > 0\n ? `$${v.toFixed(6).replace(/0+$/, '').replace(/\\.$/, '')}`\n : '$0';\n\n const haveRates = inputCost > 0 || outputCost > 0;\n\n return (\n <div ref={rootRef} className=\"relative inline-block\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n 'font-medium text-green-600 dark:text-green-400 hover:underline tabular-nums',\n )}\n title=\"Click for per-turn cost breakdown\"\n >\n ${cost.toFixed(4)}\n </button>\n {open && (\n <div className=\"absolute right-0 top-full mt-1 z-30 w-80 rounded-md border bg-popover shadow-lg p-3 text-foreground\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Cost breakdown\n </span>\n <span className=\"font-mono tabular-nums text-sm font-semibold text-green-600 dark:text-green-400\">\n {fmt$(cost)}\n </span>\n </div>\n {haveRates ? (\n <div className=\"text-[11px] text-muted-foreground font-mono mb-3 border-b pb-2\">\n <div className=\"flex justify-between\">\n <span>input /1M</span>\n <span>${inputCost.toFixed(2)}</span>\n </div>\n <div className=\"flex justify-between\">\n <span>output /1M</span>\n <span>${outputCost.toFixed(2)}</span>\n </div>\n {cacheReadCost > 0 && (\n <div className=\"flex justify-between\">\n <span>cache /1M</span>\n <span>${cacheReadCost.toFixed(2)}</span>\n </div>\n )}\n </div>\n ) : (\n <div className=\"text-[11px] text-muted-foreground italic mb-3 border-b pb-2\">\n No pricing for the current model — cost figures are zero.\n </div>\n )}\n {turns.length === 0 ? (\n <div className=\"text-xs text-muted-foreground italic\">\n No completed turns yet. Run a prompt to see per-turn cost here.\n </div>\n ) : (\n <>\n <div className=\"text-[10px] uppercase tracking-wider text-muted-foreground/80 mb-1\">\n Top {turns.length} expensive turn{turns.length === 1 ? '' : 's'}\n </div>\n <ul className=\"space-y-1\">\n {turns.map((t) => (\n <li key={t.id}>\n <button\n type=\"button\"\n onClick={() => {\n const el = document.querySelector(`[data-message-id=\"${t.id}\"]`);\n if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n setOpen(false);\n }}\n className=\"w-full text-left rounded px-2 py-1.5 hover:bg-accent/40 transition-colors\"\n >\n <div className=\"flex items-baseline justify-between gap-2\">\n <span className=\"text-xs truncate\">{t.preview}</span>\n <span className=\"text-xs font-mono tabular-nums text-green-600 dark:text-green-400 shrink-0\">\n {fmt$(t.cost)}\n </span>\n </div>\n <div className=\"text-[10px] text-muted-foreground font-mono mt-0.5\">\n {t.tools} tool{t.tools === 1 ? '' : 's'} · {(t.ms / 1000).toFixed(1)}s\n </div>\n </button>\n </li>\n ))}\n </ul>\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Tool-aware one-line summary for the collapsed bubble. The whole point is\n * that 8 parallel `read` calls should NOT look identical, and a TodoWrite\n * call with 8 todos shouldn't dump 800 chars of JSON when \"8 todos · 3\n * done\" tells the whole story.\n *\n * Cases that earn a custom branch:\n * - TodoWrite → \"8 todos · 3 done · 2 in-progress\"\n * - edit / write → \"edit foo.ts (3 lines → 5 lines)\" / \"write …\"\n * - bash / shell → \"$ <command snippet>\"\n * - fetch / http → \"GET https://…\"\n * - grep / glob → pattern + scope\n * - batch_tool_use → \"N sub-tools\"\n * - read → \"path:N..M\" if offset/limit present\n *\n * Everything else falls back to the head-field-or-JSON heuristic the\n * collapsed view used before.\n */\n\nconst FALLBACK_HEAD_FIELDS = [\n 'path',\n 'file_path',\n 'pattern',\n 'command',\n 'cmd',\n 'url',\n 'query',\n 'description',\n 'content',\n];\n\nexport function summarizeToolInput(toolName: string | undefined, input: unknown): string {\n if (input === null || input === undefined) return '';\n if (typeof input !== 'object') return clip(String(input), 120);\n\n const obj = input as Record<string, unknown>;\n const name = (toolName ?? '').toLowerCase();\n\n // ---- TodoWrite ----------------------------------------------------\n if (/^todo(_?write)?$|^todos$/i.test(name) || Array.isArray(obj.todos)) {\n const todos = (obj.todos ?? []) as Array<{ status?: string; content?: string }>;\n if (Array.isArray(todos)) {\n const done = todos.filter((t) => t.status === 'completed').length;\n const wip = todos.filter((t) => t.status === 'in_progress').length;\n const parts = [`${todos.length} todo${todos.length === 1 ? '' : 's'}`];\n if (done > 0) parts.push(`${done} done`);\n if (wip > 0) parts.push(`${wip} in-progress`);\n return parts.join(' · ');\n }\n }\n\n // ---- batch_tool_use / parallel_tool_use ---------------------------\n if (/batch|parallel/.test(name) || Array.isArray(obj.tool_uses) || Array.isArray(obj.calls)) {\n const list = (obj.tool_uses ?? obj.calls ?? obj.batch) as unknown[];\n if (Array.isArray(list)) {\n const subNames = new Set<string>();\n for (const item of list) {\n if (item && typeof item === 'object' && 'name' in item) {\n subNames.add(String((item as { name: unknown }).name));\n }\n }\n const preview = [...subNames].slice(0, 3).join(', ');\n const more = subNames.size > 3 ? ` +${subNames.size - 3}` : '';\n return `${list.length} sub-tool${list.length === 1 ? '' : 's'}${preview ? ` · ${preview}${more}` : ''}`;\n }\n }\n\n // ---- edit / str_replace / patch -----------------------------------\n if (/^(edit|str_replace|edit_file|patch)$/.test(name)) {\n const fp = pickPath(obj);\n const oldS = typeof obj.old_string === 'string' ? obj.old_string : '';\n const newS = typeof obj.new_string === 'string' ? obj.new_string : '';\n const oldLines = oldS ? oldS.split('\\n').length : 0;\n const newLines = newS ? newS.split('\\n').length : 0;\n return `edit ${fp || '(file)'}${oldLines || newLines ? ` (${oldLines} → ${newLines} lines)` : ''}`;\n }\n\n // ---- write / write_file / create_file -----------------------------\n if (/^(write|write_file|create_file|new_file)$/.test(name)) {\n const fp = pickPath(obj);\n const c = typeof obj.content === 'string' ? obj.content : '';\n const lines = c ? c.split('\\n').length : 0;\n return `write ${fp || '(file)'}${lines ? ` · ${lines} lines` : ''}`;\n }\n\n // ---- bash / shell / exec / run ------------------------------------\n if (/^(bash|shell|exec|run|run_command|run_shell)$/.test(name)) {\n const cmd = (obj.command ?? obj.cmd ?? obj.script) as string | undefined;\n if (typeof cmd === 'string') return `$ ${clip(cmd, 110)}`;\n }\n\n // ---- fetch / http / web -------------------------------------------\n if (/^(fetch|http|web|webfetch|curl|request)$/.test(name)) {\n const url = obj.url as string | undefined;\n if (typeof url === 'string') {\n const method = (obj.method as string | undefined) ?? 'GET';\n return `${method.toUpperCase()} ${clip(url, 100)}`;\n }\n }\n\n // ---- grep / search ------------------------------------------------\n if (/^(grep|search|ripgrep)$/.test(name)) {\n const pattern = obj.pattern as string | undefined;\n const scope = (obj.path ?? obj.glob ?? obj.type) as string | undefined;\n if (typeof pattern === 'string') {\n return scope ? `grep ${clip(pattern, 60)} in ${scope}` : `grep ${clip(pattern, 100)}`;\n }\n }\n\n // ---- glob / find --------------------------------------------------\n if (/^(glob|find)$/.test(name)) {\n const p = (obj.pattern ?? obj.glob) as string | undefined;\n if (typeof p === 'string') return `glob ${clip(p, 100)}`;\n }\n\n // ---- read with offset/limit --------------------------------------\n if (/^(read|read_file|cat)$/.test(name)) {\n const fp = pickPath(obj);\n const offset = obj.offset as number | undefined;\n const limit = obj.limit as number | undefined;\n if (fp && (typeof offset === 'number' || typeof limit === 'number')) {\n const start = offset ?? 0;\n const end = typeof limit === 'number' ? start + limit : '';\n return `read ${fp} (${start}…${end})`;\n }\n if (fp) return `read ${fp}`;\n }\n\n // ---- Fallback: pick the first non-empty \"headline\" string field --\n for (const k of FALLBACK_HEAD_FIELDS) {\n const v = obj[k];\n if (typeof v === 'string' && v.length > 0) {\n return `${k}: ${clip(v, 100)}`;\n }\n }\n // Last resort: compact JSON.\n const json = safeJson(input);\n return clip(json, 120);\n}\n\nfunction clip(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n - 1)}…` : s;\n}\n\nfunction pickPath(obj: Record<string, unknown>): string {\n const p = obj.file_path ?? obj.path ?? obj.filepath;\n return typeof p === 'string' ? p : '';\n}\n\nfunction safeJson(v: unknown): string {\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n","import { summarizeToolInput } from '@/lib/tool-summary';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport type { ChatMessage } from '@/stores';\nimport { useChatStore, useSessionStore, useUIStore } from '@/stores';\nimport { useConfigStore } from '@/stores';\nimport {\n Bot,\n Check,\n CheckCircle2,\n ChevronDown,\n ChevronRight,\n Clock,\n Copy,\n Download,\n FileCode2,\n Pencil,\n Pin,\n PinOff,\n RotateCcw,\n Terminal,\n User,\n XCircle,\n} from 'lucide-react';\nimport type React from 'react';\nimport { memo, useState } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport { DiffView, diffFromToolInput } from './DiffView';\nimport { ToolResult } from './ToolResult';\n\n/**\n * Tiny copy-to-clipboard helper used by the in-bubble copy buttons. Falls\n * back to the legacy `document.execCommand('copy')` path on insecure\n * (non-HTTPS, non-localhost) contexts where `navigator.clipboard` is\n * blocked — the WebUI is usually loaded from 127.0.0.1 over plain http so\n * we hit this fallback regularly.\n */\nasync function copyToClipboard(text: string): Promise<boolean> {\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return true;\n }\n } catch {\n /* fall through */\n }\n try {\n const ta = document.createElement('textarea');\n ta.value = text;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n const ok = document.execCommand('copy');\n document.body.removeChild(ta);\n return ok;\n } catch {\n return false;\n }\n}\n\n/**\n * ReactMarkdown component overrides. Fenced code blocks render with a\n * header strip (language label + copy button) and an internally scrollable\n * body so a 200-line snippet doesn't blow up the chat. Inline `code` stays\n * styled simply. Kept at module scope so the components object reference is\n * stable across renders.\n */\nconst markdownComponents = {\n code({\n inline,\n className,\n children,\n ...props\n }: {\n inline?: boolean;\n className?: string;\n children?: React.ReactNode;\n }) {\n const match = /language-(\\w+)/.exec(className ?? '');\n const codeText = String(children ?? '').replace(/\\n$/, '');\n if (inline || !match) {\n return (\n <code\n className={cn('rounded bg-muted/60 px-1.5 py-0.5 text-[0.85em] font-mono', className)}\n {...props}\n >\n {children}\n </code>\n );\n }\n return (\n <div className=\"not-prose relative my-3 rounded-lg border bg-muted/30 overflow-hidden\">\n <div className=\"flex items-center justify-between px-3 py-1.5 border-b bg-muted/40 text-xs\">\n <span className=\"font-mono text-muted-foreground\">{match[1]}</span>\n <CopyButton text={codeText} label=\"\" />\n </div>\n <pre className=\"overflow-x-auto p-3 text-xs leading-relaxed font-mono max-h-[40rem]\">\n <code>{codeText}</code>\n </pre>\n </div>\n );\n },\n};\n\nfunction CopyButton({\n text,\n className,\n label = 'Copy',\n}: {\n text: string;\n className?: string;\n label?: string;\n}) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n onClick={async (ev) => {\n ev.stopPropagation();\n const ok = await copyToClipboard(text);\n if (ok) {\n setCopied(true);\n setTimeout(() => setCopied(false), 1400);\n }\n }}\n className={cn(\n 'inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors',\n className,\n )}\n title={label}\n >\n {copied ? (\n <>\n <Check className=\"h-3 w-3 text-green-500\" />\n <span>Copied</span>\n </>\n ) : (\n <>\n <Copy className=\"h-3 w-3\" />\n <span>{label}</span>\n </>\n )}\n </button>\n );\n}\n\n/**\n * Spawn a browser download for the given text content. Uses a transient\n * Blob URL so the disk hit is browser-managed and we don't have to pump\n * the bytes through a backend route. Best-effort: no-op in non-browser\n * test environments.\n */\nfunction downloadTextFile(filename: string, text: string): void {\n if (typeof document === 'undefined') return;\n const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\n/**\n * Pick a reasonable file extension for a tool output dump based on the\n * tool name. JSON-shaped tools get `.json`, Read gets `.txt` (line numbers\n * baked in mean it isn't valid source anymore), bash gets `.log`,\n * everything else falls back to `.txt`.\n */\nfunction fileExtensionFor(toolName: string | undefined): string {\n const t = (toolName ?? '').toLowerCase();\n if (/bash|shell|exec|run/.test(t)) return 'log';\n if (/grep|search|find/.test(t)) return 'txt';\n return 'txt';\n}\n\n/**\n * Heuristic for \"the assistant just dumped a stack trace at me\". Returns\n * the index where the stack begins or -1 if none. We're tolerant of the\n * three common shapes: Node V8 (` at fn (file:line:col)`), Python\n * (`File \"x\", line N`), and Java (`at pkg.Class.method(File.java:N)`).\n * False positives are cheap (user just hits \"Show stack\" anyway).\n */\nfunction detectStackBoundary(text: string): number {\n // Look for the first line that matches a stack-frame pattern.\n const lines = text.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i]!;\n if (/^\\s*at\\s+\\S+.*\\(.*:\\d+:\\d+\\)\\s*$/.test(ln)) return i;\n if (/^\\s*at\\s+\\S+\\.\\S+\\(\\S+\\.java:\\d+\\)\\s*$/.test(ln)) return i;\n if (/^\\s+File \"[^\"]+\", line \\d+/.test(ln)) return i;\n }\n return -1;\n}\n\n/**\n * Wraps an error-flavoured assistant body and offers a \"Show/hide stack\"\n * toggle when a stack-trace boundary is detected. The lead message\n * (everything before the first frame) stays visible; the frames go\n * behind a click. Reduces the visual weight of a long traceback that\n * the user usually only needs once.\n */\nfunction ErrorBodyWithStack({ text }: { text: string }) {\n const idx = detectStackBoundary(text);\n const [open, setOpen] = useState(false);\n if (idx === -1) {\n return (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs leading-relaxed\">\n {text}\n </pre>\n );\n }\n const lines = text.split('\\n');\n const head = lines.slice(0, idx).join('\\n').trim();\n const stack = lines.slice(idx).join('\\n');\n const frameCount = stack.split('\\n').filter((l) => l.trim().length > 0).length;\n return (\n <div className=\"space-y-2\">\n {head && (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs leading-relaxed\">\n {head}\n </pre>\n )}\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"inline-flex items-center gap-1 text-xs text-destructive hover:text-destructive/80 font-medium\"\n >\n {open ? '▾' : '▸'} {open ? 'Hide' : 'Show'} stack trace ({frameCount} frame\n {frameCount === 1 ? '' : 's'})\n </button>\n {open && (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-[11px] leading-snug bg-destructive/5 border border-destructive/20 rounded p-2 max-h-80 overflow-auto\">\n {stack}\n </pre>\n )}\n </div>\n );\n}\n\n/**\n * Render a tool-call's input as a structured key/value table instead of a\n * raw JSON dump. Shallow scalars land on one row each; nested values land\n * as a collapsible row that expands into pretty-printed JSON. Falls back\n * to a single JSON block when the input is not an object (e.g. a number\n * or a string), which keeps the layout sensible for tools whose schema\n * isn't `Record<string, unknown>`.\n */\nfunction ToolInputView({ input }: { input: unknown }) {\n const [openKeys, setOpenKeys] = useState<Record<string, boolean>>({});\n if (input === null || input === undefined || typeof input !== 'object' || Array.isArray(input)) {\n return (\n <pre className=\"whitespace-pre-wrap break-all text-xs font-mono\">\n {JSON.stringify(input, null, 2)}\n </pre>\n );\n }\n const entries = Object.entries(input as Record<string, unknown>);\n if (entries.length === 0) {\n return <span className=\"text-xs text-muted-foreground italic\">(no params)</span>;\n }\n return (\n <div className=\"text-xs font-mono\">\n {entries.map(([k, v]) => {\n const isPrimitive =\n v === null ||\n v === undefined ||\n typeof v === 'string' ||\n typeof v === 'number' ||\n typeof v === 'boolean';\n if (isPrimitive) {\n const display =\n v === null\n ? 'null'\n : v === undefined\n ? 'undefined'\n : typeof v === 'string'\n ? v\n : String(v);\n // Long string values get their own line so the row stays usable.\n const isLong = typeof v === 'string' && (display.length > 80 || display.includes('\\n'));\n return (\n <div\n key={k}\n className={cn(\n 'py-0.5',\n isLong ? 'flex flex-col gap-0.5' : 'flex items-baseline gap-2',\n )}\n >\n <span className=\"text-muted-foreground shrink-0\">{k}:</span>\n <span\n className={cn(\n 'text-foreground',\n isLong\n ? 'whitespace-pre-wrap break-all bg-muted/40 rounded px-1.5 py-1'\n : 'truncate',\n typeof v === 'string' ? '' : 'text-amber-600 dark:text-amber-400',\n )}\n title={typeof v === 'string' && !isLong ? display : undefined}\n >\n {display}\n </span>\n </div>\n );\n }\n const open = !!openKeys[k];\n const summary = Array.isArray(v)\n ? `[${v.length} item${v.length === 1 ? '' : 's'}]`\n : `{${Object.keys(v as object).length} key${Object.keys(v as object).length === 1 ? '' : 's'}}`;\n return (\n <div key={k} className=\"py-0.5\">\n <button\n type=\"button\"\n onClick={() => setOpenKeys((p) => ({ ...p, [k]: !p[k] }))}\n className=\"flex items-baseline gap-2 hover:bg-muted/30 rounded px-1 -mx-1\"\n >\n <span className=\"text-muted-foreground/60 text-[10px]\">{open ? '▾' : '▸'}</span>\n <span className=\"text-muted-foreground\">{k}:</span>\n <span className=\"text-violet-600 dark:text-violet-400\">{summary}</span>\n </button>\n {open && (\n <pre className=\"ml-3 mt-1 whitespace-pre-wrap break-all text-[11px] bg-muted/40 rounded px-2 py-1.5\">\n {JSON.stringify(v, null, 2)}\n </pre>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n\ninterface MessageBubbleProps {\n message: ChatMessage;\n isFirst?: boolean;\n /** Render as a continuation of the previous item in the same agent turn.\n * Hides the avatar (replaces it with a transparent spacer so content\n * stays aligned) and the role label, and tightens the top margin — used\n * by ChatView's turn-bundling so text→tool→text reads as one flow\n * instead of three detached bubbles each with its own avatar column. */\n isContinuation?: boolean;\n}\n\nfunction formatToolDuration(ms: number): string {\n if (ms < 1000) return `${ms} ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(ms < 10_000 ? 2 : 1)}s`;\n const m = Math.floor(ms / 60_000);\n const s = Math.floor((ms % 60_000) / 1000);\n return `${m}m ${s}s`;\n}\n\nexport const MessageBubble = memo(function MessageBubble({\n message,\n isFirst = false,\n isContinuation = false,\n}: MessageBubbleProps) {\n const [expandedTools, setExpandedTools] = useState<Record<string, boolean>>({});\n const [editing, setEditing] = useState(false);\n const [editValue, setEditValue] = useState('');\n /** Per-bubble \"show raw markdown\" toggle for assistant messages. Useful\n * when the model emits weird-looking markdown and you want to verify\n * what it actually wrote (escape sequences, table cells, etc.) versus\n * how ReactMarkdown is rendering it. Hover-revealed toggle in the\n * footer; off by default so the chat stays pleasant to read. */\n const [showRaw, setShowRaw] = useState(false);\n const isUser = message.role === 'user';\n const isTool = message.role === 'tool';\n // (kept for symmetry — isAssistant referenced indirectly via !isUser && !isTool)\n void message.role;\n\n const truncateAfter = useChatStore((s) => s.truncateAfter);\n const addMessage = useChatStore((s) => s.addMessage);\n const setLoading = useChatStore((s) => s.setLoading);\n const isLoading = useChatStore((s) => s.isLoading);\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const pinnedIds = useUIStore((s) => s.pinnedIds);\n const togglePin = useUIStore((s) => s.togglePin);\n const compactMode = useUIStore((s) => s.compactMode);\n const isPinned = pinnedIds.includes(message.id);\n /** Per-token cost rates for the active provider/model, from setEnv on\n * session.start. We multiply against this message's usage to render a\n * USD figure beside the token counts — answers the \"what did this turn\n * cost?\" question without making the user open Settings. */\n const inputCost = useSessionStore((s) => s.inputCost);\n const outputCost = useSessionStore((s) => s.outputCost);\n const cacheReadCost = useSessionStore((s) => s.cacheReadCost);\n\n /** True when this is the most recent assistant message and we're not\n * in the middle of a run — eligible for the regenerate action. We\n * derive this from the store so the button only renders on one bubble\n * at a time without prop drilling. */\n const isLatestAssistant = (() => {\n if (message.role !== 'assistant' || isLoading) return false;\n const all = useChatStore.getState().messages;\n for (let i = all.length - 1; i >= 0; i--) {\n const m = all[i]!;\n if (m.role === 'assistant') return m.id === message.id;\n }\n return false;\n })();\n\n const regenerate = () => {\n // Find the user message that prompted this reply by walking backward.\n // Truncate to that user message (exclusive), then re-send its content.\n const all = useChatStore.getState().messages;\n const idx = all.findIndex((m) => m.id === message.id);\n if (idx === -1) return;\n let userIdx = -1;\n for (let i = idx - 1; i >= 0; i--) {\n if (all[i]!.role === 'user') {\n userIdx = i;\n break;\n }\n }\n if (userIdx === -1) return;\n const userMsg = all[userIdx]!;\n truncateAfter(userMsg.id);\n addMessage({ role: 'user', content: userMsg.content });\n setLoading(true);\n const client = getWSClient(wsUrl);\n client.sendMessage(userMsg.content);\n };\n\n const toggleTool = (id: string) => {\n setExpandedTools((prev) => ({ ...prev, [id]: !prev[id] }));\n };\n\n const startEdit = () => {\n setEditValue(message.content);\n setEditing(true);\n };\n const cancelEdit = () => {\n setEditing(false);\n setEditValue('');\n };\n const saveEdit = () => {\n const next = editValue.trim();\n if (!next) {\n cancelEdit();\n return;\n }\n // Wipe everything from this message forward, then send the corrected\n // prompt as a fresh user turn. The backend still has the original in\n // its server-side context.messages — that's an acceptable tradeoff for\n // a no-backend-change implementation; the user gets the rewind UX they\n // expect locally, and the model just sees a \"rephrased follow-up\".\n truncateAfter(message.id);\n addMessage({ role: 'user', content: next });\n setLoading(true);\n const client = getWSClient(wsUrl);\n client.sendMessage(next);\n setEditing(false);\n setEditValue('');\n };\n\n return (\n <div\n data-message-id={message.id}\n data-pinned={isPinned ? '1' : undefined}\n className={cn(\n 'group flex animate-message rounded-lg transition-shadow',\n compactMode ? 'gap-2' : 'gap-3',\n isUser ? 'flex-row-reverse' : 'flex-row',\n isPinned && 'ring-1 ring-amber-500/30 bg-amber-500/[0.02] px-1 -mx-1',\n )}\n >\n {/* Avatar — replaced by an invisible spacer in continuation mode so\n subsequent items in the same agent turn align under the first\n item's avatar without redrawing it. */}\n {isContinuation ? (\n <div className=\"flex-shrink-0 w-8 h-8\" aria-hidden />\n ) : (\n <div\n className={cn(\n 'flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center',\n 'ring-2 ring-offset-2 ring-offset-background',\n isUser\n ? 'bg-primary text-primary-foreground ring-primary/20'\n : isTool\n ? 'bg-secondary text-secondary-foreground ring-secondary/20'\n : 'bg-accent text-accent-foreground ring-accent/20',\n )}\n >\n {isUser ? (\n <User className=\"h-4 w-4\" />\n ) : isTool ? (\n <Terminal className=\"h-4 w-4\" />\n ) : (\n <Bot className=\"h-4 w-4\" />\n )}\n </div>\n )}\n\n {/* Content */}\n <div className={cn('flex flex-col gap-1.5 max-w-[85%]', isUser && 'items-end')}>\n {/* Role indicator for first message in a group. Suppressed for\n continuation items so the same label doesn't repeat for every\n text→tool→text segment of one agent turn. */}\n {isFirst && !isContinuation && (\n <span\n className={cn(\n 'text-xs font-medium px-1',\n isUser ? 'text-primary' : isTool ? 'text-secondary' : 'text-muted-foreground',\n )}\n >\n {isUser ? 'You' : isTool ? 'Tool' : 'Assistant'}\n </span>\n )}\n\n {/* Tool header */}\n {isTool && message.toolName && (\n <button\n type=\"button\"\n onClick={() => toggleTool(message.id)}\n className={cn(\n 'flex items-center gap-2 text-sm font-medium cursor-pointer select-none',\n 'hover:bg-muted/50 rounded-lg px-2 py-1 -mx-2 transition-colors',\n message.isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n <span className=\"text-muted-foreground/50\">\n {expandedTools[message.id] ? (\n <ChevronDown className=\"h-3 w-3\" />\n ) : (\n <ChevronRight className=\"h-3 w-3\" />\n )}\n </span>\n <Terminal className=\"h-3 w-3\" />\n <span className=\"font-mono\">{message.toolName}</span>\n {message.toolResult === undefined ? (\n // Pulsing dot while still running (matches the inline indicator below).\n <span className=\"h-1.5 w-1.5 rounded-full bg-amber-500 animate-pulse\" aria-hidden />\n ) : message.isError ? (\n <XCircle className=\"h-3 w-3 text-destructive\" />\n ) : (\n <CheckCircle2 className=\"h-3 w-3 text-green-500\" />\n )}\n {typeof message.toolDurationMs === 'number' && (\n <span className=\"text-xs text-muted-foreground tabular-nums font-normal\">\n {formatToolDuration(message.toolDurationMs)}\n </span>\n )}\n </button>\n )}\n\n {/* Message content */}\n <div\n className={cn(\n 'rounded-2xl',\n compactMode ? 'px-3 py-1.5' : 'px-4 py-3',\n isUser\n ? 'bg-primary text-primary-foreground rounded-br-md'\n : isTool\n ? message.isError\n ? 'bg-destructive/5 border border-destructive/20 text-destructive'\n : 'bg-muted/80 text-foreground'\n : 'bg-card border text-foreground',\n message.isError && !isTool && 'border-destructive/20',\n )}\n >\n {isTool ? (\n (() => {\n const expanded = !!expandedTools[message.id];\n const inputSummary =\n message.toolInput !== undefined\n ? summarizeToolInput(message.toolName, message.toolInput)\n : '';\n const lines = message.toolResult ? message.toolResult.split('\\n').length : 0;\n return (\n <div className=\"space-y-1\">\n {/* Collapsed: just a one-line input summary so parallel calls\n stay distinguishable. The output is hidden entirely —\n click the header (or \"Show details\" link) to expand. */}\n {inputSummary && !expanded && (\n <div className=\"text-xs text-muted-foreground font-mono truncate\">\n {inputSummary}\n </div>\n )}\n {/* Live progress feed while the tool is still running.\n Shown both collapsed and expanded so the user can see\n what's happening — the final result replaces this when\n tool.executed lands. */}\n {message.toolResult === undefined &&\n message.progressLines &&\n message.progressLines.length > 0 && (\n <div className=\"mt-1 rounded-md border border-amber-500/20 bg-amber-500/5 p-1.5 text-[11px] font-mono leading-snug max-h-32 overflow-auto\">\n {message.progressLines.slice(-6).map((line, i) => (\n <div\n // biome-ignore lint/suspicious/noArrayIndexKey: static progress lines\n key={i}\n className=\"truncate text-muted-foreground\"\n >\n {line}\n </div>\n ))}\n </div>\n )}\n {/* Expanded view. For the edit/write family of tools we\n replace the raw JSON dump with a real diff — the\n old_string/new_string pair (or just the new content\n for write) is the whole point of the call. Falls\n back to JSON for everything else. */}\n {expanded &&\n message.toolInput !== undefined &&\n (() => {\n const diffArgs = diffFromToolInput(message.toolName, message.toolInput);\n if (diffArgs) {\n return (\n <DiffView\n oldText={diffArgs.oldText}\n newText={diffArgs.newText}\n caption={diffArgs.caption}\n />\n );\n }\n return (\n <div className=\"p-3 bg-muted/50 rounded-lg overflow-x-auto\">\n <div className=\"flex items-center gap-1 text-muted-foreground mb-2 text-xs\">\n <Clock className=\"h-3 w-3\" />\n <span>Input</span>\n </div>\n <ToolInputView input={message.toolInput} />\n </div>\n );\n })()}\n {expanded &&\n message.toolResult !== undefined &&\n message.toolResult.length > 0 && (\n <div className=\"relative group/tool\">\n <ToolResult\n toolName={message.toolName}\n result={message.toolResult}\n isError={message.isError}\n />\n <div className=\"absolute top-1.5 right-1.5 flex items-center gap-1 opacity-0 group-hover/tool:opacity-100 transition-opacity\">\n <CopyButton\n text={message.toolResult}\n label=\"\"\n className=\"bg-background/80 border rounded px-1.5 py-0.5\"\n />\n {/* Download — only worth showing when the dump is\n big enough to actually save (>5 lines).\n Small grep hits are easier to just copy. */}\n {message.toolResult.split('\\n').length > 5 && (\n <button\n type=\"button\"\n onClick={(ev) => {\n ev.stopPropagation();\n const ext = fileExtensionFor(message.toolName);\n const base = (message.toolName ?? 'output')\n .replace(/[^a-z0-9_-]+/gi, '-')\n .toLowerCase();\n downloadTextFile(\n `${base}-${new Date().toISOString().replace(/[:.]/g, '-')}.${ext}`,\n message.toolResult ?? '',\n );\n }}\n className=\"inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground bg-background/80 border rounded px-1.5 py-0.5\"\n title=\"Download as file\"\n >\n <Download className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n </div>\n )}\n {expanded &&\n message.toolResult !== undefined &&\n message.toolResult.length === 0 && (\n <span className=\"text-xs text-muted-foreground italic\">(empty)</span>\n )}\n {/* Error case: keep the message inline even when collapsed,\n since silently hiding a failure is worse than the noise. */}\n {!expanded && message.isError && message.toolResult && (\n <div className=\"text-xs font-mono text-destructive truncate\">\n {message.toolResult.split('\\n')[0]}\n </div>\n )}\n {/* \"Show details\" toggle — only when there's anything to reveal. */}\n {((message.toolResult !== undefined && message.toolResult.length > 0) ||\n (message.toolInput !== undefined &&\n Object.keys((message.toolInput as object) ?? {}).length > 0)) && (\n <button\n type=\"button\"\n onClick={() => toggleTool(message.id)}\n className=\"text-xs text-muted-foreground hover:text-foreground transition-colors\"\n >\n {expanded\n ? 'Hide details'\n : `Show details${lines > 0 ? ` (${lines} line${lines === 1 ? '' : 's'})` : ''}`}\n </button>\n )}\n </div>\n );\n })()\n ) : editing && isUser ? (\n <div className=\"flex flex-col gap-2 min-w-[280px]\">\n <textarea\n value={editValue}\n onChange={(e) => setEditValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n cancelEdit();\n } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n saveEdit();\n }\n }}\n rows={Math.min(8, Math.max(2, editValue.split('\\n').length))}\n className=\"w-full resize-none rounded-md border bg-background text-foreground px-2 py-1.5 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-[10px] text-primary-foreground/60\">\n ⌘/Ctrl+Enter to save · Esc to cancel\n </span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={cancelEdit}\n className=\"text-xs px-2 py-0.5 rounded border border-primary-foreground/30 hover:bg-primary-foreground/10\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={saveEdit}\n disabled={!editValue.trim()}\n className=\"text-xs px-2 py-0.5 rounded bg-primary-foreground text-primary disabled:opacity-50\"\n >\n Save &amp; resend\n </button>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"text-sm leading-relaxed markdown-content\">\n {message.content ? (\n showRaw && message.role === 'assistant' ? (\n <pre className=\"whitespace-pre-wrap break-words font-mono text-xs leading-relaxed text-foreground/90 max-h-[40rem] overflow-auto\">\n {message.content}\n </pre>\n ) : message.role === 'assistant' && message.isError ? (\n <ErrorBodyWithStack text={message.content} />\n ) : (\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>\n {message.content}\n </ReactMarkdown>\n )\n ) : message.streaming ? (\n <span className=\"inline-block animate-pulse text-muted-foreground\">Typing...</span>\n ) : (\n <span className=\"text-muted-foreground italic\">No content</span>\n )}\n </div>\n )}\n </div>\n\n {/* Footer: timestamp + copy. Copy is hover-revealed so the chat\n stays clean by default. Tool bubbles get their own copy button\n on the output box, so we skip it here for them. */}\n <div\n className={cn('flex items-center gap-2 px-1', isUser ? 'flex-row-reverse' : 'flex-row')}\n >\n <span className=\"text-xs text-muted-foreground/50\">\n {new Date(message.timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n })}\n </span>\n {/* Per-message token attribution. Lands on the assistant bubble\n after provider.response with the input/output counts. Cache\n reads show up too when present — useful to see when prompt\n caching is actually saving cost. USD figure appears only when\n we know the per-token rates for the active model. */}\n {/* Run summary — attached by the run.result handler to the last\n assistant bubble of each turn. Renders as a single quiet line:\n iterations / tools / elapsed / $ delta. Hidden when nothing\n meaningful happened (e.g. plain-text reply with no tools). */}\n {message.runSummary && (\n <span\n className=\"text-[10px] text-muted-foreground/60 font-mono tabular-nums\"\n title={[\n `Iterations: ${message.runSummary.iterations}`,\n `Tool calls: ${message.runSummary.tools}`,\n `Elapsed: ${(message.runSummary.durationMs / 1000).toFixed(2)}s`,\n message.runSummary.costDelta > 0\n ? `Cost: $${message.runSummary.costDelta.toFixed(4)}`\n : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n >\n {message.runSummary.iterations} iter\n {message.runSummary.tools > 0\n ? ` · ${message.runSummary.tools} tool${message.runSummary.tools === 1 ? '' : 's'}`\n : ''}\n {' · '}\n {message.runSummary.durationMs < 60_000\n ? `${(message.runSummary.durationMs / 1000).toFixed(1)}s`\n : `${Math.floor(message.runSummary.durationMs / 60_000)}m ${Math.floor((message.runSummary.durationMs % 60_000) / 1000)}s`}\n {message.runSummary.costDelta > 0\n ? ` · $${message.runSummary.costDelta >= 0.01 ? message.runSummary.costDelta.toFixed(4) : message.runSummary.costDelta.toFixed(6).replace(/0+$/, '').replace(/\\.$/, '')}`\n : ''}\n </span>\n )}\n {message.usage &&\n (message.usage.input > 0 || message.usage.output > 0) &&\n (() => {\n const u = message.usage;\n const dollars =\n (u.input * inputCost + u.output * outputCost + (u.cacheRead ?? 0) * cacheReadCost) /\n 1_000_000;\n const haveCost = inputCost > 0 || outputCost > 0;\n const dollarStr =\n dollars >= 0.01\n ? `$${dollars.toFixed(4)}`\n : dollars > 0\n ? `$${dollars.toFixed(6).replace(/0+$/, '').replace(/\\.$/, '')}`\n : '';\n return (\n <span\n className=\"text-[10px] text-muted-foreground/60 font-mono tabular-nums\"\n title={[\n `Input: ${u.input.toLocaleString()}`,\n `Output: ${u.output.toLocaleString()}`,\n u.cacheRead ? `Cache read: ${u.cacheRead.toLocaleString()}` : '',\n haveCost ? `Cost: ${dollarStr}` : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n >\n {u.input.toLocaleString()}→{u.output.toLocaleString()}\n {u.cacheRead ? ` · ${u.cacheRead.toLocaleString()} ↺` : ''}\n {haveCost && dollarStr ? ` · ${dollarStr}` : ''}\n </span>\n );\n })()}\n {!isTool && message.content && !message.streaming && (\n <CopyButton\n text={message.content}\n label=\"\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n )}\n {/* Raw markdown toggle — assistant bubbles only. When the model\n emits literal markdown the user wants to inspect (escaped\n backticks, weird table formatting, raw HTML…), this flips the\n body from rendered → raw without leaving the chat. */}\n {message.role === 'assistant' && message.content && !message.streaming && (\n <button\n type=\"button\"\n onClick={() => setShowRaw((v) => !v)}\n title={showRaw ? 'Show rendered markdown' : 'Show raw markdown source'}\n className={cn(\n 'text-xs inline-flex items-center gap-1 transition-opacity',\n showRaw\n ? 'text-primary hover:text-primary/80 opacity-100'\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-foreground',\n )}\n >\n <FileCode2 className=\"h-3 w-3\" />\n <span>{showRaw ? 'Rendered' : 'Raw'}</span>\n </button>\n )}\n {/* Edit + regenerate — user messages only. Hidden while a run is\n in flight to avoid the textarea-vs-streaming-bubble race. */}\n {isUser && !editing && !isLoading && message.content && (\n <button\n type=\"button\"\n onClick={startEdit}\n title=\"Edit & resend this prompt\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity text-xs text-muted-foreground hover:text-foreground inline-flex items-center gap-1\"\n >\n <Pencil className=\"h-3 w-3\" />\n <span>Edit</span>\n </button>\n )}\n {/* Pin/unpin — assistant messages with real content. A pinned bubble\n gets a subtle amber ring + lands in the sidebar's Pinned panel\n so the user can jump back to it. Always visible once pinned so\n the user can tell at a glance it's bookmarked. */}\n {message.role === 'assistant' && message.content && !message.streaming && (\n <button\n type=\"button\"\n onClick={() => togglePin(message.id)}\n title={isPinned ? 'Unpin' : 'Pin this answer'}\n className={cn(\n 'text-xs inline-flex items-center gap-1 transition-opacity',\n isPinned\n ? 'text-amber-500 hover:text-amber-600 opacity-100'\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-foreground',\n )}\n >\n {isPinned ? <PinOff className=\"h-3 w-3\" /> : <Pin className=\"h-3 w-3\" />}\n <span>{isPinned ? 'Pinned' : 'Pin'}</span>\n </button>\n )}\n {/* Regenerate — only the most recent assistant reply, when the run\n has settled. Rewinds local state to the prompting user message\n and resends it, giving the user a one-click \"try again\". */}\n {isLatestAssistant && message.content && !message.streaming && (\n <button\n type=\"button\"\n onClick={regenerate}\n title=\"Regenerate this response\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity text-xs text-muted-foreground hover:text-foreground inline-flex items-center gap-1\"\n >\n <RotateCcw className=\"h-3 w-3\" />\n <span>Retry</span>\n </button>\n )}\n </div>\n </div>\n </div>\n );\n});\n","import { cn } from '@/lib/utils';\nimport { memo, useMemo } from 'react';\n\ninterface DiffViewProps {\n oldText: string;\n newText: string;\n /** Optional caption shown above the diff (file path, \"write\" vs \"edit\", etc.) */\n caption?: string;\n}\n\n/**\n * Tiny line-based diff renderer. Computes the longest-common-subsequence\n * between `oldText` and `newText` line arrays, then walks both sides\n * emitting `add`/`remove`/`context` rows. Context lines are de-emphasised\n * so the eye lands on the changes. Not as pretty as a Myers diff but it's\n * one short function with no deps and good enough for a chat preview.\n *\n * Limits: text > 5000 lines is shown without a diff (just a \"too large\"\n * note); that's a UI guard, not a hard limit on the underlying tool.\n */\nexport const DiffView = memo(function DiffView({ oldText, newText, caption }: DiffViewProps) {\n const rows = useMemo(() => computeDiff(oldText, newText), [oldText, newText]);\n\n if (rows === null) {\n return (\n <div className=\"text-xs text-muted-foreground italic px-3 py-2\">\n Diff omitted (file too large to render inline).\n </div>\n );\n }\n\n const adds = rows.filter((r) => r.kind === 'add').length;\n const dels = rows.filter((r) => r.kind === 'del').length;\n\n return (\n <div className=\"rounded-lg border bg-background/40 overflow-hidden text-xs\">\n <div className=\"flex items-center justify-between px-3 py-1.5 border-b bg-muted/40\">\n <span className=\"font-mono text-muted-foreground truncate\">{caption ?? 'diff'}</span>\n <span className=\"font-mono shrink-0\">\n <span className=\"text-emerald-600 dark:text-emerald-400\">+{adds}</span>\n <span className=\"text-muted-foreground mx-1\">·</span>\n <span className=\"text-rose-600 dark:text-rose-400\">-{dels}</span>\n </span>\n </div>\n <div className=\"font-mono leading-relaxed max-h-96 overflow-auto\">\n {rows.map((r, i) => (\n <div\n // biome-ignore lint/suspicious/noArrayIndexKey: static diff rows\n key={i}\n className={cn(\n 'flex',\n r.kind === 'add' && 'bg-emerald-500/10',\n r.kind === 'del' && 'bg-rose-500/10',\n )}\n >\n <span\n className={cn(\n 'w-6 shrink-0 text-center select-none',\n r.kind === 'add' && 'text-emerald-600 dark:text-emerald-400',\n r.kind === 'del' && 'text-rose-600 dark:text-rose-400',\n r.kind === 'ctx' && 'text-muted-foreground/40',\n )}\n >\n {r.kind === 'add' ? '+' : r.kind === 'del' ? '-' : ' '}\n </span>\n <pre\n className={cn(\n 'whitespace-pre-wrap break-all flex-1 px-2',\n r.kind === 'ctx' && 'text-muted-foreground/70',\n )}\n >\n {r.text || ' '}\n </pre>\n </div>\n ))}\n </div>\n </div>\n );\n});\n\ninterface DiffRow {\n kind: 'add' | 'del' | 'ctx';\n text: string;\n}\n\nconst MAX_LINES = 5000;\n\n/**\n * Returns null when either side exceeds the line cap. Otherwise walks the\n * LCS table to produce a clean diff. LCS table is O(n*m) memory — fine for\n * a file edit (usually <500 lines), prohibitive for a full file rewrite of\n * a giant generated file (hence the cap).\n */\nfunction computeDiff(oldText: string, newText: string): DiffRow[] | null {\n const a = oldText.split('\\n');\n const b = newText.split('\\n');\n if (a.length > MAX_LINES || b.length > MAX_LINES) return null;\n const n = a.length;\n const m = b.length;\n // dp[i][j] = LCS length of a[i..] and b[j..]\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n if (a[i] === b[j]) dp[i]![j] = dp[i + 1]![j + 1]! + 1;\n else dp[i]![j] = Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n const rows: DiffRow[] = [];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (a[i] === b[j]) {\n rows.push({ kind: 'ctx', text: a[i]! });\n i++;\n j++;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n rows.push({ kind: 'del', text: a[i]! });\n i++;\n } else {\n rows.push({ kind: 'add', text: b[j]! });\n j++;\n }\n }\n while (i < n) rows.push({ kind: 'del', text: a[i++]! });\n while (j < m) rows.push({ kind: 'add', text: b[j++]! });\n return rows;\n}\n\n/**\n * Recognise the WrongStack edit-family tools and pull a (oldText, newText,\n * caption) tuple out of their input. Returns null when the tool doesn't\n * carry diffable input. Caller uses this to decide whether to render the\n * DiffView at all.\n */\nexport function diffFromToolInput(\n toolName: string | undefined,\n input: unknown,\n): { oldText: string; newText: string; caption: string } | null {\n if (!toolName || typeof input !== 'object' || input === null) return null;\n const obj = input as Record<string, unknown>;\n const filePath = String(obj.file_path ?? obj.path ?? '');\n switch (toolName) {\n case 'edit':\n case 'str_replace':\n case 'edit_file': {\n const oldText = typeof obj.old_string === 'string' ? obj.old_string : '';\n const newText = typeof obj.new_string === 'string' ? obj.new_string : '';\n if (!oldText && !newText) return null;\n return { oldText, newText, caption: `edit ${filePath}` };\n }\n case 'write':\n case 'write_file':\n case 'create_file': {\n const content = typeof obj.content === 'string' ? obj.content : '';\n // For a fresh write there's no \"old\" — treat as additive so the\n // viewer shows everything green.\n return { oldText: '', newText: content, caption: `write ${filePath} (new)` };\n }\n default:\n return null;\n }\n}\n","import { cn } from '@/lib/utils';\nimport { ChevronDown, ChevronRight, ChevronsDown, ChevronsUp } from 'lucide-react';\nimport { memo, useMemo, useState } from 'react';\n\n/** When a tool dumps hundreds of lines of output, the chat turns into a\n * scroll-wall. This threshold gates the auto-collapse: anything longer\n * shows the first ~LONG_PEEK_LINES with a \"Show all N more\" toggle. */\nconst LONG_OUTPUT_THRESHOLD = 25;\nconst LONG_PEEK_LINES = 12;\n\n/**\n * Render `text` as a monospace block, auto-collapsing when it exceeds\n * LONG_OUTPUT_THRESHOLD lines. The first LONG_PEEK_LINES stay visible;\n * the rest expand on click. Keeping the wrap class configurable lets the\n * `numbered` path use `whitespace-pre` (no wrap) and the bash/plain path\n * use `whitespace-pre-wrap break-all` (wrap and break long URLs/paths).\n */\nfunction CollapsibleText({\n text,\n isError,\n className,\n wrapClass,\n showLineNumbers,\n}: {\n text: string;\n isError?: boolean;\n className?: string;\n wrapClass: string;\n /** Whether to render a left gutter with line numbers when the output is\n * long. Off for the numbered/Read-tool path (which already prefixes its\n * own line numbers in the content) and for wrap-mode bodies where a\n * fixed gutter can't stay aligned with wrapped lines. */\n showLineNumbers?: boolean;\n}) {\n const lines = useMemo(() => text.split('\\n'), [text]);\n const isLong = lines.length > LONG_OUTPUT_THRESHOLD;\n const [expanded, setExpanded] = useState(!isLong);\n const shown = expanded ? text : lines.slice(0, LONG_PEEK_LINES).join('\\n');\n // Only emit the gutter when the body won't wrap (alignment would break\n // otherwise) AND the user actually expanded it / it's not super short.\n const renderGutter = !!showLineNumbers && isLong && expanded;\n return (\n <div className={cn('rounded-md border bg-background/40 overflow-hidden', className)}>\n {renderGutter ? (\n // Force `whitespace-pre` inside the gutter view so the line numbers\n // stay 1:1 with content rows. Long horizontal lines scroll within\n // the shared overflow container rather than wrapping (which would\n // break alignment between the gutter and the body).\n <div className=\"flex max-h-96 overflow-auto\">\n <pre\n aria-hidden\n className=\"text-xs font-mono leading-[1.4] py-2 pl-2 pr-2 text-muted-foreground/50 select-none border-r border-border/40 bg-muted/20 tabular-nums text-right whitespace-pre shrink-0\"\n >\n {lines.map((_, i) => `${i + 1}`).join('\\n')}\n </pre>\n <pre\n className={cn(\n 'text-xs font-mono leading-[1.4] py-2 px-2 flex-1 whitespace-pre',\n isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n {shown}\n </pre>\n </div>\n ) : (\n <pre\n className={cn(\n 'text-xs font-mono p-2 max-h-96 overflow-auto',\n wrapClass,\n isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n {shown}\n </pre>\n )}\n {isLong && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"w-full flex items-center justify-center gap-1 px-2 py-1 border-t bg-muted/30 text-[11px] text-muted-foreground hover:text-foreground transition-colors\"\n >\n {expanded ? (\n <>\n <ChevronsUp className=\"h-3 w-3\" />\n Collapse to first {LONG_PEEK_LINES} lines\n </>\n ) : (\n <>\n <ChevronsDown className=\"h-3 w-3\" />\n Show all {lines.length} lines ({lines.length - LONG_PEEK_LINES} more)\n </>\n )}\n </button>\n )}\n </div>\n );\n}\n\ninterface Props {\n toolName?: string;\n result: string;\n isError?: boolean;\n className?: string;\n}\n\n/**\n * Decides which renderer to use for a tool result based on its shape /\n * tool name. The goal is to make output *legible at a glance*:\n * - Read tool with leading line numbers stays line-numbered, monospace.\n * - Bash output gets a \"exit code N\" footer split from stdout.\n * - Valid JSON gets pretty-printed and collapsible by default.\n * - Everything else falls back to raw monospace.\n */\nexport const ToolResult = memo(function ToolResult({\n toolName,\n result,\n isError,\n className,\n}: Props) {\n const shape = useMemo(() => detectShape(toolName, result), [toolName, result]);\n\n if (shape.kind === 'json') {\n return <JsonResult value={shape.value} isError={isError} className={className} />;\n }\n if (shape.kind === 'numbered') {\n return (\n <CollapsibleText\n text={result}\n isError={isError}\n className={className}\n wrapClass=\"whitespace-pre\"\n />\n );\n }\n if (shape.kind === 'bash') {\n return (\n <div className={cn('rounded-md border bg-background/40 overflow-hidden', className)}>\n {shape.stdout && (\n <CollapsibleText\n text={shape.stdout}\n isError={isError}\n // Nested CollapsibleText already adds border/bg; strip them here\n // so we get one outer frame, not two.\n className=\"border-0 rounded-none bg-transparent\"\n wrapClass=\"whitespace-pre-wrap break-all\"\n showLineNumbers\n />\n )}\n {(shape.exitCode !== undefined || shape.duration) && (\n <div\n className={cn(\n 'flex items-center gap-3 text-[11px] px-2 py-1 border-t bg-muted/30 tabular-nums',\n shape.exitCode && shape.exitCode !== 0 ? 'text-destructive' : 'text-muted-foreground',\n )}\n >\n {shape.exitCode !== undefined && (\n <span>\n exit code: <span className=\"font-mono\">{shape.exitCode}</span>\n </span>\n )}\n {shape.duration && <span>{shape.duration}</span>}\n </div>\n )}\n </div>\n );\n }\n return (\n <CollapsibleText\n text={result}\n isError={isError}\n className={className}\n wrapClass=\"whitespace-pre-wrap break-all\"\n showLineNumbers\n />\n );\n});\n\ninterface Shape {\n kind: 'numbered' | 'json' | 'bash' | 'plain';\n /** For JSON results. */\n value?: unknown;\n /** For bash-shape results. */\n stdout?: string;\n exitCode?: number;\n duration?: string;\n}\n\nfunction detectShape(toolName: string | undefined, result: string): Shape {\n const trimmed = result.trim();\n // ---- Read tool: lines like \" 42→ const foo = …\" -------------\n if (/^\\s*\\d+→/m.test(result.slice(0, 200))) {\n return { kind: 'numbered' };\n }\n // ---- JSON ---------------------------------------------------------\n if (\n (trimmed.startsWith('{') && trimmed.endsWith('}')) ||\n (trimmed.startsWith('[') && trimmed.endsWith(']'))\n ) {\n try {\n const parsed = JSON.parse(trimmed);\n if (typeof parsed === 'object' && parsed !== null) {\n return { kind: 'json', value: parsed };\n }\n } catch {\n /* fall through */\n }\n }\n // ---- Bash-ish -----------------------------------------------------\n const isBashTool = !!toolName && /^(bash|shell|exec|run)/i.test(toolName);\n // Many bash wrappers emit a trailing \"exit code: N\" or \"[exit 1]\" line.\n const exitMatch = result.match(/(?:^|\\n)\\s*(?:\\[?exit(?:\\s*code)?\\]?\\s*[:=]?\\s*)(\\d+)\\s*$/i);\n const durMatch = result.match(/(?:^|\\s)(\\d+\\s*ms|\\d+\\.\\d+s)\\s*$/i);\n if (isBashTool || exitMatch) {\n let stdout = result;\n if (exitMatch) stdout = result.slice(0, exitMatch.index).trimEnd();\n return {\n kind: 'bash',\n stdout,\n exitCode: exitMatch ? Number(exitMatch[1]) : undefined,\n duration: durMatch?.[1],\n };\n }\n return { kind: 'plain' };\n}\n\nfunction JsonResult({\n value,\n isError,\n className,\n}: {\n value: unknown;\n isError?: boolean;\n className?: string;\n}) {\n const pretty = useMemo(() => {\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n }, [value]);\n const lineCount = pretty.split('\\n').length;\n const [expanded, setExpanded] = useState(lineCount < 30);\n return (\n <div\n className={cn(\n 'rounded-md border bg-background/40 overflow-hidden',\n isError && 'border-destructive/40',\n className,\n )}\n >\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"w-full flex items-center justify-between px-2 py-1 border-b bg-muted/30 text-[11px] text-muted-foreground hover:text-foreground\"\n >\n <span className=\"flex items-center gap-1\">\n {expanded ? <ChevronDown className=\"h-3 w-3\" /> : <ChevronRight className=\"h-3 w-3\" />}\n <span className=\"font-mono\">JSON · {lineCount} lines</span>\n </span>\n <span>{expanded ? 'collapse' : 'expand'}</span>\n </button>\n {expanded && (\n <pre\n className={cn(\n 'text-xs font-mono whitespace-pre p-2 max-h-96 overflow-auto',\n isError ? 'text-destructive' : 'text-foreground',\n )}\n >\n {pretty}\n </pre>\n )}\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useSessionStore } from '@/stores';\nimport { Check, ChevronDown } from 'lucide-react';\nimport { useEffect, useRef, useState } from 'react';\n\n/**\n * Pill-shaped mode chip in the topbar. Click to drop a small picker with\n * the available modes (fetched lazily on first open from the backend's\n * modes.list handler). Selecting a mode broadcasts a session.start update\n * so the chip and the system-prompt context reflect the change without a\n * page reload.\n */\nexport function ModePicker() {\n const mode = useSessionStore((s) => s.mode);\n const modes = useSessionStore((s) => s.modes);\n const { listModes, switchMode } = useWebSocket();\n const [open, setOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // Lazy-fetch the modes list the first time the user clicks the chip\n // (and refresh on every open in case the user installed a new mode\n // through some other surface).\n if (open) listModes();\n }, [open, listModes]);\n\n // Close on outside click / Escape.\n useEffect(() => {\n if (!open) return;\n const onClick = (e: MouseEvent) => {\n if (!rootRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') setOpen(false);\n };\n document.addEventListener('mousedown', onClick);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('mousedown', onClick);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n const items =\n modes.length > 0\n ? modes\n : [{ id: 'default', name: 'Default', description: 'Standard agent behaviour' }];\n\n return (\n <div ref={rootRef} className=\"relative shrink-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n 'flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium',\n 'bg-accent/40 text-foreground hover:bg-accent transition-colors border border-transparent hover:border-primary/30',\n )}\n title=\"Active mode\"\n >\n mode: <span className=\"font-mono\">{mode || 'default'}</span>\n <ChevronDown className=\"h-3 w-3 opacity-60\" />\n </button>\n {open && (\n <div className=\"absolute top-full left-0 mt-1 w-64 rounded-md border bg-popover shadow-lg z-30 py-1\">\n <div className=\"px-3 py-1.5 text-[10px] uppercase tracking-wider text-muted-foreground border-b\">\n Mode\n </div>\n {items.map((m) => (\n <button\n key={m.id}\n type=\"button\"\n onClick={() => {\n switchMode(m.id);\n setOpen(false);\n }}\n className={cn(\n 'w-full text-left px-3 py-2 hover:bg-accent/40 flex items-start gap-2',\n m.id === mode && 'bg-accent/30',\n )}\n >\n <Check\n className={cn(\n 'h-3.5 w-3.5 mt-0.5 shrink-0',\n m.id === mode ? 'opacity-100 text-primary' : 'opacity-0',\n )}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-xs font-mono\">{m.id}</div>\n {m.description && (\n <div className=\"text-[11px] text-muted-foreground leading-snug\">\n {m.description}\n </div>\n )}\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport { useChatStore, useUIStore } from '@/stores';\nimport { ArrowDown, ArrowUp, Search, X } from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Ctrl+F overlay that searches the current chat transcript. Hits are\n * counted as the user types; ↑/↓ step between hits and Enter scrolls the\n * highlighted message into view. The actual highlight is handed off via a\n * DOM data attribute so we don't need to invasively rewrap message\n * rendering — see ChatView for where the active hit gets the highlight\n * class applied.\n */\nexport function SearchOverlay() {\n const open = useUIStore((s) => s.searchOpen);\n const setOpen = useUIStore((s) => s.setSearchOpen);\n const query = useUIStore((s) => s.searchQuery);\n const setQuery = useUIStore((s) => s.setSearchQuery);\n const messages = useChatStore((s) => s.messages);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const [activeHit, setActiveHit] = useState(0);\n\n useEffect(() => {\n if (open) requestAnimationFrame(() => inputRef.current?.focus());\n }, [open]);\n\n const hits = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return [] as string[];\n return messages\n .filter((m) => {\n if (m.role === 'tool') {\n return (\n (m.toolName ?? '').toLowerCase().includes(q) ||\n (m.toolResult ?? '').toLowerCase().includes(q) ||\n JSON.stringify(m.toolInput ?? '')\n .toLowerCase()\n .includes(q)\n );\n }\n return m.content.toLowerCase().includes(q);\n })\n .map((m) => m.id);\n }, [messages, query]);\n\n useEffect(() => {\n if (activeHit >= hits.length) setActiveHit(0);\n }, [hits, activeHit]);\n\n // Paint every match of the current query inside chat bubbles using the\n // CSS Custom Highlights API. We walk the text nodes under every\n // `[data-message-id]` element, build Range objects per hit, then register\n // two highlights: `chat-search` covers everything, `chat-search-active`\n // covers only the message currently navigated to (so the user can see\n // where they are in the list). The registry is cleared on unmount and on\n // every query change so stale ranges don't linger.\n useEffect(() => {\n // Feature-detect — falls back to silent no-op on older browsers; the\n // ring-flash navigation behaviour below still works.\n const win = window as unknown as {\n CSS?: { highlights?: Map<string, unknown> };\n Highlight?: new (...ranges: Range[]) => unknown;\n };\n const highlights = win.CSS?.highlights;\n const HighlightCtor = win.Highlight;\n if (!highlights || !HighlightCtor) return;\n const clear = () => {\n highlights.delete('chat-search');\n highlights.delete('chat-search-active');\n };\n const q = query.trim();\n if (!q || !open) {\n clear();\n return;\n }\n const lcQuery = q.toLowerCase();\n const allRanges: Range[] = [];\n const activeRanges: Range[] = [];\n const activeId = hits[activeHit];\n for (const el of document.querySelectorAll('[data-message-id]')) {\n const id = (el as HTMLElement).dataset.messageId;\n const isActive = id === activeId;\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let node = walker.nextNode() as Text | null;\n while (node) {\n const text = node.nodeValue ?? '';\n if (text.length > 0) {\n const lc = text.toLowerCase();\n let from = 0;\n while (from <= lc.length - lcQuery.length) {\n const at = lc.indexOf(lcQuery, from);\n if (at === -1) break;\n const range = document.createRange();\n range.setStart(node, at);\n range.setEnd(node, at + lcQuery.length);\n allRanges.push(range);\n if (isActive) activeRanges.push(range);\n from = at + lcQuery.length;\n }\n }\n node = walker.nextNode() as Text | null;\n }\n }\n if (allRanges.length > 0) {\n highlights.set('chat-search', new HighlightCtor(...allRanges) as never);\n } else {\n highlights.delete('chat-search');\n }\n if (activeRanges.length > 0) {\n highlights.set('chat-search-active', new HighlightCtor(...activeRanges) as never);\n } else {\n highlights.delete('chat-search-active');\n }\n return clear;\n }, [query, hits, activeHit, open]);\n\n useEffect(() => {\n const id = hits[activeHit];\n if (!id) return;\n const el = document.querySelector(`[data-message-id=\"${id}\"]`);\n if (el) {\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n }, [hits, activeHit]);\n\n if (!open) return null;\n\n const step = (dir: 1 | -1) => {\n if (hits.length === 0) return;\n setActiveHit((i) => (i + dir + hits.length) % hits.length);\n };\n\n return (\n <div className=\"absolute top-2 right-4 z-30 w-[28rem] max-w-[calc(100%-2rem)] rounded-lg border bg-popover shadow-xl\">\n <div className=\"flex items-center gap-2 px-3 py-2\">\n <Search className=\"h-4 w-4 text-muted-foreground shrink-0\" />\n <input\n ref={inputRef}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n setOpen(false);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n step(e.shiftKey ? -1 : 1);\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n step(1);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n step(-1);\n }\n }}\n placeholder=\"Search in chat…\"\n className=\"flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground\"\n />\n <span className=\"text-xs text-muted-foreground tabular-nums shrink-0\">\n {hits.length === 0 ? (query ? '0' : '') : `${activeHit + 1} / ${hits.length}`}\n </span>\n <button\n type=\"button\"\n onClick={() => step(-1)}\n disabled={hits.length === 0}\n className={cn(\n 'p-1 rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed',\n )}\n title=\"Previous hit\"\n >\n <ArrowUp className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => step(1)}\n disabled={hits.length === 0}\n className={cn(\n 'p-1 rounded hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed',\n )}\n title=\"Next hit\"\n >\n <ArrowDown className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"p-1 rounded hover:bg-muted text-muted-foreground\"\n title=\"Close (Esc)\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport type { ChatMessage } from '@/stores';\nimport { CheckCircle2, ChevronDown, ChevronRight, Loader2, Terminal, XCircle } from 'lucide-react';\nimport { memo, useState } from 'react';\nimport { MessageBubble } from './MessageBubble';\n\ninterface ToolGroupProps {\n /** A run of consecutive tool messages (>=1). Rendered as one chip while\n * collapsed, expanded into the usual MessageBubble list on click. */\n tools: ChatMessage[];\n /** Force-expand the latest group so newly-running tools are visible by\n * default (otherwise users see \"5 tool calls\" pop in and have to click to\n * understand what's running). Older groups stay collapsed. */\n defaultOpen?: boolean;\n /** Render as a continuation of the previous item in the same agent turn —\n * hides the avatar column (replaced with a transparent spacer) and the\n * group's chrome stitches into the same flow as the surrounding text /\n * tool items instead of standing alone. */\n isContinuation?: boolean;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(ms < 10_000 ? 2 : 1)}s`;\n const m = Math.floor(ms / 60_000);\n const s = Math.floor((ms % 60_000) / 1000);\n return `${m}m${s}s`;\n}\n\nexport const ToolGroup = memo(function ToolGroup({\n tools,\n defaultOpen = false,\n isContinuation = false,\n}: ToolGroupProps) {\n const [open, setOpen] = useState(defaultOpen);\n\n // Single tool? Render as a normal bubble — grouping overhead is just noise.\n if (tools.length === 1) {\n return <MessageBubble message={tools[0]!} isFirst isContinuation={isContinuation} />;\n }\n\n const running = tools.filter((t) => t.toolResult === undefined).length;\n const errored = tools.filter((t) => t.isError).length;\n const totalMs = tools.reduce((acc, t) => acc + (t.toolDurationMs ?? 0), 0);\n\n // Show the first few tool names so the user has a hint of what's inside\n // without expanding (\"Read, Grep, Bash …\").\n const names = Array.from(new Set(tools.map((t) => t.toolName).filter(Boolean) as string[]));\n const preview = names.slice(0, 3).join(', ');\n const more = names.length > 3 ? ` +${names.length - 3}` : '';\n\n return (\n <div className=\"flex gap-3 animate-message\">\n {isContinuation ? (\n <div className=\"flex-shrink-0 w-8 h-8\" aria-hidden />\n ) : (\n <div className=\"flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center bg-secondary text-secondary-foreground ring-2 ring-offset-2 ring-offset-background ring-secondary/20\">\n <Terminal className=\"h-4 w-4\" />\n </div>\n )}\n\n <div className=\"flex flex-col gap-1.5 max-w-[85%] flex-1 min-w-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n 'flex items-center gap-2 text-sm font-medium cursor-pointer select-none',\n 'hover:bg-muted/50 rounded-lg px-2 py-1.5 -mx-2 transition-colors',\n 'border border-border/40 bg-muted/30',\n )}\n >\n {open ? (\n <ChevronDown className=\"h-3.5 w-3.5 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"h-3.5 w-3.5 text-muted-foreground\" />\n )}\n <Terminal className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <span className=\"font-mono text-xs\">\n {tools.length} tool call{tools.length === 1 ? '' : 's'}\n </span>\n {running > 0 ? (\n <Loader2 className=\"h-3 w-3 animate-spin text-amber-500\" />\n ) : errored > 0 ? (\n <XCircle className=\"h-3 w-3 text-destructive\" />\n ) : (\n <CheckCircle2 className=\"h-3 w-3 text-green-500\" />\n )}\n {totalMs > 0 && (\n <span className=\"text-xs text-muted-foreground tabular-nums font-normal\">\n {formatDuration(totalMs)}\n </span>\n )}\n {preview && (\n <span className=\"text-xs text-muted-foreground/80 font-mono truncate\">\n · {preview}\n {more}\n </span>\n )}\n </button>\n\n {open && (\n <div className=\"space-y-2 pl-3 border-l-2 border-border/40 ml-2\">\n {tools.map((tool) => (\n <MessageBubble key={tool.id} message={tool} isFirst={false} />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n});\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore, useHistoryStore, useSessionStore, useUIStore } from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n ArchiveRestore,\n ArrowRight,\n Bug,\n Clock,\n KeyRound,\n Keyboard,\n Search,\n Sparkles,\n Wrench,\n Zap,\n} from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\ninterface PromptCard {\n icon: LucideIcon;\n title: string;\n hint: string;\n tone: string;\n prompts: string[];\n}\n\nconst CARDS: PromptCard[] = [\n {\n icon: Search,\n title: 'Explore',\n hint: 'Understand the code before changing it',\n tone: 'text-blue-600 dark:text-blue-400 bg-blue-500/10 border-blue-500/20',\n prompts: [\n 'Walk me through this codebase: list the top-level packages, the role of each, and how they depend on one another. Highlight any cross-cutting abstractions I should understand first.',\n \"Find every place where the WebSocket protocol is defined or consumed (server handlers, client send/receive, type contracts). Show me the message-type table and any gaps where the type isn't enforced.\",\n 'Locate the entrypoint that boots the agent for normal runs. Trace the call chain from CLI launch all the way to the first model call — what middleware, hooks, and tools are wired along the way?',\n ],\n },\n {\n icon: Wrench,\n title: 'Build',\n hint: 'Add a feature end-to-end',\n tone: 'text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 border-emerald-500/20',\n prompts: [\n \"Add a slash command `/export` that dumps the current chat (messages + tool calls + usage) as a markdown file to ~/.wrongstack/exports/ and surfaces a 'saved to X' toast. Wire backend + ws-client + slash menu entry.\",\n 'Create a notification toast system (Zustand store + portal-rendered <Toast/> component) and migrate every existing `key.operation_result` success/failure message to use it instead of dropping into chat.',\n 'Add structured JSON logging to the WebSocket server: each handler logs `{ts, level, type, payload}` to ~/.wrongstack/logs/webui.jsonl. Make it tail-friendly and respect the existing log.level config.',\n ],\n },\n {\n icon: Bug,\n title: 'Debug',\n hint: 'Track a problem to its root cause',\n tone: 'text-amber-600 dark:text-amber-400 bg-amber-500/10 border-amber-500/20',\n prompts: [\n 'Something feels off with token accounting — the cost chip and the per-message tally drift apart over a long session. Reproduce locally if you can, then propose a fix. Start by reading the TokenCounter + provider.response handler.',\n 'The WebSocket sometimes silently stops streaming text mid-response on lossy networks. Check the reconnect logic, message queue, and how we handle a half-completed text_delta stream after a reconnect.',\n 'I want to know why ctx % climbs so fast in long sessions. Use the existing /debug context breakdown to identify the largest contributors and propose three concrete pruning strategies (with token savings estimates).',\n ],\n },\n {\n icon: Sparkles,\n title: 'Refactor',\n hint: 'Clean up without breaking behavior',\n tone: 'text-violet-600 dark:text-violet-400 bg-violet-500/10 border-violet-500/20',\n prompts: [\n 'Find duplicated logic between packages/cli/src/webui-server.ts and packages/webui/src/server/index.ts. Extract the shared bits into a single source of truth (likely the webui package) and update the CLI to import it.',\n \"Look at the Zustand stores in packages/webui/src/stores/index.ts — anything that should be a derived selector instead of stored state? Anything persisted that shouldn't be? Propose a leaner shape and migration plan.\",\n \"Audit the slash command dispatcher: pull each command's run logic into its own module under packages/webui/src/commands/, make the registry data-driven, and ensure /help auto-generates from the registry (not a hardcoded list).\",\n ],\n },\n];\n\nconst SLASH_REFS: Array<{ name: string; hint: string }> = [\n { name: '/help', hint: 'list every slash command' },\n { name: '/diag', hint: 'runtime diagnostics' },\n { name: '/stats', hint: 'tokens · cache · cost · elapsed' },\n { name: '/tools', hint: 'show registered tools' },\n { name: '/memory', hint: 'show remembered notes' },\n { name: '/compact', hint: 'shrink context' },\n { name: '/clear', hint: 'wipe current context' },\n { name: '/new', hint: 'fresh session' },\n];\n\nfunction fillTextarea(text: string): void {\n const ta = document.querySelector('textarea');\n if (!ta) return;\n const setter = Object.getOwnPropertyDescriptor(\n window.HTMLTextAreaElement.prototype,\n 'value',\n )?.set;\n setter?.call(ta, text);\n ta.dispatchEvent(new Event('input', { bubbles: true }));\n ta.focus();\n}\n\nexport function WelcomeScreen() {\n const { projectName } = useSessionStore();\n const { provider, model } = useConfigStore();\n const wsConnected = useConfigStore((s) => s.wsConnected);\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const setCurrentView = useUIStore((s) => s.setCurrentView);\n /** Saved-provider count. We subscribe directly to `providers.saved`\n * because SettingsPanel is the canonical owner of that state but isn't\n * always mounted (only when the user is on the Settings tab). undefined\n * means \"not yet fetched\" — we skip the CTA in that state to avoid a\n * flash on first paint. */\n const [savedCount, setSavedCount] = useState<number | undefined>(undefined);\n useEffect(() => {\n if (!wsConnected) return;\n const client = getWSClient(wsUrl);\n const off = client.on('providers.saved', (msg: WSServerMessage) => {\n const p = msg.payload as { providers: unknown[] };\n setSavedCount(p.providers?.length ?? 0);\n });\n client.listSavedProviders();\n return () => {\n off();\n };\n }, [wsConnected, wsUrl]);\n /** Recent prompts harvested from the user's typing history. The same\n * store that powers ↑/↓ recall in the input — surfacing them here turns\n * a blank welcome screen into a useful \"pick up where you left off\"\n * surface, without any backend round-trip. Limited to 6 so it doesn't\n * dominate the page. */\n const promptHistory = useUIStore((s) => s.promptHistory);\n const recentPrompts = promptHistory.slice(0, 6);\n /** Recent sessions surfaced as one-click resume buttons. Drives the\n * \"pick back up\" workflow without sending the user to the History tab.\n * We fetch on first paint when connected; the listing is otherwise\n * populated by the History tab on demand. */\n const { listSessions, resumeSession } = useWebSocket();\n const historyEntries = useHistoryStore((s) => s.entries);\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only\n useEffect(() => {\n if (wsConnected && historyEntries.length === 0) listSessions(10);\n // Intentionally only fire on first connect — refreshing on every\n // historyEntries change would loop after the response lands.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [wsConnected]);\n const sessionNicknames = useUIStore((s) => s.sessionNicknames);\n const recentSessions = historyEntries.filter((e) => !e.isCurrent).slice(0, 4);\n\n return (\n <div className=\"flex flex-col gap-8 py-8 px-2 max-w-5xl mx-auto w-full\">\n {/* Hero */}\n <div className=\"flex flex-col items-center text-center gap-3\">\n <div className=\"relative\">\n <div className=\"w-14 h-14 rounded-2xl bg-gradient-to-br from-primary via-primary to-primary/60 flex items-center justify-center shadow-lg shadow-primary/20\">\n <Zap className=\"h-7 w-7 text-primary-foreground\" />\n </div>\n <div className=\"absolute -inset-3 bg-gradient-to-r from-transparent via-primary/10 to-transparent animate-pulse rounded-full -z-10\" />\n </div>\n <div>\n <h2 className=\"text-2xl font-semibold tracking-tight\">\n Where do you want to start\n {projectName ? (\n <>\n {' in '}\n <span className=\"text-primary\">{projectName}</span>\n </>\n ) : (\n ''\n )}\n ?\n </h2>\n <p className=\"text-sm text-muted-foreground mt-2 max-w-2xl mx-auto leading-relaxed\">\n WrongStack is connected to your project and ready to read, edit, run commands, search\n the codebase, track todos, and remember context across sessions. Pick a starting prompt\n below, write your own, or type <span className=\"font-mono text-foreground/80\">/</span>{' '}\n for the full command palette.\n </p>\n {provider && model && (\n <p className=\"text-xs text-muted-foreground/70 mt-2 font-mono\">\n {provider} / {model}\n </p>\n )}\n </div>\n </div>\n\n {/* No-keys CTA — shown only when the backend is connected and the\n providers.saved response confirmed zero registered keys. Lands\n above the prompt cards because clicking those won't work until\n the user adds a key. Quietly disappears once at least one\n provider is registered. */}\n {wsConnected && savedCount === 0 && (\n <button\n type=\"button\"\n onClick={() => setCurrentView('settings')}\n className={cn(\n 'group rounded-xl border bg-gradient-to-br from-amber-500/10 via-amber-500/5 to-transparent',\n 'border-amber-500/30 hover:border-amber-500/50 transition-colors',\n 'p-4 flex items-center gap-4 text-left',\n )}\n >\n <span className=\"flex items-center justify-center w-12 h-12 rounded-lg bg-amber-500/15 text-amber-600 dark:text-amber-400 shrink-0\">\n <KeyRound className=\"h-6 w-6\" />\n </span>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-base font-semibold mb-1\">No API key configured yet</h3>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">\n Register a provider in Settings before sending a message — otherwise the agent has\n nothing to talk to. Anthropic, OpenAI, Google, and any OpenAI-compatible endpoint all\n work.\n </p>\n </div>\n <span className=\"flex items-center gap-1 text-xs text-amber-600 dark:text-amber-400 font-medium shrink-0 group-hover:translate-x-0.5 transition-transform\">\n Open Settings <ArrowRight className=\"h-3.5 w-3.5\" />\n </span>\n </button>\n )}\n\n {/* Prompt cards */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n {CARDS.map((card) => {\n const Icon = card.icon;\n return (\n <div\n key={card.title}\n className=\"rounded-xl border bg-card/40 backdrop-blur-sm p-4 flex flex-col gap-3\"\n >\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'flex items-center justify-center w-8 h-8 rounded-lg border',\n card.tone,\n )}\n >\n <Icon className=\"h-4 w-4\" />\n </span>\n <div>\n <h3 className=\"text-sm font-semibold\">{card.title}</h3>\n <p className=\"text-xs text-muted-foreground\">{card.hint}</p>\n </div>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n {card.prompts.map((p, i) => (\n <button\n // biome-ignore lint/suspicious/noArrayIndexKey: static list\n key={i}\n type=\"button\"\n onClick={() => fillTextarea(p)}\n className=\"text-left text-xs leading-relaxed text-foreground/80 hover:text-foreground border border-transparent hover:border-border/60 rounded-lg px-3 py-2 hover:bg-muted/40 transition-colors line-clamp-3\"\n title={p}\n >\n {p}\n </button>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Recent sessions — one-click resume. We pull the most recent\n non-current sessions so the user can pick back up without leaving\n the welcome screen. Hidden when there's nothing to show (fresh\n install / first run). */}\n {recentSessions.length > 0 && (\n <div className=\"rounded-xl border bg-muted/20 p-4\">\n <div className=\"flex items-center gap-2 mb-3\">\n <ArchiveRestore className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Pick back up\n </span>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-2\">\n {recentSessions.map((entry) => (\n <button\n key={entry.id}\n type=\"button\"\n onClick={() => resumeSession(entry.id)}\n className=\"text-left rounded-lg border border-border/40 bg-background/60 hover:border-primary/40 hover:bg-accent/30 px-3 py-2 transition-colors group/sess\"\n title={entry.title}\n >\n <div className=\"text-sm font-medium truncate text-foreground group-hover/sess:text-primary\">\n {sessionNicknames[entry.id] || entry.title || '(empty)'}\n </div>\n <div className=\"text-[10px] text-muted-foreground font-mono truncate mt-0.5\">\n {entry.provider}/{entry.model}\n {entry.tokenTotal > 0 && (\n <span className=\"ml-2\">· {entry.tokenTotal.toLocaleString()} tok</span>\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Recent prompts — your own past prompts as one-click refills. Slash\n commands aren't included (the quick-commands block below handles\n those). Shows nothing until you've actually typed something. */}\n {recentPrompts.length > 0 && (\n <div className=\"rounded-xl border bg-muted/20 p-4\">\n <div className=\"flex items-center gap-2 mb-3\">\n <Clock className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Recent prompts\n </span>\n </div>\n <div className=\"flex flex-col gap-1.5\">\n {recentPrompts\n .filter((p) => !p.startsWith('/'))\n .slice(0, 5)\n .map((p, i) => (\n <button\n // biome-ignore lint/suspicious/noArrayIndexKey: static list\n key={i}\n type=\"button\"\n onClick={() => fillTextarea(p)}\n className=\"text-left text-xs leading-relaxed text-muted-foreground hover:text-foreground border border-transparent hover:border-border/60 rounded-lg px-3 py-2 hover:bg-background/60 transition-colors line-clamp-2\"\n title={p}\n >\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Slash command quick-ref */}\n <div className=\"rounded-xl border bg-muted/20 p-4\">\n <div className=\"flex items-center gap-2 mb-3\">\n <Keyboard className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground font-medium\">\n Quick commands\n </span>\n </div>\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-2\">\n {SLASH_REFS.map((c) => (\n <button\n key={c.name}\n type=\"button\"\n onClick={() => fillTextarea(c.name)}\n className=\"text-left flex flex-col gap-0.5 rounded-md border border-border/40 bg-background/60 px-3 py-2 hover:border-primary/40 hover:bg-accent/40 transition-colors\"\n >\n <span className=\"font-mono text-xs text-foreground\">{c.name}</span>\n <span className=\"text-[11px] text-muted-foreground truncate\">{c.hint}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","import { cn } from '@/lib/utils';\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';\nimport * as React from 'react';\n\nconst ScrollArea = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n <ScrollAreaPrimitive.Root\n ref={ref}\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport className=\"h-full w-full rounded-[inherit]\">\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n));\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;\n\nconst ScrollBar = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none transition-colors',\n orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',\n orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',\n className,\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb className=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n));\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;\n\nexport { ScrollArea, ScrollBar };\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { useUIStore } from '@/stores';\nimport { AlertTriangle, FileEdit, Globe, ShieldAlert, Terminal, Wrench } from 'lucide-react';\nimport { useEffect } from 'react';\nimport { DiffView, diffFromToolInput } from './DiffView';\nimport { Button } from './ui/button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from './ui/dialog';\n\n/**\n * Pick the right hero icon for the tool being confirmed. Helps the user\n * see at a glance \"is this a file edit, a shell run, a network call?\".\n */\nfunction pickToolIcon(toolName: string) {\n if (/edit|write|create|patch/i.test(toolName)) return FileEdit;\n if (/bash|shell|exec|run|command/i.test(toolName)) return Terminal;\n if (/fetch|http|web|curl|request/i.test(toolName)) return Globe;\n return Wrench;\n}\n\n/**\n * Render the tool input intelligently. For edit/write we drop the JSON\n * dump and show a proper diff. For shell-like tools we surface the\n * command as a single mono line. Everything else falls back to JSON.\n */\nfunction SmartInputPreview({\n toolName,\n input,\n}: {\n toolName: string;\n input: unknown;\n}) {\n const diffArgs = diffFromToolInput(toolName, input);\n if (diffArgs) {\n return (\n <div className=\"rounded-lg overflow-hidden border\">\n <DiffView\n oldText={diffArgs.oldText}\n newText={diffArgs.newText}\n caption={diffArgs.caption}\n />\n </div>\n );\n }\n\n // Shell-like: pull out the command string so it shows as a real terminal\n // line instead of a JSON envelope.\n if (typeof input === 'object' && input !== null) {\n const obj = input as Record<string, unknown>;\n const cmd = (obj.command ?? obj.cmd ?? obj.script) as string | undefined;\n if (typeof cmd === 'string' && cmd.trim().length > 0) {\n return (\n <div className=\"rounded-lg border bg-background/40 overflow-hidden\">\n <div className=\"px-3 py-1.5 text-[10px] uppercase tracking-wider text-muted-foreground border-b bg-muted/40 flex items-center gap-1.5\">\n <Terminal className=\"h-3 w-3\" />\n <span>Command</span>\n </div>\n <pre className=\"px-3 py-2 text-xs font-mono whitespace-pre-wrap break-all max-h-40 overflow-auto\">\n ${cmd}\n </pre>\n </div>\n );\n }\n // url + method for fetch-like calls\n const url = obj.url as string | undefined;\n if (typeof url === 'string') {\n const method = (obj.method as string | undefined) ?? 'GET';\n return (\n <div className=\"rounded-lg border bg-background/40 px-3 py-2 text-xs font-mono\">\n <span className=\"text-muted-foreground\">{method.toUpperCase()}</span>{' '}\n <span className=\"break-all\">{url}</span>\n </div>\n );\n }\n }\n\n return (\n <div className=\"p-3 rounded-lg bg-muted/50 border text-xs font-mono\">\n <div className=\"text-muted-foreground mb-2\">Input:</div>\n <pre className=\"whitespace-pre-wrap break-all max-h-60 overflow-auto\">\n {JSON.stringify(input, null, 2)}\n </pre>\n </div>\n );\n}\n\nexport function ConfirmDialog() {\n const { showConfirmDialog, confirmInfo, hideConfirm } = useUIStore();\n const { sendConfirm } = useWebSocket();\n\n const handleConfirm = (decision: 'yes' | 'no' | 'always' | 'deny') => {\n if (confirmInfo) {\n sendConfirm(confirmInfo.id, decision);\n }\n hideConfirm();\n };\n\n // Keyboard shortcuts inside the dialog: y = yes, n = no, a = always,\n // Esc = no (dismiss). Matches the CLI permission prompt so habits\n // transfer directly.\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyboard handler; handleConfirm stable\n useEffect(() => {\n if (!showConfirmDialog) return;\n const onKey = (e: KeyboardEvent) => {\n const target = e.target as HTMLElement | null;\n const tag = target?.tagName?.toLowerCase();\n if (tag === 'input' || tag === 'textarea') return;\n if (e.key === 'y' || e.key === 'Y') {\n e.preventDefault();\n handleConfirm('yes');\n } else if (e.key === 'n' || e.key === 'N' || e.key === 'Escape') {\n e.preventDefault();\n handleConfirm('no');\n } else if (e.key === 'a' || e.key === 'A') {\n e.preventDefault();\n handleConfirm('always');\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [showConfirmDialog, confirmInfo?.id]);\n\n if (!confirmInfo) {\n return (\n <Dialog open={showConfirmDialog} onOpenChange={() => hideConfirm()}>\n <DialogContent />\n </Dialog>\n );\n }\n\n const Icon = pickToolIcon(confirmInfo.toolName);\n const isEdit = /edit|write/i.test(confirmInfo.toolName);\n\n return (\n <Dialog open={showConfirmDialog} onOpenChange={() => hideConfirm()}>\n <DialogContent className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <ShieldAlert className=\"h-5 w-5 text-yellow-500\" />\n Confirm: {confirmInfo.toolName}\n </DialogTitle>\n <DialogDescription>\n The agent wants to {isEdit ? 'modify a file' : 'run this tool'}. Review the request\n below and decide whether to proceed.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"py-2 space-y-3\">\n <div className=\"flex items-center gap-3 p-3 rounded-lg bg-muted\">\n <Icon className=\"h-5 w-5 text-muted-foreground\" />\n <div className=\"min-w-0\">\n <div className=\"font-medium font-mono truncate\">{confirmInfo.toolName}</div>\n <div className=\"text-xs text-muted-foreground\">\n {isEdit ? 'File modification' : 'Tool execution'} — preview below\n </div>\n </div>\n </div>\n\n {confirmInfo.input !== undefined && (\n <SmartInputPreview toolName={confirmInfo.toolName} input={confirmInfo.input} />\n )}\n\n {confirmInfo.suggestedPattern && (\n <div className=\"flex items-start gap-2 p-3 rounded-lg bg-yellow-500/10 border border-yellow-500/20\">\n <AlertTriangle className=\"h-4 w-4 text-yellow-600 mt-0.5 shrink-0\" />\n <div className=\"text-sm min-w-0\">\n <div className=\"font-medium text-yellow-800 dark:text-yellow-200\">\n Trust pattern suggestion\n </div>\n <div className=\"font-mono text-xs mt-1 break-all\">\n {confirmInfo.suggestedPattern}\n </div>\n <div className=\"text-xs text-muted-foreground mt-1\">\n Picking <span className=\"font-medium\">Always</span> will whitelist matching calls\n for this project.\n </div>\n </div>\n </div>\n )}\n </div>\n\n <DialogFooter className=\"gap-2 sm:gap-2 flex-wrap\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleConfirm('deny')}\n title=\"Reject this and all future calls matching the pattern\"\n >\n Deny always\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleConfirm('no')}\n title=\"Reject this single call (Esc / n)\"\n >\n No <kbd className=\"ml-1 text-[10px] border rounded px-1 bg-background\">n</kbd>\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleConfirm('always')}\n title=\"Approve and remember the pattern for the project (a)\"\n >\n Always <kbd className=\"ml-1 text-[10px] border rounded px-1 bg-background\">a</kbd>\n </Button>\n <Button\n size=\"sm\"\n onClick={() => handleConfirm('yes')}\n title=\"Approve this single call (y)\"\n >\n Yes <kbd className=\"ml-1 text-[10px] border rounded px-1 bg-background/80\">y</kbd>\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import { cn } from '@/lib/utils';\nimport * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { X } from 'lucide-react';\nimport * as React from 'react';\n\nconst Dialog = DialogPrimitive.Root;\nconst DialogTrigger = DialogPrimitive.Trigger;\nconst DialogPortal = DialogPrimitive.Portal;\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n className,\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',\n className,\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} />\n);\nDialogHeader.displayName = 'DialogHeader';\n\nconst DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}\n {...props}\n />\n);\nDialogFooter.displayName = 'DialogFooter';\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-lg font-semibold leading-none tracking-tight', className)}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n","import { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore } from '@/stores';\nimport { Loader2, RotateCcw, WifiOff, X } from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\n/**\n * Prominent connection-lost banner. The ConnectionChip in the topbar is\n * easy to miss when the user is heads-down on the chat; this banner\n * stretches across the top, blocks the visual flow, and offers a \"retry\n * now\" button. Auto-hides as soon as the socket comes back open.\n *\n * Dismissable for the current outage (X button) — once dismissed, only\n * comes back if the socket recovers and then drops again. The chip in\n * the topbar still reflects the live state so the user isn't fully blind\n * after dismissing.\n */\nexport function ConnectionBanner() {\n const wsStatus = useConfigStore((s) => s.wsStatus);\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const [dismissed, setDismissed] = useState(false);\n const [now, setNow] = useState(Date.now());\n\n // Live retry countdown while reconnecting.\n useEffect(() => {\n if (wsStatus.state !== 'reconnecting') return;\n const id = setInterval(() => setNow(Date.now()), 500);\n return () => clearInterval(id);\n }, [wsStatus.state]);\n\n // Reset the dismissal flag every time the socket recovers — so the *next*\n // disconnection re-shows the banner. Without this the user would have to\n // refresh after dismissing to ever see the banner again.\n useEffect(() => {\n if (wsStatus.state === 'open') setDismissed(false);\n }, [wsStatus.state]);\n\n if (wsStatus.state === 'open' || wsStatus.state === 'connecting') return null;\n if (dismissed) return null;\n\n const retry = () => getWSClient(wsUrl).retryNow();\n const isReconnecting = wsStatus.state === 'reconnecting';\n const errorText =\n wsStatus.state === 'closed'\n ? wsStatus.error\n : wsStatus.state === 'reconnecting'\n ? wsStatus.lastError\n : undefined;\n const remaining = isReconnecting\n ? Math.max(0, Math.ceil((wsStatus.nextRetryAt - now) / 1000))\n : 0;\n\n return (\n <div\n className={cn(\n 'flex items-center gap-3 px-4 py-2 border-b text-sm',\n isReconnecting\n ? 'bg-orange-500/10 text-orange-700 dark:text-orange-300 border-orange-500/30'\n : 'bg-red-500/10 text-red-700 dark:text-red-300 border-red-500/30',\n )}\n >\n {isReconnecting ? (\n <Loader2 className=\"h-4 w-4 animate-spin shrink-0\" />\n ) : (\n <WifiOff className=\"h-4 w-4 shrink-0\" />\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"font-medium\">\n {isReconnecting\n ? `Reconnecting to backend (attempt ${wsStatus.attempt}) — retrying in ${remaining}s`\n : 'Disconnected from backend'}\n </div>\n {errorText && <div className=\"text-xs opacity-80 truncate\">{errorText}</div>}\n </div>\n <button\n type=\"button\"\n onClick={retry}\n className={cn(\n 'inline-flex items-center gap-1 px-2.5 py-1 rounded-md border text-xs font-medium',\n 'hover:bg-background/30 transition-colors shrink-0',\n isReconnecting ? 'border-orange-500/40' : 'border-red-500/40',\n )}\n title=\"Retry connection now\"\n >\n <RotateCcw className=\"h-3 w-3\" />\n Retry now\n </button>\n <button\n type=\"button\"\n onClick={() => setDismissed(true)}\n className=\"text-current/60 hover:text-current shrink-0\"\n title=\"Dismiss (chip in topbar still shows status)\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n );\n}\n","import { AlertTriangle, RefreshCw } from 'lucide-react';\nimport { Component, type ErrorInfo, type ReactNode } from 'react';\nimport { Button } from './ui/button';\n\ninterface Props {\n children: ReactNode;\n /** Optional callback when an error is caught — useful for telemetry. */\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\ninterface State {\n error: Error | null;\n}\n\n/**\n * Top-level error boundary for the entire app. Catches rendering errors in\n * any descendent component and shows a fallback UI instead of a white screen.\n * A single crash in a leaf component (e.g. a malformed markdown render) must\n * not take down the entire chat session.\n */\nexport class ErrorBoundary extends Component<Props, State> {\n state: State = { error: null };\n\n static getDerivedStateFromError(error: Error): State {\n return { error };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo): void {\n console.error('[ErrorBoundary]', error, info.componentStack);\n this.props.onError?.(error, info);\n }\n\n private handleReset = () => {\n this.setState({ error: null });\n };\n\n render() {\n if (this.state.error) {\n return (\n <div className=\"flex items-center justify-center h-screen bg-background\">\n <div className=\"flex flex-col items-center gap-4 p-8 max-w-md text-center\">\n <AlertTriangle className=\"h-12 w-12 text-destructive\" />\n <h1 className=\"text-lg font-semibold\">Something went wrong</h1>\n <p className=\"text-sm text-muted-foreground\">\n A rendering error occurred. Your session is still active on the server — reloading\n will pick up where you left off.\n </p>\n <pre className=\"text-xs font-mono text-muted-foreground bg-muted/50 rounded p-3 max-h-32 overflow-auto w-full text-left\">\n {this.state.error.message}\n </pre>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={() => window.location.reload()}>\n <RefreshCw className=\"h-4 w-4 mr-1\" />\n Reload page\n </Button>\n <Button size=\"sm\" onClick={this.handleReset}>\n Try again\n </Button>\n </div>\n </div>\n </div>\n );\n }\n return this.props.children;\n }\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { getWSClient } from '@/lib/ws-client';\nimport { useConfigStore, useUIStore } from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport { ArrowRight, Cpu, Search } from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\ninterface SavedProvider {\n id: string;\n apiKeys: Array<{ label: string; isActive: boolean }>;\n}\ninterface CatalogModel {\n id: string;\n name: string;\n contextWindow?: number;\n}\n\n/**\n * Ctrl/Cmd+M — flat searchable provider/model picker. Drops a 3-click\n * trip through Settings down to one shortcut. Pulls the list of *saved*\n * providers (the ones that actually have a registered key) and lazy-loads\n * each provider's models when the overlay opens. The active model is\n * highlighted; Enter switches via the existing model.switch WS handler\n * (which atomically swaps provider+model on the backend).\n */\nexport function QuickModelSwitcher() {\n const open = useUIStore((s) => s.modelSwitcherOpen);\n const setOpen = useUIStore((s) => s.setModelSwitcherOpen);\n const [query, setQuery] = useState('');\n const [selected, setSelected] = useState(0);\n const [saved, setSaved] = useState<SavedProvider[]>([]);\n const [modelsByProvider, setModelsByProvider] = useState<Record<string, CatalogModel[]>>({});\n const inputRef = useRef<HTMLInputElement>(null);\n\n const wsUrl = useConfigStore((s) => s.wsUrl);\n const currentProvider = useConfigStore((s) => s.provider);\n const currentModel = useConfigStore((s) => s.model);\n const paletteOpen = useUIStore((s) => s.paletteOpen);\n const ws = useWebSocket();\n\n // Ctrl/Cmd+M opens. Skip when the command palette is already open so\n // the two overlays don't fight for focus.\n // biome-ignore lint/correctness/useExhaustiveDependencies: setOpen is stable\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n const mod = e.ctrlKey || e.metaKey;\n if (mod && e.key.toLowerCase() === 'm' && !e.shiftKey && !e.altKey) {\n if (paletteOpen) return;\n e.preventDefault();\n setOpen(!open);\n return;\n }\n if (e.key === 'Escape' && open) {\n e.preventDefault();\n setOpen(false);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [open, paletteOpen]);\n\n // Wire up WS listeners + fetch on open. We listen unconditionally so a\n // late response (e.g. the user opened then closed before models arrived)\n // still populates state for the next open.\n useEffect(() => {\n const client = getWSClient(wsUrl);\n const offSaved = client.on('providers.saved', (msg: WSServerMessage) => {\n const p = msg.payload as { providers: SavedProvider[] };\n setSaved(p.providers ?? []);\n });\n const offModels = client.on('provider.models', (msg: WSServerMessage) => {\n const p = msg.payload as { provider: string; models: CatalogModel[] };\n setModelsByProvider((prev) => ({ ...prev, [p.provider]: p.models }));\n });\n return () => {\n offSaved();\n offModels();\n };\n }, [wsUrl]);\n\n useEffect(() => {\n if (!open) return;\n setQuery('');\n setSelected(0);\n ws.listSavedProviders();\n // Auto-focus the search input after the dialog paints. requestAnimationFrame\n // because the input ref isn't mounted on the same tick we flip `open`.\n requestAnimationFrame(() => inputRef.current?.focus());\n }, [open, ws]);\n\n // Lazy-load models per saved provider once we know what's saved.\n useEffect(() => {\n if (!open) return;\n for (const sp of saved) {\n if (!modelsByProvider[sp.id]) {\n ws.listProviderModels(sp.id);\n }\n }\n }, [open, saved, modelsByProvider, ws]);\n\n /** Flatten into a single list of {provider, model} candidates, then apply\n * the search filter. The active row floats to the top so the user can\n * see what they're currently on. */\n const candidates = useMemo(() => {\n const list: Array<{\n provider: string;\n model: string;\n modelName: string;\n contextWindow?: number;\n isCurrent: boolean;\n }> = [];\n for (const sp of saved) {\n const models = modelsByProvider[sp.id] ?? [];\n for (const m of models) {\n list.push({\n provider: sp.id,\n model: m.id,\n modelName: m.name || m.id,\n contextWindow: m.contextWindow,\n isCurrent: sp.id === currentProvider && m.id === currentModel,\n });\n }\n }\n const q = query.toLowerCase().trim();\n const filtered = q\n ? list.filter(\n (c) =>\n c.provider.toLowerCase().includes(q) ||\n c.model.toLowerCase().includes(q) ||\n c.modelName.toLowerCase().includes(q),\n )\n : list;\n return filtered.sort((a, b) => {\n if (a.isCurrent !== b.isCurrent) return a.isCurrent ? -1 : 1;\n return a.provider.localeCompare(b.provider) || a.model.localeCompare(b.model);\n });\n }, [saved, modelsByProvider, query, currentProvider, currentModel]);\n\n useEffect(() => {\n if (selected >= candidates.length) setSelected(0);\n }, [candidates.length, selected]);\n\n const commit = (idx: number) => {\n const pick = candidates[idx];\n if (!pick) return;\n ws.switchModel(pick.provider, pick.model);\n setOpen(false);\n };\n\n if (!open) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-start justify-center bg-background/60 backdrop-blur-sm pt-[15vh]\"\n onClick={(e) => {\n if (e.target === e.currentTarget) setOpen(false);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(false);\n }}\n >\n <div className=\"w-full max-w-xl rounded-xl border bg-popover shadow-2xl overflow-hidden\">\n <div className=\"flex items-center gap-2 border-b px-3 py-2\">\n <Search className=\"h-4 w-4 text-muted-foreground\" />\n <input\n ref={inputRef}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value);\n setSelected(0);\n }}\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setSelected((i) => Math.min(candidates.length - 1, i + 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setSelected((i) => Math.max(0, i - 1));\n } else if (e.key === 'Enter') {\n e.preventDefault();\n commit(selected);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setOpen(false);\n }\n }}\n placeholder=\"Filter providers and models…\"\n className=\"flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground\"\n />\n <span className=\"text-[10px] text-muted-foreground font-mono\">↑↓ · Enter · Esc</span>\n </div>\n <div className=\"max-h-[50vh] overflow-y-auto py-1\">\n {candidates.length === 0 ? (\n <div className=\"px-4 py-8 text-center text-sm text-muted-foreground\">\n {saved.length === 0\n ? 'No saved providers — register a key in Settings first.'\n : 'Loading models…'}\n </div>\n ) : (\n candidates.map((c, idx) => (\n <button\n type=\"button\"\n key={`${c.provider}:${c.model}`}\n onClick={() => commit(idx)}\n onMouseEnter={() => setSelected(idx)}\n className={cn(\n 'w-full flex items-center gap-3 px-3 py-2 text-left text-sm transition-colors',\n idx === selected ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/40',\n c.isCurrent && 'font-medium',\n )}\n >\n <Cpu\n className={cn(\n 'h-4 w-4 shrink-0',\n c.isCurrent ? 'text-primary' : 'text-muted-foreground',\n )}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate\">\n <span className=\"text-muted-foreground\">{c.provider}</span>\n <span className=\"mx-1 text-muted-foreground/40\">·</span>\n <span>{c.modelName}</span>\n </div>\n {c.contextWindow && (\n <div className=\"text-[10px] text-muted-foreground font-mono\">\n {c.model} · ctx {c.contextWindow.toLocaleString()}\n </div>\n )}\n </div>\n {c.isCurrent ? (\n <span className=\"text-[10px] uppercase tracking-wide text-primary font-semibold\">\n active\n </span>\n ) : (\n <ArrowRight className=\"h-3.5 w-3.5 text-muted-foreground opacity-0 group-hover:opacity-100\" />\n )}\n </button>\n ))\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport { useConfigStore, useUIStore } from '@/stores';\nimport type { WSServerMessage } from '@/types';\nimport {\n AlertCircle,\n CheckCircle2,\n Cpu,\n Eye,\n EyeOff,\n Globe,\n Key,\n Loader2,\n Monitor,\n Moon,\n Network,\n Palette,\n Plus,\n RefreshCw,\n Sun,\n Trash2,\n X,\n} from 'lucide-react';\nimport React, { useState, useEffect, useCallback } from 'react';\nimport { useTheme } from './ThemeProvider';\nimport { Button } from './ui/button';\nimport { Input } from './ui/input';\nimport { ScrollArea } from './ui/scroll-area';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';\n\ninterface CatalogProvider {\n id: string;\n name: string;\n family: string;\n apiBase?: string;\n envVars: string[];\n modelCount: number;\n hasApiKey: boolean;\n}\n\ninterface CatalogModel {\n id: string;\n name: string;\n releaseDate?: string;\n contextWindow?: number;\n inputCost?: number;\n outputCost?: number;\n capabilities: string[];\n}\n\ninterface SavedProvider {\n id: string;\n family?: string;\n baseUrl?: string;\n apiKeys: Array<{\n label: string;\n maskedKey: string;\n isActive: boolean;\n createdAt: string;\n }>;\n}\n\ntype ProviderTab = 'catalog' | 'saved';\n\nexport function SettingsPanel() {\n const { setCurrentView } = useUIStore();\n const { provider, model, setProvider, setModel, wsConnected } = useConfigStore();\n const { theme, setTheme } = useTheme();\n const ws = useWebSocket();\n const wsClient = ws.client;\n const listProviders = ws.listProviders;\n const listSavedProviders = ws.listSavedProviders;\n\n // Catalog data\n const [catalogProviders, setCatalogProviders] = useState<CatalogProvider[]>([]);\n const [catalogModels, setCatalogModels] = useState<Record<string, CatalogModel[]>>({});\n const [savedProviders, setSavedProviders] = useState<SavedProvider[]>([]);\n const [isLoadingCatalog, setIsLoadingCatalog] = useState(false);\n const [isLoadingModels, setIsLoadingModels] = useState(false);\n const [isLoadingSaved, setIsLoadingSaved] = useState(false);\n const [operationStatus, setOperationStatus] = useState<{\n success: boolean;\n message: string;\n } | null>(null);\n\n // Provider tab selection\n const [providerTab, setProviderTab] = useState<ProviderTab>('catalog');\n\n // New key form\n const [showAddKeyForm, setShowAddKeyForm] = useState<string | null>(null);\n const [newKeyLabel, setNewKeyLabel] = useState('');\n const [newKeyValue, setNewKeyValue] = useState('');\n const [showNewKeyValue, setShowNewKeyValue] = useState(false);\n\n // New provider form\n const [showAddProviderForm, setShowAddProviderForm] = useState(false);\n const [newProviderId, setNewProviderId] = useState('');\n const [newProviderFamily, setNewProviderFamily] = useState('openai-compatible');\n const [newProviderBaseUrl, setNewProviderBaseUrl] = useState('');\n const [newProviderApiKey, setNewProviderApiKey] = useState('');\n\n // Current provider config from catalog\n const currentCatalogProvider = catalogProviders.find((p) => p.id === provider);\n\n // Load catalog and saved providers on mount\n useEffect(() => {\n const handleProviderCatalog = (msg: WSServerMessage) => {\n if (msg.type === 'provider.catalog') {\n const payload = msg.payload as { providers: CatalogProvider[] };\n setCatalogProviders(payload.providers.sort((a, b) => a.id.localeCompare(b.id)));\n setIsLoadingCatalog(false);\n }\n };\n\n const handleProviderModels = (msg: WSServerMessage) => {\n if (msg.type === 'provider.models') {\n const payload = msg.payload as { provider: string; models: CatalogModel[] };\n setCatalogModels((prev) => ({ ...prev, [payload.provider]: payload.models }));\n setIsLoadingModels(false);\n }\n };\n\n const handleSavedProviders = (msg: WSServerMessage) => {\n if (msg.type === 'providers.saved') {\n const payload = msg.payload as { providers: SavedProvider[] };\n const next = payload.providers.sort((a, b) => a.id.localeCompare(b.id));\n setSavedProviders(next);\n setIsLoadingSaved(false);\n // If the user already has registered accounts (the common case after\n // running `wstack auth`), open the Saved tab automatically. Otherwise\n // most of their data would sit one click away on a tab they never\n // visit, behind a catalog list that doesn't include their aliases\n // (e.g. \"minimax-coding-plan\" isn't in models.dev).\n if (next.length > 0) setProviderTab('saved');\n }\n };\n\n const handleKeyOperationResult = (msg: WSServerMessage) => {\n if (msg.type === 'key.operation_result') {\n const payload = msg.payload as { success: boolean; message: string };\n setOperationStatus(payload);\n setTimeout(() => setOperationStatus(null), 3000);\n // Refresh saved providers after operation\n listSavedProviders?.();\n }\n };\n\n // Only subscribe / fetch once the underlying WS is actually connected.\n // SettingsPanel mounts before the WebSocket finishes its handshake, so\n // ws.client is null on the first render and silently dropping the\n // listProviders() call left users staring at \"Saved (0)\" forever. Keying\n // the effect on wsConnected re-runs it the moment the socket comes up.\n if (!wsConnected || !wsClient) return;\n\n const off1 = wsClient.on('provider.catalog', handleProviderCatalog);\n const off2 = wsClient.on('provider.models', handleProviderModels);\n const off3 = wsClient.on('providers.saved', handleSavedProviders);\n const off4 = wsClient.on('key.operation_result', handleKeyOperationResult);\n\n setIsLoadingCatalog(true);\n setIsLoadingSaved(true);\n listProviders?.();\n listSavedProviders?.();\n\n return () => {\n off1?.();\n off2?.();\n off3?.();\n off4?.();\n };\n }, [wsConnected, wsClient, listProviders, listSavedProviders]);\n\n // Selecting a provider just loads its model list and stages the pick locally.\n // The actual backend switch fires when the user picks a model — that's the\n // single point where (provider, model) both have meaningful values to send.\n const handleProviderSelect = useCallback(\n (providerId: string) => {\n setProvider(providerId);\n if (!catalogModels[providerId]) {\n setIsLoadingModels(true);\n ws.listProviderModels?.(providerId);\n }\n },\n [catalogModels, setProvider, ws],\n );\n\n const handleModelSelect = useCallback(\n (modelId: string) => {\n setModel(modelId);\n // Tell the backend to actually swap the agent's provider+model. Backend\n // will rebuild the provider instance, persist the choice to the config\n // file, and broadcast a fresh session.start — useWebSocket's session.start\n // handler then re-syncs our config store so the chip stays in sync.\n ws.switchModel?.(provider, modelId);\n setOperationStatus({ success: true, message: `Switching to ${provider} / ${modelId}…` });\n },\n [setModel, ws, provider],\n );\n\n const handleAddKey = useCallback(\n (providerId: string) => {\n if (!newKeyLabel.trim() || !newKeyValue.trim()) return;\n ws.addKey?.(providerId, newKeyLabel.trim(), newKeyValue.trim());\n setNewKeyLabel('');\n setNewKeyValue('');\n setShowAddKeyForm(null);\n },\n [ws, newKeyLabel, newKeyValue],\n );\n\n const handleDeleteKey = useCallback(\n (providerId: string, label: string) => {\n ws.deleteKey?.(providerId, label);\n },\n [ws],\n );\n\n const handleSetActiveKey = useCallback(\n (providerId: string, label: string) => {\n ws.setActiveKey?.(providerId, label);\n },\n [ws],\n );\n\n const handleAddProvider = useCallback(() => {\n if (!newProviderId.trim()) return;\n ws.addProvider?.(\n newProviderId.trim(),\n newProviderFamily,\n newProviderBaseUrl || undefined,\n newProviderApiKey || undefined,\n );\n setNewProviderId('');\n setNewProviderFamily('openai-compatible');\n setNewProviderBaseUrl('');\n setNewProviderApiKey('');\n setShowAddProviderForm(false);\n }, [ws, newProviderId, newProviderFamily, newProviderBaseUrl, newProviderApiKey]);\n\n const handleRemoveProvider = useCallback(\n (providerId: string) => {\n ws.removeProvider?.(providerId);\n },\n [ws],\n );\n\n // Group catalog by family, with optional text filter applied first. 115+\n // providers in the catalog made scrolling alone impractical.\n const [catalogQuery, setCatalogQuery] = useState('');\n const families = ['anthropic', 'openai', 'google', 'openai-compatible'] as const;\n const filteredCatalog = catalogQuery.trim()\n ? catalogProviders.filter((p) => {\n const q = catalogQuery.trim().toLowerCase();\n return (\n p.id.toLowerCase().includes(q) ||\n p.name.toLowerCase().includes(q) ||\n p.family.toLowerCase().includes(q)\n );\n })\n : catalogProviders;\n const catalogByFamily = filteredCatalog.reduce(\n (acc, p) => {\n if (!acc[p.family]) acc[p.family] = [];\n acc[p.family]!.push(p);\n return acc;\n },\n {} as Record<string, CatalogProvider[]>,\n );\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Header */}\n <header className=\"flex items-center justify-between px-4 py-3 border-b bg-card shrink-0\">\n <h1 className=\"text-lg font-semibold\">Settings</h1>\n <Button variant=\"ghost\" size=\"icon\" onClick={() => setCurrentView('chat')}>\n <X className=\"h-4 w-4\" />\n </Button>\n </header>\n\n {/* Content */}\n <ScrollArea className=\"flex-1\">\n <div className=\"p-6 max-w-2xl mx-auto\">\n <Tabs defaultValue=\"provider\">\n <TabsList className=\"w-full justify-start mb-6 grid grid-cols-4\">\n <TabsTrigger value=\"provider\" className=\"gap-2\">\n <Network className=\"h-4 w-4\" />\n Provider\n </TabsTrigger>\n <TabsTrigger value=\"model\" className=\"gap-2\">\n <Cpu className=\"h-4 w-4\" />\n Model\n </TabsTrigger>\n <TabsTrigger value=\"connection\" className=\"gap-2\">\n <Globe className=\"h-4 w-4\" />\n Connection\n </TabsTrigger>\n <TabsTrigger value=\"appearance\" className=\"gap-2\">\n <Palette className=\"h-4 w-4\" />\n Appearance\n </TabsTrigger>\n </TabsList>\n\n {/* Provider Tab */}\n <TabsContent value=\"provider\" className=\"space-y-4\">\n {/* Provider source toggle */}\n <div className=\"flex gap-2 mb-4\">\n <Button\n variant={providerTab === 'catalog' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setProviderTab('catalog')}\n >\n <Globe className=\"h-4 w-4 mr-1\" />\n Catalog\n </Button>\n <Button\n variant={providerTab === 'saved' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setProviderTab('saved')}\n >\n <Key className=\"h-4 w-4 mr-1\" />\n Saved ({savedProviders.length})\n </Button>\n </div>\n\n {operationStatus && (\n <div\n className={cn(\n 'p-3 rounded-lg mb-4 flex items-center gap-2',\n operationStatus.success\n ? 'bg-green-500/10 text-green-600'\n : 'bg-red-500/10 text-red-600',\n )}\n >\n {operationStatus.success ? (\n <CheckCircle2 className=\"h-4 w-4\" />\n ) : (\n <AlertCircle className=\"h-4 w-4\" />\n )}\n {operationStatus.message}\n </div>\n )}\n\n {/* Catalog View */}\n {providerTab === 'catalog' && (\n <div className=\"space-y-4\">\n {/* Search */}\n <Input\n placeholder={`Search ${catalogProviders.length} providers (name / id / family)…`}\n value={catalogQuery}\n onChange={(e) => setCatalogQuery(e.target.value)}\n className=\"text-sm\"\n />\n {isLoadingCatalog && catalogProviders.length === 0 ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-muted-foreground\">Loading catalog...</span>\n </div>\n ) : filteredCatalog.length === 0 && catalogQuery ? (\n <div className=\"text-center py-8 text-muted-foreground text-sm\">\n No providers match \"<span className=\"font-mono\">{catalogQuery}</span>\".\n </div>\n ) : (\n <>\n {families.map((family) => {\n const providers = catalogByFamily[family];\n if (!providers?.length) return null;\n return (\n <div key={family} className=\"space-y-2\">\n <h3 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">\n {family}\n </h3>\n <div className=\"grid grid-cols-1 gap-2\">\n {providers.map((p) => (\n <button\n type=\"button\"\n key={p.id}\n onClick={() => handleProviderSelect(p.id)}\n className={cn(\n 'flex flex-col items-start p-3 rounded-lg border text-left transition-all',\n provider === p.id\n ? 'border-primary bg-primary/5 ring-2 ring-primary/20'\n : 'border-border hover:bg-muted',\n )}\n >\n <div className=\"flex w-full justify-between items-start\">\n <div>\n <span className=\"font-medium\">{p.name}</span>\n <span className=\"ml-2 text-xs text-muted-foreground\">\n ({p.id})\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n {p.hasApiKey && (\n <span className=\"text-xs bg-green-500/10 text-green-600 px-2 py-0.5 rounded\">\n <Key className=\"h-3 w-3 inline mr-1\" />\n Configured\n </span>\n )}\n {p.envVars[0] && (\n <span className=\"text-xs text-muted-foreground\">\n ENV: {p.envVars[0]}\n </span>\n )}\n {provider === p.id && (\n <CheckCircle2 className=\"h-4 w-4 text-primary\" />\n )}\n </div>\n </div>\n <div className=\"text-xs text-muted-foreground mt-1\">\n {p.modelCount} models\n {p.apiBase && ` · ${p.apiBase}`}\n </div>\n </button>\n ))}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n )}\n\n {/* Saved Providers View */}\n {providerTab === 'saved' && (\n <div className=\"space-y-4\">\n <div className=\"flex justify-between items-center\">\n <p className=\"text-sm text-muted-foreground\">\n Manage your API keys and provider configurations\n </p>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setShowAddProviderForm(!showAddProviderForm)}\n >\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Provider\n </Button>\n </div>\n\n {/* Add Provider Form */}\n {showAddProviderForm && (\n <div className=\"p-4 border rounded-lg space-y-3 bg-muted/50\">\n <h4 className=\"font-medium\">Add Custom Provider</h4>\n <Input\n placeholder=\"Provider ID (e.g. my-llm-server)\"\n value={newProviderId}\n onChange={(e) => setNewProviderId(e.target.value)}\n />\n <select\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={newProviderFamily}\n onChange={(e) => setNewProviderFamily(e.target.value)}\n >\n <option value=\"anthropic\">Anthropic</option>\n <option value=\"openai\">OpenAI</option>\n <option value=\"openai-compatible\">OpenAI Compatible</option>\n <option value=\"google\">Google</option>\n </select>\n <Input\n placeholder=\"Base URL (optional, e.g. http://localhost:11434/v1)\"\n value={newProviderBaseUrl}\n onChange={(e) => setNewProviderBaseUrl(e.target.value)}\n />\n <Input\n type=\"password\"\n placeholder=\"API Key (optional)\"\n value={newProviderApiKey}\n onChange={(e) => setNewProviderApiKey(e.target.value)}\n />\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n onClick={handleAddProvider}\n disabled={!newProviderId.trim()}\n >\n Add\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setShowAddProviderForm(false)}\n >\n Cancel\n </Button>\n </div>\n </div>\n )}\n\n {isLoadingSaved ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n ) : savedProviders.length === 0 ? (\n <div className=\"text-center py-8 text-muted-foreground\">\n <Key className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>No saved providers yet</p>\n <p className=\"text-sm\">Add a provider to get started</p>\n </div>\n ) : (\n savedProviders.map((sp) => (\n <div key={sp.id} className=\"border rounded-lg p-4 space-y-3\">\n <div className=\"flex justify-between items-start\">\n <div>\n <h4 className=\"font-medium\">{sp.id}</h4>\n {sp.family && (\n <span className=\"text-xs text-muted-foreground\">{sp.family}</span>\n )}\n </div>\n <div className=\"flex gap-2\">\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => handleRemoveProvider(sp.id)}\n >\n <Trash2 className=\"h-4 w-4 text-destructive\" />\n </Button>\n </div>\n </div>\n\n {sp.baseUrl && (\n <div className=\"text-xs text-muted-foreground\">\n <Globe className=\"h-3 w-3 inline mr-1\" />\n {sp.baseUrl}\n </div>\n )}\n\n {/* API Keys */}\n <div className=\"space-y-2\">\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm font-medium\">API Keys</span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() =>\n setShowAddKeyForm(showAddKeyForm === sp.id ? null : sp.id)\n }\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n Add Key\n </Button>\n </div>\n\n {sp.apiKeys.length === 0 && !showAddKeyForm && (\n <p className=\"text-xs text-muted-foreground\">No keys configured</p>\n )}\n\n {sp.apiKeys.map((key) => (\n <div\n key={key.label}\n className=\"flex items-center justify-between p-2 bg-muted/50 rounded\"\n >\n <div>\n <span className=\"text-sm font-medium\">{key.label}</span>\n {key.isActive && (\n <span className=\"ml-2 text-xs bg-green-500/10 text-green-600 px-1.5 py-0.5 rounded\">\n Active\n </span>\n )}\n <div className=\"text-xs text-muted-foreground font-mono\">\n {key.maskedKey}\n </div>\n </div>\n <div className=\"flex gap-1\">\n {!key.isActive && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => handleSetActiveKey(sp.id, key.label)}\n >\n Set Active\n </Button>\n )}\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => handleDeleteKey(sp.id, key.label)}\n >\n <Trash2 className=\"h-3 w-3 text-destructive\" />\n </Button>\n </div>\n </div>\n ))}\n\n {/* Add Key Form */}\n {showAddKeyForm === sp.id && (\n <div className=\"p-3 border rounded space-y-2 bg-background\">\n <Input\n placeholder=\"Key label (e.g. default, production)\"\n value={newKeyLabel}\n onChange={(e) => setNewKeyLabel(e.target.value)}\n />\n <div className=\"flex gap-2\">\n <Input\n type={showNewKeyValue ? 'text' : 'password'}\n placeholder=\"API key\"\n value={newKeyValue}\n onChange={(e) => setNewKeyValue(e.target.value)}\n />\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={() => setShowNewKeyValue(!showNewKeyValue)}\n >\n {showNewKeyValue ? (\n <EyeOff className=\"h-4 w-4\" />\n ) : (\n <Eye className=\"h-4 w-4\" />\n )}\n </Button>\n </div>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n onClick={() => handleAddKey(sp.id)}\n disabled={!newKeyLabel.trim() || !newKeyValue.trim()}\n >\n Save Key\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setShowAddKeyForm(null);\n setNewKeyLabel('');\n setNewKeyValue('');\n }}\n >\n Cancel\n </Button>\n </div>\n </div>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n )}\n </TabsContent>\n\n {/* Model Tab */}\n <TabsContent value=\"model\" className=\"space-y-4\">\n {provider ? (\n <>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {currentCatalogProvider?.name || provider}\n </p>\n <p className=\"text-xs text-muted-foreground\">{provider}</p>\n </div>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n setIsLoadingModels(true);\n ws.listProviderModels?.(provider);\n }}\n >\n <RefreshCw className={cn('h-4 w-4', isLoadingModels && 'animate-spin')} />\n </Button>\n </div>\n\n {isLoadingModels && !catalogModels[provider] ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-muted-foreground\">Loading models...</span>\n </div>\n ) : (\n <div className=\"space-y-1\">\n {(catalogModels[provider] || []).map((m) => (\n <button\n type=\"button\"\n key={m.id}\n onClick={() => handleModelSelect(m.id)}\n className={cn(\n 'w-full flex items-center justify-between p-3 rounded-lg border text-left transition-all',\n model === m.id\n ? 'border-primary bg-primary/5 ring-2 ring-primary/20'\n : 'border-border hover:bg-muted',\n )}\n >\n <div>\n <span className=\"font-medium\">{m.name || m.id}</span>\n <div className=\"flex gap-2 mt-1\">\n {m.capabilities.map((cap) => (\n <span key={cap} className=\"text-xs bg-muted px-1.5 py-0.5 rounded\">\n {cap}\n </span>\n ))}\n </div>\n </div>\n <div className=\"text-right text-xs text-muted-foreground\">\n {m.contextWindow && <div>{m.contextWindow / 1000}k context</div>}\n {m.inputCost && m.outputCost && (\n <div>\n ${m.inputCost}/${m.outputCost}\n </div>\n )}\n {model === m.id && (\n <CheckCircle2 className=\"h-4 w-4 text-primary mt-1\" />\n )}\n </div>\n </button>\n ))}\n\n {catalogModels[provider]?.length === 0 && (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n No models found for this provider. The catalog might be empty or still\n loading.\n </p>\n )}\n </div>\n )}\n </>\n ) : (\n <div className=\"text-center py-8 text-muted-foreground\">\n <Cpu className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>Select a provider first</p>\n </div>\n )}\n </TabsContent>\n\n {/* Connection Tab */}\n <TabsContent value=\"connection\" className=\"space-y-4\">\n <div className=\"space-y-3\">\n <label\n htmlFor=\"websocket-url\"\n className=\"text-sm font-medium flex items-center gap-2\"\n >\n <Globe className=\"h-4 w-4 text-muted-foreground\" />\n WebSocket Server URL\n </label>\n <Input\n id=\"websocket-url\"\n value={useConfigStore.getState().wsUrl}\n onChange={(e) => useConfigStore.getState().setConfig({ wsUrl: e.target.value })}\n placeholder=\"ws://localhost:3457\"\n className=\"font-mono text-sm\"\n />\n <p className=\"text-xs text-muted-foreground\">\n URL of the WrongStack WebSocket server. The server runs alongside the CLI.\n </p>\n </div>\n\n <div className=\"p-4 rounded-lg border bg-muted/50\">\n <h4 className=\"text-sm font-medium mb-2\">Starting the WebSocket Server</h4>\n <p className=\"text-xs text-muted-foreground mb-3\">\n Standalone: run <code className=\"bg-muted px-1 py-0.5 rounded\">./dev.ps1</code>{' '}\n from the repo root, or set WS_HOST/WS_PORT before launching{' '}\n <code className=\"bg-muted px-1 py-0.5 rounded\">\n node packages/webui/dist/server/entry.js\n </code>\n . Or alongside the CLI:{' '}\n <code className=\"bg-muted px-1 py-0.5 rounded\">wstack --webui</code>.\n </p>\n </div>\n </TabsContent>\n\n {/* Appearance Tab */}\n <TabsContent value=\"appearance\" className=\"space-y-4\">\n <div>\n <h3 className=\"text-sm font-semibold mb-3\">Theme</h3>\n <div className=\"grid grid-cols-3 gap-2 max-w-md\">\n <Button\n variant={theme === 'light' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setTheme('light')}\n >\n <Sun className=\"h-4 w-4 mr-1\" />\n Light\n </Button>\n <Button\n variant={theme === 'dark' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setTheme('dark')}\n >\n <Moon className=\"h-4 w-4 mr-1\" />\n Dark\n </Button>\n <Button\n variant={theme === 'system' ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setTheme('system')}\n >\n <Monitor className=\"h-4 w-4 mr-1\" />\n System\n </Button>\n </div>\n <p className=\"text-xs text-muted-foreground mt-2\">\n System follows your OS-level light/dark preference.\n </p>\n </div>\n\n {/* Preferences — surfaces the persisted toggles that until\n now were only reachable via Command Palette / Ctrl+Shift+D.\n Keeps them discoverable and centralizes per-user knobs in\n one obvious place. Reads & writes via the live stores so\n changes take effect immediately. */}\n <div className=\"pt-2 border-t\">\n <h3 className=\"text-sm font-semibold mb-3 mt-3\">Preferences</h3>\n <PreferenceToggle\n label=\"Compact density\"\n hint=\"Tighter spacing throughout the chat. Toggle anywhere with Ctrl+Shift+D.\"\n selector={(s) => s.compactMode}\n onChange={() => useUIStore.getState().toggleCompactMode()}\n />\n <PreferenceToggle\n label=\"Sound on completion\"\n hint=\"Play a soft chime when a run finishes — useful when working in another tab.\"\n selector={null /* config-store path, see component */}\n configKey=\"soundOnComplete\"\n />\n </div>\n </TabsContent>\n </Tabs>\n </div>\n </ScrollArea>\n </div>\n );\n}\n\n/**\n * One row in the Preferences section. Renders a label / hint pair on the\n * left and a small switch on the right. Source-of-truth selection is\n * pluggable: pass `selector` for a UI-store-backed flag, or `configKey`\n * to bind to a `useConfigStore` boolean (we only handle `soundOnComplete`\n * today but the shape leaves room for more without growing the API).\n */\nfunction PreferenceToggle({\n label,\n hint,\n selector,\n onChange,\n configKey,\n}: {\n label: string;\n hint?: string;\n selector: ((s: ReturnType<typeof useUIStore.getState>) => boolean) | null;\n onChange?: () => void;\n configKey?: 'soundOnComplete';\n}) {\n const uiVal = useUIStore((s) => (selector ? selector(s) : false));\n const cfgVal = useConfigStore((s) => (configKey ? (s[configKey] as boolean) : false));\n const on = selector ? uiVal : cfgVal;\n const handleToggle = () => {\n if (selector) onChange?.();\n else if (configKey === 'soundOnComplete') {\n const next = !useConfigStore.getState().soundOnComplete;\n useConfigStore.getState().setSoundOnComplete(next);\n // Audible \"yes you turned it on\" — same logic as the Command Palette\n // toggle so users get the gesture-permission unlock for Web Audio.\n if (next) {\n import('@/lib/chime').then((m) => m.playCompletionChime()).catch(() => {});\n }\n }\n };\n return (\n <div className=\"flex items-start justify-between gap-3 py-2\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm font-medium\">{label}</div>\n {hint && <div className=\"text-xs text-muted-foreground mt-0.5\">{hint}</div>}\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={on}\n onClick={handleToggle}\n className={cn(\n 'shrink-0 relative inline-flex h-5 w-9 rounded-full border transition-colors',\n on ? 'bg-primary border-primary' : 'bg-muted border-input hover:bg-muted/80',\n )}\n >\n <span\n className={cn(\n 'absolute top-0.5 left-0.5 h-3.5 w-3.5 rounded-full bg-background shadow transition-transform',\n on && 'translate-x-4',\n )}\n />\n </button>\n </div>\n );\n}\n","import { useConfigStore } from '@/stores';\nimport type React from 'react';\nimport { createContext, useContext, useEffect, useState } from 'react';\n\ntype Theme = 'light' | 'dark' | 'system';\n\ninterface ThemeProviderProps {\n children: React.ReactNode;\n defaultTheme?: Theme;\n storageKey?: string;\n}\n\ninterface ThemeProviderState {\n theme: Theme;\n setTheme: (theme: Theme) => void;\n}\n\nconst ThemeProviderContext = createContext<ThemeProviderState | undefined>(undefined);\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'wrongstack-theme',\n}: ThemeProviderProps) {\n const { theme: storeTheme, setTheme: setStoreTheme } = useConfigStore();\n const [theme, setTheme] = useState<Theme>(() => {\n if (typeof window !== 'undefined') {\n return (localStorage.getItem(storageKey) as Theme) || defaultTheme;\n }\n return defaultTheme;\n });\n\n useEffect(() => {\n const root = window.document.documentElement;\n root.classList.remove('light', 'dark');\n\n if (theme === 'system') {\n const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light';\n root.classList.add(systemTheme);\n } else {\n root.classList.add(theme);\n }\n }, [theme]);\n\n const value = {\n theme,\n setTheme: (newTheme: Theme) => {\n localStorage.setItem(storageKey, newTheme);\n setTheme(newTheme);\n setStoreTheme(newTheme);\n },\n };\n\n return <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\n\nexport function useTheme() {\n const context = useContext(ThemeProviderContext);\n if (context === undefined) {\n throw new Error('useTheme must be used within a ThemeProvider');\n }\n return context;\n}\n","import { cn } from '@/lib/utils';\nimport * as React from 'react';\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n },\n);\nInput.displayName = 'Input';\n\nexport { Input };\n","import { cn } from '@/lib/utils';\nimport * as TabsPrimitive from '@radix-ui/react-tabs';\nimport * as React from 'react';\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n 'inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',\n className,\n )}\n {...props}\n />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n {...props}\n />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n","import { useUIStore } from '@/stores';\nimport { Keyboard, X } from 'lucide-react';\nimport { useEffect } from 'react';\n\ninterface Shortcut {\n keys: string[];\n description: string;\n}\n\nconst SHORTCUTS: Array<{ section: string; items: Shortcut[] }> = [\n {\n section: 'Global',\n items: [\n { keys: ['Ctrl', 'K'], description: 'Open command palette' },\n { keys: ['?'], description: 'Show this shortcuts overlay' },\n { keys: ['Ctrl', '\\\\'], description: 'Toggle sidebar' },\n { keys: ['Ctrl', '/'], description: 'Focus the message input' },\n ],\n },\n {\n section: 'Chat input',\n items: [\n { keys: ['Enter'], description: 'Send message' },\n { keys: ['Shift', 'Enter'], description: 'Insert a newline' },\n { keys: ['↑'], description: 'Recall previous prompt (in empty input)' },\n { keys: ['↓'], description: 'Recall next prompt' },\n { keys: ['/'], description: 'Open slash command popup' },\n { keys: ['Tab'], description: 'Autocomplete highlighted command' },\n { keys: ['Esc'], description: 'Dismiss popup / clear input' },\n ],\n },\n {\n section: 'Chat',\n items: [\n { keys: ['Ctrl', 'F'], description: 'Search within current chat' },\n { keys: ['Ctrl', 'L'], description: 'Clear context (same as /clear)' },\n { keys: ['Ctrl', 'N'], description: 'Start a new session (same as /new)' },\n { keys: ['Ctrl', 'E'], description: 'Export chat as markdown' },\n { keys: ['Ctrl', 'M'], description: 'Quick model switcher overlay' },\n { keys: ['Ctrl', 'Shift', 'D'], description: 'Toggle compact UI density' },\n { keys: ['Esc'], description: 'Abort the current run (when running)' },\n ],\n },\n {\n section: 'Chat navigation (when not typing)',\n items: [\n { keys: ['j'], description: 'Focus next message (alias: ↓)' },\n { keys: ['k'], description: 'Focus previous message (alias: ↑)' },\n { keys: ['g'], description: 'Jump to first message' },\n { keys: ['Shift', 'G'], description: 'Jump to last message' },\n { keys: ['c'], description: 'Copy focused message text' },\n { keys: ['Esc'], description: 'Clear focused message' },\n ],\n },\n];\n\nexport function ShortcutsOverlay() {\n const open = useUIStore((s) => s.shortcutsOpen);\n const setOpen = useUIStore((s) => s.setShortcutsOpen);\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n // \"?\" — but only when the user isn't typing in an input. Otherwise\n // typing a literal \"?\" into the chat would open this overlay.\n const target = e.target as HTMLElement | null;\n const tag = target?.tagName?.toLowerCase();\n const isTyping = tag === 'input' || tag === 'textarea' || target?.isContentEditable;\n if (!isTyping && e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) {\n e.preventDefault();\n setOpen(!useUIStore.getState().shortcutsOpen);\n return;\n }\n if (e.key === 'Escape' && useUIStore.getState().shortcutsOpen) {\n e.preventDefault();\n setOpen(false);\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [setOpen]);\n\n if (!open) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 bg-background/60 backdrop-blur-sm flex items-center justify-center px-4\"\n onClick={() => setOpen(false)}\n onKeyDown={(e) => {\n if (e.key === 'Escape') setOpen(false);\n }}\n >\n <div\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n className=\"w-full max-w-2xl rounded-xl border bg-popover shadow-2xl overflow-hidden flex flex-col max-h-[80vh]\"\n >\n <div className=\"flex items-center justify-between px-5 py-4 border-b\">\n <div className=\"flex items-center gap-2\">\n <Keyboard className=\"h-4 w-4 text-muted-foreground\" />\n <h2 className=\"text-sm font-semibold\">Keyboard shortcuts</h2>\n </div>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"text-muted-foreground hover:text-foreground p-1 rounded hover:bg-muted\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <div className=\"overflow-y-auto px-5 py-4 space-y-6\">\n {SHORTCUTS.map((group) => (\n <div key={group.section}>\n <div className=\"text-[10px] uppercase tracking-wider text-muted-foreground mb-2\">\n {group.section}\n </div>\n <div className=\"grid grid-cols-1 gap-1.5\">\n {group.items.map((s) => (\n <div\n key={s.description}\n className=\"flex items-center justify-between gap-3 text-sm px-2 py-1.5 rounded hover:bg-muted/40\"\n >\n <span className=\"text-foreground/80\">{s.description}</span>\n <span className=\"flex items-center gap-1 shrink-0\">\n {s.keys.map((k, ki) => (\n <span key={k} className=\"flex items-center gap-1\">\n {ki > 0 && <span className=\"text-muted-foreground/40 text-xs\">+</span>}\n <kbd className=\"font-mono text-[10px] border rounded px-1.5 py-0.5 bg-background\">\n {k}\n </kbd>\n </span>\n ))}\n </span>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n <div className=\"border-t px-5 py-3 text-xs text-muted-foreground\">\n Press{' '}\n <kbd className=\"font-mono text-[10px] border rounded px-1 py-0.5 bg-background\">?</kbd>{' '}\n any time to reopen this list.\n </div>\n </div>\n </div>\n );\n}\n","import { useWebSocket } from '@/hooks/useWebSocket';\nimport { cn } from '@/lib/utils';\nimport {\n useChatStore,\n useConfigStore,\n useHistoryStore,\n useSessionStore,\n useUIStore,\n} from '@/stores';\nimport {\n CheckCircle2,\n Circle,\n CircleDot,\n Database,\n History,\n ListTodo,\n Loader2,\n MessageSquare,\n PanelLeftClose,\n Pin,\n RefreshCw,\n RotateCcw,\n Search,\n Settings as SettingsIcon,\n Star,\n Trash2,\n Wifi,\n WifiOff,\n X,\n Zap,\n} from 'lucide-react';\nimport { useEffect, useState } from 'react';\nimport { Button } from './ui/button';\nimport { ScrollArea } from './ui/scroll-area';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';\n\n/**\n * Sidebar: navigation + at-a-glance session info. Settings live in the main\n * `SettingsPanel` (open via the gear in ChatView's header or the button at\n * the bottom of this sidebar) — keeping settings in two places confused\n * users who clicked the sidebar \"Settings\" tab expecting a model picker\n * and only found a theme switcher.\n */\nexport function Sidebar() {\n const { toggleSidebar, currentView, setCurrentView } = useUIStore();\n const sidebarWidth = useUIStore((s) => s.sidebarWidth);\n const setSidebarWidth = useUIStore((s) => s.setSidebarWidth);\n const { totalTokens, cost, session, todos } = useSessionStore();\n const { messages, clearMessages } = useChatStore();\n const pinnedIds = useUIStore((s) => s.pinnedIds);\n const unpinAll = useUIStore((s) => s.unpinAll);\n /** Local-only filter for the History tab. Case-insensitive substring\n * match against title, model, provider, and session id — covers the\n * ways users actually try to find a past session (\"the one where I\n * used sonnet…\"). Live as you type; clears with the X button. */\n const [historyQuery, setHistoryQuery] = useState('');\n const favoriteSessionIds = useUIStore((s) => s.favoriteSessionIds);\n const toggleFavoriteSession = useUIStore((s) => s.toggleFavoriteSession);\n const sessionNicknames = useUIStore((s) => s.sessionNicknames);\n const setSessionNickname = useUIStore((s) => s.setSessionNickname);\n /** Inline rename target — null when nothing is being edited, otherwise\n * the session id whose title is currently in edit mode. The draft text\n * is local state so Esc can cancel cleanly. */\n const [renamingId, setRenamingId] = useState<string | null>(null);\n const [renameDraft, setRenameDraft] = useState('');\n /** Pinned bubbles that still exist in the current message list. We prune\n * here (rather than auto-trimming pinnedIds in the store) so the user\n * doesn't lose pins just because we cleared and reloaded a transcript.\n * Shows a single-line preview + jumps to the bubble on click. */\n const pinnedRows = pinnedIds\n .map((id) => messages.find((m) => m.id === id))\n .filter((m): m is NonNullable<typeof m> => !!m && m.content.length > 0);\n const { wsConnected, wsUrl, provider, model } = useConfigStore();\n const {\n entries: historyEntries,\n loading: historyLoading,\n error: historyError,\n } = useHistoryStore();\n const { listSessions, deleteSession, resumeSession, client } = useWebSocket();\n // Pull the current todo snapshot once on connect so a freshly-opened\n // tab doesn't sit todo-less until the next tool runs.\n useEffect(() => {\n if (wsConnected) client?.getTodos?.();\n }, [wsConnected, client]);\n const activeSessionId = session?.id;\n\n // Refresh the history list on tab open + whenever the active session id\n // changes (a /new would push the previous session into history).\n useEffect(() => {\n void activeSessionId;\n if (currentView === 'history' && wsConnected) {\n listSessions(50);\n }\n }, [currentView, wsConnected, activeSessionId, listSessions]);\n\n const formatDuration = (start: number | null) => {\n if (!start) return '--';\n const seconds = Math.floor((Date.now() - start) / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n return `${minutes}m`;\n };\n\n const formatRelative = (iso: string): string => {\n const ts = Date.parse(iso);\n if (Number.isNaN(ts)) return '';\n const diff = Date.now() - ts;\n if (diff < 60_000) return 'just now';\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;\n if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;\n const days = Math.floor(diff / 86_400_000);\n if (days < 7) return `${days}d ago`;\n return new Date(ts).toLocaleDateString();\n };\n\n /** Bucket history rows into Today / Yesterday / This week / Earlier so the\n * list reads like a journal instead of one long undifferentiated stream.\n * Boundaries are local midnight. Entries with unparseable timestamps\n * fall into \"Earlier\" so they're not silently dropped. */\n const groupedHistory = ((): Array<{\n label: string;\n rows: typeof historyEntries;\n star?: boolean;\n }> => {\n const q = historyQuery.trim().toLowerCase();\n const filtered = q\n ? historyEntries.filter(\n (e) =>\n e.title.toLowerCase().includes(q) ||\n e.model.toLowerCase().includes(q) ||\n e.provider.toLowerCase().includes(q) ||\n e.id.toLowerCase().includes(q),\n )\n : historyEntries;\n const startOfDay = new Date();\n startOfDay.setHours(0, 0, 0, 0);\n const todayStart = startOfDay.getTime();\n const yesterdayStart = todayStart - 86_400_000;\n const weekStart = todayStart - 6 * 86_400_000;\n const buckets: {\n today: typeof historyEntries;\n yesterday: typeof historyEntries;\n week: typeof historyEntries;\n older: typeof historyEntries;\n } = {\n today: [],\n yesterday: [],\n week: [],\n older: [],\n };\n for (const e of filtered) {\n const ts = Date.parse(e.startedAt);\n if (Number.isNaN(ts)) {\n buckets.older.push(e);\n continue;\n }\n if (ts >= todayStart) buckets.today.push(e);\n else if (ts >= yesterdayStart) buckets.yesterday.push(e);\n else if (ts >= weekStart) buckets.week.push(e);\n else buckets.older.push(e);\n }\n // Favorites form their own bucket at the very top, regardless of when\n // they were started — that's the whole point of starring a session.\n const favSet = new Set(favoriteSessionIds);\n const favorites = filtered.filter((e) => favSet.has(e.id));\n const out: Array<{ label: string; rows: typeof historyEntries; star?: boolean }> = [];\n if (favorites.length) out.push({ label: 'Favorites', rows: favorites, star: true });\n // Strip favorites from the date buckets so they don't appear twice.\n const dedupe = (arr: typeof historyEntries) => arr.filter((e) => !favSet.has(e.id));\n const today = dedupe(buckets.today);\n const yesterday = dedupe(buckets.yesterday);\n const week = dedupe(buckets.week);\n const older = dedupe(buckets.older);\n if (today.length) out.push({ label: 'Today', rows: today });\n if (yesterday.length) out.push({ label: 'Yesterday', rows: yesterday });\n if (week.length) out.push({ label: 'This week', rows: week });\n if (older.length) out.push({ label: 'Earlier', rows: older });\n return out;\n })();\n\n // Drag handle: track pointer movement on a sibling element. We start the\n // drag on mousedown, update width on mousemove, drop on mouseup. Bound on\n // window so the user can drag past the handle without losing focus.\n const startDrag = (e: React.MouseEvent) => {\n e.preventDefault();\n const startX = e.clientX;\n const startWidth = sidebarWidth;\n const onMove = (ev: MouseEvent) => {\n setSidebarWidth(startWidth + (ev.clientX - startX));\n };\n const onUp = () => {\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n };\n\n return (\n <aside\n style={{ width: `${sidebarWidth}px` }}\n className=\"relative border-r bg-card flex flex-col shrink-0\"\n >\n {/* Drag handle. Hit area is wider than the visible rail so users\n don't have to be pixel-perfect; the rail itself gets a clear\n highlight + a centered grip indicator on hover so the affordance\n isn't invisible until you try. Double-click resets to default. */}\n <div\n onMouseDown={startDrag}\n onDoubleClick={() => setSidebarWidth(288)}\n className=\"group/handle absolute top-0 right-0 h-full w-2 cursor-col-resize z-10 flex items-center justify-end\"\n title=\"Drag to resize · double-click to reset\"\n >\n <div className=\"h-full w-px bg-border group-hover/handle:bg-primary/60 group-hover/handle:w-0.5 transition-all\" />\n <div className=\"absolute right-0 top-1/2 -translate-y-1/2 flex flex-col gap-0.5 opacity-0 group-hover/handle:opacity-100 transition-opacity pr-0.5\">\n <span className=\"h-1 w-1 rounded-full bg-primary/70\" />\n <span className=\"h-1 w-1 rounded-full bg-primary/70\" />\n <span className=\"h-1 w-1 rounded-full bg-primary/70\" />\n </div>\n </div>\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b\">\n <div className=\"flex items-center gap-2\">\n <div className=\"w-6 h-6 rounded bg-primary flex items-center justify-center\">\n <Zap className=\"h-4 w-4 text-primary-foreground\" />\n </div>\n <span className=\"text-sm font-semibold tracking-tight\">WrongStack</span>\n </div>\n <Button variant=\"ghost\" size=\"icon\" onClick={toggleSidebar}>\n <PanelLeftClose className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {/* Navigation — Chat | History only. Settings opens the full panel. */}\n <Tabs\n value={currentView === 'settings' ? 'chat' : currentView}\n onValueChange={(v) => setCurrentView(v as 'chat' | 'history')}\n className=\"flex-1 flex flex-col\"\n >\n <TabsList className=\"w-full rounded-none bg-transparent p-2 h-auto grid grid-cols-2\">\n <TabsTrigger\n value=\"chat\"\n className=\"flex-col gap-1.5 py-2 data-[state=active]:bg-primary/10\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n <span className=\"text-xs\">Chat</span>\n </TabsTrigger>\n <TabsTrigger\n value=\"history\"\n className=\"flex-col gap-1.5 py-2 data-[state=active]:bg-primary/10\"\n >\n <History className=\"h-4 w-4\" />\n <span className=\"text-xs\">History</span>\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"chat\" className=\"flex-1 flex flex-col m-0 overflow-hidden\">\n {/* Connection status */}\n <div className=\"px-4 py-3 border-b\">\n <div\n className={cn(\n 'flex items-center gap-2 px-3 py-2 rounded-lg text-sm',\n wsConnected\n ? 'bg-green-500/10 text-green-600 dark:text-green-400'\n : 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400',\n )}\n >\n {wsConnected ? <Wifi className=\"h-4 w-4\" /> : <WifiOff className=\"h-4 w-4\" />}\n <span className=\"font-medium\">{wsConnected ? 'Connected' : 'Disconnected'}</span>\n </div>\n <div className=\"text-xs text-muted-foreground mt-2 px-1 font-mono\">{wsUrl}</div>\n </div>\n\n {/* Active model — clickable shortcut to settings */}\n <button\n type=\"button\"\n onClick={() => setCurrentView('settings')}\n className=\"px-4 py-3 border-b text-left hover:bg-muted/40 transition-colors\"\n >\n <div className=\"text-[10px] uppercase tracking-wider text-muted-foreground mb-1\">\n Active model\n </div>\n <div className=\"font-mono text-xs truncate\">\n <span className=\"text-muted-foreground\">{provider || '—'}</span>\n <span className=\"text-muted-foreground/40 mx-1\">/</span>\n <span className=\"font-medium\">{model || '—'}</span>\n </div>\n </button>\n\n {/* Session Stats */}\n <div className=\"px-4 py-3 border-b space-y-3\">\n <h3 className=\"text-sm font-medium flex items-center gap-2\">\n <Database className=\"h-4 w-4 text-muted-foreground\" />\n Session\n </h3>\n <div className=\"grid grid-cols-2 gap-2 text-xs\">\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Messages</span>\n <span className=\"text-lg font-semibold\">{messages.length}</span>\n </div>\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Duration</span>\n <span className=\"text-lg font-semibold\">\n {formatDuration(session?.startedAt ?? null)}\n </span>\n </div>\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Input</span>\n <span className=\"text-lg font-semibold\">{totalTokens.input.toLocaleString()}</span>\n </div>\n <div className=\"flex flex-col p-2 rounded-lg bg-muted/50\">\n <span className=\"text-muted-foreground\">Output</span>\n <span className=\"text-lg font-semibold\">{totalTokens.output.toLocaleString()}</span>\n </div>\n </div>\n {cost > 0 && (\n <div className=\"flex justify-between items-center p-2 rounded-lg bg-green-500/10\">\n <span className=\"text-sm text-muted-foreground\">Cost</span>\n <span className=\"text-lg font-semibold text-green-600 dark:text-green-400\">\n ${cost.toFixed(4)}\n </span>\n </div>\n )}\n </div>\n\n {/* Live TODO list — populated by the backend's todos.updated\n broadcast after every tool.executed. Empty array → hide the\n section entirely so a vanilla session keeps its existing\n vertical rhythm. */}\n {todos.length > 0 && (\n <div className=\"px-4 py-3 border-b space-y-2\">\n <h3 className=\"text-sm font-medium flex items-center justify-between\">\n <span className=\"flex items-center gap-2\">\n <ListTodo className=\"h-4 w-4 text-muted-foreground\" />\n Todos\n </span>\n <span className=\"text-[10px] text-muted-foreground tabular-nums\">\n {todos.filter((t) => t.status === 'completed').length}/{todos.length}\n </span>\n </h3>\n <ul className=\"space-y-1 max-h-48 overflow-y-auto pr-1\">\n {todos.map((t) => {\n const Icon =\n t.status === 'completed'\n ? CheckCircle2\n : t.status === 'in_progress'\n ? CircleDot\n : Circle;\n const tone =\n t.status === 'completed'\n ? 'text-green-600 dark:text-green-400 line-through opacity-70'\n : t.status === 'in_progress'\n ? 'text-amber-600 dark:text-amber-400'\n : 'text-muted-foreground';\n return (\n <li\n key={t.id}\n className={cn('flex items-start gap-2 text-xs leading-snug', tone)}\n >\n <Icon className=\"h-3.5 w-3.5 mt-0.5 shrink-0\" />\n <span className=\"break-words\">\n {t.status === 'in_progress' && t.activeForm ? t.activeForm : t.content}\n </span>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {/* Pinned answers — click to scroll the chat to the bubble. We\n briefly highlight the target via a CSS class on data-message-id\n so the user can tell which one we just snapped to. */}\n {pinnedRows.length > 0 && (\n <div className=\"px-4 py-3 border-b space-y-2\">\n <h3 className=\"text-sm font-medium flex items-center justify-between\">\n <span className=\"flex items-center gap-2\">\n <Pin className=\"h-4 w-4 text-amber-500\" />\n Pinned\n </span>\n <button\n type=\"button\"\n onClick={unpinAll}\n className=\"text-[10px] text-muted-foreground hover:text-destructive\"\n >\n Clear\n </button>\n </h3>\n <ul className=\"space-y-1 max-h-48 overflow-y-auto pr-1\">\n {pinnedRows.map((m) => {\n const preview = m.content.replace(/\\s+/g, ' ').slice(0, 80);\n return (\n <li key={m.id}>\n <button\n type=\"button\"\n onClick={() => {\n const el = document.querySelector(`[data-message-id=\"${m.id}\"]`);\n if (!el) return;\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n el.classList.add('ring-2', 'ring-amber-500/60');\n setTimeout(() => {\n el.classList.remove('ring-2', 'ring-amber-500/60');\n }, 1600);\n }}\n className=\"w-full text-left text-xs px-2 py-1.5 rounded bg-muted/40 hover:bg-muted/70 border border-amber-500/20 leading-snug\"\n title={m.content.slice(0, 400)}\n >\n {preview}\n {m.content.length > 80 ? '…' : ''}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {/* Quick Actions */}\n <div className=\"px-4 py-3 border-b space-y-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full justify-start text-destructive hover:text-destructive\"\n onClick={() => {\n // Match /clear: drop UI + backend context together so the\n // model doesn't keep replying with knowledge from messages\n // the user just told us to forget.\n clearMessages();\n client?.clearContext?.();\n }}\n >\n <Trash2 className=\"h-4 w-4 mr-2\" />\n Clear context\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={() => client?.newSession?.()}\n disabled={!wsConnected}\n >\n <RotateCcw className=\"h-4 w-4 mr-2\" />\n New session\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={() => client?.compactContext?.()}\n disabled={!wsConnected}\n >\n <Database className=\"h-4 w-4 mr-2\" />\n Compact context\n </Button>\n </div>\n\n <div className=\"flex-1\" />\n\n {/* Footer: settings entry point */}\n <div className=\"px-3 py-3 border-t\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"w-full justify-start\"\n onClick={() => setCurrentView('settings')}\n >\n <SettingsIcon className=\"h-4 w-4 mr-2\" />\n Settings\n </Button>\n </div>\n </TabsContent>\n\n <TabsContent value=\"history\" className=\"flex-1 m-0 flex flex-col overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-2 border-b\">\n <span className=\"text-xs uppercase tracking-wider text-muted-foreground\">\n Recent sessions\n </span>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-6 w-6\"\n onClick={() => listSessions(50)}\n disabled={!wsConnected}\n title=\"Refresh\"\n >\n {historyLoading ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <RefreshCw className=\"h-3.5 w-3.5\" />\n )}\n </Button>\n </div>\n {/* History search — only renders once we have something to filter,\n otherwise it's just empty UI clutter on a fresh install. */}\n {historyEntries.length > 3 && (\n <div className=\"px-3 py-2 border-b\">\n <div className=\"relative\">\n <Search className=\"absolute left-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground/60\" />\n <input\n type=\"text\"\n value={historyQuery}\n onChange={(e) => setHistoryQuery(e.target.value)}\n placeholder=\"Filter title, model, provider…\"\n className=\"w-full pl-7 pr-7 py-1 text-xs rounded-md bg-muted/40 border border-transparent focus:bg-background focus:border-input focus:outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50\"\n />\n {historyQuery && (\n <button\n type=\"button\"\n onClick={() => setHistoryQuery('')}\n className=\"absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground/60 hover:text-foreground p-0.5\"\n title=\"Clear filter\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n </div>\n )}\n\n {historyError && (\n <div className=\"px-4 py-2 text-xs text-destructive bg-destructive/5 border-b\">\n {historyError}\n </div>\n )}\n\n <ScrollArea className=\"flex-1\">\n {historyEntries.length === 0 && !historyLoading ? (\n <div className=\"text-center text-muted-foreground py-8 px-4\">\n <History className=\"h-8 w-8 mx-auto mb-3 opacity-20\" />\n <p className=\"text-sm font-medium\">No history yet</p>\n <p className=\"text-xs mt-1\">Your conversations will appear here</p>\n </div>\n ) : groupedHistory.length === 0 ? (\n <div className=\"text-center text-muted-foreground py-8 px-4\">\n <Search className=\"h-8 w-8 mx-auto mb-3 opacity-20\" />\n <p className=\"text-sm font-medium\">No matches</p>\n <p className=\"text-xs mt-1\">Try a different filter</p>\n </div>\n ) : (\n <div className=\"p-2 space-y-3\">\n {groupedHistory.map((group) => (\n <div key={group.label} className=\"space-y-1\">\n <div\n className={cn(\n 'sticky top-0 z-[1] px-1 pb-1 text-[10px] uppercase tracking-wider font-semibold bg-card/90 backdrop-blur-sm flex items-center gap-1',\n group.star ? 'text-amber-500' : 'text-muted-foreground/80',\n )}\n >\n {group.star && <Star className=\"h-3 w-3 fill-current\" />}\n {group.label}{' '}\n <span className=\"text-muted-foreground/50 font-normal normal-case ml-1\">\n ({group.rows.length})\n </span>\n </div>\n {group.rows.map((entry) => (\n <div\n key={entry.id}\n className={cn(\n 'group relative rounded-md border text-sm transition-colors',\n entry.isCurrent\n ? 'bg-primary/5 border-primary/40'\n : 'bg-card border-border/60 hover:bg-muted/40 hover:border-primary/40',\n )}\n >\n <button\n type=\"button\"\n disabled={entry.isCurrent || renamingId === entry.id}\n onClick={() => resumeSession(entry.id)}\n onDoubleClick={(e) => {\n // Double-click anywhere on the row enters rename mode.\n // We stop propagation so it doesn't also fire the\n // single-click resume handler.\n e.stopPropagation();\n setRenamingId(entry.id);\n setRenameDraft(sessionNicknames[entry.id] ?? entry.title ?? '');\n }}\n className=\"block w-full rounded-md px-3 py-2 pr-16 text-left disabled:cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <div className=\"min-w-0 flex-1\">\n {renamingId === entry.id ? (\n <input\n value={renameDraft}\n onChange={(e) => setRenameDraft(e.target.value)}\n onClick={(e) => e.stopPropagation()}\n onBlur={() => {\n setSessionNickname(entry.id, renameDraft);\n setRenamingId(null);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n setSessionNickname(entry.id, renameDraft);\n setRenamingId(null);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setRenamingId(null);\n }\n }}\n placeholder={entry.title || 'Nickname'}\n className=\"w-full text-sm bg-background border border-input rounded px-1.5 py-0.5 focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n ) : (\n <div\n className=\"font-medium truncate text-foreground\"\n title={\n sessionNicknames[entry.id]\n ? `${sessionNicknames[entry.id]} — original: ${entry.title}`\n : `${entry.title}\\n\\nDouble-click to rename`\n }\n >\n {sessionNicknames[entry.id] || entry.title || '(empty)'}\n </div>\n )}\n <div className=\"text-[10px] text-muted-foreground font-mono truncate mt-0.5\">\n {entry.provider}/{entry.model}\n </div>\n <div className=\"flex items-center gap-2 text-[10px] text-muted-foreground/80 mt-0.5\">\n <span>{formatRelative(entry.startedAt)}</span>\n {entry.tokenTotal > 0 && (\n <>\n <span>·</span>\n <span className=\"tabular-nums\">\n {entry.tokenTotal.toLocaleString()} tok\n </span>\n </>\n )}\n {entry.isCurrent && (\n <>\n <span>·</span>\n <span className=\"text-primary font-medium\">active</span>\n </>\n )}\n </div>\n </div>\n </button>\n {/* Star toggle — always rendered (not hidden behind hover)\n when already favorited, so the user can tell at a\n glance which rows are starred without hovering each\n one. The Trash sits beside it but only on hover. */}\n <div className=\"absolute right-2 top-2 flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => toggleFavoriteSession(entry.id)}\n className={cn(\n 'transition-opacity hover:text-amber-500',\n favoriteSessionIds.includes(entry.id)\n ? 'opacity-100 text-amber-500'\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground',\n )}\n title={\n favoriteSessionIds.includes(entry.id)\n ? 'Unfavorite'\n : 'Mark as favorite'\n }\n >\n <Star\n className={cn(\n 'h-3.5 w-3.5',\n favoriteSessionIds.includes(entry.id) && 'fill-current',\n )}\n />\n </button>\n {!entry.isCurrent && (\n <button\n type=\"button\"\n onClick={() => {\n if (window.confirm(`Delete session \"${entry.title}\"?`)) {\n deleteSession(entry.id);\n }\n }}\n className=\"opacity-0 group-hover:opacity-100 transition-opacity text-muted-foreground hover:text-destructive\"\n title=\"Delete session\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n ))}\n </div>\n )}\n </ScrollArea>\n </TabsContent>\n </Tabs>\n </aside>\n );\n}\n"],"mappings":"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","names":["React","create","useEffect","client","useEffect","ArrowDown","ArrowUp","Bot","Brain","Cpu","Monitor","Moon","Sun","Zap","useCallback","useEffect","useRef","useState","useCallback","useEffect","useRef","useState","useEffect","useRef","jsx","jsxs","Icon","useEffect","useRef","useState","jsx","jsxs","client","jsx","jsx","jsxs","client","useState","useRef","useCallback","useEffect","tone","RotateCcw","useEffect","useState","jsx","jsxs","useEffect","useRef","useState","Fragment","jsx","jsxs","CheckCircle2","ChevronDown","ChevronRight","Download","Pencil","RotateCcw","XCircle","memo","useState","useMemo","jsx","jsxs","DiffView","i","j","memo","useMemo","useState","Fragment","jsx","jsxs","ToolResult","Fragment","jsx","jsxs","useState","memo","MessageBubble","client","ChevronDown","ChevronRight","XCircle","CheckCircle2","Download","Pencil","RotateCcw","Check","ChevronDown","useEffect","useRef","useState","jsx","jsxs","Search","X","useEffect","useMemo","useRef","useState","jsx","jsxs","CheckCircle2","ChevronDown","ChevronRight","Loader2","Terminal","XCircle","memo","useState","jsx","jsxs","memo","ToolGroup","useState","Terminal","ChevronDown","ChevronRight","Loader2","XCircle","CheckCircle2","ArchiveRestore","Clock","Search","Sparkles","Wrench","useEffect","useState","Fragment","jsx","jsxs","client","Icon","React","jsx","jsxs","Fragment","jsx","jsxs","Brain","useRef","useState","useCallback","useEffect","formatDuration","Zap","Cpu","Sun","Moon","Monitor","ArrowDown","ArrowUp","Bot","AlertTriangle","Terminal","Wrench","useEffect","X","React","jsx","jsxs","jsx","jsxs","X","jsx","jsxs","Terminal","Wrench","useEffect","Icon","AlertTriangle","Loader2","RotateCcw","WifiOff","X","useEffect","useState","jsx","jsxs","AlertTriangle","jsx","jsxs","AlertTriangle","ArrowRight","Cpu","Search","useEffect","useMemo","useRef","useState","jsx","jsxs","client","CheckCircle2","Cpu","Globe","Loader2","Monitor","Moon","RefreshCw","Sun","Trash2","X","useState","useEffect","useCallback","useEffect","useState","jsx","React","jsx","React","jsx","Fragment","jsx","jsxs","useState","useEffect","useCallback","X","Cpu","Globe","CheckCircle2","Loader2","Trash2","RefreshCw","Sun","Moon","Monitor","Keyboard","X","useEffect","jsx","jsxs","CheckCircle2","Database","Loader2","Pin","RefreshCw","RotateCcw","Search","SettingsIcon","Trash2","Wifi","WifiOff","X","Zap","useEffect","useState","Fragment","jsx","jsxs","useState","client","useEffect","formatDuration","Zap","Wifi","WifiOff","Database","Icon","CheckCircle2","tone","Pin","Trash2","RotateCcw","SettingsIcon","Loader2","RefreshCw","Search","X","jsx","jsxs","useEffect","jsx","React"]}