Skip to content

Fix tab bar width shift on surface focus - #6812

Merged
lawrencecchen merged 4 commits into
mainfrom
feat-tabbar-focus-width
Jul 2, 2026
Merged

lawrencecchen merged 4 commits into
mainfrom
feat-tabbar-focus-width

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Focusing a surface/pane shifted the tab bar horizontally: every tab carrying a ⌃/⌘ shortcut digit (tabs 1-8 and the last tab) grew ~11pt when its pane gained focus and shrank when it lost focus.

Cause: the modifier-hold layout-shift fix (#6786 / bonsplit #154) moved the reserved shortcut-hint slot width onto allowsShortcutHints = isFocused && tabShortcutHintsEnabled. That stopped the width from changing on modifier-hold, but left it gated on focus, so the trailing slot collapsed from the wide hint-pill width to the bare accessory width on any unfocused pane.

Fix (bonsplit manaflow-ai/bonsplit#155): reserve the hint slot based on tabShortcutHintsEnabled alone (focus-independent). TabItemView now takes tabShortcutHintsEnabled + isFocused separately; width routes through reservedShortcutHintSlotWidth (which ignores focus), while hint visibility still derives allowsShortcutHints. Neither focus nor modifier-hold changes measured tab width now; both only change the hint pill's opacity.

Principled fix: the reserved layout width is now a pure function of the global hints setting, decoupled from the transient focus/hold visibility state that was never supposed to drive layout.

Regression test (red→green, bonsplit): testTabShortcutHintSlotWidthDoesNotChangeWithFocus asserts the slot width is identical focused vs unfocused (was 29 vs 18); testTabShortcutHintSlotWidthCollapsesWhenHintsDisabled covers hints-off. Full bonsplit suite 152 passing.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Low Risk
Tab-bar layout and shortcut-hint presentation only; no security, data, or core infrastructure changes.

Overview
Fixes a tab bar horizontal jump when switching pane focus: tabs that show ⌃/⌘ digit shortcuts (1–8 and the last tab) were ~11pt wider while focused because the reserved shortcut-hint slot was sized with allowsShortcutHints = isFocused && tabShortcutHintsEnabled.

The change decouples layout from focus: reserved hint-slot width follows tabShortcutHintsEnabled only (via reservedShortcutHintSlotWidth), while TabItemView still takes isFocused separately so hint pills only show when focused. Focus and modifier-hold now affect opacity/visibility, not measured tab width.

vendor/bonsplit is bumped to pick up bonsplit#155, including regression tests that slot width stays the same focused vs unfocused and collapses when hints are off.

Reviewed by Cursor Bugbot for commit 7c63c05. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes tab bar width shifts on pane focus by overlaying the shortcut-hint pill in the close-button slot. Tab widths stay constant on focus and modifier-hold; only the hint pill’s opacity changes.

  • Bug Fixes

    • Overlay the hint pill over the close-button slot so ⌃/⌘-digit tabs (1–8 and last) aren’t wider and the bar doesn’t jump.
  • Dependencies

    • Bump vendor/bonsplit to main with manaflow-ai/bonsplit#155 (overlay hint fix); also pulls in icon-only pinned-browser updates (#156, #157), inert here.

Written for commit 7c63c05. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Chores
    • Updated a bundled third-party component to a newer revision.

Bump vendor/bonsplit to include manaflow-ai/bonsplit#155: reserve the
tab shortcut-hint slot based on tabShortcutHintsEnabled alone, not focus.

Previously focusing a pane/surface grew every ⌃/⌘-digit tab ~11pt and
shifted the whole tab bar (regression from the modifier-hold fix #6786).

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

vercel Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 2, 2026 12:26am
cmux-staging Building Building Preview, Comment Jul 2, 2026 12:26am

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR updates the vendor/bonsplit subproject reference from 8ca85b6a688072acc6e21171dfebba302dc82020 to 1c67762d49b286d6394e6c22ebe1cf3d36172d84.

Changes

Vendor Bonsplit Pointer Update

Layer / File(s) Summary
Submodule revision bump
vendor/bonsplit
The vendor/bonsplit reference changes from 8ca85b6a688072acc6e21171dfebba302dc82020 to 1c67762d49b286d6394e6c22ebe1cf3d36172d84.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Possibly related PRs

Poem

A bunny hopped by with a twitch of my nose,
and found a new commit where the green clover grows.
One tiny pin swap, thump-thump, clean and bright,
then I bounded away in the moonlit night. 🐇

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing the tab bar width shift on focus.
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.
Cmux Swift Actor Isolation ✅ Passed Only SwiftUI view files changed, and the edit just splits layout width from focus-gated visibility; no new actor-isolation issue is introduced.
Cmux Swift Blocking Runtime ✅ Passed PASS: The visible diff only moves submodule gitlinks (ghostty, vendor/bonsplit); no non-test Swift source or new blocking primitives are introduced in this repo.
Cmux Browser Automation Off-Main ✅ Passed PASS: Diff only updates vendor/bonsplit plus CI/package lockfiles; no browser.* routing, mainActor, or WebKit/AppKit command code changed.
Cmux Expensive Synchronous Load ✅ Passed The bonsplit bump only forwards isAudioPlaying in BonsplitController/tests; no agent-history, JSONL, or main-actor sync loads were added.
Cmux Cache Substitution Correctness ✅ Passed PASS: The parent diff only updates submodule gitlinks, and the bonsplit change is a transient tab-hint UI layout tweak, which the rule explicitly allows.
Cmux No Hacky Sleeps ✅ Passed PASS: The PR only updates the vendor/bonsplit gitlink; no non-Swift runtime code or delay/sleep/polling logic is introduced in the parent diff.
Cmux Algorithmic Complexity ✅ Passed The vendored Swift diff only changes tab-hint layout booleans in two view files; no nested scans, repeated filtering, or other scalable-collection work was introduced.
Cmux Swift Concurrency ✅ Passed Diff only bumps submodule gitlinks (ghostty/vendor/bonsplit); no cmux-owned Swift source changed, and the rule exempts third-party boundaries.
Cmux Swift @Concurrent ✅ Passed The PR only bumps submodule gitlinks; no Swift source files or async annotations changed in the parent diff.
Cmux Swift File And Package Boundaries ✅ Passed Only change is a vendored vendor/bonsplit submodule pointer; no cmux Swift files or package-boundary issues are touched.
Cmux Swiftpm Lockfiles ✅ Passed Only submodule gitlinks changed (ghostty, vendor/bonsplit); no cmux-owned Package.resolved/.gitignore/Xcode lockfile files changed.
Cmux Swift Logging ✅ Passed Only a vendor submodule pointer changed; no Swift logging statements were added or materially changed in app/runtime code.
Cmux User-Facing Error Privacy ✅ Passed PASS: The diff only updates the vendor/bonsplit submodule pointer; no user-facing error, alert, API body, or recovery copy changed.
Cmux Full Internationalization ✅ Passed Only submodule bumps; changed code adds API/test-only logic and no user-facing text or locale entries.
Cmux Swiftui State Layout ✅ Passed The diff only updates submodule pointers and Zig/AppKit-bridge code; it adds no SwiftUI state, GeometryReader, lazy-row store refs, or render-time state mutation.
Cmux Architecture Rethink ✅ Passed PASS: The parent diff only updates the bonsplit submodule pointer; the referenced tab-width change is a small invariant fix with tests, not a timing/observer/split-ownership workaround.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The diff only bumps vendor/bonsplit; the changed Swift code is audio-state copying plus tests, with no NSWindow/WindowGroup or cmuxAuxiliaryWindowIdentifiers changes.
Cmux Source Artifacts ✅ Passed PASS: The only changed path is the vendor/bonsplit gitlink; no build/cache/log/screenshot artifacts were added, and the submodule tree contains only intentional source/docs assets.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No Swift files under production Sources/ were changed; only submodule pointers moved, so the no-test/debug-seam rule is not implicated.
Cmux No Ambient Global State ✅ Passed PASS: The PR only updates gitlink pointers for ghostty and vendor/bonsplit; no Swift source changed, so no new top-level funcs, globals, or singletons appear.
Description check ✅ Passed The PR description is detailed and covers summary and testing, but it omits the template's demo video, checklist, and review-trigger sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-tabbar-focus-width

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11138e1f2c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vendor/bonsplit Outdated
@@ -1 +1 @@
Subproject commit 8ca85b6a688072acc6e21171dfebba302dc82020
Subproject commit 1c67762d49b286d6394e6c22ebe1cf3d36172d84

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Point bonsplit at a main-branch commit

AGENTS.md says submodule commits must be pushed to the submodule remote main before committing the parent pointer. I checked manaflow-ai/bonsplit's commits/main history and it still ends at 8ca85b6, while this gitlink now points at 1c67762 two commits ahead of main; if that temporary PR/branch ref is deleted, fresh checkouts can no longer fetch this SHA and cmux builds break. Please merge/push the bonsplit commits to main first, then update this pointer.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR bumps the vendor/bonsplit submodule pointer to fix a horizontal tab bar shift that occurred whenever a pane gained or lost focus. The root cause was that the trailing shortcut-hint slot width was gated on isFocused, so every tab with a ⌃/⌘ digit shortcut resized by ~11pt on focus change.

  • Fix: reservedShortcutHintSlotWidth is now a pure function of the global tabShortcutHintsEnabled setting; isFocused only governs the hint pill's opacity, never its layout contribution. TabItemView receives the two inputs separately to enforce this.
  • Dependency model: bonsplit is consumed as a local SwiftPM path dependency (../../../vendor/bonsplit), so no Package.resolved lockfile update is needed or expected.
  • Test coverage: bonsplit PR Bonsplit action buttons: show tooltip + shortcut and add VS Code-style split keybindings (configurable) #155 adds two regression tests — one asserting the slot width is identical focused vs. unfocused, and one covering hints-off collapse — and all 152 bonsplit suite tests pass.

Confidence Score: 5/5

Safe to merge — the only change is a submodule pointer bump delivering a focused UI layout bug fix with accompanying regression tests.

The parent-repo diff is a single submodule line. The bonsplit change decouples a layout width from a transient focus flag, which is the correct architectural fix. No cmux-owned Swift, TypeScript, or configuration files are modified. The dependency is a local SwiftPM path package so no lockfile churn is expected. Bonsplit ships two regression tests that red→green on the exact failure mode, and the full bonsplit suite passes.

No files require special attention.

Important Files Changed

Filename Overview
vendor/bonsplit Submodule pointer bump (c4aa88a → 01751ef) delivering bonsplit#155: decouples tab-width reservation from focus state so the tab bar no longer shifts on pane focus/blur.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["tabShortcutHintsEnabled (global setting)"] --> B["reservedShortcutHintSlotWidth\n(layout width — focus-independent)"]
    A --> C["allowsShortcutHints\n= tabShortcutHintsEnabled && isFocused"]
    D["isFocused"] --> C
    B --> E["TabItemView measured width\n(constant, no focus jump)"]
    C --> F["Hint pill opacity\n(0 when unfocused or modifier not held)"]
    E --> G["Tab bar layout stable"]
    F --> H["Visual hint visibility only"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["tabShortcutHintsEnabled (global setting)"] --> B["reservedShortcutHintSlotWidth\n(layout width — focus-independent)"]
    A --> C["allowsShortcutHints\n= tabShortcutHintsEnabled && isFocused"]
    D["isFocused"] --> C
    B --> E["TabItemView measured width\n(constant, no focus jump)"]
    C --> F["Hint pill opacity\n(0 when unfocused or modifier not held)"]
    E --> G["Tab bar layout stable"]
    F --> H["Visual hint visibility only"]
Loading

Reviews (4): Last reviewed commit: "Bump vendor/bonsplit to main (01751ef): ..." | Re-trigger Greptile

@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

🤖 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 `@vendor/bonsplit`:
- Line 1: The vendor/bonsplit submodule pointer is referencing an unreachable
commit, so update the submodule SHA to one that is already pushed and available
on the remote main branch. Make sure the intended changes in the bonsplit repo
are merged or otherwise present on the remote, verify the commit exists there,
then revise the vendor/bonsplit pointer in this PR to that valid commit.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2e949387-a2b1-46a2-a3ae-360cd510f187

📥 Commits

Reviewing files that changed from the base of the PR and between a4fb35c and 11138e1.

📒 Files selected for processing (1)
  • vendor/bonsplit

Comment thread vendor/bonsplit Outdated
@@ -1 +1 @@
Subproject commit 8ca85b6a688072acc6e21171dfebba302dc82020
Subproject commit 1c67762d49b286d6394e6c22ebe1cf3d36172d84

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

submodule_path="vendor/bonsplit"
new_sha="$(git ls-tree HEAD "$submodule_path" | awk '$2 == "commit" { print $3 }')"
remote_url="$(git config -f .gitmodules --get submodule.vendor/bonsplit.url)"

test -n "$new_sha"
test -n "$remote_url"

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

git -C "$tmp" init -q
git -C "$tmp" remote add origin "$remote_url"
git -C "$tmp" fetch -q --filter=blob:none origin main

git -C "$tmp" merge-base --is-ancestor "$new_sha" origin/main

Repository: manaflow-ai/cmux

Length of output: 216


🏁 Script executed:

#!/bin/bash
# Check if the SHA exists in local git history for the submodule
git log --oneline -1 vendor/bonsplit || echo "No local history for submodule"

# Try to show the git submodule status
git submodule status vendor/bonssplit 2>/dev/null || git submodule status vendor/bonsplit

Repository: manaflow-ai/cmux

Length of output: 283


🌐 Web query:

Is commit 1c67762d49b286d6394e6c22ebe1cf3d36172d84 in manaflow-ai/bonsplit main branch?

💡 Result:

Commit 1c67762d49b286d6394e6c22ebe1cf3d36172d84 is not in the main branch of the manaflow-ai/bonsplit repository. An extensive review of the repository's commit history, pull requests, and related submodule references—such as the recent updates tracked in the manaflow-ai/cmux repository—yields no evidence of this commit hash [1][2][3][4]. Based on the available data, this hash does not correspond to any known activity or code revision within the manaflow-ai/bonsplit project [5][6][7].

Citations:


Critical: Submodule pointer references an unreachable commit

The updated vendor/bonsplit pointer to 1c67762d49b286d6394e6c22ebe1cf3d36172d84 references a commit that does not exist on the remote main branch of manaflow-ai/bonsplit. This violates the submodule safety policy requiring commits to be pushed to the remote before the parent repository references them.

The PR will fail for other developers and CI when attempting to fetch the submodule.

  1. Push the intended changes to the manaflow-ai/bonsplit repository on a branch and merge to main (or the intended target branch).
  2. Verify the new commit SHA exists on the remote.
  3. Update the vendor/bonsplit submodule pointer in this PR to the valid, pushed commit SHA.
🤖 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 `@vendor/bonsplit` at line 1, The vendor/bonsplit submodule pointer is
referencing an unreachable commit, so update the submodule SHA to one that is
already pushed and available on the remote main branch. Make sure the intended
changes in the bonsplit repo are merged or otherwise present on the remote,
verify the commit exists there, then revise the vendor/bonsplit pointer in this
PR to that valid commit.

Source: Coding guidelines

@blacksmith-sh

This comment has been minimized.

Points at bonsplit bf9c0b1 (PR #155): the tab shortcut-hint pill now overlays
the close-button slot instead of reserving its own width, so tabs carrying a
⌃/⌘ digit are no wider than tabs without one. Still focus/hold-independent, so
the tab bar never shifts.

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

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 1 file

Re-trigger cubic

…int fix

bonsplit #155 (overlay the tab shortcut-hint pill instead of reserving its
width) is now merged to bonsplit main, so point at the durable main commit
rather than the feature branch. This also advances bonsplit to current main,
which includes the already-merged pinned-browser icon-only commits (#156/#157);
those are inert in cmux until the separate pinned-surfaces cmux wiring lands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lawrencecchen
lawrencecchen merged commit eecc299 into main Jul 2, 2026
31 checks passed
@lawrencecchen
lawrencecchen deleted the feat-tabbar-focus-width branch July 2, 2026 01:19
@lawrencecchen
lawrencecchen restored the feat-tabbar-focus-width branch July 18, 2026 10:24

This branch was successfully deployed

1 active deployment
Preview – cmux — 7c63c050 Deployed Jul 2, 2026 by vercel[bot]
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