diff --git a/web/pages/Assistants.tsx b/web/pages/Assistants.tsx index 0f18265..a7320b2 100644 --- a/web/pages/Assistants.tsx +++ b/web/pages/Assistants.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; -import { Plus, Search, Play, Copy, Trash2, Edit2, Mic, MessageSquare, Save, Video, PhoneOff, Camera, ArrowLeftRight, Send, Phone, MoreHorizontal, Rocket, AlertTriangle, PhoneCall, CameraOff, Image, Images, CloudSun, Calendar, TrendingUp, Coins, Wrench, Globe, Terminal, X, ClipboardCheck, Sparkles, Volume2, Timer, ChevronDown, ChevronLeft, ChevronRight, Link as LinkIcon, Database, Server, Zap, ExternalLink, Key, BrainCircuit, Ear, Book, Filter } from 'lucide-react'; +import { Plus, Search, Play, Copy, Trash2, Edit2, Mic, MessageSquare, Save, Video, PhoneOff, Camera, ArrowLeftRight, Send, Phone, MoreHorizontal, Rocket, AlertTriangle, PhoneCall, CameraOff, Image, Images, CloudSun, Calendar, TrendingUp, Coins, Wrench, Globe, Terminal, X, ClipboardCheck, Sparkles, Volume2, Timer, ChevronDown, Link as LinkIcon, Database, Server, Zap, ExternalLink, Key, BrainCircuit, Ear, Book, Filter } from 'lucide-react'; import { Button, Input, Card, Badge, Drawer, Dialog } from '../components/UI'; import { ASRModel, Assistant, KnowledgeBase, LLMModel, TabValue, Voice } from '../types'; import { createAssistant, deleteAssistant, fetchASRModels, fetchAssistantRuntimeConfig, fetchAssistants, fetchKnowledgeBases, fetchLLMModels, fetchVoices, updateAssistant as updateAssistantApi } from '../services/backendApi'; @@ -1046,6 +1046,7 @@ export const DebugDrawer: React.FC<{ const [wsError, setWsError] = useState(''); const [resolvedConfigOpen, setResolvedConfigOpen] = useState(false); const [resolvedConfigView, setResolvedConfigView] = useState(''); + const [settingsDrawerOpen, setSettingsDrawerOpen] = useState(false); const [wsUrl, setWsUrl] = useState(() => { const fromStorage = localStorage.getItem('debug_ws_url'); if (fromStorage) return fromStorage; @@ -1089,6 +1090,7 @@ export const DebugDrawer: React.FC<{ void audioCtxRef.current.close(); audioCtxRef.current = null; } + setSettingsDrawerOpen(false); setIsSwapped(false); setCallStatus('idle'); } @@ -1591,165 +1593,231 @@ export const DebugDrawer: React.FC<{ ); + const settingsPanel = ( +
+
+

调试设置

+ +
+ +
+
+ + setWsUrl(e.target.value)} placeholder="ws://localhost:8000/ws" /> +
+
+ WS: {wsStatus} + +
+
+ + +
+ {wsError &&

{wsError}

} + +
+ + {resolvedConfigOpen && ( +
+              {resolvedConfigView || 'Connect to load resolved config...'}
+            
+ )} +
+
+
+ ); + return ( + <> { handleHangup(); onClose(); }} title={`调试: ${assistant.name}`}> -
-
- {(['text', 'voice', 'video'] as const).map(m => ( - - ))} +
+
+
+ {(['text', 'voice', 'video'] as const).map(m => ( + + ))} +
-
-
+
{mode === 'text' ? ( - textSessionStarted ? ( -
-
- - WS: {wsStatus} - - - {wsError && {wsError}} -
- -
- ) : ( -
-
- - setWsUrl(e.target.value)} placeholder="ws://localhost:8000/ws" /> -
- WS: {wsStatus} - - {wsError && {wsError}} + textSessionStarted ? ( +
+
+
+
+ +
+

文本会话中...

+
+

对话记录

+ +
+
-
- -
- ) + ) : wsStatus === 'connecting' ? ( +
+
+ +
+
+

CONNECTING...

+

正在连接文本调试服务

+
+ +
+ ) : ( +
+
+
+
+ +
+
+
+

准备就绪

+

点击下方按钮开启人机交互测试

+
+ +
+ ) ) : callStatus === 'idle' ? ( -
-
+
+
- {mode === 'voice' ? :
-
-
+
+

准备就绪

点击下方按钮开启人机交互测试

-
-
+ -
- ) : callStatus === 'calling' ? ( -
-
+ +
+ ) : callStatus === 'calling' ? ( +
+
-
-
+
+

CALLING...

正在连接 AI 服务

-
- -
- ) : ( -
- {mode === 'voice' ? ( +
+ +
+ ) : ( +
+ {mode === 'voice' ? (
-
-
- -
-

通话中...

+
+
+
-

转写日志

- +

通话中...

+
+

转写日志

+
- ) : ( + ) : (
-
-
- - -
-
-
{isSwapped ? renderLocalVideo(false) : renderRemoteVideo(false)}
-
{isSwapped ? renderRemoteVideo(true) : renderLocalVideo(true)}
- -
+
+
+ +
- +
+
{isSwapped ? renderLocalVideo(false) : renderRemoteVideo(false)}
+
{isSwapped ? renderRemoteVideo(true) : renderLocalVideo(true)}
+ +
+
+
- )} - -
+ +
)}
- {(mode !== 'text' || textSessionStarted) && ( -
-
- setInputText(e.target.value)} placeholder={mode === 'text' ? "输入消息..." : "输入文本模拟交互..."} onKeyDown={e => e.key === 'Enter' && handleSend()} disabled={isLoading || (mode === 'text' ? !textSessionStarted : callStatus !== 'active')} className="flex-1" /> - -
+
+
+ setInputText(e.target.value)} + placeholder={mode === 'text' && !textSessionStarted ? "请先发起呼叫后输入消息..." : (mode === 'text' ? "输入消息..." : "输入文本模拟交互...")} + onKeyDown={e => e.key === 'Enter' && handleSend()} + disabled={isLoading || (mode === 'text' ? !textSessionStarted : callStatus !== 'active')} + className="flex-1" + /> +
- )} -
- -
- -
- -
-
-
- Resolved Runtime Config - read-only -
-
-                {resolvedConfigView || 'Connect to load resolved config...'}
-              
-
+ {isOpen && ( +
+ +
+
+ {settingsPanel} +
+
+
+ )} + ); };