@glissade/scene 0.13.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 +17 -1
- package/dist/index.js +52 -15
- package/dist/layout.js +4 -4
- package/dist/layoutEngine.d.ts +28 -2
- package/dist/layoutEngine.js +46 -29
- package/package.json +2 -2
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
|
@@ -48,8 +48,8 @@ var Highlight = class extends Node {
|
|
|
48
48
|
this.progress = init$2(signal(1), props.progress);
|
|
49
49
|
this.padding = props.padding ?? [4, 2];
|
|
50
50
|
this.cornerRadius = props.cornerRadius ?? 4;
|
|
51
|
-
this.registerTarget("progress", this.progress);
|
|
52
|
-
this.registerTarget("color", this.color);
|
|
51
|
+
this.registerTarget("progress", this.progress, "number");
|
|
52
|
+
this.registerTarget("color", this.color, "color");
|
|
53
53
|
}
|
|
54
54
|
draw(out, ctx) {
|
|
55
55
|
const progress = Math.min(1, Math.max(0, this.progress()));
|
|
@@ -132,7 +132,7 @@ var TextCursor = class extends Node {
|
|
|
132
132
|
this.solidWhileTyping = props.solidWhileTyping ?? true;
|
|
133
133
|
this.caretWidth = props.width ?? 2;
|
|
134
134
|
this.fill = init$1(signal(""), props.fill);
|
|
135
|
-
this.registerTarget("fill", this.fill);
|
|
135
|
+
this.registerTarget("fill", this.fill, "color");
|
|
136
136
|
}
|
|
137
137
|
draw(out, ctx) {
|
|
138
138
|
const head = this.target.revealHead(ctx.measurer);
|
|
@@ -288,6 +288,14 @@ function mix(seed, i) {
|
|
|
288
288
|
h = Math.imul(h ^ h >>> 15, 1935289751);
|
|
289
289
|
return (h ^ h >>> 15) >>> 0;
|
|
290
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Salt folded into the motion-jitter seed so the per-index jitter rng
|
|
293
|
+
* decorrelates from `ctx.rng` (the factory rng). Both axes derive from
|
|
294
|
+
* `mix(baseSeed, i)`; without a distinct salt they would be the SAME stream,
|
|
295
|
+
* so a factory that draws from `ctx.rng` and a `jitter` callback would see
|
|
296
|
+
* correlated "independent" randomness. An arbitrary fixed odd constant.
|
|
297
|
+
*/
|
|
298
|
+
const JITTER_SALT = 1779033703;
|
|
291
299
|
/** Resolve a built-in layout (or call the escape-hatch fn) to a fraction. */
|
|
292
300
|
function placeAt(layout, i, n) {
|
|
293
301
|
if (typeof layout === "function") return layout(i, n);
|
|
@@ -394,7 +402,7 @@ function each(n, factory, opts) {
|
|
|
394
402
|
const start = m.startSec ?? 0;
|
|
395
403
|
const at = staggerFn(m, n);
|
|
396
404
|
for (let i = 0; i < n; i++) {
|
|
397
|
-
const rngI = random(mix(baseSeed, i));
|
|
405
|
+
const rngI = random(mix(mix(baseSeed, i), JITTER_SALT));
|
|
398
406
|
const overrides = m.jitter?.(i, rngI, n);
|
|
399
407
|
const applyOpts = {
|
|
400
408
|
...overrides !== void 0 ? { overrides } : {},
|
|
@@ -815,7 +823,7 @@ var FollowPath = class extends Node {
|
|
|
815
823
|
this.progress = signal(1);
|
|
816
824
|
if (typeof props.progress === "function") this.progress.bindSource(props.progress);
|
|
817
825
|
else if (props.progress !== void 0) this.progress.set(props.progress);
|
|
818
|
-
this.registerTarget("progress", this.progress);
|
|
826
|
+
this.registerTarget("progress", this.progress, "number");
|
|
819
827
|
const sOpts = props.samplesPerSegment !== void 0 ? { samplesPerSegment: props.samplesPerSegment } : {};
|
|
820
828
|
const getPath = props.path instanceof Path ? () => props.path.data() : () => props.path;
|
|
821
829
|
let cachedPath = getPath();
|
|
@@ -919,13 +927,13 @@ var TokenHighlight = class extends Node {
|
|
|
919
927
|
run,
|
|
920
928
|
bound: runText(boxes, run)
|
|
921
929
|
};
|
|
922
|
-
this.registerTarget(`${id}/fill`, r.fill);
|
|
923
|
-
this.registerTarget(`${id}/opacity`, r.opacity);
|
|
924
|
-
this.registerTarget(`${id}/progress`, r.progress);
|
|
925
|
-
this.registerTarget(`${id}/scale`, r.scale);
|
|
926
|
-
this.registerTarget(`${id}/offset`, r.offset);
|
|
927
|
-
this.registerTarget(`${id}/offset.x`, r.offset.x);
|
|
928
|
-
this.registerTarget(`${id}/offset.y`, r.offset.y);
|
|
930
|
+
this.registerTarget(`${id}/fill`, r.fill, "color");
|
|
931
|
+
this.registerTarget(`${id}/opacity`, r.opacity, "number");
|
|
932
|
+
this.registerTarget(`${id}/progress`, r.progress, "number");
|
|
933
|
+
this.registerTarget(`${id}/scale`, r.scale, "number");
|
|
934
|
+
this.registerTarget(`${id}/offset`, r.offset, ["vec2", "vec2-arc"]);
|
|
935
|
+
this.registerTarget(`${id}/offset.x`, r.offset.x, "number");
|
|
936
|
+
this.registerTarget(`${id}/offset.y`, r.offset.y, "number");
|
|
929
937
|
return r;
|
|
930
938
|
});
|
|
931
939
|
}
|
|
@@ -1065,6 +1073,35 @@ function collectTextUsages(scene) {
|
|
|
1065
1073
|
visit(scene.root);
|
|
1066
1074
|
return out;
|
|
1067
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
|
+
}
|
|
1068
1105
|
/**
|
|
1069
1106
|
* Run §3.6 font validation for a scene + its timeline document. Builds the
|
|
1070
1107
|
* registry from `doc.assets`, loads each registered face's cmap via `loadBytes`
|
|
@@ -1074,7 +1111,7 @@ function collectTextUsages(scene) {
|
|
|
1074
1111
|
async function validateSceneFonts(scene, doc, loadBytes, options = {}) {
|
|
1075
1112
|
const mode = options.mode ?? "dev";
|
|
1076
1113
|
const registry = buildFontRegistry(doc.assets);
|
|
1077
|
-
const usages = collectTextUsages(scene);
|
|
1114
|
+
const usages = [...collectTextUsages(scene), ...options.extraUsages ?? []];
|
|
1078
1115
|
const wanted = /* @__PURE__ */ new Set();
|
|
1079
1116
|
for (const u of usages) if (registry.has(u.family)) for (const f of registry.fallbackChain(u.family)) wanted.add(f);
|
|
1080
1117
|
const cmaps = /* @__PURE__ */ new Map();
|
|
@@ -1121,7 +1158,7 @@ var ShaderEffect = class extends Group {
|
|
|
1121
1158
|
for (const [name, value] of Object.entries(props.uniforms ?? {})) {
|
|
1122
1159
|
const sig = signal(value);
|
|
1123
1160
|
map.set(name, sig);
|
|
1124
|
-
this.registerTarget(`u.${name}`, sig);
|
|
1161
|
+
this.registerTarget(`u.${name}`, sig, "number");
|
|
1125
1162
|
}
|
|
1126
1163
|
this.uniformSignals = map;
|
|
1127
1164
|
}
|
|
@@ -1813,4 +1850,4 @@ var Raster2D = class {
|
|
|
1813
1850
|
}
|
|
1814
1851
|
};
|
|
1815
1852
|
//#endregion
|
|
1816
|
-
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 };
|
package/dist/layout.js
CHANGED
|
@@ -53,10 +53,10 @@ var Layout = class extends Group {
|
|
|
53
53
|
this.direction = props.direction ?? "row";
|
|
54
54
|
this.justify = props.justify ?? "start";
|
|
55
55
|
this.align = props.align ?? "center";
|
|
56
|
-
this.registerTarget("width", this.width);
|
|
57
|
-
this.registerTarget("height", this.height);
|
|
58
|
-
this.registerTarget("gap", this.gap);
|
|
59
|
-
this.registerTarget("padding", this.padding);
|
|
56
|
+
this.registerTarget("width", this.width, "number");
|
|
57
|
+
this.registerTarget("height", this.height, "number");
|
|
58
|
+
this.registerTarget("gap", this.gap, "number");
|
|
59
|
+
this.registerTarget("padding", this.padding, "number");
|
|
60
60
|
}
|
|
61
61
|
intrinsicSize(measurer) {
|
|
62
62
|
if (!this.autoWidth && !this.autoHeight) return {
|
package/dist/layoutEngine.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BindableSignal, MeshInterpolation, MeshPaint as MeshPaint$1, MeshPoint, Paint, PathValue, ReadonlySignal, Rng, Track, Vec2, Vec2Signal } from "@glissade/core";
|
|
1
|
+
import { BindableSignal, MeshInterpolation, MeshPaint as MeshPaint$1, MeshPoint, Paint, PathValue, ReadonlySignal, Rng, Track, ValueTypeId, Vec2, Vec2Signal } from "@glissade/core";
|
|
2
2
|
|
|
3
3
|
//#region src/matrix.d.ts
|
|
4
4
|
|
|
@@ -297,6 +297,13 @@ interface NodeProps {
|
|
|
297
297
|
interface BindablePropTarget {
|
|
298
298
|
bindSource(fn: () => unknown): void;
|
|
299
299
|
unbindSource(): void;
|
|
300
|
+
/**
|
|
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,
|
|
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).
|
|
305
|
+
*/
|
|
306
|
+
readonly expects: ValueTypeId | readonly ValueTypeId[] | undefined;
|
|
300
307
|
}
|
|
301
308
|
/** Node-local hit-shape override (v2 §C.3) — fat targets for thin strokes. */
|
|
302
309
|
type HitArea = {
|
|
@@ -345,7 +352,22 @@ declare abstract class Node {
|
|
|
345
352
|
/** Track-target paths → bindable signals; subclasses register their own props. */
|
|
346
353
|
protected readonly targets: Map<string, BindablePropTarget>;
|
|
347
354
|
constructor(props?: NodeProps);
|
|
348
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Register a track-target path → bindable signal, stamping the value type the
|
|
357
|
+
* signal accepts (§2.2). The stamp is what bindTimeline's bind-time guard
|
|
358
|
+
* reads to reject a mismatched track (a scalar on a vec2, a number on a paint
|
|
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.
|
|
366
|
+
*/
|
|
367
|
+
protected registerTarget(path: string, sig: {
|
|
368
|
+
bindSource(fn: () => unknown): void;
|
|
369
|
+
unbindSource(): void;
|
|
370
|
+
}, expects?: ValueTypeId | readonly ValueTypeId[]): void;
|
|
349
371
|
resolveTarget(path: string): BindablePropTarget | undefined;
|
|
350
372
|
/** Subclass drawing: emit own commands (and children for containers). */
|
|
351
373
|
protected abstract draw(out: DisplayListBuilder, ctx: EvalContext): void;
|
|
@@ -609,6 +631,8 @@ interface ImageProps extends NodeProps {
|
|
|
609
631
|
height?: PropInit<number>;
|
|
610
632
|
}
|
|
611
633
|
declare class ImageNode extends Node {
|
|
634
|
+
/** Marks this node as referencing a kind 'image' timeline asset (§2.3). */
|
|
635
|
+
static readonly assetKind: "image";
|
|
612
636
|
readonly assetId: string;
|
|
613
637
|
readonly width: BindableSignal<number>;
|
|
614
638
|
readonly height: BindableSignal<number>;
|
|
@@ -644,6 +668,8 @@ interface VideoProps extends NodeProps {
|
|
|
644
668
|
* and references the exact source-grid frame; backends resolve it.
|
|
645
669
|
*/
|
|
646
670
|
declare class Video extends Node {
|
|
671
|
+
/** Marks this node as referencing a kind 'video' timeline asset (§3.8). */
|
|
672
|
+
static readonly assetKind: "video";
|
|
647
673
|
readonly assetId: string;
|
|
648
674
|
readonly at: number;
|
|
649
675
|
readonly trimStart: number;
|
package/dist/layoutEngine.js
CHANGED
|
@@ -560,17 +560,30 @@ 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);
|
|
564
|
-
this.registerTarget("position.x", this.position.x);
|
|
565
|
-
this.registerTarget("position.y", this.position.y);
|
|
566
|
-
this.registerTarget("rotation", this.rotation);
|
|
567
|
-
this.registerTarget("scale", this.scale);
|
|
568
|
-
this.registerTarget("scale.x", this.scale.x);
|
|
569
|
-
this.registerTarget("scale.y", this.scale.y);
|
|
570
|
-
this.registerTarget("opacity", this.opacity);
|
|
571
|
-
this.registerTarget("zIndex", this.zIndex);
|
|
572
|
-
}
|
|
573
|
-
|
|
563
|
+
this.registerTarget("position", this.position, ["vec2", "vec2-arc"]);
|
|
564
|
+
this.registerTarget("position.x", this.position.x, "number");
|
|
565
|
+
this.registerTarget("position.y", this.position.y, "number");
|
|
566
|
+
this.registerTarget("rotation", this.rotation, "number");
|
|
567
|
+
this.registerTarget("scale", this.scale, ["vec2", "vec2-arc"]);
|
|
568
|
+
this.registerTarget("scale.x", this.scale.x, "number");
|
|
569
|
+
this.registerTarget("scale.y", this.scale.y, "number");
|
|
570
|
+
this.registerTarget("opacity", this.opacity, "number");
|
|
571
|
+
this.registerTarget("zIndex", this.zIndex, "number");
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Register a track-target path → bindable signal, stamping the value type the
|
|
575
|
+
* signal accepts (§2.2). The stamp is what bindTimeline's bind-time guard
|
|
576
|
+
* reads to reject a mismatched track (a scalar on a vec2, a number on a paint
|
|
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.
|
|
584
|
+
*/
|
|
585
|
+
registerTarget(path, sig, expects) {
|
|
586
|
+
sig.expects = expects;
|
|
574
587
|
this.targets.set(path, sig);
|
|
575
588
|
if (this.id !== void 0) sig[TARGET_PATH] = `${this.id}/${path}`;
|
|
576
589
|
}
|
|
@@ -1141,10 +1154,10 @@ var Shape = class extends Node {
|
|
|
1141
1154
|
this.stroke = initProp(signal(""), props.stroke);
|
|
1142
1155
|
this.strokeWidth = initProp(signal(0), props.strokeWidth);
|
|
1143
1156
|
this.reveal = initProp(signal(1), props.reveal);
|
|
1144
|
-
this.registerTarget("fill", this.fill);
|
|
1145
|
-
this.registerTarget("stroke", this.stroke);
|
|
1146
|
-
this.registerTarget("strokeWidth", this.strokeWidth);
|
|
1147
|
-
this.registerTarget("reveal", this.reveal);
|
|
1157
|
+
this.registerTarget("fill", this.fill, ["color", "paint"]);
|
|
1158
|
+
this.registerTarget("stroke", this.stroke, "color");
|
|
1159
|
+
this.registerTarget("strokeWidth", this.strokeWidth, "number");
|
|
1160
|
+
this.registerTarget("reveal", this.reveal, "number");
|
|
1148
1161
|
if (props.sketch) validateSketch(props.sketch);
|
|
1149
1162
|
if (props.sketchFill) validateHachure(props.sketchFill);
|
|
1150
1163
|
if (props.sketchFill && !props.sketch) emitDevWarning(`${this.id !== void 0 ? `'${this.id}': ` : ""}sketchFill is ignored without sketch — hachure fill is drawn only by the sketch renderer. Set a sketch style (e.g. { kind: 'pencil' }) to see it.`);
|
|
@@ -1396,9 +1409,9 @@ var Rect = class extends Shape {
|
|
|
1396
1409
|
this.width = initProp(signal(0), props.width);
|
|
1397
1410
|
this.height = initProp(signal(0), props.height);
|
|
1398
1411
|
this.cornerRadius = initProp(signal(0), props.cornerRadius);
|
|
1399
|
-
this.registerTarget("width", this.width);
|
|
1400
|
-
this.registerTarget("height", this.height);
|
|
1401
|
-
this.registerTarget("cornerRadius", this.cornerRadius);
|
|
1412
|
+
this.registerTarget("width", this.width, "number");
|
|
1413
|
+
this.registerTarget("height", this.height, "number");
|
|
1414
|
+
this.registerTarget("cornerRadius", this.cornerRadius, "number");
|
|
1402
1415
|
}
|
|
1403
1416
|
intrinsicSize() {
|
|
1404
1417
|
return {
|
|
@@ -1418,7 +1431,7 @@ var Circle = class extends Shape {
|
|
|
1418
1431
|
constructor(props = {}) {
|
|
1419
1432
|
super(props);
|
|
1420
1433
|
this.radius = initProp(signal(0), props.radius);
|
|
1421
|
-
this.registerTarget("radius", this.radius);
|
|
1434
|
+
this.registerTarget("radius", this.radius, "number");
|
|
1422
1435
|
}
|
|
1423
1436
|
intrinsicSize() {
|
|
1424
1437
|
const d = this.radius() * 2;
|
|
@@ -1451,7 +1464,7 @@ var Path = class extends Shape {
|
|
|
1451
1464
|
constructor(props = {}) {
|
|
1452
1465
|
super(props);
|
|
1453
1466
|
this.data = initProp(signal([]), props.data);
|
|
1454
|
-
this.registerTarget("d", this.data);
|
|
1467
|
+
this.registerTarget("d", this.data, "path");
|
|
1455
1468
|
}
|
|
1456
1469
|
/** Control-point bounding box (conservative: contains the true curve). */
|
|
1457
1470
|
bounds() {
|
|
@@ -1538,6 +1551,8 @@ var Path = class extends Shape {
|
|
|
1538
1551
|
}
|
|
1539
1552
|
};
|
|
1540
1553
|
var ImageNode = class extends Node {
|
|
1554
|
+
/** Marks this node as referencing a kind 'image' timeline asset (§2.3). */
|
|
1555
|
+
static assetKind = "image";
|
|
1541
1556
|
assetId;
|
|
1542
1557
|
width;
|
|
1543
1558
|
height;
|
|
@@ -1546,8 +1561,8 @@ var ImageNode = class extends Node {
|
|
|
1546
1561
|
this.assetId = props.assetId;
|
|
1547
1562
|
this.width = initProp(signal(0), props.width);
|
|
1548
1563
|
this.height = initProp(signal(0), props.height);
|
|
1549
|
-
this.registerTarget("width", this.width);
|
|
1550
|
-
this.registerTarget("height", this.height);
|
|
1564
|
+
this.registerTarget("width", this.width, "number");
|
|
1565
|
+
this.registerTarget("height", this.height, "number");
|
|
1551
1566
|
}
|
|
1552
1567
|
intrinsicSize() {
|
|
1553
1568
|
return {
|
|
@@ -1581,6 +1596,8 @@ var ImageNode = class extends Node {
|
|
|
1581
1596
|
* and references the exact source-grid frame; backends resolve it.
|
|
1582
1597
|
*/
|
|
1583
1598
|
var Video = class extends Node {
|
|
1599
|
+
/** Marks this node as referencing a kind 'video' timeline asset (§3.8). */
|
|
1600
|
+
static assetKind = "video";
|
|
1584
1601
|
assetId;
|
|
1585
1602
|
at;
|
|
1586
1603
|
trimStart;
|
|
@@ -1599,8 +1616,8 @@ var Video = class extends Node {
|
|
|
1599
1616
|
this.sourceFps = props.sourceFps;
|
|
1600
1617
|
this.width = initProp(signal(0), props.width);
|
|
1601
1618
|
this.height = initProp(signal(0), props.height);
|
|
1602
|
-
this.registerTarget("width", this.width);
|
|
1603
|
-
this.registerTarget("height", this.height);
|
|
1619
|
+
this.registerTarget("width", this.width, "number");
|
|
1620
|
+
this.registerTarget("height", this.height, "number");
|
|
1604
1621
|
}
|
|
1605
1622
|
/** Frame-indexed media time for timeline time t; null when outside the clip. */
|
|
1606
1623
|
mediaTime(t) {
|
|
@@ -1657,11 +1674,11 @@ var Text = class extends Node {
|
|
|
1657
1674
|
this.width = initProp(signal(0), props.width);
|
|
1658
1675
|
this.lineHeight = props.lineHeight ?? 1.25;
|
|
1659
1676
|
this.reveal = initProp(signal(Number.POSITIVE_INFINITY), props.reveal);
|
|
1660
|
-
this.registerTarget("width", this.width);
|
|
1661
|
-
this.registerTarget("text", this.text);
|
|
1662
|
-
this.registerTarget("fill", this.fill);
|
|
1663
|
-
this.registerTarget("fontSize", this.fontSize);
|
|
1664
|
-
this.registerTarget("reveal", this.reveal);
|
|
1677
|
+
this.registerTarget("width", this.width, "number");
|
|
1678
|
+
this.registerTarget("text", this.text, "string");
|
|
1679
|
+
this.registerTarget("fill", this.fill, "color");
|
|
1680
|
+
this.registerTarget("fontSize", this.fontSize, "number");
|
|
1681
|
+
this.registerTarget("reveal", this.reveal, "number");
|
|
1665
1682
|
}
|
|
1666
1683
|
intrinsicSize(measurer) {
|
|
1667
1684
|
const text = this.text();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glissade/scene",
|
|
3
|
-
"version": "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.
|
|
26
|
+
"@glissade/core": "0.14.0-pre.1"
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|