PR #227 bumped tty7's direct resvg to 0.47 while the gpui fork still
pinned 0.45, so the tree compiled two resvg/usvg/tiny-skia stacks. The
fork's tty7 branch now carries resvg 0.47 (l0ng-ai/zed@3aac3ef); move
the gpui pin there so gpui's SVG renderer and tty7's tray-icon
rasterizer share one 0.47 stack again.
gpui-component still declares its own resvg 0.45.1 (semver-incompatible
with 0.47), so one legacy 0.45 stack remains until that fork catches up
- noted in the manifest comments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three holes in the readline-parity work, found in review:
- The walk's reset lived only in `handle_editor_key`, but edits arrive by
other doors too — IME-committed text (all typing on macOS and Windows),
paste, a completion pick, cmd-Z. A repeat alt-. after any of those deleted
the recorded span even though it no longer held the walk's word. The walk
now stores the word itself and resumes only while the line still shows it
at `at` with the caret at its end; `commit_text` also clears the walk
outright, mirroring the key path's reset.
- A fresh alt-. over an active selection recorded `at` from the pre-insert
caret, but `insert_str` collapses the selection to its start first — so
the span pointed past the inserted word and the next press ate the wrong
text. `at` is now derived from where the caret actually landed.
- The unknown-Meta handoff built `ESC` + the key name, which gpui reports
unshifted — alt-shift-U shipped `ESC u` instead of `ESC U`. The shared
encoder now goes first (it knows the shifted character and the Kitty form
when `key_char` is present), with the hand-built fallback uppercasing
under Shift.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Windows environment blocks are case-insensitive: portable-pty's
CommandBuilder keeps one slot per lowercased key, so a configured
`Term`/`ColorTerm` in `env` would land in the same slot as
`TERM`/`COLORTERM` and, coming later, replace it -- sidestepping the
rule that user env may rename the terminal but not contradict what the
pane's decoder implements. Filter capability keys with the platform's
own notion of "the same variable": case-insensitive on Windows, exact
elsewhere (where a differently-cased key is a genuinely distinct
variable and stays the user's to set). Pinned by a Windows-only test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dependabot only rewrote Cargo.lock, but the manifest still required
resvg 0.45, so every `cargo build --locked` CI job failed with
"cannot update the lock file". Bump the requirement to 0.47 and update
the pin-rationale comment: gpui/gpui-component still carry resvg
0.45.x, so a second resvg/usvg/tiny-skia stack now compiles until the
fork catches up (no type conflicts; only image::RgbaImage crosses the
tty7/gpui boundary).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dependabot bumped sha2 to 0.11.0 in Cargo.lock but left the manifest
requiring 0.10, so --locked builds failed on every platform. tty7's
only sha2 usage (Sha512::digest in core::keychain) is unchanged in 0.11.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
native_cell_residue was inserted between seg_clip_width's doc block and
the function itself, so rustdoc attached the clip-width prose to the
residue helper and left seg_clip_width undocumented. Move the helper
(with its own doc) above the block instead. No code change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The render call site still said "show focus rings only when split" -- the
same misdescription the parameter rename in this PR removes: nothing ever
drew a ring, the flag only gated the fade, and the fade condition is now
spelled out two lines below.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two follow-ups to #229.
Underlines ride on the `TextRun` that `paint_glyphs` builds, and the Solo
arm returned early for every natively-drawn cell — so an `ESC[4m` span or a
hovered URL showed a one-column hole wherever it crossed a box-drawing
character. The mechanism predates #229 (the Powerline branch has always had
it), but #229 widened it from a dozen private-use separators to all 256
characters of U+2500–U+259F. Such a cell now shapes a space in its own
style instead of returning, so gpui draws the line from the same
`UnderlineStyle` — curly and double included — that every other cell uses.
Stroke weight varied between cells at fractional device scale. `rectb`
snaps a rect's two edges independently, which is what makes neighbouring
cells tile, but two edges `w` apart land `w × scale` device pixels apart:
when that is not a whole number the two roundings straddle it, so a
1-logical-pixel rule came out 1 device pixel wide in one column and 2 in
the next. At Windows' default 125%/150% scaling that alternated thin/thick
across every column of a TUI table, and down every row for horizontal
rules. Integer scales are blind to it by construction, which is why 1x and
2x looked right.
`light_thickness` now quantises to whole device pixels, and `vstroke` /
`hstroke` lay that width off from the snapped near edge rather than
inferring it from a second snap — float ties at `.5` made the quantisation
alone insufficient. Stroke ends still snap, so #229's tiling guarantee is
untouched. Block elements stay on `rectb`: they are area fills, not
strokes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every character came out as a different character, one for one, consistently
— it read as a broken locale or a mangled encoding, and it was neither.
Hack, the bundled default, has no CJK, so those cells are shaped through the
font-fallback chain. gpui's Windows backend then threw away the face
DirectWrite shaped the run with and looked a fresh one up by family, weight and
style. That round trip mapped DirectWrite's italic to oblique — the enum is
numbered OBLIQUE = 1, ITALIC = 2, and the mapping had them the other way around
— so an italic fallback face resolved to a request for an oblique one, and a
family with no oblique face (Maple Mono NF CN, first in our Windows chain) came
back as its upright face instead. The glyph indices were right; the outlines
they indexed belonged to a different face, at a fixed glyph-id skew.
Fixed upstream in our gpui fork by registering the face DirectWrite actually
chose rather than re-deriving one, which also closes a latent use-after-free in
the same cache: it keyed fonts by a raw pointer to a face nothing held a
reference to, so a released face could be aliased by any later allocation.
Bumps the fork pin; no tty7 code changes. Covered there by two tests in
`gpui_windows::direct_write` — one asserting a shaped run's glyphs round-trip
through the font id the run reports, one asserting every font-face cache key is
owned by the font it maps to.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#214 added the switch itself; this is the wiring around it that a new
setting in this codebase is expected to carry.
- Index the row in `settings_search_entries`, which the settings search
box matches against. Without an entry, searching "dim", "fade" or
"unfocused" — the words someone actually looks for — finds nothing,
and the switch is only reachable by scrolling to it. Pinned in
`index_titles_match_rendered_row_labels` so the title cannot drift.
- Pin the default and the round trip, as every other `default_true`
flag here does (see `confirm_window_close_defaults_on_and_round_trips`):
a config written before the switch existed must still dim, and a
`false` must survive save/load or the effect comes back next launch.
- Hand the flag to `Pane::render` instead of reading the `Config` global
from inside it. `pane.rs` had no global state before, deliberately —
the leaf type is generic so the tree logic can be tested with plain
values. The caller already computes the split test the dimming was
gated on, so it can compute this too: one lookup per frame rather than
one per leaf, and the tree stays renderable without a Config global.
While there, `show_focus` is now named for what it does — nothing
drew a focus ring; it only ever gated the fade.
- Move the row below "Follow theme". That button clears the opacity and
blur overrides only, and a third row directly above it read as
something it would also reset.
- Changelog entry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The polyline arc band couldn't match kitty's corners on two counts,
both dictated by how gpui rasterizes paths:
* Separately painted segments composite as premultiplied sprites, so
two abutting antialiased edges meet at 75% opacity — a lighter seam
at every joint. All contours now ride in ONE Path (via move_to),
where the 4x-MSAA samples partition cleanly across shared edges.
* Straight path edges only get 4-level MSAA antialiasing, while
`curve_to` quadratics are antialiased analytically (Loop–Blinn
signed distance) — a continuous ramp, the same quality kitty gets
from supersampling. The outer arc edge is now a real quadratic per
30° slice (control point at the tangents' intersection).
The inner edge stays a fine polyline: gpui accumulates coverage with
no winding cancellation, so a concave-side curve bulge can only
over-cover (the origin of the original quarter-disc blob). Its chord
error at 7.5° steps is under 0.1px and hides inside the MSAA. Each 30°
contour is star-shaped from its start vertex, which is what the fan
fill actually requires.
Straight strokes with edges at fractional device pixels rasterize an
antialiasing ramp at each end, and two abutting 50%-coverage ramps
composite to 75% opacity — so a multi-row │ was perforated by a lighter
band at every cell boundary and read as broken next to kitty's solid
lines (kitty's cell-aligned box bitmaps never sit off-grid).
Snap every rectangle edge to whole device pixels via the window scale
factor. Edges are snapped individually (not origin + size), so the two
cells sharing a boundary snap the same coordinate to the same pixel
line: zero gap, zero overlap, whatever the window position. Arcs and
diagonals keep their antialiasing on purpose; the rounded corners'
straight stubs now reach one device pixel into the arc band to cover
the snapped-meets-unsnapped handoff.
Box characters rendered as font glyphs only cover the font's own line
height, but the cell is font_size × line_height (1.4 by default) — so
every vertical run of │/╭/╰ broke into dashes with a gap at each row
boundary: a two-line prompt's corners never connected, a TUI frame was
perforated down both sides.
New `terminal::boxdraw` module draws U+2500–U+257F and U+2580–U+259F as
geometry pinned to the cell's real edges, the same special case every
terminal with a line-height setting ships (kitty, alacritty, WezTerm)
and the same approach the existing Powerline separators use:
* light/heavy lines, corners, tees, crosses: per-arm rectangles with a
centre overshoot so any weight combination joins solid
* the double-line set: explicit per-character stroke lists, keeping the
open junctions (╬ is four corners around a hole) intact
* rounded corners ╭╮╯╰: straight stubs plus a quarter-circle band of
overlapping convex quads — a single band outline is concave, which
gpui's fan fill renders as a solid blob, and butted segments seam at
75% opacity where two antialiased edges meet
* dashed lines, diagonals ╱╲╳, block eighths/quadrants, and the ░▒▓
shades as foreground-alpha washes
Anything outside the range still renders through the font.
SARA AM (ำ U+0E33) is `Lo` and width 1, so the grid gives it its own
column — but it is not atomic to the shaper. The Thai shaper decomposes
it into NIKHAHIT + SARA AA and moves the nikhahit backwards over any
above-base marks onto the base consonant. Shaped in a run of its own it
has no base to reorder onto, so `น้ำ` came out as `น้` plus a dotted
circle, losing the vowel entirely.
Absorb a following SARA AM into the preceding cell's cluster, so base,
tone mark and SARA AM reach `shape_line` in one string. Lao SARA AM
(U+0EB3) takes the same shaper path and is handled with it.
That makes `cells == 2` ambiguous, so `Cluster` now records why: a wide
base is one glyph spanning two columns and pins at `2 × cell_width`,
while an absorbed SARA AM is two base glyphs of one column each and pins
like a `Run`. `apply_force_width_to_layout` classifies glyphs by advance
rather than by count, so the marks ride their base under either pinning.
Two deliberate limits, both pinned by tests:
A SARA AM is not a base for another one. Absorbing there would pin the
second one's glyphs past the cluster's two-cell clip and swallow it, so
`ำำ` stays two `Solo`s and both remain visible. A SARA AM with nothing
before it likewise paints alone — a dotted circle is the shaper's honest
answer for an orphaned mark, and inventing a base would be worse.
An absorbed SARA AM takes its base's style rather than its own, so a
colour change mid-syllable (`grep --color` landing between a consonant
and its vowel) recolours the vowel. Unlike `Run` and `Wide`, the cluster
cannot break on a style change: split off, the vowel renders as a dotted
circle. A recoloured vowel beats a broken one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add One Dark Pro as a ninth built-in, slotted alphabetically among the
dark themes: background #282c34, foreground #abb2bf, the classic One
Dark syntax palette for the normal ANSI slots and One Dark Pro's bright
variants for the bright ones.
Two seeds deliberately diverge from the VS Code theme's terminal set:
* The accent is the editor cursor/focus blue #528bff, not the syntax
blue #61afef — the accent doubles as the switch's checked track, and
#61afef sits at the same luminance as the #abb2bf knob (1.11:1).
* The normal red is the classic #e06c75, not the Pro terminal #e05561 —
conditioned for AA the latter lands within 37 channel-distance of the
orange-yellow #d18f52, under the 40 separability floor danger/warning
must clear.
Also bumps the theme count in README and docs (eight → nine).
The local command editor swallowed every Ctrl chord at the prompt, so
engaging shell integration silently removed keys that worked before it:
Ctrl-P/N history motions, Ctrl-T transpose, and any zle widget the user
had bound. Kills (Ctrl-W/U/K) had no yank to put them back.
- Ctrl-P / Ctrl-N are rewritten into the arrow keys up front, so the two
spellings can't drift apart across history recall, multi-line steps,
the completion picker and reverse search.
- Ctrl-Y yanks the last kill back; the kill chords now stash what they
cut in a one-slot ring. Answered locally — zle's kill ring is a
different buffer and would yank text this editor never cut.
- Alt-. is readline's yank-last-arg, walking back through history on
repeat and swapping the word the previous press inserted.
- Unrecognized Ctrl and Meta chords hand the line to the shell instead
of dying at the prompt, the same escape hatch Ctrl-R already used.
- Keys handled locally now snap the viewport back to the live prompt
(#208): the editor returns early, so it never reached the raw key
path's housekeeping and could edit a line off screen.
`TERM` names terminfo capabilities; it cannot answer "which program is
this". The de-facto standard pair that does — `TERM_PROGRAM` and
`TERM_PROGRAM_VERSION`, introduced by Apple Terminal and set by iTerm2,
WezTerm, Ghostty, VS Code and tmux — went unset, so anything asking was
told nothing.
Plenty asks. Capability probes (`supports-color`, `supports-hyperlinks`,
and the CLI ecosystem built on them) read the program name to decide on
truecolor and OSC 8; editors branch on it for terminal-specific
workarounds; shell prompts adapt their glyphs to it. Absent, they all
fall back to their most conservative behaviour. The `TTY7` marker we do
export is no substitute: it exists so globally-installed agent hooks
stay silent in other terminals, and nothing third-party looks for it.
Both new variables stay overridable through `env` in `config.json`,
unlike `TERM` and `COLORTERM`. Those two state what the pane's decoder
implements, which isn't the user's to contradict; the program name is an
identity, and posing as another terminal is a legitimate way to get a
tool that only recognises a fixed list to light up.
Building the pane's environment is now one function returning the pairs
in application order, so that precedence is testable without a
`CommandBuilder` or a real `config.json`.
Local panes only. ssh forwards environment variables solely by agreement
between client and server (`SendEnv`/`AcceptEnv`, `LANG` and `LC_*` by
default), so a native-SSH pane still sees whatever the remote host sets
for itself — as is already true of `COLORTERM` and `TTY7`.
Closes#212