sketchmark 1.3.0 → 1.3.1

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 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 {
@@ -12784,8 +12790,87 @@ class SketchmarkEmbed {
12784
12790
  this.offsetX = 0;
12785
12791
  this.offsetY = 0;
12786
12792
  this.autoFitEnabled = true;
12793
+ this.autoFocusEnabled = true;
12787
12794
  this.motionFrame = null;
12788
12795
  this.resizeObserver = null;
12796
+ this.isPanning = false;
12797
+ this.panMoved = false;
12798
+ this.activePointerId = null;
12799
+ this.lastPointerX = 0;
12800
+ this.lastPointerY = 0;
12801
+ this.suppressClickUntil = 0;
12802
+ this.onPointerDown = (event) => {
12803
+ if (event.button !== 0 && event.button !== 1)
12804
+ return;
12805
+ this.isPanning = true;
12806
+ this.panMoved = false;
12807
+ this.activePointerId = event.pointerId;
12808
+ this.lastPointerX = event.clientX;
12809
+ this.lastPointerY = event.clientY;
12810
+ try {
12811
+ this.viewport.setPointerCapture(event.pointerId);
12812
+ }
12813
+ catch {
12814
+ // Ignore pointer capture failures.
12815
+ }
12816
+ };
12817
+ this.onPointerMove = (event) => {
12818
+ if (!this.isPanning)
12819
+ return;
12820
+ if (this.activePointerId !== null && event.pointerId !== this.activePointerId)
12821
+ return;
12822
+ const dx = event.clientX - this.lastPointerX;
12823
+ const dy = event.clientY - this.lastPointerY;
12824
+ if (!this.panMoved && Math.abs(dx) + Math.abs(dy) > 4) {
12825
+ this.panMoved = true;
12826
+ this.viewport.classList.add("is-panning");
12827
+ }
12828
+ if (this.panMoved) {
12829
+ this.stopMotion();
12830
+ this.autoFitEnabled = false;
12831
+ this.autoFocusEnabled = false;
12832
+ this.offsetX += dx;
12833
+ this.offsetY += dy;
12834
+ this.applyTransform();
12835
+ this.syncViewControls();
12836
+ }
12837
+ this.lastPointerX = event.clientX;
12838
+ this.lastPointerY = event.clientY;
12839
+ };
12840
+ this.onStopPanning = (event) => {
12841
+ if (this.activePointerId !== null && event?.pointerId != null && event.pointerId !== this.activePointerId)
12842
+ return;
12843
+ if (this.panMoved)
12844
+ this.suppressClickUntil = performance.now() + 180;
12845
+ if (this.activePointerId !== null && this.viewport.hasPointerCapture?.(this.activePointerId)) {
12846
+ try {
12847
+ this.viewport.releasePointerCapture(this.activePointerId);
12848
+ }
12849
+ catch {
12850
+ // Ignore pointer capture release failures.
12851
+ }
12852
+ }
12853
+ this.activePointerId = null;
12854
+ this.isPanning = false;
12855
+ this.panMoved = false;
12856
+ this.viewport.classList.remove("is-panning");
12857
+ };
12858
+ this.onViewportClick = (event) => {
12859
+ if (performance.now() <= this.suppressClickUntil) {
12860
+ event.preventDefault();
12861
+ event.stopPropagation();
12862
+ }
12863
+ };
12864
+ this.onWheel = (event) => {
12865
+ if (!this.instance?.svg)
12866
+ return;
12867
+ event.preventDefault();
12868
+ const rect = this.viewport.getBoundingClientRect();
12869
+ const pivotX = event.clientX - rect.left;
12870
+ const pivotY = event.clientY - rect.top;
12871
+ const factor = Math.exp(-event.deltaY * 0.0015);
12872
+ this.zoomTo(this.zoom * factor, pivotX, pivotY);
12873
+ };
12789
12874
  this.options = options;
12790
12875
  this.dsl = normalizeNewlines(options.dsl);
12791
12876
  this.theme = options.theme ?? "light";
@@ -12853,6 +12938,13 @@ class SketchmarkEmbed {
12853
12938
  });
12854
12939
  this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
12855
12940
  this.btnTts.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
12941
+ this.viewport.addEventListener("pointerdown", this.onPointerDown);
12942
+ this.viewport.addEventListener("pointermove", this.onPointerMove);
12943
+ this.viewport.addEventListener("pointerup", this.onStopPanning);
12944
+ this.viewport.addEventListener("pointercancel", this.onStopPanning);
12945
+ this.viewport.addEventListener("lostpointercapture", this.onStopPanning);
12946
+ this.viewport.addEventListener("click", this.onViewportClick);
12947
+ this.viewport.addEventListener("wheel", this.onWheel, { passive: false });
12856
12948
  if (typeof ResizeObserver !== "undefined") {
12857
12949
  this.resizeObserver = new ResizeObserver(() => {
12858
12950
  this.positionViewport(false);
@@ -12904,6 +12996,7 @@ class SketchmarkEmbed {
12904
12996
  this.instance?.anim?.destroy();
12905
12997
  this.instance = null;
12906
12998
  this.autoFitEnabled = true;
12999
+ this.autoFocusEnabled = true;
12907
13000
  this.zoom = 1;
12908
13001
  this.offsetX = 0;
12909
13002
  this.offsetY = 0;
@@ -12996,6 +13089,7 @@ class SketchmarkEmbed {
12996
13089
  if (!this.instance?.svg)
12997
13090
  return;
12998
13091
  this.autoFitEnabled = true;
13092
+ this.autoFocusEnabled = true;
12999
13093
  this.positionViewport(animated);
13000
13094
  }
13001
13095
  resetView(animated = false) {
@@ -13019,6 +13113,13 @@ class SketchmarkEmbed {
13019
13113
  this.instance?.anim?.destroy();
13020
13114
  this.instance = null;
13021
13115
  this.resizeObserver?.disconnect();
13116
+ this.viewport.removeEventListener("pointerdown", this.onPointerDown);
13117
+ this.viewport.removeEventListener("pointermove", this.onPointerMove);
13118
+ this.viewport.removeEventListener("pointerup", this.onStopPanning);
13119
+ this.viewport.removeEventListener("pointercancel", this.onStopPanning);
13120
+ this.viewport.removeEventListener("lostpointercapture", this.onStopPanning);
13121
+ this.viewport.removeEventListener("click", this.onViewportClick);
13122
+ this.viewport.removeEventListener("wheel", this.onWheel);
13022
13123
  this.root.remove();
13023
13124
  }
13024
13125
  applySize(width, height) {
@@ -13078,6 +13179,7 @@ class SketchmarkEmbed {
13078
13179
  const focusTarget = this.getFocusTarget();
13079
13180
  const sceneIsLarge = scaledWidth > viewWidth || scaledHeight > viewHeight;
13080
13181
  const shouldFocus = sceneIsLarge &&
13182
+ this.autoFocusEnabled &&
13081
13183
  this.options.autoFocus !== false &&
13082
13184
  !!focusTarget;
13083
13185
  if (!shouldFocus) {
@@ -13187,6 +13289,7 @@ class SketchmarkEmbed {
13187
13289
  }
13188
13290
  this.stopMotion();
13189
13291
  this.autoFitEnabled = false;
13292
+ this.autoFocusEnabled = false;
13190
13293
  this.offsetX = pivotX - (pivotX - this.offsetX) * ratio;
13191
13294
  this.offsetY = pivotY - (pivotY - this.offsetY) * ratio;
13192
13295
  this.zoom = clampedZoom;