Skip to content

feat(expose): zoom out scope with g while Exposé is open - #134

Merged
TraderSamwise merged 3 commits into
masterfrom
feat/expose-zoom
Jun 15, 2026
Merged

feat(expose): zoom out scope with g while Exposé is open#134
TraderSamwise merged 3 commits into
masterfrom
feat/expose-zoom

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

Adds progressive zoom-out inside the Exposé overlay. While Exposé is open, pressing g widens the scope one rung along a single ladder:

worktree  →  project (all worktrees)  →  global (all projects)  →  (noop)

The launch context only sets the starting rung (agent → worktree, dashboard → project, meta dashboard → global); g walks up from there. This unifies the three existing entry points (Ctrl-A g in an agent / on the dashboard / on the meta dashboard) under one concept.

Behavior guarantees

  • Zoom is ephemeral — the rung lives only in the popup process; it's recomputed from context on every open, never persisted. Next Ctrl-A g starts back at the context default.
  • Escape/q never moves you — it just dismisses the overlay (it's a display-popup); you stay on the pane you triggered it from. Only Enter/1-9 navigate.
  • Per-item select — a tile carrying a projectRoot (global scope) jumps cross-project via openTarget; otherwise it uses the same-project runWindowSwitch. So a tile picked after zooming to global correctly jumps across projects.
  • Empty rungs render an in-overlay "No active agents" message so g still zooms out from them.

Implementation

  • Phase 1src/tmux/expose-model.ts: pure, DI-testable ExposeScope ladder — nextExposeScope (clamped), initialExposeScope (start rung from context), loadExposeScopeItems (tiles/label/sublabel per rung). Full unit tests.
  • Phase 2src/tmux/expose.ts: mutable scope + g handler + reload() + per-item select + empty-state render; README line.

No tmux/binding changes — plain g already reaches the popup's stdin.

Verification

yarn typecheck, yarn lint, yarn test (1236 passed), yarn build — all green. Built via plan-execute with Explore plan-audit + impl-audit per phase (all clean).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Ctrl+A g hotkey to access Exposé with zoom functionality; use g key to zoom out through scope levels (worktree → project → global) with per-session state that resets on reopen.
    • Added Ctrl+A Ctrl+A hotkey to send literal Ctrl+A.
    • Exposé now supports numeric keys 1–9 to jump between windows.
  • Documentation

    • Updated hotkeys documentation with new Exposé controls and scope behavior details.

test and others added 2 commits June 15, 2026 15:59
Pure, DI-testable scope ladder (worktree → project → global): ExposeScope,
nextExposeScope (clamped at global), initialExposeScope (derives the start
rung from launch context), and loadExposeScopeItems (resolves tiles/label/
sublabel per rung). Consumed by the Exposé TUI in a follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the scope ladder into the TUI: g zooms worktree → project → global
(clamped), reloading tiles/labels/sublabels and re-seeking the selection
without leaving the overlay. Scope is ephemeral — recomputed from the
launch context each open, never persisted. Escape/q still just close the
popup (no navigation); only Enter/1-9 jump. Tile select is now per-item
(openTarget when the item carries a projectRoot, else runWindowSwitch),
so a tile picked after zooming to global jumps across projects. Empty
rungs render an in-overlay message so g still zooms out from them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jun 15, 2026 8:17am

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@TraderSamwise, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 hour, 44 minutes, and 3 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6447c4c3-daf3-4a03-b96a-743f9291a9a7

📥 Commits

Reviewing files that changed from the base of the PR and between 8aac12e and a6e22f6.

📒 Files selected for processing (1)
  • src/tmux/expose.ts
📝 Walkthrough

Walkthrough

A "zoom ladder" scope model is added to the Exposé TUI: new types (ExposeScope, ExposeScopeItem, ExposeScopeView, etc.) and functions (nextExposeScope, initialExposeScope, loadExposeScopeItems) in expose-model.ts drive scope escalation from worktree → project → global. expose.ts is refactored to use these, wiring a g key that zooms out and reloads tiles. Tests and README hotkeys are updated accordingly.

Changes

Exposé Scope Zoom Ladder

Layer / File(s) Summary
Scope model types and functions
src/tmux/expose-model.ts
Adds ExposeScope, ExposeScopeItem, ExposeSublabel, ExposeScopeView, LoadExposeScopeDeps types and nextExposeScope, initialExposeScope, loadExposeScopeItems functions; imports listAllProjectsExposeItems for the global rung.
Scope model unit tests
src/tmux/expose-model.test.ts
Adds test suites for nextExposeScope traversal/clamping, initialExposeScope context-driven starting scope, and loadExposeScopeItems callback dispatch and view shape assertions.
Exposé UI: scope wiring, g-key, reload, and cross-project jump
src/tmux/expose.ts
Refactors runTmuxExpose to initialize from initialExposeScope/loadExposeScopeItems; adds reload(), g-key handler via nextExposeScope, item.projectRoot-based cross-project jump, updated help/empty-state render, and empty-tile navigation guard.
README hotkeys update
README.md
Adds Ctrl+A g and Ctrl+A Ctrl+A rows to the dashboard hotkeys table; expands agent-window Exposé description with 1–9 and zoom-out scope details.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant runTmuxExpose
  participant initialExposeScope
  participant loadExposeScopeItems
  participant nextExposeScope
  participant TmuxRender

  User->>runTmuxExpose: launch Exposé (Ctrl+A g)
  runTmuxExpose->>initialExposeScope: compute starting scope from context/config
  initialExposeScope-->>runTmuxExpose: ExposeScope
  runTmuxExpose->>loadExposeScopeItems: load items for scope
  loadExposeScopeItems-->>runTmuxExpose: ExposeScopeView
  runTmuxExpose->>TmuxRender: render tiles + help (with "g zoom out" if not global)

  User->>runTmuxExpose: press g
  runTmuxExpose->>nextExposeScope: advance scope
  nextExposeScope-->>runTmuxExpose: wider scope
  runTmuxExpose->>loadExposeScopeItems: reload items for new scope
  loadExposeScopeItems-->>runTmuxExpose: ExposeScopeView
  runTmuxExpose->>TmuxRender: re-render with updated tiles/labels
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TraderSamwise/aimux#122: Initial implementation of the Ctrl+A g Exposé TUI, touching the same expose-model.ts and expose.ts codepaths that this PR extends with the scope zoom-ladder logic.
  • TraderSamwise/aimux#126: Introduced meta-dashboard cross-project Exposé and listAllProjectsExposeItems, which this PR wires into the new global-rung path of loadExposeScopeItems.
  • TraderSamwise/aimux#133: Modifies the same drawTile/capture rendering path in expose.ts that this PR refactors to consume ExposeScopeItem.

Poem

🐇 Hop, hop, from worktree to sky,
One key "g" and the scope zooms high!
Worktree, project, global — three rungs tall,
The rabbit leaps the ladder, sees them all.
Each session remembers how far you flew,
But resets on reopen — fresh scope for you! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main feature: adding a zoom-out capability to Exposé with the 'g' key, which is the primary change across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/expose-zoom

Comment @coderabbitai help to get the list of available commands and usage tips.

render() clamped index to visibleCount-1, leaving it at -1 when a rung has
no agents. Selection was only kept safe by selectTile's !item guard; clamp
to 0 so the index never goes negative.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TraderSamwise

Copy link
Copy Markdown
Owner Author

Sub-agent review — findings (1 fixed, 2 by-design)

CodeRabbit found nothing actionable; the concurrent sub-agent raised 3 low-severity items:

  1. Fixed in a6e22f6render() clamped the selection index to visibleCount - 1, leaving it at -1 on an empty rung (relied on selectTile's !item guard). Now clamps to 0.
  2. By designexpose.forceGlobalScope starts Exposé at the project rung (all worktrees in the current project), not the new cross-project global rung. That matches the flag's original intent ("scope to all agents in the project"); pressing g reaches all-projects. Changing it to start cross-project would be an out-of-scope behavior change.
  3. No-op — the REFRESH_MS interval and the g handler can't race (single-threaded; reload(); render() runs atomically vs the interval tick, and items/visibleCount are always written by render() before any handler reads them). Confirmed no stale-state bug.

yarn typecheck, yarn lint, tests, yarn build all green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant