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.
- package/CHANGELOG.md +50 -0
- package/README.md +18 -2
- package/dist/chunks/{index-zjyrjkn6.js → index-5qmjsg8w.js} +36 -5
- package/dist/chunks/{index-060cd52n.js → index-n3gf4fyb.js} +558 -87
- package/dist/cli.js +611 -91
- package/dist/core/index.js +42 -2
- package/dist/index.js +63 -3
- package/dist/node/index.js +6 -2
- package/dist/types/core/index.d.ts +2 -1
- package/dist/types/core/render/App.d.ts +27 -0
- package/dist/types/core/render/Story.d.ts +2 -1
- package/dist/types/core/spec.d.ts +17 -1
- package/dist/types/core/story/camera.d.ts +84 -0
- package/dist/types/core/story/state.d.ts +32 -0
- package/dist/types/core/story/types.d.ts +4 -0
- package/dist/types/generated/meta.d.ts +1 -1
- package/dist/types/generated/viewer.d.ts +1 -1
- package/dist/types/node/index.d.ts +10 -0
- package/dist/types/node/snapshot.d.ts +8 -0
- package/docs/spec.md +77 -3
- package/package.json +3 -2
- package/schema/storyink.schema.json +165 -142
- package/skill/SKILL.md +8 -1
- /package/dist/chunks/{index-8fbs7b39.js → index-ygm89806.js} +0 -0
package/dist/core/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import"../chunks/index-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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,
|
package/dist/node/index.js
CHANGED
|
@@ -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-
|
|
12
|
-
import"../chunks/index-
|
|
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
|
-
|
|
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.
|
|
1
|
+
export declare const VERSION = "0.3.2";
|