bireactive 0.3.0 → 0.3.2

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.
Files changed (117) hide show
  1. package/README.md +14 -7
  2. package/dist/automerge/doc-cell.d.ts +20 -0
  3. package/dist/automerge/doc-cell.js +80 -0
  4. package/dist/automerge/index.d.ts +3 -0
  5. package/dist/automerge/index.js +12 -0
  6. package/dist/automerge/reconcile.d.ts +5 -0
  7. package/dist/automerge/reconcile.js +63 -0
  8. package/dist/core/_counts.d.ts +48 -0
  9. package/dist/core/_counts.js +51 -0
  10. package/dist/core/cell.d.ts +148 -112
  11. package/dist/core/cell.js +945 -768
  12. package/dist/core/debug.d.ts +25 -0
  13. package/dist/core/debug.js +121 -0
  14. package/dist/core/derived-geometry.js +4 -7
  15. package/dist/core/index.d.ts +9 -2
  16. package/dist/core/index.js +8 -1
  17. package/dist/core/lenses/aggregates.d.ts +42 -52
  18. package/dist/core/lenses/aggregates.js +225 -116
  19. package/dist/core/lenses/geometry.d.ts +22 -4
  20. package/dist/core/lenses/geometry.js +59 -27
  21. package/dist/core/lenses/index.d.ts +6 -6
  22. package/dist/core/lenses/index.js +6 -6
  23. package/dist/core/lenses/memory.js +4 -17
  24. package/dist/core/lenses/numerical.d.ts +100 -0
  25. package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
  26. package/dist/core/lenses/point-cloud.d.ts +67 -0
  27. package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +226 -84
  28. package/dist/core/lenses/snap.d.ts +18 -0
  29. package/dist/core/lenses/snap.js +138 -0
  30. package/dist/core/lenses/text.d.ts +40 -0
  31. package/dist/core/lenses/text.js +202 -0
  32. package/dist/core/lifecycle.js +3 -6
  33. package/dist/core/linalg.js +5 -11
  34. package/dist/core/optic.d.ts +13 -0
  35. package/dist/core/optic.js +39 -0
  36. package/dist/core/optics.d.ts +10 -0
  37. package/dist/core/optics.js +26 -0
  38. package/dist/core/store.d.ts +9 -0
  39. package/dist/core/store.js +77 -0
  40. package/dist/core/traits.d.ts +4 -7
  41. package/dist/core/traits.js +8 -12
  42. package/dist/core/values/anchor.js +0 -4
  43. package/dist/core/values/arr.d.ts +110 -0
  44. package/dist/core/values/arr.js +336 -0
  45. package/dist/core/values/audio.d.ts +8 -9
  46. package/dist/core/values/audio.js +11 -28
  47. package/dist/core/values/bool.d.ts +11 -11
  48. package/dist/core/values/bool.js +12 -22
  49. package/dist/core/values/box.d.ts +15 -20
  50. package/dist/core/values/box.js +20 -33
  51. package/dist/core/values/canvas.d.ts +18 -25
  52. package/dist/core/values/canvas.js +32 -66
  53. package/dist/core/values/color.d.ts +5 -7
  54. package/dist/core/values/color.js +5 -11
  55. package/dist/core/values/field.d.ts +6 -7
  56. package/dist/core/values/field.js +10 -35
  57. package/dist/core/values/flags.d.ts +1 -2
  58. package/dist/core/values/flags.js +1 -17
  59. package/dist/core/values/gpu.d.ts +6 -10
  60. package/dist/core/values/gpu.js +8 -22
  61. package/dist/core/values/matrix.d.ts +2 -4
  62. package/dist/core/values/matrix.js +2 -12
  63. package/dist/core/values/num.d.ts +19 -28
  64. package/dist/core/values/num.js +23 -41
  65. package/dist/core/values/pose.d.ts +2 -4
  66. package/dist/core/values/pose.js +3 -12
  67. package/dist/core/values/range.d.ts +18 -26
  68. package/dist/core/values/range.js +22 -39
  69. package/dist/core/values/reg/ambiguity.d.ts +8 -0
  70. package/dist/core/values/reg/ambiguity.js +131 -0
  71. package/dist/core/values/reg/engine.d.ts +91 -0
  72. package/dist/core/values/reg/engine.js +373 -0
  73. package/dist/core/values/reg/nfa.d.ts +42 -0
  74. package/dist/core/values/reg/nfa.js +391 -0
  75. package/dist/core/values/reg/regex.d.ts +7 -0
  76. package/dist/core/values/reg/regex.js +318 -0
  77. package/dist/core/values/reg/types.d.ts +60 -0
  78. package/dist/core/values/reg/types.js +3 -0
  79. package/dist/core/values/reg.d.ts +250 -0
  80. package/dist/core/values/reg.js +649 -0
  81. package/dist/core/values/str.d.ts +16 -60
  82. package/dist/core/values/str.js +133 -315
  83. package/dist/core/values/template.js +1 -24
  84. package/dist/core/values/transform.d.ts +3 -5
  85. package/dist/core/values/transform.js +3 -12
  86. package/dist/core/values/tri.d.ts +9 -10
  87. package/dist/core/values/tri.js +9 -15
  88. package/dist/core/values/vec.d.ts +9 -24
  89. package/dist/core/values/vec.js +9 -64
  90. package/dist/formats/lens.js +6 -9
  91. package/dist/index.d.ts +0 -11
  92. package/dist/index.js +1 -11
  93. package/dist/jsx-dev-runtime.d.ts +2 -0
  94. package/dist/jsx-dev-runtime.js +5 -0
  95. package/dist/jsx-runtime.d.ts +54 -0
  96. package/dist/jsx-runtime.js +219 -0
  97. package/dist/schema/lens.js +5 -5
  98. package/dist/shapes/drag-behaviors.d.ts +56 -0
  99. package/dist/shapes/drag-behaviors.js +102 -0
  100. package/dist/shapes/drag-spec.d.ts +52 -0
  101. package/dist/shapes/drag-spec.js +112 -0
  102. package/dist/shapes/index.d.ts +3 -1
  103. package/dist/shapes/index.js +3 -1
  104. package/dist/shapes/interaction.d.ts +2 -3
  105. package/dist/shapes/interaction.js +77 -56
  106. package/dist/shapes/label.js +6 -0
  107. package/dist/shapes/layout.d.ts +47 -1
  108. package/dist/shapes/layout.js +59 -1
  109. package/package.json +22 -1
  110. package/dist/coll.d.ts +0 -74
  111. package/dist/coll.js +0 -210
  112. package/dist/core/lenses/closed-form-policies.d.ts +0 -57
  113. package/dist/core/lenses/decompositions.d.ts +0 -14
  114. package/dist/core/lenses/decompositions.js +0 -224
  115. package/dist/core/lenses/domain-aggregates.d.ts +0 -42
  116. package/dist/core/lenses/domain-aggregates.js +0 -245
  117. package/dist/core/lenses/typed-factor.d.ts +0 -40
@@ -11,13 +11,10 @@ export interface Tex {
11
11
  * downgraded to NEAREST where `OES_texture_float_linear` is unavailable. */
12
12
  export declare function newTex(w: number, h: number, data?: Float32Array | null, linear?: boolean): WebGLTexture;
13
13
  export declare function disposeTex(t: WebGLTexture): void;
14
- /** A reusable owned texture, reallocated on size change — the GPU analog of
15
- * the CPU scratch buffer. */
14
+ /** A reusable owned texture, reallocated on size change. */
16
15
  export declare function scratch(): (w: number, h: number) => Tex;
17
16
  /** A feedback-safe scratch pair: returns an owned texture guaranteed not to
18
- * equal `avoid` (the input being read), so a pass never reads and writes the
19
- * same texture. Needed for backward passes that produce a root cell's next
20
- * value (whose current value may be this same scratch). */
17
+ * equal `avoid`, so a pass never reads and writes the same texture. */
21
18
  export declare function scratch2(): (w: number, h: number, avoid: WebGLTexture | null) => Tex;
22
19
  /** Copy `srcTex` into `dst` (distinct textures). */
23
20
  export declare function copy(srcTex: WebGLTexture, dst: Tex): void;
@@ -35,7 +32,7 @@ export declare function pass(frag: string, target: Tex, setup: (s: Setup) => voi
35
32
  /** Mean of all texels (RGBA, 0–1) via GPU pyramid reduction; one 1×1
36
33
  * readback. */
37
34
  export declare function reduceMean(src: Tex): [number, number, number, number];
38
- /** Mean of squared texels — the spring settle metric. */
35
+ /** Mean of squared texels (spring settle metric). */
39
36
  export declare function reduceMeanSquare(src: Tex): number;
40
37
  /** Render `src` to a 2D canvas (GPU draw + drawImage; no CPU readback). */
41
38
  export declare function blit(src: Tex, ctx: CanvasRenderingContext2D): void;
@@ -61,10 +58,9 @@ export declare class Spring {
61
58
  private springFbo;
62
59
  constructor(w: number, h: number, opts?: SpringOpts);
63
60
  private clearTex;
64
- /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
65
- * alias one of our own position textures (the host feeds `current()` back
66
- * as the root value), so skip any copy that would read and write the same
67
- * texture — illegal GL feedback, and a no-op anyway. */
61
+ /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may alias
62
+ * one of our own position textures, so skip a copy that would read and write
63
+ * the same texture (illegal GL feedback). */
68
64
  seed(srcTex: WebGLTexture): void;
69
65
  /** Retarget (keeps current position + velocity). */
70
66
  setTarget(srcTex: WebGLTexture): void;
@@ -1,15 +1,5 @@
1
- // gpu.ts shared WebGL2 core for GPU-resident Canvas values.
2
- //
3
- // There is ONE compute context (a hidden canvas). That is a hard WebGL
4
- // constraint, not a shared buffer: a lens pass samples its parent's texture
5
- // and renders into its child's, and textures can't cross contexts (browsers
6
- // also cap contexts at ~16). Every Canvas value still owns its own float
7
- // texture(s); display blits each value into its own DOM canvas. The only
8
- // CPU readbacks are 1×1 reductions (mean colour, settle energy).
9
- //
10
- // Coordinate convention: data row 0 lives at texture v=0, so every compute
11
- // pass works in a single consistent data space (no flips); only `blit`
12
- // flips Y so the image reads upright on screen.
1
+ // Coordinate convention: data row 0 lives at texture v=0, so every compute pass
2
+ // works in one consistent data space; only `blit` flips Y for the screen.
13
3
  let _gl = null;
14
4
  /** Lazily create the shared WebGL2 context (browser-only). */
15
5
  export function gl() {
@@ -103,8 +93,7 @@ export function newTex(w, h, data = null, linear = false) {
103
93
  export function disposeTex(t) {
104
94
  gl().deleteTexture(t);
105
95
  }
106
- /** A reusable owned texture, reallocated on size change — the GPU analog of
107
- * the CPU scratch buffer. */
96
+ /** A reusable owned texture, reallocated on size change. */
108
97
  export function scratch() {
109
98
  let t = null;
110
99
  let cw = 0;
@@ -121,9 +110,7 @@ export function scratch() {
121
110
  };
122
111
  }
123
112
  /** A feedback-safe scratch pair: returns an owned texture guaranteed not to
124
- * equal `avoid` (the input being read), so a pass never reads and writes the
125
- * same texture. Needed for backward passes that produce a root cell's next
126
- * value (whose current value may be this same scratch). */
113
+ * equal `avoid`, so a pass never reads and writes the same texture. */
127
114
  export function scratch2() {
128
115
  let a = null;
129
116
  let b = null;
@@ -246,7 +233,7 @@ export function reduceMean(src) {
246
233
  return [buf[0] / n, buf[1] / n, buf[2] / n, buf[3] / n];
247
234
  }
248
235
  let _sq = null;
249
- /** Mean of squared texels — the spring settle metric. */
236
+ /** Mean of squared texels (spring settle metric). */
250
237
  export function reduceMeanSquare(src) {
251
238
  if (!_sq || _sq.w !== src.w || _sq.h !== src.h) {
252
239
  if (_sq)
@@ -373,10 +360,9 @@ export class Spring {
373
360
  g.clearColor(0, 0, 0, 0);
374
361
  g.clear(g.COLOR_BUFFER_BIT);
375
362
  }
376
- /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
377
- * alias one of our own position textures (the host feeds `current()` back
378
- * as the root value), so skip any copy that would read and write the same
379
- * texture — illegal GL feedback, and a no-op anyway. */
363
+ /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may alias
364
+ * one of our own position textures, so skip a copy that would read and write
365
+ * the same texture (illegal GL feedback). */
380
366
  seed(srcTex) {
381
367
  for (const tex of [this.pos[0], this.pos[1], this.targetTex])
382
368
  if (srcTex !== tex)
@@ -45,9 +45,7 @@ export declare class Matrix extends Cell<V> {
45
45
  get determinant(): Num;
46
46
  }
47
47
  /** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
48
- * Each entry is a literal `number` (lifted to a fresh seed) or an existing
49
- * `Writable<Num>` (identity passthrough). RO sources are rejected at the
50
- * type level — use `Matrix.derive(...)` for reactive RO tracking, or
51
- * `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
48
+ * Each entry is a literal (new cell) or existing writable (passed through);
49
+ * for read-only sources use `Matrix.derive`. Lock an entry with `Num.pin`. */
52
50
  export declare function matrix(a?: Init<Num>, b?: Init<Num>, c?: Init<Num>, d?: Init<Num>, e?: Init<Num>, f?: Init<Num>): Writable<Matrix>;
53
51
  export {};
@@ -1,10 +1,3 @@
1
- // matrix.ts — reactive 2D affine matrix (SVG/Canvas convention).
2
- //
3
- // Sparse-trait: only `equals`. Element-wise linear combine / lerp don't
4
- // decompose for matrices, so `spring`/`tween`/`mean` reject Matrix at
5
- // compile time. Two invertibles, both `: this` via `Cell#lens`:
6
- // - `multiply(b)` — inverse multiplies by `invert(b)`
7
- // - `invert()` — its own inverse
8
1
  import { Cell, cachedDerive, fieldLens, reader, } from "../cell.js";
9
2
  import { Num, num } from "./num.js";
10
3
  export const identity = () => ({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 });
@@ -115,10 +108,8 @@ export class Matrix extends Cell {
115
108
  }
116
109
  }
117
110
  /** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
118
- * Each entry is a literal `number` (lifted to a fresh seed) or an existing
119
- * `Writable<Num>` (identity passthrough). RO sources are rejected at the
120
- * type level — use `Matrix.derive(...)` for reactive RO tracking, or
121
- * `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
111
+ * Each entry is a literal (new cell) or existing writable (passed through);
112
+ * for read-only sources use `Matrix.derive`. Lock an entry with `Num.pin`. */
122
113
  export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
123
114
  if (typeof a === "number" &&
124
115
  typeof b === "number" &&
@@ -134,6 +125,5 @@ export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
134
125
  const dN = num(d);
135
126
  const eN = num(e);
136
127
  const fN = num(f);
137
- // The view fully reconstructs all 6 cells (1-arg bwd ⇒ no source read).
138
128
  return Matrix.lens([aN, bN, cN, dN, eN, fN], ([a, b, c, d, e, f]) => ({ a, b, c, d, e, f }), v => [v.a, v.b, v.c, v.d, v.e, v.f]);
139
129
  }
@@ -22,46 +22,37 @@ export declare class Num extends Cell<V> {
22
22
  add(b: Val<V>): this;
23
23
  sub(b: Val<V>): this;
24
24
  scale(k: Val<number>): this;
25
- /** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
26
- * for `.scale(k).add(off)`. */
25
+ /** Affine map `v ↦ k·v + off`. Invertible when k ≠ 0. */
27
26
  affine(k: Val<number>, off: Val<number>): this;
28
- /** `sin(this)` (radians). Forward lands in [−1, 1]; the inverse is
29
- * multi-valued, so a write clamps to that domain and returns the
30
- * pre-image nearest the current source — the drag stays on its branch. */
27
+ /** Sine of `this` (radians). The inverse is multi-valued; a write picks the
28
+ * angle nearest the current value, so a drag stays on its branch. */
31
29
  sin(): this;
32
- /** `exp(this)` — bijection on the reals; inverse is the natural log. */
30
+ /** Natural exponential; inverts via log. */
33
31
  exp(): this;
34
- /** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
35
- * the range reads back clamped, not as written). */
32
+ /** Clamp to `[lo, hi]`. Lossy: a write outside the range reads back clamped. */
36
33
  clamp(lo: Val<V>, hi: Val<V>): this;
37
- /** Lossy lens snapping reads/writes to the nearest multiple of `step`. */
34
+ /** Snap reads and writes to the nearest multiple of `step` (lossy). */
38
35
  quantize(step: Val<number>): this;
39
- /** Cyclic-coordinate lens. Reads pass through; writes pick the
40
- * representative closest to current modulo `period`, so dragging an
41
- * angle never jumps a full revolution. The 2-arg bwd is arity-detected
42
- * as stateful, threading the accumulated value through `s`. */
36
+ /** Reads pass through; a write picks the value closest to the current one
37
+ * modulo `period`, so dragging an angle never jumps a full turn. */
43
38
  cyclic(period: Val<number>): this;
44
- /** `this > t` as a Bool. Flipping the view bumps the source across
45
- * the threshold by `eps`. */
39
+ /** `this > t` as a Bool. Flipping it bumps the source across the
40
+ * threshold by `eps`. */
46
41
  greaterThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
47
- /** `this < t`. Dual of `greaterThan`. */
42
+ /** `this < t` as a Bool. */
48
43
  lessThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
49
- /** `round(this) 0 (mod d)` as a Bool; pair with `quantize(1)` for
50
- * integer sliders. Bwd: to make divisible, snap to the nearer
51
- * multiple of `d`; to make non-divisible, bump by `+1`; no-op when
52
- * the class already matches. */
44
+ /** True when `round(this)` is divisible by `d`. A write snaps to the nearest
45
+ * multiple of `d` to make it divisible, or bumps by 1 to make it not. */
53
46
  divisibleBy<T extends Num>(this: T, d: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
54
- /** `divisibleBy(2)` lazy getter for the common case. */
47
+ /** True when even. */
55
48
  get isEven(): this extends WritableBrand ? Writable<Bool> : Bool;
56
- /** `not(divisibleBy(2))` lazy getter. */
49
+ /** True when odd. */
57
50
  get isOdd(): this extends WritableBrand ? Writable<Bool> : Bool;
58
- /** Tween-builder; `this: Writable<Num>` gates the call to writable
59
- * receivers. */
51
+ /** Tween-builder. */
60
52
  to(this: Writable<Num>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
61
53
  }
62
- /** Writable `Num`. Literal seeds a fresh cell; existing `Writable<Num>`
63
- * passes through by identity. RO sources are rejected at the type level —
64
- * use `Num.derive(...)` for reactive RO tracking, or `Num.coerce(...)` for
65
- * the permissive lift over any `Val<number>`. */
54
+ /** Writable `Num` from a literal (new cell) or existing writable (passed
55
+ * through). For read-only sources use `Num.derive`, or `Num.coerce` to lift
56
+ * any `Val<number>`. */
66
57
  export declare function num(v?: Init<Num>): Writable<Num>;
67
58
  export {};
@@ -1,7 +1,3 @@
1
- // num.ts — reactive scalar.
2
- //
3
- // Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
4
- // chained calls compose into a lens chain.
5
1
  import { tween } from "../../animation/index.js";
6
2
  import { Cell, lazy, reader, } from "../cell.js";
7
3
  import { Bool } from "./bool.js";
@@ -47,16 +43,14 @@ export class Num extends Cell {
47
43
  const kf = reader(k);
48
44
  return this.lens(v => v * kf(), n => n / kf());
49
45
  }
50
- /** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
51
- * for `.scale(k).add(off)`. */
46
+ /** Affine map `v ↦ k·v + off`. Invertible when k ≠ 0. */
52
47
  affine(k, off) {
53
48
  const kf = reader(k);
54
49
  const of = reader(off);
55
50
  return this.lens(v => v * kf() + of(), n => (n - of()) / kf());
56
51
  }
57
- /** `sin(this)` (radians). Forward lands in [−1, 1]; the inverse is
58
- * multi-valued, so a write clamps to that domain and returns the
59
- * pre-image nearest the current source — the drag stays on its branch. */
52
+ /** Sine of `this` (radians). The inverse is multi-valued; a write picks the
53
+ * angle nearest the current value, so a drag stays on its branch. */
60
54
  sin() {
61
55
  return this.lens(v => Math.sin(v), (target, s) => {
62
56
  const p = Math.asin(unit(target));
@@ -65,12 +59,11 @@ export class Num extends Cell {
65
59
  return Math.abs(a - s) <= Math.abs(b - s) ? a : b;
66
60
  });
67
61
  }
68
- /** `exp(this)` — bijection on the reals; inverse is the natural log. */
62
+ /** Natural exponential; inverts via log. */
69
63
  exp() {
70
64
  return this.lens(v => Math.exp(v), n => Math.log(n));
71
65
  }
72
- /** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
73
- * the range reads back clamped, not as written). */
66
+ /** Clamp to `[lo, hi]`. Lossy: a write outside the range reads back clamped. */
74
67
  clamp(lo, hi) {
75
68
  const lf = reader(lo);
76
69
  const hf = reader(hi);
@@ -78,31 +71,29 @@ export class Num extends Cell {
78
71
  const l = lf(), h = hf();
79
72
  return v < l ? l : v > h ? h : v;
80
73
  };
81
- // A write whose clamped projection matches the current view leaves
82
- // the source untouched (off-range source preserved).
74
+ // If the clamped write matches the current view, keep the source
75
+ // (preserves an off-range source value).
83
76
  return this.lens(c, (v, s) => {
84
77
  const cv = c(v);
85
78
  return cv === c(s) ? s : cv;
86
79
  });
87
80
  }
88
- /** Lossy lens snapping reads/writes to the nearest multiple of `step`. */
81
+ /** Snap reads and writes to the nearest multiple of `step` (lossy). */
89
82
  quantize(step) {
90
83
  const sf = reader(step);
91
84
  const q = (v) => {
92
85
  const s = sf();
93
86
  return Math.round(v / s) * s;
94
87
  };
95
- // A write that snaps to the current bucket leaves the source
96
- // untouched (off-grid remainder preserved).
88
+ // If the write snaps to the current bucket, keep the source
89
+ // (preserves an off-grid remainder).
97
90
  return this.lens(q, (v, src) => {
98
91
  const qv = q(v);
99
92
  return qv === q(src) ? src : qv;
100
93
  });
101
94
  }
102
- /** Cyclic-coordinate lens. Reads pass through; writes pick the
103
- * representative closest to current modulo `period`, so dragging an
104
- * angle never jumps a full revolution. The 2-arg bwd is arity-detected
105
- * as stateful, threading the accumulated value through `s`. */
95
+ /** Reads pass through; a write picks the value closest to the current one
96
+ * modulo `period`, so dragging an angle never jumps a full turn. */
106
97
  cyclic(period) {
107
98
  const pf = reader(period);
108
99
  return this.lens(v => v, (v, s) => {
@@ -111,13 +102,8 @@ export class Num extends Cell {
111
102
  return s + delta - p * Math.round(delta / p);
112
103
  });
113
104
  }
114
- // Predicate bridges to Bool.
115
- //
116
- // Cross-type quotient lenses projecting Num through a boolean
117
- // predicate. Conditional return type: writable receiver yields
118
- // `Writable<Bool>`, RO receiver yields RO `Bool`.
119
- /** `this > t` as a Bool. Flipping the view bumps the source across
120
- * the threshold by `eps`. */
105
+ /** `this > t` as a Bool. Flipping it bumps the source across the
106
+ * threshold by `eps`. */
121
107
  greaterThan(t, eps = 1e-6) {
122
108
  const tf = reader(t);
123
109
  const ef = reader(eps);
@@ -128,7 +114,7 @@ export class Num extends Cell {
128
114
  return target ? th + ef() : th - ef();
129
115
  });
130
116
  }
131
- /** `this < t`. Dual of `greaterThan`. */
117
+ /** `this < t` as a Bool. */
132
118
  lessThan(t, eps = 1e-6) {
133
119
  const tf = reader(t);
134
120
  const ef = reader(eps);
@@ -139,10 +125,8 @@ export class Num extends Cell {
139
125
  return target ? th - ef() : th + ef();
140
126
  });
141
127
  }
142
- /** `round(this) 0 (mod d)` as a Bool; pair with `quantize(1)` for
143
- * integer sliders. Bwd: to make divisible, snap to the nearer
144
- * multiple of `d`; to make non-divisible, bump by `+1`; no-op when
145
- * the class already matches. */
128
+ /** True when `round(this)` is divisible by `d`. A write snaps to the nearest
129
+ * multiple of `d` to make it divisible, or bumps by 1 to make it not. */
146
130
  divisibleBy(d) {
147
131
  const df = reader(d);
148
132
  return Bool.lens(this, v => Math.round(v) % df() === 0, (target, current) => {
@@ -161,24 +145,22 @@ export class Num extends Cell {
161
145
  return r + 1;
162
146
  });
163
147
  }
164
- /** `divisibleBy(2)` lazy getter for the common case. */
148
+ /** True when even. */
165
149
  get isEven() {
166
150
  return lazy(this, "isEven", () => this.divisibleBy(2));
167
151
  }
168
- /** `not(divisibleBy(2))` lazy getter. */
152
+ /** True when odd. */
169
153
  get isOdd() {
170
154
  return lazy(this, "isOdd", () => this.divisibleBy(2).not());
171
155
  }
172
- /** Tween-builder; `this: Writable<Num>` gates the call to writable
173
- * receivers. */
156
+ /** Tween-builder. */
174
157
  to(target, dur, ease) {
175
158
  return tween(this, target, dur, ease);
176
159
  }
177
160
  }
178
- /** Writable `Num`. Literal seeds a fresh cell; existing `Writable<Num>`
179
- * passes through by identity. RO sources are rejected at the type level —
180
- * use `Num.derive(...)` for reactive RO tracking, or `Num.coerce(...)` for
181
- * the permissive lift over any `Val<number>`. */
161
+ /** Writable `Num` from a literal (new cell) or existing writable (passed
162
+ * through). For read-only sources use `Num.derive`, or `Num.coerce` to lift
163
+ * any `Val<number>`. */
182
164
  export function num(v = 0) {
183
165
  if (v instanceof Num)
184
166
  return v;
@@ -27,9 +27,7 @@ export declare class Pose extends Cell<V> {
27
27
  get y(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
28
28
  get theta(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
29
29
  }
30
- /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
31
- * through by identity. RO sources are rejected at the type level — use
32
- * `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
33
- * snapshot. */
30
+ /** Writable `Pose` from a literal (new cell) or existing writable (passed
31
+ * through). For read-only sources use `Pose.derive`. */
34
32
  export declare function pose(v?: Init<Pose>): Writable<Pose>;
35
33
  export {};
@@ -1,9 +1,3 @@
1
- // pose.ts — reactive 2D rigid-body pose: { x, y, theta }.
2
- //
3
- // Single source of truth for a rigid body. The solver binds the cell as
4
- // a 3-DOF block and writes back through it, so renderers, drag handlers,
5
- // IK, and physics all observe the same value. Vec / Num lenses compose
6
- // for consumers that care only about translation or rotation.
7
1
  import { Cell, fieldLens } from "../cell.js";
8
2
  import { Num } from "./num.js";
9
3
  export const add = (a, b) => ({
@@ -38,8 +32,7 @@ const packImpl = {
38
32
  },
39
33
  write: (a, o) => ({ x: a[o], y: a[o + 1], theta: a[o + 2] }),
40
34
  };
41
- /** Rotate-about-pivot moves the position and adds dθ to orientation;
42
- * scale-about-pivot scales position, orientation untouched. */
35
+ // rotateAbout moves position and adds dθ; scaleAbout scales position, leaves θ.
43
36
  const pivotalImpl = {
44
37
  rotateAbout: (v, p, dθ) => {
45
38
  const cos = Math.cos(dθ);
@@ -80,10 +73,8 @@ export class Pose extends Cell {
80
73
  return fieldLens(this, "theta", Num);
81
74
  }
82
75
  }
83
- /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
84
- * through by identity. RO sources are rejected at the type level — use
85
- * `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
86
- * snapshot. */
76
+ /** Writable `Pose` from a literal (new cell) or existing writable (passed
77
+ * through). For read-only sources use `Pose.derive`. */
87
78
  export function pose(v = { x: 0, y: 0, theta: 0 }) {
88
79
  if (v instanceof Pose)
89
80
  return v;
@@ -12,15 +12,14 @@ export declare const sub: (a: V, b: V) => V;
12
12
  export declare const scale: (a: V, k: number) => V;
13
13
  export declare const lerp: (a: V, b: V, t: number) => V;
14
14
  export declare const equals: (a: V, b: V) => boolean;
15
- /** L2 distance over (lo, hi). Treats a range as a point in 2-space. */
15
+ /** Euclidean distance over (lo, hi). */
16
16
  export declare const metric: (a: V, b: V) => number;
17
17
  export declare const width: (r: V) => number;
18
18
  export declare const center: (r: V) => number;
19
19
  export declare const contains: (r: V, v: number) => boolean;
20
20
  export declare const clamp: (r: V, v: number) => number;
21
- /** Closest value STRICTLY outside `[lo, hi]`, displaced past the
22
- * nearest endpoint by `eps`. Used by `Range#contains` as the bwd's
23
- * false-side policy. */
21
+ /** Closest value strictly outside `[lo, hi]`, displaced past the nearest
22
+ * endpoint by `eps`. */
24
23
  export declare const eject: (r: V, v: number, eps?: number) => number;
25
24
  /** Sample at parameter `t`: `lo + t·(hi - lo)`. `t ∈ [0, 1]` stays
26
25
  * inside the range; values outside extrapolate linearly. */
@@ -43,43 +42,36 @@ export declare class Range extends Cell<V> {
43
42
  /** End endpoint. Writes preserve `lo` (end-knob semantics). */
44
43
  get hi(): this extends WritableBrand ? Writable<Num> : Num;
45
44
  get width(): Num;
46
- /** Midpoint body-drag: read returns the center; write shifts the range
47
- * so the center matches (width preserved). `.start` is the lo-anchored
48
- * variant; `.lo` / `.hi` edit the endpoints. */
45
+ /** Midpoint body-drag: reads the center; a write shifts the range so the
46
+ * center matches (width preserved). */
49
47
  get center(): Writable<Num>;
50
- /** Translate by `by`. Reads shift the interval; writes shift back. */
48
+ /** Translate by `by`. */
51
49
  shift(by: Val<number>): this;
52
- /** Scale uniformly about the origin. Iso for `k ≠ 0`. */
50
+ /** Scale about the origin. Invertible when k ≠ 0. */
53
51
  scale(k: Val<number>): this;
54
- /** Body-drag handle: read returns `lo`; write shifts the range so `lo`
55
- * matches (width preserved). For start-knob editing use `.lo`. */
52
+ /** Body-drag handle: reads `lo`; a write shifts the range so `lo` matches
53
+ * (width preserved). */
56
54
  get start(): Writable<Num>;
57
55
  /** RO sample at `t`. `t ∈ [0, 1]` stays inside; outside extrapolates. */
58
56
  sample(t: Val<number>): Num;
59
57
  /** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
60
58
  * solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
61
59
  slider(t: Writable<Num>): Writable<Num>;
62
- /** Membership predicate. Conditional return type: a writable `Num`
63
- * yields `Writable<Bool>` and flipping the view bumps the source
64
- * (`true` clamps into `[lo, hi]`, `false` ejects past the nearest
65
- * endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
60
+ /** True when `v` is in `[lo, hi]`. A writable `Num` yields a `Writable<Bool>`:
61
+ * flipping it clamps `v` inside (`true`) or ejects it past the nearest
62
+ * endpoint (`false`). Literal/RO inputs yield a read-only `Bool`. */
66
63
  contains<P extends Val<number>>(v: P): P extends WritableBrand ? Writable<Bool> : Bool;
67
- /** RO clamp: read `v` into `[lo, hi]`. For a writable clamping lens
68
- * on a single Num, see `Num#clamp(lo, hi)`. */
64
+ /** Read-only clamp of `v` into `[lo, hi]`. */
69
65
  clampedRead(v: Val<number>): Num;
70
66
  /** Inverse of `sample`: derive the `t` that would produce `v`. */
71
67
  paramOf(v: Val<number>): Num;
72
- /** Tween-builder; animates `{lo, hi}` jointly. */
68
+ /** Tween-builder. */
73
69
  to(this: Writable<Range>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
74
70
  }
75
- /** Range over `[at, at + dur]`, parameterised by start + duration. The
76
- * timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
77
- * body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
71
+ /** Range over `[at, at + dur]`, backed by the live `at` and `dur` Nums. */
78
72
  export declare function span(at: Writable<Num>, dur: Writable<Num>): Writable<Range>;
79
- /** Writable `Range` over `[lo, hi]`. Each endpoint is a literal `number`
80
- * (lifted to a fresh seed) or an existing `Writable<Num>` (identity
81
- * passthrough). RO sources are rejected at the type level — use
82
- * `Range.derive(...)` for reactive RO tracking, or `cell.value` to
83
- * snapshot. Lock an endpoint with `Num.pin(c)`. */
73
+ /** Writable `Range` over `[lo, hi]`. Each endpoint is a literal (new cell) or
74
+ * existing writable (passed through); for read-only sources use `Range.derive`.
75
+ * Lock an endpoint with `Num.pin`. */
84
76
  export declare function range(lo?: Init<Num>, hi?: Init<Num>): Writable<Range>;
85
77
  export {};
@@ -1,10 +1,3 @@
1
- // range.ts — reactive numeric interval `[lo, hi]`.
2
- //
3
- // Home for sliders, scrollbars, and timeline clip spans. Field lenses
4
- // give start-knob (`.lo`) and end-knob (`.hi`) drag; `.start` / `.center`
5
- // are body-drags (shift both, preserving width — anchored at lo / midpoint
6
- // respectively); `.slider(t)` is the bidirectional `t ↔ lo + t·(hi - lo)`
7
- // iso. Traits: `linear`, `lerp`, `equals`, `pack` (scalar-only).
8
1
  import { tween } from "../../animation/index.js";
9
2
  import { Cell, cachedDerive, fieldLens, isReadonly, reader, readNow, SKIP, } from "../cell.js";
10
3
  import { Bool } from "./bool.js";
@@ -17,15 +10,14 @@ export const lerp = (a, b, t) => ({
17
10
  hi: a.hi + (b.hi - a.hi) * t,
18
11
  });
19
12
  export const equals = (a, b) => a === b || (a.lo === b.lo && a.hi === b.hi);
20
- /** L2 distance over (lo, hi). Treats a range as a point in 2-space. */
13
+ /** Euclidean distance over (lo, hi). */
21
14
  export const metric = (a, b) => Math.hypot(a.lo - b.lo, a.hi - b.hi);
22
15
  export const width = (r) => r.hi - r.lo;
23
16
  export const center = (r) => (r.lo + r.hi) / 2;
24
17
  export const contains = (r, v) => v >= r.lo && v <= r.hi;
25
18
  export const clamp = (r, v) => (v < r.lo ? r.lo : v > r.hi ? r.hi : v);
26
- /** Closest value STRICTLY outside `[lo, hi]`, displaced past the
27
- * nearest endpoint by `eps`. Used by `Range#contains` as the bwd's
28
- * false-side policy. */
19
+ /** Closest value strictly outside `[lo, hi]`, displaced past the nearest
20
+ * endpoint by `eps`. */
29
21
  export const eject = (r, v, eps = 1e-6) => {
30
22
  if (!contains(r, v))
31
23
  return v;
@@ -71,21 +63,20 @@ export class Range extends Cell {
71
63
  get width() {
72
64
  return cachedDerive(this, "width", Num, width);
73
65
  }
74
- /** Midpoint body-drag: read returns the center; write shifts the range
75
- * so the center matches (width preserved). `.start` is the lo-anchored
76
- * variant; `.lo` / `.hi` edit the endpoints. */
66
+ /** Midpoint body-drag: reads the center; a write shifts the range so the
67
+ * center matches (width preserved). */
77
68
  get center() {
78
69
  return Num.lens(this, center, (c, src) => {
79
70
  const half = (src.hi - src.lo) / 2;
80
71
  return { lo: c - half, hi: c + half };
81
72
  });
82
73
  }
83
- /** Translate by `by`. Reads shift the interval; writes shift back. */
74
+ /** Translate by `by`. */
84
75
  shift(by) {
85
76
  const f = reader(by);
86
77
  return this.lens(v => ({ lo: v.lo + f(), hi: v.hi + f() }), n => ({ lo: n.lo - f(), hi: n.hi - f() }));
87
78
  }
88
- /** Scale uniformly about the origin. Iso for `k ≠ 0`. */
79
+ /** Scale about the origin. Invertible when k ≠ 0. */
89
80
  scale(k) {
90
81
  const kf = reader(k);
91
82
  return this.lens(v => {
@@ -96,8 +87,8 @@ export class Range extends Cell {
96
87
  return { lo: n.lo / k, hi: n.hi / k };
97
88
  });
98
89
  }
99
- /** Body-drag handle: read returns `lo`; write shifts the range so `lo`
100
- * matches (width preserved). For start-knob editing use `.lo`. */
90
+ /** Body-drag handle: reads `lo`; a write shifts the range so `lo` matches
91
+ * (width preserved). */
101
92
  get start() {
102
93
  return Num.lens(this, v => v.lo, (newLo, src) => ({ lo: newLo, hi: newLo + (src.hi - src.lo) }));
103
94
  }
@@ -108,21 +99,19 @@ export class Range extends Cell {
108
99
  /** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
109
100
  * solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
110
101
  slider(t) {
111
- // `this as Range` pins the tuple element type (polymorphic `this`
112
- // defeats the mapped-tuple inference on `[this, t]`).
102
+ // `this as Range` pins the tuple element type; polymorphic `this` breaks
103
+ // the mapped-tuple inference.
113
104
  return Num.lens([this, t], ([r, tv]) => sample(r, tv), (v, [r]) => {
114
105
  const w = r.hi - r.lo;
115
106
  return [SKIP, w === 0 ? 0 : (v - r.lo) / w];
116
107
  });
117
108
  }
118
- /** Membership predicate. Conditional return type: a writable `Num`
119
- * yields `Writable<Bool>` and flipping the view bumps the source
120
- * (`true` clamps into `[lo, hi]`, `false` ejects past the nearest
121
- * endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
109
+ /** True when `v` is in `[lo, hi]`. A writable `Num` yields a `Writable<Bool>`:
110
+ * flipping it clamps `v` inside (`true`) or ejects it past the nearest
111
+ * endpoint (`false`). Literal/RO inputs yield a read-only `Bool`. */
122
112
  contains(v) {
123
113
  if (v instanceof Num) {
124
- // RO Num has no backward path RO branch. Sources and
125
- // writable lenses both accept write-back.
114
+ // RO Num has no backward path; only writable Nums accept write-back.
126
115
  if (!isReadonly(v)) {
127
116
  return Bool.lens([this, v], (vals) => contains(vals[0], vals[1]), (target, vals) => {
128
117
  const [r, n] = vals;
@@ -134,8 +123,7 @@ export class Range extends Cell {
134
123
  }
135
124
  return Bool.derive(() => contains(this.value, readNow(v)));
136
125
  }
137
- /** RO clamp: read `v` into `[lo, hi]`. For a writable clamping lens
138
- * on a single Num, see `Num#clamp(lo, hi)`. */
126
+ /** Read-only clamp of `v` into `[lo, hi]`. */
139
127
  clampedRead(v) {
140
128
  return Num.derive(() => clamp(this.value, readNow(v)));
141
129
  }
@@ -143,27 +131,22 @@ export class Range extends Cell {
143
131
  paramOf(v) {
144
132
  return Num.derive(() => paramOf(this.value, readNow(v)));
145
133
  }
146
- /** Tween-builder; animates `{lo, hi}` jointly. */
134
+ /** Tween-builder. */
147
135
  to(target, dur, ease) {
148
136
  return tween(this, target, dur, ease);
149
137
  }
150
138
  }
151
- /** @internal 2-input lens over two writable `Num`s; `range()` delegates
152
- * here after lifting literals. */
139
+ /** Lens combining two writable `Num`s into a `Range`. */
153
140
  function ends(lo, hi) {
154
141
  return Range.lens([lo, hi], (vals) => ({ lo: vals[0], hi: vals[1] }), (target) => [target.lo, target.hi]);
155
142
  }
156
- /** Range over `[at, at + dur]`, parameterised by start + duration. The
157
- * timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
158
- * body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
143
+ /** Range over `[at, at + dur]`, backed by the live `at` and `dur` Nums. */
159
144
  export function span(at, dur) {
160
145
  return Range.lens([at, dur], (vals) => ({ lo: vals[0], hi: vals[0] + vals[1] }), (target) => [target.lo, target.hi - target.lo]);
161
146
  }
162
- /** Writable `Range` over `[lo, hi]`. Each endpoint is a literal `number`
163
- * (lifted to a fresh seed) or an existing `Writable<Num>` (identity
164
- * passthrough). RO sources are rejected at the type level — use
165
- * `Range.derive(...)` for reactive RO tracking, or `cell.value` to
166
- * snapshot. Lock an endpoint with `Num.pin(c)`. */
147
+ /** Writable `Range` over `[lo, hi]`. Each endpoint is a literal (new cell) or
148
+ * existing writable (passed through); for read-only sources use `Range.derive`.
149
+ * Lock an endpoint with `Num.pin`. */
167
150
  export function range(lo = 0, hi = 1) {
168
151
  if (typeof lo === "number" && typeof hi === "number") {
169
152
  return new Range({ lo, hi });