feat(tui): pin the last user message at the top of the viewport - #2491
Closed
lucastononro wants to merge 3 commits into
Closed
feat(tui): pin the last user message at the top of the viewport#2491lucastononro wants to merge 3 commits into
lucastononro wants to merge 3 commits into
Conversation
While the agent works and the transcript scrolls, the user's most recently sent message now stays visible as a band pinned to the top of the terminal viewport — same idea as Claude Code's pinned prompt — so the prompt that kicked off the current work is always in view. - pi-tui: expose getViewportTop()/getContentHeight() so components can read the engine's scroll state. - New PinnedUserMessageComponent, mounted as a non-capturing, top anchored, full-width overlay (the existing compositing path used by dialogs; dialogs still stack above it). It snapshots the message text (the transcript window may trim the original entry), caps the band at 3 lines with an ellipsis, and stays hidden until the original message has scrolled above the viewport so it never duplicates on-screen content. - KimiTUI snapshots the text on every live send (normal, steered, and queued-then-sent paths) and clears the pin on /clear, /new, and session switches. - New pin_last_user_message toggle in tui.toml (default true), live-reloadable via /reload. Validation: apps/kimi-code vitest suite (2479 passed), pi-tui node:test suite (739 passed), oxlint clean, monorepo typecheck clean, plus a recorded runtime demo of the feature in a real terminal.
🦋 Changeset detectedLatest commit: 7369a98 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dddd03ccbe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Restyle the pin from bold roleUser amber on a solid gray band to regular-weight primary (kimi blue) text on the terminal's own background with a thin muted rule underneath — lighter, and matches the rest of the chrome.
…data Address review feedback: - Anchor the pin's visibility to the message's actual buffer position (the transcript container's rendered height right after the append) instead of the engine's whole-buffer content height, which is a frame stale and also counts the bottom chrome and overlay padding — this could displace the anchor by several rows after batched steer items or transcript trimming. - Add changesets per the gen-changesets rules: a minor CLI entry for the feature and a separate pi-tui patch entry for the scroll-state accessors (the two changelogs must not mix). - Document pin_last_user_message in the tui.toml reference (en/zh).
Collaborator
|
Sorry, we can't accept this feature |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
Resolve #2490
Problem
During long agent runs the user's prompt quickly scrolls into terminal scrollback. Claude Code solves this nicely by keeping the last sent message pinned at the top of the viewport; Kimi Code had no equivalent, so you lose sight of the instruction that kicked off the current work.
What changed
The last sent message now stays visible as a subtle band pinned to the top of the terminal viewport once it scrolls off — kimi-blue text with a thin muted rule, capped at 3 lines with an ellipsis. It never duplicates on-screen content (hidden while the original message is still visible), updates on every send, and clears on
/clear,/new, and session switches. Opt out withpin_last_user_message = falseintui.toml(defaulttrue, live-reloadable via/reload).Evidence video (42s, real terminal, stub session — no API calls): https://github.com/lucastononro/kimi-code/releases/download/pin-last-user-message-evidence/evidence.mp4
A dev build of this branch running on macOS (Terminal.app, real session) — prompt pinned at the top while a 300-line reply streams underneath:
How it works
pi-tui renders the whole component tree as a flat line buffer whose tail is the viewport — there is no top-static region. The one existing mechanism that paints at a fixed viewport position regardless of scroll is the overlay system (
TUI.showOverlay, composited per frame), so the pin is implemented as a non-capturing, top-anchored, full-width overlay:packages/pi-tui/src/tui.ts— two read-only accessors,getViewportTop()andgetContentHeight(), so components can read the engine's scroll state. No render-path changes.PinnedUserMessageComponent(new) — snapshots the message text (the transcript window may trim the originalUserMessageComponent), and returns zero lines until the original message has scrolled above the viewport. Visibility is anchored to the message's actual position: the transcript container's rendered height right after the append (cheap via child render caches), not the engine's stale whole-buffer height.KimiTUI— snapshots the text on every live send (normal, mid-stream steer, and queued-then-sent flows), lazily mounts the overlay on first send, and clears the pin on/clear,/new, and session switches. Dialogs (higher focus order) still stack above the pin.pin_last_user_messagekey intui.toml, documented in the configuration reference (en/zh).Trade-off (accepted)
While the pin is visible, frames that append transcript lines shift the overlay's absolute row, so the differential renderer repaints from the top of the viewport on those frames (in-place updates like spinners and streaming deltas on existing lines stay cheap). Output is wrapped in synchronized-output markers, so no tearing — same cost profile as any open dialog overlay today. A DECSTBM scroll-region engine rewrite would avoid it but risks breaking scrollback semantics and the cursor math.
Validation
apps/kimi-codevitest suite: 2479 passed (incl. 7 new component tests + 4 new flow tests; config fixtures updated)packages/pi-tuinode:test suite: 739 passed (incl. 3 new viewport-accessor tests)pnpm lintclean, monorepopnpm typecheckcleanChecklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.