stacklok / stacklok/mecatl

mecatui: converge bounded scrolling and selection controls

Open
#1,589 0 comments 0 reactions 1 assignee View on GitHub

@jbeda is already working on this.

Since Sep 15, 2026.

Dominant language
Go
Stars
152
Forks
16
Avg merge
14h 48m
Merged PRs (30d)
536

Description

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.

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

  • #1488
  • #1446
  • docs/acceptance/mecatui-unified-agents-overlay-fit.md
  • cmd/mecatui/ui/agents_overlay.go
  • cmd/mecatui/ui/window.go
  • cmd/mecatui/ui/surface.go
  • cmd/mecatui/ui/hit_regions.go

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.