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
This commit is contained in:
32
apps/web/src/lib/modelName.ts
Normal file
32
apps/web/src/lib/modelName.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
// model-attribution: turn a raw model id into a short, friendly label for the
|
||||
// per-message model chip (e.g. "claude-sonnet-4-6" → "Sonnet 4.6",
|
||||
// "qwen3.6-35b-a3b-mxfp4" → "Qwen3.6 35B"). Strips provider prefixes and maps
|
||||
// the common families; falls back to the cleaned id so unknown models still
|
||||
// read. Returns null for empty/absent input so the caller can skip the chip.
|
||||
export function shortenModelName(model: string | null | undefined): string | null {
|
||||
if (!model) return null;
|
||||
let m = model.trim();
|
||||
if (!m) return null;
|
||||
|
||||
// opencode / provider-prefixed ids: "llama-swap/qwen…", "anthropic/claude…".
|
||||
const slash = m.lastIndexOf('/');
|
||||
if (slash >= 0) m = m.slice(slash + 1);
|
||||
|
||||
// claude-{opus,sonnet,haiku}-X-Y[-date] → "Opus X.Y".
|
||||
const claude = /^claude-(opus|sonnet|haiku)-(\d+)-(\d+)/i.exec(m);
|
||||
if (claude) {
|
||||
const tier = claude[1]!.charAt(0).toUpperCase() + claude[1]!.slice(1).toLowerCase();
|
||||
return `${tier} ${claude[2]}.${claude[3]}`;
|
||||
}
|
||||
|
||||
// qwen3.6-35b-a3b-… → "Qwen3.6 35B".
|
||||
const qwen = /^qwen([\d.]+)-(\d+)b/i.exec(m);
|
||||
if (qwen) return `Qwen${qwen[1]} ${qwen[2]}B`;
|
||||
|
||||
// gpt-4o, gpt-5-… → "GPT-4o" / "GPT-5".
|
||||
const gpt = /^gpt-([\w.-]+)/i.exec(m);
|
||||
if (gpt) return `GPT-${gpt[1]}`;
|
||||
|
||||
// Fallback: keep the id readable, cap the length for the chip.
|
||||
return m.length > 26 ? `${m.slice(0, 25)}…` : m;
|
||||
}
|
||||
@@ -24,7 +24,8 @@ export type ThemeId =
|
||||
| 'ivory'
|
||||
| 'chalk'
|
||||
| 'cobalt'
|
||||
| 'midnight-sapphire';
|
||||
| 'midnight-sapphire'
|
||||
| 'ember';
|
||||
|
||||
export type ThemeMode = 'dark' | 'light' | 'system';
|
||||
|
||||
@@ -74,9 +75,13 @@ export const THEMES: readonly ThemeMeta[] = [
|
||||
anchors: ['#020817', '#061434', '#0c2244', '#3060a0', '#0047ab'] },
|
||||
{ id: 'midnight-sapphire', name: 'Midnight Sapphire', family: 'Blue', supportsDark: true, supportsLight: true,
|
||||
anchors: ['#02050e', '#060c1f', '#0e1a36', '#4a6088', '#1e3a8a'] },
|
||||
{ id: 'ember', name: 'BooCode Ember', family: 'Amber', supportsDark: true, supportsLight: true,
|
||||
anchors: ['#0c0c0e', '#15151a', '#1f1f23', '#6b6b75', '#ff7a18'] },
|
||||
] as const;
|
||||
|
||||
export const DEFAULT_THEME_ID: ThemeId = 'obsidian';
|
||||
// BooCode 2.0: orange-on-black "BooCode Ember" is the out-of-the-box signature
|
||||
// (was 'obsidian' / purple). Also the dark fallback for the light-only themes.
|
||||
export const DEFAULT_THEME_ID: ThemeId = 'ember';
|
||||
export const DEFAULT_THEME_MODE: ThemeMode = 'dark';
|
||||
export const STORAGE_KEY = 'boocode.theme';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user