Skip to content

Modal confirm prompts invisible when chat is scrolled up (shell / plan / checkpoint / revision / choice) #642

@esengine

Description

@esengine

Problem

If the user has scrolled up in the chat history and then a modal-class confirm prompt arrives — shell command (run_command / run_background), plan proposed, plan checkpoint, plan revision, or ask_choice — the modal mounts but is not visible: it renders at the bottom of the chat area, which is currently off-screen below the user's scroll position. From the user's point of view they're "stuck" — they can't see the options, and the picker has captured arrow keys so scroll-down inputs no longer scroll the viewport.

Root cause

The pauseGate listener in src/cli/ui/App.tsx:3038-3114 calls setPendingShell / setPendingPlan / setPendingCheckpoint / setPendingRevision / setPendingChoice directly. None of those mount paths reset the chat scroll position. The only chatScroll.jumpToBottom() call in the whole file is on the End key (App.tsx:1218).

chatScroll.jumpToBottom() sets pinned: true; the next setMaxScroll (triggered by the modal's useReserveRows shrinking the chat area) snaps scrollRows to the new bottom. So the existing primitives are sufficient — we just have to call them.

Proposed fix

Add chatScroll.jumpToBottom() once at the top of the pauseGate listener callback, before the switch (request.kind). Covers all five modal kinds with one line. chatScroll from useChatScrollActions() is store-backed and stable across renders, so the empty-deps useEffect stays correct.

Test

Add a test that mounts the App-level pauseGate listener wiring, scrolls up via the store, fires a pauseGate.request("run_command", …), and asserts pinned === true and scrollRows === maxScroll afterward.

Scope

  • src/cli/ui/App.tsx — one line, plus a tweak to the existing biome-ignore comment so it reads correctly with the new ref.
  • tests/ — one new file covering the modal-mount → scroll-reset invariant for all five modal kinds.

Good first issue territory.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions