Commit Graph

1483 Commits (main)

Author SHA1 Message Date
Mitchell Hashimoto 77114d7927
macos: avoid any zero-sized content size increments
Fixes #9016
2025-10-03 13:35:40 -07:00
Andreas Deininger a667b740ee Fix typos 2025-10-03 18:52:26 +02:00
Mitchell Hashimoto 0cc3728803
macOS: Focus Terminal App Intent (#8961)
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.
2025-09-30 06:55:49 -07:00
himura467 c58a8b27b6 chore: update iOS membership exceptions 2025-09-30 07:14:09 +09:00
himura467 373be61482 docs 2025-09-30 06:36:29 +09:00
himura467 b3d0b6a965 refactor: no need to set from for moveFocus probably 2025-09-30 05:58:21 +09:00
himura467 bc3d0b7cbc fix: the renderer's cursor remains in an unfocused state (block_hollow) 2025-09-30 05:21:56 +09:00
Mitchell Hashimoto 958751e12c
Workaround for #8669 (#8838)
Changing `supportedModes` to `background` seems to have fixed #8669.

> Debugging AppIntents with Xcode is a pain. I had to delete all the
local builds to make it take effect. The build product of `zig` might
cause confusion if none of your changes reflect in the Shortcuts app.
There were too many ghosts on my computer. 👻👻👻

- Tahoe


https://github.com/user-attachments/assets/88d0d567-edf5-4a7e-b0a3-720e50053746

- Sequoia 


https://github.com/user-attachments/assets/a77f1431-ca92-4450-bce9-5f37ef232d4f
2025-09-29 09:30:01 -07:00
himura467 a6dd7bbeee refactor: improve asynchronous delay by delegating window/app activation process to animateIn 2025-09-29 02:02:42 +09:00
himura467 7ab0a7814b docs(BaseTerminalController) 2025-09-29 01:56:00 +09:00
himura467 337ecdd0b3 refactor(focusSurface): check app status in advance 2025-09-29 00:02:43 +09:00
himura467 8151f4bbf5 feat: focusSurface for quick terminal 2025-09-29 00:00:41 +09:00
himura467 9d33545a55 feat: focus terminal in basic cases 2025-09-28 19:20:00 +09:00
Lars 9072bab6b8
Update iOS membership exceptions 2025-09-23 16:50:13 +02:00
Lars 131f4fb8b1
Convert `Sources` to folder references 2025-09-23 09:30:49 +02:00
Lars 8beeebc21d
Force Ghostty to be active if not 2025-09-22 15:27:27 +02:00
Lars 8600954526
Workaround for #8669 2025-09-22 14:52:10 +02:00
Mitchell Hashimoto 4d761c96e5
macos: quick terminal stores the last closed size by screen
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.
2025-09-19 13:01:29 -07:00
Mitchell Hashimoto f60bdb0faa
macos: set the app icon in syncAppearance to delay the icon update
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.
2025-09-19 12:07:28 -07:00
Mitchell Hashimoto d27bc69f2e
macos: correct SurfaceView supported send/receive types for services
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.
2025-09-19 11:44:46 -07:00
Mitchell Hashimoto 6143aa8ce0
macos: "new tab" service should set preferred parent to ensure tab
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.
2025-09-19 09:58:27 -07:00
Mitchell Hashimoto fe55d90ec5
macos: implement bell-features=border on macOS 2025-09-18 14:43:09 -07:00
Mitchell Hashimoto ad92bf7ab5
macos: bell-features=title works again
This was a regression we didn't fix before 1.2.
2025-09-18 14:02:32 -07:00
Mitchell Hashimoto 4fa8b8f285
macos: opening filepaths should make proper file URLs
Fixes #8763
2025-09-18 13:28:27 -07:00
Mitchell Hashimoto 734d1a13b3
macos: set initial window in TerminalWindow awakeFromNib
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.
2025-09-18 13:05:57 -07:00
Mitchell Hashimoto 773990ada3
macos: window-position-x/y are from top-left corner
Fixes #8672

Almost fully written by AI: https://ampcode.com/threads/T-86df68a3-578c-4a1c-91f3-788f8b8f0aae

I reviewed all the code.
2025-09-18 12:18:13 -07:00
Mitchell Hashimoto 354b62d5ce
macos: add progress bar to iOS target 2025-09-18 10:48:29 -07:00
Mitchell Hashimoto 058d6808c1
macos: custom progress bar to workaround macOS 26 ProgressView bugs
Fixes #8731

The progress view in macOS 26 is broken in ways we can't work around
directly. Instead, we must create our own custom progress bar. Luckily,
our usage of the progress view is very simple.

Amp threads:
https://ampcode.com/threads/T-88b550b7-5e0d-4ab9-97d9-36fb63d18f21
https://ampcode.com/threads/T-721d6085-21d5-497d-b6ac-9f203aae0b94
2025-09-18 10:22:54 -07:00
Caleb Hearth ac9f3b88aa
Pass config to splits in NewTerminalConfig
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
2025-09-15 13:16:18 -06:00
Mitchell Hashimoto 431364cf16
macos: disable NSAutoFillHeuristicController on macOS 26
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.
2025-09-14 13:05:01 -07:00
Mitchell Hashimoto d813d82da1
macOS 26: Always set titlebarview background color for transparent title
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.
2025-09-12 20:41:02 -07:00
Mitchell Hashimoto eaaf5aa8cf
macos: always reset titlebar tab constraints on frame change
Fixes #8595

Whenever the titlebar frame changes, we should set up our constraints
again to force it to re-render properly.
2025-09-12 20:14:31 -07:00
Mitchell Hashimoto 09246780bb
macos 26: fix visual glitches with moving tabs and titlebar tabs
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.
2025-09-11 14:51:01 -07:00
Mitchell Hashimoto 22ec755e75
macos: run change title dialog in a sheet modal
This fixes a macOS 26 issue where the OK button would not be visible. 

This MUST be an AppKit bug, but I'm trying to find workarounds.
2025-09-11 14:40:25 -07:00
Mitchell Hashimoto c9574ed0c7
macOS: grab text field focus of command palette after tick
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.
2025-09-11 14:14:14 -07:00
Mitchell Hashimoto 83024e08bc
macos: update Sparkle to 2.7.3
Fixes some critical issues.
2025-09-10 20:04:24 -07:00
Mitchell Hashimoto 6324f2b3d8
macOS: ghostty launched via CLI should come to front
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.
2025-09-06 07:16:51 -07:00
Mitchell Hashimoto c8243ffd99
macOS: prevent focus loss in hidden titlebar + non-native fullscreen
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
2025-09-03 10:01:28 -07:00
Mitchell Hashimoto fe3dab9467
macOS: SurfaceView should implement Identifiable
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.
2025-09-03 09:17:37 -07:00
Mitchell Hashimoto 508e36bc03
macOS: split tree zoom state should encode as path, not full node
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.
2025-09-03 08:51:37 -07:00
Mitchell Hashimoto e8217aa007
macOS: firstRect should return full rect width/height
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.
2025-09-02 13:08:46 -07:00
Mitchell Hashimoto 2bf0d3f4c7
macOS: Notify macOS of cell width/height for firstRect
Related to #2473

This fixes an issue where the dictation icon didn't show the language 
picker.
2025-09-02 12:26:52 -07:00
Mitchell Hashimoto 650028fa9f
config: bind both physical digit plus unicode digit for `goto_tab`
Fixes #8478

The comments explain this.
2025-09-02 09:00:58 -07:00
Mitchell Hashimoto 0b58830882
macOS: Progress bar for OSC9 progress reports 2025-08-31 20:42:34 -07:00
Mitchell Hashimoto 04956f3dc1
macos: require confirmation to run any script 2025-08-28 12:34:04 -07:00
Mitchell Hashimoto f1ea30dcf1
macos: when executing a script directly, always wait after command 2025-08-28 11:26:33 -07:00
Mitchell Hashimoto 19a27383f8
macos: use visible frame for quick terminal sizing calculation
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.
2025-08-27 06:59:02 -07:00
Mitchell Hashimoto 520eaec61c
macos: fix quick terminal issue where closing while fullscreen 2025-08-26 10:40:41 -07:00
Alexander Lais e676eae640 macos: fix quick terminal fullscreen
Fullscreen on quick terminal was failing with a crash, when it tried
to save the state of a non-existent toolbar and its accessory view
controllers.
2025-08-26 10:36:06 -07:00
Mitchell Hashimoto ae48f323d7
macos: style changes for quick terminal sizing 2025-08-26 10:20:16 -07:00
Mitchell Hashimoto a90bf58080
config: change quick terminal size C layout to tagged union 2025-08-26 09:52:26 -07:00
Friedrich Stoltzfus 466fdfffe6 macOS: rename c struct, relocate QuickTerminalSize file
Renamed the ghostty_quick_terminal_size_u to
ghostty_quick_terminal_size_s and moved the QuickTerminalSize file to
the Ghostty folder as requested.
2025-08-26 09:47:31 -07:00
Friedrich Stoltzfus 58e7400ea5 macOS: Round quick terminal window position coordinates
This resolves an issue where the right side of the quick terminal would
not resize equally to the left side if adjusting the width from the left
side.
2025-08-26 09:47:31 -07:00
Friedrich Stoltzfus e5ad6603f4 Merge branch 'main' into quick-term-initial-size 2025-08-26 09:47:31 -07:00
Friedrich Stoltzfus 7cc0728fe5 macOS: enable quick terminal manual resizing
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.
2025-08-26 09:47:31 -07:00
Friedrich Stoltzfus 17f7f204e1 macOS: update zig and c structs for quick terminal size
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.
2025-08-26 09:47:31 -07:00
Friedrich Stoltzfus 63cd424678 macOS: Add support for quick terminal sizing configuration
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).
2025-08-26 09:47:31 -07:00
Jeffrey C. Ollie 52a25e9c69
parameterize close_tab
- Add mode (`this`/`other`) parameter to `close_tab` keybind/apprt action.
- Keybinds will default to `this` if not specified, eliminating backward
  compatibility issues (`keybind=x=close_tab` === `keybind=x=close_tab:this`).
- Remove `close_other_tabs` keybind and apprt action.
2025-08-25 11:00:26 -05:00
jamylak c26323d697 Close other tabs feature on Mac.
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
2025-08-24 07:55:08 -07:00
Mitchell Hashimoto 54f8dff308
macos: if parent window is fullscreen, new window is fullscreen too
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.
2025-08-22 14:31:50 -07:00
Tobias Pape 4fdf0b687e typo found typos, typo may keep them 2025-08-22 09:19:25 -07:00
Tobias Pape 85cba70c2e Work around strange SwiftUI behavior in "older" macOSen.
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
2025-08-22 09:17:49 -07:00
Moeeze Hassan f4009721a1 macOS: order out alert sheets to avoid Stage Manager focus loss
Signed-off-by: Moeeze Hassan <fammas.maz@gmail.com>
2025-08-22 09:05:20 -07:00
Daniel Wennberg 7d60c7c75b macos: move activation to after new window/tab is created 2025-08-21 15:16:20 -07:00
Mitchell Hashimoto 056ccc9818
macOS: update sparkle (#8337)
There aren't any noteworthy changes here we're just using a very old
version. Additionally, our CI was using... different versions!
2025-08-21 14:56:50 -07:00
Mitchell Hashimoto 1ce56a12fa
update sparkle
There aren't any noteworthy changes here we're just using a very old
version. Additionally, our CI was using... different versions!
2025-08-21 14:08:32 -07:00
Aljoscha Krettek f736ee8865 macos: in new_window action, activate App
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.
2025-08-21 14:03:00 -07:00
Mitchell Hashimoto e01ff4093a
macos: have macos-dock-drop-behavior apply to all drops 2025-08-21 10:45:17 -07:00
David Keegan f9ad061ea8 Add macos-dock-drop-folder-behavior configuration option
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.
2025-08-21 10:30:26 -07:00
Mitchell Hashimoto f178f4419e
macos: fix iOS builds 2025-08-21 10:03:25 -07:00
Nicholas Mata f1c68f698b Correct Swift formatting inconsistencies 2025-08-21 10:00:15 -07:00
Nicholas Mata 5948bd3f02 Add support for 'custom' on 'macos_icon' to support a completely custom app icon 2025-08-21 10:00:15 -07:00
Nicholas Mata 29419e7aac Make macos icon persistent even when app is closed 2025-08-21 10:00:15 -07:00
Mitchell Hashimoto 63ca777e0f
macos: show the copy menu item if we have any text selected 2025-08-19 14:23:50 -07:00
Alexander Lais 1f378e6775
fix: capture screenshot for app intents views as NSImage
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.
2025-08-08 16:53:26 +02:00
Aaron Ruan 1f710768e9
Update Float on Top menu icon to square.filled.on.square 2025-07-28 09:26:54 +08:00
Weizhao Ouyang b7f6da7857 macos: add more Tahoe menu item icons from SF Symbols
Signed-off-by: Weizhao Ouyang <o451686892@gmail.com>
2025-07-25 22:14:13 +08:00
Bryan Lee 852eb2e0d5
Fix tab titles not being preserved with window-save-state
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.
2025-07-23 23:21:44 +08:00
William Walker 7962651dd8
fix: rename tab title popup focus 2025-07-14 17:23:59 -04:00
Mitchell Hashimoto 02b08e0ec9
macos: restore tabs correctly into a single window
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.
2025-07-14 11:22:38 -07:00
Jeffrey C. Ollie cd9174e7e8
show child exited: fix macos build 2025-07-11 22:56:20 -05:00
Jeffrey C. Ollie 033d8c3099
core/gtk: add apprt action to show native GUI warning when child exits
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).
2025-07-11 22:56:20 -05:00
Mitchell Hashimoto 9fa26387ef
build: `zig build test` runs Xcode tests on macOS
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.
2025-07-10 21:08:51 -07:00
Mitchell Hashimoto c990d35d6d
macos: add benchmark tests to our Xcode project 2025-07-09 15:06:24 -07:00
Mitchell Hashimoto b7ffbf933f
macos: open URLs with NSWorkspace APIs instead of `open`
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).
2025-07-06 21:01:01 -07:00
Mitchell Hashimoto 984d123fe4
macos: support configuration via CLI arguments
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.
2025-07-05 21:31:23 -07:00
Mitchell Hashimoto 8c3caee15c
macOS: zig build run disables window saved state 2025-07-05 19:58:17 -07:00
Mitchell Hashimoto 7bd90e6ec4
Build system can build macOS app bundle and open it
`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.
2025-07-04 13:32:15 -07:00
Mitchell Hashimoto d8838cff0b
macOS: remove @DeferredProperty usage from TerminalEntity
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.
2025-07-04 07:30:26 -07:00
Mitchell Hashimoto 2fa4fc8902
reload configuration on SIGUSR2
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.
2025-07-01 15:51:58 -07:00
Daniel Wennberg 886e33d7b7 make hiddenStyleMask static 2025-06-30 11:21:55 -07:00
Daniel Wennberg b1f788a768 macos: don't overwrite the .fullScreen styleMask option in reapplyHiddenStyle 2025-06-30 10:36:48 -07:00
Islam Sharabash 22a624e560 Equalize splits based on children oriented in the same direction
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.
2025-06-28 09:01:41 +02:00
Mitchell Hashimoto 471098df30
macOS: Run scripts using stdin rather than executing directly
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.
2025-06-22 21:06:32 -04:00
Mitchell Hashimoto 02e05a85fc
macOS: Fix window-decoration=none regression on Tahoe
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.
2025-06-22 07:28:30 -07:00
Qwerasd 5bfdb1b9cf
The Big Renderer Rework (#7620)
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.
2025-06-21 22:00:01 -06:00
Mitchell Hashimoto c1c3f639c5
macos: Ghostty Icon Update for macOS Tahoe
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.
2025-06-21 12:34:49 -07:00
Mitchell Hashimoto 296f340ff4
macos: the approval dialog is now forever 2025-06-21 06:53:09 -07:00
Mitchell Hashimoto 020976bf88
macos: address some feedback 2025-06-21 06:42:32 -07:00
Mitchell Hashimoto e4c13cdba8
macos: Optional/Array extensions need to build for iOS too 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto b6559d0899
macos: add a macos-shortcut config 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto 647f29bad1
macos: intents all ask for permission 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto 027171bd5d
macos: can set env vars on new terminal 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto f8bc9b547c
macos: support env vars for surface config, clean up surface config 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto e6c24fbf0a
macos: remove confirmation option for close terminal 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto 2c1e83ba2f
macos: intent to open quick terminal 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto f096675eaf
macos: Close Terminal Intent 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto 2df301e2fb
macos: mouse pos and scroll intents 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto bc134016f7
macos: move mousePos and mousScroll to Ghostty.Surface 2025-06-21 06:39:20 -07:00
Mitchell Hashimoto 4445a9c637
macos: add mouse button intent 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto 71b6e223af
macos: input keyboard event can send modifiers and actions now 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto 93619ad420
macos: Ghostty.Key 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto a6074040e7
macos: input intent 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto c904e86883
macos: invoke keybind intent 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto 14e46d0979
macos: InvokeCommandPaletteIntent and CommandEntity 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto 5259d0fa55
macos: starting to work on new libghostty data models 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto bbb69c8f27
macos: NewTerminalIntent returns Terminal, can split 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto 683b38f62c
macos: can specify parent terminal for new terminal intent 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto b8d4463754
macos: terminal not found should be an error 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto e51a93ee7c
macos: Terminal entity has screen contents deferred 2025-06-21 06:39:19 -07:00
Mitchell Hashimoto 93f0ee2089
macos: GetTerminalDetails intent 2025-06-21 06:39:18 -07:00
Mitchell Hashimoto 2aa731a64e
macos: TerminalEntity 2025-06-21 06:39:18 -07:00
Mitchell Hashimoto 7ae5018fe8
macos: new terminal intent 2025-06-21 06:39:18 -07:00
Qwerasd 371d62a82c renderer: big rework, graphics API abstraction layers, unified logic
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.
2025-06-20 15:18:41 -06:00
Mitchell Hashimoto 30a8ba1bf6
macos: disambiguate close tab vs close window for confirmation (#7618)
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.
2025-06-17 16:24:46 -07:00
Mitchell Hashimoto 51b9fa751a
macos: disambiguate close tab vs close window for confirmation
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.
2025-06-17 16:16:14 -07:00
Mitchell Hashimoto e6c77789d3
macOS: Confirm close on window close
Fixes #7615

We were incorrectly closing the window without confirmation when there
were no tabs.
2025-06-17 15:03:10 -07:00
Mitchell Hashimoto 6d283c012e
ci: build macOS releases with Xcode 26
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.
2025-06-17 13:44:13 -07:00
Mitchell Hashimoto d0f116da35
macOS: Basic Read-Only Accessibility Integration (#7601)
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.

![CleanShot 2025-06-15 at 14 06
55@2x](https://github.com/user-attachments/assets/55eba1b4-6fd4-4d78-9434-5d672f374c67)

## After

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

![CleanShot 2025-06-15 at 14 07
25@2x](https://github.com/user-attachments/assets/e14cb7df-e4e2-4cc4-a214-004a8459f353)

Also, split hierarchies are navigable:



https://github.com/user-attachments/assets/a7b2ffb7-dbeb-41b2-8705-9c3200812c4d
2025-06-15 15:10:27 -07:00
Mitchell Hashimoto a2b4a2c0e4
macos: complete more ax APIs for terminal accessibility 2025-06-15 14:00:39 -07:00
Mitchell Hashimoto e69c756c89
macos: auto-expire cached screen contents 2025-06-15 13:55:06 -07:00
Mitchell Hashimoto 839d89f2dc
macos: simple cache of screen contents for ax 2025-06-15 13:46:40 -07:00
Mitchell Hashimoto c5f921bb06
apprt/embedded: improve text reading APIs (selection, random points) 2025-06-15 07:59:19 -07:00
Mitchell Hashimoto be437f5b64
macos: bare minimum terminal ax 2025-06-15 07:56:05 -07:00
Mitchell Hashimoto c90eb2e952
macos: AX for debug warning 2025-06-15 07:56:05 -07:00
Mitchell Hashimoto 4237dad240
macOS: simple SplitView AX
Proper labels, action to move the divider
2025-06-15 07:56:05 -07:00
Mitchell Hashimoto 57c79fa357
macos: Tahoe menu item icons, missed the "Ghostty" menu entirely
This is a follow up to #7594, I missed an entire menu.
2025-06-15 07:48:20 -07:00
Mitchell Hashimoto 7cc7f6cb06 macos 15 regression: transparent style shouldn't draw border
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.
2025-06-15 06:51:11 -07:00
Mitchell Hashimoto 9e45da17d0
macos: menu item symbols for Tahoe (#7594)
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
2025-06-14 19:50:58 -07:00
Mitchell Hashimoto 202020cd7d macos: menu item symbols for Tahoe
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).
2025-06-14 19:44:24 -07:00
Mitchell Hashimoto c4a978b07a
macos: set toolbar title `isBordered` to avoid glass view
This was recommended by the WWDC25 session on AppKit updates. My hack
was not the right approach.
2025-06-14 13:50:02 -07:00
Mitchell Hashimoto 928603c23e
macos: use a runtime liquid glass check for our Tahoe styling 2025-06-13 20:20:56 -07:00
Mitchell Hashimoto 1b6142b271 macos: don't restore tab bar with non-native fs 2025-06-13 15:02:06 -07:00
Mitchell Hashimoto ac4b0dcac0 macos: fix transparent tabs on sequoia 2025-06-13 14:57:54 -07:00
Mitchell Hashimoto 1388c277d5 macos: sequoia should use same tab bar identifier as TerminalWindow 2025-06-13 14:43:04 -07:00
Mitchell Hashimoto 8cfc904c0c macos: fix up some sequoia regressions 2025-06-13 14:39:12 -07:00
Mitchell Hashimoto a7df90ee55
macos: remove split zoom accessory when tabs appear 2025-06-13 13:36:42 -07:00
Mitchell Hashimoto f7f0514b9f
macos: move old toolbar into ventura file 2025-06-13 13:14:16 -07:00
Mitchell Hashimoto 59812c3b02
macos: remove TODO 2025-06-13 12:27:44 -07:00
Mitchell Hashimoto b1b74d3421
comments 2025-06-13 12:25:21 -07:00
Mitchell Hashimoto 00d41239da
macOS: prep the tab bar when system appearance changes 2025-06-13 12:22:29 -07:00
Mitchell Hashimoto 17ad77b5b0
macos: fix background color of terminal window to match surface 2025-06-12 21:36:00 -07:00
Mitchell Hashimoto 9d9c451b0a
macos: titlebar tabs handle hidden traffic buttons 2025-06-12 20:03:21 -07:00
Mitchell Hashimoto d84c30ce71
macos: titlebar tabs should be transparent 2025-06-12 18:10:37 -07:00
Mitchell Hashimoto 5f99670247
macos: tahoe titlebar tabs taking shape 2025-06-12 17:52:15 -07:00
Mitchell Hashimoto 6ae8bd737a
macos: hide the reset zoom titlebar accessory when tab bar is shown 2025-06-12 15:13:23 -07:00
Mitchell Hashimoto 5c8f1948ce
macos: remove the duplicated reset zoom accessory view from legacy 2025-06-12 14:42:09 -07:00
Mitchell Hashimoto de40e7ce02
macos: non-native fullscreen should restore toolbars 2025-06-12 14:36:33 -07:00
Mitchell Hashimoto 658ec2eb6f
macos: add reset zoom to all window titles 2025-06-12 14:33:53 -07:00
Mitchell Hashimoto 70029bf82a
macos: tahoe terminal tabs shows title 2025-06-12 13:39:19 -07:00
Mitchell Hashimoto 5877913ab8
macoS: Split out terminal tabs for ventura vs tahoe 2025-06-12 12:06:30 -07:00
Mitchell Hashimoto fd785f98bb
macos: titlebar tabs uses legacy window for now 2025-06-12 11:39:10 -07:00
Mitchell Hashimoto ccfd33022f
macos: only titlebar tabs uses legacy styling now 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto e5cb33e911
typos 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto 63e56d0402
macos: titlebar fonts work with new terminal window 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto a804dab288
macos: native terminal style works with new subclasses 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto dfa7a114de
macos: make transparent titlebars robust against show/hide tabs 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto 3595b2a847
macos: transparent titlebar handles transparent background 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto 6ce7f612a6
macos: transparent titlebar needs to be rehidden when tabs change 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto 7d02977482
macos: add NSView hierarchy debugging code 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto 4d33a73fc4
wip: redo terminal window styling 2025-06-11 15:18:02 -07:00
Mitchell Hashimoto 3db5b3da75
macos: hidden titlebar windows should cascade on new tab
Windows with `macos-titlebar-style = hidden` create new windows when the
new tab binding is pressed. This behavior has existed for a long time.

However, these windows did not cascade, meaning they'd appear overlapped
directly on top of the previous window, which is kind of nasty.

This commit changes it so that new windows created via new tab from a
hidden titlebar window will cascade.
2025-06-10 14:31:41 -07:00
Mitchell Hashimoto 1f340b4b2d
macos: for windowShouldClose, only close the tab if we have multiple
Fixes a regression from our undo/redo rework. We were accidentally
closing the entire window when the "X" button in the tab bar was
clicked.
2025-06-10 12:39:09 -07:00
Mitchell Hashimoto 2b9a6a4820
macos: unsplit window shouldn't allow split zooming
This was always the case, and is a recent regression from the SplitTree
rework. This brings it back to the previous behavior.
2025-06-10 12:11:18 -07:00
Mitchell Hashimoto e4cd90b8a0
macos: set explicit identity for split tree view based on structure
Fixes #7546

SwiftUI uses type and structure to identify views, which can lead
to issues with tree like structures where the shape and type is the same
but the content changes. This was causing #7546.

To fix this, we need to add explicit identity to the split tree view
so that SwiftUI can differentiate when it needs to redraw the view.

We don't want to blindly add Hashable to SplitTree because we don't want
to take into account all the fields. Instead, we add an explicit
"structural identity" to the SplitTreeView that can be used by SwiftUI.
2025-06-08 20:11:58 -07:00
Mitchell Hashimoto 26e1dd8f8e
macos: clear out the surface trees to prevent repeat undo
see the comment
2025-06-08 12:41:45 -07:00
Mitchell Hashimoto 3de3f48faf
macos: fix undo/redo for closing windows with multiple tabs
When closing a window that contains multiple tabs, the undo operation
now properly restores all tabs as a single tabbed window rather than
just restoring the active tab.

The implementation:
- Collects undo states from all windows in the tab group before closing
- Sorts them by their original tab index to preserve order
- Clears tab group references to avoid referencing garbage collected objects
- Restores all windows and re-adds them as tabs to the first window
- Tracks and restores which tab was focused (or focuses the last tab if none were)

AI prompts that generated this commit are below.

Each separate prompt is separated by a blank line, so this session was
made up with many prompts in a back-and-forth conversation.

> We need to update the undo/redo implementation in
> @macos/Sources/Features/Terminal/TerminalController.swift `closeWindowImmediately`
> to handle the case that multiple windows in a tab group are closed all at once,
> and to restore them as a tabbed window. To do this, I think we should collect
> all the `undoStates`, sort them by `tabIndex` (null at the end), and then on j
> restore, restore them one at a time but add them back to the same tabGroup. We
> can't use the tab group in the `undoState` because it will be garbage collected
> by then. To be sure, we should just set it to nil.

I should note at this point that the feature already worked, but the
code quality and organization wasn't up to my standards. If someone
using AI were just trying to make something work, they might be done at
this point.

I do think this is the biggest gap I worry about with AI-assisted
development: bridging between the "it works" stage at a junior quality
and the "it works and is maintainable" stage at a senior quality. I
suspect this will be a balance of LLMs getting better but also senior
code reviewers remaining highly involved in the process.

> Let's extract all the work you just did into a dedicated private method
> called `registerUndoForCloseWindow`

Manual: made some tweaks to comments, moved some lines around, didn’t change
any logic.

> I think we can pull the tabIndex directly from the undoState instead of
> storing it in a tuple.

> Instead of `var undoStates`, I think we can create a `let undoStates` and
> build and filter and sort them all in a chain of functional mappings.

> Okay, looking at your logic for restoration, the `var firstController` and
> conditionals are littly messy. Can you make your own pass at cleaning those
> up and I'll review and provide more specific guidance after.

> Excellent. Perfect. The last thing we're missing is restoring the proper
> focused window of the tab group. We should store that and make sure the
> proper window is made key. If no windows were key, then we should make the
> last one key.

> Excellent. Any more cleanups or comments you'd recommend in the places you
> changed?

Notes on the last one: it gave me a bunch of suggestions, I rejected most but
did accept some.

> Can you write me a commit message summarizing the changes?

It wrote me a part of the commit message you're reading now, but I
always manually tweak the commit message and add my own flair.
2025-06-08 08:08:01 -07:00
Mitchell Hashimoto ec043e1386
macos: red traffic light should be undoable 2025-06-08 07:00:51 -07:00
Mitchell Hashimoto 6f6d493763
macos: show quick terminal on undo/redo 2025-06-07 13:14:21 -07:00
Mitchell Hashimoto 6e77a5a6ca
macos: address quick terminal basic functionality with new API 2025-06-07 13:07:31 -07:00
Mitchell Hashimoto 20744f0482
macos: fix some CI build issues 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto 973a2afdde
macos: make sure we're not registering unnecessary undos 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto b234cb2014
macos: only process reopen if already activated 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto aeede903f5
macos: undo close all windows 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto d92db73f25
macos: undo new tab 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto 636b1fff8a
macos: initial window shouldn't support undo 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto 797c10af37
macos: undo new window 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto 33d128bcff
macos: remove TerminalManager
All logic related to TerminalController is now in TerminalController.
2025-06-07 12:46:15 -07:00
Mitchell Hashimoto 5507ec0fc0
macos: compile errors in CI 2025-06-07 12:46:15 -07:00
Mitchell Hashimoto d2d3852026
macos: remove debug log 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto 49cc88f0d3
macos: configurable undo timeout 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto 3e02c0cbd5
macos: fix an incorrect bindable write during view update 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto b044f4864a
add undo/redo keybindings, default them on macOS 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto e1847da139
macos: more robust undo tab that goes back to the same position 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto 5f74445b14
macos: basic undo tab, not quite working 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto 104cc2adfe
macos: basic undo close window, not very robust yet 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto f571519157
macos: setup undo responders at the AppDelegate level 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto 6d32b01c64
macos: implement a custom ExpiringUndoManager, setup undo for new/close 2025-06-07 12:46:14 -07:00
Mitchell Hashimoto 493b1f5350
wip: undo 2025-06-07 12:46:13 -07:00
Mitchell Hashimoto 41ee578b7a
macos: quick terminal restores previous size when exiting final surface
This fixes a regression from the new split work last week, but it was
also probably an issue before that in a slightly different way.

With the new split work, the quick terminal was becoming unusable when
the final surface explicitly `exit`-ed, because AppKit/SwiftUI would
resize the window to a very small size and you couldn't see the new
terminal on the next toggle.

Prior to this, I think the quick terminal would've reverted to its
original size but I'm not sure (even if the user resized it manually).

This commit saves the size of the quick terminal at the point all
surfaces are exited and restores it when the quick terminal is shown the
next time with a new initial surface.
2025-06-07 12:37:44 -07:00
Mitchell Hashimoto 269d29624b
Add bell feature flags for audio, attention, and title actions on macOS (#7533)
Resolve #7526
2025-06-06 12:59:23 -07:00
Aaron Ruan 5f6a15abef
Add bell feature flags for audio, attention, and title actions on macOS
Signed-off-by: Aaron Ruan <i@ar212.com>
2025-06-06 23:54:34 +08:00
Mitchell Hashimoto 70f030e3c2
macos: dismiss notifications on focus, application exit
I've only recently been using programs that use user notifications heavily
and this commit addresses a number of annoyances I've encountered.

  1. Notifications dispatched while the source terminal surface is
     focused are now only shown for a short time (3 seconds hardcoded)
     and then automatically dismiss.

  2. Notifications are dismissed when the target surface becomes focused
     from an unfocused state. This dismissal happens immediately (no
     delay).

  3. Notifications are dismissed when the application exits.

  4. This fixes a bug where notification callbacks were modifying view
     state, but the notification center doesn't guarantee that the
     callback is called on the main thread. We now ensure that
     the callback is always called on the main thread.
2025-06-06 07:28:32 -07:00
Mitchell Hashimoto 08101b0bc5
macos: fix hasWindowButtons logic (#7504 follow-up) (#7528)
Took another look through #7504 after the merge and realized that the
logic behind the `hasWindowButtons` property wasn't quite sound. It
would return `false` if either _at least one_ button were missing in the
`standardWindowButton(.theButton) == nil` sense, or if _all_ buttons
were hidden in the `isHidden` sense.

With this PR, the logic is rectified: `false` if _all_ buttons are
missing or hidden in any sense, otherwise `true`.

In practice, I suppose Ghostty won't ever instantiate a `TerminalWindow`
where `standardWindowButton(.theButton) == nil`, but might as well get
it right and sleep better at night.
2025-06-05 14:11:10 -07:00
Daniel Wennberg c2c267439b macos: fix hasWindowButtons logic 2025-06-05 13:48:53 -07:00
Mitchell Hashimoto 045c84acb7
macos: split directional navigation should use distance to leaf
Fixes regression from #7523

I messed two things up around spatial navigation in the split tree
that this commit fixes:

  1. The distance in the spatial tree only used a single dimension
     that we were navigating. This commit now uses 2D euclidean
     distance from the top-left corners of nodes. This handles the case
     where the nodes are directly above or below each other better.

  2. The spatial slots include split containers because they are layout
     elements. But we should only navigate to leaf nodes. This was
     causing the wrong navigatin to happen in some scenarios.
2025-06-05 13:43:07 -07:00
Mitchell Hashimoto efc1ceab5d
macOS: New value-based split tree implementation, move split logic out of SwiftUI into AppKit (#7523)
This is a major rework of how we represent, handle, and render splits in
the macOS app.

This new PR moves the split structure into a dedicated, generic
(non-Ghostty-specific) value-type called `SplitTree<V>`. All logic
associated with splits (new split, close split, move split, etc.) is now
handled by notifications on `BaseTerminalController`. The view hierarchy
is still SwiftUI but it has no logic associated with it anymore and
purely renders a static tree of splits.

Previously, the split hierarchy was owned by AppKit in a type called
`SplitNode` (a recursive class that contained the tree structure). All
logic around creating, zooming, etc. splits was handled by notification
listeners directly within the SwiftUI hierarchy. SwiftUI managed a
significant amount of state and we heavily used bindings, publishers,
and more. The reasoning for this is mostly historical: splits date back
to when Ghostty tried to go all-in on SwiftUI. Since then, we've taken a
more balanced approach of SwiftUI for views and AppKit for data and
business logic, and this has proven a lot more maintainable.

## Spatial Navigation

Previously, focus moving was handled by traversing the tree structure.
This led to some awkward behaviors. See:
https://github.com/ghostty-org/ghostty/issues/524#issuecomment-2668396095

In this PR, we now handle focus moving spatially. This means that move
"left" means moving to the visually left split (from the top-left
corner, a future improvement would be to do it from the cursor
position).

Concretely, given the following split structure:

```
+----------+-----+
|          |  b  |
|          |     |
|   a      +-----+
|          |     |
|          |     |
|          |     |
|          |     |
|----------|  d  |
|   c      |     |
|          |     |
+----------+-----+
```

Moving "right" from `c` now moves to `d`. Previously, it would go to
`b`. On Linux, it still goes to `b`.

## Value Types

One of the major architectural shifts is moving **purely to immutable
value types.** Whenever a split property changes such as a new split,
the ratio between splits, zoomed state, etc. we _create an entirely new
`SplitTree` value_ and replace it along the entire view hierarchy. This
is in some ways wasteful, but split hierarchies are relatively small
(even the largest I've seen in practical use are dozens of splits, which
is small for a computer). And using value types lets us get rid of a ton
of change notification soup around the SwiftUI hierarchy. We can rely on
reference counting to properly clean up our closed views.

> [!NOTE]
> 
> As an aside, I think value types are going to make it a lot easier in
the future to implement features like "undo close." We can just keep a
trailing list of surface tree states and just restore them. This PR
doesn't do anything like that, but it's now possible.

## SwiftUI Simplicity

Our SwiftUI view hierarchy is dramatically simplified. See the
difference in `TerminalSplitTreeView` (new) vs `TerminalSplit` (old).
There's so much less logic in our new views (almost none!). All of it is
in the AppKit layer which is just way nicer.

## AI Notes

This PR was heavily written by AI. I reviewed every line of code that
was rewritten, and I did manually rewrite at every step of the way in
minor ways. But it was very much written in concert. Each commit usually
started as an AI agent writing the whole commit, then nudging to get
cleaned up in the right way.

One thing I found in this task was that until the last commit, I kept
the entire previous implementation around and compiling. The agent
having access to a previous working version of code during a refactor
made the code it produced as follow up in the new architecture
significantly better, despite the new architecture having major
fundamental differences in how it works!
2025-06-05 12:59:43 -07:00
Mitchell Hashimoto a2a3863ad2
macOS: Add option to hide window buttons (#7504)
Conversion of #7497 to a PR. This implements a feature requested in
#7331: an option to hide the default window buttons on macOS for a
cleaner aesthetic.

~~Builds on #7502 as it requires the same change to avoid the main
toolbar title showing on top of the tab bar.~~ EDIT: rebased on main now
that #7502 was merged.

I aligned the scope of the new option with `macos-titlebar-style`, since
they both customize titlebar elements. This means it has the same edge
case quirks: For example, if you change the setting, reload the config,
and then open a new tab, the appearance of the current window will
depend on which tab is in the foreground. I did it this way because
`macos-titlebar-style` provided an easy template for which derived
configs and functions to modify. Let me know if you want me to try
adjusting this so that a change in the setting also takes effect for
current windows/tabs, which I _think_ should be possible.

Screenshots:
* `macos-titlebar-style = transparent` (default)
![Screenshot 2025-06-01 at 18 04
56](https://github.com/user-attachments/assets/01fa3953-d2ef-4c39-a6e3-f236488dd841)
![Screenshot 2025-06-01 at 18 07
24](https://github.com/user-attachments/assets/cd463ded-a0b2-4f69-9abe-384e7eecaa27)
* `macos-titlebar-style = tabs`
![Screenshot 2025-06-01 at 17 56
35](https://github.com/user-attachments/assets/bf99d046-cdbb-4e5d-b1c5-d51bbba79007)
![Screenshot 2025-06-01 at 17 56
48](https://github.com/user-attachments/assets/098164b8-bf97-4df1-9dff-c1c17e12665d)
2025-06-05 07:46:57 -07:00
Mitchell Hashimoto 6db455eee5
fix: exit non-native fullscreen on close (#7525)
Fixes https://github.com/ghostty-org/ghostty/discussions/7159.
2025-06-05 07:36:50 -07:00
Mitchell Hashimoto d4249679e3
macos: simplify some ServiceProvider code (#7508)
First, remove the always-inlined openTerminalFromPasteboard code and
combine it with openTerminal. Now that we're doing a bit of work inside
openTerminal, there's little better to having an intermediate, inlined
function.

Second, combine some type-casting operations (saving a .map() call).

Lastly, adjust some variable names because a generic `objs` or `urls`
was a little ambiguous now that we're all in one function scope.
2025-06-05 07:30:03 -07:00
Francisco Giordano 9008e21637 fix: exit non-native fullscreen on close 2025-06-05 07:25:53 -07:00
Mitchell Hashimoto c40ac6b785
input: add focus split directional commands to command palette 2025-06-05 07:11:18 -07:00
Mitchell Hashimoto 1966dfdef7
macos: moving some files around 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto f8e3539b7d
macos: remove the unused resizeEvent code from SplitView 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 01fa87f2ab
macos: fix iOS builds 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 9474092f77
macos: remove the old split implementation 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 69c3c359cb
macos: resize split keybind handling 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 5299f10e13
macos: unzoom on new split and focus change 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 19a9156ae1
macos: address remaining todos 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 6c97e4a59a
macos: fix focus after closing splits 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 77458ef308
macos: rename surfaceTree2 to surfaceTree 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto f1ed07caf4
macos: Remove the legacy SurfaceTree 2025-06-05 07:05:13 -07:00
Mitchell Hashimoto 22819f8a29
macos: transfer doesBorderTop 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto 8b979d6dce
macos: handle surfaceTreeDidChange 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto ea1ff438f8
macos: handle split zooming 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto b7c01b5b4a
macos: spatial focus navigation 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto ec7fd94d0f
macos: equalize splits works with new tree 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto a389926ca7
macos: use surfaceTree2 needsConfirmQuit 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto aef61661a0
macos: fix up command palette, focusing 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto 7dcfebcd5d
macos: isSplit guarding on focus split directions works 2025-06-05 07:05:12 -07:00
Mitchell Hashimoto 0fb58298a7
macos: focus split previous/next 2025-06-05 07:05:11 -07:00
Mitchell Hashimoto b84b715ddb
macos: unify confirm close in our terminal controllers 2025-06-05 07:05:11 -07:00
Mitchell Hashimoto d1dce1e372
macos: restoration for new split tree 2025-06-05 07:05:11 -07:00
Mitchell Hashimoto 33d94521ea
macos: setup sequence for SplitTree 2025-06-05 07:05:11 -07:00
Mitchell Hashimoto 672d276276
macos: confirm close on split close 2025-06-05 07:05:11 -07:00
Mitchell Hashimoto e3bc3422dc
macos: handle split resizing 2025-06-05 07:05:11 -07:00
Mitchell Hashimoto 1707159441
new SplitTree 2025-06-05 07:05:11 -07:00
Jon Parise 652f551bec macos: simplify some ServiceProvider code
First, remove the always-inlined openTerminalFromPasteboard code and
combine it with openTerminal. Now that we're doing a bit of work inside
openTerminal, there's little better to having an intermediate, inlined
function.

Second, combine some type-casting operations (saving a .map() call).

Lastly, adjust some variable names because a generic `objs` or `urls`
was a little ambiguous now that we're all in one function scope.
2025-06-02 20:11:18 -04:00
Mitchell Hashimoto d1f1be8833
macos: fix small memory leak in surface tree when closing splits
This fixes a small memory leak I found where the `SplitNode.Leaf` was
not being deinitialized properly when closing a split. It would get
deinitialized the next time a split was made or the window was closed,
so the leak wasn't big. The surface view underneath the split was also
properly deinitialized because we forced it, so again, the leak was
quite small.

But conceptually this is a big problem, because when we change the
surface tree we expect the deinit chain to propagate properly through
the whole thing, _including_ to the SurfaceView.

This fixes that by removing the `id(node)` call. I don't find this to be
necessary anymore. I don't know when that happened but we've changed
quite a lot in our split system since it was introduced. I'm also not
100% sure why the `id(node)` was causing a strong reference to begin
with... which bothers me a bit.

AI note: While I manually hunted this down, I started up Claude Code and
Codex in separate tabs to also hunt for the memory leak. They both
failed to find it and offered solutions that didn't work.
2025-06-02 14:12:26 -07:00
Daniel Wennberg 5244f8d6ac Follow-up to #7462: var -> let 2025-06-02 10:14:52 -07:00
Daniel Wennberg 232a46d2dc Add option to hide macOS traffic lights 2025-06-02 09:22:01 -07:00
Daniel Wennberg 12a01c0460 Hide main title when covered by tabs 2025-06-02 09:10:22 -07:00
Daniel Wennberg 85beda9c49 Fix reset zoom button visibility in macOS "tabs" mode when no tabs 2025-06-02 09:09:04 -07:00
Mitchell Hashimoto fd7132db71
macos: quick terminal can equalize splits
Fixes #7480
2025-05-30 15:13:30 -07:00
Jeffrey C. Ollie d3cb6d0d41
GTK: add action to show the GTK inspector
The default keybinds for showing the GTK inspector (`ctrl+shift+i` and
`ctrl+shift+d`) don't work reliably in Ghostty due to the way Ghostty
handles input. You can show the GTK inspector by setting the environment
variable `GTK_DEBUG` to `interactive` before starting Ghostty but that's
not always convenient.

This adds a keybind action that will show the GTK inspector. Due to
API limitations toggling the GTK inspector using the keybind action is
impractical because GTK does not provide a convenient API to determine
if the GTK inspector is already showing. Thus we limit ourselves to
strictly showing the GTK inspector. To close the GTK inspector the user
must click the close button on the GTK inspector window. If the GTK
inspector window is already visible but is hidden, calling the keybind
action will not bring the GTK inspector window to the front.
2025-05-29 16:07:57 -05:00
Daniel Wennberg d1501a4925 fix: properly intialize key event in GlobalEventTap 2025-05-27 22:33:15 -07:00
Jeffrey C. Ollie 48b6807ac9
nix: fix typos 2025-05-26 11:12:30 -05:00
Mitchell Hashimoto a6466c5ca0
macOS: use file parent dir for `openTerminal` service cwd (#7286) (#7292)
Fixes #7286

Previously, when using the "New Ghostty Window/Tab Here" macOS service
on a file, the new terminal window/tab would incorrectly open in the
user's home directory. This was because the service handler only
expected directory paths.

This commit updates the service handler to check if the provided path is
a file. If it is, the handler now uses the file's parent
directory as the working directory for the new Ghostty window or tab,
aligning with user expectations. If the path is a directory, it's used
directly as before.
2025-05-15 20:20:59 -07:00
Aaron Ruan f343e1ba46
Fix comma typo 2025-05-16 00:40:25 +08:00
Aaron Ruan 7ccc181332
macos: add "Check for Updates" action, menu item & key-binding support 2025-05-15 13:34:44 +08:00