Commit Graph

14517 Commits

Author SHA1 Message Date
ghostty-vouch[bot]
68646d6d22 Update VOUCHED list (#10817)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/10815) from
@jcollie.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-18 06:06:16 +00:00
ghostty-vouch[bot]
9f9c788f57 Update VOUCHED list (#10816)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/10814#issuecomment-3918912439)
from @00-kat.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-18 06:05:41 +00:00
Mitchell Hashimoto
e1f20739d0 add "Set Ghostty as Default Terminal App" on macOS (#10810)
This PR enables iTerm2-like one button "Set Ghostty as Default Terminal
App" functionality on macOS, making it easier to open a directory in
Ghostty, run shell scripts when mouse clicking, etc.
2026-02-17 21:04:55 -08:00
Mahno Kropotkinvich
aee80d208d add "Set Ghostty as Default Terminal App" on macOS 2026-02-17 20:52:49 -08:00
Mitchell Hashimoto
2080de562f GTK: wrap spatial goto around edges (#10811)
Related to #8406 (for GTK only)

Spatial split navigation now wraps at the edges.

We first attempt the nearest spatial target using the existing slot
geometry. If there is no candidate in the requested direction, we
synthesize a wrapped target by shifting the current slot by one full
grid in the opposite direction and reuse the same nearest-distance
logic.

This fake target works because the grid is 1x1, so by moving it a full
grid size in the opposite direction, we effectively wrap around to the
other side of the grid.
2026-02-17 20:40:56 -08:00
Mitchell Hashimoto
969748eb35 split_tree: wrap spatial goto around edges
Fixes #8406

Spatial split navigation now wraps at the edges.

We first attempt the nearest spatial target using the existing slot geometry. 
If there is no candidate in the requested direction, we synthesize a wrapped 
target by shifting the current slot by one full grid in the opposite 
direction and reuse the same nearest-distance logic. 

This fake target works because the grid is 1x1, so by moving it a full
grid size in the opposite direction, we effectively wrap around to the
other side of the grid.
2026-02-17 20:35:25 -08:00
Jeffrey C. Ollie
a3aa9fa136 i18n: Localize Nautilus .py script (#9976)
Closes #9266. 


Big Note: I noticed that this worked properly under `NixOS`, but on my
`Ubuntu` VM it didnt.

The reason is in
[src/build/GhosttyI18n.zig](73a93abf7b/src/build/GhosttyI18n.zig (L24-L31))
because the locale is expected in the `<lang>_<region>` without the
encoding suffix. `<lang>_<region>_<encoding>`
```
        // There is no encoding suffix in the LC_MESSAGES path on FreeBSD,
        // so we need to remove it from `locale` to have a correct destination string.
        // (/usr/local/share/locale/en_AU/LC_MESSAGES)
        const target_locale = comptime if (builtin.target.os.tag == .freebsd)
            std.mem.trimRight(u8, locale, ".UTF-8")
        else
            locale;

```
If i force it to always trim the encoding it works, but I am guessing
its there for a reason ,so maybe some of the maintainer can shed some
light in the best way forward, as I am not an expert in how other
systems deal with it. Here you see `Open in Ghostty` -> Abrir con
Ghostty

<img width="353" height="372" alt="image"
src="https://github.com/user-attachments/assets/2c0266f7-cfb3-49e3-aef1-9e98acb16ad8"
/>


- I wanted to format the `py` file with `ruff` but didnt want to drown
the changes, so maybe something that could be worth doing so that also
our `py` files have std formatting.

> [!NOTE]
> Used AI only for helping me debug where the locales could be and why
was it not detected, but no code help whatsoever
2026-02-17 18:34:53 -06:00
Mitchell Hashimoto
73d6f07c5b gtk: revamp cgroup/transient scope handling (#10611)
This changes the way Ghostty assigns itself and subprocesses to
cgroups and how resource controls are applied.
    
* Ghostty itself no longer modifies it's own cgroup or moves itself
to a transient scope. To modify the main Ghostty process' resource
controls ensure that you're launching Ghostty with a systemd unit and
use the standard systemd methods for overriding and applying changes
to systemd units.
    
* If configured (on by default), the process used to run your command
will be moved to a transient systemd scope after it is forked from
Ghostty but before the user's command is executed. Resource controls
will be applied to the transient scope at this time. Changes to
the `linux-cgroup*` configuration entries will not alter existing
commands. If changes are made to the `linux-cgroup*` configuration
entries commands will need to be relaunched. Resource limits can also
be modified after launch outside of Ghostty using systemd tooling. The
transient scope name can be shown by running `systemctl --user whoami`
in a shell running inside Ghostty.
    
Fixes #2084.
Related to #6669

Example of `systemctl status` showing main Ghostty process and one
surface:
<img width="1132" height="135" alt="Screenshot From 2026-02-07 16-31-14"
src="https://github.com/user-attachments/assets/81dffd0b-8801-4695-adf4-213647cdf0c3"
/>
2026-02-17 16:23:54 -08:00
Mitchell Hashimoto
dada3cd5fd Add MockView and SplitTreeTests (#10778)
This PR introduces unit tests and a supporting Mock NSView for testing
the SplitTree implementation in Swift. It includes 51 tests which
achieve approximately 93.13% (949/1019) coverage of SplitTree.swift's
branches.

<details>
  <summary>Coverage</summary>
  <pre>
./ghostty/macos/Sources/Features/Splits/SplitTree.swift 93.13%
(949/1019)
SplitTree.Path.isEmpty.getter 100.00% (1/1)
SplitTree.isEmpty.getter 100.00% (3/3)
SplitTree.isSplit.getter 100.00% (3/3)
SplitTree.init() 100.00% (3/3)
SplitTree.init(view:) 100.00% (3/3)
SplitTree.contains(_:) 100.00% (4/4)
SplitTree.inserting(view:at:direction:) 100.00% (6/6)
SplitTree.find(id:) 100.00% (4/4)
SplitTree.removing(_:) 93.75% (15/16)
SplitTree.replacing(node:with:) 93.75% (15/16)
SplitTree.focusTarget(for:from:) 82.14% (46/56)
closure #1 in SplitTree.focusTarget(for:from:) 100.00% (1/1)
closure #2 in SplitTree.focusTarget(for:from:) 100.00% (1/1)
closure #3 in SplitTree.focusTarget(for:from:) 100.00% (3/3)
implicit closure #1 in SplitTree.focusTarget(for:from:) 0.00% (0/1)
SplitTree.equalized() 100.00% (5/5)
SplitTree.resizing(node:by:in:with:) 92.00% (69/75)
closure #1 in SplitTree.resizing(node:by:in:with:) 100.00% (1/1)
SplitTree.viewBounds() 100.00% (4/4)
SplitTree.init(from:) 76.00% (19/25)
SplitTree.encode(to:) 100.00% (15/15)
SplitTree.Node.find(id:) 100.00% (13/13)
SplitTree.Node.node(view:) 88.89% (16/18)
SplitTree.Node.path(to:) 100.00% (32/32)
search #1 <A>(_:) in SplitTree.Node.path(to:) 100.00% (27/27)
SplitTree.Node.node(at:) 89.47% (17/19)
SplitTree.Node.inserting(view:at:direction:) 86.84% (33/38)
SplitTree.Node.replacingNode(at:with:) 100.00% (43/43)
replaceInner #1 <A>(current:pathOffset:) in
SplitTree.Node.replacingNode(at:with:) 96.67% (29/30)
SplitTree.Node.remove(_:) 70.27% (26/37)
implicit closure #1 in SplitTree.Node.remove(_:) 100.00% (1/1)
SplitTree.Node.resizing(to:) 100.00% (16/16)
SplitTree.Node.leftmostLeaf() 87.50% (7/8)
SplitTree.Node.rightmostLeaf() 87.50% (7/8)
SplitTree.Node.equalize() 100.00% (4/4)
SplitTree.Node.equalizeWithWeight() 100.00% (30/30)
SplitTree.Node.weightForDirection(_:) 83.33% (10/12)
SplitTree.Node.calculateViewBounds(in:) 100.00% (50/50)
SplitTree.Node.viewBounds() 100.00% (26/26)
SplitTree.Node.spatial(within:) 100.00% (18/18)
SplitTree.Node.dimensions() 80.77% (21/26)
SplitTree.Node.spatialSlots(in:) 100.00% (53/53)
SplitTree.Spatial.slots(in:from:) 100.00% (47/47)
closure #1 in SplitTree.Spatial.slots(in:from:) 100.00% (1/1)
distance #1 <A>(from:to:) in SplitTree.Spatial.slots(in:from:) 100.00%
(6/6)
closure #2 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
implicit closure #1 in closure #2 in SplitTree.Spatial.slots(in:from:)
100.00% (1/1)
closure #3 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
closure #4 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
implicit closure #1 in closure #4 in SplitTree.Spatial.slots(in:from:)
100.00% (1/1)
closure #5 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
closure #6 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
implicit closure #1 in closure #6 in SplitTree.Spatial.slots(in:from:)
100.00% (1/1)
closure #7 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
closure #8 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
implicit closure #1 in closure #8 in SplitTree.Spatial.slots(in:from:)
100.00% (1/1)
closure #9 in SplitTree.Spatial.slots(in:from:) 100.00% (3/3)
SplitTree.Spatial.doesBorder(side:from:) 100.00% (20/20)
closure #1 in SplitTree.Spatial.doesBorder(side:from:) 100.00% (1/1)
closure #2 in SplitTree.Spatial.doesBorder(side:from:) 100.00% (3/3)
static SplitTree.Node.== infix(_:_:) 100.00% (13/13)
SplitTree.Node.init(from:) 66.67% (12/18)
SplitTree.Node.encode(to:) 100.00% (11/11)
SplitTree.Node.leaves() 100.00% (9/9)
SplitTree.makeIterator() 100.00% (3/3)
implicit closure #1 in SplitTree.makeIterator() 100.00% (1/1)
SplitTree.Node.makeIterator() 0.00% (0/3)
SplitTree.startIndex.getter 100.00% (3/3)
SplitTree.endIndex.getter 100.00% (3/3)
implicit closure #1 in SplitTree.endIndex.getter 100.00% (1/1)
SplitTree.subscript.getter 100.00% (5/5)
implicit closure #1 in SplitTree.subscript.getter 100.00% (1/1)
implicit closure #2 in implicit closure #1 in SplitTree.subscript.getter
100.00% (1/1)
implicit closure #3 in SplitTree.subscript.getter 0.00% (0/1)
implicit closure #4 in SplitTree.subscript.getter 0.00% (0/1)
SplitTree.index(after:) 100.00% (4/4)
implicit closure #1 in SplitTree.index(after:) 100.00% (1/1)
implicit closure #2 in SplitTree.index(after:) 0.00% (0/1)
SplitTree.Node.structuralIdentity.getter 100.00% (3/3)
SplitTree.Node.StructuralIdentity.init(_:) 100.00% (3/3)
static SplitTree.Node.StructuralIdentity.== infix(_:_:) 100.00% (3/3)
SplitTree.Node.StructuralIdentity.hash(into:) 100.00% (3/3)
SplitTree.Node.isStructurallyEqual(to:) 100.00% (18/18)
implicit closure #1 in SplitTree.Node.isStructurallyEqual(to:) 100.00%
(1/1)
implicit closure #2 in SplitTree.Node.isStructurallyEqual(to:) 100.00%
(1/1)
SplitTree.Node.hashStructure(into:) 100.00% (14/14)
SplitTree.structuralIdentity.getter 100.00% (3/3)
SplitTree.StructuralIdentity.init(_:) 100.00% (4/4)
static SplitTree.StructuralIdentity.== infix(_:_:) 100.00% (4/4)
implicit closure #1 in static SplitTree.StructuralIdentity.==
infix(_:_:) 100.00% (1/1)
SplitTree.StructuralIdentity.hash(into:) 80.00% (8/10)
static SplitTree.StructuralIdentity.areNodesStructurallyEqual(_:_:)
90.00% (9/10)
  </pre>
</details>

I chose this as a good place to start contributing to Ghostty because I
was curious about the macOS implementation, and there was a specific
request for help with testing (#7879).

My process for writing the tests was basically reading
[SplitTree.swift](./macos/Sources/Features/Splits/SplitTree.swift) to
understand it, then writing tests for each high-level method and
checking against code coverage to capture all the code paths:

## Running
```bash
rm -rf /tmp/ghostty-test.xcresult
xcodebuild -project macos/Ghostty.xcodeproj \
    -scheme GhosttyTest \
    -configuration Debug \
    test \
    -destination 'platform=macOS' \
    -enableCodeCoverage YES \
    -resultBundlePath /tmp/ghostty-test.xcresult \
    -only-testing:GhosttyTests/SplitTreeTests \
    2>&1 | xcbeautify
```

## Coverage
```bash
xcrun xccov view --report /tmp/ghostty-test.xcresult | grep 'SplitTree\.'
```

This was originally implemented in [~38
commits](https://github.com/pouwerkerk/ghostty/pull/1/commits), but I
squashed them down to 1 commit for easier review.

## AI Disclosure
The tests were written by me, but I used Opus 4.6 to explain some parts
of the code, and then finally to provide feedback on the tests. It
suggested tests for `nodeStructuralIdentityInSet` and
`nodeStructuralIdentityDistinguishesLeaves` as well as [the
Parameterized
test](6a0bca43f6),
`resizingAdjustsRatio`, which seemed like a clever way to collapse 12
individual tests into 3 parameterized ones that still run 12 cases
total. I didn't know this feature existed, and it seems like a great way
to write tests that are more maintainable. I read this relatively new
feature in the [Swift
Docs](https://developer.apple.com/documentation/testing/parameterizedtesting).
I find this to be a particularly useful feature of Claude/related
agents, where it can suggest better ways of writing something in a more
idiomatic way, and it taught me something new, which is always fun.

I'm more than happy to continue work on tests for #7879 and always
welcome to any feedback you have.
2026-02-17 16:18:53 -08:00
David Matos
6d71f40907 Fix typo 2026-02-17 23:51:42 +01:00
David Matos
3779e469df Remove extra space 2026-02-17 23:48:16 +01:00
David Matos
1ec54be4d5 Add back comment 2026-02-17 23:46:50 +01:00
ghostty-vouch[bot]
b652a1ced3 Update VOUCHED list (#10804)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/10803) from
@jcollie.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 22:35:12 +00:00
David Matos
2860dd29bb Update po for new localized string 2026-02-17 23:17:07 +01:00
David Matos
aff4348529 Merge branch 'main' into localize-nautilus-script 2026-02-17 23:16:33 +01:00
David Matos
9a3df7e606 Update po/* again from main 2026-02-17 23:16:30 +01:00
Mitchell Hashimoto
41cea500f4 termio: don't auto-generate palette if user didn't customize any (#10802)
This fixes the issue where our palette generation was changing our
default palette. The default palette is based on some well known values
chosen from various terminals and it was a bit jarring to have it
change.

We now only auto-generate the palette if the user has customized at
least one entry.
2026-02-17 13:08:26 -08:00
Mitchell Hashimoto
b25edc3e93 termio: don't auto-generate palette if user didn't customize any
This fixes the issue where our palette generation was changing our
default palette. The default palette is based on some well known values
chosen from various terminals and it was a bit jarring to have it
change.

We now only auto-generate the palette if the user has customized at
least one entry.
2026-02-17 13:04:35 -08:00
Mitchell Hashimoto
16cc707c80 terminal: add osc8 tag handling for HTML formatter (#9415)
The HTML page formatter can now track hyperlink state so <a> tags open
and close when the OSC 8 data changes. Also added a new
`writeHtmlEscaped` helper to keep generated markup safe.

Originally written with Copilot, revised by hand.
2026-02-17 12:54:43 -08:00
Mitchell Hashimoto
62968e423d terminal: clean up HTML OSC8 formatting 2026-02-17 12:48:35 -08:00
Matthew Hrehirchuk
9868bf3789 fix: replaced redundant writeHtmlEscaped method with writeCodepoint 2026-02-17 11:22:39 -08:00
Matthew Hrehirchuk
5e265c9c0d feat: add osc8 to <a> tag handling for html formatter 2026-02-17 11:20:40 -08:00
Jeffrey C. Ollie
cb7e6d5d6d gtk: remove delegate setting from transient scope 2026-02-17 12:54:29 -06:00
Jeffrey C. Ollie
3c4f87abee command: fix tests 2026-02-17 12:54:29 -06:00
Jeffrey C. Ollie
3feef353d8 gtk: clarify in the docs that config-reloads does not affect linux-cgroup* configs 2026-02-17 12:54:29 -06:00
Jeffrey C. Ollie
1342eb5944 gtk: revamp cgroup/transient scope handling
This changes the way Ghostty assigns itself and subprocesses to
cgroups and how resource controls are applied.

* Ghostty itself no longer modifies it's own cgroup or moves itself
to a transient scope. To modify the main Ghostty process' resource
controls ensure that you're launching Ghostty with a systemd unit and
use the standard systemd methods for overriding and applying changes
to systemd units.

* If configured (on by default), the process used to run your command
will be moved to a transient systemd scope after it is forked from
Ghostty but before the user's command is executed. Resource controls
will be applied to the transient scope at this time. Changes to
the `linux-cgroup*` configuration entries will not alter existing
commands. If changes are made to the `linux-cgroup*` configuration
entries commands will need to be relaunched. Resource limits can also
be modified after launch outside of Ghostty using systemd tooling. The
transient scope name can be shown by running `systemctl --user whoami`
in a shell running inside Ghostty.

Fixes #2084.
Related to #6669
2026-02-17 12:54:26 -06:00
Mitchell Hashimoto
10039da572 bash: avoid mapfile for bash 3.2 compatibility (#10800)
We continue to support bash 3.2 for compatibility with /bin/bash on
macOS. `mapfile` was introduced in bash 4.0, so this change introduces a
`read -r`-based helper function for populating COMPREPLY from a list of
lines.

See: #3042
2026-02-17 10:39:35 -08:00
Mitchell Hashimoto
8435dffea9 generate 256 palette (#10554)
Hi Ghostty team,

I believe that terminals should generate the 256-color palette based on
the user's base16 theme.

The rationale and approach is written up
[here](https://gist.github.com/jake-stewart/0a8ea46159a7da2c808e5be2177e1783).

I consider it important that terminals support this out of the box so
that such behaviour can become normal and expected, because then
terminal program maintainers will consider the palette a viable choice.

I have created a PR for kitty and the maintainer seems interested. I
plan to offer this to more terminals soon.
2026-02-17 09:59:41 -08:00
Mitchell Hashimoto
f0a1b05f63 rename config 2026-02-17 09:54:34 -08:00
Mitchell Hashimoto
89dfb76778 terminal: clean up 256 color gen 2026-02-17 09:45:34 -08:00
Pieter Ouwerkerk
ce66bea581 Move MockView to SplitTreeTests itself 2026-02-17 09:37:02 -08:00
Mitchell Hashimoto
fded0e97cb terminal: clean up LAB methods, add tests, comments 2026-02-17 09:36:28 -08:00
Mitchell Hashimoto
50698c5c72 fmt 2026-02-17 09:18:03 -08:00
Jake Stewart
44d2ea25d0 explain mask 2026-02-17 09:17:54 -08:00
Jake Stewart
e268ff9a8b rename param 2026-02-17 09:17:54 -08:00
Jake Stewart
7729714935 refactor 256 color gen 2026-02-17 09:17:54 -08:00
Jake Stewart
5f89228a7a refactor lab colors 2026-02-17 09:17:54 -08:00
Jake Stewart
fad72e0ed1 generate 256 palette 2026-02-17 09:17:54 -08:00
ghostty-vouch[bot]
fa216edacc Update VOUCHED list (#10798)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/10782) from
@mitchellh.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 17:16:21 +00:00
ghostty-vouch[bot]
e67a8b2da4 Update VOUCHED list (#10797)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/10784) from
@mitchellh.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 17:14:50 +00:00
ghostty-vouch[bot]
34637b843e Update VOUCHED list (#10796)
Triggered by [discussion
comment](https://github.com/ghostty-org/ghostty/discussions/10785) from
@mitchellh.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 17:14:22 +00:00
Jon Parise
54f2be8e7d bash: avoid mapfile for bash 3.2 compatibility
We continue to support bash 3.2 for compatibility with /bin/bash on
macOS. `mapfile` was introduced in bash 4.0, so this change introduces a
`read -r`-based helper function for populating COMPREPLY from a list of
lines.
2026-02-17 11:35:33 -05:00
ghostty-vouch[bot]
3e92aa2c3a Update VOUCHED list (#10795)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/10794#issuecomment-3915601262)
from @trag1c.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 16:09:06 +00:00
ghostty-vouch[bot]
5c8d977304 Update VOUCHED list (#10793)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/10792#issuecomment-3915522813)
from @trag1c.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 15:54:21 +00:00
Kat
f07b92a022 Update zh_TW Traditional Chinese locale, cc #10632 (#10788)
GitHub Copilot pull request summary:

> This pull request updates the Traditional Chinese (zh_TW) translations
for the "Change Tab Title" strings in the application. These updates
provide accurate translations for UI elements related to changing tab
titles.
> 
> **Translation updates:**
> 
> * Added the Traditional Chinese translation for the "Change Tab
Title…" menu item in `po/zh_TW.UTF-8.po`
> * Added the Traditional Chinese translation for the "Change Tab Title"
dialog label in `po/zh_TW.UTF-8.po`
2026-02-17 15:33:30 +00:00
ghostty-vouch[bot]
0aa88e0dac Update VOUCHED list (#10790)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/10788#issuecomment-3915336424)
from @00-kat.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 15:20:52 +00:00
Peter Dave Hello
ebdf38999b Update zh_TW Traditional Chinese locale, cc #10632 2026-02-17 23:13:31 +08:00
Mitchell Hashimoto
b6dbd445d0 ci: update create-github-app-token 2026-02-16 21:02:44 -08:00
ghostty-vouch[bot]
a3dd93ae75 Update VOUCHED list (#10781)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/10379#issuecomment-3912263144)
from @mitchellh.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 04:49:58 +00:00
ghostty-vouch[bot]
f1277737ff Update VOUCHED list (#10780)
Triggered by
[comment](https://github.com/ghostty-org/ghostty/issues/10379#issuecomment-3912261301)
from @mitchellh.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-17 04:49:08 +00:00