@ztimson/utils 0.28.10 → 0.28.12

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/dist/index.mjs CHANGED
@@ -2533,9 +2533,10 @@ ${err.message || err.toString()}`);
2533
2533
  }
2534
2534
  class TTS {
2535
2535
  static QUALITY_PATTERNS = ["Google", "Microsoft", "Samantha", "Premium", "Natural", "Neural"];
2536
+ static _errorHandlerInstalled = false;
2536
2537
  _currentUtterance = null;
2537
2538
  _voicesLoaded;
2538
- _isStopping = false;
2539
+ _stoppedUtterances = /* @__PURE__ */ new WeakSet();
2539
2540
  _rate = 1;
2540
2541
  get rate() {
2541
2542
  return this._rate;
@@ -2568,8 +2569,8 @@ class TTS {
2568
2569
  this._voice = value;
2569
2570
  if (this._currentUtterance && value) this._currentUtterance.voice = value;
2570
2571
  }
2571
- /** Create a TTS instance with optional configuration */
2572
2572
  constructor(config) {
2573
+ TTS.installErrorHandler();
2573
2574
  this._voicesLoaded = this.initializeVoices();
2574
2575
  if (config) {
2575
2576
  if (config.rate !== void 0) this._rate = config.rate;
@@ -2578,7 +2579,13 @@ class TTS {
2578
2579
  this._voice = config.voice === null ? void 0 : config.voice || void 0;
2579
2580
  }
2580
2581
  }
2581
- /** Initializes voice loading and sets default voice if needed */
2582
+ static installErrorHandler() {
2583
+ if (this._errorHandlerInstalled) return;
2584
+ window.addEventListener("unhandledrejection", (event) => {
2585
+ if (event.reason?.error === "interrupted" && event.reason instanceof SpeechSynthesisErrorEvent) event.preventDefault();
2586
+ });
2587
+ this._errorHandlerInstalled = true;
2588
+ }
2582
2589
  initializeVoices() {
2583
2590
  return new Promise((resolve) => {
2584
2591
  const voices = window.speechSynthesis.getVoices();
@@ -2595,11 +2602,6 @@ class TTS {
2595
2602
  }
2596
2603
  });
2597
2604
  }
2598
- /**
2599
- * Selects the best available TTS voice, prioritizing high-quality options
2600
- * @param lang Speaking language
2601
- * @returns Highest quality voice
2602
- */
2603
2605
  static bestVoice(lang = "en") {
2604
2606
  const voices = window.speechSynthesis.getVoices();
2605
2607
  for (const pattern of this.QUALITY_PATTERNS) {
@@ -2608,11 +2610,9 @@ class TTS {
2608
2610
  }
2609
2611
  return voices.find((v) => v.lang.startsWith(lang));
2610
2612
  }
2611
- /** Cleans text for TTS by removing emojis, markdown and code block */
2612
2613
  static cleanText(text) {
2613
2614
  return removeEmojis(text).replace(/```[\s\S]*?```/g, " code block ").replace(/[#*_~`]/g, "");
2614
2615
  }
2615
- /** Creates a speech utterance with current options */
2616
2616
  createUtterance(text) {
2617
2617
  const cleanedText = TTS.cleanText(text);
2618
2618
  const utterance = new SpeechSynthesisUtterance(cleanedText);
@@ -2623,45 +2623,29 @@ class TTS {
2623
2623
  utterance.volume = this._volume;
2624
2624
  return utterance;
2625
2625
  }
2626
- /** Speaks text and returns a Promise which resolves once complete */
2627
2626
  async speak(text) {
2628
2627
  if (!text.trim()) return Promise.resolve();
2629
2628
  await this._voicesLoaded;
2630
2629
  return new Promise((resolve, reject) => {
2631
2630
  this._currentUtterance = this.createUtterance(text);
2632
- this._currentUtterance.onend = () => {
2631
+ const utterance = this._currentUtterance;
2632
+ utterance.onend = () => {
2633
2633
  this._currentUtterance = null;
2634
2634
  resolve();
2635
2635
  };
2636
- this._currentUtterance.onerror = (error) => {
2636
+ utterance.onerror = (error) => {
2637
2637
  this._currentUtterance = null;
2638
- if (this._isStopping && error.error === "interrupted") resolve();
2638
+ if (this._stoppedUtterances.has(utterance) && error.error === "interrupted") resolve();
2639
2639
  else reject(error);
2640
2640
  };
2641
- window.speechSynthesis.speak(this._currentUtterance);
2641
+ window.speechSynthesis.speak(utterance);
2642
2642
  });
2643
2643
  }
2644
- /** Stops all TTS */
2645
2644
  stop() {
2646
- this._isStopping = true;
2645
+ if (this._currentUtterance) this._stoppedUtterances.add(this._currentUtterance);
2647
2646
  window.speechSynthesis.cancel();
2648
2647
  this._currentUtterance = null;
2649
- setTimeout(() => {
2650
- this._isStopping = false;
2651
- }, 0);
2652
2648
  }
2653
- /**
2654
- * Initialize a stream that chunks text into sentences and speak them.
2655
- *
2656
- * @example
2657
- * const stream = tts.speakStream();
2658
- * stream.next("Hello ");
2659
- * stream.next("World. How");
2660
- * stream.next(" are you?");
2661
- * await stream.done();
2662
- *
2663
- * @returns Object with next function for passing chunk of streamed text and done for completing the stream
2664
- */
2665
2649
  speakStream() {
2666
2650
  let buffer = "";
2667
2651
  let streamPromise = Promise.resolve();