From ca5febfed1429ad0e2b1565cfac48b079f5ff94d Mon Sep 17 00:00:00 2001 From: adybag14-cyber <252811164+adybag14-cyber@users.noreply.github.com> Date: Wed, 6 May 2026 01:19:22 +0100 Subject: [PATCH] fix(tui): stabilize FaceTicker elapsed width to prevent composer drift --- ui-tui/src/__tests__/statusBarTicker.test.ts | 11 ++++++++++- ui-tui/src/components/appChrome.tsx | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ui-tui/src/__tests__/statusBarTicker.test.ts b/ui-tui/src/__tests__/statusBarTicker.test.ts index 4f3369bfa3..6dff476ba0 100644 --- a/ui-tui/src/__tests__/statusBarTicker.test.ts +++ b/ui-tui/src/__tests__/statusBarTicker.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { padVerb, VERB_PAD_LEN } from '../components/appChrome.js' +import { DURATION_PAD_LEN, padTickerDuration, padVerb, VERB_PAD_LEN } from '../components/appChrome.js' import { VERBS } from '../content/verbs.js' describe('FaceTicker verb padding', () => { @@ -16,3 +16,12 @@ describe('FaceTicker verb padding', () => { } }) }) + +describe('FaceTicker duration padding', () => { + it('keeps elapsed segment width stable across second/minute boundaries', () => { + const samples = [9000, 10000, 59000, 60000, 61000, 3599000] + const lens = samples.map(ms => padTickerDuration(ms).length) + + expect(new Set(lens)).toEqual(new Set([DURATION_PAD_LEN])) + }) +}) diff --git a/ui-tui/src/components/appChrome.tsx b/ui-tui/src/components/appChrome.tsx index 74dba682fe..39e66984f7 100644 --- a/ui-tui/src/components/appChrome.tsx +++ b/ui-tui/src/components/appChrome.tsx @@ -23,7 +23,9 @@ const HEART_COLORS = ['#ff5fa2', '#ff4d6d'] // Keep verb segment width stable so status-bar content to the right doesn't // jitter when the ticker rotates between short/long verbs. export const VERB_PAD_LEN = VERBS.reduce((max, v) => Math.max(max, v.length), 0) + 1 // + ellipsis +export const DURATION_PAD_LEN = 7 // e.g. " 9s", "1m 05s", "59m 59s" export const padVerb = (verb: string) => `${verb}…`.padEnd(VERB_PAD_LEN, ' ') +export const padTickerDuration = (ms: number) => fmtDuration(ms).padStart(DURATION_PAD_LEN, ' ') // Compact alternates for the `emoji` and `ascii` indicator styles. // Each entry is a fixed-width (display-width) glyph. @@ -108,7 +110,7 @@ function FaceTicker({ color, startedAt }: { color: string; startedAt?: null | nu const { frame } = renderIndicator(style, tick) const verb = VERBS[verbTick % VERBS.length] ?? '' const verbSegment = showVerb ? ` ${padVerb(verb)}` : '' - const durationSegment = startedAt ? `· ${fmtDuration(now - startedAt)}` : '' + const durationSegment = startedAt ? `· ${padTickerDuration(now - startedAt)}` : '' return (