Skip to content

Mouse: no hit-rect registry, no hover — 14 register rows blocked on it #558

Description

@emal-avala

Part of #557. Verified against origin/main (v0.29.0) on 2026-07-28.

Today

handle_mouse (crates/cli/src/ui/modern/run.rs:3225-3281) handles exactly four things:

  • ScrollUp / ScrollDown
  • Down/Drag/Up(Left) — line-granular transcript selection (TextSelection { start_line, end_line }, app.rs:577)
  • Down(Middle) — emits a toast telling you to use terminal paste

MouseEventKind::Moved has zero handlers, so hover does not exist. git grep hit_test|hover|-> Vec<Rect> over crates/cli/src/ui/ finds nothing — no UI element anywhere exposes a clickable region. Every widget draws into a Rect and forgets it.

For comparison, grok-build carries app/mouse.rs (1707 LOC) + input/mouse.rs (1451 LOC) with roughly 28-30 named hit rects.

The missing primitive

A hit-rect registry: a per-frame map of (Rect, Target) that each renderer registers into, with hover and click resolved centrally against it. Their render_menu(...) -> Vec<Rect> — documented as "Returns the Rect for each item row (for hit-testing clicks and hover)" — is that pattern in miniature.

Without it, each clickable element invents its own hit-testing. That is the duplicate-source-of-truth shape that produced most of the bugs found in the 2026-07-28 audit (conversation_len mirror, resume_loading bool, the overlay list in open_session_picker), and it is worth avoiding deliberately rather than by accident.

Register rows this unlocks

14 open rows depend on it:

Row Capability Pri
D5-22 Mouse hover states over named hit rects P2
D5-21 Scrollbar click and drag P2
D5-34 1/2/3-click = caret / word / cell-or-line P2
D2-21 Mouse caret placement, double/triple-click in composer P2
D6-23 Status-bar items carry hit rects P2
D4-18 Todo badge in the status bar, clickable P2
D4-24 Persistent idle cue, clickable P2
D5-19 Timeline rail with hover preview and click-to-jump P3
D6-15 cwd in the status bar (clickable) P3
D6-20 Context bar hover-swaps to a percentage at identical width P3
D10-18 OSC 22 cursor shape on link hover P3
D5-31 Middle-click paste reading X11 PRIMARY (currently a hint only) P2
D3-10 OSC 8 hyperlinks emitted (LinkSpan is already collected and dropped) P1
D10-12 OSC 8 gated by a capability matrix P1

markdown.rs:27 already produces LinkSpan { line, cols, url } and its doc says "Consumed by mouse/OSC-8 handling in a later milestone" — the data is being computed and thrown away at layout.rs:355, layout.rs:414 and render.rs:603.

Prerequisite: ship the escape hatch first

Hover requires capturing motion events, which makes native terminal click-drag copy worse than it is today. D5-30 (relinquish mouse capture — rated TRIVIAL, EnableMouseCapture is unconditional at run.rs:649/:711 and all four call sites exist) should land before this, not after. Capturing more input without an escape hatch is a regression for anyone who selects text with the mouse.

Acceptance

  • One registry; no widget does its own hit-testing.
  • Hover state is derived from Moved and does not require a click.
  • A user can disable mouse capture entirely (D5-30) and lose only the enhancements.
  • Frame snapshots cover hover and selected states.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions