@ztimson/utils 0.28.8 → 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.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;
|