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/dist/index.js
CHANGED
|
@@ -8278,6 +8278,36 @@ function setParentGroupData(el, groupId) {
|
|
|
8278
8278
|
if (groupId)
|
|
8279
8279
|
el.dataset.parentGroup = groupId;
|
|
8280
8280
|
}
|
|
8281
|
+
function resolveEdgeEndpointKind(id, nm, tm, gm, cm) {
|
|
8282
|
+
if (nm.has(id))
|
|
8283
|
+
return "node";
|
|
8284
|
+
if (gm.has(id))
|
|
8285
|
+
return "group";
|
|
8286
|
+
if (tm.has(id))
|
|
8287
|
+
return "table";
|
|
8288
|
+
if (cm.has(id))
|
|
8289
|
+
return "chart";
|
|
8290
|
+
return null;
|
|
8291
|
+
}
|
|
8292
|
+
function collectEdgeGroupLineage(endpointId, endpointKind, parentGroups) {
|
|
8293
|
+
const lineage = [];
|
|
8294
|
+
let groupId = endpointKind === "group"
|
|
8295
|
+
? endpointId
|
|
8296
|
+
: parentGroups.get(`${endpointKind}:${endpointId}`);
|
|
8297
|
+
while (groupId) {
|
|
8298
|
+
lineage.push(groupId);
|
|
8299
|
+
groupId = parentGroups.get(`group:${groupId}`);
|
|
8300
|
+
}
|
|
8301
|
+
return lineage;
|
|
8302
|
+
}
|
|
8303
|
+
function resolveEdgeParentGroupId(fromId, toId, nm, tm, gm, cm, parentGroups) {
|
|
8304
|
+
const fromKind = resolveEdgeEndpointKind(fromId, nm, tm, gm, cm);
|
|
8305
|
+
const toKind = resolveEdgeEndpointKind(toId, nm, tm, gm, cm);
|
|
8306
|
+
if (!fromKind || !toKind)
|
|
8307
|
+
return undefined;
|
|
8308
|
+
const toLineage = new Set(collectEdgeGroupLineage(toId, toKind, parentGroups));
|
|
8309
|
+
return collectEdgeGroupLineage(fromId, fromKind, parentGroups).find((groupId) => toLineage.has(groupId));
|
|
8310
|
+
}
|
|
8281
8311
|
// ── Node shapes ───────────────────────────────────────────────────────────
|
|
8282
8312
|
function renderShape$1(rc, n, palette) {
|
|
8283
8313
|
const s = n.style ?? {};
|
|
@@ -8435,6 +8465,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8435
8465
|
const [x1, y1] = getConnPoint(src, dstCX, dstCY, e.fromAnchor);
|
|
8436
8466
|
const [x2, y2] = getConnPoint(dst, srcCX, srcCY, e.toAnchor);
|
|
8437
8467
|
const eg = mkGroup(`edge-${e.from}-${e.to}`, "eg");
|
|
8468
|
+
setParentGroupData(eg, resolveEdgeParentGroupId(e.from, e.to, nm, tm, gmMap, cm, parentGroups));
|
|
8438
8469
|
if (e.style?.opacity != null)
|
|
8439
8470
|
eg.setAttribute("opacity", String(e.style.opacity));
|
|
8440
8471
|
const len = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) || 1;
|
|
@@ -9481,7 +9512,7 @@ const getTableEl = (svg, id) => getEl(svg, `table-${id}`);
|
|
|
9481
9512
|
const getNoteEl = (svg, id) => getEl(svg, `note-${id}`);
|
|
9482
9513
|
const getChartEl = (svg, id) => getEl(svg, `chart-${id}`);
|
|
9483
9514
|
const getMarkdownEl = (svg, id) => getEl(svg, `markdown-${id}`);
|
|
9484
|
-
const POSITIONABLE_SELECTOR = ".ng, .gg, .tg, .ntg, .cg, .mdg";
|
|
9515
|
+
const POSITIONABLE_SELECTOR = ".ng, .gg, .tg, .ntg, .cg, .eg, .mdg";
|
|
9485
9516
|
function resolveNonEdgeDrawEl(svg, target) {
|
|
9486
9517
|
return (getGroupEl(svg, target) ??
|
|
9487
9518
|
getTableEl(svg, target) ??
|
|
@@ -9989,8 +10020,12 @@ class AnimationController {
|
|
|
9989
10020
|
this._rc = _rc;
|
|
9990
10021
|
this._config = _config;
|
|
9991
10022
|
this._step = -1;
|
|
10023
|
+
this._isPlaying = false;
|
|
10024
|
+
this._playRunId = 0;
|
|
9992
10025
|
this._pendingStepTimers = new Set();
|
|
9993
10026
|
this._pendingNarrationTimers = new Set();
|
|
10027
|
+
this._playbackDelayTimerId = null;
|
|
10028
|
+
this._resolvePlaybackDelay = null;
|
|
9994
10029
|
this._transforms = new Map();
|
|
9995
10030
|
this._listeners = [];
|
|
9996
10031
|
// ── Narration caption ──
|
|
@@ -10006,6 +10041,7 @@ class AnimationController {
|
|
|
10006
10041
|
// ── TTS ──
|
|
10007
10042
|
this._tts = false;
|
|
10008
10043
|
this._speechDone = null;
|
|
10044
|
+
this._resolveSpeechDone = null;
|
|
10009
10045
|
this.drawTargetEdges = getDrawTargetEdgeIds(steps);
|
|
10010
10046
|
this.drawTargetNodes = getDrawTargetNodeIds(steps);
|
|
10011
10047
|
// Groups: non-edge draw steps whose target has a #group-{id} element in the SVG.
|
|
@@ -10070,8 +10106,16 @@ class AnimationController {
|
|
|
10070
10106
|
_buildDrawStepIndex() {
|
|
10071
10107
|
const drawStepIndexByElementId = new Map();
|
|
10072
10108
|
forEachPlaybackStep(this.steps, (step, stepIndex) => {
|
|
10073
|
-
if (step.action !== "draw"
|
|
10109
|
+
if (step.action !== "draw")
|
|
10110
|
+
return;
|
|
10111
|
+
const edge = parseEdgeTarget(step.target);
|
|
10112
|
+
if (edge) {
|
|
10113
|
+
const edgeEl = getEdgeEl(this.svg, edge.from, edge.to);
|
|
10114
|
+
if (edgeEl && !drawStepIndexByElementId.has(edgeEl.id)) {
|
|
10115
|
+
drawStepIndexByElementId.set(edgeEl.id, stepIndex);
|
|
10116
|
+
}
|
|
10074
10117
|
return;
|
|
10118
|
+
}
|
|
10075
10119
|
const el = resolveNonEdgeDrawEl(this.svg, step.target);
|
|
10076
10120
|
if (el && !drawStepIndexByElementId.has(el.id)) {
|
|
10077
10121
|
drawStepIndexByElementId.set(el.id, stepIndex);
|
|
@@ -10229,6 +10273,9 @@ class AnimationController {
|
|
|
10229
10273
|
get atEnd() {
|
|
10230
10274
|
return this._step === this.steps.length - 1;
|
|
10231
10275
|
}
|
|
10276
|
+
get isPlaying() {
|
|
10277
|
+
return this._isPlaying;
|
|
10278
|
+
}
|
|
10232
10279
|
on(listener) {
|
|
10233
10280
|
this._listeners.push(listener);
|
|
10234
10281
|
return () => {
|
|
@@ -10246,12 +10293,14 @@ class AnimationController {
|
|
|
10246
10293
|
l(e);
|
|
10247
10294
|
}
|
|
10248
10295
|
reset() {
|
|
10296
|
+
this.stop();
|
|
10249
10297
|
this._step = -1;
|
|
10250
10298
|
this._clearAll();
|
|
10251
10299
|
this.emit("animation-reset");
|
|
10252
10300
|
}
|
|
10253
10301
|
/** Remove caption and annotation layer from the DOM */
|
|
10254
10302
|
destroy() {
|
|
10303
|
+
this.stop();
|
|
10255
10304
|
this._clearAll();
|
|
10256
10305
|
this._captionEl?.remove();
|
|
10257
10306
|
this._captionEl = null;
|
|
@@ -10262,16 +10311,11 @@ class AnimationController {
|
|
|
10262
10311
|
this._pointerEl = null;
|
|
10263
10312
|
}
|
|
10264
10313
|
next() {
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
this._step++;
|
|
10268
|
-
this._applyStep(this._step, false);
|
|
10269
|
-
this.emit("step-change");
|
|
10270
|
-
if (!this.canNext)
|
|
10271
|
-
this.emit("animation-end");
|
|
10272
|
-
return true;
|
|
10314
|
+
this.stop();
|
|
10315
|
+
return this._advanceNext();
|
|
10273
10316
|
}
|
|
10274
10317
|
prev() {
|
|
10318
|
+
this.stop();
|
|
10275
10319
|
if (!this.canPrev)
|
|
10276
10320
|
return false;
|
|
10277
10321
|
this._step--;
|
|
@@ -10282,18 +10326,33 @@ class AnimationController {
|
|
|
10282
10326
|
return true;
|
|
10283
10327
|
}
|
|
10284
10328
|
async play(msPerStep = 900) {
|
|
10329
|
+
if (this._isPlaying || !this.canNext)
|
|
10330
|
+
return;
|
|
10331
|
+
const runId = ++this._playRunId;
|
|
10332
|
+
this._isPlaying = true;
|
|
10285
10333
|
this.emit("animation-start");
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10334
|
+
try {
|
|
10335
|
+
while (this.canNext && this._playRunId === runId) {
|
|
10336
|
+
const nextStep = this.steps[this._step + 1];
|
|
10337
|
+
if (!this._advanceNext())
|
|
10338
|
+
break;
|
|
10339
|
+
if (this._playRunId !== runId)
|
|
10340
|
+
break;
|
|
10341
|
+
await Promise.all([
|
|
10342
|
+
this._waitForPlaybackDelay(this._playbackWaitMs(nextStep, msPerStep)),
|
|
10343
|
+
this._speechDone ?? Promise.resolve(),
|
|
10344
|
+
]);
|
|
10345
|
+
}
|
|
10346
|
+
}
|
|
10347
|
+
finally {
|
|
10348
|
+
if (this._playRunId === runId) {
|
|
10349
|
+
this._isPlaying = false;
|
|
10350
|
+
this._cancelPlaybackDelay();
|
|
10351
|
+
}
|
|
10294
10352
|
}
|
|
10295
10353
|
}
|
|
10296
10354
|
goTo(index) {
|
|
10355
|
+
this.stop();
|
|
10297
10356
|
index = Math.max(-1, Math.min(this.steps.length - 1, index));
|
|
10298
10357
|
if (index === this._step)
|
|
10299
10358
|
return;
|
|
@@ -10307,6 +10366,30 @@ class AnimationController {
|
|
|
10307
10366
|
}
|
|
10308
10367
|
this.emit("step-change");
|
|
10309
10368
|
}
|
|
10369
|
+
stop() {
|
|
10370
|
+
if (!this._isPlaying && !this._resolvePlaybackDelay) {
|
|
10371
|
+
this._clearPendingStepTimers();
|
|
10372
|
+
this._cancelNarrationTyping();
|
|
10373
|
+
this._cancelSpeech();
|
|
10374
|
+
return;
|
|
10375
|
+
}
|
|
10376
|
+
this._isPlaying = false;
|
|
10377
|
+
this._playRunId += 1;
|
|
10378
|
+
this._cancelPlaybackDelay();
|
|
10379
|
+
this._clearPendingStepTimers();
|
|
10380
|
+
this._cancelNarrationTyping();
|
|
10381
|
+
this._cancelSpeech();
|
|
10382
|
+
}
|
|
10383
|
+
_advanceNext() {
|
|
10384
|
+
if (!this.canNext)
|
|
10385
|
+
return false;
|
|
10386
|
+
this._step++;
|
|
10387
|
+
this._applyStep(this._step, false);
|
|
10388
|
+
this.emit("step-change");
|
|
10389
|
+
if (!this.canNext)
|
|
10390
|
+
this.emit("animation-end");
|
|
10391
|
+
return true;
|
|
10392
|
+
}
|
|
10310
10393
|
_clearTimerBucket(bucket) {
|
|
10311
10394
|
bucket.forEach((id) => window.clearTimeout(id));
|
|
10312
10395
|
bucket.clear();
|
|
@@ -10332,6 +10415,34 @@ class AnimationController {
|
|
|
10332
10415
|
_scheduleStep(fn, delayMs) {
|
|
10333
10416
|
this._scheduleTimer(fn, delayMs, this._pendingStepTimers);
|
|
10334
10417
|
}
|
|
10418
|
+
_waitForPlaybackDelay(delayMs) {
|
|
10419
|
+
this._cancelPlaybackDelay();
|
|
10420
|
+
return new Promise((resolve) => {
|
|
10421
|
+
let settled = false;
|
|
10422
|
+
const finish = () => {
|
|
10423
|
+
if (settled)
|
|
10424
|
+
return;
|
|
10425
|
+
settled = true;
|
|
10426
|
+
if (this._playbackDelayTimerId !== null) {
|
|
10427
|
+
window.clearTimeout(this._playbackDelayTimerId);
|
|
10428
|
+
this._playbackDelayTimerId = null;
|
|
10429
|
+
}
|
|
10430
|
+
if (this._resolvePlaybackDelay === finish) {
|
|
10431
|
+
this._resolvePlaybackDelay = null;
|
|
10432
|
+
}
|
|
10433
|
+
resolve();
|
|
10434
|
+
};
|
|
10435
|
+
this._resolvePlaybackDelay = finish;
|
|
10436
|
+
if (delayMs <= 0) {
|
|
10437
|
+
finish();
|
|
10438
|
+
return;
|
|
10439
|
+
}
|
|
10440
|
+
this._playbackDelayTimerId = window.setTimeout(finish, delayMs);
|
|
10441
|
+
});
|
|
10442
|
+
}
|
|
10443
|
+
_cancelPlaybackDelay() {
|
|
10444
|
+
this._resolvePlaybackDelay?.();
|
|
10445
|
+
}
|
|
10335
10446
|
_stepWaitMs(step, fallbackMs) {
|
|
10336
10447
|
const delay = Math.max(0, step.delay ?? 0);
|
|
10337
10448
|
const duration = Math.max(0, step.duration ?? 0);
|
|
@@ -10373,6 +10484,7 @@ class AnimationController {
|
|
|
10373
10484
|
return this._stepWaitMs(step, fallbackMs);
|
|
10374
10485
|
}
|
|
10375
10486
|
_clearAll() {
|
|
10487
|
+
this._cancelPlaybackDelay();
|
|
10376
10488
|
this._clearPendingStepTimers();
|
|
10377
10489
|
this._cancelNarrationTyping();
|
|
10378
10490
|
this._cancelSpeech();
|
|
@@ -10703,6 +10815,7 @@ class AnimationController {
|
|
|
10703
10815
|
const el = getEdgeEl(this.svg, edge.from, edge.to);
|
|
10704
10816
|
if (!el)
|
|
10705
10817
|
return;
|
|
10818
|
+
showDrawEl(el);
|
|
10706
10819
|
if (silent) {
|
|
10707
10820
|
revealEdgeInstant(el);
|
|
10708
10821
|
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
@@ -10984,16 +11097,30 @@ class AnimationController {
|
|
|
10984
11097
|
utter.rate = 0.95;
|
|
10985
11098
|
utter.pitch = 1;
|
|
10986
11099
|
utter.lang = "en-US";
|
|
10987
|
-
// Track when speech actually finishes
|
|
11100
|
+
// Track when speech actually finishes so play() can block until the utterance ends.
|
|
10988
11101
|
this._speechDone = new Promise((resolve) => {
|
|
10989
|
-
|
|
10990
|
-
|
|
11102
|
+
let settled = false;
|
|
11103
|
+
const finish = () => {
|
|
11104
|
+
if (settled)
|
|
11105
|
+
return;
|
|
11106
|
+
settled = true;
|
|
11107
|
+
if (this._resolveSpeechDone === finish) {
|
|
11108
|
+
this._resolveSpeechDone = null;
|
|
11109
|
+
this._speechDone = null;
|
|
11110
|
+
}
|
|
11111
|
+
resolve();
|
|
11112
|
+
};
|
|
11113
|
+
this._resolveSpeechDone = finish;
|
|
11114
|
+
utter.onend = finish;
|
|
11115
|
+
utter.onerror = finish;
|
|
10991
11116
|
});
|
|
10992
11117
|
speechSynthesis.speak(utter);
|
|
10993
11118
|
}
|
|
10994
11119
|
_cancelSpeech() {
|
|
10995
11120
|
if (typeof speechSynthesis !== "undefined")
|
|
10996
11121
|
speechSynthesis.cancel();
|
|
11122
|
+
this._resolveSpeechDone?.();
|
|
11123
|
+
this._resolveSpeechDone = null;
|
|
10997
11124
|
this._speechDone = null;
|
|
10998
11125
|
}
|
|
10999
11126
|
/** Pre-warm the speech engine with a silent utterance to eliminate cold-start delay */
|
|
@@ -11302,11 +11429,12 @@ const ANIMATION_CSS = `
|
|
|
11302
11429
|
.cg.faded, .eg.faded, .mdg.faded { opacity: 0.22; }
|
|
11303
11430
|
|
|
11304
11431
|
.ng.hidden { opacity: 0; pointer-events: none; }
|
|
11305
|
-
.gg.gg-hidden { opacity: 0; }
|
|
11306
|
-
.tg.gg-hidden { opacity: 0; }
|
|
11307
|
-
.ntg.gg-hidden { opacity: 0; }
|
|
11308
|
-
.cg.gg-hidden { opacity: 0; }
|
|
11309
|
-
.
|
|
11432
|
+
.gg.gg-hidden { opacity: 0; }
|
|
11433
|
+
.tg.gg-hidden { opacity: 0; }
|
|
11434
|
+
.ntg.gg-hidden { opacity: 0; }
|
|
11435
|
+
.cg.gg-hidden { opacity: 0; }
|
|
11436
|
+
.eg.gg-hidden { opacity: 0; }
|
|
11437
|
+
.mdg.gg-hidden { opacity: 0; }
|
|
11310
11438
|
|
|
11311
11439
|
/* narration caption */
|
|
11312
11440
|
.skm-caption { pointer-events: none; user-select: none; }
|
|
@@ -11802,7 +11930,13 @@ class SketchmarkCanvas {
|
|
|
11802
11930
|
this.resetButton.addEventListener("click", () => this.resetAnimation());
|
|
11803
11931
|
this.prevButton.addEventListener("click", () => this.prevStep());
|
|
11804
11932
|
this.nextButton.addEventListener("click", () => this.nextStep());
|
|
11805
|
-
this.playButton.addEventListener("click", () =>
|
|
11933
|
+
this.playButton.addEventListener("click", () => {
|
|
11934
|
+
if (this.playInFlight) {
|
|
11935
|
+
this.stopPlayback();
|
|
11936
|
+
return;
|
|
11937
|
+
}
|
|
11938
|
+
void this.play();
|
|
11939
|
+
});
|
|
11806
11940
|
this.captionButton.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
11807
11941
|
this.ttsButton.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
|
|
11808
11942
|
this.viewport.addEventListener("pointerdown", this.onPointerDown);
|
|
@@ -11866,6 +12000,7 @@ class SketchmarkCanvas {
|
|
|
11866
12000
|
this.dsl = normalizeNewlines(nextDsl);
|
|
11867
12001
|
this.clearError();
|
|
11868
12002
|
this.mirroredEditor?.clearError();
|
|
12003
|
+
this.playInFlight = false;
|
|
11869
12004
|
this.animUnsub?.();
|
|
11870
12005
|
this.animUnsub = null;
|
|
11871
12006
|
this.instance?.anim?.destroy();
|
|
@@ -11936,9 +12071,16 @@ class SketchmarkCanvas {
|
|
|
11936
12071
|
this.syncAnimationUi();
|
|
11937
12072
|
}
|
|
11938
12073
|
}
|
|
12074
|
+
stopPlayback() {
|
|
12075
|
+
this.playInFlight = false;
|
|
12076
|
+
if (this.renderer === "svg")
|
|
12077
|
+
this.instance?.anim.stop();
|
|
12078
|
+
this.syncAnimationUi();
|
|
12079
|
+
}
|
|
11939
12080
|
nextStep() {
|
|
11940
12081
|
if (!this.instance || this.renderer !== "svg")
|
|
11941
12082
|
return;
|
|
12083
|
+
this.playInFlight = false;
|
|
11942
12084
|
this.instance.anim.next();
|
|
11943
12085
|
this.syncAnimationUi();
|
|
11944
12086
|
this.focusCurrentStep();
|
|
@@ -11946,6 +12088,7 @@ class SketchmarkCanvas {
|
|
|
11946
12088
|
prevStep() {
|
|
11947
12089
|
if (!this.instance || this.renderer !== "svg")
|
|
11948
12090
|
return;
|
|
12091
|
+
this.playInFlight = false;
|
|
11949
12092
|
this.instance.anim.prev();
|
|
11950
12093
|
this.syncAnimationUi();
|
|
11951
12094
|
this.focusCurrentStep();
|
|
@@ -11953,6 +12096,7 @@ class SketchmarkCanvas {
|
|
|
11953
12096
|
resetAnimation() {
|
|
11954
12097
|
if (!this.instance || this.renderer !== "svg")
|
|
11955
12098
|
return;
|
|
12099
|
+
this.playInFlight = false;
|
|
11956
12100
|
this.instance.anim.reset();
|
|
11957
12101
|
this.syncAnimationUi();
|
|
11958
12102
|
}
|
|
@@ -11981,6 +12125,7 @@ class SketchmarkCanvas {
|
|
|
11981
12125
|
this.render();
|
|
11982
12126
|
}
|
|
11983
12127
|
destroy() {
|
|
12128
|
+
this.playInFlight = false;
|
|
11984
12129
|
this.editorCleanup?.();
|
|
11985
12130
|
this.animUnsub?.();
|
|
11986
12131
|
this.instance?.anim?.destroy();
|
|
@@ -12055,6 +12200,9 @@ class SketchmarkCanvas {
|
|
|
12055
12200
|
this.prevButton.disabled = true;
|
|
12056
12201
|
this.nextButton.disabled = true;
|
|
12057
12202
|
this.resetButton.disabled = true;
|
|
12203
|
+
this.playButton.textContent = "Play";
|
|
12204
|
+
this.playButton.classList.remove("is-active");
|
|
12205
|
+
this.playButton.setAttribute("aria-pressed", "false");
|
|
12058
12206
|
this.playButton.disabled = true;
|
|
12059
12207
|
this.syncToggleUi();
|
|
12060
12208
|
return;
|
|
@@ -12064,7 +12212,10 @@ class SketchmarkCanvas {
|
|
|
12064
12212
|
this.prevButton.disabled = !anim.canPrev;
|
|
12065
12213
|
this.nextButton.disabled = !anim.canNext;
|
|
12066
12214
|
this.resetButton.disabled = false;
|
|
12067
|
-
this.playButton.
|
|
12215
|
+
this.playButton.textContent = this.playInFlight ? "Stop" : "Play";
|
|
12216
|
+
this.playButton.classList.toggle("is-active", this.playInFlight);
|
|
12217
|
+
this.playButton.setAttribute("aria-pressed", this.playInFlight ? "true" : "false");
|
|
12218
|
+
this.playButton.disabled = this.playInFlight ? false : !anim.canNext;
|
|
12068
12219
|
this.syncToggleUi();
|
|
12069
12220
|
}
|
|
12070
12221
|
getStepTarget(stepItem) {
|
|
@@ -12970,6 +13121,10 @@ class SketchmarkEmbed {
|
|
|
12970
13121
|
this.btnPrev.addEventListener("click", () => this.prevStep());
|
|
12971
13122
|
this.btnNext.addEventListener("click", () => this.nextStep());
|
|
12972
13123
|
this.btnPlay.addEventListener("click", () => {
|
|
13124
|
+
if (this.playInFlight) {
|
|
13125
|
+
this.stopPlayback();
|
|
13126
|
+
return;
|
|
13127
|
+
}
|
|
12973
13128
|
void this.play();
|
|
12974
13129
|
});
|
|
12975
13130
|
this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
@@ -13027,6 +13182,7 @@ class SketchmarkEmbed {
|
|
|
13027
13182
|
}
|
|
13028
13183
|
this.clearError();
|
|
13029
13184
|
this.stopMotion();
|
|
13185
|
+
this.playInFlight = false;
|
|
13030
13186
|
this.animUnsub?.();
|
|
13031
13187
|
this.animUnsub = null;
|
|
13032
13188
|
this.instance?.anim?.destroy();
|
|
@@ -13099,9 +13255,15 @@ class SketchmarkEmbed {
|
|
|
13099
13255
|
this.syncControls();
|
|
13100
13256
|
}
|
|
13101
13257
|
}
|
|
13258
|
+
stopPlayback() {
|
|
13259
|
+
this.playInFlight = false;
|
|
13260
|
+
this.instance?.anim.stop();
|
|
13261
|
+
this.syncControls();
|
|
13262
|
+
}
|
|
13102
13263
|
nextStep() {
|
|
13103
13264
|
if (!this.instance)
|
|
13104
13265
|
return;
|
|
13266
|
+
this.playInFlight = false;
|
|
13105
13267
|
this.instance.anim.next();
|
|
13106
13268
|
this.syncControls();
|
|
13107
13269
|
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
@@ -13111,6 +13273,7 @@ class SketchmarkEmbed {
|
|
|
13111
13273
|
prevStep() {
|
|
13112
13274
|
if (!this.instance)
|
|
13113
13275
|
return;
|
|
13276
|
+
this.playInFlight = false;
|
|
13114
13277
|
this.instance.anim.prev();
|
|
13115
13278
|
this.syncControls();
|
|
13116
13279
|
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
@@ -13120,6 +13283,7 @@ class SketchmarkEmbed {
|
|
|
13120
13283
|
resetAnimation() {
|
|
13121
13284
|
if (!this.instance)
|
|
13122
13285
|
return;
|
|
13286
|
+
this.playInFlight = false;
|
|
13123
13287
|
this.instance.anim.reset();
|
|
13124
13288
|
this.syncControls();
|
|
13125
13289
|
}
|
|
@@ -13146,6 +13310,7 @@ class SketchmarkEmbed {
|
|
|
13146
13310
|
}
|
|
13147
13311
|
destroy() {
|
|
13148
13312
|
this.stopMotion();
|
|
13313
|
+
this.playInFlight = false;
|
|
13149
13314
|
this.animUnsub?.();
|
|
13150
13315
|
this.instance?.anim?.destroy();
|
|
13151
13316
|
this.instance = null;
|
|
@@ -13178,6 +13343,9 @@ class SketchmarkEmbed {
|
|
|
13178
13343
|
this.btnRestart.disabled = true;
|
|
13179
13344
|
this.btnPrev.disabled = true;
|
|
13180
13345
|
this.btnNext.disabled = true;
|
|
13346
|
+
this.btnPlay.textContent = "Play";
|
|
13347
|
+
this.btnPlay.classList.remove("is-active");
|
|
13348
|
+
this.btnPlay.setAttribute("aria-pressed", "false");
|
|
13181
13349
|
this.btnPlay.disabled = true;
|
|
13182
13350
|
return;
|
|
13183
13351
|
}
|
|
@@ -13186,7 +13354,10 @@ class SketchmarkEmbed {
|
|
|
13186
13354
|
this.btnRestart.disabled = false;
|
|
13187
13355
|
this.btnPrev.disabled = !anim.canPrev;
|
|
13188
13356
|
this.btnNext.disabled = !anim.canNext;
|
|
13189
|
-
this.btnPlay.
|
|
13357
|
+
this.btnPlay.textContent = this.playInFlight ? "Stop" : "Play";
|
|
13358
|
+
this.btnPlay.classList.toggle("is-active", this.playInFlight);
|
|
13359
|
+
this.btnPlay.setAttribute("aria-pressed", this.playInFlight ? "true" : "false");
|
|
13360
|
+
this.btnPlay.disabled = this.playInFlight ? false : !anim.canNext;
|
|
13190
13361
|
}
|
|
13191
13362
|
syncViewControls() {
|
|
13192
13363
|
const hasView = !!this.instance?.svg;
|