@glissade/scene 0.22.0 → 0.23.0-pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/describe.js CHANGED
@@ -22,7 +22,7 @@ import { easings, listValueTypes } from "@glissade/core";
22
22
  * never pulled onto the base embed path — a scene that never calls `describe()`
23
23
  * pays zero bytes for it.
24
24
  */
25
- const RAW_VERSION = "0.22.0";
25
+ const RAW_VERSION = "0.23.0-pre.1";
26
26
  const PACKAGE_VERSION = RAW_VERSION.includes("GLISSADE_".concat("VERSION")) ? "0.0.0-dev" : RAW_VERSION;
27
27
  /** Arity of a value type's numeric repr: vec2/vec2-arc → 2, number → 1; others (color/paint/path/string/boolean) carry no scalar arity. */
28
28
  function arityOf(type) {
@@ -297,6 +297,12 @@ const BUILDER_METHODS = [
297
297
  * IIFE. Copy is kept verbatim from `docs/discovery.md` so docs and manifest agree.
298
298
  */
299
299
  const HELPERS = [
300
+ {
301
+ name: "measureWrappedText",
302
+ summary: "Measure how a STRING wraps to a width — size a bubble/card to wrapped text WITHOUT a Text node or re-implementing line breaking (uses the renderer's own wrapper). For a Text NODE, use text.measuredSize(measurer)/lineBoxes(measurer)/wordBoxes(measurer) instead.",
303
+ import: "@glissade/scene",
304
+ usage: "scene.measureWrappedText(text, font, width, lineHeight = 1.25): { width, lines: string[], height, ascent, descent } — node-free; or measureWrappedText(text, font, width, lineHeight, measurer) standalone. width<=0 = no wrap (only explicit \\n). Text node analogue: text.measuredSize(measurer) -> { w, h }."
305
+ },
300
306
  {
301
307
  name: "createPlayer",
302
308
  summary: "Build the transport object (play / pause / seek / rate / loop / marker + cue callbacks) directly — what mount() returns as mounted.player.",
@@ -1,4 +1,4 @@
1
- import { G as collapseReplacer, V as createDisplayListBuilder, r as Group } from "./nodes.js";
1
+ import { H as createDisplayListBuilder, K as collapseReplacer, r as Group } from "./nodes.js";
2
2
  import { a as evaluate } from "./scene.js";
3
3
  //#region src/displayDiff.ts
4
4
  /** A flat, stable JSON value for one command with its resource ids INLINED to content. */
package/dist/identity.js CHANGED
@@ -1,4 +1,4 @@
1
- import { V as createDisplayListBuilder } from "./nodes.js";
1
+ import { H as createDisplayListBuilder } from "./nodes.js";
2
2
  import { r as bindScene } from "./scene.js";
3
3
  import { evaluateAt } from "@glissade/core";
4
4
  //#region src/identity.ts
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { C as Mat2x3, D as matEquals, E as invert, O as multiply, S as IDENTITY, T as fromTRS, _ as StrokeStyle, a as FilterSpec, b as glow, c as MeshInterpolation, d as Paint, f as PathSeg, g as ShaderRef, h as ResourceId, i as DrawCommand, l as MeshPaint, m as Resource, n as DisplayList, o as FilterValidationError, p as Rect$1, r as DisplayListBuilder, s as FontSpec, t as BlendMode, u as MeshPoint, v as createDisplayListBuilder, w as applyToPoint, x as validateFilters, y as filtersToCanvasFilter } from "./displayList.js";
2
2
  import { t as collapseReplacer } from "./collapseReplacer.js";
3
- import { $ as segmentWords, A as resolveSketch, B as NodeConstructionError, C as Polyline, D as arcLength, E as SketchValidationError, F as AnchorSpec, G as TextMeasurer, H as PropInit, I as BindablePropTarget, J as breakLines, K as TextMetricsLite, L as EvalContext, M as sketchStrokes, N as validateHachure, O as flatten, P as validateSketch, Q as segmentGraphemes, R as HitArea, S as HachureSpec, T as SketchStyle, U as resolveAnchor, V as NodeProps, W as MEASURE_QUANTUM_PX, X as isEstimatingMeasurer, Y as estimatingMeasurer, Z as quantize, _ as WordBox, a as ImageNode, b as revealSchedule, c as Path, d as RevealMark, et as setDefaultMeasurer, f as ShapeProps, g as VideoProps, h as Video, i as Group, j as roughen, k as hachureLines, l as PathProps, m as TextProps, n as Custom, o as ImageProps, p as Text, q as __resetEstimateWarnings, r as GraphemeBox, s as LineBox, t as Circle, u as Rect, v as coercePathData, w as ResolvedSketch, x as roundedRectSegs, y as pathFromSegs, z as Node } from "./nodes.js";
3
+ import { $ as quantize, A as resolveSketch, B as NodeConstructionError, C as Polyline, D as arcLength, E as SketchValidationError, F as AnchorSpec, G as TextMeasurer, H as PropInit, I as BindablePropTarget, J as __resetEstimateWarnings, K as TextMetricsLite, L as EvalContext, M as sketchStrokes, N as validateHachure, O as flatten, P as validateSketch, Q as measureWrappedText, R as HitArea, S as HachureSpec, T as SketchStyle, U as resolveAnchor, V as NodeProps, W as MEASURE_QUANTUM_PX, X as estimatingMeasurer, Y as breakLines, Z as isEstimatingMeasurer, _ as WordBox, a as ImageNode, b as revealSchedule, c as Path, d as RevealMark, et as segmentGraphemes, f as ShapeProps, g as VideoProps, h as Video, i as Group, j as roughen, k as hachureLines, l as PathProps, m as TextProps, n as Custom, nt as setDefaultMeasurer, o as ImageProps, p as Text, q as WrappedTextMetrics, r as GraphemeBox, s as LineBox, t as Circle, tt as segmentWords, u as Rect, v as coercePathData, w as ResolvedSketch, x as roundedRectSegs, y as pathFromSegs, z as Node } from "./nodes.js";
4
4
  import { a as SceneModule, c as evaluate, i as SceneInit, n as ReservedNodeIdError, o as bindScene, r as Scene, s as createScene, t as DuplicateNodeIdError } from "./scene.js";
5
5
  import { a as LayoutEngineMissingError, c as requireLayoutEngine, i as LayoutEngine, l as setLayoutEngine, n as LayoutChildSpec, r as LayoutContainerSpec, s as getLayoutEngine, t as LayoutBox } from "./layoutEngine.js";
6
6
  import { BindableSignal, CoverageReport, EaseSpec, FontMode, FontUsage, MeshPaint as MeshPaint$1, Rng, Timeline, Track } from "@glissade/core";
@@ -619,4 +619,4 @@ declare function meshRasterSize(bw: number, bh: number): {
619
619
  h: number;
620
620
  };
621
621
  //#endregion
622
- export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CanvasLike, Circle, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontByteLoader, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, __resetEstimateWarnings, applyToPoint, arcLength, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
622
+ export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CanvasLike, Circle, ColdAssetError, type Ctx2DLike, Custom, DeterminismViolationError, type DisplayList, type DisplayListBuilder, type DrawCommand, type DrawOnEachOptions, type DrawOnOptions, DuplicateNodeIdError, type EachBox, type EachContext, type EachDistribute, EachError, type EachLayout, type EachMotion, type EachOpts, type EachResult, type EditMark, type EvalContext, type FilterKind, type FilterSpec, FilterValidationError, type FontByteLoader, type FontSpec, type GraphemeBox, Group, type GuardMode, type HachureSpec, Highlight, type HighlightProps, type HitArea, IDENTITY, ImageNode as Image, ImageNode, type ImageDataLike, type ImageHandle, type ImageProps, type LayoutBox, type LayoutChildSpec, type LayoutContainerSpec, type LayoutEngine, LayoutEngineMissingError, type LineBox, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, type Mat2x3, type MeshInterpolation, type MeshPaint, type MeshPoint, NODE_TAXONOMY, Node, NodeConstructionError, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSeg, type Place, type Polyline, type PropInit, Raster2D, type Raster2DHost, Rect, type Rect$1 as RectShape, type RenderBackend, ReservedNodeIdError, type ResolvedSketch, type Resource, type ResourceId, type RevealMark, type Scene, type SceneInit, type SceneModule, type ShaderCaps, ShaderEffect, type ShaderEffectProps, type ShaderRef, type ShapeProps, type SketchStyle, SketchValidationError, type StepMark, type StrokeStyle, Text, TextCursor, type TextCursorProps, type TextMeasurer, type TextMetricsLite, type TextProps, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, type WrappedTextMetrics, __resetEstimateWarnings, applyToPoint, arcLength, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as SketchValidationError, B as FilterValidationError, D as segmentWords, E as segmentGraphemes, F as resolveSketch, G as collapseReplacer, H as filtersToCanvasFilter, I as roughen, J as fromTRS, K as IDENTITY, L as sketchStrokes, M as flatten, N as hachureLines, O as setDefaultMeasurer, P as hashStr, R as validateHachure, S as estimatingMeasurer, T as quantize, U as glow, V as createDisplayListBuilder, W as validateFilters, X as matEquals, Y as invert, Z as multiply, a as Path, b as __resetEstimateWarnings, c as Video, d as revealSchedule, f as roundedRectSegs, h as resolveAnchor, i as ImageNode, j as arcLength, l as coercePathData, m as NodeConstructionError, n as Custom, o as Rect, p as Node, q as applyToPoint, r as Group, s as Text, t as Circle, u as pathFromSegs, w as isEstimatingMeasurer, x as breakLines, y as MEASURE_QUANTUM_PX, z as validateSketch } from "./nodes.js";
1
+ import { B as validateSketch, D as segmentGraphemes, E as quantize, F as hashStr, G as validateFilters, H as createDisplayListBuilder, I as resolveSketch, J as applyToPoint, K as collapseReplacer, L as roughen, M as arcLength, N as flatten, O as segmentWords, P as hachureLines, Q as multiply, R as sketchStrokes, S as estimatingMeasurer, T as measureWrappedText, U as filtersToCanvasFilter, V as FilterValidationError, W as glow, X as invert, Y as fromTRS, Z as matEquals, a as Path, b as __resetEstimateWarnings, c as Video, d as revealSchedule, f as roundedRectSegs, h as resolveAnchor, i as ImageNode, j as SketchValidationError, k as setDefaultMeasurer, l as coercePathData, m as NodeConstructionError, n as Custom, o as Rect, p as Node, q as IDENTITY, r as Group, s as Text, t as Circle, u as pathFromSegs, w as isEstimatingMeasurer, x as breakLines, y as MEASURE_QUANTUM_PX, z as validateHachure } from "./nodes.js";
2
2
  import { a as evaluate, i as createScene, n as ReservedNodeIdError, r as bindScene, t as DuplicateNodeIdError } from "./scene.js";
3
3
  import { i as setLayoutEngine, n as getLayoutEngine, r as requireLayoutEngine, t as LayoutEngineMissingError } from "./layoutEngine.js";
4
4
  import { buildFontRegistry, emitDevWarning, key, lerpColor, oklabToRgba, parseCmap, parseColor, random, rgbaToOklab, signal, stagger, track, validateFonts } from "@glissade/core";
@@ -1356,4 +1356,4 @@ var Raster2D = class {
1356
1356
  }
1357
1357
  };
1358
1358
  //#endregion
1359
- export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DeterminismViolationError, DuplicateNodeIdError, EachError, FilterValidationError, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, NodeConstructionError, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, Video, __resetEstimateWarnings, applyToPoint, arcLength, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
1359
+ export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DeterminismViolationError, DuplicateNodeIdError, EachError, FilterValidationError, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, NodeConstructionError, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, Video, __resetEstimateWarnings, applyToPoint, arcLength, bindScene, breakLines, coercePathData, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, fontString, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, isEstimatingMeasurer, matEquals, measureWrappedText, meshRasterSize, multiply, pathFromSegs, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
package/dist/nodes.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { C as Mat2x3, a as FilterSpec, d as Paint, f as PathSeg, g as ShaderRef, r as DisplayListBuilder, s as FontSpec, t as BlendMode } from "./displayList.js";
2
- import { BindableSignal, PathValue, ReadonlySignal, Rng, Track, ValueTypeId, Vec2, Vec2Signal } from "@glissade/core";
2
+ import { BindableSignal, FontAxes, PathValue, ReadonlySignal, Rng, Track, ValueTypeId, Vec2, Vec2Signal } from "@glissade/core";
3
3
 
4
4
  //#region src/text.d.ts
5
5
 
@@ -71,6 +71,31 @@ declare function segmentGraphemes(text: string): string[];
71
71
  * intra-word breaking in v1).
72
72
  */
73
73
  declare function breakLines(text: string, font: FontSpec, maxWidth: number | undefined, measurer: TextMeasurer): string[];
74
+ /** Wrapped-text metrics: the box a string occupies when wrapped to `width`,
75
+ * plus the laid-out lines — so a consumer can size a container (bubble/card)
76
+ * to wrapped text WITHOUT a Text node or re-implementing line breaking. */
77
+ interface WrappedTextMetrics {
78
+ /** Box width: the wrap `width` when wrapping, else the widest line's ink. */
79
+ width: number;
80
+ /** The wrapped lines (the SAME breaks the renderer draws — `breakLines`). */
81
+ lines: string[];
82
+ /** Box height: `quantize(fontSize * lineHeight) * lineCount` (the draw grid). */
83
+ height: number;
84
+ /** Max line ascent above the first baseline (font metric; for baseline align). */
85
+ ascent: number;
86
+ /** Max line descent below the baseline. */
87
+ descent: number;
88
+ }
89
+ /**
90
+ * Measure how `text` wraps to `width` with `font`, returning `{ width, lines,
91
+ * height, ascent, descent }` — the node-free analogue of `Text.measuredSize`/
92
+ * `lineBoxes`, for sizing a container to wrapped text. Reuses {@link breakLines}
93
+ * + the injected `measurer` (so breaks match what the rasterizer draws) — the
94
+ * exact `Text.intrinsicSize` steps. `width <= 0` = no wrap (only explicit '\n').
95
+ * `lineHeight` is a multiple of `font.size` (it lives on the Text node, not
96
+ * `FontSpec`, so it's a parameter here).
97
+ */
98
+ declare function measureWrappedText(text: string, font: FontSpec, width: number, lineHeight: number, measurer: TextMeasurer): WrappedTextMetrics;
74
99
  //#endregion
75
100
  //#region src/node.d.ts
76
101
  /**
@@ -657,14 +682,23 @@ interface TextProps extends NodeProps {
657
682
  * `ctx.fontVariationSettings`) renders the axes; the browser DOM 2D context
658
683
  * has no such property, so axes are best-effort there (a guarded no-op, never
659
684
  * a throw). OMITTED when unset, so default Text emits a byte-identical
660
- * FontSpec. Axes are STATIC only in 0.20 **animatable** axes (a `wght`
661
- * track, `opsz` driven by size, …) are deferred to 1.0 (an opaque CSS string
662
- * isn't lerp-able); a track targeting `<id>/fontVariationSettings` hard-throws
663
- * `UnboundTargetError` today (no property signal resolves to it). For a
664
- * dynamic weight, use the discrete `fontWeight` named instances your font
665
- * ships.
685
+ * FontSpec. This is the STATIC form (an opaque CSS string isn't lerp-able);
686
+ * to ANIMATE an axis (`wght`, `opsz`, …), use {@link fontAxes} instead a
687
+ * structured, per-axis-interpolated map (0.23). When both are given, `fontAxes`
688
+ * (if non-empty) wins.
666
689
  */
667
690
  fontVariationSettings?: string;
691
+ /**
692
+ * Variable-font axes as a STRUCTURED, ANIMATABLE map — `{ wght: 700, opsz: 14 }`
693
+ * (0.23). Unlike the opaque {@link fontVariationSettings} string, this is a
694
+ * lerp-able value type (`fontAxes`): a track on `<id>/fontAxes` interpolates
695
+ * each axis per-frame, formatted to the CSS `font-variation-settings` string at
696
+ * draw (so backends are unchanged). Both keyframes of a track must declare the
697
+ * SAME axis tags (a mismatched set snaps + warns, like path/paint topology).
698
+ * Empty/unset ⇒ omitted, so default Text stays byte-identical. Track target
699
+ * `<id>/fontAxes`, value type `fontAxes`.
700
+ */
701
+ fontAxes?: PropInit<FontAxes>;
668
702
  /** Horizontal alignment about the node position; default 'left'. */
669
703
  align?: 'left' | 'center' | 'right';
670
704
  /** Wrap width in px; unset = no wrapping (explicit \n still breaks). */
@@ -711,6 +745,9 @@ declare class Text extends Node {
711
745
  readonly fontStyle: 'normal' | 'italic';
712
746
  /** Static variable-font axes (CSS `font-variation-settings`); undefined = none. */
713
747
  readonly fontVariationSettings: string | undefined;
748
+ /** Animatable variable-font axes (track target `<id>/fontAxes`); empty = none.
749
+ * When non-empty, overrides {@link fontVariationSettings} in the FontSpec. */
750
+ readonly fontAxes: BindableSignal<FontAxes>;
714
751
  readonly align: 'left' | 'center' | 'right';
715
752
  readonly width: BindableSignal<number>;
716
753
  readonly lineHeight: number;
@@ -842,4 +879,4 @@ interface RevealMark {
842
879
  */
843
880
  declare function revealSchedule(text: Text, reveal: Track<number>, measurer?: TextMeasurer): RevealMark[];
844
881
  //#endregion
845
- export { segmentWords as $, resolveSketch as A, NodeConstructionError as B, Polyline as C, arcLength as D, SketchValidationError as E, AnchorSpec as F, TextMeasurer as G, PropInit as H, BindablePropTarget as I, breakLines as J, TextMetricsLite as K, EvalContext as L, sketchStrokes as M, validateHachure as N, flatten as O, validateSketch as P, segmentGraphemes as Q, HitArea as R, HachureSpec as S, SketchStyle as T, resolveAnchor as U, NodeProps as V, MEASURE_QUANTUM_PX as W, isEstimatingMeasurer as X, estimatingMeasurer as Y, quantize as Z, WordBox as _, ImageNode as a, revealSchedule as b, Path as c, RevealMark as d, setDefaultMeasurer as et, ShapeProps as f, VideoProps as g, Video as h, Group as i, roughen as j, hachureLines as k, PathProps as l, TextProps as m, Custom as n, ImageProps as o, Text as p, __resetEstimateWarnings as q, GraphemeBox as r, LineBox as s, Circle as t, Rect as u, coercePathData as v, ResolvedSketch as w, roundedRectSegs as x, pathFromSegs as y, Node as z };
882
+ export { quantize as $, resolveSketch as A, NodeConstructionError as B, Polyline as C, arcLength as D, SketchValidationError as E, AnchorSpec as F, TextMeasurer as G, PropInit as H, BindablePropTarget as I, __resetEstimateWarnings as J, TextMetricsLite as K, EvalContext as L, sketchStrokes as M, validateHachure as N, flatten as O, validateSketch as P, measureWrappedText as Q, HitArea as R, HachureSpec as S, SketchStyle as T, resolveAnchor as U, NodeProps as V, MEASURE_QUANTUM_PX as W, estimatingMeasurer as X, breakLines as Y, isEstimatingMeasurer as Z, WordBox as _, ImageNode as a, revealSchedule as b, Path as c, RevealMark as d, segmentGraphemes as et, ShapeProps as f, VideoProps as g, Video as h, Group as i, roughen as j, hachureLines as k, PathProps as l, TextProps as m, Custom as n, setDefaultMeasurer as nt, ImageProps as o, Text as p, WrappedTextMetrics as q, GraphemeBox as r, LineBox as s, Circle as t, segmentWords as tt, Rect as u, coercePathData as v, ResolvedSketch as w, roundedRectSegs as x, pathFromSegs as y, Node as z };
package/dist/nodes.js CHANGED
@@ -668,6 +668,35 @@ function breakLines(text, font, maxWidth, measurer) {
668
668
  }
669
669
  return lines;
670
670
  }
671
+ /**
672
+ * Measure how `text` wraps to `width` with `font`, returning `{ width, lines,
673
+ * height, ascent, descent }` — the node-free analogue of `Text.measuredSize`/
674
+ * `lineBoxes`, for sizing a container to wrapped text. Reuses {@link breakLines}
675
+ * + the injected `measurer` (so breaks match what the rasterizer draws) — the
676
+ * exact `Text.intrinsicSize` steps. `width <= 0` = no wrap (only explicit '\n').
677
+ * `lineHeight` is a multiple of `font.size` (it lives on the Text node, not
678
+ * `FontSpec`, so it's a parameter here).
679
+ */
680
+ function measureWrappedText(text, font, width, lineHeight, measurer) {
681
+ const lines = breakLines(text, font, width > 0 ? width : void 0, measurer);
682
+ let widest = 0;
683
+ let ascent = 0;
684
+ let descent = 0;
685
+ for (const line of lines) {
686
+ const m = measurer.measureText(line, font);
687
+ const w = quantize(m.width);
688
+ if (w > widest) widest = w;
689
+ if (m.ascent > ascent) ascent = m.ascent;
690
+ if (m.descent > descent) descent = m.descent;
691
+ }
692
+ return {
693
+ width: width > 0 ? width : widest,
694
+ lines,
695
+ height: quantize(font.size * lineHeight) * lines.length,
696
+ ascent,
697
+ descent
698
+ };
699
+ }
671
700
  //#endregion
672
701
  //#region src/constructionProps.ts
673
702
  /**
@@ -1815,6 +1844,12 @@ var Video = class Video extends Node {
1815
1844
  });
1816
1845
  }
1817
1846
  };
1847
+ /** Format a variable-font axis map → the CSS `font-variation-settings` string,
1848
+ * `'"wght" 700, "opsz" 14'`. Axis tags are SORTED so the same axes always emit
1849
+ * the same string regardless of insertion order — a deterministic FontSpec. */
1850
+ function formatFontAxes(axes) {
1851
+ return Object.keys(axes).sort().map((tag) => `"${tag}" ${axes[tag]}`).join(", ");
1852
+ }
1818
1853
  var Text = class Text extends Node {
1819
1854
  /** Taxonomy name pinned literally (survives IIFE minification — see {@link Group}). */
1820
1855
  get describeType() {
@@ -1828,6 +1863,9 @@ var Text = class Text extends Node {
1828
1863
  fontStyle;
1829
1864
  /** Static variable-font axes (CSS `font-variation-settings`); undefined = none. */
1830
1865
  fontVariationSettings;
1866
+ /** Animatable variable-font axes (track target `<id>/fontAxes`); empty = none.
1867
+ * When non-empty, overrides {@link fontVariationSettings} in the FontSpec. */
1868
+ fontAxes;
1831
1869
  align;
1832
1870
  width;
1833
1871
  lineHeight;
@@ -1849,6 +1887,7 @@ var Text = class Text extends Node {
1849
1887
  this.fontWeight = props.fontWeight ?? 400;
1850
1888
  this.fontStyle = props.fontStyle ?? "normal";
1851
1889
  this.fontVariationSettings = props.fontVariationSettings;
1890
+ this.fontAxes = initProp(signal({}), props.fontAxes);
1852
1891
  this.align = props.align ?? "left";
1853
1892
  this.width = initProp(signal(0), props.width);
1854
1893
  this.lineHeight = props.lineHeight ?? 1.25;
@@ -1861,6 +1900,7 @@ var Text = class Text extends Node {
1861
1900
  this.registerTarget("fontSize", this.fontSize, "number");
1862
1901
  this.registerTarget("reveal", this.reveal, "number");
1863
1902
  this.registerTarget("revealFraction", this.revealFraction, "number");
1903
+ this.registerTarget("fontAxes", this.fontAxes, "fontAxes");
1864
1904
  if (new.target === Text) this.checkProps(props);
1865
1905
  }
1866
1906
  /**
@@ -1871,12 +1911,14 @@ var Text = class Text extends Node {
1871
1911
  * golden corpus depends on it).
1872
1912
  */
1873
1913
  fontSpec() {
1914
+ const axes = this.fontAxes();
1915
+ const fvs = Object.keys(axes).length > 0 ? formatFontAxes(axes) : this.fontVariationSettings;
1874
1916
  return {
1875
1917
  family: this.fontFamily,
1876
1918
  size: this.fontSize(),
1877
1919
  weight: this.fontWeight,
1878
1920
  ...this.fontStyle === "italic" ? { style: "italic" } : {},
1879
- ...this.fontVariationSettings !== void 0 ? { fontVariationSettings: this.fontVariationSettings } : {},
1921
+ ...fvs !== void 0 ? { fontVariationSettings: fvs } : {},
1880
1922
  ...this.letterSpacing !== void 0 ? { letterSpacing: this.letterSpacing } : {}
1881
1923
  };
1882
1924
  }
@@ -2245,4 +2287,4 @@ function revealSchedule(text, reveal, measurer) {
2245
2287
  return marks;
2246
2288
  }
2247
2289
  //#endregion
2248
- export { SketchValidationError as A, FilterValidationError as B, fallbackMeasurer as C, segmentWords as D, segmentGraphemes as E, resolveSketch as F, collapseReplacer as G, filtersToCanvasFilter as H, roughen as I, fromTRS as J, IDENTITY as K, sketchStrokes as L, flatten as M, hachureLines as N, setDefaultMeasurer as O, hashStr as P, validateHachure as R, estimatingMeasurer as S, quantize as T, glow as U, createDisplayListBuilder as V, validateFilters as W, matEquals as X, invert as Y, multiply as Z, NODE_CONSTRUCTION_PROP_NAMES as _, Path as a, __resetEstimateWarnings as b, Video as c, revealSchedule as d, roundedRectSegs as f, BASE_CONSTRUCTION_PROP_NAMES as g, resolveAnchor as h, ImageNode as i, arcLength as j, warnIfEstimating as k, coercePathData as l, NodeConstructionError as m, Custom as n, Rect as o, Node as p, applyToPoint as q, Group as r, Text as s, Circle as t, pathFromSegs as u, isConstructionProp as v, isEstimatingMeasurer as w, breakLines as x, MEASURE_QUANTUM_PX as y, validateSketch as z };
2290
+ export { warnIfEstimating as A, validateSketch as B, fallbackMeasurer as C, segmentGraphemes as D, quantize as E, hashStr as F, validateFilters as G, createDisplayListBuilder as H, resolveSketch as I, applyToPoint as J, collapseReplacer as K, roughen as L, arcLength as M, flatten as N, segmentWords as O, hachureLines as P, multiply as Q, sketchStrokes as R, estimatingMeasurer as S, measureWrappedText as T, filtersToCanvasFilter as U, FilterValidationError as V, glow as W, invert as X, fromTRS as Y, matEquals as Z, NODE_CONSTRUCTION_PROP_NAMES as _, Path as a, __resetEstimateWarnings as b, Video as c, revealSchedule as d, roundedRectSegs as f, BASE_CONSTRUCTION_PROP_NAMES as g, resolveAnchor as h, ImageNode as i, SketchValidationError as j, setDefaultMeasurer as k, coercePathData as l, NodeConstructionError as m, Custom as n, Rect as o, Node as p, IDENTITY as q, Group as r, Text as s, Circle as t, pathFromSegs as u, isConstructionProp as v, isEstimatingMeasurer as w, breakLines as x, MEASURE_QUANTUM_PX as y, validateHachure as z };
package/dist/scene.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { n as DisplayList } from "./displayList.js";
2
- import { G as TextMeasurer, I as BindablePropTarget, i as Group, z as Node } from "./nodes.js";
1
+ import { n as DisplayList, s as FontSpec } from "./displayList.js";
2
+ import { G as TextMeasurer, I as BindablePropTarget, i as Group, q as WrappedTextMetrics, z as Node } from "./nodes.js";
3
3
  import { BoundTimeline, CompiledTimeline, Playhead, Timeline } from "@glissade/core";
4
4
 
5
5
  //#region src/scene.d.ts
@@ -20,6 +20,14 @@ interface Scene {
20
20
  */
21
21
  setTextMeasurer(measurer: TextMeasurer): void;
22
22
  readonly textMeasurer: TextMeasurer;
23
+ /**
24
+ * Measure how `text` wraps to `width` with `font`, using THIS scene's measurer
25
+ * — `{ width, lines, height, ascent, descent }`. Size a container (bubble/card)
26
+ * to wrapped text WITHOUT a Text node (the node-free analogue of
27
+ * `Text.measuredSize`/`lineBoxes`). `lineHeight` is a multiple of `font.size`,
28
+ * default 1.25.
29
+ */
30
+ measureWrappedText(text: string, font: FontSpec, width: number, lineHeight?: number): WrappedTextMetrics;
23
31
  }
24
32
  declare class DuplicateNodeIdError extends Error {
25
33
  constructor(id: string);
package/dist/scene.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as fallbackMeasurer, V as createDisplayListBuilder, r as Group, v as isConstructionProp } from "./nodes.js";
1
+ import { C as fallbackMeasurer, H as createDisplayListBuilder, T as measureWrappedText, r as Group, v as isConstructionProp } from "./nodes.js";
2
2
  import { bindTimeline, compileTimeline, createPlayhead, evaluateAt, signal } from "@glissade/core";
3
3
  //#region src/scene.ts
4
4
  /**
@@ -52,7 +52,8 @@ function createScene(init) {
52
52
  },
53
53
  get textMeasurer() {
54
54
  return measurer.peek() ?? fallbackMeasurer();
55
- }
55
+ },
56
+ measureWrappedText: (text, font, width, lineHeight = 1.25) => measureWrappedText(text, font, width, lineHeight, measurer.peek() ?? fallbackMeasurer())
56
57
  };
57
58
  }
58
59
  const bindings = /* @__PURE__ */ new WeakMap();
package/dist/tokens.js CHANGED
@@ -1,4 +1,4 @@
1
- import { K as IDENTITY, X as matEquals, f as roundedRectSegs, p as Node } from "./nodes.js";
1
+ import { Z as matEquals, f as roundedRectSegs, p as Node, q as IDENTITY } from "./nodes.js";
2
2
  import { signal, vec2Signal } from "@glissade/core";
3
3
  //#region src/tokenHighlight.ts
4
4
  /**
package/dist/type.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as fallbackMeasurer, T as quantize, k as warnIfEstimating, r as Group, s as Text } from "./nodes.js";
1
+ import { A as warnIfEstimating, C as fallbackMeasurer, E as quantize, r as Group, s as Text } from "./nodes.js";
2
2
  //#region src/type.ts
3
3
  /**
4
4
  * `@glissade/scene/type` — `splitText()`: build-time split-text sub-targets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/scene",
3
- "version": "0.22.0",
3
+ "version": "0.23.0-pre.1",
4
4
  "description": "glissade scene graph: nodes, transforms, DisplayList emission. Renderer-agnostic; zero DOM/Node dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -59,7 +59,7 @@
59
59
  ],
60
60
  "dependencies": {
61
61
  "yoga-layout": "^3.2.1",
62
- "@glissade/core": "0.22.0"
62
+ "@glissade/core": "0.23.0-pre.1"
63
63
  },
64
64
  "repository": {
65
65
  "type": "git",