commcoach: mobile debug overlay for speech recognition diagnosis
Made-with: Cursor
This commit is contained in:
parent
d5a4c880a4
commit
b28a7fcf33
1 changed files with 45 additions and 0 deletions
|
|
@ -52,6 +52,19 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
const [liveTranscript, setLiveTranscript] = useState('');
|
const [liveTranscript, setLiveTranscript] = useState('');
|
||||||
const [isTtsPlaying, setIsTtsPlaying] = useState(false);
|
const [isTtsPlaying, setIsTtsPlaying] = useState(false);
|
||||||
|
|
||||||
|
// #region agent log
|
||||||
|
const debugLogsRef = useRef<string[]>([]);
|
||||||
|
const [debugVisible, setDebugVisible] = useState(false);
|
||||||
|
const [debugSnapshot, setDebugSnapshot] = useState<string[]>([]);
|
||||||
|
const _dlog = useCallback((tag: string, info?: string) => {
|
||||||
|
const t = new Date();
|
||||||
|
const ts = `${t.getMinutes()}:${String(t.getSeconds()).padStart(2,'0')}.${String(t.getMilliseconds()).padStart(3,'0')}`;
|
||||||
|
const entry = `[${ts}] ${tag}${info ? ' ' + info : ''}`;
|
||||||
|
debugLogsRef.current.push(entry);
|
||||||
|
if (debugLogsRef.current.length > 60) debugLogsRef.current.shift();
|
||||||
|
}, []);
|
||||||
|
// #endregion
|
||||||
|
|
||||||
// Auto-select first context
|
// Auto-select first context
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!coach.selectedContextId && coach.contexts.length > 0) {
|
if (!coach.selectedContextId && coach.contexts.length > 0) {
|
||||||
|
|
@ -136,6 +149,10 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
|
|
||||||
const _sendAndClearTranscript = () => {
|
const _sendAndClearTranscript = () => {
|
||||||
const fullTranscript = transcriptPartsRef.current.join(' ').trim();
|
const fullTranscript = transcriptPartsRef.current.join(' ').trim();
|
||||||
|
// #region agent log
|
||||||
|
const wc = fullTranscript.split(/\s+/).filter(Boolean).length;
|
||||||
|
_dlog('SEND', `words=${wc} send=${wc>=MIN_WORDS_TO_INTERRUPT} "${fullTranscript.substring(0,60)}"`);
|
||||||
|
// #endregion
|
||||||
if (fullTranscript) {
|
if (fullTranscript) {
|
||||||
const wordCount = fullTranscript.split(/\s+/).filter(Boolean).length;
|
const wordCount = fullTranscript.split(/\s+/).filter(Boolean).length;
|
||||||
if (wordCount >= MIN_WORDS_TO_INTERRUPT) coach.sendMessage(fullTranscript);
|
if (wordCount >= MIN_WORDS_TO_INTERRUPT) coach.sendMessage(fullTranscript);
|
||||||
|
|
@ -155,6 +172,9 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onspeechstart = () => {
|
recognition.onspeechstart = () => {
|
||||||
|
// #region agent log
|
||||||
|
_dlog('SPCH-START', `tts=${coach.isTtsPlayingRef.current}`);
|
||||||
|
// #endregion
|
||||||
if (cancelled || coach.isTtsPlayingRef.current) return;
|
if (cancelled || coach.isTtsPlayingRef.current) return;
|
||||||
setIsUserSpeaking(true);
|
setIsUserSpeaking(true);
|
||||||
transcriptPartsRef.current = [];
|
transcriptPartsRef.current = [];
|
||||||
|
|
@ -189,6 +209,9 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onspeechend = () => {
|
recognition.onspeechend = () => {
|
||||||
|
// #region agent log
|
||||||
|
_dlog('SPCH-END', `tts=${coach.isTtsPlayingRef.current} parts=${transcriptPartsRef.current.length}`);
|
||||||
|
// #endregion
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
if (silenceTimerRef.current) clearTimeout(silenceTimerRef.current);
|
if (silenceTimerRef.current) clearTimeout(silenceTimerRef.current);
|
||||||
if (coach.isTtsPlayingRef.current) {
|
if (coach.isTtsPlayingRef.current) {
|
||||||
|
|
@ -202,6 +225,9 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onend = () => {
|
recognition.onend = () => {
|
||||||
|
// #region agent log
|
||||||
|
_dlog('REC-END', `cancelled=${cancelled} sameRef=${speechRecognitionRef.current===recognition}`);
|
||||||
|
// #endregion
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
setIsUserSpeaking(false);
|
setIsUserSpeaking(false);
|
||||||
transcriptPartsRef.current = [];
|
transcriptPartsRef.current = [];
|
||||||
|
|
@ -212,6 +238,9 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onerror = (event: any) => {
|
recognition.onerror = (event: any) => {
|
||||||
|
// #region agent log
|
||||||
|
_dlog('REC-ERR', event.error);
|
||||||
|
// #endregion
|
||||||
if (event.error === 'no-speech' || event.error === 'aborted') return;
|
if (event.error === 'no-speech' || event.error === 'aborted') return;
|
||||||
console.warn('SpeechRecognition error:', event.error);
|
console.warn('SpeechRecognition error:', event.error);
|
||||||
};
|
};
|
||||||
|
|
@ -225,6 +254,9 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
|
|
||||||
init();
|
init();
|
||||||
return () => {
|
return () => {
|
||||||
|
// #region agent log
|
||||||
|
_dlog('CLEANUP', `tab=${activeTab} sess=${coach.session?.id} muted=${coach.isMuted}`);
|
||||||
|
// #endregion
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
coach.stopTts();
|
coach.stopTts();
|
||||||
if (silenceTimerRef.current) clearTimeout(silenceTimerRef.current);
|
if (silenceTimerRef.current) clearTimeout(silenceTimerRef.current);
|
||||||
|
|
@ -703,6 +735,19 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>)}
|
</>)}
|
||||||
|
{/* #region agent log */}
|
||||||
|
<div style={{position:'fixed',bottom:0,right:0,zIndex:9999}}>
|
||||||
|
<button
|
||||||
|
onClick={() => { setDebugSnapshot([...debugLogsRef.current]); setDebugVisible(v => !v); }}
|
||||||
|
style={{background:'#333',color:'#0f0',border:'none',padding:'4px 8px',fontSize:'10px',borderRadius:'4px 0 0 0'}}
|
||||||
|
>DBG ({debugLogsRef.current.length})</button>
|
||||||
|
{debugVisible && (
|
||||||
|
<div style={{background:'rgba(0,0,0,0.9)',color:'#0f0',fontSize:'9px',maxHeight:'40vh',overflow:'auto',padding:'4px',fontFamily:'monospace',whiteSpace:'pre-wrap',width:'100vw'}}>
|
||||||
|
{debugSnapshot.map((l,i) => <div key={i}>{l}</div>)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{/* #endregion */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue