@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 +132 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +132 -0
- package/dist/index.mjs.map +1 -1
- package/dist/string.d.ts +6 -0
- package/dist/tts.d.ts +53 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
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,
|