pediatric-ai-scribe-v3/public/js/transcriptionSettings.js
ifedan-ed ca14094c0a Add Web Speech Recognition option for real-time streaming
Provides two transcription options:

1. Browser Whisper (Offline, Batch) - RECOMMENDED
   - 100% offline, zero network calls
   - HIPAA-compliant, audio never leaves device
   - Highest accuracy (Whisper)
   - Processes after recording (batch mode)
   - Models self-hosted, bundled in v2

2. Web Speech API (Real-time, Streaming) - EXPERIMENTAL
   - Real-time transcription (see words as you speak)
   - Uses browser's built-in speech recognition
   - ⚠️ Sends audio to cloud (Chrome/Edge → Google)
   - ⚠️ NOT HIPAA-compliant
   - Requires user consent with clear warnings

Features:
- Settings UI for both options
- Clear privacy warnings for Web Speech
- Mutual exclusion (only one active at a time)
- Browser detection shows which provider is used
- Confirmation dialog before enabling Web Speech

Use Cases:
- Clinical/HIPAA: Use Browser Whisper only
- Personal/Non-clinical: Can use Web Speech for real-time feedback
- Maximum privacy: Browser Whisper (offline)
- Maximum speed: Web Speech (if privacy not required)

Implementation:
- speechRecognition.js: Web Speech API wrapper
- transcriptionSettings.js: Settings UI handler
- Privacy info displayed per browser

User can choose based on their privacy vs. speed preference.
2026-03-31 21:57:24 +00:00

166 lines
6.5 KiB
JavaScript

// ============================================================
// TRANSCRIPTION SETTINGS — Browser Whisper + Web Speech API
// ============================================================
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'settings' || _inited) return;
_inited = true;
initTranscriptionSettings();
});
// Also init on direct page load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
if (!_inited && document.getElementById('browser-whisper-enabled')) {
_inited = true;
initTranscriptionSettings();
}
}, 500);
});
} else {
setTimeout(function() {
if (!_inited && document.getElementById('browser-whisper-enabled')) {
_inited = true;
initTranscriptionSettings();
}
}, 500);
}
function initTranscriptionSettings() {
console.log('[TranscriptionSettings] Initializing...');
// ── Browser Whisper ──────────────────────────────────────
var whisperCheckbox = document.getElementById('browser-whisper-enabled');
var whisperStatus = document.getElementById('browser-whisper-status');
var whisperModel = document.getElementById('browser-whisper-model');
var whisperPreload = document.getElementById('btn-whisper-preload');
var whisperProgress = document.getElementById('browser-whisper-progress');
var whisperProgressText = document.getElementById('browser-whisper-progress-text');
if (whisperCheckbox && window.BrowserWhisper) {
whisperCheckbox.checked = BrowserWhisper.isEnabled();
whisperStatus.textContent = BrowserWhisper.isEnabled() ? 'On — audio stays on device' : 'Off';
if (whisperModel) {
whisperModel.value = BrowserWhisper.getModel();
}
whisperCheckbox.addEventListener('change', function() {
BrowserWhisper.setEnabled(whisperCheckbox.checked);
whisperStatus.textContent = whisperCheckbox.checked ? 'On — audio stays on device' : 'Off';
if (whisperCheckbox.checked) {
// Auto-preload on enable
BrowserWhisper.preload(function(file, pct) {
if (whisperProgress && whisperProgressText) {
if (pct >= 100) {
whisperProgress.style.display = 'none';
return;
}
whisperProgress.style.display = 'block';
whisperProgressText.textContent = file + (pct > 0 ? ' ' + pct + '%' : '');
}
});
}
});
if (whisperModel) {
whisperModel.addEventListener('change', function() {
BrowserWhisper.setModel(whisperModel.value);
});
}
if (whisperPreload) {
whisperPreload.addEventListener('click', function() {
if (!BrowserWhisper || !BrowserWhisper.isSupported()) {
showToast('Browser Whisper not supported', 'error');
return;
}
whisperPreload.disabled = true;
whisperPreload.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Downloading...';
whisperProgress.style.display = 'block';
whisperProgressText.textContent = 'Initializing...';
BrowserWhisper.setEnabled(true);
whisperCheckbox.checked = true;
whisperStatus.textContent = 'On — audio stays on device';
BrowserWhisper.preload(function(file, pct) {
if (pct >= 100) {
whisperProgress.style.display = 'none';
whisperPreload.disabled = false;
whisperPreload.innerHTML = '<i class="fas fa-download"></i> Pre-download model';
showToast('Whisper model ready!', 'success');
return;
}
whisperProgress.style.display = 'block';
whisperProgressText.textContent = file + (pct > 0 ? ' ' + pct + '%' : '');
});
});
}
}
// ── Web Speech Recognition ───────────────────────────────
var speechCheckbox = document.getElementById('web-speech-enabled');
var speechStatus = document.getElementById('web-speech-status');
var speechBrowserInfo = document.getElementById('web-speech-browser-info');
if (speechCheckbox && window.WebSpeechRecognition) {
speechCheckbox.checked = WebSpeechRecognition.isEnabled();
speechStatus.textContent = WebSpeechRecognition.isEnabled() ? 'On — real-time streaming' : 'Off';
// Show privacy info
if (speechBrowserInfo) {
var privacyInfo = WebSpeechRecognition.getPrivacyInfo();
speechBrowserInfo.innerHTML =
'<strong>Provider:</strong> ' + privacyInfo.provider + '<br>' +
'<strong>Privacy:</strong> ' + privacyInfo.privacy + '<br>' +
'<strong style="color:var(--orange);">⚠️ ' + privacyInfo.warning + '</strong>';
}
speechCheckbox.addEventListener('change', function() {
if (speechCheckbox.checked) {
// Show confirmation for privacy warning
if (!confirm(
'WARNING: Real-time streaming uses your browser\'s speech recognition, ' +
'which may send audio to cloud servers (e.g., Google).\n\n' +
'This is NOT HIPAA-compliant.\n\n' +
'Only enable if you understand and accept this privacy trade-off.\n\n' +
'Continue?'
)) {
speechCheckbox.checked = false;
return;
}
// Disable Browser Whisper if enabling Web Speech
if (whisperCheckbox && whisperCheckbox.checked) {
whisperCheckbox.checked = false;
BrowserWhisper.setEnabled(false);
whisperStatus.textContent = 'Off';
showToast('Browser Whisper disabled (Web Speech takes priority)', 'info');
}
}
WebSpeechRecognition.setEnabled(speechCheckbox.checked);
speechStatus.textContent = speechCheckbox.checked ? 'On — real-time streaming' : 'Off';
});
// If not supported, disable and show message
if (!WebSpeechRecognition.isSupported()) {
speechCheckbox.disabled = true;
speechStatus.textContent = 'Not supported in this browser';
if (speechBrowserInfo) {
speechBrowserInfo.innerHTML = '<strong style="color:var(--red);">Not supported</strong> - Web Speech API not available in this browser.';
}
}
}
console.log('✅ Transcription settings initialized');
}
})();