Repository navigation
Allow configurable Dock max width - #4385
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a persisted ChangesConfigurable Sidebar Width
Sequence Diagram(s) sequenceDiagram
participant SettingsFile
participant SettingsStore
participant ApplyHelpers
participant UserDefaults
SettingsFile->>SettingsStore: provide JSON sections (sidebar, browser, etc.)
SettingsStore->>ApplyHelpers: apply mappings for booleans/strings/arrays
ApplyHelpers->>UserDefaults: persist normalized values
ApplyHelpers->>SettingsStore: log invalid-path when present with wrong type
sequenceDiagram
participant AppStorage
participant ContentView
participant ClampLogic
participant SettingsUI
AppStorage->>ContentView: rightSidebarMaxWidthSetting value
SettingsUI->>AppStorage: user toggles / edits max width
ContentView->>ClampLogic: request clamped width (availableWidth, configuredMaximum)
ClampLogic->>ContentView: return clamped width (min..max)
AppStorage->>ContentView: onChange triggers reclamp and portal sync
Estimated code review effort: Possibly related PRs:
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (2 errors, 1 warning, 2 inconclusive)
✅ Passed checks (12 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
28669e4 to
33675a0
Compare
Greptile SummaryThis PR adds a user-configurable max width for the right Dock sidebar via a new
Confidence Score: 5/5Safe to merge — clamping math is well-tested, live geometry re-sync is correctly wired, and the settings-JSON refactor uses verified matching UserDefaults keys. The clamp logic is exercised by five new unit tests covering defaults, configured override, available-width cap, minimum clamp, and settings-file round-trips. The AutomationCatalogSection keys share the same userDefaultsKey slots as the old IntegrationsCatalogSection calls they replace, so no settings are silently dropped. The only findings are a committed local lock file and a one-character API inconsistency in a rowHeightProbe closure — neither affects runtime behaviour. No files require special attention; .claude/scheduled_tasks.lock should be gitignored to prevent future noise. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[cmux.json sidebar.rightMaxWidth] -->|parse and clamp| B[KeyboardShortcutSettingsFileStore parseSidebarSection]
B -->|UserDefaults write| C[rightSidebarMaxWidth key]
D[Settings UI SidebarSection toggle+field] -->|write| C
C -->|AppStorage| E[ContentView rightSidebarMaxWidthSetting]
E -->|configuredMaximumWidth| F[rightSidebarConfiguredMaximumWidth Optional CGFloat]
F -->|passed into| G[clampedRightSidebarWidth]
G --> H[min configuredOrDefaultCap availableWidthCap]
H --> I[fileExplorerWidth clamped sidebar width]
E -->|onChange| J[clampRightSidebarWidthIfNeeded and schedulePortalGeometrySynchronize]
J --> I
Reviews (23): Last reviewed commit: "merge: resolve automation settings parsi..." | Re-trigger Greptile |
There was a problem hiding this comment.
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 `@Sources/ContentView.swift`:
- Around line 1661-1671: When a configuredMaximumWidth is present the code must
still reserve the terminal minimum width; change the configured branch to cap
configuredMaximumWidth by sanitizedAvailableWidth -
Self.minimumTerminalWidthWithRightSidebar before applying min/max. In other
words, compute an availableWidthCap = min(configuredMaximumWidth,
sanitizedAvailableWidth - Self.minimumTerminalWidthWithRightSidebar) (while
keeping the configuredMaximumWidth.isFinite check), then set maximumWidth =
min(sanitizedAvailableWidth, max(minimumWidth, availableWidthCap)) so the right
sidebar can never consume the terminal's reserved minimum width (references:
configuredMaximumWidth, Self.minimumTerminalWidthWithRightSidebar,
sanitizedAvailableWidth, maximumWidth, minimumWidth).
In `@web/data/cmux.schema.json`:
- Line 503: The new user-facing "description" string added in
web/data/cmux.schema.json must be added to the localized message sets for every
locale defined in web/i18n/routing.ts; update the locale message files (the
per-locale JSON/TS files referenced by routing.ts) with a new key for this
Dock/right sidebar description (use a clear key tied to cmux.schema.json, e.g.,
cmux.sidebarMaxWidth.description) and provide translations for all locales, then
replace the inline description in cmux.schema.json with a reference to that
message key so the schema uses the i18n copy used across the app.
🪄 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: a99b224f-e434-4322-b527-5092f9bfc058
📒 Files selected for processing (7)
Sources/CmuxSettingsFileStore+CompactSectionParsing.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/ContentView.swiftSources/KeyboardShortcutSettingsFileStore.swiftcmux.xcodeproj/project.pbxprojcmuxTests/SidebarWidthPolicyTests.swiftweb/data/cmux.schema.json
33675a0 to
3c3759b
Compare
Allow sidebar.rightMaxWidth from cmux.json to override the right sidebar dynamic cap while preserving the existing default policy.
3c3759b to
1b2ae13
Compare
There was a problem hiding this comment.
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 `@Sources/ContentView.swift`:
- Around line 3122-3125: The right-sidebar onChange observer (watching
rightSidebarMaxWidthSetting) adjusts sizes via clampRightSidebarWidthIfNeeded()
and updateSidebarResizerBandState() but doesn't resync portal geometry; after
those calls (inside the same observer) call schedulePortalGeometrySynchronize()
when the right sidebar is visible so portal-hosted content gets updated to the
new width immediately—i.e., add a call to schedulePortalGeometrySynchronize() in
the rightSidebarMaxWidthSetting onChange handler (conditional on the sidebar
being visible if such a visibility check exists).
In `@web/data/cmux.schema.json`:
- Around line 499-503: Add a missing JSON Schema "description" for the sidebar
property "rightMaxWidth": update the schema entry for "rightMaxWidth" (the
property name) to include a description string consistent with other sidebar
properties, e.g. a concise tooltip like "Maximum width in points for the right
sidebar/Dock. When omitted, the built-in cap applies." so editors show
autocompletion help for this property.
🪄 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: 16c1f53f-f9e6-4330-ab4a-778b7a84bd21
📒 Files selected for processing (5)
Sources/CmuxSettingsJSONPathSupport.swiftSources/ContentView.swiftSources/KeyboardShortcutSettingsFileStore.swiftcmuxTests/SidebarWidthPolicyTests.swiftweb/data/cmux.schema.json
There was a problem hiding this comment.
♻️ Duplicate comments (2)
web/data/cmux.schema.json (1)
500-504:⚠️ Potential issue | 🟠 MajorUser-facing description requires localization.
The description at line 503 is user-facing (appears in editor tooltips) and requires localization coverage for all supported locales. This issue has already been flagged in a previous review.
As per coding guidelines:
web/data/**/*.json: "Verify user-facing schema titles, descriptions, and data copy have matching localized message coverage for every locale listed in web/i18n/routing.ts."🤖 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 `@web/data/cmux.schema.json` around lines 500 - 504, The schema property "rightMaxWidth" in web/data/cmux.schema.json has a user-facing description that must be localized; replace the inline description with a reference key (e.g., "description": "cmux.rightMaxWidth.description") and add the corresponding localized message entries for that key in each locale file referenced by web/i18n/routing.ts, ensuring message IDs match and content mirrors the original English text so editor tooltips display correctly for all supported locales.Sources/ContentView.swift (1)
1661-1673:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTreat the configured max as an extra cap, not a replacement.
This branch drops both
minimumTerminalWidthWithRightSidebarandmaximumRightSidebarWidthwheneversidebar.rightMaxWidthis set. In a 900pt content area withsidebar.rightMaxWidth = 800, the sidebar can now grow to ~800pt and leave the terminal at ~100pt, which makes the new setting behave like a full override instead of a maximum.💡 Suggested fix
let minimumWidth = Self.minimumRightSidebarWidth let sanitizedCandidate = candidate.isFinite ? candidate : 220 let sanitizedAvailableWidth = availableWidth.isFinite && availableWidth > 0 ? availableWidth : 1920 - let maximumWidth: CGFloat - if let configuredMaximumWidth, configuredMaximumWidth.isFinite { - // An explicit cmux.json max width replaces the legacy terminal-reservation cap. - maximumWidth = min( - sanitizedAvailableWidth, - max(minimumWidth, configuredMaximumWidth) - ) - } else { - let availableWidthCap = sanitizedAvailableWidth - Self.minimumTerminalWidthWithRightSidebar - maximumWidth = min( - Self.maximumRightSidebarWidth, - max(minimumWidth, availableWidthCap) - ) - } + let availableWidthCap = max( + minimumWidth, + sanitizedAvailableWidth - Self.minimumTerminalWidthWithRightSidebar + ) + let derivedMaximumWidth = min(Self.maximumRightSidebarWidth, availableWidthCap) + let configuredCap: CGFloat? = { + guard let configuredMaximumWidth, configuredMaximumWidth.isFinite else { return nil } + return max(minimumWidth, configuredMaximumWidth) + }() + let maximumWidth = min(derivedMaximumWidth, configuredCap ?? derivedMaximumWidth) return max(minimumWidth, min(maximumWidth, sanitizedCandidate))🤖 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 `@Sources/ContentView.swift` around lines 1661 - 1673, The configuredMaximumWidth branch currently replaces the legacy caps; instead treat configuredMaximumWidth as an extra cap on top of existing logic: compute availableWidthCap = sanitizedAvailableWidth - Self.minimumTerminalWidthWithRightSidebar and then set maximumWidth to min(Self.maximumRightSidebarWidth, max(minimumWidth, min(availableWidthCap, configuredMaximumWidth))). Update the code paths that reference configuredMaximumWidth (e.g., configuredMaximumWidth, minimumTerminalWidthWithRightSidebar, maximumRightSidebarWidth, sanitizedAvailableWidth, maximumWidth, minimumWidth) so the configured value only further restricts the sidebar width rather than overriding the terminal-reservation cap.
🤖 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.
Duplicate comments:
In `@Sources/ContentView.swift`:
- Around line 1661-1673: The configuredMaximumWidth branch currently replaces
the legacy caps; instead treat configuredMaximumWidth as an extra cap on top of
existing logic: compute availableWidthCap = sanitizedAvailableWidth -
Self.minimumTerminalWidthWithRightSidebar and then set maximumWidth to
min(Self.maximumRightSidebarWidth, max(minimumWidth, min(availableWidthCap,
configuredMaximumWidth))). Update the code paths that reference
configuredMaximumWidth (e.g., configuredMaximumWidth,
minimumTerminalWidthWithRightSidebar, maximumRightSidebarWidth,
sanitizedAvailableWidth, maximumWidth, minimumWidth) so the configured value
only further restricts the sidebar width rather than overriding the
terminal-reservation cap.
In `@web/data/cmux.schema.json`:
- Around line 500-504: The schema property "rightMaxWidth" in
web/data/cmux.schema.json has a user-facing description that must be localized;
replace the inline description with a reference key (e.g., "description":
"cmux.rightMaxWidth.description") and add the corresponding localized message
entries for that key in each locale file referenced by web/i18n/routing.ts,
ensuring message IDs match and content mirrors the original English text so
editor tooltips display correctly for all supported locales.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: f422a895-8ec1-4e9a-8803-f85b408cd04c
📒 Files selected for processing (2)
Sources/ContentView.swiftweb/data/cmux.schema.json
There was a problem hiding this comment.
1 issue found across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Stale CodeRabbit changes-requested reviews: all inline findings have been addressed, all review threads are resolved, and the latest CodeRabbit status is passing.
…idth-config # Conflicts: # Resources/Localizable.xcstrings # Sources/KeyboardShortcutSettingsFileStore.swift # Sources/SettingsNavigation.swift # web/messages/ar.json # web/messages/bs.json # web/messages/da.json # web/messages/de.json # web/messages/en.json # web/messages/es.json # web/messages/fr.json # web/messages/it.json # web/messages/ja.json # web/messages/km.json # web/messages/ko.json # web/messages/no.json # web/messages/pl.json # web/messages/pt-BR.json # web/messages/ru.json # web/messages/th.json # web/messages/tr.json # web/messages/uk.json # web/messages/zh-CN.json # web/messages/zh-TW.json
…idth-config # Conflicts:
…idth-config # Conflicts: # .github/swift-file-length-budget.tsv
…idth-config # Conflicts: # .github/swift-file-length-budget.tsv
…idth-config # Conflicts: # Sources/ContentView.swift
…idth-config # Conflicts: # .github/swift-file-length-budget.tsv # web/messages/ar.json # web/messages/bs.json # web/messages/da.json # web/messages/de.json # web/messages/en.json # web/messages/es.json # web/messages/fr.json # web/messages/it.json # web/messages/ja.json # web/messages/km.json # web/messages/ko.json # web/messages/no.json # web/messages/pl.json # web/messages/pt-BR.json # web/messages/ru.json # web/messages/th.json # web/messages/tr.json # web/messages/uk.json # web/messages/zh-CN.json # web/messages/zh-TW.json
…idth-config # Conflicts: # .github/swift-file-length-budget.tsv # Sources/KeyboardShortcutSettingsFileStore.swift
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…riven mapping, add workspaceAutoNaming/autoNamingAgent) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Strategy: -X theirs + cherry-pick fork features back Upstream changes pulled in (highlights): - Stagger restored terminal surface spawns (manaflow-ai#6149) - Configurable Dock max width (manaflow-ai#4385) - Polish canvas minimap navigation (manaflow-ai#6105) - Opt-in AI auto-naming workspaces (manaflow-ai#6071) - Dissolve namespace-enums into value types (manaflow-ai#6126) - Fix stale remote connected state after proxy disconnect (manaflow-ai#4513) - Surface browser Safari (manaflow-ai#6113) - Fix terminal top-row mouse (manaflow-ai#4391) - iOS Shift key support (manaflow-ai#6104) Fork-only features verified intact post-merge: - cmux_term socket handlers: surface.snapshot, screen_text, screen_hash, wait_for_text, wait_for_idle, wait_for_screen_change, wait_for_kind, wait_for_cursor, tui_probe, expect, screen_region (6 handler funcs) - agent-bus: notification.create with $bus dispatch - TerminalSurface.visibleSnapshot() + processHasExited() helpers - skills/cmux-terminal-control/* (13 Python lib files + ORCHESTRATOR_TEMPLATE.md) - HerdrWorkspaceSync + CustomTitleSource.herdrInbound Adapter changes (fork-side): - Drop InternalImportsByDefault from CmuxFeedback/CmuxFeedbackUI packages (default-arg parameter types remained internal-only across modules, blocking FeedbackComposerBridge() construction from cmux app target) - Strip .rawValue from SurfaceKind cases (became plain String) - Add herdrInbound to CustomTitleSource enum - Delete shadowing local enums in ContentView (CommandPaletteOverlayPromotionPolicy, ExtensionSidebarBrowserStackDropPlanner, SidebarDragFailsafePolicy, SidebarDragLifecycleNotification, SidebarMarkdownRenderer, SidebarOutsideDropResetPolicy, SidebarShortcutHintFreezePolicy, SidebarTrailingAccessoryWidthPolicy, SidebarWorkspaceSelectionSyncPolicy, ShortcutHintDebugSettings, ShortcutHintModifierPolicy, FeedbackComposerBridge, FeedbackComposerBridgeError, ExtensionSidebarBrowserStackDropRow, SidebarTabDropIndicatorPredicate, SidebarDropEdge, SidebarDropIndicator) - Delete local TerminalSurfaceClaudeCommandShim (use package's via typealias) - Delete local TerminalSurfaceRuntimeTeardownCoordinator + Request stubs (use package's enqueueRuntimeTeardown public API directly) - Adapt static→instance: CmuxGhosttyConfigSettingEditor.X → ().X, CmuxApplicationSupportDirectories.userDirectories → init(env:).userDirectories - newTerminalSurface/TerminalPanel: thread externalIo: parameter for herdr - Add createReplacementTerminalPanel(in:) overload - Add attachToViewForInputDemand/requestInputDemandSurfaceStartIfNeeded stubs - TerminalSurfaceRuntimeFilesystem.live(): wrap installClaudeCommandShim sync→async - AppDelegate.sortedMainWindowContextsForSessionSnapshot: add includeQuickTerminal param - Wire BackgroundSessionStore + BackgroundSessionsSidebarSection + SidebarSectionDivider in pbxproj (PBXFileReference + PBXBuildFile entries were missing post-merge) - Add SidebarSectionDivider stub View (was referenced but never created) Rebuilt clean against /tmp/cmux-p52 derivedData.
…ure) Codex review found the blind spot: the group-header row is assembled by sidebarWorkspaceGroupHeader(...) in VerticalTabsSidebar+WorkspaceGroups.swift, where modifiers wrap the header before it enters the LazyVStack — a GeometryReader or anchorPreference added there defeats laziness exactly like one inside the row view (the #4385 regression entered through the header path). The guard now scans that whole file for the row-forbidden shapes with a rename-protected marker, and the scale fixture groups the first 20 workspaces into 5 groups so group-header realization and convergence are asserted by the behavioral backstop (bounds on groupHeaderBodies at mount and in the quiet check). Verified on the AWS M4 Pro runner: 3/3 pass in 2.7s, no host restarts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-view guard (#7221) * Extend sidebar lazy-layout guard to the row views (TabItemView, group header) The source-scan guard from #6870 protected only the two container functions, but four of the five historical livelock regressions entered through the row views: the #2586/#6556 GeometryReader -> @State rowHeight probes lived in TabItemView and SidebarWorkspaceGroupHeaderView (removed by #6111, reintroduced by #4385, removed again by #7117) and shipped in stable v0.64.17, which livelocked in the wild on 2026-07-02 with exactly that signature (#2586 (comment)). Scan the TabItemView region of ContentView.swift and the whole group header file for per-row geometry feedback: GeometryReader, onGeometryChange, manual sizeThatFits, ProposedViewSize(nil), per-row anchorPreference/overlayPreferenceValue, and any discovered custom Layout. Rows must stay measurement-free; the only sanctioned geometry path is the container's drag-gated reader. Missing row types fail loudly so a rename cannot rot the guard into a no-op. Verified the extended guard retroactively flags both v0.64.17 row views. New self-test cases (j)-(m) cover clean-pass, the #6556 probe shape, the #5323 anchorPreference shape, and rename protection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add behavioral scale gate for the sidebar lazy-layout contract The lazy-layout contract (sidebar layout/diff work stays O(visible rows), never O(all workspaces)) has regressed five times through five different mechanisms (#5323 anchorPreference aggregation, #5764 String ids, #5845 animated height interpolation, #6210 force-measuring custom Layout, #6556 GeometryReader -> @State feedback), each shipping to stable before detection because nothing exercises the sidebar at the 100+ workspace scale where O(N) per pass livelocks the main thread (#2586). SidebarLazyLayoutScaleTests mounts the real VerticalTabsSidebar with 300 workspaces in an NSHostingView and counts actual row body evaluations through a DEBUG-only environment probe (SidebarLazyContractProbe, same pattern as MinimalModeInvalidationProbe): - mount must realize only viewport rows (catches any virtualization defeat, present or future, regardless of mechanism) - a 40-burst unread-model storm (the sidebar's highest-frequency whole-body invalidation path) must stay row-scoped and go quiet when the burst stops (catches feedback loops the way #6556 manifested) - a harness canary reproduces the GeometryReader -> @State shape in divergent form and asserts the harness detects it, so the gate cannot silently rot This is the mechanism-independent backstop behind the source-shape scan in scripts/check-sidebar-lazy-layout.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix scale-test autorelease avalanche that hung/crashed the app host Creating 300 workspaces inside one main-actor job accumulated every autoreleased object from the O(N)-per-add snapshot work into a single autorelease pool; the closing objc_autoreleasePoolPop then crashed CI (Signal 11 in AutoreleasePoolPage::releaseUntil, masked as a green run, see #5641) and hung for hours when reproduced on an AWS M4 Pro (sampled: main thread pinned in releaseUntil). The app never does this; real workspace creation happens one per event-loop turn with AppKit popping the pool between turns. Make the harness match real cadence: per-iteration autoreleasepool around addWorkspace and a run-loop turn every 20 creations. Also hoist the RunLoop.run call into a synchronous helper (fixes the Swift 6 unavailable-from-async warning) wrapped in its own pool. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix harness NSWindow double-release that killed the app host NSZombies named the corpse: "-[NSKVONotifying_NSWindow release]: message sent to deallocated instance". The harness windows used the NSWindow default isReleasedWhenClosed=true, so tearDown's close() performed AppKit's own release on top of ARC's; the double-release SEGV'd the host at the next autorelease-pool pop, before the pass was recorded, and CI masked the crash as a green run (#5641 (comment)). With zombies absorbing the over-release, all assertions pass in under a second, isolating the crash entirely to window teardown. Set isReleasedWhenClosed = false on both harness windows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Guard --file mode: require container functions only when one is present Addresses Greptile P2 on the PR: --file against a row-view source (no workspaceScrollContent/workspaceRows) emitted false could-not-locate violations that masked real row findings. In --file mode the container checks now apply only when at least one guarded function exists in the source, so ad-hoc row-view scans are clean while a fixture that renamed one function still fails loudly. Self-test cases added for both sides. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Split probe env key + extension into their own files (Aziz policy) One major type per Swift file, matching the MinimalModeInvalidationProbe three-file layout exactly. No content changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Cover the group-header row wrapper (guard target + grouped scale fixture) Codex review found the blind spot: the group-header row is assembled by sidebarWorkspaceGroupHeader(...) in VerticalTabsSidebar+WorkspaceGroups.swift, where modifiers wrap the header before it enters the LazyVStack — a GeometryReader or anchorPreference added there defeats laziness exactly like one inside the row view (the #4385 regression entered through the header path). The guard now scans that whole file for the row-forbidden shapes with a rename-protected marker, and the scale fixture groups the first 20 workspaces into 5 groups so group-header realization and convergence are asserted by the behavioral backstop (bounds on groupHeaderBodies at mount and in the quiet check). Verified on the AWS M4 Pro runner: 3/3 pass in 2.7s, no host restarts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Make the scale harness hermetic against persisted sidebar provider Codex review: VerticalTabsSidebar selects between the workspace list and extension/built-in sidebars via @AppStorage(CmuxExtensionSidebarSelection.defaultsKey), so a host with a persisted non-default provider would mount the wrong sidebar and the probes would never fire. Use a scratch UserDefaults suite pinned to the default provider via .defaultAppStorage, cleaned in tearDown — the WorkspaceContentViewVisibilityTests pattern. Verified on the AWS M4 Pro runner: 3/3 pass in 2.7s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…-view guard (manaflow-ai#7221) * Extend sidebar lazy-layout guard to the row views (TabItemView, group header) The source-scan guard from manaflow-ai#6870 protected only the two container functions, but four of the five historical livelock regressions entered through the row views: the manaflow-ai#2586/manaflow-ai#6556 GeometryReader -> @State rowHeight probes lived in TabItemView and SidebarWorkspaceGroupHeaderView (removed by manaflow-ai#6111, reintroduced by livelocked in the wild on 2026-07-02 with exactly that signature (manaflow-ai#2586 (comment)). Scan the TabItemView region of ContentView.swift and the whole group header file for per-row geometry feedback: GeometryReader, onGeometryChange, manual sizeThatFits, ProposedViewSize(nil), per-row anchorPreference/overlayPreferenceValue, and any discovered custom Layout. Rows must stay measurement-free; the only sanctioned geometry path is the container's drag-gated reader. Missing row types fail loudly so a rename cannot rot the guard into a no-op. Verified the extended guard retroactively flags both v0.64.17 row views. New self-test cases (j)-(m) cover clean-pass, the manaflow-ai#6556 probe shape, the manaflow-ai#5323 anchorPreference shape, and rename protection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add behavioral scale gate for the sidebar lazy-layout contract The lazy-layout contract (sidebar layout/diff work stays O(visible rows), never O(all workspaces)) has regressed five times through five different mechanisms (manaflow-ai#5323 anchorPreference aggregation, manaflow-ai#5764 String ids, manaflow-ai#5845 animated height interpolation, manaflow-ai#6210 force-measuring custom Layout, manaflow-ai#6556 GeometryReader -> @State feedback), each shipping to stable before detection because nothing exercises the sidebar at the 100+ workspace scale where O(N) per pass livelocks the main thread (manaflow-ai#2586). SidebarLazyLayoutScaleTests mounts the real VerticalTabsSidebar with 300 workspaces in an NSHostingView and counts actual row body evaluations through a DEBUG-only environment probe (SidebarLazyContractProbe, same pattern as MinimalModeInvalidationProbe): - mount must realize only viewport rows (catches any virtualization defeat, present or future, regardless of mechanism) - a 40-burst unread-model storm (the sidebar's highest-frequency whole-body invalidation path) must stay row-scoped and go quiet when the burst stops (catches feedback loops the way manaflow-ai#6556 manifested) - a harness canary reproduces the GeometryReader -> @State shape in divergent form and asserts the harness detects it, so the gate cannot silently rot This is the mechanism-independent backstop behind the source-shape scan in scripts/check-sidebar-lazy-layout.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix scale-test autorelease avalanche that hung/crashed the app host Creating 300 workspaces inside one main-actor job accumulated every autoreleased object from the O(N)-per-add snapshot work into a single autorelease pool; the closing objc_autoreleasePoolPop then crashed CI (Signal 11 in AutoreleasePoolPage::releaseUntil, masked as a green run, see manaflow-ai#5641) and hung for hours when reproduced on an AWS M4 Pro (sampled: main thread pinned in releaseUntil). The app never does this; real workspace creation happens one per event-loop turn with AppKit popping the pool between turns. Make the harness match real cadence: per-iteration autoreleasepool around addWorkspace and a run-loop turn every 20 creations. Also hoist the RunLoop.run call into a synchronous helper (fixes the Swift 6 unavailable-from-async warning) wrapped in its own pool. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix harness NSWindow double-release that killed the app host NSZombies named the corpse: "-[NSKVONotifying_NSWindow release]: message sent to deallocated instance". The harness windows used the NSWindow default isReleasedWhenClosed=true, so tearDown's close() performed AppKit's own release on top of ARC's; the double-release SEGV'd the host at the next autorelease-pool pop, before the pass was recorded, and CI masked the crash as a green run (manaflow-ai#5641 (comment)). With zombies absorbing the over-release, all assertions pass in under a second, isolating the crash entirely to window teardown. Set isReleasedWhenClosed = false on both harness windows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Guard --file mode: require container functions only when one is present Addresses Greptile P2 on the PR: --file against a row-view source (no workspaceScrollContent/workspaceRows) emitted false could-not-locate violations that masked real row findings. In --file mode the container checks now apply only when at least one guarded function exists in the source, so ad-hoc row-view scans are clean while a fixture that renamed one function still fails loudly. Self-test cases added for both sides. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Split probe env key + extension into their own files (Aziz policy) One major type per Swift file, matching the MinimalModeInvalidationProbe three-file layout exactly. No content changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Cover the group-header row wrapper (guard target + grouped scale fixture) Codex review found the blind spot: the group-header row is assembled by sidebarWorkspaceGroupHeader(...) in VerticalTabsSidebar+WorkspaceGroups.swift, where modifiers wrap the header before it enters the LazyVStack — a GeometryReader or anchorPreference added there defeats laziness exactly like one inside the row view (the manaflow-ai#4385 regression entered through the header path). The guard now scans that whole file for the row-forbidden shapes with a rename-protected marker, and the scale fixture groups the first 20 workspaces into 5 groups so group-header realization and convergence are asserted by the behavioral backstop (bounds on groupHeaderBodies at mount and in the quiet check). Verified on the AWS M4 Pro runner: 3/3 pass in 2.7s, no host restarts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Make the scale harness hermetic against persisted sidebar provider Codex review: VerticalTabsSidebar selects between the workspace list and extension/built-in sidebars via @AppStorage(CmuxExtensionSidebarSelection.defaultsKey), so a host with a persisted non-default provider would mount the wrong sidebar and the probes would never fire. Use a scratch UserDefaults suite pinned to the default provider via .defaultAppStorage, cleaned in tearDown — the WorkspaceContentViewVisibilityTests pattern. Verified on the AWS M4 Pro runner: 3/3 pass in 2.7s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 2071529)
Summary
Fixes #4375
Verification
Note
Medium Risk
Touches main-window layout clamping and live geometry when the cap changes; incorrect clamp math could shrink the terminal or allow oversized Dock panels, though bounds and tests mitigate this.
Overview
Adds a Dock max width preference so the right sidebar can use a user-defined cap (up to 4096pt) instead of only the built-in 1200pt dynamic limit, while still reserving minimum terminal width.
Policy & persistence: New
RightSidebarWidthSettingscentralizes min/max, clamping, and “override off” (-1) vs active values; catalog keyssidebar.rightMaxWidthand a remembered width support re-enabling the toggle.cmux.jsonacceptssidebar.rightMaxWidth(validated/clamped on import); schema and locale strings are updated.Runtime:
ContentViewreads the setting via@AppStorageand passes an optional configured cap intoclampedRightSidebarWidth; changing the setting re-clamps the Dock and syncs geometry.Settings UI: Sidebar Appearance gets a switch plus numeric field; disabling override stores the last custom value for restore.
Refactor: Settings JSON parsing gains shared boolean/string/array mapping tables (app, notifications, terminal, sidebar, automation, browser) used when applying
cmux.json.Tests: Unit tests for width policy and settings-file import; expanded sidebar width clamp tests.
Reviewed by Cursor Bugbot for commit c6a3fc8. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds a configurable max width for the Dock/right sidebar that can exceed the old cap while preserving terminal space. Also updates settings import/UI and removes unstable drop-target code for steadier layouts.
New Features
sidebar.rightMaxWidth(>0) incmux.jsonand Settings UI; clamps to 276–4096pt; invalid inputs logged; schema, i18n, and settings search/anchors updated.RightSidebarWidthSettingsused by JSON import, Settings UI, and runtime; can exceed the 1200pt cap while reserving 360pt for the terminal; applies live and restores your last custom width when re-enabled.Bug Fixes
sidebar.rightMaxWidthimports correctly.BrowserSearchSettingsStoreafter a main rename.workspaceAutoNamingandautoNamingAgentkeys.Written for commit c61fe60. Summary will update on new commits.
Summary by CodeRabbit
New Features
Tests
Documentation