sketchmark 1.1.3 → 1.1.5
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.
Potentially problematic release.
This version of sketchmark might be problematic. Click here for more details.
- package/README.md +149 -132
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/index.cjs +366 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +366 -71
- package/dist/index.js.map +1 -1
- package/dist/render.d.ts +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +366 -71
- package/dist/ui/canvas.d.ts +12 -0
- package/dist/ui/canvas.d.ts.map +1 -1
- package/dist/ui/embed.d.ts +36 -1
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/sketchmark.iife.js
CHANGED
|
@@ -8808,6 +8808,7 @@ var AIDiagram = (function (exports) {
|
|
|
8808
8808
|
}, delayMs);
|
|
8809
8809
|
}
|
|
8810
8810
|
const NODE_DRAW_GUIDE_ATTR = "data-node-draw-guide";
|
|
8811
|
+
const TEXT_REVEAL_CLIP_ATTR = "data-text-reveal-clip-id";
|
|
8811
8812
|
const GUIDED_NODE_SHAPES = new Set([
|
|
8812
8813
|
"box",
|
|
8813
8814
|
"circle",
|
|
@@ -8936,6 +8937,7 @@ var AIDiagram = (function (exports) {
|
|
|
8936
8937
|
});
|
|
8937
8938
|
const text = nodeText(el);
|
|
8938
8939
|
if (text) {
|
|
8940
|
+
clearTextReveal(text);
|
|
8939
8941
|
text.style.opacity = text.style.transition = "";
|
|
8940
8942
|
}
|
|
8941
8943
|
}
|
|
@@ -8981,55 +8983,74 @@ var AIDiagram = (function (exports) {
|
|
|
8981
8983
|
clearNodeDrawStyles(el);
|
|
8982
8984
|
}
|
|
8983
8985
|
// ── Text writing reveal (clipPath) ───────────────────────
|
|
8986
|
+
function clearTextReveal(textEl, clipId) {
|
|
8987
|
+
const activeClipId = textEl.getAttribute(TEXT_REVEAL_CLIP_ATTR);
|
|
8988
|
+
const shouldClearCurrentClip = !clipId || activeClipId === clipId;
|
|
8989
|
+
if (shouldClearCurrentClip) {
|
|
8990
|
+
textEl.removeAttribute("clip-path");
|
|
8991
|
+
textEl.removeAttribute(TEXT_REVEAL_CLIP_ATTR);
|
|
8992
|
+
}
|
|
8993
|
+
const clipIdToRemove = clipId ?? activeClipId;
|
|
8994
|
+
if (clipIdToRemove) {
|
|
8995
|
+
textEl.ownerSVGElement?.querySelector(`#${clipIdToRemove}`)?.remove();
|
|
8996
|
+
}
|
|
8997
|
+
}
|
|
8984
8998
|
function animateTextReveal(textEl, delayMs, durationMs = ANIMATION.textRevealMs) {
|
|
8985
8999
|
const ownerSvg = textEl.ownerSVGElement;
|
|
9000
|
+
clearTextReveal(textEl);
|
|
8986
9001
|
if (!ownerSvg) {
|
|
8987
9002
|
// fallback: just fade
|
|
8988
9003
|
textEl.style.transition = `opacity ${ANIMATION.textFade}ms ease ${delayMs}ms`;
|
|
8989
9004
|
textEl.style.opacity = "1";
|
|
8990
9005
|
return;
|
|
8991
9006
|
}
|
|
8992
|
-
|
|
9007
|
+
const bbox = textEl.getBBox?.();
|
|
9008
|
+
if (!bbox || bbox.width === 0) {
|
|
9009
|
+
// fallback if can't measure
|
|
9010
|
+
textEl.style.transition = `opacity ${ANIMATION.textFade}ms ease ${delayMs}ms`;
|
|
9011
|
+
textEl.style.opacity = "1";
|
|
9012
|
+
return;
|
|
9013
|
+
}
|
|
9014
|
+
let defs = ownerSvg.querySelector("defs");
|
|
9015
|
+
if (!defs) {
|
|
9016
|
+
defs = document.createElementNS(SVG_NS$1, "defs");
|
|
9017
|
+
ownerSvg.insertBefore(defs, ownerSvg.firstChild);
|
|
9018
|
+
}
|
|
9019
|
+
const clipId = `skm-clip-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
9020
|
+
const clipPath = document.createElementNS(SVG_NS$1, "clipPath");
|
|
9021
|
+
clipPath.setAttribute("id", clipId);
|
|
9022
|
+
const rect = document.createElementNS(SVG_NS$1, "rect");
|
|
9023
|
+
rect.setAttribute("x", String(bbox.x - 2));
|
|
9024
|
+
rect.setAttribute("y", String(bbox.y - 2));
|
|
9025
|
+
rect.setAttribute("width", "0");
|
|
9026
|
+
rect.setAttribute("height", String(bbox.height + 4));
|
|
9027
|
+
clipPath.appendChild(rect);
|
|
9028
|
+
defs.appendChild(clipPath);
|
|
9029
|
+
textEl.setAttribute("clip-path", `url(#${clipId})`);
|
|
9030
|
+
textEl.setAttribute(TEXT_REVEAL_CLIP_ATTR, clipId);
|
|
8993
9031
|
textEl.style.opacity = "1";
|
|
8994
|
-
|
|
9032
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
9033
|
+
rect.style.transition = `width ${durationMs}ms cubic-bezier(.4,0,.2,1) ${delayMs}ms`;
|
|
9034
|
+
rect.setAttribute("width", String(bbox.width + 4));
|
|
9035
|
+
}));
|
|
9036
|
+
// Cleanup after animation
|
|
8995
9037
|
setTimeout(() => {
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
// fallback if can't measure
|
|
8999
|
-
return;
|
|
9000
|
-
}
|
|
9001
|
-
let defs = ownerSvg.querySelector("defs");
|
|
9002
|
-
if (!defs) {
|
|
9003
|
-
defs = document.createElementNS(SVG_NS$1, "defs");
|
|
9004
|
-
ownerSvg.insertBefore(defs, ownerSvg.firstChild);
|
|
9005
|
-
}
|
|
9006
|
-
const clipId = `skm-clip-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
9007
|
-
const clipPath = document.createElementNS(SVG_NS$1, "clipPath");
|
|
9008
|
-
clipPath.setAttribute("id", clipId);
|
|
9009
|
-
const rect = document.createElementNS(SVG_NS$1, "rect");
|
|
9010
|
-
rect.setAttribute("x", String(bbox.x - 2));
|
|
9011
|
-
rect.setAttribute("y", String(bbox.y - 2));
|
|
9012
|
-
rect.setAttribute("width", "0");
|
|
9013
|
-
rect.setAttribute("height", String(bbox.height + 4));
|
|
9014
|
-
clipPath.appendChild(rect);
|
|
9015
|
-
defs.appendChild(clipPath);
|
|
9016
|
-
textEl.setAttribute("clip-path", `url(#${clipId})`);
|
|
9017
|
-
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
9018
|
-
rect.style.transition = `width ${durationMs}ms cubic-bezier(.4,0,.2,1)`;
|
|
9019
|
-
rect.setAttribute("width", String(bbox.width + 4));
|
|
9020
|
-
}));
|
|
9021
|
-
// Cleanup after animation
|
|
9022
|
-
setTimeout(() => {
|
|
9023
|
-
textEl.removeAttribute("clip-path");
|
|
9024
|
-
clipPath.remove();
|
|
9025
|
-
}, durationMs + 50);
|
|
9026
|
-
}, delayMs);
|
|
9038
|
+
clearTextReveal(textEl, clipId);
|
|
9039
|
+
}, delayMs + durationMs + 50);
|
|
9027
9040
|
}
|
|
9028
|
-
function animateNodeDraw(el, strokeDur = ANIMATION.nodeStrokeDur) {
|
|
9041
|
+
function animateNodeDraw(el, strokeDur = ANIMATION.nodeStrokeDur, textOnlyDur = ANIMATION.textRevealMs) {
|
|
9029
9042
|
showDrawEl(el);
|
|
9030
9043
|
const guide = nodeGuidePathEl(el);
|
|
9031
9044
|
if (!guide) {
|
|
9032
9045
|
const firstPath = el.querySelector("path");
|
|
9046
|
+
const text = nodeText(el);
|
|
9047
|
+
if (!firstPath && el.dataset.nodeShape === "text" && text) {
|
|
9048
|
+
animateTextReveal(text, 0, textOnlyDur);
|
|
9049
|
+
setTimeout(() => {
|
|
9050
|
+
clearNodeDrawStyles(el);
|
|
9051
|
+
}, textOnlyDur + 80);
|
|
9052
|
+
return;
|
|
9053
|
+
}
|
|
9033
9054
|
if (!firstPath?.style.strokeDasharray)
|
|
9034
9055
|
prepareForDraw(el);
|
|
9035
9056
|
animateShapeDraw(el, strokeDur, ANIMATION.nodeStagger);
|
|
@@ -9423,8 +9444,17 @@ var AIDiagram = (function (exports) {
|
|
|
9423
9444
|
}
|
|
9424
9445
|
/** Enable/disable browser text-to-speech for narrate steps */
|
|
9425
9446
|
get tts() { return this._tts; }
|
|
9426
|
-
set tts(on) {
|
|
9427
|
-
|
|
9447
|
+
set tts(on) {
|
|
9448
|
+
const next = !!on;
|
|
9449
|
+
const changed = next !== this._tts;
|
|
9450
|
+
this._tts = next;
|
|
9451
|
+
if (!next) {
|
|
9452
|
+
this._cancelSpeech();
|
|
9453
|
+
return;
|
|
9454
|
+
}
|
|
9455
|
+
if (changed)
|
|
9456
|
+
this._warmUpSpeech();
|
|
9457
|
+
}
|
|
9428
9458
|
get currentStep() {
|
|
9429
9459
|
return this._step;
|
|
9430
9460
|
}
|
|
@@ -10050,7 +10080,7 @@ var AIDiagram = (function (exports) {
|
|
|
10050
10080
|
if (!nodeGuidePathEl(nodeEl) && !nodeEl.querySelector("path")?.style.strokeDasharray) {
|
|
10051
10081
|
prepareNodeForDraw(nodeEl);
|
|
10052
10082
|
}
|
|
10053
|
-
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur);
|
|
10083
|
+
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10054
10084
|
}
|
|
10055
10085
|
}
|
|
10056
10086
|
// ── erase ─────────────────────────────────────────────────
|
|
@@ -10703,7 +10733,7 @@ var AIDiagram = (function (exports) {
|
|
|
10703
10733
|
}
|
|
10704
10734
|
|
|
10705
10735
|
function render(options) {
|
|
10706
|
-
const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
|
|
10736
|
+
const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
|
|
10707
10737
|
if (injectCSS && !document.getElementById("ai-diagram-css")) {
|
|
10708
10738
|
const style = document.createElement("style");
|
|
10709
10739
|
style.id = "ai-diagram-css";
|
|
@@ -10752,6 +10782,9 @@ var AIDiagram = (function (exports) {
|
|
|
10752
10782
|
const containerEl = el instanceof SVGSVGElement ? undefined : el;
|
|
10753
10783
|
anim = new AnimationController(svg, ast.steps, containerEl, rc, ast.config);
|
|
10754
10784
|
}
|
|
10785
|
+
if (typeof tts === "boolean") {
|
|
10786
|
+
anim.tts = tts;
|
|
10787
|
+
}
|
|
10755
10788
|
onReady?.(anim, svg);
|
|
10756
10789
|
return {
|
|
10757
10790
|
scene,
|
|
@@ -10803,13 +10836,14 @@ var AIDiagram = (function (exports) {
|
|
|
10803
10836
|
const CANVAS_STYLE_ID = "sketchmark-canvas-ui";
|
|
10804
10837
|
const CANVAS_CSS = `
|
|
10805
10838
|
.skm-canvas{display:flex;flex-direction:column;width:100%;height:100%;min-height:320px;overflow:hidden;border:1px solid #caba98;border-radius:10px;background:#f8f4ea;color:#3a2010;font-family:"Courier New",monospace}
|
|
10806
|
-
.skm-canvas__animbar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#eee7d8;border-bottom:1px solid #caba98;flex-shrink:0}
|
|
10839
|
+
.skm-canvas__animbar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#eee7d8;border-bottom:1px solid #caba98;flex-shrink:0;flex-wrap:wrap}
|
|
10807
10840
|
.skm-canvas__status{min-width:96px;text-align:center;color:#6a4820;font-size:11px}
|
|
10808
10841
|
.skm-canvas__label{color:#8a6040;font-size:11px;font-style:italic}
|
|
10809
10842
|
.skm-canvas__spacer{flex:1}
|
|
10810
10843
|
.skm-canvas__stats{color:#9a7848;font-size:10px}
|
|
10811
10844
|
.skm-canvas__button{border:1px solid #caba98;background:#f5eedd;color:#3a2010;border-radius:6px;padding:4px 9px;font:inherit;font-size:11px;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease}
|
|
10812
10845
|
.skm-canvas__button:hover:not(:disabled){background:#c8a060;border-color:#c8a060;color:#fff}
|
|
10846
|
+
.skm-canvas__button.is-active{background:#c8a060;border-color:#c8a060;color:#fff}
|
|
10813
10847
|
.skm-canvas__button:disabled{opacity:.45;cursor:default}
|
|
10814
10848
|
.skm-canvas__error{display:none;padding:8px 12px;background:#280a0a;border-bottom:1px solid #5a1818;color:#f07070;font-size:11px;line-height:1.4;white-space:pre-wrap;flex-shrink:0}
|
|
10815
10849
|
.skm-canvas__error.is-visible{display:block}
|
|
@@ -10831,6 +10865,8 @@ var AIDiagram = (function (exports) {
|
|
|
10831
10865
|
this.instance = null;
|
|
10832
10866
|
this.emitter = new EventEmitter();
|
|
10833
10867
|
this.dsl = "";
|
|
10868
|
+
this.showCaption = true;
|
|
10869
|
+
this.ttsOverride = null;
|
|
10834
10870
|
this.panX = 60;
|
|
10835
10871
|
this.panY = 60;
|
|
10836
10872
|
this.zoom = 1;
|
|
@@ -10918,6 +10954,8 @@ var AIDiagram = (function (exports) {
|
|
|
10918
10954
|
this.options = options;
|
|
10919
10955
|
this.renderer = options.renderer ?? "svg";
|
|
10920
10956
|
this.theme = options.theme ?? "light";
|
|
10957
|
+
this.showCaption = options.showCaption !== false;
|
|
10958
|
+
this.ttsOverride = typeof options.tts === "boolean" ? options.tts : null;
|
|
10921
10959
|
this.dsl = normalizeNewlines(options.dsl ?? "");
|
|
10922
10960
|
injectStyleOnce(CANVAS_STYLE_ID, CANVAS_CSS);
|
|
10923
10961
|
const host = resolveContainer(options.container);
|
|
@@ -10936,6 +10974,8 @@ var AIDiagram = (function (exports) {
|
|
|
10936
10974
|
<span class="skm-canvas__status">No steps</span>
|
|
10937
10975
|
<button type="button" class="skm-canvas__button" data-action="next">Next</button>
|
|
10938
10976
|
<button type="button" class="skm-canvas__button" data-action="play">Play</button>
|
|
10977
|
+
<button type="button" class="skm-canvas__button" data-action="toggle-caption">Caption On</button>
|
|
10978
|
+
<button type="button" class="skm-canvas__button" data-action="toggle-tts">TTS Off</button>
|
|
10939
10979
|
<span class="skm-canvas__label"></span>
|
|
10940
10980
|
<span class="skm-canvas__spacer"></span>
|
|
10941
10981
|
<span class="skm-canvas__stats"></span>
|
|
@@ -10970,6 +11010,8 @@ var AIDiagram = (function (exports) {
|
|
|
10970
11010
|
this.prevButton = this.root.querySelector('[data-action="prev"]');
|
|
10971
11011
|
this.nextButton = this.root.querySelector('[data-action="next"]');
|
|
10972
11012
|
this.resetButton = this.root.querySelector('[data-action="reset"]');
|
|
11013
|
+
this.captionButton = this.root.querySelector('[data-action="toggle-caption"]');
|
|
11014
|
+
this.ttsButton = this.root.querySelector('[data-action="toggle-tts"]');
|
|
10973
11015
|
this.gridPattern = this.root.querySelector(`#${patternId}`);
|
|
10974
11016
|
this.gridDot = this.gridPattern.querySelector("circle");
|
|
10975
11017
|
this.root.querySelector('[data-action="fit"]')?.addEventListener("click", () => this.fitContent());
|
|
@@ -10980,6 +11022,8 @@ var AIDiagram = (function (exports) {
|
|
|
10980
11022
|
this.prevButton.addEventListener("click", () => this.prevStep());
|
|
10981
11023
|
this.nextButton.addEventListener("click", () => this.nextStep());
|
|
10982
11024
|
this.playButton.addEventListener("click", () => void this.play());
|
|
11025
|
+
this.captionButton.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
11026
|
+
this.ttsButton.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
|
|
10983
11027
|
this.viewport.addEventListener("pointerdown", this.onPointerDown);
|
|
10984
11028
|
this.viewport.addEventListener("pointermove", this.onPointerMove);
|
|
10985
11029
|
this.viewport.addEventListener("pointerup", this.onStopPanning);
|
|
@@ -11001,6 +11045,16 @@ var AIDiagram = (function (exports) {
|
|
|
11001
11045
|
if (renderNow)
|
|
11002
11046
|
this.render();
|
|
11003
11047
|
}
|
|
11048
|
+
setCaptionVisible(visible) {
|
|
11049
|
+
this.showCaption = visible;
|
|
11050
|
+
this.applyCaptionVisibility(this.instance);
|
|
11051
|
+
this.syncToggleUi();
|
|
11052
|
+
}
|
|
11053
|
+
setTtsEnabled(enabled) {
|
|
11054
|
+
this.ttsOverride = enabled;
|
|
11055
|
+
this.applyTtsSetting(this.instance);
|
|
11056
|
+
this.syncToggleUi();
|
|
11057
|
+
}
|
|
11004
11058
|
bindEditor(editor, options = {}) {
|
|
11005
11059
|
this.editorCleanup?.();
|
|
11006
11060
|
const renderOnRun = options.renderOnRun !== false;
|
|
@@ -11045,6 +11099,8 @@ var AIDiagram = (function (exports) {
|
|
|
11045
11099
|
onNodeClick: this.options.onNodeClick,
|
|
11046
11100
|
});
|
|
11047
11101
|
this.instance = instance;
|
|
11102
|
+
this.applyCaptionVisibility(instance);
|
|
11103
|
+
this.applyTtsSetting(instance);
|
|
11048
11104
|
this.statsLabel.textContent = `${instance.scene.nodes.length}n / ${instance.scene.edges.length}e / ${instance.scene.groups.length}g`;
|
|
11049
11105
|
if (this.renderer === "svg") {
|
|
11050
11106
|
this.animUnsub = instance.anim.on((event) => {
|
|
@@ -11177,6 +11233,37 @@ var AIDiagram = (function (exports) {
|
|
|
11177
11233
|
this.zoom = clampedZoom;
|
|
11178
11234
|
this.applyTransform();
|
|
11179
11235
|
}
|
|
11236
|
+
applyCaptionVisibility(instance) {
|
|
11237
|
+
const caption = instance?.anim.captionElement;
|
|
11238
|
+
if (!caption)
|
|
11239
|
+
return;
|
|
11240
|
+
caption.style.display = this.showCaption ? "" : "none";
|
|
11241
|
+
caption.setAttribute("aria-hidden", this.showCaption ? "false" : "true");
|
|
11242
|
+
}
|
|
11243
|
+
applyTtsSetting(instance) {
|
|
11244
|
+
if (!instance || this.ttsOverride === null)
|
|
11245
|
+
return;
|
|
11246
|
+
instance.anim.tts = this.ttsOverride;
|
|
11247
|
+
}
|
|
11248
|
+
getTtsEnabled() {
|
|
11249
|
+
if (this.ttsOverride !== null)
|
|
11250
|
+
return this.ttsOverride;
|
|
11251
|
+
return !!this.instance?.anim.tts;
|
|
11252
|
+
}
|
|
11253
|
+
syncToggleUi() {
|
|
11254
|
+
const canToggleCaption = this.renderer === "svg" && !!this.instance;
|
|
11255
|
+
const canToggleTts = canToggleCaption &&
|
|
11256
|
+
typeof speechSynthesis !== "undefined";
|
|
11257
|
+
const ttsEnabled = this.getTtsEnabled();
|
|
11258
|
+
this.captionButton.textContent = this.showCaption ? "Caption On" : "Caption Off";
|
|
11259
|
+
this.captionButton.classList.toggle("is-active", this.showCaption);
|
|
11260
|
+
this.captionButton.setAttribute("aria-pressed", this.showCaption ? "true" : "false");
|
|
11261
|
+
this.captionButton.disabled = !canToggleCaption;
|
|
11262
|
+
this.ttsButton.textContent = ttsEnabled ? "TTS On" : "TTS Off";
|
|
11263
|
+
this.ttsButton.classList.toggle("is-active", ttsEnabled);
|
|
11264
|
+
this.ttsButton.setAttribute("aria-pressed", ttsEnabled ? "true" : "false");
|
|
11265
|
+
this.ttsButton.disabled = !canToggleTts;
|
|
11266
|
+
}
|
|
11180
11267
|
syncAnimationUi() {
|
|
11181
11268
|
const anim = this.instance?.anim;
|
|
11182
11269
|
const canAnimate = this.renderer === "svg" && !!anim && anim.total > 0;
|
|
@@ -11187,6 +11274,7 @@ var AIDiagram = (function (exports) {
|
|
|
11187
11274
|
this.nextButton.disabled = true;
|
|
11188
11275
|
this.resetButton.disabled = true;
|
|
11189
11276
|
this.playButton.disabled = true;
|
|
11277
|
+
this.syncToggleUi();
|
|
11190
11278
|
return;
|
|
11191
11279
|
}
|
|
11192
11280
|
this.stepDisplay.textContent = anim.currentStep < 0 ? `${anim.total} steps` : `${anim.currentStep + 1} / ${anim.total}`;
|
|
@@ -11195,6 +11283,7 @@ var AIDiagram = (function (exports) {
|
|
|
11195
11283
|
this.nextButton.disabled = !anim.canNext;
|
|
11196
11284
|
this.resetButton.disabled = false;
|
|
11197
11285
|
this.playButton.disabled = this.playInFlight || !anim.canNext;
|
|
11286
|
+
this.syncToggleUi();
|
|
11198
11287
|
}
|
|
11199
11288
|
getStepTarget(stepItem) {
|
|
11200
11289
|
if (!stepItem)
|
|
@@ -11851,6 +11940,7 @@ var AIDiagram = (function (exports) {
|
|
|
11851
11940
|
|
|
11852
11941
|
.skm-embed__controls {
|
|
11853
11942
|
display: flex;
|
|
11943
|
+
flex-wrap: wrap;
|
|
11854
11944
|
align-items: center;
|
|
11855
11945
|
gap: 8px;
|
|
11856
11946
|
padding: 10px 12px;
|
|
@@ -11868,6 +11958,13 @@ var AIDiagram = (function (exports) {
|
|
|
11868
11958
|
display: none;
|
|
11869
11959
|
}
|
|
11870
11960
|
|
|
11961
|
+
.skm-embed__controls-group {
|
|
11962
|
+
display: flex;
|
|
11963
|
+
align-items: center;
|
|
11964
|
+
gap: 8px;
|
|
11965
|
+
flex-wrap: wrap;
|
|
11966
|
+
}
|
|
11967
|
+
|
|
11871
11968
|
.skm-embed__button {
|
|
11872
11969
|
border: 1px solid #caba98;
|
|
11873
11970
|
background: #f5eedd;
|
|
@@ -11886,6 +11983,12 @@ var AIDiagram = (function (exports) {
|
|
|
11886
11983
|
color: #fff;
|
|
11887
11984
|
}
|
|
11888
11985
|
|
|
11986
|
+
.skm-embed__button.is-active {
|
|
11987
|
+
background: #c8a060;
|
|
11988
|
+
border-color: #c8a060;
|
|
11989
|
+
color: #fff;
|
|
11990
|
+
}
|
|
11991
|
+
|
|
11889
11992
|
.skm-embed--dark .skm-embed__button {
|
|
11890
11993
|
border-color: #4a3520;
|
|
11891
11994
|
background: #22190e;
|
|
@@ -11903,6 +12006,17 @@ var AIDiagram = (function (exports) {
|
|
|
11903
12006
|
cursor: default;
|
|
11904
12007
|
}
|
|
11905
12008
|
|
|
12009
|
+
.skm-embed__zoom {
|
|
12010
|
+
min-width: 48px;
|
|
12011
|
+
text-align: center;
|
|
12012
|
+
color: #8a6040;
|
|
12013
|
+
font-size: 11px;
|
|
12014
|
+
}
|
|
12015
|
+
|
|
12016
|
+
.skm-embed--dark .skm-embed__zoom {
|
|
12017
|
+
color: #d0b176;
|
|
12018
|
+
}
|
|
12019
|
+
|
|
11906
12020
|
.skm-embed__step {
|
|
11907
12021
|
margin-left: auto;
|
|
11908
12022
|
min-width: 96px;
|
|
@@ -11921,13 +12035,19 @@ var AIDiagram = (function (exports) {
|
|
|
11921
12035
|
this.emitter = new EventEmitter();
|
|
11922
12036
|
this.animUnsub = null;
|
|
11923
12037
|
this.playInFlight = false;
|
|
12038
|
+
this.showCaption = true;
|
|
12039
|
+
this.ttsOverride = null;
|
|
12040
|
+
this.zoom = 1;
|
|
11924
12041
|
this.offsetX = 0;
|
|
11925
12042
|
this.offsetY = 0;
|
|
12043
|
+
this.autoFitEnabled = true;
|
|
11926
12044
|
this.motionFrame = null;
|
|
11927
12045
|
this.resizeObserver = null;
|
|
11928
12046
|
this.options = options;
|
|
11929
12047
|
this.dsl = normalizeNewlines(options.dsl);
|
|
11930
12048
|
this.theme = options.theme ?? "light";
|
|
12049
|
+
this.showCaption = options.showCaption !== false;
|
|
12050
|
+
this.ttsOverride = typeof options.tts === "boolean" ? options.tts : null;
|
|
11931
12051
|
injectStyleOnce(EMBED_STYLE_ID, EMBED_CSS);
|
|
11932
12052
|
const host = resolveContainer(options.container);
|
|
11933
12053
|
host.innerHTML = "";
|
|
@@ -11943,10 +12063,22 @@ var AIDiagram = (function (exports) {
|
|
|
11943
12063
|
</div>
|
|
11944
12064
|
<div class="skm-embed__error"></div>
|
|
11945
12065
|
<div class="skm-embed__controls">
|
|
11946
|
-
<
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
12066
|
+
<div class="skm-embed__controls-group">
|
|
12067
|
+
<button type="button" class="skm-embed__button" data-action="zoom-out">-</button>
|
|
12068
|
+
<span class="skm-embed__zoom">100%</span>
|
|
12069
|
+
<button type="button" class="skm-embed__button" data-action="zoom-in">+</button>
|
|
12070
|
+
<button type="button" class="skm-embed__button" data-action="fit">Reset</button>
|
|
12071
|
+
</div>
|
|
12072
|
+
<div class="skm-embed__controls-group">
|
|
12073
|
+
<button type="button" class="skm-embed__button" data-action="restart">Restart</button>
|
|
12074
|
+
<button type="button" class="skm-embed__button" data-action="prev">Prev</button>
|
|
12075
|
+
<button type="button" class="skm-embed__button" data-action="next">Next</button>
|
|
12076
|
+
<button type="button" class="skm-embed__button" data-action="play">Play</button>
|
|
12077
|
+
</div>
|
|
12078
|
+
<div class="skm-embed__controls-group">
|
|
12079
|
+
<button type="button" class="skm-embed__button" data-action="toggle-caption">Caption On</button>
|
|
12080
|
+
<button type="button" class="skm-embed__button" data-action="toggle-tts">TTS Off</button>
|
|
12081
|
+
</div>
|
|
11950
12082
|
<span class="skm-embed__step">No steps</span>
|
|
11951
12083
|
</div>
|
|
11952
12084
|
`;
|
|
@@ -11956,17 +12088,28 @@ var AIDiagram = (function (exports) {
|
|
|
11956
12088
|
this.errorElement = this.root.querySelector(".skm-embed__error");
|
|
11957
12089
|
this.controlsElement = this.root.querySelector(".skm-embed__controls");
|
|
11958
12090
|
this.stepInfoElement = this.root.querySelector(".skm-embed__step");
|
|
11959
|
-
this.
|
|
12091
|
+
this.zoomInfoElement = this.root.querySelector(".skm-embed__zoom");
|
|
12092
|
+
this.btnFit = this.root.querySelector('[data-action="fit"]');
|
|
12093
|
+
this.btnZoomIn = this.root.querySelector('[data-action="zoom-in"]');
|
|
12094
|
+
this.btnZoomOut = this.root.querySelector('[data-action="zoom-out"]');
|
|
12095
|
+
this.btnRestart = this.root.querySelector('[data-action="restart"]');
|
|
11960
12096
|
this.btnPrev = this.root.querySelector('[data-action="prev"]');
|
|
11961
12097
|
this.btnNext = this.root.querySelector('[data-action="next"]');
|
|
11962
12098
|
this.btnPlay = this.root.querySelector('[data-action="play"]');
|
|
12099
|
+
this.btnCaption = this.root.querySelector('[data-action="toggle-caption"]');
|
|
12100
|
+
this.btnTts = this.root.querySelector('[data-action="toggle-tts"]');
|
|
11963
12101
|
this.controlsElement.classList.toggle("is-hidden", options.showControls === false);
|
|
11964
|
-
this.
|
|
12102
|
+
this.btnFit.addEventListener("click", () => this.resetView());
|
|
12103
|
+
this.btnZoomIn.addEventListener("click", () => this.zoomIn());
|
|
12104
|
+
this.btnZoomOut.addEventListener("click", () => this.zoomOut());
|
|
12105
|
+
this.btnRestart.addEventListener("click", () => this.resetAnimation());
|
|
11965
12106
|
this.btnPrev.addEventListener("click", () => this.prevStep());
|
|
11966
12107
|
this.btnNext.addEventListener("click", () => this.nextStep());
|
|
11967
12108
|
this.btnPlay.addEventListener("click", () => {
|
|
11968
12109
|
void this.play();
|
|
11969
12110
|
});
|
|
12111
|
+
this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
12112
|
+
this.btnTts.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
|
|
11970
12113
|
if (typeof ResizeObserver !== "undefined") {
|
|
11971
12114
|
this.resizeObserver = new ResizeObserver(() => {
|
|
11972
12115
|
this.positionViewport(false);
|
|
@@ -11984,6 +12127,16 @@ var AIDiagram = (function (exports) {
|
|
|
11984
12127
|
if (renderNow)
|
|
11985
12128
|
this.render();
|
|
11986
12129
|
}
|
|
12130
|
+
setCaptionVisible(visible) {
|
|
12131
|
+
this.showCaption = visible;
|
|
12132
|
+
this.applyCaptionVisibility(this.instance);
|
|
12133
|
+
this.syncToggleControls();
|
|
12134
|
+
}
|
|
12135
|
+
setTtsEnabled(enabled) {
|
|
12136
|
+
this.ttsOverride = enabled;
|
|
12137
|
+
this.applyTtsSetting(this.instance);
|
|
12138
|
+
this.syncToggleControls();
|
|
12139
|
+
}
|
|
11987
12140
|
setSize(width, height) {
|
|
11988
12141
|
this.applySize(width, height);
|
|
11989
12142
|
this.positionViewport(false);
|
|
@@ -12007,7 +12160,12 @@ var AIDiagram = (function (exports) {
|
|
|
12007
12160
|
this.animUnsub = null;
|
|
12008
12161
|
this.instance?.anim?.destroy();
|
|
12009
12162
|
this.instance = null;
|
|
12163
|
+
this.autoFitEnabled = true;
|
|
12164
|
+
this.zoom = 1;
|
|
12165
|
+
this.offsetX = 0;
|
|
12166
|
+
this.offsetY = 0;
|
|
12010
12167
|
this.diagramWrap.innerHTML = "";
|
|
12168
|
+
this.applyTransform();
|
|
12011
12169
|
try {
|
|
12012
12170
|
const instance = render({
|
|
12013
12171
|
container: this.diagramWrap,
|
|
@@ -12023,6 +12181,8 @@ var AIDiagram = (function (exports) {
|
|
|
12023
12181
|
onNodeClick: this.options.onNodeClick,
|
|
12024
12182
|
});
|
|
12025
12183
|
this.instance = instance;
|
|
12184
|
+
this.applyCaptionVisibility(instance);
|
|
12185
|
+
this.applyTtsSetting(instance);
|
|
12026
12186
|
this.animUnsub = instance.anim.on((event) => {
|
|
12027
12187
|
this.syncControls();
|
|
12028
12188
|
if (event.type === "step-change") {
|
|
@@ -12088,6 +12248,21 @@ var AIDiagram = (function (exports) {
|
|
|
12088
12248
|
this.syncControls();
|
|
12089
12249
|
this.positionViewport(true);
|
|
12090
12250
|
}
|
|
12251
|
+
fitToViewport(animated = false) {
|
|
12252
|
+
if (!this.instance?.svg)
|
|
12253
|
+
return;
|
|
12254
|
+
this.autoFitEnabled = true;
|
|
12255
|
+
this.positionViewport(animated);
|
|
12256
|
+
}
|
|
12257
|
+
resetView(animated = false) {
|
|
12258
|
+
this.fitToViewport(animated);
|
|
12259
|
+
}
|
|
12260
|
+
zoomIn() {
|
|
12261
|
+
this.zoomAroundViewportCenter(1.2);
|
|
12262
|
+
}
|
|
12263
|
+
zoomOut() {
|
|
12264
|
+
this.zoomAroundViewportCenter(0.8);
|
|
12265
|
+
}
|
|
12091
12266
|
exportSVG(filename) {
|
|
12092
12267
|
this.instance?.exportSVG(filename);
|
|
12093
12268
|
}
|
|
@@ -12110,10 +12285,15 @@ var AIDiagram = (function (exports) {
|
|
|
12110
12285
|
return typeof value === "number" ? `${value}px` : value;
|
|
12111
12286
|
}
|
|
12112
12287
|
syncControls() {
|
|
12288
|
+
this.syncAnimationControls();
|
|
12289
|
+
this.syncViewControls();
|
|
12290
|
+
this.syncToggleControls();
|
|
12291
|
+
}
|
|
12292
|
+
syncAnimationControls() {
|
|
12113
12293
|
const anim = this.instance?.anim;
|
|
12114
12294
|
if (!anim || !anim.total) {
|
|
12115
12295
|
this.stepInfoElement.textContent = "No steps";
|
|
12116
|
-
this.
|
|
12296
|
+
this.btnRestart.disabled = true;
|
|
12117
12297
|
this.btnPrev.disabled = true;
|
|
12118
12298
|
this.btnNext.disabled = true;
|
|
12119
12299
|
this.btnPlay.disabled = true;
|
|
@@ -12121,56 +12301,69 @@ var AIDiagram = (function (exports) {
|
|
|
12121
12301
|
}
|
|
12122
12302
|
this.stepInfoElement.textContent =
|
|
12123
12303
|
anim.currentStep < 0 ? `${anim.total} steps` : `${anim.currentStep + 1} / ${anim.total}`;
|
|
12124
|
-
this.
|
|
12304
|
+
this.btnRestart.disabled = false;
|
|
12125
12305
|
this.btnPrev.disabled = !anim.canPrev;
|
|
12126
12306
|
this.btnNext.disabled = !anim.canNext;
|
|
12127
12307
|
this.btnPlay.disabled = this.playInFlight || !anim.canNext;
|
|
12128
12308
|
}
|
|
12309
|
+
syncViewControls() {
|
|
12310
|
+
const hasView = !!this.instance?.svg;
|
|
12311
|
+
const zoomMin = this.getZoomMin();
|
|
12312
|
+
const zoomMax = this.getZoomMax();
|
|
12313
|
+
this.zoomInfoElement.textContent = `${Math.round(this.zoom * 100)}%`;
|
|
12314
|
+
this.btnFit.disabled = !hasView;
|
|
12315
|
+
this.btnZoomOut.disabled = !hasView || this.zoom <= zoomMin + 0.001;
|
|
12316
|
+
this.btnZoomIn.disabled = !hasView || this.zoom >= zoomMax - 0.001;
|
|
12317
|
+
}
|
|
12129
12318
|
positionViewport(animated) {
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
const svg = this.instance.svg;
|
|
12133
|
-
const svgWidth = parseFloat(svg.getAttribute("width") || "0");
|
|
12134
|
-
const svgHeight = parseFloat(svg.getAttribute("height") || "0");
|
|
12135
|
-
if (!svgWidth || !svgHeight)
|
|
12319
|
+
const size = this.getContentSize();
|
|
12320
|
+
if (!size)
|
|
12136
12321
|
return;
|
|
12322
|
+
const { width: svgWidth, height: svgHeight } = size;
|
|
12137
12323
|
const viewportRect = this.viewport.getBoundingClientRect();
|
|
12138
12324
|
const viewWidth = viewportRect.width || this.viewport.clientWidth;
|
|
12139
12325
|
const viewHeight = viewportRect.height || this.viewport.clientHeight;
|
|
12140
12326
|
if (!viewWidth || !viewHeight)
|
|
12141
12327
|
return;
|
|
12142
|
-
|
|
12328
|
+
if (this.autoFitEnabled) {
|
|
12329
|
+
this.zoom = this.getFitZoom(svgWidth, svgHeight, viewWidth, viewHeight);
|
|
12330
|
+
}
|
|
12331
|
+
this.syncViewControls();
|
|
12332
|
+
const scaledWidth = svgWidth * this.zoom;
|
|
12333
|
+
const scaledHeight = svgHeight * this.zoom;
|
|
12334
|
+
const focusTarget = this.getFocusTarget();
|
|
12335
|
+
const sceneIsLarge = scaledWidth > viewWidth || scaledHeight > viewHeight;
|
|
12143
12336
|
const shouldFocus = sceneIsLarge &&
|
|
12144
12337
|
this.options.autoFocus !== false &&
|
|
12145
|
-
!!
|
|
12338
|
+
!!focusTarget;
|
|
12146
12339
|
if (!shouldFocus) {
|
|
12147
|
-
this.animateTo(
|
|
12340
|
+
this.animateTo(scaledWidth <= viewWidth ? (viewWidth - scaledWidth) / 2 : 0, scaledHeight <= viewHeight ? (viewHeight - scaledHeight) / 2 : 0, animated);
|
|
12148
12341
|
return;
|
|
12149
12342
|
}
|
|
12150
|
-
const target = this.findTargetElement(
|
|
12343
|
+
const target = this.findTargetElement(focusTarget);
|
|
12151
12344
|
if (!target) {
|
|
12152
12345
|
this.animateTo(0, 0, animated);
|
|
12153
12346
|
return;
|
|
12154
12347
|
}
|
|
12155
|
-
const
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
let nextX = viewWidth / 2 -
|
|
12161
|
-
let nextY = viewHeight / 2 -
|
|
12348
|
+
const targetBox = this.getTargetBox(target, viewportRect);
|
|
12349
|
+
if (!targetBox) {
|
|
12350
|
+
this.animateTo(0, 0, animated);
|
|
12351
|
+
return;
|
|
12352
|
+
}
|
|
12353
|
+
let nextX = viewWidth / 2 - targetBox.centerX;
|
|
12354
|
+
let nextY = viewHeight / 2 - targetBox.centerY;
|
|
12162
12355
|
const padding = this.options.focusPadding ?? 24;
|
|
12163
|
-
if (
|
|
12164
|
-
nextX = (viewWidth -
|
|
12356
|
+
if (scaledWidth <= viewWidth) {
|
|
12357
|
+
nextX = (viewWidth - scaledWidth) / 2;
|
|
12165
12358
|
}
|
|
12166
12359
|
else {
|
|
12167
|
-
nextX = clamp(nextX, viewWidth -
|
|
12360
|
+
nextX = clamp(nextX, viewWidth - scaledWidth - padding, padding);
|
|
12168
12361
|
}
|
|
12169
|
-
if (
|
|
12170
|
-
nextY = (viewHeight -
|
|
12362
|
+
if (scaledHeight <= viewHeight) {
|
|
12363
|
+
nextY = (viewHeight - scaledHeight) / 2;
|
|
12171
12364
|
}
|
|
12172
12365
|
else {
|
|
12173
|
-
nextY = clamp(nextY, viewHeight -
|
|
12366
|
+
nextY = clamp(nextY, viewHeight - scaledHeight - padding, padding);
|
|
12174
12367
|
}
|
|
12175
12368
|
this.animateTo(nextX, nextY, animated);
|
|
12176
12369
|
}
|
|
@@ -12202,7 +12395,109 @@ var AIDiagram = (function (exports) {
|
|
|
12202
12395
|
this.motionFrame = requestAnimationFrame(frame);
|
|
12203
12396
|
}
|
|
12204
12397
|
applyTransform() {
|
|
12205
|
-
this.world.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px)`;
|
|
12398
|
+
this.world.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(${this.zoom})`;
|
|
12399
|
+
this.zoomInfoElement.textContent = `${Math.round(this.zoom * 100)}%`;
|
|
12400
|
+
}
|
|
12401
|
+
getContentSize() {
|
|
12402
|
+
if (!this.instance?.svg)
|
|
12403
|
+
return null;
|
|
12404
|
+
const svg = this.instance.svg;
|
|
12405
|
+
const width = parseFloat(svg.getAttribute("width") || "0");
|
|
12406
|
+
const height = parseFloat(svg.getAttribute("height") || "0");
|
|
12407
|
+
if (!width || !height)
|
|
12408
|
+
return null;
|
|
12409
|
+
return { width, height };
|
|
12410
|
+
}
|
|
12411
|
+
getFitZoom(svgWidth, svgHeight, viewWidth, viewHeight) {
|
|
12412
|
+
const padding = this.getFitPadding(viewWidth, viewHeight);
|
|
12413
|
+
const availableWidth = Math.max(viewWidth - padding * 2, 1);
|
|
12414
|
+
const availableHeight = Math.max(viewHeight - padding * 2, 1);
|
|
12415
|
+
const nextZoom = Math.min(availableWidth / svgWidth, availableHeight / svgHeight, 1);
|
|
12416
|
+
return clamp(nextZoom || 1, this.getZoomMin(), this.getZoomMax());
|
|
12417
|
+
}
|
|
12418
|
+
getFitPadding(viewWidth, viewHeight) {
|
|
12419
|
+
if (typeof this.options.fitPadding === "number") {
|
|
12420
|
+
return Math.max(0, this.options.fitPadding);
|
|
12421
|
+
}
|
|
12422
|
+
return Math.max(16, Math.min(40, Math.round(Math.min(viewWidth, viewHeight) * 0.08)));
|
|
12423
|
+
}
|
|
12424
|
+
getZoomMin() {
|
|
12425
|
+
return this.options.zoomMin ?? 0.08;
|
|
12426
|
+
}
|
|
12427
|
+
getZoomMax() {
|
|
12428
|
+
return this.options.zoomMax ?? 4;
|
|
12429
|
+
}
|
|
12430
|
+
zoomAroundViewportCenter(factor) {
|
|
12431
|
+
if (!this.instance?.svg)
|
|
12432
|
+
return;
|
|
12433
|
+
const pivotX = this.viewport.clientWidth / 2;
|
|
12434
|
+
const pivotY = this.viewport.clientHeight / 2;
|
|
12435
|
+
this.zoomTo(this.zoom * factor, pivotX, pivotY);
|
|
12436
|
+
}
|
|
12437
|
+
zoomTo(nextZoom, pivotX, pivotY) {
|
|
12438
|
+
const clampedZoom = clamp(nextZoom, this.getZoomMin(), this.getZoomMax());
|
|
12439
|
+
const ratio = clampedZoom / this.zoom;
|
|
12440
|
+
if (!Number.isFinite(ratio) || ratio === 1) {
|
|
12441
|
+
this.syncViewControls();
|
|
12442
|
+
return;
|
|
12443
|
+
}
|
|
12444
|
+
this.stopMotion();
|
|
12445
|
+
this.autoFitEnabled = false;
|
|
12446
|
+
this.offsetX = pivotX - (pivotX - this.offsetX) * ratio;
|
|
12447
|
+
this.offsetY = pivotY - (pivotY - this.offsetY) * ratio;
|
|
12448
|
+
this.zoom = clampedZoom;
|
|
12449
|
+
this.applyTransform();
|
|
12450
|
+
this.syncViewControls();
|
|
12451
|
+
}
|
|
12452
|
+
applyCaptionVisibility(instance) {
|
|
12453
|
+
const caption = instance?.anim.captionElement;
|
|
12454
|
+
if (!caption)
|
|
12455
|
+
return;
|
|
12456
|
+
caption.style.display = this.showCaption ? "" : "none";
|
|
12457
|
+
caption.setAttribute("aria-hidden", this.showCaption ? "false" : "true");
|
|
12458
|
+
}
|
|
12459
|
+
applyTtsSetting(instance) {
|
|
12460
|
+
if (!instance || this.ttsOverride === null)
|
|
12461
|
+
return;
|
|
12462
|
+
instance.anim.tts = this.ttsOverride;
|
|
12463
|
+
}
|
|
12464
|
+
getTtsEnabled() {
|
|
12465
|
+
if (this.ttsOverride !== null)
|
|
12466
|
+
return this.ttsOverride;
|
|
12467
|
+
return !!this.instance?.anim.tts;
|
|
12468
|
+
}
|
|
12469
|
+
syncToggleControls() {
|
|
12470
|
+
const hasView = !!this.instance?.svg;
|
|
12471
|
+
const canToggleTts = hasView &&
|
|
12472
|
+
typeof speechSynthesis !== "undefined";
|
|
12473
|
+
const ttsEnabled = this.getTtsEnabled();
|
|
12474
|
+
this.btnCaption.textContent = this.showCaption ? "Caption On" : "Caption Off";
|
|
12475
|
+
this.btnCaption.classList.toggle("is-active", this.showCaption);
|
|
12476
|
+
this.btnCaption.setAttribute("aria-pressed", this.showCaption ? "true" : "false");
|
|
12477
|
+
this.btnCaption.disabled = !hasView;
|
|
12478
|
+
this.btnTts.textContent = ttsEnabled ? "TTS On" : "TTS Off";
|
|
12479
|
+
this.btnTts.classList.toggle("is-active", ttsEnabled);
|
|
12480
|
+
this.btnTts.setAttribute("aria-pressed", ttsEnabled ? "true" : "false");
|
|
12481
|
+
this.btnTts.disabled = !canToggleTts;
|
|
12482
|
+
}
|
|
12483
|
+
getTargetBox(target, viewportRect) {
|
|
12484
|
+
if (target instanceof SVGGraphicsElement) {
|
|
12485
|
+
try {
|
|
12486
|
+
const bounds = target.getBBox();
|
|
12487
|
+
return {
|
|
12488
|
+
centerX: (bounds.x + bounds.width / 2) * this.zoom,
|
|
12489
|
+
centerY: (bounds.y + bounds.height / 2) * this.zoom,
|
|
12490
|
+
};
|
|
12491
|
+
}
|
|
12492
|
+
catch {
|
|
12493
|
+
// Ignore and fall back to layout-based bounds below.
|
|
12494
|
+
}
|
|
12495
|
+
}
|
|
12496
|
+
const currentRect = target.getBoundingClientRect();
|
|
12497
|
+
return {
|
|
12498
|
+
centerX: currentRect.left - viewportRect.left - this.offsetX + currentRect.width / 2,
|
|
12499
|
+
centerY: currentRect.top - viewportRect.top - this.offsetY + currentRect.height / 2,
|
|
12500
|
+
};
|
|
12206
12501
|
}
|
|
12207
12502
|
getFocusTarget() {
|
|
12208
12503
|
const anim = this.instance?.anim;
|