fixed build
This commit is contained in:
parent
18e6b3e4d8
commit
275164e7cb
4 changed files with 49 additions and 53 deletions
45
src/global.d.ts
vendored
Normal file
45
src/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
interface SpeechRecognition extends EventTarget {
|
||||||
|
continuous: boolean;
|
||||||
|
interimResults: boolean;
|
||||||
|
lang: string;
|
||||||
|
onstart: ((this: SpeechRecognition, ev: Event) => void) | null;
|
||||||
|
onend: ((this: SpeechRecognition, ev: Event) => void) | null;
|
||||||
|
onerror: ((this: SpeechRecognition, ev: SpeechRecognitionErrorEvent) => void) | null;
|
||||||
|
onresult: ((this: SpeechRecognition, ev: SpeechRecognitionEvent) => void) | null;
|
||||||
|
onspeechstart: ((this: SpeechRecognition, ev: Event) => void) | null;
|
||||||
|
onspeechend: ((this: SpeechRecognition, ev: Event) => void) | null;
|
||||||
|
start(): void;
|
||||||
|
stop(): void;
|
||||||
|
abort(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpeechRecognitionErrorEvent extends Event {
|
||||||
|
error: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpeechRecognitionEvent extends Event {
|
||||||
|
resultIndex: number;
|
||||||
|
results: SpeechRecognitionResultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpeechRecognitionResultList {
|
||||||
|
length: number;
|
||||||
|
[index: number]: SpeechRecognitionResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpeechRecognitionResult {
|
||||||
|
isFinal: boolean;
|
||||||
|
length: number;
|
||||||
|
[index: number]: SpeechRecognitionAlternative;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpeechRecognitionAlternative {
|
||||||
|
transcript: string;
|
||||||
|
confidence: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
SpeechRecognition: new () => SpeechRecognition;
|
||||||
|
webkitSpeechRecognition: new () => SpeechRecognition;
|
||||||
|
}
|
||||||
|
|
@ -9,10 +9,9 @@ import { useApiRequest } from './useApi';
|
||||||
import { useInstanceId } from './useCurrentInstance';
|
import { useInstanceId } from './useCurrentInstance';
|
||||||
import {
|
import {
|
||||||
getContextsApi, createContextApi, getContextDetailApi,
|
getContextsApi, createContextApi, getContextDetailApi,
|
||||||
startSessionStreamApi, getSessionApi, completeSessionApi, cancelSessionApi,
|
startSessionStreamApi, completeSessionApi, cancelSessionApi,
|
||||||
sendMessageStreamApi, sendAudioStreamApi,
|
sendMessageStreamApi, sendAudioStreamApi,
|
||||||
getTasksApi, createTaskApi, updateTaskStatusApi, deleteTaskApi,
|
createTaskApi, updateTaskStatusApi, deleteTaskApi,
|
||||||
getProfileApi, testVoiceApi,
|
|
||||||
type CoachingContext, type CoachingSession, type CoachingMessage,
|
type CoachingContext, type CoachingSession, type CoachingMessage,
|
||||||
type CoachingTask, type CoachingScore, type SSEEvent,
|
type CoachingTask, type CoachingScore, type SSEEvent,
|
||||||
} from '../api/commcoachApi';
|
} from '../api/commcoachApi';
|
||||||
|
|
@ -84,45 +83,9 @@ export function useCommcoach(): CommcoachHookReturn {
|
||||||
const isMountedRef = useRef(true);
|
const isMountedRef = useRef(true);
|
||||||
const currentAudioRef = useRef<HTMLAudioElement | null>(null);
|
const currentAudioRef = useRef<HTMLAudioElement | null>(null);
|
||||||
const isTtsPlayingRef = useRef(false);
|
const isTtsPlayingRef = useRef(false);
|
||||||
const profileRef = useRef<{ preferredLanguage?: string; preferredVoice?: string } | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []);
|
useEffect(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []);
|
||||||
|
|
||||||
const _speakText = useCallback(async (text: string) => {
|
|
||||||
if (!instanceId) return;
|
|
||||||
const plain = _stripMarkdownForTts(text);
|
|
||||||
if (!plain.trim()) return;
|
|
||||||
if (currentAudioRef.current) {
|
|
||||||
currentAudioRef.current.pause();
|
|
||||||
currentAudioRef.current = null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
let profile = profileRef.current;
|
|
||||||
if (!profile) {
|
|
||||||
const p = await getProfileApi(request, instanceId);
|
|
||||||
profile = { preferredLanguage: p?.preferredLanguage || 'de-DE', preferredVoice: p?.preferredVoice };
|
|
||||||
profileRef.current = profile;
|
|
||||||
}
|
|
||||||
const lang = profile?.preferredLanguage || 'de-DE';
|
|
||||||
const voiceId = profile?.preferredVoice || undefined;
|
|
||||||
const result = await testVoiceApi(request, instanceId, { text: plain, language: lang, voiceId });
|
|
||||||
if (result?.success && result?.audio && isMountedRef.current) {
|
|
||||||
const audio = new Audio(`data:audio/mp3;base64,${result.audio}`);
|
|
||||||
currentAudioRef.current = audio;
|
|
||||||
audio.onended = () => { currentAudioRef.current = null; };
|
|
||||||
try {
|
|
||||||
await audio.play();
|
|
||||||
} catch (playErr: any) {
|
|
||||||
if (playErr?.name === 'NotAllowedError') {
|
|
||||||
console.warn('CommCoach TTS: Browser blocked audio. Click "Session starten" or "Senden" first.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// TTS failed silently, text is still visible
|
|
||||||
}
|
|
||||||
}, [request, instanceId]);
|
|
||||||
|
|
||||||
const refreshContexts = useCallback(async () => {
|
const refreshContexts = useCallback(async () => {
|
||||||
if (!instanceId) return;
|
if (!instanceId) return;
|
||||||
setLoadingContexts(true);
|
setLoadingContexts(true);
|
||||||
|
|
@ -520,8 +483,6 @@ export function useCommcoach(): CommcoachHookReturn {
|
||||||
|
|
||||||
useEffect(() => { if (instanceId) refreshContexts(); }, [instanceId, refreshContexts]);
|
useEffect(() => { if (instanceId) refreshContexts(); }, [instanceId, refreshContexts]);
|
||||||
|
|
||||||
useEffect(() => { profileRef.current = null; }, [instanceId]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
contexts, selectedContextId, selectedContext, loadingContexts,
|
contexts, selectedContextId, selectedContext, loadingContexts,
|
||||||
session, messages, isStreaming, streamingStatus,
|
session, messages, isStreaming, streamingStatus,
|
||||||
|
|
@ -536,16 +497,6 @@ export function useCommcoach(): CommcoachHookReturn {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function _stripMarkdownForTts(text: string): string {
|
|
||||||
return text
|
|
||||||
.replace(/\*\*(.+?)\*\*/g, '$1')
|
|
||||||
.replace(/\*(.+?)\*/g, '$1')
|
|
||||||
.replace(/\[(.+?)\]\(.+?\)/g, '$1')
|
|
||||||
.replace(/^#+\s*/gm, '')
|
|
||||||
.replace(/`[^`]+`/g, (m) => m.slice(1, -1))
|
|
||||||
.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _unlockAudioForTts(): Promise<void> {
|
async function _unlockAudioForTts(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
|
const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import styles from './CommcoachDashboardView.module.css';
|
||||||
export const CommcoachDashboardView: React.FC = () => {
|
export const CommcoachDashboardView: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { mandateId, instanceId } = useCurrentInstance();
|
const { mandateId, instanceId } = useCurrentInstance();
|
||||||
const { dashboard, profile, loading, error, refresh } = useCommcoachDashboard();
|
const { dashboard, loading, error } = useCommcoachDashboard();
|
||||||
|
|
||||||
const handleContextClick = (contextId: string) => {
|
const handleContextClick = (contextId: string) => {
|
||||||
if (mandateId && instanceId) {
|
if (mandateId && instanceId) {
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ export const CommcoachDossierView: React.FC = () => {
|
||||||
|
|
||||||
interface ScoreGroup {
|
interface ScoreGroup {
|
||||||
dimension: string;
|
dimension: string;
|
||||||
latest: { score: number; trend: string; evidence?: string };
|
latest: { score: number; trend: string; evidence?: string; createdAt?: string };
|
||||||
history: Array<{ score: number; createdAt?: string }>;
|
history: Array<{ score: number; createdAt?: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue