Skip to content

fix(tui): keep approval controls visible for long prompts (#1132)#1156

Merged
Aaronontheweb merged 4 commits into
netclaw-dev:devfrom
Aaronontheweb:claude-wt-long-prompts-tui
May 24, 2026
Merged

fix(tui): keep approval controls visible for long prompts (#1132)#1156
Aaronontheweb merged 4 commits into
netclaw-dev:devfrom
Aaronontheweb:claude-wt-long-prompts-tui

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #1132. Long shell_execute approval prompts (cd with many path arguments, etc.) wrapped over many lines inside the Input panel and pushed both the selection list and the [Enter] Confirm status hint off-screen, leaving users unable to respond.

The fix renders a summary + a bounded body + hint + selection list. Ctrl+V toggles between collapsed (one-line truncated) and expanded (multi-line) body. All sizing scales with the terminal: body width, expanded body cap, Input panel cap, and status bar text are all derived from live IAnsiTerminal.Width/Height at each render, and the page subscribes to ResizeEvent to re-render on terminal resize.

  • IAnsiTerminal is injected into ChatPage. The DynamicLayoutNode callback reads width/height every render.
  • Expanded body cap = panelMax - (4 + optionCount) where panelMax = max(8, terminalHeight / 2). Adapts to the actual ApprovalOptions.Count so the 5-option production set (Once, This chat, Always here, Always anywhere, Deny) doesn't clip.
  • Body width budget = terminalWidth - 4; narrow terminals get a proportionally shorter truncation.
  • Status bar picks short/medium/full key-hint variants by width (full ≥100 cols, medium ≥70, terse below). Model ID is dropped below 80 cols.
  • [PgUp/PgDn] Scroll hint is restored to the pending-interaction state — the design's stated escape hatch for >cap bodies.
  • Wire-level DisplayText (ShellApprovalMatcher.FormatForDisplay, ToolAccessPolicy, SessionToolExecutionPipeline) is unchanged — non-TUI surfaces (headless -p, JSON-RPC, future channels) still see the full prompt.

What the new screen looks like

Collapsed (default) on a 120-col terminal:

╭─Input────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│Approval required for shell_execute.                                                                                  │
│cd /Users/kevin/code/compiler/Diagnostics.pn compiler/Symbol.pn compiler/Binder /private/var/… [Ctrl+V to view full]  │
│Select an option and press Enter.                                                                                     │
│1. Once                                                                                                               │
│2. This chat                                                                                                          │
│3. Deny                                                                                                               │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[Up/Down] Select [Enter] Confirm [Ctrl+V] View full [PgUp/PgDn] Scroll [Ctrl+Q] Quit | Approval required | test-model

Expanded (after pressing Ctrl+V) — body now scales to the available panel height:

╭─Input────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│Approval required for shell_execute.                                                                                  │
│cd /Users/kevin/code/compiler/Diagnostics.pn compiler/Symbol.pn compiler/Binder                                       │
│/private/var/folders/pj/ncqg4f1s58l87j6n_9xvnz9m0000gn/T/tmp.aa                                                       │
│/private/var/folders/pj/ncqg4f1s58l87j6n_9xvnz9m0000gn/T/tmp.bb                                                       │
│/private/var/folders/pj/ncqg4f1s58l87j6n_9xvnz9m0000gn/T/tmp.cc                                                       │
│/private/var/folders/pj/ncqg4f1s58l87j6n_9xvnz9m0000gn/T/tmp.dd                                                       │
│/private/var/folders/pj/ncqg4f1s58l87j6n_9xvnz9m0000gn/T/tmp.ee SENTINEL_TAIL_MARKER Patterns: cd                     │
│Select an option and press Enter.                                                                                     │
│1. Once                                                                                                               │
│2. This chat                                                                                                          │
│3. Deny                                                                                                               │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
[Up/Down] Select [Enter] Confirm [Ctrl+V] Collapse [PgUp/PgDn] Scroll [Ctrl+Q] Quit | Approval required | test-model

The tail of the body (SENTINEL_TAIL_MARKER Patterns: cd) now renders inside the Input panel — earlier versions of this branch clipped it at a hardcoded 5-row cap.

Full ASCII captures committed under src/Netclaw.Cli.Tests/Tui/__snapshots__/chat-approval-{collapsed,expanded}.txt.

Tests

ChatPageTests exercises the fix headlessly via Termina's VirtualTerminal (same pattern as ApprovalsManagerPageTests):

  • LongApprovalBody_KeepsControlsVisible — direct regression for UX: Long approval prompts hide choices #1132 at 120×40.
  • CollapsedView_ShowsEllipsisAndCtrlVHint, CtrlV_TogglesFullBodyAndKeepsControlsVisible, NewInteraction_ResetsCollapsedState — toggle and state semantics.
  • NarrowTerminal_PreservesCtrlVHint — 60-col terminal: Ctrl+V affordance must remain visible.
  • FiveOptionApproval_AllOptionsVisibleWhenExpanded — the canonical production option set fits in expanded mode without clipping.
  • SmallTerminal_PanelDoesNotEatChatHistory — 16-row terminal: the Input panel cap scales down so the chat history pane still renders.
  • Resize_RerendersStatusBarAndBody — 120 → 50 col resize re-renders with the shortened status text.
  • LongApprovalBody_RenderedFrameSnapshot — writes the ASCII frames above to __snapshots__/.

Test plan

  • dotnet test src/Netclaw.Cli.Tests — 752/752 pass (9 ChatPage tests + 743 existing)
  • dotnet slopwatch analyze — 0 issues
  • ./scripts/Add-FileHeaders.ps1 -Verify — clean
  • Manual verification: launch netclaw chat against a real provider; trigger a long approval; resize the terminal; confirm layout adapts and Ctrl+V works as shown above.

Marked draft

Per request, opening in draft so the rendering and scaling behavior can be reviewed before merging.

@Aaronontheweb Aaronontheweb added the tui Terminal UI (Termina) issues label May 23, 2026
…v#1132)

Long shell_execute approval bodies in `netclaw chat` wrapped over many
lines inside the Input panel and pushed the selection list and
`[Enter] Confirm` hint past the panel cap, leaving the user unable to
respond. Render a one-line summary plus a single-line truncated body
with a `[Ctrl+V to view full]` affordance by default; Ctrl+V expands
the body to a 5-row cap (with full text always available in the chat
history pane above) while keeping all selection options on-screen. The
wire-level `DisplayText` is untouched so non-TUI surfaces still see the
full prompt.

Adds headless ChatPageTests using Termina's VirtualTerminal and commits
ASCII frame snapshots under tests/Tui/__snapshots__/ as the visual
regression artifact for the issue.
Address scaling issues flagged in code review of the initial netclaw-dev#1132 fix.
Previously the body width (76 cols), expanded body cap (5 rows), Input
panel cap (14 rows), and status bar text were all hardcoded, producing
clipped affordances on narrow terminals, clipped tails for >5-line
bodies, and a layout that hit the cap exactly with the 5-option
production approval set.

- Inject IAnsiTerminal into ChatPage; read live width/height in the
  DynamicLayoutNode callback so body width, expanded body cap, and
  panel cap all scale with the actual terminal size.
- Expanded body cap derives from terminal height and live option count:
  panelMax = max(8, height/2); bodyMax = panelMax - (4 + optionCount).
- Status bar text now picks short/medium/full key-hint variants based
  on width and includes UiVersion in CombineLatest so resize re-emits.
- Subscribe to ResizeEvent in OnBound and bump UiVersion to trigger
  a layout re-evaluation on terminal resize.
- Restore the [PgUp/PgDn] Scroll hint to the pending-interaction key
  string (the design's stated escape hatch for >cap bodies).

Adds four new headless tests covering 60-col narrow terminal, the
5-option production set in expanded mode, a 16-row small terminal
(chat history must stay visible), and a terminal-resize round-trip.
The outer Input panel's HeightAuto(max) constraint was captured once at
first navigation and never updated on terminal resize. Even though the
inner DynamicLayoutNode re-evaluated body width and expanded-body caps
on resize, the outer panel's frozen max prevented the layout from
actually growing when the terminal got taller.

Termina 0.10.0 adds ReactivePage.InvalidateLayout() which discards the
cached layout tree and rebuilds via BuildLayout() with fresh state. Now
the resize handler calls InvalidateLayout() so the panel cap follows
_terminal.Height changes — completing the dynamic scaling fix for netclaw-dev#1132.

Ref: Termina PR netclaw-dev#219 (ResizeEvent forwarding), PR netclaw-dev#220 (InvalidateLayout)
@Aaronontheweb Aaronontheweb force-pushed the claude-wt-long-prompts-tui branch from 9f927b1 to 79b4595 Compare May 24, 2026 11:08
@Aaronontheweb Aaronontheweb marked this pull request as ready for review May 24, 2026 12:49
@Aaronontheweb Aaronontheweb enabled auto-merge (squash) May 24, 2026 12:49
@Aaronontheweb

Copy link
Copy Markdown
Collaborator Author

Haven't had a chance to test this locally but best as I can tell from the ASCII output from Termina's VirtualInputSource it should be resolved.

@Aaronontheweb Aaronontheweb merged commit bcd0200 into netclaw-dev:dev May 24, 2026
14 checks passed
@Aaronontheweb Aaronontheweb mentioned this pull request May 26, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tui Terminal UI (Termina) issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UX: Long approval prompts hide choices

1 participant