feat(cli): TUI component architecture — status bars, message list, input bar
- Split monolithic app.tsx into composable components: - TopBar: connection indicator (●/○), gateway URL, model name, conversation ID - BottomBar: cwd, git branch, token usage - MessageList: timestamped messages, tool call indicators, thinking display - InputBar: context-aware prompt with streaming/disconnect states - Extract socket logic into useSocket hook with typed events - Extract git/cwd info into useGitInfo hook - Quiet disconnect: single indicator instead of error flood - Add @mosaic/types dependency for typed Socket.IO events - Add PRD and task tracking docs Tasks: TUI-001 through TUI-007 (Wave 1)
This commit is contained in:
61
packages/cli/src/tui/components/bottom-bar.tsx
Normal file
61
packages/cli/src/tui/components/bottom-bar.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import type { TokenUsage } from '../hooks/use-socket.js';
|
||||
import type { GitInfo } from '../hooks/use-git-info.js';
|
||||
|
||||
export interface BottomBarProps {
|
||||
gitInfo: GitInfo;
|
||||
tokenUsage: TokenUsage;
|
||||
}
|
||||
|
||||
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`;
|
||||
return String(n);
|
||||
}
|
||||
|
||||
/** Compact the cwd — show ~ for home, truncate long paths */
|
||||
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;
|
||||
}
|
||||
|
||||
export function BottomBar({ gitInfo, tokenUsage }: BottomBarProps) {
|
||||
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>
|
||||
<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>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user