@ztimson/utils 0.28.7 → 0.28.9

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
@@ -723,6 +723,10 @@ ${opts.message || this.desc}`;
723
723
  if (!str) return "";
724
724
  return wordSegments(str).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
725
725
  }
726
+ function removeEmojis(str) {
727
+ const emojiRegex = /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud83c[\udde6-\uddff]|[\ud83d[\ude00-\ude4f]|[\ud83d[\ude80-\udeff]|[\ud83c[\udd00-\uddff]|[\ud83d[\ude50-\ude7f]|[\u2600-\u26ff]|[\u2700-\u27bf]|[\ud83e[\udd00-\uddff]|[\ud83c[\udf00-\uffff]|[\ud83d[\ude00-\udeff]|[\ud83c[\udde6-\uddff])/g;
728
+ return str.replace(emojiRegex, "");
729
+ }
726
730
  function randomHex(length) {
727
731
  return Array(length).fill(null).map(() => Math.round(Math.random() * 15).toString(16)).join("");
728
732
  }
@@ -2531,6 +2535,132 @@ ${err.message || err.toString()}`);
2531
2535
  }
2532
2536
  return process(template);
2533
2537
  }
2538
+ class TTS {
2539
+ static QUALITY_PATTERNS = ["Google", "Microsoft", "Samantha", "Premium", "Natural", "Neural"];
2540
+ _currentUtterance = null;
2541
+ _rate = 1;
2542
+ get rate() {
2543
+ return this._rate;
2544
+ }
2545
+ set rate(value) {
2546
+ this._rate = value;
2547
+ if (this._currentUtterance) this._currentUtterance.rate = value;
2548
+ }
2549
+ _pitch = 1;
2550
+ get pitch() {
2551
+ return this._pitch;
2552
+ }
2553
+ set pitch(value) {
2554
+ this._pitch = value;
2555
+ if (this._currentUtterance) this._currentUtterance.pitch = value;
2556
+ }
2557
+ _volume = 1;
2558
+ get volume() {
2559
+ return this._volume;
2560
+ }
2561
+ set volume(value) {
2562
+ this._volume = value;
2563
+ if (this._currentUtterance) this._currentUtterance.volume = value;
2564
+ }
2565
+ _voice;
2566
+ get voice() {
2567
+ return this._voice;
2568
+ }
2569
+ set voice(value) {
2570
+ this._voice = value;
2571
+ if (this._currentUtterance && value) this._currentUtterance.voice = value;
2572
+ }
2573
+ /** Create a TTS instance with optional configuration */
2574
+ constructor(config) {
2575
+ if (config) {
2576
+ if (config.rate !== void 0) this._rate = config.rate;
2577
+ if (config.pitch !== void 0) this._pitch = config.pitch;
2578
+ if (config.volume !== void 0) this._volume = config.volume;
2579
+ this._voice = config.voice === null ? void 0 : config.voice || void 0;
2580
+ }
2581
+ }
2582
+ /**
2583
+ * Selects the best available TTS voice, prioritizing high-quality options
2584
+ * @param lang Speaking language
2585
+ * @returns Highest quality voice
2586
+ */
2587
+ static bestVoice(lang = "en") {
2588
+ const voices = window.speechSynthesis.getVoices();
2589
+ for (const pattern of this.QUALITY_PATTERNS) {
2590
+ const voice = voices.find((v) => v.name.includes(pattern) && v.lang.startsWith(lang));
2591
+ if (voice) return voice;
2592
+ }
2593
+ return voices.find((v) => v.lang.startsWith(lang));
2594
+ }
2595
+ /** Cleans text for TTS by removing emojis, markdown and code block */
2596
+ static cleanText(text) {
2597
+ return removeEmojis(text).replace(/```[\s\S]*?```/g, " code block ").replace(/[#*_~`]/g, "");
2598
+ }
2599
+ /** Creates a speech utterance with current options */
2600
+ createUtterance(text) {
2601
+ const cleanedText = TTS.cleanText(text);
2602
+ const utterance = new SpeechSynthesisUtterance(cleanedText);
2603
+ const voice = this._voice || TTS.bestVoice();
2604
+ if (voice) utterance.voice = voice;
2605
+ utterance.rate = this._rate;
2606
+ utterance.pitch = this._pitch;
2607
+ utterance.volume = this._volume;
2608
+ return utterance;
2609
+ }
2610
+ /** Speaks text and returns a Promise which resolves once complete */
2611
+ speak(text) {
2612
+ if (!text.trim()) return Promise.resolve();
2613
+ return new Promise((resolve, reject) => {
2614
+ this._currentUtterance = this.createUtterance(text);
2615
+ this._currentUtterance.onend = () => {
2616
+ this._currentUtterance = null;
2617
+ resolve();
2618
+ };
2619
+ this._currentUtterance.onerror = (error) => {
2620
+ this._currentUtterance = null;
2621
+ reject(error);
2622
+ };
2623
+ window.speechSynthesis.speak(this._currentUtterance);
2624
+ });
2625
+ }
2626
+ /** Stops all TTS */
2627
+ stop() {
2628
+ window.speechSynthesis.cancel();
2629
+ this._currentUtterance = null;
2630
+ }
2631
+ /**
2632
+ * Initialize a stream that chunks text into sentences and speak them.
2633
+ *
2634
+ * @example
2635
+ * const stream = tts.speakStream();
2636
+ * stream.next("Hello ");
2637
+ * stream.next("World. How");
2638
+ * stream.next(" are you?");
2639
+ * await stream.done();
2640
+ *
2641
+ * @returns Object with next function for passing chunk of streamed text and done for completing the stream
2642
+ */
2643
+ speakStream() {
2644
+ let buffer = "";
2645
+ const sentenceRegex = /[^.!?\n]+[.!?\n]+/g;
2646
+ return {
2647
+ next: (text) => {
2648
+ buffer += text;
2649
+ const sentences = buffer.match(sentenceRegex);
2650
+ if (sentences) {
2651
+ sentences.forEach((sentence) => this.speak(sentence.trim()));
2652
+ buffer = buffer.replace(sentenceRegex, "");
2653
+ }
2654
+ },
2655
+ done: async () => {
2656
+ if (buffer.trim()) {
2657
+ await this.speak(buffer.trim());
2658
+ buffer = "";
2659
+ }
2660
+ }
2661
+ };
2662
+ }
2663
+ }
2534
2664
  var dist = {};
2535
2665
  var persist = {};
2536
2666
  var hasRequiredPersist;
@@ -2753,6 +2883,7 @@ ${err.message || err.toString()}`);
2753
2883
  exports2.PromiseProgress = PromiseProgress;
2754
2884
  exports2.SYMBOL_LIST = SYMBOL_LIST;
2755
2885
  exports2.ServiceUnavailableError = ServiceUnavailableError;
2886
+ exports2.TTS = TTS;
2756
2887
  exports2.Table = Table;
2757
2888
  exports2.TemplateError = TemplateError;
2758
2889
  exports2.TooManyRequestsError = TooManyRequestsError;
@@ -2819,6 +2950,7 @@ ${err.message || err.toString()}`);
2819
2950
  exports2.randomHex = randomHex;
2820
2951
  exports2.randomString = randomString;
2821
2952
  exports2.randomStringBuilder = randomStringBuilder;
2953
+ exports2.removeEmojis = removeEmojis;
2822
2954
  exports2.renderTemplate = renderTemplate;
2823
2955
  exports2.reservedIp = reservedIp;
2824
2956
  exports2.search = search;