// 识别浮层窗口:深色玻璃、不抢焦点;状态完全由 Rust 事件驱动。 // listening / text 两态 + 错误态文案,规格见 doc/frontend-design.html 5.1/5.2 import { useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; import '@dudu/design/styles.css'; import { RecognitionOverlay } from '@dudu/design/components/voice/RecognitionOverlay'; import { listen } from '../shared/tauri'; const ERR_TEXT = { QUOTA_EXCEEDED: '今日试用已用完,去购买时长', RATE_LIMITED: '操作过于频繁,稍后再试', SESSION_LIMIT: '单次最长 3 分钟,松开后可继续', ASR_UNAVAILABLE: '识别服务暂不可用,稍后再试', NETWORK: '连接中断,松开重试', NO_ACCESSIBILITY: '需要辅助功能权限才能上屏,去系统设置开启 dudu', }; function OverlayApp() { const [finalText, setFinalText] = useState(''); const [partialText, setPartialText] = useState(''); const [error, setError] = useState(''); useEffect(() => { const offs = []; listen('hotkey', ({ payload }) => { if (payload.state === 'down') { setFinalText(''); setPartialText(''); setError(''); } }).then((off) => offs.push(off)); listen('asr', ({ payload }) => { if (payload.type === 'partial') setPartialText(payload.text); else if (payload.type === 'final') { setFinalText((f) => f + payload.text); setPartialText(''); } else if (payload.type === 'error') setError(ERR_TEXT[payload.code] || payload.message || ''); }).then((off) => offs.push(off)); return () => offs.forEach((off) => off()); }, []); const hasText = finalText || partialText; return (
{error ? ( ) : ( )}
); } createRoot(document.getElementById('root')).render();