Skip to content

fix(tui): make user_prompt elicitation dialog scrollable#2509

Merged
dgageot merged 1 commit intodocker:mainfrom
dgageot:board/fix-docker-agent-github-issue-2495-35d25358
Apr 27, 2026
Merged

fix(tui): make user_prompt elicitation dialog scrollable#2509
dgageot merged 1 commit intodocker:mainfrom
dgageot:board/fix-docker-agent-github-issue-2495-35d25358

Conversation

@dgageot
Copy link
Copy Markdown
Member

@dgageot dgageot commented Apr 25, 2026

Fixes #2495.

When `user_prompt` opens an elicitation pop-up with many enum items (or many fields, or a long message), the dialog overflowed the bottom of the terminal with no way to access the clipped content — neither visually nor for keyboard navigation.

What changed

The body region (message + fields, or message + free-form input) is now rendered inside a scrollview, capped at 80% of the terminal height (max 40 rows). Title/separator stay fixed at the top, the help line stays fixed at the bottom.

  • The scrollview consumes mouse wheel, scrollbar drag, and PgUp/PgDn/Home/End. Up/Down keep their existing role of cycling enum/boolean selections.
  • Auto-scroll keeps the focused field's active line visible:
    • Tab / Shift-Tab brings the new field into view.
    • Up/Down inside an enum/boolean keeps the selected option in view even when the field is taller than the viewport.
    • Submit-with-error focuses and scrolls to the offending field.
  • Mouse clicks translate the absolute Y through the current scroll offset before mapping to a field/option, so clicks on visible options still work when scrolled.
  • The help line gains a `pgup/pgdn scroll` hint when scrolling is active.
  • A single `layout()` method computes geometry (dialog width, viewport, body lines, per-field line offsets) shared by `View()` and `Position()` so layout math lives in one place.
  • `buildBody` tracks line offsets incrementally to stay O(N) in the number of fields.

Tests

New unit tests in `pkg/tui/dialog/elicitation_test.go`:

  • `TestElicitationDialog_LongEnumScrolls` — 30-option enum on a 20-row terminal needs a scrollbar; selecting an option near the end auto-scrolls it into view.
  • `TestElicitationDialog_FieldsBelowFold_AreReachable` — tabbing through 15 fields on an 18-row terminal keeps each focused field visible.
  • `TestElicitationDialog_SmallContent_NoScrollbar` — small dialogs do not show a scrollbar.

All existing tests still pass; `golangci-lint run ./...` is clean.

How to test manually

```yaml

/tmp/triage.yaml

version: "2"
agents:
root:
model: openai/gpt-5
description: Triage tester
instruction: |
As soon as the user says anything, call user_prompt EXACTLY ONCE with a
'category' enum field whose 12 options are long incident-triage labels.
toolsets:
- type: user_prompt
```

```sh
./bin/cagent run /tmp/triage.yaml

type any message → dialog appears with a scrollbar; Up/Down/PgUp/PgDn navigate;

all 12 items are reachable; mouse wheel & scrollbar drag work.

```

Fixes docker#2495.

When `user_prompt` opens an elicitation pop-up with many enum items
(or many fields, or a long message) the dialog overflowed the bottom
of the terminal with no way to access the clipped content — neither
visually nor for keyboard navigation.

The body region (message + fields, or message + free-form input) is
now rendered inside a scrollview, capped at 80% of the terminal
height (max 40 rows). Title/separator stay fixed at the top, the help
line stays fixed at the bottom.

- The scrollview consumes mouse wheel, scrollbar drag, and
  PgUp/PgDn/Home/End. Up/Down keep their existing role of cycling
  enum/boolean selections.
- Auto-scroll keeps the focused field's active line visible: Tab /
  Shift-Tab brings the new field into view, and Up/Down inside an
  enum/boolean keeps the selected option in view even when the field
  itself is taller than the viewport. Submit-with-error focuses and
  scrolls to the offending field.
- Mouse clicks translate the absolute Y through the current scroll
  offset before mapping to a field/option, so clicks on visible
  options still work when the dialog is scrolled.
- The help line gains a "pgup/pgdn scroll" hint when scrolling is
  active.
- A single layout() method computes geometry (dialog width, viewport,
  body lines, per-field line offsets) shared by View() and
  Position(), so layout math lives in exactly one place.
- buildBody tracks line offsets incrementally to stay O(N) in the
  number of fields.

Tests cover long enums (30 options on a 20-row terminal), many
fields below the fold (15 fields on an 18-row terminal), and small
content that should not show a scrollbar.

Assisted-By: docker-agent
@dgageot dgageot requested a review from a team as a code owner April 25, 2026 18:00
@dgageot dgageot merged commit d84a8a6 into docker:main Apr 27, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants