According to the [Contributors' Guides for
I18n](https://github.com/ghostty-org/ghostty/blob/main/po/README_CONTRIBUTORS.md):
> This file must stay in sync with the list of translatable strings
present in source code or Blueprints at all times. A CI action would be
run for every PR, which checks if the translation template requires any
updates.
Currently, the CI passes even if the POT file is outdated and has not
been updated for a long time. Add `set -euxo pipefail` to
`check-translations.sh` to ensure the CI fails when contributors do not
update the POT file with their changes.
Before:
```
❯ bash .github/scripts/check-translations.sh; echo "status code: $?"
po/com.mitchellh.ghostty.pot:87: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:91: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:95: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:99: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:103: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:110: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:114: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:126: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:297: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:301: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
po/com.mitchellh.ghostty.pot:305: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.YxAlAg28KW
msgcmp: found 11 fatal errors
status code: 0
```
After:
```
❯ bash .github/scripts/check-translations.sh; echo "status code: $?"
++ mktemp
+ old_pot=/var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
+ cp po/com.mitchellh.ghostty.pot /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
+ zig build update-translations
+ msgcmp /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR po/com.mitchellh.ghostty.pot --use-untranslated
po/com.mitchellh.ghostty.pot:87: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:91: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:95: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:99: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:103: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:110: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:114: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:126: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:297: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:301: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
po/com.mitchellh.ghostty.pot:305: this message is used but not defined in /var/folders/sq/_thdhjtd653cbg8c_8_4r6mc0000gn/T/tmp.OfLyQ8IKsR
msgcmp: found 11 fatal errors
status code: 1
```
Add `set -euxo pipefail` to `check-translations.sh` to ensure the CI
fails when contributors do not update the POT file with their changes.
Currently, the CI passes even if the POT file is outdated and the POT
has not been updated for a long time.
This adds the ability to use two fingers on a touchpad to scroll left or
right on a Ghostty window to change tab pages. Uses the same basic
machinery as scrolling up and down the scrollback buffer. Scrolling
pages does not wrap around at the start or end of the tabs.
When we're building an input string that's explicitly meant to be used
as a shell command, quote it using the same logic as Python's
`shlex.quote` function.
This specifically addresses issues we've seen when open(1)'ing Ghostty
with filename arguments that contain spaces.
See #2633, #3030
#### [Core Audio Tap API](https://github.com/insidegui/AudioCap)
There's a new NS permission needed for macos (>14.x) to capture system
audio with the `Core Audio Tap API`. Previously desktop-audio capture
was handled by a different permissions API with GUI & preferences. Both
APIs continue to exist but this newer one has a NS permission we should
include. The "geniuses" have spoken and this API doesn't have public
methods for querying the permissions state. As a strong side-effect of
this "feature", failures due to lack of NS permissions are likely to be
handled poorly by software makers.
---
I've spent [the last 5
days](https://github.com/electron/electron/issues/49607) reviewing
C++/Objective-C Chromium source to hone in on why my DesktopAudioCapture
broke on an electron version update. It is in-fact chromium [started
using](source.chromium.org/chromium/chromium/src/+/ad17e8f8b93d5f34891b06085d373a668918255e)
this new `Core Audio Tap API` and has no fallback to the old API if the
new one fails.
Should be your easiest code-review of the week. Shoutout to the
maintainers/creator, big fan of this Zig based terminal with good shader
support.
When we're building an input string that's explicitly meant to be used
as a shell command, quote (escape) it using the same logic as Python's
shlex.quote function.
This specifically addresses issues we've seen when open(1)'ing Ghostty
with filename arguments that contain spaces.
[Reference](https://github.com/insidegui/AudioCap). This is Apple's latest system for allowing apps to access loopback audio streams eg: Desktop-Audio, Window-Audio, etc...
This adds the ability to use two fingers on a touchpad to scroll left
or right on a Ghostty window to change tab pages. Uses the same basic
machinery as scrolling up and down the scrollback buffer. Scrolling
pages does not wrap around at the start or end of the tabs.
Based on some prior private discussion -- this is needed for the GitHub
bot to correctly dispatch, track, and collect workflow runs based on the
triggering PR.
I'm not sure if covering only `build-macos` is enough and if outputs
from other jobs (like `build-macos-debug-slow`) are also useful here. I
went with just `build-macos` since it's the only one to also link a DMG
(and it sounds the most basic of course).
Related to #1972
The URL regex for file path detection requires paths to start with
`../`, `./`, or `/`. For bare relative paths like
`"src/config/url.zig"`, the regex could only match starting at the first
`/`, producing `"/config/url.zig"` as a result — always dropping the
first part of the path.
Fix: added a third top-level alternative to the regex. This matches bare
relative paths where:
1. The first component is word characters (possibly with dots/dashes):
`[\w][\w\-.]*\/`
2. The remaining path must contain a dot (via positive lookahead) — this
requires a file extension to avoid false positives on text like
`"input/output"`
3. Add a `(?<!\w)\/` instead of `\/` in the existing prefix group — the
standalone `/` prefix now requires that `/` is not preceded by a word
character. This prevents `"input/output"` from falsely matching
`"/output"`
Test cases added:
- src/config/url.zig → matches fully
- app/folder/file.rb:1 → matches with line number
- modified: src/config/url.zig → matches only the path part
- lib/ghostty/terminal.zig:42:10 → matches with line:col
- some-pkg/src/file.txt more text → stops before trailing text
- input/output and foo/bar → correctly do not match (no file extension)
The issue was nailed down here:
https://github.com/ghostty-org/ghostty/issues/1972#issuecomment-3845717672
Related to #1972
The URL regex for file path detection requires paths to start with
`../`, `./`, or `/`. For bare relative paths like
`"src/config/url.zig"`, the regex could only match starting at the first
`/`, producing `"/config/url.zig"` as a result — always dropping the
first part of the path.
Fix: added a third top-level alternative to the regex. This matches bare
relative paths where:
1. The first component is word characters (possibly with dots/dashes):
`[\w][\w\-.]*\/`
2. The remaining path must contain a dot (via positive lookahead) — this
requires a file extension to avoid false positives on text like
input/output
3. Add a `(?<!\w)\/` instead of `\/` in the existing prefix group — the
standalone `/` prefix now requires that `/` is not preceded by a word
character. This prevents `"input/output"` from falsely matching
`"/output"`
Test cases added:
- src/config/url.zig → matches fully
- app/folder/file.rb:1 → matches with line number
- modified: src/config/url.zig → matches only the path part
- lib/ghostty/terminal.zig:42:10 → matches with line:col
- some-pkg/src/file.txt more text → stops before trailing text
- input/output and foo/bar → correctly do not match (no file extension)
The issue was nailed down here:
https://github.com/ghostty-org/ghostty/issues/1972#issuecomment-3845717672
We always add GHOSTTY_SHELL_INTEGRATION_XDG_DIR to XDG_DATA_DIRS with a
tailing colon (via our prependEnv routine), so we can greatly simplify
this cleanup code with a single str:replace call.
We always add GHOSTTY_SHELL_INTEGRATION_XDG_DIR to XDG_DATA_DIRS with a
tailing colon (via our prependEnv routine), so we can greatly simplify
this cleanup code with a single str:replace call.
Related to #1966
This adds support for OSC133 `cl=line` (single line movement with
left/right arrow keys) and modifies our bash and zsh shell integration
to advertise support for it. With this, bash and zsh both support click
to move at the prompt without any modifiers:
https://github.com/user-attachments/assets/7f6cb0b8-390c-4136-8c25-059b21b138c5
This also removes our legacy `promptPath` and related functionality
(pressing alt) since this is superior and there's no reason to keep that
around.
Fixes#10522
This also fixes possible runtime safety crashes. Whenever the underlying
size information doesn't match what our renderer or grid see, then we
should deinit and reinit.
Fixes#10522
This also fixes possible runtime safety crashes. Whenever the underlying
size information doesn't match what our renderer or grid see, then we
should deinit and reinit.
This adds support for the `OSC 133 A click_events=1` extension
introduced by Kitty and supported by Fish.[^1]
**What this means:** If the shell advertises `click_events=1` support,
Ghostty will _unconditionally_ (no modifier required) send mouse events
to the shell for clicks on a prompt line, delegating to the supporting
shell to move the cursor as needed. For Fish 4.1+ this means that
clicking on the prompt line moves the cursor (see demo video below).
This PR also contains:
* A minor fix in `cl` parsing but we don't yet implement the logic there
* Updated inspector to show the semantic prompt click mode
## Demo
https://github.com/user-attachments/assets/03ef8975-7ad9-441f-aaa2-9d0eb5c5e36d
## Implementation Details
`click_events` is wildly underspecified, so here are the details the
best I understand them. This itself is not a specification (I omit
details) but adds some more context to it.
The `click_events=1` option can be specified with `OSC 133 A` (Ghostty
also allows it on OSC 133 N). When that is specified, it flags for all
future prompts that the screen supports click events for semantic
prompts. If both `click_events` and `cl` are specified, `click_events`
takes priority if true. If `click_events=0` (disable), then any set `cl`
will take priority.
When a mouse click comes in, we check for the following conditions:
1. The screen supports click events
2. The screen cursor is currently at a prompt
3. The mouse click was at or below the starting prompt line of the
current prompt
If those are met, we encode an SGR mouse event with: left button, press,
coordinates of click. It is up to the shell after that to handle it. Out
of prompt bounds SGR events are possible (specifically below). The shell
should robustly handle this.
[^1]: I don't know any other terminal or shell that supports it at the
moment.
Wrapping `use ghostty-integration` in a `try .. catch` here makes this
suggestion more resilient to environments where we didn't inject our
resource directory into XDG_DATA_DIRS (but are still running Ghostty).
The ssh wrapper previously used a separate set_ssh_terminfo function
that returned a record to be merged, which result in some redundant
control flow and TERM assignments.
This inlines the terminfo logic and builds env/opts incrementally based
on feature flags. TERM is set to a fallback early and only overridden on
success, which simplifies our error handling and avoids mutable variable
capture issues in closures.
Lastly, warnings are now consistently written to stderr, and I made
various other control flow and syntax improvements.
Wrapping `use ghostty-integration` in a `try .. catch` here makes this
suggestion more resilient to environments where we didn't inject our
resource directory into XDG_DATA_DIRS (but are still running Ghostty).
This reporting shouldn't have been tied to the 'title' shell features.
That's a different feature where we change the window title (icon) to
reflect the current command using OSC 2.
This reporting shouldn't have been tied to the 'title' shell features.
That's a different feature where we change the window title (icon) to
reflect the current command using OSC 2.
Bumps
[namespacelabs/nscloud-setup-buildx-action](https://github.com/namespacelabs/nscloud-setup-buildx-action)
from 0.0.21 to 0.0.22.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f5814dcf37"><code>f5814dc</code></a>
Add tag input for named builder selection (<a
href="https://redirect.github.com/namespacelabs/nscloud-setup-buildx-action/issues/14">#14</a>)</li>
<li><a
href="a204134a6b"><code>a204134</code></a>
build(deps): bump lodash from 4.17.21 to 4.17.23 (<a
href="https://redirect.github.com/namespacelabs/nscloud-setup-buildx-action/issues/13">#13</a>)</li>
<li>See full diff in <a
href="a7e5254161...f5814dcf37">compare
view</a></li>
</ul>
</details>
<br />
[](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 merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@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>
Fixes#10524
This changes our inspector from being renderer-change driven to being
FPS driven. Both macOS and GTK now draw the inspector at most at 30 FPS
on a timer. Details between platforms are slightly different and covered
later.
The motivation for this is that triggering an inspector redraw on frame
update was causing _too many_ draws, leading to high CPU usage. Further,
terminal change isn't a good proxy for all the state that the inspector
shows, and tracking changes to all those to trigger a redraw is just a
lot of complexity.
Instead, moving to a standard, game-like framerate driven redraw
simplifies a lot. It does cost some CPU when idle, but actually lowers
our CPU under normal usage since it's rendering less often (30 FPS isn't
much for what we're doing).
**For macOS,** this uses CADisplayLink, so the refresh rate is variable.
I've seen macOS drop it to 1fps when there isn't much happening, which
is nice. We also setup an occlusion event so when the window is fully
occluded we stop rendering entirely.
**For GTK,** the tools to control this are limited. We do a standard
max-30 FPS tick redraw but can't support occlusion beyond what the
window server supports. For Wayland, I believe we get it for free
(occluded windows aren't drawn).