feat: BooCode 2.0 UI — Ember theme, brand banner, coder tabs, model-attribution chips

- Ember theme (Obsidian charcoal + #ff7a18 orange), now DEFAULT_THEME_ID; server theme_id whitelist gains 'ember'
- Brand banner: transparent Westie mascot + >_BooCode wordmark, big/edge-to-edge (flood-filled to transparency + cropped)
- Coder panes are multi-tab: + opens a BooCode tab, split opens a pane (shared ChatTabBar via tabKind + createCoderTab; closeOtherTabs/tab-numbering extended to coder)
- Model-attribution: new messages.model column stamped at finalizeCompletion (BooChat/native coder) + dispatcher assistant-row creation (external coder); surfaced via view + wire types + live frame; rendered as a subtle shortened-name chip (shortenModelName)
- Composer Web toggle moved into a boxed focus-ringed input; glowing accent dot on tool rows
- Claude SDK follow-ups (1M context, follow-up-message fix, collapsed thinking/tool chips) + CLAUDE_SDK_BACKEND=1

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 22:30:47 +00:00
parent 7098014261
commit 3a646fd6df
34 changed files with 482 additions and 163 deletions

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { History, MessageSquare, X } from 'lucide-react';
import { Code, History, MessageSquare, X } from 'lucide-react';
import type { Chat, WorkspacePane } from '@/api/types';
import { StatusDot } from '@/components/StatusDot';
import { PaneHeaderActions } from '@/components/PaneHeaderActions';
@@ -17,6 +17,9 @@ import { cn } from '@/lib/utils';
interface Props {
pane: WorkspacePane;
tabs: Chat[];
// Host pane kind — 'coder' shows the Code glyph + routes the "+" to a new
// BooCode tab. Defaults to 'chat' (the BooChat tab bar).
tabKind?: 'chat' | 'coder';
// v2.6.x (Batch 3a): stable session-scoped tab number per chat id. Keyed by
// chat.id, NEVER by tab position.
tabNumbers: Record<string, number>;
@@ -36,6 +39,7 @@ interface Props {
export function ChatTabBar({
pane,
tabs,
tabKind = 'chat',
tabNumbers,
onSwitchTab,
onRemoveTab,
@@ -51,6 +55,8 @@ export function ChatTabBar({
}: Props) {
const [renamingId, setRenamingId] = useState<string | null>(null);
const [renameValue, setRenameValue] = useState('');
const TabIcon = tabKind === 'coder' ? Code : MessageSquare;
const newLabel = tabKind === 'coder' ? 'New BooCode' : 'New chat';
// Long-press: dispatch a synthetic contextmenu event on the tab so the
// existing Radix ContextMenuTrigger opens at the touch coordinates. Works
@@ -104,7 +110,7 @@ export function ChatTabBar({
: 'bg-muted/30 text-muted-foreground hover:bg-muted/60'
)}
>
<MessageSquare size={12} className="shrink-0" />
<TabIcon size={12} className="shrink-0" />
<StatusDot chatId={chat.id} />
{renamingId === chat.id ? (
<input
@@ -142,7 +148,7 @@ export function ChatTabBar({
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem onSelect={onNewTab}>
New chat
{newLabel}
</ContextMenuItem>
<ContextMenuItem
onSelect={() =>
@@ -189,6 +195,7 @@ export function ChatTabBar({
<PaneHeaderActions
className="ml-auto px-1"
onNewTab={onNewTab}
tabKind={tabKind}
onSplitPane={onSplitPane}
onReopenPane={onReopenPane}
onShowHistory={onShowHistory}