Commit Graph

17347 Commits

Author SHA1 Message Date
Mitchell Hashimoto
44f06d4e4f macOS: rework for #12712 and #13645 (#13717)
`needleSelection` was introduced in #12712 to select all texts when
syncing pasteboard, the crash happens most on macOS 15 in
`readPasteboardNeedle`. It seems that `objectWillChange` fires
differently there, and it's hard to reproduce on macOS 26/27. I think
guaranteeing from ourside is enough, I believe SwiftUI already as its
own when updating the binding.

**Confirmed with a simple example on macOS 15, it seems a SwiftUI
issue🫪. So I changed the minimal macOS version for text selection to
macOS 26. I don't see an elegant way to fix it.**

<img width="1352" height="849" alt="image"
src="https://github.com/user-attachments/assets/1dfef3f5-ceaa-41dd-bb91-c23dbc5e4ad3"
/>


```swift
struct ContentView: View {
    @State private var text = ""
    @State private var selection: TextSelection?
    var body: some View {
        TextField("Search", text: $text, selection: $selection)
    }
}
```
2026-08-10 18:08:42 -07:00
ghostty-vouch[bot]
09557e91dc Update VOUCHED list (#13739)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/13738#discussioncomment-17968662)
from @jcollie.

Vouch: @PRIHLOP

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-10 22:48:46 +00:00
Jeffrey C. Ollie
d929e6a34a config/edit: better handling of existing paths (#13736)
This adds some better handling of existing paths when editing
configuration files:

* If we've found an existing file we just skip any attempts to create
files/dirs, and just return the path.

* If the path (including file) does not exist, we check to see if the
directory exists first (possibly following symlinks). Directory creation
happens normally after this (note that any intermediary symlinks in this
process will still cause the process to fail, this is to prevent
infinite loops, as per the comments in
std.Io.Threaded.dirCreateDirPath).
2026-08-10 16:33:22 -05:00
Chris Marchesi
b68eb67e95 config/edit: better handling of existing paths
This adds some better handling of existing paths when editing
configuration files:

* If we've found an existing file we just skip any attempts to create
  files/dirs, and just return the path.

* If the path (including file) does not exist, we check to see if the
  directory exists first (possibly following symlinks). Directory
  creation happens normally after this (note that any intermediary
  symlinks in this process will still cause the process to fail, this is
  to prevent infinite loops, as per the comments in
  std.Io.Threaded.dirCreateDirPath).
2026-08-10 14:03:42 -07:00
Lukas
f0e3be3eef macOS: support decoding the surrogate pair with UnicodeHexInput 2026-08-10 22:55:57 +02:00
Jeffrey C. Ollie
951a03b58b apprt/gtk: add WeakRef.deinit and use it at teardown sites (#13732)
Fixes #13713.

`WeakRef(T)` offers `set` and `get`, so releasing one is spelled
`set(null)` — indistinguishable from an ordinary assignment. The
requirement that it *must* happen before the owning memory is freed
lives in a comment in `class/inspector_window.zig`, which is not where
somebody using the type is looking.

This adds `deinit`, forwarding to `g_weak_ref_clear` — the call GLib
documents for a `GWeakRef` that is going away — and switches the
dispose-time clears to it.

### What changed

- `weak_ref.zig`: new `deinit`, with the reasoning in its doc comment.
- `window.zig`, `split_tree.zig`, `application.zig`,
`command_palette.zig`: the four dispose-time clears now call `deinit`.

`set(null)` is unchanged and still valid. The clear in
`Application.handleReloadConfig` deliberately stays a `set(null)`: the
object is alive there and the reference is reused, so it is a logical
clear rather than teardown — which is the distinction the new name is
meant to make visible.

### Why it is worth a method

Zig has no destructors, so this enforces nothing; it is documentation
that happens to be executable. The concrete case is in #13713: I added a
`WeakRef(Window)` in a downstream branch, did not clear it, and closing
a window that had shown that dialog deadlocked the GTK main loop inside
`weak_ref_data_clear_list` locking freed memory. Every upstream call
site already gets this right — the point is only to put the rule where
the next person will see it.

### Testing

`zig build test` passes. `zig fmt --check` clean. Built and used on
Linux/GTK; the change is behaviourally identical to what was there,
since `g_weak_ref_clear` and `g_weak_ref_set(NULL)` both unregister.

---

**AI disclosure per `AI_POLICY.md`:** I investigated the underlying
incident with Claude Code and it drafted this change; I reviewed it.
2026-08-10 12:01:45 -05:00
Jeffrey C. Ollie
8b7c57c756 gtk: add window title renaming
Fixes #10469 for GTK.
2026-08-10 10:07:11 -05:00
Jeffrey C. Ollie
0914c5c2f1 core/gtk: allow editing Ghostty config in a Ghostty window (#11905)
This PR extends the `open_config` keybind action to allow editing the
Ghostty config in a new Ghostty window using the editor configured in
`$EDITOR` or `$VISUAL`.
2026-08-10 09:25:29 -05:00
Leah Amelia Chen
e53b18a647 gtk: free hotkeys memory on app teardown (#13727)
In debug builds the DebugAllocator throws an error about leaked memory
when you close Ghostty, if you have global keybinds in your config with
a Wayland compositor that supports the vicinae-hotkey protocol. The
cause is the `Hotkeys.entries` array list never actually being freed.
Not really a problem because the list should be kept around until app
teardown anyway, but not getting an error every time would be nice (even
if you need a somewhat specific setup for this to even happen right
now).

To fix this free the array list memory in Hotkeys.deinit with
`ArrayList.clearAndFree`. As the existing comment on `deinit` already
mentions, we can't use `ArrayList.deinit` because it leaves the list in
an invalid state and `Hotkeys.clear` might still get called and use it.
2026-08-10 22:22:10 +08:00
Jeffrey C. Ollie
0a183c923b core/gtk: allow editing Ghostty config in a Ghostty window
This PR extends the `open_config` keybind action to allow editing the
Ghostty config in a new Ghostty window using the editor configured in
`$EDITOR` or `$VISUAL`.
2026-08-10 08:54:36 -05:00
Håkon Hægland
1dbc8ca30c apprt/gtk: add WeakRef.deinit and use it at teardown sites
A GWeakRef must be released before the memory holding it is freed: the
target keeps a pointer to the GWeakRef so it can clear it at finalize,
and if that memory is gone by then the target walks into whatever now
occupies it. inspector_window.zig already carries this warning, and
every call site follows it — but the rule lives in a comment in one
file, while the type itself offers only set and get, so releasing one
looks like an ordinary assignment.

Give it a name. deinit forwards to g_weak_ref_clear, which is the call
GLib documents for a GWeakRef that is going away, and the dispose-time
clears now use it. set(null) still works and is unchanged; the clear in
handleReloadConfig stays a set(null) because the object is still alive
there and the reference is reused.

Zig has no destructors so this enforces nothing. It puts the
requirement on the type someone is already looking at.
2026-08-10 15:24:57 +02:00
Daniel Kinzler
fd47b15cd4 gtk: free hotkeys memory on app teardown
Free array list memory in Hotkeys.deinit to avoid DebugAllocator
throwing an error about leaked memory.
2026-08-10 13:29:59 +02:00
Lukas
0c8ec225b5 macOS: remove unused menu validations 2026-08-10 12:12:25 +02:00
Lukas
4b9d589bcb macOS: disable text selection on macOS 15 2026-08-10 11:44:43 +02:00
Lukas
7e463bc65d ghostty.h: mark as internal (#13724) 2026-08-10 09:20:18 +02:00
Leah Amelia Chen
d6248a32dd ghostty.h: mark as internal
Its moniker has been `libghostty-internal` for *quite* a while now among
maintainers but that has never really been clarified for the public aside
from a couple comments on discussions. Judging by how many people still
try to vibe their way into making this work for their purposes, I think
we should clear this up once and for all.
2026-08-10 14:21:03 +08:00
Mitchell Hashimoto
156bc8c814 terminal/kitty: clear placements on image retransmit (#13723)
Fixes #13719

The Kitty graphics protocol requires retransmitting data for a specific
image ID to delete the previous image and all of its placements.

Ghostty instead preserved the placement count and map when replacing
image data. Repeated `a=T` commands therefore added one anonymous
placement per frame and retained its tracked pin.

Spec:
https://sw.kovidgoyal.net/kitty/graphics-protocol/#display-images-on-screen
2026-08-09 20:45:01 -07:00
Mitchell Hashimoto
b8222f4a84 terminal/kitty: clear placements on image retransmit
Fixes #13719

The Kitty graphics protocol requires retransmitting data for a
specific image ID to delete the previous image and all of its
placements. 

Ghostty instead preserved the placement count and map when replacing image 
data. Repeated `a=T` commands therefore added one anonymous placement per 
frame and retained its tracked pin.

Spec: https://sw.kovidgoyal.net/kitty/graphics-protocol/#display-images-on-screen
2026-08-09 20:34:27 -07:00
Mitchell Hashimoto
ad08f3b037 macos: reduce app launch time ~15%, time to first frame ~27% (#13722)
Startup optimizations for macOS! Highlights:

 * Process exec to visible window: **15% reduction, ~193ms to ~165ms.**
 * Time to first rendered frame: **27% reduction, ~126ms to ~92ms.**
* Zig startup time goes from **20ms to ~5ms**, the remainder is
AppKit/Swift stuff.

> [!NOTE]
>
> "Time to first rendered frame?" I measured the time between global
init start to the first Metal callback saying that a frame was
completed/drawn. This is faster than when it is _presented_ because we
can create an IOSurfaceLayer and draw to it before AppKit finishes its
startup and shows the window. But, the good news is this means that when
the window is shown, the frame is already drawn!

See individual commits for speeds, but a summary below:

1. **Resolve Sentry directories on the init thread, not startup thread
(~3-4ms).** Sentry init already ran on a thread, but directory
resolution happened on the main thread first, and on macOS that calls
`NSFileManager URLForDirectory:` which is slow as shit.

2. **Initialize the TIS keymap lazily (~7ms).** The keymap is only
needed once keyboard events flow. If AppKit isn't warmed up, this is
SLOW. Defer setup until its needed.

3. **Warm up the font registry and Metal on background threads (~7ms+
off the first surface).** The first CoreText query initializes the
system font database (~7ms) and the first Metal device/queue/pipeline
use pays framework init and shader compilation costs. `App.create` now
spawns a detached warmup thread per subsystem so this overlaps config
load, AppKit launch, and window creation. First font grid init went from
~4.5ms to ~1.1ms, renderer init from ~6.8ms to ~1.5ms.

4. **Look up Apple Color Emoji by exact name.** We know exactly which
font we want, so skip the system-wide `CTFontCollection` matching
(~312us to ~13us).

5. **Cache unified logging loggers per scope.** We created and released
an `os_log_t` on every log call. I actually had a comment saying this is
slow but probably won't matter. Well, we log a lot on startup, and this
actually mattered.

## Warmup Threads

As a note, some of the biggest speedups are by using "warmup" threads.
These are one-time launched threads on system start that basically just
"touch" the relevant frameworks (CoreText/Metal). The initial touching
of these frameworks has a ton of cost associated with them (and they're
thread-safe), so we can shave off a bunch of time by just touching them
in the background.

This sets up a race between our own startup needing it and these warmup
threads, but in every case I measured, the warmup threads win.

## Linux

All the optimizations here focused really on slow macOS APIs. I plan on
measuring on Linux, but nothing here should slow it down.

**AI usage:** Fable was used for this one to find the issues, help
perform the measurements, and draft commit messages by splitting up my
work. I wrote the code, then edited the commit messages. This PR message
is fully hand-written.
2026-08-09 20:24:02 -07:00
Mitchell Hashimoto
931a538a39 comments 2026-08-09 20:22:30 -07:00
Mitchell Hashimoto
da745630be macos: only check for auto-tabbing when tabbing preference is always
windowDidLoad undoes macOS automatic window tabbing by inspecting
window.tabGroup. Accessing tabGroup on a fresh window materializes
AppKit's tab group machinery, which takes ~15-20ms and is on the
critical path of every window creation, including the first window at
app launch.

AppKit only auto-tabs a fresh window when the system tabbing
preference is "always": the tab bar "+" button goes through
newWindowForTab which we intercept and route through our own tab
logic, so it never auto-tabs. Guard the check on
NSWindow.userTabbingPreference == .always so everyone else skips the
tab group materialization entirely.

Measured on macOS (Apple Silicon) during app launch via the startup
timeline instrumentation:

  windowDidLoad tab group check: 17.8ms -> ~0ms
  main() -> window visible: median ~173ms -> ~165ms (n=7)
2026-08-09 19:54:28 -07:00
Mitchell Hashimoto
4b1e02c7c3 macos: do not load the config errors window when there are no errors
Measured on macOS (Apple Silicon) during app launch, via a startup
timeline instrumented across the Swift app and libghostty:

  config apply, errors step:      35.5ms -> 0.1ms
  main() -> first frame rendered: ~126ms -> ~93ms
  main() -> window visible:       ~193ms -> ~173ms
2026-08-09 19:54:28 -07:00
Mitchell Hashimoto
db6d20dce1 apprt/embedded: initialize the TIS keymap lazily
The embedded apprt App init created the keyboard layout keymap
eagerly, which requires talking to the text input system (TIS). The
first TIS call in a process is slow: 6.6ms measured inside
ghostty_app_new during app launch (up to ~30ms in a cold process).

The keymap is only used for keyboard layout queries (option-as-alt
detection, layout change reload), which happen once keyboard events
are flowing. By then AppKit has already warmed TIS and the call is
effectively free (~0.2us measured warm). So initialize the keymap
lazily on first use. If the layout changes before the keymap was ever
created, reload is a no-op since lazy init picks up the current
layout.

Measured on macOS (Apple Silicon) with local timing instrumentation
during app launch:

  embedded app init before: ~6.7ms (keymap 6614us)
  embedded app init after:  ~60us (config clone only)
2026-08-09 19:54:27 -07:00
Mitchell Hashimoto
de1336fadd renderer/metal: warm up command queue and shader pipelines
Extend the Metal portion of the startup warmup thread to also create
(and discard) a command queue and build (and discard) the shader
pipelines for both pixel formats we may use (which one is used
depends on the blending config). The first command queue for a device
and the first render pipeline state creations pay one-time driver
setup and shader compilation costs; once warm, the real creations
during surface initialization hit driver and OS caches.

Measured on macOS (Apple Silicon) with local timing instrumentation
during app launch, first surface renderer initialization:

  queue creation:  717us -> 93us
  pipeline builds: 1023us -> 347us
  renderer init total: 2777us -> 1466us
2026-08-09 19:54:27 -07:00
Mitchell Hashimoto
131b293dbb renderer/metal: warm up the Metal device machinery at app creation
The first Metal device query in a process (MTLCopyAllDevices) takes
multiple milliseconds; once the framework is warm, subsequent queries
are effectively free (measured ~15ms cold, ~1us warm in isolation).
This cost was paid during the first surface's renderer
initialization, on the critical path to the first window.

Measured on macOS (Apple Silicon) with local timing instrumentation
during app launch, first surface renderer initialization:

  GraphicsAPI.init before: 4227us (device query ~3.5ms)
  GraphicsAPI.init after:  ~900us (device query 20-25us)
2026-08-09 19:54:27 -07:00
Mitchell Hashimoto
c454a3bf47 font: support warmup threads
The first CoreText font query in a process initializes the system
font database, which takes multiple milliseconds (~7ms measured in an
isolated process; 2-4ms observed inside Ghostty startup). This cost
was previously paid during the first surface's font grid
initialization, on the critical path to the first window.

App.create can now spawn a background thread that performs the warmup.
2026-08-09 19:53:35 -07:00
Mitchell Hashimoto
afc79b8ccf font: look up Apple Color Emoji by exact name on macOS
The Apple Color Emoji fallback font was discovered with the generic
discovery path, which builds a CTFontCollection and runs system-wide
font matching. Since we know the exact font we want, we can look it
up directly with CTFontCreateWithName instead.
2026-08-09 19:30:25 -07:00
Mitchell Hashimoto
3225e9ebb1 macos: cache unified logging loggers per scope
The logFn for macOS unified logging created and released an os_log_t
logger on every single log call. Loggers are now cached per log scope for 
the process lifetime via an atomic pointer (a creation race wastes at most 
one create).

Measured on macOS (Apple Silicon) with local timing instrumentation
during app launch, the version-info logging block in global.init:

  before: 1070us-2629us
  after:   858us-1319us
2026-08-09 19:26:53 -07:00
Mitchell Hashimoto
a82637b53a crash: resolve sentry directories on the init thread
Sentry initialization already ran on a separate thread, but the cache
and state directory resolution happened on the main thread before
spawning it. On macOS the cache dir resolution calls NSFileManager
URLForDirectory:inDomain:appropriateForURL:create:error: which takes
multiple milliseconds and was the single largest cost in global.init.

All directory resolution now happens on the init thread. 

  before: 2967us-4018us
  after:    30us-70us (env map snapshot + thread spawn)

global.init total drops from ~3.4-5.0ms to ~0.4-1.0ms.
2026-08-09 18:32:48 -07:00
Jeffrey C. Ollie
bb876a0d28 build(deps): bump dorny/paths-filter from 4.0.2 to 4.0.3 (#13718)
Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from
4.0.2 to 4.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dorny/paths-filter/releases">dorny/paths-filter's
releases</a>.</em></p>
<blockquote>
<h2>v4.0.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Update Outputs in readme to account for the 'every'
predicate-quantifier by <a
href="https://github.com/hintron"><code>@​hintron</code></a> in <a
href="https://redirect.github.com/dorny/paths-filter/pull/247">dorny/paths-filter#247</a></li>
<li>fix: scope base-ignored warning to API path by <a
href="https://github.com/saschabratton"><code>@​saschabratton</code></a>
in <a
href="https://redirect.github.com/dorny/paths-filter/pull/319">dorny/paths-filter#319</a></li>
<li>docs: add contents permission to PR example by <a
href="https://github.com/134130"><code>@​134130</code></a> in <a
href="https://redirect.github.com/dorny/paths-filter/pull/248">dorny/paths-filter#248</a></li>
<li>feat: add 'some-with-excludes' predicate quantifier by <a
href="https://github.com/arxeiss"><code>@​arxeiss</code></a> in <a
href="https://redirect.github.com/dorny/paths-filter/pull/322">dorny/paths-filter#322</a></li>
<li>Document safe handling of file list outputs in workflows by <a
href="https://github.com/dorny"><code>@​dorny</code></a> in <a
href="https://redirect.github.com/dorny/paths-filter/pull/326">dorny/paths-filter#326</a></li>
</ul>
<h2>Security</h2>
<ul>
<li>Escape multi-line filenames in list-files shell and csv output] by
<a href="https://github.com/ken-matsui"><code>@​ken-matsui</code></a>
and <a href="https://github.com/tjswlsgg"><code>@​tjswlsgg</code></a> in
<a
href="https://github.com/advisories/GHSA-7hc6-8hq5-9q2m">https://github.com/advisories/GHSA-7hc6-8hq5-9q2m</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/hintron"><code>@​hintron</code></a> made
their first contribution in <a
href="https://redirect.github.com/dorny/paths-filter/pull/247">dorny/paths-filter#247</a></li>
<li><a href="https://github.com/134130"><code>@​134130</code></a> made
their first contribution in <a
href="https://redirect.github.com/dorny/paths-filter/pull/248">dorny/paths-filter#248</a></li>
<li><a href="https://github.com/arxeiss"><code>@​arxeiss</code></a> made
their first contribution in <a
href="https://redirect.github.com/dorny/paths-filter/pull/322">dorny/paths-filter#322</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dorny/paths-filter/compare/v4...v4.0.3">https://github.com/dorny/paths-filter/compare/v4...v4.0.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md">dorny/paths-filter's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.0.3</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/326">Document
safe handling of file list outputs in workflows</a></li>
<li><a href="https://github.com/advisories/GHSA-7hc6-8hq5-9q2m">Escape
multi-line filenames in list-files shell and csv output</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/322">Add
'some-with-excludes' predicate quantifier</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/248">Add
contents permission to PR example</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/319">Scope
base-ignored warning to API path</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/247">Update
outputs in readme to account for the 'every'
predicate-quantifier</a></li>
</ul>
<h2>v4.0.2</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/317">Work
around git dubious ownership errors in container jobs</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/303">Use
rev-parse instead of branch --show-current for older git compat</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/282">Fix
warning message</a></li>
</ul>
<h2>v4.0.1</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/255">Support
merge queue</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/294">Update
action runtime to node24</a></li>
</ul>
<h2>v3.0.4</h2>
<ul>
<li><a href="https://github.com/advisories/GHSA-7hc6-8hq5-9q2m">Escape
multi-line filenames in list-files shell and csv output</a></li>
</ul>
<h2>v3.0.3</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/279">Add
missing predicate-quantifier</a></li>
</ul>
<h2>v3.0.2</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/224">Add
config parameter for predicate quantifier</a></li>
</ul>
<h2>v3.0.1</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/133">Compare
base and ref when token is empty</a></li>
</ul>
<h2>v3.0.0</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/210">Update to
Node.js 20</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/215">Update
all dependencies</a></li>
</ul>
<h2>v2.11.1</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/167">Update
@​actions/core to v1.10.0 - Fixes warning about deprecated
set-output</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/168">Document
need for pull-requests: read permission</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/164">Updating
to actions/checkout@v3</a></li>
</ul>
<h2>v2.11.0</h2>
<ul>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/157">Set
list-files input parameter as not required</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/161">Update
Node.js</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/162">Fix
incorrect handling of Unicode characters in exec()</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/163">Use
Octokit pagination</a></li>
<li><a
href="https://redirect.github.com/dorny/paths-filter/pull/160">Updates
real world links</a></li>
</ul>
<h2>v2.10.2</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ceb8a2b8f2"><code>ceb8a2b</code></a>
Update CHANGELOG.md for v4.0.3 and v3.0.4 (<a
href="https://redirect.github.com/dorny/paths-filter/issues/327">#327</a>)</li>
<li><a
href="ef09b88f3e"><code>ef09b88</code></a>
Document safe handling of file list outputs in workflows (<a
href="https://redirect.github.com/dorny/paths-filter/issues/326">#326</a>)</li>
<li><a
href="44adc5b06d"><code>44adc5b</code></a>
Merge commit from fork</li>
<li><a
href="4711b7a31b"><code>4711b7a</code></a>
feat: add 'some-with-excludes' predicate quantifier (<a
href="https://redirect.github.com/dorny/paths-filter/issues/322">#322</a>)</li>
<li><a
href="93c889f9e5"><code>93c889f</code></a>
fix: escape multi-line filenames in list-files shell and csv output</li>
<li><a
href="b41dfa943b"><code>b41dfa9</code></a>
docs: add contents permission to PR example (<a
href="https://redirect.github.com/dorny/paths-filter/issues/248">#248</a>)</li>
<li><a
href="9af6e5a9d0"><code>9af6e5a</code></a>
fix: scope base-ignored warning to API path (<a
href="https://redirect.github.com/dorny/paths-filter/issues/319">#319</a>)</li>
<li><a
href="cae9006b65"><code>cae9006</code></a>
docs: update outputs in readme to account for the 'every'
predicate-quantifie...</li>
<li>See full diff in <a
href="7b450fff21...ceb8a2b8f2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dorny/paths-filter&package-manager=github_actions&previous-version=4.0.2&new-version=4.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>
2026-08-09 19:56:20 -05:00
dependabot[bot]
c285d3c244 build(deps): bump dorny/paths-filter from 4.0.2 to 4.0.3
Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 4.0.2 to 4.0.3.
- [Release notes](https://github.com/dorny/paths-filter/releases)
- [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md)
- [Commits](7b450fff21...ceb8a2b8f2)

---
updated-dependencies:
- dependency-name: dorny/paths-filter
  dependency-version: 4.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 00:16:56 +00:00
Mitchell Hashimoto
fea378e565 libghostty: reduce binary size ~16% (macOS), ~22% (Linux) (#13715)
This shrinks the binary size of libghostty-vt by **16% on aarch64 macOS
and 22% on x86_64 Linux**. It also shrinks the in-memory footprint by
~256KB per thread + ~20KB per app. All benchmarks remain the same, no
speedups or slowdowns.

Each commit message explains an individual tactic used, but to
summarize:

1. **No stack traces in release panic handlers (~160KB).** This requires
the Zig stack unwind and symbolication logic. I don't think this makes
sense in an embedded library because the embedder should handle this.

2. **An alternate `std.Io` implementation called `TinyIo` (~100KB to
200KB).** See later... since this is the big one.

3. **Disable recursive Parser.Action logging (~35KB).** We now only log
the top-level fields of a Parser.Action, which lowers the amount of
`std.fmt` codegen significantly.

All sizes above are aarch64 macOS and x86_64 Linux ReleaseFast
libghostty builds.

## TinyIo

I think the main complexity introduction here is our alternate `std.Io`
implementation `TinyIo`. This is an IO implementation that implements IO
operations we need through direct syscalls and does not support
concurrency or any other options like network, progress, etc.

Why? Because of the way `std.Io` works through vtable dispatch, the
linker and dead code removal can't prune ANY of the function pointers.
So our binary has full implementations of all the networking,
concurrency, etc. related code even though we don't use it.

This has a runtime effect too: even though we put `std.Io.Threaded` in
single-threaded mode, it still allocates ~256KB of TLS _per thread_, and
its raw struct state is ~18KB (versus 80 _bytes_ for `TinyIo`).

For future maintenance: I exhaustively implemented the vtable rather
than use the failing vtable from Zig stdlib so any Zig changes to add
new fields to this error so we can determine if we want to support it or
not.
2026-08-09 14:23:15 -07:00
Lukas
49e4df7833 macOS: rework for #12712 and #13645 2026-08-09 23:19:47 +02:00
Mitchell Hashimoto
a15dddae2a TinyIo: fix failing tests in CI 2026-08-09 14:05:41 -07:00
Mitchell Hashimoto
d524a0fe64 libghostty: guard release builds against std debug Io
Release libghostty-vt builds carefully avoid referencing
std.Options.debug_io because its default implementation is
std.Io.Threaded, and referencing that vtable keeps every operation
Threaded supports linked into the binary: roughly 110KB of unreachable
code. Nothing references it today, but any std.debug.print,
std.debug.lockStderr, or std.log default-handler call added to
release-reachable code would silently reintroduce all of it.

Declare std_options_debug_io in the root module so std uses our value
instead of constructing the Threaded default. Development builds
(Debug, ReleaseSafe, tests) forward the std default so std.debug.print
and friends work normally. ReleaseFast and ReleaseSmall builds declare
it as a @compileError: since std only analyzes the declaration lazily,
at the moment something references a debug Io code path, the error
fires exactly at the offending reference, turning a silent size
regression into a build failure with a message explaining the
alternatives.

Release binaries are byte-identical when the guard is not tripped.
2026-08-09 13:55:56 -07:00
Mitchell Hashimoto
82df79ec8d libghostty: replace Io.Threaded with custom Io impl (TinyIo)
Add a new Io implementation `TinyIo` that only supports the operations
we need and doesn't support concurrency. This shrinks the binary size
of libghostty by anywhere from ~100KB (macOS) to ~200KB (Linux) and 
runtime memory requirements by over 256KB (the thread-local storage
`std.Io.Threaded` creates plus the 18KB threaded structure is gone).

`TinyIo` is POSIX-only: Windows keeps std.Io.Threaded, and on
freestanding targets (wasm) TinyIo degrades to std.Io.failing
behavior just like before.  

It is also exported from the Zig module as `ghostty.TinyIo` so
Zig embedders can opt into the same size win when constructing
terminals.
2026-08-09 13:55:56 -07:00
Mitchell Hashimoto
579db41893 libghostty: log to stderr with raw writes, not lockStderr
see the prior commit, but most importantly this makes it so we can
replace our IO impl from Threaded.
2026-08-09 12:47:09 -07:00
Mitchell Hashimoto
f36854345e libghostty: skip stack traces in release panic handlers
The default Zig panic handler unwinds the stack and symbolicates it,
which drags in ~160KB worth of helper machinery. For an embedded library
this isn't great because the embedder's environment should be providing
this as long as libghostty is compiled with symbols or has a way to
symbolize.

Change ReleaseFast/ReleaseSmall libghostty-vt builds to use a custom
panic handler. Debug/ReleaseSafe keep the full Zig handlers.

This shrinks libghostty-vt on macOS by ~160KB (~9%).
2026-08-09 12:36:58 -07:00
Mitchell Hashimoto
0aa71d02ed terminal: reduce Parser.Action log formatting code size
Shrinks libghostty-vt dylib by ~2%.
2026-08-09 10:36:27 -07:00
Jeffrey C. Ollie
9d8fbd15b3 gtk: add +new-tab action (#11762)
This PR adds a `+new-tab` CLI action, useful for automation on GTK. This
mainly re-uses machinery added for the `+new-window`, but adds in a
unique surface ID for identifying surfaces for IPC purposes (and
eliminates use of raw pointers for callbacks from notifications).
2026-08-09 11:31:36 -05:00
Mitchell Hashimoto
2dc08839b1 build: fix Linux Android SDK fallback path (#13705)
Use the standard `~/Android/Sdk` capitalization for the Linux SDK
fallback.

This lets NDK discovery work when neither `ANDROID_NDK_HOME` nor an
`SDK` environment variable is set.
2026-08-09 08:04:28 -07:00
Mitchell Hashimoto
da59a2ec42 macOS: support drag-handle config (#13709)
Closes https://github.com/ghostty-org/ghostty/discussions/12332

Also a tiny rephrasing for the documentation.
2026-08-09 07:35:55 -07:00
Mitchell Hashimoto
349664f031 agents: remove double negative (#13710) 2026-08-09 07:31:48 -07:00
Jon Parise
fde9e281c4 agents: remove double negative 2026-08-09 10:13:44 -04:00
Lukas
74f91d1b43 macOS: support drag-handle config 2026-08-09 15:14:50 +02:00
Leah Amelia Chen
05221c11c9 core,gtk: add drag-handle config (#13706)
Fixes hundreds of complaints about the fact that drag handles cannot be
hidden, on GTK at least.

I'm not sure if we ever made an issue for this? If you come across any
discussions asking for this, please link them here :)
2026-08-09 20:10:38 +08:00
Leah Amelia Chen
850ca8c7b1 gtk: rebind is-split after moving split cross-tree
It turns out we never unbound the split from its original tree after
moving, which means `is-split` in particular is desynced and leads to
hilarious artifacts like how `unfocused-split-*` options just stop
working properly. I only realized this is a thing after the naïve
drag handle config option didn't work properly. Fun!
2026-08-09 16:09:02 +08:00
Leah Amelia Chen
65a3e666ef gtk: drag overlay toggle 2026-08-09 16:09:02 +08:00
Leah Amelia Chen
c6e7e9e4e2 config: add drag-handle 2026-08-09 16:09:00 +08:00
Fredrik Fornwall
16e13a59ae build: fix Linux Android SDK fallback path
Use the standard ~/Android/Sdk capitalization for the Linux SDK fallback.

This lets NDK discovery work when neither ANDROID_NDK_HOME nor an SDK
environment variable is set.

Signed-off-by: Fredrik Fornwall <fredrik@fornwall.net>
2026-08-09 05:37:06 +02:00