bireactive 0.3.1 → 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 (89) hide show
  1. package/README.md +14 -7
  2. package/dist/core/_counts.js +5 -12
  3. package/dist/core/cell.d.ts +3 -3
  4. package/dist/core/cell.js +6 -7
  5. package/dist/core/derived-geometry.js +4 -7
  6. package/dist/core/index.d.ts +3 -1
  7. package/dist/core/index.js +3 -1
  8. package/dist/core/lenses/aggregates.d.ts +42 -52
  9. package/dist/core/lenses/aggregates.js +225 -116
  10. package/dist/core/lenses/geometry.d.ts +22 -4
  11. package/dist/core/lenses/geometry.js +59 -27
  12. package/dist/core/lenses/index.d.ts +5 -6
  13. package/dist/core/lenses/index.js +5 -6
  14. package/dist/core/lenses/memory.js +4 -17
  15. package/dist/core/lenses/numerical.d.ts +100 -0
  16. package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
  17. package/dist/core/lenses/point-cloud.d.ts +67 -0
  18. package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +218 -81
  19. package/dist/core/lenses/snap.d.ts +1 -1
  20. package/dist/core/lenses/snap.js +3 -10
  21. package/dist/core/lenses/text.d.ts +40 -0
  22. package/dist/core/lenses/text.js +202 -0
  23. package/dist/core/lifecycle.js +3 -6
  24. package/dist/core/linalg.js +5 -11
  25. package/dist/core/optic.js +10 -15
  26. package/dist/core/optics.js +4 -8
  27. package/dist/core/store.d.ts +1 -2
  28. package/dist/core/store.js +7 -15
  29. package/dist/core/traits.d.ts +4 -7
  30. package/dist/core/traits.js +8 -12
  31. package/dist/core/values/anchor.js +0 -4
  32. package/dist/core/values/arr.d.ts +110 -0
  33. package/dist/core/values/arr.js +336 -0
  34. package/dist/core/values/audio.d.ts +8 -9
  35. package/dist/core/values/audio.js +7 -23
  36. package/dist/core/values/bool.d.ts +11 -11
  37. package/dist/core/values/bool.js +12 -22
  38. package/dist/core/values/box.d.ts +15 -20
  39. package/dist/core/values/box.js +20 -33
  40. package/dist/core/values/canvas.d.ts +18 -25
  41. package/dist/core/values/canvas.js +17 -48
  42. package/dist/core/values/color.d.ts +5 -7
  43. package/dist/core/values/color.js +5 -11
  44. package/dist/core/values/field.d.ts +6 -7
  45. package/dist/core/values/field.js +10 -35
  46. package/dist/core/values/flags.d.ts +1 -2
  47. package/dist/core/values/flags.js +1 -17
  48. package/dist/core/values/gpu.d.ts +6 -10
  49. package/dist/core/values/gpu.js +8 -22
  50. package/dist/core/values/matrix.d.ts +2 -4
  51. package/dist/core/values/matrix.js +2 -12
  52. package/dist/core/values/num.d.ts +19 -28
  53. package/dist/core/values/num.js +23 -41
  54. package/dist/core/values/pose.d.ts +2 -4
  55. package/dist/core/values/pose.js +3 -12
  56. package/dist/core/values/range.d.ts +18 -26
  57. package/dist/core/values/range.js +22 -39
  58. package/dist/core/values/reg/ambiguity.d.ts +8 -0
  59. package/dist/core/values/reg/ambiguity.js +131 -0
  60. package/dist/core/values/reg/engine.d.ts +91 -0
  61. package/dist/core/values/reg/engine.js +373 -0
  62. package/dist/core/values/reg/nfa.d.ts +42 -0
  63. package/dist/core/values/reg/nfa.js +391 -0
  64. package/dist/core/values/reg/regex.d.ts +7 -0
  65. package/dist/core/values/reg/regex.js +318 -0
  66. package/dist/core/values/reg/types.d.ts +60 -0
  67. package/dist/core/values/reg/types.js +3 -0
  68. package/dist/core/values/reg.d.ts +250 -0
  69. package/dist/core/values/reg.js +649 -0
  70. package/dist/core/values/str.d.ts +16 -60
  71. package/dist/core/values/str.js +133 -315
  72. package/dist/core/values/template.js +1 -24
  73. package/dist/core/values/transform.d.ts +3 -5
  74. package/dist/core/values/transform.js +3 -12
  75. package/dist/core/values/tri.d.ts +9 -10
  76. package/dist/core/values/tri.js +9 -15
  77. package/dist/core/values/vec.d.ts +9 -24
  78. package/dist/core/values/vec.js +9 -64
  79. package/dist/index.d.ts +0 -11
  80. package/dist/index.js +1 -11
  81. package/package.json +6 -7
  82. package/dist/coll.d.ts +0 -74
  83. package/dist/coll.js +0 -210
  84. package/dist/core/lenses/closed-form-policies.d.ts +0 -57
  85. package/dist/core/lenses/decompositions.d.ts +0 -14
  86. package/dist/core/lenses/decompositions.js +0 -224
  87. package/dist/core/lenses/domain-aggregates.d.ts +0 -42
  88. package/dist/core/lenses/domain-aggregates.js +0 -245
  89. package/dist/core/lenses/typed-factor.d.ts +0 -40
@@ -1,14 +1,8 @@
1
- // linalg.ts — small dense linear algebra for the numerical backward
2
- // directions. Two callers, one kernel:
3
- // - lenses: the factor/argmin pseudoinverse solves `A k = δy` where
4
- // `A = J W Jᵀ + λI` is SPD by construction.
5
- // - constraints: AVBD's per-cell local Newton solve, `n = cell DOF`.
6
- //
7
- // Systems are tiny (n ≤ ~12), so unrolled direct solves (n = 2, 3) and
8
- // in-place LDLᵀ beat anything general-purpose AND allocate nothing — the
9
- // matrix is destroyed, the solution is written into `b`. This replaces the
10
- // old full-inverse-then-multiply path (1.9–5.3× slower; allocated an
11
- // `n×2n` augmented buffer per write).
1
+ // Small dense linear algebra for the numerical backward directions (the
2
+ // factor/argmin pseudoinverse `A k = δy` with `A = J W Jᵀ + λI` SPD, and the
3
+ // per-cell Newton solves in constraints). Systems are tiny (n ~12), so
4
+ // unrolled solves (n = 2, 3) and in-place LDLᵀ beat anything general-purpose and
5
+ // allocate nothing: the matrix is destroyed, the solution written into `b`.
12
6
  //
13
7
  // Convention: matrices are row-major, length `n*n`.
14
8
  const TINY = 1e-14;
@@ -1,19 +1,14 @@
1
- // optic.ts — lenses as first-class values, independent of any `Cell`.
1
+ // Lenses as first-class values, independent of any `Cell`. An `Optic<A, B>` is a
2
+ // lens transform *unbound* — a `get`/`put` pair you compose, store, and apply to
3
+ // a source with `cell.through(optic)` (≡ `lens(cell, o.get, o.put)`). Composition
4
+ // is ordinary lens composition: `(f ∘ g).put(c, a) = f.put(g.put(c, f.get(a)),
5
+ // a)`, reconstructing the inner source from `a` each back-write. An `iso` is the
6
+ // lossless case whose `put` ignores the source (`readsSource = false`), so
7
+ // `through` can bind a cheaper 1-arg backward. No complement here — use
8
+ // `lens(parent, spec)` when you need one.
2
9
  //
3
- // A `Cell` lens binds a transform to a specific source. An `Optic<A, B>` is that
4
- // transform *unbound* a pair of pure functions you can compose, store, and
5
- // hand around, then apply to a source with `cell.through(optic)` (≡ `lens(cell,
6
- // o.get, o.put)`). Composition is ordinary lens composition: `(f ∘ g).put(c, a) =
7
- // f.put(g.put(c, f.get(a)), a)`, so the inner source is reconstructed from `a` on
8
- // every back-write. An `iso` is the lossless special case whose `put` ignores the
9
- // source (`readsSource = false`), letting `through` bind a cheaper 1-arg backward.
10
- //
11
- // This is deliberately the pure/stateless slice of the lens algebra: no
12
- // complement, no effects. Stateful and effectful optics-as-values are future work
13
- // (see _notes); for now reach for `lens(parent, spec)` when you need a complement.
14
- //
15
- // The `Optic` type lives in cell.ts (so cell.ts stays import-free and its
16
- // `Cell.through` can name it); this module is the constructor/algebra surface.
10
+ // The `Optic` type lives in cell.ts so that file stays import-free and its
11
+ // `Cell.through` can name it; this module is the constructor/algebra surface.
17
12
  function make(get, put, readsSource) {
18
13
  return {
19
14
  get,
@@ -1,11 +1,7 @@
1
- // optics.ts — plain-record field optics over a `Cell<T>`.
2
- //
3
- // `fieldOf` / `fieldLens` (cell.ts) project a value-class field and need the
4
- // field's Cell constructor, so `Vec.x` comes back as a typed `Num` carrying its
5
- // domain methods. For a plain record you just want `Cell<T[K]>` with the same
6
- // spread-replace put — `at` / `fields` supply that with full key inference and
7
- // no constructor argument. Both are thin sugar over `fieldOf` with the base
8
- // `Cell` as the result type.
1
+ // Plain-record field optics over a `Cell<T>`. Unlike `fieldOf`/`fieldLens`
2
+ // (cell.ts), which need the field's Cell constructor to return a typed value
3
+ // class, `at`/`fields` return a base `Cell<T[K]>` with full key inference and no
4
+ // constructor argument thin sugar over `fieldOf` for plain records.
9
5
  import { Cell, fieldOf } from "./cell.js";
10
6
  export function at(c, key) {
11
7
  const ctor = Cell;
@@ -5,6 +5,5 @@ export type Store<T> = Writable<Cell<T>> & (T extends readonly any[] ? unknown :
5
5
  [K in keyof T]-?: Store<T[K]>;
6
6
  } : unknown);
7
7
  /** Deep, lens-backed store view of `cell`. Field access returns a nested `Store`;
8
- * write through `.value` at any depth. Works over any writable cell, including a
9
- * lens, so the store stays one source of truth with the rest of the graph. */
8
+ * write through `.value` at any depth. */
10
9
  export declare function store<T>(cell: Writable<Cell<T>>): Store<T>;
@@ -1,17 +1,10 @@
1
- // store.ts a lens-backed deep store proxy over a `Cell`.
1
+ // Deep store proxy over a `Cell`: `store(cell).a.b` is a chain of `at` field
2
+ // lenses, so it's an ordinary `Cell` (read/write `.value`, bind in JSX, compose
3
+ // with the graph). Writes are spread-replace puts back to the root.
2
4
  //
3
- // Signal libraries grow a separate `store` primitive for nested objects:
4
- // `store.user.name` reads a path, `setStore(...)` writes one. Here that's just
5
- // field lenses (`at`) under a recursive proxy — no second reactive primitive, no
6
- // fine-grained store engine. `store(cell).a.b` is a `Cell` (a chain of `at`
7
- // lenses), so it composes with everything else: read `.value`, write `.value`,
8
- // pass it to a component, bind it in JSX. Writes are spread-replace puts straight
9
- // back to the root cell, so the whole tree stays one source of truth.
10
- //
11
- // Navigation yields Stores; the leaf is written through `.value`
12
- // (`store.user.name.value = "x"`). A property whose name collides with the cell
13
- // surface below (`value`, `peek`, `lens`, `derive`, `merge`, `through`) resolves
14
- // to the cell member, not a field — drop to `at(cell, key)` for such a field.
5
+ // A field whose name collides with the cell surface (`value`, `peek`, `lens`,
6
+ // `derive`, `merge`, `through`) resolves to the cell member use `at(cell, key)`
7
+ // to reach such a field.
15
8
  import { at } from "./optics.js";
16
9
  // Cell members forwarded to the underlying cell rather than treated as a field,
17
10
  // plus the object protocol methods JS itself may touch.
@@ -78,8 +71,7 @@ function wrap(cell) {
78
71
  return proxy;
79
72
  }
80
73
  /** Deep, lens-backed store view of `cell`. Field access returns a nested `Store`;
81
- * write through `.value` at any depth. Works over any writable cell, including a
82
- * lens, so the store stays one source of truth with the rest of the graph. */
74
+ * write through `.value` at any depth. */
83
75
  export function store(cell) {
84
76
  return wrap(cell);
85
77
  }
@@ -41,13 +41,10 @@ export interface TraitDict<T> {
41
41
  }
42
42
  /** Valid keys of `TraitDict`. The set of declarable traits. */
43
43
  export type TraitKey = keyof TraitDict<unknown>;
44
- /** "A reactive whose class declares the listed traits." `_t` is a
45
- * phantom slot typed against `typeof Cls.traits`; listed keys must
46
- * resolve to non-null. Pure constraint — doesn't imply `Cell<T>`;
47
- * intersect with `Writable<Cell<T>>` / `Read<T>` for capability.
48
- *
49
- * function spring<T>(sig: Traits<T, "linear" | "metric">, target: Val<T>)
50
- * function tween<T>(sig: Traits<T, "lerp">, target: T, dur: Val<number>) */
44
+ /** "A reactive whose class declares the listed traits." `_t` is a phantom slot
45
+ * typed against `typeof Cls.traits`; listed keys must resolve to non-null. Pure
46
+ * constraint — doesn't imply `Cell<T>`; intersect with `Writable<Cell<T>>` /
47
+ * `Read<T>` for capability. */
51
48
  export type Traits<T, K extends TraitKey = never> = {
52
49
  /** @internal Phantom slot; never accessed at runtime. */
53
50
  readonly _t: {
@@ -1,19 +1,15 @@
1
- // Traits — polymorphic interfaces declared on the class via a single
2
- // `static traits = { … }` dictionary. One nominal constraint type
3
- // `Traits<T, K>` covers any combination of required traits via a union
4
- // of keys (no per-trait alias proliferation):
1
+ // Traits — polymorphic interfaces declared on a class via a single
2
+ // `static traits = { … }` dictionary. One constraint type `Traits<T, K>` covers
3
+ // any combination of required traits via a union of keys (no per-trait alias
4
+ // proliferation):
5
5
  //
6
6
  // function spring<T>(sig: Traits<T, "linear" | "metric">, target: Val<T>) …
7
7
  // function tween<T>(sig: Traits<T, "lerp">, target: T, dur: Val<number>) …
8
8
  //
9
- // The dictionary shape is `TraitDict<T>`; `Traits<T, K>` is the
10
- // constraint consumers see. Two separate axes:
11
- // - Type level: `Traits<T, K>` requires a phantom `_t` slot typed
12
- // against the class's static traits (`declare readonly _t: …`).
13
- // - Runtime: `requireLinear` & siblings walk `s.constructor.traits.*`.
14
- //
15
- // The engine (signal.ts) is trait-ignorant; subclasses thread equality
16
- // through `super(v, { equals })` themselves.
9
+ // Two axes: at the type level `Traits<T, K>` requires a phantom `_t` slot typed
10
+ // against the class's static traits; at runtime `requireLinear` & siblings walk
11
+ // `s.constructor.traits.*`. The engine itself is trait-ignorant subclasses
12
+ // thread equality through `super(v, { equals })`.
17
13
  /** Class-level traits dictionary for any Cell subclass. */
18
14
  const dictOf = (s) => s.constructor?.traits ?? {};
19
15
  const className = (s) => s.constructor?.name ?? "?";
@@ -1,7 +1,3 @@
1
- // anchor.ts — points on the unit box `[0,1]²` (registration on a shape).
2
- // dir.ts — unit direction vectors in `[-1,1]²` (displacement from rest).
3
- //
4
- // Plain constants — no reactive wrapping. Use as defaults / arguments.
5
1
  /** Anchor points on the unit box (`Center = {0.5, 0.5}`). */
6
2
  export const Anchor = {
7
3
  TopLeft: { x: 0, y: 0 },
@@ -0,0 +1,110 @@
1
+ import { Cell, type Read, type Writable } from "../cell.js";
2
+ import { Num } from "./num.js";
3
+ /** The value of an `Arr<T>`: its element cells, in order. */
4
+ type Elems<T> = readonly Cell<T>[];
5
+ /** A predicate over an element cell; optional `assert(c)` makes the test pass
6
+ * by writing the cell. */
7
+ export interface CellPred<T> {
8
+ (c: Cell<T>): boolean;
9
+ assert?: (c: Cell<T>) => void;
10
+ }
11
+ export declare class Arr<T> extends Cell<Elems<T>> {
12
+ #private;
13
+ static traits: {
14
+ equals: <E>(a: readonly E[], b: readonly E[]) => boolean;
15
+ };
16
+ readonly _t: typeof Arr.traits;
17
+ constructor(items?: Elems<T>);
18
+ /** An `Arr` whose elements are derived from an external `source` and whose
19
+ * structural edits rewrite that source via `ops`. */
20
+ static fromSource<S, R>(source: Read<S>, getter: (s: S) => Elems<R>, ops: {
21
+ insert?: (v: R | Cell<R>, at?: number) => Cell<R>;
22
+ remove?: (e: Cell<R>) => void;
23
+ moveBefore?: (e: Cell<R>, anchor: Cell<R> | null) => void;
24
+ }): Arr<R>;
25
+ /** The element cells in order; tracked when read in an effect/derive. */
26
+ get cells(): Elems<T>;
27
+ /** Snapshot of element values, `readonly T[]`; re-derives on any value or
28
+ * structural change. Memoized per instance. */
29
+ get values(): Read<readonly T[]>;
30
+ /** Element count as a reactive `Num` (structural changes only). */
31
+ get length(): Num;
32
+ /** This element's index as a writable `Num`: reading gives its position,
33
+ * writing reorders (splices the reference to the rounded, clamped target).
34
+ * Writable on a base `Arr`; read-only over a derived view. */
35
+ indexOf(e: Cell<T>): Writable<Num>;
36
+ /** Append `v` (a value is wrapped in a fresh cell; a cell passes through).
37
+ * Returns the element cell. */
38
+ push(v: T | Cell<T>): Cell<T>;
39
+ /** Insert at `at` (default: end). On a view, delegates through the view's
40
+ * `insert` (a filter asserts its predicate; `map` has none, so it throws). */
41
+ insert(v: T | Cell<T>, at?: number): Cell<T>;
42
+ /** Remove element `e` (by reference). On a view, delegates through the
43
+ * view's `remove`. */
44
+ remove(e: Cell<T>): void;
45
+ /** Remove the element at index `i` in this (view's) order. */
46
+ removeAt(i: number): void;
47
+ /** Move `e` to index `to` (rounded, clamped) in this (view's) order. */
48
+ move(e: Cell<T>, to: number): void;
49
+ /** Splice `e` to just before `anchor` (or to the end when `anchor` is null).
50
+ * On a base `Arr` this rewrites the reference order; on a view it delegates
51
+ * through the view's `moveBefore`, where the shared `anchor` cell stays
52
+ * meaningful in the base order. No-op if `e` isn't present. */
53
+ moveBefore(e: Cell<T>, anchor: Cell<T> | null): void;
54
+ /** Make `e` appear in this view: insert into the base if absent, then assert
55
+ * each view's constraint up the chain. */
56
+ assertContains(e: Cell<T>): void;
57
+ /** Empty the collection (a view removes only its visible elements). */
58
+ clear(): void;
59
+ /** Subset whose elements pass `pred` (which reads element values, so the
60
+ * view re-derives reactively). Shares the element cells. `insert` adds to
61
+ * the parent and asserts the predicate. */
62
+ filter(pred: CellPred<T>): Arr<T>;
63
+ /** Projection ordered by `key` (read off each element, so it re-sorts
64
+ * reactively). Shares the element cells; structural edits delegate to the
65
+ * base (a `move`/`insert` repositions in the base order, not the view's). */
66
+ sortBy(key: (c: Cell<T>) => number): Arr<T>;
67
+ /** Per-element map. `f` projects each element value; with the inverse `g`
68
+ * the mapped elements are writable lenses (editing one writes the source
69
+ * cell), else they're read-only. Element identity is stable: one lens per
70
+ * source cell, memoized. */
71
+ map<U>(f: (v: T) => U, g?: (u: U, v: T) => T): Arr<U>;
72
+ /** Partition by a per-element key field into derived sub-`Arr`s, one writable
73
+ * filter view per bucket. `move(e, key, index)` writes the key field and
74
+ * splices the base so `e` lands at `index` in its new group. `order` seeds
75
+ * empty buckets and pins their order. */
76
+ groupBy<K>(field: (e: Cell<T>) => Writable<Cell<K>>, opts?: {
77
+ order?: readonly K[];
78
+ }): GroupArr<K, T>;
79
+ }
80
+ /** One bucket of a `groupBy`: its key and the derived sub-`Arr` of members. */
81
+ export interface Group<K, T> {
82
+ key: K;
83
+ items: Arr<T>;
84
+ }
85
+ /** The result of `Arr.groupBy`: derived buckets plus a grouped `move`/`insert`
86
+ * whose backward pass writes the group field and reorders the base. */
87
+ export declare class GroupArr<K, T> {
88
+ #private;
89
+ /** The buckets in order; tracked when read in an effect/derive. */
90
+ readonly groups: Read<readonly Group<K, T>[]>;
91
+ constructor(parent: Arr<T>, field: (e: Cell<T>) => Writable<Cell<K>>, order: readonly K[]);
92
+ get value(): readonly Group<K, T>[];
93
+ map<F>(f: (g: Group<K, T>) => F): Read<readonly F[]>;
94
+ /** Place `e` in group `key` at `index` (within that group's order). Asserts
95
+ * every upstream filter, writes the group field, and splices the base — one
96
+ * batch, so every view re-flows once. Omit `index` to keep base order. */
97
+ move(e: Cell<T>, key: K, index?: number): void;
98
+ /** Alias of `move`. */
99
+ insert(e: Cell<T>, key: K, index?: number): void;
100
+ /** Remove `e` from the source through the chain. */
101
+ remove(e: Cell<T>): void;
102
+ }
103
+ /** `field === value`, assertable by writing the field; for `filter` predicates. */
104
+ export declare function is<T, V>(field: (e: Cell<T>) => Writable<Cell<V>>, value: V): CellPred<T>;
105
+ /** Conjunction of cell predicates; asserts every clause. */
106
+ export declare function allPass<T>(...preds: readonly CellPred<T>[]): CellPred<T>;
107
+ /** Writable `Arr<T>` from values and/or cells (values are wrapped in fresh
108
+ * cells; cells pass through by identity). */
109
+ export declare function arr<T>(items?: Iterable<T | Cell<T>>): Writable<Arr<T>>;
110
+ export {};
@@ -0,0 +1,336 @@
1
+ import { batch, Cell, cell, derive, lazy, lens } from "../cell.js";
2
+ import { Num } from "./num.js";
3
+ /** Structural equality: same element cells (by reference) in the same order; a
4
+ * value change inside an element doesn't affect it. */
5
+ const sameCells = (a, b) => a === b || (a.length === b.length && a.every((c, i) => c === b[i]));
6
+ const clampInt = (v, lo, hi) => {
7
+ const r = Math.round(v);
8
+ return r < lo ? lo : r > hi ? hi : r;
9
+ };
10
+ const views = new WeakMap();
11
+ export class Arr extends Cell {
12
+ static traits = {
13
+ equals: sameCells,
14
+ };
15
+ constructor(items = []) {
16
+ super(items, { equals: sameCells });
17
+ }
18
+ /** Build a derived `Arr` over any `Read` source. Variance escape: `Cell<R>`
19
+ * isn't assignable to `Cell<unknown>` (contravariant `_equals`), so typed
20
+ * `Arr.derive` can't see element subtyping. */
21
+ static #view(parent, getter) {
22
+ // biome-ignore lint/suspicious/noExplicitAny: variance escape (see above)
23
+ return Arr.derive(parent, getter);
24
+ }
25
+ /** An `Arr` whose elements are derived from an external `source` and whose
26
+ * structural edits rewrite that source via `ops`. */
27
+ static fromSource(source, getter, ops) {
28
+ const view = Arr.#view(source, getter);
29
+ views.set(view, {
30
+ insert: ops.insert,
31
+ remove: ops.remove,
32
+ moveBefore: ops.moveBefore,
33
+ });
34
+ return view;
35
+ }
36
+ // ── reads ────────────────────────────────────────────────────────────
37
+ /** The element cells in order; tracked when read in an effect/derive. */
38
+ get cells() {
39
+ return this.value;
40
+ }
41
+ /** Snapshot of element values, `readonly T[]`; re-derives on any value or
42
+ * structural change. Memoized per instance. */
43
+ get values() {
44
+ return lazy(this, "values", () => derive(this, cs => cs.map(c => c.value)));
45
+ }
46
+ /** Element count as a reactive `Num` (structural changes only). */
47
+ get length() {
48
+ return lazy(this, "length", () => Num.derive(this, cs => cs.length));
49
+ }
50
+ /** This element's index as a writable `Num`: reading gives its position,
51
+ * writing reorders (splices the reference to the rounded, clamped target).
52
+ * Writable on a base `Arr`; read-only over a derived view. */
53
+ indexOf(e) {
54
+ return Num.lens(this, cs => cs.indexOf(e), (to, cs) => {
55
+ const from = cs.indexOf(e);
56
+ if (from < 0)
57
+ return cs;
58
+ const others = cs.filter(x => x !== e);
59
+ const t = clampInt(to, 0, others.length);
60
+ if (t === from)
61
+ return cs;
62
+ return [...others.slice(0, t), e, ...others.slice(t)];
63
+ });
64
+ }
65
+ // ── structural edits ────────────────────────────────────────────────
66
+ /** Append `v` (a value is wrapped in a fresh cell; a cell passes through).
67
+ * Returns the element cell. */
68
+ push(v) {
69
+ return this.insert(v);
70
+ }
71
+ /** Insert at `at` (default: end). On a view, delegates through the view's
72
+ * `insert` (a filter asserts its predicate; `map` has none, so it throws). */
73
+ insert(v, at) {
74
+ const info = views.get(this);
75
+ if (info) {
76
+ if (!info.insert)
77
+ throw new TypeError("Arr: this view does not support insert");
78
+ return info.insert(v, at);
79
+ }
80
+ const e = (v instanceof Cell ? v : cell(v));
81
+ const cur = this.peek();
82
+ const next = cur.slice();
83
+ if (at == null || at >= next.length)
84
+ next.push(e);
85
+ else
86
+ next.splice(Math.max(0, at), 0, e);
87
+ this._writeSource(next);
88
+ return e;
89
+ }
90
+ /** Remove element `e` (by reference). On a view, delegates through the
91
+ * view's `remove`. */
92
+ remove(e) {
93
+ const info = views.get(this);
94
+ if (info) {
95
+ if (!info.remove)
96
+ throw new TypeError("Arr: this view does not support remove");
97
+ info.remove(e);
98
+ return;
99
+ }
100
+ const cur = this.peek();
101
+ const next = cur.filter(x => x !== e);
102
+ if (next.length !== cur.length)
103
+ this._writeSource(next);
104
+ }
105
+ /** Remove the element at index `i` in this (view's) order. */
106
+ removeAt(i) {
107
+ const e = this.peek()[i];
108
+ if (e)
109
+ this.remove(e);
110
+ }
111
+ /** Move `e` to index `to` (rounded, clamped) in this (view's) order. */
112
+ move(e, to) {
113
+ const cur = this.peek();
114
+ const others = cur.filter(x => x !== e);
115
+ const t = clampInt(to, 0, others.length);
116
+ this.moveBefore(e, t < others.length ? others[t] : null);
117
+ }
118
+ /** Splice `e` to just before `anchor` (or to the end when `anchor` is null).
119
+ * On a base `Arr` this rewrites the reference order; on a view it delegates
120
+ * through the view's `moveBefore`, where the shared `anchor` cell stays
121
+ * meaningful in the base order. No-op if `e` isn't present. */
122
+ moveBefore(e, anchor) {
123
+ const info = views.get(this);
124
+ if (info) {
125
+ if (!info.moveBefore)
126
+ throw new TypeError("Arr: this view does not support move");
127
+ info.moveBefore(e, anchor);
128
+ return;
129
+ }
130
+ const cur = this.peek();
131
+ if (!cur.includes(e))
132
+ return;
133
+ const without = cur.filter(x => x !== e);
134
+ const ai = anchor == null ? -1 : without.indexOf(anchor);
135
+ const at = ai < 0 ? without.length : ai;
136
+ const next = [...without.slice(0, at), e, ...without.slice(at)];
137
+ if (!sameCells(next, cur))
138
+ this._writeSource(next);
139
+ }
140
+ /** Make `e` appear in this view: insert into the base if absent, then assert
141
+ * each view's constraint up the chain. */
142
+ assertContains(e) {
143
+ const info = views.get(this);
144
+ if (info) {
145
+ info.assertContains?.(e);
146
+ return;
147
+ }
148
+ if (!this.peek().includes(e))
149
+ this.insert(e);
150
+ }
151
+ /** Empty the collection (a view removes only its visible elements). */
152
+ clear() {
153
+ if (views.get(this)) {
154
+ for (const e of [...this.peek()])
155
+ this.remove(e);
156
+ return;
157
+ }
158
+ if (this.peek().length > 0)
159
+ this._writeSource([]);
160
+ }
161
+ // ── views ────────────────────────────────────────────────────────────
162
+ /** Subset whose elements pass `pred` (which reads element values, so the
163
+ * view re-derives reactively). Shares the element cells. `insert` adds to
164
+ * the parent and asserts the predicate. */
165
+ filter(pred) {
166
+ const base = this;
167
+ const view = Arr.#view(base, cs => cs.filter(c => pred(c)));
168
+ views.set(view, {
169
+ insert: (v, at) => {
170
+ const e = base.insert(v, at);
171
+ pred.assert?.(e);
172
+ return e;
173
+ },
174
+ remove: e => base.remove(e),
175
+ moveBefore: (e, anchor) => base.moveBefore(e, anchor),
176
+ assertContains: e => {
177
+ base.assertContains(e);
178
+ pred.assert?.(e);
179
+ },
180
+ });
181
+ return view;
182
+ }
183
+ /** Projection ordered by `key` (read off each element, so it re-sorts
184
+ * reactively). Shares the element cells; structural edits delegate to the
185
+ * base (a `move`/`insert` repositions in the base order, not the view's). */
186
+ sortBy(key) {
187
+ const base = this;
188
+ const view = Arr.#view(base, cs => [...cs].sort((a, b) => key(a) - key(b)));
189
+ views.set(view, {
190
+ insert: (v, at) => base.insert(v, at),
191
+ remove: e => base.remove(e),
192
+ moveBefore: (e, anchor) => base.moveBefore(e, anchor),
193
+ assertContains: e => base.assertContains(e),
194
+ });
195
+ return view;
196
+ }
197
+ /** Per-element map. `f` projects each element value; with the inverse `g`
198
+ * the mapped elements are writable lenses (editing one writes the source
199
+ * cell), else they're read-only. Element identity is stable: one lens per
200
+ * source cell, memoized. */
201
+ map(f, g) {
202
+ const fwd = new WeakMap();
203
+ const back = new WeakMap();
204
+ const lensOf = (c) => {
205
+ let m = fwd.get(c);
206
+ if (m === undefined) {
207
+ m = (g ? lens(c, f, (u, v) => g(u, v)) : derive(c, f));
208
+ fwd.set(c, m);
209
+ back.set(m, c);
210
+ }
211
+ return m;
212
+ };
213
+ const base = this;
214
+ const view = Arr.#view(base, cs => cs.map(lensOf));
215
+ views.set(view, {
216
+ remove: mc => {
217
+ const src = back.get(mc);
218
+ if (src)
219
+ base.remove(src);
220
+ },
221
+ moveBefore: (mc, anchor) => {
222
+ const src = back.get(mc);
223
+ const a = anchor ? (back.get(anchor) ?? null) : null;
224
+ if (src)
225
+ base.moveBefore(src, a);
226
+ },
227
+ assertContains: mc => {
228
+ const src = back.get(mc);
229
+ if (src)
230
+ base.assertContains(src);
231
+ },
232
+ });
233
+ return view;
234
+ }
235
+ /** Partition by a per-element key field into derived sub-`Arr`s, one writable
236
+ * filter view per bucket. `move(e, key, index)` writes the key field and
237
+ * splices the base so `e` lands at `index` in its new group. `order` seeds
238
+ * empty buckets and pins their order. */
239
+ groupBy(field, opts = {}) {
240
+ return new GroupArr(this, field, opts.order ?? []);
241
+ }
242
+ }
243
+ /** The result of `Arr.groupBy`: derived buckets plus a grouped `move`/`insert`
244
+ * whose backward pass writes the group field and reorders the base. */
245
+ export class GroupArr {
246
+ #parent;
247
+ #field;
248
+ #order;
249
+ #subs = new Map();
250
+ /** The buckets in order; tracked when read in an effect/derive. */
251
+ groups;
252
+ constructor(parent, field, order) {
253
+ this.#parent = parent;
254
+ this.#field = field;
255
+ this.#order = order;
256
+ this.groups = derive(parent, cells => this.#bucket(cells));
257
+ }
258
+ #bucket(cells) {
259
+ const keys = [...this.#order];
260
+ const seen = new Set(keys);
261
+ for (const c of cells) {
262
+ const k = this.#field(c).value;
263
+ if (!seen.has(k)) {
264
+ seen.add(k);
265
+ keys.push(k);
266
+ }
267
+ }
268
+ return keys.map(key => ({ key, items: this.#sub(key) }));
269
+ }
270
+ /** One memoized filter view per key — stable identity across re-buckets. */
271
+ #sub(key) {
272
+ let s = this.#subs.get(key);
273
+ if (s === undefined) {
274
+ const pred = c => Object.is(this.#field(c).value, key);
275
+ pred.assert = c => {
276
+ this.#field(c).value = key;
277
+ };
278
+ s = this.#parent.filter(pred);
279
+ this.#subs.set(key, s);
280
+ }
281
+ return s;
282
+ }
283
+ get value() {
284
+ return this.groups.value;
285
+ }
286
+ map(f) {
287
+ return derive(this.groups, gs => gs.map(f));
288
+ }
289
+ /** Place `e` in group `key` at `index` (within that group's order). Asserts
290
+ * every upstream filter, writes the group field, and splices the base — one
291
+ * batch, so every view re-flows once. Omit `index` to keep base order. */
292
+ move(e, key, index) {
293
+ batch(() => {
294
+ this.#parent.assertContains(e);
295
+ this.#field(e).value = key;
296
+ if (index != null) {
297
+ const members = this.#parent
298
+ .peek()
299
+ .filter(c => c !== e && Object.is(this.#field(c).peek(), key));
300
+ const t = index < 0 ? 0 : index > members.length ? members.length : index;
301
+ this.#parent.moveBefore(e, t < members.length ? members[t] : null);
302
+ }
303
+ });
304
+ }
305
+ /** Alias of `move`. */
306
+ insert(e, key, index) {
307
+ this.move(e, key, index);
308
+ }
309
+ /** Remove `e` from the source through the chain. */
310
+ remove(e) {
311
+ this.#parent.remove(e);
312
+ }
313
+ }
314
+ /** `field === value`, assertable by writing the field; for `filter` predicates. */
315
+ export function is(field, value) {
316
+ const p = ((c) => Object.is(field(c).value, value));
317
+ p.assert = c => {
318
+ field(c).value = value;
319
+ };
320
+ return p;
321
+ }
322
+ /** Conjunction of cell predicates; asserts every clause. */
323
+ export function allPass(...preds) {
324
+ const p = ((c) => preds.every(q => q(c)));
325
+ p.assert = c => {
326
+ for (const q of preds)
327
+ q.assert?.(c);
328
+ };
329
+ return p;
330
+ }
331
+ /** Writable `Arr<T>` from values and/or cells (values are wrapped in fresh
332
+ * cells; cells pass through by identity). */
333
+ export function arr(items = []) {
334
+ const cells = [...items].map(x => (x instanceof Cell ? x : cell(x)));
335
+ return new Arr(cells);
336
+ }
@@ -1,13 +1,13 @@
1
1
  import { Cell, type Init, type Val, type Writable } from "../cell.js";
2
2
  import { Num } from "./num.js";
3
- /** Clip header. The graph compares `epoch`; `pcm` is one channel buffer each. */
3
+ /** Clip header; the graph compares `epoch`. One `pcm` buffer per channel. */
4
4
  export interface AudioClip {
5
5
  readonly pcm: readonly Float32Array[];
6
6
  readonly sampleRate: number;
7
7
  readonly epoch: number;
8
8
  }
9
9
  type V = AudioClip;
10
- /** Stamp channel buffers with a fresh epoch — the only way to mint a value. */
10
+ /** Stamp buffers with a fresh epoch — the only way to mint a `Clip` value. */
11
11
  export declare const stamp: (pcm: readonly Float32Array[], sampleRate: number) => V;
12
12
  export declare const equals: (a: V, b: V) => boolean;
13
13
  export declare class Audio extends Cell<V> {
@@ -16,18 +16,17 @@ export declare class Audio extends Cell<V> {
16
16
  };
17
17
  readonly _t: typeof Audio.traits;
18
18
  constructor(v?: V);
19
- /** Time-reverse every channel. Involution. */
19
+ /** Time-reverse every channel. */
20
20
  reverse(): this;
21
- /** Scalar gain. Invertible while k ≠ 0 — the audio twin of Canvas.brightness. */
21
+ /** Scalar gain. Invertible while k ≠ 0. */
22
22
  gain(k: Val<number>): this;
23
- /** Peak-normalize to reactive `target` (default 1). The view alone can't
24
- * know the source's loudness, so the complement carries the original peak and
25
- * the backward pass restores it. The audio analog of str.lowercase(). */
23
+ /** Peak-normalize to `target` (default 1). The complement stores the original
24
+ * peak so a write-back restores it. */
26
25
  normalize(target?: Val<number>): Writable<Audio>;
27
26
  /** RMS loudness as a writable `Num`; writing rescales the clip to hit it. */
28
27
  rms(): Writable<Num>;
29
28
  }
30
- /** Writable `Audio`. A `Clip` seeds a fresh cell; an existing `Writable<Audio>`
31
- * passes through by identity. */
29
+ /** Writable `Audio` from a `Clip` (new cell) or existing writable (passed
30
+ * through). */
32
31
  export declare function audio(v: Init<Audio>): Writable<Audio>;
33
32
  export {};