diff --git a/packages/cli/src/tui/app.tsx b/packages/cli/src/tui/app.tsx index 889247a..0214d1f 100644 --- a/packages/cli/src/tui/app.tsx +++ b/packages/cli/src/tui/app.tsx @@ -47,13 +47,20 @@ export function TuiApp({ gatewayUrl, conversationId, sessionCookie }: TuiAppProp activeToolCalls={socket.activeToolCalls} /> - - + + ); } diff --git a/packages/cli/src/tui/components/bottom-bar.tsx b/packages/cli/src/tui/components/bottom-bar.tsx index 6d00e21..7c494db 100644 --- a/packages/cli/src/tui/components/bottom-bar.tsx +++ b/packages/cli/src/tui/components/bottom-bar.tsx @@ -6,55 +6,101 @@ import type { GitInfo } from '../hooks/use-git-info.js'; export interface BottomBarProps { gitInfo: GitInfo; tokenUsage: TokenUsage; + connected: boolean; + connecting: boolean; + modelName: string | null; + providerName: string | null; } function formatTokens(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; - if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`; + if (n >= 1_000) return `${(n / 1_000).toFixed(0)}k`; return String(n); } -/** Compact the cwd — show ~ for home, truncate long paths */ +/** Compact the cwd — replace home with ~ */ function compactCwd(cwd: string): string { const home = process.env['HOME'] ?? ''; if (home && cwd.startsWith(home)) { - cwd = '~' + cwd.slice(home.length); - } - // If still very long, show last 3 segments - const parts = cwd.split('/'); - if (parts.length > 4) { - return '…/' + parts.slice(-3).join('/'); + return '~' + cwd.slice(home.length); } return cwd; } -export function BottomBar({ gitInfo, tokenUsage }: BottomBarProps) { +export function BottomBar({ + gitInfo, + tokenUsage, + connected, + connecting, + modelName, + providerName, +}: BottomBarProps) { + const gatewayStatus = connected ? 'Connected' : connecting ? 'Connecting…' : 'Disconnected'; + const gatewayColor = connected ? 'green' : connecting ? 'yellow' : 'red'; + const hasTokens = tokenUsage.total > 0; return ( - - - cwd: - {compactCwd(gitInfo.cwd)} - {gitInfo.branch && ( - <> - - {gitInfo.branch} - - )} + + {/* Line 1: path (branch) ····· Gateway: Status */} + + + {compactCwd(gitInfo.cwd)} + {gitInfo.branch && ({gitInfo.branch})} + + + Gateway: + {gatewayStatus} + - - {hasTokens ? ( - <> - tokens: - ↑{formatTokens(tokenUsage.input)} - / - ↓{formatTokens(tokenUsage.output)} - ({formatTokens(tokenUsage.total)}) - - ) : ( - tokens: — - )} + + {/* Line 2: token stats ····· (provider) model */} + + + {hasTokens ? ( + <> + ^{formatTokens(tokenUsage.input)} + {' '} + v{formatTokens(tokenUsage.output)} + {tokenUsage.cacheRead > 0 && ( + <> + {' '} + R{formatTokens(tokenUsage.cacheRead)} + + )} + {tokenUsage.cacheWrite > 0 && ( + <> + {' '} + W{formatTokens(tokenUsage.cacheWrite)} + + )} + {tokenUsage.cost > 0 && ( + <> + {' '} + ${tokenUsage.cost.toFixed(3)} + + )} + {tokenUsage.contextPercent > 0 && ( + <> + {' '} + + {tokenUsage.contextPercent.toFixed(1)}%/{formatTokens(tokenUsage.contextWindow)} + + + )} + + ) : ( + + )} + + + {(providerName ?? modelName) && ( + + {providerName ? `(${providerName}) ` : ''} + {modelName ?? ''} + + )} + ); diff --git a/packages/cli/src/tui/components/input-bar.tsx b/packages/cli/src/tui/components/input-bar.tsx index 6f65ef6..ab02c2f 100644 --- a/packages/cli/src/tui/components/input-bar.tsx +++ b/packages/cli/src/tui/components/input-bar.tsx @@ -26,12 +26,10 @@ export function InputBar({ onSubmit, isStreaming, connected }: InputBarProps) { ? 'waiting for response…' : 'message mosaic…'; - const promptColor = !connected ? 'red' : isStreaming ? 'yellow' : 'green'; - return ( - - - ❯{' '} + + + {'❯ '} void; connectionError: string | null; } @@ -65,8 +71,18 @@ export function useSocket(opts: UseSocketOptions): UseSocketReturn { const [currentThinkingText, setCurrentThinkingText] = useState(''); const [activeToolCalls, setActiveToolCalls] = useState([]); // TODO: wire up once gateway emits token-usage and model-info events - const tokenUsage: TokenUsage = { input: 0, output: 0, total: 0 }; + const tokenUsage: TokenUsage = { + input: 0, + output: 0, + total: 0, + cacheRead: 0, + cacheWrite: 0, + cost: 0, + contextPercent: 0, + contextWindow: 0, + }; const modelName: string | null = null; + const providerName: string | null = null; const [connectionError, setConnectionError] = useState(null); const socketRef = useRef(null); @@ -191,6 +207,7 @@ export function useSocket(opts: UseSocketOptions): UseSocketReturn { activeToolCalls, tokenUsage, modelName, + providerName, sendMessage, connectionError, };