Mobile header is now two rows. Row 1: hamburger | project · branch indicator (live via GET /api/projects/:id/git, 30s poll) | ModelPicker | FolderTree. Row 2: pane-switcher pill (hand-rolled BottomSheet) + NewPaneMenu. Chat-within-pane navigation hidden on mobile; users switch panes via the sheet. Cross-tab status sync via chat_status frames published from inference.ts at working/idle/error transitions; StatusDot component renders amber-pulse/green/red/gray on each pane row and on desktop ChatTabBar tabs. Level 1 git awareness exposes a read-only git_status tool to the model, backed by services/git_meta.ts (execFile + 2s timeout + 30s cache). Workspace.tsx now receives panes/chats hooks as props (hoisted into Session.tsx) so the header pill shares state with the pane grid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
37 lines
884 B
TypeScript
37 lines
884 B
TypeScript
import { useChatStatus, type DerivedStatus } from '@/hooks/useChatStatus';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface Props {
|
|
chatId: string | null | undefined;
|
|
className?: string;
|
|
}
|
|
|
|
const STATUS_CLASS: Record<DerivedStatus, string> = {
|
|
working: 'bg-amber-500 animate-pulse',
|
|
idle_warm: 'bg-emerald-500',
|
|
idle_cold: 'bg-muted-foreground/40',
|
|
error: 'bg-destructive',
|
|
};
|
|
|
|
const STATUS_LABEL: Record<DerivedStatus, string> = {
|
|
working: 'working',
|
|
idle_warm: 'idle',
|
|
idle_cold: 'idle',
|
|
error: 'error',
|
|
};
|
|
|
|
export function StatusDot({ chatId, className }: Props) {
|
|
const status = useChatStatus(chatId);
|
|
return (
|
|
<span
|
|
aria-label={`Status: ${STATUS_LABEL[status]}`}
|
|
title={STATUS_LABEL[status]}
|
|
className={cn(
|
|
'inline-block w-1.5 h-1.5 rounded-full shrink-0',
|
|
STATUS_CLASS[status],
|
|
className,
|
|
)}
|
|
/>
|
|
);
|
|
}
|