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
@@ -1,10 +1,3 @@
1
- // transform.ts — reactive 2D transform.
2
- //
3
- // Invertibles (`add`, `sub`) return `: this` and ride on
4
- // `Cell#lens(fwd, bwd)`; chained calls compose into a lens chain.
5
- // Field-lens getters use `fieldLens()`, so writability propagates through
6
- // nested chains
7
- // (`Transform.translate.x.value = 5` works on writable receivers).
8
1
  import { tween } from "../../animation/index.js";
9
2
  import { Cell, fieldLens, reader, readNow } from "../cell.js";
10
3
  import { Num } from "./num.js";
@@ -58,8 +51,6 @@ export const metric = (a, b) => vMetric(a.translate, b.translate) +
58
51
  const linearImpl = { add, sub, scale };
59
52
  export class Transform extends Cell {
60
53
  static traits = { linear: linearImpl, lerp, metric, equals };
61
- /** Scalar `scale` is the `.scale` Vec field lens, not an eager method;
62
- * scalar-multiply via `Transform.lens(...)` or field writes. */
63
54
  constructor(v = DEFAULT) {
64
55
  super(v, { equals });
65
56
  }
@@ -89,13 +80,13 @@ export class Transform extends Cell {
89
80
  get opacity() {
90
81
  return fieldLens(this, "opacity", Num);
91
82
  }
92
- /** Tween-builder, implied by the lerp trait. */
83
+ /** Tween-builder. */
93
84
  to(target, dur, ease) {
94
85
  return tween(this, target, dur, ease);
95
86
  }
96
87
  }
97
- /** Seed a `Writable<Transform>` from literal values. For reactive
98
- * sources, use `Transform.lens(...)` or field-write composition. */
88
+ /** Writable `Transform` from literal fields. For reactive sources use
89
+ * `Transform.lens` or field writes. */
99
90
  export function transform(init) {
100
91
  const tr = new Transform();
101
92
  if (init) {
@@ -15,19 +15,18 @@ export declare class Tri extends Cell<V> {
15
15
  };
16
16
  readonly _t: typeof Tri.traits;
17
17
  constructor(v?: V);
18
- /** Kleene negation. Involution; fixed at `"mixed"`. */
18
+ /** Kleene negation. */
19
19
  not(): this;
20
- /** Aggregate over N writable `Bool` / `Tri` children. Read: all-true
21
- * `true`, all-false `false`, any disagreement (or any child already
22
- * `"mixed"`) `"mixed"`. Write: `true` / `false` broadcast to every
23
- * child, recursing through nested aggregates; `"mixed"` is a no-op. */
20
+ /** AND-aggregate over writable `Bool`/`Tri` children: `true` if all are true,
21
+ * `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
22
+ * every child (recursing into nested aggregates); `"mixed"` is a no-op. */
24
23
  static allOf(parents: readonly (Bool | Tri)[]): Writable<Tri>;
25
- /** Dual of `allOf` (Kleene OR) over `Bool` / `Tri` children: any-true
26
- * `true`, all-false → `false`, else (or any child `"mixed"`)
27
- * `"mixed"`. Same broadcast write policy. */
24
+ /** OR-aggregate over writable `Bool`/`Tri` children: `true` if any is true,
25
+ * `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
26
+ * every child; `"mixed"` is a no-op. */
28
27
  static anyOf(parents: readonly (Bool | Tri)[]): Writable<Tri>;
29
28
  }
30
- /** Writable `Tri`. Strict factory: `Tri.value | Writable<Tri>` in,
31
- * `Writable<Tri>` out. Default initial value is `"mixed"`. */
29
+ /** Writable `Tri` from a literal (new cell) or existing writable (passed
30
+ * through). Defaults to `"mixed"`. */
32
31
  export declare function tri(v?: Init<Tri>): Writable<Tri>;
33
32
  export {};
@@ -1,8 +1,3 @@
1
- // tri.ts — three-valued logical type (Kleene logic).
2
- //
3
- // `Tri.value ∈ { true, false, "mixed" }` — Bool plus an unknown state
4
- // fixed under negation. Strong-Kleene AND/OR follow the partial-info
5
- // reading (`mixed AND false` → `false`, `mixed AND true` → `mixed`).
6
1
  import { Cell, SKIP } from "../cell.js";
7
2
  const equals = (a, b) => a === b;
8
3
  /** Kleene negation: `true` / `false` swap, `"mixed"` is fixed. */
@@ -30,14 +25,13 @@ export class Tri extends Cell {
30
25
  constructor(v = "mixed") {
31
26
  super(v, { equals });
32
27
  }
33
- /** Kleene negation. Involution; fixed at `"mixed"`. */
28
+ /** Kleene negation. */
34
29
  not() {
35
30
  return this.lens(not, not);
36
31
  }
37
- /** Aggregate over N writable `Bool` / `Tri` children. Read: all-true
38
- * `true`, all-false `false`, any disagreement (or any child already
39
- * `"mixed"`) `"mixed"`. Write: `true` / `false` broadcast to every
40
- * child, recursing through nested aggregates; `"mixed"` is a no-op. */
32
+ /** AND-aggregate over writable `Bool`/`Tri` children: `true` if all are true,
33
+ * `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
34
+ * every child (recursing into nested aggregates); `"mixed"` is a no-op. */
41
35
  static allOf(parents) {
42
36
  return Tri.lens(parents, (vs) => {
43
37
  let anyT = false;
@@ -59,9 +53,9 @@ export class Tri extends Cell {
59
53
  return parents.map(() => target);
60
54
  });
61
55
  }
62
- /** Dual of `allOf` (Kleene OR) over `Bool` / `Tri` children: any-true
63
- * `true`, all-false → `false`, else (or any child `"mixed"`)
64
- * `"mixed"`. Same broadcast write policy. */
56
+ /** OR-aggregate over writable `Bool`/`Tri` children: `true` if any is true,
57
+ * `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
58
+ * every child; `"mixed"` is a no-op. */
65
59
  static anyOf(parents) {
66
60
  return Tri.lens(parents, (vs) => {
67
61
  let anyT = false;
@@ -86,8 +80,8 @@ export class Tri extends Cell {
86
80
  });
87
81
  }
88
82
  }
89
- /** Writable `Tri`. Strict factory: `Tri.value | Writable<Tri>` in,
90
- * `Writable<Tri>` out. Default initial value is `"mixed"`. */
83
+ /** Writable `Tri` from a literal (new cell) or existing writable (passed
84
+ * through). Defaults to `"mixed"`. */
91
85
  export function tri(v = "mixed") {
92
86
  if (v instanceof Tri)
93
87
  return v;
@@ -21,6 +21,9 @@ export declare const scaleAbout: (v: V, p: V, k: number) => V;
21
21
  * (y-down screen coords flip the visual sense). Returns `c` if `p` is
22
22
  * inside or on the circle. */
23
23
  export declare function tangentPoint(p: V, c: V, r: number, side?: 1 | -1): V;
24
+ /** Representative of cyclic angle `target` closest to `current`
25
+ * (shortest-arc inverse). */
26
+ export declare const nearestAngle: (target: number, current: number) => number;
24
27
  export declare class Vec extends Cell<V> {
25
28
  static traits: {
26
29
  linear: Linear<V>;
@@ -34,11 +37,9 @@ export declare class Vec extends Cell<V> {
34
37
  constructor(v?: V);
35
38
  add(b: Val<V>): this;
36
39
  sub(b: Val<V>): this;
37
- /** Uniform scale by `k` about `pivot` (default origin). Inverse scales
38
- * by `1/k`; exact bijection for `k ≠ 0`. */
40
+ /** Uniform scale by `k` about `pivot` (default origin). Invertible when k ≠ 0. */
39
41
  scale(k: Val<number>, pivot?: Val<V>): this;
40
- /** Rotate by `angle` (radians) about `pivot` (default origin). Inverse
41
- * rotates by `−angle`; exact bijection. */
42
+ /** Rotate by `angle` (radians) about `pivot` (default origin). */
42
43
  rotate(angle: Val<number>, pivot?: Val<V>): this;
43
44
  offset(dx: Val<number>, dy: Val<number>): this;
44
45
  up(n: Val<number>): this;
@@ -52,27 +53,11 @@ export declare class Vec extends Cell<V> {
52
53
  get x(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
53
54
  get y(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
54
55
  get magnitude(): Num;
55
- /** Tween-builder; `this: Writable<Vec>` gates the call to writable
56
- * receivers. */
56
+ /** Tween-builder. */
57
57
  to(this: Writable<Vec>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
58
58
  }
59
- /** Writable `Vec` at `(x, y)`. Each axis is a literal `number` (lifted
60
- * to a fresh seed) or an existing `Writable<Num>` (identity passthrough).
61
- * RO sources are rejected at the type level — use `Vec.derive(...)` for
62
- * reactive RO tracking, or `cell.value` to snapshot. Lock an axis with
63
- * `Num.pin(c)`: `vec(slider, Num.pin(100))`. */
59
+ /** Writable `Vec` at `(x, y)`. Each axis is a literal (new cell) or existing
60
+ * writable (passed through); for read-only sources use `Vec.derive`. Lock an
61
+ * axis with `Num.pin`: `vec(slider, Num.pin(100))`. */
64
62
  export declare function vec(x?: Init<Num>, y?: Init<Num>): Writable<Vec>;
65
- /** Policy for `polar`'s inverse — which inputs absorb a write:
66
- *
67
- * - `rotate` — c fixed; write r and a to land on target.
68
- * - `translate` — r and a fixed; shift c by Δ.
69
- * - `radial` — c and a fixed; project the drag onto the ray.
70
- * - `circular` — c and r fixed; project the drag onto the circle. */
71
- export type PolarPolicy = "rotate" | "translate" | "radial" | "circular";
72
- /** Vec at polar offset from `center`: `center + (r·cos a, r·sin a)`.
73
- * Bidirectional; each input is a literal (lifted to a fresh seed) or an
74
- * existing writable cell. RO inputs are rejected at the type level.
75
- * `policy` selects which inputs absorb writes; lock one with
76
- * `Num.pin(c)`: `polar(c, Num.pin(100), a)`. */
77
- export declare function polar(center: Init<Vec>, r: Init<Num>, a: Init<Num>, policy?: PolarPolicy): Writable<Vec>;
78
63
  export {};
@@ -1,10 +1,5 @@
1
- // vec.ts — reactive 2D point.
2
- //
3
- // Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
4
- // chained calls compose into a lens chain. Field-lens getters use
5
- // `fieldLens()` (propagates writability); `cachedDerive()` wraps RO views.
6
1
  import { tween } from "../../animation/index.js";
7
- import { Cell, cachedDerive, fieldLens, reader, readNow, SKIP, } from "../cell.js";
2
+ import { Cell, cachedDerive, fieldLens, reader, readNow, } from "../cell.js";
8
3
  import { Num, num } from "./num.js";
9
4
  export const add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
10
5
  export const sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
@@ -51,7 +46,7 @@ export function tangentPoint(p, c, r, side = -1) {
51
46
  const wrapToPi = (x) => x - 2 * Math.PI * Math.round(x / (2 * Math.PI));
52
47
  /** Representative of cyclic angle `target` closest to `current`
53
48
  * (shortest-arc inverse). */
54
- const nearestAngle = (target, current) => current + wrapToPi(target - current);
49
+ export const nearestAngle = (target, current) => current + wrapToPi(target - current);
55
50
  const linearImpl = { add, sub, scale };
56
51
  const packImpl = {
57
52
  dim: 2,
@@ -94,8 +89,7 @@ export class Vec extends Cell {
94
89
  return { x: n.x + o.x, y: n.y + o.y };
95
90
  });
96
91
  }
97
- /** Uniform scale by `k` about `pivot` (default origin). Inverse scales
98
- * by `1/k`; exact bijection for `k ≠ 0`. */
92
+ /** Uniform scale by `k` about `pivot` (default origin). Invertible when k ≠ 0. */
99
93
  scale(k, pivot) {
100
94
  const kf = reader(k);
101
95
  const pf = pivot === undefined ? undefined : reader(pivot);
@@ -107,8 +101,7 @@ export class Vec extends Cell {
107
101
  return pf ? scaleAbout(n, pf(), 1 / k) : { x: n.x / k, y: n.y / k };
108
102
  });
109
103
  }
110
- /** Rotate by `angle` (radians) about `pivot` (default origin). Inverse
111
- * rotates by `−angle`; exact bijection. */
104
+ /** Rotate by `angle` (radians) about `pivot` (default origin). */
112
105
  rotate(angle, pivot = ORIGIN) {
113
106
  const af = reader(angle);
114
107
  const pf = reader(pivot);
@@ -119,7 +112,6 @@ export class Vec extends Cell {
119
112
  const yf = reader(dy);
120
113
  return this.lens(v => ({ x: v.x + xf(), y: v.y + yf() }), n => ({ x: n.x - xf(), y: n.y - yf() }));
121
114
  }
122
- // Axis-aligned offset sugar — same fwd/bwd shape as offset.
123
115
  up(n) {
124
116
  const f = reader(n);
125
117
  return this.lens(v => ({ x: v.x, y: v.y - f() }), o => ({ x: o.x, y: o.y + f() }));
@@ -157,68 +149,21 @@ export class Vec extends Cell {
157
149
  get magnitude() {
158
150
  return cachedDerive(this, "magnitude", Num, v => Math.hypot(v.x, v.y));
159
151
  }
160
- /** Tween-builder; `this: Writable<Vec>` gates the call to writable
161
- * receivers. */
152
+ /** Tween-builder. */
162
153
  to(target, dur, ease) {
163
154
  return tween(this, target, dur, ease);
164
155
  }
165
156
  }
166
- /** @internal 2-input lens over two writable `Num`s; `vec()` delegates
167
- * here after lifting literals. */
157
+ /** Lens combining two writable `Num`s into a `Vec`. */
168
158
  function axes(x, y) {
169
- // The view fully reconstructs both axes (1-arg bwd ⇒ no source read).
170
159
  return Vec.lens([x, y], ([xv, yv]) => ({ x: xv, y: yv }), v => [v.x, v.y]);
171
160
  }
172
- /** Writable `Vec` at `(x, y)`. Each axis is a literal `number` (lifted
173
- * to a fresh seed) or an existing `Writable<Num>` (identity passthrough).
174
- * RO sources are rejected at the type level — use `Vec.derive(...)` for
175
- * reactive RO tracking, or `cell.value` to snapshot. Lock an axis with
176
- * `Num.pin(c)`: `vec(slider, Num.pin(100))`. */
161
+ /** Writable `Vec` at `(x, y)`. Each axis is a literal (new cell) or existing
162
+ * writable (passed through); for read-only sources use `Vec.derive`. Lock an
163
+ * axis with `Num.pin`: `vec(slider, Num.pin(100))`. */
177
164
  export function vec(x = 0, y = 0) {
178
165
  if (typeof x === "number" && typeof y === "number") {
179
166
  return new Vec({ x, y });
180
167
  }
181
168
  return axes(num(x), num(y));
182
169
  }
183
- /** Vec at polar offset from `center`: `center + (r·cos a, r·sin a)`.
184
- * Bidirectional; each input is a literal (lifted to a fresh seed) or an
185
- * existing writable cell. RO inputs are rejected at the type level.
186
- * `policy` selects which inputs absorb writes; lock one with
187
- * `Num.pin(c)`: `polar(c, Num.pin(100), a)`. */
188
- export function polar(center, r, a, policy = "rotate") {
189
- // Lift literals; already-writable inputs pass through by identity.
190
- const cSig = center instanceof Vec ? center : vec(center.x, center.y);
191
- const rSig = num(r);
192
- const aSig = num(a);
193
- const project = (c, rv, av) => ({
194
- x: c.x + rv * Math.cos(av),
195
- y: c.y + rv * Math.sin(av),
196
- });
197
- let bwd;
198
- switch (policy) {
199
- case "rotate":
200
- bwd = (p, [cv, , av]) => {
201
- const dx = p.x - cv.x;
202
- const dy = p.y - cv.y;
203
- return [SKIP, Math.hypot(dx, dy), nearestAngle(Math.atan2(dy, dx), av)];
204
- };
205
- break;
206
- case "translate":
207
- bwd = (p, [cv, rv, av]) => {
208
- const f = project(cv, rv, av);
209
- return [{ x: cv.x + (p.x - f.x), y: cv.y + (p.y - f.y) }, SKIP, SKIP];
210
- };
211
- break;
212
- case "radial":
213
- bwd = (p, [cv, , av]) => {
214
- const dx = p.x - cv.x;
215
- const dy = p.y - cv.y;
216
- return [SKIP, dx * Math.cos(av) + dy * Math.sin(av), SKIP];
217
- };
218
- break;
219
- case "circular":
220
- bwd = (p, [cv, , av]) => [SKIP, SKIP, nearestAngle(Math.atan2(p.y - cv.y, p.x - cv.x), av)];
221
- break;
222
- }
223
- return Vec.lens([cSig, rSig, aSig], ([c, rv, av]) => project(c, rv, av), bwd);
224
- }
@@ -36,19 +36,16 @@ export function valueHub(initial) {
36
36
  * the error regions. */
37
37
  export function formatSpoke(hub, adapter) {
38
38
  return lens(hub, {
39
- init: ([v]) => fromValue(adapter, v),
40
- step: ([v], c) => (v === c.synced ? c : absorb(adapter, c, v)),
41
- fwd: (_vals, c) => c.text,
42
- bwd: (target, _vals, c) => {
39
+ init: v => fromValue(adapter, v),
40
+ step: (v, c) => (v === c.synced ? c : absorb(adapter, c, v)),
41
+ fwd: (_v, c) => c.text,
42
+ bwd: (target, _v, c) => {
43
43
  const { tree, errors } = adapter.parse(target);
44
44
  if (errors.length === 0) {
45
45
  const v = valueOf(tree);
46
- return { updates: [v], complement: { text: target, tree, errors, synced: v } };
46
+ return { update: v, complement: { text: target, tree, errors, synced: v } };
47
47
  }
48
- return {
49
- updates: [SKIP],
50
- complement: { text: target, tree, errors, synced: c.synced },
51
- };
48
+ return { update: SKIP, complement: { text: target, tree, errors, synced: c.synced } };
52
49
  },
53
50
  });
54
51
  }
package/dist/index.d.ts CHANGED
@@ -1,20 +1,9 @@
1
- /** @group Reactivity */
2
- /** @group Animation */
3
1
  export * from "./animation/index.js";
4
- /** @group Utilities */
5
2
  export * from "./assert/index.js";
6
- /** @group Rendering */
7
3
  export { type CodeOpts, CodeShape, code, codeStyles, type Token, tokenize } from "./code/index.js";
8
- /** @group Utilities */
9
- export * from "./coll.js";
10
4
  export * from "./core/index.js";
11
- /** @group Utilities */
12
5
  export * from "./ext/index.js";
13
- /** @group Shapes */
14
6
  export * from "./shapes/index.js";
15
- /** @group Rendering */
16
7
  export * from "./tex/index.js";
17
- /** @group Utilities */
18
8
  export { allNodes, atPath, isLeaf, leavesOf, node as treeNode, nodeCount, type TreeNode, walkTree, } from "./tree.js";
19
- /** @group Web */
20
9
  export * from "./web/index.js";
package/dist/index.js CHANGED
@@ -1,22 +1,12 @@
1
- /** @group Reactivity */
2
- /** @group Animation */
1
+ // API-reference groups are assigned by source folder in theme/group-by-source.mjs.
3
2
  export * from "./animation/index.js";
4
- /** @group Utilities */
5
3
  export * from "./assert/index.js";
6
4
  // `code` and `tex` both export `Part`; re-export `code`'s other symbols
7
5
  // explicitly so the wildcard below lets `tex`'s `Part` win.
8
- /** @group Rendering */
9
6
  export { CodeShape, code, codeStyles, tokenize } from "./code/index.js";
10
- /** @group Utilities */
11
- export * from "./coll.js";
12
7
  export * from "./core/index.js";
13
- /** @group Utilities */
14
8
  export * from "./ext/index.js";
15
- /** @group Shapes */
16
9
  export * from "./shapes/index.js";
17
- /** @group Rendering */
18
10
  export * from "./tex/index.js";
19
- /** @group Utilities */
20
11
  export { allNodes, atPath, isLeaf, leavesOf, node as treeNode, nodeCount, walkTree, } from "./tree.js";
21
- /** @group Web */
22
12
  export * from "./web/index.js";
@@ -0,0 +1,2 @@
1
+ export * from "./jsx-runtime.js";
2
+ export { jsx as jsxDEV } from "./jsx-runtime.js";
@@ -0,0 +1,5 @@
1
+ // Development entry for the automatic JSX runtime. esbuild/tsc import this when
2
+ // `jsxDev` is enabled; the dev signature carries extra debug args we ignore, so
3
+ // `jsxDEV` just forwards to `jsx`.
4
+ export * from "./jsx-runtime.js";
5
+ export { jsx as jsxDEV } from "./jsx-runtime.js";
@@ -0,0 +1,54 @@
1
+ import { Cell } from "./core/cell.js";
2
+ /** Marker for `<>…</>`; lowered to `jsx(Fragment, …)`. */
3
+ export declare const Fragment: unique symbol;
4
+ type Disposer = () => void;
5
+ /** Register a teardown with the active scope (`mount` / `scope` / an `each`
6
+ * item) — for raw `effect`s or listeners created in a component body, which
7
+ * the JSX helpers otherwise track for you. No-op outside a scope. */
8
+ export declare function onCleanup(fn: Disposer): void;
9
+ type Props = Record<string, unknown> & {
10
+ children?: unknown;
11
+ };
12
+ type Component = (props: Props) => Node;
13
+ /** Build a DOM node for one JSX element. */
14
+ export declare function jsx(type: string | symbol | Component, props?: Props): Node;
15
+ /** Static-children variant; behaviourally identical in a runtime builder. */
16
+ export declare const jsxs: typeof jsx;
17
+ /** Render `component` into `host`, collecting reactive teardowns. The returned
18
+ * disposer releases them — call it on unmount (e.g. `disconnectedCallback`). */
19
+ export declare function mount(component: () => Node, host: Node): Disposer;
20
+ /** Run `fn` under a fresh reactive scope, returning its result and a disposer
21
+ * for every effect created during it — `mount` without a host. `each` gives
22
+ * each keyed item its own scope so its effects die when the item leaves. */
23
+ export declare function scope<T>(fn: () => T): [T, Disposer];
24
+ /** Keyed list rendering: keep `parent`'s children in sync with a reactive array,
25
+ * reusing and reordering nodes by key, disposing those that leave. Each item is
26
+ * rendered in its own `scope` (untracked from the list effect, so item-internal
27
+ * reads don't retrigger the whole list). Attach via `ref`:
28
+ * `<div ref={el => each(el, items, s => s.id, render)} />`. */
29
+ export declare function each<T>(parent: Element, items: Cell<T[]> | (() => readonly T[]), key: (item: T, index: number) => string, render: (item: T, index: number) => Node): void;
30
+ type Reactive<T> = T | Cell<T> | (() => T);
31
+ export declare namespace JSX {
32
+ export type Element = Node;
33
+ export interface ElementChildrenAttribute {
34
+ children: unknown;
35
+ }
36
+ export interface IntrinsicAttributes {
37
+ children?: unknown;
38
+ }
39
+ export interface CommonProps {
40
+ class?: Reactive<string>;
41
+ style?: Reactive<string | Partial<CSSStyleDeclaration>>;
42
+ id?: Reactive<string>;
43
+ lens?: Cell<any>;
44
+ ref?: (el: Element) => void;
45
+ children?: unknown;
46
+ [attr: string]: any;
47
+ }
48
+ type Tag = keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap;
49
+ export type IntrinsicElements = {
50
+ [K in Tag]: CommonProps;
51
+ };
52
+ export {};
53
+ }
54
+ export {};
@@ -0,0 +1,219 @@
1
+ // jsx-runtime.ts — minimal runtime JSX for bireactive (no compiler step).
2
+ //
3
+ // esbuild's automatic runtime (`jsxImportSource: "@bireactive"`) lowers JSX to
4
+ // `jsx`/`jsxs`/`Fragment` calls; this module builds real DOM nodes and wires
5
+ // reactive props and children through `effect`. The one binding with no React
6
+ // analogue is the `lens` prop: a single bidirectional terminal — the dual of
7
+ // the value-plus-onInput pair — that reads a writable cell forward into the
8
+ // control and writes edits back, so a chain of lenses can be driven from the
9
+ // leaf. Components are plain `props → Node`; reactive expressions are passed as
10
+ // thunks (`{() => expr}`) or cells, since there is no compile step to wrap them.
11
+ import { Cell, effect, untracked } from "./core/cell.js";
12
+ /** Marker for `<>…</>`; lowered to `jsx(Fragment, …)`. */
13
+ export const Fragment = Symbol.for("bireactive.jsx.Fragment");
14
+ // The active mount scope: reactive teardowns created while building a tree are
15
+ // collected here so `mount` can release them all on unmount. Non-reentrant —
16
+ // `mount` saves/restores the previous owner around the render.
17
+ let currentOwner = null;
18
+ function track(d) {
19
+ currentOwner?.push(d);
20
+ }
21
+ /** Register a teardown with the active scope (`mount` / `scope` / an `each`
22
+ * item) — for raw `effect`s or listeners created in a component body, which
23
+ * the JSX helpers otherwise track for you. No-op outside a scope. */
24
+ export function onCleanup(fn) {
25
+ track(fn);
26
+ }
27
+ /** Build a DOM node for one JSX element. */
28
+ export function jsx(type, props) {
29
+ if (typeof type === "function")
30
+ return type(props ?? {});
31
+ if (type === Fragment) {
32
+ const frag = document.createDocumentFragment();
33
+ append(frag, props?.children);
34
+ return frag;
35
+ }
36
+ const el = document.createElement(type);
37
+ if (props)
38
+ for (const key in props)
39
+ applyProp(el, key, props[key]);
40
+ return el;
41
+ }
42
+ /** Static-children variant; behaviourally identical in a runtime builder. */
43
+ export const jsxs = jsx;
44
+ function applyProp(el, key, value) {
45
+ if (key === "children")
46
+ return append(el, value);
47
+ if (key === "ref") {
48
+ if (typeof value === "function")
49
+ value(el);
50
+ return;
51
+ }
52
+ if (key === "lens")
53
+ return bindLens(el, value);
54
+ if (key.startsWith("on") && typeof value === "function") {
55
+ el.addEventListener(key.slice(2).toLowerCase(), value);
56
+ return;
57
+ }
58
+ if (value instanceof Cell) {
59
+ track(effect(() => setProp(el, key, value.value)));
60
+ return;
61
+ }
62
+ if (typeof value === "function") {
63
+ track(effect(() => setProp(el, key, value())));
64
+ return;
65
+ }
66
+ setProp(el, key, value);
67
+ }
68
+ function setProp(el, key, value) {
69
+ if (key === "class" || key === "className") {
70
+ el.setAttribute("class", value == null ? "" : String(value));
71
+ }
72
+ else if (key === "style") {
73
+ if (value && typeof value === "object")
74
+ Object.assign(el.style, value);
75
+ else
76
+ el.setAttribute("style", value == null ? "" : String(value));
77
+ }
78
+ else if (key === "value") {
79
+ el.value = value == null ? "" : String(value);
80
+ }
81
+ else if (key === "checked" || key === "disabled" || key === "selected") {
82
+ // biome-ignore lint/suspicious/noExplicitAny: boolean DOM properties
83
+ el[key] = !!value;
84
+ }
85
+ else if (value == null || value === false) {
86
+ el.removeAttribute(key);
87
+ }
88
+ else {
89
+ el.setAttribute(key, value === true ? "" : String(value));
90
+ }
91
+ }
92
+ /** Append a child (array / Node / text / cell / thunk) to `parent`. */
93
+ function append(parent, child) {
94
+ if (Array.isArray(child)) {
95
+ for (const c of child)
96
+ append(parent, c);
97
+ }
98
+ else if (child instanceof Node) {
99
+ parent.appendChild(child);
100
+ }
101
+ else if (child instanceof Cell) {
102
+ parent.appendChild(reactiveText(() => child.value));
103
+ }
104
+ else if (typeof child === "function") {
105
+ parent.appendChild(reactiveText(child));
106
+ }
107
+ else if (child != null && child !== false && child !== true) {
108
+ parent.appendChild(document.createTextNode(String(child)));
109
+ }
110
+ }
111
+ /** A text node whose content tracks `get()`. Primitive children only — the
112
+ * minimal runtime does not reconcile dynamic element children. */
113
+ function reactiveText(get) {
114
+ const node = document.createTextNode("");
115
+ track(effect(() => {
116
+ const v = get();
117
+ node.data = v == null ? "" : String(v);
118
+ }));
119
+ return node;
120
+ }
121
+ /** Two-way bind a form control to a writable cell: read forward into the
122
+ * control, write back on input. The forward write is skipped while the
123
+ * control is focused, so a live edit is never clobbered mid-drag (the
124
+ * controlled-input focus guard, written once). */
125
+ function bindLens(el, lens) {
126
+ const checkbox = el.type === "checkbox";
127
+ track(effect(() => {
128
+ const v = lens.value;
129
+ if (checkbox) {
130
+ el.checked = !!v;
131
+ return;
132
+ }
133
+ const next = v == null ? "" : String(v);
134
+ const root = el.getRootNode();
135
+ if (root.activeElement !== el && el.value !== next)
136
+ el.value = next;
137
+ }));
138
+ const evt = checkbox || el.tagName === "SELECT" ? "change" : "input";
139
+ el.addEventListener(evt, () => {
140
+ lens.value = checkbox
141
+ ? el.checked
142
+ : el.type === "range" || el.type === "number"
143
+ ? Number(el.value)
144
+ : el.value;
145
+ });
146
+ }
147
+ /** Render `component` into `host`, collecting reactive teardowns. The returned
148
+ * disposer releases them — call it on unmount (e.g. `disconnectedCallback`). */
149
+ export function mount(component, host) {
150
+ const [node, dispose] = scope(component);
151
+ host.appendChild(node);
152
+ return dispose;
153
+ }
154
+ /** Run `fn` under a fresh reactive scope, returning its result and a disposer
155
+ * for every effect created during it — `mount` without a host. `each` gives
156
+ * each keyed item its own scope so its effects die when the item leaves. */
157
+ export function scope(fn) {
158
+ const prev = currentOwner;
159
+ const owner = [];
160
+ currentOwner = owner;
161
+ try {
162
+ return [
163
+ fn(),
164
+ () => {
165
+ for (const d of owner)
166
+ d();
167
+ owner.length = 0;
168
+ },
169
+ ];
170
+ }
171
+ finally {
172
+ currentOwner = prev;
173
+ }
174
+ }
175
+ /** Keyed list rendering: keep `parent`'s children in sync with a reactive array,
176
+ * reusing and reordering nodes by key, disposing those that leave. Each item is
177
+ * rendered in its own `scope` (untracked from the list effect, so item-internal
178
+ * reads don't retrigger the whole list). Attach via `ref`:
179
+ * `<div ref={el => each(el, items, s => s.id, render)} />`. */
180
+ export function each(parent, items, key, render) {
181
+ const read = typeof items === "function" ? items : () => items.value;
182
+ const cache = new Map();
183
+ const stop = effect(() => {
184
+ const arr = read();
185
+ const seen = new Set();
186
+ const nodes = [];
187
+ arr.forEach((item, i) => {
188
+ const k = key(item, i);
189
+ seen.add(k);
190
+ let entry = cache.get(k);
191
+ if (entry === undefined) {
192
+ const [node, dispose] = untracked(() => scope(() => render(item, i)));
193
+ entry = { node, dispose };
194
+ cache.set(k, entry);
195
+ }
196
+ nodes.push(entry.node);
197
+ });
198
+ for (const [k, entry] of cache) {
199
+ if (!seen.has(k)) {
200
+ entry.dispose();
201
+ cache.delete(k);
202
+ }
203
+ }
204
+ // Only touch the DOM when the ordered node set actually changed — re-inserting
205
+ // identical children mid-interaction would steal focus and reset clicks.
206
+ const cur = parent.childNodes;
207
+ let same = cur.length === nodes.length;
208
+ for (let i = 0; same && i < nodes.length; i++)
209
+ same = cur[i] === nodes[i];
210
+ if (!same)
211
+ parent.replaceChildren(...nodes);
212
+ });
213
+ track(() => {
214
+ stop();
215
+ for (const entry of cache.values())
216
+ entry.dispose();
217
+ cache.clear();
218
+ });
219
+ }