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:
@@ -9,50 +9,7 @@
|
||||
<title>VoicePaste</title>
|
||||
</head>
|
||||
<body class="h-full bg-bg text-fg overflow-hidden select-none">
|
||||
<div id="app" class="relative z-1 flex flex-col h-full max-w-[480px] mx-auto">
|
||||
<header class="flex items-center justify-between pt-2 pb-5 shrink-0">
|
||||
<h1 class="text-[22px] font-bold tracking-[-0.03em]">VoicePaste</h1>
|
||||
<div id="status" class="status disconnected flex items-center gap-[7px] text-xs font-medium text-fg-dim px-3 py-[5px] rounded-full bg-surface border border-edge transition-all">
|
||||
<span class="dot size-[7px] rounded-full bg-fg-dim shrink-0 transition-all duration-300"></span>
|
||||
<span id="status-text">连接中…</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="preview-section" class="shrink-0 pb-3">
|
||||
<div id="preview" class="preview-box bg-surface border border-edge rounded-card px-[18px] py-4 min-h-20 max-h-40 overflow-y-auto transition-all duration-300">
|
||||
<p id="preview-text" class="placeholder text-base leading-relaxed break-words">按住说话…</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="mic-section" class="flex flex-col items-center pt-5 pb-4 shrink-0 gap-3.5">
|
||||
<div class="mic-wrapper relative flex items-center justify-center touch-none">
|
||||
<button id="mic-btn" class="relative z-1 size-24 rounded-full border-2 border-edge text-fg-secondary flex items-center justify-center cursor-pointer select-none touch-none" type="button" disabled>
|
||||
<svg viewBox="0 0 24 24" width="48" height="48" fill="currentColor" aria-label="麦克风" role="img">
|
||||
<title>麦克风</title>
|
||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/>
|
||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="mic-rings absolute inset-0 pointer-events-none" aria-hidden="true">
|
||||
<span class="ring absolute inset-0 rounded-full border-[1.5px] border-accent opacity-0"></span>
|
||||
<span class="ring absolute inset-0 rounded-full border-[1.5px] border-accent opacity-0"></span>
|
||||
<span class="ring absolute inset-0 rounded-full border-[1.5px] border-accent opacity-0"></span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-fg-dim text-sm font-medium">按住说话</p>
|
||||
</section>
|
||||
|
||||
<section id="history-section" class="flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||
<div class="flex items-center justify-between pb-2.5 shrink-0">
|
||||
<h2 class="text-[13px] font-semibold text-fg-dim uppercase tracking-[0.06em]">历史记录</h2>
|
||||
<button id="clear-history" type="button" class="text-btn bg-transparent border-none text-fg-dim text-xs font-medium cursor-pointer px-2.5 py-1 rounded-lg transition-all duration-150">清空</button>
|
||||
</div>
|
||||
<ul id="history-list" class="list-none flex-1 overflow-y-auto"></ul>
|
||||
<p id="history-empty" class="text-fg-dim text-sm text-center py-10">暂无记录</p>
|
||||
</section>
|
||||
</div>
|
||||
<div id="toast" class="toast fixed left-1/2 z-[999] rounded-3xl text-[13px] font-medium text-fg border border-edge pointer-events-none opacity-0 px-[22px] py-2.5"></div>
|
||||
|
||||
<script type="module" src="app.ts"></script>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user