feat(tui): segment turns with rule above non-first user msgs; trim ticker dead space (#21846)

Multi-turn transcripts ran together visually because every user message
got the same vertical rhythm regardless of position. Adds a short ─── in
the border colour above every user message after the first, so each turn
reads as its own block. Height estimator gains a `withSeparator` flag so
virtual scrolling pre-allocates the extra two rows (rule + top margin)
and avoids a jump on first measurement.

While in the area: the busy-indicator duration was padded with
`padStart(7)`, leaving five visible spaces between `·` and the digits
(`⠋ ·      2s`) — especially loud under the verb-less `unicode` style.
Drop the padding entirely (`⠋ · 2s`); the model label now shifts a few
columns as the duration grows, which is the right trade-off for the
minimal indicator styles. The verb-padding test stays; the
duration-padding test is removed alongside the function it covered.
This commit is contained in:
brooklyn! 2026-05-08 05:12:09 -07:00 committed by GitHub
parent 7190e20e0b
commit 42f9234da3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 17 deletions

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
import { DURATION_PAD_LEN, padTickerDuration, padVerb, VERB_PAD_LEN } from '../components/appChrome.js'
import { padVerb, VERB_PAD_LEN } from '../components/appChrome.js'
import { VERBS } from '../content/verbs.js'
describe('FaceTicker verb padding', () => {
@ -16,12 +16,3 @@ 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]))
})
})

View file

@ -31,4 +31,12 @@ describe('virtual height estimates', () => {
estimatedMsgHeight(msg, 80, { compact: false, details: false })
)
})
it('reserves two extra rows for the inter-turn separator on non-first user messages', () => {
const msg: Msg = { role: 'user', text: 'follow-up question' }
const base = estimatedMsgHeight(msg, 80, { compact: false, details: false })
const withSep = estimatedMsgHeight(msg, 80, { compact: false, details: false, withSeparator: true })
expect(withSep).toBe(base + 2)
})
})