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.
package/README.md CHANGED
@@ -84,9 +84,11 @@ end
84
84
  `.trim(),
85
85
  });
86
86
 
87
- const canvas = new SketchmarkCanvas({
88
- container: document.getElementById('viewport'),
89
- });
87
+ const canvas = new SketchmarkCanvas({
88
+ container: document.getElementById('viewport'),
89
+ showCaption: false,
90
+ tts: true,
91
+ });
90
92
 
91
93
  canvas.bindEditor(editor);
92
94
  ```
@@ -100,13 +102,15 @@ const embed = new SketchmarkEmbed({
100
102
  width: 960,
101
103
  height: 540,
102
104
  playStepDelay: 700,
105
+ showCaption: false,
106
+ tts: true,
103
107
  fitPadding: 24,
104
108
  zoomMin: 0.08,
105
109
  zoomMax: 4,
106
110
  });
107
111
  ```
108
112
 
109
- 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 `+`, `-`, and reset-to-fit controls alongside step playback.
113
+ 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.
110
114
 
111
115
  ---
112
116
 
@@ -661,9 +665,11 @@ step narrate "Plants need sunlight to make food"
661
665
  step narrate "This is the most important step" pace=slow
662
666
  ```
663
667
 
664
- - Caption is rendered as a fixed-position `<div>` on `document.body` (independent of diagram pan/zoom).
665
- - Access via `anim.captionElement` to reparent it anywhere.
666
- - Supports built-in browser text-to-speech (see [TTS](#text-to-speech)).
668
+ - Caption is rendered as a fixed-position `<div>` on `document.body` (independent of diagram pan/zoom).
669
+ - Access via `anim.captionElement` to reparent it anywhere.
670
+ - `SketchmarkCanvas` and `SketchmarkEmbed` support `showCaption: false` to hide the caption bar UI.
671
+ - Both widget UIs also include a built-in caption toggle button by default.
672
+ - Supports built-in browser text-to-speech (see [TTS](#text-to-speech)).
667
673
 
668
674
  ### Annotations
669
675
 
@@ -799,10 +805,14 @@ anim.destroy() // remove caption, annotations, pointer from DOM
799
805
 
800
806
  // Toggle TTS programmatically
801
807
  anim.tts = true; // enable browser speech
802
- anim.tts = false; // disable (stops current speech)
803
-
804
- // Reparent the narration caption to a custom container
805
- myDiv.appendChild(anim.captionElement);
808
+ anim.tts = false; // disable (stops current speech)
809
+
810
+ // Reparent the narration caption to a custom container
811
+ myDiv.appendChild(anim.captionElement);
812
+
813
+ // UI widgets can suppress the visible caption bar
814
+ const embed = new SketchmarkEmbed({ container, dsl, showCaption: false });
815
+ embed.setCaptionVisible(true);
806
816
 
807
817
  // Event listener
808
818
  const unsub = anim.on((event) => {
@@ -814,22 +824,31 @@ const unsub = anim.on((event) => {
814
824
  unsub(); // unsubscribe
815
825
  ```
816
826
 
817
- ### Text-to-Speech
818
-
819
- Enable browser-native speech synthesis for narrate steps:
820
-
821
- ```
822
- # In DSL
823
- config tts=on
824
- ```
825
-
826
- ```javascript
827
- // Or via API
828
- anim.tts = true;
829
-
830
- // Custom TTS (e.g. ElevenLabs) via event listener
831
- anim.tts = false; // disable built-in
832
- anim.on((e) => {
827
+ ### Text-to-Speech
828
+
829
+ Enable browser-native speech synthesis for narrate steps. You can drive it from the diagram config or from the JS API. If both are provided, the JS option wins. `SketchmarkCanvas` and `SketchmarkEmbed` also include a built-in TTS toggle button by default.
830
+
831
+ ```
832
+ # In DSL
833
+ config tts=on
834
+ ```
835
+
836
+ ```javascript
837
+ // Direct render option
838
+ const instance = render({ container, dsl, tts: true });
839
+
840
+ // Widget options
841
+ const canvas = new SketchmarkCanvas({ container, dsl, tts: true });
842
+ const embed = new SketchmarkEmbed({ container, dsl, tts: true });
843
+
844
+ // Or toggle at runtime
845
+ anim.tts = true;
846
+ canvas.setTtsEnabled(false);
847
+ embed.setTtsEnabled(true);
848
+
849
+ // Custom TTS (e.g. ElevenLabs) via event listener
850
+ anim.tts = false; // disable built-in
851
+ anim.on((e) => {
833
852
  if (e.step?.kind === 'step' && e.step.action === 'narrate') {
834
853
  myTTSService.speak(e.step.value);
835
854
  }
@@ -1211,14 +1230,15 @@ end
1211
1230
  ```typescript
1212
1231
  render(options: RenderOptions): DiagramInstance
1213
1232
 
1214
- interface RenderOptions {
1215
- container: string | HTMLElement | SVGSVGElement; // CSS selector or element
1216
- dsl: string; // DSL source text
1217
- renderer?: 'svg' | 'canvas'; // default: 'svg'
1218
- injectCSS?: boolean; // inject animation CSS (default: true)
1219
- svgOptions?: SVGRendererOptions;
1220
- canvasOptions?: CanvasRendererOptions;
1221
- onNodeClick?: (nodeId: string) => void; // click handler
1233
+ interface RenderOptions {
1234
+ container: string | HTMLElement | SVGSVGElement; // CSS selector or element
1235
+ dsl: string; // DSL source text
1236
+ renderer?: 'svg' | 'canvas'; // default: 'svg'
1237
+ injectCSS?: boolean; // inject animation CSS (default: true)
1238
+ tts?: boolean; // override diagram TTS config
1239
+ svgOptions?: SVGRendererOptions;
1240
+ canvasOptions?: CanvasRendererOptions;
1241
+ onNodeClick?: (nodeId: string) => void; // click handler
1222
1242
  onReady?: (anim, svg?) => void; // callback after render
1223
1243
  }
1224
1244
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/animation/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/animation/index.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -9443,8 +9443,17 @@ class AnimationController {
9443
9443
  }
9444
9444
  /** Enable/disable browser text-to-speech for narrate steps */
9445
9445
  get tts() { return this._tts; }
9446
- set tts(on) { this._tts = on; if (!on)
9447
- this._cancelSpeech(); }
9446
+ set tts(on) {
9447
+ const next = !!on;
9448
+ const changed = next !== this._tts;
9449
+ this._tts = next;
9450
+ if (!next) {
9451
+ this._cancelSpeech();
9452
+ return;
9453
+ }
9454
+ if (changed)
9455
+ this._warmUpSpeech();
9456
+ }
9448
9457
  get currentStep() {
9449
9458
  return this._step;
9450
9459
  }
@@ -10723,7 +10732,7 @@ class EventEmitter {
10723
10732
  }
10724
10733
 
10725
10734
  function render(options) {
10726
- const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
10735
+ const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
10727
10736
  if (injectCSS && !document.getElementById("ai-diagram-css")) {
10728
10737
  const style = document.createElement("style");
10729
10738
  style.id = "ai-diagram-css";
@@ -10772,6 +10781,9 @@ function render(options) {
10772
10781
  const containerEl = el instanceof SVGSVGElement ? undefined : el;
10773
10782
  anim = new AnimationController(svg, ast.steps, containerEl, rc, ast.config);
10774
10783
  }
10784
+ if (typeof tts === "boolean") {
10785
+ anim.tts = tts;
10786
+ }
10775
10787
  onReady?.(anim, svg);
10776
10788
  return {
10777
10789
  scene,
@@ -10823,13 +10835,14 @@ function toError(error) {
10823
10835
  const CANVAS_STYLE_ID = "sketchmark-canvas-ui";
10824
10836
  const CANVAS_CSS = `
10825
10837
  .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}
10826
- .skm-canvas__animbar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#eee7d8;border-bottom:1px solid #caba98;flex-shrink:0}
10838
+ .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}
10827
10839
  .skm-canvas__status{min-width:96px;text-align:center;color:#6a4820;font-size:11px}
10828
10840
  .skm-canvas__label{color:#8a6040;font-size:11px;font-style:italic}
10829
10841
  .skm-canvas__spacer{flex:1}
10830
10842
  .skm-canvas__stats{color:#9a7848;font-size:10px}
10831
10843
  .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}
10832
10844
  .skm-canvas__button:hover:not(:disabled){background:#c8a060;border-color:#c8a060;color:#fff}
10845
+ .skm-canvas__button.is-active{background:#c8a060;border-color:#c8a060;color:#fff}
10833
10846
  .skm-canvas__button:disabled{opacity:.45;cursor:default}
10834
10847
  .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}
10835
10848
  .skm-canvas__error.is-visible{display:block}
@@ -10851,6 +10864,8 @@ class SketchmarkCanvas {
10851
10864
  this.instance = null;
10852
10865
  this.emitter = new EventEmitter();
10853
10866
  this.dsl = "";
10867
+ this.showCaption = true;
10868
+ this.ttsOverride = null;
10854
10869
  this.panX = 60;
10855
10870
  this.panY = 60;
10856
10871
  this.zoom = 1;
@@ -10938,6 +10953,8 @@ class SketchmarkCanvas {
10938
10953
  this.options = options;
10939
10954
  this.renderer = options.renderer ?? "svg";
10940
10955
  this.theme = options.theme ?? "light";
10956
+ this.showCaption = options.showCaption !== false;
10957
+ this.ttsOverride = typeof options.tts === "boolean" ? options.tts : null;
10941
10958
  this.dsl = normalizeNewlines(options.dsl ?? "");
10942
10959
  injectStyleOnce(CANVAS_STYLE_ID, CANVAS_CSS);
10943
10960
  const host = resolveContainer(options.container);
@@ -10956,6 +10973,8 @@ class SketchmarkCanvas {
10956
10973
  <span class="skm-canvas__status">No steps</span>
10957
10974
  <button type="button" class="skm-canvas__button" data-action="next">Next</button>
10958
10975
  <button type="button" class="skm-canvas__button" data-action="play">Play</button>
10976
+ <button type="button" class="skm-canvas__button" data-action="toggle-caption">Caption On</button>
10977
+ <button type="button" class="skm-canvas__button" data-action="toggle-tts">TTS Off</button>
10959
10978
  <span class="skm-canvas__label"></span>
10960
10979
  <span class="skm-canvas__spacer"></span>
10961
10980
  <span class="skm-canvas__stats"></span>
@@ -10990,6 +11009,8 @@ class SketchmarkCanvas {
10990
11009
  this.prevButton = this.root.querySelector('[data-action="prev"]');
10991
11010
  this.nextButton = this.root.querySelector('[data-action="next"]');
10992
11011
  this.resetButton = this.root.querySelector('[data-action="reset"]');
11012
+ this.captionButton = this.root.querySelector('[data-action="toggle-caption"]');
11013
+ this.ttsButton = this.root.querySelector('[data-action="toggle-tts"]');
10993
11014
  this.gridPattern = this.root.querySelector(`#${patternId}`);
10994
11015
  this.gridDot = this.gridPattern.querySelector("circle");
10995
11016
  this.root.querySelector('[data-action="fit"]')?.addEventListener("click", () => this.fitContent());
@@ -11000,6 +11021,8 @@ class SketchmarkCanvas {
11000
11021
  this.prevButton.addEventListener("click", () => this.prevStep());
11001
11022
  this.nextButton.addEventListener("click", () => this.nextStep());
11002
11023
  this.playButton.addEventListener("click", () => void this.play());
11024
+ this.captionButton.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
11025
+ this.ttsButton.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
11003
11026
  this.viewport.addEventListener("pointerdown", this.onPointerDown);
11004
11027
  this.viewport.addEventListener("pointermove", this.onPointerMove);
11005
11028
  this.viewport.addEventListener("pointerup", this.onStopPanning);
@@ -11021,6 +11044,16 @@ class SketchmarkCanvas {
11021
11044
  if (renderNow)
11022
11045
  this.render();
11023
11046
  }
11047
+ setCaptionVisible(visible) {
11048
+ this.showCaption = visible;
11049
+ this.applyCaptionVisibility(this.instance);
11050
+ this.syncToggleUi();
11051
+ }
11052
+ setTtsEnabled(enabled) {
11053
+ this.ttsOverride = enabled;
11054
+ this.applyTtsSetting(this.instance);
11055
+ this.syncToggleUi();
11056
+ }
11024
11057
  bindEditor(editor, options = {}) {
11025
11058
  this.editorCleanup?.();
11026
11059
  const renderOnRun = options.renderOnRun !== false;
@@ -11065,6 +11098,8 @@ class SketchmarkCanvas {
11065
11098
  onNodeClick: this.options.onNodeClick,
11066
11099
  });
11067
11100
  this.instance = instance;
11101
+ this.applyCaptionVisibility(instance);
11102
+ this.applyTtsSetting(instance);
11068
11103
  this.statsLabel.textContent = `${instance.scene.nodes.length}n / ${instance.scene.edges.length}e / ${instance.scene.groups.length}g`;
11069
11104
  if (this.renderer === "svg") {
11070
11105
  this.animUnsub = instance.anim.on((event) => {
@@ -11197,6 +11232,37 @@ class SketchmarkCanvas {
11197
11232
  this.zoom = clampedZoom;
11198
11233
  this.applyTransform();
11199
11234
  }
11235
+ applyCaptionVisibility(instance) {
11236
+ const caption = instance?.anim.captionElement;
11237
+ if (!caption)
11238
+ return;
11239
+ caption.style.display = this.showCaption ? "" : "none";
11240
+ caption.setAttribute("aria-hidden", this.showCaption ? "false" : "true");
11241
+ }
11242
+ applyTtsSetting(instance) {
11243
+ if (!instance || this.ttsOverride === null)
11244
+ return;
11245
+ instance.anim.tts = this.ttsOverride;
11246
+ }
11247
+ getTtsEnabled() {
11248
+ if (this.ttsOverride !== null)
11249
+ return this.ttsOverride;
11250
+ return !!this.instance?.anim.tts;
11251
+ }
11252
+ syncToggleUi() {
11253
+ const canToggleCaption = this.renderer === "svg" && !!this.instance;
11254
+ const canToggleTts = canToggleCaption &&
11255
+ typeof speechSynthesis !== "undefined";
11256
+ const ttsEnabled = this.getTtsEnabled();
11257
+ this.captionButton.textContent = this.showCaption ? "Caption On" : "Caption Off";
11258
+ this.captionButton.classList.toggle("is-active", this.showCaption);
11259
+ this.captionButton.setAttribute("aria-pressed", this.showCaption ? "true" : "false");
11260
+ this.captionButton.disabled = !canToggleCaption;
11261
+ this.ttsButton.textContent = ttsEnabled ? "TTS On" : "TTS Off";
11262
+ this.ttsButton.classList.toggle("is-active", ttsEnabled);
11263
+ this.ttsButton.setAttribute("aria-pressed", ttsEnabled ? "true" : "false");
11264
+ this.ttsButton.disabled = !canToggleTts;
11265
+ }
11200
11266
  syncAnimationUi() {
11201
11267
  const anim = this.instance?.anim;
11202
11268
  const canAnimate = this.renderer === "svg" && !!anim && anim.total > 0;
@@ -11207,6 +11273,7 @@ class SketchmarkCanvas {
11207
11273
  this.nextButton.disabled = true;
11208
11274
  this.resetButton.disabled = true;
11209
11275
  this.playButton.disabled = true;
11276
+ this.syncToggleUi();
11210
11277
  return;
11211
11278
  }
11212
11279
  this.stepDisplay.textContent = anim.currentStep < 0 ? `${anim.total} steps` : `${anim.currentStep + 1} / ${anim.total}`;
@@ -11215,6 +11282,7 @@ class SketchmarkCanvas {
11215
11282
  this.nextButton.disabled = !anim.canNext;
11216
11283
  this.resetButton.disabled = false;
11217
11284
  this.playButton.disabled = this.playInFlight || !anim.canNext;
11285
+ this.syncToggleUi();
11218
11286
  }
11219
11287
  getStepTarget(stepItem) {
11220
11288
  if (!stepItem)
@@ -11914,6 +11982,12 @@ const EMBED_CSS = `
11914
11982
  color: #fff;
11915
11983
  }
11916
11984
 
11985
+ .skm-embed__button.is-active {
11986
+ background: #c8a060;
11987
+ border-color: #c8a060;
11988
+ color: #fff;
11989
+ }
11990
+
11917
11991
  .skm-embed--dark .skm-embed__button {
11918
11992
  border-color: #4a3520;
11919
11993
  background: #22190e;
@@ -11960,6 +12034,8 @@ class SketchmarkEmbed {
11960
12034
  this.emitter = new EventEmitter();
11961
12035
  this.animUnsub = null;
11962
12036
  this.playInFlight = false;
12037
+ this.showCaption = true;
12038
+ this.ttsOverride = null;
11963
12039
  this.zoom = 1;
11964
12040
  this.offsetX = 0;
11965
12041
  this.offsetY = 0;
@@ -11969,6 +12045,8 @@ class SketchmarkEmbed {
11969
12045
  this.options = options;
11970
12046
  this.dsl = normalizeNewlines(options.dsl);
11971
12047
  this.theme = options.theme ?? "light";
12048
+ this.showCaption = options.showCaption !== false;
12049
+ this.ttsOverride = typeof options.tts === "boolean" ? options.tts : null;
11972
12050
  injectStyleOnce(EMBED_STYLE_ID, EMBED_CSS);
11973
12051
  const host = resolveContainer(options.container);
11974
12052
  host.innerHTML = "";
@@ -11996,6 +12074,10 @@ class SketchmarkEmbed {
11996
12074
  <button type="button" class="skm-embed__button" data-action="next">Next</button>
11997
12075
  <button type="button" class="skm-embed__button" data-action="play">Play</button>
11998
12076
  </div>
12077
+ <div class="skm-embed__controls-group">
12078
+ <button type="button" class="skm-embed__button" data-action="toggle-caption">Caption On</button>
12079
+ <button type="button" class="skm-embed__button" data-action="toggle-tts">TTS Off</button>
12080
+ </div>
11999
12081
  <span class="skm-embed__step">No steps</span>
12000
12082
  </div>
12001
12083
  `;
@@ -12013,6 +12095,8 @@ class SketchmarkEmbed {
12013
12095
  this.btnPrev = this.root.querySelector('[data-action="prev"]');
12014
12096
  this.btnNext = this.root.querySelector('[data-action="next"]');
12015
12097
  this.btnPlay = this.root.querySelector('[data-action="play"]');
12098
+ this.btnCaption = this.root.querySelector('[data-action="toggle-caption"]');
12099
+ this.btnTts = this.root.querySelector('[data-action="toggle-tts"]');
12016
12100
  this.controlsElement.classList.toggle("is-hidden", options.showControls === false);
12017
12101
  this.btnFit.addEventListener("click", () => this.resetView());
12018
12102
  this.btnZoomIn.addEventListener("click", () => this.zoomIn());
@@ -12023,6 +12107,8 @@ class SketchmarkEmbed {
12023
12107
  this.btnPlay.addEventListener("click", () => {
12024
12108
  void this.play();
12025
12109
  });
12110
+ this.btnCaption.addEventListener("click", () => this.setCaptionVisible(!this.showCaption));
12111
+ this.btnTts.addEventListener("click", () => this.setTtsEnabled(!this.getTtsEnabled()));
12026
12112
  if (typeof ResizeObserver !== "undefined") {
12027
12113
  this.resizeObserver = new ResizeObserver(() => {
12028
12114
  this.positionViewport(false);
@@ -12040,6 +12126,16 @@ class SketchmarkEmbed {
12040
12126
  if (renderNow)
12041
12127
  this.render();
12042
12128
  }
12129
+ setCaptionVisible(visible) {
12130
+ this.showCaption = visible;
12131
+ this.applyCaptionVisibility(this.instance);
12132
+ this.syncToggleControls();
12133
+ }
12134
+ setTtsEnabled(enabled) {
12135
+ this.ttsOverride = enabled;
12136
+ this.applyTtsSetting(this.instance);
12137
+ this.syncToggleControls();
12138
+ }
12043
12139
  setSize(width, height) {
12044
12140
  this.applySize(width, height);
12045
12141
  this.positionViewport(false);
@@ -12084,6 +12180,8 @@ class SketchmarkEmbed {
12084
12180
  onNodeClick: this.options.onNodeClick,
12085
12181
  });
12086
12182
  this.instance = instance;
12183
+ this.applyCaptionVisibility(instance);
12184
+ this.applyTtsSetting(instance);
12087
12185
  this.animUnsub = instance.anim.on((event) => {
12088
12186
  this.syncControls();
12089
12187
  if (event.type === "step-change") {
@@ -12188,6 +12286,7 @@ class SketchmarkEmbed {
12188
12286
  syncControls() {
12189
12287
  this.syncAnimationControls();
12190
12288
  this.syncViewControls();
12289
+ this.syncToggleControls();
12191
12290
  }
12192
12291
  syncAnimationControls() {
12193
12292
  const anim = this.instance?.anim;
@@ -12349,6 +12448,37 @@ class SketchmarkEmbed {
12349
12448
  this.applyTransform();
12350
12449
  this.syncViewControls();
12351
12450
  }
12451
+ applyCaptionVisibility(instance) {
12452
+ const caption = instance?.anim.captionElement;
12453
+ if (!caption)
12454
+ return;
12455
+ caption.style.display = this.showCaption ? "" : "none";
12456
+ caption.setAttribute("aria-hidden", this.showCaption ? "false" : "true");
12457
+ }
12458
+ applyTtsSetting(instance) {
12459
+ if (!instance || this.ttsOverride === null)
12460
+ return;
12461
+ instance.anim.tts = this.ttsOverride;
12462
+ }
12463
+ getTtsEnabled() {
12464
+ if (this.ttsOverride !== null)
12465
+ return this.ttsOverride;
12466
+ return !!this.instance?.anim.tts;
12467
+ }
12468
+ syncToggleControls() {
12469
+ const hasView = !!this.instance?.svg;
12470
+ const canToggleTts = hasView &&
12471
+ typeof speechSynthesis !== "undefined";
12472
+ const ttsEnabled = this.getTtsEnabled();
12473
+ this.btnCaption.textContent = this.showCaption ? "Caption On" : "Caption Off";
12474
+ this.btnCaption.classList.toggle("is-active", this.showCaption);
12475
+ this.btnCaption.setAttribute("aria-pressed", this.showCaption ? "true" : "false");
12476
+ this.btnCaption.disabled = !hasView;
12477
+ this.btnTts.textContent = ttsEnabled ? "TTS On" : "TTS Off";
12478
+ this.btnTts.classList.toggle("is-active", ttsEnabled);
12479
+ this.btnTts.setAttribute("aria-pressed", ttsEnabled ? "true" : "false");
12480
+ this.btnTts.disabled = !canToggleTts;
12481
+ }
12352
12482
  getTargetBox(target, viewportRect) {
12353
12483
  if (target instanceof SVGGraphicsElement) {
12354
12484
  try {