From 5f8afb4b7e3cd627e6c935eb6f0e9155bb5dd7db Mon Sep 17 00:00:00 2001 From: aaron Date: Sun, 9 Aug 2026 13:05:28 -0400 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20clean=20thinker=20=E2=80=94=20remo?= =?UTF-8?q?ve=20H-mark,=20slide=20left,=20shuffle=20verbs=20(#157)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes AmicoMark from ThinkingLine (keeps prop as deprecated no-op), collapses 3-col grid to 2-col with meta flush left, and shuffles verbs per reload + random per-mount start. Stories/comments updated. --- .../src/components/message-part.tsx | 5 ++--- .../ui/src/amicode/amico-presence.stories.tsx | 3 +-- .../ui/src/amicode/amico-wave.stories.tsx | 10 ++++----- packages/ui/src/amicode/amicode.css | 21 +++++++++--------- .../ui/src/amicode/thinking-line.stories.tsx | 10 ++++----- packages/ui/src/amicode/thinking-line.tsx | 22 ++++++++----------- packages/ui/src/amicode/thinking.ts | 22 +++++++++++++++++++ 7 files changed, 53 insertions(+), 40 deletions(-) diff --git a/packages/session-ui/src/components/message-part.tsx b/packages/session-ui/src/components/message-part.tsx index c80e756df..77fbb7066 100644 --- a/packages/session-ui/src/components/message-part.tsx +++ b/packages/session-ui/src/components/message-part.tsx @@ -916,9 +916,8 @@ export function AssistantParts(props: { {/* amicode: Amico's working presence — the offset accent lane. Shown only while an in-domain turn streams (state === "on"), decoupled from any - card-suppression: the thinking block runs (H-mark + cycling gerund + - live elapsed/tokens — it owns its mark now, no lane-head here; two - signature marks side by side compete with each other). Pops out the + card-suppression: the thinking block (wave + cycling gerund + live + elapsed/tokens) runs without a duplicate mark. Pops out the moment working flips false. spec-20260712-amico-third-actor. */}
diff --git a/packages/ui/src/amicode/amico-presence.stories.tsx b/packages/ui/src/amicode/amico-presence.stories.tsx index 28eaee4cb..237e4a4f5 100644 --- a/packages/ui/src/amicode/amico-presence.stories.tsx +++ b/packages/ui/src/amicode/amico-presence.stories.tsx @@ -87,8 +87,7 @@ function MockFlow(props: { state: PresenceState }) { {/* Amico lane */}
- {/* stepping_in gets a bare lane-head; once working, the thinking block - OWNS its H-mark (thinking-line.tsx) so no lane-head here */} + {/* stepping_in gets a bare lane-head; the thinking block (wave+verb) owns the working indicator now */}
stepping in…
diff --git a/packages/ui/src/amicode/amico-wave.stories.tsx b/packages/ui/src/amicode/amico-wave.stories.tsx index 7f89918ec..c54348fb5 100644 --- a/packages/ui/src/amicode/amico-wave.stories.tsx +++ b/packages/ui/src/amicode/amico-wave.stories.tsx @@ -40,7 +40,6 @@ // mapping from its source file, so it tracks theme changes instead of a hand-copied hex. import { createSignal, For, onMount } from "solid-js" import { AmicoWave } from "./amico-wave" -import { AmicoMark } from "./spinner" import { MODE_WAVELENGTHS, WAVE_BOX } from "./wave-geometry" import oc2ThemeJson from "../theme/themes/oc-2.json" import { resolveThemeVariantV2 } from "../theme/v2/resolve" @@ -111,14 +110,13 @@ function SchemePane(props: { scheme: "light" | "dark"; children: unknown }) { ) } -// Mimics the real mount site (thinking-line.tsx): H-mark, glyph and bold gerund -// on the top row; the muted meta below, starting under the glyph — reusing the -// actual .amc-thinking* classes from amicode.css. DOM order is word-first so -// the block's first baseline is the verb's (see thinking-line.tsx). +// Mimics the real mount site (thinking-line.tsx): glyph and bold gerund on the +// top row; the muted meta below, starting under the glyph — reusing the actual +// .amc-thinking* classes from amicode.css. DOM order is word-first so the +// block's first baseline is the verb's (see thinking-line.tsx). const ThinkingRow = () => ( Percolating… - 5m 13s · ↑ 2.4k tokens diff --git a/packages/ui/src/amicode/amicode.css b/packages/ui/src/amicode/amicode.css index a7878b799..ba5b8230f 100644 --- a/packages/ui/src/amicode/amicode.css +++ b/packages/ui/src/amicode/amicode.css @@ -53,13 +53,13 @@ } /* ---- thinking block (Claude-Code-esque working indicator) ---------------- */ -/* Two-row grid shown while a reply streams (thinking-line.tsx): the H-mark, - * harmonic wave and cycling gerund ride the top row; the live meta line - * (elapsed · tokens · esc) sits below, starting under the wave. Anti-glitch - * invariants, earned the hard way: +/* Two-row grid shown while a reply streams (thinking-line.tsx): the harmonic + * wave and cycling gerund ride the top row; the live meta line (elapsed · + * tokens · esc) sits below, starting under the wave. Anti-glitch invariants, + * earned the hard way: * - GRID, never flex-wrap — the old flex row once let a greedy sibling * heading squeeze the meta until "↑ 8.7k tokens" shattered across three - * lines, and baseline alignment scattered the mark/wave/verb; + * lines, and baseline alignment scattered the wave/verb; * - the verb reserves the longest gerund's width (16ch covers * "Fidelitymaxxing…") so word rotation never shifts the layout around it; * - the meta renders from mount (elapsed always ticks), so the block's height @@ -67,10 +67,10 @@ * - nowrap + ellipsis on the meta for absurdly narrow panes. * DOM order is word-first so the block's first baseline is the VERB's — the * sibling activity heading (session-turn-thinking) aligns with the verb, not - * the mark's bottom edge. Explicit grid placement keeps the visual order. */ + * the wave's edge. Explicit grid placement keeps the visual order. */ .amc-thinking { display: inline-grid; - grid-template-columns: auto auto 1fr; + grid-template-columns: auto 1fr; column-gap: 8px; row-gap: 2px; align-items: center; @@ -80,10 +80,9 @@ letter-spacing: 0.01em; color: var(--v2-text-text-muted); } -.amc-thinking .amc-mark { grid-column: 1; grid-row: 1; } -.amc-thinking .amc-wave { grid-column: 2; grid-row: 1; } +.amc-thinking .amc-wave { grid-column: 1; grid-row: 1; } .amc-thinking-word { - grid-column: 3; + grid-column: 2; grid-row: 1; font-weight: 600; white-space: nowrap; @@ -91,7 +90,7 @@ color: var(--v2-text-text-accent); } .amc-thinking-meta { - grid-column: 2 / -1; + grid-column: 1 / -1; grid-row: 2; min-width: 0; overflow: hidden; diff --git a/packages/ui/src/amicode/thinking-line.stories.tsx b/packages/ui/src/amicode/thinking-line.stories.tsx index 76c507b62..a5ef75449 100644 --- a/packages/ui/src/amicode/thinking-line.stories.tsx +++ b/packages/ui/src/amicode/thinking-line.stories.tsx @@ -11,11 +11,11 @@ export default { component: `### Thinking block The Claude-Code-esque "working" indicator shown while a reply streams. A -two-row grid block that OWNS its H-mark: the mark, harmonic wave glyph and -cycling gerund ride the top row; the live meta line (elapsed · tokens · esc) -sits below, starting under the wave. The verb reserves the longest gerund's -width so word rotation never shifts the layout, and the meta can never wrap -mid-phrase. +two-row grid block: the harmonic wave glyph and cycling gerund ride the top +row; the live meta line (elapsed · tokens · esc) sits below, starting under +the wave. The verb reserves the longest gerund's width so word rotation never +shifts the layout, and the meta can never wrap mid-phrase. Verbs are shuffled +per mount so each turn opens on a different word. Under \`prefers-reduced-motion\` the wave and word are static; the elapsed counter still advances (it's information, not decoration).`, diff --git a/packages/ui/src/amicode/thinking-line.tsx b/packages/ui/src/amicode/thinking-line.tsx index 69d7f7513..2250f24ea 100644 --- a/packages/ui/src/amicode/thinking-line.tsx +++ b/packages/ui/src/amicode/thinking-line.tsx @@ -1,15 +1,12 @@ import { createSignal, onCleanup, onMount, Show, type ComponentProps } from "solid-js" -import { wordAt, formatElapsed, formatTokens } from "./thinking" +import { shuffledWordAt, SHUFFLED_WORDS, formatElapsed, formatTokens } from "./thinking" import { AmicoWave } from "./amico-wave" -import { AmicoMark } from "./spinner" // AMICODE: the "thinking" working indicator — a two-row block shown while a // reply streams (app TimelineThinkingRow; session-ui lane in message-part.tsx). -// Row 1: H-mark + harmonic wave (amico-wave.tsx) + the cycling gerund. Row 2: -// the live meta line (elapsed · tokens · esc), starting under the wave. The -// block OWNS the mark — mount sites no longer add their own (a `mark={false}` -// opt-out remains for contexts that carry their own signature). Pure bits -// (word rotation, label formatting) live in ./thinking for testing. +// Row 1: harmonic wave (amico-wave.tsx) + the cycling gerund. Row 2: +// the live meta line (elapsed · tokens · esc), starting under the wave. +// Pure bits (word rotation, label formatting) live in ./thinking for testing. // // Layout invariants (earned the hard way — the CSS side lives in amicode.css): // - the meta can NEVER wrap mid-phrase — the old flex row once let a greedy @@ -47,14 +44,16 @@ export function ThinkingLine(props: { tokens?: number /** show the "esc to interrupt" hint — only pass when the mount site wires esc */ interruptible?: boolean - /** render the H-mark as the block's anchor (default true) */ + /** @deprecated no-op — the H-mark was removed (wave+verb is the indicator now); kept for compat */ mark?: boolean class?: string style?: ComponentProps<"span">["style"] }) { const still = reducedMotion() const [elapsedMs, setElapsedMs] = createSignal(0) - const [tick, setTick] = createSignal(0) + // Random start so each turn/mount opens on a different verb; the module-level + // shuffle already decorrelates reloads, this decorrelates simultaneous mounts. + const [tick, setTick] = createSignal(Math.floor(Math.random() * SHUFFLED_WORDS.length)) onMount(() => { const start = Date.now() @@ -66,7 +65,7 @@ export function ThinkingLine(props: { } }) - const word = () => (still ? "Thinking" : wordAt(tick())) + const word = () => (still ? "Thinking" : shuffledWordAt(tick())) return ( - - -