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:
2026-06-12 17:46:25 +08:00
parent 10f676f3b4
commit b5c0d40609
26 changed files with 4236 additions and 0 deletions

148
frontend/src/types/index.ts Normal file
View File

@@ -0,0 +1,148 @@
// ============================================================
// CamTalk 前端类型定义
// 来源docs/03-接口文档.md
// ============================================================
// ---- 会话模型 ----
export interface SessionConfig {
ttsEnabled: boolean;
detailLevel: "low" | "high";
language: string;
}
export interface Session {
sessionId: string;
createdAt: string;
config: SessionConfig;
}
// ---- 聊天消息 ----
export interface ChatMessage {
role: "user" | "assistant";
content: string;
imageUrl?: string;
timestamp: number;
tokensUsed?: number;
}
// ---- WebSocket 通用信封 ----
export interface WsMessage {
type: string;
request_id?: string;
timestamp?: number;
[key: string]: unknown;
}
// ---- 客户端 → 服务端消息 ----
export interface QueryMessage {
type: "query";
request_id: string;
image: string; // Base64 JPEG不含 data: 前缀)
audio: string; // Base64 PCM 16kHz
mime_type?: string; // 默认 "audio/pcm"
}
export interface ConfigMessage {
type: "config";
payload: {
tts_enabled?: boolean;
detail_level?: "low" | "high";
language?: string;
};
}
export interface InterruptMessage {
type: "interrupt";
request_id?: string;
}
export interface PingMessage {
type: "ping";
}
export type ClientMessage =
| QueryMessage
| ConfigMessage
| InterruptMessage
| PingMessage;
// ---- 服务端 → 客户端消息 ----
export interface ConnectedMessage {
type: "connected";
session_id: string;
server_version: string;
}
export interface STTResultMessage {
type: "stt_result";
request_id: string;
text: string;
is_final: boolean;
}
export interface LLMChunkMessage {
type: "llm_chunk";
request_id: string;
delta: string;
role: "assistant";
}
export interface LLMDoneMessage {
type: "llm_done";
request_id: string;
full_text: string;
tokens_used: {
prompt: number;
completion: number;
total: number;
};
model: string;
latency_ms: number;
}
export interface TTSAudioMessage {
type: "tts_audio";
request_id: string;
audio: string; // Base64 音频片段
mime_type: string; // "audio/mp3" 或 "audio/pcm"
is_last: boolean;
}
export interface ErrorMessage {
type: "error";
request_id?: string;
code: string;
message: string;
}
export interface PongMessage {
type: "pong";
}
export type ServerMessage =
| ConnectedMessage
| STTResultMessage
| LLMChunkMessage
| LLMDoneMessage
| TTSAudioMessage
| ErrorMessage
| PongMessage;
// ---- 错误码 ----
export type ErrorCode =
| "INVALID_MESSAGE"
| "SESSION_NOT_FOUND"
| "RATE_LIMITED"
| "IMAGE_TOO_LARGE"
| "AUDIO_TOO_SHORT"
| "LLM_TIMEOUT"
| "LLM_ERROR"
| "STT_ERROR"
| "TTS_ERROR"
| "INTERNAL_ERROR";