Commit Graph

13074 Commits

Author SHA1 Message Date
Qwerasd
6d65abc489 fix(pkg/freetype): fully correct load flags
These now properly match the FreeType API- compared directly in the unit
tests against the values provided by the FreeType header itself.

This was ridiculously wrong before, like... wow.
2025-11-24 17:57:02 -07:00
Qwerasd
3cd6939af6 pkg/freetype: add failing unit tests for LoadFlags 2025-11-24 17:35:53 -07:00
Qwerasd
6a9c869f9d Partially revert 25856d6 since it broke pkg/freetype tests 2025-11-24 17:24:47 -07:00
Pyry Takala
5bfeba6603 Fix LoadFlags struct bit alignment to match FreeType API
The struct was missing padding at bit position 8, causing all subsequent flag fields (bits 9+) to be misaligned by one bit position.

See: https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx
2025-11-24 23:34:47 +00:00
Mitchell Hashimoto
8278718c57 Fix LangSet.hasLang() to compare against FcLangEqual instead of FcTrue (#9685)
FcLangSetHasLang returns FcLangResult enum values:
- FcLangEqual (0): Exact match
- FcLangDifferentTerritory (1): Same language, different territory
- FcLangDifferentLang (2): Different language
See also
https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fclangsethaslang.html
and
https://codebrowser.dev/qt6/include/fontconfig/fontconfig.h.html#_FcLangResult

The previous comparison to FcTrue (1) caused:
- Exact matches (0) to incorrectly return false
- Partial matches (1) to incorrectly return true

This fix changes the comparison to FcLangEqual (0) so hasLang()
correctly returns true only for exact language matches.
2025-11-24 13:13:51 -08:00
Pyry Takala
d4c2376c2d Fix LangSet.hasLang() to compare against FcLangEqual instead of FcTrue
FcLangSetHasLang returns FcLangResult enum values:
- FcLangEqual (0): Exact match
- FcLangDifferentTerritory (1): Same language, different territory
- FcLangDifferentLang (2): Different language

The previous comparison to FcTrue (1) caused:
- Exact matches (0) to incorrectly return false
- Partial matches (1) to incorrectly return true

This fix changes the comparison to FcLangEqual (0) so hasLang()
correctly returns true only for exact language matches.

Fixes emoji font detection which relies on checking for 'und-zsye'
language tag support.
2025-11-24 20:34:07 +00:00
Mitchell Hashimoto
b5dfe5dbfe Add GHOSTTY_QUICK_TERMINAL for quick terminal detection (#9673)
## Summary

Adds a new environment variable `GHOSTTY_QUICK_TERMINAL=1` that is set
when a terminal surface is launched as a quick terminal. This allows
shell configurations to conditionally adjust behavior based on whether
the terminal is a quick terminal.

Closes #3985

<img width="1430" height="228" alt="CleanShot 2025-11-23 at 12 39 27
png"
src="https://github.com/user-attachments/assets/1085c19b-9d58-4603-a03d-662bfde47095"
/>

## Motivation

Quick terminals are designed as ephemeral, singleton windows for quick
commands. Many users (especially tmux users) have shell configurations
that automatically attach to or start tmux sessions on terminal launch.
This automatic behavior conflicts with the quick terminal use case.

Example from the issue:
```zsh
# Users want to skip this in quick terminals
if [[ -z "$TMUX" ]]; then
  tmux attach || tmux new
fi

With this PR, users can now detect quick terminals and adjust their shell behavior:
if [[ -z "$TMUX" ]] && [[ -z "$GHOSTTY_QUICK_TERMINAL" ]]; then
  tmux attach || tmux new
fi
```

## Implementation

- macOS: Added GHOSTTY_QUICK_TERMINAL=1 to SurfaceConfiguration
environment variables in QuickTerminalController.swift:345-351
- Linux/GTK: Added environment variable check in
src/apprt/gtk/class/surface.zig:1469-1472 within defaultTermioEnv()

 The environment variable is automatically inherited by split surfaces.

## Note on Scripting API

I'm aware that @rhodes-b mentioned this functionality was planned for
the scripting API (issue #3985). However, I believe this simpler
environment variable approach provides immediate value for tmux users
and shell configuration use cases, while the scripting API can later
provide more comprehensive surface introspection capabilities.

## AI Assistance Disclosure

This PR was written with Claude Code assistance. The Linux/GTK
implementation was fully AI-generated. I implemented the mac version
myself after Claude Code helped me understand the codebase architecture
and locate the appropriate files.
2025-11-24 08:21:21 -08:00
Mitchell Hashimoto
98e7c17fcc unicode: fix VS15/VS16 check to consider emoji bases (#9679)
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 fixes an issue with the VS15/VS16 handling in terminal `print`,
where before we didn't check for valid sequences if the base code point
is an emoji. This meant that a VS15 after an emoji that isn't part of a
valid sequence would narrow the cell, despite that the emoji doesn't
have a text presentation.

This PR also uses a single `is_emoji_vs_base` instead of the
`is_emoji_vs_emoji` and `is_emoji_vs_text`. See the [uucode
comment](215ff09730/src/config.zig (L239-L254))
for why I'm recommending this.

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.
2025-11-24 08:20:07 -08:00
Jacob Sandlund
a0203e8d1a Merge remote-tracking branch 'upstream/main' into vs-correctness 2025-11-24 10:47:22 -05:00
Mitchell Hashimoto
38fbbba8e9 deps: Update uucode to latest (#9678)
This updates uucode to the latest version, with the following changes:
b309dfb4e2...31655fba3c

For this PR, the new `uucode` version has two changes that contribute to
the diff:

* The `grapheme_break` now includes `emoji_modifier` and
`emoji_modifier_base`, so we don't need to grab those from
`is_emoji_modifier` and `is_emoji_modifier_base`.
* The `wcwidth` calculation has been split into `wcwidth_standalone`
(width of a code point when it's the only code point in a grapheme) and
`wcwidth_zero_in_grapheme` (whether the code point is _not_ contributing
to the width of a multi-code-point grapheme). To keep the current
Ghostty behavior for this PR, this sets `width` to 0 if
`wcwidth_zero_in_grapheme`, but with the exception of
`is_emoji_modifier` (see comment).
* While this PR isn't affected by it, take a look at
[wcwidth.zig](https://github.com/jacobsandlund/uucode/blob/main/src/x/config_x/wcwidth.zig)
for the considerations that went into determining the width of a code
point, along with many comments.
* See
[x/grapheme.zig](https://github.com/jacobsandlund/uucode/blob/main/src/x/grapheme.zig)
for the calculation of the width of a grapheme based on the width of the
code points with exceptions, again with many comments.
* See
[resources/wcwidth](https://github.com/jacobsandlund/uucode/tree/main/resources/wcwidth)
for a comparison of other unicode libraries calculation of "wcwidth".

PRs will follow this in a moment to also take advantage of the new
`uucode` version for:

* Better grapheme segmentation
* Correct VS15/VS16 handling
* More correct cell width calculation (especially certain scripts such
as Devanagari)
2025-11-24 07:31:22 -08:00
Mitchell Hashimoto
d3f24179c9 build(deps): bump peter-evans/create-pull-request from 7.0.8 to 7.0.9 (#9676)
Bumps
[peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request)
from 7.0.8 to 7.0.9.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/peter-evans/create-pull-request/releases">peter-evans/create-pull-request's
releases</a>.</em></p>
<blockquote>
<h2>Create Pull Request v7.0.9</h2>
<p>⚙️ Fixes an <a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4228">incompatibility</a>
with the recently released <code>actions/checkout@v6</code>.</p>
<h2>What's Changed</h2>
<ul>
<li>~70 dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a></li>
<li>docs: fix workaround description about <code>ready_for_review</code>
by <a href="https://github.com/ybiquitous"><code>@​ybiquitous</code></a>
in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3939">peter-evans/create-pull-request#3939</a></li>
<li>Docs: <code>add-paths</code> default behavior by <a
href="https://github.com/joeflack4"><code>@​joeflack4</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3928">peter-evans/create-pull-request#3928</a></li>
<li>docs: update to create-github-app-token v2 by <a
href="https://github.com/Goooler"><code>@​Goooler</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4063">peter-evans/create-pull-request#4063</a></li>
<li>Fix compatibility with actions/checkout@v6 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4230">peter-evans/create-pull-request#4230</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/joeflack4"><code>@​joeflack4</code></a>
made their first contribution in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/3928">peter-evans/create-pull-request#3928</a></li>
<li><a href="https://github.com/Goooler"><code>@​Goooler</code></a> made
their first contribution in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4063">peter-evans/create-pull-request#4063</a></li>
<li><a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> made
their first contribution in <a
href="https://redirect.github.com/peter-evans/create-pull-request/pull/4230">peter-evans/create-pull-request#4230</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/peter-evans/create-pull-request/compare/v7.0.8...v7.0.9">https://github.com/peter-evans/create-pull-request/compare/v7.0.8...v7.0.9</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="84ae59a2cd"><code>84ae59a</code></a>
fix: compatibility with actions/checkout@v6 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4230">#4230</a>)</li>
<li><a
href="b4733b9419"><code>b4733b9</code></a>
build(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4222">#4222</a>)</li>
<li><a
href="0edc001d28"><code>0edc001</code></a>
build(deps-dev): bump the npm group with 2 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4201">#4201</a>)</li>
<li><a
href="430aea0fb1"><code>430aea0</code></a>
build(deps): bump the github-actions group with 3 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4200">#4200</a>)</li>
<li><a
href="46cdba753c"><code>46cdba7</code></a>
build(deps-dev): bump the npm group with 3 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4185">#4185</a>)</li>
<li><a
href="b937339b17"><code>b937339</code></a>
build(deps): bump the github-actions group with 2 updates (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4184">#4184</a>)</li>
<li><a
href="e9af275c37"><code>e9af275</code></a>
ci: update dependabot config</li>
<li><a
href="d3e081a03a"><code>d3e081a</code></a>
build(deps-dev): bump <code>@​types/node</code> from 18.19.127 to
18.19.128 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4178">#4178</a>)</li>
<li><a
href="9ec683ee07"><code>9ec683e</code></a>
build(deps-dev): bump <code>@​types/node</code> from 18.19.125 to
18.19.127 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4165">#4165</a>)</li>
<li><a
href="65d8d10bf7"><code>65d8d10</code></a>
build(deps-dev): bump ts-jest from 29.4.2 to 29.4.4 (<a
href="https://redirect.github.com/peter-evans/create-pull-request/issues/4163">#4163</a>)</li>
<li>Additional commits viewable in <a
href="271a8d0340...84ae59a2cd">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=peter-evans/create-pull-request&package-manager=github_actions&previous-version=7.0.8&new-version=7.0.9)](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>
2025-11-24 07:23:11 -08:00
Jacob Sandlund
8f033c7022 Add test with just a single emoji followed by VS15 (invalid) 2025-11-24 09:26:01 -05:00
Jacob Sandlund
b58656be8a Merge branch 'uucode-update' into vs-correctness 2025-11-24 09:14:32 -05:00
Jacob Sandlund
61c7381452 Update comment. PR for wcwidth_standalone might be a bit 2025-11-24 09:14:03 -05:00
Jacob Sandlund
808d31f6ee nix cache --update 2025-11-24 09:13:19 -05:00
Jacob Sandlund
e4c5670266 Merge branch 'uucode-update' into vs-correctness 2025-11-24 08:29:51 -05:00
Jacob Sandlund
2b6c309217 Update uucode to latest 2025-11-24 08:29:27 -05:00
Jacob Sandlund
c3c9181e7a Merge branch 'uucode-update' into vs-correctness 2025-11-23 23:05:22 -05:00
Jacob Sandlund
6e0e1d1388 update uucode to latest 2025-11-23 23:05:03 -05:00
Jacob Sandlund
55c1cb3aad Merge branch 'uucode-update' into vs-correctness 2025-11-23 22:56:15 -05:00
Jacob Sandlund
62ec34072f fix typo 2025-11-23 22:56:00 -05:00
Jacob Sandlund
36c3295806 unicode: don't narrow invalid text presentation (VS15) sequences 2025-11-23 22:39:21 -05:00
dependabot[bot]
6588e1e9e7 build(deps): bump peter-evans/create-pull-request from 7.0.8 to 7.0.9
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.8 to 7.0.9.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](271a8d0340...84ae59a2cd)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-version: 7.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 00:08:15 +00:00
Jacob Sandlund
97926ca307 Update uucode to the latest, for future width and grapheme break changes 2025-11-23 17:26:25 -05:00
Vinícius Soares
92aa960381 Add flag for quick terminal 2025-11-23 12:43:11 -03:00
Mitchell Hashimoto
6b28671ead macOS: Only change the icon if needed (#9670)
Fixes #9666

> [!NOTE]
> AI proofread my comments
2025-11-22 14:43:12 -08:00
Mitchell Hashimoto
4e6498a04c renderer: make cursorStyle depend on RenderState (#9672)
This makes `cursorStyle` utilize `RenderState` to determine the
appropriate cursor style. This moves the cursor style logic outside the
critical area, although it was cheap to begin with.

This always removes `viewport_is_bottom` which had no practical use.
2025-11-22 14:41:25 -08:00
Mitchell Hashimoto
df466f3c73 renderer: make cursorStyle depend on RenderState
This makes `cursorStyle` utilize `RenderState` to determine the
appropriate cursor style. This moves the cursor style logic outside the
critical area, although it was cheap to begin with.

This always removes `viewport_is_bottom` which had no practical use.
2025-11-22 14:36:53 -08:00
Lukas
6f75cc56f6 macOS: Only change the icon if needed
Fixes #9666
2025-11-22 16:48:39 +01:00
Mitchell Hashimoto
6e9412cbab build(deps): bump actions/checkout from 5.0.1 to 6.0.0 (#9659)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.1
to 6.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>v6-beta by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2298">actions/checkout#2298</a></li>
<li>update readme/changelog for v6 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2311">actions/checkout#2311</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5.0.0...v6.0.0">https://github.com/actions/checkout/compare/v5.0.0...v6.0.0</a></p>
<h2>v6-beta</h2>
<h2>What's Changed</h2>
<p>Updated persist-credentials to store the credentials under
<code>$RUNNER_TEMP</code> instead of directly in the local git
config.</p>
<p>This requires a minimum Actions Runner version of <a
href="https://github.com/actions/runner/releases/tag/v2.329.0">v2.329.0</a>
to access the persisted credentials for <a
href="https://docs.github.com/en/actions/tutorials/use-containerized-services/create-a-docker-container-action">Docker
container action</a> scenarios.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>V6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>V5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>V5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>V4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>V4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1af3b93b68"><code>1af3b93</code></a>
update readme/changelog for v6 (<a
href="https://redirect.github.com/actions/checkout/issues/2311">#2311</a>)</li>
<li><a
href="71cf2267d8"><code>71cf226</code></a>
v6-beta (<a
href="https://redirect.github.com/actions/checkout/issues/2298">#2298</a>)</li>
<li><a
href="069c695914"><code>069c695</code></a>
Persist creds to a separate file (<a
href="https://redirect.github.com/actions/checkout/issues/2286">#2286</a>)</li>
<li><a
href="ff7abcd0c3"><code>ff7abcd</code></a>
Update README to include Node.js 24 support details and requirements (<a
href="https://redirect.github.com/actions/checkout/issues/2248">#2248</a>)</li>
<li>See full diff in <a
href="93cb6efe18...1af3b93b68">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=5.0.1&new-version=6.0.0)](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>
2025-11-22 06:50:34 -08:00
Mitchell Hashimoto
6529baea46 Change renderer from screen clones to new RenderState (#9662)
This adds a new API called `RenderState` that produces the proper state
required for a renderer to draw a viewport and updates our renderer to
use it instead of the prior screen clone method.

The newsworthy change is here is that we've shortened the critical area
where the renderer holds the terminal lock and blocks IO by anywhere
from **2x to 5x faster**, and in about half the frames we're now in the
critical area for **zero microseconds** because `RenderState` has much
better dirty/damage tracking.

**For libghostty Zig users**, this API is available to the Zig module
and can be used to create your own renderers (to any artifact, it
doesn't have to be graphical! It is even useful for text like a tmux
clone or something).

## Differences vs Old Method

The renderer previously called `Screen.clone`. This produces a copy of
all the screen data (even stuff the renderer may not need) and attempts
to create a standalone, fully functional `Screen` allocation. I didn't
microbenchmark this to understand exactly where the slowdown was, but I
think it was based primarily in two places:

- Screens have a minimum allocation of two Ghostty "pages" which are
quite large. I think this allocation was the primary expensive part.
Without refactoring our entire Screen/PageList work, this was
unavoidable.
- The clone was unconditional and would produce a fully new Screen on
every frame.

The new structure `RenderState` is stateful and each frame calls
`update` on the prior value and it modifies itself in place. This
addresses the above two points in a few ways:

- Allocation is minimized to only what we need, no full pages.
- Since it is stateful (updating in-place), we only change dirty data
and don't need to copy everything. **Note the benchmarks below only test
_full updates_, so you aren't even seeing the benefits of this!**
- Also since it is stateful, we cache expensive calculations (such as
for selections) so future screen updates can reuse those cached values
rather than recompute them.
- We retain memory from prior updates even when the screen is dirty, so
even in dirty states, it's unlikely we allocate.

More details in the "Design" section.

## Benchmarks

Here are some microbenchmarks on the previous render critical area
versus the new one.

For each of the benchmarks below, ignore the time units (milliseconds)
and instead **focus on the relative speedup.** The benchmarks are all
doing a full render frame setup around 1000 times, because the actual
cost of a frame update is in dozens or hundreds of microseconds.

> [!NOTE]
>
> I'm still working on some more benchmarks before merging. I'll update
this space.

### Full screen, single style

<img width="1542" height="480" alt="CleanShot 2025-11-21 at 07 35 13@2x"
src="https://github.com/user-attachments/assets/c28c9f33-d1aa-4723-8a8e-3c6d70fe3667"
/>

### Full screen, plaintext

<img width="1642" height="612" alt="CleanShot 2025-11-21 at 07 36 06@2x"
src="https://github.com/user-attachments/assets/b51f57cf-7c48-46c8-a347-8ecc0bdd3d47"
/>

### Full screen, different style per cell (pathological case)

<img width="1704" height="456" alt="CleanShot 2025-11-21 at 07 37 55@2x"
src="https://github.com/user-attachments/assets/71a98250-d8d1-47ab-ae69-5e6b3b60bf2d"
/>

### Critical Area: Typing at Shell Prompt

| This PR | Main Branch |
---------|--------
| <img width="1064" height="764" alt="CleanShot 2025-11-21 at 14 44
31@2x"
src="https://github.com/user-attachments/assets/8a0ab3a1-3d68-41f0-9469-bc08a4569286"
/> | <img width="1040" height="684" alt="CleanShot 2025-11-21 at 14 47
10@2x"
src="https://github.com/user-attachments/assets/04ffa607-8841-436b-b6e9-eeeb6ee9482d"
/> |

### Critical Area: Neovim Scrolling

| This PR | Main Branch |
---------|--------
| <img width="1054" height="748" alt="CleanShot 2025-11-21 at 14 45
06@2x"
src="https://github.com/user-attachments/assets/ccafaee8-720f-41be-820d-fd705835607a"
/> | <img width="1068" height="796" alt="CleanShot 2025-11-21 at 14 47
48@2x"
src="https://github.com/user-attachments/assets/68087496-d371-4c7c-8b4c-b967dbaeaa7c"
/> |

### Critical Area: `btop -u 100`

This is closer to a pathological case, about as close as you get with a
real tool in the wild. `btop` uses hundreds of unique styles and updates
many cells across many rows very frequently (every 100ms in this case).
You can see that some of our frame times in this case are similar but
there are _so many more near-idle frames_ thanks to our dirty tracking.

| This PR | Main Branch |
---------|--------
| <img width="1088" height="900" alt="CleanShot 2025-11-21 at 14 45
44@2x"
src="https://github.com/user-attachments/assets/ea63f0eb-f06e-4d00-95a3-c55a3755cc67"
/> | <img width="1078" height="906" alt="CleanShot 2025-11-21 at 14 48
18@2x"
src="https://github.com/user-attachments/assets/cef360de-2b12-440f-8c4c-6a69b5ce4058"
/> |

### "DOOM Fire" 

Fullscreen on my macOS when from 770 FPS to ~808 FPS consistently, a
solid 5% increase repeatedly.

<img width="3520" height="2392" alt="CleanShot 2025-11-21 at 07 45
29@2x"
src="https://github.com/user-attachments/assets/033effca-0abb-4ff8-a21b-83214d118d12"
/>


### IO 

While this was rendering focused, the smaller critical area does help IO
performance a bit.

We've already tracked down the remaining issues to the IO thread going
to sleep and overhead with context switching. We're investigating
switching to a spin lock for the IO thread only in another track of
work.

> [!NOTE]
>
> **This is comparing with `main`, which already has a 20-30%
performance improvement over v1.2.3.**

<img width="982" height="698" alt="image"
src="https://github.com/user-attachments/assets/52a86f6c-6f09-45fe-9ac7-ca62c7ac6ee4"
/>

## Design

The design of the API is a _stateful_ `RenderState` struct that you call
`update` on each frame with the target terminal, and it only updates
what is needed. RenderState keeps track of rows, cells, hyperlinks,
selections, etc.

```zig
// Start empty
var state: terminal.RenderState = .empty;
defer state.deinit(alloc);

// Each frame update it with a terminal. 
// THIS IS THE ONLY PART THAT ACCESS `t`
try state.update(alloc, &t);

// Access render data (can be outside any locking for `t`)
...
```

The ergonomics of the `RenderState` structure a wee bit clunky because
we make use of struct-of-arrays (SoA, Zig's MultiArrayList) to better
optimize cache locality for renderer data vs. update data (what we need
to update the render state is different from what we need to draw).

Once you get used to the API though, it's pretty beautiful. I mean, look
at this:

```zig
        for (
            0..,
            row_data.items(.raw),
            row_data.items(.cells),
        ) |y, row, cells| {
            const cells_slice = cells.slice();
            for (
                0..,
                cells_slice.items(.raw),
                cells_slice.items(.grapheme),
            ) |x, cell, graphemes| {
```

## Improvements

This PR makes various improvements across the board:

- It bears repeating in case it was missed previously that the critical
area time of a render has gone down 2x to 5x when there is work and is
now free when there is no work (the previous implementation always did
work).
- Font shaping is much more efficient now and only requires access to a
render state.
- Selection handling is now cached and works with dirty tracking.
Previously, if you had an active selection, we'd search the entire
screen multiple times (like... once per row). Yikes.
- Hyperlink handling is _much_ more efficient. Instead of iterating
through the entire screen contents _per configured link_ we now cache
the screen contents as a string and search one whole string multiple
times. Obvious, but we didn't do this before.
- The `contrainedWidth` and `rowNeverExtendBg` helper methods are now
both much more efficient and live within the renderer package rather
than being awkwardly in the terminal package.

## Future Notes

- Our `terminal.Selection` API is very bad. It conceptually makes sense
and I understand why I designed it this way (easy) but it makes it hard
to render or manipulate performantly.

**AI Disclosure:** AI was used only to assist with writing some tests
and converting some tests. The primary logic is all organic, meatbag
produced.
2025-11-22 06:36:05 -08:00
Mitchell Hashimoto
3283f57fd2 lib-vt: expose RenderState API 2025-11-21 16:01:22 -08:00
Mitchell Hashimoto
82f5c1a13c renderer: clear renderstate memory periodically 2025-11-21 09:03:03 -08:00
Mitchell Hashimoto
2ecaf4a595 font/shaper: fix harfbuzz tests 2025-11-20 22:00:44 -08:00
Mitchell Hashimoto
3d56a3a02b font/shaper: remove old pre-renderstate logic 2025-11-20 22:00:44 -08:00
Mitchell Hashimoto
c892599385 terminal: cache some selection state to make render state faster 2025-11-20 22:00:44 -08:00
Mitchell Hashimoto
7728620ea8 terminal: render state dirty state 2025-11-20 22:00:44 -08:00
Mitchell Hashimoto
86fcf9ff4a terminal: render state selection 2025-11-20 22:00:44 -08:00
Mitchell Hashimoto
a15f13b962 terminal: renderstate tests 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
5d58487fb8 terminal: update renderstate to use new assert 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
6e5e24c3ca terminal: fix lib-vt test builds 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
cd00a8a2ab renderer: handle normal non-osc8 links with new render state 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
fa26e9a384 terminal: OSC8 hyperlinks in render state 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
81142265aa terminal: renderstate stores pins 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
b8363a8417 terminal: update render state for new dirty tracking 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
cc268694ed renderer: convert bg extend to new render state 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
07115ce9a9 terminal: render state contains raw row data 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
d1e87c73fb terminal: renderstate now has terminal colors 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
ebc8bff8f1 renderer: switch to using render state 2025-11-20 22:00:43 -08:00
Mitchell Hashimoto
9162e71bcc terminal: render state contains cursor state 2025-11-20 22:00:43 -08:00