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