@glissade/scene 0.13.0 → 0.14.0-pre.0

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.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");
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
  }
@@ -1121,7 +1129,7 @@ var ShaderEffect = class extends Group {
1121
1129
  for (const [name, value] of Object.entries(props.uniforms ?? {})) {
1122
1130
  const sig = signal(value);
1123
1131
  map.set(name, sig);
1124
- this.registerTarget(`u.${name}`, sig);
1132
+ this.registerTarget(`u.${name}`, sig, "number");
1125
1133
  }
1126
1134
  this.uniformSignals = map;
1127
1135
  }
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 {
@@ -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,11 @@ 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
+ */
304
+ readonly expects: ValueTypeId | readonly ValueTypeId[];
300
305
  }
301
306
  /** Node-local hit-shape override (v2 §C.3) — fat targets for thin strokes. */
302
307
  type HitArea = {
@@ -345,7 +350,16 @@ declare abstract class Node {
345
350
  /** Track-target paths → bindable signals; subclasses register their own props. */
346
351
  protected readonly targets: Map<string, BindablePropTarget>;
347
352
  constructor(props?: NodeProps);
348
- protected registerTarget(path: string, sig: BindablePropTarget): void;
353
+ /**
354
+ * Register a track-target path → bindable signal, stamping the value type the
355
+ * signal accepts (§2.2). The stamp is what bindTimeline's bind-time guard
356
+ * reads to reject a mismatched track (a scalar on a vec2, a number on a paint
357
+ * prop, …) instead of silently sampling to NaN/undefined.
358
+ */
359
+ protected registerTarget(path: string, sig: {
360
+ bindSource(fn: () => unknown): void;
361
+ unbindSource(): void;
362
+ }, expects: ValueTypeId | readonly ValueTypeId[]): void;
349
363
  resolveTarget(path: string): BindablePropTarget | undefined;
350
364
  /** Subclass drawing: emit own commands (and children for containers). */
351
365
  protected abstract draw(out: DisplayListBuilder, ctx: EvalContext): void;
@@ -609,6 +623,8 @@ interface ImageProps extends NodeProps {
609
623
  height?: PropInit<number>;
610
624
  }
611
625
  declare class ImageNode extends Node {
626
+ /** Marks this node as referencing a kind 'image' timeline asset (§2.3). */
627
+ static readonly assetKind: "image";
612
628
  readonly assetId: string;
613
629
  readonly width: BindableSignal<number>;
614
630
  readonly height: BindableSignal<number>;
@@ -644,6 +660,8 @@ interface VideoProps extends NodeProps {
644
660
  * and references the exact source-grid frame; backends resolve it.
645
661
  */
646
662
  declare class Video extends Node {
663
+ /** Marks this node as referencing a kind 'video' timeline asset (§3.8). */
664
+ static readonly assetKind: "video";
647
665
  readonly assetId: string;
648
666
  readonly at: number;
649
667
  readonly trimStart: number;
@@ -560,17 +560,24 @@ 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
- registerTarget(path, sig) {
563
+ this.registerTarget("position", this.position, "vec2");
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");
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
+ registerTarget(path, sig, expects) {
580
+ sig.expects = expects;
574
581
  this.targets.set(path, sig);
575
582
  if (this.id !== void 0) sig[TARGET_PATH] = `${this.id}/${path}`;
576
583
  }
@@ -1141,10 +1148,10 @@ var Shape = class extends Node {
1141
1148
  this.stroke = initProp(signal(""), props.stroke);
1142
1149
  this.strokeWidth = initProp(signal(0), props.strokeWidth);
1143
1150
  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);
1151
+ this.registerTarget("fill", this.fill, ["color", "paint"]);
1152
+ this.registerTarget("stroke", this.stroke, "color");
1153
+ this.registerTarget("strokeWidth", this.strokeWidth, "number");
1154
+ this.registerTarget("reveal", this.reveal, "number");
1148
1155
  if (props.sketch) validateSketch(props.sketch);
1149
1156
  if (props.sketchFill) validateHachure(props.sketchFill);
1150
1157
  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 +1403,9 @@ var Rect = class extends Shape {
1396
1403
  this.width = initProp(signal(0), props.width);
1397
1404
  this.height = initProp(signal(0), props.height);
1398
1405
  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);
1406
+ this.registerTarget("width", this.width, "number");
1407
+ this.registerTarget("height", this.height, "number");
1408
+ this.registerTarget("cornerRadius", this.cornerRadius, "number");
1402
1409
  }
1403
1410
  intrinsicSize() {
1404
1411
  return {
@@ -1418,7 +1425,7 @@ var Circle = class extends Shape {
1418
1425
  constructor(props = {}) {
1419
1426
  super(props);
1420
1427
  this.radius = initProp(signal(0), props.radius);
1421
- this.registerTarget("radius", this.radius);
1428
+ this.registerTarget("radius", this.radius, "number");
1422
1429
  }
1423
1430
  intrinsicSize() {
1424
1431
  const d = this.radius() * 2;
@@ -1451,7 +1458,7 @@ var Path = class extends Shape {
1451
1458
  constructor(props = {}) {
1452
1459
  super(props);
1453
1460
  this.data = initProp(signal([]), props.data);
1454
- this.registerTarget("d", this.data);
1461
+ this.registerTarget("d", this.data, "path");
1455
1462
  }
1456
1463
  /** Control-point bounding box (conservative: contains the true curve). */
1457
1464
  bounds() {
@@ -1538,6 +1545,8 @@ var Path = class extends Shape {
1538
1545
  }
1539
1546
  };
1540
1547
  var ImageNode = class extends Node {
1548
+ /** Marks this node as referencing a kind 'image' timeline asset (§2.3). */
1549
+ static assetKind = "image";
1541
1550
  assetId;
1542
1551
  width;
1543
1552
  height;
@@ -1546,8 +1555,8 @@ var ImageNode = class extends Node {
1546
1555
  this.assetId = props.assetId;
1547
1556
  this.width = initProp(signal(0), props.width);
1548
1557
  this.height = initProp(signal(0), props.height);
1549
- this.registerTarget("width", this.width);
1550
- this.registerTarget("height", this.height);
1558
+ this.registerTarget("width", this.width, "number");
1559
+ this.registerTarget("height", this.height, "number");
1551
1560
  }
1552
1561
  intrinsicSize() {
1553
1562
  return {
@@ -1581,6 +1590,8 @@ var ImageNode = class extends Node {
1581
1590
  * and references the exact source-grid frame; backends resolve it.
1582
1591
  */
1583
1592
  var Video = class extends Node {
1593
+ /** Marks this node as referencing a kind 'video' timeline asset (§3.8). */
1594
+ static assetKind = "video";
1584
1595
  assetId;
1585
1596
  at;
1586
1597
  trimStart;
@@ -1599,8 +1610,8 @@ var Video = class extends Node {
1599
1610
  this.sourceFps = props.sourceFps;
1600
1611
  this.width = initProp(signal(0), props.width);
1601
1612
  this.height = initProp(signal(0), props.height);
1602
- this.registerTarget("width", this.width);
1603
- this.registerTarget("height", this.height);
1613
+ this.registerTarget("width", this.width, "number");
1614
+ this.registerTarget("height", this.height, "number");
1604
1615
  }
1605
1616
  /** Frame-indexed media time for timeline time t; null when outside the clip. */
1606
1617
  mediaTime(t) {
@@ -1657,11 +1668,11 @@ var Text = class extends Node {
1657
1668
  this.width = initProp(signal(0), props.width);
1658
1669
  this.lineHeight = props.lineHeight ?? 1.25;
1659
1670
  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);
1671
+ this.registerTarget("width", this.width, "number");
1672
+ this.registerTarget("text", this.text, "string");
1673
+ this.registerTarget("fill", this.fill, "color");
1674
+ this.registerTarget("fontSize", this.fontSize, "number");
1675
+ this.registerTarget("reveal", this.reveal, "number");
1665
1676
  }
1666
1677
  intrinsicSize(measurer) {
1667
1678
  const text = this.text();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/scene",
3
- "version": "0.13.0",
3
+ "version": "0.14.0-pre.0",
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.13.0"
26
+ "@glissade/core": "0.14.0-pre.0"
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",