feat(cli): match TUI footer to reference design
- Remove borders from input bar — clean '❯ message mosaic…' prompt - Two-line footer without borders: - Line 1: compact cwd (branch) | Gateway: Connected/Disconnected - Line 2: token stats (^in v_out R_cache W_cache $cost ctx%) | (provider) model - Extend TokenUsage with cacheRead, cacheWrite, cost, contextPercent, contextWindow - Add providerName to socket hook return - Reorder layout: top bar → messages → input → footer
This commit is contained in:
@@ -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 (
|
||||
<Box borderStyle="single" borderColor="gray" paddingX={1} justifyContent="space-between">
|
||||
<Box>
|
||||
<Text dimColor>cwd: </Text>
|
||||
<Text>{compactCwd(gitInfo.cwd)}</Text>
|
||||
{gitInfo.branch && (
|
||||
<>
|
||||
<Text dimColor> ⎇ </Text>
|
||||
<Text color="cyan">{gitInfo.branch}</Text>
|
||||
</>
|
||||
)}
|
||||
<Box flexDirection="column" paddingX={0} marginTop={0}>
|
||||
{/* Line 1: path (branch) ····· Gateway: Status */}
|
||||
<Box justifyContent="space-between">
|
||||
<Box>
|
||||
<Text dimColor>{compactCwd(gitInfo.cwd)}</Text>
|
||||
{gitInfo.branch && <Text dimColor> ({gitInfo.branch})</Text>}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text dimColor>Gateway: </Text>
|
||||
<Text color={gatewayColor}>{gatewayStatus}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
{hasTokens ? (
|
||||
<>
|
||||
<Text dimColor>tokens: </Text>
|
||||
<Text color="green">↑{formatTokens(tokenUsage.input)}</Text>
|
||||
<Text dimColor> / </Text>
|
||||
<Text color="yellow">↓{formatTokens(tokenUsage.output)}</Text>
|
||||
<Text dimColor> ({formatTokens(tokenUsage.total)})</Text>
|
||||
</>
|
||||
) : (
|
||||
<Text dimColor>tokens: —</Text>
|
||||
)}
|
||||
|
||||
{/* Line 2: token stats ····· (provider) model */}
|
||||
<Box justifyContent="space-between">
|
||||
<Box>
|
||||
{hasTokens ? (
|
||||
<>
|
||||
<Text dimColor>^{formatTokens(tokenUsage.input)}</Text>
|
||||
<Text dimColor>{' '}</Text>
|
||||
<Text dimColor>v{formatTokens(tokenUsage.output)}</Text>
|
||||
{tokenUsage.cacheRead > 0 && (
|
||||
<>
|
||||
<Text dimColor>{' '}</Text>
|
||||
<Text dimColor>R{formatTokens(tokenUsage.cacheRead)}</Text>
|
||||
</>
|
||||
)}
|
||||
{tokenUsage.cacheWrite > 0 && (
|
||||
<>
|
||||
<Text dimColor>{' '}</Text>
|
||||
<Text dimColor>W{formatTokens(tokenUsage.cacheWrite)}</Text>
|
||||
</>
|
||||
)}
|
||||
{tokenUsage.cost > 0 && (
|
||||
<>
|
||||
<Text dimColor>{' '}</Text>
|
||||
<Text dimColor>${tokenUsage.cost.toFixed(3)}</Text>
|
||||
</>
|
||||
)}
|
||||
{tokenUsage.contextPercent > 0 && (
|
||||
<>
|
||||
<Text dimColor>{' '}</Text>
|
||||
<Text dimColor>
|
||||
{tokenUsage.contextPercent.toFixed(1)}%/{formatTokens(tokenUsage.contextWindow)}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text dimColor>—</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
{(providerName ?? modelName) && (
|
||||
<Text dimColor>
|
||||
{providerName ? `(${providerName}) ` : ''}
|
||||
{modelName ?? ''}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user