Commit Graph
100 Commits
Author SHA1 Message Date
75918e0338 fix(android): protect credentials with Android Keystore (#838)
* fix(android): protect credentials with Keystore

* fix(android): harden credential migration

* fix(android): recover sanitized credential migration

* fix(ci): target Android x86_64 test variant

* fix(ci): prepare Tauri Android test project

* fix(ci): set Tauri Android build metadata

* fix(ci): isolate Android Keystore test build

* fix(ci): generate Tauri Android Kotlin bridge

* fix(android): harden credential persistence

* fix(android): preserve credential migration roots

* fix(android): recover verified credential writes

* fix(android): clear unrecoverable credential recovery

* fix(ci): accept CRLF in Android credential contract

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: baiqing <lbx12309@icloud.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-18 18:13:34 +08:00
TRIPandsim 033eb5e731 chore(release): prepare 1.3.15-Beta.1 (#834)
Co-authored-by: sim <sim@local>
2026-07-15 17:22:02 +08:00
TRIPandsim 3143892daa fix(updater): discover modern Beta release tags (#833)
* fix(updater): discover modern Beta release tags

Recognize the current -Beta.N-tauri convention while preserving historical lowercase beta tags so desktop and Android resolve the newest prerelease manifest.

* fix(updater): validate complete modern Beta tags

Reject malformed modern feed tags before they can preempt a valid release, while preserving the historical lowercase beta suffix.

---------

Co-authored-by: sim <sim@local>
2026-07-15 16:51:02 +08:00
TRIPandsim 4bdef5d7a3 ci: run all frontend and contract tests (#832)
Co-authored-by: sim <sim@local>
2026-07-15 15:55:37 +08:00
TRIPandsim 07b2e6a141 fix(security): redact provider URL log secrets (#831)
Co-authored-by: sim <sim@local>
2026-07-15 15:18:10 +08:00
TRIPandsim e3e63cad04 fix(security): harden remote pairing PIN persistence (#821)
* fix(security): harden remote pairing PIN persistence

* fix(security): harden pairing PIN persistence

* test(ci): accept Windows line endings in PIN contract

* fix(linux): declare libc for secure PIN opens

---------

Co-authored-by: sim <sim@local>
2026-07-15 14:46:06 +08:00
TRIPandsim 472b914985 fix(security): authenticate marketplace writes with GitHub OAuth (#816)
* fix(security): authenticate marketplace writes with OAuth

* fix(security): harden marketplace OAuth lifecycle

* fix(security): enforce marketplace credential invalidation

* fix(security): reject public marketplace redirects

---------

Co-authored-by: sim <sim@local>
2026-07-15 14:20:29 +08:00
TRIPandsim 9991b325b8 test(windows): repair UI config contract (#829)
Co-authored-by: sim <sim@local>
2026-07-15 13:53:09 +08:00
TRIPandsim a5a9c7f0b5 test: pin local tsx runner (#828)
Co-authored-by: sim <sim@local>
2026-07-15 13:44:39 +08:00
TRIPandsim d6ca07a6b2 fix(android): break IPC settings barrel cycle (#827)
Co-authored-by: sim <sim@local>
2026-07-15 13:20:57 +08:00
TRIPandsim fa53f47f08 fix(security): redact Less Computer approval tokens (#820)
Co-authored-by: sim <sim@local>
2026-07-14 19:07:20 +08:00
TRIPandsim 6aaae670b0 fix(security): bound style-pack ZIP imports (#819)
* fix(security): bound style pack archive imports

* fix(security): validate physical ZIP directory entries

---------

Co-authored-by: sim <sim@local>
2026-07-14 19:02:03 +08:00
145a0ea17d feat(asr): add ElevenLabs Scribe v2 provider (#807)
* fix(asr): 完善 ElevenLabs Scribe 接入

* fix(asr): omit ElevenLabs word timestamps

* fix(qa): treat ElevenLabs cancellation as idle

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
Co-authored-by: sim <sim@local>
2026-07-14 15:29:16 +08:00
TRIPandsim 66d084792f fix(security): patch RustSec dependency advisories (#809)
Co-authored-by: sim <sim@local>
2026-07-14 14:54:05 +08:00
TRIPandsim ef6f1fadb4 fix(cask): update Homebrew package to v1.3.14 (#802)
* fix(cask): point Homebrew to v1.3.14

* fix(cask): declare Monterey requirement

---------

Co-authored-by: sim <sim@local>
2026-07-14 14:43:36 +08:00
TRIPandsim d8789c783d feat: 统一聊天面板(官方 shadcn)+ 1.3.14 审计修复批 + CVE 补丁 (#767)
* fix(deps): patch tauri-plugin-shell CVE (GHSA-c9pr-q8gx-3mgp)

@tauri-apps/plugin-shell (npm) 与 tauri-plugin-shell (crate) 的声明下限
——npm `^2.0.1`、crate `"2"`——都落在 GHSA-c9pr-q8gx-3mgp 影响范围
(`< 2.2.1`,CRITICAL:`open` 端点作用域校验绕过,可致 RCE)。本项目
src/lib/ipc/utils.ts 正是通过该 `open` 端点打开外链,属受影响用法。

两个 lockfile 实际已解析到 2.3.5,但清单声明的下限仍是漏洞版本,供应链
扫描(SkillSpector SC4)因此读取到 2.0.1 报警。此处仅把两侧声明下限抬到
最新安全版 2.3.5,使清单与锁定版本一致、扫描不再命中;解析版本不变,无
运行时行为变化。

Closes #668

* fix(audit): 1.3.14 审计修复一批(Apple Speech 取消/IME 保存锁死/侧向键卡死/火山429 等)

- CRIT Apple Speech 取消/超时不终止 SFSpeechRecognitionTask(SendableTask+cancel_flag+[task cancel]+100ms 轮询)
- HIGH windows_ime 未安装分支反致设置保存事务锁死(抽纯函数 keyboard_list_pref_short_circuit)
- HIGH 侧向键 Released 卡死自愈 + macOS flags 类位竞态 + Linux 显式拒绝
- HIGH coordinator/hotkey_loops unused_assignments clippy 回归
- MED 火山 429 限流不重试(RateLimited)/ Heatmap clamp / 授权失败反馈 / SendInput 分类单一真相 / SendInputOptions 收敛
- 清理 删死码 ?? '重启' / side_aware 死字段 / 孤儿 MarketplaceModal

cargo test 552-0 / npm build 绿。windows cfg 部分需 CI 四平台验证。

* feat(chat): 统一聊天面板组件库——官方 shadcn base 组件按 CLI 规则内嵌

@shadcn/react 官方 primitive + base-rhea registry 原文内嵌 components/chat/ui/
(message-scroller/marker/bubble/message/card/empty/input-group/button/input/
textarea/spinner,仅按 CLI 规则改 import 路径;button 保留 3 行 React18 inert
归一)。Tailwind v4 作用域隔离:utilities source(none) + @source 只扫聊天文件、
无层导入压过 global.css;官方主题 shadcn/tailwind.css + tw-animate-css。
胶囊同款思考动画(orbFeed 单 GL 源镜像多头像)、GitHub 头像(CSP img-src 放行)、
出现/消失生命周期 hook。平台门控:非 darwin 构建 DCE 掉 Less Computer chunk。
删除旧自制 Marker/MessageScroller 组件。

* feat(panels): QA/LC 浮窗重写为官方 message-scroller-demo 同款骨架

Card + Empty + InputGroup + MessageScroller 官方组合;QA=Message 带头像形态
(右 GitHub / 左旋转 orb),LC=Marker 工具行形态(无头像)。
语音状态只在输入区:录音红光/转译黑光绕输入组跑圈,问题落定即停;
思考头像行门控在用户消息落定之后(修「AI 头像先出现、动画抽搐」)。
LC 事件空轮自愈 + mount 后 less_computer_sync 重放(webview 冷加载丢
user 事件导致「后端在跑前端空白/用户的话不显示」)。纯白背景、官方
24px 圆角、出现/消失动画(chat-panel:shown/closing)。

* fix(backend): 聊天面板后端配套——命令注册/非激活面板/事件缓冲/生命周期

- 恢复桌面 invoke_handler 的 qa_submit_text / qa_toggle_recording(此前删
  qa_window_pin 时误删相邻项,真机报 Command qa_submit_text not found)
- QA/LC 窗口转非激活 NSPanel(胶囊同管线:NonactivatingPanelMask +
  CanJoinAllSpaces|FullScreenAuxiliary),chat_panel_focus_keyboard 改主线程
  makeKeyAndOrderFront:打字不激活 app、主窗口不再被带出、frontmost 保持
  用户原 app(划词选区在多轮期间仍可读)
- Less Computer 事件按全局单调 seq 写入环形缓冲(fresh user 清空、cap 2048
  丢最旧),新命令 less_computer_sync 供前端 mount 重放(仅 less-computer
  窗口可调);附 2 个单测
- show/hide 生命周期:show 发 chat-panel:shown 并作废挂起 hide;hide 先发
  chat-panel:closing、240ms 后真正隐藏(AtomicU64 世代防快速关-开竞态)
- QA/LC 窗口统一固定 420×540;删除 qa_window_pin / less_computer_window_resize
  整链(无消费方)

---------

Co-authored-by: sim <sim@local>
2026-07-06 22:21:16 +08:00
2e3c0f5c29 fix(windows-ime): stop explorer.exe AppHang from IME pipe server shutdown (#764)
OpenLessIme.dll is a system-wide TSF keyboard TIP, so CTF loads it into
every process that has text input (explorer.exe, the taskbar, ...) and
runs it on that process's UI thread. The per-process pipe server blocked
on a synchronous ConnectNamedPipe, and Deactivate() -> Stop() joined that
worker from the host UI thread, relying on a racy CancelSynchronousIo +
self-connect WakePipe() to unblock it. When both lost the race the worker
re-entered ConnectNamedPipe forever and join() froze the host UI thread
indefinitely -> explorer.exe AppHangB1 (taskbar unresponsive, Explorer
restarts). WER reports for the reported hangs show OpenLessIme.dll loaded
in explorer.exe, and disabling the TSF profile stops the hang.

Make every pipe wait deterministically cancelable: open the pipe with
FILE_FLAG_OVERLAPPED and wait on {io_event, stop_event} via
WaitForMultipleObjects. Stop() now just signals stop_event and joins, so
it returns at once, never blocks the host UI thread, and leaks no worker
thread. Connect/read/write go through WaitForClient / RunOverlapped, which
CancelIoEx + drain the overlapped op before the stack OVERLAPPED / buffer
go out of scope. Removes the racy WakePipe / CancelSynchronousIo /
pipe_handle_ / pipe_mutex_.

Pipe name, JSON wire protocol and the Rust client (windows_ime_ipc.rs)
are unchanged.

Fixes #665
Fixes #707

Co-authored-by: appergb <lubaiqing9@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:08:29 +08:00
TRIPandsim 95e4cdf203 fix(audio-cue): recover when resume() is rejected or wakes non-running (#757)
Long sessions park the shared AudioContext in 'suspended' / WebKit
'interrupted' after the system audio session is repeatedly preempted by
ASR and recording. The earlier fix only recreated a 'closed' context: a
rejected resume() was swallowed and a resume() that resolved without
reaching 'running' scheduled voices on a frozen currentTime, both of
which leave the start cue permanently silent until the app restarts.

Treat both "won't wake" states as recoverable — discard the dead context
and retry once on a fresh one, bounded to avoid infinite recreation. The
decision is extracted into the pure, unit-tested cueActionAfterResume.

Co-authored-by: sim <sim@local>
2026-07-06 20:01:40 +08:00
7de0c04ba5 docs: 用协调沉降定律重构 README 能力/增强叙事,凸显开屏即授权完基础设施 (#765)
- 新增「从工具到基础设施 / From tool to infrastructure」专章:把 OpenLess 定位为
  消灭「想法 → 干净文字」之间每一次协调的基础设施——语气沉降成风格包、专有名词
  沉降成词典热词、文字落点沉降成插入协议、模型与凭据沉降进系统保管库,开屏授权
  一次即为默认协议。
- 改写「更新亮点(增强)」与「当前状态(能力)」两节开场,从功能罗列改为「你授权
  一次之后就不必再操心的基础设施层」;其余条目与版本号(v1.3.6)保留不动。
- 中英双 README 同步(README.md + README.zh.md),纯文档改动,不涉及代码/版本门禁。

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 21:28:36 +08:00
TRIPandsim 3ca6883d3a fix(deps): patch tauri-plugin-shell CVE (GHSA-c9pr-q8gx-3mgp) (#763)
@tauri-apps/plugin-shell (npm) 与 tauri-plugin-shell (crate) 的声明下限
——npm `^2.0.1`、crate `"2"`——都落在 GHSA-c9pr-q8gx-3mgp 影响范围
(`< 2.2.1`,CRITICAL:`open` 端点作用域校验绕过,可致 RCE)。本项目
src/lib/ipc/utils.ts 正是通过该 `open` 端点打开外链,属受影响用法。

两个 lockfile 实际已解析到 2.3.5,但清单声明的下限仍是漏洞版本,供应链
扫描(SkillSpector SC4)因此读取到 2.0.1 报警。此处仅把两侧声明下限抬到
最新安全版 2.3.5,使清单与锁定版本一致、扫描不再命中;解析版本不变,无
运行时行为变化。

Closes #668

Co-authored-by: sim <sim@local>
2026-07-05 00:40:23 +08:00
351e0211bd 集成 1.3.14 → beta(主窗 UI 大改 + 一批 fix/feat) (#761)
* fix(insertion): macOS 让「恢复剪贴板」开关真正生效,修复被回退的 #525

#525 的 macOS 剪贴板恢复(commit 4b4d219)被 Android 移植 commit 1ea467a
连同 cfg 门控一起回退:整套恢复机制被重新 #[cfg] 排除出 macOS,macOS insert()
退回「只写剪贴板 + Cmd+V」,于是设置里的「恢复剪贴板」开关在 macOS 上完全无效
——无论开关开还是关,剪贴板都被留成转写文字。

- 把共享的剪贴板恢复机制(ClipboardRestorePlan / schedule_clipboard_restore /
  restore_clipboard_after_delay / should_restore_clipboard 等)的 cfg 从
  not(any(macos,android,ios)) 放宽为 not(any(android,ios)),重新纳入 macOS
  (移动端仍排除;Windows/Linux 不变)。
- macOS insert() 改为:保存原剪贴板 → 写转写文字 → Cmd+V → 仅当「恢复剪贴板」
  开关开启且粘贴成功时按 CLIPBOARD_RESTORE_DELAY 延迟恢复;粘贴失败则保留转写
  文字供手动粘贴、不恢复。开 = 恢复,关 = 保持现状,开关说了算。
- 删除随之失效的 macos_insert_status_after_paste;恢复相关单测在 macOS 也运行。

Windows/Linux 专用项(insert_with_clipboard_restore / paste_keys /
simulate_paste(shortcut) / 其 insertion_success_status 变体)的 macOS 排除保持不变。

* fix(less-computer): 护栏 sudo/dd/chmod/chown 等设为不可批准,修批准注入畸形规则 (#679)

旧审批流对所有命中 pattern 一律 format!("Bash({p}:*)") 生成 allow 规则:
- 对带空格/参数的子串("sudo " → "Bash(sudo :*)","dd if=" → "Bash(dd if=:*)",
  "chmod -r 777 /"、"chown -r")生成的串既不等于 deny "Bash(sudo:*)" 等(故 deny
  没被移除、批准静默失效),又把畸形规则注入了 allowed_tools。

改为:新增 guard::deny_rule_for_pattern(pattern),把「可批准」命令(rm -rf / git
push --force / git reset --hard / git clean 等)映射到其在 default_deny_rules 里的
**精确** deny 规则;提权/毁盘/系统级命令(sudo / dd / mkfs / chmod / chown / shutdown
/ reboot / 管道执行 / fork 炸弹)返回 None = 不可批准,即使被批准也保持拦截
(fail-closed),不再注入畸形 allow。审批流用 filter_map 取精确规则,移除的 deny 与
加入的 allow 严格一致。

附单测:每个可批准 pattern 的映射规则确实存在于 default_deny_rules;危险系统命令一律
不可批准。

* fix(ui): unify macOS shell top inset with sidebar/background surface

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.

* fix(capsule): 胶囊跟随鼠标光标所在屏,修复多屏/多 Space 只在第一块屏显示

多屏 + 多桌面下胶囊只出现在第一块屏、其他屏闪一下就消失。根因:定位
(position_capsule_bottom_center) 用 AX caret 选屏,多数 App 取不到 caret 矩形就
跌回胶囊自己的 current_monitor(第一块屏);而去重缓存 capsule_layout_snapshot 也
用 current_monitor,与定位看的不是同一块屏 → 胶囊一落第一块屏即被判“无变化”跳过
重定位,被锁死。两条路都没跟踪鼠标。

- 新增 mouse_cursor_point():CoreGraphics CGEventGetLocation 取光标,坐标系与 AX
  一致、零权限、永远可用
- focused_input_target_monitor → capsule_target_monitor,选屏改“光标优先”(caret
  仅兜底),选屏循环抽成 monitor_for_anchor_point
- 定位与去重快照统一走 capsule_target_monitor,彻底消除不一致
- 保留 CAN_JOIN_ALL_SPACES(Spaces 契约强制且本就正确:帧落在光标屏→只那块屏显示)
- 扩展 macos-capsule-spaces 契约测试,grep 守护“光标优先 + 两路共用同一函数”

Windows 路径未动(本就跟随输入 App 屏且快照一致)。
验证:cargo check + lib 纯函数单测 + check:macos-capsule-spaces 全过。

* feat(less-computer): 边缘发光改用 EdgeGlow 式四层模糊堆叠 + iridescent 配色

把 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、单文件。

* fix(vault): Windows 凭据读取瞬时失败重试,修概率性「火山引擎未配置」

Windows Credential Manager(CredReadW)在登录后/并发下读 manifest+各 chunk 条目
时可能瞬时失败。任一条目读失败会让整组凭据看起来为空 → load_keyring_credentials
返回 Err → load_credentials 回退到空默认值 → 概览页显示「火山引擎未配置」,但下
一次听写重读成功,所以是「概率性」且「实际可正常使用」。凭据 chunk 越多,单次
load 读取次数越多,命中瞬时失败的概率越高。

修法:仅在 Windows 上对非 NoEntry 的读错误做几次短退避重试(4 次 / 60·n ms)。
NoEntry 是确定的「未存储」,立即返回不付重试延迟。macOS/Linux 保持原单次行为
——它们的读错误是 ACL 拒绝,重试无益,且未缓存的错误路径下次调用本就会重读,
加 sleep 只会拖慢 macOS 首次 Keychain 授权流程。纯后端、cfg 隔离,零 PC-macOS 影响。

* fix(asr): 火山引擎连接增加超时与重试,修弱网下无法语音输入

connect_async 此前既无超时也无重试:弱网下 TLS/WebSocket 握手可能挂到 OS 级
TCP 超时(几十秒),期间用户卡在「Starting」;而协调器全局超时只覆盖
await_final_result,不覆盖 open_session,所以握手挂起完全不受约束。单次网络
抖动(连接被重置/瞬时 DNS 失败)也会直接让整次听写失败。

修法:每次握手包 5s 超时,最多 3 次尝试、250·n ms 退避。AuthRejected(凭据被拒)
短路不重试——重试不会变好只会拖慢报错。把请求构造抽成 build_connect_request
(connect_async 消费 request 且 http::Request 非 Clone,需每次重建)。

桌面与安卓共享此路径,改动对两端等效;最坏耗时从「无界(~75s)」降到有界(~15s)
并新增抖动可恢复,对 PC 端只增不减。

* 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。

* fix(updater): 安装失败可见化 + 手动下载兜底 + 错误写日志 (#706)

下载完成后若 install() 抛错,旧代码把 status 置成 'error',而 UpdateDialog 不渲染 error 态 → 弹框消失、错误只 console.error,用户表现为「下载后无反应、重启仍旧版」(#706)。

- 新增独立状态 installError:仅下载/安装失败用它;'error' 仍表示「检查失败」,行为不变(CheckUpdateButton 按钮内轻提示、后台自动检查都不弹框,零回归)。
- 失败弹框留在原地显示真实错误 + 「手动下载」按钮直达 GitHub Releases 兜底。
- 新增 Rust IPC log_client_error,把前端更新错误写进 openless.log(webview console 不入文件日志),便于用户「导出日志」后定位真因。
- 5 个 i18n 文件补 updateDialog.installError / manualDownload。

注意:这是兜底(失败可见 + 可诊断),不解决 ad-hoc/quarantine 机器自动更新真正成功的问题——那需要 Apple Developer ID 签名 + 公证(本次暂缓)。

* 手动补:dictation/ipc 拆分后的 OpenCode + updater 改动迁移

* fix: AutoUpdate 兼容性修复(补回 CheckUpdateOptions + Android 参数类型)

* fix(audio-cue): 重建/唤醒退化的 AudioContext,修「用久了提示音消失」

长时间使用后「开始录音」提示音与设置页「试听」双双静音,且重启才恢复。

根因在 audioCue.ts 的 AudioContext 单例 sharedCtx 从不检查/恢复运行期状态:
- WKWebView/WebView2 在频繁录音抢占音频会话后,ctx 可能转入 WebKit 非标准的
  `interrupted` 态。旧代码只判断 `suspended`,interrupted 被当作可直接排期 →
  排到非 running 的 ctx 上,无声且不尝试 resume → 永久静默(macOS 最可能根因)。
- ctx 也可能被系统 `closed`。closed 无法复活(resume 必拒、createOscillator 必抛、
  被 catch 静默吞掉),而 getContext 从不重建 → 永久静默。
- 胶囊窗口与设置窗口各持一份 ctx,都会这样退化 → 两处提示音同时失效。

修复:
- 新增纯函数 audioContextActionForState(state):closed→recreate / running→ready /
  其余(suspended/interrupted/未知非运行态)→resume,单一真相、可单测。
- getContext 在 ctx 为 closed 时丢弃重建(并清空挂在旧 ctx 上的 activeVoices)。
- playRecordStartCue / primeAudioCue 改用该分类器,把 interrupted 一并按 resume 唤醒。

公共 API 签名不变,仅 AudioCue.tsx / RecordingInputSection.tsx 复用,行为向后兼容。
tsc --noEmit 通过;audioCue.test.ts 新增 5 条状态分类断言,全部通过。

* fix(macos): onboarding Accessibility authorization page stuck (#703 follow-up)

Root cause: permissions::request_accessibility() was called in setup(),
which runs BEFORE the AppKit event loop is live. On some macOS versions,
AXIsProcessTrustedWithOptions depends on the run loop for XPC to tccd.
When the event loop isn't ready, TCC may not show the dialog but still
consumes the one-time prompt. Later calls from the onboarding page's
'grant accessibility' button silently return Denied, trapping users on
the onboarding page. Recovery requires System Settings grant + app
restart, but users had no clear restart action — only a text hint.

Changes:

- lib.rs: Remove setup() TCC prompt. The prompt is now exclusively
  triggered by user action on the onboarding page, aligning with
  Apple HIG (explain why → then prompt).

- Onboarding.tsx:
  * onGrantAccessibility now checks the TCC result: skip
    openSystemSettings when already granted (previously always opened)
  * New tccPromptShown state distinguishes 'never attempted' vs
    'already denied' for correct button labels
  * Denied state now shows a prominent 'Restart OpenLess' button
    calling restartApp()

- PermissionsSection.tsx: Same fix for settings page accessibility
  row — check TCC result, guide to System Settings when denied,
  show 'Restart App' button

- i18n: Add onboarding.actionRestart + settings.permissions.restart
  for zh-CN / zh-TW / en / ja / ko

Closes: macos-onboarding-accessibility-stuck

* chore(release): publish 1.3.13

* fix(audio-cue): recover when resume() is rejected or wakes non-running

Long sessions park the shared AudioContext in 'suspended' / WebKit
'interrupted' after the system audio session is repeatedly preempted by
ASR and recording. The earlier fix only recreated a 'closed' context: a
rejected resume() was swallowed and a resume() that resolved without
reaching 'running' scheduled voices on a frozen currentTime, both of
which leave the start cue permanently silent until the app restarts.

Treat both "won't wake" states as recoverable — discard the dead context
and retry once on a fresh one, bounded to avoid infinite recreation. The
decision is extracted into the pure, unit-tested cueActionAfterResume.

* chore(release): integrate 1.3.14

* fix(ipc): 去除 coding-agent.ts/index.ts 中 OpenCodeDetection 和 codingAgentDetectOpencode 的重复声明

integrate/cloud-sync-20260621 合并后 IPC barrel 出现重复 export(beta 已含 PR #638 的
OpenCode 适配器,integrate 分支又加了一份)。合并去重,npm build 通过。

* style(less-computer): 气泡/审批/发送按钮改用 accent 主题 token

把 LessComputerPanel 里三处硬编码渐变(#3d6bff→#7a55f2)替换为
--ol-accent-solid-bg / --ol-accent-solid-ink,跟随浅/深主题。

* feat(capsule/asr/ui): 胶囊即时显示与预测展开、Apple 语音修复、弹窗全局遮罩

胶囊入场(延迟出现 / 无入场动画 / 亮起→卡→展开):
- 按下热键即「乐观显示」胶囊并播入场动画,不等麦克风就绪;level_handler 首帧 PCM
  到达后从预备态翻正式态、光条点亮(新增 warming 标志贯穿 emit_capsule payload)
- 修入场动画被吞:入场帧把发给前端的事件挪到 window.show 之后再 emit
- 预测式展开:光条按历史平均加载时长(EMA + localStorage 跨会话学习)从按下就平滑
  展开,就绪早则加速补满、未就绪则慢爬至 90%,消除「亮起→卡→突然展开」

Apple 语音(说中文识别成英文 / 偶发不可用):
- initWithLocale 跟随用户工作语言主语言,修 init 无参落系统首选语言(英文)导致
  中文被英文引擎误识别
- on-device 优先(requiresOnDeviceRecognition):离线可用 + 隐私 + 不受网络波动/限流
- isAvailable 就绪轮询等待,修刚 init 时瞬时不可用就报错的竞态

弹窗遮罩:Modal 用 createPortal 到 body,修被 WindowChrome/FloatingShell 常驻
will-change: transform 祖先困住 fixed 遮罩(登录弹窗浮在亮着的设置页上、无全局压暗)

服务页 Apple 语音文案优化(zh-CN/zh-TW)

---------

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
Co-authored-by: sim <sim@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 23:41:41 +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
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
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
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
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
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
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
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
TRIPand吕柏青 9f3eebcb88 perf(less-computer-glow): 隐藏时卸载全屏发光层,消除空闲 GPU 占用 (#675)
issue #470:彩虹描边 glow 是全屏 transparent 浮层 + 4 条无限动画(lcg-spin/breathe/
spin-reverse/flow)。原组件无状态无监听,窗口 .hide() 后 webview 仍持续合成发光层、
跑满 4 条动画,GPU 永不释放(Windows 尤其不释放)。

改为后端 show/hide 主动 emit `less-computer-glow:active` 驱动可见状态(不依赖在隐藏
窗口上失灵的 WebView2 visibilitychange):可见时渲染与原来完全一致的 DOM(视觉零变化),
不可见时返回 null 卸载发光层 → 零 GPU。另加 document.hidden 二级兜底(只停不启)。

注:glow 当前为 macOS-only(Windows show/hide 是 no-op),本修复消除 macOS 隐藏态泄漏,
并让组件具备跨平台「隐藏即零 GPU」安全性,为后续 Windows 重启用铺路。

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 10:56:27 +08:00
TRIPand吕柏青 28ea5123b4 perf(capsule): 删除无效的 translation 徽章 backdrop-filter (#676)
issue #470:该 backdrop-filter:blur(20px) 作用在浮于透明窗口之上的 translation 徽章
pill 上,背后只有透明窗口=桌面,而 webview 模糊不了窗口背后的桌面(Tauri 上游限制,
本文件上方 pill 已有同款注释)。故它不产生任何模糊效果、纯空耗一次合成。删除零视觉变化。

Co-authored-by: 吕柏青 <lubaiqing9@gmail.com>
2026-06-16 10:56:22 +08:00
TRIP dbb0676ccb Merge pull request #656 from Open-Less/revert/aura-ui-to-beta3-keep-features
revert(ui): 回退 AURA 界面大改到 beta-3 外观,保留功能与今日修复
2026-06-13 14:12:11 +08:00
TRIP 8142cceebd Merge pull request #649 from aron-76/codex/macos-capsule-input-monitor
Fix macOS capsule monitor targeting
2026-06-13 10:47:00 +08:00
TRIP 8c1e0c50e3 Merge pull request #652 from HKLHaoBin/feat/card-ui-unification
fix(ui): 统一卡片样式并修复深色模式可读性
2026-06-13 10:39:37 +08:00
TRIP 19ae8641d6 Merge pull request #621 from Open-Less/fix/aura-ui-polish
revert(ui): 回退 aura 皮肤 + 钥匙串/彩虹边框/提示词修复 (1.3.6-3)
2026-06-08 17:43:24 +08:00
TRIP 9b14c031da Merge pull request #619 from Open-Less/refactor/split-dictation
refactor(dictation): dictation.rs 2766→425 拆为 4 个子模块(机械式·零回归)
2026-06-08 13:27:57 +08:00
TRIP cf1ddae523 Merge pull request #617 from Open-Less/fix/issue-609-security-audit
fix(security): 收 #609 安全审计 — SSRF/prompt注入/history HMAC/QA信封 (F-01~F-06)
2026-06-08 13:27:40 +08:00
TRIP b4038de4f1 Merge pull request #616 from Open-Less/fix/cross-platform-popup
fix(popup): 跨端弹窗一致性 — capsule 四边 clamp + Windows 句柄重试(#470) + qa 焦点契约(#466)
2026-06-08 13:27:22 +08:00
TRIP 5da5445a10 Merge pull request #615 from Open-Less/refactor/srp-split-dedup
refactor: coordinator.rs/commands.rs 单一职责拆分 + 重复函数合并(机械式·零回归)
2026-06-08 13:26:09 +08:00
TRIP e9ecc25b7d Merge pull request #597 from Open-Less/feat/fast-agent
feat(less-computer): Less Computer 语音 Agent —— 专用键 + 动作模式 + 流式聊天弹框 + 内联审批(启发式)
2026-06-07 13:07:18 +08:00
TRIP 1b6f0dffe1 Merge pull request #605 from Ltdaaa/view
feat: introduce a theme-aware frosted-glass visual system for OpenLess frontend
2026-06-07 12:45:11 +08:00
TRIP 5ca3a80cd6 Merge pull request #581 from aeoform/chore/linux-plugin-check-and-ux-cleanup
chore(linux): plugin 路径检查增强 + 去除启动加载界面 + 修正快捷键描述
2026-06-05 08:02:14 +08:00
TRIP 30cf33e2b1 Merge pull request #589 from Open-Less/docs/readme-real-example
docs(readme): 实际效果示例换成客户回邮件用例(中英双语)
2026-06-04 19:33:36 +08:00
TRIP 2067367d83 Merge pull request #588 from ig505gi/fix-beta-reset
fix(macos): Beta 更新重启前重置 TCC 权限记录
2026-06-04 19:29:46 +08:00
TRIP 61f5416c73 Merge pull request #587 from Open-Less/docs/discord-community-link
docs(readme): 顶部加入 Discord 社区入口
2026-06-04 19:00:47 +08:00
TRIP 7cc8175d98 Merge pull request #586 from Open-Less/ci/auto-update-homebrew-cask
ci(release): 正式发版自动更新 Homebrew cask(+ 补 cask 到 1.3.5)
2026-06-04 13:08:27 +08:00
TRIP 13a0a03ab3 Merge pull request #584 from Open-Less/fix/audio-cue-missing
fix(audioCue): 修复快速录音时录音提示音偶尔丢失
2026-06-04 13:08:19 +08:00
TRIP 85d6ed851f Merge pull request #583 from Open-Less/fix/polish-context-isolation
fix(polish): 对话感知上下文——按风格包隔离 + 翻译会话参与润色(不串味)
2026-06-04 13:08:09 +08:00
TRIP 4bc94ecc5e Merge pull request #560 from Open-Less/feat/audio-cue-on-record
feat(capsule): 录音开始播放合成提示音 + 录音与输入设置开关
2026-05-31 14:01:35 +08:00
TRIP 1b8217eddb Merge pull request #521 from Open-Less/feat/settings-revamp
feat(settings): 设置界面重构 — 分节化 / 精简 / Beta 渠道 / 麦克风 / 市场登录
2026-05-22 23:33:46 +08:00
TRIP 32d7c22a8c Merge pull request #519 from Open-Less/feat/frosted-glass-overlays
feat(ui): 划词追问框与语音胶囊磨砂玻璃质感
2026-05-21 23:54:01 +08:00
TRIP 370eaa858e Merge pull request #506 from Open-Less/pr2/ui-i18n-refresh
refactor(ui): 精简多语言文案 + 设置/风格/市场界面与图标重构
2026-05-20 16:31:31 +08:00
TRIP e8f8ac0f59 Merge pull request #505 from Open-Less/fix/saved-toast-position
fix(ui): 统一 SavedToast 弹框位置与进出方向
2026-05-20 16:29:09 +08:00
TRIPandbaiqing a1d9549db2 fix(startup): autostart catch 块缺 return 导致复活主窗口 (#468) (#488)
* fix(startup): autostart catch 块缺 return 导致复活主窗口 (#468)

PR #474 引入的"前端兜底"useEffect 在 webview ready 后读
prefs.startMinimized,true 则跳过 currentWindow.show()。但 catch 块只
console.warn 没有 return,控制流继续走到 show()——等于"prefs 读失败 = 强 show"。
autostart 早期 IPC 抖动时 getSettings() 偶发失败,旧逻辑让用户明明开了
静默启动仍把主窗口弹出来。

修复: catch 块加 return,安全侧默认 = 当作 startMinimized=true 保持隐藏。
宁可让用户从 tray 手动唤起,也不要在 autostart 抖动时强 show 一个白色
/ 透明主窗口。

LSUIElement 不在本 PR 范围: 那是全局 macOS UX 决策,会影响 Finder
双击 / Spotlight / Launchpad 启动场景,应单独 PR + 单独评审。
正确做法是 Rust 端动态切 activationPolicy 而非 plist 静态写死。

Refs #468

* docs(startup): clarify catch rationale + improve warn diagnostics

Both local review and PR-Agent bot flagged the "fallback when settings
read fails" semantic reversal. Re-analysis confirms the change is
correct, but the rationale needs to be explicit in the code:

- Rust `get_settings` signature is `pub fn -> UserPreferences` (not
  Result), so this catch is unreachable from a normal first-launch
  scenario (returns defaults). Only Tauri IPC infrastructure failures
  (e.g. __TAURI_INTERNALS__ not ready during autostart early-mount)
  reach this branch.
- Tray is registered by Rust setup() before webview ready, so it remains
  the universal fallback entry point — no "user locked out" risk.
- Improved console.warn to include err.message for diagnostics, since
  raw err objects serialize as [object Object] in Tauri devtools.

No behavior change; comment + log improvement only.

Refs review of #488

---------

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-18 19:00:28 +08:00
TRIPandbaiqing 6e55e3059b fix(windows): capsule/qa 透明窗口 Acrylic 兜底 (#487)
* fix(windows): capsule/qa 透明窗口 acrylic 兜底 + CSS 可读性兜底

根因: tauri.conf.json 三个窗口都 transparent: true,但 lib.rs 只对 main
窗口调用 apply_mica。capsule / qa 在 Windows 上是裸 transparent,
Webview2 的 backdrop-filter 只能模糊页面内部 DOM 不能模糊 OS 桌面,
导致用户看到的胶囊 / QA 浮窗几乎完全透出桌面壁纸。

macOS 上 NSVisualEffectView (HudWindow vibrancy) 直接由系统合成器
混合桌面,所以 macOS 不需要额外处理。

修复:
1. lib.rs 在 setup 时对 capsule / qa webview 调用 apply_acrylic(
   color=(30,32,38,140)) — 选 Acrylic 而非 Mica 因为 Acrylic 支持
   Win10,更适合小浮窗。失败仅 log::warn 不 panic。
2. global.css 末尾追加 @supports not (backdrop-filter) 和
   @media (prefers-reduced-transparency: reduce) 兜底,给 html/body
   设实色背景。组件层 inline style 无法被覆盖,这只是二级防护。

Note: Capsule.tsx 和 FloatingShell.tsx 的 inline alpha 保留不动;
Acrylic 兜底通常足够。如果 Win10 实测仍偏透(Mica 不支持 Win10),
后续 PR 可考虑给 main 也加 Acrylic 作为 Mica fallback。

* style(windows): drop global html/body fallback rules from acrylic PR

Local code review flagged that `html, body { background: ... }` applies
to ALL windows including `main`, which would clobber the macOS
NSVisualEffectView vibrancy in `prefers-reduced-transparency: reduce`
mode.

Re-analysis confirms both rules are speculative:
- @supports never fires on modern Webview2 / WKWebView / Chromium
  (all support backdrop-filter)
- prefers-reduced-transparency triggers OS-level vibrancy neutralization
  on macOS already, but the CSS rule would still wrongly land on main

Acrylic in lib.rs is the canonical fix for the user-reported P0
(capsule/qa 透明遮罩看不清). The CSS layer is dropped to keep the PR
scoped to a single concern and avoid the cross-window collateral.

Refs review of #487

---------

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-18 18:57:43 +08:00
TRIPandbaiqing f30a787227 fix(windows): hotkey toggle 二次按键失效 - 串行化 Pressed/Released edge (#489)
根因: hotkey_bridge_loop 和 combo_hotkey_bridge_loop 把每条 Pressed/Released
边沿独立 async_runtime::spawn 成两个 task,被 tokio work-stealing 调度器
并行执行。Windows WH_KEYBOARD_LL 边沿间隔微秒级,Released task 经常先
swap(true→false),导致下一次 Pressed 看到 was_held=true → toggle 被静默吞掉。

macOS 上 CGEventTap 边沿间隔几十毫秒,race window 极窄,肉眼不可见,
所以仅 Windows 必现。ESC 走 HotkeyEvent::Cancelled 不经过 latch,
是用户唯一能逃出 stuck-latch 的路径。

修复: 用 tauri::async_runtime::block_on 顺序 await 两个 handler,
bridge 线程的 mpsc::Receiver::recv() FIFO 保证 swap 顺序正确。
bridge 在独立 OS 线程而非 tokio worker,block_on 安全。

代价: Hold 模式短按 stop 多 ≤500ms 延迟,被 request_stop_during_starting
覆盖,可接受。qa / translation bridge 不走 handle_*_edge 不受影响。

附带清理 coordinator.rs unreachable_code warning。

Refs #468

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-18 18:54:53 +08:00
TRIPandbaiqing b4e806e3ca fix(windows): 静默启动 #468 最后一公里 - 前端兜底 (#474)
* fix(windows): 修复 #466 #468 #469 IME 切回 / 静默启动 / QA 焦点 + #470 capsule 诊断 (#471)

集中修复 4 个 Windows-only bug:

- #469 IME 切不回 — restore_profile 对称补 legacy TSF ChangeCurrentLanguage + ActivateLanguageProfile,回归点是 c3b59329 升级激活路径时漏改恢复路径。现代 ActivateProfile 失败降为 warn(legacy 已完成视觉切回)。
- #468 Win11 静默启动失效 — single-instance 回调读 start_minimized,true 时跳过 show_main_window;托盘 / CLI intent 路径不受影响。
- #466 QA 弹窗 X/ESC 关不掉 — Win 上从 SW_SHOWNOACTIVATE 切到 Tauri window.show + set_focus,让 webview 拿键盘焦点。配套 focus-dance:begin_qa_session 抓选区前临时把焦点还给用户原 app,抓完再 refocus QA;多轮场景下用 GetWindowThreadProcessId 区分本进程窗口,刷新 qa_focus_target,避免 stale HWND。
- #470 录音胶囊不显示 — 加 per-process 一次性诊断 log(CapsuleState 用 capsule_state_log_name 显式短名,不走 Debug 防字段扩展时泄露),下次能直接定位 prefs / state / Win32 哪一环失败。

pr_agent 五轮迭代到 No major issues + No security concerns;Cloud 评审建议的 begin_qa_session 双锁 TOCTOU 已合并为单 scope。三平台 build + pr_agent CI 全绿。

#470 没标 closes,是 refs—需要真机日志才能定位真因,先 ship 诊断手段。

* chore(release): bump version to 1.3.4-3 (Beta) — Windows #466 #468 #469 修复

* fix(settings-ui): 权限页徽章不换行 + 本地 ASR 区块 Win 灰显

权限页 (Settings → 快捷键 → 权限):
- Pill 组件加 whiteSpace:nowrap + flexShrink:0 → "已安装"徽章在 Win
  窄宽下不再被挤成 3 行(每个汉字一行)。
- 行内 message span 加 whiteSpace:nowrap + overflow:hidden +
  textOverflow:ellipsis + minWidth:0 → 长文本("Windows 低层键盘 hook
  已安装")超宽时省略号收尾,徽章保持完整。
- zh-CN 描述大段精简:descAcc / descNoAcc / micDesc / accDesc /
  hotkeyDesc / networkDesc / windowsImeDesc / windowsIme.* 都改短,
  减少 Win 上拥挤感(用户反馈 zh 文案过长)。

高级页 (Settings → 高级 → 本地 ASR):
- Windows 上把"本地 ASR 模型(实验性)"标题区 + 警告小字 + Qwen3 行
  整组 opacity:0.45 灰显 —— Qwen3 在 Win 是 stub 不支持,那条"实验性"
  主线在 Win 没意义;用户视觉关注点应落到下方独立的 Foundry 行(Foundry
  保持正常颜色)。Toggle 的 disabled 行为已经在原 onToggle 条件里,本次
  纯加视觉灰显。

* fix(windows): 前端 App.tsx 在 show 主窗口前读 startMinimized 兜底

#471 在 Rust 端 setup() / single-instance callback 都加了 start_minimized
suppression,本机日志确认 Rust 路径完全正确("[main] start_minimized=true
→ 跳过初始 show")。但用户在 Win11 1.3.4-3 上仍然「重启进桌面那一刻主
窗口就出来了」。

定位到最后一条遗漏路径在前端:App.tsx 里 mount 时一条 useEffect 无条件
通过 IPC 调 `getCurrentWindow().show()`,把 Rust 端已经 suppress 的窗口
又拉出来 —— 这条路径 Rust log 完全看不见(走的是 plugin-window 的
showWindow IPC,不经过我们的 show_main_window helper)。

那条 useEffect 当初是给 issue #163 引入的:Windows 权限探测可能死锁让
首屏卡灰,所以前端兜底 force show 一次。

修法:show 前先读 prefs.startMinimized,true 就 return。读 prefs 失败
(极少数情况)保持原 show 行为,不让 #163 的兜底语义丢失。

closes #468 (the actual one this time, Rust + JS 双端都收敛了)

---------

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-18 15:29:33 +08:00
TRIP ceda87f64f fix(settings-ui): 权限页徽章不换行 + 本地 ASR 区块 Win 灰显 (#473)
Windows 真机反馈的两条 UI 问题,纯 frontend:

- 权限页徽章 3 行 wrap — Pill 加 whiteSpace:nowrap + flexShrink:0;message span 加 ellipsis 五件套;zh-CN 描述大段精简
- 本地 ASR 区块 Win 灰显不完整 — Win 上标题区 + 警告 + Qwen3 行整组 opacity:0.45,Foundry 保持正常色

cherry-pick from main (be24bb7),PR base=beta 保持双线同步。pr_agent 一轮 clean、Claude LGTM、三平台 CI 全绿。
2026-05-18 13:35:20 +08:00
TRIP c4bbec9cef Merge pull request #451 from aeoform/beta
feat(linux): fcitx5 plugin for Wayland input + deb packaging
2026-05-18 09:07:13 +08:00
TRIPandbaiqing fe56eb3038 fix(ci): detach HEAD before claude-code-action on fork PR (#464)
PR #451 (来自 fork aeoform/openless, head ref 也叫 beta) 触发 @claude 时,
claude-code-action 执行 `git fetch origin pull/451/head:beta` 把 PR head
拉到本地 beta 分支,但此时 worktree 正 checkout 在 refs/heads/beta,
git 拒绝 fetch 到已 checkout 的分支:

    fatal: refusing to fetch into branch 'refs/heads/beta' checked out at ...
    Action failed with error: Command failed: git fetch origin --depth=20 pull/451/head:beta

修复: 在 actions/checkout 后立即 git checkout --detach,释放
refs/heads/<default-branch> 这个本地分支名,让 action 自己 fetch 不撞名.
仅在 fork head ref == base ref name 的 corner case 才会触发问题,
detach 对其他场景无副作用.

失败 run: https://github.com/Open-Less/openless/actions/runs/25978532215

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-17 14:09:58 +08:00
TRIPandbaiqing 859fd5370f feat(updater): Beta 渠道接通 auto-update(Builder.endpoints runtime 切换) (#465)
* feat(updater): Beta 渠道接通 auto-update(Builder.endpoints runtime 切换)

CLAUDE.md 之前的论断「tauri-plugin-updater 2.10 Builder 不暴露 endpoints()」在
2.10.1 已不成立 —— Builder 现在有 .endpoints(Vec<Url>) -> Result<Self>,build()
里 self.endpoints.unwrap_or_else(|| self.config.endpoints.clone()) 走 runtime 覆盖。

实现 channel-aware 自动更新(不 fork plugin、不写自定义下载/签名校验):

Rust(~100 行新增)
- commands.rs::app_check_update_with_channel:
  Stable → webview.updater_builder().build()?.check()(默认 endpoints 从 tauri.conf)
  Beta   → fetch_latest_beta_release() → 拼镜像 + 直连两份 -beta manifest URL
         → builder.endpoints(urls)?.build()?.check()
- AppUpdateMetadata 字段形状对齐 plugin 的 UpdateMetadata(rid + currentVersion
  + version + body + rawJson),前端 new Update(metadata) 即可复用 plugin 的
  download / install / close。
- lib.rs 注册新 command。

前端(~30 行改动)
- AutoUpdate.tsx::checkForUpdates 从 import('@tauri-apps/plugin-updater').check
  改为 invoke('app_check_update_with_channel');其余 download / install / close
  全走 plugin 原生 JS API(new Update(metadata) 后链式调用)。
- SettingsModal::BetaChannelControl 删掉「打开 GitHub 下载」按钮 ——
  auto-update 接管了,手动下载路径冗余;保留「最新 Beta 版本号 + 刷新」做信息透明。
- i18n 5 个 locale 改 betaChannelDesc 文案,把「需要手动下载安装」改成
  「自动接收最新 Beta 版本」。betaChannelDownloadBtn 等 i18n key 暂留(无用但无害)。

物理隔离仍生效:Beta tag 的 manifest 文件名带 -beta 后缀,跟 Stable 的
latest-{tgt}-{arch}.json 在 GitHub Release assets 里是分开两份;即使代码逻辑
错把 Beta URL 给 Stable 用户也是 HTTP 404,不会拿到错档。

验证:cargo test --lib serial 270 / 0 / 0;tsc clean;vite build clean。

* feat(settings): BetaChannelControl 加版本比对 + 「立即更新」按钮

之前 BetaChannelControl 在 toggle 打开后,只机械显示「最新 Beta: vX-beta-tauri」,
不管本机版本是不是已经等于那个 tag。用户报告:「显示有最新 Beta,但应该比对版本号」。

修复:
- parseVersionFromBetaTag("v1.3.4-1-beta-tauri") → "1.3.4-1"
- semverGreater(remote, local) 做 SemVer 风格比对(支持 X.Y.Z 和 X.Y.Z-N,覆盖
  本仓库实际版本格式;prerelease 按 SemVer 规则 lower than no-prerelease)
- 本机 >= 最新 → 「最新 Beta: vX」 + 蓝色「已是最新」chip
- 本机 < 最新 → 「最新 Beta: vX」 + 「立即更新」按钮(蓝色高亮)
- 「立即更新」点击 → useAutoUpdate.checkForUpdates → 走和全局「检查更新」按钮
  完全相同的 download/install 流程,UpdateDialog 直接挂在 BetaChannelControl 内部
- 用户的第二个 feature 诉求:「在 Beta 渠道旁边一键检查 + 自动更新」 = 这个按钮

i18n 5 个 locale 加 4 个 key:betaChannelUpToDate / betaChannelUpdateNow /
betaChannelUpdateNowTitle / betaChannelChecking。

* chore(release): bump 1.3.4-1 → 1.3.4-2 for Beta channel

`-2` 后缀让 appVersion.ts 的 IS_BETA_BUILD 仍判为 Beta,UI BETA 标签照常显示。
配本 PR 的 Beta auto-update + BetaChannelControl「立即更新」按钮,1.3.4-1 用户
切到 Beta 渠道后点检查会发现 1.3.4-2 并走 auto-update 流程一键升上来。

发布 tag:v1.3.4-2-beta-tauri。

---------

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-17 11:15:06 +08:00
TRIPandbaiqing 061558a0fa fix(shell): sidebar 底部翻回两行(版本在上、设置在下) (#463)
PR #462 把底部折成一行,结果在窄 sidebar 下「设置」被挤成两行
竖字、版本 chip 糊在旁边,可读性大幅退化。改回两行但反过来:
- 上行:[BETA] 版本 v1.3.4-1(容下 BETA 标 + 完整版本号)
- 下行:⚙ 设置(真正落到最底,符合最初诉求)

设置按钮恢复 display:flex + label flex:1 跟上面 nav 按钮统一风格。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-17 10:27:41 +08:00
TRIPandbaiqing 0ecb3bd5a4 chore: bump 1.3.4-1 + sidebar 底部行折成单行 (#462)
* feat(shell): sidebar 底部行折成单行(设置 + 版本)

把 sidebar 底部「⚙ 设置」和「版本 v1.3.x」从上下两行折成一行:
- 设置按钮 display 由 flex 改成 inline-flex,去掉 label 的 flex:1
  (不再撑满 sidebar 宽度)
- 版本 chip(含 BETA 标)用 marginLeft: auto 推到行尾

外观更紧凑、纵向更省、视觉重心从两行散开收到一行。

* chore(release): bump version 1.3.3 → 1.3.4-1 for Beta channel

`-1` 后缀让 appVersion.ts 的 IS_BETA_BUILD 自动判为 Beta,UI 上的
BETA 标签会自动恢复显示。bump-version.sh 不收带 dash 的 prerelease
格式,所以这次手动同步 5 个文件(package.json / package-lock.json
root + nested / tauri.conf.json / Cargo.toml / Cargo.lock)。

发布 tag 仍走 v<v>-beta-tauri:v1.3.4-1-beta-tauri。

---------

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-17 10:18:21 +08:00
TRIPandbaiqing f47c2e4a29 docs(windows): 记录独占全屏游戏下 capsule overlay / hotkey 的 OS 级限制 (#457) (#458)
- coordinator.rs: 在 show_capsule_window_no_activate (Windows 路径) 上方加注释,
  说明 SetWindowPos(HWND_TOPMOST) 在独占全屏 DirectX/OpenGL 应用之上失效,以及
  UIPI 阻挡 hotkey 的同源现象,避免后续维护者再次尝试用户空间 workaround。
- USAGE.md: 给最终用户加 FAQ —— Minecraft 等全屏游戏下应切换到 borderless
  windowed fullscreen,对齐管理员权限,预先打开游戏聊天框。

此次仅文档与注释改动,不修改运行时行为。修复独占全屏 overlay 需要 DirectX
overlay 注入(Discord / MSI Afterburner 路线),工程量与风险都不在 surgical
修复范围。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-17 09:59:30 +08:00
TRIP 98dd4c7e47 chore(ci): 允许 Open-Less 组织成员触发 @claude (#461)
After repo transfer to Open-Less org, author_association == 'OWNER' no longer matches any human user on org repos. Expand the gate to [OWNER, MEMBER] so any org member can trigger @claude. No other changes.
2026-05-17 09:54:26 +08:00
TRIPandbaiqing ecf2b0f6c2 feat: style pack i18n + 差量缓存 + 内置三包升级到 v2 (#460)
* docs(windows): 记录独占全屏游戏下 capsule overlay / hotkey 的 OS 级限制 (#457)

- coordinator.rs: 在 show_capsule_window_no_activate (Windows 路径) 上方加注释,
  说明 SetWindowPos(HWND_TOPMOST) 在独占全屏 DirectX/OpenGL 应用之上失效,以及
  UIPI 阻挡 hotkey 的同源现象,避免后续维护者再次尝试用户空间 workaround。
- USAGE.md: 给最终用户加 FAQ —— Minecraft 等全屏游戏下应切换到 borderless
  windowed fullscreen,对齐管理员权限,预先打开游戏聊天框。

此次仅文档与注释改动,不修改运行时行为。修复独占全屏 overlay 需要 DirectX
overlay 注入(Discord / MSI Afterburner 路线),工程量与风险都不在 surgical
修复范围。

* feat: style pack i18n + 差量缓存 + 内置三包升级到 v2

i18n(5 个 locale 全覆盖)
- 新增 style.pack.* 命名空间(~70 keys)覆盖 Style 页面所有 UI 文案。
- 扩展 marketplace.* 命名空间(~50 keys,含 myPacks/upload/oauth/state/modal/derivativeBadge/sortLiked/likedEmpty 等)。
- ja.ts / ko.ts 原本完全缺失 marketplace.* —— JP/KR 用户进风格市场会 fallback 看见中文,本次补齐。
- Style.tsx 删除 100+ 行 `isEnglish ? '...' : '...'` 三元 copy 对象,全走 t()。
- Marketplace.tsx + MarketplaceModal.tsx 把硬编码 '我赞过的'/'我的发布'/'确定上传'/'已下架'/'撤回发布'
  /'审核中'/'已上架'/'未通过'/'未登录'/'衍生自' 等 30+ 处全走 t(),含 statusLabel(state, t) 重构。

差量缓存(src/lib/ipc.ts + Marketplace.tsx)
- list cache 从 sessionStorage 升到 localStorage,跨重启秒呈现,TTL 24h。
- 新增 detail cache:按 (id, version, updatedAt) 三元组缓存 marketplace_detail;
  三元组等价于内容版本签名,version/updatedAt 任一变化即失效重拉。命中 = 0 请求。
- 自动驱逐:写入 list cache 时根据当前三元组集合 prune 不在列表里的 detail 条目
  —— 满足「云端哈希被移除时本机也移除」的语义。
- 安全审计:
  - 包 ID 必须匹配 UUID v4 正则(防 key 注入)。
  - detail.id 必须等于请求 packId(防服务端返回错位内容)。
  - 单条 prompt 长度上限 200KB(防 OOM)。
  - detail 条目上限 64,LRU 淘汰(防 localStorage 配额耗尽)。
- openDetail 先查本机三元组缓存,命中即渲染 + 跳过网络;未命中再走 IPC + 校验回写。

内置 style pack 升级到 v2.0(types.rs + polish.rs tests)
- 「轻度润色」「清晰结构」「正式表达」三个 builtin pack 整体替换为社区维护的 PRO v2 prompt
  (含独立角色 + {{HOTWORDS}} + 七/八节中文序号骨架 + 高密度示例 + ASR 纠错词表)。
- default_style_system_prompt_for_mode 对这三个 mode 早返回各自的 BUILTIN_PROMPT,
  跳过标准 ROLE_BLOCK + COMMON_RULES + OUTPUT_BLOCK wrapper,避免与 v2 内同名段落重复。
- StylePack literal 同步:name 保留中文短名(去掉 PRO 后缀),version → 2.0.0,
  description/examples/tags 同步 manifest。
- 老用户磁盘已有的 builtin pack 不会被自动覆盖(persistence migration 只填空字段,
  保护用户对内置包的编辑);要应用 v2 需在「风格」页面点一次「重置」;新装用户直接拿 v2。
- polish.rs 的 4 处依赖旧 heading 的断言更新为 v2 实际锚点;
  common_rules_include_auto_correction_and_natural_organization 拆为
  Raw(走 wrapper)+ Light/Structured/Formal(走 v2 PRO 自带 ASR 纠错段)两组。

后端服务(apic.openless.top)这次没动。

* feat: 英文翻译注入 EN 专用指引 + 修「我的发布」加载/计数/驱逐

英文翻译加专用补丁
- translate_system_prompt 检测 target_language == English(含 '美式英文' / '英文' / 'english'
  / 'British English' 等别名)时,在基础 prompt 末尾拼一段 EN 专用指引:
    · ASR 纠错优先策略(高/中/低置信度分级)
    · 中→英技术词规范化表(令牌→Token / 密钥→Secret Key / 应用ID→App ID /
      鉴权失败→authentication failure / 调用接口→call the API …)
    · 模型名 / 产品名还原(克劳德→Claude、双子座→Gemini、卡布奇诺→Cappuccino …)
    · 自然英文要求(idiomatic、avoid Chinglish、code/URL/版本号原样保留)
- 其它语言行为不变。
- 来源:社区贡献的「重写为英文」prompt,精简后避免与 base 重复。
- 新增 translate_prompt_appends_en_extras_only_for_english 单测覆盖 happy path + 别名 + 非英文。

修「我的发布」三个 bug
1. 数据拉取态:新增 myPacksLoading / myPacksError 状态机。
   原行为:拉取中显示「你还没有发布过风格包」,失败只弹 toast;用户分不清"empty"和"loading"。
   现行为:四态优先级 loading → error → empty → list;loading 显示「正在拉取,请稍后…」;
   error 单独成块带「重试」按钮(inline 比 toast 更稳定可达)。
2. 计数错位:visibleMyPacks 立即过滤 withdrawn / superseded(移除原 5 分钟可见窗口)。
   原 bug:发布 1 个删 1 个后,刚下架的那个还在 5 分钟窗口内 → 计数显示 2,跟实际不符。
   下架的反馈走 actionMsg toast 即可,不需要在列表里继续占位。
3. 计数源切换:'已发布 N 个'里的 N 改用 visibleMyPacks.length(active 包),
   跟列表里实际看到的卡片数对得上。pending 计数同样基于 visible。
4. 顺手清理:刷新按钮在 myPacksLoading=true 时 disabled,避免并发请求;
   删掉只为旧 5 分钟窗口存在的 nowTick / setInterval 状态与 effect。

i18n(5 个 locale):marketplace.myPacks 新增 loadingTitle / loadingHint /
loadErrorTitle / loadErrorRetry 四个 key。

* refactor(translate): EN 目标整段切到专用 prompt(不再走 append)

上一版是「通用 base + 末尾追加 EN 专用补丁」,两段重复 / 注意力被稀释。这版改为:
- target_language 命中 English 时(含 '美式英文' / '英文' / 'english' / 'British English' 别名)
  → translate_system_prompt 直接 return 新增的 EN_TRANSLATE_SYSTEM_PROMPT 整段,不再拼通用 base。
- 其它语言行为不变(继续走原通用 # 任务(翻译输出) base)。

新 EN_TRANSLATE_SYSTEM_PROMPT 自包含、~60 行精简版(来源:社区「重写为英文」prompt):
  · 角色 + 输入是 ASR 不可靠 + 任务定义(先纠错再翻译,结果直插光标)
  · 工作流程 6 步(不可换顺序)
  · ASR 纠错按高/中/低置信度分级
  · 中→英技术术语规范化表(Token / Secret Key / App ID / Access Token / Endpoint /
    Webhook / authentication failure / call the API / quota / front-end / back-end /
    Claude / Gemini / Cappuccino / Coder / LongCat …)
  · 翻译要求:自然 idiomatic、不要 Chinglish、保持原语气、英语写法的日期数字
  · 原样保留:代码标识符 / 命令 / 路径 / env vars / 布尔值 / 完整版本号 /
    大写缩略语(API SDK JWT OAuth …)/ 人名地名品牌名
  · 边界 case + 5 条禁止 + 输出约束

替换 translate_prompt_appends_en_extras_only_for_english 单测为
translate_prompt_swaps_to_en_dedicated_when_target_is_english:断言
- 英文目标命中 EN 专用 prompt("# 任务(中文转写 → 英文翻译)"),不再含通用 base 标题
- 非英文目标仍走通用 base
- 别名 '美式英文' / '英文' / 'english' / 'British English' 都解析为英文

cargo test 269 / 0 / 0;tsc clean。

---------

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-17 09:45:28 +08:00
TRIP e33fc0047b Merge pull request #450 from Open-Less/beta
release: 1.3.2 stable — marketplace UI gated, ASR correction upgraded
2026-05-15 23:03:04 +08:00
TRIP e8d0ccf303 Merge pull request #449 from Open-Less/chore/marketplace-disable-stable
chore(stable): disable marketplace entry + strengthen ASR correction rules
2026-05-15 22:44:48 +08:00
TRIP 74208322bb Merge pull request #444 from Open-Less/feat/marketplace-tab
feat: marketplace tab + 一键发布 + Settings 风格市场配置
2026-05-15 15:00:34 +08:00
TRIP ec93a4a87a Merge pull request #443 from Open-Less/fix/polish-network-retry
fix(llm): retry transient network errors once before failing polish
2026-05-15 12:33:30 +08:00
TRIP 1f5051d8e0 Merge pull request #442 from Open-Less/fix/streaming-default-prompt-restructure
feat: streaming-on by default, structured default, agent-style hotword block middle
2026-05-15 11:41:14 +08:00
TRIP e297bfdd6e Merge pull request #438 from Open-Less/fix/volcengine-403-msg
fix(asr): surface Volcengine 403 as 'credentials rejected'
2026-05-15 09:45:56 +08:00
TRIP a0c239a716 Merge pull request #436 from Open-Less/feat/voice-quality-audio-archive
feat: polish hotword overrides, audio archive, auto-update check
2026-05-15 08:40:35 +08:00
TRIP 0aa3ee64d3 Merge pull request #435 from Open-Less/feat/style-pack-ui-refresh
feat(style-pack): rework UI + 模板新建 + 清理 PR #429 遗留死代码
2026-05-14 15:01:38 +08:00
TRIP 1ab0807ada Merge pull request #434 from Open-Less/fix/qwen-asr-long-audio-loss
fix(local-asr): Qwen3-ASR 长语音末段丢内容 + 长录音超时
2026-05-13 23:08:48 +08:00
TRIP 61325e3e03 Merge pull request #433 from Open-Less/feat/wayland-callout-three-commands
feat(wayland): callout 补全 --toggle-qa / --cancel-dictation
2026-05-13 23:08:21 +08:00
TRIP 1f2b259909 Merge pull request #428 from Open-Less/fix/selectlite-zoom-offset
fix(SelectLite): zoom 补偿修位置偏移 + 同步二次定位消除闪动 + 三平台统一
2026-05-13 14:25:15 +08:00
TRIP b699384762 Merge pull request #427 from Open-Less/fix/issue-420-wayland-cli
fix(hotkey): Wayland 下用 CLI 触发录音,修复 #420
2026-05-13 12:45:58 +08:00
TRIP db86a14169 Merge pull request #426 from Open-Less/fix/v1.3.1-7
fix(SelectLite): 下拉位置错位 + 关闭动画 + 滚外部自动收缩 (v1.3.1-7)
2026-05-12 19:14:51 +08:00
TRIP 545c3312c3 Merge pull request #425 from Open-Less/fix/v1.3.1-6
fix(capsule): 入场出场动画加慢加曲线 + thinking 字号/颜色重做 (v1.3.1-6)
2026-05-12 18:44:04 +08:00
TRIP 784f947cf3 Merge pull request #423 from Open-Less/fix/v1.3.1-5
fix(v1.3.1-5): 胶囊动画 + Qwen3 灰显 + 5 SVG 重画 + 6 项 Windows 视觉修复 + polish 反降级
2026-05-12 17:45:27 +08:00
TRIP ec55d9e509 Merge pull request #422 from Open-Less/fix/v1.3.1-4-multi-tweaks
fix: v1.3.1-4 Windows 字号/顶栏色/玻璃 + Vocab 大圈 + polish 回滚
2026-05-12 16:59:24 +08:00
TRIP 8799830e3e Merge pull request #421 from Open-Less/fix/win-button-appearance
fix: Windows 主窗口 chrome 丢失 + Vocab 大圈 + polish 清晰结构回归 (v1.3.1-3 hotfix)
2026-05-12 15:34:25 +08:00
TRIP 993bb90dd3 Merge pull request #419 from Open-Less/fix/windows-ui-417-418
fix(ui): Windows 原生 chrome / 胶囊缩小 / 自定义下拉 (#417 #418)
2026-05-12 14:54:37 +08:00
TRIPandbaiqing 6baca19e05 refactor(settings): split Settings.tsx into per-section modules + simplify Advanced labels (#416)
Settings.tsx (2835 → ~2150 lines) split by logical responsibility: each tab
now lives in src/pages/settings/. Pure mechanical extraction — no behavior change.

- settings/shared.tsx     — SettingRow, Toggle, inputStyle, AsrPresetId
- settings/AdvancedSection.tsx
- settings/ShortcutsSection.tsx
- settings/PermissionsSection.tsx (incl. PermissionPill / HotkeyStatusPill / WindowsImeStatusPill)
- settings/LanguageSection.tsx
- settings/AboutUpdateControl.tsx
- Settings.tsx keeps RecordingSection + ProvidersSection (their helper tangle
  is large; follow-up PR), re-exports Toggle / AboutUpdateControl so
  SettingsModal's existing imports keep working.

Advanced settings labels: shortened verbose copy across zh-CN / zh-TW / en /
ja / ko. "启用流式输入" → "流式输入", "同步写入剪贴板" → "同步到剪贴板",
desc / hint blocks trimmed; the toggle already conveys enable/disable so the
verb prefix was redundant.

Verified: tsc clean, vite build green, cargo check green, cargo test --lib
222/222 passing.

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-05-12 12:48:31 +08:00
TRIP 71f2792f09 Merge pull request #415 from H-Chris233/fix/issue-413-streaming-sse-edges
修复流式 SSE UTF-8 切分与部分输入丢失
2026-05-12 10:58:47 +08:00
TRIP d4162cf9cc Merge pull request #412 from Open-Less/pr/streaming-cross-platform
feat: cross-platform streaming polish output (Win/Linux/macOS)
2026-05-12 02:02:57 +08:00