Commit Graph

14011 Commits

Author SHA1 Message Date
David Matos
d70eef69f9 address changes 2026-01-27 00:51:50 +01:00
David Matos
0a2b90ed64 Expand Readme to reflect new changes 2026-01-21 13:31:08 +01:00
David Matos
b87e8d8172 Update to new nu ssh ghostty integration 2026-01-21 13:19:53 +01:00
David Matos
6d1125951e Merge branch 'main' into nu-ssh-support 2026-01-21 09:06:42 +01:00
Mitchell Hashimoto
dc43ded149 ci: authenticate pinact to avoid GH rate limits (#10393)
When we're running a lot of CI we're hitting low unauthenticated rate
limits.
2026-01-20 13:09:16 -08:00
Mitchell Hashimoto
8ef0842b01 ci: authenticate pinact to avoid GH rate limits
When we're running a lot of CI we're hitting low unauthenticated rate
limits.
2026-01-20 13:07:07 -08:00
Mitchell Hashimoto
fabfe4b944 Misc error handling improvements (#10392)
See individual commits. An overview:

* Added explicit error sets in more places
* Removed `!` from functions that can't ever fail
* `renderer.cell.Contents.resize` `errdefer` now does proper cleanup
* `renderer.rebuildCells` can now only fail due to allocator OOM
* If shaping fails during rendering, that row is skipped (previously
it'd halt rendering there)
* Failed image rendering setup in the renderer now skips that image
(previously would halt the full frame)
* GPU texture cleanup for failed image setup now works properly
2026-01-20 12:38:20 -08:00
Mitchell Hashimoto
6ec2bfe288 renderer: kitty graphics prep can't fail (skip failed conversions) 2026-01-20 12:29:10 -08:00
Mitchell Hashimoto
d235c490e9 renderer: handle rebuildCells failures gracefully 2026-01-20 12:13:30 -08:00
Mitchell Hashimoto
db092ac3ce renderer: extract rebuildRow to its own function 2026-01-20 12:07:42 -08:00
Mitchell Hashimoto
112363c4e1 font: Collection.getEntry explicit error set 2026-01-20 11:57:22 -08:00
Mitchell Hashimoto
e875b453b7 font/shaper: hook functions can't fail 2026-01-20 11:51:01 -08:00
Mitchell Hashimoto
e3c39ed502 renderer: cell.Contents.resize errdefer handling is now safe 2026-01-20 11:46:27 -08:00
Mitchell Hashimoto
ad1e22c29f custom shaders: add colorscheme information to shader uniforms (#9656)
#9417
Adds palette and color scheme uniforms to custom shaders, allowing
custom shaders to access terminal color information:

  - iPalette[256]: Full 256-color terminal palette (RGB)
  - iBackgroundColor, iForegroundColor: Terminal colors (RGB)
  - iCursorColor, iCursorText: Cursor colors (RGB)
- iSelectionBackgroundColor, iSelectionForegroundColor: Selection colors
(RGB)

Colors are normalized to [0.0, 1.0] range and update when the palette
changes via OSC sequences or configuration changes. The palette_dirty
flag tracks when colors need to be refreshed, initialized to true to
ensure correct colors on new surfaces.
2026-01-20 11:41:08 -08:00
Mitchell Hashimoto
7204f7ef9e renderer: remove palette_dirty, rely on terminal state 2026-01-20 11:34:53 -08:00
Mitchell Hashimoto
8786745969 renderer: don't access shared state for custom shader color palettes 2026-01-20 11:25:27 -08:00
ClearAspect
8d2eb280db custom shaders: add colorscheme information to shader uniforms
Adds palette and color scheme uniforms to custom shaders, allowing
custom shaders to access terminal color information:

  - iPalette[256]: Full 256-color terminal palette (RGB)
  - iBackgroundColor, iForegroundColor: Terminal colors (RGB)
  - iCursorColor, iCursorText: Cursor colors (RGB)
  - iSelectionBackgroundColor, iSelectionForegroundColor: Selection
colors (RGB)

Colors are normalized to [0.0, 1.0] range and update when the palette
changes via OSC sequences or configuration changes. The palette_dirty
flag tracks when colors need to be refreshed, initialized to true to
ensure correct colors on new surfaces.
2026-01-20 11:15:09 -08:00
Mitchell Hashimoto
245886d568 build: libghostty-vt now depends on uucode directly (#10389)
This doesn't add any real weight to it, we only need it for a type
definition. This fixes our example builds.

https://ampcode.com/threads/T-019bdc95-07bb-70c9-9db4-1218d41decb2
2026-01-20 10:30:24 -08:00
Mitchell Hashimoto
cb25c0a8ae build: libghostty-vt now depends on uucode directly
This doesn't add any real weight to it, we only need it for a type
definition. This fixes our example builds.
2026-01-20 10:18:47 -08:00
Mitchell Hashimoto
1c2c61bc3b config: fix physical -> unicode keybinds for alternate keyboard layouts (#9469)
Dvorak input (and presumably others) on MacOS causes certain keys to not
work as expected: `[` `]` and `=`.

### Related

This fixes https://github.com/ghostty-org/ghostty/discussions/8743 as
well as an unmentioned problem where bracket navigation and equalize
panes are also broken.

This is similar to https://github.com/ghostty-org/ghostty/pull/8759 but
fixes more of the combos. Switching the `+` binding was not enough to
fix the problem for me since the right bracket physical keys is where
equals should be and overrides the combo.

### What this PR does

Switches several default keybindings from physical key codes to support
alternative keyboard layouts like Dvorak and keyboards with dedicated
plus keys. Effectively:

```diff
-  .physical = .equal // or .bracket_left or .bracket_right
+  .unicode = '=' // or '[' or ']'
```

### Details

In testing, I found that all of these bindings need to be fixed
otherwise the bracket physical keys overshadows the dvorak plus key.

This seems like the right solution for the same reason that we don't use
any physical letter or number keys. They move around with different
layouts and `=`, `[`, and `]` are no different than other keys like `-`
and `0` which use unicode in other default keybinds.

With this fix, tab and pane navigation (cmd+[], cmd+shift+[]), as well
as increase font size (cmd+shift+equals and cmd+equals) and equalize
panes (ctrl+cmd+=) now work as expected on dvoark layout on MacOS.

Note, I switch between dvorak virtual layout on the laptop and a
physical dvorak keyboard (passed through qwerty input) so my combos
would need to change depending on which keyboard I was using if we used
physical keys only.

I consulted Claude Code to help try to understand what order and
precedence was being applied in this change, but I wrote and tested the
code myself (however, this is my first `zig` code so take that with a
grain of salt).
2026-01-20 10:03:32 -08:00
Martin Emde
c2deda3231 config: switch certain physical keybinds to unicode
Switches several default keybindings from physical key codes

`.physical = .equal // or .bracket_left or .bracket_right`

to unicode characters

`.unicode = '=' // or '[' or ']'`

to support alternative keyboard layouts like Dvorak and
keyboards with dedicated plus keys (like German layouts).

I found in testing that all of these must be fixed at once otherwise
the bracket physical keys overshadew the correct (for dvorak) plus key.
With this fix, tab and pane navigation (cmd+[], cmd+shift+[]), as well
as cmd+shift+equals and cmd+equals work as expected on dvoark layout on MacOS.
2026-01-20 09:58:34 -08:00
Mitchell Hashimoto
492eb40d28 gtk: add GSettings generic module and respect gtk-enable-primary-paste on gtk systems (#10328)
# Add GSettings Support for Primary Paste

Implements support for `org.gnome.desktop.interface
gtk-enable-primary-paste` to allow users to disable middle-click paste.
Also refactors GTK Settings access into a reusable generic module.

## Changes

- **NEW**: `src/apprt/gtk/gsettings.zig` - Generic GTK Settings reader
supporting `bool` and `c_int` types, portal-aware for Flatpak/Snap
- **MODIFIED**: `src/apprt/gtk/class/surface.zig` - Reads primary paste
setting and refactors gtk-xft-dpi to use new module

## Behavior
- Setting `false` → Middle-click paste blocked
- Setting `true` or unavailable → Middle-click paste works (default)
- Uses GTK Settings API which automatically uses XDG Desktop Portal in
sandboxed environments

Note: No unit tests added as this is a thin wrapper around GTK Settings
API that's already tested indirectly through surface.zig. Happy to add
tests if desired, though they would require an active display
environment and skip on most CI systems.
2026-01-20 09:49:48 -08:00
Mitchell Hashimoto
c8ffc0faa5 termio: report color scheme synchronously (#9705)
The reporting of color scheme was handled asynchronously by queuing a
handler in the surface. This could lead to race conditions where the DSR
is reported after subsequent VT sequences.

Fixes #5922
2026-01-20 09:48:43 -08:00
Mitchell Hashimoto
49b2b8d644 unicode: switch to uucode grapheme break to (mostly) match unicode spec (#9680)
This PR builds on https://github.com/ghostty-org/ghostty/pull/9678 ~so
the diff from there is included here (it's not possible to stack PRs
unless it's a PR against my own fork)--review that one first!~

This PR updates the `graphemeBreak` calculation to use `uucode`'s
`computeGraphemeBreakNoControl`, which has [tests in
uucode](215ff09730/src/x/grapheme.zig (L753))
that confirm it passes the `GraphemeBreakTest.txt` (minus some
exceptions).

Note that the `grapheme_break` (and `grapheme_break_no_control`)
property in `uucode` incorporates `emoji_modifier` and
`emoji_modifier_base`, diverging from UAX #29 but matching UTS #51. See
[this comment in
uucode](215ff09730/src/grapheme.zig (L420-L434))
for details.

The `grapheme_break_no_control` property and
`computeGraphemeBreakNoControl` both assume `control`, `cr`, and `lf`
have been filtered out, matching the current grapheme break logic in
Ghostty.

This PR keeps the `Precompute.data` logic mostly equivalent, since the
`uucode` `precomputedGraphemeBreak` lacks benchmarks in the `uucode`
repository (it was benchmarked in [the original PR adding `uucode` to
Ghostty](https://github.com/ghostty-org/ghostty/pull/8757)). Note
however, that due to `grapheme_break` being one bit larger than
`grapheme_boundary_class` and the new `BreakState` also being one bit
larger, the state jumps up by a factor of 8 (u10 -> u13), to 8KB.

## Benchmarks

~I benchmarked the old `main` version versus this PR for
`+grapheme-break` and surprisingly this PR is 2% faster (?). Looking at
the assembly though, I'm thinking something else might be causing that.
Once I get to the bottom of that I'll remove the below TODO and include
the benchmark results here.~

When seeing the speedup with `data.txt` and maybe a tiny speedup on
English wiki, I was surprised given the 1KB -> 8KB tables. Here's what
AI said when I asked it to inspect the assembly:
https://ampcode.com/threads/T-979b1743-19e7-47c9-8074-9778b4b2a61e, and
here's what it said when I asked it to predict the faster version:
https://ampcode.com/threads/T-3291dcd3-7a21-4d24-a192-7b3f6e18cd31

It looks like two loads got reordered and that put the load that
depended on stage1 -> stage2 -> stage3 second, "hiding memory latency".
So that makes the new one faster when looking up the `grapheme_break`
property. These gains go away with the Japanese and Arabic benchmarks,
which spend more time processing utf8, and may even have more grapheme
clusters too.

### with data.txt (200 MB ghostty-gen random utf8)

<img width="1822" height="464" alt="CleanShot 2025-11-26 at 08 42 03@2x"
src="https://github.com/user-attachments/assets/56d4ee98-21db-4eab-93ab-a0463a653883"
/>

### with English wiki dump

<img width="2012" height="506" alt="CleanShot 2025-11-26 at 08 43 15@2x"
src="https://github.com/user-attachments/assets/230fbfb7-272d-4a2a-93e7-7268962a9814"
/>

### with Japanese wiki dump

<img width="2008" height="518" alt="CleanShot 2025-11-26 at 08 43 49@2x"
src="https://github.com/user-attachments/assets/edb408c8-a604-4a8f-bd5b-80f19e3d65ee"
/>

### with Arabic wiki dump

<img width="2010" height="512" alt="CleanShot 2025-11-26 at 08 44 25@2x"
src="https://github.com/user-attachments/assets/81a29ac8-0586-4e82-8276-d7fa90c31c90"
/>


TODO:

* [x] Take a closer look at the assembly and understand why this PR (8
KB vs 1 KB table) is faster on my machine.
* [x] _(**edit**: checking this off because it seems unnecessary)_ If
this turns out to actually be unacceptably slower, one possibility is to
switch to `uucode`'s `precomputedGraphemeBreak` which uses a 1445 byte
table since it uses a dense table (indexed using multiplication instead
of bitCast, though, which did show up in the initial benchmarks from
https://github.com/ghostty-org/ghostty/pull/8757 a small amount.)

AI was used in some of the uucode changes in
https://github.com/ghostty-org/ghostty/pull/9678 (Amp--primarily for
tests), but everything was carefully vetted and much of it done by hand.
This PR was made without AI with the exception of consulting AI about
whether the "Prepend + ASCII" scenario is common (hopefully it's right
about that being uncommon).
2026-01-20 09:44:15 -08:00
Mitchell Hashimoto
e5afaa47b3 Add configurable word boundary characters for text selection (#9335)
## Summary

This PR adds a new `selection-word-chars` configuration option that
allows users to customize which characters mark word boundaries during
text selection operations (double-click, word selection, etc.).

## Motivation

This's been on my wishlist for a while. Inspired by #9069 which added
semicolon as a hardcoded word boundary, this PR takes the concept
further by making word boundaries fully configurable. Different
workflows and use cases benefit from different boundary characters - SQL
developers might want semicolons as boundaries, while others working
with file paths or URLs might prefer different settings.

This approach is similar to zsh's `WORDCHARS` environment variable,
giving users fine-grained control over text selection behavior.

## Changes

- **New config option**: `selection-word-chars` with default value `` `
\t'"│`|:;,()[]{}<>$` ``
- **Runtime UTF-8 parsing**: Boundary characters are parsed from UTF-8
string to u32 codepoints
- **Updated function signatures**: `selectWord()` and
`selectWordBetween()` now accept boundary characters as parameters
- **All call sites updated**: Surface.zig, embedded.zig, and all test
cases updated

## Usage

Users can now customize word boundaries in their config:

```ini
# Remove semicolon from boundaries (treat as part of words)
selection-word-chars = " \t'\"│`|:,()[]{}<>$"

# Remove periods for better URL selection
selection-word-chars = " \t'\"│`|:;,()[]{}<>$"
```

## Implementation Details

- Boundary characters are stored in `DerivedConfig` and passed through
to selection functions
- UTF-8 parsing happens at runtime with graceful fallback for invalid
input
- Null character (U+0000) is always included as a boundary automatically
- Multi-byte UTF-8 characters are fully supported

## AI Assistance Disclosure

With gratitude for the team and respect for the [Contributing
Guidelines](https://github.com/ghostty-org/ghostty/blob/main/CONTRIBUTING.md),
I want to disclose that this PR was written with AI assistance (Claude
Code). I have reviewed all the code, and to the extent of my
understanding, I'm prepared to answer any questions about the changes.

## Related

- Inspired by #9069
2026-01-20 09:42:21 -08:00
Mitchell Hashimoto
2e0141fcdf config: clarify selection-word-boundary docs 2026-01-20 09:38:41 -08:00
mauroporras
9b7c20f500 refactor: use u21 for Unicode codepoints and Zig 0.15 ArrayList
- Change all codepoint types from u32 to u21 to align with Zig stdlib
- Update ArrayList to use Zig 0.15 unmanaged pattern (.empty)
- Remove unnecessary @intCast when encoding UTF-8
- Fix formatEntry to use stack-allocated buffer
2026-01-20 09:37:31 -08:00
mauroporras
6f662d70bc refactor: clean up selection-word-chars documentation and formatting 2026-01-20 09:37:31 -08:00
mauroporras
27602bb4b4 refactor: optimize selection-word-chars with pre-parsed codepoints
Refactor the selection-word-chars implementation to parse UTF-8 boundary
characters once during config initialization instead of on every selection
operation.

Changes:
- Add SelectionWordChars type that stores pre-parsed []const u32 codepoints
- Parse UTF-8 to codepoints in parseCLI() during config load
- Remove UTF-8 parsing logic from selectWord() hot path (27 lines removed)
- Remove arbitrary 64-character buffer limit
- Update selectWord() and selectWordBetween() to accept []const u32
- Update DerivedConfig to store codepoints directly
- Update all tests to use codepoint arrays

Benefits:
- No runtime UTF-8 parsing overhead on every selection
- No arbitrary character limit (uses allocator instead)
- Cleaner separation of concerns (config handles parsing, selection uses data)
- Better performance in selection hot path
2026-01-20 09:37:31 -08:00
mauroporras
811e3594eb feat: add configurable word boundary characters for text selection
Add new `selection-word-chars` config option to customize which characters
mark word boundaries during text selection operations (double-click, word
selection, etc.). Similar to zsh's WORDCHARS environment variable, but
specifies boundary characters rather than word characters.

Default boundaries: ` \t'"│`|:;,()[]{}<>$`

Users can now customize word selection behavior, such as treating
semicolons as part of words or excluding periods from boundaries:

    selection-word-chars = " \t'\"│`|:,()[]{}<>$"

Changes:
- Add selection-word-chars config field with comprehensive documentation
- Modify selectWord() and selectWordBetween() to accept boundary_chars parameter
- Parse UTF-8 boundary string to u32 codepoints at runtime
- Update all call sites in Surface.zig and embedded.zig
- Update all test cases to pass boundary characters
2026-01-20 09:37:31 -08:00
evertonstz
8c9891b5de Refactor primary paste settings handling and documentation for clarity 2026-01-20 14:32:47 -03:00
Mitchell Hashimoto
6730afe312 macOS: Add GhosttyUITests (Drafting to save some ci checks) (#9185)
### Background

~~I was trying to add a few UI test cases for
`macOS-titlebar-style`[Already in this PR]~~. In order to do this, I
need a way from `GhosttyKit` to load a temporary configuration without
messing around with users'.

### Changes

- Add `ghostty_config_load_file` using the existing
[`loadFile`](dafb9e89a3/src/config/Config.zig (L3399))
- Use `xcbeautify` to format test&build errors
**Couldn't find a way to do this in `GhosttyXcodebuild`, if you have a
better approach please let me know!**
- Add GhosttyUITests target and test cases for
`GhosttyTitlebarTabsUITests`(#2349) and `GhosttyThemeTests`(#9360)

### NOTE

Running UI tests on the runner could be **very** slow and I couldn't
find a way to guarantee success, so I made these only runnable by
manually testing in Xcode.

Better to squash this🤪

> > Some of the test cases could fail when testing all the cases
together; a rerun would succeed.
2026-01-20 09:30:39 -08:00
Mitchell Hashimoto
7abdc94540 feat: Select/Copy Links On Right Click If Present (#9298)
This is a solution for
https://github.com/ghostty-org/ghostty/issues/2107.

**AI Disclosure:** I used Gemini CLI to help me with this PR because
while I have many years of programming experience, this is my first time
writing Zig. I prototyped a couple different approaches with AI before
landing on this one, so AI generated various prototypes and I chose the
final imlementation. I've verified that my code compiles and works as
intended.

When a user right-clicks, and there's no existing selection, the
existing behavior is to try to select the word under the cursor:


3548acfac6/src/Surface.zig (L3740-L3742)

This PR tweaks that behavior _slightly_: If there's a link under our
cursor, as determined by `linkAtPos`, select the link (to copy with the
right-click context menu). Otherwise, select the word as before.

As noted in https://github.com/ghostty-org/ghostty/issues/2107, this
matches the behavior of iTerm and Gnome Terminal.

It's worth noting that `linkAtPos` already does the right thing in terms
of checking the links from config and their highlight/hover states
(modified by Ctrl or Super depending on platform).


3548acfac6/src/Surface.zig (L3896-L3901)

It also therefore respects `link-url` from config.


3548acfac6/src/config/Config.zig (L3411-L3416)

By using `linkAtPos`, we get all that behavior for free. In practical
terms, that means:
- If I'm holding Ctrl so a link is underlined and I right click on it,
it selects the underlined link.
- If I'm not holding Ctrl and I right click on a link that is no
underlined, it selects the word as before.
- This behavior respects per-platform key bindings and user config
settings.

`linkAtPos` requires that the render state mutex is held. I believe it's
safe to call because we're inside a block holding the mutex:

3548acfac6/src/Surface.zig (L3702-L3704)

**Original Behavior:**
(first without ctrl, then with ctrl)


https://github.com/user-attachments/assets/f9236c44-bea4-4be8-a54b-24d5ae24b2e7

**New Behavior:**
(first without ctrl, then with ctrl, then pasting)


https://github.com/user-attachments/assets/1e7fa1a9-236e-471d-9504-c820c68600bb
2026-01-20 09:30:25 -08:00
evertonstz
bc067fc782 Refactor gtk_enable_primary_paste to remove optional type and simplify condition checks 2026-01-20 14:23:22 -03:00
evertonstz
7b6147aa28 Refactor GValueType and getImpl functions to use type-based switches for improved clarity and maintainability 2026-01-20 14:23:10 -03:00
Lars
3fda31a66a skip checking config file 2026-01-20 09:15:14 -08:00
Lars
bfe5a4be8f move config loading to Config 2026-01-20 09:15:14 -08:00
Lukas
32562e0c98 Update macos/Sources/Ghostty/Ghostty.App.swift
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-20 09:15:14 -08:00
Lars
35b2e820ce typo fix 2026-01-20 09:15:14 -08:00
Lars
4154a730ee Fix some edge cases 2026-01-20 09:15:14 -08:00
Lars
27729bf8a7 only run ui tests manually with xcode 2026-01-20 09:15:14 -08:00
Lars
85da70c98d Add more theme test cases from #9360 2026-01-20 09:15:14 -08:00
Lars
4aabd94716 add theme tests 2026-01-20 09:15:14 -08:00
Lars
d9ed325818 update config explicitly 2026-01-20 09:15:14 -08:00
Lars
6ada9c7844 test: add test for mergeAllWindows 2026-01-20 09:15:14 -08:00
Lars
f52013787e test: using XCTAssertEqual to get more information 2026-01-20 09:15:14 -08:00
Lars
f088ce38d9 test: read config before launching XCUIApplication 2026-01-20 09:15:14 -08:00
Lars
f7608d0b95 macOS: reduce press duration 2026-01-20 09:15:14 -08:00
Lars
b1290dc47b macOS: add titlebar tabs tests 2026-01-20 09:15:14 -08:00
Lars
bcf42dde6c build: change deployment target 2026-01-20 09:15:14 -08:00