Skip to content

feat: TUI mode with scroll, mouse, conversation rendering fixes#24

Merged
PTFOPlayer merged 11 commits into
masterfrom
tui-fixes
Jun 9, 2026
Merged

feat: TUI mode with scroll, mouse, conversation rendering fixes#24
PTFOPlayer merged 11 commits into
masterfrom
tui-fixes

Conversation

@PTFOPlayer

Copy link
Copy Markdown
Owner
  • Add full TUI mode with conversation pane, input bar, status bar, sidebar
  • Fix scroll: mouse scroll and PageUp/PageDown/Home/End now always go to conversation widget regardless of focus
  • Fix mouse scroll: SGR mouse event parsing was broken (< prefix not stripped from parameter string, causing scroll events to be parsed as left-click)
  • Fix conversation text overflow: text now wraps at conversation area width (area.x + area.width - 1) instead of full screen width, preventing overflow into sidebar area
  • Fix conversation line height: now uses character-level wrapping matching actual screen rendering, preventing visual rows from being undercounted
  • Fix scroll offset: changed from conversation-line units to visual-row units that account for text wrapping, so scrolling works correctly with long messages
  • Fix partially-scrolled line rendering: added write_str_wrapped_skip_clipped to Screen that skips N visual rows before rendering, so lines scrolled off the top of the viewport don't re-render their full text
  • ToolResult now renders all content lines (not just first), with background fill, line truncation, and a 20-line cap with truncation indicator
  • ToolResult trims trailing blank lines from content
  • Scrollbar column is reserved (1 column) so text doesn't overlap it
  • Add thinking indicator: shows [thinking] with spinner during thinking phase, accumulates thinking text, transitions to 'Responding' label on first streaming text
  • Widget trait render method changed from &self to &mut self
  • All widget implementations updated accordingly

PTFOPlayer added 11 commits June 5, 2026 13:58
- Add full TUI mode with conversation pane, input bar, status bar, sidebar
- Fix scroll: mouse scroll and PageUp/PageDown/Home/End now always go to
  conversation widget regardless of focus
- Fix mouse scroll: SGR mouse event parsing was broken (< prefix not stripped
  from parameter string, causing scroll events to be parsed as left-click)
- Fix conversation text overflow: text now wraps at conversation area width
  (area.x + area.width - 1) instead of full screen width, preventing
  overflow into sidebar area
- Fix conversation line height: now uses character-level wrapping matching
  actual screen rendering, preventing visual rows from being undercounted
- Fix scroll offset: changed from conversation-line units to visual-row units
  that account for text wrapping, so scrolling works correctly with long messages
- Fix partially-scrolled line rendering: added write_str_wrapped_skip_clipped
  to Screen that skips N visual rows before rendering, so lines scrolled off
  the top of the viewport don't re-render their full text
- ToolResult now renders all content lines (not just first), with background
  fill, line truncation, and a 20-line cap with truncation indicator
- ToolResult trims trailing blank lines from content
- Scrollbar column is reserved (1 column) so text doesn't overlap it
- Add thinking indicator: shows [thinking] with spinner during thinking phase,
  accumulates thinking text, transitions to 'Responding' label on first
  streaming text
- Widget trait render method changed from &self to &mut self
- All widget implementations updated accordingly
TUI mode now prompts the user (y/n/a) before executing destructive
tool calls (write, edit, run) instead of auto-approving everything.

Changes:
- Add ConfirmPrompt line type to conversation widget (yellow bold)
- Add confirmation mode to InputBarWidget (y=yes, n=no, a=all)
- Add ConfirmYes/ConfirmNo/ConfirmAll action variants
- TUI agent loop sends ConfirmTool event and blocks for response
- Add blink style to cell rendering for confirmation cursor
- Fix auto-scroll: add explicit scroll_to_bottom() calls during
  StreamingText and StreamingThinking events
… sidebar with project context

- Sidebar now shows project name, type, git branch, build/test commands,
  and directory structure from WorkspaceContext
- Sidebar is scrollable when focused (F3): arrow keys, PageUp/PageDown, Home
- Scrollbar indicator appears when content overflows
- Mouse wheel and PageUp/PageDown route to the focused widget
  (sidebar when focused, conversation otherwise)
- Added SIGWINCH signal handler to detect terminal resizes and
  inject Event::Resize events for proper screen buffer updates
- Added signal-hook dependency for cross-platform SIGWINCH handling
…I polish

- Add interactive file browser in sidebar (Ctrl+P) with directory
  navigation, file type icons, scroll, and enter/escape
- Implement TUI question tool support: questions are shown
  interactively instead of auto-selecting the first answer
- Add multi-line wrapping for tool call argument summaries in
  conversation view
- Add UTF-8-safe truncate_str utility with tests, use in spinner
  label clipping
- Add focus indicator to status bar showing active widget
- Add Focus::Structure variant and wire up scroll/input delegation
- Fix thinking header missing newline in CLI mode
- Preserve whitespace in TUI system messages (remove trim)
… and extensive UI polish

- Add context window warning banner (70%/90% thresholds) in conversation
- Add diff preview in confirmation prompts for edit/write tool calls
- Implement conversation search (Ctrl+F) with match highlighting and navigation
- Add help overlay (Ctrl+H) showing available keybindings
- Add tool output panel toggle (Ctrl+T) showing full tool results
- Add Ctrl+C interrupt support during LLM streaming
- Enhance input bar: mouse click positioning, paste support, kill ring
  (Ctrl+K/U/W/Y), word navigation (Ctrl+Left/Right, Alt+Backspace)
- Replace emoji sidebar icons with narrow single-cell alternatives to fix
  column alignment in cell-based renderer
- Add file filter in sidebar (type to filter visible entries)
- Show hidden files toggle in sidebar (Ctrl+H in structure mode)
- Add Question prompt type support in conversation widget
- Improve tool argument formatting for TUI (full paths, commands)
- Add render_diff_plain and compute_edit_diff_plain for cell-based display
- Fix tool output widget: uncollapse_all, default bg for non-errors
…focus

Help overlay improvements:
- Add scroll support (Up/Down, j/k, PageUp/PageDown, Home/End, mouse wheel)
- Add Escape key to explicitly close the overlay
- Allow Ctrl+C/Ctrl+D to pass through even when help is open
- Clamp scroll on terminal resize to prevent stale offsets
- Extract help content into a static method to avoid per-frame allocation
- Fix scroll indicator overlapping the right border (now drawn inside)
- Reserve a dedicated hint row so content isn't hidden behind it
- Add scroll position indicator (↕/↑/↓) on the right edge
- Early return when terminal is too small (< 4 rows) for the overlay

Unified input/conversation focus:
- Remove Conversation from the normal focus cycle (Tab order)
- Input bar and conversation are now treated as one unit:
  typing goes to input bar, scroll keys always affect conversation
- Focus::Conversation is only entered for search mode (Ctrl+F)
  and automatically returns to InputBar when search is closed
- Clicking on the conversation area no longer steals focus from input
- Status bar shows 'input' for both InputBar and Conversation focus
- Update help overlay section from 'Conversation' to 'Navigation'
  to clarify these keys work from the input bar
- Update all affected tests for the new focus cycle
Move COMMAND_NAMES and subcommand_completions from hardcoded constants
in tinyharness-ui into dynamic data sourced from CommandRegistry at
runtime. CommandHelper and InputBarWidget now accept command names and
subcommand maps via constructors/setters, populated from the single
source of truth in build_registry(). This eliminates the manual sync
burden when adding or removing commands.
…d modules

Deduplicate signal handling, tool confirmation, command results, and
tool result types that were duplicated between the CLI agent loop
(mod.rs + tools.rs) and the TUI agent loop (tui_loop.rs).

New shared modules:
- signal.rs: SignalResult enum + handle_signal_event(), question
  validation/answer helpers, parse error helper
- confirm.rs: ConfirmationDecision enum + decide_tool_confirmation()
  pure decision logic (no I/O)
- tool_result.rs: GenericToolResult struct, batch_tool_results(),
  audit_info_for_tool(), log_tool_audit()
- command_result.rs: CommandResultInfo + apply_switch_session(),
  apply_rename_session(), apply_init(), apply_skill_use(),
  apply_skill_unload(), apply_ok()
- stream.rs: StreamingResult, StreamingAccumulator, ProcessEvent
  (foundation types for future streaming loop unification)

Modified files:
- tools.rs: -298 lines (uses signal/confirm/tool_result modules)
- tui_loop.rs: -283 lines (uses signal/confirm/tool_result/command_result)
- mod.rs: -110 lines (uses command_result module)
… SPINNER_FRAMES

- Remove unused stream.rs module (139 lines) — StreamingAccumulator
  and ProcessEvent were never used by either loop
- Deduplicate SPINNER_FRAMES constant: was defined 3 times
  (style.rs, tools.rs, spinner.rs). tools.rs now uses the one from
  tinyharness_ui::style (already imported via glob), spinner.rs
  imports from crate::style
- Extract compute_tool_diff() and tool_display_content() into
  tool_result.rs, replacing 2 near-identical copies in tui_loop.rs
  (compute_diff_preview + inline display_content block)
- Use audit_info_for_tool() in TUI GenericToolResult construction
  instead of inline duplication

Net: -182 lines
Update taglines, feature lists, CLI flags, and project structure
sections across README.md, TINYHARNESS.md, docs/contributing.md,
and docs/configuration.md to document the --tui flag and the TUI
subsystem in tinyharness-ui. Marked as experimental with ⚠️ warnings.
@PTFOPlayer PTFOPlayer merged commit ddc464b into master Jun 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant