Commit Graph
1319 Commits
Author SHA1 Message Date
Duyi-Wang 28a52e949b feat(llm): add custom provider extra headers
Add support for custom LLM provider extra HTTP headers stored in credentials, with validation that prevents overriding protocol-critical headers.
2026-06-27 17:06:13 +08:00
katanumahotoriandClaude Opus 4.8 bafddd4a91 fix(history): fall back to raw transcript when polished text is empty + copy button on raw panel (#752)
When polishing returns an empty finalText (errorCode stays null), the History
"Copy" button copied an empty string, so the raw transcript that is still
visible in the UI could not be recovered. Re-transcribe does not apply here
(ASR succeeded; polish is what failed).

- onCopy: fall back to rawTranscript when finalText is blank
- raw panel: add a dedicated copy button (shown only when rawTranscript exists)
- reuse existing common.copy / common.copied, no new i18n; frontend-only

Complements #653 (polish identical to raw); independent of the in-progress
history re-polish work (#666/#694). No backend/IPC changes.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 14:08:44 +08:00
jmr 1df2838604 NO AI SLOP PLEASE
AI SLOP 会导致项目产生大量垃圾内容 且在此workflow被跳过的情况下无法正常merge pr
2026-06-27 14:05:38 +08:00
sim de8b6e3bd8 fix(release): skip beta Windows MSI 2026-06-25 15:32:28 +08:00
sim 8d74b3e0eb chore(release): prepare 1.3.13-Beta.1 2026-06-25 14:59:31 +08:00
浩彬(^-^) ee0f746324 feat(windows): add SendInput insertion and keyboard list controls
Merge PR #740 after review and full CI. This supersedes PR #734 by including its SendInput insertion mode work plus the keyboard-list visibility preference for #738.
2026-06-25 13:51:11 +08:00
浩彬(^-^) fa09eaf605 feat(hotkey): support side-specific modifier combos
Add side-aware combo detection for dictation on macOS/Windows, shortcut recording support for side-specific modifiers, overlap validation, and tests.
2026-06-25 13:41:28 +08:00
NarwhalChen 346942e1bb style(ui): improve dark theme polish
Rework dark theme tokens, replace hardcoded dark-mode colors with theme variables, and remove the redundant overview metrics wrapper border.
2026-06-25 13:41:06 +08:00
Yu Peng Zheng c10ef381cb fix(ui): clean up late Tauri listener subscriptions
Immediately unsubscribe Tauri listeners if their async listen() call resolves after component unmount, preventing leaked handlers in AudioCue and Linux window chrome.
2026-06-25 13:39:58 +08:00
TRIP b4636f9620 fix(audio-cue): recover degraded AudioContext
Recreate closed AudioContext instances and resume interrupted/suspended contexts before scheduling generated record-start cues. Includes state classification tests.
2026-06-25 13:39:10 +08:00
TRIP 124a49ca05 fix(macos): recover stuck Accessibility onboarding
Fix macOS Accessibility onboarding getting stuck after TCC prompt/identity drift. Request permission only from onboarding, add targeted Accessibility TCC reset + restart recovery, and mirror the flow in settings.
2026-06-25 13:38:52 +08:00
sim 8563e707ba [cask] openless 1.3.12 (auto from release) [skip ci] 2026-06-24 13:56:50 +08:00
sim a4df800d66 chore(release): publish 1.3.12
- add OpenCode CLI provider support for voice agent workflows

- improve updater fallback/error visibility and compatibility

- bump app, Tauri, and Cargo versions to 1.3.12
2026-06-24 12:42:25 +08:00
e1c1be2942 feat(coding-agent): OpenCode CLI 适配器,语音 Agent 可选 OpenCode 后端 (Refs #579) (#638)
* feat(coding-agent): OpenCode CLI 适配器,语音 Agent 可选 OpenCode 后端 (issue #579)

把 OpenCode 当作与 Claude Code 同类的 coding agent CLI 接入,照既有 coding_agent
适配模式(不另造架构),复用同一套 CodingAgentRequest / CodingAgentEvent /
CodingAgentError / 审批护栏链路。

后端:
- coding_agent/opencode.rs(新):detect_opencode(opencode --version)、
  build_opencode_args(opencode run --format json --model --dir
  [--dangerously-skip-permissions],prompt 作为 argv)、parse_opencode_json_line
  (NDJSON:text/tool_use/error;text 块累计,EOF 合成 Completed)、
  run_opencode_agent(取消/超时 kill,护栏经 OPENCODE_CONFIG_CONTENT 注入)。
- coding_agent/args.rs:CodingAgentProvider 枚举(claude-code-cli/opencode-cli,
  未知回落 Claude)+ from_pref/default_exe。
- coding_agent/guard.rs:build_opencode_guard_config —— 把高风险 bash 前缀翻译成
  OpenCode permission.bash deny glob + webfetch deny,审批放行的前缀显式 allow。
- coordinator/dictation_voice_agent: run_less_computer_once 按 coding_agent_provider
  分派 Claude / OpenCode 运行器;两路都 fail-closed(护栏生成失败即中止,不裸跑)。
  审批拦截探测/重跑放行链路 provider 无关,自动复用。
- commands: coding_agent_detect_opencode 命令(已注册 lib.rs)。

前端:
- CodingAgentSection: OpenCode 选项从「即将支持」改为可用 + 选中后探测安装状态并提示。
- ipc: codingAgentDetectOpencode + OpenCodeDetection。
- i18n: opencodeReady / opencodeMissing(5 语言,替换 providerOpenCodeSoon)。

验证:cargo check + cargo test(492 通过)+ npm run build 均通过。

Refs #579
(#579 还含「语音润色模型配置」:prefs 字段已存在但全链路未实现/未接 UI,
转写直接进 agent 未经语音润色模型整理 —— 故用 Refs 不用 Closes。)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(coding-agent): OpenCode 防参数注入 + 可配置 Agent 可执行文件路径 (#638)

注入风险(@H-Chris233 评审):`opencode run` 的 prompt 作为最后位置参数,但之前缺
end-of-options 分隔。以 `-` / `--` 开头的 prompt(语音转写或被 prompt 注入诱导)会被
OpenCode 当作 flag 解析,可绕过护栏(混入 --dangerously-skip-permissions、--dir 改写
工作目录等)。修复:build_opencode_args 末尾追加 `--`,运行器把 prompt 接在其后;新增单测。

可配置路径(@PR-Agent 指出之前 hardcode "opencode"):新增 prefs.coding_agent_exe,
语音 Agent 路径据此取 exe(留空回退默认 claude / opencode),检测命令亦用配置路径;
设置「高级 → Less Computer」新增「可执行文件路径」输入;补 5 语言 i18n。

---------

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-06-24 12:30:12 +08:00
Haoqian 0881338e2e fix(asr): route Apple Speech dictation locally (#741) 2026-06-24 12:29:54 +08:00
jmr 95920a6b41 Merge pull request #729 from Open-Less/fix/issue-643-traditional-chinese-output
fix(ime): 繁中用户语音输入始终输出繁体 / enforce 简繁 script on output (#643)
2026-06-21 20:57:47 +08:00
Andy Jiang 48e190be20 fix(ime): always enforce 简/繁 script preference on dictation output (#643)
Traditional-Chinese users got Simplified output. finalize_polished_text only ran
the deterministic OpenCC conversion for Raw mode / polish failures, leaving
successful polish modes to the LLM — which defaults to Simplified for Chinese.
The streaming-insert path bypassed the conversion entirely.

- finalize_polished_text: for non-translation dictation, always apply
  apply_chinese_script_preference. It is a no-op for ChineseScriptPreference::Auto
  (the default), so zero impact on users who didn't pick a script; for Simplified/
  Traditional it now guarantees the output script instead of trusting the LLM.
- streaming_insert_eligible: gains the script preference and disables streaming
  insert when it is non-Auto, so the one-shot path runs (streaming types live and
  has no finished text to post-process).

Tests: polish_output_honors_chinese_script_preference (S2t actually converts;
Auto is a no-op) + streaming_disabled_for_non_auto_script_so_opencc_runs. 470 lib
tests pass.

Closes #643
2026-06-21 19:22:46 +08:00
github-actions[bot] e55ac03fa2 [cask] openless 1.3.11 (auto from release) [skip ci] 2026-06-21 06:49:44 +00:00
Andy Jiang 950ad58006 chore(release): 1.3.11 2026-06-21 13:55:15 +08:00
Andy Jiang 65361606f7 feat(dictation): failure-resilience, record-next queue, manual re-transcribe
- preserve audio on ASR failure + auto silent retry: always-archive recordings
  (delete on success unless debug), route all engine failure arms through a single
  fail_dictation -> recoverable transcribeFailed history; auto-retry re-feeds the
  archived WAV via retranscribe_pcm; fix empty-transcript id mismatch (#613)
- hotkey: queue-chain -- a press buffered during processing auto-starts the next
  recording (grace window distinguishes it from the #545 post-session cooldown)
- history: wire the manual re-transcribe button for transcribeFailed/emptyTranscript
  entries with audio

Rebased onto beta 1.3.10; my capsule multi-monitor fix dropped (superseded by #714).
2026-06-21 13:53:56 +08:00
Chris233 b07f78bc1d Merge pull request #723 from H-Chris233/fix/whisper-dynamic-timeout-721
fix(asr): Whisper/OpenRouter 动态超时 + 全局兜底 15→30s
2026-06-21 13:26:07 +08:00
Chris233 e1f24b61a4 test: 修复 local_qwen_timeout 测试 + 新增 whisper_transcribe_timeout 测试
- local_qwen_timeout_ceils_partial_seconds: 10.1s 音频原期望 17s,全局常量
  提升到 30s 后改为期望 COORDINATOR_GLOBAL_TIMEOUT_SECS
- 更新相关注释中的 15→30
- 新增 3 个 whisper_transcribe_timeout 测试:短音频兜底、长音频缩放、ceil
2026-06-21 12:50:41 +08:00
Chris233 3ec8c5ef0c fix(asr): Whisper/OpenRouter 动态超时 + 全局兜底 15→30s
- COORDINATOR_GLOBAL_TIMEOUT_SECS 从 15 改为 30,为云端 batch ASR 留网络预算
- 新增 whisper_transcribe_timeout(audio_secs):max(30, ceil(audio×0.5)+20)
- WhisperBatchASR 新增 buffer_duration_ms() 供 coordinator 计算动态超时
- dictation.rs Whisper 路径从固定 15s 切到动态超时,超时日志包含音频时长

Closes #721
2026-06-21 20:48:38 +08:00
TRIPandsim 323f44d450 feat(less-computer): 边缘发光改用 EdgeGlow 式四层模糊堆叠 + iridescent 配色 (#720)
把 Less Computer 全屏彩虹边缘发光从原来「两条都带模糊的软光带」改成
EdgeGlow(github.com/vector4wang/EdgeGlow,MIT)的实现方式,让彩虹「实线」
更清晰、发光更有层次。

- 4 层模糊堆叠:最内一条 0.5px 锐利彩虹实线(对应 EdgeGlow blur=0 那层,
  定义边缘),外面叠 3 层逐渐变宽、变虚的光晕(blur 3/8/15px),得到
  霓虹灯管式发光。原生用 CAShapeLayer + 高斯模糊,这里用 CSS「边框遮罩环
  + filter:blur」等价复刻。
- 配色移植 EdgeGlow 默认的 iridescent(Apple Intelligence 高饱和霓虹:
  紫→蓝→青→绿→金→橙→红→粉),按 12 段均匀铺满 360°。
- 流光整圈 8s/圈通过 --lcg-angle 旋转(只动渐变角度不动元素形状,GPU 友好),
  光晕层各带不同周期呼吸;实线用 normal 保色准,光晕用 screen 叠加增亮。

保留 issue #470 的隐藏即卸载发光层(零 GPU)逻辑、pointer-events:none、
prefers-reduced-motion 降级与 --lcg-radius 圆角变量。纯视觉、仅 macOS、单文件。

Co-authored-by: sim <sim@local>
2026-06-21 10:04:35 +08:00
GanAO da9bf15795 fix(asr): add MiMo provider to credential gate validation (#713)
ensure_asr_credentials() 的 if-else 链未处理 MiMo provider,fall through 到末尾被火山引擎凭证检查误拦。本 PR 把 is_mimo_provider(&active_asr) 加入 API Key 验证分支(+4 行),与 build_qa_asr_start / active_asr_provider_kind 的处理保持一致。

Claude 审核:修复正确且最小化,无副作用,可合并。四平台 CI 全绿。
2026-06-21 00:27:47 +08:00
TRIP 50274d1acd fix(insertion): macOS 让「恢复剪贴板」开关真正生效,修复被回退的 #525 (#683)
#525 的 macOS 剪贴板恢复被 Android 移植 commit 1ea467a 用 cfg(not(any(macos,android,ios))) 误伤排除。本 PR 把 40+ 处 cfg 放宽为 not(any(android,ios)),macOS 重新纳入;insert() 改为「保存原剪贴板 → 写转写 → Cmd+V → 按开关决定是否恢复」。

已解决与 beta(#712 安卓集成)在 insert() 签名上的冲突,cargo check 通过,四平台 CI 全绿。
Claude 审核:cfg 边界清晰、三态逻辑正确,无阻塞性问题。Closes #525
2026-06-21 00:27:39 +08:00
TRIP cfb09b14f8 fix(capsule): 胶囊跟随鼠标光标所在屏,修复多屏/多 Space 只在第一块屏显示 (#714)
新增 CGEventGetLocation 取鼠标光标位置(零权限),focused_input_target_monitor → capsule_target_monitor,定位与去重快照统一走光标优先选屏,消除「定位用 AX caret、去重用 current_monitor」两套选屏不一致导致的锁屏。

Claude 审核:5 个核心确认点(坐标系一致/选屏逻辑/快照统一/零权限/平台隔离)全部通过,可合并。
2026-06-21 00:05:45 +08:00
TRIP 7a33db0605 fix(less-computer): 护栏高危系统命令设为不可批准,修批准注入畸形规则 (#688)
deny_rule_for_pattern 只对可安全前缀化的命令(rm -rf / git push --force 等)返回精确 deny 规则;sudo/dd/mkfs/chmod/shutdown 等返回 None,即使被点批准也保持拦截、不注入 allow。修复旧代码 format!("Bash({p}:*)") 对 "sudo " 生成 Bash(sudo :*) 与 deny 规则 Bash(sudo:*) 不匹配导致批准静默失效的问题。

Closes #679

Claude 审核:方向正确,核心修复逻辑无误,无阻塞性问题。
2026-06-21 00:03:56 +08:00
TRIP fbe40ab583 feat(android): 集成 #704 安卓自动更新 + 同步 PC updater 修复(#705/#708)到安卓 (#712)
合并 #704(安卓应用内自动更新 + JNI 修复)并把 PC updater 修复同步到安卓:①安卓公钥回退 6F4B5994→1DEAA803(同步#705)②installError 安装失败兜底+去 throw(同步#708)③还原被自动合并误删的桌面 logClientError。桌面机制零变更。CI 全绿 + @claude 审核通过。取代 #704 及 #694 安卓部分。
2026-06-19 13:55:53 +08:00
github-actions[bot] 6c34e0e579 [cask] openless 1.3.10 (auto from release) [skip ci] 2026-06-18 03:50:17 +00:00
github-actions[bot] a5d1b0b579 [cask] openless 1.3.10 (auto from release) [skip ci] 2026-06-17 16:59:48 +00:00
sim dbdc73f278 chore(release): 1.3.10 (stable / 正式版) 2026-06-18 00:33:00 +08:00
sim 412dc9968a chore(release): 1.3.10-4 (beta) 2026-06-17 23:30:58 +08:00
TRIPandsim 6c99ddf1f7 fix(updater): revert signing pubkey to 1DEAA803 to repair auto-update (#705)
Auto-update was broken on every platform: builds since aaf4ccc embed pubkey
6F4B5994, but shipped 1.3.8 stable users trust 1DEAA803 and CI now signs with
1DEAA803 again. A key the installed app doesn't trust => download succeeds,
install() signature verification fails => "downloads but never updates".

Revert the desktop updater pubkey to 1DEAA803 so it matches both the CI
signing key and android/updater.rs (which already hardcodes 1DEAA803). This
also repairs Android auto-update, which was signing-vs-verifying mismatched.
One line, no logic change.

Co-authored-by: sim <sim@local>
2026-06-17 23:30:01 +08:00
sim f94f9be07a chore(release): 1.3.10-3 (beta)
Re-cut of the 1.3.10-2 beta from the synced cloud beta branch (supersedes the
-2 pre-release, which was deleted). Identical code to 1.3.10-2 — version-number
bump only. Bundles #701 persistence split, #702 top-bar unification, #703
onboarding accessibility gate, on top of 1.3.10-1 audit fixes.
2026-06-17 18:57:05 +08:00
sim aa643b972d chore(release): 1.3.10-2 (beta) — macOS top-bar unification + onboarding accessibility gate
Bundles the two macOS UX fixes already merged into beta:
- #702 unify macOS shell top inset with sidebar/background surface
- #703 require macOS Accessibility grant before first-run onboarding completes

Version synced across package.json, package-lock.json, tauri.conf.json,
Cargo.toml, Cargo.lock. Local release build verified (1.3.10-2 installs and
runs; top bar seamless; first-run requires Accessibility).
2026-06-17 17:54:09 +08:00
TRIPandsim f3467ae5f9 fix(onboarding): require macOS Accessibility grant before first-run completes (#703)
On macOS the hotkey needs Accessibility, but first-run onboarding could finish
after Microphone only and drop users into the main UI with a dead hotkey
("nothing happens when I press the key").

Cause: desktop onboarding gated completion on the hotkey *capability* flag
(requiresAccessibilityPermission). That flag is null while the capability IPC
loads, so requiresAccessibility was briefly false — if Mic got granted in that
window, refresh() called onComplete() before Accessibility was ever requested.
App.tsx's startup gate, by contrast, always requires Accessibility, so the two
disagreed.

Fix: gate completion on the real permission status, mirroring App.tsx — macOS
returns granted/denied, every other platform returns notApplicable, so this is
timing-independent and cross-platform safe. Also surface the Accessibility step
whenever it is denied (macOS-only state) and drop the capability dependency from
its grant button, so the step always appears and is actionable even before the
capability finishes loading.

No change on Windows/Linux/Android (Accessibility = notApplicable there).

Co-authored-by: sim <sim@local>
2026-06-17 17:39:16 +08:00
TRIPandsim 4903f4442e fix(ui): unify macOS shell top inset with sidebar/background surface (#702)
The 28px top inset that clears the macOS overlay traffic lights sat on the
content wrapper with no background, so it exposed the lighter WindowChrome
glass (--ol-window-bg) as a full-width band above the sidebar and panel —
reading as an extra top row that the Windows build (no inset) doesn't have.

Paint that wrapper with the same --ol-app-shell-bg the sidebar/background
already use, so the inset blends into one continuous surface with the panel
card floating on top. One property; token carries its own light + dark
values; no layout, token, or other UI changes.

Co-authored-by: sim <sim@local>
2026-06-17 17:27:15 +08:00
TRIPandsim a94ea3d05a refactor(persistence): split persistence.rs into domain modules behind a barrel (#701)
Behavior-unchanged decomposition of the 2757-line `persistence.rs` god-file
into a `persistence/` module, one file per concern. Everything is re-exported
via glob (`pub use <submodule>::*`) so the `crate::persistence::*` public
surface is byte-for-byte identical and no call site changes.

Layout:
- mod.rs         shared fs/io helpers (data_dir, ensure_dir, atomic_write,
                 read_or_default) + the two cross-cutting consts + re-exports
- paths.rs       models / recordings / Foundry roots + migration helpers
- credentials.rs OS credential vault (CredsRoot model, keyring/android/legacy,
                 CredentialAccount, CredentialsSnapshot, CredentialsVault)
- history.rs     HistoryStore
- preferences.rs read_preferences + PreferencesStore
- style_pack.rs  StylePackStore + ZIP import/export + prefs reconciliation
- dictionary.rs  DictionaryStore + vocab presets
- correction.rs  CorrectionRuleStore

Pure move + re-export: no logic, signatures, public API, or UI changed. Unit
tests moved next to the code they now cover. Shared helpers stay private in
`mod.rs` (parent items are visible to child modules), and glob re-exports keep
cfg-gated items (Android/Windows) resolution-safe across every target —
avoiding the explicit-`pub use` breakage seen in the earlier types.rs split.

Verified on macOS: cargo fmt/check/clippy clean (no new persistence warnings),
446 lib tests green. Android/Windows cfg paths verified by CI.

Part of #697.

Co-authored-by: sim <sim@local>
2026-06-17 16:08:26 +08:00
TRIP b98e353a2b refactor(ipc): split lib/ipc.ts into domain modules behind a barrel (#700)
Behavior-preserving: 1765-line ipc.ts → 22 domain modules + barrel index.ts; all ~118 exports preserved, 0 consumer changes, tsc/build/all-platform CI green. Part of #697.
2026-06-17 13:38:34 +08:00
appergb 6aad6e814f chore(release): 1.3.10-1 (beta) — audit fixes batch 1+2 (critical Android updater, security hardening, correctness bugs) 2026-06-17 11:45:38 +08:00
TRIP 5aed1e46ed fix: audit batch 2 — correctness bugs + Android robustness (#696)
7 medium findings: corrupt-timestamp context truncation, voice_agent flag timing, Volcengine/Bailian ASR cancel-on-error, graceful store-init fallback (no startup panic), Android OVERLAY_VISIBLE desync (Rust+Kotlin onDestroy), JNI null-check, accessibility clipboard restore. All-platform CI green; 446 tests pass.
2026-06-17 11:39:55 +08:00
TRIP 121bb11447 fix: audit batch 1 — critical Android updater + security hardening + bug fixes (#695)
14 confirmed audit findings: critical Android auto-update fix (SUPPORTED_ABIS), security hardening (exe allowlist, window-scoped approval, update-URL validation, remote PIN rate-limit/entropy, mirror order, prompt sanitization), correctness bugs (prefs race, hotkey spawn race, voice-agent focus clear), build-hygiene, and Android robustness. All-platform CI green; 446 tests pass.
2026-06-17 11:13:27 +08:00
TRIP 0eb69fc0f4 chore: codebase cleanup + behavior-preserving refactor (#693)
Audit-driven cleanup: 6 issue fixes (#689/#686/#680/#691/#678/#612), Aura skin removal (visually-neutral), dead-code cleanup (98→87 warnings), behavior-preserving modularization (coordinator.rs 6961→3083 + LocalAsr/ + polish/ submodules), and RELEASING.md (admin-only release policy). Gates: cargo test --lib 446 passed, tsc, all-platform build + contract checks green. Supersedes #685/#687/#690/#692/#682/#681.
2026-06-16 23:22:21 +08:00
appergb 4f73dbd5e4 fix(cask): revert openless to 1.3.8 — v1.3.9 unpublished (unauthorized buggy release) [skip ci] 2026-06-16 21:08:08 +08:00
TRIPand吕柏青 ee42d1a2e7 perf(onboarding,settings): 权限/热键状态纯事件驱动,去掉高频轮询 (#673)
* perf(onboarding,settings): 权限/热键状态纯事件驱动,去掉所有高频轮询

issue #470:引导页桌面(1s)/Android(3s)、设置页热键(1s) 的 setInterval 高频权限/热键轮询
全部改为 window focus / visibilitychange 事件驱动,连低频兜底也去掉(纯事件驱动)。
覆盖论证:麦克风前台原生弹窗授予由申请结果回调 + 800ms 兜底覆盖;辅助功能授予必经系统
设置 App、切回 OpenLess 必触发 focus/visibilitychange。并给 onGrantAccessibility 补对称的
void refresh() + 800ms 兜底(与 onRequestMicrophone 对齐)。PermissionsSection 的
refreshPermissions(10s)/refreshNetwork(30s) 保留不动。

* fix(onboarding): DesktopOnboarding effect 内 refresh() 统一加 void

@claude 审查 #673 指出:DesktopOnboarding 的 useEffect 里 refresh()/onFocus/onVisibility
三处 refresh() 是 floating promise(与上方 Android 段的 void refresh() 不一致,IPC 失败会
静默)。统一加 void 明确 fire-and-forget。issue #470。

---------

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 11:10:53 +08:00
TRIPand吕柏青 001615ebf4 perf(local-asr): 引擎状态改后端事件驱动,前端零轮询 (#672)
* perf(local-asr): 引擎状态改后端事件驱动,前端零轮询

issue #470:LocalAsr 前端原本 setInterval 每 5s 轮询 refreshEngineStatus。改为后端在每个
状态转换点主动 emit `local-asr:engine-changed`(load: build_local_qwen3 / preload;idle
释放: schedule_local_asr_release 的 release_if_idle==true;手动释放: release_local_asr_engine
汇聚点;keepLoadedSecs 变更),前端 listen 该事件增量更新、删掉 5s 轮询与 engineStatusTimer。
挂载时仍拉一次初值,listener 在 cleanup unlisten。LocalAsrEngineStatus 加 Clone 供 emit 复用。
helper 只用跨平台符号,不碰 Foundry/Sherpa。

* fix(local-asr): emit_local_asr_engine_status 加 Android no-op stub

CI Android cargo check 失败:LocalAsrEngineStatus 不在 android target 编译。给 helper 加
#[cfg(not(target_os="android"))] 真实现 + #[cfg(target_os="android")] no-op stub,
各调用点在所有平台统一编译。issue #470 / #6。

---------

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 11:10:29 +08:00
TRIPand吕柏青 49a6dc800f perf(tray): 托盘麦克风设备监听改 OS 原生通知,空闲零唤醒 + 60s 兜底 (#670)
* perf(tray): 托盘麦克风设备监听改 OS 原生通知,空闲零唤醒 + 60s 兜底

issue #470:原 watcher 每 10s 轮询 list_input_devices()(CoreAudio/WASAPI 全枚举)永不
停歇。新增 device_watch 模块用 OS 原生设备变更通知替代:macOS CoreAudio
AudioObjectAddPropertyListener(kAudioHardwarePropertyDevices) 专用 CFRunLoop 线程常驻;
Windows IMMNotificationClient 经 IMMDeviceEnumerator 注册端点通知。回调复用现有去抖
(microphone_device_signature) + refresh_tray_microphone_menu + emit 收尾。

works 保证:保留一条 60s 全平台慢速兜底轮询无条件运行——原生注册失败/Linux 无原生路径
时退化到它,三平台永远能检测到设备。coreaudio-sys 提升为 macOS 直接依赖(本就在依赖树)。

注:Windows IMMNotificationClient 分支开发机(macOS)无法本地编译,靠 CI 三平台 cargo check
+ 真机验证;60s 兜底保证即使该分支失效也不会漏检设备。Cargo.lock 未提交(macOS check 会
引入大量与本改动无关的环境性条目),cargo 构建时自愈。

* fix(tray): 砍掉无法本机验证的 Windows IMMNotificationClient 原生路径

CI Windows checks 失败:windows-rs 的 IMMNotificationClient_Impl / Win32_UI_Shell_PropertiesSystem
feature 与 implement 宏的 windows_core 路径在开发机(macOS)无法编译验证,靠 CI 反复试代价大。

简化为:macOS 走 CoreAudio 原生通知(已验证),Windows / Linux 统一返回 false → 走 lib.rs
的 60s 慢速兜底轮询(仍比原 10s 少 6 倍唤醒、保证三平台编译通过)。Windows 原生设备通知
留作后续单独项(需 Windows 开发机验证)。issue #470。

---------

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 11:10:22 +08:00
TRIPand吕柏青 2d783ce337 perf(coordinator): 可选热键 supervisor 注册成功即退出,彻底消除稳态轮询 (#671)
issue #470:combo/translation/action 三类可选热键 supervisor 原本注册成功后不退出,稳态
每 5s 醒来读 prefs,多线程常驻空转。改为对齐主 hotkey_supervisor_loop 的 exit-on-success
模式——到达「已安装」或「已主动停用」稳态即 return 退出,零轮询。

安全前提(已逐函数确认):用户启用/改/停这些热键时,set_*_hotkey 命令与 persist_settings
diff 都会调 update_*_hotkey_binding,其覆盖「monitor 为 None → 从零 start+spawn」安装路径,
故退出后启停改键无需重启即时生效;ComboHotkeyMonitor 注册后驻留、无运行中自注销,monitor
不会自己死,无需 supervisor 守护。失败重试 sleep(3s)/recv_timeout(5s)/app未就绪 sleep(1s)
等有界路径全部保留。

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 10:56:37 +08:00
TRIPand吕柏青 16e0f27685 perf(marketplace,localasr): 清理冗余定时器 (#674)
issue #470:
- Marketplace 上传后双重 setTimeout(1.5s+5s) 去重为单次 5s——乐观更新已即时反映「我的
  发布」,延时刷新仅做服务端状态校准,1.5s 那次纯冗余。
- LocalAsr scheduleScrollGuardRestore 三连 setTimeout(0/80/200ms) 仅删等价的 0ms 那枪
  (已被紧随的 rAF 覆盖),保留 80/200ms 兜住 rAF 之后的异步重排,避免滚动跳变回归。

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 10:56:32 +08:00