From da745630bed8689365be0ec9a0cfe283a2ed965d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 9 Aug 2026 17:07:04 -0700 Subject: [PATCH] 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) --- .../Features/Terminal/TerminalController.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 36b863fc0..587510f09 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -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).