feat: 添加自定义模型路径配置功能
后端变更: - src/app/commands.rs: 新增 select_model_file 命令,使用 Tauri dialog 打开文件选择器 - src/app/mod.rs: 注册 select_model_file 命令到 invoke_handler 前端变更: - web/src/pages/SettingsPage.tsx: - 添加 modelPath 字段到 Settings 接口 - 添加 handleSelectModel 函数处理文件选择 - 添加模型路径配置 UI(输入框 + 选择按钮) - web/src/App.css: 添加.model-path-selector 样式 功能说明: - 用户可通过托盘菜单或设置页面选择自定义 ONNX 模型文件 - 支持 .onnx 扩展名过滤 - 模型路径保存在配置中,可选功能,留空时使用内置模型 - 设置保存后应用到 ASR 引擎
This commit is contained in:
parent
7ad06cc54a
commit
a4d6353f1a
@ -160,3 +160,26 @@ pub fn set_theme(theme: String, state: State<'_, AppState>) {
|
||||
let app_theme = AppTheme::from_str(&theme);
|
||||
state.set_theme(app_theme);
|
||||
}
|
||||
|
||||
/// 选择模型文件
|
||||
#[tauri::command]
|
||||
pub async fn select_model_file(app: tauri::AppHandle) -> Result<String, String> {
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
app.dialog()
|
||||
.file()
|
||||
.add_filter("ONNX Model", &["onnx"])
|
||||
.pick_file(move |file_path| {
|
||||
let result = match file_path {
|
||||
Some(path) => Ok(path.to_string_lossy().to_string()),
|
||||
None => Err("用户取消选择".to_string()),
|
||||
};
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
|
||||
rx.recv()
|
||||
.map_err(|e| e.to_string())
|
||||
.and_then(|r| r)
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ pub fn run() -> Result<()> {
|
||||
commands::clear_history,
|
||||
commands::get_theme,
|
||||
commands::set_theme,
|
||||
commands::select_model_file,
|
||||
])
|
||||
.setup(|app| {
|
||||
info!("[设置] 初始化应用插件...");
|
||||
|
||||
@ -299,3 +299,16 @@ body {
|
||||
background: var(--accent);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 模型路径选择器 */
|
||||
.model-path-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.model-path-selector .input {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ interface Settings {
|
||||
hotkeyRecord: string
|
||||
hotkeyCopy: string
|
||||
hotkeyToggle: string
|
||||
modelPath?: string
|
||||
}
|
||||
|
||||
interface SettingsPageProps {
|
||||
@ -20,6 +21,12 @@ interface SettingsPageProps {
|
||||
onThemeChange: (theme: 'light' | 'dark' | 'system') => void
|
||||
}
|
||||
|
||||
declare const window: Window & {
|
||||
__TAURI__: {
|
||||
invoke: (cmd: string, args?: Record<string, unknown>) => Promise<unknown>
|
||||
}
|
||||
}
|
||||
|
||||
export default function SettingsPage({ theme, onThemeChange }: SettingsPageProps) {
|
||||
const [settings, setSettings] = useState<Settings>({
|
||||
model: 'sensevoice-small',
|
||||
@ -63,11 +70,24 @@ export default function SettingsPage({ theme, onThemeChange }: SettingsPageProps
|
||||
historyKeepDays: 30,
|
||||
hotkeyRecord: 'Ctrl+Shift+R',
|
||||
hotkeyCopy: 'Ctrl+Shift+C',
|
||||
hotkeyToggle: 'Ctrl+Shift+H'
|
||||
hotkeyToggle: 'Ctrl+Shift+H',
|
||||
modelPath: undefined
|
||||
})
|
||||
setModified(true)
|
||||
}
|
||||
|
||||
const handleSelectModel = async () => {
|
||||
try {
|
||||
const modelPath = await window.__TAURI__.invoke<string>('select_model_file')
|
||||
if (modelPath) {
|
||||
setSettings(prev => ({ ...prev, modelPath }))
|
||||
setModified(true)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('选择模型文件失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="settings-page">
|
||||
<h1 className="page-title">设置</h1>
|
||||
@ -94,6 +114,27 @@ export default function SettingsPage({ theme, onThemeChange }: SettingsPageProps
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="setting-item">
|
||||
<div>
|
||||
<div className="setting-label">模型路径</div>
|
||||
<div className="setting-description">自定义模型文件路径(可选,留空使用内置模型)</div>
|
||||
</div>
|
||||
<div className="model-path-selector">
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
style={{ flex: 1, marginRight: '8px' }}
|
||||
value={settings.modelPath || ''}
|
||||
onChange={(e) => handleChange('modelPath', e.target.value)}
|
||||
placeholder="选择自定义模型文件..."
|
||||
readOnly
|
||||
/>
|
||||
<button className="btn btn-secondary" onClick={handleSelectModel}>
|
||||
选择文件
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="setting-item">
|
||||
<div>
|
||||
<div className="setting-label">识别语言</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user