Skip to content

mecatui: converge bounded scrolling and selection controls #1589

Open

Description

@jbeda

Goal

Introduce a package-private, reusable bounded scrolling and cursor-selection system for mecatui, then migrate the TUI incrementally while targeted fixes continue.

This is an umbrella issue. It does not authorize a single repository-wide rewrite or replacement of the specialized conversation viewport.

Current tracking (2026-10-16)

The bounded controls already exist in cmd/mecatui/ui/internal/bounded; the proposal and initial slices below describe their origin, not work that must be repeated. Use #1742 as the detailed adoption checklist and #2237 for remaining surface ownership migrations.

Next coordinated slice: #2173, bounding /schedule inventory and fire history while moving its legacy overlay onto surface. Compare the full-conversation browser interaction with /sessions and add reusable conventions to docs/tui.md. This umbrella remains open; partial migrations must not close it.

Historical proposal context

The following motivation and sketch predate the implemented controls. The current contracts live in docs/tui.md, cmd/mecatui/ui/surface.go, and the bounded package.

Why

Mecatui currently has several independent implementations of list windowing, rendered-line scrolling, cursor movement, overflow indicators, selection styling, and mouse routing. PR #1488 added the strongest multiline selectable-row implementation for the unified Agents overlay, but it remains Agents-specific and keyboard-only.

Current inconsistencies include:

  • selectable rows use different markers and styles (›, ▶, >, button-active, spinner, accent);
  • wheel events scroll some surfaces, do nothing on others, and can reach the hidden conversation beneath legacy overlays;
  • several lists render without a viewport bound;
  • multiline logical items are sometimes selected as items and sometimes split into independently scrolled physical lines;
  • browsing, cursor selection, keyboard focus, current/default state, hover, and text selection do not have a shared vocabulary.

Proposal sketch

Build two package-private layers under cmd/mecatui/ui rather than one monolithic widget.

1. Bounded window

  • explicit width and height;
  • physical-line accounting;
  • bounded vertical offset, line/page movement, top/end, and wheel input;
  • explicit wrap-or-clip width policy;
  • overflow/range metadata;
  • browsing without a cursor.

2. Selectable list over the bounded window

  • optional cursor;
  • logical items spanning one or more physical lines;
  • keep the entire cursor item visible when possible;
  • item movement plus physical-height paging;
  • mouse hit regions spanning all physical lines of an item;
  • click-to-cursor with activation remaining an explicit surface callback;
  • filtering, actions, fetching, confirmation, and server pagination remain surface-owned.

Use distinct semantics and styles for text selection, list cursor, keyboard focus, current/active value, default value, hover, and activation. Width bounds are required; horizontal panning is not part of the initial contract without a concrete diff/table use case.

The main conversation viewport remains specialized because it owns streaming-tail following, logical text selection, reflow anchors, ANSI/grapheme handling, and drag autoscroll.

Incremental migration

Slice 1 — establish and prove the control

Create a Bounded acceptance plan and implementation PR that extracts the multiline physical-window behavior from #1488, defines the interaction/style contract, and proves it against representative consumers:

  • unified Agents overlay — multiline cursor rows plus browsing-only detail panes;
  • /models — simple fixed-height cursor rows.

The Agents overlay proves both control modes without coupling the first slice to another surface's mixed domain behavior. Other browsing-only surfaces remain follow-ups.

Slice 2 — mechanical list adoption

After Slice 1 establishes the contract, classify each batch independently. Mechanical adoption can be Routine; behavioral changes remain Bounded.

Candidate batches:

  • command palette + @ mentions;
  • /sessions roster + /worktrees;
  • /schedule + MCP lists + /connect.

Slice 3 — complex and legacy surfaces

Handle /user model, reflections, dream, help, skills, agent inventory, detail panes, and previews in focused follow-ups. Any decision about migrating legacy overlays onto the formal surface lifecycle should get its own Bounded contract rather than being hidden in a mechanical migration.

Interaction principles

  • Wheel input belongs to the visible surface and never silently scrolls hidden content.
  • A multiline row is one logical cursor and pointer target.
  • Hover is visual only and does not move the keyboard cursor.
  • Cursor movement and activation are distinct.
  • Current/default status markers remain orthogonal to cursor and focus.
  • Existing frame-scoped hit regions remain the pointer-dispatch mechanism.
  • Existing remappable navigation actions remain authoritative where already supported.

Process

Track this issue with non-closing Relates to links from partial Plan / Interface and implementation PRs. Only the final PR that completes the umbrella should close it. Each independently shippable slice is classified separately under docs/development-process.md.

References

Activity

  1. self-assigned this
    on Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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