Skip to content

TUI: per-agent quick-jump numbers + title-band modal system - #141

Merged
TraderSamwise merged 3 commits into
masterfrom
chore/tui-next
Jun 15, 2026
Merged

TUI: per-agent quick-jump numbers + title-band modal system#141
TraderSamwise merged 3 commits into
masterfrom
chore/tui-next

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

Two TUI design-language follow-ups.

1. Restore per-agent quick-jump numbers

The card redesign dropped the per-agent index column inside worktree groups, which made it hard to eyeball-jump to a specific agent. Re-added as a muted [N] cell after the status dot, sourced from each quick-jump entry digit so the number you see is the number you type (worktree digit + agent digit). Applies to the worktree-grouped layout and the flat no-worktree fallback.

2. Redesign the modal/dialog system (Direction B — title band)

The old dialogs were ugly and off-center: the tool-picker had its own solid-fill renderBox that wasn't centered, and modals lacked a coherent identity.

  • New modalBand(label, tone, width) theme token — a tinted, full-width window-chrome header bar (info/danger), joining the existing pill/chip/keycap/card primitives.
  • renderOverlayBox rebuilt around it: centered, intent-tinted rounded border → title band → separator rule → padded body. Danger variant auto-adds the ⚠ glyph; the busy overlay passes its spinner as the band icon.
  • Every modal now routes through the one primitive — tool picker, launch options, create service, name agent, worktree list, graveyard & graveyard-delete confirms, busy, error, switcher, migrate, teammate picker, and the two-pane notification panel.
  • Deleted the bespoke solid-fill renderBox (the off-center offender).

Test plan

  • New renderer test: agents show [1]/[2] within a worktree group.
  • Rewrote box.test.ts and the tool-picker overlay tests for the band structure (obsolete solid-fill assertion removed).
  • yarn typecheck, yarn lint, full yarn vitest (1263 tests), and yarn build all green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added numbered quick-jump indices for dashboard sessions to enable faster navigation.
  • Style

    • Enhanced overlay rendering with title bands and improved visual hierarchy for dialog boxes.

test and others added 2 commits June 15, 2026 20:42
The card redesign dropped the per-agent index column. Re-add it as a
muted [N] cell after the status dot, sourced from each quick-jump entry's
digit so the displayed number matches the jump key. Applies to both the
worktree-grouped layout and the flat no-worktree fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a `modalBand` theme token (tinted full-width window-chrome header)
and rebuild `renderOverlayBox` around it: a centered, intent-tinted rounded
border with a title band above a separator rule and the padded body. Route
every modal through this one primitive — tool picker, launch options, name
agent, create service, worktree list, graveyard/delete confirms, busy,
error, switcher, migrate, teammate picker, and the two-pane notification
panel.

This replaces the tool-picker's bespoke solid-fill renderBox (which was also
not centered) and unifies the dialog system on the design language.

Co-Authored-By: Claude Opus 4.8 <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 1:27pm

@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, 27 minutes, and 44 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: e6c688b8-2fb8-4648-a273-897edad9abcf

📥 Commits

Reviewing files that changed from the base of the PR and between 916d913 and 6abbcc5.

📒 Files selected for processing (5)
  • src/multiplexer/tool-picker.test.ts
  • src/multiplexer/tool-picker.ts
  • src/tui/render/box.ts
  • src/tui/render/theme.ts
  • src/tui/screens/overlay-renderers.ts
📝 Walkthrough

Walkthrough

Refactors renderOverlayBox from a positional-argument API into an object-based OverlayBoxSpec renderer with a tinted title band and separator. Adds modalBand to the theme layer. Migrates all TUI overlay builders to the new API. Separately adds numbered quick-jump [N] index columns to dashboard agent and service rows.

Changes

Overlay Rendering Infrastructure and Migration

Layer / File(s) Summary
OverlayBoxSpec interface and modalBand theme helper
src/tui/render/box.ts, src/tui/render/theme.ts
Exports OverlayBoxSpec (title, body, cols, rows, variant, icon) and a VARIANT mapping in box.ts; adds BandTone, BAND_SGR, and modalBand(label, tone, width) to theme.ts for rendering a full-width tinted header bar.
renderOverlayBox rewrite with title band
src/tui/render/box.ts, src/tui/render/box.test.ts
Rewrites renderOverlayBox to accept OverlayBoxSpec, compute an uppercased band label with optional icon glyph, derive box sizing from visible body content, reserve chrome rows, and paint via absolute cursor positioning. Tests verify separator, uppercased title, red variant warning glyph, ANSI preservation, and row width behavior.
Migrate all overlay builders in overlay-renderers.ts
src/tui/screens/overlay-renderers.ts
Migrates service input, label input, worktree list/removal, dashboard busy/error, teammate picker, help, switch agent, and migrate picker overlays from positional lines+variant calls to { title, body, cols, rows, variant, icon } objects. Replaces the inline notifications header with a modalBand call inside the border loop and adjusts panel sizing/row offsets.
Migrate tool-picker and subscreen overlays, update tests
src/multiplexer/tool-picker.ts, src/multiplexer/tool-picker.test.ts, src/tui/screens/subscreen-renderers.ts
Updates tool-picker imports, adds a footer() helper, and refactors both picker/options overlays to use body arrays with renderOverlayBox. Migrates buildGraveyardWorktreeDeleteConfirmOverlay similarly. Tests add stripAnsi and assert title-band content, section labels, and red ANSI error-state output.

Dashboard Quick-Jump Index Rendering

Layer / File(s) Summary
Quick-jump digit column in dashboard rows
src/tui/screens/dashboard-renderers.ts, src/tui/screens/dashboard-renderers.test.ts
Imports DASHBOARD_QUICK_JUMP_LIMIT, adds COL_INDEX and an indexCell helper, updates agentRow/serviceRow to accept and display an optional digit, computes digitById for grouped worktrees, and switches the flat session list to forEach with index-bounded digits. New test asserts [1]/[2] labels in ANSI-stripped dashboard output.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • TraderSamwise/aimux#110: Modifies buildToolOptionsOverlayOutput in src/multiplexer/tool-picker.ts and its output assertions, which this PR also refactors to use the new renderOverlayBox API.
  • TraderSamwise/aimux#111: Reworks the structured launch options editor and parse error surfacing in the same buildToolOptionsOverlayOutput function that this PR migrates to renderOverlayBox.
  • TraderSamwise/aimux#136: Directly overlaps at the overlay rendering level, refactoring renderOverlayBox usage across tool-picker and overlay-renderers — the same files this PR updates.

Poem

🐇 A box without a title? How plain, how bare!
Now bands of color crown each modal with flair,
The ANSI escapes in orderly rows,
Quick-jump digits dance wherever code goes.
[1], [2] — the agents line up bright,
Every overlay gleams with themed delight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% 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 two main changes: per-agent quick-jump numbers and a title-band modal system redesign, matching the core objectives.
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

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

@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

🧹 Nitpick comments (2)
src/tui/render/theme.ts (1)

106-112: 💤 Low value

Consider reusing padVisible to reduce duplication.

The truncation/padding logic here duplicates what padVisible already does. The function could be simplified:

 export function modalBand(label: string, tone: BandTone, width: number): string {
   const text = ` ${label}`;
   const w = Math.max(0, width);
-  const visible = visibleWidth(text);
-  const filled = visible >= w ? truncateAnsi(text, w) : `${text}${" ".repeat(w - visible)}`;
+  const filled = padVisible(text, w);
   return `${BAND_SGR[tone]}${filled}${RESET}`;
 }

This eliminates the subtle difference (using >= vs > for the truncation branch) and keeps one canonical implementation.

🤖 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/tui/render/theme.ts` around lines 106 - 112, The modalBand function
contains truncation and padding logic that duplicates functionality already
provided by the padVisible function. Replace the manual truncation and padding
logic in modalBand with a call to padVisible, passing the label text and width
parameters. This will eliminate code duplication, remove the subtle difference
in the truncation condition (using >= vs >), and ensure consistent behavior by
reusing the single canonical implementation.
src/tui/render/box.ts (1)

32-37: 💤 Low value

Use visibleWidth for the band label measurement for consistency.

The body lines use visibleWidth(line) to measure width, but bandLabel uses .length. While bandLabel currently contains no ANSI codes, using visibleWidth would be consistent with the pattern used for body lines and defensive against future changes where icon might include styling.

-  const measuredContentWidth = Math.max(bandLabel.length + 1, 0, ...body.map((line) => visibleWidth(line)));
+  const measuredContentWidth = Math.max(visibleWidth(bandLabel) + 1, 0, ...body.map((line) => visibleWidth(line)));
🤖 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/tui/render/box.ts` around lines 32 - 37, The bandLabel measurement in the
measuredContentWidth calculation uses .length while body lines use
visibleWidth() for width measurement. Replace bandLabel.length with
visibleWidth(bandLabel) in the Math.max call where measuredContentWidth is
calculated to maintain consistency with the body line measurement pattern and be
defensive against future ANSI code additions to the icon variable.
🤖 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/multiplexer/tool-picker.ts`:
- Around line 111-128: The tool picker overlay does not explicitly handle the
case when the tools array is empty, resulting in an interface that shows action
hints without any tools to select. Add a check before the body construction loop
in the tool-picker.ts function to detect when tools.length is zero, and if so,
insert an appropriate message into the body array (such as "No tools enabled" or
similar explanation) to clearly communicate to the user why the list is empty
and no selections are available.

---

Nitpick comments:
In `@src/tui/render/box.ts`:
- Around line 32-37: The bandLabel measurement in the measuredContentWidth
calculation uses .length while body lines use visibleWidth() for width
measurement. Replace bandLabel.length with visibleWidth(bandLabel) in the
Math.max call where measuredContentWidth is calculated to maintain consistency
with the body line measurement pattern and be defensive against future ANSI code
additions to the icon variable.

In `@src/tui/render/theme.ts`:
- Around line 106-112: The modalBand function contains truncation and padding
logic that duplicates functionality already provided by the padVisible function.
Replace the manual truncation and padding logic in modalBand with a call to
padVisible, passing the label text and width parameters. This will eliminate
code duplication, remove the subtle difference in the truncation condition
(using >= vs >), and ensure consistent behavior by reusing the single canonical
implementation.
🪄 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: 88b651f2-8656-4ba3-91b3-250629105f1b

📥 Commits

Reviewing files that changed from the base of the PR and between 0c1ea5d and 916d913.

📒 Files selected for processing (9)
  • src/multiplexer/tool-picker.test.ts
  • src/multiplexer/tool-picker.ts
  • src/tui/render/box.test.ts
  • src/tui/render/box.ts
  • src/tui/render/theme.ts
  • src/tui/screens/dashboard-renderers.test.ts
  • src/tui/screens/dashboard-renderers.ts
  • src/tui/screens/overlay-renderers.ts
  • src/tui/screens/subscreen-renderers.ts

Comment thread src/multiplexer/tool-picker.ts
- Tool picker now shows an explicit "No enabled tools" state instead of bare
  action hints when no tools are enabled (CodeRabbit).
- modalBand reuses padVisible instead of duplicating truncate/pad logic.
- renderOverlayBox measures the band label with visibleWidth for consistency.
- Guard the notification panel against a degenerate height on tiny terminals.

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