Skip to content

Add opt-in setting to stretch surface tabs to fill pane width (#5091)#5147

Open
austinywang wants to merge 8 commits into
mainfrom
issue-5091-tab-fill-pane-width
Open

Add opt-in setting to stretch surface tabs to fill pane width (#5091)#5147
austinywang wants to merge 8 commits into
mainfrom
issue-5091-tab-fill-pane-width

Conversation

@austinywang

@austinywang austinywang commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Closes #5091

Summary

Adds an opt-in setting so surface tabs can stretch to fill their pane's available tab-bar width instead of sitting at a fixed width. The headline case: when a pane has a single tab, that lone tab now spans the full pane width. The current fixed-width behavior remains the default — this is strictly opt-in, exactly as the reporter framed it.

When enabled:

  • A single tab spans the full available tab-bar width.
  • Multiple tabs distribute the width equally.
  • When tabs would overflow at their natural width, the strip falls back to fixed sizing and scrolls (fill never shrinks tabs below natural width).

When disabled (default): the tab strip is byte-for-byte unchanged.

How it's exposed (mirrors the #2534 surface-tab-bar-font-size pattern)

  • Ghostty config key: surface-tabs-fill-pane-width = true (default false).
  • Settings UI: a discoverable "Stretch Tabs to Fill Pane Width" toggle in the Terminal section (localized en + ja).
  • Live update: toggling the config reloads open panes without restart (mirrors the existing fontSizeChanged detection in Workspace.applyGhosttyChrome).

Cross-repo

The tab strip is rendered by Bonsplit (vendor/bonsplit submodule). The layout capability lives there:

This cmux PR bumps the submodule pointer to the bonsplit branch commit (887ece7, pushed to that PR's branch) and wires the config key/Settings toggle through to Appearance.tabWidthMode.

Changes

  • vendor/bonsplit → tab-width-mode commit.
  • Sources/GhosttyConfig.swift — parse surface-tabs-fill-pane-width.
  • Sources/CmuxApplicationSupportDirectories.swift — key/default + shared bool parse/format helpers.
  • Sources/Workspace.swift — thread the flag into bonsplitAppearance + live-update on reload.
  • Sources/HostSettingsActions.swift + Packages/CmuxSettingsUI/... — Settings toggle.
  • Resources/Localizable.xcstrings — en + ja strings.
  • web/app/[locale]/docs/configuration/page.tsx — document the key.
  • cmuxTests/GhosttyConfigTests.swift — parse + editor round-trip coverage.

Testing

  • Bonsplit: testTabWidthModeDefaultsToFixed, testTabWidthModeFillIsSettableAndDistinct.
  • cmux: SurfaceTabsFillPaneWidthConfigTests (default off, parse truthy/falsy forms, clamp/ignore invalid, loader, editor round-trip).

🤖 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
UI and tab-bar layout preference only; follows existing Ghostty config + reload patterns with tests and no auth or data-path changes.

Overview
Adds an opt-in way to stretch surface tabs across each pane’s tab bar (default stays fixed-width). The new Ghostty key surface-tabs-fill-pane-width is parsed into GhosttyConfig, persisted via shared bool helpers, and exposed in Terminal settings through SettingsHostActions with ordered async saves, rollback, and save-failure messaging.

Open workspaces map the flag to Bonsplit tabWidthMode (.fill / .fixed), including on config reload via applyGhosttyChrome and in the appearance signature so live updates apply. Config disk writes share the renamed GhosttyConfigWriter actor (formerly font-only). Docs, broad localization, and parse/persistence tests are included.

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


Summary by cubic

Adds an opt‑in setting to stretch surface tabs to fill each pane’s tab bar, with fixed‑width tabs remaining the default. Includes a Terminal Settings toggle with live updates and a localized UI.

  • New Features

    • Config key: surface-tabs-fill-pane-width (default false, accepts common truthy/falsy forms).
    • Behavior: one tab spans full width; multiple tabs share width; on overflow, tabs keep natural size and the strip scrolls.
    • Terminal Settings toggle “Stretch Tabs to Fill Pane Width” with async, ordered saves via SettingsHostActions.setSurfaceTabsFillPaneWidth; cancels superseded writes, rolls back on failure, and uses cached reads for initial state.
    • Workspace maps the flag to BonsplitConfiguration.Appearance.tabWidthMode (.fill/.fixed) with change detection and live updates; appearance signature includes the flag for reliable reloads.
    • Docs updated; tests cover parsing (including alternate truthy/falsy forms), editor round‑trip, and appearance signature.
  • Dependencies

    • Bumped vendor/bonsplit to add Appearance.tabWidthMode and wire .fill vs .fixed.

Written for commit 705f264. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added "Stretch Tabs to Fill Pane Width" toggle in Terminal settings with persistent, live-reloadable configuration and inline save-error feedback; workspace/tab appearance now respects the new tab-width mode.
  • Documentation

    • Updated configuration docs and examples to include the new setting.
  • Tests

    • Added tests covering parsing, formatting, and persistence of the new tab-width config.
  • Chores

    • Updated vendored bonsplit revision.

Lets surface tabs stretch to fill their pane's available tab-bar width
instead of using a fixed width. Off by default — strictly opt-in.

- Bonsplit submodule: bump to the tab-width-mode commit; pass
  Appearance.tabWidthMode (.fill when enabled, .fixed otherwise).
- GhosttyConfig: parse the `surface-tabs-fill-pane-width` boolean key
  (mirrors the surface-tab-bar-font-size / #2534 pattern).
- CmuxGhosttyConfigSettingEditor: key/default + shared bool parse/format
  helpers used by the parser, settings, and tests.
- Workspace: thread the flag into bonsplitAppearance and live-update open
  panes on config reload (tabWidthModeChanged, mirroring fontSizeChanged).
- Settings: discoverable "Stretch Tabs to Fill Pane Width" toggle in the
  Terminal section (SettingsHostActions API + HostSettingsActions impl),
  localized (en + ja).
- Docs: document the key in the configuration example.
- Tests: GhosttyConfig parse + editor round-trip coverage for the flag.

Bonsplit PR: manaflow-ai/bonsplit#141

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

vercel Bot commented Jun 1, 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 Jun 6, 2026 11:11am
cmux-staging Building Building Preview, Comment Jun 6, 2026 11:11am

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new Ghostty boolean setting surface-tabs-fill-pane-width, parses and persists it, exposes getter/setter via host actions, adds a Terminal settings toggle with async persistence and error handling, maps the flag into Bonsplit tab-width appearance, adds tests, and updates docs and the Bonsplit submodule.

Changes

Surface Tabs Fill Pane Width Feature

Layer / File(s) Summary
Configuration parsing and Ghostty config support
Sources/CmuxApplicationSupportDirectories.swift, Sources/GhosttyConfig.swift
New Ghostty setting key and default, boolean formatter/parser helpers, and config parsing that recognizes surface-tabs-fill-pane-width.
Settings host API and implementation
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsHostActions.swift, Sources/HostSettingsActions.swift
Protocol adds sync getter and async setter with default stubs; HostSettingsActions implements persistence using GhosttyConfigWriter and reloads configuration on success.
Terminal settings UI and localization
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift, Resources/Localizable.xcstrings
SwiftUI toggle with state, cancellable async save task, dynamic subtitle, inline save-failure indicator; new localization keys and translations added.
Workspace appearance and Bonsplit integration
Sources/Workspace.swift
Map config flag to Bonsplit TabWidthMode, thread through appearance builder, include in diffing and apply mode updates to bonsplit controller.
Configuration behavior testing
cmuxTests/GhosttyConfigTests.swift
Test suite verifies defaults, parsing (including alternate spellings), loader-based loading, editor helpers, and write round-trip.
Documentation and dependency updates
web/app/[locale]/docs/configuration/page.tsx, vendor/bonsplit
Example config snippet updated with the new setting; Bonsplit submodule pointer updated.

Sequence Diagram(s)

(omitted — changes are a single cohesive feature without a multi-actor sequential protocol that requires diagramming)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • manaflow-ai/cmux#4798: Extends Ghostty configuration parsing/writing helpers for additional surface/sidebar tab-bar font-size settings (related config infrastructure).

Poem

🐰 A pane with a tab stretched wide and bright,
I toggle a flag, and the tab fits just right.
From Ghostty file to Bonsplit view,
A rabbit cheers — the UI grew.
Hooray for tabs that span the light!


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Logging ❌ Error New file Sources/HostSettingsActions.swift violates swift-logging.md: file-scoped Logger at line 7 must be declared as nonisolated private let (class is @MainActor). Change line 7 from private let hostSettingsLogger to nonisolated private let hostSettingsLogger in Sources/HostSettingsActions.swift.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an opt-in setting for stretching surface tabs to fill pane width, with issue reference for traceability.
Linked Issues check ✅ Passed The code changes fully implement all coding objectives from #5091: opt-in config key with false default, Settings UI toggle, Bonsplit appearance mapping, live reload capability, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the tab-width feature: config parsing, Settings UI, Bonsplit integration, localization, docs, and tests; no unrelated modifications detected.
Cmux Swift Actor Isolation ✅ Passed All new Swift code properly adheres to actor isolation: @MainActor protocol/implementation, @MainActor SwiftUI struct, private actor for background work, and nonisolated static helpers.
Cmux Swift Blocking Runtime ✅ Passed PR introduces no new blocking synchronization patterns; uses async/await for async operations, simple property mapping for config updates, and leaves pre-existing blocking infrastructure unchanged.
Cmux No Hacky Sleeps ✅ Passed No problematic timing code (setTimeout/sleep/delay/polling) added. Changes are Swift code, documentation example, and localization strings.
Cmux Algorithmic Complexity ✅ Passed New code uses existing config patterns, operates on single GhosttyConfig objects, contains no loops over scalable collections, and follows the established surface-tab-bar-font-size pattern.
Cmux Swift Concurrency ✅ Passed PR uses modern Swift concurrency with async/await, actor-based serialization, and properly managed Tasks. No legacy DispatchQueue, Combine, completion handlers, or fire-and-forget Tasks.
Cmux Swift @Concurrent ✅ Passed New async setSurfaceTabsFillPaneWidth method uses explicit actor hop (await) for file I/O, matching the allowed case with explicit actor boundary and matching the existing setSidebarFontSize pattern.
Cmux Swift File And Package Boundaries ✅ Passed Small focused additions to existing files following established patterns; all changes under 250-line threshold for oversized files; no inappropriate package boundary violations.
Cmux User-Facing Error Privacy ✅ Passed All user-facing text follows privacy rules: no vendor/provider names, internal flags, secrets, or sensitive data exposed. Error messages are generic. Internal logging is not user-visible.
Cmux Full Internationalization ✅ Passed All Swift text uses String(localized:defaultValue:); all 4 new string catalog keys have complete translations across 20 locales; config keys and web changes are literal tokens only.
Cmux Swiftui State Layout ✅ Passed Uses modern @State for new local view state. No ObservableObject/@published, no GeometryReader effects, no render-time mutations. State changes only in event handlers and async Tasks.
Cmux Architecture Rethink ✅ Passed Follows existing surfaceTabBarFontSize pattern: single source of truth (GhosttyConfig), proper ownership (HostSettingsActions), serialized writes, live reload, transient UI state with error rollback.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds config and Settings UI toggle for tab width mode with no new window declarations; changes thread existing config to Bonsplit appearance.
Description check ✅ Passed The PR description comprehensively covers all required template sections: a detailed summary of what changed and why, explicit testing details including test suite names, and a complete checklist with all items addressed.
✨ 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 issue-5091-tab-fill-pane-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 and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an opt-in surface-tabs-fill-pane-width Ghostty config key and a matching Terminal Settings toggle that stretches surface tabs to fill each pane's tab bar width. The default behavior (fixed-width tabs) is unchanged. Implementation follows the existing surface-tab-bar-font-size pattern end-to-end — config parsing, editor round-trip, live workspace reload, and Settings UI with async save/rollback.

  • Config + workspace wiring: GhosttyConfig parses the new bool key using a shared parsedBoolLiteral helper; Workspace.bonsplitAppearance maps it to BonsplitConfiguration.Appearance.TabWidthMode (.fill / .fixed); the appearance signature is extended so live-reload correctly detects the change.
  • Settings UI: TerminalSection adds generation-based ordered saves with cancel-and-await chaining, per-toggle rollback on failure, and inline error text — all localized across all 20 supported locales.
  • Infra: FontConfigWriter is promoted to GhosttyConfigWriter to serve all editable config writes, not just font-size; the submodule pointer is bumped to the Bonsplit branch that introduces tabWidthMode.

Confidence Score: 5/5

Safe to merge — the change is strictly additive and opt-in, touching only config parsing, a new Settings toggle, and Bonsplit appearance wiring guarded by a no-op check.

The new config key, editor helper, appearance mapping, and Settings toggle all mirror established patterns in the codebase. The generation-based ordered-save logic is sound, localization is complete for all 20 supported locales, the appearance signature is correctly extended, and no actor isolation mistakes or blocking primitives were introduced.

No files require special attention.

Important Files Changed

Filename Overview
Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift Adds generation-tracked ordered async save with cancel-and-await chaining, UI rollback on failure, and fully localized toggle; mirrors the existing font-size pattern correctly.
Sources/HostSettingsActions.swift FontConfigWriter renamed to GhosttyConfigWriter; new setSurfaceTabsFillPaneWidth writes through the shared actor and triggers a config reload; surfaceTabsFillPaneWidth() reads from cached config, matching the sidebar font-size pattern.
Sources/Workspace.swift bonsplitTabWidthMode correctly maps the bool flag to .fill/.fixed; change detection added alongside fontSizeChanged; workspace creation uses a single GhosttyConfig.load() call for both font size and tab width mode.
Sources/WorkspaceAppearanceResolution.swift Appearance signature correctly extended with surfaceTabsFillPaneWidth, ensuring live-reload detects toggling the setting; covered by new test.
Resources/Localizable.xcstrings All four new string keys (title, subtitleOn, subtitleOff, saveFailed) carry translations for all 20 supported locales including the 18 previously flagged as missing.

Reviews (6): Last reviewed commit: "Merge origin/main into issue-5091-tab-fi..." | Re-trigger Greptile

Comment thread Resources/Localizable.xcstrings
Comment thread Sources/HostSettingsActions.swift

@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.

1 issue found across 10 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift Outdated
Comment thread Sources/HostSettingsActions.swift

@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
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift`:
- Around line 64-71: The saveTabsFillPaneWidth(_:)'s Task currently drops the
Bool result from hostActions.setSurfaceTabsFillPaneWidth(_:) so failures are
ignored; change the Task to await the Bool result, and if it returns false
revert the optimistic toggle state and surface the same save-failed feedback
used by the tab-bar font-size path (i.e., call the same feedback UI/handler used
there). Keep the existing cancellation behavior on tabsFillSaveTask, but ensure
you check the returned Bool from hostActions.setSurfaceTabsFillPaneWidth, revert
the UI model when it fails, and invoke the existing save-failure feedback
routine.
🪄 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: e6586b14-36f5-40e4-a025-1efc17abf6fd

📥 Commits

Reviewing files that changed from the base of the PR and between 0b4c138 and d6c9a3d.

📒 Files selected for processing (10)
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Environment/SettingsHostActions.swift
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Resources/Localizable.xcstrings
  • Sources/CmuxApplicationSupportDirectories.swift
  • Sources/GhosttyConfig.swift
  • Sources/HostSettingsActions.swift
  • Sources/Workspace.swift
  • cmuxTests/GhosttyConfigTests.swift
  • vendor/bonsplit
  • web/app/[locale]/docs/configuration/page.tsx

Comment thread Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 4, 2026

@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
`@Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift`:
- Around line 74-81: The current saveTabsFillPaneWidth(_:) awaits
hostActions.setSurfaceTabsFillPaneWidth(enabled) then bails out early on
Task.isCancelled, which prevents updating tabsFillPaneWidthLastSaved when the
disk write actually succeeded; move the cancellation check below the saved
handling so that when saved == true you always set tabsFillPaneWidthLastSaved =
enabled and tabsFillSaveFailed = false (so model matches disk), and only skip
UI-only work (like reverting tabsFillPaneWidth) when the task is cancelled;
specifically update the logic around hostActions.setSurfaceTabsFillPaneWidth,
tabsFillPaneWidthLastSaved, tabsFillPaneWidth, and tabsFillSaveFailed so saved
results are applied even if Task.isCancelled immediately after the await.
🪄 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: 0453534f-2de9-4967-b6c3-54e7d03aec75

📥 Commits

Reviewing files that changed from the base of the PR and between d6c9a3d and 8a0181a.

📒 Files selected for processing (3)
  • Packages/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Resources/Localizable.xcstrings
  • Sources/HostSettingsActions.swift
💤 Files with no reviewable changes (2)
  • Resources/Localizable.xcstrings
  • Sources/HostSettingsActions.swift

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3cb346a. Configure here.

@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.

1 issue found across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@austinywang austinywang dismissed coderabbitai[bot]’s stale review June 4, 2026 13:08

Stale automated review: the actionable CodeRabbit thread was resolved after follow-up fixes, and the CodeRabbit status check is passing on the current PR head.

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.

Add an option for surface tabs to span the full pane width

1 participant