sketchmark 1.3.5 → 1.3.7
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/README.md +20 -18
- package/dist/animation/index.d.ts +11 -1
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/index.cjs +200 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +200 -29
- package/dist/index.js.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +200 -29
- package/dist/ui/canvas.d.ts +1 -0
- package/dist/ui/canvas.d.ts.map +1 -1
- package/dist/ui/embed.d.ts +1 -0
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ const embed = new SketchmarkEmbed({
|
|
|
149
149
|
});
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
Use `SketchmarkCanvas` for the full playground-style surface, and `SketchmarkEmbed` for fixed-size embeds that clip overflow, auto-fit large diagrams, support drag-to-pan plus wheel/trackpad zoom, and expose built-in zoom, playback, caption, and TTS controls.
|
|
152
|
+
Use `SketchmarkCanvas` for the full playground-style surface, and `SketchmarkEmbed` for fixed-size embeds that clip overflow, auto-fit large diagrams, support drag-to-pan plus wheel/trackpad zoom, and expose built-in zoom, playback, caption, and TTS controls. While autoplay is running, their built-in `Play` control switches to `Stop` so you can hard-stop the sequence immediately.
|
|
153
153
|
|
|
154
154
|
---
|
|
155
155
|
|
|
@@ -854,17 +854,19 @@ anim.total // number of steps
|
|
|
854
854
|
anim.currentStep // current step index (-1 = before start)
|
|
855
855
|
anim.canNext // boolean
|
|
856
856
|
anim.canPrev // boolean
|
|
857
|
-
anim.atEnd // boolean
|
|
857
|
+
anim.atEnd // boolean
|
|
858
|
+
anim.isPlaying // boolean
|
|
858
859
|
anim.captionElement // HTMLDivElement | null — the narration caption element
|
|
859
860
|
anim.tts // boolean — text-to-speech enabled/disabled
|
|
860
861
|
|
|
861
862
|
// Methods
|
|
862
|
-
anim.next() // advance one step (returns bool)
|
|
863
|
-
anim.prev() // go back one step (returns bool)
|
|
864
|
-
anim.reset() // reset to before step 0
|
|
865
|
-
anim.goTo(index) // jump to step N
|
|
866
|
-
await anim.play(700) // play all remaining steps (700ms between)
|
|
867
|
-
anim.
|
|
863
|
+
anim.next() // advance one step (returns bool)
|
|
864
|
+
anim.prev() // go back one step (returns bool)
|
|
865
|
+
anim.reset() // reset to before step 0
|
|
866
|
+
anim.goTo(index) // jump to step N
|
|
867
|
+
await anim.play(700) // play all remaining steps (700ms between)
|
|
868
|
+
anim.stop() // hard-stop autoplay without resetting the current step
|
|
869
|
+
anim.destroy() // remove caption, annotations, pointer from DOM
|
|
868
870
|
|
|
869
871
|
// Toggle TTS programmatically
|
|
870
872
|
anim.tts = true; // enable browser speech
|
|
@@ -936,19 +938,19 @@ The pointer only appears during annotation steps — it follows the guide path a
|
|
|
936
938
|
|
|
937
939
|
Any element targeted by a `step draw` action starts **hidden** and only appears when that step fires. Elements NOT targeted by `draw` are always visible.
|
|
938
940
|
|
|
939
|
-
For groups, this applies to the whole subtree:
|
|
940
|
-
|
|
941
|
-
- `step draw group1` pre-hides the group and all descendant nodes, nested groups, tables, charts, notes, and
|
|
942
|
-
- When the group step fires, descendants without their own later `draw` step are revealed immediately.
|
|
943
|
-
- Descendants with an explicit later `draw` step stay hidden until that later step.
|
|
944
|
-
-
|
|
941
|
+
For groups, this applies to the whole subtree:
|
|
942
|
+
|
|
943
|
+
- `step draw group1` pre-hides the group and all descendant nodes, nested groups, tables, charts, notes, markdown blocks, and any edge whose endpoints stay inside that group subtree.
|
|
944
|
+
- When the group step fires, descendants without their own later `draw` step are revealed immediately.
|
|
945
|
+
- Descendants with an explicit later `draw` step stay hidden until that later step.
|
|
946
|
+
- Boundary-crossing edges are still independent; a group draw only cascades to edges whose endpoints share that group subtree.
|
|
945
947
|
|
|
946
948
|
For group targets, these actions also apply recursively to the same subtree:
|
|
947
949
|
|
|
948
|
-
- `fade` / `unfade`
|
|
949
|
-
- `show` / `hide`
|
|
950
|
-
- `erase`
|
|
951
|
-
-
|
|
950
|
+
- `fade` / `unfade`
|
|
951
|
+
- `show` / `hide`
|
|
952
|
+
- `erase`
|
|
953
|
+
- The same internal-edge rule applies here too; boundary-crossing edges remain explicit.
|
|
952
954
|
|
|
953
955
|
---
|
|
954
956
|
|
|
@@ -19,8 +19,12 @@ export declare class AnimationController {
|
|
|
19
19
|
private _rc?;
|
|
20
20
|
private _config?;
|
|
21
21
|
private _step;
|
|
22
|
+
private _isPlaying;
|
|
23
|
+
private _playRunId;
|
|
22
24
|
private _pendingStepTimers;
|
|
23
25
|
private _pendingNarrationTimers;
|
|
26
|
+
private _playbackDelayTimerId;
|
|
27
|
+
private _resolvePlaybackDelay;
|
|
24
28
|
private _transforms;
|
|
25
29
|
private _listeners;
|
|
26
30
|
readonly drawTargetEdges: Set<string>;
|
|
@@ -43,6 +47,7 @@ export declare class AnimationController {
|
|
|
43
47
|
private _pointerType;
|
|
44
48
|
private _tts;
|
|
45
49
|
private _speechDone;
|
|
50
|
+
private _resolveSpeechDone;
|
|
46
51
|
get drawTargets(): Set<string>;
|
|
47
52
|
constructor(svg: SVGSVGElement, steps: ASTStepItem[], _container?: HTMLElement | undefined, _rc?: any | undefined, _config?: Record<string, string | number | boolean> | undefined);
|
|
48
53
|
private _buildDrawStepIndex;
|
|
@@ -62,6 +67,7 @@ export declare class AnimationController {
|
|
|
62
67
|
get canNext(): boolean;
|
|
63
68
|
get canPrev(): boolean;
|
|
64
69
|
get atEnd(): boolean;
|
|
70
|
+
get isPlaying(): boolean;
|
|
65
71
|
on(listener: AnimationListener): () => void;
|
|
66
72
|
private emit;
|
|
67
73
|
reset(): void;
|
|
@@ -71,11 +77,15 @@ export declare class AnimationController {
|
|
|
71
77
|
prev(): boolean;
|
|
72
78
|
play(msPerStep?: number): Promise<void>;
|
|
73
79
|
goTo(index: number): void;
|
|
80
|
+
stop(): void;
|
|
81
|
+
private _advanceNext;
|
|
74
82
|
private _clearTimerBucket;
|
|
75
83
|
private _clearPendingStepTimers;
|
|
76
84
|
private _cancelNarrationTyping;
|
|
77
85
|
private _scheduleTimer;
|
|
78
86
|
private _scheduleStep;
|
|
87
|
+
private _waitForPlaybackDelay;
|
|
88
|
+
private _cancelPlaybackDelay;
|
|
79
89
|
private _stepWaitMs;
|
|
80
90
|
private _playbackWaitMs;
|
|
81
91
|
private _clearAll;
|
|
@@ -116,5 +126,5 @@ export declare class AnimationController {
|
|
|
116
126
|
private _doAnnotationBracket;
|
|
117
127
|
private _initPointer;
|
|
118
128
|
}
|
|
119
|
-
export declare const ANIMATION_CSS = "\n.ng, .gg, .tg, .ntg, .cg, .eg, .mdg {\n transform-box: fill-box;\n transform-origin: center;\n transition: filter 0.3s, opacity 0.35s;\n}\n\n/* highlight */\n.ng.hl path, .ng.hl rect, .ng.hl ellipse, .ng.hl polygon,\n.tg.hl path, .tg.hl rect,\n.ntg.hl path, .ntg.hl polygon,\n.cg.hl path, .cg.hl rect,\n.mdg.hl text,\n.eg.hl path, .eg.hl line, .eg.hl polygon { stroke-width: 2.8 !important; }\n\n.ng.hl, .tg.hl, .ntg.hl, .cg.hl, .mdg.hl, .eg.hl {\n animation: ng-pulse 1.4s ease-in-out infinite;\n}\n@keyframes ng-pulse {\n 0%, 100% { filter: drop-shadow(0 0 7px rgba(200,84,40,.6)); }\n 50% { filter: drop-shadow(0 0 14px rgba(200,84,40,.9)); }\n}\n\n/* fade */\n.ng.faded, .gg.faded, .tg.faded, .ntg.faded,\n.cg.faded, .eg.faded, .mdg.faded { opacity: 0.22; }\n\n.ng.hidden { opacity: 0; pointer-events: none; }\n.gg.gg-hidden { opacity: 0; }\n.tg.gg-hidden { opacity: 0; }\n.ntg.gg-hidden { opacity: 0; }\n.cg.gg-hidden { opacity: 0; }\n.mdg.gg-hidden { opacity: 0; }\n\n/* narration caption */\n.skm-caption { pointer-events: none; user-select: none; }\n";
|
|
129
|
+
export declare const ANIMATION_CSS = "\n.ng, .gg, .tg, .ntg, .cg, .eg, .mdg {\n transform-box: fill-box;\n transform-origin: center;\n transition: filter 0.3s, opacity 0.35s;\n}\n\n/* highlight */\n.ng.hl path, .ng.hl rect, .ng.hl ellipse, .ng.hl polygon,\n.tg.hl path, .tg.hl rect,\n.ntg.hl path, .ntg.hl polygon,\n.cg.hl path, .cg.hl rect,\n.mdg.hl text,\n.eg.hl path, .eg.hl line, .eg.hl polygon { stroke-width: 2.8 !important; }\n\n.ng.hl, .tg.hl, .ntg.hl, .cg.hl, .mdg.hl, .eg.hl {\n animation: ng-pulse 1.4s ease-in-out infinite;\n}\n@keyframes ng-pulse {\n 0%, 100% { filter: drop-shadow(0 0 7px rgba(200,84,40,.6)); }\n 50% { filter: drop-shadow(0 0 14px rgba(200,84,40,.9)); }\n}\n\n/* fade */\n.ng.faded, .gg.faded, .tg.faded, .ntg.faded,\n.cg.faded, .eg.faded, .mdg.faded { opacity: 0.22; }\n\n.ng.hidden { opacity: 0; pointer-events: none; }\n.gg.gg-hidden { opacity: 0; }\n.tg.gg-hidden { opacity: 0; }\n.ntg.gg-hidden { opacity: 0; }\n.cg.gg-hidden { opacity: 0; }\n.eg.gg-hidden { opacity: 0; }\n.mdg.gg-hidden { opacity: 0; }\n\n/* narration caption */\n.skm-caption { pointer-events: none; user-select: none; }\n";
|
|
120
130
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/animation/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,WAAW,EAAE,MAAM,cAAc,CAAC;AAGlE,MAAM,MAAM,kBAAkB,GAC1B,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,CAAC;AACpB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;AA+a5D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAQtE;AACD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAOtE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAOvE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAOtE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAOvE;AAsKD,qBAAa,mBAAmB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/animation/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -8280,6 +8280,36 @@ function setParentGroupData(el, groupId) {
|
|
|
8280
8280
|
if (groupId)
|
|
8281
8281
|
el.dataset.parentGroup = groupId;
|
|
8282
8282
|
}
|
|
8283
|
+
function resolveEdgeEndpointKind(id, nm, tm, gm, cm) {
|
|
8284
|
+
if (nm.has(id))
|
|
8285
|
+
return "node";
|
|
8286
|
+
if (gm.has(id))
|
|
8287
|
+
return "group";
|
|
8288
|
+
if (tm.has(id))
|
|
8289
|
+
return "table";
|
|
8290
|
+
if (cm.has(id))
|
|
8291
|
+
return "chart";
|
|
8292
|
+
return null;
|
|
8293
|
+
}
|
|
8294
|
+
function collectEdgeGroupLineage(endpointId, endpointKind, parentGroups) {
|
|
8295
|
+
const lineage = [];
|
|
8296
|
+
let groupId = endpointKind === "group"
|
|
8297
|
+
? endpointId
|
|
8298
|
+
: parentGroups.get(`${endpointKind}:${endpointId}`);
|
|
8299
|
+
while (groupId) {
|
|
8300
|
+
lineage.push(groupId);
|
|
8301
|
+
groupId = parentGroups.get(`group:${groupId}`);
|
|
8302
|
+
}
|
|
8303
|
+
return lineage;
|
|
8304
|
+
}
|
|
8305
|
+
function resolveEdgeParentGroupId(fromId, toId, nm, tm, gm, cm, parentGroups) {
|
|
8306
|
+
const fromKind = resolveEdgeEndpointKind(fromId, nm, tm, gm, cm);
|
|
8307
|
+
const toKind = resolveEdgeEndpointKind(toId, nm, tm, gm, cm);
|
|
8308
|
+
if (!fromKind || !toKind)
|
|
8309
|
+
return undefined;
|
|
8310
|
+
const toLineage = new Set(collectEdgeGroupLineage(toId, toKind, parentGroups));
|
|
8311
|
+
return collectEdgeGroupLineage(fromId, fromKind, parentGroups).find((groupId) => toLineage.has(groupId));
|
|
8312
|
+
}
|
|
8283
8313
|
// ── Node shapes ───────────────────────────────────────────────────────────
|
|
8284
8314
|
function renderShape$1(rc, n, palette) {
|
|
8285
8315
|
const s = n.style ?? {};
|
|
@@ -8437,6 +8467,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8437
8467
|
const [x1, y1] = getConnPoint(src, dstCX, dstCY, e.fromAnchor);
|
|
8438
8468
|
const [x2, y2] = getConnPoint(dst, srcCX, srcCY, e.toAnchor);
|
|
8439
8469
|
const eg = mkGroup(`edge-${e.from}-${e.to}`, "eg");
|
|
8470
|
+
setParentGroupData(eg, resolveEdgeParentGroupId(e.from, e.to, nm, tm, gmMap, cm, parentGroups));
|
|
8440
8471
|
if (e.style?.opacity != null)
|
|
8441
8472
|
eg.setAttribute("opacity", String(e.style.opacity));
|
|
8442
8473
|
const len = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) || 1;
|
|
@@ -9483,7 +9514,7 @@ const getTableEl = (svg, id) => getEl(svg, `table-${id}`);
|
|
|
9483
9514
|
const getNoteEl = (svg, id) => getEl(svg, `note-${id}`);
|
|
9484
9515
|
const getChartEl = (svg, id) => getEl(svg, `chart-${id}`);
|
|
9485
9516
|
const getMarkdownEl = (svg, id) => getEl(svg, `markdown-${id}`);
|
|
9486
|
-
const POSITIONABLE_SELECTOR = ".ng, .gg, .tg, .ntg, .cg, .mdg";
|
|
9517
|
+
const POSITIONABLE_SELECTOR = ".ng, .gg, .tg, .ntg, .cg, .eg, .mdg";
|
|
9487
9518
|
function resolveNonEdgeDrawEl(svg, target) {
|
|
9488
9519
|
return (getGroupEl(svg, target) ??
|
|
9489
9520
|
getTableEl(svg, target) ??
|
|
@@ -9991,8 +10022,12 @@ class AnimationController {
|
|
|
9991
10022
|
this._rc = _rc;
|
|
9992
10023
|
this._config = _config;
|
|
9993
10024
|
this._step = -1;
|
|
10025
|
+
this._isPlaying = false;
|
|
10026
|
+
this._playRunId = 0;
|
|
9994
10027
|
this._pendingStepTimers = new Set();
|
|
9995
10028
|
this._pendingNarrationTimers = new Set();
|
|
10029
|
+
this._playbackDelayTimerId = null;
|
|
10030
|
+
this._resolvePlaybackDelay = null;
|
|
9996
10031
|
this._transforms = new Map();
|
|
9997
10032
|
this._listeners = [];
|
|
9998
10033
|
// ── Narration caption ──
|
|
@@ -10008,6 +10043,7 @@ class AnimationController {
|
|
|
10008
10043
|
// ── TTS ──
|
|
10009
10044
|
this._tts = false;
|
|
10010
10045
|
this._speechDone = null;
|
|
10046
|
+
this._resolveSpeechDone = null;
|
|
10011
10047
|
this.drawTargetEdges = getDrawTargetEdgeIds(steps);
|
|
10012
10048
|
this.drawTargetNodes = getDrawTargetNodeIds(steps);
|
|
10013
10049
|
// Groups: non-edge draw steps whose target has a #group-{id} element in the SVG.
|
|
@@ -10072,8 +10108,16 @@ class AnimationController {
|
|
|
10072
10108
|
_buildDrawStepIndex() {
|
|
10073
10109
|
const drawStepIndexByElementId = new Map();
|
|
10074
10110
|
forEachPlaybackStep(this.steps, (step, stepIndex) => {
|
|
10075
|
-
if (step.action !== "draw"
|
|
10111
|
+
if (step.action !== "draw")
|
|
10112
|
+
return;
|
|
10113
|
+
const edge = parseEdgeTarget(step.target);
|
|
10114
|
+
if (edge) {
|
|
10115
|
+
const edgeEl = getEdgeEl(this.svg, edge.from, edge.to);
|
|
10116
|
+
if (edgeEl && !drawStepIndexByElementId.has(edgeEl.id)) {
|
|
10117
|
+
drawStepIndexByElementId.set(edgeEl.id, stepIndex);
|
|
10118
|
+
}
|
|
10076
10119
|
return;
|
|
10120
|
+
}
|
|
10077
10121
|
const el = resolveNonEdgeDrawEl(this.svg, step.target);
|
|
10078
10122
|
if (el && !drawStepIndexByElementId.has(el.id)) {
|
|
10079
10123
|
drawStepIndexByElementId.set(el.id, stepIndex);
|
|
@@ -10231,6 +10275,9 @@ class AnimationController {
|
|
|
10231
10275
|
get atEnd() {
|
|
10232
10276
|
return this._step === this.steps.length - 1;
|
|
10233
10277
|
}
|
|
10278
|
+
get isPlaying() {
|
|
10279
|
+
return this._isPlaying;
|
|
10280
|
+
}
|
|
10234
10281
|
on(listener) {
|
|
10235
10282
|
this._listeners.push(listener);
|
|
10236
10283
|
return () => {
|
|
@@ -10248,12 +10295,14 @@ class AnimationController {
|
|
|
10248
10295
|
l(e);
|
|
10249
10296
|
}
|
|
10250
10297
|
reset() {
|
|
10298
|
+
this.stop();
|
|
10251
10299
|
this._step = -1;
|
|
10252
10300
|
this._clearAll();
|
|
10253
10301
|
this.emit("animation-reset");
|
|
10254
10302
|
}
|
|
10255
10303
|
/** Remove caption and annotation layer from the DOM */
|
|
10256
10304
|
destroy() {
|
|
10305
|
+
this.stop();
|
|
10257
10306
|
this._clearAll();
|
|
10258
10307
|
this._captionEl?.remove();
|
|
10259
10308
|
this._captionEl = null;
|
|
@@ -10264,16 +10313,11 @@ class AnimationController {
|
|
|
10264
10313
|
this._pointerEl = null;
|
|
10265
10314
|
}
|
|
10266
10315
|
next() {
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
this._step++;
|
|
10270
|
-
this._applyStep(this._step, false);
|
|
10271
|
-
this.emit("step-change");
|
|
10272
|
-
if (!this.canNext)
|
|
10273
|
-
this.emit("animation-end");
|
|
10274
|
-
return true;
|
|
10316
|
+
this.stop();
|
|
10317
|
+
return this._advanceNext();
|
|
10275
10318
|
}
|
|
10276
10319
|
prev() {
|
|
10320
|
+
this.stop();
|
|
10277
10321
|
if (!this.canPrev)
|
|
10278
10322
|
return false;
|
|
10279
10323
|
this._step--;
|
|
@@ -10284,18 +10328,33 @@ class AnimationController {
|
|
|
10284
10328
|
return true;
|
|
10285
10329
|
}
|
|
10286
10330
|
async play(msPerStep = 900) {
|
|
10331
|
+
if (this._isPlaying || !this.canNext)
|
|
10332
|
+
return;
|
|
10333
|
+
const runId = ++this._playRunId;
|
|
10334
|
+
this._isPlaying = true;
|
|
10287
10335
|
this.emit("animation-start");
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10336
|
+
try {
|
|
10337
|
+
while (this.canNext && this._playRunId === runId) {
|
|
10338
|
+
const nextStep = this.steps[this._step + 1];
|
|
10339
|
+
if (!this._advanceNext())
|
|
10340
|
+
break;
|
|
10341
|
+
if (this._playRunId !== runId)
|
|
10342
|
+
break;
|
|
10343
|
+
await Promise.all([
|
|
10344
|
+
this._waitForPlaybackDelay(this._playbackWaitMs(nextStep, msPerStep)),
|
|
10345
|
+
this._speechDone ?? Promise.resolve(),
|
|
10346
|
+
]);
|
|
10347
|
+
}
|
|
10348
|
+
}
|
|
10349
|
+
finally {
|
|
10350
|
+
if (this._playRunId === runId) {
|
|
10351
|
+
this._isPlaying = false;
|
|
10352
|
+
this._cancelPlaybackDelay();
|
|
10353
|
+
}
|
|
10296
10354
|
}
|
|
10297
10355
|
}
|
|
10298
10356
|
goTo(index) {
|
|
10357
|
+
this.stop();
|
|
10299
10358
|
index = Math.max(-1, Math.min(this.steps.length - 1, index));
|
|
10300
10359
|
if (index === this._step)
|
|
10301
10360
|
return;
|
|
@@ -10309,6 +10368,30 @@ class AnimationController {
|
|
|
10309
10368
|
}
|
|
10310
10369
|
this.emit("step-change");
|
|
10311
10370
|
}
|
|
10371
|
+
stop() {
|
|
10372
|
+
if (!this._isPlaying && !this._resolvePlaybackDelay) {
|
|
10373
|
+
this._clearPendingStepTimers();
|
|
10374
|
+
this._cancelNarrationTyping();
|
|
10375
|
+
this._cancelSpeech();
|
|
10376
|
+
return;
|
|
10377
|
+
}
|
|
10378
|
+
this._isPlaying = false;
|
|
10379
|
+
this._playRunId += 1;
|
|
10380
|
+
this._cancelPlaybackDelay();
|
|
10381
|
+
this._clearPendingStepTimers();
|
|
10382
|
+
this._cancelNarrationTyping();
|
|
10383
|
+
this._cancelSpeech();
|
|
10384
|
+
}
|
|
10385
|
+
_advanceNext() {
|
|
10386
|
+
if (!this.canNext)
|
|
10387
|
+
return false;
|
|
10388
|
+
this._step++;
|
|
10389
|
+
this._applyStep(this._step, false);
|
|
10390
|
+
this.emit("step-change");
|
|
10391
|
+
if (!this.canNext)
|
|
10392
|
+
this.emit("animation-end");
|
|
10393
|
+
return true;
|
|
10394
|
+
}
|
|
10312
10395
|
_clearTimerBucket(bucket) {
|
|
10313
10396
|
bucket.forEach((id) => window.clearTimeout(id));
|
|
10314
10397
|
bucket.clear();
|
|
@@ -10334,6 +10417,34 @@ class AnimationController {
|
|
|
10334
10417
|
_scheduleStep(fn, delayMs) {
|
|
10335
10418
|
this._scheduleTimer(fn, delayMs, this._pendingStepTimers);
|
|
10336
10419
|
}
|
|
10420
|
+
_waitForPlaybackDelay(delayMs) {
|
|
10421
|
+
this._cancelPlaybackDelay();
|
|
10422
|
+
return new Promise((resolve) => {
|
|
10423
|
+
let settled = false;
|
|
10424
|
+
const finish = () => {
|
|
10425
|
+
if (settled)
|
|
10426
|
+
return;
|
|
10427
|
+
settled = true;
|
|
10428
|
+
if (this._playbackDelayTimerId !== null) {
|
|
10429
|
+
window.clearTimeout(this._playbackDelayTimerId);
|
|
10430
|
+
this._playbackDelayTimerId = null;
|
|
10431
|
+
}
|
|
10432
|
+
if (this._resolvePlaybackDelay === finish) {
|
|
10433
|
+
this._resolvePlaybackDelay = null;
|
|
10434
|
+
}
|
|
10435
|
+
resolve();
|
|
10436
|
+
};
|
|
10437
|
+
this._resolvePlaybackDelay = finish;
|
|
10438
|
+
if (delayMs <= 0) {
|
|
10439
|
+
finish();
|
|
10440
|
+
return;
|
|
10441
|
+
}
|
|
10442
|
+
this._playbackDelayTimerId = window.setTimeout(finish, delayMs);
|
|
10443
|
+
});
|
|
10444
|
+
}
|
|
10445
|
+
_cancelPlaybackDelay() {
|
|
10446
|
+
this._resolvePlaybackDelay?.();
|
|
10447
|
+
}
|
|
10337
10448
|
_stepWaitMs(step, fallbackMs) {
|
|
10338
10449
|
const delay = Math.max(0, step.delay ?? 0);
|
|
10339
10450
|
const duration = Math.max(0, step.duration ?? 0);
|
|
@@ -10375,6 +10486,7 @@ class AnimationController {
|
|
|
10375
10486
|
return this._stepWaitMs(step, fallbackMs);
|
|
10376
10487
|
}
|
|
10377
10488
|
_clearAll() {
|
|
10489
|
+
this._cancelPlaybackDelay();
|
|
10378
10490
|
this._clearPendingStepTimers();
|
|
10379
10491
|
this._cancelNarrationTyping();
|
|
10380
10492
|
this._cancelSpeech();
|
|
@@ -10705,6 +10817,7 @@ class AnimationController {
|
|
|
10705
10817
|
const el = getEdgeEl(this.svg, edge.from, edge.to);
|
|
10706
10818
|
if (!el)
|
|
10707
10819
|
return;
|
|
10820
|
+
showDrawEl(el);
|
|
10708
10821
|
if (silent) {
|
|
10709
10822
|
revealEdgeInstant(el);
|
|
10710
10823
|
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
@@ -10986,16 +11099,30 @@ class AnimationController {
|
|
|
10986
11099
|
utter.rate = 0.95;
|
|
10987
11100
|
utter.pitch = 1;
|
|
10988
11101
|
utter.lang = "en-US";
|
|
10989
|
-
// Track when speech actually finishes
|
|
11102
|
+
// Track when speech actually finishes so play() can block until the utterance ends.
|
|
10990
11103
|
this._speechDone = new Promise((resolve) => {
|
|
10991
|
-
|
|
10992
|
-
|
|
11104
|
+
let settled = false;
|
|
11105
|
+
const finish = () => {
|
|
11106
|
+
if (settled)
|
|
11107
|
+
return;
|
|
11108
|
+
settled = true;
|
|
11109
|
+
if (this._resolveSpeechDone === finish) {
|
|
11110
|
+
this._resolveSpeechDone = null;
|
|
11111
|
+
this._speechDone = null;
|
|
11112
|
+
}
|
|
11113
|
+
resolve();
|
|
11114
|
+
};
|
|
11115
|
+
this._resolveSpeechDone = finish;
|
|
11116
|
+
utter.onend = finish;
|
|
11117
|
+
utter.onerror = finish;
|
|
10993
11118
|
});
|
|
10994
11119
|
speechSynthesis.speak(utter);
|
|
10995
11120
|
}
|
|
10996
11121
|
_cancelSpeech() {
|
|
10997
11122
|
if (typeof speechSynthesis !== "undefined")
|
|
10998
11123
|
speechSynthesis.cancel();
|
|
11124
|
+
this._resolveSpeechDone?.();
|
|
11125
|
+
this._resolveSpeechDone = null;
|
|
10999
11126
|
this._speechDone = null;
|
|
11000
11127
|
}
|
|
11001
11128
|
/** Pre-warm the speech engine with a silent utterance to eliminate cold-start delay */
|
|
@@ -11304,11 +11431,12 @@ const ANIMATION_CSS = `
|
|
|
11304
11431
|
.cg.faded, .eg.faded, .mdg.faded { opacity: 0.22; }
|
|
11305
11432
|
|
|
11306
11433
|
.ng.hidden { opacity: 0; pointer-events: none; }
|
|
11307
|
-
.gg.gg-hidden { opacity: 0; }
|
|
11308
|
-
.tg.gg-hidden { opacity: 0; }
|
|
11309
|
-
.ntg.gg-hidden { opacity: 0; }
|
|
11310
|
-
.cg.gg-hidden { opacity: 0; }
|
|
11311
|
-
.
|
|
11434
|
+
.gg.gg-hidden { opacity: 0; }
|
|
11435
|
+
.tg.gg-hidden { opacity: 0; }
|
|
11436
|
+
.ntg.gg-hidden { opacity: 0; }
|
|
11437
|
+
.cg.gg-hidden { opacity: 0; }
|
|
11438
|
+
.eg.gg-hidden { opacity: 0; }
|
|
11439
|
+
.mdg.gg-hidden { opacity: 0; }
|
|
11312
11440
|
|
|
11313
11441
|
/* narration caption */
|
|
11314
11442
|
.skm-caption { pointer-events: none; user-select: none; }
|
|
@@ -11804,7 +11932,13 @@ class SketchmarkCanvas {
|
|
|
11804
11932
|
this.resetButton.addEventListener("click", () => this.resetAnimation());
|
|
11805
11933
|
this.prevButton.addEventListener("click", () => this.prevStep());
|
|
11806
11934
|
this.nextButton.addEventListener("click", () => this.nextStep());
|
|
11807
|
-
this.playButton.addEventListener("click", () =>
|
|
11935
|
+
this.playButton.addEventListener("click", () => {
|
|
11936
|
+
if (this.playInFlight) {
|
|
11937
|
+
this.stopPlayback();
|
|
11938
|
+
return;
|
|
11939
|
+
}
|
|
11940
|
+
void this.play();
|
|
11941
|
+
});
|
|
11808
11942
|
this.captionButton.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
11809
11943
|
this.ttsButton.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
|
|
11810
11944
|
this.viewport.addEventListener("pointerdown", this.onPointerDown);
|
|
@@ -11868,6 +12002,7 @@ class SketchmarkCanvas {
|
|
|
11868
12002
|
this.dsl = normalizeNewlines(nextDsl);
|
|
11869
12003
|
this.clearError();
|
|
11870
12004
|
this.mirroredEditor?.clearError();
|
|
12005
|
+
this.playInFlight = false;
|
|
11871
12006
|
this.animUnsub?.();
|
|
11872
12007
|
this.animUnsub = null;
|
|
11873
12008
|
this.instance?.anim?.destroy();
|
|
@@ -11938,9 +12073,16 @@ class SketchmarkCanvas {
|
|
|
11938
12073
|
this.syncAnimationUi();
|
|
11939
12074
|
}
|
|
11940
12075
|
}
|
|
12076
|
+
stopPlayback() {
|
|
12077
|
+
this.playInFlight = false;
|
|
12078
|
+
if (this.renderer === "svg")
|
|
12079
|
+
this.instance?.anim.stop();
|
|
12080
|
+
this.syncAnimationUi();
|
|
12081
|
+
}
|
|
11941
12082
|
nextStep() {
|
|
11942
12083
|
if (!this.instance || this.renderer !== "svg")
|
|
11943
12084
|
return;
|
|
12085
|
+
this.playInFlight = false;
|
|
11944
12086
|
this.instance.anim.next();
|
|
11945
12087
|
this.syncAnimationUi();
|
|
11946
12088
|
this.focusCurrentStep();
|
|
@@ -11948,6 +12090,7 @@ class SketchmarkCanvas {
|
|
|
11948
12090
|
prevStep() {
|
|
11949
12091
|
if (!this.instance || this.renderer !== "svg")
|
|
11950
12092
|
return;
|
|
12093
|
+
this.playInFlight = false;
|
|
11951
12094
|
this.instance.anim.prev();
|
|
11952
12095
|
this.syncAnimationUi();
|
|
11953
12096
|
this.focusCurrentStep();
|
|
@@ -11955,6 +12098,7 @@ class SketchmarkCanvas {
|
|
|
11955
12098
|
resetAnimation() {
|
|
11956
12099
|
if (!this.instance || this.renderer !== "svg")
|
|
11957
12100
|
return;
|
|
12101
|
+
this.playInFlight = false;
|
|
11958
12102
|
this.instance.anim.reset();
|
|
11959
12103
|
this.syncAnimationUi();
|
|
11960
12104
|
}
|
|
@@ -11983,6 +12127,7 @@ class SketchmarkCanvas {
|
|
|
11983
12127
|
this.render();
|
|
11984
12128
|
}
|
|
11985
12129
|
destroy() {
|
|
12130
|
+
this.playInFlight = false;
|
|
11986
12131
|
this.editorCleanup?.();
|
|
11987
12132
|
this.animUnsub?.();
|
|
11988
12133
|
this.instance?.anim?.destroy();
|
|
@@ -12057,6 +12202,9 @@ class SketchmarkCanvas {
|
|
|
12057
12202
|
this.prevButton.disabled = true;
|
|
12058
12203
|
this.nextButton.disabled = true;
|
|
12059
12204
|
this.resetButton.disabled = true;
|
|
12205
|
+
this.playButton.textContent = "Play";
|
|
12206
|
+
this.playButton.classList.remove("is-active");
|
|
12207
|
+
this.playButton.setAttribute("aria-pressed", "false");
|
|
12060
12208
|
this.playButton.disabled = true;
|
|
12061
12209
|
this.syncToggleUi();
|
|
12062
12210
|
return;
|
|
@@ -12066,7 +12214,10 @@ class SketchmarkCanvas {
|
|
|
12066
12214
|
this.prevButton.disabled = !anim.canPrev;
|
|
12067
12215
|
this.nextButton.disabled = !anim.canNext;
|
|
12068
12216
|
this.resetButton.disabled = false;
|
|
12069
|
-
this.playButton.
|
|
12217
|
+
this.playButton.textContent = this.playInFlight ? "Stop" : "Play";
|
|
12218
|
+
this.playButton.classList.toggle("is-active", this.playInFlight);
|
|
12219
|
+
this.playButton.setAttribute("aria-pressed", this.playInFlight ? "true" : "false");
|
|
12220
|
+
this.playButton.disabled = this.playInFlight ? false : !anim.canNext;
|
|
12070
12221
|
this.syncToggleUi();
|
|
12071
12222
|
}
|
|
12072
12223
|
getStepTarget(stepItem) {
|
|
@@ -12972,6 +13123,10 @@ class SketchmarkEmbed {
|
|
|
12972
13123
|
this.btnPrev.addEventListener("click", () => this.prevStep());
|
|
12973
13124
|
this.btnNext.addEventListener("click", () => this.nextStep());
|
|
12974
13125
|
this.btnPlay.addEventListener("click", () => {
|
|
13126
|
+
if (this.playInFlight) {
|
|
13127
|
+
this.stopPlayback();
|
|
13128
|
+
return;
|
|
13129
|
+
}
|
|
12975
13130
|
void this.play();
|
|
12976
13131
|
});
|
|
12977
13132
|
this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
@@ -13029,6 +13184,7 @@ class SketchmarkEmbed {
|
|
|
13029
13184
|
}
|
|
13030
13185
|
this.clearError();
|
|
13031
13186
|
this.stopMotion();
|
|
13187
|
+
this.playInFlight = false;
|
|
13032
13188
|
this.animUnsub?.();
|
|
13033
13189
|
this.animUnsub = null;
|
|
13034
13190
|
this.instance?.anim?.destroy();
|
|
@@ -13101,9 +13257,15 @@ class SketchmarkEmbed {
|
|
|
13101
13257
|
this.syncControls();
|
|
13102
13258
|
}
|
|
13103
13259
|
}
|
|
13260
|
+
stopPlayback() {
|
|
13261
|
+
this.playInFlight = false;
|
|
13262
|
+
this.instance?.anim.stop();
|
|
13263
|
+
this.syncControls();
|
|
13264
|
+
}
|
|
13104
13265
|
nextStep() {
|
|
13105
13266
|
if (!this.instance)
|
|
13106
13267
|
return;
|
|
13268
|
+
this.playInFlight = false;
|
|
13107
13269
|
this.instance.anim.next();
|
|
13108
13270
|
this.syncControls();
|
|
13109
13271
|
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
@@ -13113,6 +13275,7 @@ class SketchmarkEmbed {
|
|
|
13113
13275
|
prevStep() {
|
|
13114
13276
|
if (!this.instance)
|
|
13115
13277
|
return;
|
|
13278
|
+
this.playInFlight = false;
|
|
13116
13279
|
this.instance.anim.prev();
|
|
13117
13280
|
this.syncControls();
|
|
13118
13281
|
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
@@ -13122,6 +13285,7 @@ class SketchmarkEmbed {
|
|
|
13122
13285
|
resetAnimation() {
|
|
13123
13286
|
if (!this.instance)
|
|
13124
13287
|
return;
|
|
13288
|
+
this.playInFlight = false;
|
|
13125
13289
|
this.instance.anim.reset();
|
|
13126
13290
|
this.syncControls();
|
|
13127
13291
|
}
|
|
@@ -13148,6 +13312,7 @@ class SketchmarkEmbed {
|
|
|
13148
13312
|
}
|
|
13149
13313
|
destroy() {
|
|
13150
13314
|
this.stopMotion();
|
|
13315
|
+
this.playInFlight = false;
|
|
13151
13316
|
this.animUnsub?.();
|
|
13152
13317
|
this.instance?.anim?.destroy();
|
|
13153
13318
|
this.instance = null;
|
|
@@ -13180,6 +13345,9 @@ class SketchmarkEmbed {
|
|
|
13180
13345
|
this.btnRestart.disabled = true;
|
|
13181
13346
|
this.btnPrev.disabled = true;
|
|
13182
13347
|
this.btnNext.disabled = true;
|
|
13348
|
+
this.btnPlay.textContent = "Play";
|
|
13349
|
+
this.btnPlay.classList.remove("is-active");
|
|
13350
|
+
this.btnPlay.setAttribute("aria-pressed", "false");
|
|
13183
13351
|
this.btnPlay.disabled = true;
|
|
13184
13352
|
return;
|
|
13185
13353
|
}
|
|
@@ -13188,7 +13356,10 @@ class SketchmarkEmbed {
|
|
|
13188
13356
|
this.btnRestart.disabled = false;
|
|
13189
13357
|
this.btnPrev.disabled = !anim.canPrev;
|
|
13190
13358
|
this.btnNext.disabled = !anim.canNext;
|
|
13191
|
-
this.btnPlay.
|
|
13359
|
+
this.btnPlay.textContent = this.playInFlight ? "Stop" : "Play";
|
|
13360
|
+
this.btnPlay.classList.toggle("is-active", this.playInFlight);
|
|
13361
|
+
this.btnPlay.setAttribute("aria-pressed", this.playInFlight ? "true" : "false");
|
|
13362
|
+
this.btnPlay.disabled = this.playInFlight ? false : !anim.canNext;
|
|
13192
13363
|
}
|
|
13193
13364
|
syncViewControls() {
|
|
13194
13365
|
const hasView = !!this.instance?.svg;
|