Releases: tyevco/glissade
Release list
v0.79.0
Completes the 3-cut layout-critique arc. ① group→composed-box: a containBounds node / alignGroups member that is a container Group resolves to the UNION bbox of its DRAWN descendants (declare the natural Group id, critique measures the whole card), retiring Cut 2's leaf-workaround; a truly-empty Group still fails loud with the accurate cause. OUT_OF_BOUNDS / MISALIGNED / UNEVEN_SPACING / settledFrame all read the composed box. ② Layout.computedBoxes()/computedPadding()/computedGaps() public instance methods, one-source with the memoized Yoga compute the DisplayList origins came from (computedBoxes() == what rendered) -> byte-neutral pure reads; describe() gains a Layout methods table (these 3 + retroactively computedSize/intrinsicSize). LAYOUT_OVERFLOW runs automatically over every Layout node, comparing each child's rendered ink to its computed flex slot at the integer-bbox-SETTLED HOLD frame (same discipline as MISALIGNED; a Layout whose children never settle silent-skips) -> fires when ink exceeds slot by >0.5px; a settled-FIT check (distinct from OUT_OF_BOUNDS's whole-span lifetime-containment).
Unanimous 3-seat green on the SHIPPED pre.1 (content composed-box before/after on real Group rows + measured 2px center-delta = union-all confirmed / determinism carry-b4e6060006-byte-identical + _layoutalign 18-leg incl accessor-one-source + LAYOUT_OVERFLOW-settled-HOLD / surface guard-66 + describe-legibility + the Cut-1/Cut-2 container-Group self-consistency flip). RENDER-NEUTRAL: goldens 460/460 byte-identical (7 new layout-overflow showcase + existing untouched), b4e6060006 holds 0.20 to 0.79; SACRED base embed 38.83 held throughout the arc; off-base convenience budgets browser 91->94, scene/diagnostics 19->22, scene/layoutCtors 4->5. Cut 3 of 3 (card oEibfHtXySxw) — arc COMPLETE.
v0.79.0-pre.1
Carries Cut-3's composed-box + Layout accessors + LAYOUT_OVERFLOW (0.79.0-pre.0, whose CI failed at check:size — two off-base subpath budgets tripped the ~0.16 kB CI gz-delta: scene/layoutCtors 0-margin + scene/diagnostics 0.10-margin; nothing published). Two fixes:
-
LAYOUT_OVERFLOW checks the integer-bbox-settled HOLD frame (the last frame where a Layout's flowable children's integer device bboxes are still, bbox[f]==bbox[f+1]) — the SAME settledFrame discipline MISALIGNED/UNEVEN_SPACING use — instead of the last-sampled frame (which judged an animated Layout child mid-transition + missed a develops-and-persists overflow at a non-last hold). settledFrame refactored to take members[] so both callers share it. A Layout whose children never settle -> silent-skip (best-effort auto, no CritiqueError — the declared alignGroups path still fails loud). video-canary's determinism pin. +4 tests (static-fires / transient-settles-to-fit-no-false-fire / persistent-at-non-last-hold-fires / never-settles-silent-skip).
-
Off-base convenience budgets scene/layoutCtors 4->5 + scene/diagnostics 21->22, lifted with CI gz-delta headroom. LESSON: the ~0.16 kB CI gz-delta applies to Node subpath budgets too, not just the browser IIFE — the 0-margin/0.10-margin lines tripped in CI while passing locally. The SACRED base embed held 38.83 throughout.
base 38.83 UNCHANGED, browser 93.06/94, scene/diagnostics 20.98/22, scene/layoutCtors 4.00/5, bundle 14, typecheck 38/38, check:api up-to-date, critique 60/60, goldens 460/460 byte-identical (settled-frame is critique-only, render-neutral).
v0.78.0
critique(scene, timeline, { alignGroups }) checks author-declared sibling-alignment groups. Each AlignGroup { id?, members (>=2), axis? } is read at its SETTLED FRAME — the max grid frame where every member is present AND at rest (integer device bbox[f]==bbox[f+1], so entrance-stagger / exit-whoosh / rotation-settle transients are excluded by construction). MISALIGNED = members share NONE of {top, center, bottom} cross-axis references (mode-aware; all three integer spreads exceed alignTolerance). UNEVEN_SPACING = inter-member main-axis gaps span > gapTolerance. Deterministic median-reference + max-deviation offender (tie-broken node id), canonical settledFrame sort, integer geometry, render-neutral. Fail-loud CritiqueError on an uncheckable declared group: unknown/typo'd member, <2 resolvable, no-own-box member (container Group / fill-less leaf -> names the container cause, Cut-1-consistent, not a misleading timing message), non-integer/negative tolerance, never-co-settle. axis omitted -> geometry-inferred. Auto-inference of groups deferred. New AlignGroup type on the /diagnostics subpath + browser IIFE; alignGroups/alignTolerance/gapTolerance options + AlignGroup in describe(). New layout-critique showcase golden.
Unanimous 3-seat green on the SHIPPED pre.2 (content real-component desk-sweep/send-whoosh/panel + misaligned/top-aligned variants / determinism carry-b4e6060006-byte-identical + _layoutalign_gate / surface guard-65 both-fixes-gating), across 3 pre-iters each closing a rendered-bytes-measured finding (settledFrame HOLD-selection -> mode-aware 3-edge-min -> no-own-box container-cause message). RENDER-NEUTRAL: goldens 451/451 byte-identical (7 new layout-critique + existing untouched), b4e6060006 holds 0.20 to 0.78; sacred base 38.83 held; off-base convenience budgets browser 91->93 + scene/diagnostics 19->21 (the Cut-2 critique code on the /diagnostics subpath + IIFE). Cut 2 of the 3-cut layout-critique arc (card oEibfHtXySxw).
v0.78.0-pre.2
Cut-2 pre.0/pre.1 threw the settle-timing message 'no settled frame — never simultaneously present and at rest' for a member with NO own rendered box (a container Group, or a fill-less/hidden leaf). The fail-loud was correct (a no-box member can't be measured), but it blamed a TIMING cause on a fully-static member — 3 seats independently burned real debugging effort chasing a non-existent settle problem (ai-training container Group, video-canary fill-less Rect, edcc browser IIFE) before isolating it to Group-vs-leaf.
Fix: a member with empty presence (never produced a draw command across the timeline) now throws the Cut-1-consistent CritiqueError naming the real cause — 'member X produced no rendered box across the timeline — it is likely a container Group (which has no own box) or a fill-less / hidden node. Declare its leaf node ids (e.g. the background) instead of the container.' — detected BEFORE the settle check, matching containBounds verbatim. 'No settled frame' is reserved for genuinely-never-still DRAWN members (message tightened to 'its members each render but are never simultaneously present and at rest'). The composed-children-box resolution (declare the natural Group id) is front-loaded for Cut 3.
+1 test (container-Group member -> container message, NOT no-settled-frame; leaf child works). Carries pre.1's mode-aware 3-edge-min MISALIGNED (all 3 seats verified GREEN on shipped pre.1). base 38.83 UNCHANGED, browser 92.19/93, scene/diagnostics 20.45/21, bundle 14, typecheck 38/38, check:api up-to-date, critique 48/48, goldens 451/451 byte-identical (critique-only, render-neutral).
v0.78.0-pre.1
Cut-2 pre.0 shipped a CENTER-only MISALIGNED reference. Both video-canary and edcc MEASURED on the shipped 0.78.0-pre.0 IIFE that center-only false-fires a legitimately TOP- or BOTTOM-aligned row of DIFFERENT-height members (tops align, centers differ) — real UI center- OR edge-aligns different-sized elements (button-chip, icon-label). ai-training surfaced it from the send-line's center-aligned draft+send pair (which the shipped center-only actually passes; the gap is the opposite mode).
Fix: mode-aware reference — members are aligned if they share ANY of the three cross-axis references (row top/center/bottom = minY/cy/maxY, column left/center/right = minX/cx/maxX). MISALIGNED fires only when all three integer spreads exceed alignTolerance (genuinely scattered). Reported reference = min-spread one (spread tie prefers center, then start, then end); offender furthest-from-median on that reference, tie-broken node id (video-canary's two pinned tie-breaks). detail.alignMode names the nearest-shared reference. All-integer device-px, deterministic, a strict superset of center-only. No new option (auto mode-aware; an align hint deferred like auto-inference). +2 tests (top-aligned-heterogeneous passes, fully-scattered fires); the 4 existing MISALIGNED tests unchanged.
base 38.83 UNCHANGED, browser 92.13/93, scene/diagnostics 20.39/21, bundle 14, typecheck 38/38, check:api up to date (no export change), critique 47/47, goldens 451/451 byte-identical (critique-only change, render-neutral).
v0.78.0-pre.0
critique(scene, timeline, { alignGroups }) checks author-declared sibling-alignment groups. Each AlignGroup { id?, members (>=2), axis? } is read at its SETTLED FRAME — the max grid frame where every member is present AND at rest (integer device bbox[f]==bbox[f+1], so entrance-stagger / exit-whoosh / rotation-settle transients are excluded by construction; integer-quantized so asymptotic eases settle cleanly). MISALIGNED = members' cross-axis centers span > alignTolerance (default 2px), names the offender furthest from the median center. UNEVEN_SPACING = inter-member main-axis gaps span > gapTolerance (default 2px), sort by (main-axis-start, node-id) tie-break, names the trailing member of the offending gap. Deterministic median-reference + max-deviation offender (tie-broken node id), canonical detail.frame sort, integer geometry, render-neutral (goldens byte-identical). axis omitted -> geometry-inferred (larger center-spread axis, tie->row).
Fail-loud CritiqueError (extending Cut 1 arc-wide) on an uncheckable declared group: unknown/typo'd member id, <2 resolvable members, no settled frame (members never simultaneously present-and-still), non-integer/negative tolerance. Auto-inference of groups DEFERRED (real layouts mix bg/divider with row members -> naive all-children over-includes; card UjIHWvVp0Zgu) — declare explicitly.
New AlignGroup type on @glissade/scene/diagnostics + re-exported onto the browser IIFE; alignGroups/alignTolerance/gapTolerance options + AlignGroup registered in describe(). New layout-critique showcase golden (a Row: card 3 breaks the baseline = MISALIGNED, gap before card 4 widened = UNEVEN_SPACING), visually inspected. Design converged with the 3 review seats over ~12 messages to the integer-bbox-settledFrame definition before any code.
base 38.83 UNCHANGED (sacred; critique off-base /diagnostics), scene/diagnostics 19->21 + browser IIFE 91->93 (off-base convenience budgets, +1.48/+1.72 for Cut-2 critique code, base held), bundle 14, typecheck 38/38, check:api regenerated (browser.api.md +AlignGroup), critique 45/45, goldens 451/451 (7 new + existing byte-identical). Cut 2 of the 3-cut layout-critique arc (card oEibfHtXySxw).
v0.77.0
critique(scene, timeline, { containBounds: [{ node, within }] }) flags a node whose rendered box drifts OUTSIDE its declared keep-within box for its WHOLE on-stage span (persistent-drift discipline; a transient overshoot during animation does not fire; >0.5px noise guard). First-class ContainBound type paralleling SafeArea, registered in describe().types. Three geometry fix levers (position/scale/fontSize, fixClass geometry); additive DiagnosticCode (no schema bump); assess() + gs critique auto-consume. Closes the exact gap the ask came from — an on-frame node overflowing its box, which OFF_CANVAS (fully-off-frame only) misses.
Node resolution fails loud (new instanceof-catchable CritiqueError), symmetric with the box's validateRegion: an unknown/typo'd id throws at ingest, a container Group (no own box) throws after the sample pass with a declare-leaf-ids hint — never a silent no-op (the confident-wrong-by-omission a declared guard must not become). CritiqueError re-exported on both the Node /diagnostics subpath AND the browser IIFE (window.glissade.CritiqueError instanceof-catchable).
Unanimous 3-seat green on the SHIPPED pre.2 (content real-concept-card / determinism carry-b4e6060006-byte-identical / surface guard-64 instanceof-gating), across 4 pre-iters each closing a real seat-surfaced gap (leaf → fail-loud node → browser-export → api-report). RENDER-NEUTRAL: goldens 442/442 byte-identical, b4e6060006 holds 0.20 to 0.77; sacred base 38.83 held; browser IIFE 90->91 (containBounds/OUT_OF_BOUNDS/CritiqueError on window.glissade). Cut 1 of the 3-cut layout-critique arc (card oEibfHtXySxw).
v0.77.0-pre.2
The pre.2 cut (64dc41e) added CritiqueError + ContainBound to the browser IIFE barrel but did not regenerate etc/browser.api.md — check:api gates the publish job, so the release run failed there (nothing published). Ran api:update; the only drift is the two new re-export lines (import + export for CritiqueError + ContainBound), exactly the intended surface addition. Lesson: run check:api LOCAL before a cut whenever exports change (a browser re-export addition IS an api-surface change).
v0.77.0-pre.1
The leaf path shipped GREEN in pre.0 (3-seat gated). This closes the one flagged trustworthiness gap: containBounds fails loud on a bad within box (validateRegion) but SILENTLY no-oped on an unresolvable node — a typo'd id (matched nothing) or a container Group (no own rendered box). A declared keep-within guard that silently does nothing is confident-wrong-by-omission (an author who added a guard is worse off than one who knew they had none).
New instanceof-catchable CritiqueError: an unknown/typo'd id throws at INGEST (before the sample loop, symmetric with the box fail-loud); a container Group id (indexed but emits no draw command, so no own box) throws AFTER the sample pass where onStage===0 proves it produced no box. Declare its leaf node ids instead (Cut 3 will resolve a Group to its composed-children box). CritiqueError + ContainBound exported from @glissade/scene/diagnostics. +2 tests (unknown-id throws, container-Group throws, leaf works). 3-seat converged: ai-training (hand-typed long ids, a typo bites), video-canary (box-fail-loud/node-silent inconsistency), edcc (the asymmetry is baked into the type — node:string unvalidated vs within:Region validated).
base 38.83 UNCHANGED, browser 90.25/91, scene/diagnostics 18.77/19, bundle 14, typecheck 38/38, critique 31/31, goldens 442/442 byte-identical (off render path).
v0.77.0-pre.0
An author declares a keep-WITHIN box per node via critique(scene, timeline, { containBounds: [{ node, within }] }) using a first-class ContainBound type paralleling SafeArea. OUT_OF_BOUNDS fires when a node's rendered composed box is NOT fully inside its declared box for its WHOLE on-stage span (the persistent-drift discipline OFF_CANVAS/CAPTION_COLLISION use; a transient overshoot during animation does not fire), with a >0.5px overshoot noise guard. Each within is ingested through the shared validateRegion (integer-quantize + fail-loud). Three geometry fix levers (position/scale/fontSize, all fixClass geometry). ContainBound registered in describe().types; containBounds on the critique options schema. Additive DiagnosticCode (no schema bump); assess() + gs critique consume it automatically. RENDER-NEUTRAL pure read: goldens 442/442 byte-identical, base embed 38.83 UNCHANGED (critique rides the tree-shakeable /diagnostics subpath). browser IIFE 90->91 (containBounds/OUT_OF_BOUNDS on window.glissade for the no-build author, +0.38). Drive-by: correct the pre-existing kenBurns scene/motion budget label 0.71.0 -> 0.76.0. 3-seat design-converged. First cut of the 3-cut layout-critique arc (card oEibfHtXySxw).