@glissade/scene 0.14.0-pre.0 → 0.14.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/index.d.ts CHANGED
@@ -653,6 +653,15 @@ declare function tokenHighlight(text: Text, props: Omit<TokenHighlightProps, 'te
653
653
  //#region src/fontUsage.d.ts
654
654
  /** Walk `scene` for Text nodes; one usage per node carrying its full text. */
655
655
  declare function collectTextUsages(scene: Scene): FontUsage[];
656
+ /**
657
+ * Collect font usages from the POST-localize document's STRING tracks (FIX 3,
658
+ * 0.14 canary). For every `'string'` track whose target node is a Text node,
659
+ * emit one usage per distinct localized KEY VALUE under that node's fontFamily —
660
+ * so a localized CJK message bound to a Latin-only font surfaces as an uncovered
661
+ * glyph. `collectTextUsages` only sees the authored BASE `node.text()`, which is
662
+ * resolved BEFORE the localized string tracks bind, so it misses this.
663
+ */
664
+ declare function collectLocalizedTextUsages(scene: Scene, doc: Timeline): FontUsage[];
656
665
  /**
657
666
  * Caller-supplied I/O: fetch the raw bytes for a font face URL (the export
658
667
  * paths read a file / fetch a URL; this keeps core pure). Returning undefined
@@ -664,6 +673,13 @@ interface ValidateSceneFontsOptions {
664
673
  mode?: FontMode;
665
674
  /** OS-installed families to treat as registered (case-insensitive). */
666
675
  osFamilies?: ReadonlySet<string> | undefined;
676
+ /**
677
+ * Additional usages to validate alongside the scene's authored Text (FIX 3):
678
+ * the POST-localize document's localized string-track values, which the
679
+ * scene-walk can't see (they bind AFTER `node.text()` is read). Build them
680
+ * with `collectLocalizedTextUsages(scene, localizedDoc)`.
681
+ */
682
+ extraUsages?: readonly FontUsage[] | undefined;
667
683
  }
668
684
  /**
669
685
  * Run §3.6 font validation for a scene + its timeline document. Builds the
@@ -876,4 +892,4 @@ declare function meshRasterSize(bw: number, bh: number): {
876
892
  h: number;
877
893
  };
878
894
  //#endregion
879
- export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CacheColdResult, type CanvasLike, Circle, ColdAssetError, type CommandDelta, type Ctx2DLike, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, type DisplayDiff, type DisplayList, type DisplayListBuilder, type DlSnapshot, DlSnapshotError, 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 FieldChange, type FilterKind, type FilterSpec, FilterValidationError, FollowPath, type FollowPathProps, type FontByteLoader, type FontSpec, 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, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSampler, 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, TokenHighlight, type TokenHighlightProps, TokenMatchError, type TokenRange, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
895
+ export { ALL_FILTER_KINDS, type AnchorSpec, type BackendCaps, type BindablePropTarget, type BlendMode, type CacheColdResult, type CanvasLike, Circle, ColdAssetError, type CommandDelta, type Ctx2DLike, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, type DisplayDiff, type DisplayList, type DisplayListBuilder, type DlSnapshot, DlSnapshotError, 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 FieldChange, type FilterKind, type FilterSpec, FilterValidationError, FollowPath, type FollowPathProps, type FontByteLoader, type FontSpec, 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, type NodeProps, type NodeTypeName, type Paint, Path, type PathLike, type PathProps, type PathSampler, 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, TokenHighlight, type TokenHighlightProps, TokenMatchError, type TokenRange, type TypeEdit, type TypewriterResult, type ValidateSceneFontsOptions, Video, type VideoFrameSource, type VideoProps, type WordBox, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
package/dist/index.js CHANGED
@@ -931,7 +931,7 @@ var TokenHighlight = class extends Node {
931
931
  this.registerTarget(`${id}/opacity`, r.opacity, "number");
932
932
  this.registerTarget(`${id}/progress`, r.progress, "number");
933
933
  this.registerTarget(`${id}/scale`, r.scale, "number");
934
- this.registerTarget(`${id}/offset`, r.offset, "vec2");
934
+ this.registerTarget(`${id}/offset`, r.offset, ["vec2", "vec2-arc"]);
935
935
  this.registerTarget(`${id}/offset.x`, r.offset.x, "number");
936
936
  this.registerTarget(`${id}/offset.y`, r.offset.y, "number");
937
937
  return r;
@@ -1073,6 +1073,35 @@ function collectTextUsages(scene) {
1073
1073
  visit(scene.root);
1074
1074
  return out;
1075
1075
  }
1076
+ /** The node-id of a track target ('<nodeId>/<prop.path>' → '<nodeId>'). */
1077
+ function nodeIdOf(target) {
1078
+ const slash = target.indexOf("/");
1079
+ return slash >= 0 ? target.slice(0, slash) : target;
1080
+ }
1081
+ /**
1082
+ * Collect font usages from the POST-localize document's STRING tracks (FIX 3,
1083
+ * 0.14 canary). For every `'string'` track whose target node is a Text node,
1084
+ * emit one usage per distinct localized KEY VALUE under that node's fontFamily —
1085
+ * so a localized CJK message bound to a Latin-only font surfaces as an uncovered
1086
+ * glyph. `collectTextUsages` only sees the authored BASE `node.text()`, which is
1087
+ * resolved BEFORE the localized string tracks bind, so it misses this.
1088
+ */
1089
+ function collectLocalizedTextUsages(scene, doc) {
1090
+ const out = [];
1091
+ for (const tr of doc.tracks) {
1092
+ if (tr.type !== "string") continue;
1093
+ const node = scene.nodes.get(nodeIdOf(tr.target));
1094
+ if (!(node instanceof Text)) continue;
1095
+ for (const k of tr.keys) {
1096
+ const value = k.value;
1097
+ if (typeof value === "string" && value) out.push({
1098
+ family: node.fontFamily,
1099
+ text: value
1100
+ });
1101
+ }
1102
+ }
1103
+ return out;
1104
+ }
1076
1105
  /**
1077
1106
  * Run §3.6 font validation for a scene + its timeline document. Builds the
1078
1107
  * registry from `doc.assets`, loads each registered face's cmap via `loadBytes`
@@ -1082,7 +1111,7 @@ function collectTextUsages(scene) {
1082
1111
  async function validateSceneFonts(scene, doc, loadBytes, options = {}) {
1083
1112
  const mode = options.mode ?? "dev";
1084
1113
  const registry = buildFontRegistry(doc.assets);
1085
- const usages = collectTextUsages(scene);
1114
+ const usages = [...collectTextUsages(scene), ...options.extraUsages ?? []];
1086
1115
  const wanted = /* @__PURE__ */ new Set();
1087
1116
  for (const u of usages) if (registry.has(u.family)) for (const f of registry.fallbackChain(u.family)) wanted.add(f);
1088
1117
  const cmaps = /* @__PURE__ */ new Map();
@@ -1821,4 +1850,4 @@ var Raster2D = class {
1821
1850
  }
1822
1851
  };
1823
1852
  //#endregion
1824
- export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, DlSnapshotError, DuplicateNodeIdError, EachError, FilterValidationError, FollowPath, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, TokenHighlight, TokenMatchError, Video, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
1853
+ export { ALL_FILTER_KINDS, Circle, ColdAssetError, Custom, DL_SNAPSHOT_VERSION, DeterminismViolationError, DlSnapshotError, DuplicateNodeIdError, EachError, FilterValidationError, FollowPath, Group, Highlight, IDENTITY, ImageNode as Image, ImageNode, LayoutEngineMissingError, MEASURE_QUANTUM_PX, MESH_DOWNSCALE, MESH_SHEPARD_POWER, MESH_SIGMA, NODE_TAXONOMY, Node, Path, Raster2D, Rect, ReservedNodeIdError, ShaderEffect, SketchValidationError, Text, TextCursor, TokenHighlight, TokenMatchError, Video, applyToPoint, arcLength, auditCacheCold, bindScene, breakLines, collapseReplacer, collectLocalizedTextUsages, collectTextUsages, createDisplayListBuilder, createScene, diffDisplayLists, drawOn, drawOnEach, each, estimatingMeasurer, evaluate, filtersToCanvasFilter, flatten, followPath, fontString, formatDisplayDiff, fromTRS, getLayoutEngine, glow, hachureLines, highlight, invert, matEquals, matchTokenRun, meshRasterSize, motionPath, multiply, parseDisplaySnapshot, pathFromSegs, pathLength, pointAtLength, quantize, rasterizeMesh, requireLayoutEngine, resolveAnchor, resolveSketch, revealSchedule, roughen, roundedRectSegs, segmentGraphemes, segmentWords, serializeDisplayList, setDefaultMeasurer, setLayoutEngine, sketchStrokes, textCursor, tokenHighlight, typewriter, validateFilters, validateHachure, validateSceneFonts, validateSketch, withDeterminismGuards };
@@ -299,9 +299,11 @@ interface BindablePropTarget {
299
299
  unbindSource(): void;
300
300
  /**
301
301
  * The value type(s) this prop accepts — bindTimeline hard-throws a mismatched
302
- * track (§2.2). An array for a polymorphic prop (a Shape `fill` is color|paint).
302
+ * track (§2.2). An array for a polymorphic prop (a Shape `fill` is color|paint,
303
+ * a vec2 prop is vec2|vec2-arc). UNDEFINED for an untagged target (the 2-arg
304
+ * registerTarget form): bindTimeline skips the guard (0.13 back-compat seam).
303
305
  */
304
- readonly expects: ValueTypeId | readonly ValueTypeId[];
306
+ readonly expects: ValueTypeId | readonly ValueTypeId[] | undefined;
305
307
  }
306
308
  /** Node-local hit-shape override (v2 §C.3) — fat targets for thin strokes. */
307
309
  type HitArea = {
@@ -355,11 +357,17 @@ declare abstract class Node {
355
357
  * signal accepts (§2.2). The stamp is what bindTimeline's bind-time guard
356
358
  * reads to reject a mismatched track (a scalar on a vec2, a number on a paint
357
359
  * prop, …) instead of silently sampling to NaN/undefined.
360
+ *
361
+ * `expects` is OPTIONAL: omitting it (the 2-arg form) leaves the target
362
+ * UNtagged — bindTimeline then skips the type guard for it, which is the
363
+ * back-compat seam for external `Custom`/`Node` subclasses (DESIGN.md §329)
364
+ * and prebuilt 0.13 nodes that called the 2-arg form (0.13 had no guard). A
365
+ * built-in node opts INTO the guard by tagging.
358
366
  */
359
367
  protected registerTarget(path: string, sig: {
360
368
  bindSource(fn: () => unknown): void;
361
369
  unbindSource(): void;
362
- }, expects: ValueTypeId | readonly ValueTypeId[]): void;
370
+ }, expects?: ValueTypeId | readonly ValueTypeId[]): void;
363
371
  resolveTarget(path: string): BindablePropTarget | undefined;
364
372
  /** Subclass drawing: emit own commands (and children for containers). */
365
373
  protected abstract draw(out: DisplayListBuilder, ctx: EvalContext): void;
@@ -560,11 +560,11 @@ var Node = class {
560
560
  ]);
561
561
  }, { equals: matEquals });
562
562
  this.worldMatrix = computed(() => this.parent ? multiply(this.parent.worldMatrix(), this.localMatrix()) : this.localMatrix(), { equals: matEquals });
563
- this.registerTarget("position", this.position, "vec2");
563
+ this.registerTarget("position", this.position, ["vec2", "vec2-arc"]);
564
564
  this.registerTarget("position.x", this.position.x, "number");
565
565
  this.registerTarget("position.y", this.position.y, "number");
566
566
  this.registerTarget("rotation", this.rotation, "number");
567
- this.registerTarget("scale", this.scale, "vec2");
567
+ this.registerTarget("scale", this.scale, ["vec2", "vec2-arc"]);
568
568
  this.registerTarget("scale.x", this.scale.x, "number");
569
569
  this.registerTarget("scale.y", this.scale.y, "number");
570
570
  this.registerTarget("opacity", this.opacity, "number");
@@ -575,6 +575,12 @@ var Node = class {
575
575
  * signal accepts (§2.2). The stamp is what bindTimeline's bind-time guard
576
576
  * reads to reject a mismatched track (a scalar on a vec2, a number on a paint
577
577
  * prop, …) instead of silently sampling to NaN/undefined.
578
+ *
579
+ * `expects` is OPTIONAL: omitting it (the 2-arg form) leaves the target
580
+ * UNtagged — bindTimeline then skips the type guard for it, which is the
581
+ * back-compat seam for external `Custom`/`Node` subclasses (DESIGN.md §329)
582
+ * and prebuilt 0.13 nodes that called the 2-arg form (0.13 had no guard). A
583
+ * built-in node opts INTO the guard by tagging.
578
584
  */
579
585
  registerTarget(path, sig, expects) {
580
586
  sig.expects = expects;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/scene",
3
- "version": "0.14.0-pre.0",
3
+ "version": "0.14.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": {
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "yoga-layout": "^3.2.1",
26
- "@glissade/core": "0.14.0-pre.0"
26
+ "@glissade/core": "0.14.0-pre.1"
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",