Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b77d7c2
Add titlebar visibility options
lawrencecchen Mar 13, 2026
ca6f83f
Refine hidden titlebar behavior
lawrencecchen Mar 13, 2026
cdcfa19
Remove hidden titlebar safe-area gap
lawrencecchen Mar 13, 2026
c8d561c
Add Bonsplit hidden-titlebar drag regression test
lawrencecchen Mar 13, 2026
7385a01
Fix hidden-titlebar pane tab bar controls
lawrencecchen Mar 13, 2026
d5cf95a
Tighten hidden titlebar pane tab layout
lawrencecchen Mar 13, 2026
6b281dc
Remove hidden titlebar top gap
lawrencecchen Mar 13, 2026
3e75be2
Fix hidden-titlebar tab drag host
lawrencecchen Mar 13, 2026
cf3a132
Fix hidden-titlebar pane drag regression
lawrencecchen Mar 13, 2026
6244e49
Add hidden-titlebar top-gap UI test
lawrencecchen Mar 13, 2026
d7d6c60
Remove titlebar accessories when hidden
lawrencecchen Mar 13, 2026
945881b
Use non-movable pane tab bar host
lawrencecchen Mar 13, 2026
cc51646
Underlap hidden workspace content
lawrencecchen Mar 13, 2026
dc394ad
Strengthen hidden titlebar UI regressions
lawrencecchen Mar 13, 2026
9ecefc5
Remove Bonsplit hidden-titlebar inset
lawrencecchen Mar 13, 2026
239ec1d
Wait for Bonsplit UI test setup readiness
lawrencecchen Mar 13, 2026
a82fa63
Add hidden titlebar chrome UI regressions
lawrencecchen Mar 13, 2026
18d2d5f
Fix hidden titlebar chrome regressions
lawrencecchen Mar 13, 2026
5cd4b30
Assert tab drag does not move the window
lawrencecchen Mar 13, 2026
67f2bda
Add raw mouse Bonsplit drag UI regression
lawrencecchen Mar 13, 2026
afd2d21
Fix hidden-titlebar Bonsplit tab drags
lawrencecchen Mar 13, 2026
278a2ff
Stabilize Bonsplit drag UI tests
lawrencecchen Mar 13, 2026
5c42da4
Add stronger Bonsplit drag reorder regression
lawrencecchen Mar 13, 2026
f1e0abe
Fix hidden-titlebar Bonsplit tab reorder drops
lawrencecchen Mar 13, 2026
8d96c97
Add stronger Bonsplit drag and titlebar regressions
lawrencecchen Mar 13, 2026
ae0a4e3
Fix hidden-titlebar traffic lights and Bonsplit drops
lawrencecchen Mar 13, 2026
b61698c
test: cover hidden-titlebar collapsed-sidebar controls
lawrencecchen Mar 15, 2026
87ee59f
fix: collapse hidden-titlebar controls with hidden sidebar
lawrencecchen Mar 15, 2026
39d8cc2
test: cover hidden-titlebar sidebar traffic-light gap
lawrencecchen Mar 15, 2026
3a41872
fix: align hidden-titlebar tabs with the sidebar edge
lawrencecchen Mar 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions GhosttyTabs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
A5001002 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001012 /* ContentView.swift */; };
E62155868BB29FEB5DAAAF25 /* SidebarSelectionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */; };
B9000018A1B2C3D4E5F60719 /* WindowDragHandleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */; };
C9A10001A1B2C3D4E5F60719 /* TitlebarVisibilitySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A10002A1B2C3D4E5F60719 /* TitlebarVisibilitySettings.swift */; };
A5001003 /* TabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001013 /* TabManager.swift */; };
A5001004 /* GhosttyConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001014 /* GhosttyConfig.swift */; };
A5001005 /* GhosttyTerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001015 /* GhosttyTerminalView.swift */; };
Expand Down Expand Up @@ -158,6 +159,7 @@
A5001012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarSelectionState.swift; sourceTree = "<group>"; };
B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowDragHandleView.swift; sourceTree = "<group>"; };
C9A10002A1B2C3D4E5F60719 /* TitlebarVisibilitySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitlebarVisibilitySettings.swift; sourceTree = "<group>"; };
A5001013 /* TabManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabManager.swift; sourceTree = "<group>"; };
A5001014 /* GhosttyConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyConfig.swift; sourceTree = "<group>"; };
A5001015 /* GhosttyTerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyTerminalView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -357,6 +359,7 @@
A5001012 /* ContentView.swift */,
9AD52285508B1D6A9875E7B3 /* SidebarSelectionState.swift */,
B9000017A1B2C3D4E5F60719 /* WindowDragHandleView.swift */,
C9A10002A1B2C3D4E5F60719 /* TitlebarVisibilitySettings.swift */,
A50012F0 /* Backport.swift */,
A50012F2 /* KeyboardShortcutSettings.swift */,
A50012F4 /* KeyboardLayout.swift */,
Expand Down Expand Up @@ -628,6 +631,7 @@
A5001002 /* ContentView.swift in Sources */,
E62155868BB29FEB5DAAAF25 /* SidebarSelectionState.swift in Sources */,
B9000018A1B2C3D4E5F60719 /* WindowDragHandleView.swift in Sources */,
C9A10001A1B2C3D4E5F60719 /* TitlebarVisibilitySettings.swift in Sources */,
A50012F1 /* Backport.swift in Sources */,
A50012F3 /* KeyboardShortcutSettings.swift in Sources */,
A50012F5 /* KeyboardLayout.swift in Sources */,
Expand Down
187 changes: 187 additions & 0 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -73093,6 +73093,193 @@
}
}
}
},
"settings.app.showWorkspaceTitlebar": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Show Workspace Title Bar"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ワークスペースのタイトルバーを表示"
}
}
}
},
"settings.app.showWorkspaceTitlebar.subtitleOff": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Hide the folder/title strip and drag from empty space in the top pane tab bar."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "フォルダとタイトルの帯を隠し、上部のペインタブバーの空き領域からウィンドウをドラッグできます。"
}
}
}
},
"settings.app.showWorkspaceTitlebar.subtitleOn": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Show the folder and active title above pane tabs."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ペインタブの上にフォルダ名と現在のタイトルを表示します。"
}
}
}
},
"settings.app.titlebarControls": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Titlebar Controls"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "タイトルバーのコントロール"
}
}
}
},
"settings.app.titlebarControls.always": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Always Visible"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "常に表示"
}
}
}
},
"settings.app.titlebarControls.hover": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Show on Hover"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ホバー時に表示"
}
}
}
},
"settings.app.titlebarControls.subtitleAlways": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Keep the sidebar, notifications, and new workspace buttons visible."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "サイドバー、通知、新規ワークスペースのボタンを常に表示します。"
}
}
}
},
"settings.app.titlebarControls.subtitleHover": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Hide titlebar buttons until the pointer reaches them."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ポインタが近づくまでタイトルバーのボタンを隠します。"
}
}
}
},
"settings.app.paneTabBarControls": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Pane Tab Bar Controls"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ペインタブバーのコントロール"
}
}
}
},
"settings.app.paneTabBarControls.subtitleAlways": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Keep the pane tab bar's new tab and split buttons visible."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ペインタブバーの新規タブボタンと分割ボタンを常に表示します。"
}
}
}
},
"settings.app.paneTabBarControls.subtitleHover": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Hide the pane tab bar's new tab and split buttons until you hover the bar."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ペインタブバーにホバーするまで、新規タブボタンと分割ボタンを隠します。"
}
}
}
Comment on lines +73233 to +73282
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how paneTabBarControls mode labels are resolved in the codebase
# Looking for whether it shares titlebarControls labels or needs its own

# Search for paneTabBarControls localization key usage
rg -n "paneTabBarControls" --type swift -C 3

# Search for how the visibility mode displayName is implemented
ast-grep --pattern 'displayName'

Repository: manaflow-ai/cmux

Length of output: 8792


🏁 Script executed:

rg -n "enum ChromeControlsVisibilityMode" -A 20 --type swift

Repository: manaflow-ai/cmux

Length of output: 1632


🏁 Script executed:

rg -n "extension ChromeControlsVisibilityMode" -A 15 --type swift

Repository: manaflow-ai/cmux

Length of output: 1056


Add separate mode display name keys for paneTabBarControls or explicitly document intentional key sharing.

The paneTabBarControls picker uses ChromeControlsVisibilityMode.displayName, which hardcodes localization keys from settings.app.titlebarControls.always and settings.app.titlebarControls.hover (lines 3047-3049 in cmuxApp.swift). While both controls share the same enum, this creates implicit coupling.

Consider either:

  1. Adding separate settings.app.paneTabBarControls.always and settings.app.paneTabBarControls.hover keys to the localization file for independent control, or
  2. Refactoring the displayName implementation to make the key sharing explicit and documented in code.

Currently, changes to titlebar mode labels would unintentionally affect pane tab bar mode labels.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Localizable.xcstrings` around lines 73233 - 73282, The
paneTabBarControls picker is reusing ChromeControlsVisibilityMode.displayName
keys (from cmuxApp.swift) which couples pane labels to titlebar labels; add
explicit localization keys or make the sharing explicit: either add
settings.app.paneTabBarControls.always and settings.app.paneTabBarControls.hover
entries to Resources/Localizable.xcstrings and update the UI to use those keys
for paneTabBarControls, or modify ChromeControlsVisibilityMode.displayName (or
the code that maps modes for paneTabBarControls in cmuxApp.swift) to return the
titlebar key intentionally with a clear comment and/or a dedicated mapping
function so changes to titlebar labels won’t silently affect pane labels.

}
}
}
105 changes: 55 additions & 50 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1931,8 +1931,7 @@ struct ContentView: View {
.frame(width: sidebarWidth)
}

/// Space at top of content area for the titlebar. This must be at least the actual titlebar
/// height; otherwise controls like Bonsplit tab dragging can be interpreted as window drags.
/// Space at top of content area reserved for the custom workspace titlebar.
@State private var titlebarPadding: CGFloat = 32

private var terminalContent: some View {
Expand All @@ -1941,56 +1940,59 @@ struct ContentView: View {
let selectedWorkspaceId = tabManager.selectedTabId
let retiringWorkspaceId = self.retiringWorkspaceId

return ZStack {
return ZStack(alignment: .top) {
ZStack {
ForEach(mountedWorkspaces) { tab in
let isSelectedWorkspace = selectedWorkspaceId == tab.id
let isRetiringWorkspace = retiringWorkspaceId == tab.id
let shouldPrimeInBackground = tabManager.pendingBackgroundWorkspaceLoadIds.contains(tab.id)
// Keep the retiring workspace visible during handoff, but never input-active.
// Allowing both selected+retiring workspaces to be input-active lets the
// old workspace steal first responder (notably with WKWebView), which can
// delay handoff completion and make browser returns feel laggy.
let isInputActive = isSelectedWorkspace
let isVisible = isSelectedWorkspace || isRetiringWorkspace
let portalPriority = isSelectedWorkspace ? 2 : (isRetiringWorkspace ? 1 : 0)
WorkspaceContentView(
workspace: tab,
isWorkspaceVisible: isVisible,
isWorkspaceInputActive: isInputActive,
workspacePortalPriority: portalPriority,
onThemeRefreshRequest: { reason, eventId, source, payloadHex in
scheduleTitlebarThemeRefreshFromWorkspace(
workspaceId: tab.id,
reason: reason,
backgroundEventId: eventId,
backgroundSource: source,
notificationPayloadHex: payloadHex
)
ZStack {
ForEach(mountedWorkspaces) { tab in
let isSelectedWorkspace = selectedWorkspaceId == tab.id
let isRetiringWorkspace = retiringWorkspaceId == tab.id
let shouldPrimeInBackground = tabManager.pendingBackgroundWorkspaceLoadIds.contains(tab.id)
// Keep the retiring workspace visible during handoff, but never input-active.
// Allowing both selected+retiring workspaces to be input-active lets the
// old workspace steal first responder (notably with WKWebView), which can
// delay handoff completion and make browser returns feel laggy.
let isInputActive = isSelectedWorkspace
let isVisible = isSelectedWorkspace || isRetiringWorkspace
let portalPriority = isSelectedWorkspace ? 2 : (isRetiringWorkspace ? 1 : 0)
WorkspaceContentView(
workspace: tab,
isWorkspaceVisible: isVisible,
isWorkspaceInputActive: isInputActive,
workspacePortalPriority: portalPriority,
onThemeRefreshRequest: { reason, eventId, source, payloadHex in
scheduleTitlebarThemeRefreshFromWorkspace(
workspaceId: tab.id,
reason: reason,
backgroundEventId: eventId,
backgroundSource: source,
notificationPayloadHex: payloadHex
)
}
)
.opacity(isVisible ? 1 : 0)
.allowsHitTesting(isSelectedWorkspace)
.accessibilityHidden(!isVisible)
.zIndex(isSelectedWorkspace ? 2 : (isRetiringWorkspace ? 1 : 0))
.task(id: shouldPrimeInBackground ? tab.id : nil) {
await primeBackgroundWorkspaceIfNeeded(workspaceId: tab.id)
}
)
.opacity(isVisible ? 1 : 0)
.allowsHitTesting(isSelectedWorkspace)
.accessibilityHidden(!isVisible)
.zIndex(isSelectedWorkspace ? 2 : (isRetiringWorkspace ? 1 : 0))
.task(id: shouldPrimeInBackground ? tab.id : nil) {
await primeBackgroundWorkspaceIfNeeded(workspaceId: tab.id)
}
}
.opacity(sidebarSelectionState.selection == .tabs ? 1 : 0)
.allowsHitTesting(sidebarSelectionState.selection == .tabs)
.accessibilityHidden(sidebarSelectionState.selection != .tabs)

NotificationsPage(selection: $sidebarSelectionState.selection)
.opacity(sidebarSelectionState.selection == .notifications ? 1 : 0)
.allowsHitTesting(sidebarSelectionState.selection == .notifications)
.accessibilityHidden(sidebarSelectionState.selection != .notifications)
Comment on lines +1956 to +2001
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Gate WorkspaceContentView visibility on the active page.

isWorkspaceVisible / isWorkspaceInputActive stay true for the selected workspace even when sidebarSelectionState.selection == .notifications. The outer .opacity(0) / .allowsHitTesting(false) only hide the SwiftUI wrapper; portal-backed workspace surfaces can still sit above SwiftUI and keep focus, so the notifications page can leak the previously selected workspace underneath.

Suggested fix
-        return ZStack(alignment: .top) {
+        let showsWorkspacePage = sidebarSelectionState.selection == .tabs
+        return ZStack(alignment: .top) {
             ZStack {
                 ZStack {
                     ForEach(mountedWorkspaces) { tab in
                         let isSelectedWorkspace = selectedWorkspaceId == tab.id
                         let isRetiringWorkspace = retiringWorkspaceId == tab.id
                         let shouldPrimeInBackground = tabManager.pendingBackgroundWorkspaceLoadIds.contains(tab.id)
@@
-                        let isInputActive = isSelectedWorkspace
-                        let isVisible = isSelectedWorkspace || isRetiringWorkspace
-                        let portalPriority = isSelectedWorkspace ? 2 : (isRetiringWorkspace ? 1 : 0)
+                        let isInputActive = showsWorkspacePage && isSelectedWorkspace
+                        let isVisible = showsWorkspacePage && (isSelectedWorkspace || isRetiringWorkspace)
+                        let portalPriority = isVisible ? (isSelectedWorkspace ? 2 : 1) : 0
@@
-                .opacity(sidebarSelectionState.selection == .tabs ? 1 : 0)
-                .allowsHitTesting(sidebarSelectionState.selection == .tabs)
-                .accessibilityHidden(sidebarSelectionState.selection != .tabs)
+                .opacity(showsWorkspacePage ? 1 : 0)
+                .allowsHitTesting(showsWorkspacePage)
+                .accessibilityHidden(!showsWorkspacePage)

Based on learnings, Portal-hosted terminal views can sit above SwiftUI during split/workspace churn.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/ContentView.swift` around lines 1944 - 1989, The WorkspaceContentView
instances are still marked visible/input-active when the sidebar is showing
Notifications, letting their portal-backed surfaces remain above SwiftUI; update
the visibility/input logic so mountedWorkspaces computes isWorkspaceVisible and
isWorkspaceInputActive only when sidebarSelectionState.selection == .tabs (e.g.
combine selectedWorkspaceId/retiringWorkspaceId checks with
sidebarSelectionState.selection == .tabs), and use those combined flags when
constructing WorkspaceContentView (and for .opacity, .allowsHitTesting,
.accessibilityHidden, .zIndex, and the .task id) so the workspace portals are
fully deactivated whenever the active page is not .tabs.

}
.opacity(sidebarSelectionState.selection == .tabs ? 1 : 0)
.allowsHitTesting(sidebarSelectionState.selection == .tabs)
.accessibilityHidden(sidebarSelectionState.selection != .tabs)
.padding(.top, titlebarPadding)

NotificationsPage(selection: $sidebarSelectionState.selection)
.opacity(sidebarSelectionState.selection == .notifications ? 1 : 0)
.allowsHitTesting(sidebarSelectionState.selection == .notifications)
.accessibilityHidden(sidebarSelectionState.selection != .notifications)
}
.padding(.top, titlebarPadding)
.overlay(alignment: .top) {
// Titlebar overlay is only over terminal content, not the sidebar.
customTitlebar
if showWorkspaceTitlebar {
// Titlebar overlay is only over terminal content, not the sidebar.
customTitlebar
Comment on lines +2005 to +2007
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep a drag handle when hiding the workspace titlebar

When this branch omits customTitlebar, it also removes the WindowDragHandleView overlay for the terminal area. In hidden-titlebar mode, users can still land on NotificationsPage with the sidebar hidden, and that page does not add its own drag handle; combined with the window being non-movable by background (isMovableByWindowBackground = false and isMovable = false), this leaves no draggable region and the window cannot be repositioned until another UI state is restored.

Useful? React with 👍 / 👎.

}
}
}

Expand All @@ -2007,6 +2009,8 @@ struct ContentView: View {
@AppStorage("bgGlassTintHex") private var bgGlassTintHex = "#000000"
@AppStorage("bgGlassTintOpacity") private var bgGlassTintOpacity = 0.03
@AppStorage("bgGlassEnabled") private var bgGlassEnabled = false
@AppStorage(WorkspaceTitlebarSettings.showTitlebarKey)
private var showWorkspaceTitlebar = WorkspaceTitlebarSettings.defaultShowTitlebar
@AppStorage("debugTitlebarLeadingExtra") private var debugTitlebarLeadingExtra: Double = 0

@State private var titlebarLeadingInset: CGFloat = 12
Expand Down Expand Up @@ -2623,7 +2627,7 @@ struct ContentView: View {
removeSidebarResizerPointerMonitor()
})

view = AnyView(view.background(WindowAccessor { [sidebarBlendMode, bgGlassEnabled, bgGlassTintHex, bgGlassTintOpacity] window in
view = AnyView(view.background(WindowAccessor { [sidebarBlendMode, bgGlassEnabled, bgGlassTintHex, bgGlassTintOpacity, showWorkspaceTitlebar] window in
window.identifier = NSUserInterfaceItemIdentifier(windowIdentifier)
window.titlebarAppearsTransparent = true
// Do not make the entire background draggable; it interferes with drag gestures
Expand All @@ -2646,10 +2650,11 @@ struct ContentView: View {
}
}

// Keep content below the titlebar so drags on Bonsplit's tab bar don't
// get interpreted as window drags.
// Reserve space for the custom titlebar only when it is enabled. When hidden, the
// top Bonsplit tab bar moves into this strip and its empty space gets an explicit
// drag handle overlay instead.
let computedTitlebarHeight = window.frame.height - window.contentLayoutRect.height
let nextPadding = max(28, min(72, computedTitlebarHeight))
let nextPadding = showWorkspaceTitlebar ? max(28, min(72, computedTitlebarHeight)) : 0
if abs(titlebarPadding - nextPadding) > 0.5 {
Comment on lines 2669 to 2671
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recompute padding after workspace-titlebar setting changes

nextPadding now depends on showWorkspaceTitlebar, but this computation is inside a WindowAccessor callback created with default deduping, so it only runs on initial attach for the same window. When users toggle “Show Workspace Title Bar” at runtime, the view condition changes immediately but titlebarPadding can stay stale, causing either a leftover empty strip when hidden or a collapsed titlebar region when re-enabled until a new window/session refreshes it.

Useful? React with 👍 / 👎.

DispatchQueue.main.async {
titlebarPadding = nextPadding
Expand Down
Loading
Loading