fixed coaching rules

This commit is contained in:
ValueOn AG 2026-03-02 21:10:36 +01:00
parent 0518ee48d5
commit 18e6b3e4d8
2 changed files with 33 additions and 8 deletions

View file

@ -268,6 +268,12 @@
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
} }
.messageLive {
opacity: 0.7;
font-style: italic;
border: 1px dashed rgba(255, 255, 255, 0.4);
}
.messageAssistant .messageBubble { .messageAssistant .messageBubble {
background: var(--bg-card, #f5f5f5); background: var(--bg-card, #f5f5f5);
color: var(--text-primary, #333); color: var(--text-primary, #333);

View file

@ -28,6 +28,7 @@ export const CommcoachCoachingView: React.FC = () => {
const transcriptPartsRef = useRef<string[]>([]); const transcriptPartsRef = useRef<string[]>([]);
const [isListening, setIsListening] = useState(false); const [isListening, setIsListening] = useState(false);
const [isUserSpeaking, setIsUserSpeaking] = useState(false); const [isUserSpeaking, setIsUserSpeaking] = useState(false);
const [liveTranscript, setLiveTranscript] = useState('');
const handleSend = useCallback(async () => { const handleSend = useCallback(async () => {
if (!coach.inputValue.trim() || coach.isStreaming) return; if (!coach.inputValue.trim() || coach.isStreaming) return;
@ -124,16 +125,26 @@ export const CommcoachCoachingView: React.FC = () => {
if (cancelled) return; if (cancelled) return;
setIsUserSpeaking(true); setIsUserSpeaking(true);
transcriptPartsRef.current = []; transcriptPartsRef.current = [];
setLiveTranscript('');
}; };
recognition.onresult = (event: SpeechRecognitionEvent) => { recognition.onresult = (event: SpeechRecognitionEvent) => {
if (cancelled) return; if (cancelled) return;
const result = event.results[event.resultIndex]; const finalized: string[] = [];
if (!result.isFinal) return; let currentInterim = '';
const transcript = result[0].transcript.trim(); for (let i = 0; i < event.results.length; i++) {
if (transcript) transcriptPartsRef.current.push(transcript); const r = event.results[i];
const wordCount = transcript.split(/\s+/).filter(Boolean).length; if (r.isFinal) {
if (wordCount >= MIN_WORDS_TO_INTERRUPT) coach.stopTts(); finalized.push(r[0].transcript.trim());
} else {
currentInterim = r[0].transcript.trim();
}
}
transcriptPartsRef.current = finalized.filter(Boolean);
const preview = [...transcriptPartsRef.current, currentInterim].join(' ').trim();
setLiveTranscript(preview);
const totalWords = preview.split(/\s+/).filter(Boolean).length;
if (totalWords >= MIN_WORDS_TO_INTERRUPT) coach.stopTts();
}; };
recognition.onspeechend = () => { recognition.onspeechend = () => {
@ -144,6 +155,7 @@ export const CommcoachCoachingView: React.FC = () => {
if (wordCount >= MIN_WORDS_TO_INTERRUPT) coach.sendMessage(fullTranscript); if (wordCount >= MIN_WORDS_TO_INTERRUPT) coach.sendMessage(fullTranscript);
} }
transcriptPartsRef.current = []; transcriptPartsRef.current = [];
setLiveTranscript('');
setIsUserSpeaking(false); setIsUserSpeaking(false);
}; };
@ -302,7 +314,7 @@ export const CommcoachCoachingView: React.FC = () => {
</div> </div>
</div> </div>
<AutoScroll scrollDependency={coach.messages.length + (coach.isStreaming ? 1 : 0)}> <AutoScroll scrollDependency={coach.messages.length + (coach.isStreaming ? 1 : 0) + liveTranscript.length}>
<div className={styles.messages}> <div className={styles.messages}>
{coach.messages.map(msg => ( {coach.messages.map(msg => (
<div <div
@ -319,6 +331,13 @@ export const CommcoachCoachingView: React.FC = () => {
</div> </div>
</div> </div>
))} ))}
{liveTranscript && (
<div className={`${styles.message} ${styles.messageUser}`}>
<div className={`${styles.messageBubble} ${styles.messageLive}`}>
{liveTranscript}
</div>
</div>
)}
{coach.isStreaming && ( {coach.isStreaming && (
<div className={`${styles.message} ${styles.messageAssistant}`}> <div className={`${styles.message} ${styles.messageAssistant}`}>
<div className={styles.messageBubble}> <div className={styles.messageBubble}>
@ -341,7 +360,7 @@ export const CommcoachCoachingView: React.FC = () => {
: coach.isStreaming : coach.isStreaming
? (coach.streamingStatus || 'Coach antwortet...') ? (coach.streamingStatus || 'Coach antwortet...')
: isUserSpeaking : isUserSpeaking
? 'Aufnahme...' ? 'Spricht...'
: isListening : isListening
? 'Mikrofon an bitte sprechen' ? 'Mikrofon an bitte sprechen'
: 'Mikrofon wird gestartet...'} : 'Mikrofon wird gestartet...'}