Skip to content

[Bug]: CLI - interactive TUI (regular mode): no way to scroll back the transcript — the wheel is not handled by the TUI and PageUp/PageDown are bound to the composer #535

Open

Description

@sandeep-alluru

Product or interface

CLI - interactive TUI

Version

0.6.12

Platform

Linux

OS version and architecture

Ubuntu 24.04.4 LTS x86_64

Issue area

UI / UX

Desktop log upload ID (optional)

No response

Steps to reproduce

  1. Run the interactive TUI in regular mode — the default. mcode, with no --tui-mode fullscreen.
  2. Run it over SSH, inside tmux, with tmux's own mouse reporting off (set -g mouse off, which is the tmux default). Environment: TERM=tmux-256color, tmux 3.4, Node v22.22.2.
  3. Produce a transcript taller than the viewport — send a few messages, or let an answer stream until the output overflows.
  4. Try to scroll the transcript back up:
    • Mouse wheel — nothing happens at all. The wheel is never handled.
    • PageUp — the composer scrolls (the text in the input box moves); the transcript does not.
    • PageDown — same, in the other direction.
  5. For contrast, run the same session in fullscreen mode — mcode --tui-mode fullscreen. There the wheel scrolls the transcript, and PageUp/PageDown scroll it too.

No keys, tokens or account details are needed to reproduce; synthetic text is enough to overflow the viewport.

Expected and actual behavior

Expected: in regular mode there is some way to scroll the transcript back — the wheel, or a key binding.

Actual: in regular mode there is none. The transcript is effectively write-only: as soon as it is taller than the viewport there is no way back through it. There are two separate causes, and both have to be fixed for regular mode to be usable.

1. The TUI only asks the terminal for mouse events when the alt screen is active.

In the shipped bundle (@minimax-ai/code, chunks/launcher-*.js), the mouse-reporting enable sequence — \x1B[?1000h\x1B[?1002h\x1B[?1004h\x1B[?1006h, plus \x1B[?1003h when not under tmux/screen — is written in beforeTerminalStart(). That same method writes \x1B[?1049h (enter alt screen) and sets altScreenActive = true. The matching disable (\x1B[?1006l\x1B[?1004l\x1B[?1003l\x1B[?1002l\x1B[?1000l) is written in beforeTerminalStop() and is guarded by if (this.altScreenActive).

The regular-mode path never writes the enable sequence (this.mouseEnabled defaults to true, but it is only consulted inside the alt-screen branch). So the terminal never sends wheel events, and the TUI never receives one to handle.

2. PageUp/PageDown are bound to the composer, not to the transcript.

The viewport bindings that scroll the transcript — tui.altScreen.pageUp / tui.altScreen.pageDown — live in the alt-screen namespace and are dispatched by handleViewportInput(), which is the fullscreen path.

In regular mode the composer has focus, so interactionActive is false and pageUp/pageDown resolve to tui.editor.pageUp / tui.editor.pageDown (default keys pageUp and pageDown), whose handler calls this.pageScroll(-1) / this.pageScroll(1) — the editor's own scroll. The interaction.scroll-up / interaction.scroll-down bindings (also pageUp / pageDown) only apply while the interaction layer has focus, which is not the normal regular-mode state; the context resolver treats interaction and composer as mutually exclusive.

Notes

Activity

  1. 1anZhang commented on Oct 24, 2026

    @1anZhang
    Collaborator

    Hi @sandeep-alluru, thanks a lot for the incredibly detailed report — the repro steps and the code walkthrough made this very easy to look into 🙏

    Your reading of the code is accurate. The behavior you're seeing comes from a deliberate design choice in regular mode, though, rather than a missing handler:

    • Regular mode renders into the terminal's main buffer and leaves scrollback to the terminal. Finalized transcript lines (submitted prompts, finished steps, committed Markdown blocks) are written into the terminal's native scrollback, so the terminal owns the wheel, scrolling, text selection and copy. That's also why the TUI doesn't turn on mouse reporting in this mode: if it did, it would take over the wheel and break native selection/copy, which is the main reason to pick regular mode over fullscreen.
    • PageUp/PageDown go to the composer because regular mode has no TUI-managed transcript viewport to scroll. The transcript is just the terminal's history.

    So the missing piece in your setup is tmux itself. tmux runs inside the outer terminal's alternate screen, so the outer terminal's scrollback isn't reachable, and with mouse off (the tmux default) the wheel never gets to tmux's own pane history. You'd see the same thing with any plain main-buffer program, e.g. cat of a long file.

    Ways to scroll back in regular mode under tmux

    1. Enable tmux mouse support (recommended):

      # ~/.tmux.conf
      set -g mouse on

      Then tmux source-file ~/.tmux.conf. tmux's default WheelUpPane binding enters copy mode when the pane isn't in the alternate screen and hasn't requested mouse events, which is exactly the case for mcode in regular mode. The wheel will then scroll the transcript right away, and you can still select and copy.

    2. Use tmux copy mode from the keyboard: press prefix + [ (by default Ctrl-b [), then use PageUp/PageDown or the arrow keys. Press q to exit.

    3. Use fullscreen mode, as you found: mcode --tui-mode fullscreen, or set it permanently via /settings. Here the TUI manages its own transcript viewport, so the wheel and PageUp/PageDown scroll it directly, no tmux config needed.

    One small caveat: when the terminal is resized, regular mode does a full redraw that rebuilds the visible history, so what tmux's copy mode shows may be re-laid out (and older scrollback cleared) after a resize. This is a known limitation we're still working to reduce.

    We'll also look into adding a note about tmux to the docs so others don't run into this. Thanks again for the thorough write-up, and please let us know if any of the above doesn't work for you!

  2. sandeep-alluru commented on Oct 25, 2026

    @sandeep-alluru
    Author

    Thanks — this is a very clear explanation, and it matches what I found in the shipped 0.6.12 bundle.

    The two mouse-enable constants (CP under tmux/screen/zellij, wP elsewhere) are written from exactly one call site, and that site lives in the renderer class whose mode is "fullscreen"; the "regular" renderer is a separate class and never writes them, and the mode is chosen by tuiMode === "fullscreen" ? "fullscreen" : "regular". So regular mode genuinely does not request mouse events, and your account of it is right — including why PageUp/PageDown are the composer's.

    One thing I want to pin down, because it changes what I run: the fullscreen renderer enables mouse reporting by default (mouseEnabled = s.mouse ?? !0), and under tmux it still sends CP — 1000/1002/1004/1006. With set -g mouse on in my tmux config the wheel is forwarded to the app, so fullscreen should scroll its own viewport, which is what I want, since regular mode's full redraw on resize rebuilds the visible history and clears older scrollback — that is the part that actually bites me, since I resize panes constantly.

    So the ask: would you consider exposing the mouse option the fullscreen renderer already reads as a user-facing setting, so fullscreen mode can be run without requesting mouse events at all? It looks like a one-line change from s.mouse ?? !0 to a config-backed default. And if the resize redraw's scrollback handling is on the roadmap, a note in the docs about the tmux setup for regular mode would save other people the same detour.

    On contributing: I have the repo cloned and can work against pnpm verify. I see CONTRIBUTING says contributions are currently accepted from collaborators only, so please let me know the preferred route — a branch you pull from, a patch attached to the issue, or a collaborator invite — and I'll put it up whichever way is easiest for you.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    tuiInteractive terminal UI (TUI)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions