73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
|
|
// ============================================================
|
||
|
|
// CamTalk — 主应用组件
|
||
|
|
// ============================================================
|
||
|
|
|
||
|
|
import { useVisionSession } from "./hooks/useVisionSession";
|
||
|
|
import { VideoPreview } from "./components/VideoPreview";
|
||
|
|
import { ChatPanel } from "./components/ChatPanel";
|
||
|
|
import "./App.css";
|
||
|
|
|
||
|
|
function App() {
|
||
|
|
const {
|
||
|
|
messages,
|
||
|
|
currentReply,
|
||
|
|
isProcessing,
|
||
|
|
isSpeaking,
|
||
|
|
connectionStatus,
|
||
|
|
videoRef,
|
||
|
|
stream,
|
||
|
|
startSession,
|
||
|
|
stopSession,
|
||
|
|
interrupt,
|
||
|
|
} = useVisionSession();
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="app">
|
||
|
|
<header className="app-header">
|
||
|
|
<h1>CamTalk</h1>
|
||
|
|
<span className={`status status--${connectionStatus}`}>
|
||
|
|
{connectionStatus === "connected" ? "已连接" : connectionStatus === "connecting" ? "连接中..." : "未连接"}
|
||
|
|
</span>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<main className="app-main">
|
||
|
|
<div className="video-section">
|
||
|
|
<VideoPreview ref={videoRef} isStreaming={!!stream} />
|
||
|
|
{isSpeaking && <div className="vad-indicator">🎤 正在聆听...</div>}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="chat-section">
|
||
|
|
<ChatPanel messages={messages} />
|
||
|
|
{currentReply && (
|
||
|
|
<div className="chat-message chat-message--assistant chat-message--streaming">
|
||
|
|
<div className="chat-message__role">AI</div>
|
||
|
|
<div className="chat-message__content">{currentReply}</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</main>
|
||
|
|
|
||
|
|
<footer className="app-footer">
|
||
|
|
{connectionStatus !== "connected" ? (
|
||
|
|
<button className="btn btn--primary" onClick={startSession}>
|
||
|
|
开始对话
|
||
|
|
</button>
|
||
|
|
) : (
|
||
|
|
<>
|
||
|
|
<button className="btn btn--secondary" onClick={stopSession}>
|
||
|
|
结束对话
|
||
|
|
</button>
|
||
|
|
{isProcessing && (
|
||
|
|
<button className="btn btn--warning" onClick={interrupt}>
|
||
|
|
打断
|
||
|
|
</button>
|
||
|
|
)}
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
</footer>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export default App;
|