Skip to content

fix(text): draw word-granularity units with the tracking they were laid out with - #208

Merged
LeadcodeDev merged 4 commits into
mainfrom
fix/word-anim-letter-spacing
Aug 13, 2026
Merged

fix(text): draw word-granularity units with the tracking they were laid out with#208
LeadcodeDev merged 4 commits into
mainfrom
fix/word-anim-letter-spacing

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

What

A word-granularity char animation advances its cursor by the width measured with letter-spacing, but apply_text_anim_preset drew every unit at 0.0. With negative tracking the glyphs overrun their slot by |tracking| * (chars - 1) and eat into the following space.

letter-spacing: -2, 54 px, granularity: "word"
"without a maintenance window"  →  "withouta maintenancewindow"

How it hides

The overrun is a fixed number of pixels, so it only shows once it approaches the width of a space:

size space ≈ overrun on an 11-char word visible
143 px 35 px 20 px words look tight
54 px 13 px 20 px words collide

Which is why the same register renders correctly on display type and breaks on the smaller opening line.

Isolated by rendering one string five ways — no animation at tracking 0 and −2, word granularity at 0 and −2, and a plain fade_in at −2. Only the pair (word granularity × negative tracking) reproduces it.

Fix

Thread the tracking into apply_text_anim_preset and draw with it. The character path passes 0.0 explicitly rather than by omission: a single character has no internal tracking, and saying so keeps the two branches from drifting apart.

What I could not do

I have no automated test for this. I wrote three, and all three passed in both the fixed and the broken state — a test that cannot fail on the bug it targets is worse than none, so I removed it rather than ship advertised coverage that does not exist. The overall ink span does not move (the cursor positions are unchanged), and the inter-word gap measurement I tried could not separate word gaps from letter gaps reliably at this size. The fix is verified visually, on a five-case probe, in both directions.

Worth a follow-up by someone who knows the text pixel-test helpers better than I do.

Also in this PR

rules/terminal-product-register.md — the measured visual register this was found while reproducing: palette, the two type scales (with the stem-to-cap method for picking a weight, which is what caught a wrong reading here), the composed terminal pane, beat proportions, and the traps collected along the way.

cargo test --workspace green, cargo fmt --check and cargo clippy --all-targets -- -D warnings clean.

The cell alpha oscillated over 0.10..1.00, so a cell never actually went out.
A lattice whose cells only dip to a tenth still reads as a fixed field of
permanent dots, just dimmer — which is not what an animated texture is for.

0.5 + 0.5 * sin gives the full 0 → 1 → 0. The phase still comes from each
cell's own hash, or the whole field blinks in unison.

The test pins the range rather than the fact that something moves: it samples
400 instants and asserts the floor goes under 0.01 and the ceiling over 0.99.
"It animates" and "it reaches zero" are different claims.
…t a corner

Measured on a reference piece, over 15 frames (0.5 s): the right and top edges
stay pinned to the frame while the left travels 2160 -> 0 and the bottom
1480 -> 2152. Both moving edges advance at once, and the incoming scene sits
still behind the growing window — it is uncovered, not pushed.

None of the thirteen existing types expresses that. wipe_* moves a single
full-width band on one axis, iris is a circle, and slide translates both frames
together. The closest approximation, a dissolve, loses the whole gesture.

`corner` selects which two edges are pinned: top_right (the measured default),
top_left, bottom_right, bottom_left. It is inert for every other type.

Five tests pin the geometry rather than describe it: the anchored edges never
move, the travelling ones open monotonically in the direction the corner names,
the ends are empty and full, each corner anchors its own pair, and out-of-range
progress clamps instead of inverting the rectangle — an inverted rect clips to
nothing and the transition would silently look like a cut.
…id out with

A word-granularity char animation advances its cursor by the width measured
*with* letter-spacing, but apply_text_anim_preset drew every unit at 0.0. With
negative tracking the glyphs then overrun their slot by |tracking| * (chars-1)
and eat into the following space:

    letter-spacing: -2, 54 px, granularity: word
    "without a maintenance window"  ->  "withouta maintenancewindow"

The overrun is a fixed number of pixels, so it only shows once it approaches a
space's width: invisible on 143 px display type, fatal at 54 px. Isolated by
rendering the same string five ways — neither word granularity nor negative
tracking does it alone, only the pair.

The character path passes 0.0 explicitly: a single character has no internal
tracking, and stating it keeps the two branches from drifting apart.

Also adds rules/terminal-product-register.md, the measured visual register this
was found while reproducing.
@LeadcodeDev LeadcodeDev added the bug Something isn't working label Aug 12, 2026
@LeadcodeDev LeadcodeDev self-assigned this Aug 12, 2026
@LeadcodeDev
LeadcodeDev merged commit b168fd2 into main Aug 13, 2026
3 checks passed
@LeadcodeDev
LeadcodeDev deleted the fix/word-anim-letter-spacing branch August 13, 2026 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant