@@ -57,6 +57,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
5757 /// The notification cancellable for focused surface property changes.
5858 private var surfaceAppearanceCancellables : Set < AnyCancellable > = [ ]
5959
60+ /// The KVO cancellable for `window.contentView.intrinsicContentSize` changes.
61+ private var contentIntrinsicContentSizeCancellable : AnyCancellable ?
62+
6063 /// This will be set to the initial frame of the window from the xib on load.
6164 private var initialFrame : NSRect ?
6265
@@ -1038,9 +1041,10 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
10381041 }
10391042
10401043 // Initialize our content view to the SwiftUI root
1041- window . contentView = TerminalViewContainer {
1044+ let contentView = TerminalViewContainer {
10421045 TerminalView ( ghostty: ghostty, viewModel: self , delegate: self )
10431046 }
1047+ window. contentView = contentView
10441048
10451049 // If we have a default size, we want to apply it.
10461050 if let defaultSize {
@@ -1049,17 +1053,21 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
10491053 // Frames can be applied immediately
10501054 defaultSize. apply ( to: window)
10511055
1052- case . contentIntrinsicSize:
1053- // Content intrinsic size requires a short delay so that AppKit
1054- // can layout our SwiftUI views.
1055- DispatchQueue . main. asyncAfter ( deadline: . now( ) + . microseconds( 10_000 ) ) { [ weak self, weak window] in
1056- guard let self, let window else { return }
1057- defaultSize. apply ( to: window)
1058- if let screen = window. screen ?? NSScreen . main {
1059- let frame = self . adjustForWindowPosition ( frame: window. frame, on: screen)
1060- window. setFrameOrigin ( frame. origin)
1056+ case let . contentIntrinsicSize( minSize) :
1057+ // We wait for the first proper content intrinsic size,
1058+ // after AppKit laid out our SwiftUI views.
1059+ contentIntrinsicContentSizeCancellable = contentView. publisher ( for: \. intrinsicContentSize)
1060+ . filter { $0. width >= minSize. width && $0. height >= minSize. height }
1061+ . first ( )
1062+ . sink { [ weak self, weak window] _ in
1063+ guard let self, let window else { return }
1064+ defaultSize. apply ( to: window)
1065+ if let screen = window. screen ?? NSScreen . main {
1066+ let frame = self . adjustForWindowPosition ( frame: window. frame, on: screen)
1067+ window. setFrameOrigin ( frame. origin)
1068+ }
1069+ contentIntrinsicContentSizeCancellable = nil
10611070 }
1062- }
10631071 }
10641072 }
10651073
@@ -1560,6 +1568,21 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
15601568 self . windowPositionX = config. windowPositionX
15611569 self . windowPositionY = config. windowPositionY
15621570 }
1571+
1572+ /// The minimum size a window can be resized to as of macOS 26.3, it should cover most cases.
1573+ ///
1574+ /// We use this to filter incorrect intrinsicContentSize values when positioning a new window.
1575+ fileprivate var minimumWindowSize : CGSize {
1576+ switch macosTitlebarStyle {
1577+ case " native " , " transparent " :
1578+ CGSize ( width: 105 + 1 , height: 33 + 1 )
1579+ case " tabs " :
1580+ CGSize ( width: 90 + 1 , height: 48 + 1 )
1581+ // hidden or others
1582+ default :
1583+ CGSize ( width: 27 + 1 , height: 40 + 1 )
1584+ }
1585+ }
15631586 }
15641587}
15651588
@@ -1607,7 +1630,7 @@ extension TerminalController {
16071630 case frame( NSRect )
16081631
16091632 /// A content size, set with `window.setContentSize`
1610- case contentIntrinsicSize
1633+ case contentIntrinsicSize( _ minimum : CGSize )
16111634
16121635 func isChanged( for window: NSWindow ) -> Bool {
16131636 switch self {
@@ -1644,7 +1667,7 @@ extension TerminalController {
16441667 } else if focusedSurface? . initialSize != nil {
16451668 // Initial size as requested by the configuration (e.g. `window-width`)
16461669 // takes next priority.
1647- return . contentIntrinsicSize
1670+ return . contentIntrinsicSize( derivedConfig . minimumWindowSize )
16481671 } else if let initialFrame {
16491672 // The initial frame we had when we started otherwise.
16501673 return . frame( initialFrame)
0 commit comments