feat(cli): full-width dim separator above and below user bubble#4095
feat(cli): full-width dim separator above and below user bubble#4095CnsMaple wants to merge 2 commits into
Conversation
Add a full-width dim '─' rule above and below each committed user bubble as a visual scroll anchor — when mouse-scrolling back to an older turn, the rule frames the bubble so it stands out from the surrounding assistant markdown. The rule is committed as a tiny token (NUL + PUA \uE000 + NUL) and expanded to a real renderUserSeparator(width) call at wrap time, so it always spans the *current* viewport width — survives terminal resizes, replays at a different width, and any other rewrap with no re-commit. * renderUserSeparator stays as a public helper; width <= 0 collapses to ''. * renderUserBubbleBlock now returns token form (no width parameter) and is reused by the submit path and the replay path. * expandSeparatorTokens is invoked once before wrapTranscript on every transcript change; non-separator lines pay no per-frame cost. * Tests cover full-width, color/ASCII fallbacks, the regression guard on the existing TestUserBubbleIsLightweightTranscriptLine contract, and a dedicated TestSeparatorTokenAdaptsToResize that proves the same token renders at width 60 and width 100.
|
Here is a side-by-side ASCII comparison of the transcript area before and BeforeAfterWhy the rule adapts to resizeThe The placeholder is invisible in the raw transcript (a NUL-framed |
|
已处理这两个 review 点并推到 PR head:
验证:
|
|
approve |
🤖 Generated by AI (CnsMaple + Reasonix)
Problem
When mouse-scrolling back to find an older turn in the chat transcript, the
user's submitted prompt is hard to spot. The assistant's markdown reply ends
without a strong visual edge into the user bubble, so the eye has to hunt
through dense, dimmed text to find the
› …line. This is a small but realergonomic pain on long sessions.
Fix
A full-width dim
─rule above and below each committed user bubble. Therule frames the bubble and acts as a scroll anchor — when you scroll up
through an older turn, the two rules bracket the user's input cleanly.
The rule is committed as a tiny placeholder token (NUL + U+E000 + NUL)
and expanded to a real
renderUserSeparator(width)call at wrap time, soit always spans the current viewport width. Terminal resizes, replays at
a different width, and any other rewrap pick up the new width with no
re-commit.
What changed
internal/cli/transcript.gouserSeparatorTokenconstant,renderUserSeparatorToken()factory,and
expandSeparatorTokens(lines, width)expander. The expander walkstranscript entries and replaces every token with a freshly rendered
renderUserSeparator(width), so resize is implicit.renderUserSeparator(width int) stringis kept as a public helper fordirect use; it falls back to ASCII
-whencolorEnabledis false andto
""whenwidth <= 0.internal/cli/chat_tui.gowrapTranscriptis now preceded byexpandSeparatorTokens(cm.transcript, contentW)on every transcript change — one insertion at the call site.renderUserBubbleBlocknow returns the token form (nowidthparameter) and is reused by both the submit path (
chat_tui.go:3180)and the replay path (
replaySectionsFor).renderUserBubbleis untouched; the regression guardTestUserBubbleIsLightweightTranscriptLinestill passes.Tests
TestRenderUserSeparatorIsFullWidthAndDimmed— covers color/ASCII pathsand
width <= 0.TestRenderUserBubbleBlockEmitsThreeLines— middle line is unchanged;separators are the token.
TestUserBubbleCommitPathIncludesSeparators— three transcript entriesafter a submit, top/bottom are tokens.
TestUserBubbleBlockScrollsAsThreeLines— full pipelineexpandSeparatorTokens → wrapTranscript; top/bottom are exactlycontent-width cells.
TestSeparatorTokenAdaptsToResize— same token-form transcript renderedat width 60 and width 100 produces rules of the matching widths and the
wider one contains strictly more
─runes (anti-cache guard).Notes
scrolls with the content and survives resumed sessions through the replay
path. Historical sessions are not backfilled — only turns submitted after
this change pick up the rule, matching the "rendering change" semantics.
transcript. The user bubble itself is unchanged.