From e15edc702e22d9ec479ab465384ddf05fa0804fe Mon Sep 17 00:00:00 2001 From: ciregenz Date: Sun, 14 Jun 2026 07:27:00 -0700 Subject: [PATCH] [eric] chat: unify thinking labels into one shared list, add quirkier verbs --- .../src/app/pages/AgentChat/AgentChat.tsx | 16 ++--- .../pages/AgentChat/bubbles/MessageBubble.tsx | 21 +----- .../src/app/pages/AgentChat/thinkingLabels.ts | 64 +++++++++++++++++++ 3 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 frontend/src/app/pages/AgentChat/thinkingLabels.ts diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index d581c616..ab084251 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -47,6 +47,7 @@ import { createSessionWs, acquireSessionWs, releaseSessionWs } from '@/shared/ws import StreamingBubble from './bubbles/StreamingBubble'; import WelcomeQuickReplies from './WelcomeQuickReplies'; import { useWelcomeGreeting } from './useWelcomeGreeting'; +import { THINKING_LABELS } from './thinkingLabels'; import MessageBubble from './bubbles/MessageBubble'; import { estimateRenderedTextHeight, RECHECK_VISIBILITY_EVENT } from './bubbles/markdownMeasure'; import CompactionMarker from './bubbles/CompactionMarker'; @@ -166,22 +167,15 @@ const thinkingShimmerKeyframes = ` } `; -// Single-word labels picked deterministically per session-turn so the pill -// has variety without flickering between renders. Mirrors MessageBubble's list. -const STREAMING_LABELS: ReadonlyArray = [ - 'Thinking', 'Pondering', 'Cooking', 'Marinating', 'Deliberating', - 'Reasoning', 'Reflecting', 'Untangling', 'Stewing', 'Locking-in', - 'Considering', 'Processing', 'Vibing', 'Calculating', 'Chefing', - 'Geeking', 'Brewing', -]; - +// Pick a label deterministically per session-turn so the pill has variety +// without flickering between renders. Shared list with MessageBubble. function streamingLabelFor(seedKey: string | undefined): string { - if (!seedKey) return STREAMING_LABELS[0]; + if (!seedKey) return THINKING_LABELS[0].live; let h = 0; for (let i = 0; i < seedKey.length; i++) { h = ((h << 5) - h + seedKey.charCodeAt(i)) | 0; } - return STREAMING_LABELS[Math.abs(h) % STREAMING_LABELS.length]; + return THINKING_LABELS[Math.abs(h) % THINKING_LABELS.length].live; } const ThinkingBubble: React.FC<{ label?: string | null; seedKey?: string }> = ({ label, seedKey }) => { diff --git a/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx b/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx index 20d68a64..d9b20127 100644 --- a/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx +++ b/frontend/src/app/pages/AgentChat/bubbles/MessageBubble.tsx @@ -20,6 +20,7 @@ import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import WindowedMarkdown from './WindowedMarkdown'; import { estimateRenderedTextHeight, oversizedCharThreshold, RECHECK_VISIBILITY_EVENT } from './markdownMeasure'; +import { THINKING_LABELS } from '../thinkingLabels'; import { AgentMessage } from '@/shared/state/agentsSlice'; import { openSettingsModal } from '@/shared/state/settingsSlice'; import { shallowEqual } from 'react-redux'; @@ -559,26 +560,6 @@ const MessageImageThumbnails: React.FC<{ ); }; -const THINKING_LABELS: ReadonlyArray<{ live: string; past: string }> = [ - { live: 'Thinking', past: 'Thought' }, - { live: 'Pondering', past: 'Pondered' }, - { live: 'Cooking', past: 'Cooked' }, - { live: 'Marinating', past: 'Marinated' }, - { live: 'Deliberating', past: 'Deliberated' }, - { live: 'Reasoning', past: 'Reasoned' }, - { live: 'Reflecting', past: 'Reflected' }, - { live: 'Untangling', past: 'Untangled' }, - { live: 'Stewing', past: 'Stewed' }, - { live: 'Locking-in', past: 'Locked-in' }, - { live: 'Considering', past: 'Considered' }, - { live: 'Processing', past: 'Processed' }, - { live: 'Vibing', past: 'Vibed' }, - { live: 'Calculating', past: 'Calculated' }, - { live: 'Chefing', past: 'Chefed' }, - { live: 'Geeking', past: 'Geeked' }, - { live: 'Brewing', past: 'Brewed' }, -]; - /** Stable hash of message id to label index; reload, scroll-back, and resume keep the same label. */ function labelIndexFromId(id: string | undefined): number { if (!id) return 0; diff --git a/frontend/src/app/pages/AgentChat/thinkingLabels.ts b/frontend/src/app/pages/AgentChat/thinkingLabels.ts new file mode 100644 index 00000000..493efbd1 --- /dev/null +++ b/frontend/src/app/pages/AgentChat/thinkingLabels.ts @@ -0,0 +1,64 @@ +// One source of truth for the agent's whimsical "busy" verbs, shared by the +// streaming pill (AgentChat) and the per-message thinking bubble (MessageBubble). +// `live` shows while the agent works; `past` shows once the step is done +// ("Marinated for 3s"). Keep them fun but never self-deprecating (no +// "hallucinating") so they read as personality, not a malfunction. +export interface ThinkingLabel { + live: string; + past: string; +} + +// Index 0 is the safe default the pill falls back to with no seed, so keep it +// the plain one. Everything after is fair game for chaos. +export const THINKING_LABELS: ReadonlyArray = [ + { live: 'Thinking', past: 'Thought' }, + { live: 'Tokenmaxing', past: 'Tokenmaxed' }, + { live: 'Pondering', past: 'Pondered' }, + { live: 'Cooking', past: 'Cooked' }, + { live: 'Grokking', past: 'Grokked' }, + { live: 'Marinating', past: 'Marinated' }, + { live: 'Galaxy-braining', past: 'Galaxy-brained' }, + { live: 'Reasoning', past: 'Reasoned' }, + { live: 'Noodling', past: 'Noodled' }, + { live: 'Percolating', past: 'Percolated' }, + { live: 'Reflecting', past: 'Reflected' }, + { live: 'Untangling', past: 'Untangled' }, + { live: 'Crunching', past: 'Crunched' }, + { live: 'Stewing', past: 'Stewed' }, + { live: 'Locking-in', past: 'Locked-in' }, + { live: 'Manifesting', past: 'Manifested' }, + { live: 'Big-braining', past: 'Big-brained' }, + { live: 'Vibing', past: 'Vibed' }, + { live: 'Scheming', past: 'Schemed' }, + { live: 'Riffing', past: 'Riffed' }, + { live: 'Calculating', past: 'Calculated' }, + { live: 'Tinkering', past: 'Tinkered' }, + { live: 'Finessing', past: 'Finessed' }, + { live: 'Chefing', past: 'Chefed' }, + { live: 'Min-maxing', past: 'Min-maxed' }, + { live: 'Geeking', past: 'Geeked' }, + { live: 'Ruminating', past: 'Ruminated' }, + { live: 'Simmering', past: 'Simmered' }, + { live: 'Brewing', past: 'Brewed' }, + { live: 'Wrangling', past: 'Wrangled' }, + { live: 'Spelunking', past: 'Spelunked' }, + { live: 'Conjuring', past: 'Conjured' }, + { live: 'Synthesizing', past: 'Synthesized' }, + { live: 'Overclocking', past: 'Overclocked' }, + { live: 'Caffeinating', past: 'Caffeinated' }, + { live: 'Sleuthing', past: 'Sleuthed' }, + { live: 'Larping', past: 'Larped' }, + { live: 'Speedrunning', past: 'Speedran' }, + { live: 'Theorycrafting', past: 'Theorycrafted' }, + { live: 'Sussing', past: 'Sussed' }, + { live: 'Hyperfixating', past: 'Hyperfixated' }, + { live: 'Nerd-sniping', past: 'Nerd-sniped' }, + { live: 'Promptmaxing', past: 'Promptmaxed' }, + { live: 'Pontificating', past: 'Pontificated' }, + { live: 'Vibe-checking', past: 'Vibe-checked' }, + { live: 'Mogging', past: 'Mogged' }, + { live: 'Goblin-moding', past: 'Goblin-moded' }, + { live: 'Side-questing', past: 'Side-quested' }, + { live: 'Tryharding', past: 'Tryharded' }, + { live: 'Grinding', past: 'Grinded' }, +];