Skip to content

perf(expose): lightweight entry + pre-paint flash mask; keep tile colors, opt-in inactive dimming#170

Merged
TraderSamwise merged 6 commits into
masterfrom
chore/tui-next-25
Jun 18, 2026
Merged

perf(expose): lightweight entry + pre-paint flash mask; keep tile colors, opt-in inactive dimming#170
TraderSamwise merged 6 commits into
masterfrom
chore/tui-next-25

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Summary

Continues the exposé polish work (follows #169). Four focused changes:

  • Lightweight exposé entry — new src/popup-expose.ts exposes registerExposeCommand / buildExposeProgram / runExpose: a minimal commander program that imports only runTmuxExpose, not the full ~4.5M CLI graph (~40ms vs ~110ms). main.ts now registers the expose command through the shared registerExposeCommand (single source of truth).
  • Route the popup through itbin/aimux / bin/aimux-dev route the expose subcommand via runRoutedCli(), cutting exposé cold-start from ~110ms to ~40ms (near the raw-node floor). Other commands unchanged.
  • Pre-paint flash mask — the popup paints the pre-popup screen snapshot instantly (printf home + cat) before exec-ing exposé, so the residual node cold-start no longer shows a blank: the screen holds, then exposé atomically repaints it dimmed (its first render is already synchronized output). No shell-side dimming.
  • Keep tile colors; opt-in inactive dimming — previews now render in the captured pane's real colors by default instead of flattening to gray, and the active tile is never dimmed. Dimming non-selected tiles (gray preview + dim border) is gated behind a new expose.dimInactive config option, default off.

Test plan

  • yarn typecheck, yarn lint, full yarn vitest (1431 passing), yarn build — all clean.
  • Rebuilt + installed local 0.1.21-tui-ia; verified dimInactive present in installed dist/config.js and exposé routing through the lightweight entry.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added dimInactive configuration option to control whether inactive tiles are dimmed in the exposé popup.
    • Implemented instant background pre-rendering in the exposé popup for improved visual feedback.
  • Performance

    • Optimized expose command startup through lightweight CLI routing, reducing cold-start latency.

test and others added 4 commits June 18, 2026 22:01
Add src/popup-expose.ts exposing registerExposeCommand / buildExposeProgram /
runExpose — a minimal commander program that imports only runTmuxExpose, not
the full 4.5M CLI graph (importing it loads in ~40ms vs ~110ms for main.js).
main.ts now registers the expose command via the shared registerExposeCommand
(single source of truth). Inert until bin/aimux routes to it next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bin/aimux and bin/aimux-dev now route the `expose` subcommand to the
lightweight popup entry via runRoutedCli(), instead of always loading the
full CLI. Cuts exposé cold-start from ~110ms to ~40ms (near the raw-node
floor), shrinking the residual open-blank. Other commands are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The popup now paints the pre-popup screen snapshot instantly (printf home +
cat) before exec-ing exposé, so the ~40ms node cold-start no longer shows a
blank — your screen holds, then exposé atomically repaints it dimmed with the
panel (its first render is already synchronized output). No shell-side dimming.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Exposé previews now render in the captured pane's real colors by default
instead of being flattened to gray, and the active tile is never dimmed.
Dimming non-selected tiles (gray preview + dim border) is now gated behind
a new expose.dimInactive config option, default off.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 18, 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 18, 2026 4:07pm

@coderabbitai

coderabbitai Bot commented Jun 18, 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 3 hours and 30 minutes. 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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 283e0af2-8df8-4dd3-83f7-3485b5a490a4

📥 Commits

Reviewing files that changed from the base of the PR and between 5d1f25e and e1cbdcd.

📒 Files selected for processing (2)
  • src/launcher-env.ts
  • src/popup-expose.ts
📝 Walkthrough

Walkthrough

The PR splits the expose subcommand into a dedicated popup-expose.ts module with buildExposeProgram/runExpose, adds cliEntryFor/runRoutedCli routing in launcher-env.ts to dispatch expose to that lightweight path, wires both CLI entrypoints to use the router, adds a configurable dimInactive option to ExposeConfig that gates tile dimming in drawTile, and updates show_local_expose in the shell script to pre-paint the backdrop instantly before the popup renders.

Changes

Expose routing split + dimInactive UI feature

Layer / File(s) Summary
dimInactive config option
src/config.ts, src/config.test.ts
ExposeConfig gains dimInactive: boolean defaulting to false; config tests assert the new default and that expose.dimInactive: true merges correctly.
drawTile conditional dimming
src/tmux/expose.ts, src/tmux/expose-tile.test.ts
drawTile accepts a dimInactive flag; non-selected tiles use the off-palette and recede(..., "deep") only when dimmed; runTmuxExpose passes config.expose.dimInactive at the call site; tile tests update border and preview-color expectations.
runTmuxExpose backdrop snapshot timing
src/tmux/expose.ts
backdropFile is now read and deleted immediately at function startup; fallback host-pane capture only runs when the pre-provided snapshot is absent.
popup-expose.ts dedicated expose module
src/popup-expose.ts, src/popup-expose.test.ts
New module defines ExposeCliOptions, toExposeOptions (resolves paths to absolute), registerExposeCommand, buildExposeProgram, and runExpose; tests cover path resolution and optional-field passthrough.
CLI routing via launcher-env and entrypoints
src/launcher-env.ts, src/launcher-env.test.ts, src/main.ts, bin/aimux, bin/aimux-dev
cliEntryFor/runRoutedCli added to launcher-env.ts; main.ts replaces its inline expose block with registerExposeCommand(program); both binary entrypoints call runRoutedCli() instead of directly importing main.js; tests cover routing decisions.
tmux-control.sh expose pre-paint snapshot
scripts/tmux-control.sh, src/tmux/control-script.test.ts
show_local_expose constructs a prepaint prefix that cursor-resets and cats the captured screen before the expose command; control-script test asserts the printf prefix and cat-then-exec sequence.

Sequence Diagram(s)

sequenceDiagram
  participant bin as bin/aimux
  participant env as launcher-env.ts
  participant pe as popup-expose.ts
  participant expose as expose.ts (runTmuxExpose)
  participant main as main.ts

  bin->>env: prepareStableCliEnv()
  bin->>env: runRoutedCli()
  env->>env: cliEntryFor(process.argv)
  alt argv[2] === "expose"
    env->>pe: dynamic import → runExpose()
    pe->>pe: buildExposeProgram().parse()
    pe->>expose: runTmuxExpose(toExposeOptions(opts))
    expose->>expose: read+delete backdropFile
    expose->>expose: drawTile(…, config.expose.dimInactive)
  else other subcommand
    env->>main: dynamic import main.js
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TraderSamwise/aimux#169: Both PRs modify runTmuxExpose's backdropFile handling in src/tmux/expose.ts and show_local_expose in scripts/tmux-control.sh — directly overlapping at the expose backdrop/popup code path.
  • TraderSamwise/aimux#168: Both PRs touch src/tmux/expose.ts's drawTile rendering and runTmuxExpose's backdrop-file flow, overlapping at the expose dimming and backdrop wiring code level.
  • TraderSamwise/aimux#9: Both PRs modify the bin/aimux-dev CLI entrypoint startup sequence — #9 introduced the dynamic main.js import, which this PR replaces with runRoutedCli().

Poem

🐇 A rabbit hops through split-screen haze,
The expose path now lights its own blaze.
Dim the tiles or keep them bright,
dimInactive sets the night.
Pre-paint flashes, backdrop snaps—
Fast popup lands without the gaps! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% 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 summarizes the main change: a lightweight exposé entry point to improve performance, pre-paint flash masking for visual polish, preserved tile colors, and optional inactive dimming configuration.
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 chore/tui-next-25

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

Move the launcher backdrop-file read and unlink to before terminal setup and
the SIGINT/SIGTERM handlers, so a fatal signal during exposé startup can't
leak the temp file (the shell-side rm only fires when display-popup itself
fails). Add a config-merge test for expose.dimInactive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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: 2

🤖 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/launcher-env.ts`:
- Around line 89-94: The runRoutedCli() function has dynamic imports that lack
explicit error handling, which means rejected promises could become unhandled
rejections. Add .catch() handlers to both the
import("./popup-expose.js").then(...) chain in the expose condition and the
import("./main.js") in the else branch to explicitly catch and handle any import
rejections. Log the errors appropriately when they occur to align with error
handling patterns used in other parts of the codebase.

In `@src/popup-expose.ts`:
- Around line 69-70: In the runExpose function, the call to
buildExposeProgram().parse() needs to be changed to
buildExposeProgram().parseAsync() because registerExposeCommand defines async
action handlers that need to be properly awaited. Since async handlers are
present, using parseAsync is required by Commander.js to ensure proper execution
flow and error handling. Additionally, update the runExpose function signature
from void to return a Promise so that the async operation can be properly
awaited by callers.
🪄 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: 538bede7-c0ca-4b07-ac95-841cbcef1f8c

📥 Commits

Reviewing files that changed from the base of the PR and between e88232d and 5d1f25e.

📒 Files selected for processing (13)
  • bin/aimux
  • bin/aimux-dev
  • scripts/tmux-control.sh
  • src/config.test.ts
  • src/config.ts
  • src/launcher-env.test.ts
  • src/launcher-env.ts
  • src/main.ts
  • src/popup-expose.test.ts
  • src/popup-expose.ts
  • src/tmux/control-script.test.ts
  • src/tmux/expose-tile.test.ts
  • src/tmux/expose.ts

Comment thread src/launcher-env.ts Outdated
Comment thread src/popup-expose.ts Outdated
runExpose now uses parseAsync so commander awaits the async expose action
(parse() would exit before runTmuxExpose settled). runRoutedCli catches a
rejected dynamic import / runExpose and exits non-zero instead of relying on
unhandled-rejection behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TraderSamwise
TraderSamwise merged commit 3515bdb into master Jun 18, 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