refactor: 迁移前端到 React 19 + Zustand + Tailwind CSS v4
- 将 vanilla TS 单文件 (app.ts 395行) 拆分为 React 组件化架构 - 引入 Zustand 管理全局状态 (连接/录音/预览/历史/toast) - 自定义 hooks 封装 WebSocket 连接和音频录制管线 - CSS 全面 Tailwind 化,style.css 从 234 行精简到 114 行 (仅保留 tokens + keyframes) - 新增依赖: react, react-dom, zustand, @vitejs/plugin-react - Go 后端 embed 路径 web/dist 不变,无需改动
This commit is contained in:
40
AGENTS.md
40
AGENTS.md
@@ -7,7 +7,7 @@ VoicePaste: phone as microphone via browser → LAN WebSocket → Go server →
|
||||
## Tech Stack
|
||||
|
||||
- **Backend**: Go 1.25+, Fiber v3, fasthttp/websocket, CGO required (robotgo + clipboard)
|
||||
- **Frontend**: TypeScript, Vite 7, Biome 2, bun (package manager + runtime)
|
||||
- **Frontend**: React 19, TypeScript, Zustand, Vite 7, Tailwind CSS v4, Biome 2, bun (package manager + runtime)
|
||||
- **Tooling**: Taskfile (not Make), mise (Go + bun + task)
|
||||
- **ASR**: Doubao Seed-ASR-2.0 via custom binary WebSocket protocol
|
||||
|
||||
@@ -72,13 +72,29 @@ internal/
|
||||
asr/client.go # WSS client to Doubao, audio streaming, result forwarding
|
||||
paste/paste.go # clipboard.Write + robotgo key simulation (Ctrl+V / Cmd+V)
|
||||
web/
|
||||
app.ts # Main app: WS client, audio pipeline, recording, history, UI
|
||||
audio-processor.ts # AudioWorklet: PCM capture, 200ms frame accumulation
|
||||
index.html # Mobile-first UI (all Chinese)
|
||||
style.css # Dark theme
|
||||
vite.config.ts # Vite config
|
||||
biome.json # Biome config
|
||||
tsconfig.json # TypeScript strict config
|
||||
index.html # HTML shell with React root
|
||||
vite.config.ts # Vite config (React + Tailwind plugins)
|
||||
biome.json # Biome config (lint, format, Tailwind class sorting)
|
||||
tsconfig.json # TypeScript strict config (React JSX)
|
||||
src/
|
||||
main.tsx # React entry point
|
||||
App.tsx # Root component: composes hooks + layout
|
||||
app.css # Tailwind imports, design tokens (@theme), keyframes
|
||||
stores/
|
||||
app-store.ts # Zustand store: connection, recording, preview, history, toast
|
||||
hooks/
|
||||
useWebSocket.ts # WS client hook: connect, reconnect, message dispatch
|
||||
useRecorder.ts # Audio pipeline hook: getUserMedia, AudioWorklet, resample
|
||||
components/
|
||||
StatusBadge.tsx # Connection status indicator
|
||||
PreviewBox.tsx # Real-time transcription preview
|
||||
MicButton.tsx # Push-to-talk button with animations
|
||||
HistoryList.tsx # Transcription history with re-send
|
||||
Toast.tsx # Auto-dismiss toast notifications
|
||||
lib/
|
||||
resample.ts # Linear interpolation resampler (native rate → 16kHz Int16)
|
||||
workers/
|
||||
audio-processor.ts # AudioWorklet: PCM capture, 200ms frame accumulation
|
||||
```
|
||||
|
||||
## Code Style — Go
|
||||
@@ -137,13 +153,15 @@ Per-connection loggers via `slog.With("remote", addr)`.
|
||||
- Target: ES2022, module: ESNext, bundler resolution
|
||||
- DOM + DOM.Iterable libs
|
||||
|
||||
### Patterns
|
||||
- No framework — vanilla TypeScript with direct DOM manipulation
|
||||
- State object pattern: single `AppState` interface with mutable fields
|
||||
- React 19 with functional components and hooks
|
||||
- Zustand for global state management (connection, recording, preview, history, toast)
|
||||
- Custom hooks for imperative APIs: `useWebSocket`, `useRecorder`
|
||||
- Zustand `getState()` in hooks/callbacks to avoid stale closures
|
||||
- Pointer Events for touch/mouse (not touch + mouse separately)
|
||||
- AudioWorklet for audio capture (not MediaRecorder)
|
||||
- `?worker&url` Vite import for AudioWorklet files
|
||||
- WebSocket: binary for audio frames, JSON text for control messages
|
||||
- Tailwind CSS v4 with `@theme` design tokens; minimal custom CSS (keyframes only)
|
||||
|
||||
## Language & Locale
|
||||
|
||||
|
||||
Reference in New Issue
Block a user