Fixes https://github.com/ghostty-org/ghostty/issues/14135.
<img width="1398" height="652" alt="image"
src="https://github.com/user-attachments/assets/6901a05e-9d23-4e25-89a7-c16c1694a0f9"
/>
> The #9168 fix is no longer needed, since the frame is now higher than
the actual glyph.
The frame change observation only affects those who have a custom window
title font set in their config. I asked Claude to run some main thread
benchmarking compared to `main`; it will gain some delays for rapid
title changes and window resizing. The additional cost is brought by the
frequent frame updates which are done by AppKit. But that's necessary
for updating the title to the correct style.
> I tried to do some diffing and removing duplicates, but it will add
too many changes too, and I didn't think it's worth doing so.
The amount looks ok to me.
### `window-title-font-family = PT Mono`
| Phase | Metric | base | branch | Δ | ratio |
|---|---|---:|---:|---:|---:|
| Idle, 3 s | main-thread CPU | 2.8 ms | 2.8 ms | -0.0 | 1.00 |
| | process CPU | 11.3 ms | 11.2 ms | -0.1 | 0.99 |
| Paced title updates, 150 × 100 ms | main-thread CPU | 878.5 ms |
**946.7 ms** | **+68.3** | **1.08** |
| | process CPU | 1219.9 ms | 1335.0 ms | +115.1 | 1.09 |
| | wall | 17.24 s | 17.34 s | +0.1 | 1.01 |
| Title burst, 5000 back-to-back | main-thread CPU | 180.7 ms | 181.4 ms
| +0.7 | 1.00 |
| | process CPU | 254.2 ms | 254.8 ms | +0.6 | 1.00 |
| | wall | 2.31 s | 2.32 s | +0.0 | 1.00 |
| `toggle_maximize` × 16 (animated resize) | main-thread CPU | 1947.8 ms
| **2178.0 ms** | **+230.2** | **1.12** |
| | process CPU | 4166.8 ms | 4403.2 ms | +236.4 | 1.06 |
| | wall | 16.68 s | 16.72 s | +0.0 | 1.00 |
| Native fullscreen enter/exit × 2 | main-thread CPU | 196.8 ms | 195.9
ms | -0.9 | 1.00 |
| | process CPU | 360.8 ms | 361.5 ms | +0.7 | 1.00 |
| | wall | 8.47 s | 8.47 s | +0.0 | 1.00 |
### AI Disclosure
Asked Claude to generate the harness to run the benchmark and review my
changes. I did the changes myself.
The action and its keybind exist, and GTK implements them, but macOS had no
handler so the binding did nothing there. AppKit already has the command for
window tabs, so this forwards to it.
A window that isn't in a tab group, or is alone in one, is already a window of
its own, so there is nothing to move and the action reports it did nothing.
Implements the remaining macOS half of #2630.
Found another regression when investigating #14107 after the last fix.
This regression appears on macOS 15 and 26 as well: **New window by
Shortcuts.app or service menu while a window is visible would create a
tab**.
It appears that for `new-window` triggered by Shortcuts/Service, a small
delay is needed to avoid automatic tabbing. It's either removing
`NSWindow.userTabbingPreference == .always` completely or adding another
"delay" for cascading. The latter should be better.
Also fixes another cascading for `macos-titlebar-style = hidden`
previously missed.
Discussion #14048
Directory URLs no longer export a trailing slash through PWD, which
keeps zsh's %1~ prompt expansion from resolving to an empty string.
A shared URL helper removes trailing separators while preserving the
filesystem root and percent-decoding behavior. Tests cover normal,
repeated, encoded, and root paths.
Discussion #14048
Directory URLs no longer export a trailing slash through PWD, which
keeps zsh's %1~ prompt expansion from resolving to an empty string.
A shared URL helper removes trailing separators while preserving the
filesystem root and percent-decoding behavior. Tests cover normal,
repeated, encoded, and root paths.
`send key` only works for control keys like `enter` currently; this adds
(fixes) the support for other keys listed as available. Found by
@paaloeye in #13180
The core of this fix is relying on `UCKeyTranslate` to get the
corresponding character and code point from a key code using
`KeyboardLayout.character(for:modifiers:)`.
ScriptKeyEventCommand now respects `macos-option-as-alt`, and attach
`text`, `unshifted_codepoint` and `consumed_mods` under the same
condition as a manual input events like in `performKeyEquivalent` and
`localEventKeyDown`.
## AI Disclosure
Claude did the heavy lifting, I reviewed and rephrased some of the
comments it generated. And ofc reviewed and tested myself.
Partial changes for #13205, known issues are marked as warnings.
### AI Disclosure
Claude generated these tests from linked pr, I cherrypicked and reviewed
myself.
Discussion #13979
Dropped paths and text once again honor bracketed paste mode. IME,
dictation, emoji picker, and character viewer commits remain typed
input.
sendText calls ghostty_surface_text, which applies the clipboard paste
pipeline and bracketed paste framing when enabled. Separating the paths
at the drag-and-drop caller preserves the input-method behavior
introduced by #13817.
A Kitty clipboard protocol (OSC 5522) write transaction targeting
`loc=primary` replied `type=write:status=DONE` in the macOS app even
though macOS has no primary selection and the data was silently
discarded.
The spec requires ENOSYS when the requested location is not
available on the system, which the read path already answers correctly:
https://sw.kovidgoyal.net/kitty/clipboard/
Discussion #13979
Dropped paths and text once again honor bracketed paste mode.
IME, dictation, emoji picker, and character viewer commits remain typed input.
sendText calls ghostty_surface_text, which applies the clipboard paste
pipeline and bracketed paste framing when enabled. Separating the
paths at the drag-and-drop caller preserves the input-method behavior
introduced by #13817.
Programs can now write the system clipboard through the Kitty
clipboard protocol in the macOS app. This also does all the hard work
plumbing through core termio/apprt so GTK should be an easy follow.
This functionality lets clients copy arbitrary representations (images,
HTML, etc.) into the clipboard. Writes honor `clipboard-write`: allow
applies silently, deny answers EPERM up front before any data is used,
and ask shows the standard confirmation prompt.
Translate printable physical keybindings through the current macOS
keyboard layout before assigning menu key equivalents. Previously these
bindings could not be represented because SwiftUI shortcuts are
character-based, so actions such as `super+backquote` had no native menu
shortcut.
Keep native keycodes as dispatch identity so translated display
characters do not change physical semantics or precedence over Unicode
bindings. Refresh shortcuts when the input source changes, and prevent
AppKit from transforming equivalents that are already localized.
**AI Usage:** The approach was suggested by GPT 5.6 Sol, but I wrote
most of the code and understand it all.
Translate synthetic physical-key events with characters(byApplyingModifiers:).
This preserves current-layout and Command-table behavior while avoiding a
duplicate direct UCKeyTranslate implementation in Swift.
Text Input Sources APIs are not thread-safe, but shortcut translation could
be called outside a declared main-actor context.
Mark keyboard layout and shortcut conversion as main-actor isolated, update
their tests, and dispatch key-sequence UI notifications to the main queue
before translating their shortcuts.