Add initialContentSize fallback on TerminalViewContainer so
intrinsicContentSize returns the correct value immediately,
without waiting for @FocusedValue to propagate. This removes
the need for the DispatchQueue.main.asyncAfter 40ms delay.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This fixes two overlapping issues regarding window positioning and Cmd+W window closures on macOS:
1. `window-position-x` and `window-position-y` coordinates were being ignored on initial launch because `TerminalWindow.setInitialWindowPosition` depended on the `TerminalController`, which isn't fully attached during `awakeFromNib`. This logic was moved so explicit coordinates are correctly enforced.
2. When closing a window via Cmd+W (leaving the app active), reopening the window would continuously cascade down and to the right rather than restoring to the previous position. It now checks if there are other windows open before cascading.
3. `LastWindowPosition` was updated to save both the frame origin and size (width/height), ensuring that restoring a closed window correctly mimics native AppKit State Restoration size behaviors while honoring explicit configurations.
Depends on #11030
- Update constraints of `TerminalGlassView`
- Use `TerminalViewContainer.DerivedConfig` to map styling properties
- Add TerminalViewContainerTests
- Instead of using delay, now the view updates are explicitly called by
window controllers
When window-width/height is configured, the window size is set via
setContentSize in windowDidLoad. However, window-position-x/y was not
being applied after this resize, causing the window to appear at an
incorrect position.
This was a regression introduced in c75bade89 which refactored the
default size logic from a computed NSRect property to a DefaultSize
enum. The original code called adjustForWindowPosition after calculating
the frame, but this was lost during the refactoring.
Fixes the issue by calling adjustForWindowPosition after applying
contentIntrinsicSize to ensure window position is correctly set.
### Background
After #9344, the Ghostty theme won't change after switching systems', and reverting #9344 will bring back the issue it fixed.
The reason these two issues are related is because the scheme change is based on changes of `effectiveAppearance`, which is also affected by setting the window's `appearance` or changing `NSAppearance.currentDrawing()`.
### Changes
Instead of observing `effectiveAppearance`, we now explicitly update the color scheme of surfaces, so that we can control when it happens to avoid callback loops and redundant updates.
### Regression Tests
- [x] #8282
- [x] Reloading with `window-theme = light` should update Ghostty with the default dark theme with a dark window theme (break before [#83104ff](83104ff27a))
- [x] `window-theme = light \n macos-titlebar-style = native` should update Ghostty with the default dark theme with a light window theme
- [x] Reloading from the default config to `theme=light:3024 Day,dark:3024 Night \n window-theme = light`, should update Ghostty with the theme `3024 Day` with a light window theme (break on [#d39cc6d](d39cc6d478))
- [x] Using `theme=light:3024 Day,dark:3024 Night`; Switching the system's appearance should change Ghostty's appearance (break on [#d39cc6d](d39cc6d478))
- [x] Reloading from `theme=light:3024 Day,dark:3024 Night` with a light window theme to the default config, should update Ghostty with the default dark theme with a dark window theme
- [x] Reloading from the default config to `theme=light:3024 Day,dark:3024 Night \n window-theme=dark`, should update Ghostty with the theme `3024 Night` with a dark window theme
- [x] Reloading from `theme=light:3024 Day,dark:3024 Night \n window-theme=dark` to `theme=light:3024 Day,dark:3024 Night` with light system appearance, should update Ghostty from dark to light
- [x] Reload with quick terminal open
# Conflicts:
# macos/Sources/Features/Terminal/BaseTerminalController.swift
Fixes#2660
Rather than calculate our window frame size based on various chrome
calculations, we now utilize SwiftUI layouts and view intrinsic content
sizes with `setContentSize` to setup our content size ignoring all our
other widgets.
I'm sure there's some edge cases I'm missing here but this should be a
whole lot more reliable on the whole.
Fixes#9132
We were processing our window size defaults separate from our window
position and the result was that you'd get some incorrect behavior.
Unify the logic more to fix the positioning.
Note there is room to improve this further, I think that all initial
positioning could go into the controller completely. But I wanted to
minimize the diff for a backport.
Fixes#8783
Our new tab flow will never have a previously focused window because its
triggered by a service so we need to use the "preferred parent" logic we
have to open this in the last focused window.