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.
You can now resize the quick terminal both vertically and horizontally. To incorporate adjusting the custom secondary size on the quick terminal we needed to have the ability to resize the width (if from top, bottom, or center), and height (if from right, left, or center). The quick terminal will retain the user's manually adjusted size while the app is open. A new feature with this is that when the secondary size is adjusted (or primary if the quick terminal is center), the size will increase or decrease on both sides of the terminal.
Applying the feedback given by @pluiedev to use an enum to specify the
type of quick terminal size configuration given (pixels or percentage).
Updated the Swift code to work with the enum as well.
Added C bindings for the already existing quick-terminal-size
configuration. Created a new QuickTerminalSize struct to hold these
values in Swift. Updated the QuickTerminal implementation to use the
user's configuration if supplied. Retains defaults. Also adds support to
customize the width of the quick terminal (height if quick terminal is
set to right or left).
Supporting command line, file menu and keybindings.
Default mac shortcut of `super + alt + o` (other)
Not able to test on Linux so excluding `close_other_tabs` from `gtk` for now
make a default short cut for close other tabs
Fixes#8229
This was a regression.
The discussion noted in #8229 requests we create a new window on the
non-fullscreen desktop but that isn't how Ghostty has behaved
historically. I bisected back and tried 1.1.3 as well and we always
created a new fullscreen window when the parent was fullscreen.
This behavior matches iTerm2. Its noteworthy that native tabbing and
Apple apps such as Terminal.app and Safari do NOT do this. For both of
these, new window creates a _tab_ when in fullscreen. I don't think
that's particularly desirable, though.
The Quick Terminal would not appear anymore, as somewhere
in the framework the Quick Terminal Window's geometry
gets corrupted when the window is added to the UI.
This works around by caching the windows geometry and
reusing it afterwards
This change makes sure that the new window is focused and visible.
When commit 33d128bcff removed the
TerminalManager class and moved its functionality into
TerminalController, it accidentally removed app activation for windows
triggered by global keybinds.
How the bug works:
1. Menu actions (like File → New Window) call AppDelegate.newWindow()
which:
2. Calls TerminalController.newWindow()
3. AND explicitly calls NSApp.activate(ignoringOtherApps: true) in
the AppDelegate
4. Global keybind actions trigger ghosttyNewWindow() notification
handler which:
5. Only calls TerminalController.newWindow()
6. Does NOT call NSApp.activate(ignoringOtherApps: true)
7. While TerminalController.newWindow() does call
NSApp.activate(ignoringOtherApps: true) internally, this call
happens before the async dispatch that shows the window, so the
activation occurs but the window isn't focused when it's actually
shown.
8. In the old TerminalManager.newWindow(), the activation happened
immediately before the async dispatch, ensuring proper timing for
window focus.
The fix would be to either move the NSApp.activate() call back into
TerminalController.newWindow(), as it was for TerminalManager, or add
the activation call to the notification handlers in AppDelegate.
This adds a new configuration option that controls whether folders
dropped onto the Ghostty dock icon open in a new tab (default) or
a new window.
The option accepts two values:
- tab: Opens folders in a new tab in the main window (default)
- window: Opens folders in a new window
This is useful for users who prefer window-based workflows over
tab-based workflows when opening folders via drag and drop.
SwiftUI's ImageRenderer must not be called outside the main thread.
The `@MainActor` annotation is only relevant for our own code, not
for calls from frameworks. The machinations around Shortcuts end up
calling the displayRepresentation method outside the main thread.
By capturing the screenshot as NSImage, all data is retained and can
be processed outside the main thread.
Add serialization for tab titles in SurfaceView to persist user-set titles across app restarts. Bump TerminalRestorableState version to 4 to handle the new format.
Fixes#7941
I don't fully understand the fix here. Its code we've had for awhile it
was just in the wrong place after I refactored our window management.
The comment clearly states why its there but I don't know why it is
required.
Addresses #7649 for the core and GTK. macOS support will need to be
added later.
This adds an apprt action to show a native GUI warning of some kind when
the child process of a terminal exits.
Also adds a basic GTK implementation of this. In GTK it overlays an
Adwaita banner at the bottom of the window (similar to the banner that
shows up in at the top of windows in debug builds).
Related to #7879
This commit updates `zig build test` to run Xcode tests, too. These run
in parallel to the Zig tests, so they don't add any time to the test.
The Xcode tests will _not_ run when: (1) the target is not macOS, or (2)
the `-Dtest-filter` option is non-empty. This makes it so that this
change doesn't affect non-macOS and doesn't affect the general dev cycle
because you usually will run `-Dtest-filter` when developing a core
feature.
I didn't add a step to only run Xcode tests because I find that when I'm
working in Xcode I'm probably going to run the tests from there anyways.
The integration with `zig build test` is just a convenience, especially
around CI.
Speaking of CI, this change also makes it so this will run in CI.
Fixes#5256
This updates the macOS apprt to implement the `OPEN_URL` apprt action to
use the NSWorkspace APIs instead of the `open` command line utility.
As part of this, we removed the `ghostty_config_open` libghostty API and
instead introduced a new `ghostty_config_open_path` API that returns the
path to open, and then we use the `NSWorkspace` APIs to open it (same
function as the `OPEN_URL` action).
This makes it so `zig build run` can take arguments such as
`--config-default-files=false` or any other configuration. Previously,
it only accepted commands such as `+version`.
Incidentally, this also makes it so that the app in general can now take
configuration arguments via the CLI if it is launched as a new instance
via `open`. For example:
open -n Ghostty.app --args --config-default-files=false
This previously didn't work. This is kind of cool.
To make this work, the libghostty C API was modified so that
initialization requires the CLI args, and there is a new C API to try to
execute an action if it was set.
`zig build run` on macOS now builds the app bundle via the `xcodebuild`
CLI and runs it. The experience for running the app is now very similar
to Linux or the prior GLFW build, where the app runs, blocks the zig
command, and logs to the terminal.
`xcodebuild` has its own build cache system that we can't really hook
into so it runs on every `zig build run` command, but it does cache and
I find its actually relatively fast so I think this is a good
replacement for the glfw-based system.
This fixes an Apple Shortcuts crash for macOS 15 and earlier.
Unfortunately it looks like we can't guard these with `@available`. I'm
going to report an Apple Feedback about this but for now this gets
shortcuts working on macOS 15 and earlier.
This is done at the apprt-level for a couple reasons.
(1) For libghostty, we don't have a way to know what the embedding
application is doing, so its risky to create signal handlers that
might overwrite the application's signal handlers.
(2) It's extremely messy to deal with signals and multi-threading.
Apprts have framework access that handles this for us.
For GTK, we use g_unix_signal_add.
For macOS, we use `DispatchSource.makeSignalSource`. This is an awkward
API but made for this purpose.
This changes equalization so it only counts children oriented in the
same direction.
This makes splits a bit more aesthetic, and replicates how split
equalization works in neovim.
Fixes#7647
See #7647 for context. This commit works by extending the `input` work
introduced in #7652 to libghostty so that the macOS can take advantage
of it. At that point, its just the macOS utilizing `input` in order to
set the command and `exit` up similar to Terminal and iTerm2.
This regression may have existed on Sequoia too, but I only saw it on
Tahoe.
The issue is that we should not be setting up titlebar accessory views
when there is no titlebar. This was triggering an AppKit assertion.
To further simplify things, I always use the basic window styling if
window decorations are off, too.
It's here, the long-foretold and long-procrastinated renderer rework!
Hopefully this makes it easier to adapt and modify the renderer in the
future and ensures feature parity between Metal and OpenGL. Despite
having been a lot of work to write initially, with the abstraction layer
in place I feel like working on the renderer will be a much more
pleasant experience going forward.
## Key points
- CPU-side renderer logic is now mostly unified via a generic
`Renderer`.
- A graphics API abstraction layer over OpenGL and Metal has been
introduced.
- Minimum OpenGL version bumped to `4.3`, so can no longer be run on
macOS; I used the nix VM stuff for my testing during development. (Edit
by @mitchellh: Note for readers that Ghostty still works on macOS, but
the OpenGL backend doesn't, only the Metal one)
- The OpenGL backend now supports linear blending! Woohoo! The default
`alpha-blending` has been updated to `linear-corrected` since it's
essentially a strict improvement over `native`. The default on macOS is
still `native` though to match other mac apps in appearance, since macOS
users are more sensitive to text appearance.
- Custom shaders can now be hot reloaded.
- The background color is once again drawn by us, so custom shaders can
interact with it properly. In general, custom shaders should be a little
more robust.
## The abstraction layer
The general hierarchy of the abstraction layer is as such:
```
[ GraphicsAPI ] - Responsible for configuring the runtime surface
| | and providing render `Target`s that draw to it,
| | as well as `Frame`s and `Pipeline`s.
| V
| [ Target ] - Represents an abstract target for rendering, which
| could be a surface directly but is also used as an
| abstraction for off-screen frame buffers.
V
[ Frame ] - Represents the context for drawing a given frame,
| provides `RenderPass`es for issuing draw commands
| to, and reports the frame health when complete.
V
[ RenderPass ] - Represents a render pass in a frame, consisting of
: one or more `Step`s applied to the same target(s),
[ Step ] - - - - each describing the input buffers and textures and
: the vertex/fragment functions and geometry to use.
:_ _ _ _ _ _ _ _ _ _/
v
[ Pipeline ] - Describes a vertex and fragment function to be used
for a `Step`; the `GraphicsAPI` is responsible for
these and they should be constructed and cached
ahead of time.
[ Buffer ] - An abstraction over a GPU buffer.
[ Texture ] - An abstraction over a GPU texture.
```
More specific documentation can be found on the relevant structures.
## Miscellany
Renderers (which effectively just means the generic renderer) are now
expected to only touch GPU resources in `init`, certain lifecycle
functions such as the `displayRealized`/`displayUnrealized` callbacks
from GTK-- and `drawFrame`; and are also expected to be thread-safe.
This allows the renderer thread to build the CPU-side buffers
(`updateFrame`) even if we can only *draw* from the app thread.
Because of this change, we can draw synchronously from the main thread
on macOS when necessary to always have a frame of the correct size
during a resize animation. This was necessary to allow the background to
be drawn by our GPU code (instead of setting a background color on the
layer) while still avoiding holes during resize.
The OpenGL backend now theoretically has access to multi-buffering, but
it's disabled (by setting the buffer count to 1) because it
synchronously waits for frames to complete anyway which means that the
extra buffers were just a waste of memory.
## Validation
To validate that there are no significant or obvious problems, I
exercised both backends with a variety of configurations, and visually
inspected the results. Everything looks to be in order.
The images are available in a gist here:
https://gist.github.com/qwerasd205/c1bd3e4c694d888e41612e53c0560179
## Memory
Here's a comparison of memory usage for ReleaseFast builds on macOS,
between `main` and this branch.
Memory figures given are values from Activity Monitor measuring windows
of the same size, with two tabs with 3 splits each.
||Before|After|
|-:|-|-|
|**Memory**|247.9 MB|224.2 MB|
|**Real Memory**|174.4 MB|172.5 MB|
Happily, the rework has slightly *reduced* the memory footprint- likely
due to removing the overhead of `CAMetalLayer`. (The footprint could be
reduced much further if we got rid of multi-buffering and satisfied
ourselves with blocking for each frame, but that's a discussion for
another day.)
If someone could do a similar comparison for Linux, that'd be much
appreciated!
## Notes / future work
- There are a couple structures that *can* be unified using the
abstraction layer, but I haven't gotten around to unifying yet.
Specifically, in `renderer/(opengl|metal)/`, there's `cell.zig` and
`image.zig`, both of which are substantially identical between the two
backends. `shaders.zig` may also be a candidate for unification, but
that might be *overly* DRY.
- ~~I did not double-check the documentation for config options, which
may mention whether certain options can be hot-reloaded; if it does then
that will need to be updated.~~ Fixed: be5908f
- The `fragCoord` for custom shaders originates at the top left for
Metal, but *bottom* left for OpenGL; fixing this will be a bit annoying,
since the screen texture is likewise vertically flipped between the two.
Some shaders rely on the fragcoord for things like falling particles, so
this does need to be fixed.
- `Target` should be improved to support multiple types of targets right
now it only represents a framebuffer or iosurface, but it should also be
able to represent a texture; right now a kind of messy tagged union is
used so that steps can accept both.
- Custom shader cursor uniforms (#6912) and terminal background images
(#4226, #5233) should be much more straightforward to implement on top
of this rework, and I plan to make follow-up PRs for them once this is
merged.
- I *do* want to do a rework of the pipelines themselves, since the way
we're rendering stuff is a bit messy currently, but this is already a
huge enough PR as it is- so for now the renderer still uses the same
rendering passes that Metal did before.
- We should probably add a system requirements section to the README
where we can note the minimum required OpenGL version of `4.3`, any even
slightly modern Linux system will support this, but it would be good to
document it somewhere user-facing anyway.
# TODO BEFORE MERGE
- [x] Have multiple people test this on both macOS and linux.
- [ ] ~~Have someone with a better dev setup on linux check for memory
leaks and other problems.~~ (Skipped, will merge and let tip users
figure this out, someone should *specifically* look for memory leaks
before the next versioned release though.)
- [x] Address any code review feedback.
This updates the Ghostty icon to be compatible with macOS Tahoe
(supports glass effects, light/dark, tinting, etc.). This icon is made
in the new Apple Icon Composer as the source format, and all other
formats are exported from it.
This commit also updates the icon for non-Apple platforms because the
icon is fundamentally the same and I don't see any reason to maintain
multiple icons of fundamentally the same design and style.
This commit also includes updates to the macOS app so that the About
Window and so on will use the new icon.
This commit is very large, representing about a month of work with many
interdependent changes that don't separate cleanly in to atomic commits.
The main change here is unifying the renderer logic to a single generic
renderer, implemented on top of an abstraction layer over OpenGL/Metal.
I'll write a more complete summary of the changes in the description of
the PR.
This fixes an issue where pressing the red close button in a window or
the "x" button on a tab couldn't differentiate and would always close
the tab or close the window (depending on tab counts).
It seems like in both cases, AppKit triggers the `windowShouldClose`
delegate method on the controller, but for the close window case it
triggers this on ALL the windows in the group, not just the one that was
clicked.
I implemented a kind of silly coordinator that debounces
`windowShouldClose` calls over 100ms and uses that to differentiate
between the two cases.
This fixes an issue where pressing the red close button in a window or
the "x" button on a tab couldn't differentiate and would always close
the tab or close the window (depending on tab counts).
It seems like in both cases, AppKit triggers the `windowShouldClose`
delegate method on the controller, but for the close window case it
triggers this on ALL the windows in the group, not just the one
that was clicked.
I implemented a kind of silly coordinator that debounces
`windowShouldClose` calls over 100ms and uses that to differentiate
between the two cases.
Resolves#7591
This moves our CI to build macOS on Sequoia (macOS 15) with Xcode 26,
including the new macOS 26 beta SDK.
Importantly, this will make our builds on macOS 26 use the new styling.
I've added a new job that ensures we can continue to build with Xcode 16 and
the macOS 15 SDK, as well, although I think that might come to an end
when we switch over to an IconComposer-based icon. I'll verify then. For
now, we continue to support both.
I've also removed our `hasLiquidGlass` check, since this will now always
be true for macOS 26 builds.
This integrates with macOS accessibility APIs to expose Ghostty terminal
structure and content.
This is a very, very bare implementation and the terminal contents
currently reported are the _full screen and scrollback_ which is way too
much for realistic human accessibility use. The target use case for this
PR is to enable automated tooling (namely, AI screen readers). However,
this is all groundwork we'll need to iterate and improve the
accessibility work anyways.
To make this work, I also replatformed some of our hacky C APIs onto a
more robust `ghostty_surface_read_text` API that can now read arbitrary
ranges of the screen into C strings for consumers to use. This will be
useful in more places going forward (hint hint).
## Before
Accessibility tooling can't read anything, Ghostty has no attributes, no
contents, just shows up as a square.

## After
A lot of metadata, including the screen contents as text.

Also, split hierarchies are navigable:
https://github.com/user-attachments/assets/a7b2ffb7-dbeb-41b2-8705-9c3200812c4d
This fixes a regression from our Tahoe window styling changes on
earlier, stable versions of macOS. We need to set
"titlebarAppearsTransparent" to true in order to hide the bottom
border.
This is recommended for macOS Tahoe and all standard menu items now have
associated images. This makes our app look more polished and native for
macOS Tahoe.
On earlier versions of macOS (macOS 15 and earlier), we _do not_ set the
menu item image. Cocoa has supported menu item images for a long time
but it isn't idiomatic to show them in earlier versions, so we only do
this for later macOS versions.
For icon choice, I tried to copy other native macOS apps as much as
possible, mostly from Xcode. It looks like a lot of apps aren't updated
yet. I'm absolutely open to suggestions for better icons but I think
these are a good starting point.
One menu change is I moved "reset font size" above "increase font size"
which better matches other apps (e.g. Terminal.app).
https://github.com/user-attachments/assets/50a68326-221f-454f-9a9c-078878010a63
This is recommended for macOS Tahoe and all standard menu items now have
associated images. This makes our app look more polished and native for
macOS Tahoe.
For icon choice, I tried to copy other native macOS apps as much as
possible, mostly from Xcode. It looks like a lot of apps aren't updated
yet. I'm absolutely open to suggestions for better icons but I think
these are a good starting point.
One menu change is I moved "reset font size" above "increase font size"
which better matches other apps (e.g. Terminal.app).