@ztimson/utils 0.28.9 → 0.28.11

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
@@ -2534,6 +2534,8 @@ ${err.message || err.toString()}`);
2534
2534
  class TTS {
2535
2535
  static QUALITY_PATTERNS = ["Google", "Microsoft", "Samantha", "Premium", "Natural", "Neural"];
2536
2536
  _currentUtterance = null;
2537
+ _voicesLoaded;
2538
+ _stoppedUtterances = /* @__PURE__ */ new WeakSet();
2537
2539
  _rate = 1;
2538
2540
  get rate() {
2539
2541
  return this._rate;
@@ -2568,6 +2570,7 @@ class TTS {
2568
2570
  }
2569
2571
  /** Create a TTS instance with optional configuration */
2570
2572
  constructor(config) {
2573
+ this._voicesLoaded = this.initializeVoices();
2571
2574
  if (config) {
2572
2575
  if (config.rate !== void 0) this._rate = config.rate;
2573
2576
  if (config.pitch !== void 0) this._pitch = config.pitch;
@@ -2575,6 +2578,23 @@ class TTS {
2575
2578
  this._voice = config.voice === null ? void 0 : config.voice || void 0;
2576
2579
  }
2577
2580
  }
2581
+ /** Initializes voice loading and sets default voice if needed */
2582
+ initializeVoices() {
2583
+ return new Promise((resolve) => {
2584
+ const voices = window.speechSynthesis.getVoices();
2585
+ if (voices.length > 0) {
2586
+ if (!this._voice) this._voice = TTS.bestVoice();
2587
+ resolve();
2588
+ } else {
2589
+ const handler = () => {
2590
+ window.speechSynthesis.removeEventListener("voiceschanged", handler);
2591
+ if (!this._voice) this._voice = TTS.bestVoice();
2592
+ resolve();
2593
+ };
2594
+ window.speechSynthesis.addEventListener("voiceschanged", handler);
2595
+ }
2596
+ });
2597
+ }
2578
2598
  /**
2579
2599
  * Selects the best available TTS voice, prioritizing high-quality options
2580
2600
  * @param lang Speaking language
@@ -2604,23 +2624,27 @@ class TTS {
2604
2624
  return utterance;
2605
2625
  }
2606
2626
  /** Speaks text and returns a Promise which resolves once complete */
2607
- speak(text) {
2627
+ async speak(text) {
2608
2628
  if (!text.trim()) return Promise.resolve();
2629
+ await this._voicesLoaded;
2609
2630
  return new Promise((resolve, reject) => {
2610
2631
  this._currentUtterance = this.createUtterance(text);
2611
- this._currentUtterance.onend = () => {
2632
+ const utterance = this._currentUtterance;
2633
+ utterance.onend = () => {
2612
2634
  this._currentUtterance = null;
2613
2635
  resolve();
2614
2636
  };
2615
- this._currentUtterance.onerror = (error) => {
2637
+ utterance.onerror = (error) => {
2616
2638
  this._currentUtterance = null;
2617
- reject(error);
2639
+ if (this._stoppedUtterances.has(utterance) && error.error === "interrupted") resolve();
2640
+ else reject(error);
2618
2641
  };
2619
- window.speechSynthesis.speak(this._currentUtterance);
2642
+ window.speechSynthesis.speak(utterance);
2620
2643
  });
2621
2644
  }
2622
2645
  /** Stops all TTS */
2623
2646
  stop() {
2647
+ if (this._currentUtterance) this._stoppedUtterances.add(this._currentUtterance);
2624
2648
  window.speechSynthesis.cancel();
2625
2649
  this._currentUtterance = null;
2626
2650
  }
@@ -2638,21 +2662,23 @@ class TTS {
2638
2662
  */
2639
2663
  speakStream() {
2640
2664
  let buffer = "";
2665
+ let streamPromise = Promise.resolve();
2641
2666
  const sentenceRegex = /[^.!?\n]+[.!?\n]+/g;
2642
2667
  return {
2643
2668
  next: (text) => {
2644
2669
  buffer += text;
2645
2670
  const sentences = buffer.match(sentenceRegex);
2646
2671
  if (sentences) {
2647
- sentences.forEach((sentence) => this.speak(sentence.trim()));
2672
+ sentences.forEach((sentence) => streamPromise = this.speak(sentence.trim()));
2648
2673
  buffer = buffer.replace(sentenceRegex, "");
2649
2674
  }
2650
2675
  },
2651
2676
  done: async () => {
2652
2677
  if (buffer.trim()) {
2653
- await this.speak(buffer.trim());
2678
+ streamPromise = this.speak(buffer.trim());
2654
2679
  buffer = "";
2655
2680
  }
2681
+ await streamPromise;
2656
2682
  }
2657
2683
  };
2658
2684
  }