sketchmark 1.1.3 → 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.
Potentially problematic release.
This version of sketchmark might be problematic. Click here for more details.
- package/README.md +101 -104
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/index.cjs +232 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +232 -67
- package/dist/index.js.map +1 -1
- package/dist/sketchmark.iife.js +232 -67
- package/dist/ui/embed.d.ts +24 -1
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +11 -12
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);
|
|
@@ -10050,7 +10071,7 @@ var AIDiagram = (function (exports) {
|
|
|
10050
10071
|
if (!nodeGuidePathEl(nodeEl) && !nodeEl.querySelector("path")?.style.strokeDasharray) {
|
|
10051
10072
|
prepareNodeForDraw(nodeEl);
|
|
10052
10073
|
}
|
|
10053
|
-
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur);
|
|
10074
|
+
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10054
10075
|
}
|
|
10055
10076
|
}
|
|
10056
10077
|
// ── erase ─────────────────────────────────────────────────
|
|
@@ -11851,6 +11872,7 @@ var AIDiagram = (function (exports) {
|
|
|
11851
11872
|
|
|
11852
11873
|
.skm-embed__controls {
|
|
11853
11874
|
display: flex;
|
|
11875
|
+
flex-wrap: wrap;
|
|
11854
11876
|
align-items: center;
|
|
11855
11877
|
gap: 8px;
|
|
11856
11878
|
padding: 10px 12px;
|
|
@@ -11868,6 +11890,13 @@ var AIDiagram = (function (exports) {
|
|
|
11868
11890
|
display: none;
|
|
11869
11891
|
}
|
|
11870
11892
|
|
|
11893
|
+
.skm-embed__controls-group {
|
|
11894
|
+
display: flex;
|
|
11895
|
+
align-items: center;
|
|
11896
|
+
gap: 8px;
|
|
11897
|
+
flex-wrap: wrap;
|
|
11898
|
+
}
|
|
11899
|
+
|
|
11871
11900
|
.skm-embed__button {
|
|
11872
11901
|
border: 1px solid #caba98;
|
|
11873
11902
|
background: #f5eedd;
|
|
@@ -11903,6 +11932,17 @@ var AIDiagram = (function (exports) {
|
|
|
11903
11932
|
cursor: default;
|
|
11904
11933
|
}
|
|
11905
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
|
+
|
|
11906
11946
|
.skm-embed__step {
|
|
11907
11947
|
margin-left: auto;
|
|
11908
11948
|
min-width: 96px;
|
|
@@ -11921,8 +11961,10 @@ var AIDiagram = (function (exports) {
|
|
|
11921
11961
|
this.emitter = new EventEmitter();
|
|
11922
11962
|
this.animUnsub = null;
|
|
11923
11963
|
this.playInFlight = false;
|
|
11964
|
+
this.zoom = 1;
|
|
11924
11965
|
this.offsetX = 0;
|
|
11925
11966
|
this.offsetY = 0;
|
|
11967
|
+
this.autoFitEnabled = true;
|
|
11926
11968
|
this.motionFrame = null;
|
|
11927
11969
|
this.resizeObserver = null;
|
|
11928
11970
|
this.options = options;
|
|
@@ -11943,10 +11985,18 @@ var AIDiagram = (function (exports) {
|
|
|
11943
11985
|
</div>
|
|
11944
11986
|
<div class="skm-embed__error"></div>
|
|
11945
11987
|
<div class="skm-embed__controls">
|
|
11946
|
-
<
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
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>
|
|
11950
12000
|
<span class="skm-embed__step">No steps</span>
|
|
11951
12001
|
</div>
|
|
11952
12002
|
`;
|
|
@@ -11956,12 +12006,19 @@ var AIDiagram = (function (exports) {
|
|
|
11956
12006
|
this.errorElement = this.root.querySelector(".skm-embed__error");
|
|
11957
12007
|
this.controlsElement = this.root.querySelector(".skm-embed__controls");
|
|
11958
12008
|
this.stepInfoElement = this.root.querySelector(".skm-embed__step");
|
|
11959
|
-
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"]');
|
|
11960
12014
|
this.btnPrev = this.root.querySelector('[data-action="prev"]');
|
|
11961
12015
|
this.btnNext = this.root.querySelector('[data-action="next"]');
|
|
11962
12016
|
this.btnPlay = this.root.querySelector('[data-action="play"]');
|
|
11963
12017
|
this.controlsElement.classList.toggle("is-hidden", options.showControls === false);
|
|
11964
|
-
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());
|
|
11965
12022
|
this.btnPrev.addEventListener("click", () => this.prevStep());
|
|
11966
12023
|
this.btnNext.addEventListener("click", () => this.nextStep());
|
|
11967
12024
|
this.btnPlay.addEventListener("click", () => {
|
|
@@ -12007,7 +12064,12 @@ var AIDiagram = (function (exports) {
|
|
|
12007
12064
|
this.animUnsub = null;
|
|
12008
12065
|
this.instance?.anim?.destroy();
|
|
12009
12066
|
this.instance = null;
|
|
12067
|
+
this.autoFitEnabled = true;
|
|
12068
|
+
this.zoom = 1;
|
|
12069
|
+
this.offsetX = 0;
|
|
12070
|
+
this.offsetY = 0;
|
|
12010
12071
|
this.diagramWrap.innerHTML = "";
|
|
12072
|
+
this.applyTransform();
|
|
12011
12073
|
try {
|
|
12012
12074
|
const instance = render({
|
|
12013
12075
|
container: this.diagramWrap,
|
|
@@ -12088,6 +12150,21 @@ var AIDiagram = (function (exports) {
|
|
|
12088
12150
|
this.syncControls();
|
|
12089
12151
|
this.positionViewport(true);
|
|
12090
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
|
+
}
|
|
12091
12168
|
exportSVG(filename) {
|
|
12092
12169
|
this.instance?.exportSVG(filename);
|
|
12093
12170
|
}
|
|
@@ -12110,10 +12187,14 @@ var AIDiagram = (function (exports) {
|
|
|
12110
12187
|
return typeof value === "number" ? `${value}px` : value;
|
|
12111
12188
|
}
|
|
12112
12189
|
syncControls() {
|
|
12190
|
+
this.syncAnimationControls();
|
|
12191
|
+
this.syncViewControls();
|
|
12192
|
+
}
|
|
12193
|
+
syncAnimationControls() {
|
|
12113
12194
|
const anim = this.instance?.anim;
|
|
12114
12195
|
if (!anim || !anim.total) {
|
|
12115
12196
|
this.stepInfoElement.textContent = "No steps";
|
|
12116
|
-
this.
|
|
12197
|
+
this.btnRestart.disabled = true;
|
|
12117
12198
|
this.btnPrev.disabled = true;
|
|
12118
12199
|
this.btnNext.disabled = true;
|
|
12119
12200
|
this.btnPlay.disabled = true;
|
|
@@ -12121,56 +12202,69 @@ var AIDiagram = (function (exports) {
|
|
|
12121
12202
|
}
|
|
12122
12203
|
this.stepInfoElement.textContent =
|
|
12123
12204
|
anim.currentStep < 0 ? `${anim.total} steps` : `${anim.currentStep + 1} / ${anim.total}`;
|
|
12124
|
-
this.
|
|
12205
|
+
this.btnRestart.disabled = false;
|
|
12125
12206
|
this.btnPrev.disabled = !anim.canPrev;
|
|
12126
12207
|
this.btnNext.disabled = !anim.canNext;
|
|
12127
12208
|
this.btnPlay.disabled = this.playInFlight || !anim.canNext;
|
|
12128
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
|
+
}
|
|
12129
12219
|
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)
|
|
12220
|
+
const size = this.getContentSize();
|
|
12221
|
+
if (!size)
|
|
12136
12222
|
return;
|
|
12223
|
+
const { width: svgWidth, height: svgHeight } = size;
|
|
12137
12224
|
const viewportRect = this.viewport.getBoundingClientRect();
|
|
12138
12225
|
const viewWidth = viewportRect.width || this.viewport.clientWidth;
|
|
12139
12226
|
const viewHeight = viewportRect.height || this.viewport.clientHeight;
|
|
12140
12227
|
if (!viewWidth || !viewHeight)
|
|
12141
12228
|
return;
|
|
12142
|
-
|
|
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;
|
|
12143
12237
|
const shouldFocus = sceneIsLarge &&
|
|
12144
12238
|
this.options.autoFocus !== false &&
|
|
12145
|
-
!!
|
|
12239
|
+
!!focusTarget;
|
|
12146
12240
|
if (!shouldFocus) {
|
|
12147
|
-
this.animateTo(
|
|
12241
|
+
this.animateTo(scaledWidth <= viewWidth ? (viewWidth - scaledWidth) / 2 : 0, scaledHeight <= viewHeight ? (viewHeight - scaledHeight) / 2 : 0, animated);
|
|
12148
12242
|
return;
|
|
12149
12243
|
}
|
|
12150
|
-
const target = this.findTargetElement(
|
|
12244
|
+
const target = this.findTargetElement(focusTarget);
|
|
12151
12245
|
if (!target) {
|
|
12152
12246
|
this.animateTo(0, 0, animated);
|
|
12153
12247
|
return;
|
|
12154
12248
|
}
|
|
12155
|
-
const
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
let nextX = viewWidth / 2 -
|
|
12161
|
-
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;
|
|
12162
12256
|
const padding = this.options.focusPadding ?? 24;
|
|
12163
|
-
if (
|
|
12164
|
-
nextX = (viewWidth -
|
|
12257
|
+
if (scaledWidth <= viewWidth) {
|
|
12258
|
+
nextX = (viewWidth - scaledWidth) / 2;
|
|
12165
12259
|
}
|
|
12166
12260
|
else {
|
|
12167
|
-
nextX = clamp(nextX, viewWidth -
|
|
12261
|
+
nextX = clamp(nextX, viewWidth - scaledWidth - padding, padding);
|
|
12168
12262
|
}
|
|
12169
|
-
if (
|
|
12170
|
-
nextY = (viewHeight -
|
|
12263
|
+
if (scaledHeight <= viewHeight) {
|
|
12264
|
+
nextY = (viewHeight - scaledHeight) / 2;
|
|
12171
12265
|
}
|
|
12172
12266
|
else {
|
|
12173
|
-
nextY = clamp(nextY, viewHeight -
|
|
12267
|
+
nextY = clamp(nextY, viewHeight - scaledHeight - padding, padding);
|
|
12174
12268
|
}
|
|
12175
12269
|
this.animateTo(nextX, nextY, animated);
|
|
12176
12270
|
}
|
|
@@ -12202,7 +12296,78 @@ var AIDiagram = (function (exports) {
|
|
|
12202
12296
|
this.motionFrame = requestAnimationFrame(frame);
|
|
12203
12297
|
}
|
|
12204
12298
|
applyTransform() {
|
|
12205
|
-
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
|
+
};
|
|
12206
12371
|
}
|
|
12207
12372
|
getFocusTarget() {
|
|
12208
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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sketchmark",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "A plain-text DSL for hand-drawn diagrams. Write boxes, edges, and groups as code — renders sketchy SVG/Canvas via rough.js with a built-in step-by-step animation system.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"diagram",
|
|
@@ -31,15 +31,6 @@
|
|
|
31
31
|
"README.md",
|
|
32
32
|
"LICENSE"
|
|
33
33
|
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "rollup -c rollup.config.js",
|
|
36
|
-
"build:watch": "rollup -c rollup.config.js --watch",
|
|
37
|
-
"typecheck": "tsc --noEmit",
|
|
38
|
-
"lint": "eslint src --ext .ts",
|
|
39
|
-
"prepublishOnly": "npm run typecheck && npm run build",
|
|
40
|
-
"deploy": "wrangler deploy",
|
|
41
|
-
"preview": "wrangler dev"
|
|
42
|
-
},
|
|
43
34
|
"dependencies": {
|
|
44
35
|
"@chenglou/pretext": "^0.0.4"
|
|
45
36
|
},
|
|
@@ -67,5 +58,13 @@
|
|
|
67
58
|
"bugs": {
|
|
68
59
|
"url": "https://github.com/anmism/sketchmark/issues"
|
|
69
60
|
},
|
|
70
|
-
"homepage": "https://github.com/anmism/sketchmark#readme"
|
|
71
|
-
|
|
61
|
+
"homepage": "https://github.com/anmism/sketchmark#readme",
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "rollup -c rollup.config.js",
|
|
64
|
+
"build:watch": "rollup -c rollup.config.js --watch",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"lint": "eslint src --ext .ts",
|
|
67
|
+
"deploy": "wrangler deploy",
|
|
68
|
+
"preview": "wrangler dev"
|
|
69
|
+
}
|
|
70
|
+
}
|