Problem
When the model asks a question via request_user_input, the dialog is unreliable on real terminals: content gets cut off, the underlying conversation becomes invisible, and mistakes are irreversible.
Concrete failures (reproduced on a 141×38 terminal):
- Options are clipped. The modal is capped at 22 rows of height. A question with several options (each with a description) plus the header and hint rows overflows, and everything past the cap is silently cut off. I often cannot see which option is currently selected — the selection highlight is below the visible area.
- Typed input is invisible. The "Custom response" input line is appended after all options in the render output. With a long question and several options it falls below the 22-row cap, so while typing I cannot see what I am writing. There is no cursor tracking and no scroll-into-view.
- The dialog opens centered and disables transcript scrolling. The popup is vertically centered over the conversation (
Layout [Min(0), Length(height), Min(0)]), and while any modal is open the event loop routes every key to the modal (view_stack.handle_key() with continue). PageUp/PageDown and the other transcript-scroll bindings never run. Questions are usually based on the on-screen conversation, so covering it and forbidding scroll forces me to answer from memory.
- No way to go back or correct. Left/Right arrows are unhandled (
_ => ViewAction::None), and there is no previous-question stack: advance_question moves forward only, and Esc in selection mode cancels the whole dialog. An accidental Enter on the wrong option is irreversible.
- Resizing the terminal does not help. The height is
desired.clamp(6, 22).min((r.height * 60 / 100).clamp(6, 22)).min(r.height) — the 22-row cap dominates for any terminal ≥ 37 rows (38 → 22, 50 → 22, 100 → 22). Smaller terminals get even less. The width is also capped at 110 columns, so on wide terminals long questions wrap into more lines and exhaust the height budget faster.
- The modal is undocumented. No hotkey, no "Custom response" behavior, no layout expectations appear anywhere in
docs/GUIDE.md, docs/KEYS.md, or the TUI docs. There is no collapse/minimize action either — Esc is the only escape, and it cancels the request.
Root cause (code)
crates/tui/src/tui/user_input.rs:540-556 — compact_popup_rect: hard height cap clamp(6, 22) + 60% of terminal, width cap min(110), [Min(0), Length(height), Min(0)] vertical centering.
crates/tui/src/tui/user_input.rs:205-244 — handle_selecting_key: only Up/Down/j/k, digits, Space, Enter, Esc; Left/Right and any "back" action are absent.
crates/tui/src/tui/user_input.rs:430-450 (render) — "Custom response" line appended last; no scroll-into-view.
crates/tui/src/tui/ui/event_loop.rs:4791-4809 — non-empty view_stack → view_stack.handle_key(key) + continue; transcript scroll handlers at ~5640+ unreachable while a modal is open.
Expected behavior
Matches how opencode handles model questions:
- Bottom-anchored sheet instead of a centered overlay, so the conversation stays visible above it.
- Scrolling works: either the transcript remains scrollable while the dialog is open, or the dialog itself scrolls internally (selected option and typed text always visible, no silent clipping).
- Back-navigation to the previous question (and a documented way to correct an accidentally submitted answer, e.g. an explicit "go back" binding rather than Esc-cancels-everything).
- Adaptive height: no fixed cap that clips content; the dialog grows or scrolls.
Related
#6003 — the same question/approval flow hardcodes a 300-second wait (USER_INPUT_TIMEOUT, crates/tui/src/core/engine/approval.rs:15; APPROVAL_DECISION_TIMEOUT, crates/tui/src/runtime_threads.rs:547) with no config or kill switch. Both issues are the same UX surface: I'd gently suggest treating them together — a dialog that clips the answer and then silently cancels it after five minutes breaks the workflow twice. Making the timeout configurable/disableable (and the dialog usable at all) is important for anyone who runs local, unattended, or review-heavy sessions.
Acceptance criteria
Problem
When the model asks a question via
request_user_input, the dialog is unreliable on real terminals: content gets cut off, the underlying conversation becomes invisible, and mistakes are irreversible.Concrete failures (reproduced on a 141×38 terminal):
Layout [Min(0), Length(height), Min(0)]), and while any modal is open the event loop routes every key to the modal (view_stack.handle_key()withcontinue). PageUp/PageDown and the other transcript-scroll bindings never run. Questions are usually based on the on-screen conversation, so covering it and forbidding scroll forces me to answer from memory._ => ViewAction::None), and there is no previous-question stack:advance_questionmoves forward only, and Esc in selection mode cancels the whole dialog. An accidental Enter on the wrong option is irreversible.desired.clamp(6, 22).min((r.height * 60 / 100).clamp(6, 22)).min(r.height)— the 22-row cap dominates for any terminal ≥ 37 rows (38 → 22, 50 → 22, 100 → 22). Smaller terminals get even less. The width is also capped at 110 columns, so on wide terminals long questions wrap into more lines and exhaust the height budget faster.docs/GUIDE.md,docs/KEYS.md, or the TUI docs. There is no collapse/minimize action either — Esc is the only escape, and it cancels the request.Root cause (code)
crates/tui/src/tui/user_input.rs:540-556—compact_popup_rect: hard height capclamp(6, 22)+ 60% of terminal, width capmin(110),[Min(0), Length(height), Min(0)]vertical centering.crates/tui/src/tui/user_input.rs:205-244—handle_selecting_key: only Up/Down/j/k, digits, Space, Enter, Esc; Left/Right and any "back" action are absent.crates/tui/src/tui/user_input.rs:430-450(render) — "Custom response" line appended last; no scroll-into-view.crates/tui/src/tui/ui/event_loop.rs:4791-4809— non-emptyview_stack→view_stack.handle_key(key)+continue; transcript scroll handlers at ~5640+ unreachable while a modal is open.Expected behavior
Matches how opencode handles model questions:
Related
#6003 — the same question/approval flow hardcodes a 300-second wait (
USER_INPUT_TIMEOUT,crates/tui/src/core/engine/approval.rs:15;APPROVAL_DECISION_TIMEOUT,crates/tui/src/runtime_threads.rs:547) with no config or kill switch. Both issues are the same UX surface: I'd gently suggest treating them together — a dialog that clips the answer and then silently cancels it after five minutes breaks the workflow twice. Making the timeout configurable/disableable (and the dialog usable at all) is important for anyone who runs local, unattended, or review-heavy sessions.Acceptance criteria