Skip to content

fix(expose): keep navigation responsive - #367

Merged
TraderSamwise merged 6 commits into
masterfrom
fix/expose-navigation-responsiveness
Jul 20, 2026
Merged

fix(expose): keep navigation responsive#367
TraderSamwise merged 6 commits into
masterfrom
fix/expose-navigation-responsiveness

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • redraw only the old and new Exposé tiles for single-step navigation
  • pause preview refresh briefly during active input so capture-pane work does not fight key handling
  • make number keys open the selected tile, including global Exposé

Verification

  • yarn vitest run src/tmux/expose.test.ts
  • yarn typecheck
  • yarn lint
  • yarn build
  • pre-push: yarn typecheck && yarn lint && yarn test

Summary by CodeRabbit

  • New Features
    • Numeric keys 1–9 now immediately open the corresponding Exposé item.
    • Help text updated to describe numeric keys as opening items.
  • Performance Improvements
    • Selection navigation now redraws only affected tiles when possible.
    • Refresh repaint and resize/client-size checks are reduced during rapid active input.
  • Bug Fixes
    • Reload/zoom more reliably preserves selection, ignoring stale refresh results.
  • Tests
    • Expanded tmux Exposé coverage for deterministic output timing, resize/input behavior, redraw precision, and global-scope numeric selection focus.

@vercel

vercel Bot commented Jul 20, 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 Jul 20, 2026 1:52pm

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Exposé now delays refresh and resize checks during input bursts, incrementally redraws moved selections, protects reloads from stale commits, preserves user selection across reloads, and expands navigation, focus, resize, and redraw test coverage.

Changes

Exposé interaction updates

Layer / File(s) Summary
Incremental input and refresh handling
src/tmux/expose.ts
Input timing gates refresh and resize checks; navigation repaints affected tiles with a full-render fallback when partial repaint is invalid.
Selection persistence and reload generations
src/tmux/expose.ts
Reloads track selection versions and generations, preserve current user movement, and ignore stale results; numeric keys select targets immediately and help text says “open”.
Input, resize, and focus regression coverage
src/tmux/expose.test.ts
Tests cover active-input resize behavior, throttled probing, global numeric focus routing, deterministic redraw ordering, selection preservation, and stale reload commits.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant runTmuxExpose
  participant ExposeItemsAPI
  participant ExposeFocusAPI
  participant TmuxOutput
  User->>runTmuxExpose: send navigation or numeric key
  runTmuxExpose->>TmuxOutput: redraw affected selection tiles
  runTmuxExpose->>ExposeItemsAPI: reload expose items
  ExposeItemsAPI-->>runTmuxExpose: return scoped tiles
  runTmuxExpose->>runTmuxExpose: reject stale reload result
  runTmuxExpose->>ExposeFocusAPI: focus selected tile
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main goal of improving Exposé navigation responsiveness.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/expose-navigation-responsiveness

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/tmux/expose.ts (1)

469-517: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared tile-draw logic used by render() and renderTileIndexes().

Both functions independently compute per-tile geometry (r, c, top, left) and call drawTile(...) with the same argument shape (lines 490-508 here mirror lines 564-581 in render()). Any future change to tile geometry or drawTile's call signature now needs to be kept in sync in two places.

♻️ Suggested extraction
+  const renderTileAt = (tileIndex: number, layout: ReturnType<typeof computeLayout>, geo: ReturnType<typeof panelGeometry>): string => {
+    const r = Math.floor(tileIndex / layout.tileCols);
+    const c = tileIndex % layout.tileCols;
+    const top = geo.top + layout.gridTopRow + r * layout.tileHeight;
+    const left = geo.left + 1 + c * (layout.tileWidth + GAP);
+    const item = items[tileIndex]!;
+    const preview = tilePreview(captures.get(item.target.windowId) ?? "", layout.bodyLines);
+    return drawTile(
+      item,
+      preview,
+      tileIndex + 1,
+      tileIndex === index,
+      top,
+      left,
+      layout.tileWidth,
+      layout,
+      tileSublabel(item),
+      options,
+      false,
+    );
+  };

Then both renderTileIndexes and render can call out += renderTileAt(tileIndex, layout, geo); instead of duplicating the block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tmux/expose.ts` around lines 469 - 517, Extract the duplicated per-tile
geometry and drawTile invocation from renderTileIndexes and render into a shared
renderTileAt helper that accepts tileIndex, layout, and geo and returns the
rendered tile string. Replace both existing tile-drawing blocks with this helper
while preserving their current filtering, selection, preview, and output
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/tmux/expose.ts`:
- Around line 469-517: Extract the duplicated per-tile geometry and drawTile
invocation from renderTileIndexes and render into a shared renderTileAt helper
that accepts tileIndex, layout, and geo and returns the rendered tile string.
Replace both existing tile-drawing blocks with this helper while preserving
their current filtering, selection, preview, and output behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 15b91227-46df-4d38-9770-9dc7394206bb

📥 Commits

Reviewing files that changed from the base of the PR and between 548ab2d and d89eadb.

📒 Files selected for processing (2)
  • src/tmux/expose.test.ts
  • src/tmux/expose.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tmux/expose.ts (1)

787-795: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Timestamp only actionable input for refresh throttling.

lastInputAt is updated before focusin, focusout, and mouse events are filtered out. Repeated ignored terminal events can therefore keep scheduleRefresh() in its quiet-period branch without any navigation occurring. Move the assignment after filtering and gate it on events.length > 0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tmux/expose.ts` around lines 787 - 795, Update onData so lastInputAt is
assigned only after parseKeys events are filtered and expanded, and only when
events.length > 0. Remove the current unconditional assignment before filtering
while preserving the existing opening guard and event-processing flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/tmux/expose.ts`:
- Around line 591-596: Update the reload flow around loadExposeScopeItems to
prevent stale overlapping requests from replacing newer state: track a reload
generation/request token (or serialize reloads), and before assigning items,
index, view, or selection, verify the request is still current. Preserve the
selection snapshot only for the request that successfully commits its
corresponding item list.

---

Outside diff comments:
In `@src/tmux/expose.ts`:
- Around line 787-795: Update onData so lastInputAt is assigned only after
parseKeys events are filtered and expanded, and only when events.length > 0.
Remove the current unconditional assignment before filtering while preserving
the existing opening guard and event-processing flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85bdb177-6544-41d0-a825-11cf907324cd

📥 Commits

Reviewing files that changed from the base of the PR and between d89eadb and 0fec40e.

📒 Files selected for processing (2)
  • src/tmux/expose.test.ts
  • src/tmux/expose.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tmux/expose.test.ts

Comment thread src/tmux/expose.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/tmux/expose.ts (1)

792-834: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Restart resize throttling for every input burst.

Line 792 does not reset lastResizeCheckAt. After a prior burst, a new keypress can immediately satisfy Line 820 and call synchronous queryClientSize() while input is active—the behavior this throttling intends to avoid.

Proposed fix
-        lastInputAt = Date.now();
+        lastInputAt = Date.now();
+        lastResizeCheckAt = lastInputAt;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tmux/expose.ts` around lines 792 - 834, Reset lastResizeCheckAt when
processing input in the key-handling loop so each new input burst starts the
resize-check throttle window anew. Update the event-processing logic around
parseKeys and handleKey, preserving the existing refresh scheduling and resize
behavior for inactive input.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/tmux/expose.ts`:
- Around line 792-834: Reset lastResizeCheckAt when processing input in the
key-handling loop so each new input burst starts the resize-check throttle
window anew. Update the event-processing logic around parseKeys and handleKey,
preserving the existing refresh scheduling and resize behavior for inactive
input.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 162b6b7b-55ec-4253-9e02-00b4b8fb9af6

📥 Commits

Reviewing files that changed from the base of the PR and between 0fec40e and 7d0d61a.

📒 Files selected for processing (2)
  • src/tmux/expose.test.ts
  • src/tmux/expose.ts

@TraderSamwise
TraderSamwise merged commit 87ab737 into master Jul 20, 2026
3 checks passed
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