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:
2026-03-02 06:36:02 +08:00
parent ea46ad71bf
commit 70344bcd98
21 changed files with 914 additions and 687 deletions

View File

@@ -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