feat: 初始化前端项目,搭建 React + TypeScript + Vite 工程结构
- 创建 Vite + React 18 + TypeScript 项目(严格模式) - 安装 @ricky0123/vad-web、onnxruntime-web、uuid 依赖 - 定义全部 WebSocket 消息类型和数据模型(对齐接口文档) - 实现 WebSocket 连接管理(心跳保活、指数退避重连) - 实现音频编码工具(PCM↔Base64、DataURL 转换) - 创建组件骨架:CameraManager、MicManager、EdgeProcessor、WebSocketManager、ChatPanel、VideoPreview - 实现核心 useVisionSession Hook 骨架 - 配置 ESLint(_前缀变量忽略规则) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
72
frontend/src/App.tsx
Normal file
72
frontend/src/App.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
// ============================================================
|
||||
// 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;
|
||||
Reference in New Issue
Block a user