import { useState } from 'react'; import { createPortal } from 'react-dom'; import { LoaderCircle, Pencil, PanelRightClose, PanelRightOpen, RotateCcw, Square, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { CodingConversationRenameDialog } from './CodingConversationRenameDialog'; import { cn } from '@/lib/utils'; import { useSettingsStore } from '@/stores/settings'; import { TITLEBAR_LOGO_WIDTH, WINDOWS_TITLEBAR_CONTROLS_WIDTH } from '@/components/layout/titlebar-metrics'; import type { ConversationSnapshot } from '@/types/coding-conversation'; import type { CodingConversationMetadata } from '@/types/coding-project'; const WORKSPACE_COLUMN_WIDTH = 256; interface LocalActionError { message: string; backendCode?: string; startedGeneration: number; startedSeq: number; } function localActionError( error: unknown, startedGeneration: number, startedSeq: number, ): LocalActionError { const details = error && typeof error === 'object' && 'details' in error && error.details && typeof error.details === 'object' ? error.details as { backendCode?: unknown } : null; return { message: error instanceof Error ? error.message : String(error), ...(typeof details?.backendCode === 'string' ? { backendCode: details.backendCode } : {}), startedGeneration, startedSeq, }; } export function CodingConversationHeader({ conversation, snapshot, onRename, onAbort, onRecover, browserOpen = false, browserAvailable = false, onToggleBrowser, }: { conversation: CodingConversationMetadata | null; snapshot: ConversationSnapshot | null; onRename(title: string): Promise; onAbort(): Promise; onRecover(): Promise; browserOpen?: boolean; browserAvailable?: boolean; onToggleBrowser?(): void; }) { const sidebarCollapsed = useSettingsStore((state) => state.sidebarCollapsed); const [busyAction, setBusyAction] = useState(null); const [actionError, setActionError] = useState(null); const [renameOpen, setRenameOpen] = useState(false); const runStatus = snapshot?.run.status ?? 'preparing'; const running = ['queued', 'running', 'retrying', 'compacting', 'aborting'].includes(runStatus); const recoverable = Boolean(snapshot?.run.error?.recoverable || snapshot?.worker.error?.recoverable); const runtimeErrorCode = snapshot?.run.error?.code ?? snapshot?.worker.error?.code; const cursorAdvancedPastAction = Boolean(actionError && snapshot && ( snapshot.cursor.workerGeneration > actionError.startedGeneration || (snapshot.cursor.workerGeneration === actionError.startedGeneration && snapshot.cursor.seq > actionError.startedSeq) )); const visibleActionError = actionError?.backendCode === 'CODING_REQUEST_UNCERTAIN' && !running && runtimeErrorCode !== 'CODING_REQUEST_UNCERTAIN' && cursorAdvancedPastAction ? null : actionError; const platform = typeof window === 'undefined' ? undefined : window.electron?.platform; const hasCustomTitleBar = platform === 'darwin' || platform === 'win32'; const perform = (key: string, action: () => Promise) => { if (busyAction || !conversation) return; const startedGeneration = snapshot?.cursor.workerGeneration ?? 0; const startedSeq = snapshot?.cursor.seq ?? 0; setBusyAction(key); setActionError(null); void action() .catch((error) => setActionError(localActionError(error, startedGeneration, startedSeq))) .finally(() => setBusyAction((current) => current === key ? null : current)); }; const iconButtonClass = 'no-drag h-8 w-8 shrink-0 rounded-md p-0'; const header = (
{onToggleBrowser ? ( ) : null} {running && runStatus !== 'aborting' && ( )} {recoverable && ( )}
); return ( <> {hasCustomTitleBar && typeof document !== 'undefined' ? createPortal(
{header}
, document.body, ) : header} {visibleActionError && (

{visibleActionError.message}

)} {renameOpen && conversation && setRenameOpen(false)} />} ); }