macos: only check for auto-tabbing when tabbing preference is always

windowDidLoad undoes macOS automatic window tabbing by inspecting
window.tabGroup. Accessing tabGroup on a fresh window materializes
AppKit's tab group machinery, which takes ~15-20ms and is on the
critical path of every window creation, including the first window at
app launch.

AppKit only auto-tabs a fresh window when the system tabbing
preference is "always": the tab bar "+" button goes through
newWindowForTab which we intercept and route through our own tab
logic, so it never auto-tabs. Guard the check on
NSWindow.userTabbingPreference == .always so everyone else skips the
tab group materialization entirely.

Measured on macOS (Apple Silicon) during app launch via the startup
timeline instrumentation:

  windowDidLoad tab group check: 17.8ms -> ~0ms
  main() -> window visible: median ~173ms -> ~165ms (n=7)
This commit is contained in:
Mitchell Hashimoto
2026-08-09 17:07:04 -07:00
parent 4b1e02c7c3
commit da745630be

View File

@@ -1119,7 +1119,16 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
// We don't run this logic in fullscreen because in fullscreen this will end up
// removing the window and putting it into its own dedicated fullscreen, which is not
// the expected or desired behavior of anyone I've found.
if !window.styleMask.contains(.fullScreen) {
//
// We also only run this when the system tabbing preference is "always",
// which is the only scenario AppKit will have auto-tabbed a fresh window
// at this point: the tab bar "+" button goes through newWindowForTab
// which we route through our own tab logic. This check matters because
// accessing `window.tabGroup` materializes the window's tab group
// machinery, which takes ~15-20ms and is otherwise not needed during
// window creation.
if NSWindow.userTabbingPreference == .always,
!window.styleMask.contains(.fullScreen) {
// If we have more than 1 window in our tab group we know we're a new window.
// Since Ghostty manages tabbing manually this will never be more than one
// at this point in the AppKit lifecycle (we add to the group after this).