Normalize source bytes to representable shaped-cluster cursors and
reflow the code buffer whenever gutter or minimap geometry changes.
Measure alternate monospace advances across complete shaped runs and
load hermetic fixture faces before FontSystem construction. Complete
the rendered geometry, popup, caret, snapshot, and fixture acceptance
coverage, and record the review fixes in framing revision 5.
Twenty-one new headless tests covering the GPU-side acceptance items of
docs/gpu-set-font-framing.md (9-14, 16-19):
- 9: size route re-derives metrics; (None, None) reset reproduces
the never-set frame byte-for-byte
- 10: 600/7200 bounds render; minibuffer dropdown windows its rows
above the band at the derived row height; context menu keeps
the clipped route with coherent hit geometry
- 11: wrapped caret survives 16->72->6 px; an optimistic insertion
that wraps a new bottom row follows immediately and its
confirming CursorByte needs no second repair; overscan caret
never snaps the viewport; narrowing resize re-follows via the
coarse + visual-run + fold pipeline; adornment projection
shifts the caret past injected text with left gravity at the
anchor; the CursorByte arm follows into a wrapped run as a
normalized sub-line residual; explicit scrolls clear the
residual (including wheel-up at the clamp edge); the
minibuffer suspends the follow decision
- 4 (GPU half): BufferSnapshot resets the residual while the font
preference/metrics survive -- the replacement buffer wraps
too, so the EOF clamp cannot mask a leaked residual
- 12: unknown + proportional families fall back; the four-style
gate rejects a proportional BOLD sibling the normal-only
query would accept; a valid second monospace family resolves,
changes the ink, and resets cleanly; the parameterized
sanitizer removes exactly the same-family proportional
collision
- 13: the "\0" sentinel defeats the status string-equality gates
- 14: a shrinking visible slice re-declares the scoped viewport
- 16: 0/599/7201/u32::MAX reject the whole message, state untouched
- 17: metrics + drawable dimensions atomic on all seven buffers,
resize symmetric via Buffer::size()
- 18: popup rows never wrap (Wrap::None + unique line_i per run)
- 19: with line numbers and a context menu open over an EMPTY
buffer, the measured probe advance drives gutter reservation
and menu hit width (fixture ratio exactly 1.2 vs the bundled
default); reset restores the exact geometry and frame
Family routing is hermetic: four generated fixture faces under
pmacs-gpu/fonts/test/ (second monospace at 720/1000, a proportional,
and a "Pmacs Test Family" whose NORMAL face is monospaced but whose
BOLD face is proportional), built by the committed generate.py --
provenance and license in LICENSE.txt beside them. fontdb requires a
PostScript name (nameID 6), which the generator sets explicitly.
assemble() now routes construction through sync_buffer_dimensions
too: shape_until_cursor and wrapping must use the painter's clip
from the first frame -- a v16 daemon never sends the FontFacts that
would sync them later (found by the CursorByte-follow test: the
caret parked in the status band at surface-height dims).
Every protection was bitten (temporarily reverted, its test observed
failing, then restored): wire validation, CursorByte visual-run
follow, painted-before gate, projection inversion, Wrap::None,
four-style gate, sanitizer, status sentinel, normalize fold,
scroll_by_lines residual clear, BufferSnapshot residual reset, the
all-seven dimension helper, and the optimistic-completion follow.
The snapshot-reset test was strengthened after its first bite did
NOT fail (a short replacement buffer let cosmic's EOF clamp zero the
residual anyway).
PMACS_REQUIRE_GPU suite 90/90; clippy -D warnings and fmt clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VoiEyuPjoBhvwACf8HAnLB