This commit is contained in:
retoor 2025-06-29 20:56:44 +02:00
parent e96cb5bdaa
commit 88bb78fb23

View File

@ -1,22 +1,19 @@
class STTButton extends HTMLElement { class STTButton extends HTMLElement {
/** monitor target attribute so it can change on-the-fly */
static get observedAttributes() { return ['target']; } static get observedAttributes() { return ['target']; }
simulateTypingWithEvents(element, text, delay = 100) { simulateTypingWithEvents(element, text, delay = 100) {
let resolver = null; let resolver = null;
let promise = new Promise((resolve) => { const promise = new Promise((resolve) => resolver = resolve);
resolver = resolve;
});
let index = 0; let index = 0;
function triggerEvent(type, key) { const triggerEvent = (type, key) => {
const event = new KeyboardEvent(type, { const event = new KeyboardEvent(type, {
key: key, key: key,
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
}); });
element.dispatchEvent(event); element.dispatchEvent(event);
} };
const interval = setInterval(() => { const interval = setInterval(() => {
if (index < text.length) { if (index < text.length) {
@ -32,7 +29,6 @@ class STTButton extends HTMLElement {
triggerEvent('keypress', char); triggerEvent('keypress', char);
triggerEvent('keyup', char); triggerEvent('keyup', char);
index++; index++;
} else { } else {
clearInterval(interval); clearInterval(interval);
@ -58,8 +54,8 @@ class STTButton extends HTMLElement {
button { all:unset; cursor:pointer; } button { all:unset; cursor:pointer; }
button:hover { background:#e8e8e8; } button:hover { background:#e8e8e8; }
:host([listening]) button { :host([listening]) button {
background:#d32f2f; color:#fff; background:#d32f2f; color:#fff;
animation:pulse 1.2s ease-in-out infinite; } animation:pulse 1.2s ease-in-out infinite; }
@keyframes pulse { @keyframes pulse {
0%,100% { transform:scale(1); box-shadow:0 0 0 0 rgba(211,47,47,.6);} 0%,100% { transform:scale(1); box-shadow:0 0 0 0 rgba(211,47,47,.6);}
50% { transform:scale(1.12);box-shadow:0 0 0 12px rgba(211,47,47,0);} 50% { transform:scale(1.12);box-shadow:0 0 0 12px rgba(211,47,47,0);}
@ -80,12 +76,14 @@ class STTButton extends HTMLElement {
let interim = ''; let interim = '';
let committed = ''; let committed = '';
let previousInterim = '';
this.recog.onresult = (e) => { this.recog.onresult = (e) => {
interim = ''; interim = '';
for (let i = e.resultIndex; i < e.results.length; i++) { for (let i = e.resultIndex; i < e.results.length; i++) {
const res = e.results[i]; const res = e.results[i];
const txt = res[0].transcript.trim(); const alt = res[0];
const txt = alt.transcript.trim();
if (res.isFinal) { if (res.isFinal) {
const sentence = txt.charAt(0).toUpperCase() + txt.slice(1); const sentence = txt.charAt(0).toUpperCase() + txt.slice(1);
@ -94,26 +92,46 @@ class STTButton extends HTMLElement {
committed += punctuated + ' '; committed += punctuated + ' ';
if (this.targetEl) { if (this.targetEl) {
this.targetEl.focus(); this.targetEl.focus();
punctuated = punctuated.replace(/\./g, ".\n"); punctuated = punctuated.replace(/\./g, ".\n")
punctuated = punctuated.replace(/\?/g, "?\n"); .replace(/\?/g, "?\n")
punctuated = punctuated.replace(/\!/g, "!\n"); .replace(/\!/g, "!\n");
this.simulateTypingWithEvents(this.targetEl, punctuated, 1).then(() => { this.targetEl.value = ''; // punctuated;
this.simulateTypingWithEvents(this.targetEl, punctuated, 1).then(() => {
const chatInput = document.querySelector('chat-input'); const chatInput = document.querySelector('chat-input');
chatInput.finalizeMessage(); chatInput.finalizeMessage();
}); });
} }
previousInterim = '';
} else { } else {
interim += txt + ' '; if (alt.confidence >= 0.85) {
interim += txt + ' ';
}
} }
} }
if (interim && this.targetEl) {
const el = this.targetEl;
el.focus();
if (el.isContentEditable) {
el.innerText = el.innerText.replace(new RegExp(previousInterim + '$'), interim);
} else {
el.value = interim
//el.value = el.value.replace(new RegExp(previousInterim + '$'), interim);
}
/*
this.simulateTypingWithEvents(el, interim, 0).then(() => {
previousInterim = interim;
});*/
}
}; };
this.recog.onend = () => { this.recog.onend = () => {
// Auto-restart recognition unless we explicitly stopped
if (this.listening) { if (this.listening) {
try { try {
this.recog.start(); // attempt restart this.recog.start();
} catch (e) { } catch (e) {
console.warn('Failed to restart speech recognition:', e); console.warn('Failed to restart speech recognition:', e);
} }