diff --git a/web/src/components/HistoryList.tsx b/web/src/components/HistoryList.tsx index 08a67e3..e03be4c 100644 --- a/web/src/components/HistoryList.tsx +++ b/web/src/components/HistoryList.tsx @@ -8,19 +8,19 @@ function formatTime(ts: number): string { } interface HistoryListProps { - sendJSON: (obj: Record) => void; + sendPaste: (text: string) => void; } -export function HistoryList({ sendJSON }: HistoryListProps) { +export function HistoryList({ sendPaste }: HistoryListProps) { const history = useAppStore((s) => s.history); const clearHistory = useAppStore((s) => s.clearHistory); const handleItemClick = useCallback( (text: string) => { - sendJSON({ type: "paste", text }); + sendPaste(text); toast.info("发送粘贴…"); }, - [sendJSON], + [sendPaste], ); return ( diff --git a/web/src/components/MicButton.tsx b/web/src/components/MicButton.tsx index 0edb2a8..4546aa5 100644 --- a/web/src/components/MicButton.tsx +++ b/web/src/components/MicButton.tsx @@ -8,9 +8,13 @@ interface MicButtonProps { export function MicButton({ onStart, onStop }: MicButtonProps) { const connected = useAppStore((s) => s.connectionStatus === "connected"); + const micReady = useAppStore((s) => s.micReady); const recording = useAppStore((s) => s.recording); const pendingStart = useAppStore((s) => s.pendingStart); - const isActive = recording || pendingStart; + const stopping = useAppStore((s) => s.stopping); + const weakNetwork = useAppStore((s) => s.weakNetwork); + const isActive = recording || pendingStart || stopping; + const disabled = !connected || !micReady || stopping; const handlePointerDown = useCallback( (e: React.PointerEvent) => { @@ -46,6 +50,8 @@ export function MicButton({ onStart, onStop }: MicButtonProps) { "cursor-not-allowed border-edge bg-linear-to-br from-surface-hover to-surface text-fg-secondary opacity-30 shadow-[0_2px_12px_rgba(0,0,0,0.3),inset_0_1px_0_rgba(255,255,255,0.04)]"; const activeClasses = "animate-mic-breathe scale-[1.06] border-accent-hover bg-accent text-white shadow-[0_0_32px_rgba(99,102,241,0.35),0_0_80px_rgba(99,102,241,0.2)]"; + const weakClasses = + "border-amber-400 bg-linear-to-br from-amber-400/15 to-surface text-amber-200 shadow-[0_0_22px_rgba(251,191,36,0.28)]"; const idleClasses = "border-edge bg-linear-to-br from-surface-hover to-surface text-fg-secondary shadow-[0_2px_12px_rgba(0,0,0,0.3),inset_0_1px_0_rgba(255,255,255,0.04)]"; @@ -54,13 +60,15 @@ export function MicButton({ onStart, onStop }: MicButtonProps) {
-

{"按住说话"}

+

+ {!micReady + ? "请先准备麦克风" + : !connected + ? "连接中断,等待重连" + : stopping + ? "收尾中…" + : weakNetwork + ? "网络波动,已启用缓冲" + : "按住说话"} +

); } diff --git a/web/src/components/PreviewBox.tsx b/web/src/components/PreviewBox.tsx index 9dfb7d0..f1de4ea 100644 --- a/web/src/components/PreviewBox.tsx +++ b/web/src/components/PreviewBox.tsx @@ -3,7 +3,11 @@ import { useAppStore } from "../stores/app-store"; export function PreviewBox() { const text = useAppStore((s) => s.previewText); const active = useAppStore((s) => s.previewActive); + const weakNetwork = useAppStore((s) => s.weakNetwork); + const recording = useAppStore((s) => s.recording); const hasText = text.length > 0; + const placeholder = + weakNetwork && recording ? "网络波动中,音频缓冲后发送…" : "按住说话…"; return (
@@ -17,7 +21,7 @@ export function PreviewBox() {

- {hasText ? text : "按住说话…"} + {hasText ? text : placeholder}

diff --git a/web/src/components/StatusBadge.tsx b/web/src/components/StatusBadge.tsx index 3260792..81315a5 100644 --- a/web/src/components/StatusBadge.tsx +++ b/web/src/components/StatusBadge.tsx @@ -20,7 +20,12 @@ const statusConfig = { export function StatusBadge() { const status = useAppStore((s) => s.connectionStatus); - const { text, dotClass, borderClass } = statusConfig[status]; + const weakNetwork = useAppStore((s) => s.weakNetwork); + const { dotClass, borderClass } = statusConfig[status]; + const text = + status === "connected" && weakNetwork + ? "网络波动" + : statusConfig[status].text; return (