Skip to content

Text sizing: span width (w), packed sub-cell runs, TextSizing converter, wide-glyph guarantee - #74

Merged
mstrobel merged 3 commits into
developfrom
feat/text-sizing-span-width
Sep 12, 2026
Merged

mstrobel merged 3 commits into
developfrom
feat/text-sizing-span-width

Conversation

@mstrobel

Copy link
Copy Markdown
Owner

Kitty text sizing: the w key is a span width (maintainer-verified 2026-09-12), so it is now emitted, measured and packed; plus sub-cell measuring for read-only text and the wide-glyph guarantee in the renderer. Three commits, each shippable on its own.

1. Span width, packing, string form. TextSizing.Width (0–7) is the width in cells of the whole text of one sequence and is emitted as w. TextSizing.Packed has the writer compute w per sequence as the packed natural width, ⌈natural·n/d⌉ cells, chunking on cluster boundaries so no sequence exceeds the spec's seven cells (TextSizingPacking) — that's how several half-size glyphs share a cell (TextSizing.Superscript / Subscript). SpanSize / SpanColumns give the whole-cell footprint layout, the caret and the buffer see; SizedTextFragment measures with it; IsSupported gates w on the Width capability. The fraction rule is the spec's strict d > n. TextSizing also gains Parse/TryParse/ToString and a [TypeConverter] for XAML and options: named tokens (Normal, Double, 2x7x, Superscript, Subscript [n/d], Packed, Top/Bottom/Center, Left/Right/HCenter, a bare fraction) and the OSC keys (s= w= n= d= v= h= p=), mixed freely; ToString is the canonical metadata form and round-trips.

2. Sub-cell measuring. ScaledGlyphMetrics is fraction-aware: a packed cluster's own advance rounds up (wrap decisions stay conservative), StringWidth is the exact packed span, and a MeasuresSpans metrics has TextFormatter re-measure each emitted piece — H + a packed superscript 12 + O lays out three cells wide, matching the fragment it paints. TextPresenter is untouched; this serves read-only text.

3. Width guarantee. On a w-capable terminal FrameRenderer emits a wide glyph as a w=2 sequence and an East-Asian-Ambiguous glyph as w=1, so the terminal renders exactly the buffer's footprint whatever its width tables say, the cursor advance is known, and both defenses (the pre-paint + CUP-back, the neighbor-first + skip with its distinct-neighbors limitation) are bypassed there.

Gallery: packed superscript/subscript/double samples on terminals that negotiate w — worth an eyeball in kitty, especially the w=1 ambiguous path under an ambiguous-as-wide setting.

Tests: Core 1178, Rendering 1876, UI 3962, Bars 314, Xaml 647, Interactivity 47, Drawing 707 — all green.

🤖 Generated with Claude Code

https://claude.ai/code/session_013qbF5ru6uh5w7fqhP3yoPE

mstrobel and others added 3 commits September 12, 2026 11:07
…ured, packed; TextSizing string form + converter

The 'w' key is the width in cells of the WHOLE text of one sequence — 'all the text in that escape code must be
rendered in s·w cells' (spec re-read; maintainer-verified against kitty, 2026-09-12) — not the per-cluster advance
the 2026-08-02 'unsupported by decision' stance assumed. It is now emitted and measured: TextSizing.Width (0–7)
pins one sequence's width (TextSizing.FixedWidth — the wide-glyph guarantee); TextSizing.Packed has the writer
compute w per sequence as the packed natural width, ⌈natural·n/d⌉ cells, chunking the text on cluster boundaries so
no sequence exceeds the spec's seven cells (TextSizingPacking) — several half-size glyphs per cell for read-only
sub/superscripts (TextSizing.Superscript/Subscript). SpanSize/SpanColumns give the whole-cell footprint layout,
the caret and the buffer see; SizedTextFragment measures with it. IsSupported gates w on the Width capability
(kitty negotiates Width with Scale). The fraction rule is the spec's strict d > n.

TextSizing gains a string form and a TypeConverter (XAML and options): named tokens (Normal, Double, 2x…7x,
Superscript, Subscript [n/d], Packed, Top/Bottom/Center, Left/Right/HCenter, a bare fraction) and the OSC
metadata keys (s= w= n= d= v= h= p=), mixed freely; ToString is the canonical metadata form and round-trips.
Gallery: packed samples on terminals that negotiate w.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013qbF5ru6uh5w7fqhP3yoPE
…red pieces in the formatter

ScaledGlyphMetrics is fraction-aware: a packed cluster's own advance rounds up to whole cells (so a formatter's wrap
decisions stay conservative — a packed run breaks a touch early, never late), while StringWidth is the exact packed
span footprint. A metrics that MeasuresSpans has TextFormatter re-measure each piece it emits, so the width recorded
for a piece is the footprint its SizedTextFragment claims: 'H' + a packed superscript '12' + 'O' lays out three
cells wide. True sub-cell layout (a caret between two glyphs in one cell) stays out of scope — TextPresenter is
untouched; this serves read-only text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013qbF5ru6uh5w7fqhP3yoPE
… emitted as OSC 66 spans pinned to their cell count

On a terminal that honors the 'w' key, FrameRenderer emits a WideLeft glyph as a w=2 sequence and an East-Asian-
Ambiguous single as w=1: the terminal renders exactly the footprint the buffer allotted whatever its own width
tables say, the cursor advance is known, and the two defenses (the two-space pre-paint + CUP-back for untrusted
wide glyphs; the neighbor-first + skip for ambiguous glyphs, with its distinct-neighbors limitation) are bypassed
there. Plain ASCII and empty cells stay plain. Gated on TextSizingCapabilities.Width (kitty negotiates it with Scale).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013qbF5ru6uh5w7fqhP3yoPE
@mstrobel
mstrobel merged commit 3c2a721 into develop Sep 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant