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