storyink 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
- import"../chunks/index-8fbs7b39.js";
1
+ import"../chunks/index-ygm89806.js";
2
2
  import {
3
3
  ARROWS,
4
+ CAMERA,
4
5
  DIAGRAM_TYPES,
5
6
  DIRECTIONS,
6
7
  EDGE_STYLES,
@@ -8,26 +9,38 @@ import {
8
9
  GRAPH_NODE_KINDS,
9
10
  MESSAGE_KINDS,
10
11
  PARTICIPANT_KINDS,
12
+ STEP_BEAT,
13
+ STORY_MOTIONS,
11
14
  StoryinkError,
12
15
  VERSION,
13
16
  animatedHeaderHeight,
14
17
  animatedSvg,
15
18
  autoStory,
16
19
  beatTimes,
20
+ cameraAt,
21
+ cameraAtEnd,
22
+ centreOn,
23
+ clampCamera,
17
24
  compileStory,
18
25
  counterValue,
26
+ deadZoneRect,
19
27
  detectMermaid,
20
28
  elbowPath,
21
29
  elbowPathV,
22
30
  exports_tokens,
31
+ fitCamera,
32
+ fitIsReadable,
33
+ followStep,
23
34
  fontCss,
24
35
  formatDiagnostic,
25
36
  fromMermaid,
37
+ inView,
26
38
  isSequence,
27
39
  layout,
28
40
  parseHash,
29
41
  pinnedCss,
30
42
  readTime,
43
+ readableScale,
31
44
  renderAnimatedSvg,
32
45
  renderHtml,
33
46
  renderSvg,
@@ -36,15 +49,23 @@ import {
36
49
  roundedPolyline,
37
50
  simplify,
38
51
  simplifyError,
52
+ stepAt,
53
+ stepFocus,
54
+ steppedIndex,
55
+ steppedSchedule,
56
+ steppedStop,
57
+ steppedTime,
39
58
  storyState,
40
59
  textWidth,
41
60
  toScene,
61
+ toScreen,
42
62
  validate,
43
63
  wirePath,
44
64
  wrap
45
- } from "../chunks/index-060cd52n.js";
65
+ } from "../chunks/index-n3gf4fyb.js";
46
66
  export {
47
67
  ARROWS,
68
+ CAMERA,
48
69
  DIAGRAM_TYPES,
49
70
  DIRECTIONS,
50
71
  EDGE_STYLES,
@@ -52,25 +73,37 @@ export {
52
73
  GRAPH_NODE_KINDS,
53
74
  MESSAGE_KINDS,
54
75
  PARTICIPANT_KINDS,
76
+ STEP_BEAT,
77
+ STORY_MOTIONS,
55
78
  StoryinkError,
56
79
  VERSION,
57
80
  animatedHeaderHeight,
58
81
  animatedSvg,
59
82
  autoStory,
60
83
  beatTimes,
84
+ cameraAt,
85
+ cameraAtEnd,
86
+ centreOn,
87
+ clampCamera,
61
88
  compileStory,
62
89
  counterValue,
90
+ deadZoneRect,
63
91
  detectMermaid,
64
92
  elbowPath,
65
93
  elbowPathV,
94
+ fitCamera,
95
+ fitIsReadable,
96
+ followStep,
66
97
  fontCss,
67
98
  formatDiagnostic,
68
99
  fromMermaid,
100
+ inView,
69
101
  isSequence,
70
102
  layout,
71
103
  parseHash,
72
104
  pinnedCss,
73
105
  readTime,
106
+ readableScale,
74
107
  renderAnimatedSvg,
75
108
  renderHtml,
76
109
  renderSvg,
@@ -79,9 +112,16 @@ export {
79
112
  roundedPolyline,
80
113
  simplify,
81
114
  simplifyError,
115
+ stepAt,
116
+ stepFocus,
117
+ steppedIndex,
118
+ steppedSchedule,
119
+ steppedStop,
120
+ steppedTime,
82
121
  storyState,
83
122
  textWidth,
84
123
  toScene,
124
+ toScreen,
85
125
  exports_tokens as tokens,
86
126
  validate,
87
127
  wirePath,
package/dist/index.js CHANGED
@@ -1,14 +1,17 @@
1
- import"./chunks/index-8fbs7b39.js";
1
+ import"./chunks/index-ygm89806.js";
2
2
  import {
3
3
  findBrowser,
4
4
  loadSpec,
5
5
  parseSource,
6
+ setStoryCamera,
7
+ setStoryMotion,
6
8
  snapshot,
7
9
  writeAnimatedSvg,
8
10
  writeDiagram
9
- } from "./chunks/index-zjyrjkn6.js";
11
+ } from "./chunks/index-5qmjsg8w.js";
10
12
  import {
11
13
  ARROWS,
14
+ CAMERA,
12
15
  DIAGRAM_TYPES,
13
16
  DIRECTIONS,
14
17
  EDGE_STYLES,
@@ -16,26 +19,38 @@ import {
16
19
  GRAPH_NODE_KINDS,
17
20
  MESSAGE_KINDS,
18
21
  PARTICIPANT_KINDS,
22
+ STEP_BEAT,
23
+ STORY_MOTIONS,
19
24
  StoryinkError,
20
25
  VERSION,
21
26
  animatedHeaderHeight,
22
27
  animatedSvg,
23
28
  autoStory,
24
29
  beatTimes,
30
+ cameraAt,
31
+ cameraAtEnd,
32
+ centreOn,
33
+ clampCamera,
25
34
  compileStory,
26
35
  counterValue,
36
+ deadZoneRect,
27
37
  detectMermaid,
28
38
  elbowPath,
29
39
  elbowPathV,
30
40
  exports_tokens,
41
+ fitCamera,
42
+ fitIsReadable,
43
+ followStep,
31
44
  fontCss,
32
45
  formatDiagnostic,
33
46
  fromMermaid,
47
+ inView,
34
48
  isSequence,
35
49
  layout,
36
50
  parseHash,
37
51
  pinnedCss,
38
52
  readTime,
53
+ readableScale,
39
54
  renderAnimatedSvg,
40
55
  renderHtml,
41
56
  renderSvg,
@@ -44,13 +59,20 @@ import {
44
59
  roundedPolyline,
45
60
  simplify,
46
61
  simplifyError,
62
+ stepAt,
63
+ stepFocus,
64
+ steppedIndex,
65
+ steppedSchedule,
66
+ steppedStop,
67
+ steppedTime,
47
68
  storyState,
48
69
  textWidth,
49
70
  toScene,
71
+ toScreen,
50
72
  validate,
51
73
  wirePath,
52
74
  wrap
53
- } from "./chunks/index-060cd52n.js";
75
+ } from "./chunks/index-n3gf4fyb.js";
54
76
  // src/plugin.ts
55
77
  import fs2 from "node:fs";
56
78
  import path2 from "node:path";
@@ -154,6 +176,16 @@ var plugin = {
154
176
  svg: { type: "string", description: "Optional output .svg path" },
155
177
  theme: { ...THEME, description: "Pin the theme; default follows the viewer's system preference" },
156
178
  story: { type: "string", enum: ["auto"], description: "Add an auto-generated storyboard (only when the user asked for an animated diagram)" },
179
+ motion: {
180
+ type: "string",
181
+ enum: ["full", "reduced", "system"],
182
+ description: `Story playback motion: "full" (default, animates even when the reader's OS reduces motion), "reduced" (step by step), "system" (follow prefers-reduced-motion)`
183
+ },
184
+ camera: {
185
+ type: "string",
186
+ enum: ["follow", "fit"],
187
+ description: 'Viewer camera while playing: "follow" (default: zoom to a readable scale when the diagram is large and pan to each step) or "fit" (whole diagram always in view)'
188
+ },
157
189
  animatedSvg: {
158
190
  type: ["boolean", "string"],
159
191
  enum: [true, false, "both"],
@@ -174,6 +206,10 @@ var plugin = {
174
206
  const s = specFrom(i);
175
207
  if (s.ok && s.spec && i.story === "auto" && s.spec.story === undefined)
176
208
  s.spec.story = "auto";
209
+ if (s.ok && s.spec && i.motion)
210
+ setStoryMotion(s.spec, i.motion);
211
+ if (s.ok && s.spec && i.camera)
212
+ setStoryCamera(s.spec, i.camera);
177
213
  if (!s.ok || !s.spec)
178
214
  return {
179
215
  content: `storyink: spec is invalid, nothing written.
@@ -281,6 +317,8 @@ ${summarize(s.diagnostics)}`,
281
317
  enum: ["overview", "full", "none"],
282
318
  description: 'Inline image: "overview" (default: one compact image; beat sheets reflow into more columns), "full" (normal sheet layout, split into ≤ 3 downscaled parts when tall), "none" (paths only)'
283
319
  },
320
+ motion: { type: "string", enum: ["full", "reduced"], description: 'Motion mode for `at` frames: "reduced" = stepped playback (settled step states)' },
321
+ camera: { type: "string", enum: ["fit", "follow"], description: '`at` frames: "fit" (default, whole diagram) or "follow" (the follow camera\'s view at a 1280×720 stage)' },
284
322
  maxImageSize: { type: "number", description: "Longest side of the returned image in px (default 1024, 256–2048)" }
285
323
  },
286
324
  required: ["html"],
@@ -295,6 +333,8 @@ ${summarize(s.diagnostics)}`,
295
333
  ...i.at ? { at: i.at.map((x) => x === "end" ? "end" : Number(x)) } : {},
296
334
  sheet: i.sheet === "none" ? false : i.sheet === "beats" ? "beats" : true,
297
335
  ...i.themes ? { themes: i.themes } : {},
336
+ ...i.motion ? { motion: i.motion } : {},
337
+ ...i.camera === "follow" ? { camera: "follow" } : {},
298
338
  ...i.width ? { width: i.width } : {},
299
339
  ...i.outDir ? { outDir: abs(i.outDir) } : {},
300
340
  signal: context.signal
@@ -323,6 +363,7 @@ ${summarize(s.diagnostics)}`,
323
363
  var plugin_default = plugin;
324
364
  export {
325
365
  ARROWS,
366
+ CAMERA,
326
367
  DIAGRAM_TYPES,
327
368
  DIRECTIONS,
328
369
  EDGE_STYLES,
@@ -331,22 +372,33 @@ export {
331
372
  MESSAGE_KINDS,
332
373
  PARTICIPANT_KINDS,
333
374
  PLUGIN_ID,
375
+ STEP_BEAT,
376
+ STORY_MOTIONS,
334
377
  StoryinkError,
335
378
  VERSION,
336
379
  animatedHeaderHeight,
337
380
  animatedSvg,
338
381
  autoStory,
339
382
  beatTimes,
383
+ cameraAt,
384
+ cameraAtEnd,
385
+ centreOn,
386
+ clampCamera,
340
387
  compileStory,
341
388
  counterValue,
389
+ deadZoneRect,
342
390
  plugin_default as default,
343
391
  detectMermaid,
344
392
  elbowPath,
345
393
  elbowPathV,
346
394
  findBrowser,
395
+ fitCamera,
396
+ fitIsReadable,
397
+ followStep,
347
398
  fontCss,
348
399
  formatDiagnostic,
349
400
  fromMermaid,
401
+ inView,
350
402
  isSequence,
351
403
  layout,
352
404
  loadSpec,
@@ -354,6 +406,7 @@ export {
354
406
  parseSource,
355
407
  pinnedCss,
356
408
  readTime,
409
+ readableScale,
357
410
  renderAnimatedSvg,
358
411
  renderHtml,
359
412
  renderSvg,
@@ -363,9 +416,16 @@ export {
363
416
  simplify,
364
417
  simplifyError,
365
418
  snapshot,
419
+ stepAt,
420
+ stepFocus,
421
+ steppedIndex,
422
+ steppedSchedule,
423
+ steppedStop,
424
+ steppedTime,
366
425
  storyState,
367
426
  textWidth,
368
427
  toScene,
428
+ toScreen,
369
429
  exports_tokens as tokens,
370
430
  validate,
371
431
  wirePath,
@@ -5,11 +5,13 @@ import {
5
5
  parseSource,
6
6
  pictureSnippet,
7
7
  screenshotPage,
8
+ setStoryCamera,
9
+ setStoryMotion,
8
10
  snapshot,
9
11
  writeAnimatedSvg,
10
12
  writeDiagram
11
- } from "../chunks/index-zjyrjkn6.js";
12
- import"../chunks/index-060cd52n.js";
13
+ } from "../chunks/index-5qmjsg8w.js";
14
+ import"../chunks/index-n3gf4fyb.js";
13
15
  export {
14
16
  browserVersion,
15
17
  findBrowser,
@@ -17,6 +19,8 @@ export {
17
19
  parseSource,
18
20
  pictureSnippet,
19
21
  screenshotPage,
22
+ setStoryCamera,
23
+ setStoryMotion,
20
24
  snapshot,
21
25
  writeAnimatedSvg,
22
26
  writeDiagram
@@ -10,7 +10,8 @@ export { renderSvg, renderHtml, toScene, fontCss, StoryinkError, type SvgOptions
10
10
  export { parseHash, type HashParams } from "./render/App.js";
11
11
  export * as tokens from "../theme/tokens.js";
12
12
  export { VERSION } from "../generated/meta.js";
13
- export { storyState, restFrame, beatTimes, counterValue, type StateOptions } from "./story/state.js";
13
+ export { storyState, restFrame, beatTimes, counterValue, steppedSchedule, steppedIndex, steppedStop, steppedTime, STEP_BEAT, type StateOptions, type SteppedStop } from "./story/state.js";
14
+ export { CAMERA, cameraAt, cameraAtEnd, centreOn, clampCamera, deadZoneRect, fitCamera, fitIsReadable, followStep, inView, readableScale, stepAt, stepFocus, toScreen, type Camera, type Viewport } from "./story/camera.js";
14
15
  export { compileStory, resolveEdge, readTime, type CompileResult } from "./story/compile.js";
15
16
  export { autoStory } from "./story/auto.js";
16
17
  export type { Timeline, TimelineStep, TimelinePulse, TimelineGlow, TimelineDraw, PulseFrame, GlowFrame, Frame as StoryFrame } from "./story/types.js";
@@ -27,8 +27,35 @@ export interface HashParams {
27
27
  /** Beat sheet layout: fixed column count and a tile range (for compact previews). */
28
28
  cols?: number;
29
29
  range?: [number, number];
30
+ /** `#camera=follow|fit`: follow the story (also places a `#t=` frame) or keep the whole diagram in view. */
31
+ camera?: "follow" | "fit";
30
32
  }
31
33
  export declare function parseHash(hash: string): HashParams;
34
+ /**
35
+ * Effective motion mode. Precedence: `#motion=` hash, then the reader's stored toolbar choice
36
+ * (`localStorage["storyink-motion"]`), then the author's `story.motion` (default "full"). The OS
37
+ * `prefers-reduced-motion` is consulted only when the author chose "system".
38
+ */
39
+ export declare function resolveMotion(o: {
40
+ hash?: "full" | "reduced";
41
+ stored?: string | null;
42
+ author?: "full" | "reduced" | "system";
43
+ system: boolean;
44
+ }): "full" | "reduced";
45
+ export declare const MOTION_KEY = "storyink-motion";
46
+ export declare const FOLLOW_KEY = "storyink-follow";
47
+ /**
48
+ * Whether the camera follows the story. Precedence: `#camera=` hash, then the reader's stored
49
+ * toolbar choice (`localStorage["storyink-follow"]` = "on" | "off"), then the author's
50
+ * `story.camera` (default "follow").
51
+ */
52
+ export declare function resolveFollow(o: {
53
+ hash?: "follow" | "fit";
54
+ stored?: string | null;
55
+ author?: "follow" | "fit";
56
+ }): boolean;
57
+ /** Pointerdown targets that never start a pan (controls inside the stage). */
58
+ export declare const NO_PAN = "button, input, select, textarea, a, [role=slider], .si-tools, .si-transport, .si-gate, [data-no-pan]";
32
59
  /** Viewer shell. Server-rendered with defaults, then hydrated; hash state applies after hydration. */
33
60
  export declare function App({ scene, hooks }: AppProps & {
34
61
  hooks?: ViewerHooks;
@@ -35,8 +35,9 @@ export interface StoryOptions {
35
35
  */
36
36
  export declare function useStory(scene: Scene, tl: Timeline | undefined, opts: StoryOptions): StoryControls | undefined;
37
37
  /** The click-to-play gate (STYLE.md §6): dimmed t = 0 frame, centred play triangle, idle rings. */
38
- export declare function Gate({ onPlay }: {
38
+ export declare function Gate({ onPlay, still }: {
39
39
  onPlay: () => void;
40
+ still?: boolean;
40
41
  }): ReactElement;
41
42
  /** Play/pause, replay, scrubber with step and chapter ticks. */
42
43
  export declare function Transport({ c, tl }: {
@@ -90,8 +90,24 @@ export interface Story {
90
90
  autoplay?: boolean;
91
91
  /** "hold" (default) the final frame, or "loop". */
92
92
  end?: "hold" | "loop";
93
- steps: StoryStep[];
93
+ /**
94
+ * Playback motion (default "full"): "full" always animates, "reduced" always plays step by
95
+ * step, "system" follows the reader's `prefers-reduced-motion`. The reader's toolbar choice
96
+ * and `#motion=` still win.
97
+ */
98
+ motion?: StoryMotion;
99
+ /**
100
+ * Viewer camera while playing (default "follow"): "follow" zooms to a readable scale when the
101
+ * fit view is too small and pans to each step; "fit" keeps the whole diagram in view. The
102
+ * reader's Follow toggle and `#camera=` still win.
103
+ */
104
+ camera?: StoryCamera;
105
+ /** Steps, or "auto" to derive them (like `"story": "auto"`, with the options above). */
106
+ steps: StoryStep[] | "auto";
94
107
  }
108
+ export type StoryMotion = "full" | "reduced" | "system";
109
+ export type StoryCamera = "follow" | "fit";
110
+ export declare const STORY_MOTIONS: readonly ["full", "reduced", "system"];
95
111
  export interface GraphGroup {
96
112
  id: string;
97
113
  label?: string;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The follow camera: pure geometry. Where each story step happens (`stepFocus`) and where the
3
+ * viewer's camera sits for it (`followStep`, `cameraAt`). No DOM, no wall time: the viewer, the
4
+ * snapshot `#camera=follow` capture and the tests all evaluate the same functions.
5
+ *
6
+ * Camera convention (the viewer's `.si-canvas` transform): a diagram point p is drawn on screen at
7
+ * `x + (p.x - viewBox.x) * k`, `y + (p.y - viewBox.y) * k`.
8
+ */
9
+ import type { Box, Scene } from "../scene.js";
10
+ import type { Timeline } from "./types.js";
11
+ export interface Camera {
12
+ k: number;
13
+ x: number;
14
+ y: number;
15
+ }
16
+ /** The stage in CSS px. `bottom` is reserved for overlaid chrome (transport, toolbar). */
17
+ export interface Viewport {
18
+ w: number;
19
+ h: number;
20
+ bottom?: number;
21
+ }
22
+ export declare const CAMERA: {
23
+ /** Node label size in diagram px. */
24
+ readonly label: 11;
25
+ /** Fit is readable when labels render at least this large on screen. */
26
+ readonly minLabel: 10;
27
+ /** Target on-screen label size when fit is too small. */
28
+ readonly readableLabel: 12.5;
29
+ /** Never zoom in past this for readability. */
30
+ readonly maxK: 2;
31
+ /** Fit padding (matches the viewer's Fit button). */
32
+ readonly fitPad: 32;
33
+ /** Padding around a step's focus, diagram px. */
34
+ readonly focusPad: 24;
35
+ /** Follow only when the focus leaves the inner share of the viewport. */
36
+ readonly deadZone: 0.8;
37
+ /** Keep this much page margin at the diagram's edges when clamping, CSS px. */
38
+ readonly edge: 24;
39
+ /** Camera move: bounce-free spring (STYLE.md: content springs have no bounce). */
40
+ readonly spring: {
41
+ readonly type: "spring";
42
+ readonly bounce: 0;
43
+ readonly visualDuration: 0.75;
44
+ };
45
+ };
46
+ /** The whole diagram in view (identical to the viewer's Fit, snapped for pixel-stable captures). */
47
+ export declare function fitCamera(vb: Box, vp: Viewport): Camera;
48
+ /** Fit when its labels are readable (≥ 10 px on screen); otherwise the scale for ~12.5 px labels (capped). */
49
+ export declare function readableScale(vb: Box, vp: Viewport): number;
50
+ /** Whether fit already renders labels readably. */
51
+ export declare const fitIsReadable: (vb: Box, vp: Viewport) => boolean;
52
+ /** Index of the step in effect at `t` (the last one started; 0 before the first). */
53
+ export declare function stepAt(tl: Timeline, t: number): number;
54
+ /**
55
+ * Bounding box (diagram px, padded) of everything step i animates: reveals, pulse routes, drawn
56
+ * wires, highlights and arrival glows, counter nodes (captions point at those same elements).
57
+ * Undefined when the step touches nothing on the diagram (a caption-only step).
58
+ */
59
+ export declare function stepFocus(scene: Scene, tl: Timeline, i: number, pad?: number): Box | undefined;
60
+ /** A diagram box on screen under `cam`. */
61
+ export declare function toScreen(cam: Camera, vb: Box, b: Box): Box;
62
+ /** The inner `deadZone` share of the usable viewport, CSS px. */
63
+ export declare function deadZoneRect(vp: Viewport, dz?: number): Box;
64
+ /** True when `focus` (diagram px) sits inside the dead zone under `cam`. */
65
+ export declare function inView(cam: Camera, vb: Box, vp: Viewport, focus: Box, dz?: number): boolean;
66
+ /** Keep the diagram covering the viewport where it can (no panning into empty page), whole pixels. */
67
+ export declare function clampCamera(cam: Camera, vb: Box, vp: Viewport): Camera;
68
+ /** Centre `focus` at scale k (clamped to the diagram). */
69
+ export declare function centreOn(vb: Box, vp: Viewport, focus: Box, k: number): Camera;
70
+ /**
71
+ * One follow move. The scale is the base (readable or the user's zoom), reduced only as far as
72
+ * needed for the focus to fit the dead zone. Inside the dead zone at that scale: stay. Otherwise
73
+ * centre on the focus (clamped to the diagram edges). A camera zoomed out for an earlier, larger
74
+ * focus zooms back to the base when the focus allows it.
75
+ */
76
+ export declare function followStep(cam: Camera, vb: Box, vp: Viewport, focus: Box | undefined, base?: number, dz?: number): Camera;
77
+ /** True once every event has settled: the camera shows the whole diagram again. */
78
+ export declare const cameraAtEnd: (tl: Timeline, t: number) => boolean;
79
+ /**
80
+ * Where the follow camera sits at story time t for a viewport and base scale (default: readable).
81
+ * Pure: plays the dead-zone moves forward from fit through step 0..i, so a capture at t equals the uninterrupted
82
+ * live camera. After the last event it is fit.
83
+ */
84
+ export declare function cameraAt(scene: Scene, tl: Timeline, t: number, vp: Viewport, k?: number): Camera;
@@ -3,6 +3,12 @@ import type { Frame, Timeline } from "./types.js";
3
3
  export interface StateOptions {
4
4
  /** Reduced motion: springs become steps, no pulses or glows. */
5
5
  reduced?: boolean;
6
+ /**
7
+ * Stepped playback (implies `reduced`): `t` names the step in effect (see `steppedTime`) and
8
+ * the frame is that step's settled state: its pulses have landed (wires drawn), while
9
+ * reveals, counters and captions of later steps have not begun.
10
+ */
11
+ stepped?: boolean;
6
12
  }
7
13
  /** The resting frame: everything shown, nothing moving. Equals the static diagram. */
8
14
  export declare function restFrame(t?: number): Frame;
@@ -34,3 +40,29 @@ export declare function beatTimes(tl: Timeline, max?: number): Beat[];
34
40
  export declare function beatCaption(tl: Timeline, frame: Frame, beat: Beat): string | undefined;
35
41
  /** Minimum beat-tile width for a diagram (wide diagrams get fewer, larger tiles). */
36
42
  export declare const beatTileMin: (w: number) => number;
43
+ /** Reduced-motion hold for a step without a caption (STYLE.md: a beat of ~1.2–1.8 s). */
44
+ export declare const STEP_BEAT = 1.5;
45
+ /** One stop of stepped (reduced-motion) playback: the settled time of a step and how long it holds. */
46
+ export interface SteppedStop {
47
+ /** Step index (-1 = before the story, `steps.length` = the final frame). */
48
+ step: number;
49
+ /** Story time whose reduced frame is this step's settled state. */
50
+ t: number;
51
+ /** Seconds to hold before advancing (0 for the final frame). */
52
+ hold: number;
53
+ }
54
+ /**
55
+ * Stepped playback schedule (reduced motion, "Play steps"): each step shown
56
+ * settled (its reveals, draws, counters and caption applied at once) for its
57
+ * reading time, then the final frame. Pure data: the same for every run.
58
+ */
59
+ export declare function steppedSchedule(tl: Timeline): SteppedStop[];
60
+ /** Index into `steppedSchedule` of the step in effect at `t` (-1 before the first step starts). */
61
+ export declare function steppedIndex(tl: Timeline, t: number): number;
62
+ /** Position in `steppedSchedule` of the stop in effect at `t` (-1 before the first step). */
63
+ export declare function steppedStop(tl: Timeline, t: number): number;
64
+ /**
65
+ * Quantise any `t` to the settled time of the step in effect (reduced motion).
66
+ * Before the first step: 0. At or after the end: the duration (final frame).
67
+ */
68
+ export declare function steppedTime(tl: Timeline, t: number): number;
@@ -55,6 +55,10 @@ export interface Timeline {
55
55
  duration: number;
56
56
  lastEvent: number;
57
57
  autoplay: boolean;
58
+ /** Author's playback motion ("full" default; "system" follows prefers-reduced-motion). */
59
+ motion: "full" | "reduced" | "system";
60
+ /** Author's viewer camera ("follow" default). */
61
+ camera?: "follow" | "fit";
58
62
  loop: boolean;
59
63
  steps: TimelineStep[];
60
64
  /** Element id → time it is revealed. Absent = visible from t = 0. */
@@ -1 +1 @@
1
- export declare const VERSION = "0.3.0";
1
+ export declare const VERSION = "0.3.2";