Skip to content

TUI: usability testing issues #3

@AlexChesser

Description

@AlexChesser

Plan: TUI Fixes — 8 Issues, 8 Discrete Commits

Context

Testing the TUI locally revealed eight issues. Each is a separate commit.


Commit 1: Disable mouse capture so text is copyable

Problem

ail/src/tui/mod.rs:29 calls EnableMouseCapture, forwarding mouse events to the app. The TUI doesn't use mouse events — this breaks native terminal text selection and copy.

Changes

  • Remove EnableMouseCapture from execute! on line 29
  • Remove DisableMouseCapture from cleanup execute! on line 38
  • Remove the unused imports

Files

  • ail/src/tui/mod.rs

Commit 2: Redirect tracing to stderr

Problem

main.rs:9 inits tracing with tracing_subscriber::fmt().json().init() which writes to stdout. When the TUI owns the alternate screen, stdout writes corrupt the display — JSON log lines appear at the cursor and vanish on resize.

Changes

  • Change to .with_writer(std::io::stderr) — tracing is diagnostic and should never mix with program output.

Files

  • ail/src/main.rs

Commit 3: Enable text wrapping in viewport

Problem

The viewport Paragraph has no wrap mode. Long lines are truncated — LLM output falls offscreen and is unreadable.

Changes

  • Add .wrap(Wrap { trim: false }) to the Paragraph in viewport::draw().
  • Import ratatui::widgets::Wrap.

Files

  • ail/src/tui/ui/viewport.rs

Commit 4: Fix scrollback — don't snap to bottom during streaming

Problem

viewport_scroll is reset to 0 on every StreamDelta, StepStarted, ToolUse, etc. If the user scrolls up (PageUp) to read earlier output, the next streaming chunk snaps them back to the bottom.

Changes

  • In apply_executor_event (app.rs), only reset viewport_scroll = 0 when it's already 0 (i.e., user hasn't scrolled away). Replace all the self.viewport_scroll = 0; lines with a conditional: only auto-scroll if the user is already at the bottom.
  • Add a helper: fn auto_scroll(&mut self) { if self.viewport_scroll == 0 { /* already at bottom, nothing to do — 0 means "follow tail" */ } } — actually, since 0 already means "at bottom / auto-follow", the fix is simpler: just remove the explicit = 0 resets from streaming events (StreamDelta, ToolUse, Completed). The viewport_scroll field already defaults to 0 (auto-follow). Only reset to 0 at the start of a new run (reset_for_run) and when the user explicitly scrolls to bottom (viewport_page_down going past 0).
  • Keep viewport_scroll = 0 in StepStarted to auto-scroll when a new step begins (reasonable UX).
  • Remove viewport_scroll = 0 from: StreamDelta, ToolUse, Completed, HitlGateReached, PipelineCompleted.

Files

  • ail/src/tui/app.rs

Commit 5: Replace cost display with token counters

Problem

Cost display shows dollar amounts from runner-reported cost_usd, but when running against Ollama (free local model), it reports bogus cost ($17 for one query). Cost data is unreliable across providers.

Changes

Replace the $0.0032 cost display with a token counter format: [↑1.2K | ↓3.4K] showing input and output tokens separately. Format with K/M/B suffixes and one decimal when crossing thresholds.

A. Add a formatting helperail/src/tui/ui/statusbar.rs

fn fmt_tokens(n: u64) -> String:
  < 1_000        → "123"
  < 10_000       → "1.2K"  (one decimal)
  < 1_000_000    → "12K"   (no decimal)
  < 10_000_000   → "1.2M"
  < 1_000_000_000 → "12M"
  else            → "1.2B"

B. Replace cost spansail/src/tui/ui/statusbar.rs

  • Remove the $0.0032 cost span
  • Replace with: [↑{fmt(input)} | ↓{fmt(output)}]
  • Keep the format in both Running and Completed phases

Files

  • ail/src/tui/ui/statusbar.rs

Commit 6: TUI backend missing invocation step (BUG)

Problem

Spec §4.1: "Every pipeline has an implicit first step called invocation. The pipeline's authored steps begin executing only after invocation completes."

main.rs (--once) correctly runs the user's prompt through the runner before execute() when the pipeline lacks an invocation step. backend.rs (TUI) skips this — jumps to execute_with_control() directly. The user's prompt never reaches the LLM for custom pipelines.

Changes

A. Add pre-invocation logicail/src/tui/backend.rs
Before execute_with_control():

  1. Check if pipeline's first step has id == "invocation"
  2. If not:
    • Emit StepStarted { step_id: "invocation", step_index: 0, total_steps: declared + 1 }
    • Call runner.invoke_streaming() forwarding RunnerEvents
    • Append TurnEntry to session.turn_log
    • Emit StepCompleted { step_id: "invocation" }

B. Prepend "invocation" to sidebarail/src/tui/app.rs
When building sidebar steps for a pipeline whose first step is NOT invocation, prepend StepDisplay { id: "invocation", glyph: NotReached }. Also in hot-reload path (mod.rs:109-116).

Files

  • ail/src/tui/backend.rs
  • ail/src/tui/app.rs
  • ail/src/tui/mod.rs

Commit 7: Streaming feedback — thinking blocks

Problem

ClaudeCliRunner::invoke_streaming() drops "thinking" content blocks silently (catch-all _ => {} at claude.rs:263). No visual indication the LLM is reasoning.

Changes

A. Add RunnerEvent::Thinking variantail-core/src/runner/mod.rs

B. Parse thinking blocksail-core/src/runner/claude.rs

  • Match "thinking" → emit RunnerEvent::Thinking { text }

C. Handle in AppStateail/src/tui/app.rs

  • New match arm, prefix with [thinking] , append to viewport + step buffer

D. Style thinking linesail/src/tui/ui/viewport.rs

  • Lines starting with [thinking] render in Color::DarkGray

Files

  • ail-core/src/runner/mod.rs
  • ail-core/src/runner/claude.rs
  • ail/src/tui/app.rs
  • ail/src/tui/ui/viewport.rs

Commit 8: Lower sidebar breakpoints

Problem

Sidebar disappears at < 100 cols. User wants it visible at narrow widths.

Changes — ail/src/tui/ui/layout.rs

  • FULL_WIDTH: 120 → 60
  • GLYPH_WIDTH: 100 → 40
  • STATUS_WIDTH: 80 → 30
  • Cap sidebar_width at area.width / 2

Files

  • ail/src/tui/ui/layout.rs

Verification

After each commit: cargo build && cargo clippy -- -D warnings && cargo nextest run

Manual TUI test after all commits:

  • Text is selectable/copyable in terminal
  • No JSON log lines corrupt the TUI
  • Long lines wrap within the viewport
  • ScrollUp preserves position during streaming; new-step resets to bottom
  • Status bar shows [↑1.2K | ↓3.4K] token counts, no dollar amounts
  • Sidebar shows "invocation" → "review" for .include-review.yaml
  • Invocation actually runs the user's prompt before pipeline steps
  • Thinking lines appear in dim grey
  • Sidebar visible at 40-col terminal width

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtuiUI / UX issues affecting the HITL interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions