feat: TUI mode with scroll, mouse, conversation rendering fixes#24
Merged
Conversation
PTFOPlayer
commented
Jun 5, 2026
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
- 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.