feat(cli): branded top bar with mosaic windmill icon

- ASCII art mosaic windmill: 4 colored tiles (blue, purple, teal, amber)
  with pink center, matching the Mosaic Stack brand
- 3-line info block (Claude Code style):
  Line 1: 'Mosaic Stack v0.0.0'
  Line 2: model (context) · thinking · agent name
  Line 3: ● host connection status
- Remove bordered box in favor of open layout with icon
This commit is contained in:
2026-03-15 14:28:28 -05:00
parent f0b31d9983
commit fb40ed0af0

View File

@@ -3,18 +3,94 @@ import { Box, Text } from 'ink';
export interface TopBarProps {
gatewayUrl: string;
version: string;
modelName: string | null;
thinkingLevel: string;
contextWindow: number;
agentName: string;
connected: boolean;
connecting: boolean;
}
export function TopBar({ gatewayUrl }: TopBarProps) {
// Strip protocol for compact display
const host = gatewayUrl.replace(/^https?:\/\//, '');
/** Compact the URL — strip protocol */
function compactHost(url: string): string {
return url.replace(/^https?:\/\//, '');
}
function formatContextWindow(n: number): string {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(0)}M`;
if (n >= 1_000) return `${(n / 1_000).toFixed(0)}k`;
return String(n);
}
/**
* Mosaic windmill icon — 4 colored tiles + pink center
* Colors from the Mosaic brand:
* TL: blue (#2f80ff) TR: purple (#8b5cf6)
* BL: amber (#f59e0b) BR: teal (#14b8a6)
* Center: pink (#ec4899)
*/
function MosaicIcon() {
return (
<Box borderStyle="single" borderColor="gray" paddingX={1} justifyContent="space-between">
<Text bold color="blue">
Mosaic Stack TUI
<Box flexDirection="column" marginRight={1}>
<Text>
<Text color="#2f80ff"></Text>
<Text> </Text>
<Text color="#8b5cf6"></Text>
</Text>
<Text>
<Text> </Text>
<Text color="#ec4899"></Text>
<Text> </Text>
</Text>
<Text>
<Text color="#f59e0b"></Text>
<Text> </Text>
<Text color="#14b8a6"></Text>
</Text>
<Text dimColor>{host}</Text>
</Box>
);
}
export function TopBar({
gatewayUrl,
version,
modelName,
thinkingLevel,
contextWindow,
agentName,
connected,
connecting,
}: TopBarProps) {
const host = compactHost(gatewayUrl);
const connectionIndicator = connected ? '●' : '○';
const connectionColor = connected ? 'green' : connecting ? 'yellow' : 'red';
// Build model description line like: "claude-opus-4-6 (1M context) · default"
const modelDisplay = modelName ?? 'awaiting model';
const contextStr = contextWindow > 0 ? ` (${formatContextWindow(contextWindow)} context)` : '';
const thinkingStr = thinkingLevel !== 'off' ? ` · ${thinkingLevel}` : '';
return (
<Box paddingX={1} paddingY={0}>
<MosaicIcon />
<Box flexDirection="column">
<Text>
<Text bold color="#56a0ff">
Mosaic Stack
</Text>
<Text dimColor> v{version}</Text>
</Text>
<Text dimColor>
{modelDisplay}
{contextStr}
{thinkingStr} · {agentName}
</Text>
<Text>
<Text color={connectionColor}>{connectionIndicator}</Text>
<Text dimColor> {host}</Text>
</Text>
</Box>
</Box>
);
}