Compare commits

...

2 Commits

Author SHA1 Message Date
Brooklyn Nicholson
4d0acda134 review(copilot): keep separator space when verb hits pad limit + reword comment 2026-04-28 15:30:11 -05:00
Brooklyn Nicholson
ec89694472 fix(tui): stabilize status bar ticker width (#13610)
The `FaceTicker` rotated through `VERBS` every 2.5s without padding the
verb segment, so the rest of the status bar (cwd label, ctx bar, voice,
bg-task counter) shifted by 1–3 columns on every tick whenever the
verb length changed (e.g. `cogitating…` 11 → `synthesizing…` 13).

Pad the verb to the longest entry in the catalogue + the trailing
ellipsis. This keeps the segment a stable column width without making
narrow terminals waste extra space — `wrap="truncate-end"` on the
parent Text already handles overflow.

Tests: new `statusBarTicker.test.ts` exports `padVerb` / `VERB_PAD_LEN`
and asserts every catalogue verb pads to the same width and preserves
its trailing ellipsis.

Validation: `npm run type-check` clean, `npm test --run` 393/393.
2026-04-28 15:29:19 -05:00
2 changed files with 42 additions and 1 deletions

View File

@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest'
import { padVerb, VERB_PAD_LEN } from '../components/appChrome.js'
import { VERBS } from '../content/verbs.js'
describe('FaceTicker verb padding (#13610)', () => {
it('pads every verb in the catalogue to the same column width (+1 separator)', () => {
for (const verb of VERBS) {
expect(padVerb(verb)).toHaveLength(VERB_PAD_LEN + 1)
}
})
it('keeps the trailing ellipsis attached to the verb', () => {
for (const verb of VERBS) {
expect(padVerb(verb).startsWith(`${verb}`)).toBe(true)
}
})
it('handles empty verbs without truncating the pad target', () => {
expect(padVerb('')).toHaveLength(VERB_PAD_LEN + 1)
})
it('keeps a trailing space even when the verb is already at the limit', () => {
const longest = VERBS.reduce((a, b) => (b.length > a.length ? b : a), '')
expect(padVerb(longest)).toBe(`${longest}`)
expect(padVerb(longest).endsWith(' ')).toBe(true)
})
})

View File

@@ -17,6 +17,18 @@ import type { Msg, Usage } from '../types.js'
const FACE_TICK_MS = 2500
const HEART_COLORS = ['#ff5fa2', '#ff4d6d']
// Stable verb-segment width keeps the rest of the status bar from
// jittering when the ticker rotates between short and long verbs
// (#13610). Without padding, the cwd label and ctx bar to the right
// shift on every cycle. We pad to the longest verb in the catalogue
// plus the trailing ellipsis so every rotation lands on the same
// column.
export const VERB_PAD_LEN = VERBS.reduce((max, v) => Math.max(max, v.length), 0) + 1 // + the '…'
// Always include the trailing space so the duration suffix has a
// stable separator even when the verb is already at the pad limit.
export const padVerb = (verb: string) => `${verb}`.padEnd(VERB_PAD_LEN + 1, ' ')
function FaceTicker({ color, startedAt }: { color: string; startedAt?: null | number }) {
const [tick, setTick] = useState(() => Math.floor(Math.random() * 1000))
const [now, setNow] = useState(() => Date.now())
@@ -33,7 +45,8 @@ function FaceTicker({ color, startedAt }: { color: string; startedAt?: null | nu
return (
<Text color={color}>
{FACES[tick % FACES.length]} {VERBS[tick % VERBS.length]}{startedAt ? ` · ${fmtDuration(now - startedAt)}` : ''}
{FACES[tick % FACES.length]} {padVerb(VERBS[tick % VERBS.length] ?? '')}
{startedAt ? `· ${fmtDuration(now - startedAt)}` : ''}
</Text>
)
}