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.

@@ -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
- // Make text visible but clipped to zero width
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
- // We need to wait for text to be visible before we can measure it
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
- const bbox = textEl.getBBox?.();
8997
- if (!bbox || bbox.width === 0) {
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
- <button type="button" class="skm-embed__button" data-action="reset">Reset</button>
11947
- <button type="button" class="skm-embed__button" data-action="prev">Prev</button>
11948
- <button type="button" class="skm-embed__button" data-action="next">Next</button>
11949
- <button type="button" class="skm-embed__button" data-action="play">Play</button>
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.btnReset = this.root.querySelector('[data-action="reset"]');
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.btnReset.addEventListener("click", () => this.resetAnimation());
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.btnReset.disabled = true;
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.btnReset.disabled = false;
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
- if (!this.instance?.svg)
12131
- return;
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
- const sceneIsLarge = svgWidth > viewWidth || svgHeight > viewHeight;
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
- !!this.getFocusTarget();
12239
+ !!focusTarget;
12146
12240
  if (!shouldFocus) {
12147
- this.animateTo(svgWidth <= viewWidth ? (viewWidth - svgWidth) / 2 : 0, svgHeight <= viewHeight ? (viewHeight - svgHeight) / 2 : 0, animated);
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(this.getFocusTarget());
12244
+ const target = this.findTargetElement(focusTarget);
12151
12245
  if (!target) {
12152
12246
  this.animateTo(0, 0, animated);
12153
12247
  return;
12154
12248
  }
12155
- const currentRect = target.getBoundingClientRect();
12156
- const sceneX = currentRect.left - viewportRect.left - this.offsetX;
12157
- const sceneY = currentRect.top - viewportRect.top - this.offsetY;
12158
- const targetCenterX = sceneX + currentRect.width / 2;
12159
- const targetCenterY = sceneY + currentRect.height / 2;
12160
- let nextX = viewWidth / 2 - targetCenterX;
12161
- let nextY = viewHeight / 2 - targetCenterY;
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 (svgWidth <= viewWidth) {
12164
- nextX = (viewWidth - svgWidth) / 2;
12257
+ if (scaledWidth <= viewWidth) {
12258
+ nextX = (viewWidth - scaledWidth) / 2;
12165
12259
  }
12166
12260
  else {
12167
- nextX = clamp(nextX, viewWidth - svgWidth - padding, padding);
12261
+ nextX = clamp(nextX, viewWidth - scaledWidth - padding, padding);
12168
12262
  }
12169
- if (svgHeight <= viewHeight) {
12170
- nextY = (viewHeight - svgHeight) / 2;
12263
+ if (scaledHeight <= viewHeight) {
12264
+ nextY = (viewHeight - scaledHeight) / 2;
12171
12265
  }
12172
12266
  else {
12173
- nextY = clamp(nextY, viewHeight - svgHeight - padding, padding);
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;
@@ -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 btnReset;
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;
@@ -1 +1 @@
1
- {"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../../src/ui/embed.ts"],"names":[],"mappings":"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"}
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;AA2I1D,KAAK,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AACnC,KAAK,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,eAAe,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CACxE;AAED,MAAM,WAAW,qBAAsB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE,MAAM,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,eAAe,CAAA;KAAE,CAAC;IAC9D,KAAK,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,eAAe,CAAA;KAAE,CAAC;IAChD,UAAU,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,eAAe,CAAA;KAAE,CAAC;CAC/E;AAED,qBAAa,eAAe;IAC1B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IACtC,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAQ;IAExC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IACrE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAA+B;gBAEzC,OAAO,EAAE,sBAAsB;IA6E3C,MAAM,IAAI,MAAM;IAIhB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,IAAI;IAK5C,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAKpD,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAMjC,EAAE,CAAC,CAAC,SAAS,MAAM,qBAAqB,EACtC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC,CAAC,KAAK,IAAI,GACpD,MAAM,IAAI;IAKb,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAkE1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,QAAQ,IAAI,IAAI;IAOhB,QAAQ,IAAI,IAAI;IAOhB,cAAc,IAAI,IAAI;IAOtB,aAAa,CAAC,QAAQ,UAAQ,GAAG,IAAI;IAMrC,SAAS,CAAC,QAAQ,UAAQ,GAAG,IAAI;IAIjC,MAAM,IAAI,IAAI;IAId,OAAO,IAAI,IAAI;IAIf,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,OAAO,IAAI,IAAI;IASf,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,qBAAqB;IAmB7B,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,gBAAgB;IAiExB,OAAO,CAAC,SAAS;IA+BjB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,UAAU;CAKnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sketchmark",
3
- "version": "1.1.3",
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
+ }