@cardstack/choreo 0.0.0 → 0.1.0-unstable.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 (165) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +55 -6
  4. package/addon-main.cjs +4 -0
  5. package/declarations/anchors.d.ts +26 -0
  6. package/declarations/anchors.d.ts.map +1 -0
  7. package/declarations/arming.d.ts +33 -0
  8. package/declarations/arming.d.ts.map +1 -0
  9. package/declarations/beacon.d.ts +9 -0
  10. package/declarations/beacon.d.ts.map +1 -0
  11. package/declarations/beacons.d.ts +23 -0
  12. package/declarations/beacons.d.ts.map +1 -0
  13. package/declarations/changeset.d.ts +46 -0
  14. package/declarations/changeset.d.ts.map +1 -0
  15. package/declarations/choreo.d.ts +261 -0
  16. package/declarations/choreo.d.ts.map +1 -0
  17. package/declarations/compile.d.ts +45 -0
  18. package/declarations/compile.d.ts.map +1 -0
  19. package/declarations/deliver.d.ts +44 -0
  20. package/declarations/deliver.d.ts.map +1 -0
  21. package/declarations/easings.d.ts +20 -0
  22. package/declarations/easings.d.ts.map +1 -0
  23. package/declarations/far.d.ts +33 -0
  24. package/declarations/far.d.ts.map +1 -0
  25. package/declarations/film/clip.d.ts +51 -0
  26. package/declarations/film/clip.d.ts.map +1 -0
  27. package/declarations/film/clips.d.ts +155 -0
  28. package/declarations/film/clips.d.ts.map +1 -0
  29. package/declarations/film/film.d.ts +1208 -0
  30. package/declarations/film/film.d.ts.map +1 -0
  31. package/declarations/film/graph/adjust.d.ts +112 -0
  32. package/declarations/film/graph/adjust.d.ts.map +1 -0
  33. package/declarations/film/graph/compile.d.ts +140 -0
  34. package/declarations/film/graph/compile.d.ts.map +1 -0
  35. package/declarations/film/graph/host.d.ts +67 -0
  36. package/declarations/film/graph/host.d.ts.map +1 -0
  37. package/declarations/film/graph/nodes.d.ts +287 -0
  38. package/declarations/film/graph/nodes.d.ts.map +1 -0
  39. package/declarations/film/index.d.ts +24 -0
  40. package/declarations/film/index.d.ts.map +1 -0
  41. package/declarations/film/joins.d.ts +143 -0
  42. package/declarations/film/joins.d.ts.map +1 -0
  43. package/declarations/film/math.d.ts +15 -0
  44. package/declarations/film/math.d.ts.map +1 -0
  45. package/declarations/film/overlays.d.ts +54 -0
  46. package/declarations/film/overlays.d.ts.map +1 -0
  47. package/declarations/film/picture.d.ts +104 -0
  48. package/declarations/film/picture.d.ts.map +1 -0
  49. package/declarations/film/plate.d.ts +37 -0
  50. package/declarations/film/plate.d.ts.map +1 -0
  51. package/declarations/film/player.d.ts +120 -0
  52. package/declarations/film/player.d.ts.map +1 -0
  53. package/declarations/film/rail.d.ts +40 -0
  54. package/declarations/film/rail.d.ts.map +1 -0
  55. package/declarations/film/schedule.d.ts +93 -0
  56. package/declarations/film/schedule.d.ts.map +1 -0
  57. package/declarations/film/seam.d.ts +38 -0
  58. package/declarations/film/seam.d.ts.map +1 -0
  59. package/declarations/film/titles.d.ts +37 -0
  60. package/declarations/film/titles.d.ts.map +1 -0
  61. package/declarations/film/types.d.ts +544 -0
  62. package/declarations/film/types.d.ts.map +1 -0
  63. package/declarations/film.d.ts +3 -0
  64. package/declarations/film.d.ts.map +1 -0
  65. package/declarations/gesture.d.ts +33 -0
  66. package/declarations/gesture.d.ts.map +1 -0
  67. package/declarations/index.d.ts +26 -0
  68. package/declarations/index.d.ts.map +1 -0
  69. package/declarations/measure.d.ts +29 -0
  70. package/declarations/measure.d.ts.map +1 -0
  71. package/declarations/path.d.ts +73 -0
  72. package/declarations/path.d.ts.map +1 -0
  73. package/declarations/registry.d.ts +46 -0
  74. package/declarations/registry.d.ts.map +1 -0
  75. package/declarations/run.d.ts +406 -0
  76. package/declarations/run.d.ts.map +1 -0
  77. package/declarations/space.d.ts +31 -0
  78. package/declarations/space.d.ts.map +1 -0
  79. package/declarations/steps.d.ts +479 -0
  80. package/declarations/steps.d.ts.map +1 -0
  81. package/declarations/test-support/index.d.ts +30 -0
  82. package/declarations/test-support/index.d.ts.map +1 -0
  83. package/declarations/types.d.ts +761 -0
  84. package/declarations/types.d.ts.map +1 -0
  85. package/dist/anchors.js +34 -0
  86. package/dist/anchors.js.map +1 -0
  87. package/dist/arming.js +120 -0
  88. package/dist/arming.js.map +1 -0
  89. package/dist/beacon.js +25 -0
  90. package/dist/beacon.js.map +1 -0
  91. package/dist/beacons.js +77 -0
  92. package/dist/beacons.js.map +1 -0
  93. package/dist/changeset.js +129 -0
  94. package/dist/changeset.js.map +1 -0
  95. package/dist/choreo.js +1026 -0
  96. package/dist/choreo.js.map +1 -0
  97. package/dist/compile.js +1403 -0
  98. package/dist/compile.js.map +1 -0
  99. package/dist/deliver.js +326 -0
  100. package/dist/deliver.js.map +1 -0
  101. package/dist/easings.js +39 -0
  102. package/dist/easings.js.map +1 -0
  103. package/dist/far.js +147 -0
  104. package/dist/far.js.map +1 -0
  105. package/dist/film/clip.js +100 -0
  106. package/dist/film/clip.js.map +1 -0
  107. package/dist/film/clips.js +147 -0
  108. package/dist/film/clips.js.map +1 -0
  109. package/dist/film/film.js +4424 -0
  110. package/dist/film/film.js.map +1 -0
  111. package/dist/film/graph/adjust.js +160 -0
  112. package/dist/film/graph/adjust.js.map +1 -0
  113. package/dist/film/graph/compile.js +225 -0
  114. package/dist/film/graph/compile.js.map +1 -0
  115. package/dist/film/graph/host.js +77 -0
  116. package/dist/film/graph/host.js.map +1 -0
  117. package/dist/film/graph/nodes.js +500 -0
  118. package/dist/film/graph/nodes.js.map +1 -0
  119. package/dist/film/index.js +18 -0
  120. package/dist/film/index.js.map +1 -0
  121. package/dist/film/joins.js +260 -0
  122. package/dist/film/joins.js.map +1 -0
  123. package/dist/film/math.js +49 -0
  124. package/dist/film/math.js.map +1 -0
  125. package/dist/film/overlays.js +66 -0
  126. package/dist/film/overlays.js.map +1 -0
  127. package/dist/film/picture.js +58 -0
  128. package/dist/film/picture.js.map +1 -0
  129. package/dist/film/plate.js +36 -0
  130. package/dist/film/plate.js.map +1 -0
  131. package/dist/film/player.js +79 -0
  132. package/dist/film/player.js.map +1 -0
  133. package/dist/film/rail.js +38 -0
  134. package/dist/film/rail.js.map +1 -0
  135. package/dist/film/schedule.js +238 -0
  136. package/dist/film/schedule.js.map +1 -0
  137. package/dist/film/seam.js +64 -0
  138. package/dist/film/seam.js.map +1 -0
  139. package/dist/film/titles.js +45 -0
  140. package/dist/film/titles.js.map +1 -0
  141. package/dist/film/types.js +2 -0
  142. package/dist/film/types.js.map +1 -0
  143. package/dist/film.js +18 -0
  144. package/dist/film.js.map +1 -0
  145. package/dist/gesture.js +111 -0
  146. package/dist/gesture.js.map +1 -0
  147. package/dist/index.js +9 -0
  148. package/dist/index.js.map +1 -0
  149. package/dist/measure.js +99 -0
  150. package/dist/measure.js.map +1 -0
  151. package/dist/path.js +272 -0
  152. package/dist/path.js.map +1 -0
  153. package/dist/registry.js +55 -0
  154. package/dist/registry.js.map +1 -0
  155. package/dist/run.js +2423 -0
  156. package/dist/run.js.map +1 -0
  157. package/dist/space.js +57 -0
  158. package/dist/space.js.map +1 -0
  159. package/dist/steps.js +831 -0
  160. package/dist/steps.js.map +1 -0
  161. package/dist/test-support/index.js +150 -0
  162. package/dist/test-support/index.js.map +1 -0
  163. package/dist/types.js +2 -0
  164. package/dist/types.js.map +1 -0
  165. package/package.json +202 -6
package/dist/steps.js ADDED
@@ -0,0 +1,831 @@
1
+ import Component from '@glimmer/component';
2
+ import { modifier } from 'ember-modifier';
3
+ import { at } from './anchors.js';
4
+ import { precompileTemplate } from '@ember/template-compilation';
5
+ import { setComponentTemplate } from '@ember/component';
6
+
7
+ const providers = new WeakMap();
8
+ /** unnamed crossings still need a stable, unique inner anchor name */
9
+ let crossings = 0;
10
+ /** the steps directly inside an element, in document order; a nested <Choreo> is another region's */
11
+ function collect(el) {
12
+ const out = [];
13
+ for (const child of Array.from(el.children)) {
14
+ if (child.hasAttribute('data-choreo')) {
15
+ continue;
16
+ }
17
+ const provider = providers.get(child);
18
+ if (provider) {
19
+ out.push(provider.node());
20
+ } else {
21
+ out.push(...collect(child));
22
+ }
23
+ }
24
+ return out;
25
+ }
26
+ /**
27
+ * args that are the step's own; every other named arg is a property to animate.
28
+ * `rotate` is NOT here although Move has a `@rotate` arg: on a Tween or
29
+ * Spring it is a property (Keynote's Spin), and Move never reads its props.
30
+ */
31
+ const RESERVED = new Set(['align', 'at', 'by', 'delay', 'duration', 'ease', 'fill', 'from', 'name', 'of', 'order', 'path', 'read', 'repeat', 'repeatType', 'rest', 'shadow', 'size', 'space', 'spring', 'stagger', 'swap', 'to', 'times']);
32
+ /** the pre-seconds spellings, kept only to fail loudly with the new name */
33
+ const RENAMED = {
34
+ crossfade: '@swap',
35
+ ms: '@duration (in seconds)',
36
+ overlap: '@stagger (in seconds)'
37
+ };
38
+ function propsOf(args) {
39
+ const props = {};
40
+ for (const key of Object.keys(args)) {
41
+ if (args[key] === undefined) {
42
+ continue;
43
+ }
44
+ if (RENAMED[key]) {
45
+ throw new Error(`choreo: @${key} was renamed — use ${RENAMED[key]}`);
46
+ }
47
+ if (!RESERVED.has(key)) {
48
+ props[key] = args[key];
49
+ }
50
+ }
51
+ return props;
52
+ }
53
+ /**
54
+ * The template speaks seconds, as Motion does; the compiler's clock is ms.
55
+ * A composite step writes node literals, which are BELOW that boundary —
56
+ * so it converts its own seconds args here, and `undefined` stays
57
+ * `undefined` so an unset arg still means "take the default".
58
+ */
59
+ function toMs(seconds) {
60
+ // the conditional return type is the whole point — a required `ms` field
61
+ // takes toMs(x) without a null check when x is a number — and TypeScript
62
+ // cannot verify a conditional return from inside, so the cast is the
63
+ // price of stating it
64
+ return seconds === undefined ? undefined : seconds * 1000;
65
+ }
66
+ const msOf = toMs;
67
+ /** the args every step shares — a composite step extends this */
68
+
69
+ /** …and the args of a step that names sprites */
70
+
71
+ /**
72
+ * The base every step is built on, and the public seam for a COMPOSITE
73
+ * step — a new word in the timeline that expands into the built-in
74
+ * vocabulary. `c.Crossing` is one, and is written in nothing but this.
75
+ *
76
+ * Subclass it, implement `node()`, and render the component anywhere
77
+ * inside a `<Choreo>`: the region reads the tree back in document order
78
+ * and your node takes its place in it.
79
+ *
80
+ * ```gts
81
+ * export class Reveal extends StepComponent<StepArgs & { rise?: number }> {
82
+ * node(): TimelineNode {
83
+ * const { of, name, rise = 12 } = this.args;
84
+ * return {
85
+ * at: this.args.at,
86
+ * children: [
87
+ * { kind: 'tween', generic: true, of, ms: 240, props: { opacity: [0, 1] } },
88
+ * { kind: 'tween', generic: true, of, ms: 240, props: { y: [rise, 0] } },
89
+ * ],
90
+ * kind: 'parallel',
91
+ * name,
92
+ * };
93
+ * }
94
+ * }
95
+ * ```
96
+ *
97
+ * **`node()` is called on every pass**, and its result is fingerprinted to
98
+ * decide whether an edited timeline replays the run. So it must be pure
99
+ * and cheap: no measurement, no DOM writes, no tracked writes (a tracked
100
+ * write re-renders, which replays the pass, which calls `node()`). And
101
+ * because functions in the tree are compared by identity, a `node()` that
102
+ * allocates a fresh closure per call declares an edit on every pass —
103
+ * hoist property functions to module scope.
104
+ *
105
+ * Two conventions worth keeping: mark children `generic: true` so a
106
+ * sibling step can claim a sprite away from your defaults (the yield
107
+ * rule, §4.7), and derive any inner `name` from your own `@name` so two
108
+ * of your step in one timeline do not collide.
109
+ */
110
+ class StepComponent extends Component {
111
+ mark = modifier(el => {
112
+ providers.set(el, this);
113
+ return () => providers.delete(el);
114
+ });
115
+ static {
116
+ setComponentTemplate(precompileTemplate("<span hidden data-choreo-step {{this.mark}}></span>", {
117
+ strictMode: true
118
+ }), this);
119
+ }
120
+ }
121
+ class Tween extends StepComponent {
122
+ node() {
123
+ const {
124
+ of,
125
+ by,
126
+ duration,
127
+ ease,
128
+ delay,
129
+ order,
130
+ repeat,
131
+ repeatType,
132
+ times
133
+ } = this.args;
134
+ return {
135
+ at: this.args.at,
136
+ by,
137
+ delay: msOf(delay),
138
+ ease,
139
+ kind: 'tween',
140
+ name: this.args.name,
141
+ ms: duration * 1000,
142
+ of,
143
+ order,
144
+ props: propsOf(this.args),
145
+ repeat,
146
+ repeatType,
147
+ times,
148
+ stagger: msOf(this.args.stagger)
149
+ };
150
+ }
151
+ }
152
+ class Spring extends StepComponent {
153
+ node() {
154
+ const {
155
+ of,
156
+ by,
157
+ spring,
158
+ delay,
159
+ order
160
+ } = this.args;
161
+ return {
162
+ at: this.args.at,
163
+ by,
164
+ delay: msOf(delay),
165
+ kind: 'spring',
166
+ name: this.args.name,
167
+ of,
168
+ order,
169
+ props: propsOf(this.args),
170
+ spring,
171
+ stagger: msOf(this.args.stagger)
172
+ };
173
+ }
174
+ }
175
+ class Move extends StepComponent {
176
+ node() {
177
+ const {
178
+ of,
179
+ duration,
180
+ ease,
181
+ delay,
182
+ path,
183
+ rotate,
184
+ space,
185
+ spring,
186
+ size,
187
+ swap,
188
+ from,
189
+ to,
190
+ stagger
191
+ } = this.args;
192
+ propsOf(this.args); // no properties — evaluated for the renamed-arg errors
193
+ return {
194
+ at: this.args.at,
195
+ delay: msOf(delay),
196
+ ease,
197
+ from,
198
+ kind: 'move',
199
+ name: this.args.name,
200
+ ms: msOf(duration),
201
+ of,
202
+ path,
203
+ rotate,
204
+ size,
205
+ space,
206
+ spring,
207
+ stagger: msOf(stagger),
208
+ swap,
209
+ to
210
+ };
211
+ }
212
+ }
213
+ class Hold extends StepComponent {
214
+ node() {
215
+ const {
216
+ of,
217
+ duration,
218
+ delay,
219
+ fill,
220
+ stagger
221
+ } = this.args;
222
+ return {
223
+ at: this.args.at,
224
+ delay: msOf(delay),
225
+ fill,
226
+ kind: 'hold',
227
+ name: this.args.name,
228
+ ms: msOf(duration),
229
+ of,
230
+ props: propsOf(this.args),
231
+ stagger: msOf(stagger)
232
+ };
233
+ }
234
+ }
235
+ /**
236
+ * `<c.Wait />` — a hole in a sequence. It has no subject: `@of` is accepted
237
+ * (a wait can be laddered across sprites with `@stagger`) but never needed.
238
+ */
239
+ class Wait extends StepComponent {
240
+ node() {
241
+ const {
242
+ of,
243
+ duration,
244
+ delay,
245
+ stagger
246
+ } = this.args;
247
+ return {
248
+ at: this.args.at,
249
+ delay: msOf(delay),
250
+ kind: 'wait',
251
+ name: this.args.name,
252
+ ms: duration * 1000,
253
+ of,
254
+ stagger: msOf(stagger)
255
+ };
256
+ }
257
+ }
258
+ /**
259
+ * `<c.Perform />` — a semantic command on the timeline (§C4). It occupies
260
+ * an instant, positioned like any step (sequence order, `@at`, `@delay`),
261
+ * and carries `@action` (required), `@target` and `@payload` to the
262
+ * region's dispatcher (`<Choreo @onPerform>`). The fold's law: forward
263
+ * playback dispatches it once as the clock crosses it; a seek past it
264
+ * includes it; a seek before it resets the host and replays the remaining
265
+ * prefix. Commands are idempotent statements of state — `lightbox.open`,
266
+ * never `lightbox.toggle`.
267
+ */
268
+ class Perform extends StepComponent {
269
+ node() {
270
+ const {
271
+ action,
272
+ delay,
273
+ payload,
274
+ target
275
+ } = this.args;
276
+ return {
277
+ action,
278
+ at: this.args.at,
279
+ delay: msOf(delay),
280
+ kind: 'perform',
281
+ name: this.args.name,
282
+ payload,
283
+ target
284
+ };
285
+ }
286
+ }
287
+ /**
288
+ * `<c.Scroll />` — animate the sprite's scroll container so the sprite lands
289
+ * at `@align`; occupies the sequence like any step (§6.1).
290
+ */ /**
291
+ * `<c.Attach @region @duration />` — drive another region's run over a
292
+ * window of this one's clock. Positioned like any step (sequence order,
293
+ * `@at`, `@delay`); `@in` and `@rate` map the window onto the child's own
294
+ * seconds; `@end` says what happens past it; `@exact` refuses a child
295
+ * that cannot be driven exactly (a spring, a follow).
296
+ */
297
+ class Attach extends StepComponent {
298
+ node() {
299
+ const {
300
+ delay,
301
+ duration,
302
+ end,
303
+ exact,
304
+ rate,
305
+ region
306
+ } = this.args;
307
+ return {
308
+ at: this.args.at,
309
+ delay: msOf(delay),
310
+ end,
311
+ exact,
312
+ in: this.args.in,
313
+ kind: 'attach',
314
+ ms: duration * 1000,
315
+ name: this.args.name,
316
+ rate,
317
+ region
318
+ };
319
+ }
320
+ }
321
+ class Scroll extends StepComponent {
322
+ node() {
323
+ const {
324
+ of,
325
+ align,
326
+ duration,
327
+ delay,
328
+ stagger
329
+ } = this.args;
330
+ return {
331
+ align,
332
+ at: this.args.at,
333
+ delay: msOf(delay),
334
+ kind: 'scroll',
335
+ ms: msOf(duration),
336
+ name: this.args.name,
337
+ of,
338
+ stagger: msOf(stagger)
339
+ };
340
+ }
341
+ }
342
+ /**
343
+ * `<c.Raise />` — promote the sprites to the region's elevated layer for the
344
+ * span of its block (or `@duration`): above every stacking context and clip
345
+ * in the region, with measured continuity both ways. `@shadow` casts on the
346
+ * layer below (§6.3).
347
+ */
348
+ class Raise extends StepComponent {
349
+ node() {
350
+ const {
351
+ of,
352
+ duration,
353
+ delay,
354
+ shadow,
355
+ stagger
356
+ } = this.args;
357
+ return {
358
+ at: this.args.at,
359
+ delay: msOf(delay),
360
+ kind: 'raise',
361
+ ms: msOf(duration),
362
+ name: this.args.name,
363
+ of,
364
+ shadow,
365
+ stagger: msOf(stagger)
366
+ };
367
+ }
368
+ }
369
+ /**
370
+ * `<c.Camera />` — the region's frame as a step (§6.3): `@zoom` / `@x` /
371
+ * `@y` animate the scene, `@origin` aims the zoom at a sprite, `@steady`
372
+ * names sprites that keep their size against it (damped by default).
373
+ * `@fit` is the other, more common shape — dive on this sprite and CENTRE
374
+ * it, zoom and pan computed from rest-layout geometry (`@margin` sets the
375
+ * share of the frame it fills); pass `null` to fit nothing: back to rest.
376
+ */
377
+ class Camera extends StepComponent {
378
+ node() {
379
+ const {
380
+ duration,
381
+ ease,
382
+ delay,
383
+ fit,
384
+ margin,
385
+ origin,
386
+ spring,
387
+ steady
388
+ } = this.args;
389
+ const {
390
+ x,
391
+ y,
392
+ zoom
393
+ } = this.args;
394
+ return {
395
+ at: this.args.at,
396
+ delay: msOf(delay),
397
+ ease,
398
+ fit,
399
+ kind: 'camera',
400
+ margin,
401
+ ms: msOf(duration),
402
+ name: this.args.name,
403
+ of: this.args.of ?? {},
404
+ origin,
405
+ spring,
406
+ steady,
407
+ x,
408
+ y,
409
+ zoom
410
+ };
411
+ }
412
+ }
413
+ /**
414
+ * `<c.Camera3D @yaw={{-24}} @pitch={{-6}} @dolly={{0.9}} />` — the shot,
415
+ * for a scene Choreo is not the one drawing.
416
+ *
417
+ * `c.Camera` moves the region's own frame, which is a 2D transform on real
418
+ * DOM. A 3D scene has no such frame: its camera belongs to whatever is
419
+ * rendering it. So this step carries the POSE and nothing else — yaw and
420
+ * pitch in degrees, dolly as a multiple of the host's own framing — and
421
+ * the region hands it to `@onCamera3D` every frame it changes. The host
422
+ * applies it to three.js, to a CSS 3D stage, to anything that takes three
423
+ * numbers.
424
+ *
425
+ * What Choreo keeps is the part it is actually good at: the pose is a pure
426
+ * function of the clock, so a scrub lands the shot exactly where playing
427
+ * there would, and a preset expands into this one seekable cue rather than
428
+ * a second scheduler.
429
+ *
430
+ * `@by` makes it relative — added to the pose in force when the cue
431
+ * starts, the way `Pan` and `SlowZoom` are relative — so a drift composes
432
+ * with whatever shot preceded it.
433
+ */
434
+ class Camera3D extends StepComponent {
435
+ node() {
436
+ const {
437
+ by,
438
+ delay,
439
+ dolly,
440
+ duration,
441
+ ease,
442
+ look,
443
+ pitch,
444
+ settle,
445
+ spring,
446
+ tension,
447
+ through,
448
+ x,
449
+ y,
450
+ yaw
451
+ } = this.args;
452
+ return {
453
+ at: this.args.at,
454
+ by,
455
+ delay: msOf(delay),
456
+ dolly,
457
+ ease,
458
+ kind: 'camera3d',
459
+ look,
460
+ ms: msOf(duration),
461
+ name: this.args.name,
462
+ of: this.args.of ?? {},
463
+ pitch,
464
+ settle,
465
+ spring,
466
+ tension,
467
+ through,
468
+ x,
469
+ y,
470
+ yaw
471
+ };
472
+ }
473
+ }
474
+ /* ---- the direction vocabulary (notes/choreo-composition.md C5) ----
475
+ Presets expand into the SAME seekable camera cue — sugar, never a new
476
+ runtime primitive. Frame and Aim are absolute (computed from measured
477
+ geometry at compile); Pan and SlowZoom are RELATIVE, resolved against
478
+ the pose in force when the cue starts, so they compose with whatever
479
+ shot preceded them and reconstruct under random access like any cue. */ /**
480
+ * `<c.Frame @of={{c.id 'hero'}} @padding={{0.8}} />` — fit-and-centre the
481
+ * target: the fit camera under its editorial name. `@padding` is the
482
+ * share of the frame the target fills on whichever axis fits first.
483
+ */
484
+ class Frame extends StepComponent {
485
+ node() {
486
+ const {
487
+ duration,
488
+ ease,
489
+ delay,
490
+ of,
491
+ padding,
492
+ spring,
493
+ steady
494
+ } = this.args;
495
+ return {
496
+ at: this.args.at,
497
+ delay: msOf(delay),
498
+ ease,
499
+ fit: of,
500
+ kind: 'camera',
501
+ margin: padding,
502
+ ms: msOf(duration),
503
+ name: this.args.name,
504
+ of: {},
505
+ spring,
506
+ steady
507
+ };
508
+ }
509
+ }
510
+ /**
511
+ * `<c.Aim @of={{c.id 'hero'}} />` — recentre the picture on the target
512
+ * with the zoom HELD: the reframe that does not change magnification.
513
+ */
514
+ class Aim extends StepComponent {
515
+ node() {
516
+ const {
517
+ duration,
518
+ ease,
519
+ delay,
520
+ of,
521
+ spring,
522
+ steady
523
+ } = this.args;
524
+ return {
525
+ aim: of,
526
+ at: this.args.at,
527
+ delay: msOf(delay),
528
+ ease,
529
+ kind: 'camera',
530
+ ms: msOf(duration),
531
+ name: this.args.name,
532
+ of: {},
533
+ spring,
534
+ steady
535
+ };
536
+ }
537
+ }
538
+ /**
539
+ * `<c.Pan @x={{40}} @y={{-20}} />` — shift the picture by exactly this
540
+ * many pixels from wherever it stands. Relative on purpose: a pan after
541
+ * any shot means "from here", not "to there".
542
+ */
543
+ class Pan extends StepComponent {
544
+ node() {
545
+ const {
546
+ duration,
547
+ ease,
548
+ delay,
549
+ spring,
550
+ steady,
551
+ x,
552
+ y
553
+ } = this.args;
554
+ return {
555
+ at: this.args.at,
556
+ delay: msOf(delay),
557
+ ease,
558
+ kind: 'camera',
559
+ ms: msOf(duration),
560
+ name: this.args.name,
561
+ of: {},
562
+ panBy: {
563
+ x,
564
+ y
565
+ },
566
+ spring,
567
+ steady
568
+ };
569
+ }
570
+ }
571
+ /**
572
+ * `<c.SlowZoom @by={{1.05}} @duration={{2}} />` — multiply the zoom in
573
+ * force: the push-in (or, below 1, the pull-back) that gives a hold its
574
+ * life. The aim in force is kept, so it pushes toward what the previous
575
+ * shot was looking at.
576
+ */
577
+ class SlowZoom extends StepComponent {
578
+ node() {
579
+ const {
580
+ by,
581
+ duration,
582
+ ease,
583
+ delay,
584
+ spring,
585
+ steady
586
+ } = this.args;
587
+ return {
588
+ at: this.args.at,
589
+ delay: msOf(delay),
590
+ ease,
591
+ kind: 'camera',
592
+ ms: msOf(duration),
593
+ name: this.args.name,
594
+ of: {},
595
+ spring,
596
+ steady,
597
+ zoomBy: by
598
+ };
599
+ }
600
+ }
601
+ /**
602
+ * `<c.Tether />` — geometry continuously derived from sprites (§6.1):
603
+ * `@path` receives both endpoints' region-relative boxes every frame and
604
+ * returns the path data the wire draws.
605
+ */
606
+ class Tether extends StepComponent {
607
+ node() {
608
+ const {
609
+ duration,
610
+ delay,
611
+ from,
612
+ path,
613
+ to
614
+ } = this.args;
615
+ return {
616
+ at: this.args.at,
617
+ delay: msOf(delay),
618
+ from,
619
+ kind: 'tether',
620
+ ms: msOf(duration),
621
+ name: this.args.name,
622
+ of: this.args.of,
623
+ path,
624
+ to
625
+ };
626
+ }
627
+ }
628
+ /**
629
+ * `<c.Crossing />` — the canned scene crossing (§4.7): what only the old
630
+ * scene had fades as the flight lifts off, everything paired flies (skins
631
+ * swapping per `@swap`), and what only the new scene has fades in near the
632
+ * settle. One step in the template; a whole overlapped score on the clock —
633
+ * Keynote's Magic Move does not wait for the dissolve to finish before the
634
+ * movers leave, and a crossing that does reads as three acts, not one.
635
+ *
636
+ * It is also the reference COMPOSITE step, and deliberately written in
637
+ * nothing but the public seam (`StepComponent`, `toMs`, `at`, and node
638
+ * literals): if this needed a privilege an author could not have, the
639
+ * contract on `StepComponent` would be a lie.
640
+ */
641
+ class Crossing extends StepComponent {
642
+ /**
643
+ * The flight is addressable from outside — `{{at 'page:flight' 0.5}}`
644
+ * for a step that wants to ride the move itself. Derived from this
645
+ * crossing's own `@name` rather than fixed, so two crossings in one
646
+ * timeline do not collide over it; unnamed, it takes a per-instance
647
+ * number, which is stable for the life of the component and so does not
648
+ * disturb the tree fingerprint between passes.
649
+ */
650
+ flightName = `${this.args.name ?? `crossing-${++crossings}`}:flight`;
651
+ node() {
652
+ const {
653
+ arrive = 0.22,
654
+ duration,
655
+ ease,
656
+ leave = 0.18,
657
+ overlap = 0.7,
658
+ size = 'crop',
659
+ spring,
660
+ swap
661
+ } = this.args;
662
+ const FLIGHT = this.flightName;
663
+ return {
664
+ at: this.args.at,
665
+ delay: toMs(this.args.delay),
666
+ name: this.args.name,
667
+ children: [{
668
+ generic: true,
669
+ kind: 'tween',
670
+ ms: toMs(leave),
671
+ of: {
672
+ onstage: true,
673
+ type: 'departed'
674
+ },
675
+ props: {
676
+ opacity: 0
677
+ }
678
+ }, {
679
+ ease,
680
+ generic: true,
681
+ kind: 'move',
682
+ ms: toMs(duration),
683
+ name: FLIGHT,
684
+ of: {
685
+ type: 'received'
686
+ },
687
+ // iOS's rule: uniform scale matched by cover, the aspect mismatch
688
+ // cropped by the interpolating window — never a stretch, and
689
+ // never layout (a receiver animating real width/height reflows
690
+ // its whole row for the flight). Overridable; the default stays
691
+ // crop so a crossing in a grid cannot stretch the row.
692
+ size,
693
+ spring,
694
+ swap
695
+ }, {
696
+ generic: true,
697
+ kind: 'hold',
698
+ of: {
699
+ type: 'received'
700
+ },
701
+ props: {
702
+ zIndex: 2
703
+ }
704
+ }, {
705
+ at: at(FLIGHT, overlap),
706
+ generic: true,
707
+ kind: 'tween',
708
+ ms: toMs(arrive),
709
+ of: {
710
+ onstage: true,
711
+ type: 'inserted'
712
+ },
713
+ props: {
714
+ opacity: [0, 1]
715
+ }
716
+ }],
717
+ kind: 'parallel'
718
+ };
719
+ }
720
+ }
721
+ /**
722
+ * `<c.Follow />` — a value DERIVED from the scene rather than interpolated
723
+ * between two keyframes (§4.10). `@to` names what to read; `@read` gets
724
+ * the pass's measurements every frame — each source's resting boxes
725
+ * (`from`, `to`) and its composed position this frame (`now`), plus the
726
+ * follower's own `rest` — and returns the properties to write; `@rest`
727
+ * says what those properties are when nothing is driving them, so a
728
+ * measure pass can put the element back.
729
+ *
730
+ * ```gts
731
+ * <c.Follow @of={{c.id 'badge'}} @to={{c.id 'card'}}
732
+ * @read={{corner}} @rest={{hash x=0 y=0}} />
733
+ * ```
734
+ *
735
+ * `@read` never sees the live page. Everything it is handed was measured
736
+ * by the pass or composed from the run's own values, so it cannot read
737
+ * back what it wrote last frame and it cannot force a style recalculation
738
+ * mid-move — pure by construction, and correct under interruption because
739
+ * a replacement pass re-measures (docs/postmortem-follow.md).
740
+ *
741
+ * Three things it is not: it is not accelerated (a derived value is
742
+ * computed on the main thread, every frame — the price, and the same one
743
+ * `c.Tether` pays); it may not write layout, only transform, opacity and
744
+ * filter; and `@read` must still be a pure function of its context,
745
+ * because the run is scrubbable in both directions and a value with
746
+ * memory could not be sought back to.
747
+ */
748
+ class Follow extends StepComponent {
749
+ node() {
750
+ const {
751
+ duration,
752
+ of,
753
+ read,
754
+ rest,
755
+ to
756
+ } = this.args;
757
+ return {
758
+ at: this.args.at,
759
+ delay: msOf(this.args.delay),
760
+ kind: 'follow',
761
+ ms: msOf(duration),
762
+ name: this.args.name,
763
+ of,
764
+ read,
765
+ rest,
766
+ stagger: msOf(this.args.stagger),
767
+ to
768
+ };
769
+ }
770
+ }
771
+ /**
772
+ * `<c.Gate />` — park the run until `c.advance()`; `@delay` opens it by
773
+ * itself after that many seconds (§4.1). A gate is a pause, and a pause is
774
+ * a total order: it may only stand in a sequence that no parallel contains.
775
+ */
776
+ class Gate extends Component {
777
+ node() {
778
+ return {
779
+ kind: 'gate',
780
+ ms: msOf(this.args.delay)
781
+ };
782
+ }
783
+ mark = modifier(el => {
784
+ providers.set(el, this);
785
+ return () => providers.delete(el);
786
+ });
787
+ static {
788
+ setComponentTemplate(precompileTemplate("<span hidden data-choreo-step {{this.mark}}></span>", {
789
+ strictMode: true
790
+ }), this);
791
+ }
792
+ }
793
+ /**
794
+ * A block is a step's equal to the anchor system: it takes `@name`, `@at`
795
+ * and `@delay` on the same terms, so a composite step — a `node()` that
796
+ * returns a block — is something the rest of the score can point at.
797
+ */
798
+ class BlockComponent extends Component {
799
+ el;
800
+ node() {
801
+ return {
802
+ at: this.args.at,
803
+ children: this.el ? collect(this.el) : [],
804
+ delay: msOf(this.args.delay),
805
+ kind: this.kind,
806
+ name: this.args.name
807
+ };
808
+ }
809
+ mark = modifier(el => {
810
+ this.el = el;
811
+ providers.set(el, this);
812
+ return () => {
813
+ providers.delete(el);
814
+ this.el = undefined;
815
+ };
816
+ });
817
+ static {
818
+ setComponentTemplate(precompileTemplate("<span hidden data-choreo-block {{this.mark}}>{{yield}}</span>", {
819
+ strictMode: true
820
+ }), this);
821
+ }
822
+ }
823
+ class Sequence extends BlockComponent {
824
+ kind = 'sequence';
825
+ }
826
+ class Parallel extends BlockComponent {
827
+ kind = 'parallel';
828
+ }
829
+
830
+ export { Aim, Attach, Camera, Camera3D, Crossing, Follow, Frame, Gate, Hold, Move, Pan, Parallel, Perform, Raise, Scroll, Sequence, SlowZoom, Spring, StepComponent, Tether, Tween, Wait, collect, toMs };
831
+ //# sourceMappingURL=steps.js.map