(function() { var _inited = false; document.addEventListener('tabChanged', function(e) { if (e.detail.tab !== 'soap' || _inited) return; _inited = true; var recordBtn = document.getElementById('soap-record-btn'); var indicator = document.getElementById('soap-recording-indicator'); var timerEl = document.getElementById('soap-timer'); var transcript = document.getElementById('soap-transcript'); var clearBtn = document.getElementById('soap-clear'); var generateBtn = document.getElementById('soap-generate-btn'); var outputCard = document.getElementById('soap-output'); var soapText = document.getElementById('soap-text'); var modelTag = document.getElementById('soap-model-tag'); var recorder = new AudioRecorder(); var timer = createTimer(timerEl); var isRecording = false; var recognition = createSpeechRecognition(); var finalText = ''; var sessionFinals = ''; function escHtml(s) { return String(s).replace(/&/g,'&').replace(//g,'>'); } if (recognition) { recognition.onresult = function(e) { var interim = ''; for (var i = e.resultIndex; i < e.results.length; i++) { if (e.results[i].isFinal) { var chunk = e.results[i][0].transcript + ' '; var deduped = deduplicateFinal(chunk, finalText + sessionFinals); sessionFinals += deduped; } else { interim = e.results[i][0].transcript; } } var combined = finalText + sessionFinals; transcript.innerHTML = escHtml(combined) + (interim ? '' + escHtml(interim) + '' : ''); }; recognition.onend = function() { finalText += sessionFinals; sessionFinals = ''; if (isRecording) try { recognition.start(); } catch(e) {} }; } recordBtn.addEventListener('click', function() { if (!isRecording) { finalText = ''; sessionFinals = ''; recorder.start().then(function() { isRecording = true; recordBtn.classList.add('recording'); recordBtn.querySelector('span').textContent = 'Stop'; indicator.classList.remove('hidden'); timer.start(); if (recognition) try { recognition.start(); } catch(e) {} }).catch(function() { showToast('Microphone denied', 'error'); }); } else { isRecording = false; var dur = timer.stop(); recordBtn.classList.remove('recording'); recordBtn.querySelector('span').textContent = 'Dictate'; indicator.classList.add('hidden'); if (recognition) try { recognition.stop(); } catch(e) {} showLoading('Transcribing...'); recorder.stop().then(function(blob) { if (!blob) { hideLoading(); if (finalText.trim()) transcript.textContent = finalText.trim(); return; } if (blob.size > 24 * 1024 * 1024) { hideLoading(); transcript.textContent = finalText.trim(); showToast('Recording too large for AI transcription — using live transcript', 'info'); return; } return transcribeAudio(blob).then(function(data) { hideLoading(); if (data.success) transcript.textContent = data.text; else if (finalText.trim()) transcript.textContent = finalText.trim(); }); }).catch(function() { hideLoading(); if (finalText.trim()) transcript.textContent = finalText.trim(); }); } }); clearBtn.addEventListener('click', function() { transcript.textContent = ''; finalText = ''; outputCard.classList.add('hidden'); }); generateBtn.addEventListener('click', function() { var text = transcript.innerText.trim(); if (!text) { showToast('No input', 'error'); return; } showLoading('Generating SOAP note...'); var memoriesPromise = (typeof getUserMemoryContext === 'function') ? getUserMemoryContext() : Promise.resolve(''); memoriesPromise.then(function(memCtx) { return fetch('/api/generate-soap', { method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ transcript: text, patientAge: document.getElementById('soap-age').value, patientGender: document.getElementById('soap-gender').value, type: document.getElementById('soap-type').value, additionalInstructions: document.getElementById('soap-instructions').value, physicianMemories: memCtx || null, model: getSelectedModel() }) }); }) .then(function(r) { return r.json(); }) .then(function(data) { hideLoading(); if (data.success) { setOutputText(soapText, data.soap); modelTag.textContent = (data.model || '').split('/').pop(); outputCard.classList.remove('hidden'); outputCard.scrollIntoView({ behavior: 'smooth' }); showToast('SOAP note generated!', 'success'); } else showToast(data.error || 'Failed', 'error'); }) .catch(function(err) { hideLoading(); showToast(err.message, 'error'); }); }); document.getElementById('soap-refine-btn').addEventListener('click', function() { refineDocument('soap-text', 'soap-refine-input'); }); document.getElementById('soap-shorten-btn').addEventListener('click', function() { shortenDocument('soap-text'); }); console.log('✅ SOAP module loaded'); }); })();