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'}`); } }); }