Skip to content

feat(tui): dock the input box to the bottom of the terminal - #3022

Open
droid-ash wants to merge 3 commits into
MoonshotAI:mainfrom
droid-ash:feat/tui-always-full-screen
Open

feat(tui): dock the input box to the bottom of the terminal#3022
droid-ash wants to merge 3 commits into
MoonshotAI:mainfrom
droid-ash:feat/tui-always-full-screen

Conversation

@droid-ash

@droid-ash droid-ash commented Aug 18, 2026

Copy link
Copy Markdown

Related Issue

Resolve #2959
Resolve #891
Resolve #875
Resolve #2721
Resolve #2195

Problem

The input box is not docked to the bottom of the terminal — it renders directly after the last message. Two things follow from that.

At rest, the input sits wherever the conversation happens to end, with dead space below it:

                 today                                        this PR
   ..........................................    ..........................................
   :  Welcome to Kimi Code!                 :    :  Welcome to Kimi Code!                 :
   :  Send /help for help information.      :    :  Send /help for help information.      :
   :                                        :    :                                        :
   :  > _                                   :    :         conversation area              :
   :                                        :    :          (scrolls here)                :
   :                                        :    :                                        :
   :            dead space                  :    :........................................:
   :                                        :    :  > _                                   :
   :                                        :    :  Kimi K3 Fast   ~/project   main       :
   :........................................:    :........................................:

While scrolling back through a long turn, the input scrolls away with the content. You have to scroll all the way back down before you can type again, which breaks the read-then-type loop:

                 today                                        this PR
   ..........................................    ..........................................
   :  earlier message                       :    :  earlier message                       :
   :  earlier message                       :    :  earlier message                       :
   :  earlier message                       :    :  earlier message                       :
   :  earlier message                       :    :........................................:
   :  earlier message                       :    :  > _                                   :
   :........................................:    :  Kimi K3 Fast   ~/project   main       :
                                                 :........................................:
      input box is off-screen — scroll all          input box never moves; only the
      the way down before you can type              conversation area scrolls

#875 additionally asks for the panels attached to the input (plan, context status, hints) to stay pinned with it. #2721 asks for a fullscreen mode with a stable, app-controlled rendering surface, plus app-level selection that actually copies.

What changed

The interactive TUI renders on the alternate screen. The transcript scrolls inside a ScrollView; the editor and the rest of the chrome — activity, todo, queue, btw, footer — sit in a dock pinned to the bottom, so the input box is always visible and always in the same place.

createTUIState constructs TuiAltScreen and mounts that layout root. There is no mode switch: buildLayout() is gone, mountFooter() appends to the dock, and dockContainer is non-optional.

Quitting is unaffected. stopUiForExit leaves the alternate screen with the frame preserved and replays the transcript through a TuiMainScreen, so the conversation still lands in native scrollback.

Scrolling the conversation area is served by page / half-page / line scrolling, the mouse wheel, Ctrl-Shift-Up/Down to jump between prompts, and Ctrl-Shift-F to search the transcript.

Two details the docked layout depends on:

  • home / end belong to the editor. TUI.handleInput runs input listeners before the focused component, and a listener returning {consume: true} returns early — so a scrollable transcript would otherwise let the viewport claim keys the editor binds to line start / line end. Viewport scroll-to-top/bottom is bound to shift+home / shift+end instead.
  • Selections reach the system clipboard. Mouse capture replaces the terminal's own drag-selection, and OSC 52 alone is refused by default in many terminals, which would let the "Copied!" flash claim a copy that never landed. TuiAltScreenOptions.onCopy is called alongside the OSC 52 write and kimi-code wires its native clipboard binding to it; OSC 52 remains the fallback where no native clipboard is reachable (SSH, headless). This is the split Feature request: fullscreen TUI mode with mouse text selection/copy (like Claude Code) #2721 asks for.

KIMI_CODE_TUI_FULL_SCREEN no longer selects anything, so its row is dropped from the env-vars docs in both locales. Historical changelog entries that mention it are left alone.

On #2195 (opt out of the alternate screen)

That issue asks for either inline rendering or "In-TUI transcript scrolling (PageUp/PageDown or mouse wheel) as an alternative." The docked layout provides the second in full, and the transcript still reaches native scrollback on exit. Its premise was also mistaken when filed: the script -qec kimi capture on #2721 shows 0.34.0 emitted no \x1b[?1049h, so the TUI was already inline and the reported scroll problem had another cause.

An automatic inline fallback for terminals that cannot do the alternate screen was considered and left out: it would reinstate the dual layout path this PR deletes, to serve non-TTY / TERM=dumb interactive sessions that were already unsupported. Happy to add either that or a tui.toml opt-out if you would rather have one — both are small.

Notes for reviewers

Bump level. The changeset is minor. This changes default rendering for every user and turns a documented env var into a no-op, so major is arguable — flag it and I will change it.

Wider audience for two open bugs. #2193 (VS Code integrated terminal jumps to the top on mouse scroll) and #2324 (flicker during terminal resize) are not introduced here, but they reach every user rather than opt-in users. #2193 looks like the right follow-up.

pi-tui divergence. onCopy is a local divergence from upstream pi-tui, recorded as entry 9 in packages/pi-tui/AGENTS.md with its guarding test.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

The interactive TUI now always renders on the alternate screen: the
transcript scrolls inside a ScrollView while the editor and the rest of
the chrome stay docked at the bottom, so the input box no longer scrolls
out of view when reviewing earlier output.

The layout already existed behind KIMI_CODE_TUI_FULL_SCREEN; this makes
it the only mode and drops the env gate. Quitting still replays the
transcript through a main-screen renderer, so native scrollback ends up
with the conversation.
@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 95d25bc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@moonshot-ai/pi-tui Patch
@moonshot-ai/kimi-code Minor

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45cdd5a5ed

ℹ️ 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".

// chrome stay docked at the bottom while the transcript scrolls above them.
// `stopUiForExit` replays the transcript through a main-screen renderer on
// the way out, so native scrollback still ends up with the conversation.
const ui = new TuiAltScreen(terminal, undefined, undefined, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Home/End editing in the default TUI

Because createTUIState now always constructs TuiAltScreen, every interactive session gets the viewport input listener by default; I checked packages/pi-tui/src/tui-alt-screen.ts and it consumes home/end whenever the primary scroll view can scroll, while packages/pi-tui/src/keybindings.ts also uses those same keys for editor line start/end. In any conversation long enough to make the transcript scrollable, pressing Home or End while typing in the docked editor will jump the transcript instead of moving the cursor, so the default input box regresses for common terminal-editing keys unless the viewport bindings are made conditional/remapped or editor focus is allowed to win.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in 4f49d99.

You are right about the mechanism. TUI.handleInput runs inputListeners before dispatching to focusedComponent, and a listener returning {consume: true} returns early, so once getPrimaryScrollView().canScroll is true the viewport claimed home/end and the editor never saw them. Harmless while the docked layout was opt-in; a real regression now that it is the default.

Fix: createTUIState installs a KeybindingsManager that remaps tui.altScreen.top / tui.altScreen.bottom to shift+home / shift+end. The editor is focused for nearly the whole session, so home/end belong to it; scroll-to-top/bottom keeps a binding rather than being dropped. shift+home / shift+end are recognized key ids and were unbound.

Added a test in fullscreen-layout.test.ts covering both halves: with a scrollable transcript, home/end move the editor cursor to col 0 / end-of-line, and shift+home / shift+end still scroll to top and resume following output. It fails on the previous commit.

The alternate screen's viewport input listener runs before the focused
component, so its default home/end bindings swallowed the editor's
line-start / line-end keys once the transcript grew long enough to
scroll. That was harmless while the docked layout was opt-in; it is a
regression now that every session uses it.

Remap the viewport's scroll-to-top/bottom to shift+home / shift+end so
home/end reach the editor, and cover both halves with a test.
Mouse capture in the alternate screen replaces the terminal's own
drag-selection, and pi-tui's copy path wrote OSC 52 only. Many terminals
refuse OSC 52 writes by default, so on those the "Copied!" flash claimed
a copy that never reached the clipboard — harmless while the docked
layout was opt-in, but not once every session uses it.

Add a TuiAltScreenOptions.onCopy hook called alongside the OSC 52 write
and wire kimi-code's native clipboard binding to it. OSC 52 remains the
fallback for hosts with no reachable native clipboard (SSH, headless),
which is the split MoonshotAI#2721 asked for.
@droid-ash

Copy link
Copy Markdown
Author

Follow-up on the escape-hatch note I left in the description — I have retracted it, and pushed two fixes.

Retracting the full_screen offer. I re-read #2195: it asks for either inline rendering or "In-TUI transcript scrolling (PageUp/PageDown or mouse wheel) as an alternative". The alternate screen provides the second in full (page/half-page/line scrolling, wheel, Ctrl-Shift-Up/Down prompt jumps, Ctrl-Shift-F search), and the transcript still reaches native scrollback on exit. Its premise was also mistaken when filed — the script -qec kimi capture on #2721 shows 0.34.0 emitted no \x1b[?1049h, so the TUI was already inline. I have added #2195 to the resolves list rather than adding a config key to work around it.

I also dropped the idea of an automatic inline fallback: it would mean reinstating the dual layout path this PR deletes, to serve non-TTY / TERM=dumb interactive sessions that were already unsupported.

Two real regressions found and fixed instead, both consequences of promoting this layout to the default:

  • 4f49d99home/end were swallowed by the viewport once the transcript could scroll, so they never reached the editor. Remapped scroll-to-top/bottom to shift+home / shift+end.
  • 95d25bc — mouse capture replaces the terminal own drag-selection, and pi-tui copied via OSC 52 only, which many terminals refuse by default; "Copied!" could claim a copy that never landed. Added a TuiAltScreenOptions.onCopy hook called alongside the OSC 52 write and wired kimi-code native clipboard binding to it, OSC 52 staying the fallback for SSH/headless. That is the split Feature request: fullscreen TUI mode with mouse text selection/copy (like Claude Code) #2721 asked for, so it is now in the resolves list too.

Both are covered by tests that fail without the fix. Full CLI suite 2943 passing, pi-tui suite 975 passing, typecheck clean, lint unchanged. The onCopy hook is recorded as pi-tui divergence 9 per that package guide.

@droid-ash

Copy link
Copy Markdown
Author

@liruifengv — could you kick off the workflow run here when you have a moment? No checks have run yet, so there is nothing green to review against.

This builds directly on your #2830: it takes the docked alternate-screen layout you added behind KIMI_CODE_TUI_FULL_SCREEN and makes it the only mode, which is why it lands in your area. The two supporting fixes (home/end reaching the editor, and selections copying via the native clipboard with OSC 52 as fallback) are both consequences of that layout becoming the default.

No rush on the review itself — it is a default-behaviour change and the minor-vs-major question in the description is worth a real opinion. Just want CI moving.

@droid-ash

Copy link
Copy Markdown
Author

@wbxl2000 — following up since no workflow run has started here yet; could you kick one off? There is nothing green to review against until then.

This builds on @liruifengv's #2830, making the docked alternate-screen layout the only mode. No rush on the review itself — it is a default-behaviour change and the minor-vs-major note in the description deserves a proper look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment