sketchmark 1.1.2 → 1.1.4
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 +101 -104
- package/dist/animation/index.d.ts +5 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/index.cjs +258 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +258 -76
- package/dist/index.js.map +1 -1
- package/dist/sketchmark.iife.js +258 -76
- package/dist/ui/embed.d.ts +24 -1
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +70 -72
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);
|
|
@@ -9249,11 +9270,13 @@ var AIDiagram = (function (exports) {
|
|
|
9249
9270
|
this._config = _config;
|
|
9250
9271
|
this._step = -1;
|
|
9251
9272
|
this._pendingStepTimers = new Set();
|
|
9273
|
+
this._pendingNarrationTimers = new Set();
|
|
9252
9274
|
this._transforms = new Map();
|
|
9253
9275
|
this._listeners = [];
|
|
9254
9276
|
// ── Narration caption ──
|
|
9255
9277
|
this._captionEl = null;
|
|
9256
9278
|
this._captionTextEl = null;
|
|
9279
|
+
this._narrationRunId = 0;
|
|
9257
9280
|
// ── Annotations ──
|
|
9258
9281
|
this._annotationLayer = null;
|
|
9259
9282
|
this._annotations = [];
|
|
@@ -9516,20 +9539,30 @@ var AIDiagram = (function (exports) {
|
|
|
9516
9539
|
}
|
|
9517
9540
|
this.emit("step-change");
|
|
9518
9541
|
}
|
|
9542
|
+
_clearTimerBucket(bucket) {
|
|
9543
|
+
bucket.forEach((id) => window.clearTimeout(id));
|
|
9544
|
+
bucket.clear();
|
|
9545
|
+
}
|
|
9519
9546
|
_clearPendingStepTimers() {
|
|
9520
|
-
this.
|
|
9521
|
-
this._pendingStepTimers.clear();
|
|
9547
|
+
this._clearTimerBucket(this._pendingStepTimers);
|
|
9522
9548
|
}
|
|
9523
|
-
|
|
9549
|
+
_cancelNarrationTyping() {
|
|
9550
|
+
this._narrationRunId += 1;
|
|
9551
|
+
this._clearTimerBucket(this._pendingNarrationTimers);
|
|
9552
|
+
}
|
|
9553
|
+
_scheduleTimer(fn, delayMs, bucket = this._pendingStepTimers) {
|
|
9524
9554
|
if (delayMs <= 0) {
|
|
9525
9555
|
fn();
|
|
9526
9556
|
return;
|
|
9527
9557
|
}
|
|
9528
9558
|
const id = window.setTimeout(() => {
|
|
9529
|
-
|
|
9559
|
+
bucket.delete(id);
|
|
9530
9560
|
fn();
|
|
9531
9561
|
}, delayMs);
|
|
9532
|
-
|
|
9562
|
+
bucket.add(id);
|
|
9563
|
+
}
|
|
9564
|
+
_scheduleStep(fn, delayMs) {
|
|
9565
|
+
this._scheduleTimer(fn, delayMs, this._pendingStepTimers);
|
|
9533
9566
|
}
|
|
9534
9567
|
_stepWaitMs(step, fallbackMs) {
|
|
9535
9568
|
const delay = Math.max(0, step.delay ?? 0);
|
|
@@ -9573,6 +9606,7 @@ var AIDiagram = (function (exports) {
|
|
|
9573
9606
|
}
|
|
9574
9607
|
_clearAll() {
|
|
9575
9608
|
this._clearPendingStepTimers();
|
|
9609
|
+
this._cancelNarrationTyping();
|
|
9576
9610
|
this._cancelSpeech();
|
|
9577
9611
|
this._transforms.clear();
|
|
9578
9612
|
// Nodes
|
|
@@ -10037,7 +10071,7 @@ var AIDiagram = (function (exports) {
|
|
|
10037
10071
|
if (!nodeGuidePathEl(nodeEl) && !nodeEl.querySelector("path")?.style.strokeDasharray) {
|
|
10038
10072
|
prepareNodeForDraw(nodeEl);
|
|
10039
10073
|
}
|
|
10040
|
-
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur);
|
|
10074
|
+
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10041
10075
|
}
|
|
10042
10076
|
}
|
|
10043
10077
|
// ── erase ─────────────────────────────────────────────────
|
|
@@ -10127,6 +10161,7 @@ var AIDiagram = (function (exports) {
|
|
|
10127
10161
|
_doNarrate(text, silent) {
|
|
10128
10162
|
if (!this._captionEl || !this._captionTextEl)
|
|
10129
10163
|
return;
|
|
10164
|
+
this._cancelNarrationTyping();
|
|
10130
10165
|
this._captionEl.style.opacity = "1";
|
|
10131
10166
|
if (silent || !text) {
|
|
10132
10167
|
this._captionTextEl.textContent = text;
|
|
@@ -10137,12 +10172,16 @@ var AIDiagram = (function (exports) {
|
|
|
10137
10172
|
this._speak(text);
|
|
10138
10173
|
// Typing effect
|
|
10139
10174
|
this._captionTextEl.textContent = "";
|
|
10175
|
+
const narrationRunId = this._narrationRunId;
|
|
10140
10176
|
let charIdx = 0;
|
|
10141
10177
|
const typeNext = () => {
|
|
10178
|
+
if (this._narrationRunId !== narrationRunId || !this._captionTextEl)
|
|
10179
|
+
return;
|
|
10142
10180
|
if (charIdx < text.length) {
|
|
10143
10181
|
this._captionTextEl.textContent += text[charIdx++];
|
|
10144
|
-
|
|
10145
|
-
|
|
10182
|
+
if (charIdx < text.length) {
|
|
10183
|
+
this._scheduleTimer(typeNext, ANIMATION.narrationTypeMs, this._pendingNarrationTimers);
|
|
10184
|
+
}
|
|
10146
10185
|
}
|
|
10147
10186
|
};
|
|
10148
10187
|
typeNext();
|
|
@@ -10252,12 +10291,11 @@ var AIDiagram = (function (exports) {
|
|
|
10252
10291
|
requestAnimationFrame(animate);
|
|
10253
10292
|
}
|
|
10254
10293
|
// After guide finishes: reveal rough.js element, remove guide
|
|
10255
|
-
|
|
10294
|
+
this._scheduleTimer(() => {
|
|
10256
10295
|
roughEl.style.transition = `opacity 120ms ease`;
|
|
10257
10296
|
roughEl.style.opacity = "1";
|
|
10258
10297
|
guide.remove();
|
|
10259
10298
|
}, dur + 30);
|
|
10260
|
-
this._pendingStepTimers.add(id);
|
|
10261
10299
|
}));
|
|
10262
10300
|
}
|
|
10263
10301
|
_doAnnotationCircle(target, silent) {
|
|
@@ -11834,6 +11872,7 @@ var AIDiagram = (function (exports) {
|
|
|
11834
11872
|
|
|
11835
11873
|
.skm-embed__controls {
|
|
11836
11874
|
display: flex;
|
|
11875
|
+
flex-wrap: wrap;
|
|
11837
11876
|
align-items: center;
|
|
11838
11877
|
gap: 8px;
|
|
11839
11878
|
padding: 10px 12px;
|
|
@@ -11851,6 +11890,13 @@ var AIDiagram = (function (exports) {
|
|
|
11851
11890
|
display: none;
|
|
11852
11891
|
}
|
|
11853
11892
|
|
|
11893
|
+
.skm-embed__controls-group {
|
|
11894
|
+
display: flex;
|
|
11895
|
+
align-items: center;
|
|
11896
|
+
gap: 8px;
|
|
11897
|
+
flex-wrap: wrap;
|
|
11898
|
+
}
|
|
11899
|
+
|
|
11854
11900
|
.skm-embed__button {
|
|
11855
11901
|
border: 1px solid #caba98;
|
|
11856
11902
|
background: #f5eedd;
|
|
@@ -11886,6 +11932,17 @@ var AIDiagram = (function (exports) {
|
|
|
11886
11932
|
cursor: default;
|
|
11887
11933
|
}
|
|
11888
11934
|
|
|
11935
|
+
.skm-embed__zoom {
|
|
11936
|
+
min-width: 48px;
|
|
11937
|
+
text-align: center;
|
|
11938
|
+
color: #8a6040;
|
|
11939
|
+
font-size: 11px;
|
|
11940
|
+
}
|
|
11941
|
+
|
|
11942
|
+
.skm-embed--dark .skm-embed__zoom {
|
|
11943
|
+
color: #d0b176;
|
|
11944
|
+
}
|
|
11945
|
+
|
|
11889
11946
|
.skm-embed__step {
|
|
11890
11947
|
margin-left: auto;
|
|
11891
11948
|
min-width: 96px;
|
|
@@ -11904,8 +11961,10 @@ var AIDiagram = (function (exports) {
|
|
|
11904
11961
|
this.emitter = new EventEmitter();
|
|
11905
11962
|
this.animUnsub = null;
|
|
11906
11963
|
this.playInFlight = false;
|
|
11964
|
+
this.zoom = 1;
|
|
11907
11965
|
this.offsetX = 0;
|
|
11908
11966
|
this.offsetY = 0;
|
|
11967
|
+
this.autoFitEnabled = true;
|
|
11909
11968
|
this.motionFrame = null;
|
|
11910
11969
|
this.resizeObserver = null;
|
|
11911
11970
|
this.options = options;
|
|
@@ -11926,10 +11985,18 @@ var AIDiagram = (function (exports) {
|
|
|
11926
11985
|
</div>
|
|
11927
11986
|
<div class="skm-embed__error"></div>
|
|
11928
11987
|
<div class="skm-embed__controls">
|
|
11929
|
-
<
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11988
|
+
<div class="skm-embed__controls-group">
|
|
11989
|
+
<button type="button" class="skm-embed__button" data-action="zoom-out">-</button>
|
|
11990
|
+
<span class="skm-embed__zoom">100%</span>
|
|
11991
|
+
<button type="button" class="skm-embed__button" data-action="zoom-in">+</button>
|
|
11992
|
+
<button type="button" class="skm-embed__button" data-action="fit">Reset</button>
|
|
11993
|
+
</div>
|
|
11994
|
+
<div class="skm-embed__controls-group">
|
|
11995
|
+
<button type="button" class="skm-embed__button" data-action="restart">Restart</button>
|
|
11996
|
+
<button type="button" class="skm-embed__button" data-action="prev">Prev</button>
|
|
11997
|
+
<button type="button" class="skm-embed__button" data-action="next">Next</button>
|
|
11998
|
+
<button type="button" class="skm-embed__button" data-action="play">Play</button>
|
|
11999
|
+
</div>
|
|
11933
12000
|
<span class="skm-embed__step">No steps</span>
|
|
11934
12001
|
</div>
|
|
11935
12002
|
`;
|
|
@@ -11939,12 +12006,19 @@ var AIDiagram = (function (exports) {
|
|
|
11939
12006
|
this.errorElement = this.root.querySelector(".skm-embed__error");
|
|
11940
12007
|
this.controlsElement = this.root.querySelector(".skm-embed__controls");
|
|
11941
12008
|
this.stepInfoElement = this.root.querySelector(".skm-embed__step");
|
|
11942
|
-
this.
|
|
12009
|
+
this.zoomInfoElement = this.root.querySelector(".skm-embed__zoom");
|
|
12010
|
+
this.btnFit = this.root.querySelector('[data-action="fit"]');
|
|
12011
|
+
this.btnZoomIn = this.root.querySelector('[data-action="zoom-in"]');
|
|
12012
|
+
this.btnZoomOut = this.root.querySelector('[data-action="zoom-out"]');
|
|
12013
|
+
this.btnRestart = this.root.querySelector('[data-action="restart"]');
|
|
11943
12014
|
this.btnPrev = this.root.querySelector('[data-action="prev"]');
|
|
11944
12015
|
this.btnNext = this.root.querySelector('[data-action="next"]');
|
|
11945
12016
|
this.btnPlay = this.root.querySelector('[data-action="play"]');
|
|
11946
12017
|
this.controlsElement.classList.toggle("is-hidden", options.showControls === false);
|
|
11947
|
-
this.
|
|
12018
|
+
this.btnFit.addEventListener("click", () => this.resetView());
|
|
12019
|
+
this.btnZoomIn.addEventListener("click", () => this.zoomIn());
|
|
12020
|
+
this.btnZoomOut.addEventListener("click", () => this.zoomOut());
|
|
12021
|
+
this.btnRestart.addEventListener("click", () => this.resetAnimation());
|
|
11948
12022
|
this.btnPrev.addEventListener("click", () => this.prevStep());
|
|
11949
12023
|
this.btnNext.addEventListener("click", () => this.nextStep());
|
|
11950
12024
|
this.btnPlay.addEventListener("click", () => {
|
|
@@ -11990,7 +12064,12 @@ var AIDiagram = (function (exports) {
|
|
|
11990
12064
|
this.animUnsub = null;
|
|
11991
12065
|
this.instance?.anim?.destroy();
|
|
11992
12066
|
this.instance = null;
|
|
12067
|
+
this.autoFitEnabled = true;
|
|
12068
|
+
this.zoom = 1;
|
|
12069
|
+
this.offsetX = 0;
|
|
12070
|
+
this.offsetY = 0;
|
|
11993
12071
|
this.diagramWrap.innerHTML = "";
|
|
12072
|
+
this.applyTransform();
|
|
11994
12073
|
try {
|
|
11995
12074
|
const instance = render({
|
|
11996
12075
|
container: this.diagramWrap,
|
|
@@ -12071,6 +12150,21 @@ var AIDiagram = (function (exports) {
|
|
|
12071
12150
|
this.syncControls();
|
|
12072
12151
|
this.positionViewport(true);
|
|
12073
12152
|
}
|
|
12153
|
+
fitToViewport(animated = false) {
|
|
12154
|
+
if (!this.instance?.svg)
|
|
12155
|
+
return;
|
|
12156
|
+
this.autoFitEnabled = true;
|
|
12157
|
+
this.positionViewport(animated);
|
|
12158
|
+
}
|
|
12159
|
+
resetView(animated = false) {
|
|
12160
|
+
this.fitToViewport(animated);
|
|
12161
|
+
}
|
|
12162
|
+
zoomIn() {
|
|
12163
|
+
this.zoomAroundViewportCenter(1.2);
|
|
12164
|
+
}
|
|
12165
|
+
zoomOut() {
|
|
12166
|
+
this.zoomAroundViewportCenter(0.8);
|
|
12167
|
+
}
|
|
12074
12168
|
exportSVG(filename) {
|
|
12075
12169
|
this.instance?.exportSVG(filename);
|
|
12076
12170
|
}
|
|
@@ -12093,10 +12187,14 @@ var AIDiagram = (function (exports) {
|
|
|
12093
12187
|
return typeof value === "number" ? `${value}px` : value;
|
|
12094
12188
|
}
|
|
12095
12189
|
syncControls() {
|
|
12190
|
+
this.syncAnimationControls();
|
|
12191
|
+
this.syncViewControls();
|
|
12192
|
+
}
|
|
12193
|
+
syncAnimationControls() {
|
|
12096
12194
|
const anim = this.instance?.anim;
|
|
12097
12195
|
if (!anim || !anim.total) {
|
|
12098
12196
|
this.stepInfoElement.textContent = "No steps";
|
|
12099
|
-
this.
|
|
12197
|
+
this.btnRestart.disabled = true;
|
|
12100
12198
|
this.btnPrev.disabled = true;
|
|
12101
12199
|
this.btnNext.disabled = true;
|
|
12102
12200
|
this.btnPlay.disabled = true;
|
|
@@ -12104,56 +12202,69 @@ var AIDiagram = (function (exports) {
|
|
|
12104
12202
|
}
|
|
12105
12203
|
this.stepInfoElement.textContent =
|
|
12106
12204
|
anim.currentStep < 0 ? `${anim.total} steps` : `${anim.currentStep + 1} / ${anim.total}`;
|
|
12107
|
-
this.
|
|
12205
|
+
this.btnRestart.disabled = false;
|
|
12108
12206
|
this.btnPrev.disabled = !anim.canPrev;
|
|
12109
12207
|
this.btnNext.disabled = !anim.canNext;
|
|
12110
12208
|
this.btnPlay.disabled = this.playInFlight || !anim.canNext;
|
|
12111
12209
|
}
|
|
12210
|
+
syncViewControls() {
|
|
12211
|
+
const hasView = !!this.instance?.svg;
|
|
12212
|
+
const zoomMin = this.getZoomMin();
|
|
12213
|
+
const zoomMax = this.getZoomMax();
|
|
12214
|
+
this.zoomInfoElement.textContent = `${Math.round(this.zoom * 100)}%`;
|
|
12215
|
+
this.btnFit.disabled = !hasView;
|
|
12216
|
+
this.btnZoomOut.disabled = !hasView || this.zoom <= zoomMin + 0.001;
|
|
12217
|
+
this.btnZoomIn.disabled = !hasView || this.zoom >= zoomMax - 0.001;
|
|
12218
|
+
}
|
|
12112
12219
|
positionViewport(animated) {
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
const svg = this.instance.svg;
|
|
12116
|
-
const svgWidth = parseFloat(svg.getAttribute("width") || "0");
|
|
12117
|
-
const svgHeight = parseFloat(svg.getAttribute("height") || "0");
|
|
12118
|
-
if (!svgWidth || !svgHeight)
|
|
12220
|
+
const size = this.getContentSize();
|
|
12221
|
+
if (!size)
|
|
12119
12222
|
return;
|
|
12223
|
+
const { width: svgWidth, height: svgHeight } = size;
|
|
12120
12224
|
const viewportRect = this.viewport.getBoundingClientRect();
|
|
12121
12225
|
const viewWidth = viewportRect.width || this.viewport.clientWidth;
|
|
12122
12226
|
const viewHeight = viewportRect.height || this.viewport.clientHeight;
|
|
12123
12227
|
if (!viewWidth || !viewHeight)
|
|
12124
12228
|
return;
|
|
12125
|
-
|
|
12229
|
+
if (this.autoFitEnabled) {
|
|
12230
|
+
this.zoom = this.getFitZoom(svgWidth, svgHeight, viewWidth, viewHeight);
|
|
12231
|
+
}
|
|
12232
|
+
this.syncViewControls();
|
|
12233
|
+
const scaledWidth = svgWidth * this.zoom;
|
|
12234
|
+
const scaledHeight = svgHeight * this.zoom;
|
|
12235
|
+
const focusTarget = this.getFocusTarget();
|
|
12236
|
+
const sceneIsLarge = scaledWidth > viewWidth || scaledHeight > viewHeight;
|
|
12126
12237
|
const shouldFocus = sceneIsLarge &&
|
|
12127
12238
|
this.options.autoFocus !== false &&
|
|
12128
|
-
!!
|
|
12239
|
+
!!focusTarget;
|
|
12129
12240
|
if (!shouldFocus) {
|
|
12130
|
-
this.animateTo(
|
|
12241
|
+
this.animateTo(scaledWidth <= viewWidth ? (viewWidth - scaledWidth) / 2 : 0, scaledHeight <= viewHeight ? (viewHeight - scaledHeight) / 2 : 0, animated);
|
|
12131
12242
|
return;
|
|
12132
12243
|
}
|
|
12133
|
-
const target = this.findTargetElement(
|
|
12244
|
+
const target = this.findTargetElement(focusTarget);
|
|
12134
12245
|
if (!target) {
|
|
12135
12246
|
this.animateTo(0, 0, animated);
|
|
12136
12247
|
return;
|
|
12137
12248
|
}
|
|
12138
|
-
const
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
let nextX = viewWidth / 2 -
|
|
12144
|
-
let nextY = viewHeight / 2 -
|
|
12249
|
+
const targetBox = this.getTargetBox(target, viewportRect);
|
|
12250
|
+
if (!targetBox) {
|
|
12251
|
+
this.animateTo(0, 0, animated);
|
|
12252
|
+
return;
|
|
12253
|
+
}
|
|
12254
|
+
let nextX = viewWidth / 2 - targetBox.centerX;
|
|
12255
|
+
let nextY = viewHeight / 2 - targetBox.centerY;
|
|
12145
12256
|
const padding = this.options.focusPadding ?? 24;
|
|
12146
|
-
if (
|
|
12147
|
-
nextX = (viewWidth -
|
|
12257
|
+
if (scaledWidth <= viewWidth) {
|
|
12258
|
+
nextX = (viewWidth - scaledWidth) / 2;
|
|
12148
12259
|
}
|
|
12149
12260
|
else {
|
|
12150
|
-
nextX = clamp(nextX, viewWidth -
|
|
12261
|
+
nextX = clamp(nextX, viewWidth - scaledWidth - padding, padding);
|
|
12151
12262
|
}
|
|
12152
|
-
if (
|
|
12153
|
-
nextY = (viewHeight -
|
|
12263
|
+
if (scaledHeight <= viewHeight) {
|
|
12264
|
+
nextY = (viewHeight - scaledHeight) / 2;
|
|
12154
12265
|
}
|
|
12155
12266
|
else {
|
|
12156
|
-
nextY = clamp(nextY, viewHeight -
|
|
12267
|
+
nextY = clamp(nextY, viewHeight - scaledHeight - padding, padding);
|
|
12157
12268
|
}
|
|
12158
12269
|
this.animateTo(nextX, nextY, animated);
|
|
12159
12270
|
}
|
|
@@ -12185,7 +12296,78 @@ var AIDiagram = (function (exports) {
|
|
|
12185
12296
|
this.motionFrame = requestAnimationFrame(frame);
|
|
12186
12297
|
}
|
|
12187
12298
|
applyTransform() {
|
|
12188
|
-
this.world.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px)`;
|
|
12299
|
+
this.world.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(${this.zoom})`;
|
|
12300
|
+
this.zoomInfoElement.textContent = `${Math.round(this.zoom * 100)}%`;
|
|
12301
|
+
}
|
|
12302
|
+
getContentSize() {
|
|
12303
|
+
if (!this.instance?.svg)
|
|
12304
|
+
return null;
|
|
12305
|
+
const svg = this.instance.svg;
|
|
12306
|
+
const width = parseFloat(svg.getAttribute("width") || "0");
|
|
12307
|
+
const height = parseFloat(svg.getAttribute("height") || "0");
|
|
12308
|
+
if (!width || !height)
|
|
12309
|
+
return null;
|
|
12310
|
+
return { width, height };
|
|
12311
|
+
}
|
|
12312
|
+
getFitZoom(svgWidth, svgHeight, viewWidth, viewHeight) {
|
|
12313
|
+
const padding = this.getFitPadding(viewWidth, viewHeight);
|
|
12314
|
+
const availableWidth = Math.max(viewWidth - padding * 2, 1);
|
|
12315
|
+
const availableHeight = Math.max(viewHeight - padding * 2, 1);
|
|
12316
|
+
const nextZoom = Math.min(availableWidth / svgWidth, availableHeight / svgHeight, 1);
|
|
12317
|
+
return clamp(nextZoom || 1, this.getZoomMin(), this.getZoomMax());
|
|
12318
|
+
}
|
|
12319
|
+
getFitPadding(viewWidth, viewHeight) {
|
|
12320
|
+
if (typeof this.options.fitPadding === "number") {
|
|
12321
|
+
return Math.max(0, this.options.fitPadding);
|
|
12322
|
+
}
|
|
12323
|
+
return Math.max(16, Math.min(40, Math.round(Math.min(viewWidth, viewHeight) * 0.08)));
|
|
12324
|
+
}
|
|
12325
|
+
getZoomMin() {
|
|
12326
|
+
return this.options.zoomMin ?? 0.08;
|
|
12327
|
+
}
|
|
12328
|
+
getZoomMax() {
|
|
12329
|
+
return this.options.zoomMax ?? 4;
|
|
12330
|
+
}
|
|
12331
|
+
zoomAroundViewportCenter(factor) {
|
|
12332
|
+
if (!this.instance?.svg)
|
|
12333
|
+
return;
|
|
12334
|
+
const pivotX = this.viewport.clientWidth / 2;
|
|
12335
|
+
const pivotY = this.viewport.clientHeight / 2;
|
|
12336
|
+
this.zoomTo(this.zoom * factor, pivotX, pivotY);
|
|
12337
|
+
}
|
|
12338
|
+
zoomTo(nextZoom, pivotX, pivotY) {
|
|
12339
|
+
const clampedZoom = clamp(nextZoom, this.getZoomMin(), this.getZoomMax());
|
|
12340
|
+
const ratio = clampedZoom / this.zoom;
|
|
12341
|
+
if (!Number.isFinite(ratio) || ratio === 1) {
|
|
12342
|
+
this.syncViewControls();
|
|
12343
|
+
return;
|
|
12344
|
+
}
|
|
12345
|
+
this.stopMotion();
|
|
12346
|
+
this.autoFitEnabled = false;
|
|
12347
|
+
this.offsetX = pivotX - (pivotX - this.offsetX) * ratio;
|
|
12348
|
+
this.offsetY = pivotY - (pivotY - this.offsetY) * ratio;
|
|
12349
|
+
this.zoom = clampedZoom;
|
|
12350
|
+
this.applyTransform();
|
|
12351
|
+
this.syncViewControls();
|
|
12352
|
+
}
|
|
12353
|
+
getTargetBox(target, viewportRect) {
|
|
12354
|
+
if (target instanceof SVGGraphicsElement) {
|
|
12355
|
+
try {
|
|
12356
|
+
const bounds = target.getBBox();
|
|
12357
|
+
return {
|
|
12358
|
+
centerX: (bounds.x + bounds.width / 2) * this.zoom,
|
|
12359
|
+
centerY: (bounds.y + bounds.height / 2) * this.zoom,
|
|
12360
|
+
};
|
|
12361
|
+
}
|
|
12362
|
+
catch {
|
|
12363
|
+
// Ignore and fall back to layout-based bounds below.
|
|
12364
|
+
}
|
|
12365
|
+
}
|
|
12366
|
+
const currentRect = target.getBoundingClientRect();
|
|
12367
|
+
return {
|
|
12368
|
+
centerX: currentRect.left - viewportRect.left - this.offsetX + currentRect.width / 2,
|
|
12369
|
+
centerY: currentRect.top - viewportRect.top - this.offsetY + currentRect.height / 2,
|
|
12370
|
+
};
|
|
12189
12371
|
}
|
|
12190
12372
|
getFocusTarget() {
|
|
12191
12373
|
const anim = this.instance?.anim;
|
package/dist/ui/embed.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export interface SketchmarkEmbedOptions {
|
|
|
12
12
|
theme?: EmbedTheme;
|
|
13
13
|
showControls?: boolean;
|
|
14
14
|
playStepDelay?: number;
|
|
15
|
+
fitPadding?: number;
|
|
16
|
+
zoomMin?: number;
|
|
17
|
+
zoomMax?: number;
|
|
15
18
|
focusPadding?: number;
|
|
16
19
|
focusDuration?: number;
|
|
17
20
|
autoFocus?: boolean;
|
|
@@ -43,19 +46,25 @@ export declare class SketchmarkEmbed {
|
|
|
43
46
|
readonly errorElement: HTMLDivElement;
|
|
44
47
|
readonly controlsElement: HTMLDivElement;
|
|
45
48
|
readonly stepInfoElement: HTMLSpanElement;
|
|
49
|
+
readonly zoomInfoElement: HTMLSpanElement;
|
|
46
50
|
instance: DiagramInstance | null;
|
|
47
51
|
private readonly emitter;
|
|
48
52
|
private readonly options;
|
|
49
|
-
private readonly
|
|
53
|
+
private readonly btnRestart;
|
|
50
54
|
private readonly btnPrev;
|
|
51
55
|
private readonly btnNext;
|
|
52
56
|
private readonly btnPlay;
|
|
57
|
+
private readonly btnFit;
|
|
58
|
+
private readonly btnZoomIn;
|
|
59
|
+
private readonly btnZoomOut;
|
|
53
60
|
private animUnsub;
|
|
54
61
|
private playInFlight;
|
|
55
62
|
private dsl;
|
|
56
63
|
private theme;
|
|
64
|
+
private zoom;
|
|
57
65
|
private offsetX;
|
|
58
66
|
private offsetY;
|
|
67
|
+
private autoFitEnabled;
|
|
59
68
|
private motionFrame;
|
|
60
69
|
private resizeObserver;
|
|
61
70
|
constructor(options: SketchmarkEmbedOptions);
|
|
@@ -69,15 +78,29 @@ export declare class SketchmarkEmbed {
|
|
|
69
78
|
nextStep(): void;
|
|
70
79
|
prevStep(): void;
|
|
71
80
|
resetAnimation(): void;
|
|
81
|
+
fitToViewport(animated?: boolean): void;
|
|
82
|
+
resetView(animated?: boolean): void;
|
|
83
|
+
zoomIn(): void;
|
|
84
|
+
zoomOut(): void;
|
|
72
85
|
exportSVG(filename?: string): void;
|
|
73
86
|
exportPNG(filename?: string): Promise<void>;
|
|
74
87
|
destroy(): void;
|
|
75
88
|
private applySize;
|
|
76
89
|
private formatSize;
|
|
77
90
|
private syncControls;
|
|
91
|
+
private syncAnimationControls;
|
|
92
|
+
private syncViewControls;
|
|
78
93
|
private positionViewport;
|
|
79
94
|
private animateTo;
|
|
80
95
|
private applyTransform;
|
|
96
|
+
private getContentSize;
|
|
97
|
+
private getFitZoom;
|
|
98
|
+
private getFitPadding;
|
|
99
|
+
private getZoomMin;
|
|
100
|
+
private getZoomMax;
|
|
101
|
+
private zoomAroundViewportCenter;
|
|
102
|
+
private zoomTo;
|
|
103
|
+
private getTargetBox;
|
|
81
104
|
private getFocusTarget;
|
|
82
105
|
private findTargetElement;
|
|
83
106
|
private getStepTarget;
|
package/dist/ui/embed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../../src/ui/embed.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../../src/ui/embed.ts"],"names":[],"mappings":"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"}
|