There is a sparkle-related 'issue' with the previous implementation. When you download/install in the `updateAvailable` state, if you don't install it, then check the updates again. Sparkle loses its downloaded stage in the delegate (it's normal when I use the sparkle source code). This time, when you click install in the `updateAvailable` state, it just uses the previous downloaded package and starts to install, without calling `showReady(toInstallAndRelaunch:)`.
I think removing `readyToInstall` in our customed ui, will reduce one step to install an update for most of the users out there, which makes sense, since the current package is pretty small, only takes a few seconds to download for a normal network, and they intended to install this update.
Resolves Issue #8357
### Implementation
Following the existing `onResize` callback pattern in
`TerminalSplitTreeView`, I added an `onEqualize` callback to
`SplitView`. When a divider is double-tapped, the callback retrieves a
surface from that `TerminalView`'s `SplitTree` and calls `splitEqualize`
to equalize the entire tree.
### Context
There is an existing PR #8364 that implements this feature but uses
`focusedSurface`, which doesn't work for unfocused windows. Since that
PR has been inactive for a few months after requested changes, I've
implemented this alternative approach.
Credit to @liby for that initial implementation!
### AI Usage
I chatted with Claude Code in Plan mode to understand the relationship
between surfaces and the split tree/split views, but I wrote all the
code myself.
### Screenshot
https://github.com/user-attachments/assets/0efd70ef-c90e-4b50-b853-b05e2ca2be67
Partially fixes#8493.
After dictating some texts, the icon still appears above, but it will
return to its right position after resizing or `\n` (saying newline, not
hitting enter).
This behaviour is better than before, where the icon always appeared
above.
> Quicklook doesn't seem to call this on Tahoe, but it still works well
anyway.
### Reference:
9e905357bb/src/nsterm.m (L7426)https://github.com/user-attachments/assets/6bf818c3-a0bb-412f-ae06-673f67cdeae4
Partially fixes#8493.
After dictating some texts, the icon still appears above, but it will return to its right position after resizing or `\n` (saying newline, not hitting enter).
This behaviour is better than before, where the icon always appeared above.
### Reference:
9e905357bb/src/nsterm.m (L7426)
With its being `focusable`(default), the first responder became the text
editor instead of the paste button.
This fixes the issue where one can't confirm with the keyboard.
This doesn't affect its selection.
After `ghostty_app_update_config`, `ghostty_action_config_change_s` was
fired with the correct config. This happens synchronously, which will
update `App.config` in `App.configChange(_:target:v:)`.
Previously, after updating, `App.config` was set with the stale one,
which caused #8282.
Fixes#9322
SwiftUI `Text` has huge performance issues. On my maxed out MBP it hangs
for any text more than 100KB (it took ~8s to display it!). `TextEditor`
with a constant value works much better and handles scrolling for us,
too!
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.
NSScreen instances can be garbage collected at any time, even for
screens that remain connected, making NSMapTable with weak keys
unreliable for tracking per-screen state.
This changes the quick terminal to use CGDisplay UUIDs as stable
identifiers, keyed in a strong dictionary. Each entry stores the
window frame along with screen dimensions, scale factor, and last-seen
timestamp.
Rules for pruning:
- Entries are invalidated when screens shrink or change scale
- Entries persist and update when screens grow (allowing cached state
to work with larger resolutions)
- Stale entries for disconnected screens expire after 14 days.
- Maximum of 10 screen entries to prevent unbounded growth
When the preferred scrollbar style is "legacy", the scrollbar takes up
space that offsets the actual terminal. To prevent reflow, we detect
this before the scrollbar becomes visible and shrink our terminal width
to prepare for it.
This doesn't account for the style changing at runtime, yet.
This mainly allows users who have a pending update but didn't install it
for some time to re-check to see if there is something newer in the mean
time.
Most of the changes seem to be Tahoe UI related and now that we have a
custom UI I don't think there is anything important here but we should
update nonetheless.
This includes multiple changes to clean up the "installing" state:
- Ghostty will not confirm quit, since the user has already confirmed
they want to restart to install the update.
- If termination fails for any reason, the popover has a button to retry
restarting.
- The copy and badge symbol have been updated to better match the
reality of the "installing" state.
<img width="1756" height="890" alt="CleanShot 2025-10-12 at 15 04 08@2x"
src="https://github.com/user-attachments/assets/1b769518-e15f-4758-be3b-c45163fa2603"
/>
AI written:
https://ampcode.com/threads/T-623d1030-419f-413f-a285-e79c86a4246b fully
understood.
Resolves#8689
For various reason, ghostty wants to have a unique file extension for
the config files. The name was settled on `config.ghostty`. This will
help with tooling. See #8438 (original discussion) for more details.
This PR introduces the preferred default of `.ghostty` while still
supporting the previous `config` file. If both files exist, a warning
log is sent.
The docs / website will need to be updated to reflect this change.
> [!NOTE]
> Only tested on macOS 26.0.
---------
Co-authored-by: Mitchell Hashimoto <m@mitchellh.com>
### Background
Been running Ghostty locally for a while now, and I use the Finder
service a lot. It often confuses me which one is the official one, until
I actually open it.
### Changes
- Use blueprint to distinguish from release app, if no custom icon
specified
- Change BundleDisplayName to Ghostty[Debug]
- Enable Info.plist preprocessing for reading
`$(INFOPLIST_KEY_CFBundleDisplayName)` for providing different services
with different configurations
> (Preprocessing was once reverted
before](https://github.com/ghostty-org/ghostty/commit/6508fec), so I'm
not sure whether this follows the 'rules' here, but for now, there are
no links in the plist file, so I think it’s
[safe](https://developer.apple.com/library/archive/technotes/tn2175/_index.html#:~:text=can%20pass%20the-,%2Dtraditional,-flag%20to%20the)
to enable it
### This PR depends on #9162#1691 doesn't seem to be an issue anymore, but changing appearance while
Ghosty is active will result in similar nastiness.
https://github.com/user-attachments/assets/fcd7761e-a521-4382-8d7a-9d93dc0806bc
### Changes
- [Sequoia/Ventura] Fix flickering new tab icon, and it also didn't
respond to window's key status change correctly
- [Sequoia/Ventura] Fix after changing appearance, tab bar may disappear
or have inconsistent background colour
- Fix initial tint of reset zoom button on Sequoia/Ventura with
`macos-titlebar-style=tabs` and all `native/transparent` titlebars
- Fix title alignment with custom font with `native/transparent`
titlebar
If an update is available, you can now trigger the full download,
install, and restart from a single command palette action. This allows
for a fully keyboard-driven update process.
While an update is being installed, an option to cancel or skip the
current update is also shown as an option, so that can also be
keyboard-driven.
This currently can't be bound to a keyboard action, but that may be
added in the future if there's demand for it.
**AI Disclosure:** Amp was used considerably. I reviewed all the code
and understand it.
## Demo
https://github.com/user-attachments/assets/df6307f8-9967-40d4-9a62-04feddf00ac2
Closes#8791
Discussion: #8657
### Summary
This adds the FocusTerminalIntent App Intent and related function
(focusSurface), allows external tools (such as Shortcuts/Siri) to
programmatically move focus to a specific terminal window or tab.
### Verification
This functionality has been tested across following scenarios,
confirming correct focus behavior for:
- Split Window
- Tab Group
- Quick Terminal
### Note
It is not supported to move focus to a split that is hidden by a zoomed
split. The same applies to the CloseTerminalIntent.
### AI Disclosure
This pull request was made with assistance from Claude Code.
I reviewed all AI-generated code and wrote the final output manually.
Fixes#8713
This stores the last closed state of the quick terminal by screen
pointer. We use a weak mapping so if a screen is unplugged we'll clear
the memory. We will not remember the size if you unplug and replug in a
monitor.
Fixes#8734
This forces the app icon to be set on another event loop tick from
the main startup.
In the future, we should load and set the icon completely in another
thread. It appears that all the logic we have is totally thread-safe.
Fixes#8785
This is the callback AppKit sends when it wants to know if our
application can handle sending and receiving certain types of data.
The prior implementaiton was incorrect and would erroneously claim
support over combinations that we couldn't handle (at least, at the
SurfaceView layer).
This corrects the implementation. The services we expect still show up
and the error in 8785 goes away.
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.
Maybe fixes#8736
I thought `windowDidLoad` was early on because its before the window is
shown but apparently not. Let's try `awakeFromNib` which is called
just after the window is loaded from the nib. It is hard to get any
earlier than that.
Config contains the command, working directory, and environment
variables intended to be passed to the new split, but it looks like we
forgot to include it as an argument in this branch.
Discussion: https://github.com/ghostty-org/ghostty/discussions/8637
Fixes#8616
macOS 26 (as of RC1) has some pathological performance bug where the
terminal becomes unusably slow after some period of time. We aren't 100%
sure what triggers the slowdown, but it is app-wide (new tabs or windows
don't resolve it) and Instruments traces point directly to
NSAutoFillHeuristicController. Specifically, to the `debounceTextUpdate`
selector.
This is all not documented as far as I can find and also not open
source, so I have no idea what's going on.
The best I can tell is that the NSAutoFillHeuristicController has
something to do with enabling heuristic-based autofill such as SMS auth
codes in text input fields. I don't know what is causing it to go
haywire.
SMS autofill is not desirable in a terminal app, nor is any of the other
automatic autofill in macOS I know of (contact info, passwords, etc.).
So, we can just disable it.
This default isn't documented but I found it via a strings dump of the
AppKit binary blob and comparing it to the disassembly to see how it is
used. In my limited testing, this seems to work around the problem.
This fixes an issue where new tabs would not have the proper transparent
background set whilst in native fullscreen. This is because in native
fullscreen, the NSTitlebarView always is visible, so our guard was
preventing us from setting it before.
This is a hacky fix to fix some visual glitches when titlebar tabs is on
and we're using the `move_tab` keybinding action (I test via the command
palette).
There is probably a more graceful way to fix this but this might be good
enough for a 1.2 to fix a very obviously nasty UI render.
Fixes#8497
This works on every other supported version of macOS but doesn't work on
macOS tahoe. Putting it on the next event loop tick works at least on
Sequoia and Tahoe so let's just do that.
This fixes an issue I noticed where manually launching the `ghostty`
binary in the app bundle via the CLI would open the app but not create a
window or bring it to the front.
When using hidden titlebar with non-native fullscreen, the window would
lose focus after entering the first command. This occurred because:
1. Shell commands update the window title
2. Title changes trigger reapplyHiddenStyle()
3. reapplyHiddenStyle() re-adds .titled to the style mask
4. Style mask changes during fullscreen confuse AppKit, causing focus loss
Fixed by adding a guard to skip titlebar restyling while fullscreen is
active, using terminalController.fullscreenStyle.isFullscreen for
proper detection of both native and non-native fullscreen modes.
https://ampcode.com/threads/T-c4ef59cc-1232-4fa5-8f09-c65724ee84d3
This has no meaningful functionality yet, it was one of the paths I was
looking at for #8505 but didn't pursue further. But I still think that
this makes more sense in general for the macOS app and will likely be
more useful later.
Fixes#8356
Zoom state should encode as a path so that it can be mapped to a
reference to the node in `root`. Previously, we were encoding a full
node which was instantiating an extra terminal on restore.
Fixes#2473
This commit changes `ghostty_surface_ime_point` to return a full rect
with the width/height calculated for the preedit.
The `firstRect` function, which calls `ghostty_surface_ime_point` was
previously setting the width/height to zero. macOS didn't like this. We
then changed it to just hardcode it to width/height of one cell. This
worked but made it so the IME cursor didn't follow the preedit.
Fixes#8418
This fixes issues where left/right positions would be cut off from the
menu bar. And makes it so that size 100%,100% doesn't overflow into the
non-visible space of the edge of the screen.