From 8397b9d1a5c5c4fc78b215a161024d15a29fa99c Mon Sep 17 00:00:00 2001 From: baiqing Date: Wed, 6 May 2026 14:16:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(settings):=20=E5=90=88=E5=B9=B6=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=86=85=E5=A4=96=E4=B8=A4=E4=B8=AA"=E5=85=B3?= =?UTF-8?q?=E4=BA=8E"=20+=20=E4=B8=80=E9=94=AE=E5=AF=BC=E5=87=BA=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户反馈: 1. 设置侧栏内有"关于" tab,弹窗里也有一个"关于",重复入口让人混乱 2. 需要"一键导出错误日志"功能,让用户选择保存位置,跨 macOS/Windows ## 设置"关于"合并 (T2) - Settings.tsx 删 'about' 从 SettingsSectionId / SECTION_ORDER / 渲染分支 + 整个 AboutSection 函数(保留 settings.about i18n 块以防其他引用) - SettingsModal.tsx 的 AboutMini 接管所有内容: - 新增 source GitHub 链接(迁自 AboutSection) - 新增 QQ 群行(含 1078960553 群号 + 复制按钮,逻辑迁自 AboutSection) - 保留原有 docs / feedback / privacy / localFirst pill / AboutUpdateControl ## 一键导出错误日志 (T3) - Cargo.toml + package.json 引入 tauri-plugin-dialog(拿原生 save dialog) - lib.rs:plugin init + log_dir_path 改 pub fn 让 commands.rs 能调 - commands.rs:新增 export_error_log(target_path) — std::fs::copy log_dir_path()/openless.log → target_path(mac: ~/Library/Logs/OpenLess/openless.log,windows: %LOCALAPPDATA%\OpenLess\Logs\openless.log) - ipc.ts:exportErrorLog wrapper —— save dialog 选路径 → invoke 后端复制 - AboutMini 新增"导出错误日志"行:busy / ok / err 状态 + 实际保存路径 tooltip ## 5 个 locale 同步 modal.about 块统一新增 10 个 key(保证 typeof zhCN 类型完整不破坏编译): - source / qq / qqDesc / copyQq - exportErrorLog / exportErrorLogDesc / exportErrorLogBtn - exporting / exportSuccess / exportFailed zh-CN / zh-TW / en 用产品惯用术语;ja / ko 沿用 Beta 翻译风格。 ## 验证 - npx tsc --noEmit 通过(5 locale 类型一致) - vite build 通过 - cargo check 通过(plugin-dialog 与锁定的 tauri ~2.10 兼容) --- openless-all/app/package-lock.json | 16 +++- openless-all/app/package.json | 1 + openless-all/app/src-tauri/Cargo.lock | 91 ++++++++++++++++--- openless-all/app/src-tauri/Cargo.toml | 1 + openless-all/app/src-tauri/src/commands.rs | 14 +++ openless-all/app/src-tauri/src/lib.rs | 4 +- .../app/src/components/SettingsModal.tsx | 81 ++++++++++++++++- openless-all/app/src/i18n/en.ts | 10 ++ openless-all/app/src/i18n/ja.ts | 10 ++ openless-all/app/src/i18n/ko.ts | 10 ++ openless-all/app/src/i18n/zh-CN.ts | 10 ++ openless-all/app/src/i18n/zh-TW.ts | 10 ++ openless-all/app/src/lib/ipc.ts | 18 ++++ openless-all/app/src/pages/Settings.tsx | 65 +------------ 14 files changed, 262 insertions(+), 79 deletions(-) diff --git a/openless-all/app/package-lock.json b/openless-all/app/package-lock.json index 3e66f920..dc916113 100644 --- a/openless-all/app/package-lock.json +++ b/openless-all/app/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@tauri-apps/api": "^2.1.1", "@tauri-apps/plugin-autostart": "^2.5.1", + "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-shell": "^2.0.1", "@tauri-apps/plugin-updater": "^2.10.1", "i18next": "^26.0.8", @@ -1117,9 +1118,9 @@ ] }, "node_modules/@tauri-apps/api": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", - "integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz", + "integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==", "license": "Apache-2.0 OR MIT", "funding": { "type": "opencollective", @@ -1352,6 +1353,15 @@ "@tauri-apps/api": "^2.8.0" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz", + "integrity": "sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, "node_modules/@tauri-apps/plugin-shell": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz", diff --git a/openless-all/app/package.json b/openless-all/app/package.json index f7de3b17..6345d62b 100644 --- a/openless-all/app/package.json +++ b/openless-all/app/package.json @@ -13,6 +13,7 @@ "dependencies": { "@tauri-apps/api": "^2.1.1", "@tauri-apps/plugin-autostart": "^2.5.1", + "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-shell": "^2.0.1", "@tauri-apps/plugin-updater": "^2.10.1", "i18next": "^26.0.8", diff --git a/openless-all/app/src-tauri/Cargo.lock b/openless-all/app/src-tauri/Cargo.lock index 3e0ce8b7..d39cd7fa 100644 --- a/openless-all/app/src-tauri/Cargo.lock +++ b/openless-all/app/src-tauri/Cargo.lock @@ -99,7 +99,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -110,7 +110,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1168,7 +1168,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1409,7 +1409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2242,7 +2242,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.58.0", + "windows-core 0.61.2", ] [[package]] @@ -3445,6 +3445,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-autostart", + "tauri-plugin-dialog", "tauri-plugin-shell", "tauri-plugin-single-instance", "tauri-plugin-updater", @@ -3524,7 +3525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4439,6 +4440,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2 0.6.2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "ring" version = "0.17.14" @@ -4508,7 +4533,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4565,7 +4590,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5055,7 +5080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5448,6 +5473,48 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation 0.3.2", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", +] + [[package]] name = "tauri-plugin-shell" version = "2.3.5" @@ -5629,7 +5696,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6115,7 +6182,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6604,7 +6671,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/openless-all/app/src-tauri/Cargo.toml b/openless-all/app/src-tauri/Cargo.toml index c503da7a..dad98be2 100644 --- a/openless-all/app/src-tauri/Cargo.toml +++ b/openless-all/app/src-tauri/Cargo.toml @@ -24,6 +24,7 @@ tauri-plugin-shell = "2" tauri-plugin-updater = "2" tauri-plugin-single-instance = "2" tauri-plugin-autostart = "2" +tauri-plugin-dialog = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] } diff --git a/openless-all/app/src-tauri/src/commands.rs b/openless-all/app/src-tauri/src/commands.rs index c9b7fad9..93cc6cdd 100644 --- a/openless-all/app/src-tauri/src/commands.rs +++ b/openless-all/app/src-tauri/src/commands.rs @@ -886,6 +886,20 @@ pub fn local_asr_set_keep_loaded_secs( coord.prefs().set(prefs).map_err(|e| e.to_string()) } +/// 把当前会话的 openless.log 复制到用户选择的位置(前端用 plugin-dialog 拿 target_path)。 +/// 路径来自 lib::log_dir_path() —— mac: ~/Library/Logs/OpenLess/openless.log, +/// windows: %LOCALAPPDATA%\OpenLess\Logs\openless.log。 +#[tauri::command] +pub fn export_error_log(target_path: String) -> Result<(), String> { + let src = crate::log_dir_path().join("openless.log"); + if !src.exists() { + return Err(format!("日志文件不存在:{}", src.display())); + } + std::fs::copy(&src, std::path::Path::new(&target_path)) + .map(|_| ()) + .map_err(|e| format!("复制日志失败:{}", e)) +} + // ─────────────────────────── unused but exported (silences dead_code) ─────────────────────────── #[allow(dead_code)] diff --git a/openless-all/app/src-tauri/src/lib.rs b/openless-all/app/src-tauri/src/lib.rs index fd18b20b..95afe7d0 100644 --- a/openless-all/app/src-tauri/src/lib.rs +++ b/openless-all/app/src-tauri/src/lib.rs @@ -62,6 +62,7 @@ pub fn run() { })) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_dialog::init()) // 跨平台开机自启:mac 写 LaunchAgent plist,linux 写 ~/.config/autostart/*.desktop, // windows 写 HKCU\Software\Microsoft\Windows\CurrentVersion\Run。前端 toggle 直接 // 调插件 isEnabled / enable / disable,不维持本地 prefs,让 OS 当唯一真相。issue #194。 @@ -244,6 +245,7 @@ pub fn run() { commands::local_asr_release_engine, commands::local_asr_preload, commands::local_asr_set_keep_loaded_secs, + commands::export_error_log, restart_app, ]) .build(tauri::generate_context!()) @@ -420,7 +422,7 @@ fn init_file_logger() { let _ = CombinedLogger::init(loggers); } -fn log_dir_path() -> std::path::PathBuf { +pub fn log_dir_path() -> std::path::PathBuf { #[cfg(target_os = "macos")] { if let Ok(home) = std::env::var("HOME") { diff --git a/openless-all/app/src/components/SettingsModal.tsx b/openless-all/app/src/components/SettingsModal.tsx index b3295cd1..bfaa23de 100644 --- a/openless-all/app/src/components/SettingsModal.tsx +++ b/openless-all/app/src/components/SettingsModal.tsx @@ -4,13 +4,13 @@ // 开机自启)已从此弹窗移除,避免 "看似可点实际无效" 的负面体感。 // 待 backend 就位后再补回(参见 issue #69)。 -import { useEffect, useState, type CSSProperties } from 'react'; +import { useEffect, useRef, useState, type CSSProperties } from 'react'; import { useTranslation } from 'react-i18next'; import { Icon } from './Icon'; import { AboutUpdateControl, Settings as SettingsContent, type SettingsSectionId } from '../pages/Settings'; import { Row } from './ui/Row'; import { readFontScale, setFontScale, type FontScaleId } from '../lib/fontScale'; -import { openExternal } from '../lib/ipc'; +import { exportErrorLog, openExternal } from '../lib/ipc'; import { FOLLOW_SYSTEM, getLocalePreference, @@ -270,6 +270,41 @@ function PersonalizeSection() { function AboutMini() { const { t } = useTranslation(); + const [qqCopied, setQqCopied] = useState(false); + const qqCopiedRef = useRef(null); + const [exportStatus, setExportStatus] = useState<'idle' | 'busy' | 'ok' | 'err'>('idle'); + const [exportMessage, setExportMessage] = useState(''); + + useEffect(() => () => { + if (qqCopiedRef.current) clearTimeout(qqCopiedRef.current); + }, []); + + const copyQq = () => { + navigator.clipboard?.writeText('1078960553'); + setQqCopied(true); + if (qqCopiedRef.current) clearTimeout(qqCopiedRef.current); + qqCopiedRef.current = window.setTimeout(() => setQqCopied(false), 1500); + }; + + const onExportLog = async () => { + setExportStatus('busy'); + setExportMessage(''); + try { + const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + const target = await exportErrorLog(`openless-${ts}.log`); + if (target == null) { + setExportStatus('idle'); + return; + } + setExportStatus('ok'); + setExportMessage(target); + window.setTimeout(() => setExportStatus('idle'), 4000); + } catch (err) { + setExportStatus('err'); + setExportMessage(err instanceof Error ? err.message : String(err)); + } + }; + return (
@@ -279,6 +314,14 @@ function AboutMini() {
+ + + + {qqCopied && {t('common.copied')}} + + + +
+ + {exportStatus === 'ok' && ( + + {t('modal.about.exportSuccess')} + + )} + {exportStatus === 'err' && ( + + {t('modal.about.exportFailed')} + + )} +
+
{t('modal.about.localFirst')} diff --git a/openless-all/app/src/i18n/en.ts b/openless-all/app/src/i18n/en.ts index a262c4ce..c55ee618 100644 --- a/openless-all/app/src/i18n/en.ts +++ b/openless-all/app/src/i18n/en.ts @@ -495,6 +495,16 @@ export const en: typeof zhCN = { docsBtn: 'openless.app/docs ↗', feedback: 'Feedback channel', feedbackBtn: 'GitHub Issues ↗', + source: 'Source', + qq: 'Community QQ Group', + qqDesc: 'Search the group number on QQ to join, or scan the QR code.', + copyQq: 'Copy group number', + exportErrorLog: 'Export error log', + exportErrorLogDesc: 'Save the current session log to disk for debugging or sending us feedback.', + exportErrorLogBtn: 'Export', + exporting: 'Exporting…', + exportSuccess: 'Saved', + exportFailed: 'Export failed', privacy: 'Privacy', privacyDesc: 'All transcripts stay on this device. Cloud APIs are used only for real-time calls.', localFirst: 'Local-first', diff --git a/openless-all/app/src/i18n/ja.ts b/openless-all/app/src/i18n/ja.ts index 17dcc908..c98989f6 100644 --- a/openless-all/app/src/i18n/ja.ts +++ b/openless-all/app/src/i18n/ja.ts @@ -497,6 +497,16 @@ export const ja: typeof zhCN = { docsBtn: 'openless.app/docs ↗', feedback: 'フィードバックチャネル', feedbackBtn: 'GitHub Issues ↗', + source: 'ソース', + qq: 'コミュニティ QQ グループ', + qqDesc: 'QQ でグループ番号を検索して参加するか、QR コードをスキャンしてください。', + copyQq: 'グループ番号をコピー', + exportErrorLog: 'エラーログをエクスポート', + exportErrorLogDesc: '現在のセッションの実行ログをローカルに保存。問題の調査やフィードバック送付にお使いください。', + exportErrorLogBtn: 'エクスポート', + exporting: 'エクスポート中…', + exportSuccess: '保存しました', + exportFailed: 'エクスポート失敗', privacy: 'プライバシー', privacyDesc: 'すべての認識結果はローカルにのみ保存されます。クラウド API はリアルタイム呼び出しにのみ使用。', localFirst: 'ローカル優先', diff --git a/openless-all/app/src/i18n/ko.ts b/openless-all/app/src/i18n/ko.ts index e669e07a..ac669fc1 100644 --- a/openless-all/app/src/i18n/ko.ts +++ b/openless-all/app/src/i18n/ko.ts @@ -497,6 +497,16 @@ export const ko: typeof zhCN = { docsBtn: 'openless.app/docs ↗', feedback: '피드백 채널', feedbackBtn: 'GitHub Issues ↗', + source: '소스', + qq: '커뮤니티 QQ 그룹', + qqDesc: 'QQ 에서 그룹 번호를 검색해 가입하거나 QR 코드로 입장하세요.', + copyQq: '그룹 번호 복사', + exportErrorLog: '오류 로그 내보내기', + exportErrorLogDesc: '현재 세션의 실행 로그를 로컬에 저장합니다. 문제 조사나 피드백 전송에 사용하세요.', + exportErrorLogBtn: '내보내기', + exporting: '내보내는 중…', + exportSuccess: '저장됨', + exportFailed: '내보내기 실패', privacy: '프라이버시', privacyDesc: '모든 인식 결과는 로컬에만 저장됩니다. 클라우드 API 는 실시간 호출에만 사용됩니다.', localFirst: '로컬 우선', diff --git a/openless-all/app/src/i18n/zh-CN.ts b/openless-all/app/src/i18n/zh-CN.ts index 041c46e6..ac6798ec 100644 --- a/openless-all/app/src/i18n/zh-CN.ts +++ b/openless-all/app/src/i18n/zh-CN.ts @@ -493,6 +493,16 @@ export const zhCN = { docsBtn: 'openless.app/docs ↗', feedback: '反馈渠道', feedbackBtn: 'GitHub Issues ↗', + source: '源码', + qq: '社区 QQ 群', + qqDesc: '使用 QQ 搜索群号加入,或扫码进群。', + copyQq: '复制群号', + exportErrorLog: '导出错误日志', + exportErrorLogDesc: '把当前会话的运行日志保存到本地,便于排查问题或反馈给我们。', + exportErrorLogBtn: '导出', + exporting: '导出中…', + exportSuccess: '已保存', + exportFailed: '导出失败', privacy: '隐私', privacyDesc: '所有识别结果只保存在本机,云端 API 仅用于实时调用。', localFirst: '本地优先', diff --git a/openless-all/app/src/i18n/zh-TW.ts b/openless-all/app/src/i18n/zh-TW.ts index 794a2095..5491af92 100644 --- a/openless-all/app/src/i18n/zh-TW.ts +++ b/openless-all/app/src/i18n/zh-TW.ts @@ -495,6 +495,16 @@ export const zhTW: typeof zhCN = { docsBtn: 'openless.app/docs ↗', feedback: '反饋渠道', feedbackBtn: 'GitHub Issues ↗', + source: '原始碼', + qq: '社群 QQ 群', + qqDesc: '使用 QQ 搜尋群號加入,或掃碼進群。', + copyQq: '複製群號', + exportErrorLog: '匯出錯誤日誌', + exportErrorLogDesc: '把當前會話的執行日誌儲存到本地,便於排查問題或反饋給我們。', + exportErrorLogBtn: '匯出', + exporting: '匯出中…', + exportSuccess: '已儲存', + exportFailed: '匯出失敗', privacy: '隱私', privacyDesc: '所有識別結果只保存在本機,雲端 API 僅用於實時調用。', localFirst: '本地優先', diff --git a/openless-all/app/src/lib/ipc.ts b/openless-all/app/src/lib/ipc.ts index 146691eb..77823fd5 100644 --- a/openless-all/app/src/lib/ipc.ts +++ b/openless-all/app/src/lib/ipc.ts @@ -320,4 +320,22 @@ export async function openExternal(url: string): Promise { await open(url); } +/** + * 让用户选 save 路径并把当前会话日志(openless.log)复制过去。 + * 浏览器开发模式下走 mock 不实际写盘。返回最终 save 的绝对路径,取消选择则返回 null。 + */ +export async function exportErrorLog(suggestedFileName: string): Promise { + if (!isTauri) { + return `~/Downloads/${suggestedFileName}`; + } + const { save } = await import('@tauri-apps/plugin-dialog'); + const target = await save({ + defaultPath: suggestedFileName, + filters: [{ name: 'Log', extensions: ['log', 'txt'] }], + }); + if (!target) return null; + await invokeOrMock('export_error_log', { targetPath: target }, () => undefined); + return target; +} + export { isTauri }; diff --git a/openless-all/app/src/pages/Settings.tsx b/openless-all/app/src/pages/Settings.tsx index 2fdd0a82..f7aa64c2 100644 --- a/openless-all/app/src/pages/Settings.tsx +++ b/openless-all/app/src/pages/Settings.tsx @@ -60,9 +60,10 @@ interface SettingsProps { initialSection?: SettingsSectionId; } -export type SettingsSectionId = 'recording' | 'providers' | 'shortcuts' | 'permissions' | 'language' | 'about'; +// "关于" tab 已移除(内容并入外层 SettingsModal 的 About 页,避免设置内外重复入口)。 +export type SettingsSectionId = 'recording' | 'providers' | 'shortcuts' | 'permissions' | 'language'; -const SECTION_ORDER: SettingsSectionId[] = ['recording', 'providers', 'shortcuts', 'permissions', 'language', 'about']; +const SECTION_ORDER: SettingsSectionId[] = ['recording', 'providers', 'shortcuts', 'permissions', 'language']; async function autostartIsEnabled(): Promise { const { invoke } = await import('@tauri-apps/api/core'); @@ -140,7 +141,6 @@ export function Settings({ embedded = false, initialSection = 'recording' }: Set {section === 'shortcuts' && } {section === 'permissions' && } {section === 'language' && } - {section === 'about' && } @@ -1144,64 +1144,7 @@ function LanguageSection() { ); } -function AboutSection() { - const { t } = useTranslation(); - const [qqCopied, setQqCopied] = useState(false); - const qqCopiedRef = useRef(null); - - useEffect(() => { - return () => { - if (qqCopiedRef.current) clearTimeout(qqCopiedRef.current); - }; - }, []); - - const copyQq = () => { - navigator.clipboard?.writeText('1078960553'); - setQqCopied(true); - if (qqCopiedRef.current) clearTimeout(qqCopiedRef.current); - qqCopiedRef.current = window.setTimeout(() => setQqCopied(false), 1500); - }; - - return ( - -
-
OL
-
-
OpenLess
- -
-
- openExternal('https://github.com/appergb/openless')}>GitHub - openExternal('https://github.com/appergb/openless#readme')}>README - openExternal('https://github.com/appergb/openless/issues')}>GitHub Issues - -
- 1078960553 - - {qqCopied && {t('common.copied')}} -
-
- - {t('settings.about.localFirst')} - -
- ); -} +// AboutSection 已移除:内容并入 SettingsModal 的 AboutMini,避免设置内外两个"关于"重复入口。 export function AboutUpdateControl({ tagline }: { tagline: string }) { const { t } = useTranslation();