This adds a cross-platform ability to draw an overlay of debug
information directly on top of our terminal.
The debug overlay is drawn on the CPU using z2d and then rendered as an
image on the GPU (using the same subsystem and shaders as things like
the Kitty Graphics Protocol). This lets us iterate more quickly on
overlays since it's all simple imperative 2D drawing operations, and
also lets it be cross-platform immediately.
The goal of this PR is to **introduce the framework.** To enable the
overlays, you have to modify code and recompile it, but of course in the
future this will be runtime toggle-able via the inspector. Additionally,
the dummy overlay in this PR (OSC8 highlighting) isn't particularly
useful and has limitations; it's only meant to be a demo. More realistic
overlays will come later.
## Demo
Here is a demo of an overlay highlighting OSC8 hyperlinks:
<img width="1746" height="1648" alt="CleanShot_2026-01-30_at_15 04 582x"
src="https://github.com/user-attachments/assets/4c490d11-5baa-4ef6-b97b-1cfd321235d8"
/>
## Performance
```
Empty but overlay active:
generic_renderer: [rebuildOverlay time] start_micro=1769835575905814 duration=12ns
generic_renderer: [updateFrame time] start_micro=1769835575905766 duration=199ns
Hyperlink on every line:
generic_renderer: [rebuildOverlay time] start_micro=1769835573560797 duration=452ns
generic_renderer: [updateFrame time] start_micro=1769835573560682 duration=891ns
```
Pretty great! 🎉
With the overlay active but nothing to draw, the overlay adds ~6%
overhead. With the overlay active and hyperlinks on every line, the
overlay adds ~50% overhead. These are big percentages, but the absolute
numbers are miniscule. And, for a high value debug feature that is only
on some of the time, it's totally acceptable.
Also, as a reminder, for 120fps we have _8.3 million nanoseconds_ per
frame (including the time to draw, of course). If we're spending
sub-1000ns we're in the realm of 1M FPS. So, it's fine. lol.
(Edit: I previously had debug timings which are significantly worse but
have a lot more safety checks.)
## Other Notes
* **Damage/dirty tracking.** I don't do any for the overlay, and we just
rebuild it on every frame. I'm not convinced the complexity is worth it
for a debug overlay and the performance numbers at this point do not
necessitate it in any way.
* **Update vs draw times.** We call `draw` _far_ more often than
`update` so putting the overlay rebuild into `update` made way more
sense. I did measure both.
This extracts all our image renderer state into a separate struct,
blandly named `renderer.image.State`. This structure owns all the
storage of images and placements and exposes a limited public API to
manage them.
One motivation was to limit state access by our Kitty graphics functions
within the generic renderer. Another was to limit our own generic
renderer from getting our image system into an incoherent state. This is
prevented now on both sides due to some encapsulation.
This currently only supports Kitty images, since that's the only image
protocol we support. But I intend to add additional image types to this,
namely the ability to add overlay images for debug information. **There
are no plans to add new image protocols to the terminal,** the
extraction is purely to support some internal features. But, it could be
used for other protocols one day.
This extracts all our image renderer state into a separate struct,
blandly named `renderer.image.State`. This structure owns all the
storage of images and placements and exposes a limited public API
to manage them.
One motivation was to limit state access by our Kitty graphics functions
within the generic renderer. Another was to limit our own generic
renderer from getting our image system into an incoherent state. This is
prevented now on both sides due to some encapsulation.
This currently only supports Kitty images, since that's the only image
protocol we support. But I intend to add additional image types to this,
namely the ability to add overlay images for debug information.
**There are no plans to add new image protocols to the terminal,** the
extraction is purely to support some internal features. But, it could be
used for other protocols one day.
The current runVM function gets called only with a module and without a desc,
resulting in an error when running `nix flake show` or similar
commands.
This commit drops the `desc` argument to `runVM` and gets rid of that problem.
Inspector is getting some love! While working on some new functionality
I found a bunch of bugs. Sending the bug fixes separately.
- Mode checkboxes didn't have a unique ID, causing Imgui warnings
- renderer: we keep a draw timer active while the inspector is visible,
allowing animations to work
- GTK and macOS: we were always calculating a delta time of 0 since we
converted to float after int math, making hover timers not work
- macOS: precision scrolling made scrolling way too fast, slow it down
No AI, just meat sticks
As mentioned in
https://github.com/ghostty-org/ghostty/pull/10332#issuecomment-3800353166,
the Harfbuzz shaping tests that depend on specific fonts (that are on
macOS, but not whatever linux distro we use for CI) aren't being checked
in CI. The `build-macos-freetype` CI check is primarily to make sure
Freetype can build on Mac, but if we switch to the `coretext_freetype`
backend, we still use Freetype for rendering, but then we get Coretext
for font discovery which then enables these tests to run.
Closes#7877.
Small disclaimer: First Ghostty PR and Zig PR, but looking forward to
contributing to the project.
This PR supports shell-integration-features `ssh-env` and `ssh-terminfo`
as per other shells, but not the rest as this is what the issue states.
That being said, with this PR, then you would see this:
- `warning(io_exec): shell could not be detected, no automatic shell
integration will be injected`, but given that the default mode is
`detect` it will pick up the executable and if ssh features are enabled
it will integrate it. This might be confusing for users.
- I decided to not add `nu` to `pub const Shell` because if we do so,
then from what I understand from the code, then the code flow would
imply that "shell integration will be injected" but it will only do so
if those `ssh-*` features are enabled, which may be misleading. But on
the other hand, providing `ssh` shell integration but returning `null`
for `?!ShellIntegration` does not seem very correct either.
- I dont like that I added `features` argument to `setupshell`, just to
check them if `nu` was used. The reasoning is because the way Nushell
works, if we autoload the `nushell` directory (by `setupXdgDatadirs()`)
even if no `ssh` features were present, it will wrap the `ssh` function
and I think that is not desirable, even if we end up just forwarding the
arguments.
Sorry for the long wall of text, but I think it was worth to add some of
the doubts I have had myself, plus the ones that you folks may add. I am
very happy to iterate on this, even if its a very "Easy" one, so I much
welcome the feedback.
> [!NOTE]
>
> Used `GPT` for helping with nushell variable naming
verification/improvement
> Used `Gemini` for helping with understanding the `Zsh` ssh integration
so that I could replicate the logic with nushell. Just because I find
`zsh` language very difficult to understand in detail.
Bumps
[hustcer/milestone-action](https://github.com/hustcer/milestone-action)
from 3.0 to 3.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/hustcer/milestone-action/releases">hustcer/milestone-action's
releases</a>.</em></p>
<blockquote>
<h2>v3.1</h2>
<h2>[3.1] - 2026-01-23</h2>
<h3>Documentation</h3>
<ul>
<li>Update milestone-action version in README (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/162">#162</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>Break before sleep when milestone found</li>
</ul>
<h3>Miscellaneous Tasks</h3>
<ul>
<li>Update README.md (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/166">#166</a>)</li>
</ul>
<h3>Deps</h3>
<ul>
<li>Update Nu to 0.109.1</li>
<li>Update Nushell to 0.110.0 (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/167">#167</a>)</li>
<li>Upgrade hustcer/setup-nu to v3.22</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/hustcer/milestone-action/blob/main/CHANGELOG.md">hustcer/milestone-action's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<h2>[3.1] - 2026-01-23</h2>
<h3>Documentation</h3>
<ul>
<li>Update milestone-action version in README (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/162">#162</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>Break before sleep when milestone found</li>
</ul>
<h3>Miscellaneous Tasks</h3>
<ul>
<li>Update README.md (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/166">#166</a>)</li>
</ul>
<h3>Deps</h3>
<ul>
<li>Update Nu to 0.109.1</li>
<li>Update Nushell to 0.110.0 (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/167">#167</a>)</li>
<li>Upgrade hustcer/setup-nu to v3.22</li>
</ul>
<h1>Changelog</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<h2>[3.0] - 2025-10-26</h2>
<p>This release introduces changes that may impact some users. If the
action fails due to insufficient permissions, please add the
<code>issues: write</code> and <code>pull-requests: write</code>
permissions to your workflow. Additionally, the API for binding
milestones has been modified. Due to these changes, the major version
has been incremented to 3.</p>
<h3>Bug Fixes</h3>
<ul>
<li>Try to fix GitHub Projects (classic) deprecation warning by using
REST API instead of GraphQL (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/157">#157</a>)</li>
<li>Fix "Resource not accessible by integration" error for
issue milestone binding by adding <code>issues: write</code>
permission</li>
</ul>
<h3>Documentation</h3>
<ul>
<li>Update README with required permissions configuration</li>
<li>Remove unnecessary <code>contents: write</code> permission
requirement</li>
</ul>
<h2>[2.12] - 2025-10-25</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Fix some typos</li>
</ul>
<h3>Miscellaneous Tasks</h3>
<ul>
<li>Sort milestones by due date, then creation time (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/153">#153</a>)</li>
<li>Update output table width for logs</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ebed8d5daa"><code>ebed8d5</code></a>
Bump to v3.1</li>
<li><a
href="e2f911c127"><code>e2f911c</code></a>
chore: Update Nushell to 0.110.0 (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/167">#167</a>)</li>
<li><a
href="800adcbc5e"><code>800adcb</code></a>
deps: Upgrade hustcer/setup-nu to v3.22</li>
<li><a
href="91ff3608b5"><code>91ff360</code></a>
deps: Update Nu to 0.109.1</li>
<li><a
href="906178b9f5"><code>906178b</code></a>
chore: Update README.md (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/166">#166</a>)</li>
<li><a
href="90f06528b6"><code>90f0652</code></a>
feat: Break before sleep when milestone found</li>
<li><a
href="93d96c3f77"><code>93d96c3</code></a>
Update CHANGELOG.md</li>
<li><a
href="7ee8118a50"><code>7ee8118</code></a>
docs: Update milestone-action version in README (<a
href="https://redirect.github.com/hustcer/milestone-action/issues/162">#162</a>)</li>
<li><a
href="8d333973bd"><code>8d33397</code></a>
Try to remove permission section</li>
<li>See full diff in <a
href="dcd6c3742a...ebed8d5daa">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>