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:
@@ -6,6 +6,7 @@ import type { Chat, ErrorReason, Message } from '@/api/types';
|
||||
import { api } from '@/api/client';
|
||||
import { sessionEvents } from '@/hooks/sessionEvents';
|
||||
import { sendToTerminal, terminalsRegistry, type TerminalRegistration } from '@/lib/events';
|
||||
import { shortenModelName } from '@/lib/modelName';
|
||||
import { CapHitSentinel } from './CapHitSentinel';
|
||||
import { DoomLoopSentinel } from './DoomLoopSentinel';
|
||||
import { MarkdownRenderer } from './MarkdownRenderer';
|
||||
@@ -608,12 +609,12 @@ function SummaryCard({ message }: { message: Message }) {
|
||||
|
||||
// Collapsible "Thinking" block for assistant reasoning. Fed by either
|
||||
// reasoning_text (coder wire / live reasoning_delta stream) or reasoning_parts
|
||||
// (native inference, persisted from message_parts). Auto-expands while the turn
|
||||
// is still streaming so the user watches it think (Paseo-style), then stays
|
||||
// where the user left it once the turn completes — initial state is captured
|
||||
// once at mount, so we never fight a manual collapse on later re-renders.
|
||||
// (native inference, persisted from message_parts). Starts COLLAPSED to start
|
||||
// (a quiet chip) — for native BooChat/BooCode and the external agents (opencode,
|
||||
// claude SDK) alike — so the transcript stays tidy; click to expand. The
|
||||
// `streaming` pulse still animates while the turn runs.
|
||||
function ReasoningBlock({ text, streaming }: { text: string; streaming: boolean }) {
|
||||
const [expanded, setExpanded] = useState(() => streaming);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
return (
|
||||
<div className="max-w-[90%] rounded-lg border bg-muted/30 text-sm">
|
||||
<button
|
||||
@@ -768,7 +769,7 @@ export function MessageBubble({
|
||||
return (
|
||||
<div className="group flex flex-col items-end gap-1">
|
||||
<SendToTerminalMenu>
|
||||
<div className="max-w-[80%] rounded-lg bg-primary text-primary-foreground px-3 py-2 text-sm whitespace-pre-wrap break-words min-w-0">
|
||||
<div className="boo-user-bubble max-w-[80%] rounded-lg bg-primary text-primary-foreground px-3 py-2 text-sm whitespace-pre-wrap break-words min-w-0">
|
||||
{message.content}
|
||||
</div>
|
||||
</SendToTerminalMenu>
|
||||
@@ -782,6 +783,8 @@ export function MessageBubble({
|
||||
// v1.13.7: match the MessageList.flatten trim guard so a whitespace-only
|
||||
// assistant turn doesn't render an empty bubble + dangling ActionRow.
|
||||
const hasContent = message.content.trim().length > 0;
|
||||
// model-attribution chip: short label for the model that produced this turn.
|
||||
const modelLabel = shortenModelName(message.model);
|
||||
// Reasoning arrives as a pre-joined string (coder wire) or as parts (native
|
||||
// inference). Read whichever is present; loose ?? chain tolerates the coder
|
||||
// shape where reasoning_parts is undefined (see CLAUDE.md null-guard note).
|
||||
@@ -823,6 +826,14 @@ export function MessageBubble({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isStreaming && (modelLabel || null) && (
|
||||
<span
|
||||
className="inline-flex w-fit items-center rounded-full border border-primary/25 bg-primary/10 px-2 py-0.5 text-[10px] font-mono text-primary/90"
|
||||
title={message.model ?? undefined}
|
||||
>
|
||||
{modelLabel}
|
||||
</span>
|
||||
)}
|
||||
{!isStreaming && <StatsLine message={message} />}
|
||||
{!isStreaming && hasContent && (
|
||||
<ActionRow
|
||||
|
||||
Reference in New Issue
Block a user