@msb235/blits 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +31 -7
  3. package/dist/bare.js +7 -1
  4. package/dist/book.d.ts +7 -0
  5. package/dist/book.js +35 -4
  6. package/dist/chain.d.ts +2 -1
  7. package/dist/chain.js +7 -3
  8. package/dist/channels.d.ts +12 -19
  9. package/dist/channels.js +18 -118
  10. package/dist/clock.d.ts +9 -2
  11. package/dist/clock.js +10 -2
  12. package/dist/closed.d.ts +15 -0
  13. package/dist/closed.js +144 -0
  14. package/dist/color.d.ts +61 -0
  15. package/dist/color.js +229 -0
  16. package/dist/columns.js +2 -1
  17. package/dist/crowd.d.ts +1 -1
  18. package/dist/crowd.js +4 -1
  19. package/dist/cue.d.ts +2 -0
  20. package/dist/cue.js +78 -27
  21. package/dist/due.js +3 -0
  22. package/dist/fade.d.ts +13 -0
  23. package/dist/fade.js +55 -4
  24. package/dist/fill.d.ts +1 -1
  25. package/dist/fill.js +15 -8
  26. package/dist/fit.d.ts +144 -0
  27. package/dist/fit.js +181 -0
  28. package/dist/fold.d.ts +3 -3
  29. package/dist/fold.js +18 -11
  30. package/dist/gather.d.ts +6 -0
  31. package/dist/gather.js +18 -0
  32. package/dist/handle.d.ts +27 -2
  33. package/dist/handle.js +60 -11
  34. package/dist/held.js +4 -0
  35. package/dist/history.d.ts +15 -2
  36. package/dist/history.js +54 -9
  37. package/dist/hosts.d.ts +2 -2
  38. package/dist/hosts.js +18 -5
  39. package/dist/index.d.ts +8 -2
  40. package/dist/index.js +5 -1
  41. package/dist/lane.d.ts +1 -1
  42. package/dist/lanes.d.ts +32 -9
  43. package/dist/lanes.js +53 -24
  44. package/dist/locus.d.ts +3 -3
  45. package/dist/locus.js +1 -1
  46. package/dist/marks.d.ts +3 -0
  47. package/dist/marks.js +80 -12
  48. package/dist/meet.d.ts +3 -2
  49. package/dist/meet.js +15 -3
  50. package/dist/mixer.d.ts +52 -36
  51. package/dist/mixer.js +148 -86
  52. package/dist/motion.d.ts +2 -199
  53. package/dist/motion.js +2 -652
  54. package/dist/motions.d.ts +266 -0
  55. package/dist/motions.js +724 -0
  56. package/dist/move.d.ts +1 -1
  57. package/dist/move.js +23 -7
  58. package/dist/owner.d.ts +1 -1
  59. package/dist/owner.js +3 -3
  60. package/dist/pace.d.ts +2 -0
  61. package/dist/pace.js +8 -0
  62. package/dist/pack.d.ts +31 -0
  63. package/dist/pack.js +65 -0
  64. package/dist/paging.d.ts +63 -0
  65. package/dist/paging.js +110 -0
  66. package/dist/patch.d.ts +3 -0
  67. package/dist/patch.js +2 -0
  68. package/dist/place.js +74 -25
  69. package/dist/project.d.ts +7 -4
  70. package/dist/project.js +117 -78
  71. package/dist/pull.js +4 -3
  72. package/dist/qualify.js +4 -2
  73. package/dist/revive.d.ts +58 -0
  74. package/dist/revive.js +158 -0
  75. package/dist/sample.d.ts +1 -1
  76. package/dist/sample.js +6 -1
  77. package/dist/seek.d.ts +3 -0
  78. package/dist/seek.js +311 -0
  79. package/dist/spans.d.ts +29 -0
  80. package/dist/spans.js +165 -0
  81. package/dist/strays.d.ts +1 -1
  82. package/dist/strays.js +21 -0
  83. package/dist/tape.d.ts +21 -0
  84. package/dist/tape.js +65 -0
  85. package/dist/ticker.d.ts +28 -3
  86. package/dist/ticker.js +3 -0
  87. package/dist/transport.d.ts +121 -0
  88. package/dist/transport.js +271 -0
  89. package/dist/types.d.ts +404 -38
  90. package/dist/unpage.d.ts +8 -0
  91. package/dist/unpage.js +163 -0
  92. package/dist/voice.d.ts +73 -7
  93. package/dist/voice.js +116 -4
  94. package/dist/weigh.js +2 -2
  95. package/docs/schema.html +120 -31
  96. package/package.json +2 -1
package/dist/handle.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FadeOptions, Handle } from './types.js';
1
+ import type { FadeOptions, FitResult, Handle } from './types.js';
2
2
  import type { Voice } from './voice.js';
3
3
  /** What a voice's handle asks of the mix that cued it: one per mix, shared by every handle. */
4
4
  export interface HandleHost<I, O> {
@@ -9,17 +9,39 @@ export interface HandleHost<I, O> {
9
9
  /** The voice's clock jumped: for an owner, so did every clock it holds. */
10
10
  sought(voice: Voice<I, O>): void;
11
11
  fade(voice: Voice<I, O>, opts: FadeOptions<I> | undefined): void;
12
+ rise(voice: Voice<I, O>, opts: {
13
+ over?: number;
14
+ } | undefined): void;
12
15
  weightOf(voice: Voice<I, O>, subject: I): number;
16
+ /** Records a write the host made, which `again` makes once more where a seek replays it. */
17
+ record(label: string, again: () => void): void;
13
18
  }
14
- /** A voice's handle: the host's only way to control a voice once cued. */
19
+ /** What a handle answers for a voice a history store paged out, which left long ago. */
20
+ export interface Gone {
21
+ weight: number;
22
+ played: boolean;
23
+ }
24
+ /**
25
+ * A voice's handle: the host's only way to control a voice once cued. A voice a history store
26
+ * pages out is let go of here too, so the handle answers as a gone voice's does, and a seek that
27
+ * revives it puts the revived voice behind the same handle.
28
+ */
15
29
  export declare class VoiceHandle<I, O> implements Handle<I> {
16
30
  #private;
17
31
  readonly id: number;
18
32
  constructor(voice: Voice<I, O>, host: HandleHost<I, O>);
33
+ /** The voice behind a handle; null while a history store holds it. */
34
+ static voiceOf<I, O>(h: VoiceHandle<I, O>): Voice<I, O> | null;
35
+ /** Lets go of a voice a history store paged out. */
36
+ static page<I, O>(h: VoiceHandle<I, O>, gone: Gone): void;
37
+ /** Puts a voice revived from a history store behind the handle it had. */
38
+ static rehome<I, O>(h: VoiceHandle<I, O>, voice: Voice<I, O>): void;
19
39
  get state(): Handle<I>['state'];
20
40
  get owner(): Handle<I> | undefined;
21
41
  get played(): Promise<boolean>;
22
42
  get done(): Promise<void>;
43
+ /** How a span's children were last fitted; undefined for any voice but a span. */
44
+ get result(): FitResult | undefined;
23
45
  get weight(): number;
24
46
  set weight(w: number);
25
47
  get rate(): number;
@@ -27,5 +49,8 @@ export declare class VoiceHandle<I, O> implements Handle<I> {
27
49
  ramp(r: number, over: number): void;
28
50
  seek(elapsed: number): void;
29
51
  fade(opts?: FadeOptions<I>): void;
52
+ rise(opts?: {
53
+ over?: number;
54
+ }): void;
30
55
  weightOf(subject: I): number;
31
56
  }
package/dist/handle.js CHANGED
@@ -1,56 +1,105 @@
1
1
  import { retime } from './clock.js';
2
- /** A voice's handle: the host's only way to control a voice once cued. */
2
+ /**
3
+ * A voice's handle: the host's only way to control a voice once cued. A voice a history store
4
+ * pages out is let go of here too, so the handle answers as a gone voice's does, and a seek that
5
+ * revives it puts the revived voice behind the same handle.
6
+ */
3
7
  export class VoiceHandle {
4
8
  id;
5
9
  #voice;
10
+ #gone = null;
6
11
  #host;
7
12
  constructor(voice, host) {
8
13
  this.id = voice.id;
9
14
  this.#voice = voice;
10
15
  this.#host = host;
11
16
  }
17
+ /** The voice behind a handle; null while a history store holds it. */
18
+ static voiceOf(h) {
19
+ return h.#voice;
20
+ }
21
+ /** Lets go of a voice a history store paged out. */
22
+ static page(h, gone) {
23
+ h.#voice = null;
24
+ h.#gone = gone;
25
+ }
26
+ /** Puts a voice revived from a history store behind the handle it had. */
27
+ static rehome(h, voice) {
28
+ h.#voice = voice;
29
+ h.#gone = null;
30
+ }
12
31
  get state() {
13
- return this.#voice.state;
32
+ return this.#voice?.state ?? 'done';
14
33
  }
15
34
  get owner() {
16
- return this.#voice.owner?.handle ?? undefined;
35
+ return this.#voice?.owner?.handle ?? undefined;
17
36
  }
18
37
  get played() {
19
- return this.#voice.played;
38
+ return this.#voice?.played ?? Promise.resolve(this.#gone?.played ?? false);
20
39
  }
21
40
  get done() {
22
- return this.#voice.done;
41
+ return this.#voice?.done ?? Promise.resolve();
42
+ }
43
+ /** How a span's children were last fitted; undefined for any voice but a span. */
44
+ get result() {
45
+ return this.#voice?.fitting?.report;
23
46
  }
24
47
  get weight() {
25
- return this.#voice.weight;
48
+ return this.#voice?.weight ?? this.#gone?.weight ?? 0;
26
49
  }
50
+ // A write to a paged voice changes nothing: it had left when the host made it, as it has on replay.
27
51
  set weight(w) {
28
- this.#voice.weight = w;
29
- this.#host.changed(this.#voice);
52
+ const voice = this.#voice;
53
+ if (voice === null)
54
+ return;
55
+ voice.weight = w;
56
+ this.#host.changed(voice);
57
+ this.#host.record('weight', () => {
58
+ this.weight = w;
59
+ });
30
60
  }
31
61
  get rate() {
32
- return this.#voice.rateAt(this.#host.nowFor(this.#voice));
62
+ const voice = this.#voice;
63
+ return voice === null ? 0 : voice.rateAt(this.#host.nowFor(voice));
33
64
  }
34
65
  set rate(r) {
35
66
  this.ramp(r, 0);
36
67
  }
37
68
  ramp(r, over) {
38
69
  const voice = this.#voice;
70
+ if (voice === null)
71
+ return;
39
72
  retime(voice, this.#host.nowFor(voice), r, over);
40
73
  this.#host.changed(voice);
74
+ this.#host.record('rate', () => this.ramp(r, over));
41
75
  }
42
76
  seek(elapsed) {
43
77
  const voice = this.#voice;
78
+ if (voice === null)
79
+ return;
44
80
  voice.rebase(this.#host.nowFor(voice));
45
81
  voice.anchorElapsed = elapsed;
46
82
  voice.seeks++;
47
83
  this.#host.sought(voice);
48
84
  this.#host.changed(voice);
85
+ this.#host.record('seek', () => this.seek(elapsed));
49
86
  }
50
87
  fade(opts) {
51
- this.#host.fade(this.#voice, opts);
88
+ const voice = this.#voice;
89
+ if (voice === null)
90
+ return;
91
+ this.#host.fade(voice, opts);
92
+ this.#host.record('fade', () => this.fade(opts));
93
+ }
94
+ rise(opts) {
95
+ const voice = this.#voice;
96
+ if (voice === null)
97
+ return;
98
+ this.#host.rise(voice, opts);
99
+ this.#host.record('rise', () => this.rise(opts));
52
100
  }
53
101
  weightOf(subject) {
54
- return this.#host.weightOf(this.#voice, subject);
102
+ const voice = this.#voice;
103
+ return voice === null ? 0 : this.#host.weightOf(voice, subject);
55
104
  }
56
105
  }
package/dist/held.js CHANGED
@@ -49,6 +49,10 @@ export function held(voice, subject, now, slot) {
49
49
  voice.subjects.set(subject, none);
50
50
  return none;
51
51
  }
52
+ if (this.keys !== null) {
53
+ voice.keyed ??= new Set();
54
+ voice.keyed.add(this.keys.key(subject));
55
+ }
52
56
  const delay = voice.spec.stagger ? voice.spec.stagger(subject) : 0;
53
57
  const since = this.sinceOf(voice, delay);
54
58
  held = {
package/dist/history.d.ts CHANGED
@@ -17,6 +17,19 @@ export declare function copyHeld<I, O>(voice: Voice<I, O>, h: Subject<unknown>):
17
17
  /** Under `history` with `inputs`, copies the host fields any voice reads, once a frame, when they changed. */
18
18
  export declare function recordHost<I, O>(mix: Mixer<I, O>, now: number): void;
19
19
  /** Under `history` with `inputs`, keeps what an input signal read, each time it changes. */
20
- export declare function record<I, O>(mix: Mixer<I, O>, held: Subject<unknown>, value: number): void;
20
+ export declare function record<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>, value: number): void;
21
21
  /** Under `history`, keeps a copy of a stateful voice's record for this subject every so often. */
22
- export declare function remember<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, held: Subject<unknown>): void;
22
+ export declare function remember<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>): void;
23
+ /**
24
+ * Under history, keeps the record a subject had when it left a voice, faded out of it or dropped,
25
+ * for a seek or a read back to before then; without history, keeps nothing.
26
+ */
27
+ export declare function leave<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown> | undefined): void;
28
+ /** The record a subject had at mix time `t` and left a voice with after it, if it left after `t`. */
29
+ export declare function leftAt<I>(left: readonly {
30
+ subject: I;
31
+ at: number;
32
+ held: Subject<unknown>;
33
+ }[] | null, subject: I, t: number): Subject<unknown> | undefined;
34
+ /** The mix time a motion patch keeps a released subject from, and how far back, under history. */
35
+ export declare function releasing<I, O>(mix: Mixer<I, O>): [at?: number, reach?: number];
package/dist/history.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { elapsedWith } from './clock.js';
2
2
  import { clone } from './clone.js';
3
3
  import { schedule } from './due.js';
4
+ import { packHeld } from './pack.js';
5
+ import { pageOut } from './paging.js';
4
6
  import { none } from './voice.js';
5
7
  /**
6
8
  * The last entry taken before `t`, or at it where `inclusive` says so, as it does for a change a
@@ -61,7 +63,15 @@ export function noted(mix, voice) {
61
63
  while (drop + 1 < log.length && log[drop + 1].at <= reach)
62
64
  drop++;
63
65
  if (drop > 0)
64
- log.splice(0, drop);
66
+ pageOut(mix, 'controls', voice.id, undefined, log.splice(0, drop), copied, reach);
67
+ }
68
+ const copied = (e) => ({ ...e });
69
+ /** Takes off the front of a list kept by time what a read back to `reach` no longer needs. */
70
+ function older(list, reach) {
71
+ let n = 0;
72
+ while (n + 1 < list.length && list[n + 1].at <= reach)
73
+ n++;
74
+ return n === 0 ? [] : list.splice(0, n);
65
75
  }
66
76
  /** A copy of a subject's record that shares nothing a read could change. */
67
77
  export function copyHeld(voice, h) {
@@ -77,6 +87,7 @@ export function copyHeld(voice, h) {
77
87
  bands: h.bands === null ? null : h.bands.slice(),
78
88
  state: h.state === undefined ? undefined : patch.clone ? patch.clone(h.state) : clone(h.state),
79
89
  kept,
90
+ unkept: h.unkept?.map(clone),
80
91
  probed: Number.NaN,
81
92
  delta: null,
82
93
  phase: 0,
@@ -107,11 +118,10 @@ export function recordHost(mix, now) {
107
118
  return;
108
119
  log.push({ at: now, fields });
109
120
  const reach = now - mix.opts.history.ms;
110
- while (log.length > 1 && log[1].at <= reach)
111
- log.shift();
121
+ pageOut(mix, 'host', undefined, undefined, older(log, reach), (e) => e.fields, reach);
112
122
  }
113
123
  /** Under `history` with `inputs`, keeps what an input signal read, each time it changes. */
114
- export function record(mix, held, value) {
124
+ export function record(mix, voice, subject, held, value) {
115
125
  const history = mix.opts.history;
116
126
  if (!history?.inputs || mix.projecting)
117
127
  return;
@@ -125,11 +135,12 @@ export function record(mix, held, value) {
125
135
  }
126
136
  inputs.push({ at: mix.now, value });
127
137
  const reach = mix.now - history.ms;
128
- while (inputs.length > 1 && inputs[1].at <= reach)
129
- inputs.shift();
138
+ const out = older(inputs, reach);
139
+ if (out.length > 0)
140
+ pageOut(mix, 'input', voice.id, mix.keys?.key(subject), out, (e) => e.value, reach);
130
141
  }
131
142
  /** Under `history`, keeps a copy of a stateful voice's record for this subject every so often. */
132
- export function remember(mix, voice, held) {
143
+ export function remember(mix, voice, subject, held) {
133
144
  const history = mix.opts.history;
134
145
  if (!history || mix.projecting)
135
146
  return;
@@ -144,6 +155,40 @@ export function remember(mix, voice, held) {
144
155
  return;
145
156
  snaps.push({ at: mix.now, held: copyHeld(voice, held) });
146
157
  const reach = mix.now - history.ms;
147
- while (snaps.length > 1 && snaps[1].at <= reach)
148
- snaps.shift();
158
+ const out = older(snaps, reach);
159
+ if (out.length > 0)
160
+ pageOut(mix, 'snap', voice.id, mix.keys?.key(subject), out, (e) => packHeld(voice, e.held), reach);
161
+ }
162
+ /**
163
+ * Under history, keeps the record a subject had when it left a voice, faded out of it or dropped,
164
+ * for a seek or a read back to before then; without history, keeps nothing.
165
+ */
166
+ export function leave(mix, voice, subject, held) {
167
+ const history = mix.opts.history;
168
+ if (history === undefined || held === undefined)
169
+ return;
170
+ const reach = mix.now - history.ms;
171
+ const left = [];
172
+ for (const e of voice.left ?? [])
173
+ if (e.at >= reach)
174
+ left.push(e);
175
+ else if (mix.keys !== null)
176
+ pageOut(mix, 'left', voice.id, mix.keys.key(e.subject), [e], (x) => packHeld(voice, x.held), reach);
177
+ left.push({ subject, at: Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now, held });
178
+ voice.left = left;
179
+ }
180
+ /** The record a subject had at mix time `t` and left a voice with after it, if it left after `t`. */
181
+ export function leftAt(left, subject, t) {
182
+ let found;
183
+ for (const e of left ?? [])
184
+ if (e.at > t && Object.is(e.subject, subject) && (found === undefined || e.at < found.at))
185
+ found = e;
186
+ return found?.held;
187
+ }
188
+ /** The mix time a motion patch keeps a released subject from, and how far back, under history. */
189
+ export function releasing(mix) {
190
+ const history = mix.opts.history;
191
+ if (history === undefined || Number.isNaN(mix.now))
192
+ return [];
193
+ return [mix.now, mix.now - history.ms];
149
194
  }
package/dist/hosts.d.ts CHANGED
@@ -7,8 +7,8 @@ export declare function laneHost<I, O>(mix: Mixer<I, O>): LaneHost<I, O>;
7
7
  /**
8
8
  * Whether a voice's patch and spec can run on a lane, its channels aside. A voice whose patch has
9
9
  * kept state on a record through `setting.keep` is stateful from then on, and leaves its lane;
10
- * one that starts keeping partway through may advance that state once for one subject not
11
- * probed on the frame it starts, which declaring `state` avoids.
10
+ * one that starts keeping partway through may advance that state once for one subject probed the
11
+ * frame before it starts and not on that frame, which declaring `state` avoids.
12
12
  */
13
13
  export declare function fits<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>): boolean;
14
14
  /** A voice's patch kept state on a record: it is stateful from now on. */
package/dist/hosts.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Book } from './book.js';
2
+ import { numericOf } from './channels.js';
3
+ import { crowdable } from './crowd.js';
2
4
  import { schedule } from './due.js';
3
- import { beginFade, fadeSubject } from './fade.js';
5
+ import { beginFade, beginRise, fadeAt, fadeSubject } from './fade.js';
4
6
  import { VoiceHandle } from './handle.js';
5
7
  import { noted, remember } from './history.js';
6
8
  import { hostOf, listed, markOf, readingAt } from './marks.js';
@@ -8,6 +10,7 @@ import { nextFrame } from './move.js';
8
10
  import { descendants, ownWeight, signalled } from './owner.js';
9
11
  import { localNow } from './place.js';
10
12
  import { reading } from './reading.js';
13
+ import { record } from './tape.js';
11
14
  import { unreach } from './unreached.js';
12
15
  export function book(mix, opts) {
13
16
  if (mix.projecting)
@@ -42,6 +45,7 @@ export function laneHost(mix) {
42
45
  },
43
46
  channels: mix.channels,
44
47
  names: mix.names,
48
+ clock: mix,
45
49
  fits: (voice) => fits(mix, voice),
46
50
  meet: (voice, subject, head, slot) => {
47
51
  for (let held = head; held !== null; held = held.next)
@@ -56,6 +60,7 @@ export function laneHost(mix) {
56
60
  for (const voice of mix.gone)
57
61
  unreach(voice, slot, false);
58
62
  },
63
+ passes: (was, w) => mix.passes(was, w),
59
64
  naming: (subject) => (mix.naming === 0 ? undefined : mix.named.get(subject)),
60
65
  slotOf: (subject) => mix.chains.get(subject)?.slot ?? -1,
61
66
  envelope: (voice, since) => voice.owner === null
@@ -84,15 +89,15 @@ export function laneHost(mix) {
84
89
  reading.horizon = mix.horizonFor(voice, delay, mix.now);
85
90
  },
86
91
  keeps: mix.opts.history !== undefined,
87
- after: (voice, held) => remember(mix, voice, held),
92
+ after: (voice, subject, held) => remember(mix, voice, subject, held),
88
93
  kept: (voice) => stateful(mix, voice),
89
94
  };
90
95
  }
91
96
  /**
92
97
  * Whether a voice's patch and spec can run on a lane, its channels aside. A voice whose patch has
93
98
  * kept state on a record through `setting.keep` is stateful from then on, and leaves its lane;
94
- * one that starts keeping partway through may advance that state once for one subject not
95
- * probed on the frame it starts, which declaring `state` avoids.
99
+ * one that starts keeping partway through may advance that state once for one subject probed the
100
+ * frame before it starts and not on that frame, which declaring `state` avoids.
96
101
  */
97
102
  export function fits(mix, voice) {
98
103
  const spec = voice.spec;
@@ -112,6 +117,10 @@ export function fits(mix, voice) {
112
117
  return false;
113
118
  if (spec.from === 'current')
114
119
  return false;
120
+ // A rest-less channel's band is read on the record `one` folds; crowds, motions and loci fold elsewhere.
121
+ if ((voice.motion !== undefined || spec.locus !== undefined || crowdable(voice)) &&
122
+ voice.slots.some((s) => numericOf(mix.channels[s])?.op === 'last'))
123
+ return false;
115
124
  if (voice.out?.rest)
116
125
  return false;
117
126
  if (patch.state !== undefined || patch.step !== undefined)
@@ -159,9 +168,12 @@ function handleHost(mix) {
159
168
  throw new Error('blits: an owner fades as a whole, not by subject or at rest');
160
169
  if (opts !== undefined && 'subject' in opts)
161
170
  fadeSubject(mix, voice, opts.subject, opts.over);
171
+ else if (typeof opts?.at === 'number')
172
+ fadeAt(mix, voice, opts.at, opts.over);
162
173
  else
163
- beginFade(mix, voice, opts ?? {});
174
+ beginFade(mix, voice, (opts ?? {}));
164
175
  },
176
+ rise: (voice, opts) => beginRise(mix, voice, opts ?? {}),
165
177
  weightOf: (voice, subject) => {
166
178
  if (voice.state === 'done')
167
179
  return 0;
@@ -174,5 +186,6 @@ function handleHost(mix) {
174
186
  }
175
187
  return voice.subjects.get(subject)?.weight ?? 0;
176
188
  },
189
+ record: (label, again) => record(mix, label, again),
177
190
  };
178
191
  }
package/dist/index.d.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  export type { NumberOptions } from './channels.js';
2
- export { hex, kit, last, max, mixHex, mul, sum, vec } from './channels.js';
2
+ export { kit, last, max, mul, sum, vec } from './channels.js';
3
+ export type { ColorOptions } from './color.js';
4
+ export { color, css, hex, mixHex, oklab, toHex } from './color.js';
5
+ export type { Claim, Fit, FitPlan, Order, SpanClaim, Strength } from './fit.js';
6
+ export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
3
7
  export { mix, mixer } from './mixer.js';
4
8
  export type { Motion, Moving, PerSubject, Value } from './motion.js';
5
9
  export { glide, spring, tween } from './motion.js';
10
+ export { HistoryMiss } from './paging.js';
6
11
  export type { KeysOptions, PatchOptions } from './patch.js';
7
12
  export { keys, patch } from './patch.js';
8
13
  export { gate, lag, level, peak, slew } from './signals.js';
9
14
  export type { Ticked, Ticker, TickerOptions } from './ticker.js';
10
15
  export { ticker } from './ticker.js';
11
- export type { Anchor, BookedHit, Booker, BookOptions, Channel, Columns, Doubt, Easing, Engine, FadeOptions, FadeSpec, Handle, Hit, Keyframe, Kit, Mark, Marked, Mix, MixOptions, MotionSpec, OwnerSpec, Patch, Placement, Projection, Query, Sent, Setting, Signal, VoiceSpec, } from './types.js';
16
+ export { transport } from './transport.js';
17
+ export type { Anchor, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Keyframe, Kit, Mark, Marked, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Projection, Query, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, Tape, TapeBranch, TapeMaker, TapeOp, Transport, TransportOptions, TransportProjection, VoiceSpec, } from './types.js';
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
- export { hex, kit, last, max, mixHex, mul, sum, vec } from './channels.js';
1
+ export { kit, last, max, mul, sum, vec } from './channels.js';
2
+ export { color, css, hex, mixHex, oklab, toHex } from './color.js';
3
+ export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
2
4
  export { mix, mixer } from './mixer.js';
3
5
  export { glide, spring, tween } from './motion.js';
6
+ export { HistoryMiss } from './paging.js';
4
7
  export { keys, patch } from './patch.js';
5
8
  export { gate, lag, level, peak, slew } from './signals.js';
6
9
  export { ticker } from './ticker.js';
10
+ export { transport } from './transport.js';
package/dist/lane.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Numeric } from './channels.js';
2
- import { type Motions } from './motion.js';
2
+ import type { Motions } from './motions.js';
3
3
  import type { Scratch } from './patch.js';
4
4
  import type { Subject, Voice } from './voice.js';
5
5
  /** One laned channel: its arithmetic, and every numbered subject's folded value, `axes` per subject. */
package/dist/lanes.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { Column } from './columns.js';
2
2
  import { type Crowd, row } from './crowd.js';
3
3
  import { call, foldKeys, one, parting, readKeyed, reset, run, signalled, subjectAt } from './fill.js';
4
- import { fold, foldDelta, foldInto, foldRun, gather } from './gather.js';
4
+ import { fold, foldDelta, foldInto, foldRun, gate, gather } from './gather.js';
5
5
  import { type Lane, type Laned, type Locus } from './lane.js';
6
- import type { Watcher } from './motion.js';
6
+ import type { Watcher } from './motions.js';
7
7
  import { type absent, Numbers } from './numbers.js';
8
8
  import { Owed } from './owed.js';
9
9
  import type { Channel } from './types.js';
@@ -13,6 +13,10 @@ export interface LaneHost<I, O> {
13
13
  readonly voices: readonly Voice<I, O>[];
14
14
  readonly channels: readonly Channel<unknown>[];
15
15
  readonly names: readonly string[];
16
+ /** The mix itself, for its clock: a getter V8 does not inline would box `now` on return. */
17
+ readonly clock: {
18
+ readonly now: number;
19
+ };
16
20
  /** Whether a voice's patch and spec can run on a lane, its channels aside. */
17
21
  fits(voice: Voice<I, O>): boolean;
18
22
  /** The voice's record for a subject this probe has already linked it to, from `head` if there. */
@@ -35,12 +39,14 @@ export interface LaneHost<I, O> {
35
39
  * stateful if the signal keeps state. `slot` is the subject's number.
36
40
  */
37
41
  signal(voice: Voice<I, O>, subject: I, held: Subject<unknown>, elapsed: number, pass: number, slot: number): number;
42
+ /** Whether a rest-less channel's contribution passes the band, as the general path decides it. */
43
+ passes(was: boolean | undefined, w: number): boolean;
38
44
  /** Sets `reading.horizon` for the voice and a subject delayed `delay` voice ms. */
39
45
  horizon(voice: Voice<I, O>, delay: number): void;
40
46
  /** Whether the mix keeps history, so a record a patch call changed may need a copy kept. */
41
47
  readonly keeps: boolean;
42
48
  /** After a patch call, keeps history of a record that grew kept state. */
43
- after(voice: Voice<I, O>, held: Subject<unknown>): void;
49
+ after(voice: Voice<I, O>, subject: I, held: Subject<unknown>): void;
44
50
  /** A patch call left kept state on a record: the voice is stateful from now on. */
45
51
  kept(voice: Voice<I, O>): void;
46
52
  }
@@ -62,6 +68,9 @@ export declare class Lanes<I, O> implements Watcher {
62
68
  overlap: boolean;
63
69
  /** The loci whose voices are on lanes, every member of each on one. */
64
70
  loci: Locus<I, O>[];
71
+ /** The lane and record `one` is folding, whose band state a rest-less channel reads. */
72
+ folding: Lane<I, O> | null;
73
+ rec: Subject<unknown> | null;
65
74
  /** While a fill gathers a locus: that locus and the member gathering, where folds go instead. */
66
75
  into: Locus<I, O> | null;
67
76
  intoId: number;
@@ -88,14 +97,17 @@ export declare class Lanes<I, O> implements Watcher {
88
97
  /** The highest voice id the last qualify or settling of `touched` considered. */
89
98
  known: number;
90
99
  /**
91
- * Per subject number, `SLOT` numbers side by side, so a probe reads one place in memory: which
92
- * fill last wrote it (0 for none); the latest lane epoch a probe of it has checked it against;
93
- * how many idle lanes reach it, since a probe reads lanes only where none do; and the probe count
94
- * at its last probe read from the lanes and at its last probe folded by the general path, with
95
- * the fill the former read, which say whether a lane or a record holds what `weightOf` reports.
100
+ * Per subject number, `SLOT` numbers side by side, so a probe reads one place in memory: the
101
+ * fill that last left it to the general path, being late, owing, newly numbered or forgotten;
102
+ * the latest lane epoch a probe of it has checked it against; how many idle lanes reach it,
103
+ * since a probe reads lanes only where none do; and the probe count at its last probe read from
104
+ * the lanes and at its last probe folded by the general path, with the fill the former read,
105
+ * which say whether a lane or a record holds what `weightOf` reports.
96
106
  */
97
107
  per: Float64Array<ArrayBuffer>;
98
108
  fills: number;
109
+ /** The latest fill if it ran a lane, else 0: a subject whose `FILLED` is below it reads it. */
110
+ busyFill: number;
99
111
  probes: number;
100
112
  /**
101
113
  * Each subject by number as last probed, held from that probe until the next fill uses it, so a
@@ -140,6 +152,11 @@ export declare class Lanes<I, O> implements Watcher {
140
152
  lastDistinct: number;
141
153
  /** Numbered subjects alive. */
142
154
  live: number;
155
+ /**
156
+ * The probe count a subject's last probe must pass for this fill to fill it: the start of the
157
+ * last frame with a probe, or -1 while every subject alive was probed then, when it fills all.
158
+ */
159
+ lately: number;
143
160
  filledVersion: number;
144
161
  qualifiedVersion: number;
145
162
  constructor(host: LaneHost<I, O>);
@@ -171,7 +188,7 @@ export declare class Lanes<I, O> implements Watcher {
171
188
  * them. One that a lane has not met yet, newly numbered or newly reached, takes the general path
172
189
  * this frame, which is where it is first seen; so does one probed from inside a fill.
173
190
  */
174
- prepare(slot: number, subject: I, now: number, version: number, head: Subject<unknown> | null): boolean;
191
+ prepare(slot: number, subject: I, version: number, head: Subject<unknown> | null): boolean;
175
192
  /** Whether this frame's lanes are filled and nothing since asks `begin` to look again. */
176
193
  filled(now: number, version: number): boolean;
177
194
  /**
@@ -180,6 +197,11 @@ export declare class Lanes<I, O> implements Watcher {
180
197
  */
181
198
  begin(now: number, version: number): number;
182
199
  general(slot: number): false;
200
+ /**
201
+ * Whether this fill leaves the subject at `slot` to the general path, for not having been probed
202
+ * this frame or the last: a probe of it before the next fill then takes the general path.
203
+ */
204
+ unread(slot: number): boolean;
183
205
  /** Whether a probe has read the subject since the frame began, on either path. */
184
206
  probedThisFrame(slot: number): boolean;
185
207
  /** Whether the subject at `slot` reads some laned voice from the general path this fill. */
@@ -221,6 +243,7 @@ declare const methods: {
221
243
  foldDelta: typeof foldDelta;
222
244
  foldRun: typeof foldRun;
223
245
  foldInto: typeof foldInto;
246
+ gate: typeof gate;
224
247
  };
225
248
  type Methods = typeof methods;
226
249
  export interface Lanes<I, O> extends Methods {