import {
createMicrophoneRecorder,
isSpeechToTextConfigured,
transcribeAudioBySettings,
} from '../services/speech-tools-service.js';
import { state } from '../state.js';
function formatDuration(ms) {
const totalSec = Math.max(0, Math.floor(Number(ms || 0) / 1000));
const mm = String(Math.floor(totalSec / 60)).padStart(2, '0');
const ss = String(totalSec % 60).padStart(2, '0');
return `${mm}:${ss}`;
}
function showSttMissingConfigDialog(navigate) {
const goSettings = window.confirm(
'Распознавание речи не настроено. Перейти в настройки инструментов?'
);
if (goSettings) navigate('tools-settings-view');
}
export async function openSpeechInputModal({ navigate, onTextReady, onSendText, onSendQueued }) {
if (!isSpeechToTextConfigured(state.entrySettings)) {
showSttMissingConfigDialog(navigate);
return;
}
const root = document.getElementById('modal-root');
const host = document.createElement('div');
host.innerHTML = `
`;
root.append(host);
const statusEl = host.querySelector('#speech-input-status');
const timeEl = host.querySelector('#speech-input-time');
const levelEl = host.querySelector('#speech-level-fill');
const errorEl = host.querySelector('#speech-input-error');
const cancelBtn = host.querySelector('#speech-cancel');
const sendNowBtn = host.querySelector('#speech-send-now');
const okBtn = host.querySelector('#speech-ok');
const recorder = createMicrophoneRecorder();
let closed = false;
let busy = false;
const close = () => {
if (closed) return;
closed = true;
host.remove();
};
const setBusy = (flag) => {
busy = !!flag;
cancelBtn.disabled = busy;
sendNowBtn.disabled = busy;
okBtn.disabled = busy;
okBtn.textContent = busy ? 'Распознаю...' : 'OK';
sendNowBtn.textContent = busy ? 'Распознаю...' : 'Распознать и сразу отправить сообщение';
};
try {
await recorder.start(({ elapsedMs, level }) => {
if (timeEl) timeEl.textContent = formatDuration(elapsedMs);
if (levelEl) levelEl.style.width = `${Math.max(2, Math.round((Number(level) || 0) * 100))}%`;
});
} catch (error) {
close();
window.alert(`Не удалось получить доступ к микрофону: ${error?.message || 'unknown'}`);
return;
}
cancelBtn.addEventListener('click', () => {
recorder.cancel();
close();
});
okBtn.addEventListener('click', async () => {
if (busy) return;
setBusy(true);
try {
const audioBlob = await recorder.stop();
host.innerHTML = `
`;
const text = await transcribeAudioBySettings(audioBlob, state.entrySettings);
if (typeof onTextReady === 'function') onTextReady(text);
close();
} catch (error) {
setBusy(false);
statusEl.textContent = 'Идёт запись...';
errorEl.textContent = `Ошибка распознавания: ${error?.message || 'unknown'}`;
}
});
sendNowBtn.addEventListener('click', async () => {
if (busy) return;
setBusy(true);
try {
const audioBlob = await recorder.stop();
close();
if (typeof onSendQueued === 'function') onSendQueued();
const text = await transcribeAudioBySettings(audioBlob, state.entrySettings);
if (typeof onSendText === 'function') {
await onSendText(text);
} else if (typeof onTextReady === 'function') {
onTextReady(text);
}
} catch (error) {
setBusy(false);
close();
window.alert(`Ошибка распознавания: ${error?.message || 'unknown'}`);
}
});
}