sketchmark 1.3.0 → 1.3.2
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 +1 -1
- package/dist/index.cjs +159 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +159 -5
- package/dist/index.js.map +1 -1
- package/dist/sketchmark.iife.js +159 -5
- package/dist/ui/embed.d.ts +13 -0
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ const embed = new SketchmarkEmbed({
|
|
|
149
149
|
});
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
Use `SketchmarkCanvas` for the full playground-style surface, and `SketchmarkEmbed` for fixed-size embeds that clip overflow, auto-fit large diagrams, and expose built-in zoom, playback, caption, and TTS controls.
|
|
152
|
+
Use `SketchmarkCanvas` for the full playground-style surface, and `SketchmarkEmbed` for fixed-size embeds that clip overflow, auto-fit large diagrams, support drag-to-pan plus wheel/trackpad zoom, and expose built-in zoom, playback, caption, and TTS controls.
|
|
153
153
|
|
|
154
154
|
---
|
|
155
155
|
|
package/dist/index.cjs
CHANGED
|
@@ -12656,6 +12656,12 @@ const EMBED_CSS = `
|
|
|
12656
12656
|
overflow: hidden;
|
|
12657
12657
|
min-height: 0;
|
|
12658
12658
|
background: inherit;
|
|
12659
|
+
cursor: grab;
|
|
12660
|
+
touch-action: none;
|
|
12661
|
+
}
|
|
12662
|
+
|
|
12663
|
+
.skm-embed__viewport.is-panning {
|
|
12664
|
+
cursor: grabbing;
|
|
12659
12665
|
}
|
|
12660
12666
|
|
|
12661
12667
|
.skm-embed__world {
|
|
@@ -12786,6 +12792,83 @@ class SketchmarkEmbed {
|
|
|
12786
12792
|
this.autoFitEnabled = true;
|
|
12787
12793
|
this.motionFrame = null;
|
|
12788
12794
|
this.resizeObserver = null;
|
|
12795
|
+
this.isPanning = false;
|
|
12796
|
+
this.panMoved = false;
|
|
12797
|
+
this.activePointerId = null;
|
|
12798
|
+
this.lastPointerX = 0;
|
|
12799
|
+
this.lastPointerY = 0;
|
|
12800
|
+
this.suppressClickUntil = 0;
|
|
12801
|
+
this.onPointerDown = (event) => {
|
|
12802
|
+
if (event.button !== 0 && event.button !== 1)
|
|
12803
|
+
return;
|
|
12804
|
+
this.isPanning = true;
|
|
12805
|
+
this.panMoved = false;
|
|
12806
|
+
this.activePointerId = event.pointerId;
|
|
12807
|
+
this.lastPointerX = event.clientX;
|
|
12808
|
+
this.lastPointerY = event.clientY;
|
|
12809
|
+
try {
|
|
12810
|
+
this.viewport.setPointerCapture(event.pointerId);
|
|
12811
|
+
}
|
|
12812
|
+
catch {
|
|
12813
|
+
// Ignore pointer capture failures.
|
|
12814
|
+
}
|
|
12815
|
+
};
|
|
12816
|
+
this.onPointerMove = (event) => {
|
|
12817
|
+
if (!this.isPanning)
|
|
12818
|
+
return;
|
|
12819
|
+
if (this.activePointerId !== null && event.pointerId !== this.activePointerId)
|
|
12820
|
+
return;
|
|
12821
|
+
const dx = event.clientX - this.lastPointerX;
|
|
12822
|
+
const dy = event.clientY - this.lastPointerY;
|
|
12823
|
+
if (!this.panMoved && Math.abs(dx) + Math.abs(dy) > 4) {
|
|
12824
|
+
this.panMoved = true;
|
|
12825
|
+
this.viewport.classList.add("is-panning");
|
|
12826
|
+
}
|
|
12827
|
+
if (this.panMoved) {
|
|
12828
|
+
this.stopMotion();
|
|
12829
|
+
this.autoFitEnabled = false;
|
|
12830
|
+
this.offsetX += dx;
|
|
12831
|
+
this.offsetY += dy;
|
|
12832
|
+
this.applyTransform();
|
|
12833
|
+
this.syncViewControls();
|
|
12834
|
+
}
|
|
12835
|
+
this.lastPointerX = event.clientX;
|
|
12836
|
+
this.lastPointerY = event.clientY;
|
|
12837
|
+
};
|
|
12838
|
+
this.onStopPanning = (event) => {
|
|
12839
|
+
if (this.activePointerId !== null && event?.pointerId != null && event.pointerId !== this.activePointerId)
|
|
12840
|
+
return;
|
|
12841
|
+
if (this.panMoved)
|
|
12842
|
+
this.suppressClickUntil = performance.now() + 180;
|
|
12843
|
+
if (this.activePointerId !== null && this.viewport.hasPointerCapture?.(this.activePointerId)) {
|
|
12844
|
+
try {
|
|
12845
|
+
this.viewport.releasePointerCapture(this.activePointerId);
|
|
12846
|
+
}
|
|
12847
|
+
catch {
|
|
12848
|
+
// Ignore pointer capture release failures.
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12851
|
+
this.activePointerId = null;
|
|
12852
|
+
this.isPanning = false;
|
|
12853
|
+
this.panMoved = false;
|
|
12854
|
+
this.viewport.classList.remove("is-panning");
|
|
12855
|
+
};
|
|
12856
|
+
this.onViewportClick = (event) => {
|
|
12857
|
+
if (performance.now() <= this.suppressClickUntil) {
|
|
12858
|
+
event.preventDefault();
|
|
12859
|
+
event.stopPropagation();
|
|
12860
|
+
}
|
|
12861
|
+
};
|
|
12862
|
+
this.onWheel = (event) => {
|
|
12863
|
+
if (!this.instance?.svg)
|
|
12864
|
+
return;
|
|
12865
|
+
event.preventDefault();
|
|
12866
|
+
const rect = this.viewport.getBoundingClientRect();
|
|
12867
|
+
const pivotX = event.clientX - rect.left;
|
|
12868
|
+
const pivotY = event.clientY - rect.top;
|
|
12869
|
+
const factor = Math.exp(-event.deltaY * 0.0015);
|
|
12870
|
+
this.zoomTo(this.zoom * factor, pivotX, pivotY);
|
|
12871
|
+
};
|
|
12789
12872
|
this.options = options;
|
|
12790
12873
|
this.dsl = normalizeNewlines(options.dsl);
|
|
12791
12874
|
this.theme = options.theme ?? "light";
|
|
@@ -12853,6 +12936,13 @@ class SketchmarkEmbed {
|
|
|
12853
12936
|
});
|
|
12854
12937
|
this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
|
|
12855
12938
|
this.btnTts.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
|
|
12939
|
+
this.viewport.addEventListener("pointerdown", this.onPointerDown);
|
|
12940
|
+
this.viewport.addEventListener("pointermove", this.onPointerMove);
|
|
12941
|
+
this.viewport.addEventListener("pointerup", this.onStopPanning);
|
|
12942
|
+
this.viewport.addEventListener("pointercancel", this.onStopPanning);
|
|
12943
|
+
this.viewport.addEventListener("lostpointercapture", this.onStopPanning);
|
|
12944
|
+
this.viewport.addEventListener("click", this.onViewportClick);
|
|
12945
|
+
this.viewport.addEventListener("wheel", this.onWheel, { passive: false });
|
|
12856
12946
|
if (typeof ResizeObserver !== "undefined") {
|
|
12857
12947
|
this.resizeObserver = new ResizeObserver(() => {
|
|
12858
12948
|
this.positionViewport(false);
|
|
@@ -12930,9 +13020,9 @@ class SketchmarkEmbed {
|
|
|
12930
13020
|
this.animUnsub = instance.anim.on((event) => {
|
|
12931
13021
|
this.syncControls();
|
|
12932
13022
|
if (event.type === "step-change") {
|
|
12933
|
-
if (this.options.autoFocusOnStep !== false) {
|
|
13023
|
+
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
12934
13024
|
requestAnimationFrame(() => {
|
|
12935
|
-
window.setTimeout(() => this.
|
|
13025
|
+
window.setTimeout(() => this.focusCurrentStep(true), 40);
|
|
12936
13026
|
});
|
|
12937
13027
|
}
|
|
12938
13028
|
this.emitter.emit("stepchange", {
|
|
@@ -12976,21 +13066,24 @@ class SketchmarkEmbed {
|
|
|
12976
13066
|
return;
|
|
12977
13067
|
this.instance.anim.next();
|
|
12978
13068
|
this.syncControls();
|
|
12979
|
-
this.
|
|
13069
|
+
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
13070
|
+
this.focusCurrentStep(true);
|
|
13071
|
+
}
|
|
12980
13072
|
}
|
|
12981
13073
|
prevStep() {
|
|
12982
13074
|
if (!this.instance)
|
|
12983
13075
|
return;
|
|
12984
13076
|
this.instance.anim.prev();
|
|
12985
13077
|
this.syncControls();
|
|
12986
|
-
this.
|
|
13078
|
+
if (this.options.autoFocus !== false && this.options.autoFocusOnStep !== false) {
|
|
13079
|
+
this.focusCurrentStep(true);
|
|
13080
|
+
}
|
|
12987
13081
|
}
|
|
12988
13082
|
resetAnimation() {
|
|
12989
13083
|
if (!this.instance)
|
|
12990
13084
|
return;
|
|
12991
13085
|
this.instance.anim.reset();
|
|
12992
13086
|
this.syncControls();
|
|
12993
|
-
this.positionViewport(true);
|
|
12994
13087
|
}
|
|
12995
13088
|
fitToViewport(animated = false) {
|
|
12996
13089
|
if (!this.instance?.svg)
|
|
@@ -13019,6 +13112,13 @@ class SketchmarkEmbed {
|
|
|
13019
13112
|
this.instance?.anim?.destroy();
|
|
13020
13113
|
this.instance = null;
|
|
13021
13114
|
this.resizeObserver?.disconnect();
|
|
13115
|
+
this.viewport.removeEventListener("pointerdown", this.onPointerDown);
|
|
13116
|
+
this.viewport.removeEventListener("pointermove", this.onPointerMove);
|
|
13117
|
+
this.viewport.removeEventListener("pointerup", this.onStopPanning);
|
|
13118
|
+
this.viewport.removeEventListener("pointercancel", this.onStopPanning);
|
|
13119
|
+
this.viewport.removeEventListener("lostpointercapture", this.onStopPanning);
|
|
13120
|
+
this.viewport.removeEventListener("click", this.onViewportClick);
|
|
13121
|
+
this.viewport.removeEventListener("wheel", this.onWheel);
|
|
13022
13122
|
this.root.remove();
|
|
13023
13123
|
}
|
|
13024
13124
|
applySize(width, height) {
|
|
@@ -13081,6 +13181,10 @@ class SketchmarkEmbed {
|
|
|
13081
13181
|
this.options.autoFocus !== false &&
|
|
13082
13182
|
!!focusTarget;
|
|
13083
13183
|
if (!shouldFocus) {
|
|
13184
|
+
if (!this.autoFitEnabled) {
|
|
13185
|
+
this.applyTransform();
|
|
13186
|
+
return;
|
|
13187
|
+
}
|
|
13084
13188
|
this.animateTo(scaledWidth <= viewWidth ? (viewWidth - scaledWidth) / 2 : 0, scaledHeight <= viewHeight ? (viewHeight - scaledHeight) / 2 : 0, animated);
|
|
13085
13189
|
return;
|
|
13086
13190
|
}
|
|
@@ -13193,6 +13297,56 @@ class SketchmarkEmbed {
|
|
|
13193
13297
|
this.applyTransform();
|
|
13194
13298
|
this.syncViewControls();
|
|
13195
13299
|
}
|
|
13300
|
+
focusCurrentStep(animated) {
|
|
13301
|
+
const anim = this.instance?.anim;
|
|
13302
|
+
if (!anim || anim.currentStep < 0 || anim.currentStep >= anim.total)
|
|
13303
|
+
return;
|
|
13304
|
+
const targetId = this.getStepTarget(anim.steps[anim.currentStep]);
|
|
13305
|
+
if (targetId)
|
|
13306
|
+
this.focusTarget(targetId, animated);
|
|
13307
|
+
}
|
|
13308
|
+
focusTarget(targetId, animated) {
|
|
13309
|
+
const size = this.getContentSize();
|
|
13310
|
+
if (!size)
|
|
13311
|
+
return;
|
|
13312
|
+
const viewportRect = this.viewport.getBoundingClientRect();
|
|
13313
|
+
const viewWidth = viewportRect.width || this.viewport.clientWidth;
|
|
13314
|
+
const viewHeight = viewportRect.height || this.viewport.clientHeight;
|
|
13315
|
+
if (!viewWidth || !viewHeight)
|
|
13316
|
+
return;
|
|
13317
|
+
const target = this.findTargetElement(targetId);
|
|
13318
|
+
if (!target)
|
|
13319
|
+
return;
|
|
13320
|
+
const targetBox = this.getTargetBox(target, viewportRect);
|
|
13321
|
+
if (!targetBox)
|
|
13322
|
+
return;
|
|
13323
|
+
const centerX = this.offsetX + targetBox.centerX;
|
|
13324
|
+
const centerY = this.offsetY + targetBox.centerY;
|
|
13325
|
+
const padding = this.options.focusPadding ?? 24;
|
|
13326
|
+
if (centerX >= padding &&
|
|
13327
|
+
centerX <= viewWidth - padding &&
|
|
13328
|
+
centerY >= padding &&
|
|
13329
|
+
centerY <= viewHeight - padding) {
|
|
13330
|
+
return;
|
|
13331
|
+
}
|
|
13332
|
+
const scaledWidth = size.width * this.zoom;
|
|
13333
|
+
const scaledHeight = size.height * this.zoom;
|
|
13334
|
+
let nextX = viewWidth / 2 - targetBox.centerX;
|
|
13335
|
+
let nextY = viewHeight / 2 - targetBox.centerY;
|
|
13336
|
+
if (scaledWidth <= viewWidth) {
|
|
13337
|
+
nextX = (viewWidth - scaledWidth) / 2;
|
|
13338
|
+
}
|
|
13339
|
+
else {
|
|
13340
|
+
nextX = clamp(nextX, viewWidth - scaledWidth - padding, padding);
|
|
13341
|
+
}
|
|
13342
|
+
if (scaledHeight <= viewHeight) {
|
|
13343
|
+
nextY = (viewHeight - scaledHeight) / 2;
|
|
13344
|
+
}
|
|
13345
|
+
else {
|
|
13346
|
+
nextY = clamp(nextY, viewHeight - scaledHeight - padding, padding);
|
|
13347
|
+
}
|
|
13348
|
+
this.animateTo(nextX, nextY, animated);
|
|
13349
|
+
}
|
|
13196
13350
|
applyCaptionVisibility(instance) {
|
|
13197
13351
|
const caption = instance?.anim.captionElement;
|
|
13198
13352
|
if (!caption)
|