vellum
Low-Level Graphics with Device-Independent Layout and Queryable Scenes
vellum's bugs are now found by using it, not testing it — the downstream grammar is doing the QA.
◆Recent moves
- 11d ago
Linter grows to 20 rules and stops firing on every plot
Running the new rules against vellumplot's output exposed invisible_fill and bleed as pure false-positive generators — five ordinary plots produced one of each with no true positives. Both now carry exemptions: a mark spanning its whole viewport is a substrate, and text overhanging its strip by a few pixels is normal. The linter reached 20 rules and is positioned as something you can gate a build on, which only works if the noise floor is near zero.
View source ↗ - 13d ago
Animated SVGs no longer blink once and vanish or play in reverse
Three rendering fixes with a shared root: per-frame independence was never accounted for. Each frame restarted its defs id counters, so every clip-path reference resolved to frame zero's hidden clip and the panel was clipped away for 47 of 48 frames; the negative animation-delay was offset by i rather than n minus i, running the cycle backwards. Gradient strokes on circles also collapsed to a single stop on the batched fast path.
View source ↗ - 13d ago
Pick table now reports device pixels instead of two coordinate systems
element_geometry() returned viewport-local coordinates for most row types and device pixels for text and roundrect — one table in two coordinate systems — which made vl_nearest() actively wrong in any offset panel, reporting a mark at distance zero with the cursor nowhere near it. Both now report device pixels matching scene_model() and the rendered SVG, as documented.
View source ↗ - 14d ago
Contrast rule stops flagging every plot; gridlines become PDF artifacts
The contrast rule sampled four points around a label and took the worst, so any label grazing a tick or gridline read as roughly 1:1 and dark text on white panels was flagged nearly everywhere. Taking the second-worst side ignores incidental adjacent ink while still catching genuinely low-contrast placement. Separately, nodes with role grid now register as PDF artifacts rather than bogus Figure entries in the structure tree.
View source ↗ - 14d ago
grobwidth and grobheight now measure wrapped text, not the unwrapped line
The same shape as the previous release: a capability that worked when drawn and not when measured. Layout tracks sized by grobheight() reserved one line for labels that drew as several, so titles and captions overran whatever sat above them. Extent now wraps and stacks lines through the draw path rather than a parallel implementation, which is what stops the two from diverging again.
View source ↗ - 14d ago
Keyed roundrect becomes a real batch after downstream integration exposes it
roundrect_grob() draws one box per element but was treated as a single shape on both sides of the seam, silently truncating keys to length one and aborting the element-count check. It is now a keyed batch like text_grob(). The note attached to this release is the significant part: the first bug in the series found by using the engine from vellumplot rather than testing it in isolation.
View source ↗