diff --git a/src/components/ChatView.tsx b/src/components/ChatView.tsx index 50e967b8c6..2574cae15e 100644 --- a/src/components/ChatView.tsx +++ b/src/components/ChatView.tsx @@ -49,6 +49,7 @@ import { ReactionBar, ReactionChips } from "./Reactions"; import { SpeakButton } from "./SpeakButton"; import { CallButton, CallOverlay } from "./CallView"; import { cn } from "@/lib/cn"; +import { COMPACT_BUBBLE, COMPACT_SQUARE } from "@/lib/compact-chip"; import { useFocusMessage } from "@/lib/focus-message"; import { webhookMessageView } from "@/lib/webhook-message"; import { attachmentBasename, splitAttachedImages } from "@/lib/composer-attachments"; @@ -893,7 +894,9 @@ export function ChatView({ bot }: { bot: Bot }) { {/* Header */}
}
-
+
{bot.busy && ( )} @@ -1125,13 +1131,16 @@ function UsageChip({ bot }: { bot: Bot }) { ] .filter(Boolean) .join("\n"); + // folded: one figure — cost when the engine reports one, else tokens + const short = usage.costUsd !== null ? formatUsd(usage.costUsd) : formatTokens(usage.input + usage.output); return ( ); } @@ -1148,11 +1157,14 @@ function WorkingFolderChip({ bot }: { bot: Bot }) { return ( ); } diff --git a/src/components/ModelPicker.tsx b/src/components/ModelPicker.tsx index 535d27c0aa..f98047d7a0 100644 --- a/src/components/ModelPicker.tsx +++ b/src/components/ModelPicker.tsx @@ -10,6 +10,7 @@ import { ProviderMark } from "./ProviderIcons"; import { EngineSetup, needsCli, needsSignIn } from "./EngineSetup"; import { EngineGroupLabel } from "./EngineGroupLabel"; import { cn } from "@/lib/cn"; +import { COMPACT_SQUARE } from "@/lib/compact-chip"; type ModelOption = InstanceInfo["models"]["options"][number]; const COMPACT_MODEL_COUNT = 5; @@ -206,12 +207,23 @@ export function ModelPicker({ bot, className }: { bot: Bot; className?: string } }} aria-expanded={open} aria-haspopup="dialog" - className="flex items-center gap-1.5 rounded-full border border-hairline/40 bg-raised/60 py-1 pl-2 pr-2.5 text-[13px] text-ink hover:bg-raised" + className={cn( + "flex items-center gap-1.5 rounded-full border border-hairline/40 bg-raised/60 py-1 pl-2 pr-2.5 text-[13px] text-ink hover:bg-raised", + // in a narrow chat header fold to a rounded square with just the + // provider mark; the model name rides the tooltip (a bot with no + // resolved engine keeps its label — the mark is what would hide it) + active && COMPACT_SQUARE, + )} title={active ? `${active.displayName} · ${modelLabel(active, selection.model)}` : selection.model} > {active && } - {modelLabel(active, selection.model)} - + + {modelLabel(active, selection.model)} + + {open && ( diff --git a/src/components/TaskPicker.tsx b/src/components/TaskPicker.tsx index f6625211d7..62afc65302 100644 --- a/src/components/TaskPicker.tsx +++ b/src/components/TaskPicker.tsx @@ -8,6 +8,7 @@ import { useEffect, useRef, useState } from "react"; import { Check, ChevronDown, Plus, Trash2 } from "lucide-react"; import { useStore, formatTime, type Bot, type Task } from "@/state/store"; import { cn } from "@/lib/cn"; +import { COMPACT_BUBBLE } from "@/lib/compact-chip"; import { formatTokens } from "@/lib/format-tokens"; /** Quiet per-task token tally — input+output combined, because one honest @@ -57,9 +58,13 @@ export function TaskPicker({ bot }: { bot: Bot }) { onClick={() => dispatch({ type: "newTask", botId: bot.id })} disabled={bot.busy} title={bot.busy ? "Let this turn finish first" : "New task — a fresh context on this bot"} - className="flex items-center gap-1 rounded-full border border-hairline/40 px-2.5 py-1 text-[12.5px] text-ink-secondary hover:bg-raised hover:text-ink disabled:opacity-40" + className={cn( + "flex items-center gap-1 rounded-full border border-hairline/40 px-2.5 py-1 text-[12.5px] text-ink-secondary hover:bg-raised hover:text-ink disabled:opacity-40", + COMPACT_BUBBLE, + )} > - Task + + Task ); } @@ -84,11 +89,15 @@ export function TaskPicker({ bot }: { bot: Bot }) { {open && ( diff --git a/src/lib/compact-chip.ts b/src/lib/compact-chip.ts new file mode 100644 index 0000000000..b12ae4d1fe --- /dev/null +++ b/src/lib/compact-chip.ts @@ -0,0 +1,14 @@ +// Chat-header chips fold to icon-only shapes when the header is narrow — +// the computer/inspector panel is open or the window is small — so the +// row stops wrapping and crushing the bot's name. The header is the +// `@container/chathead`; below 4xl (56rem) these variants kick in. +// +// Kept as plain literal strings so Tailwind's scanner sees every class. + +/** Round bubble, icon only — Stop, + Task. */ +export const COMPACT_BUBBLE = + "@max-4xl/chathead:size-[30px] @max-4xl/chathead:justify-center @max-4xl/chathead:gap-0 @max-4xl/chathead:p-0"; + +/** Rounded square, icon only — working folder, model. */ +export const COMPACT_SQUARE = + "@max-4xl/chathead:size-[30px] @max-4xl/chathead:justify-center @max-4xl/chathead:gap-0 @max-4xl/chathead:rounded-md @max-4xl/chathead:p-0";