diff --git a/public/components/soap.html b/public/components/soap.html
index e336936..e76ea0f 100644
--- a/public/components/soap.html
+++ b/public/components/soap.html
@@ -42,6 +42,8 @@
diff --git a/public/js/soap.js b/public/js/soap.js
index 355bc71..eb69ec3 100644
--- a/public/js/soap.js
+++ b/public/js/soap.js
@@ -13,10 +13,14 @@
var soapText = document.getElementById('soap-text');
var modelTag = document.getElementById('soap-model-tag');
+ var pauseBtn = document.getElementById('soap-pause-btn');
+ var stopBtn = document.getElementById('soap-stop-btn');
+
var recorder = new AudioRecorder();
recorder._module = 'soap';
var timer = createTimer(timerEl);
var isRecording = false;
+ var isPaused = false;
var recognition = createSpeechRecognition();
var finalText = '';
var sessionFinals = '';
@@ -41,7 +45,7 @@
recognition.onend = function() {
finalText += sessionFinals;
sessionFinals = '';
- if (isRecording) try { recognition.start(); } catch(e) {}
+ if (isRecording && !isPaused) try { recognition.start(); } catch(e) {}
};
}
@@ -51,19 +55,33 @@
sessionFinals = '';
recorder.start().then(function() {
isRecording = true;
+ isPaused = false;
recordBtn.classList.add('recording');
- recordBtn.querySelector('span').textContent = 'Stop';
+ recordBtn.style.display = 'none';
+ if (pauseBtn) pauseBtn.classList.remove('hidden');
+ if (stopBtn) stopBtn.classList.remove('hidden');
indicator.classList.remove('hidden');
timer.start();
if (recognition) try { recognition.start(); } catch(e) {}
+ if (typeof nativeHaptic === 'function') nativeHaptic('heavy');
+ if (typeof nativeKeepAwake === 'function') nativeKeepAwake(true);
+ if (typeof nativeStartRecordingService === 'function') nativeStartRecordingService();
+ showToast('Recording started', 'info');
}).catch(function() { showToast('Microphone denied', 'error'); });
} else {
isRecording = false;
+ isPaused = false;
var dur = timer.stop();
recordBtn.classList.remove('recording');
+ recordBtn.style.display = '';
recordBtn.querySelector('span').textContent = 'Dictate';
+ if (pauseBtn) { pauseBtn.classList.add('hidden'); pauseBtn.innerHTML = ' Pause'; pauseBtn.classList.remove('btn-primary'); pauseBtn.classList.add('btn-ghost'); }
+ if (stopBtn) stopBtn.classList.add('hidden');
indicator.classList.add('hidden');
if (recognition) try { recognition.stop(); } catch(e) {}
+ if (typeof nativeHaptic === 'function') nativeHaptic('medium');
+ if (typeof nativeKeepAwake === 'function') nativeKeepAwake(false);
+ if (typeof nativeStopRecordingService === 'function') nativeStopRecordingService();
var liveText = (finalText + sessionFinals).trim();
if (window._transcribeAvailable === false) {
@@ -91,6 +109,49 @@
}
});
+ // Dedicated stop button
+ if (stopBtn) {
+ stopBtn.addEventListener('click', function() {
+ if (!isRecording) return;
+ recordBtn.style.display = '';
+ recordBtn.click();
+ });
+ }
+
+ // Pause / Resume
+ if (pauseBtn) {
+ pauseBtn.addEventListener('click', function() {
+ if (!isRecording) return;
+ if (!isPaused) {
+ try { if (recorder.mediaRecorder && recorder.mediaRecorder.state === 'recording') recorder.mediaRecorder.pause(); } catch(e) {}
+ timer.stop();
+ isPaused = true;
+ pauseBtn.innerHTML = ' Resume';
+ pauseBtn.classList.add('btn-primary');
+ pauseBtn.classList.remove('btn-ghost');
+ if (recognition) try { recognition.stop(); } catch(e) {}
+ showToast('Paused', 'info');
+ } else {
+ try {
+ if (recorder.mediaRecorder && recorder.mediaRecorder.state === 'paused') { recorder.mediaRecorder.resume(); }
+ else if (recorder.mediaRecorder && recorder.mediaRecorder.state === 'inactive' && recorder.stream && recorder.stream.active) {
+ var mime = MediaRecorder.isTypeSupported('audio/webm;codecs=opus') ? 'audio/webm;codecs=opus' : 'audio/webm';
+ recorder.mediaRecorder = new MediaRecorder(recorder.stream, { mimeType: mime, audioBitsPerSecond: 32000 });
+ recorder.mediaRecorder.ondataavailable = function(e) { if (e.data.size > 0) recorder.chunks.push(e.data); };
+ recorder.mediaRecorder.start(1000);
+ }
+ } catch(e) {}
+ timer.resume();
+ isPaused = false;
+ pauseBtn.innerHTML = ' Pause';
+ pauseBtn.classList.remove('btn-primary');
+ pauseBtn.classList.add('btn-ghost');
+ if (recognition) try { recognition.start(); } catch(e) {}
+ showToast('Resumed', 'info');
+ }
+ });
+ }
+
clearBtn.addEventListener('click', function() {
transcript.textContent = ''; finalText = ''; outputCard.classList.add('hidden');
var instrEl = document.getElementById('soap-instructions');