Commit Graph
533 Commits
Author SHA1 Message Date
millionart 4fa34b486e merge upstream main into windows local asr 2026-05-06 20:55:25 +08:00
millionart 018cacad4f fix: skip Foundry transcription timeout 2026-05-06 20:48:26 +08:00
TRIP 095e3127bb Merge pull request #277 from H-Chris233/fix/issue-230-credential-storage-model
对齐凭据存储策略与威胁模型
2026-05-06 20:27:13 +08:00
TRIP c0118ee481 Merge pull request #290 from appergb/chore/skip-macos-13-runner
chore(ci): 暂时搁置 macos-13 (Intel mac) 构建 — GH runner pool 卡死
2026-05-06 20:27:08 +08:00
TRIP 08b5b882f1 Merge pull request #291 from appergb/fix/startup-splash-transparent
fix(startup): splash 透明背景 + 卡片化,避免长启动时左半白屏
2026-05-06 20:27:03 +08:00
baiqing 0505c2fc54 fix(vault): keyring chunks 用稳定名 + 不再每次 load 都删 legacy
用户实测:首次允许 Keychain 后仍反复弹窗十几次。

根因:
1. save_credentials 每次都生成新 UUID 作为 chunk account 命名前缀
   (credentials.v1.chunk.<NEW_UUID>.0/.1)。macOS Keychain 的「始终允许」
   绑定到 (binary 签名, entry 名),每次保存设置都让 chunk entry 名变 →
   之前 ACL 全失效 → 重新弹。
2. load_credentials / load_credentials_for_update 每次都调
   remove_legacy_keyring_credentials() —— 对 9 个旧 stable account
   各做一次 keyring delete。delete 在 macOS 上仍触发 ACL 检查,
   每次 load 都弹 9 次「OpenLess 想删除 X」。

合计每次 IPC 触发的 load 12 次弹窗,跟实测吻合。

修复(数据安全优先):

A. chunk account 命名稳定化
   - chunk_account(generation: Option<&str>, idx) 接受可选 generation
   - save_credentials 总用 None(稳定 credentials.v1.chunk.{idx})
   - 新 manifest 不写 generation 字段(serde skip_serializing_if=None)
   - 旧 manifest 仍可读(generation: Option<String>,含 UUID 时按旧格式
     读)→ 一次 save 后无缝迁移到新格式 + 删除旧 UUID chunks

B. partial-write 安全
   - 仍保留「先写所有 chunks,再写 manifest」顺序
   - chunks 数量减少时主动删除 idx >= new count 的旧 entries
   - 旧 UUID chunks 保留删除路径(迁移用)

C. legacy keyring delete 仅在 migration 路径触发
   - load_credentials / load_credentials_for_update 不再调用
     remove_legacy_keyring_credentials()
   - migrate_legacy_sources_for_update 内部仍调用一次 → 首次成功迁移
     就清干净;之后 load 不再尝试 delete 不存在的 entries
   - legacy 文件 delete 不需 ACL,best-effort 调用保留

D. 不影响 Windows
   - Windows Credential Manager 没有 ACL 弹窗概念
   - 仍走 chunked storage(2560 byte 限制需要分块)

修复后体感:
- 首次启动迁移:~3 次弹(manifest + 2 chunks),用户点 Always Allow
- 之后启动 / 保存设置:0 次弹(ACL 长期有效)
2026-05-06 20:18:02 +08:00
millionart 2a03f15292 Merge remote-tracking branch 'upstream/main' into codex/windows-local-asr
# Conflicts:
#	openless-all/app/src-tauri/Cargo.lock
#	openless-all/app/src-tauri/src/commands.rs
#	openless-all/app/src-tauri/src/coordinator.rs
#	openless-all/app/src-tauri/src/lib.rs
2026-05-06 20:15:12 +08:00
baiqing fa30161f52 fix(startup): splash 透明背景 + 卡片化,避免长启动时左半白屏
用户反馈:当启动时间较长时(凭据迁移 / 权限 probe / hotkey hook 还没
ready),窗口左侧出现白色矩形并显示「OpenLess 正在启动」,本应是纯
磨砂玻璃背景。

根因:main window 在 tauri.conf.json 是 transparent + macOSPrivateApi
(NSVisualEffectView vibrancy)。但 StartupShell 之前用:

    background: linear-gradient(180deg, rgba(245,245,247,0.96), ...)

96% 不透明渐变盖过 vibrancy。在窗口内容布局首帧或啥的时候,左侧 paint
StartupShell 浅灰,右侧未完成 vibrancy → 视觉割裂「左白右磨砂」。

修:StartupShell 容器改 background: transparent 让 vibrancy 全屏展开;
提示文字 + icon 用一个圆角 pill 卡片承载(rgba(255,255,255,0.55) +
backdrop-filter blur),跟 Capsule 视觉一致。
2026-05-06 20:05:46 +08:00
millionart 2d07493d40 feat: improve Foundry local ASR setup 2026-05-06 20:02:28 +08:00
baiqing b2868378bf chore(ci): 暂时搁置 macos-13 (Intel mac) 构建
GitHub Actions 把 macos-13 runner 标记为 deprecated,pool 容量紧张到
每次 dispatch 都 queue 1-2h 拿不到分配。已观测 4 次 release run
(25415755417 / 25417633813 / 25419948896 / 25420193625)均因 mac-13
卡 queue 整个 run conclusion 永远不变 success,阻塞自动化判断。

暂时把 macos-13 matrix 项注释掉:
- macOS arm64 (Apple Silicon) 仍发 dmg
- Windows x64 + Linux 不变
- Intel mac 用户在 v1.2.21+ 期间用 Apple Silicon dmg + Rosetta(性能稍降但可用)

恢复条件(任一):
- GH 把 macos-13 pool 容量恢复
- GH 提供 macos-14-x86_64 或 macos-15-large-x86_64 替代 runner
- 项目 self-host runner

恢复方法:删除注释符即可。
2026-05-06 19:49:45 +08:00
TRIP ab65432c1c Merge pull request #289 from appergb/fix/recording-no-swallow-first-words
fix(recording): 录音条出现时 mic 已 capture,不再吞开头字
2026-05-06 19:21:13 +08:00
TRIP 96ca96609f Merge pull request #287 from appergb/fix/windows-ime-static-crt
fix(windows-ime): 静态链接 CRT 防 host 进程 MSVCP140 劫持(修 QQ 切微软拼音崩溃)
2026-05-06 19:21:09 +08:00
baiqing 5c5b5758b6 fix(recording): emit Recording 加 race 检查,避免覆盖 stop/cancel
Codex P2 (PR #289):上一版我把 emit_capsule(Recording) 放到
stop_recorder_if_pending_start_stop 之后,且无条件 emit。这会让短按热键
(用户在 Recorder::start 仍在 cpal init 期间松开)的场景出现 UI 闪烁:
stop / cancel 路径已经发出 Transcribing / Cancelled,本行又把 UI 覆盖
回 Recording,并在 Volcengine 路径的 open_session().await 期间持续保持。

修复(按 Codex 建议):
1. emit 挪到 stop_recorder_if_pending_start_stop 之前
2. emit 之前在同一 lock 内检查 phase / pending_stop / cancelled / session_id;
   只有 recorder 真的会继续运行时才 emit
3. 不满足条件就让 stop / cancel 路径接管 UI 信号

不改变首次说话不丢字的修复目标(emit 仍在 Recorder::start 成功后),
只让 race 路径下的 UI 一致。
2026-05-06 19:13:50 +08:00
baiqing 02dd8c56ea fix(recording): 录音条出现时 mic 已 capture,不再吞开头字
用户反馈:按 Option 键后看到录音条,但开口的前几个字被吞掉。

根因:begin_session() 在 line 1128 立刻 emit_capsule(Recording),但
Recorder::start 还要再走 ~57 行(read creds / new ASR / new bridge /
acquire mute / cpal init),其中 cpal init 在 macOS 上有 50-200ms 启动
延迟。窗口期内:
- 视觉:录音条已显示
- 物理:mic 还没产生第一帧 PCM
- 用户开口的前几个字物理上无法被 capture(不是丢字,是没录到)

DeferredAsrBridge 已经能覆盖 "recorder 已开 → ASR 没连上" 的间隙(缓存
PCM 等 ASR attach 后按顺序后送),但 "录音条已显 → recorder 没开" 这段
之前没人覆盖。

修复(surgical):把 emit_capsule(Recording) 从 begin_session() 挪到
start_recorder_for_starting() 内部 Recorder::start 成功的 Ok 分支里。
三条 ASR 路径(Local Qwen3 / Whisper / Volcengine)共用同一函数,自动
受益。代价是用户按热键到看到录音条多 50-200ms 延迟,但「录音条出现 =
mic 100% 在 capture」体感诚实,开头不再丢。

ASR 慢连接的兜底逻辑(DeferredAsrBridge 按顺序缓冲 + attach 后 drain)
保持不变。
2026-05-06 19:09:32 +08:00
baiqing 901e9dfe3f fix(windows-ime): 移除 DisableThreadLibraryCalls — /MT 下需 thread 通知
Codex P1 (PR #287):DLL 改用 /MT 静态链接 CRT 后,仍调
DisableThreadLibraryCalls(instance) 会让静态 CRT 拿不到 DLL_THREAD_DETACH
通知,per-thread TLS 初始化/清理断裂 — 在 host 进程(QQ / Office /
微软拼音切换)创建新 input thread 时反而把这次想修的崩溃问题重新引回。

Microsoft 文档 DisableThreadLibraryCalls 备注明确:使用 static CRT 的
DLL 不应禁用 thread library calls。

修复:删除该调用;保留 g_module 缓存。
2026-05-06 19:07:54 +08:00
millionart f03499576e fix: show Foundry ASR option on Windows 2026-05-06 18:49:23 +08:00
millionart 05f4fce895 fix: gate Foundry local ASR to Windows 2026-05-06 18:34:11 +08:00
millionart 19977bc52e test(windows): add local ASR smoke mode 2026-05-06 18:14:21 +08:00
baiqing 80de07851c fix(windows-ime): 静态链接 CRT 防 host 进程 MSVCP140 劫持
用户反馈:在 Windows 11 切到微软拼音时 QQ (QQEX.exe v9.9.26.44725) 崩
0xc0000005,故障模块 D:\program\QQ\versions\<ver>\resources\app\
MSVCP140.dll @ 偏移 0x13080。

根因(DLL Hell 经典场景):
1. QQ 把私有版本的 MSVCP140.dll 放在 app/ 目录,DLL 搜索顺序优先于系统
2. ctfmon 切微软拼音 → 把所有已注册 TSF DLL 加载进 QQ 进程
3. 我们 OpenLessIme.dll 此前用 MSBuild 默认 RuntimeLibrary(=
   MultiThreadedDLL,动态链接 MSVCP140.dll/VCRUNTIME140.dll/UCRT)
4. 在 QQ 进程内被绑给 QQ 自带的旧版 MSVCP140 → 调 std::wstring /
   std::thread / std::mutex 等跑的是 QQ 那份 → ABI 行为漂移 → AV

修复:
- vcxproj 4 处 ItemDefinitionGroup(Debug/Release × Win32/x64)显式声明
  <RuntimeLibrary>MultiThreaded</RuntimeLibrary>(Release)/
  <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>(Debug)。
- DLL 自带 CRT 副本,QQ 私有 MSVCP140 劫持不到。
- DLL 体积 +~300 KB;DLL 内部 STL 不能跨边界共享,但我们 IPC 走 named
  pipe + JSON(C ABI 友好),不受影响。

不动的部分:
- TSF 接口本身已是 COM C ABI(vtable + COM-typed 参数),无需改
- 重量级 ASR/LLM/polish 早已在 OpenLess.exe 主进程,DLL 只做 TSF text
  service hooks + IPC(已合规"组件极简,IPC 通信"原则)

QQ 客观脆弱性已整理详细报告(桌面 PDF/DOCX + GitHub issue),用于上报
TSRC:app/ 目录下 MSVCP140 劫持系统 CRT、版本号目录嵌套混乱、无 IME
崩溃隔离机制、CRT 漂移。
2026-05-06 18:08:21 +08:00
millionart a2f56840e5 feat(ui): add Foundry local ASR model controls 2026-05-06 17:55:44 +08:00
millionart 71240e2c3d feat(ui): add Foundry local ASR provider 2026-05-06 17:28:52 +08:00
millionart 05e45b41fb feat(asr): route dictation through Foundry local Whisper 2026-05-06 17:02:16 +08:00
millionart 702e09405e feat(asr): expose Foundry local ASR status 2026-05-06 16:43:47 +08:00
millionart 28ffe03cbf feat(asr): add Foundry local Whisper provider 2026-05-06 16:16:08 +08:00
H-Chris233 1d04131bed Preserve strict credential gate for real regression mode
Real regression runs that explicitly pass -RequireCredentials should still fail fast when credentials cannot be confirmed. This keeps the real-ASR signal strict while leaving the default runtime smoke suite decoupled from legacy credentials.json.\n\nConstraint: default smoke suite must not depend on migrated plaintext JSON.\nRejected: Keep warning-only behavior in real regression mode | allows credential-missing runs to appear as valid real-ASR regressions.\nConfidence: high\nScope-risk: narrow\nDirective: Keep -RequireCredentials in windows-real-regression as a hard gate unless credential-vault probing is added.\nTested: git diff --check\nNot-tested: Running Windows PowerShell regression scripts in Linux environment.\nRelated: #230
2026-05-06 15:49:28 +08:00
millionart 89e48ba3b8 feat(asr): add Foundry local runtime wrapper 2026-05-06 15:43:22 +08:00
H-Chris233 c06db84d3e Keep Windows smoke gates compatible with vault storage
Windows smoke scripts should no longer fail solely because credentials.json was migrated into the OS credential vault. The default suite now runs runtime startup without the obsolete JSON gate, and explicit legacy JSON checks warn instead of blocking vault-backed test machines.\n\nConstraint: Provider credentials now live in the OS credential vault after migration.\nRejected: Query Windows Credential Manager from smoke scripts | larger platform-specific parsing surface for a smoke gate that only needs to avoid the obsolete JSON blocker.\nConfidence: high\nScope-risk: narrow\nDirective: Do not make Windows smoke defaults depend on migrated plaintext credentials.json.\nTested: git diff --check\nTested: Python sanity check for conflict markers in edited PowerShell scripts\nNot-tested: PowerShell parser unavailable in this Linux environment.\nRelated: #230
2026-05-06 15:21:29 +08:00
millionart e7b1c7f558 feat(asr): add Foundry local provider defaults 2026-05-06 15:01:01 +08:00
H-Chris233 deb11960d3 Keep vault updates independent of obsolete JSON cleanup
Credential writes should not fail only because an old migration source cannot be deleted. Once current vault data is readable and writable, legacy plaintext cleanup remains best-effort and logged so users can still save or remove credentials even if credentials.json is locked or read-only.\n\nThe rebase also brought the new Japanese and Korean locale files onto this branch, so the credential storage notice key is filled there to keep the frontend type contract intact.\n\nConstraint: credentials.json is obsolete migration data after vault write.\nRejected: Propagate legacy JSON deletion failures on every vault save | blocks credential updates despite a valid vault.\nConfidence: high\nScope-risk: narrow\nDirective: Do not make credential updates depend on deleting legacy plaintext files.\nTested: cargo check --manifest-path src-tauri/Cargo.toml\nTested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit\nTested: npm run -s build\nTested: git diff --check\nNot-tested: Manual locked/read-only credentials.json cleanup.\nRelated: #230
2026-05-06 14:40:49 +08:00
H-Chris233 c1be1ddd55 Clean stale vault entries after JSON migration
When the current legacy credentials.json source wins migration, older per-account vault entries should not remain as stale fallback data. After the JSON payload is successfully written to the chunked vault and plaintext cleanup succeeds, the migration now removes those older per-account entries as well.

Constraint: credentials.json is the previous Tauri source of truth and must win over stale Swift-era vault entries.\nRejected: Leave per-account entries for fallback | keeps stale secrets reachable after successful migration.\nConfidence: high\nScope-risk: narrow\nDirective: Clean old per-account vault entries after any successful authoritative migration source is saved.\nTested: cargo check --manifest-path src-tauri/Cargo.toml\nTested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit\nTested: npm run -s build\nTested: git diff --check\nNot-tested: Manual upgrade with both credentials.json and stale per-account vault entries.\nRelated: #230
2026-05-06 14:40:49 +08:00
H-Chris233 5bf9d72724 Preserve legacy vault entries on partial read failure
Legacy per-account vault migration must be all-or-nothing. If one old entry is temporarily unreadable, saving the readable subset and deleting the original entries can silently drop the failed secret, so update-path migration now aborts on any legacy vault read error while the non-mutating read fallback remains conservative.

Constraint: migration cleanup deletes old per-account vault entries after saving the chunked manifest.\nRejected: Migrate the readable subset | can discard an unreadable legacy secret before retry.\nConfidence: high\nScope-risk: narrow\nDirective: Do not remove legacy per-account vault entries unless every legacy account read completed without error.\nTested: cargo check --manifest-path src-tauri/Cargo.toml\nTested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit\nTested: npm run -s build\nTested: git diff --check\nNot-tested: Manual transient per-account vault read failure during upgrade.\nRelated: #230
2026-05-06 14:40:49 +08:00
H-Chris233 579efb0546 Keep Linux vault credentials persistent
Linux credential storage must survive reboot after the legacy plaintext file is removed. The keyring linux-native feature uses kernel keyutils, so the Linux build now enables the persistent Secret Service-backed combo and the migration read fallback also considers older per-account vault entries when a current manifest is unreadable.

Constraint: credentials.json must be removed after successful vault migration.\nConstraint: keyring linux-native/keyutils is an in-memory cache across reboot.\nRejected: Keep plaintext JSON as Linux fallback | violates the vault storage boundary.\nConfidence: high\nScope-risk: moderate\nDirective: Linux credential storage must remain persistent if legacy plaintext cleanup is enforced.\nTested: cargo check --manifest-path src-tauri/Cargo.toml\nTested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit\nTested: npm run -s build\nTested: git diff --check\nNot-tested: Manual Secret Service read/write on a desktop Linux keyring session.\nRelated: #230
2026-05-06 14:40:49 +08:00
H-Chris233 b97a54bb89 Surface legacy plaintext cleanup failures
Vault migration should not silently report success when credentials were written to the OS vault but the previous plaintext credentials file could not be removed. Propagating that cleanup error on write/update paths makes the remaining plaintext exposure visible instead of treating the migration as complete.

Constraint: credentials.json is allowed only as a migration source, not a durable fallback after a successful vault write.\nRejected: Keep deletion best-effort only | can leave plaintext credentials indefinitely with no user-visible failure.\nConfidence: high\nScope-risk: narrow\nDirective: Do not hide legacy plaintext deletion failures on credential write paths.\nTested: cargo check --manifest-path src-tauri/Cargo.toml\nTested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit\nTested: npm run -s build\nTested: git diff --check\nNot-tested: Manual permission-denied legacy file cleanup on installed apps.\nRelated: #230
2026-05-06 14:39:40 +08:00
H-Chris233 9a1461bcb6 Protect vault migrations from stale fallback overwrites
The migration path must prefer the current legacy JSON source over older per-account vault leftovers, clean those leftovers after a valid manifest is available, and avoid write operations overwriting a damaged or unreadable vault payload with fallback defaults.

Constraint: credentials.json is migration-only, but it was the previous Tauri source of truth.\nRejected: Use read fallback for mutating updates | can overwrite unreadable vault data with defaults.\nConfidence: high\nScope-risk: narrow\nDirective: Mutating credential operations must propagate chunked vault read errors instead of saving fallback roots.\nTested: cargo check --manifest-path src-tauri/Cargo.toml\nTested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit\nTested: npm run -s build\nTested: git diff --check\nNot-tested: Manual migration with simultaneous JSON and stale per-account vault entries.\nRelated: #230
2026-05-06 14:39:40 +08:00
H-Chris233 4498bedf4c Make vault chunk migration crash-safe
Credential vault saves now write a fresh generation of chunks before switching the manifest, so failed chunk writes or crashes do not overwrite chunks still referenced by the previous manifest. The same change also migrates documented per-account vault entries into the chunked manifest before falling back to legacy JSON.

Constraint: Windows Credential Manager requires chunking, and legacy credentials may exist either as plaintext JSON or documented per-account vault entries.

Rejected: Overwrite stable chunk names before manifest update | can mix old and new payloads after partial writes.

Confidence: high

Scope-risk: narrow

Directive: Treat the manifest as the commit pointer; write new-generation chunks before updating it and delete old chunks only after manifest write succeeds.

Tested: cargo check --manifest-path src-tauri/Cargo.toml

Tested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit

Tested: npm run -s build

Tested: git diff --check

Not-tested: Manual crash injection during OS vault writes.

Related: #230
2026-05-06 14:39:40 +08:00
H-Chris233 ddd6528088 Preserve legacy credentials when vault migration cannot run
Users upgrading from the plaintext credentials file should not lose access to existing settings just because the OS vault backend is temporarily unavailable or migration cannot be written. Keep the old file migration-only, but return its contents as the compatibility source when vault reads or writes fail.

Constraint: credentials.json remains a legacy compatibility source only, not the normal write target.

Rejected: Return defaults on vault read/write errors | hides existing upgrade data and makes configured providers appear empty.

Confidence: high

Scope-risk: narrow

Directive: Do not reintroduce plaintext writes; fallback reads are only for pre-PR legacy migration compatibility.

Tested: cargo check --manifest-path src-tauri/Cargo.toml

Tested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit

Tested: npm run -s build

Tested: git diff --check

Not-tested: Manual vault-unavailable upgrade run on Windows/macOS/Linux.

Related: #230
2026-05-06 14:39:40 +08:00
H-Chris233 64296e3555 Avoid Windows vault blob overflow for provider credentials
The OS vault migration keeps provider state as one logical JSON payload, but Windows Credential Manager limits each credential blob. Store the logical payload as a manifest plus bounded chunks so long API keys, multiple providers, or extra headers do not make future saves fail on Windows.

Constraint: Windows Credential Manager generic credential blob is capped and keyring stores passwords as UTF-16 on Windows.

Rejected: Split each provider field into independent vault entries | broader schema rewrite and more migration surface than needed for this review fix.

Confidence: high

Scope-risk: narrow

Directive: Keep the legacy plaintext credentials.json path migration-only; do not add compatibility for transient PR-internal vault formats unless the release shipped them.

Tested: cargo check --manifest-path src-tauri/Cargo.toml

Tested: cargo test --manifest-path src-tauri/Cargo.toml credential_payload_chunks_stay_under_windows_blob_limit

Tested: npm run -s build

Tested: git diff --check

Not-tested: Live Windows Credential Manager write with oversized real provider payload.

Related: #230
2026-05-06 14:39:40 +08:00
H-Chris233 13ccf85a48 Store credentials in the system vault
Provider credentials should follow the platform credential-store threat model instead of continuing normal plaintext JSON writes. The vault now stores the existing multi-provider credentials payload in the OS credential store, migrates the legacy JSON file only when the vault has no entry, removes the legacy file after a successful write, and keeps raw credential IPC restricted to the main settings window.

Constraint: User explicitly chose Keychain/Credential Manager/Linux keyring for issue #230.

Constraint: Preserve the existing provider schema and active-provider routing with minimal frontend churn.

Rejected: Keep plaintext JSON as the documented product choice | user rejected this direction.

Rejected: Split every credential field into separate vault entries | larger migration surface and risks losing active provider state.

Confidence: medium

Scope-risk: moderate

Directive: Security-model and storage-strategy choices must be confirmed with the user before implementation.

Tested: cargo check --manifest-path src-tauri/Cargo.toml; npm run -s build; git diff --check; grep for stale plaintext/Keychain fallback wording.

Not-tested: Manual migration on macOS Keychain, Windows Credential Manager, or Linux desktop keyring.

Related: https://github.com/appergb/openless/issues/230
2026-05-06 14:39:40 +08:00
baiqing 2bd77fe87e fix(tauri): 释放 Rust crate ~2.11 — npm 端已发 2.11,旧 ~2.10 锁反向 fail
Release run 25419948896 三平台 (Linux / Win / mac arm64) 都挂在
tauri build 启动检查:

    Found version mismatched Tauri packages.
    tauri (v2.10.3) : @tauri-apps/api (v2.11.0)

根因翻转:
- 之前 (PR #281) Rust crate 漂上 2.11 而 npm 没发,所以锁 ~2.10 把 Rust
  拉下来。
- 这次 PR #286 加 @tauri-apps/plugin-dialog 时 npm 解析把
  @tauri-apps/api 升到 2.11.0(plugin-dialog 依赖 ^2.11.0),
  Rust 还锁 ~2.10 → 反向 mismatch。

修复:
- Cargo.toml 锁 ~2.11
- cargo update 让 tauri / tauri-runtime / tauri-runtime-wry 三件套都升到
  2.11.0(连带 wry 0.54.4 → 0.55.1 等 transitive 依赖)。

后续如果 Tauri 团队再发 2.12 而 npm 滞后,回头看 PR #281 同款做法处理。
2026-05-06 14:33:01 +08:00
millionart 23f1dcb8dc refactor(asr): share wav encoding 2026-05-06 14:31:58 +08:00
baiqing 26ce2ff0b5 chore(release): 1.2.20
主要修复
─────────
- fix(windows): NSIS 安装包注册 OpenLess TSF DLL(含 32-bit COM 注册到
  Wow6432Node + Sysnative 绕过 32-bit installer 的 WOW64 重定向);
  改 perMachine 安装让 regsvr32 能写 HKLM。修「Windows 输入法后端:不可用」
  自 1.2.15 起的长期 bug。
- fix(local-asr): 切走/删模型/手动释放时引擎真正归还 1+ GB RAM;下载稳定性
  4 次指数退避 + Range 分块 + 取消正确传播;点击下载即崩(tokio::spawn →
  tauri::async_runtime)。
- fix(tauri): Rust crate tauri 锁 ~2.10,避开 npm 端 @tauri-apps/api
  尚未发布 2.11 导致的跨 minor 版本检查 fail。
- fix(capsule): AudioBars 用 cubic-bezier 缓动 + 0.18s transition,圆点→
  长条形变在 60Hz audio level 高频更新下平滑混合,不再阶梯式跳变。
- fix(recorder): liveness watchdog + 全局超时;watchdog 从首次回调开始计时
  避免慢启动误报(issue #238)。
- fix(windows): hotkey trigger leak、capsule 视觉异常、音量计稳定(多 PR)。
- fix(credentials): 凭据状态按 active provider 判断。

新功能
─────────
- feat(asr): 本地 Qwen3-ASR-Flash 全链路 — 模型注册表、HF/HF-Mirror 4 并发
  分块下载、Settings → 模型设置页、Coordinator 第三分支接入。
- feat(i18n): 日本語 / 한국어 完整翻译(217 string × 2 语种)+ 5 locale
  在语言名后加 Beta 标识。
- feat(translation): 输出语言扩展为日 / 韩,与翻译链路解耦。
- feat(recording): 录音时静音系统输出,多会话共享 mute guard。
- feat(i18n): 繁體中文 UI 完整 + 简繁脚本随 UI 切换同步。
- feat(settings): 合并设置内外两个「关于」(QQ 群 / 反馈 / 源码 / 隐私
  全部并入弹窗的关于)+ 一键导出错误日志(plugin-dialog 选保存位置,
  跨 macOS / Windows)。

工程
─────────
- chore(ci): release-tauri.yml + ci.yml checkout 拉 submodule,修 mac 端
  qwen-asr build 缺源文件。
- chore(release-prep): 清理 #268/#279 双 NSIS 方案残留。
- chore(deps): 引入 tauri-plugin-dialog ~2 给前端 save dialog 用。
2026-05-06 14:25:29 +08:00
TRIP eaf1dbed08 Merge pull request #286 from appergb/feat/settings-about-merge-export-log
feat(settings): 合并内外两个'关于' + 一键导出错误日志
2026-05-06 14:23:57 +08:00
baiqing 8397b9d1a5 feat(settings): 合并设置内外两个"关于" + 一键导出错误日志
用户反馈:
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 兼容)
2026-05-06 14:16:42 +08:00
millionart 9ebedebf7d docs: plan Windows local ASR 2026-05-06 14:14:58 +08:00
TRIP f29a3ff196 Merge pull request #284 from appergb/feat/i18n-ja-ko-beta-full
feat(i18n): 完整翻译 ja/ko + 5 locale 加 Beta 标识
2026-05-06 14:00:50 +08:00
millionart 844c1da098 docs: design Windows local ASR 2026-05-06 13:59:39 +08:00
TRIP 83f1c807b3 Merge pull request #283 from appergb/feat/capsule-audio-easing
feat(capsule): AudioBars 用 cubic-bezier 缓动 + 加长 transition
2026-05-06 13:45:08 +08:00
baiqing 13e6084ce2 feat(i18n): 完整翻译 ja/ko + 5 locale 加 Beta 标识
用户反馈:之前 ja.ts / ko.ts 只是 35 行 scaffold,缺失的 key 全部 fallback
到英文,对日韩用户体验割裂;用户希望"完善为完整版本,并在语言名称
后加 Beta 标识"。

改动:
1. 5 个 locale (en / zh-CN / zh-TW / ja / ko) 的
   settings.language.{ja,ko} 都加上 " (Beta)" 后缀,让用户在任何 UI
   语言下都能看到日韩为预览版本。
2. ja.ts 重写为完整 217 string 翻译版(保留 ...en spread 作为安全网,
   未来新增 key 仍 fallback 英文不会破坏编译)。
3. ko.ts 同上。

翻译质量:AI 机器翻译初版,符合 i18n 模板结构与占位符约束({{var}} /
markdown emphasis 全部保留),通过 tsc strict 校验(typeof zhCN)。
建议后续 native speaker review 调整敬语、专业术语、文体一致性。

类型保证:keep `typeof zhCN` 约束让 ts 编译强制 ja/ko 与 zh-CN 同
schema;新加 zh-CN key 时 ja/ko 仍能编译(...en fallback),但用户在
日韩 UI 下会看到英文 — 提示团队补全翻译。
2026-05-06 13:35:25 +08:00
baiqing 0d5d275370 feat(capsule): AudioBars 用 cubic-bezier 缓动 + 加长 transition
用户反馈:录音胶囊的语音条 UI 跳转有问题——目前是圆形跳成矩形,
视觉效果不自然,且存在卡顿,动效不够优雅、速度太快。

根因:AudioBars 每个 bar 的 height 跟随 audio level 在 2~24px 之间
变化(width=3 + borderRadius:999,height=2 像圆点、height=24 像
长条),但 transition 只有 0.08s。audio level 来自后端 RMS 回调,
60Hz 高频更新;每次 React re-render 修改 inline style.height 都重启
transition 动画。0.08s 在持续 retrigger 场景下永远在动画前段,
视觉上是阶梯式跳变("圆形跳成矩形"+ 卡顿)。

修复:transition 0.08s → 0.18s + cubic-bezier(0.22, 1, 0.36, 1)
(easeOutExpo-like),让多次 update 在曲线内平滑混合,圆→长条
的形变变得自然顺滑。0.18s 仍跟得上音量节奏,不影响响应感。
2026-05-06 13:26:33 +08:00
TRIP b1d65fa05c Merge pull request #282 from appergb/fix/ci-checkout-submodules
fix(ci): checkout 拉 submodules — 修 mac 端 qwen-asr build fail
2026-05-06 13:09:47 +08:00