This adds the `UTF8_STRING` atom and explicit UTF-8 character set MIME
type (`text/plain;charset=utf-8`) to text content when being sent to the
clipboard under the new multipart support.
This fixes clipboard support under X11 particularly, which generally
looks for the `UTF8_STRING` atom when looking for text content. This can
be verified with `xclip -out -verbose`, or trying to do things like
paste in Firefox.
I've noted that there's a number of other older atoms that exist, but
I've refrained from adding them for now. Kitty only seems to set
`UTF8_STRING` and I've had a hard time finding consensus on what exactly
is the correct set otherwise.
This adds the UTF8_STRING atom and explicit UTF-8 character set MIME
type (text/plain;charset=utf-8) to text content when being sent to the
clipboard under the new multipart support.
This fixes clipboard support under X11 particularly, which generally
looks for the UTF8_STRING atom when looking for text content. This can be
verified with xclip -out -verbose, or trying to do things like paste in
Firefox.
I've noted that there's a number of other older atoms that exist, but
I've refrained from adding them for now. Kitty only seems to set
UTF8_STRING and I've had a hard time finding consensus on what exactly
is the correct set otherwise.
This supports the new `setClipboard` parameter that may provide data in
multiple formats, allowing us to copy rich text to/from the clipboard as
well as other types in the future.
This all fixes a memory leak on all clipboard ops that snuck through.
This supports the new `setClipboard` parameter that may provide data in
multiple formats, allowing us to copy rich text to/from the clipboard as
well as other types in the future.
Fixes#9426
Since we can't set the meta charset tag since we emit partial HTML, we
use codepoint entities like `{` for non-ASCII characters to ensure
proper rendering.
Fixes#9426
Since we can't set the meta charset tag since we emit partial HTML, we
use codepoint entities like `{` for non-ASCII characters to
ensure proper rendering.
Fixes#9420
The problem was ultimately that the padding calculations assumed that
the total vertical padding is always less than one cell height (just
doing `fmod(contentHeight, cellHeight)` instead of the more careful
`contentHeight - scrollbar.len * cellHeight`). This is not at all true,
and as a result, the calculated document height was often a cell height
short of what it should be.
For similar reasons, we shouldn't rely on `fmod`-based padding update
calculations during relayouting. This PR takes the proper approach of
saving and reusing the current scrollbar state to calculate the correct
document height on every layout. As a bonus, this removes the flickering
scrollbar during resize that I complained about in #9296.
Fixes#9397
This makes `copy_to_clipboard` take an optional parameter with the
format to copy. **The default has changed to `mixed`,** which will set
multiple content types on the clipboard allowing the OS or target
application to choose what they prefer. In this case, we set both
`text/plain` and `text/html`.
This only includes the macOS implementation. The GTK side still needs to
be done, but is likely trivial to do.
https://github.com/user-attachments/assets/b1b2f5cd-d59a-496e-bb77-86a60571ed7f
This moves the bg, fg, cursor color state into the `Terminal` struct,
including all default and override handling. I've rewritten our color
palette handling so that overrides, defaults, resets, etc are all
handled by the terminal package. I've added much more unit test
coverage.
This has various benefits:
* libghostty now handles OSC and Kitty color operations
* formatters can be aware of all of these colors (not implemented in
this PR)
* renderer has way less inter-thread messages
* renderer uses less memory
* termio stream handler uses less memory and becomes simpler
* override logic, changing defaults can all be unit tested
* we have unit tests for kitty color operations end-to-end now (cc
@jcollie heyo!)
There's a ton of risk on this PR too. There's a lot of really tiny
conditionals EVERYWHERE and I'm sure I got at least one wrong. We'll let
this bake in tip to find those, I'm sure they're minor and will show up
as just the wrong color somewhere.
**AI disclosure:** Amp wrote many of the tests. I did all the
implementation.
With its being `focusable`(default), the first responder became the text
editor instead of the paste button.
This fixes the issue where one can't confirm with the keyboard.
This doesn't affect its selection.
std.fs.makeDirAbsolute() only creates the last directory. We instead
need Dir.makePath() to make the entire path, including intermediate
directories.
This fixes the problem where a missing $XDG_STATE_HOME directory (e.g.
~/.local/state/) would prevent our ssh cache file from being created.
Fixes#9393
Bumps
[cachix/install-nix-action](https://github.com/cachix/install-nix-action)
from 31.8.1 to 31.8.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/cachix/install-nix-action/releases">cachix/install-nix-action's
releases</a>.</em></p>
<blockquote>
<h2>v31.8.2</h2>
<h2>What's Changed</h2>
<ul>
<li>nix: 2.32.1 -> 2.32.2 by <a
href="https://github.com/github-actions"><code>@github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/cachix/install-nix-action/pull/259">cachix/install-nix-action#259</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/cachix/install-nix-action/compare/v31.8.1...v31.8.2">https://github.com/cachix/install-nix-action/compare/v31.8.1...v31.8.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="456688f15b"><code>456688f</code></a>
Merge pull request <a
href="https://redirect.github.com/cachix/install-nix-action/issues/259">#259</a>
from cachix/create-pull-request/patch</li>
<li><a
href="0cacfe0f2a"><code>0cacfe0</code></a>
nix: 2.32.1 -> 2.32.2</li>
<li>See full diff in <a
href="fd24c48048...456688f15b">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>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This adds HTML formatting capabilities to the formatter package. HTML is
emitted as inline styles. For palette indexes, direct RGB is emitted if
we have access to a palette; otherwise, we fall back to CSS variables.
This isn't exposed to end users yet, but will enable copy as html
features. This is available in libghostty.
Fixes#9395
**AI disclosure:** I used AI (Amp) to help me write tests, but the
implementation was done manually. I reviewed everything.
There have been frequent reports of key encoding issues in vim and tmux
with version 1.2.3 on macOS: #9340, #9361, #9401,
https://discord.com/channels/1005603569187160125/1432413679806320772.
I think I found the culprit: the option modifier is always passed as alt
to the core, regardless of `macos-option-as-alt`. Since #9289, this
means that a key event where option was used (as option) for translation
is encoded as if it also has the alt modifier.
For example, consider the many European keyboard layouts where option+8
sends `[`. If `macos-option-as-alt = true`, Ghostty correctly intercepts
the option and encodes option+8 as alt+8 instead (that is,
`^[[27;3;56~`). But if `macos-option-as-alt = false`, Ghostty first
allows option to be used for translation, obtaining `[`, and then
encodes the key event as alt+[ (that is, `^[[27;3;91~`), rather than
just `[`.
Tweaking the test case from #9289, here's a quick way to see this: set
`macos-option-as-alt = left`, run
```
printf '\033[>4;2m'
cat
```
choose a European keyboard layout (e.g., Norwegian), and hit both
left-option+8 and right-option+8. The former inserts `^[[27;3;56~` in
all well-behaved terminals. The latter inserts `[` in other terminals,
but `^[[27;3;91~` in Ghostty.
Basically, while modify other keys 2 does require encoding consumed
modifiers, the option key is not one of the supported modifiers, and
should not be included (as alt or anything else) when
`macos-option-as-alt = false`.
This PR removes alts that were actually options when using modify other
keys 2.
This removes all existing functionality that I know of that encodes a
terminal, screen, pagelist, or page as plaintext and unifies all logic
onto the formatter system.
This replaces the logic of Screen.selectionString with calls to
ScreenFormatter.
This means that all our various selection-based features like copying to
clipboards now uses the new formatter. The formatter code is now
user-facing.
This forced us to pass all selectionString tests which revealed some
edge cases that were not handled correctly before in the formatter! The
formatter now handles:
- Plain text now emits `\n` instead of `\r\n`. VT emits `\r\n`
- Rectangular selections
- Various wide character edge cases
- Selection is now inclusive on the end, not exclusive
This adds the option `pin_map` or `point_map` (for pages) to formatter,
allowing callers to get a byte-by-byte mapping for where on the screen
each encoding maps to. This is used by search internals and hyperlinks.
I haven't hooked that all up yet. This diff was big enough I wanted this
as one.
Tests significantly cover the new feature.
Next up, we'll rip out `selectionString` and replace it with formatters!
This adds a new formatter that can be used with standard Zig `{f}`
formatting that emits any portion of the terminal screen as VT
sequences. In addition to simply styling, this can emit the entire
terminal/screen state such as cursor positions, active style, terminal
modes, etc.
To do this, I've extracted all formatting to a dedicated `formatter`
package within `terminal`. This handles all formatting types (currently
plaintext and VT formatting, but can imagine things like HTML in the
future). Presently, we have "formatting" split out across a variety of
places in Terminal, Screen, PageList, and Page. I didn't remove this
code yet but I intend to unify it all on formatter in the future.
This also doesn't expose this functionality in any user-facing way yet.
This PR just adds it to the ghostty-vt Zig module and unit tests it.
Ghostty app changes will come later.
**This also improves the readonly stream** to handle OSC color
operations for _setting_ but it doesn't emit any responses of course,
since its readonly.
Fixes a bug described in #9291, where resizing an empty window causes
the scrollbar to appear even though the window remains larger than the
total content, because the relayouting fails to account for the change
in padding around the cell grid.
To reproduce the issue:
1. Enable legacy scrollbars (System Settings -> Appearance -> Show
scroll bars -> Always)
2. Open Ghostty
3. Vertically resize the window to make it smaller. The scrollbar will
pop up, and as you drag the window edge, it will cycle between a maximum
offset and zero depending on how far the resize is from an integer
multiple of the cell height.
With this PR you'll still see the scrollbar flicker while resizing, but
when you stop it will always disappear. Haven't figured out how to get
rid of the flickering yet. I tried to condition various updates on the
window not being in a live resize, but so far no luck.
contains() checks the cache for an existing entry. It's a read-only
operation, so we can drop the write bit and fixupPermissions() call.
This is also consistent with the list() operation.
fixupPermissions() is unnecessary in this code path. It provided minimal
additional security because all of our creation and update operations
enforce 0o600 (owner-only) permissions, so anyone tampering with this
file has already gotten around that. The contents of this (ssh host
cache) file are also not sensitive enough to warrant any additional
hardening on reads.