This adds two new propeties to make it easy to get the frontmost (main)
window and the focused terminal within a tab. We already had a property
to get the selected tab of a tab group.
If you have multiple splits and start searching naturally the focus
transfers over to the search widget which would apply the unfocused
options. This could make it difficult to view your matches from
searching without re-focusing the surface.
This was discovered when I tested
https://github.com/ghostty-org/ghostty/discussions/11218 (which is a
different issue)
Remove the stale GHOSTTY_SGR_ATTR_RESET_UNDERLINE entry from the C
header and renumber subsequent GhosttySgrAttributeTag values to match
src/terminal/sgr.zig Attribute.Tag ordering.
This fixes misclassified attributes from ghostty_sgr_next for C
consumers that switch on the enum tags from include/ghostty/vt/sgr.h.
Remove the stale GHOSTTY_SGR_ATTR_RESET_UNDERLINE entry from the C header
and renumber subsequent GhosttySgrAttributeTag values to match
src/terminal/sgr.zig Attribute.Tag ordering.
This fixes misclassified attributes from ghostty_sgr_next for C consumers
that switch on the enum tags from include/ghostty/vt/sgr.h.
Add `aid=$pid` to 133;A and 133;D for nested shell tracking, and fix the
state comparison which was incorrectly using `constantly` (comparing a
string to a function, which always evaluated to true).
OSC 133;B (input start) and 133;P;k=r (right prompt) cannot be reliably
implemented at the script level because Elvish escapes control
characters in prompt function output, and writing directly to /dev/tty
has timing issues because Elvish renders its prompts on a background
thread. Full semantic prompt support requires a native implementation:
https://github.com/elves/elvish/pull/1917
See: #10523
Updated translation for Indonesian (id_ID). This is a duplicate of PR
#10794, as the original PR has not been updated since last week. I think
it would be better to merge this updated translation before the 1.3
release.
Add `aid=$pid` to 133;A and 133;D for nested shell tracking, and fix the
state comparison which was incorrectly using `constantly` (comparing a
string to a function, which always evaluated to true).
OSC 133;B (input start) and 133;P;k=r (right prompt) cannot be reliably
implemented at the script level because Elvish escapes control
characters in prompt function output, and writing directly to /dev/tty
has timing issues because Elvish renders its prompts on a background
thread. Full semantic prompt support requires a native implementation:
https://github.com/elves/elvish/pull/1917
See: #10523
Fixes#10760. The snap launcher script was overriding the default system
EGL vendor directories, preventing NVIDIA's proprietary EGL driver from
being discovered. This caused crashes for users with NVIDIA GPUs on snap
systems.
The fix adds the standard host system paths (/etc/glvnd/egl_vendor.d and
/usr/share/glvnd/egl_vendor.d) to __EGL_VENDOR_LIBRARY_DIRS before the
snap-internal path. This is safe for classic confinement snaps since the
host filesystem is fully accessible.
This adds AppleScript support to the macOS app.
AppleScript is still one of the best ways to script macOS apps. It is
more CLI friendly and share-able than Apple Shortcuts and can be used by
other CLI programs like editors (Neovim plugins), launchers
(Raycast/Alfred), etc. It has been heavily requested to introduce more
scriptability into Ghostty and this is a really good, powerful option on
macOS.
> [!NOTE]
>
> I definitely still want to do something cross-platform and more
official as a plugin/scripting API for Ghostty. But native integrations
like this are a goal of Ghostty as well and this implementation is just
some thin logic over already existing internals to expose it.
I plan on merging this ahead of 1.3. Normally I wouldn't ship a feature
so late in the game but this is fairly hermetic (doesn't impact other
systems) and I plan on documenting it as a "preview" feature since the
API and stability are in question.
## Security
Apple secures AppleScript via TCC by asking for permission when a script
is run whether an app is allowed to be controlled. Because this is
always asked, we do default AppleScript to being enabled. This is
typical of macOS native applications already.
AppleScript can be wholesale disabled via `macos-applescript = false`.
## Future
There is a big question of what else to expose to this to make it
useful. I'm going to make a call to action for the 1.3 cycle to gather
feedback on this, since we can expose mostly anything!
## Capabilities
### Objects
| Object | Key Properties | Key Elements |
| --- | --- | --- |
| `application` | `name`, `frontmost`, `version` | `windows`,
`terminals` |
| `window` | `id`, `name`, `selected tab` | `tabs`, `terminals` |
| `tab` | `id`, `name`, `index`, `selected` | `terminals` |
| `terminal` | `id`, `name`, `working directory` | None |
### Commands
| Category | Command | Purpose |
| --- | --- | --- |
| Application | `perform action` | Execute a Ghostty action string on a
terminal. |
| Configuration | `new surface configuration` | Create/copy a reusable
surface configuration record. |
| Creation | `new window` | Open a new Ghostty window (optional
configuration). |
| Creation | `new tab` | Open a new tab (optional target
window/configuration). |
| Layout | `split` | Split a terminal and return the new terminal. |
| Focus/Selection | `focus` | Focus a terminal. |
| Focus/Selection | `activate window` | Bring a window to front and
activate app. |
| Focus/Selection | `select tab` | Select and foreground a tab. |
| Lifecycle | `close` | Close a terminal. |
| Lifecycle | `close tab` | Close a tab. |
| Lifecycle | `close window` | Close a window. |
| Input | `input text` | Paste-style text input into terminal. |
| Input | `send key` | Send key press/release with optional modifiers. |
| Input | `send mouse button` | Send mouse button press/release. |
| Input | `send mouse position` | Send mouse position update. |
| Input | `send mouse scroll` | Send scroll event with
precision/momentum options. |
| Standard Suite | `count`, `exists`, `quit` | Standard Cocoa scripting
functionality. |
## Examples
### Layout
```AppleScript
-- Tmux-like layout: 4 panes in one tab (2x2), each with a job.
set projectDir to POSIX path of (path to home folder) & "src/ghostty"
tell application "Ghostty"
activate
-- Reusable config for all panes.
set cfg to new surface configuration
set initial working directory of cfg to projectDir
-- Create the first window/tab + split into 4 panes.
set win to new window with configuration cfg
set paneEditor to terminal 1 of selected tab of win
set paneBuild to split paneEditor direction right with configuration cfg
set paneGit to split paneEditor direction down with configuration cfg
set paneLogs to split paneBuild direction down with configuration cfg
-- Seed each pane with a command.
input text "nvim ." to paneEditor
send key "enter" to paneEditor
input text "zig build -Demit-macos-app=false" to paneBuild
input text "git status -sb" to paneGit
input text "tail -f /tmp/dev.log" to paneLogs
send key "enter" to paneLogs
-- Put focus back where you want to type.
focus paneEditor
end tell
```
### Broadcast Commands
```AppleScript
-- Run one command across every open terminal surface.
set cmd to "echo sync && date"
tell application "Ghostty"
set allTerms to terminals
repeat with t in allTerms
input text cmd to t
send key "enter" to t
end repeat
display dialog ("Broadcasted to " & (count of allTerms) & " terminal(s).")
end tell
```
### Jump by Working Directory
```applescript
-- Find the first terminal whose cwd contains this text.
set needle to "ghostty"
tell application "Ghostty"
set matches to every terminal whose working directory contains needle
-- Fallback: try title if cwd had no match.
if (count of matches) = 0 then
set matches to every terminal whose name contains needle
end if
if (count of matches) = 0 then
display dialog ("No terminal matched: " & needle)
else
set t to item 1 of matches
focus terminal t
input text "echo '[focused by AppleScript]'" to t
send key "enter" to t
end if
end tell
```
Change split, focus, close, activate window, select tab, close tab, and
close window commands to accept their target object as a direct parameter
instead of a named parameter. This produces natural AppleScript syntax:
activate window (window 1)
close tab (tab 1 of window 1)
split (terminal 1) direction right
instead of the awkward redundant form:
activate window window (window 1)
close tab tab (tab 1 of window 1)
split terminal (terminal 1) direction right
The implementation moves command logic from NSScriptCommand subclasses
into responds-to handler methods on ScriptTerminal, ScriptWindow, and
ScriptTab, which is the standard Cocoa Scripting pattern for commands
whose direct parameter is an application class.
Add scripting dictionary commands for activating windows, selecting tabs,
closing tabs, and closing windows.
Implement the corresponding Cocoa AppleScript command handlers and expose
minimal ScriptWindow/ScriptTab helpers needed to resolve live targets.
Verified by building Ghostty and running osascript commands against the
absolute Debug app path to exercise all four new commands.
Add a `surface configuration` record type to the scripting dictionary,
implement `new surface configuration` (with optional copy-from), and allow
`new window` to accept `with configuration`.
Add a `new window` command to the scripting dictionary and wire it to
`NSApplication` so AppleScript can create Ghostty windows.
The command returns a scripting `window` object for the created window,
with a fallback to a direct wrapper when AppKit window ordering has not
yet refreshed in the current run loop.
Add a `name` property (code `pnam`, cocoa key `title`) to the window, tab,
and terminal classes in the scripting definition. This follows the standard
Cocoa scripting convention where `name`/`pnam` maps to the `title` KVC key,
matching what Apple does in CocoaStandard.sdef for NSWindow.
Also fixes the pre-existing terminal `title` property which used a custom
four-char code (`Gttl`) that AppleScript could not resolve directly — only
via `properties of terminal`. All three classes now use the standard `pnam`
code so `name of window 1`, `name of tab 1 of window 1`, and
`name of terminal 1` all work correctly.
Expose terminal surfaces as elements on both ScriptWindow and ScriptTab,
allowing AppleScript to enumerate terminals scoped to a specific window
or tab (e.g. `terminals of window 1`, `terminals of tab 1 of window 1`).
Changes:
- Add `<element type="terminal">` to window and tab classes in Ghostty.sdef
- Add `terminals` computed property and `valueInTerminalsWithUniqueID:`
lookup to ScriptWindow (returns all surfaces across all tabs)
- Add `terminals` computed property and `valueInTerminalsWithUniqueID:`
lookup to ScriptTab (returns surfaces within that tab)
The application class in Ghostty.sdef was missing a responds-to
declaration for the quit command. Apple's Cocoa Scripting requires
the application class to explicitly declare it responds to quit via
handleQuitScriptCommand: for the aevtquit event to be dispatched.
Add standard Cocoa scripting definitions to the AppleScript dictionary:
- Application properties: name, frontmost, version
- Standard Suite commands: exists, quit
These are backed by built-in Cocoa scripting classes (NSExistsCommand,
NSQuitCommand) and standard NSApplication KVC keys, so no Swift code
changes are needed.
Add five new AppleScript commands to Ghostty.sdef mirroring the existing
App Intents for terminal input:
- `input text`: send text to a terminal as if pasted
- `send key`: simulate a keyboard event with optional action and modifiers
- `send mouse button`: send a mouse button press/release event
- `send mouse position`: send a mouse cursor position event
- `send mouse scroll`: send a scroll event with precision and momentum
A shared `input action` enumeration (press/release) is used by both key
and mouse button commands. Modifier keys are passed as a comma-separated
string parameter (shift, control, option, command).