import { useMemo, useState } from 'react'; import { Activity, BadgeCheck, Check, CircleHelp, Clipboard, Coins, Cpu, Gauge, Package, Search, Server, Sparkles, TerminalSquare, Timer, RefreshCw, X, } from 'lucide-react'; import { Badge, Button, Dialog, DialogContent, DialogTitle, Input } from '../../../../shared/view/ui'; import type { LLMProvider, ProviderModelsCacheInfo, ProviderModelsDefinition } from '../../../../types/app'; import type { CommandModalPayload, CostCommandData, HelpCommandData, ModelCommandData, StatusCommandData, } from '../../hooks/useChatComposerState'; type CommandResultModalProps = { payload: CommandModalPayload | null; onClose: () => void; providerModelCatalog: Partial>; providerModelCacheCatalog: Partial>; providerModelsRefreshing: boolean; onHardRefreshProviderModels: () => void; currentSessionId: string | null; onSelectProviderModel: ( provider: LLMProvider, model: string, sessionId?: string | null, ) => Promise<{ scope: 'default' | 'session'; changed: boolean; model: string; }>; }; type CommandEntry = { name: string; description?: string; namespace?: string; }; type ModelOption = { value: string; label?: string; description?: string; }; const formatUpdatedAt = (value?: string) => { if (!value) { return 'Not cached yet'; } const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return 'Not cached yet'; } return parsed.toLocaleString(); }; const PROVIDER_LABELS: Record = { claude: 'Claude', cursor: 'Cursor', codex: 'Codex', gemini: 'Gemini', opencode: 'OpenCode', }; const FALLBACK_COMMANDS: CommandEntry[] = [ { name: '/models', description: 'Browse available models for the active provider.' }, { name: '/cost', description: 'Review token usage for the active session.' }, { name: '/status', description: 'Inspect runtime, version, provider, and environment status.' }, { name: '/memory', description: 'Open the project CLAUDE.md memory file.' }, { name: '/config', description: 'Open settings and configuration.' }, { name: '/help', description: 'Show command documentation and syntax.' }, ]; const getProviderLabel = (provider: string | undefined, fallback = 'Unknown') => { if (!provider) { return fallback; } return PROVIDER_LABELS[provider] || provider; }; const formatNumber = (value: number) => { if (!Number.isFinite(value)) { return '0'; } return value.toLocaleString(); }; function MetricCard({ label, value, icon: Icon, tone = 'neutral', compact = false, }: { label: string; value: string; icon: typeof Activity; tone?: 'neutral' | 'primary' | 'success'; compact?: boolean; }) { const toneClass = tone === 'primary' ? 'border-primary/35 bg-primary/10 text-primary' : tone === 'success' ? 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300' : 'border-border/70 bg-background/75 text-muted-foreground'; return (

{label}

{value}

); } function SearchField({ value, onChange, placeholder, }: { value: string; onChange: (value: string) => void; placeholder: string; }) { return (
onChange(event.target.value)} placeholder={placeholder} className="h-10 rounded-xl border-border/70 bg-background/75 pl-9 pr-3 shadow-none focus-visible:ring-primary/40" />
); } function HelpContent({ data }: { data: HelpCommandData }) { const [query, setQuery] = useState(''); const commands = (Array.isArray(data.commands) && data.commands.length > 0 ? data.commands : FALLBACK_COMMANDS) as CommandEntry[]; const filteredCommands = useMemo(() => { const normalized = query.trim().toLowerCase(); if (!normalized) { return commands; } return commands.filter((command) => { const haystack = `${command.name} ${command.description || ''} ${command.namespace || ''}`.toLowerCase(); return haystack.includes(normalized); }); }, [commands, query]); return (
{filteredCommands.map((command, index) => (
{command.name} {command.namespace || 'builtin'}

{command.description || 'No description available.'}

))}
{filteredCommands.length === 0 && (
No commands match that filter.
)}
); } function ModelsContent({ data, providerModelCatalog, providerModelCacheCatalog, providerModelsRefreshing, onHardRefreshProviderModels, currentSessionId, onSelectProviderModel, }: { data: ModelCommandData; providerModelCatalog: Partial>; providerModelCacheCatalog: Partial>; providerModelsRefreshing: boolean; onHardRefreshProviderModels: () => void; currentSessionId: string | null; onSelectProviderModel: CommandResultModalProps['onSelectProviderModel']; }) { const [query, setQuery] = useState(''); const [copiedModel, setCopiedModel] = useState(null); const [changingModel, setChangingModel] = useState(null); const [pendingSessionModel, setPendingSessionModel] = useState(null); const [selectionNotice, setSelectionNotice] = useState(null); const currentProvider = (data?.current?.provider || 'claude') as LLMProvider; const currentModel = data?.current?.model || 'Unknown'; const providerLabel = data?.current?.providerLabel || getProviderLabel(currentProvider); const liveDefinition = providerModelCatalog[currentProvider]; const currentCache = providerModelCacheCatalog[currentProvider] ?? data?.cache; const availableOptions = useMemo(() => { if (liveDefinition?.OPTIONS && liveDefinition.OPTIONS.length > 0) { return liveDefinition.OPTIONS; } if (Array.isArray(data?.availableOptions) && data.availableOptions.length > 0) { return data.availableOptions; } const availableModels = Array.isArray(data?.availableModels) ? data.availableModels : []; return availableModels.map((model) => ({ value: model, label: model })); }, [data, liveDefinition]); const defaultModel = liveDefinition?.DEFAULT || data?.defaultModel || currentModel; const filteredOptions = useMemo(() => { const normalized = query.trim().toLowerCase(); if (!normalized) { return availableOptions; } return availableOptions.filter((option) => { const haystack = `${option.value} ${option.label || ''} ${option.description || ''}`.toLowerCase(); return haystack.includes(normalized); }); }, [availableOptions, query]); const activeOption = availableOptions.find((option) => option.value === currentModel); const hasConcreteSessionId = typeof currentSessionId === 'string' && currentSessionId.trim().length > 0; const copyModel = (model: string) => { if (typeof navigator !== 'undefined' && navigator.clipboard) { void navigator.clipboard.writeText(model).catch(() => undefined); } setCopiedModel(model); window.setTimeout(() => { setCopiedModel((current) => (current === model ? null : current)); }, 1300); }; const handleSelectModel = async (model: string) => { setChangingModel(model); try { const result = await onSelectProviderModel(currentProvider, model, currentSessionId); if (result.scope === 'session') { setPendingSessionModel(result.model); setSelectionNotice(`Next response will resume with ${result.model}.`); return; } setPendingSessionModel(null); setSelectionNotice(`Default ${providerLabel} model set to ${result.model}.`); } catch (error) { const message = error instanceof Error ? error.message : 'Unable to change the model right now.'; setSelectionNotice(message); } finally { setChangingModel(null); } }; return (
{providerLabel} {availableOptions.length} models

Active Model

{currentModel}

{activeOption?.label && activeOption.label !== currentModel && (

{activeOption.label}

)} {activeOption?.description && (

{activeOption.description}

)} {pendingSessionModel && pendingSessionModel !== currentModel && (

Next response: {pendingSessionModel}

)}

Default

{defaultModel}

Updated

{formatUpdatedAt(currentCache?.updatedAt)}

Catalog Refresh

All providers

Model lists are cached for 3 days. Refresh after CLI, auth, or config changes, or when a new model is missing.

{hasConcreteSessionId ? 'Selecting a model stores a session override and applies it on the next response for this session.' : 'Selecting a model updates the default model used for new turns in this provider.'} {selectionNotice && {selectionNotice}}
{filteredOptions.length} shown
{filteredOptions.length > 0 ? (
{filteredOptions.map((option, index) => { const isCurrent = option.value === currentModel; const wasCopied = copiedModel === option.value; const isPendingSelection = option.value === pendingSessionModel; const isChanging = option.value === changingModel; return (
); })}
) : (
No models match that search.
)}
); } function CostContent({ data }: { data: CostCommandData }) { const used = Number(data.tokenUsage?.used ?? 0); const total = Number(data.tokenUsage?.total ?? 0); const model = data.model || 'Unknown'; const provider = getProviderLabel(data.provider, data.provider || 'Unknown'); const hasBreakdown = typeof data.tokenBreakdown?.input === 'number' || typeof data.tokenBreakdown?.output === 'number'; const usageRows = [ { label: 'Total tokens used', value: formatNumber(used), icon: Activity }, ...(hasBreakdown ? [ { label: 'Input tokens', value: formatNumber(Number(data.tokenBreakdown?.input ?? 0)), icon: TerminalSquare, }, { label: 'Output tokens', value: formatNumber(Number(data.tokenBreakdown?.output ?? 0)), icon: Coins, }, ] : [ { label: 'Breakdown', value: 'Unavailable', icon: TerminalSquare, }, ]), ...(total > 0 ? [{ label: 'Context window', value: formatNumber(total), icon: Gauge }] : []), ]; return (
{usageRows.map((row) => { const Icon = row.icon; return (
{row.label}
{row.value}
); })}

Provider

{provider}

Model

{model}

); } function StatusContent({ data }: { data: StatusCommandData }) { const memoryRssMb = data.memoryUsage?.rssMb; const rows = [ { label: 'Package', value: data.packageName || 'claude-code-ui', icon: Package }, { label: 'Version', value: data.version || 'Unknown', icon: BadgeCheck, tone: 'success' as const }, { label: 'Uptime', value: data.uptime || 'Unknown', icon: Timer }, { label: 'Provider', value: getProviderLabel(data.provider, data.provider || 'Unknown'), icon: Server, tone: 'primary' as const }, { label: 'Model', value: data.model || 'Unknown', icon: Cpu }, { label: 'Node.js', value: data.nodeVersion || 'Unknown', icon: TerminalSquare }, { label: 'Platform', value: data.platform || 'Unknown', icon: Activity }, { label: 'Memory', value: typeof memoryRssMb === 'number' ? `${memoryRssMb} MB RSS` : 'Unknown', icon: Gauge }, ]; return (

Runtime online

Process {data.pid ? `#${data.pid}` : 'status'} is responding.

Healthy
{rows.map((row) => ( ))}
); } export default function CommandResultModal({ payload, onClose, providerModelCatalog, providerModelCacheCatalog, providerModelsRefreshing, onHardRefreshProviderModels, currentSessionId, onSelectProviderModel, }: CommandResultModalProps) { const isOpen = Boolean(payload); const kind = payload?.kind; const isModelsModal = kind === 'models'; const modalMeta = { help: { eyebrow: 'Command center', title: 'Help & Shortcuts', subtitle: 'Search built-ins, syntax patterns, and command usage without leaving the chat.', icon: CircleHelp, }, models: { eyebrow: 'Model inventory', title: 'Available Models', subtitle: 'Browse, search, and copy model IDs for the active provider.', icon: Cpu, }, cost: { eyebrow: 'Session telemetry', title: 'Token Usage', subtitle: 'Input, output, and total token counts for this session.', icon: Coins, }, status: { eyebrow: 'Runtime health', title: 'System Status', subtitle: 'Version, provider, runtime, and environment details in one place.', icon: Activity, }, } as const; const activeMeta = kind ? modalMeta[kind] : null; const HeaderIcon = activeMeta?.icon || Sparkles; return ( !open && onClose()}> {activeMeta?.title || 'Command Result'}

{activeMeta?.eyebrow}

{activeMeta?.title}

{activeMeta?.subtitle}

{payload?.kind === 'help' && } {payload?.kind === 'models' && ( )} {payload?.kind === 'cost' && } {payload?.kind === 'status' && }
Esc closes the modal.
); }