@luckydraw/cumulus 0.30.24 → 0.30.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/dist/gateway/static/widget.js +65 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.30.25
|
|
4
|
+
- Restored tap-to-interject on the voice mode mic indicator
|
|
5
|
+
|
|
6
|
+
|
|
3
7
|
## v0.30.23
|
|
4
|
-
- Voice replies now finish playing fully before the session ends
|
|
5
8
|
|
|
9
|
+
- Voice replies now finish playing fully before the session ends
|
|
6
10
|
|
|
7
11
|
## v0.30.22
|
|
8
12
|
|
|
@@ -1282,7 +1282,11 @@
|
|
|
1282
1282
|
' display: flex; align-items: center; justify-content: center;',
|
|
1283
1283
|
' transition: all 0.3s ease;',
|
|
1284
1284
|
' margin-bottom: 24px;',
|
|
1285
|
+
' cursor: pointer;',
|
|
1286
|
+
' user-select: none;',
|
|
1287
|
+
' -webkit-tap-highlight-color: transparent;',
|
|
1285
1288
|
'}',
|
|
1289
|
+
'.cumulus-voice-indicator:active { transform: scale(0.95); }',
|
|
1286
1290
|
'.cumulus-voice-overlay.listening .cumulus-voice-indicator {',
|
|
1287
1291
|
' background: rgba(0, 102, 204, 0.2);',
|
|
1288
1292
|
' border: 2px solid #0066cc;',
|
|
@@ -4374,6 +4378,7 @@
|
|
|
4374
4378
|
audioQueue: [], // queue of Float32Array PCM chunks
|
|
4375
4379
|
audioPlaying: false, // currently playing audio
|
|
4376
4380
|
audioDone: false, // server signaled all audio sent
|
|
4381
|
+
micRestartTimer: null, // pending mic restart, cancellable when PCM resumes
|
|
4377
4382
|
// Voice loading state for Safari compatibility
|
|
4378
4383
|
voicesLoaded: false,
|
|
4379
4384
|
speakQueue: [],
|
|
@@ -4398,6 +4403,24 @@
|
|
|
4398
4403
|
indicator.appendChild(indicatorIcon);
|
|
4399
4404
|
overlay.appendChild(indicator);
|
|
4400
4405
|
|
|
4406
|
+
// Tap-to-interject: clicking the indicator stops TTS and starts listening
|
|
4407
|
+
// immediately, regardless of phase. Cancels any pending mic-restart timer
|
|
4408
|
+
// so the manual action wins over the scheduled auto-restart.
|
|
4409
|
+
indicator.addEventListener('click', function () {
|
|
4410
|
+
if (!state.active) return;
|
|
4411
|
+
if (state.micRestartTimer) {
|
|
4412
|
+
clearTimeout(state.micRestartTimer);
|
|
4413
|
+
state.micRestartTimer = null;
|
|
4414
|
+
}
|
|
4415
|
+
if (state.phase === 'speaking') {
|
|
4416
|
+
stopSpeaking();
|
|
4417
|
+
}
|
|
4418
|
+
if (state.phase !== 'listening') {
|
|
4419
|
+
setPhase('listening');
|
|
4420
|
+
startRecognition();
|
|
4421
|
+
}
|
|
4422
|
+
});
|
|
4423
|
+
|
|
4401
4424
|
var transcriptEl = document.createElement('div');
|
|
4402
4425
|
transcriptEl.className = 'cumulus-voice-transcript';
|
|
4403
4426
|
overlay.appendChild(transcriptEl);
|
|
@@ -4501,9 +4524,7 @@
|
|
|
4501
4524
|
} else {
|
|
4502
4525
|
// No speech detected — restart listening
|
|
4503
4526
|
setPhase('idle');
|
|
4504
|
-
|
|
4505
|
-
if (state.active) startRecognition();
|
|
4506
|
-
}, 300);
|
|
4527
|
+
scheduleMicRestart(300);
|
|
4507
4528
|
}
|
|
4508
4529
|
};
|
|
4509
4530
|
|
|
@@ -4513,15 +4534,11 @@
|
|
|
4513
4534
|
// Normal — just restart
|
|
4514
4535
|
if (state.active) {
|
|
4515
4536
|
setPhase('idle');
|
|
4516
|
-
|
|
4517
|
-
if (state.active) startRecognition();
|
|
4518
|
-
}, 500);
|
|
4537
|
+
scheduleMicRestart(500);
|
|
4519
4538
|
}
|
|
4520
4539
|
} else {
|
|
4521
4540
|
label.textContent = 'Mic error: ' + event.error;
|
|
4522
|
-
|
|
4523
|
-
if (state.active) startRecognition();
|
|
4524
|
-
}, 1000);
|
|
4541
|
+
scheduleMicRestart(1000);
|
|
4525
4542
|
}
|
|
4526
4543
|
};
|
|
4527
4544
|
|
|
@@ -4700,12 +4717,18 @@
|
|
|
4700
4717
|
state.speaking = false;
|
|
4701
4718
|
if (state.active) {
|
|
4702
4719
|
setPhase('idle');
|
|
4703
|
-
|
|
4704
|
-
if (state.active) startRecognition();
|
|
4705
|
-
}, 300);
|
|
4720
|
+
scheduleMicRestart(300);
|
|
4706
4721
|
}
|
|
4707
4722
|
}
|
|
4708
4723
|
|
|
4724
|
+
function scheduleMicRestart(delay) {
|
|
4725
|
+
if (state.micRestartTimer) clearTimeout(state.micRestartTimer);
|
|
4726
|
+
state.micRestartTimer = setTimeout(function () {
|
|
4727
|
+
state.micRestartTimer = null;
|
|
4728
|
+
if (state.active && state.phase !== 'speaking') startRecognition();
|
|
4729
|
+
}, delay);
|
|
4730
|
+
}
|
|
4731
|
+
|
|
4709
4732
|
// ── Server-side TTS (Web Audio API playback) ──
|
|
4710
4733
|
function ensureAudioCtx() {
|
|
4711
4734
|
if (!state.audioCtx) {
|
|
@@ -4721,7 +4744,31 @@
|
|
|
4721
4744
|
}
|
|
4722
4745
|
|
|
4723
4746
|
function queuePCMAudio(arrayBuffer) {
|
|
4724
|
-
if (!state.active
|
|
4747
|
+
if (!state.active) return;
|
|
4748
|
+
|
|
4749
|
+
// Server is sending audio — it always wins over the mic. If the widget
|
|
4750
|
+
// transitioned to listening (or back to idle) prematurely, recover by
|
|
4751
|
+
// killing recognition and resuming the speaking phase. Without this,
|
|
4752
|
+
// any subsequent PCM is silently dropped and the mic gets stuck in an
|
|
4753
|
+
// idle/listen flicker loop.
|
|
4754
|
+
if (state.phase !== 'speaking') {
|
|
4755
|
+
console.log('[Voice] PCM arrived in phase=' + state.phase + ', recovering to speaking');
|
|
4756
|
+
if (state.recognition) {
|
|
4757
|
+
try {
|
|
4758
|
+
state.recognition.abort();
|
|
4759
|
+
} catch (e) {}
|
|
4760
|
+
}
|
|
4761
|
+
clearWatchdog();
|
|
4762
|
+
// Cancel any pending mic-restart timer so it can't override us
|
|
4763
|
+
if (state.micRestartTimer) {
|
|
4764
|
+
clearTimeout(state.micRestartTimer);
|
|
4765
|
+
state.micRestartTimer = null;
|
|
4766
|
+
}
|
|
4767
|
+
// Reset audioDone — server is still sending, so a previously-seen
|
|
4768
|
+
// voice_audio_done signal must have been spurious (or stale from a
|
|
4769
|
+
// prior turn). Wait for the next legitimate one.
|
|
4770
|
+
state.audioDone = false;
|
|
4771
|
+
}
|
|
4725
4772
|
|
|
4726
4773
|
// Strip 4-byte "VPCM" header
|
|
4727
4774
|
var pcmData = new Int16Array(arrayBuffer, 4);
|
|
@@ -4785,9 +4832,7 @@
|
|
|
4785
4832
|
state.audioDone = false;
|
|
4786
4833
|
if (state.active) {
|
|
4787
4834
|
setPhase('idle');
|
|
4788
|
-
|
|
4789
|
-
if (state.active) startRecognition();
|
|
4790
|
-
}, 300);
|
|
4835
|
+
scheduleMicRestart(300);
|
|
4791
4836
|
}
|
|
4792
4837
|
}
|
|
4793
4838
|
|
|
@@ -4849,6 +4894,10 @@
|
|
|
4849
4894
|
clearTimeout(state.voiceLoadTimer);
|
|
4850
4895
|
state.voiceLoadTimer = null;
|
|
4851
4896
|
}
|
|
4897
|
+
if (state.micRestartTimer) {
|
|
4898
|
+
clearTimeout(state.micRestartTimer);
|
|
4899
|
+
state.micRestartTimer = null;
|
|
4900
|
+
}
|
|
4852
4901
|
state.speakQueue.length = 0;
|
|
4853
4902
|
state.voicesLoaded = false;
|
|
4854
4903
|
state.waitingForServerTTS = false;
|