sketchmark 1.1.4 → 1.1.5

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/dist/index.js CHANGED
@@ -9441,8 +9441,17 @@ class AnimationController {
9441
9441
  }
9442
9442
  /** Enable/disable browser text-to-speech for narrate steps */
9443
9443
  get tts() { return this._tts; }
9444
- set tts(on) { this._tts = on; if (!on)
9445
- this._cancelSpeech(); }
9444
+ set tts(on) {
9445
+ const next = !!on;
9446
+ const changed = next !== this._tts;
9447
+ this._tts = next;
9448
+ if (!next) {
9449
+ this._cancelSpeech();
9450
+ return;
9451
+ }
9452
+ if (changed)
9453
+ this._warmUpSpeech();
9454
+ }
9446
9455
  get currentStep() {
9447
9456
  return this._step;
9448
9457
  }
@@ -10721,7 +10730,7 @@ class EventEmitter {
10721
10730
  }
10722
10731
 
10723
10732
  function render(options) {
10724
- const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
10733
+ const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
10725
10734
  if (injectCSS && !document.getElementById("ai-diagram-css")) {
10726
10735
  const style = document.createElement("style");
10727
10736
  style.id = "ai-diagram-css";
@@ -10770,6 +10779,9 @@ function render(options) {
10770
10779
  const containerEl = el instanceof SVGSVGElement ? undefined : el;
10771
10780
  anim = new AnimationController(svg, ast.steps, containerEl, rc, ast.config);
10772
10781
  }
10782
+ if (typeof tts === "boolean") {
10783
+ anim.tts = tts;
10784
+ }
10773
10785
  onReady?.(anim, svg);
10774
10786
  return {
10775
10787
  scene,
@@ -10821,13 +10833,14 @@ function toError(error) {
10821
10833
  const CANVAS_STYLE_ID = "sketchmark-canvas-ui";
10822
10834
  const CANVAS_CSS = `
10823
10835
  .skm-canvas{display:flex;flex-direction:column;width:100%;height:100%;min-height:320px;overflow:hidden;border:1px solid #caba98;border-radius:10px;background:#f8f4ea;color:#3a2010;font-family:"Courier New",monospace}
10824
- .skm-canvas__animbar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#eee7d8;border-bottom:1px solid #caba98;flex-shrink:0}
10836
+ .skm-canvas__animbar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#eee7d8;border-bottom:1px solid #caba98;flex-shrink:0;flex-wrap:wrap}
10825
10837
  .skm-canvas__status{min-width:96px;text-align:center;color:#6a4820;font-size:11px}
10826
10838
  .skm-canvas__label{color:#8a6040;font-size:11px;font-style:italic}
10827
10839
  .skm-canvas__spacer{flex:1}
10828
10840
  .skm-canvas__stats{color:#9a7848;font-size:10px}
10829
10841
  .skm-canvas__button{border:1px solid #caba98;background:#f5eedd;color:#3a2010;border-radius:6px;padding:4px 9px;font:inherit;font-size:11px;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease}
10830
10842
  .skm-canvas__button:hover:not(:disabled){background:#c8a060;border-color:#c8a060;color:#fff}
10843
+ .skm-canvas__button.is-active{background:#c8a060;border-color:#c8a060;color:#fff}
10831
10844
  .skm-canvas__button:disabled{opacity:.45;cursor:default}
10832
10845
  .skm-canvas__error{display:none;padding:8px 12px;background:#280a0a;border-bottom:1px solid #5a1818;color:#f07070;font-size:11px;line-height:1.4;white-space:pre-wrap;flex-shrink:0}
10833
10846
  .skm-canvas__error.is-visible{display:block}
@@ -10849,6 +10862,8 @@ class SketchmarkCanvas {
10849
10862
  this.instance = null;
10850
10863
  this.emitter = new EventEmitter();
10851
10864
  this.dsl = "";
10865
+ this.showCaption = true;
10866
+ this.ttsOverride = null;
10852
10867
  this.panX = 60;
10853
10868
  this.panY = 60;
10854
10869
  this.zoom = 1;
@@ -10936,6 +10951,8 @@ class SketchmarkCanvas {
10936
10951
  this.options = options;
10937
10952
  this.renderer = options.renderer ?? "svg";
10938
10953
  this.theme = options.theme ?? "light";
10954
+ this.showCaption = options.showCaption !== false;
10955
+ this.ttsOverride = typeof options.tts === "boolean" ? options.tts : null;
10939
10956
  this.dsl = normalizeNewlines(options.dsl ?? "");
10940
10957
  injectStyleOnce(CANVAS_STYLE_ID, CANVAS_CSS);
10941
10958
  const host = resolveContainer(options.container);
@@ -10954,6 +10971,8 @@ class SketchmarkCanvas {
10954
10971
  <span class="skm-canvas__status">No steps</span>
10955
10972
  <button type="button" class="skm-canvas__button" data-action="next">Next</button>
10956
10973
  <button type="button" class="skm-canvas__button" data-action="play">Play</button>
10974
+ <button type="button" class="skm-canvas__button" data-action="toggle-caption">Caption On</button>
10975
+ <button type="button" class="skm-canvas__button" data-action="toggle-tts">TTS Off</button>
10957
10976
  <span class="skm-canvas__label"></span>
10958
10977
  <span class="skm-canvas__spacer"></span>
10959
10978
  <span class="skm-canvas__stats"></span>
@@ -10988,6 +11007,8 @@ class SketchmarkCanvas {
10988
11007
  this.prevButton = this.root.querySelector('[data-action="prev"]');
10989
11008
  this.nextButton = this.root.querySelector('[data-action="next"]');
10990
11009
  this.resetButton = this.root.querySelector('[data-action="reset"]');
11010
+ this.captionButton = this.root.querySelector('[data-action="toggle-caption"]');
11011
+ this.ttsButton = this.root.querySelector('[data-action="toggle-tts"]');
10991
11012
  this.gridPattern = this.root.querySelector(`#${patternId}`);
10992
11013
  this.gridDot = this.gridPattern.querySelector("circle");
10993
11014
  this.root.querySelector('[data-action="fit"]')?.addEventListener("click", () => this.fitContent());
@@ -10998,6 +11019,8 @@ class SketchmarkCanvas {
10998
11019
  this.prevButton.addEventListener("click", () => this.prevStep());
10999
11020
  this.nextButton.addEventListener("click", () => this.nextStep());
11000
11021
  this.playButton.addEventListener("click", () => void this.play());
11022
+ this.captionButton.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
11023
+ this.ttsButton.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
11001
11024
  this.viewport.addEventListener("pointerdown", this.onPointerDown);
11002
11025
  this.viewport.addEventListener("pointermove", this.onPointerMove);
11003
11026
  this.viewport.addEventListener("pointerup", this.onStopPanning);
@@ -11019,6 +11042,16 @@ class SketchmarkCanvas {
11019
11042
  if (renderNow)
11020
11043
  this.render();
11021
11044
  }
11045
+ setCaptionVisible(visible) {
11046
+ this.showCaption = visible;
11047
+ this.applyCaptionVisibility(this.instance);
11048
+ this.syncToggleUi();
11049
+ }
11050
+ setTtsEnabled(enabled) {
11051
+ this.ttsOverride = enabled;
11052
+ this.applyTtsSetting(this.instance);
11053
+ this.syncToggleUi();
11054
+ }
11022
11055
  bindEditor(editor, options = {}) {
11023
11056
  this.editorCleanup?.();
11024
11057
  const renderOnRun = options.renderOnRun !== false;
@@ -11063,6 +11096,8 @@ class SketchmarkCanvas {
11063
11096
  onNodeClick: this.options.onNodeClick,
11064
11097
  });
11065
11098
  this.instance = instance;
11099
+ this.applyCaptionVisibility(instance);
11100
+ this.applyTtsSetting(instance);
11066
11101
  this.statsLabel.textContent = `${instance.scene.nodes.length}n / ${instance.scene.edges.length}e / ${instance.scene.groups.length}g`;
11067
11102
  if (this.renderer === "svg") {
11068
11103
  this.animUnsub = instance.anim.on((event) => {
@@ -11195,6 +11230,37 @@ class SketchmarkCanvas {
11195
11230
  this.zoom = clampedZoom;
11196
11231
  this.applyTransform();
11197
11232
  }
11233
+ applyCaptionVisibility(instance) {
11234
+ const caption = instance?.anim.captionElement;
11235
+ if (!caption)
11236
+ return;
11237
+ caption.style.display = this.showCaption ? "" : "none";
11238
+ caption.setAttribute("aria-hidden", this.showCaption ? "false" : "true");
11239
+ }
11240
+ applyTtsSetting(instance) {
11241
+ if (!instance || this.ttsOverride === null)
11242
+ return;
11243
+ instance.anim.tts = this.ttsOverride;
11244
+ }
11245
+ getTtsEnabled() {
11246
+ if (this.ttsOverride !== null)
11247
+ return this.ttsOverride;
11248
+ return !!this.instance?.anim.tts;
11249
+ }
11250
+ syncToggleUi() {
11251
+ const canToggleCaption = this.renderer === "svg" && !!this.instance;
11252
+ const canToggleTts = canToggleCaption &&
11253
+ typeof speechSynthesis !== "undefined";
11254
+ const ttsEnabled = this.getTtsEnabled();
11255
+ this.captionButton.textContent = this.showCaption ? "Caption On" : "Caption Off";
11256
+ this.captionButton.classList.toggle("is-active", this.showCaption);
11257
+ this.captionButton.setAttribute("aria-pressed", this.showCaption ? "true" : "false");
11258
+ this.captionButton.disabled = !canToggleCaption;
11259
+ this.ttsButton.textContent = ttsEnabled ? "TTS On" : "TTS Off";
11260
+ this.ttsButton.classList.toggle("is-active", ttsEnabled);
11261
+ this.ttsButton.setAttribute("aria-pressed", ttsEnabled ? "true" : "false");
11262
+ this.ttsButton.disabled = !canToggleTts;
11263
+ }
11198
11264
  syncAnimationUi() {
11199
11265
  const anim = this.instance?.anim;
11200
11266
  const canAnimate = this.renderer === "svg" && !!anim && anim.total > 0;
@@ -11205,6 +11271,7 @@ class SketchmarkCanvas {
11205
11271
  this.nextButton.disabled = true;
11206
11272
  this.resetButton.disabled = true;
11207
11273
  this.playButton.disabled = true;
11274
+ this.syncToggleUi();
11208
11275
  return;
11209
11276
  }
11210
11277
  this.stepDisplay.textContent = anim.currentStep < 0 ? `${anim.total} steps` : `${anim.currentStep + 1} / ${anim.total}`;
@@ -11213,6 +11280,7 @@ class SketchmarkCanvas {
11213
11280
  this.nextButton.disabled = !anim.canNext;
11214
11281
  this.resetButton.disabled = false;
11215
11282
  this.playButton.disabled = this.playInFlight || !anim.canNext;
11283
+ this.syncToggleUi();
11216
11284
  }
11217
11285
  getStepTarget(stepItem) {
11218
11286
  if (!stepItem)
@@ -11912,6 +11980,12 @@ const EMBED_CSS = `
11912
11980
  color: #fff;
11913
11981
  }
11914
11982
 
11983
+ .skm-embed__button.is-active {
11984
+ background: #c8a060;
11985
+ border-color: #c8a060;
11986
+ color: #fff;
11987
+ }
11988
+
11915
11989
  .skm-embed--dark .skm-embed__button {
11916
11990
  border-color: #4a3520;
11917
11991
  background: #22190e;
@@ -11958,6 +12032,8 @@ class SketchmarkEmbed {
11958
12032
  this.emitter = new EventEmitter();
11959
12033
  this.animUnsub = null;
11960
12034
  this.playInFlight = false;
12035
+ this.showCaption = true;
12036
+ this.ttsOverride = null;
11961
12037
  this.zoom = 1;
11962
12038
  this.offsetX = 0;
11963
12039
  this.offsetY = 0;
@@ -11967,6 +12043,8 @@ class SketchmarkEmbed {
11967
12043
  this.options = options;
11968
12044
  this.dsl = normalizeNewlines(options.dsl);
11969
12045
  this.theme = options.theme ?? "light";
12046
+ this.showCaption = options.showCaption !== false;
12047
+ this.ttsOverride = typeof options.tts === "boolean" ? options.tts : null;
11970
12048
  injectStyleOnce(EMBED_STYLE_ID, EMBED_CSS);
11971
12049
  const host = resolveContainer(options.container);
11972
12050
  host.innerHTML = "";
@@ -11994,6 +12072,10 @@ class SketchmarkEmbed {
11994
12072
  <button type="button" class="skm-embed__button" data-action="next">Next</button>
11995
12073
  <button type="button" class="skm-embed__button" data-action="play">Play</button>
11996
12074
  </div>
12075
+ <div class="skm-embed__controls-group">
12076
+ <button type="button" class="skm-embed__button" data-action="toggle-caption">Caption On</button>
12077
+ <button type="button" class="skm-embed__button" data-action="toggle-tts">TTS Off</button>
12078
+ </div>
11997
12079
  <span class="skm-embed__step">No steps</span>
11998
12080
  </div>
11999
12081
  `;
@@ -12011,6 +12093,8 @@ class SketchmarkEmbed {
12011
12093
  this.btnPrev = this.root.querySelector('[data-action="prev"]');
12012
12094
  this.btnNext = this.root.querySelector('[data-action="next"]');
12013
12095
  this.btnPlay = this.root.querySelector('[data-action="play"]');
12096
+ this.btnCaption = this.root.querySelector('[data-action="toggle-caption"]');
12097
+ this.btnTts = this.root.querySelector('[data-action="toggle-tts"]');
12014
12098
  this.controlsElement.classList.toggle("is-hidden", options.showControls === false);
12015
12099
  this.btnFit.addEventListener("click", () => this.resetView());
12016
12100
  this.btnZoomIn.addEventListener("click", () => this.zoomIn());
@@ -12021,6 +12105,8 @@ class SketchmarkEmbed {
12021
12105
  this.btnPlay.addEventListener("click", () => {
12022
12106
  void this.play();
12023
12107
  });
12108
+ this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
12109
+ this.btnTts.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
12024
12110
  if (typeof ResizeObserver !== "undefined") {
12025
12111
  this.resizeObserver = new ResizeObserver(() => {
12026
12112
  this.positionViewport(false);
@@ -12038,6 +12124,16 @@ class SketchmarkEmbed {
12038
12124
  if (renderNow)
12039
12125
  this.render();
12040
12126
  }
12127
+ setCaptionVisible(visible) {
12128
+ this.showCaption = visible;
12129
+ this.applyCaptionVisibility(this.instance);
12130
+ this.syncToggleControls();
12131
+ }
12132
+ setTtsEnabled(enabled) {
12133
+ this.ttsOverride = enabled;
12134
+ this.applyTtsSetting(this.instance);
12135
+ this.syncToggleControls();
12136
+ }
12041
12137
  setSize(width, height) {
12042
12138
  this.applySize(width, height);
12043
12139
  this.positionViewport(false);
@@ -12082,6 +12178,8 @@ class SketchmarkEmbed {
12082
12178
  onNodeClick: this.options.onNodeClick,
12083
12179
  });
12084
12180
  this.instance = instance;
12181
+ this.applyCaptionVisibility(instance);
12182
+ this.applyTtsSetting(instance);
12085
12183
  this.animUnsub = instance.anim.on((event) => {
12086
12184
  this.syncControls();
12087
12185
  if (event.type === "step-change") {
@@ -12186,6 +12284,7 @@ class SketchmarkEmbed {
12186
12284
  syncControls() {
12187
12285
  this.syncAnimationControls();
12188
12286
  this.syncViewControls();
12287
+ this.syncToggleControls();
12189
12288
  }
12190
12289
  syncAnimationControls() {
12191
12290
  const anim = this.instance?.anim;
@@ -12347,6 +12446,37 @@ class SketchmarkEmbed {
12347
12446
  this.applyTransform();
12348
12447
  this.syncViewControls();
12349
12448
  }
12449
+ applyCaptionVisibility(instance) {
12450
+ const caption = instance?.anim.captionElement;
12451
+ if (!caption)
12452
+ return;
12453
+ caption.style.display = this.showCaption ? "" : "none";
12454
+ caption.setAttribute("aria-hidden", this.showCaption ? "false" : "true");
12455
+ }
12456
+ applyTtsSetting(instance) {
12457
+ if (!instance || this.ttsOverride === null)
12458
+ return;
12459
+ instance.anim.tts = this.ttsOverride;
12460
+ }
12461
+ getTtsEnabled() {
12462
+ if (this.ttsOverride !== null)
12463
+ return this.ttsOverride;
12464
+ return !!this.instance?.anim.tts;
12465
+ }
12466
+ syncToggleControls() {
12467
+ const hasView = !!this.instance?.svg;
12468
+ const canToggleTts = hasView &&
12469
+ typeof speechSynthesis !== "undefined";
12470
+ const ttsEnabled = this.getTtsEnabled();
12471
+ this.btnCaption.textContent = this.showCaption ? "Caption On" : "Caption Off";
12472
+ this.btnCaption.classList.toggle("is-active", this.showCaption);
12473
+ this.btnCaption.setAttribute("aria-pressed", this.showCaption ? "true" : "false");
12474
+ this.btnCaption.disabled = !hasView;
12475
+ this.btnTts.textContent = ttsEnabled ? "TTS On" : "TTS Off";
12476
+ this.btnTts.classList.toggle("is-active", ttsEnabled);
12477
+ this.btnTts.setAttribute("aria-pressed", ttsEnabled ? "true" : "false");
12478
+ this.btnTts.disabled = !canToggleTts;
12479
+ }
12350
12480
  getTargetBox(target, viewportRect) {
12351
12481
  if (target instanceof SVGGraphicsElement) {
12352
12482
  try {