beta
142
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6f9af89583 |
release: OpenLess 2.0.0-Beta.4+build.20260930 (#1126)
Integrate ASR/Omni fixes, API Route, Android IME and confirmation-based vocabulary learning. Complete settings transitions and macOS single-item credential storage. Synchronize all application versions to 2.0.0-Beta.4+build.20260930. Integrated PRs: #1116, #1120, #1121, #1123, #1124, #1067 and #1128. Linux #1117 remains independent and is excluded from automatic release. Validated: full cross-platform CI, 18 UI motion cases, 112 frontend test files, 1245 Core tests (1 ignored) and 572 macOS native tests. Co-authored-by: HKLHaoBin <we3q@qq.com> |
||
|
|
913279c049 |
feat(linux-egui): native EGUI parity host (#1060)
* refactor(linux): move shared runtime assets out of Tauri
* feat(linux-egui): adopt redesigned native shell
* feat(linux-egui): add typed popup process bridge
* feat(linux-egui): add native desktop services
* feat(linux-egui): add verified AppImage replacement
* feat(linux-egui): render native popup processes
* feat(linux-egui): bind vocabulary corrections and styles
* feat(linux-egui): add real history actions
* feat(linux-egui): wire native popup supervisors
* feat(linux-egui): bind marketplace and device login
* fix(linux-egui): do not overreport update capability
* feat(linux-egui): honor minimized startup
* test(linux-egui): lock Tauri-free parity contracts
* fix(core): keep provider URL validation clippy-clean
* refactor(linux-egui): keep view data clippy-clean
* feat(settings): transact launch at login effects
* feat(linux-egui): complete verified AppImage updater
* feat(linux-egui): restore native desktop lifecycle
* feat(linux-egui): restore history audio workflows
* feat(linux-egui): complete local model controls
* feat(linux-egui): add native style pack editor
* feat(linux-egui): complete marketplace workflows
* feat(linux-egui): merge dirty settings on revision conflict
* feat(linux-egui): complete fcitx5 desktop hotkeys
* feat(linux-egui): harden and restore native popups
* feat(linux-egui): bind shared vocabulary presets
* feat(linux-egui): edit style hotkeys without blocking frames
* feat(linux-egui): persist and export diagnostic logs
* feat(linux-egui): edit all fcitx5 hotkeys
* fix(linux-egui): follow terminal capture cancellation
* docs(linux-egui): record parity and device limits
* docs(linux-egui): track latest 1019 head
* fix(linux-egui): reload fcitx5 after addon update
* feat(linux-egui): restore recording and appearance settings
* feat(linux-egui): restore Core-backed overview
* feat(linux-egui): restore native recording feedback
* ci(linux-egui): lock release artifacts and checksums
* feat(linux-egui): localize native interface
* fix(linux-egui): tolerate legacy fcitx addon methods
* [pi] Work in progress
* [pi] Work in progress
* [pi] All tasks are complete. Here's a summary of what w...
* fix(linux-egui): preserve original frontend content geometry
* fix(linux-egui): align beta contracts and slim release
* ci: make Linux PR and release gates reliable
* fix(linux): remove dead frontend artifacts and restore autostart
* fix(settings): handle launch-at-login in Tauri restore
* fix(ci): follow relocated Linux remote input assets
* fix(ci): normalize Windows contract paths
* refactor(linux): rely on packaged fcitx addon
* feat(linux): add audio backend fallback and fcitx clipboard
* ci(linux): use native deb and rpm packaging
* fix(linux): bound addon reload and use app version
* fix(linux-egui): restore input and CI build
* fix(linux-egui): align window input regions
* fix(linux-egui): restore frontend input routing
* wip(linux-egui): 2.0 parity checkpoint before parallel page rewrites
* chore: parity agent orchestration script
* [pi] 我负责的三个功能性 bug 全部修完,全量测试通过。报告如下。
* [pi] 全绿(24 个 test binary 全 ok;`coding_agent` 那条是已知时序 fl...
* [pi] 快捷键与选区做完了,全量验证通过。
* [pi] 三个浮窗按 Tauri 重做完了,旧的臆造实现已整段删除。
* [pi] 四项遗留全部做完,并回答了 Vulkan 的问题。
* [pi] Siri 光效 GPU 化(GLSL 移植)+ 圆角矩形扫光着色器,接胶囊与思考态;Vulkan 结论附证据
* [pi] 实现完成,验证全绿并打好包。
* [pi] Work in progress
* [pi] Work in progress
* [pi] **任务 1(胶囊 wlr-layer-shell 主路径)完成并出包。**
* [pi] 任务 2 完成。**开工时先核对了前任的产物,结果发现任务 1 其实从未真正生效**——两个阻塞性 ...
* [pi] 三个问题里属于我这个工单的(③ 吞键、④ 选区助手)已经修完并出包。**其中「吞键」我现在就把你机器...
* [pi] 任务2:静音失败归一化 + 胶囊终态自动收起
① 不说话报 InvalidArgument:
- 根因 Core `mark_dictation_failed` 把错误码名当 message(format!("{:?}", code)),
宿主原样送进胶囊/状态栏;空音频在 Core 里就是 InvalidArgument。
- 新增 linux-egui/src/dictation_feedback.rs(纯函数):capsule_outcome 分类相位+message,
内部错误码永不作为用户文案;normalize_stop_result 把 InvalidArgument/InvalidState/
Busy/Cancelled 这类预期内停止错误归一为「没有结果」,真失败仍上报。
- 三个停止/取消入口(胶囊 ✕/✓、Core 自动停止)改走 normalize_stop_result。
② 弹窗不自动收回:
- 按 Tauri coordinator::core_capsule_hide_delay 契约落地 capsule_hide_delay:
成功/失败 2s、取消立即、进行中不收;schedule_capsule_dismissal 延时后经
UiResult::CapsuleDismissDue 回主线程,capsule_hide_is_still_current 用会话+相位
双守卫,避免误关用户中途新开的录音。
- 收起=结束弹窗进程(layer surface 只能随进程销毁),并在进行中的相位按需
ensure_popup 重新拉起(phase_shows_capsule 排除终态,避免迟到事件把药丸喊回来)。
- 新增进程级测试:request_shutdown 必须真的让子进程退出且不算 crash。
验证:全量 cargo test 全绿(含 localization 契约)、i18n --check 干净(未新增 key)、
两个发布契约通过;包 2.0.0-Beta.1-28。
* [pi] 修复热键:修饰键触发不再吞键、Shift+符号键匹配(Ctrl+Shift+;),插件更新后自动重启 fcitx5
- 插件侧新增 hotkey_match.h 纯函数:字母大小写折叠 + US 布局 base/shifted 视为同一物理键
(实测 fcitx 的 Key::normalize 对符号键只改 states 不改 sym,所以注册 ';' 永远匹配不到到达的 ':')。
21 条断言的 hotkey_match_contract 覆盖 Ctrl+Shift+; / Ctrl+; 不误触发 / 字母 / 裸修饰键。
- 取消「裸修饰键一律拒绝注册」:改由插件保证不吞修饰键(shouldConsume),
按住修饰键当热键期间若按下其它键则判定组合键并放弃触发。
- 录制器支持录裸修饰键(跨帧看 Modifiers:按住→无其它键→松开),记为 LeftControl 等。
- 新增 /proc 时间戳比较:安装包替换 libopenless.so 后,若插件比运行中的 fcitx5 新,
启动时自动 fcitx5 -r,否则新匹配规则不会生效。
- 诊断日志:命中与「修饰位一致但键不同」的近似未命中(限速 1 次/秒)。
验证:cargo test 全绿;ctest 2/2(input_target_contract + hotkey_match_contract);
deb 内 libopenless.so 与新构建 sha256 一致(66d534f0…)。
* [pi] 任务2:报错路径胶囊收回(Core reset 时序 + 宿主兜底 + 宿主断开自退场)
* [pi] Work in progress
* [pi] 执行完了。取证结论很明确,而且**deb 打包没问题**。
* [pi] Work in progress
* [pi] 修复:启动不再被插件探测/重载阻塞(主窗口必现)+ /usr/local 手工残留不再冒充随包插件 + 指纹持久化(消除每次启动重载);热键注册摘要与取证日志
* fix(linux-egui): 胶囊去掉自造外圈扫光(Tauri 经典药丸只有 1px 中性描边)+ 风格包草稿行不再误改已有行 + 打包 postinst 改 D-Bus 重载
- 胶囊:录音/思考不再画红/黑外圈(用户报「语音输入弹窗有一个红边」),
改成 Tauri 的「1px 中性描边 + 随音量放大 1.8%」;中心声波/流体圆点保留。
两条新测试锁死:录音相位不得出现偏红涂色、GPU 回调数固定 1/1/0。
- 风格直达快捷键:草稿行选择器曾把 draft_pack 当行号下发 StyleHotkeyRepack,
会把已有行的包改掉,且 StyleHotkeyDraftPack 从未构造(编译告警)。
新增 style_pack_pick_action(row: Option<usize>, next) + 测试。
- popup_layer:去掉不必要的 unsafe 与未被使用的 FrameCallback 别名。
- 打包脚本:postinst/postrm 不再 `fcitx5 -r`(会替换 daemon 并前台常驻、装包卡 5s),
改用 D-Bus controller Restart(实测 0.002s 返回)。
* fix(linux-egui): Ctrl+Shift+; 热键被 CapsLock 状态位吃掉——插件按 fcitx 的 normalize 语义只比 ctrl/alt/shift/super;近失日志降到 Info 并加可开关逐键 trace;宿主逐项记录注册的 (sym,states)
* fix(linux-egui): fcitx5 缺席/插件过旧不再拖死后端——启动链对 fcitx5 失败改为警告降级(heatkey listener 变 Option)
* ci(release): 共用一条跨平台发布流水线(-tauri/-egui 按后缀分流)+ 末端聚合 zip
Linux egui 的发布此前是一条从没被调用、也从没跑成功过的独立工作流:
fork 里 dispatch 时它带了一个不以 v 开头的 release_tag,在第 8 步
(依赖安装 + fcitx5 插件构建 + 全量测试之后)报
"release_tag must name an existing v* release",白烧 4.5 分钟 runner。
- release-tauri.yml 变成唯一一条发版流水线,按 tag 后缀决定构建哪些平台:
v*-tauri → macOS/Windows 的 Tauri 包;v*-egui → Linux deb/rpm(egui 原生宿主,
通过 workflow_call 复用 release-linux-egui.yml);手动 dispatch → 两侧都构建。
- 新增 bundle-all-artifacts:把本次 run 的全部 artifact 收成一个
OpenLess-<tag>-all-artifacts.zip(zip 名自带 -tauri/-egui 后缀),
根目录附覆盖全部文件的 SHA256SUMS,tag 推送时挂到同一个 release。
与 build job 同一套渠道判定,beta release 不会被这个只挂 zip 的 job 升格。
- release-linux-egui.yml:tag 校验提到编译之前(几秒出结论);release target
从「release 必须先存在」改为「tag 必须存在」,让 -egui 这个新命名空间
首次推送时能自己建 release;版本推导先剥 -egui(-tauri 仅作历史兼容,
保持 release 契约测试要求的写法);首次为 -egui tag 建 release 时正确标 prerelease。
* fix(linux-egui): 关闭键改为退回托盘(不退出进程)+ 托盘唤起按 最小化恢复→显示→聚焦 回任务栏;QA 热键主键被输入法引擎占用时释放(pinyin QuickPhraseKey,带备份)
* ci(release): Linux egui 发布 job 换到 ubuntu-24.04(与 ci.yml 同因)
实测 run 35120587777:22.04 上冷缓存重编 libspa 0.10.1 直接编译失败
(spa_video_info_raw has no field named flags / missing spa_meta_first),
整条发布流程卡在第 8 步。ci.yml 的 linux-core-contract 早就因为同一个原因
换到 ubuntu-24.04 并留了注释,这条工作流从没跑过所以一直没跟上。
注意:deb/rpm 的 glibc 基线随之抬高到 24.04(glibc 2.39);若需要覆盖
22.04 用户,得在依赖层解决 libspa/PipeWire 版本,而不是换 runner。
* ci(release): 单一 -egui tag 触发同一 run 内四平台并发(macOS arm64/x64 + Windows + Linux egui)
按用户拍板的方案 B 收口 tag 约定:v<version>-egui(例 v2.0.0-Beta.1-egui)
一次推送 → 同一个 run 里四平台并发构建 → 所有产物挂同一个 Release
→ 末尾 bundle-all-artifacts 打成 OpenLess-<tag>-all-artifacts.zip。
- 后缀不再是平台分发开关:-egui tag 下 build(Tauri 三腿)与
release-linux-egui(workflow_call,tag 由编排层传入)都跑,zip job needs 全四者。
- build 里所有 release 相关步骤(签名可用性检查、release body prelude、
Create/update release)从「仅 -tauri」放宽到任何 v* tag,否则 -egui 发布时
dmg/msi/exe 只进 artifacts、不会挂到 Release。
- 历史 v*-tauri tag 兼容保留:仍只跑 Tauri 三平台(旧行为),文件头注释写清
两者关系与迁移注意(Cask URL/README/bump-version.sh/android-apk.yml)。
- Linux 产物命名统一带 egui 标识:deb/rpm 已是
OpenLess-Linux-egui-<ver>-<arch>(脚本原有),校验和文件改名 SHA256SUMS-egui,
避免与其它平台/聚合层那份混淆;版本串仍由传入 tag 推导(剥 -egui)。
- update-homebrew-cask 保持仅 -tauri:cask 下载 URL 写死 v#{version}-tauri,
迁移到 -egui 前不能动,已作为待办写进文件头注释。
* fix(linux-egui): 关闭键真正隐藏(Wayland 塌缩 1x1,不能 unmap/最小化)+ 事件泵用真线程保活 + Ctrl+Shift+; 匹配「Shift 折进符号」
三个实测问题(-40 真机反馈):
1. 点关闭键没反应:winit 在原生 Wayland 上 `set_visible` 是空实现
(winit-0.30 wayland/window/mod.rs「Not possible on Wayland」),
所以 -40 的 `Visible(false)` 等于什么都没做。最小化虽受支持,但最小化的
surface 拿不到 frame callback → eframe 的 update() 停摆 → 热键、托盘、
单实例唤起全部失效(实测心跳停、按热键无反应、重启也拉不回来)。
改为 Wayland 上隐藏 = 临时放开最小内尺寸 + 塌缩到 1x1 并保持映射:
窗口看不见了,事件循环照常跑(实测隐藏后 recording=true,热键仍生效;
再次启动能把窗口按原尺寸恢复)。X11 仍走真 unmap。
2. 事件泵保活必须用真线程:tokio 运行时是 current-thread,spawn 的任务只在
别处 block_on 时才推进,当后台泵用等于没保活。
3. Ctrl+Shift+; 到达插件时是 sym=0x3a(':') 且 states 里【没有 Shift 位】
(trace 实测;字母如 Ctrl+Shift+S 则是 sym=0x53 states=0x05)。注册值
0x3b/0x05 因此永远匹配不上。匹配规则改为:符号相同则严格比修饰位;符号
成 base/shifted 对时,只有注册值本身带 Shift 才接受、并忽略事件里的
Shift 位(Ctrl+; 与 Ctrl+Shift+; 双向仍可区分)。近失日志同步放宽到
「除 Shift 外的修饰位一致」,否则这类失败永远没有日志。
* ci(release): 四平台真正并发——egui job 去掉 needs: build,Linux 资产的 release 写入交给聚合 job
needs: build 会让 Linux 腿等 macOS/Windows 编完才启动,那不是四平台并发。
去掉它之后两个 job 可能同时建同一个 release,所以:
- release-linux-egui.yml 新增 attach_release 输入(默认 true 保持单独 dispatch 行为)
- 编排层传 attach_release: false,Linux 资产改由 bundle-all-artifacts(needs 四者、
最后一个 job)统一挂到 release,顺带和 zip 一起写,写入点唯一。
bundle job 里 Linux 资产那步用 hashFiles 守卫:egui 腿失败时不会让整步失败、
也不会把 zip 一起拖下水(zip 自己有 test -s 硬门禁)。
* ci(release): 聚合 zip 只装 Linux 平台产物(用户口径:all 指 Linux 各产物)
- bundle-all-artifacts 只下载 openless-linux-egui-x86_64 这一个 artifact,
zip 根部就是打包脚本的原始文件名(.deb/.rpm/SHA256SUMS),不再塞 dmg/msi/exe,
也不再另造一层汇总校验和——打包脚本自带的 SHA256SUMS 就是权威。
- Linux 校验和文件命名改回 SHA256SUMS(撤销上一轮的 SHA256SUMS-egui)。
- needs 仍为 [build, release-linux-egui]:它仍是 Linux 资产的唯一 release 写入点
(egui job 保持 attach_release: false)。
- ubuntu-24.04 的分发基线写进注释:glibc ≥ 2.39,支持范围 Ubuntu 24.04+,
22.04 / Debian 12 不在支持范围内(用户已确认,防止被改回去)。
- Homebrew、Cask、README、bump-version.sh、android-apk.yml、macOS/Windows 构建步骤均未动。
* fix(linux-egui): 最小化键改为退回托盘(Wayland 无法取消最小化,最小化的窗口会永久停掉 egui 重绘循环 → 热键消费与弹窗拉起一起失效)
- 有托盘:最小化 → 与关闭键同一条 hide 路径(Wayland 塌缩隐藏,事件循环继续跑)
- 无托盘:保持真正的窗口管理器最小化,否则窗口找不回来
- 新增 minimize_action() 决策函数 + 测试锁死该语义
* fix(linux-egui): 发布门禁单测不再依赖宿主是否装了 deb
a_per_user_copy_only_shadows_when_the_package_has_the_addon 之前直接断言
/usr/lib/x86_64-linux-gnu/fcitx5/libopenless.so 存在:装了 deb 的开发机绿,
干净的 CI runner 上 is_file()=false → 该断言必挂,于是 Linux 发布腿在第 8 步
「Verify framework-independent Linux contract」失败,后续 upload/checksums
与聚合 zip job 级联失败。
改为 tempfile 自建 per-user 文件,覆盖「无文件」与「有文件 + 包内有 addon」两个
分支,并保留「无包内 addon 时不 shadow」的反例断言。用 unshare + tmpfs 隐藏系统
插件目录后该测试仍通过,证明不再依赖宿主文件系统。
* fix(linux-egui): 删除自动改写输入法配置的逻辑,并用契约测试锁死
已实测证伪「拼音的 QuickPhraseKey=semicolon 会吃掉 Ctrl+Shift+;」:
fcitx 的 Key::check 要求修饰位精确相等(semicolon 是 states=0,而
Ctrl+Shift+; 到达时是 states=Ctrl),两者本就能共存。用户真机验证:
保持 QuickPhraseKey=semicolon 时按 Ctrl+Shift+; 面板照常弹出。
- settings.rs: 去掉 apply_hotkeys 里对 release_engine_key_conflict 的调用
- fcitx5.rs: 删除 release_engine_key_conflict{,_in}、release_engine_reserved_key、
engine_reserved_keys、fcitx_key_aliases、fcitx_punctuation_name、
reload_addon_config 及 4 个相关单元测试(-259 行)
- tests/fcitx5_config_contract.rs: 新增不变量契约,断言宿主源码里不出现
输入法配置目录与引擎保留键;已用注入违规验证它非空转
- 真机 A/B:旧二进制会把临时 HOME 的 QuickPhraseKey 清空并留日志,
新二进制字节不变、0 条日志,且启动路径确实跑过(有热键注册日志)
* [pi] Work in progress
* fix(linux-egui): 恢复被清理删掉的 provider/凭据编辑能力,发布门禁不再被拦
`1157fe51` 的界面重写把旧 monolith 的 provider 编辑器一起删了,新设置页只留住
「渠道列表 + 新建 + 启停 + 校验 + 删除」,于是 Linux 版既不能录入/修改/删除云服务
凭据,也不能改名、换 provider 类型、重排渠道、选当前渠道 —— 发布流水线第 8 步的
`check-linux-public-surface.ps1` 正是在拦这个(它的注释写明:只有状态、或只有本地
模型捷径的界面不算可用安装路径)。
- 宿主重新接上 Core 的 provider/凭据 API:rename_channel、set_channel_provider_type、
reorder_channels、set_active_provider、set_credential、remove_credential,以及
provider.validate / provider.list_models(模型目录属于 Core)。
- 编辑器草稿放在宿主侧(ProviderEditorForm)并每帧镜像给 view model:重新拉取渠道
列表不会冲掉正在输入的内容;密钥字段只写,打开编辑器不会把已存密钥读进 egui。
- 界面补回:渠道行的编辑/上移下移/换类型/设为当前,以及按 AuthRequirement 渲染的
编辑器(Endpoint/Model/Resource ID/密钥/鉴权模式 + 读取模型 + 保存 + 清空密钥)。
- 顺手修掉「新建渠道」名称框推的是编辑前的值:宿主会把旧值写回,每敲一个字被回灌。
- 新增本地契约测试 provider_surface_contract.rs,把这组 Core 调用钉住,避免下次合并
或清理再把实现删掉(历史上正是这样丢的)。
* ci(release): 聚合 zip 改成「手动安装」散装包(不再装 deb/rpm)+ 附 install.sh
用户澄清了这个 zip 的用途:不是把安装包再打一层,而是给装不了 deb/rpm 的发行版
手动安装用。所以:
- payload 用 `dpkg-deb -x` 解开 deb 得到精确安装树(usr/bin/openless、
usr/lib/*/fcitx5/libopenless.so、addon conf、desktop、metainfo、icon),
并新增硬门禁:散装清单必须与 `dpkg-deb -c` 的文件清单逐项一致(漏文件最难发现)。
- 新增 `openless-all/app/scripts/linux-egui-manual-install.sh`,随包发布为
install.sh:检查 root、先验 SHA256SUMS、按用途给权限铺到 /、用 D-Bus controller
Restart 重载 fcitx5(与 deb postinst 一致,不用会卡住的 `fcitx5 -r`),最后打印
重启与卸载提示。
- SHA256SUMS 改为对散装文件 + install.sh 重算;zip 内硬门禁断言不得出现 deb/rpm。
- zip 名自解释:OpenLess-Linux-egui-<tag 或 run>-x86_64-manual.zip;job 名/artifact
名与注释一并从 all-artifacts 改为 manual install,避免语义继续误导。
- deb/rpm 与它们自己的 SHA256SUMS 仍作为独立 release 资产保留(那条 attach 步骤没动)。
- bundle job 补 checkout(脚本随仓库版本走,不内联进 workflow)。
* ci(release): Linux 腿内联成平级 job(不再折叠)+ deb/rpm 与 manual zip 合并到一个 job + 升 action 到 node24 版本消掉 node 警告
- 去掉 workflow_call 调用:release-linux-egui.yml 的步骤内联为 release-tauri.yml 里与
build 平级的 linux-egui job,Actions 页面上不再被渲染成嵌套折叠条目
- 同一个 job 内完成 deb/rpm 打包与手动安装 zip 组装(删掉单独的 bundle job)
- release 竞态:内联后与 Tauri 三层并发写同一 release,先用 gh release view||create
幂等地建出来(吞掉并发 422),softprops 只做 attach/update
- manual zip 语义:散装 usr/ 树 + install.sh + 重算 SHA256SUMS,不含 deb/rpm;
deb/rpm 仍作为独立 release 资产
- action 升版(仅这两个发布工作流):checkout v4→v6、setup-node v4→v7、
upload-artifact v4→v7,消除 GitHub action 自身 node20 运行时带来的
DEP0040 punycode / DEP0169 url.parse 弃用警告(用户点名的 node 警告)
- 契约测试加锁:断言 Linux 腿必须是内联普通 job、manual zip 必须在同一 job 产出、
两份副本的关键门禁不能漂移
* ci(release): 统一发布语义——任何 v* 标签都出全平台(Tauri 三平台 + Linux egui + 安卓)
- release-tauri.yml: push.tags 收敛为 'v*';build 与 linux-egui 的 if 不再按 -tauri/-egui
后缀分流(后缀降级为命名约定);Homebrew cask 仍只跟稳定的 -tauri 正式版
(注释写明该边界与影响,避免被顺手放开)
- android-apk.yml: 触发从 'v*-tauri' 扩展为 'v*';is_tag_release 判定不再要求 -tauri;
OPENLESS_RELEASE_CHANNEL 判定与桌面统一(含 -beta-egui);未签名 debug fallback 语义不变;
注释说明为何 Android 是独立 workflow(另一套 JDK/SDK/Gradle 工具链,塞进 matrix 会让
每步都长满 if)以及它的资产如何落到同一个 release
- 顺带把这几个 action 升到 node24 版本(checkout v6 / setup-node v7 / upload-artifact v7),
与发布工作流一致,消掉 action 自身运行时带出的弃用警告
- 契约测试新增:两份工作流都必须对 'v*' 触发、安卓必须挂到同一个 release、
build/linux 两个 job 必须无条件运行、Homebrew 仍锁 -tauri
* feat(linux-egui): 关窗后切到无窗口宿主(窗口进程真退出)+ 事件泵不再依赖窗口绘制
- 抽出与渲染无关的 tick():原生事件、托盘命令、自动更新检查、心跳日志;
eframe 的 update() 与无窗口宿主的 run_headless() 循环共用它,热键消费与
弹窗拉起不再依赖「窗口是否在绘制」(最小化/隐藏/无窗口都照常工作)。
- 关闭键/最小化键:有托盘且没有进行中的会话时切到无窗口宿主。Wayland 下
winit 无法隐藏窗口(set_visible 是空实现),只有窗口进程真的退出,任务栏
与窗口列表里的条目才会消失;会话进行中退回托盘,避免新进程重开后端丢会话。
- 进程形态切换用同可执行文件的新进程 + --takeover:新进程等旧进程释放单实例
锁后接管(不在交接期被转发后直接退出),托盘在接管后重试一次;接管超时降级
为无锁启动,保证托盘/窗口仍可用。
- Core 的 ShowMain/FocusMain 提示在无窗口宿主里不拉起窗口进程(那只是「把已有
窗口推前面」),只有托盘「显示主窗口」才是明确的用户动作。
* feat(linux-egui): 常驻无窗口宿主 + 独立 UI 窗口进程(真实时 IPC,关窗不再重开后端)
形态:宿主进程(无窗口,长期存活)持有 Core 后端 / 数据目录 / 单实例锁 /
热键监听 / 托盘 / 弹窗监督器;UI 窗口进程(--ui-client)只渲染。
- 新增 `src/ui/bridge.rs`:unix socket + JSONL 双向协议。快照带单调递增序号,
UI 只采纳更大序号(重放/乱序/回退一律丢弃);快照可合并(UI 慢时跳帧,
绝不画过期状态),控制帧永远先于待发快照落盘。
- 关窗 = **只有窗口进程退出**(Wayland 下任务栏条目真的消失);托盘「显示主窗口」
或用户再次启动 = 宿主拉起新窗口进程,宿主 PID 与后端状态原地不动
(实测:宿主 PID 不变、开窗约 50ms,不再重开后端)。
- 时序:先 bind socket 再拉 UI(UI 连不上就直接退出);UI 断连立即作废窗口句柄,
会话/录音/弹窗不受影响;宿主退出先发 Shutdown 再释放单实例锁。
- 删除重启式交接:--headless / --takeover / HandoffMode / spawn_handoff_process /
塌缩 1x1 隐藏 / 最小化改道 / 事件泵后台唤醒线程全部移除。
- `FrontendViewModel` + `FrontendAction` 加最小 serde 集:Lang 按 tag 序列化
(i18n.rs 不允许手改),theme_mode 随快照下发,UI 进程据此配色。
- 事件消费与弹窗拉起改由宿主循环驱动(无窗口 ⇒ 必须线程化),另加
OPENLESS_UI_DEBUG=1 诊断开关记录 UI 侧指针点击与动作。
* feat(linux-egui): 设置页排版对齐 Tauri(行=200px 标签列+1fr 控件列/左对齐、卡片与分区标题 14px)+ 浮窗尺寸语义对齐(qa 420×540、preview 640×440 可缩放且抢焦点)
* feat(linux-egui): 补齐设置页剩余排版对齐(行标签 500 字重 / 输入框 32·13.5 / 分段选中片阴影 / 无热键后端隐藏快捷键分区)
* feat(linux-egui): 在 Linux 实现 Less Computer 浮窗(420×540 / 常驻宿主唯一所有者 / 事件序列经 IPC 呈现 + 审批与取消链路)
- 新增 PopupKind::LessComputer(--less-computer)与协议版本 4:HostToPopup::LessComputer
携带宿主事件序列(user/tool/compaction/assistant/error)、working、审批与错误;
PopupToHost 增加 Submit/Approve/Cancel/Dismiss 四条动作。
- 宿主是事件序列唯一所有者:Core 的 LessComputerEventKind 逐条转成面板条目,
流式增量合并为一条助手条目,终局正文替换它;窗口进程重连即收到完整序列。
- HostAction::ShowLessComputer 不再是空实现;面板尺寸取自共享常量(420×540),
位置与 qa 一致(居中卡片);设置里的开关此前只翻转 coding_agent_enabled,现在
有真实 UI 可看。
- 顺手修正 qa 弹窗视口尺寸的硬编码 520×520(应为共享常量 QA_WINDOW_SIZE=420×540)。
* [pi] # 选区读取为什么是「缓冲区里最后一次」——调查结论
* feat(linux-egui): 设置页顶栏/录音行/胶囊设置与试听按 Tauri 补齐 + 模态居中修正
用户实测反馈的七条:
1. 去掉设置页顶部那一层「设置」标题栏(桌面端左右两栏各自通到顶:左栏顶端是
搜索框,右栏顶端才是「分类标题 + 修改后自动保存 + 关闭」,对齐 SettingsModal
的桌面分支)。
2. 通用页第一行「录音快捷键」此前是只读文本,改成真正的 ShortcutRecorder
(行内展开 → 录制 / 停用),并补上行下方的录音方式提示语。
3. 补上「录音提示音」的试听按钮,走与录音开始同一段合成提示音。
4. 补上「录音胶囊」开关与「胶囊样式」选择(siri / classic / typeless),并让样式
真正改变胶囊渲染;关掉开关时胶囊立刻收起。协议 v4 → v5 增加 style 字段。
5. 默认录音方式一行补上可见提示语(Tauri modeDesc)。
6. 麦克风枚举过滤 cpal/PipeWire 混进来的输出监听与重复节点,并在枚举失败/无设备
时显式提示;每次打开设置重新枚举。
7. 设置模态此前稳定偏下 19.5px、卡片被内容撑宽 22px(横向偏 11px),且在窗口缩放
后要等下一帧才居中:改为由 body.center() 显式算位置并修正两栏宽度预算。
* fix(linux-egui): 补齐 P0 —— 窗口边缘拉伸光标 + 锁定胶囊按钮可点
P0(本轮核对结果):
* 胶囊退出/结束转录按钮可点:行为本来就是对的(layer-shell 只给指针输入,两个圆钮
靠 `ui.interact` 命中),但没有任何测试锁住它。新增 `the_capsule_buttons_report_
cancel_and_confirm`:把真实指针事件按到 ✕ / ✓ 的圆心,必须分别返回 Cancel/Confirm。
* resize 光标:**确实缺失** —— 八个拖拽区只发 BeginResize、不设光标,无边框窗口
完全没有「这里能拉伸」的提示。按方向设置 ResizeHorizontal/Vertical/NwSe/NeSw,
并新增 `the_window_edges_show_a_resize_cursor_on_hover` 四角四边逐一断言。
第 8 条(qa / Less Computer 避开任务栏)按决定**不改代码**:Wayland 没有
skip-taskbar 协议,winit 的该开关只在 Windows 生效;换成自研 layer-shell 就必须
自己补 xkb 键码 + zwp_text_input_v3 + wl_data_device,而 winit 在 Wayland 上已经
提供输入法支持(fcitx5 中文输入依赖它)—— 为一个任务栏条目牺牲追问面板的中文输入
不划算。
* test(linux-egui): 锁住两个被报为回归的交互(拖标题栏移窗口 / 分组开合)
用户报「拖标题栏移不动窗口」「设置里可展开分组点不开」。排查结果:**两者都不复现**。
- 无头:`the_titlebar_press_starts_a_window_drag`(标题栏按下必须发出
ViewportCommand::StartDrag)、`the_collapsible_group_header_toggles_on_click`
(点分组标题行后内容消失 = 真的点开了)—— 都通过。
- 真机(uinput 绝对坐标注入,源码版单实例):
* 点侧栏「历史」→ 截图对比 AE=21984,页面确实切到历史记录(点击被送达)
* 拖标题栏(+150,+100)→ KWin 读到的窗口几何 340,118 → **490,218**(窗口真被拖走)
- 结论:两条都不是代码回归。真机不clean 状态(陈旧单实例套接字 / 两个宿主并存)
会让人以为交互失灵 —— 本轮排查时我确实复现过这种假象。
把这两条固化成回归测试,以后真回归会被抓住。
* test(linux-egui): 补上分组开合测试用的 test-only 入口
上一个提交 d69642ac 里的 `the_collapsible_group_header_toggles_on_click` 依赖
settings.rs 里的 `test_group_toggle`(cfg(test) 包装 card_group),漏提交会让
干净检出编译不过。这里补上。
* [pi] Work in progress
* feat(linux-egui): 润色结果并入「选区助手」面板(收尾 9a109965 的半成品)
9a109965 把独立 preview 窗口拆掉了但停在中间态:面板里还留着「可编辑」的
TextEdit、`preview_focus_requested` 只为它服务的字段、以及指向已删窗口的注释与
常量。这次把它做完并定型:
* 润色结果模式 = 选区助手面板里的第二套 UI(`polish_result_mode`),签名收成
`&QaPolishState`:**只读展示**(原先的可编辑 TextEdit 换成可选可滚动的只读
文本区,用户要求不做就地编辑),底部按钮集与 Tauri `SelectionPolishPreview.tsx`
一致:✕ / 取消(`selectionPolishPreview.cancel`,line 72/125)+ ✓ 确认并替换
(`selectionPolishPreview.confirmReplace`,line 139)—— 后者就是把结果写回
原选区的「插入」(→ `PopupToHost::ConfirmPolish` → Core `selection.confirm`)。
* 副标题跟着语义改:Tauri 的 `selectionPolishPreview.subtitle` 原写「可直接编辑」,
与只读冲突,5 个语言一起改成「只读结果;点「确认并替换」才会写回原选区。」
(egui 目录由 `scripts/sync-egui-i18n.mjs` 写入)。
* `HostAction::HideSelectionPreview` 只撤掉「润色结果」模式:面板若正在提问对话
中,不能被润色流程的收尾动作关掉。
* 清掉半成品留下的尾巴:`preview_focus_requested` 字段、`PreviewAction` →
`PolishResultAction`、`selection_preview_visible` → `polish_result_visible`、
已删窗口的常量注释(`PREVIEW_WINDOW_SIZE`/`PREVIEW_MIN_SIZE`/`PREVIEW_PADDING`)。
验证:`cargo test --locked` 1257 passed / 0 failed;`sync-egui-i18n.mjs --check` 干净。
* [pi] Work in progress
* fix(linux-egui): 标题栏拖动补两点 —— 未聚焦时先要回焦点、最大化时先还原再拖
egui-winit 对 ViewportCommand::StartDrag 带 window.has_focus() 前置条件
(egui-winit-0.33.3/src/lib.rs:1403-1409),未聚焦那一帧会被整帧丢掉,而 Wayland
的 xdg_toplevel.move 只认按下那一帧的 serial;另外最大化窗口的 move 请求会被
合成器忽略。现在:未聚焦时同帧补发 Focus,最大化时先 Maximized(false) 再拖。
* fix(linux-egui): 设置弹窗降到 Order::Foreground,修「点开的控件被弹窗盖住」
egui 的 ComboBox 下拉/弹出菜单是 Order::Foreground(popup.rs:150),而同层后创建者
在上、跨 Order 是 Tooltip > Foreground。设置弹窗占着 Tooltip,于是里面所有
ComboBox(渠道 provider / 认证方式 / 新增渠道 / 通用下拉等)展开后都被弹窗挡住,
表现为需要点开的控件都打不开。降到 Foreground:先创建的遮罩输入层仍在其下,
弹窗内再打开的下拉因创建更晚而在其上。
* fix(linux-egui): 设置遮罩改用 Area 排序(不再盖在弹窗上)+ 去掉「模拟粘贴快捷键」选项
- 遮罩原先用 ctx.layer_painter() 建原生图层,同 Order 内相对位置不受控;弹窗从
Tooltip 降到 Foreground 后遮罩反而盖到弹窗上面(用户报「设置整体被阴影层压住」)。
合并进原本就存在的 backdrop-input Area 并在弹窗之前创建,遮罩稳定在下面。
- 按用户要求移除「模拟粘贴快捷键」设置行(Ctrl+V / Ctrl+Shift+V)。
* feat(linux-egui): 接入上游 #1082 的「稳定模式(先录音后识别)」
合并 origin/beta 后唯一的冲突是 linux-egui/src/main.rs:上游往旧的 settings_ui 里加了
一个复选框,而那一整块我们早已重写成前端模块架构 —— 逐块保留 HEAD 侧,并把该设置
按新架构接上(否则上游这个新开关在 Linux 侧等于不存在):
view model 增 stable_transcription、SettingsField::StableTranscription、
SettingsDirty 白名单与 merge、宿主回灌与写回偏好、录音卡片里的开关行;
i18n 两个 key 由 sync 脚本从 Tauri 目录写入(904 keys,--check 干净)。
* fix(linux-egui): 遮罩与卡片合并成同一个 Area(点遮罩不再盖住卡片)+ 条带提到 Tooltip
- 阴影上移的真因:egui 在 Area 被按下时把它抬到同层最上面
(egui-0.33.3/src/containers/area.rs:549 move_to_top)。遮罩原本是独立 Area,
于是点一下遮罩就盖住卡片、设置没法用。现在遮罩、点击拦截与卡片同属一个 Area
且遮罩先画 —— 结构上不可能压住卡片。
- 设置打开时改不了窗口大小:遮罩覆盖 body,而左/右/下三侧拉伸条带落在 body 范围内,
被遮罩吃掉。条带提到 Order::Tooltip(层级高一层,仍只占外圈 6px/18px)。
- 卡片矩形改写进 egui memory,供居中/宽度测试查询(Area 面积已不等于卡片)。
* refactor(tauri): 删除 Tauri Linux 专有文件(linux_fcitx.rs / tauri.linux.conf.json)
Linux 桌面不再由 Tauri 提供(已改用 egui 前端 openless-all/app/linux-egui),先删掉
两个只服务 Linux 的文件:
- src-tauri/src/linux_fcitx.rs(976 行):fcitx5 插件桥 —— 热键同步、CommitText 插入、
PRIMARY 选区读取、aux-down 跟随、AppImage 期插件自动安装。egui 侧有自己的
linux-egui/src/fcitx5.rs,不依赖它。
- src-tauri/tauri.linux.conf.json:Linux 平台覆盖配置。
调用点与编译期门禁在后续提交里一并清理(见 7b778d9b、68fe1442)。
* refactor(tauri): 删除 Tauri 的 Linux 桌面实现(Linux 已改用 egui 前端)
Linux 桌面不再由 Tauri 提供,相关实现整体删除:
- src-tauri/tauri.linux.conf.json(Linux 平台覆盖配置)
- src-tauri/src/linux_fcitx.rs(976 行的 fcitx5 插件桥:热键同步、CommitText 插入、
PRIMARY 选区读取、aux-down 跟随、插件自动安装)
- hotkey.rs 的 Linux 平台 adapter 模块(fcitx5 作为热键源)
- unicode_keystroke.rs 的 Linux impl / enigo 错误变体 / Linux 再导出 / 相关测试助手
- insertion.rs 的 Linux insert(fcitx CommitText + 剪贴板回退)与 3 条 Linux 测试
- selection.rs 的 Linux PRIMARY 选区读取模块与两处 Linux 分支
- lib.rs 的 mod linux_fcitx 与 AppImage 期插件自动安装调用
为免误在 Linux 上构建 Tauri 版时面对一堆“找不到模块”的零散错误,lib.rs 增加一条
显式的 compile_error!(仅 target_os = "linux"),指向 egui 前端与出包脚本。
macOS / Windows / Android(target_os = "android")不受影响。
egui 侧未受影响:cargo check --workspace 通过、openless-linux-egui 测试全绿(181+87…)、
package-linux-egui.sh 出包成功。
* feat(linux-egui): 图标装多档 hicolor 尺寸 + 提示音与 Tauri 逐参数对齐
图标(用户要求:复用 Tauri 的图标集):
- egui 侧不能引用 Tauri 源码树(两条契约都禁止脚本出现该路径),所以把
src-tauri/icons 的 5 档 PNG 以**字节相同的副本**放进 linux-egui/packaging/icons/,
由 release 契约做逐字节一致性断言 —— 素材仍是一份,且不会静默漂移。
- package-linux-egui.sh 由「只装一档、且把 512×512 的图塞进 256x256 目录」改为装
32/64/128/256/512 五档 hicolor(256 取自 128x128@2x.png,512 取自 icon.png)。
- 已核对 public/AppIcon.png 与 src-tauri/icons/icon.png md5 相同(7017a34f…,均 512×512)。
提示音(用户要求:与 Tauri 完全一致):
- 参数本就与 audioCue.ts 逐字段一致;本轮补上两处**语义**差异:
* 包络改为 Web Audio 的两段指数斜坡语义
(0.0001 → 5ms 指数 attack 到 peakGain → 指数 decay 回 0.0001),
原来是线性 4ms attack + 任意 tau 的 exp(-5t) 衰减;
* 去掉 egui 自加的「结束提示音」(下行小三度)—— Tauri 的 stopAudioCue() 只停不发声;
现在 play_cue_stop() 等价于作废当前代次(正在播的提示音静音),与 Tauri 同语义;
* 新增代次机制:新提示音开始时在播的旧音立刻静音,对应 Tauri 排期前的 stopVoices()。
- 设置页「试听」继续复用同一实现(main.rs 的 PreviewAudioCue → play_cue_start())。
* refactor(tauri): 清掉已删除的 linux_fcitx 模块的残留调用点
linux_fcitx.rs 与 Linux 平台实现删除后,仍有多处 Linux 专有路径引用已删的模块/函数。
这些调用只出现在 cfg(target_os = "linux") 分支里,但等于给 Linux 构建留一堆悬空符号,
所以按「Linux 桌面不再支持」一并清理:
- coordinator/capsule_focus.rs:整段 fcitx5「候选词下方显示听写状态」的 aux-down 逻辑
(LAST_AUX / RETRY_GEN / set_aux_down / clear_aux_down)。
- coordinator/hotkey_loops.rs:启动前的插件可用性检查、fcitx 通道准备、
start_dictation_signal_listener 启动块,以及 custom_dictation_key_string /
sync_custom_dictation_to_plugin 两个 Linux 辅助函数。
- coordinator.rs:ensure_modifier_hotkey_monitor 里的插件绑定同步、同样的 fcitx 监听
启动块,以及 combo 热键更新路径里的插件同步调用。
- selection.rs:两处「其它平台」兜底不再排除 Linux(非 Linux 代码不动)。
- core_adapters.rs / unicode_keystroke.rs:流式插入的 Linux 分支不再调用已删的
type_unicode_chunk,改返回显式的 TypeError::UnsupportedPlatform。
macOS / Windows / Android 不受影响(这些分支本就不为它们编译;mac 有自己的
switch_to_ascii / restore_input_source 实现)。egui 侧未受影响:workspace check 通过、
openless-linux-egui 测试全绿。
* refactor: 把 Remote Input TLS 身份实现移到平台中立目录 shared/remote_tls
原先 linux-egui 通过 #[path] 直接引 src-tauri 的源码,egui 无法在 Tauri 之外独立构建。
现在两侧(src-tauri/remote_server、linux-egui/remote_input、backend-tests)都引
shared/remote_tls/tls_identity.rs,egui 对 src-tauri 的引用清零,并收紧
linux-egui-tauri-free-contract(去掉 remote_input.rs 的豁免)。
* fix(linux-egui): 修风格市场对官网 API 的请求放大
对齐 Tauri 市场页(src/pages/Marketplace.tsx):
- 搜索加 300ms 防抖(原先每敲一个字符、以及每次输入法预编辑变化都发请求)
- my_likes 从 load_marketplace 拆出,改为每次会话只取一次(原先每次搜索都带一个认证请求)
- 列表请求加单调序号,丢弃过期响应(对齐 Tauri 的 reqSeqRef,latest-wins)
- limit 100 -> 50
原先一次 4~6 字符的搜索会打出 8~12 个请求(半数为认证请求),输入法预编辑下更多。
* [pi] 盘点完成(只读,未改任何仓库文件)。报告已落盘 `/tmp/ime-inventory-report...
* refactor(fcitx5): 删除插件里已无人调用的候选词下方状态弹窗
SetAuxDown/ClearAuxDown 的实现、焦点切换补写、PostInputMethod 恢复、
lastAuxText_ 缓存全部删除:Tauri 的 Linux 调用点已随 linux_fcitx.rs 一起删除,
egui 从未调用过,全仓库已无调用者(纯死代码)。
* mod(selection): 划词润色预览独立弹窗下线,改为复用选区助手(qa)面板
- 删除独立窗口 selection-polish-preview:窗口创建、能力表条目、前端路由
(main.tsx / App.tsx)与页面 src/pages/SelectionPolishPreview.tsx 一并移除
- 后端 show_selection_polish_preview 改为 show_qa_window(app, "polish-preview")
并向 qa 发 selection-polish-preview:shown;hide 只发 hide 事件,是否收起面板
交给前端判断(面板可能正在提问对话中,不能被润色流程收尾一起关掉;与 egui
侧 HostAction::HideSelectionPreview 的语义一致)
- 新增 SELECTION_POLISH_PREVIEW_PENDING 标志:get_selection_polish_preview 只在
真正处于润色模式时返回负载 —— 面板懒创建后挂载补拉不会误入润色模式
- QaPanel 增加「润色结果」模式:标题/副标题、只读结果框、原文摘要、
取消 / 「确认并替换」(= 写回原选区的插入,走 Core selection.confirm);
Esc 与 ✕ 都是取消并关闭。沿用 selectionPolishPreview.* 文案(5 语言已具备)
- 新增 scripts/selection-polish-panel-contract.test.mjs 锁住合并(npm test 自动发现)
- docs/architecture.md 的窗口清单同步
* docs(linux-egui): 注释改为指向 Tauri 侧合并后的选区助手面板
Tauri 的独立润色预览窗口(SelectionPolishPreview.tsx)已下线,注释不再指向已删文件。
* refactor(tauri): 删除 Tauri 侧剩余的 Linux 专用实现
Linux 桌面已由 egui 前端承担,Tauri 不再构建 Linux。本次清掉残留:
- 纯 Linux cfg 块:audio_mute 的 wpctl/pactl 后端、tauri_coordinator_host 的
gtk 版胶囊窗口与 Linux 早退分支、permissions 的 Linux 麦克风探测、lib.rs 的
gtk 光标穿透与 WebKitGTK nudge、main.rs 的 WebKit 环境变量、core_adapters 的
Linux ASR/插入分支、asr/local 的 Linux 后端与空实现、side_aware_combo 的
「Linux 暂不支持」分支
- 12+ 个文件上的 cfg_attr(target_os = "linux", allow(dead_code, ...)) 抑制
- 保留 lib.rs 的 compile_error! 防线(有意拦住误在 Linux 上构建 Tauri)
- 保留 any(target_os = "macos", target_os = "linux") 共享块(macOS 本地 Qwen ASR)
- 补齐非 mac/win 桌面的中立兜底(hotkey 后端桩、insertion 剪贴板兜底、ASR 不可用),
不引入任何 Linux 命名;删除无调用者的 host 包装 switch_to_ascii/restore_input_source
* chore(tauri): 移除 Linux 专用依赖与 wayland-scanner vendor 补丁
- 删 [target.'cfg(target_os = "linux")'.dependencies](dbus 已零引用、gtk 只被删除的
Linux 代码使用)与 keyring 的 unix-except-macos/android 段(含 linux-native-sync-persistent)
- 删 [patch.crates-io] wayland-scanner 与 src-tauri/vendor/wayland-scanner/(只为 Linux
构建修 issue #801 的 quick-xml 漏洞)
- arboard 去掉 wayland-data-control feature
- Cargo.lock 随 manifest 重解析(-379 行)
* refactor(react): 移除前端里的 Linux 分支与专属文案键
- WindowChrome:OS 类型/UA 嗅探/LinuxTitlebar/ol-linux-* 类名/专用常量全部删除,
外壳圆角恒为 0(交回原生装饰),并清掉随之无用的 import 与 SVG 组件
- RecordingInputSection:去掉 os !== 'linux' 条件与 Linux 专属「流式输入」开关行
- AudioCue / localAsr / platform / ChannelList / CodingAgentSection / ShortcutsSection:
本地引擎与能力判断只保留 macOS / Windows / Android
- global.css 删掉 html[data-ol-platform='linux'] 规则(该属性在所有分支都已不再取 linux)
- i18n 9 个语言删除 streamingInsertTitleLinux / streamingInsertHintLinux(React 与 egui 均无引用),
同步更新 experimentalTitles.test.ts;「仅 Windows / Linux」这类平台说明文案保留(Linux 仍由 egui 提供产品)
* test(contracts): 新增 tauri-linux-free 契约并修受影响的既有契约
- 新增 scripts/tauri-linux-free-contract.test.mjs:断言 src-tauri 无 Linux 专用 cfg、
manifest 无 Linux 目标依赖/wayland-scanner patch、vendor 补丁目录不存在、React 无 Linux 分支、
lib.rs 的 compile_error! 防线仍在(反证:注入探针文件后契约确实失败)
- macos-capsule-spaces-contract:函数体边界改为「下一项平台属性」,不再假定紧跟 Linux 实现
- windows-ui-config:外壳断言改查 shellRadius 恒 0;删除已退役的 Linux 标题栏断言
* fix(linux-egui): 胶囊边距按实测面板高度计算,不再依赖 exclusive_zone(-1)
KDE 下胶囊会压在底部面板上:原先靠 exclusive_zone(-1) 让合成器把表面挪出其他
独占区,但并非所有合成器都兑现该请求。现在改为自己量:
- bind wl_output,读 CURRENT mode 得到整屏高度
- 两个一次性 layer surface 探针(anchor=bottom+left+right,size=0)分别请求
exclusive_zone(-1) 与 (0),拿到合成器给出的可用高度,探针随后销毁
- 纯函数 capsule_bottom_margin/PanelInsetProbe 决策:合成器不避让面板时
margin = panel + gap;已避让时 margin = gap;测不到时退化为 gap(原行为)
- 胶囊改用 exclusive_zone(0),边距自己扛;测量值打到 stderr 供实机核对
- 新增 5 个单测覆盖决策树
* Revert "fix(linux-egui): 胶囊边距按实测面板高度计算,不再依赖 exclusive_zone(-1)"
This reverts commit 73c59cfd3750e70dcc07933b5224b79b6574d838.
* fix(fcitx5): 选区探针自行读 PRIMARY,修「旧选区被当成当前选区」
现象:在新选区里选中了没有 text mime 的东西(图片/文件/密码)时,划词助手
拿到的还是**上一次**的选区文本。
根因(已用源码核实):fcitx5 clipboard addon 把 PRIMARY 缓存在全局 primary_,
其 waylandclipboard.cpp 的 DataOffer::receiveRealData() 在 offer 既没有
text/plain;charset=utf-8 也没有 text/plain 时直接 return,回调不触发、setPrimary()
不被调用 —— 缓存里留着上一次的文本。而 IClipboard 公共接口
(clipboard_public.h)只有 primary()/setPrimary(),没有任何"选区变了"或"还有效吗"
的通知,插件无从判断缓存是否过期。
改动:
- 新增 primary_selection.{h,cpp}:用 ext-data-control(KWin 6 提供,
协议规定绑定 device 时补发一次 primary_selection,故一次 roundtrip 即拿到当前
状态)读当前 PRIMARY 的 mime 列表与文本;区分 NoSelection / NoText /
ReadFailed / Unsupported,并沿用 fcitx5 的密码管理器 hint 与 1s→150ms 超时策略。
- ③ 来源优先级改为「合成器此刻的 PRIMARY」优先,应用 surrounding 只在探针给不出
结论时兜底(它是客户端本地缓存,且协议限定 ≤4000 字节)。
- ② NoText 判失效,既不回退 clipboard 缓存也不回退 surrounding。
- ④(部分):更新插件文件头 GetSelectionText 的说明;GetSelectionText 与
CaptureSelectionTarget 共用同一探针,探针不可用才退回缓存。
- 构建:内置 wayland-protocols 1.49 的 ext-data-control-v1.xml(见 protocols/README.md),
由 wayland-scanner 生成胶水;project() 需声明 C 语言,否则生成的 .c 会被
CMake 当作头文件跳过(链接期 undefined reference)。
- 契约:input_target_contract 通过 setPrimaryReader 注入探针结果,新增 ①②③ 用例。
* fix(fcitx5): 探针不可用时才回退 PRIMARY 缓存,并加契约双向覆盖
任务书 ② 要求“新选区没有 text mime 就判失效、不回退旧值”,但严格照做会让
**探针不可用**的场景(X11 会话、合成器没有 ext-data-control)失去唯一来源:
X11/XIM 类应用既不导出 PRIMARY、也没有 surrounding text,于是划词彻底拿不到
选区 —— 相对改动前是功能回退。
改为分级边界:
- 探针**给出结论**(NoText / NoSelection)→ 仍严格判失效,绝不回退旧值(② 的本意);
- 探针**不可用**(Unsupported)且应用 surrounding 也为空 → 才退回 clipboard
addon 缓存,这是最后一级兜底,也是改动前的行为。
为此给缓存读取加了注入缝 selectionCacheReader_(默认实现仍是
cachedPrimarySelection()),契约用例据此同时覆盖两条边界:
- Unsupported + surrounding 空 → 命中 "clipboard-cache-fallback"(返回缓存文本);
- NoSelection / NoText → 捕获为空、getSelectionText() 也为空,缓存里有值也不读。
* refactor(linux-egui): 市场/风格弹窗遮罩统一为「单 Area」,历史确认框加固
「点阴影后阴影上移、设置没法用」的根因是遮罩与卡片处在不同容器:egui 会在指针按下时
把被按到的 Area 抬到同层最上(egui-0.33.3 area.rs:549 的 move_to_top),独立遮罩一旦
被抬起就盖住卡片。设置弹窗此前已改成「遮罩 + 点击拦截 + 卡片同属一个 Area」,这次把
同样的结构铺到其余两处遮罩:
- marketplace.rs:删掉 painter 遮罩 + 独立 backdrop-input Area + Tooltip 卡片的三段式,
合并为单个 openless-marketplace-detail-modal(Foreground)。卡片高度由内容决定(描述
行数可变),所以把上一帧实测高度记在 memory 里居中(首帧用估算值 + request_repaint
收敛),并把本帧**实际画出**的卡片矩形写进 openless-marketplace-detail-card-rect
供测试查询。
- style.rs:同款三段式合并为单个 openless-style-editor-modal(Foreground);卡片尺寸本
来就固定,矩形直接算出并写进 openless-style-editor-card-rect。
- history.rs:confirm_overlay 本来就是单 Area,但 Area 只 sense hover —— 点遮罩会漏到
下方页面(确认框开着时列表行仍会被选中)。改成 Sense::click() 吃掉点击(Tauri 这里用
原生 window.confirm(),点外面不关闭,所以只拦截不关闭),并写
openless-history-confirm-card-rect。
- 前两处顺带从 Order::Tooltip 挪回 Foreground:跨 Order 是 Tooltip > Foreground,弹窗
占 Tooltip 会盖住同层弹出的下拉/菜单。
- layout.rs 的 resize 条带仍是 Order::Tooltip(必须在遮罩之上),未改动。
测试(ui/frontend/mod.rs):新增 assert_overlay_keeps_the_card_on_top 结构性断言与三处
弹窗各一条用例——卡片必须落在弹窗自己的 LayerId 上、在自己的遮罩区里居中、遮罩点上的
输入层也是弹窗自己;点一下遮罩后卡片既不移位、也不会被抬起的遮罩盖住。反向验证:把遮罩
临时挪到另一个容器(不同 Order/不同 Area)后,用例立即失败于
「the card must live in the modal's own layer」,还原后通过。
* fix(linux-egui): 自己窗口里的热键本地兜底,弹窗崩溃重启加预算,QA 热键按 Tauri 切面板
插件只在 fcitx5 的 InputContextKeyEvent 上匹配热键,而我们的窗口从不注册
text-input(winit 默认 ime_allowed=false),所以窗口有焦点时插件一个信号都
不发:主窗口里热键全死,选区助手弹出并开始录音后按语音热键也停不下来。
现在每个窗口进程(主窗口 + 各弹窗)自己做本地匹配,复用设置页录制器的
captured_binding 与注册给插件的 (keysym, states),并与插件信号共用一张去重表
(同一物理按键只处理一次)。
弹窗崩溃(Exited{crashed:true})以前会无条件立刻重开,必崩的面板就变成
无限弹出;改为 60s 窗口内最多重开 N 次,超预算停手并记日志。
QA 热键以前走 Core 的 toggle_recording(切换录音),与 Tauri 的「切换面板显隐」
不一致;现在按 Tauri 语义切面板。
* fix(linux-egui): openless --help/--version 只打印并退出,不再启动应用
* fix(linux-egui): 润色结果沿用选区助手面板的外壳
润色内容并进选区助手面板后,egui 侧仍自己一套排版:没有头尾分隔线、
没有选区助手的 14px 内容边距,看着就像已经下线的独立预览窗口。现在
CardHeader / CardContent / CardFooter 的留白、字号、分隔线全部与选区
助手面板一致(头部还可拖动窗口)。
新增回归测试 the_polish_result_reuses_the_selection_ask_chrome:断言
润色模式画出的头部分隔线与选区助手面板的位置、宽度一致。
* docs: Linux egui 依赖升级计划(依赖批次 / Rust 1.98.1 / Vulkan-wgpu 工作量评估)
把升级调研结论固化进仓库:当前图形栈与全部依赖的可升/已最新清单、
四批执行计划(Rust 工具链、semver patch、10 个 breaking、egui 0.36+glow 0.17)、
Vulkan(wgpu) 的文件级工作量(siri_gl.rs 1090 行 / popup_layer.rs 919 行 / 4 个
GLSL→WGSL)与'双后端不划算、先 profiling'的结论、以及 egui 升级必查的回归清单。
状态:暂缓执行,后续照单执行。
* fix(linux-egui): 修「alt+X 弹出空面板」与「录音热键误弹选区助手」
两个宿主侧状态/时序 bug:
1) show_selection_popup() 先 ensure_popup 再 early-return:没有选区或没
有会话时会把弹窗进程拉起来却一个内容帧都不发 → 凭空出现一个**空的
选区助手面板**(用户报的 alt+X 弹出错误弹窗),并留下
polish_result_visible 脏标志。现在先校验负载再拉弹窗,缺负载时记 warn
并清标志。
2) qa_visible 残留:面板 ✕、弹窗进程正常退出、崩溃重启预算用尽这三条
路径都没清 qa_visible,于是下一次「录音热键」被 apply_local_hotkey_edges
当成「向选区助手提问」,把面板又弹出来(用户报的第二个症状)。现在
统一走 forget_qa_panel_visibility(),并要求 qa_state 存在才算面板在
提问态。
* alt+X 不再弹空润色面板:快照要带选区且结果非空,否则只在语音窗报错
- SelectionStateChanged 以前只看 phase:起手 Capturing 就 ensure_popup +
送一帧空 PolishPreview,于是按 alt+X 会先冒出一个空的润色结果面板
(用户报的「弹窗不对」),没有选区时更是空转一圈再消失。
- 现在要求 phase=Preview 且 preview_text 非空、并且 source_text(捕获到的
选区)非空,才把润色结果帧送进选区助手面板并置 polish_result_visible。
- 没有选区时改为弹语音输入窗(胶囊)报错后退出:文案用 Tauri 的
capsule.selectionPolish.noSelection('未选中内容'),终态 2 秒后自动收起,
不再静默无反馈。
- 诊断日志:有快照但结果不可用时打 debug,无选区时打 warn,便于下次真机定位。
* fix(core): 缓冲转写后台挂接改走注入的 TaskSpawner
CI 的 linux job 卡在 Enforce Linux dependency boundaries:
crates/openless-core/src/dictation_engine.rs:1154 在生产代码里直接
tokio::spawn,绕过 TaskSpawner(check-core-runtime-seam.ps1 明令禁止)。
该行由上游 a8ebcf6ff(#1082 录音优先/稳定模式)引入。
- BufferedTranscriptionInner 增加 task_spawner 字段;
BufferedTranscriptionSession 增 with_task_spawner 构造器,
new/new_with_limit 保持原签名并默认 TokioTaskSpawner(与
BackendDependencies::default 同源)。
- buffered_transcription_session 接收 task_spawner。
- DictationEngine 增加可覆写的 buffered_task_spawner(),默认返回
TokioTaskSpawner;start_transcription_with_progress 用它喂给缓冲区。
- attach_in_background 不再直接 tokio::spawn,行为不变。
* chore(linux-egui): 清掉全部编译警告(死代码与残留状态)
bin 与 test 构建现在都是 0 warning:
- UiResult 里 ProviderMutation / MarketplaceFlow / MarketplaceAuthPoll /
UpdateProgress / UpdateInstalled 五个变体从未被构造(新前端没有市场登录
与「安装更新」入口),删掉变体及其处理分支。
- OverviewSummary 的 last_7 / last_30 恒无读者(前端用 activity_daily 的
尾段自己切窗口):连 ActivityAggregate、aggregate_window 一起删,
测试改为只断言 daily/heatmap 的窗口化。
- 旧 settings 表单草稿字段(qa_input / new_channel_name / vocabulary_* /
correction_* / vocab_preset_* / history_search / marketplace_flow /
style_hotkey_*)在 OpenLessEguiApp 上已无读者,删字段与两处构造初始化
(弹窗进程自己那份 qa_input 保留)。
- ProvidersState::Failed / ProviderEditorState::Failed 的 String 负载、
ProviderEditorState::Loading 的 kind/channel_id 无人读,改为无负载变体。
- 删掉被新 UI 取代的 shortcut_editor / optional_shortcut_editor;
set_style_pack_hotkey 只有测试用,移进 mod tests。
- animated / painted 的初值恒被覆盖,改为延迟初始化。
- 顺手修掉 --help 那个测试上重复的 #[test](重复注册了同一个用例),
以及 popup.rs 测试模块里没用的 use super::*。
* [pi] Work in progress
* [pi] Work in progress
* test(linux-egui): 跟上 egui 0.36 的测试侧 API,并消费 FullOutput 的纹理增量
d055873a 把 egui/eframe 升到 0.36.2 时只改了生产代码,测试目标编译不过
(13 个 E0308/E0599/E0560),另有一批测试在 drop FullOutput 时 panic。
- 0.36 删掉了 CentralPanel::show(&Context, ..) 与 Context::run:测试改用
ctx.run_ui(RawInput, |ui| ..),即取 root Ui 再交给被测函数。
- RawInput 不再有 modifiers 字段:改在 begin_pass 后用
ctx.input_mut(|i| i.modifiers = ..) 注入(仅测试需要)。
- epaint 0.36 起 FullOutput 的 TexturesDelta 未处理就 drop 会 panic
("Dropped TexturesDelta with N unapplied deltas")。无头测试没有渲染器,
新增 run_pass / end_pass 两个测试助手统一 clear,替换 50 处出口。
剩余 6 个 GPU 回调断言仍失败:d055873a 把 siri_gl 的 glow 着色器路径从渲染
路径上摘掉(见该文件第 7 行的 allow(dead_code) 注释),paint() 现在只画 CPU
形状且恒返回 true,warm_up() 是空函数,但测试还在断言 Shape::Callback。
* test(linux-egui): 光效测试跟上「着色器路径已摘除」的现状
d055873a 把 siri_gl 的 glow 着色器从渲染路径上摘掉(paint 改为直接用 CPU
形状画光效并恒返回 true,warm_up 成空函数),但 6 个测试还在断言旧的
「GPU 回调 + CPU 回退所有权」语义。按现状改写断言,等 wgpu 光效落地后再恢复:
- paint_draws_every_mode_on_the_cpu_and_claims_ownership(原
paint_queues_a_gpu_callback):三种模式都画 CPU 形状且返回 true,帧里没有
Shape::Callback。
- warm_up_queues_nothing_while_the_shader_path_is_off:warm_up 不排队、不改
状态,也不影响 paint 的所有权结论。
- a_rejected_driver_still_gets_the_cpu_glow:GPU_FAILED 时 CPU 光效照画并
拿走中心,回调数 0。
- capsule_keeps_the_centre_glow_off_the_gpu_path(原
capsule_queues_the_gpu_glow_per_state):录音/思考的中心光效仍在(CPU
波形折线 / 流体圆点,新增 count_centre_glow 计数),回调数恒为 0,终态
无光效;继续锁死「没有外圈红扫光」。
- recording_capsule_keeps_its_centre_visual:中心视觉的判据补上 CPU 波形。
- capsule_paints_state_specific_content:文字口径按样式分——classic 中心画
「思考中」文字,siri 中心是光效不再叠文字(与 GPU 就绪后的终态一致)。
测试:--lib 192 passed、bin 93 passed、5 契约 PASS、i18n --check PASS。
* [pi] 搞完了,包给你:
* fix(linux-egui): refine titlebar and hotkey capture
* fix(linux-egui): synchronize provider channel editing
* fix(linux-egui): align shortcut handling and settings UI
* fix(linux-egui): refine Siri capsule and popup layout
* fix(linux-egui): keep capsule within desktop work area
* fix(linux-egui): keep capsule native on Wayland
* fix(linux-egui): keep Wayland capsule from stealing focus
* fix(linux-egui): align settings controls in fixed columns
* fix(linux-egui): refine Siri capsule and settings identity
* fix(linux-egui): 设置行固定标签列,模型块照 Tauri 重写
两处用户报的问题 + 三个测试构建遗留错误。
1) 录音与输入等设置页的开关/下拉又贴回标签了。17c8a620 把 row_desc 从
ui.horizontal 换成 allocate_ui_with_layout,但没撑住标签列宽度——egui 只按
内容实际宽度推进光标,于是标签列塌缩成文字宽度,控件紧贴标签(实测同一页
12 个开关的 x 是 235/167/124/138/181/100…)。现在显式 set_min_width,恢复
Tauri 的 minmax(0,200px) + 16px 间距硬列宽。新增几何回归测试
setting_rows_put_every_control_on_one_column 把「所有控件同一列」钉住。
2) provider 编辑器的「模型」块照 Tauri ProvidersSection 重写:
- 模型字段(ChannelModelField):descriptor 带 staticModels 时是预设下拉,
末尾附「自定义模型…」逃生口切手输;无预设时是手输框;值为空且有
defaultModel 时给「填入默认」。
- 可用模型(ProviderTools):当前 endpoint 命中带文档页的预设时按钮是
「查看支持的模型」(打开文档),否则「拉取模型」;拉取为空时明说
「鉴权成功,但没有返回可用模型。」;拉到后是带搜索框的下拉,**选中即写入
模型字段并落地凭据**(Tauri applyModel),不再要求用户再按一次保存。
- orcarouter 这类没有固定清单的服务商,打开编辑器时自动拉一次目录。
仍然替换掉自造的 providers.model_list / btn.list_models 文案,改用 Tauri
catalog 的 settings.channels.modelTitle/modelHint/availableModels 与
settings.providers.*(i18n 同步新增 16 个 key)。
3) 修 ChatGPT 那两个提交留下的测试构建错误(以前根本没编译过,所以从没跑):
popup.rs 里 env 迭代拿到的是 &OsStr,不能和 &str 直接比;fcitx5_contract /
host_contract 还在用 TranslationPressed 的旧 unit 形态;capsule 的 Wayland
断言与 popup_command 实际行为不符(只设 OPENLESS_CAPSULE_PATH,不显式设
WAYLAND_DISPLAY),改成断言「不删 WAYLAND_DISPLAY + 带上 layer 提示」。
测试:lib 192 passed、bin 95 passed、5 契约 PASS、i18n --check PASS、fmt 干净。
* fix(linux-egui): 对齐上游 Beta.2 的窗口层次、侧栏与历史详情
用户拿官方 Tauri Beta.2 实况(视频 2026-09-25)逐项对照,一次修完:
窗口/侧栏底色(左右搞反了)
- 侧栏改 n--ol-sidebar-bg (#f0f0f1),内容区底板从前是 CANVAS (#fafafa) 改回
纯白(Tauri 的 ol-console-main 是 --ol-surface);两个层次之前正好反着。
- 自绘标题栏不再是纯白,改浅灰 #f4f4f5 + 一条底部分隔线(对照视频里那条
标题栏带的灰底);标题栏高度 38 → 36(对齐 Tauri LINUX_TITLEBAR_HEIGHT)。
- 新增几何回归测试 shell_paints_a_gray_sidebar_over_white_content_and_a_
tinted_titlebar,直接钉住「侧栏灰 / 内容白 / 标题栏更灰」。
BETA 徽章与版本号
- 徽章照 Tauri FloatingShell:透明底 + 0.5px 蓝描边(--ol-pill-blue-border)+
圆角 5 + 10px 半粗蓝字(之前是蓝淡底、无描边、圆角 7)。
- 版本号另起一行:侧栏只有 188px,同行会把版本号拦腰折断
(「版本 v2.0.0-」/「Beta.2+…」),视频里上游也是换行显示的。
设置/内容区留白与滚动
- 页面顶部留白 0 → 56px、底部 8 → 24px,对齐 Tauri 的页面容器
(padding: 56px 28px 24px)——「今日概览」「历史记录」这些标题下移。
- 滚动条改预留 8px 槽位(Tauri 是 scrollbar-gutter: stable),不再悬浮压住
右边那列文字。
概览页
- 「概览」导航图标改成上游的 lucide ChartNoAxesColumn(只有三根柱,没有
坐标轴);之前多画了一条 L 形轴。
- 大标题 / 分区标题改用 Medium 字重(Tauri 是 600),字号不变。
- 窄屏判定改成按**窗口宽度**比 720(Tauri useMobileLayout 的阀值),不再是内容区
比 760:主窗口缩到最小(960)时内容区只有 748,之前会错把「星期几张图」和
「最近识别」上下堆叠。窗口太矮时整页滚动,不再把年度活动热力图裁掉。
历史页(按用户要求 + 上游 History.tsx)
- 详情头右侧的三个平铺按钮(删除/重新转录/导出)收进「…」菜单:右对齐、向下
展开,含 播放 / 导出录音 / 重新转录 / 删除(上游还有「重新润色」,egui 后端
还没这个能力)。菜单默认收起,点外面或 Esc 关闭。
- 流水线明细只留「识别」一行(用来语音识别的模型);润色/插入两步不再展示。
- 结果区默认只显示润色结果(胶囊是「润色 · 风格名」),原文靠「查看原文 /
隐藏原文」开关展开,不再默认摆两栏重复内容;润色失败时回退显示原文。
- 新增 i18n key:history.show_raw / history.hide_raw / shell.beta_tag /
history.play_recording(前两个同时补进 Tauri 的 8 个语言文件,与上游一致)。
省略号「···」变「…」
- 根因是字体:本机 fc-match sans-serif 命中的是 Noto Sans CJK SC,CJK 面的
U+2026 是**居中**的全角省略号;把拉丁面(sans-serif:lang=en)排到链首,
与 macOS 的 system-ui(拉丁优先、CJK 回退)一致。
测试:lib 192 passed、bin 96 passed(新增 1 个配色几何测试、改 2 个过时断言)、
5 契约全 PASS、i18n --check PASS、fmt 干净。
* fix(linux-egui): 纯图标按钮的图标居中
历史详情右上角的「…」菜单按钮传的是空 label,原来会按「图标 + 间距」的一半
偏移 3px,看着不居中。
* feat(linux-egui): 对齐历史、词典、风格与市场的第一批交互
* fix(ci): 用 basename 判定 lib.rs,兼容 Windows 路径分隔符
`tauri-linux-free-contract.test.mjs` 之前用 `file.endsWith('/lib.rs')`
判断「Linux 守卫文件」,在 Windows runner 上路径分隔符是 `\`,于是
`openless-linux-egui/src/lib.rs` 的 `cfg(target_os = "linux")` 守卫块被判成
违规,job 直接失败。改用 `path.basename`,并把 i18n 目录的跳过条件换成能同时
匹配 `/` 与 `\` 的正则。
本地 `node scripts/tauri-linux-free-contract.test.mjs` 通过(122 个 Rust 文件、
214 个 React 文件);远端 Windows CI 需在推送后确认。
* feat(core): 速记采用独立历史语义(永久录音、同一 id 更新、免于普通清理)
对齐上游 Beta.2 的 QuickNote:速记不是「history 页改个名」,它有自己的
持久化语义。
- `HistorySource::QuickNote` + `DictationOutputTarget::QuickNote`:录音开始时
先落一条草稿(`error_code = "recording"`),完成时用**同一个历史 id** 覆盖它,
取消时把草稿标记为 `cancelled`,而不是留下半条记录或整条消失。
- 速记不插入文本(`insert_text = false`)且音频永久归档。
- 历史清理策略区分两类记录:普通记录仍受 `retention_days` 与 `max_entries`
限制,速记只受 200 条硬上限约束;`clear()` 也只清普通记录,不再连速记一起删。
- `dictation_output_target()` 让宿主能查询当前会话的输出目标(按钮/快捷键
切换录音时不必猜)。
- 偏好里新增可选的速记快捷键,并接入既有的冲突消解顺序(听写 > 翻译 > 选区
助手 > 速记 > …)。
`cargo test --locked -p openless-core` 通过。
* feat(linux-egui): 速记页、快捷键、风格包图标与翻译页缩放修复
对齐上游 Beta.2 的界面与交互,并修掉用户报告的几处偏差。
速记(QuickNote)
- 新增侧栏页面:复用历史列表/详情,只筛 `HistorySource::QuickNote`;按钮与
快捷键共用同一条开关逻辑(未在速记中则开始永久录音,开始中则结束)。
- 速记页顶部是上游同款可收起的快捷键卡片(录制 / 停用 / × 收起),收起状态
持久化到 `linux-ui-state.json` 的 `quick_note_shortcut_hidden`(该写入改为
「合并单键」而不是覆盖整个文件,避免把语言偏好清掉)。
- 设置页「快捷键与选区」新增速记行;宿主的本地热键、fcitx5 插件(新增
`QuickNoteEvent` / `SetQuickNoteHotkeyRaw`)与去重都接上同一条通路;插件没
这个新方法时不报错(旧插件兼容)。
风格包图标
- 新增 `style_icon.rs`:本地把用户选的 SVG 栅格化成 128px PNG 再交给 Core。
先做 XML 结构校验(根元素、禁用 `script`/`foreignObject`/`image`/`animate*`、
禁止外部 `href`/`url(...)`/`@import`/`on*`),再限 256 KB 输入 / 64 KiB 输出。
- 卡片上的图标按钮(含铅笔角标、自定义时的 × 复位)走真实的
`set_style_pack_icon`;纹理解码按 `(pack id, icon path)` 缓存,不再每帧重解码。
翻译页
- 卡片头部的标题/说明与状态药丸在同一 `horizontal` 里:窄列下文字不换行会把
药丸顶出卡片,egui 的 `columns` 随即把整页宽度记为「内容宽度」,下一帧按更
大的宽度再排一次——这正是用户看到的缩放卡顿与「使用方法」被顶出可视区的
来源。给文字块加上按药丸宽度算出的 `set_max_width`,让说明换行。
- `ComboBox::width` 是最小文本宽度,最终按钮还要加图标与内边距,原来的
`available - 4` 会溢出卡片;改为预留 28px。
- 新增回归测试:大→中→小三种窗口下页面都不得超出视口,且最小窗口下内容必须
高于视口(说明「使用方法」靠滚动可达)。
纠正规则页
- 按上游合成单张卡片:「只看自动收集」改为真正的复选框(此前只是个不可点的
徽章,筛选根本用不了),与「一键清空」同行;规则为空时在同一张卡片内给出
占位说明。
选区助手
- 「保存历史」卡片的图标改为上游同款 40×40 圆角底 + 居中图标,标题/说明按
12/13px 与 5px 间距居中排布,开关尺寸对齐 40×24。
词典
- 筛选标签的选中胶囊只用一个动画值插值(原来在同一帧对同一个 id 写三次不同
目标,胶囊会抖)。
风格/市场
- 卡片角标配色与「衍生自」判定对齐上游(作者与来源作者相同时也算衍生);
市场卡片去掉未打开详情就出现的安装按钮,详情弹窗按 560px 固定宽度居中。
i18n:`src/i18n/*.ts` 与 `linux-egui/src/i18n.rs` 同步(速记导航、快捷键卡片、
图标提示等)。`node scripts/sync-egui-i18n.mjs --check` 无漂移。
* chore(release): 同步上游 Beta.2 的两条发版约定
按上游 Beta.2 的修法同步(上游 commit
|
||
|
|
51f6f4cfa7 | feat(ui): integrate desktop panel refresh and session-safe voice controls | ||
|
|
62e09f1f1d | fix(beta3): correct installer layout and refine agent progress feedback | ||
|
|
9731a7f082 | feat(beta3): integrate encrypted sync and macOS experience updates | ||
|
|
06db7d2978 |
chore(release): integrate reviewed changes for Beta 3 build 20260925
Integrate Android CI acceleration, modifier-only dictation chords, Requesty, and remote-input generation guards over the verified Beta 2 baseline and macOS CI improvements. Fix review findings in signing configuration caching, APK validation, and modifier arbitration/recording. Keep Linux egui and its release workflows identical to Beta 2. Co-authored-by: HKLHaoBin <we3q@qq.com> Co-authored-by: leolulu <35873316+leolulu@users.noreply.github.com> Co-authored-by: Thibault Jaigu <thibault.jaigu@gmail.com> Co-authored-by: Ethan <257484394+EthanYoQ@users.noreply.github.com> |
||
|
|
b268dcf978 | perf(macos): 加速 Rust CI 与桌面打包 | ||
|
|
16c3f841b5 |
chore(release): prepare 2.0.0-Beta.2 build 20260924
Integrate the reviewed 2.0 PRs and local fixes from 03136853. Preserve the verified subtitle behavior and model/runtime corrections. Keep the numeric Beta tag compatible with released clients while exposing the date as application build metadata. Co-authored-by: DepengWang <2818245+DepengWang@users.noreply.github.com> Co-authored-by: HKLHaoBin <we3q@qq.com> Co-authored-by: Nahida <1139500183@qq.com> Co-authored-by: TRIP <1933142963@qq.com> Co-authored-by: chyuwei <weichangyuwcy@gmail.com> Co-authored-by: leonoxo <leonoxo@gmail.com> Co-authored-by: tayir <arnoldincredible@126.com> |
||
|
|
c8bd7aa295 |
fix(foundry): keep local ASR warm and stream transcripts (#1052)
* fix(foundry): keep local ASR warm and stream transcripts * fix(foundry): cancel stale release schedules |
||
|
|
82357e8df3 |
fix(android): 对齐应用内更新 minisign 并修复凭据 vault (#1061)
* fix(android): parse Tauri-wrapped minisign keys so in-app APK install can open Android updater called PublicKey::from_base64 on the wrapped tauri.conf pubkey, so install died before installApk. Also keep vault envelope failures from looking like an unconfigured Volcengine ASR provider. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(linux-egui): 补齐根证书指纹字段并让 fcitx5 门禁匹配实际安装调用 Release Linux egui 在 beta 上因 RemoteInputStatus 缺字段、match 臂类型不一致,以及门禁要求 ensure_fcitx5_ready(?) 而失败;安装调用本身已在监听器之前,错误会展示在 UI 里。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): Keystore 暂时不可用时不要让 Tauri setup abort Core.start() 会走 configuration_snapshot → load_credentials_for_update;信封读失败若返回 Err,setup hook 直接 SIGABRT。改为记录 last_read_error 并返回未缓存默认值,应用可以启动。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 凭据信封暂时读失败时不要挡住 2.0 启动握手 Core.start() 在 setup 成功后还会被 event bridge 和 get_startup_snapshot 再调一次;此时 last_read_error 已记下,require_readable_vault 把 Persistence 抛成「暂时无法启动」。握手改为降级空凭据状态继续运行。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 创建渠道时重试 Keystore,失败不覆盖空信封 启动时信封暂时读失败后,require_readable_vault 会在真正 load 之前短路,导致添加提供商一直操作失败。改为每次读写都重试 Keystore;读失败返回 Persistence 且不缓存/不写入默认空根;前端展示后端具体错误。 Co-authored-by: Cursor <cursoragent@cursor.com> * debug(android): 把 Keystore 暂时不可用的具体异常名带回日志 添加提供商已会重试信封,但 Keystore 仍返回 temporarily unavailable。JNI 原先丢掉 Kotlin 异常与 JNI 失败路径,无法区分 class load、status 3 还是 BackendBusy。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 无法用于 Cipher 的 Keystore 密钥按失效信封恢复 真机 open 稳定抛出 InvalidKeyException(非 UserNotAuthenticated)。继续当成 temporarily unavailable 会让已保存凭据永远读不出来。改为走密钥缺失路径,删除无法解密的信封后可以重新配置提供商。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 凭据 Keystore 写入改走主线程并换用 v3 别名 清掉失效信封后,增加提供商仍在 seal 上失败(ProviderException/KeyStoreException)。HyperOS 上 Keystore2 从 JNI 后台线程调用常失败;改为主线程执行,并换用未损坏的 v3 别名,失败时删除别名再生成一次。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): KeyMint AES-GCM 失败时回退到应用私有软件密钥 HyperOS 上 seal 稳定返回 KeyStoreException 10,主线程与 v3 别名仍无法写信封。Keystore 不可用时改用 UID 私有软件 AES-GCM,避免增加提供商失败。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): preserve credentials across key fallback --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
4ec44b2b0b |
merge: 汇入 origin/beta 第二轮(#1048 根证书指纹核验、#1049 录音保亮屏)
- 五语 PC/手机页文案取云端指纹核验版;de/es/fr 全套补译(指纹 6 键、保亮屏 4 键、恢复 4 键、中断/离线 2 键) - index.html:帮助块改为指纹核验流程 + 录音屏新增保亮屏开关(默认开) - RemoteInputStatus 补 caFingerprintSha256;Linux egui 面板移植指纹显示与复制 - remote-input-audio-queue 测试取云端(#1049 音频队列改造),我方仅格式差异 |
||
|
|
3ff3df1626 |
fix(merge): 落实合并后的契约与目录对齐
- build-mac.sh 移除 hdiutil DMG 兜底:bundle contract 测试显式禁止手搓 DMG 绕过签名/公证链路;DMG 缺失即失败(保留 qwen3-asr-rs 剪枝与 BUILD_START_TS 新鲜度校验) - 红绿灯 y=26 契约:tao 几何下视觉顶距≈y-14,x==y 并不相等(实测左 21.5px) - 胶囊几何契约同步 typeless 206×57(原 460×128 的 1/5 面积) - LLM_LABELS 补 tencentTokenHub 静态目录项(ASR_LABELS 已含 tencent-cloud) |
||
|
|
f39a68a795 | 修复远程录音息屏丢失并默认保持亮屏 (#1049) | ||
|
|
bd5656b5fc |
build(macos): 构建前清理重复的 qwen3-asr-rs 目录;新鲜度基准改为构建开始时间
- 本地混跑 cargo check/test/build 会按 feature 上下文生成多份 qwen3-asr-rs 构建目录,多份 metallib 会让暂存脚本拒绝猜测 - 打包后原始二进制还会被签名触碰,bundle 新鲜度不能与它比,改与本构建 开始时间比 |
||
|
|
e3f63e97fc | build(macos): 兜底校验加新鲜度——打包失败时旧 bundle 不允许再通过校验 | ||
|
|
ce43e1b11b |
build(macos): DMG 兜底(hdiutil + Applications 替身)并按第一性原则收紧注释
- tauri build 退出码非零不再夭折脚本:.app 已生成时用 hdiutil 兜底打包 DMG, 内容含 /Applications 替身,保留拖拽安装 - Cargo.toml Xcode 27 beta 构建注释压缩为一行事实 + 对策 |
||
|
|
9a88bee5c6 | feat(app): polish desktop UI, expand languages and prepare cloud sync | ||
|
|
4519f19c92 | style(app): 统一前端与平台源码格式 | ||
|
|
d686406a70 | refactor(app): 清理废弃代码并明确模块注释 | ||
|
|
a6ed77c0d0 | feat(ui): 完善设置子页与自定义风格图标 | ||
|
|
69f454bd68 |
feat(ui): integrate 2.0 interface and agent work
Consolidate the current 2.0 sidebar, overview, settings, vocabulary editing, correction rules page, and first-launch splash with their Core and Tauri adapters. Include the accompanying source and architecture documentation updates. Validation: npm test (build and 73 discovered test files); cargo test -p openless-core --locked; Core/Linux and Tauri rustfmt checks. |
||
|
|
82aa33ffd6 |
fix(windows-ime): 避免 TSF 预检导致文本重复插入 (#1021)
Co-authored-by: tonyzaca7755963 <tonyzaca7755963@users.noreply.github.com> |
||
|
|
fd632c2fc7 |
fix(windows): 粘贴模式下也可隐藏键盘列表中的 OpenLess (#1015) (#1017)
* fix(windows): allow hiding OpenLess from keyboard list in paste mode Extend the keyboard-list visibility toggle and desired-profile logic from SendInput-only to all non-TSF insertion modes so clipboard paste can hide OpenLess without blocking TSF when switched back. Refs: #1015 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(macos): 把 mlx.metallib 放到 Resources,避免 ad-hoc codesign 失败 Contents/MacOS 里的非 Mach-O 会被当成 nested code;克隆仓没有 Developer ID 时 identity '-' 会直接 bundle 失败。metallib 改为 Contents/Resources,worker 启动时调用 MLX set_metallib_path。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ci): dispatch 时按版本号跳过 Windows MSI,避免 Beta.N 被 WiX 拒绝 workflow_dispatch 的 ref 不是 tag,OPENLESS_RELEASE_CHANNEL 会落成 stable,但 app 版本仍是 1.3.18-Beta.7。WiX 要求 prerelease 只能是数字,MSI 直接失败。版本带 Beta/Alpha/RC 时与 beta 渠道一样跳过 MSI。 Co-authored-by: Cursor <cursoragent@cursor.com> * ci(linux): test core without egui host --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
fc38edd1a3 |
refactor: consolidate Core 2.0 and restore cross-platform behavior (#1019)
* refactor: split Linux egui host from shared backend * fix: keep CI contracts portable across runners * fix(ci): preserve CRLF while removing qwen dependency * fix(ci): regenerate target lockfile and install ripgrep * fix(ci): gate desktop-only selection voice module * ci: limit native build parallelism on cross-platform runners * fix(host): expose blocking task seam to coordinator * fix(settings): avoid macOS default hotkey collision * docs: record cross-platform CI evidence * ci: expose Linux validation package artifact * ci: grant reusable Linux package permissions * refactor: remove legacy coordinator dictation facade * ci: skip unsupported MSI prerelease builds * ci: install minisign only for signed Linux releases * fix: keep test-only coordinator session helper * refactor: move style preview diagnostics into core * fix: import shared preferences type from core * refactor: centralize enabled vocabulary projection * ci: install ripgrep for Linux package gates * test: make source contract line-ending agnostic * fix: keep Linux desktop entry standards compliant * docs: expose core style diagnostics contract * ci: resolve Linux package version in app directory * refactor: centralize ASR vocabulary priority * fix: use shared vocabulary note constant * refactor: keep vocabulary priority inside core * test: retain starting-stop queue coverage * ci: scope Linux updater URL to the current repository * docs: close implementation and artifact evidence * docs: record final CI artifact evidence * fix(android): align updater plugin versions * fix(android): pin updater crate exactly * refactor(core): remove unused type anchor * docs: record final artifact runs * fix(remote-input): preserve beta status fields * fix(tauri): finish beta host merge * fix(asr): pass scoped operation to native qwen adapter * fix(macos): bundle mlx metallib as a resource * test(remote-input): update status wire contract * feat: share Less Computer voice flow with Linux * refactor(core): centralize host-independent runtime policy * refactor(core): complete 2.0 backend consolidation * fix(ci): prepare Sherpa after cache restore * fix(macos): route native model paths through Core store * fix(macos): use ModelStore catalog API * fix(ci): serialize macOS native test build * test(ci): preserve macOS cargo diagnostics * fix(macos): release input source lock before await * test(core): replace legacy hotkey injection gate * test(core): move Codex sandbox contract into Core * fix(core): close final runtime policy gaps * fix(windows): terminate coding agent process trees * fix(core): complete local model migration * fix(credentials): align Core LLM model account * fix(core): restore 1.x parity across shared backend * fix(android): complete shared backend stubs * fix: close cross-platform migration review gaps * fix: complete migration lifecycles and platform compatibility * docs: define desktop 2.0 acceptance and Linux handoff * fix: close desktop parity and session lifecycle gaps * fix: finish voice lifecycle and desktop feedback parity * fix: close desktop cancellation and feedback race gaps * fix: honor proxy settings and reject stale session effects * fix: dispatch native cleanup on host runtimes and order events * fix: keep optional recording effects best effort * fix: close desktop agent model and QA lifecycle gaps * fix: preserve desktop raw voice and QA turn ownership * fix: finalize desktop dictation and QA feedback |
||
|
|
2cf5069846 |
fix(asr): 修复 macOS MLX 后端闪退与发布资源缺失 (#999)
* fix/local-qwen-asr-exit: 修复使用本地的qwen3-asr识别闪退的问题 #986 * add mlx error * 修复github action打包出来的dmg运行本地qwen3-asr-rs时崩溃的问题 * fix(asr): 隔离 macOS MLX worker 并修复发布资源 * fix(asr): 修复 macOS worker 超时与 socket 测试 * fix(asr): scope MLX cancellation to owning operation * fix(asr): preserve abort semantics on target switch --------- Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
58c0ade3ab |
fix(windows-ime): make TSF shutdown fully cancelable (#991)
* fix(windows-ime): make TSF shutdown fully cancelable Remove the uncancelable named-pipe flush, post owned submit requests to the TSF owner thread, cancel pending edit sessions during shutdown, and keep the DLL loaded while asynchronous edit sessions remain alive.\n\nRelated to #954. * fix(windows-ime): handle uncertain submits and startup failures * fix(windows-ime): keep untrusted responses outcome-unknown |
||
|
|
ec33bd5dd2 |
fix(android): 修复小屏大字号下的设置与风格页面排版 (#960) (#983)
* fix(android): 修复移动端设置与润色页排版及底栏遮挡 (#960) 设置弹窗 z-index 低于底栏导致底部控件无法点击;润色风格/市场页与 Android 权限区补齐 mobile 布局。 Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): 通用设置新增易读布局,大字号小屏防溢出换行 新增 stackedRowLayout 偏好与 useLayoutStack;设置外观区可手动开启同行控件换行,覆盖老人机等极限场景。关联 #960。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 安全查询悬浮窗权限,修复 HyperOS canDrawOverlays 闪退 WebView IPC 线程不再直接 JNI 调用 Settings.canDrawOverlays,改走 Kotlin 主线程桥接并捕获异常,避免 Redmi/HyperOS 在权限轮询时崩溃。 Co-authored-by: Cursor <cursoragent@cursor.com> * feat(ui): 保守排版开关与易读布局 min-width 增强 新增 conservativeLayout 偏好与 ol-conservative-scope 单列满宽规则;易读布局补充 min-width 换行;设置与主要页面支持双开关独立并存。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): can_draw_overlays JNI 生命周期以通过 Android CI 编译 为 env 与 context 绑定同一 lifetime,满足 call_static_bool_with_context_class 的借用约束。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 恢复 AccessibilityService 对 Settings.Secure 的 import isEnabled 仍使用 Settings.Secure 查询无障碍状态,与 canDrawOverlays 移除的 import 无关。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): 将易读/保守排版开关独立为「布局」区块并置顶 原开关藏在「外观」卡片深处不易发现;现于设置→通用页录音输入下方展示独立「布局」卡片。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): 统一易读与保守排版布局规则 --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
b7ac91f0e6 |
feat(coding-agent): Less Computer 接入 dsh(DeepSeek Harness),自带事件插件补齐流式与工具展示 (#975)
* fix(coding-agent): 版本号解析认预发布号 dsh 的真实输出是 `0.1.0-rc.6`,而解析器要求严格的三段全数字:它被切成 ["0","1","0-rc","6"] 四段,判定失败。用这个判「装没装」的地方就会说它没装。 改成认 `-rc.6` / `+build.7` 这类预发布与构建后缀,并原样保留(用户看到的版本号才是 真的)。同时把排版噪声挡在外面:`(1.2.3)` 取到的是 1.2.3,不是 `1.2.3)`。 单测里的样例串是手抄的,抄错了测试照样绿。所以另加一条 #[ignore] 的 live 测试,直接问 本机四个 CLI 要 --version 输出再喂给解析器——那才是「设置页会不会误报没装」的可信证据。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(coding-agent): 「装没装」改看命令能否跑通,不再依赖版本号解析 上一条修的是解析器,那只是补了一个洞。根子在于判据选错了: installed: version.is_some() // 解析不出版本号 = 判定没装 版本字符串是这些 CLI 里最容易变的东西——改排版、加后缀、换前缀,对它们都不算 breaking change,但对「拿版本号当判据」的我们就是。 改成:命令跑通(进程起得来且 exit 0)= 装了;版本号只用来显示,解析不出来最多让界面少 显示一个号码,不会把能用的后端说成没装。三个后端共用一个 probe_cli,顺手删掉了三份 几乎一样的 detect_* 。 单测拿 `echo` 当替身——`echo --version` 会原样打印并 exit 0,正是「跑得通但读不出版本号」 这个形状,且不依赖本机装了哪些 agent CLI。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(dsh): 无头 dsh 后端,自带事件 tap 插件 dsh(DeepSeek Harness)的 headless 只在结束时往 stdout 打一行最终文本——没有 JSON、 没有逐字流、没有工具事件。但这不是它缺数据:它内部的会话事件总线上 assistant/chunk、 tool/call、turn/end 一应俱全,只是 headless 汇总完就丢了。 dsh 是插件栈架构,`--patch` 是官方留给外部叠自己那层的入口。这里挂上一个订阅 session/event 的小插件,把需要的事件规范化成 NDJSON 打到 stderr,stdout 的「只打最终 文本」契约原样不动。挂载不侵入用户的 profile,插件随运行落到临时目录、跑完删掉。 上游耦合被刻意压缩在那个 30 行的 JS 里:它把 dsh 的内部事件翻译成我们自己定义的稳定 形状,上游改字段只需要改 JS,Rust 的解析和单测都不用动。tap 是纯增益——挂不上也只是 少了逐字流和工具展示,最终结果照样从 stdout 拿得到。 prompt 不进 argv:dsh 的启动器不认 `--` 作为选项终止符(实测 `dsh --profile headless "--version"` 会真的去执行 --version),所以以 `-` 开头的转写会被劫持成 flag。既然本来 就要写 patch 文件,索性用它直接覆盖 runner 的 task 配置,prompt 连进程列表都不会出现 ——这点比另外三家都严。 护栏走 DSH_PERMISSION_MODE 三档沙箱,沙箱根 = 子进程工作目录。和 Codex 一样没有逐命令 deny 清单,所以审批卡对它同样不生效,撞墙时如实报错。 模型解析永远返回 None:headless profile 没有 --model,模型由 dsh 自己的 profile 决定, 返回 Some 只会让调用方以为选得动。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(settings): Less Computer 后端下拉加入 dsh 第四项。它的能力面和另外三家不一样,设置页如实反映,不给假开关: - 模型那一行**整行不显示**,换成一句说明:dsh 的 headless 没有模型开关,模型由它自己的 profile 决定,在这里改不了。给个选不动的下拉比不给更糟。 - 复用 Codex 那条沙箱护栏说明(两家都只有粗粒度档位、都不弹审批卡)。 - 装没装当场检测;可执行文件 placeholder 跟着后端走。 文案 5 语言齐全。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(dsh): tap 插件抽成独立项目 dsh-jsonl,OpenLess 改消费它的公开 schema 原来这个插件是内嵌在 dsh.rs 里的一段 Rust 字符串,只发我们自己要的 4 类事件、用我们 自己的私有形状。它要开源出去(dsh 缺结构化输出,而上游不收 PR,第三方插件是这个能力 唯一可能存在的形式),于是有了「两份会各自跑偏的代码」这个问题。 解法是保证世界上只有一份这个 JS: - 源头在 github.com/bigsongeth/dsh-jsonl,发完整的、有文档、有版本的事件词表。 - OpenLess 用 include_str! 内嵌一份逐字副本(vendor/dsh-jsonl.js),带版本标记。 内嵌而不是依赖 npm 包:用户装机即用,不需要网络、不需要 npm i,也不碰用户自己的 dsh profile 配置。 - Rust 侧改成解析那个公开 schema(text.delta / tool.call / turn.end),不再有私有形状。 用不上的事件按 schema 的兼容性约定忽略——上游加新事件不会把我们打挂。 **跨仓库没有任何自动同步**,这点没被消除,只是变便宜了: - scripts/vendor-dsh-jsonl.sh 把同步变成一条命令 - 单测盯着 vendor 文件的版本标记与 VENDORED_DSH_JSONL_VERSION 一致,防止有人直接手改副本 - 插件报的 schema 大版本与我们支持的不一致时留警告日志(不中止:最终结果始终从 stdout 拿得到,流式展示只是锦上添花) - 真的对不上时,live::tap_plugin_delivers_streaming_and_tool_events 会红 顺带把「禁止任何 import」的断言放宽成「只准 node: 内置」——公开插件要写文件,用了 node:fs,而 node: 内置不需要 node_modules,不影响从临时目录绝对路径加载。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(dsh): 沙箱验收的靶子选错了,它一直是靠运气过的 那条「沙箱挡得住工作目录之外的写入」把靶子放在 $TMPDIR。但 dsh 的 workspace-write 把 可写根写死成 [工作目录, "/tmp", $TMPDIR](见 @deepseek-ai/dsh-sandbox 的 writableRoots) ——往 $TMPDIR 写**本来就是允许的**。 它之前能过,只是因为模型碰巧挑了受限更严的 `write` 工具;这次挑了 `bash`,文件就真的 落下去了。也就是说这条测试的成败取决于模型当次的选择,不取决于沙箱是否失效。 改成打真正的边界(家目录),实测会被拒: bash: /Users/…: Operation not permitted [sandbox: file access denied under workspace-write mode] 另加一条把 dsh 的既有行为钉成契约:$TMPDIR 可写,且**没有配置项能收掉**——Codex 那边 有 -c sandbox_workspace_write.exclude_* 可以收紧,dsh 没有对应物。哪天 dsh 收紧了这条 会红,那是好消息,届时删掉它并更新文档。 所以对用户的措辞只能是「撞到限制会如实报错」,不能说成「写入限制在工作目录内」。 模块文档里把这个边界写清楚了。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(dsh): 收紧运行协议与权限边界 * fix(dsh): 修复权限与连续对话审查项 --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
5ba0fa725c |
feat(coding-agent): Less Computer 接入 Codex,语音使唤电脑不再只能用 Claude (#974)
* fix(coding-agent): 从用户 shell 取真实 PATH,打包后也找得到 CLI macOS 从 Finder 启动的 GUI 进程拿到的是一份极简 PATH,不经过用户的 shell 配置。 后果是:凡是版本管理器装的 agent CLI 在打包版里一个都找不到——`opencode` 官方脚本 装在 `~/.opencode/bin`,全局 npm 前缀和 nvm 的路径更是带版本号、根本没法硬编码。 用户只能手动去高级设置里填绝对路径。 改成先问用户的 shell 要一份真实 PATH。三个细节是踩出来的,都写进注释了: - 用 `-lic` 而不是 `-lc`。nvm 这类版本管理器通常在 .zshrc 里初始化,而 .zshrc 只有 交互式 shell 才读;`-lc` 拿到的 PATH 里 nvm 排在 /usr/local/bin 之后, `#!/usr/bin/env node` 会命中错版本的 node,子进程直接崩。 - 拼接必须保序。逐项前插等于把整个 PATH 倒过来,同样会选错解释器。 - 分隔符交给 std::env::split_paths / join_paths,不能手写 `:`。Windows 用 `;`, 而且盘符自带冒号,手写会把 `C:\Windows` 切成两半、整条 PATH 报废。 shell 卡住有 5 秒硬上限,超时回落到静态目录列表。整段探测限定 unix:Windows 没有 「登录 shell 的 PATH」这个概念,装了 Git Bash 的机器 SHELL 有值,真去跑只会莫名其妙 拉起一个 bash。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(codex): 无头 Codex 适配器 Less Computer 现在只认 Claude Code 和 OpenCode,因为每个后端都得手写一套适配器。 这个是 Codex 的那套,形状和前两家一致,共用同一套请求/事件/错误类型。 - 运行:`codex exec --json`,逐行解析 JSONL。 - prompt 走 stdin(末尾的 `-`),不进 argv,也不会出现在进程列表里。 - 续接用 `resume --last`。参数顺序有坑:`-s` / `--json` 这些必须排在 `resume` 之前, 写反了 CLI 直接报 unexpected argument,已对 0.146.0 核实并写进注释。 - 工具展示会剥掉 `/bin/zsh -lc '…'` 这层 shell 包装,胶囊里显示的是真正执行的命令名。 - 护栏:Codex 给不了逐命令 deny 清单(`.rules` 只从 $CODEX_HOME / 项目目录读,实测 `-c rules=[…]` 会被静默忽略)。它的护栏是自带的 seatbelt 沙箱,走 `-s <mode>`。 额外关掉 workspace-write 对 /tmp 与 $TMPDIR 的默认放行——不关的话「写入限制在工作 目录内」就是句假话,实测任务真的能在 $TMPDIR 里落文件。 有一处刻意不做:Codex 会把「模型元数据没找到」这类警告也塞成 error 事件,本机每次运行 都会发一条。把它当终局失败会让所有运行都被误判,所以只认 turn.failed。 联机验收默认 #[ignore],其中一条不调模型不花钱:它比对加固前后实际生效的可写根, 盯住护栏别被上游无声改宽(codex 对未知 -c 键是静默接受的,改名了不会报错)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(coding-agent): Less Computer 可选 Codex 后端,审批卡按能力启用 把 Codex 接进语音链路,后端从两家变三家。 护栏这块两类后端不一样,代码里如实分开:Claude / OpenCode 有逐命令 deny 清单,撞了能 弹审批卡、批准后放行那一条重跑;Codex 只有粗粒度沙箱档位,唯一的「放行」办法是整体 降档,那等于把护栏关掉,不是放行这一条。 所以给 provider 加了一个能力位,只有支持逐命令放行的后端才弹审批卡。否则用户点了批准、 重跑还是同样被拦,那是个假承诺。撞墙时直接把失败如实报出去。 模型解析也按后端分开:Codex 收裸模型名(gpt-5 / o3 这种),留空交给它自己的 ~/.codex/config.toml,不替用户瞎猜一个默认。 另加一个通用的安装检测命令:选了后端却没装 CLI 时,在设置页当场看到,而不是等说完 一句话才发现跑不起来。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(settings): Less Computer 后端下拉加入 Codex 设置页的后端下拉从两项变三项,并且如实反映能力差异,不给假开关: - 模型:Claude 用别名下拉,OpenCode 拉账号可用列表,Codex 改成自由文本——它的模型名 是裸名(gpt-5 / 自建网关的任意名字),枚举不过来;留空 = 用 Codex 自己的默认。 - 护栏:选了 Codex 会挂一条说明,讲清楚它只有粗粒度沙箱、撞到限制会直接报错而不是弹 「批准这条命令」的卡片。免得用户按 Claude 的心智去用。 - 装没装:当场检测并提示,没装时按警示色显示。 - 可执行文件的 placeholder 跟着后端走。 文案 5 语言齐全。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(coding-agent): harden Codex execution --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
cb3d3f8a3a | fix(local-asr): preserve child state across refreshes | ||
|
|
3d2811dcae | fix(release): link Apple compiler runtime for MLX | ||
|
|
1933a283c6 | chore(release): prepare 1.3.18-Beta.5 | ||
|
|
42c1b31754 |
本地模型增加whisper相关模型的支持; 增加qwen3-asr的rust版本, 相比C版本速度更快 (#940)
* feat: add selectable local ASR backends * fix(build): 修复本地 ASR 多平台构建与旧渠道兼容 * fix(asr): 修复 MLX 权重跨线程 stream 错误 * fix(ci): exclude MLX Qwen3 from non-macOS jobs * fix(ci): use app-relative ASR submodule path * fix(android): skip qwen-asr C backend * fix(android): guard qwen-asr by target triple * fix(ci): 修正语法错误 * fix(ci): remove invalid checkout step options * fix(asr): harden local backend integration * fix(asr): 处理 PR #940 审查发现(9 项) - strip: 全局保留 symbols,macOS release profile 单独退回 debuginfo(Homebrew rustc 的 LINKEDIT 问题仅限 macOS) - 转录超时改为驱逐引擎而非无效 cancel():spawn_blocking 解码任务无法被中止,驱逐后下次会话加载新引擎而非排队 - Turbo/Q5 共享目录模型在 is_downloaded/downloaded_bytes/delete_model 上互相认账 - MLX 临时 WAV 用 RAII guard 兜底清理;HF tree API 改 Link rel=next 游标分页(offset 被服务器静默忽略),带防御上限与 5 个单测 - Intel Mac 渠道下拉不再闪现 MLX 预设;MetalToolchainGuide 默认折叠并标注仅源码构建期要求 - qwen3-asr-rs 子模块 URL 与 gitlink 指向 Open-Less org 镜像,嵌套 mlx-c 同步改指 org(供应链 N-6) - README/注释补充:所有平台源码构建均需递归初始化子模块(path 依赖跨 target 解析) * fix: harden local whisper model lifecycle and downloads * fix(asr): address PR 940 review findings * fix(ci): add local ASR model stub to backend tests * fix(linux): bundle and sync fcitx5 selection plugin * fix(macos): avoid duplicate whisper test module * fix(history): make detail sibling keys unique * fix(ci): stub macos whisper model constant * test(ci): avoid macOS streaming timeout race --------- Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
fac8ddc685 |
fix(ui): 修复模型选择刷新闪烁 (#970)
* fix(ui): 修复模型选择刷新闪烁 (#958) * test(polish): 放宽流式首字测试时序裕量 * fix(ui): ignore stale LocalAsr refresh results |
||
|
|
ec5ea4b07a |
fix(vocab-card): 收起添加词卡片后,胶囊要回到屏幕中间 (#969)
* fix(vocab-card): 收起卡片时把胶囊的位置也还回去
卡片和录音胶囊是同一个窗口。弹卡片要把窗口挪到右下角,收卡片时
`hide_vocab_suggestion_card` 还了尺寸、还了鼠标穿透,唯独没还位置 ——
于是下一次录音,胶囊出现在右下角。
漏这一步之所以致命,是因为 `maybe_position_capsule_bottom_center` 有个
去重缓存,而那个缓存只记「显示器 + 翻译态」。卡片这一挪它一无所知,
下次录音时拿到相同的显示器快照就判定「没变化」,直接跳过重新定位。
换句话说:窗口被挪走了,而唯一会把它挪回来的那段代码以为自己不用动。
所以复位和清缓存两件事都做,各堵一个方向:清缓存保证「就算这次复位
失败,下一次 emit_capsule 也一定会重算」;复位保证「就算有哪条路径
绕过 emit_capsule 直接 show,窗口也已经在对的地方」。
顺带把同一形状的另一处堵上:卡片直接调 set_ignore_cursor_events,却没
同步 `capsule_cursor_passthrough` 那个缓存。目前触发不了,但一旦缓存和
窗口真实状态分家,emit_capsule 就会跳过它该调的那一次 —— 表现是经典
胶囊上的 ✓/✕ 点不动。
复位顺序改成先 hide 再改几何:尺寸和位置要一起动,窗口还亮着时改就有
概率被合成出一帧「卡片被拉宽、还横着飞过半个屏幕」。
真机量过(临时脚手架 + CGWindowList 取窗口 frame,外接屏 1920x1080):
修复前 录音 → x=453(居中)→ 卡片 x=1299(右下)→ 收卡片后窗口
仍在 x=1299 → 下一次录音胶囊 x=1299 ← bug
修复后 录音 → x=526(居中)→ 卡片 x=1168(右下)→ 收卡片当场回到
x=453(居中)→ 下一次录音胶囊 x=453 ← 对
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit d3fedcd13da0a3ba002a6f29dda330eeabee62f1)
* test(capsule): 把「卡片借走胶囊窗口必须完整归还」钉成契约
上一条修复(收起卡片时把胶囊位置也还回去)在 8 月丢过一次 —— 它只活在
一个没合并的本地分支上,主线从 PR #917 重新长出了漏还位置的版本,于是
真机 bug 原样复发:用过一次带添加词的卡片,下一次录音的胶囊就出现在
右下角,再也回不到底部居中。
这类不变量单测抓不到:整段是 Tauri 窗口调用,跑在 run_on_main_thread 的
闭包里,没有窗口就没有断言对象。所以按 repo 既有的做法收进
macos-capsule-spaces-contract —— 那个文件已经是「胶囊窗口几何」这类
源码级不变量的归属地(多屏定位缓存那条就在里面)。
守两侧四个函数(词条卡片 + 落字回退卡片):
弹卡片:挪走了共享窗口,就必须让 capsule_layout 去重缓存当场作废,
并同步 capsule_cursor_passthrough
收卡片:穿透、尺寸、位置、两个缓存,一样都不能少;且必须先 hide
再改几何(否则复原会被合成出一帧横飞过半个屏幕的动画)
「还位置」和「清缓存」分开断言是有意的:这两件事各堵一个方向,漏任何
一个 bug 都会回来,所以要能各自报出自己的名字。
验证(三态):
未修复的 coordinator.rs(HEAD~1) → 红,报 show_vocab_suggestion_card
缺 capsule_layout 失效
变异:只删掉 hide_vocab 里那一行
position_capsule_bottom_center → 红,且精确报出「restoring size
alone leaves it in the card's
bottom-right corner」
修复后 → 绿;npm test 全量退出码 0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
789c82051e |
fix(history): 详情面板同层 key 去重,切换条目不再残留「播放录音」按钮 (#964)
* fix(history): 详情面板同层 key 去重,切换条目不再残留「播放录音」按钮
详情面板里 AudioRecordingPlayer 和 RepolishPanel 是同一层的兄弟节点,两个都用
裸 `item.id` 当 key。同层重复 key 下 React 只打警告(Encountered two children
with the same key)不报错,但 reconcile 匹配不上旧 fiber,旧的播放器节点删不掉:
每切换一次历史条目就在 DOM 里多留一个「播放录音」按钮,一直开着不关的窗口能叠出
一整列,把下面的流水线信息挤走。
改成 `audio-${item.id}` / `repolish-${item.id}`,key 仍随条目 id 变化(切换条目
时照旧重挂载、重置播放与润色状态),但同层不再撞车。
另加契约测试 scripts/history-detail-sibling-keys.test.mjs:扫详情面板里所有
key 表达式,断言互不相同且都带 item.id,后续再往这层加带 key 的兄弟组件会被拦下。
复现与验证(浏览器 dev,mock 条目打开 hasAudioRecording):
- 修复前:连续切换 12 次条目 → 播放按钮 1→12,控制台每次都刷重复 key 警告
- 修复后:同样切换 12 次 → 播放按钮恒为 1,重复 key 警告 0 条
- npm test(tsc + vite build + 全部前端测试)exit=0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(history): validate sibling keys at runtime
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
|
||
|
|
f008cc6f05 | fix(windows): allow synthetic hotkeys to trigger OpenLess (#948) | ||
|
|
c84bfb98f3 |
chore(ci): optimize CI caching, concurrency, and Tauri security (#937)
* ci: optimize CI caching, concurrency, and Tauri security Address multiple CI/CD efficiency and security gaps identified during project optimization analysis. The changes reduce PR build times, prevent wasteful concurrent runs, tighten the Tauri security boundary, and eliminate unnecessary submodule fetches on non-macOS jobs. CI workflow improvements: - Add swatinem/rust-cache@v2 to both ci.yml jobs (android-check and cross-platform), matching the caching already present in release-tauri.yml. This converts PR Rust compilation from full rebuilds to incremental builds, saving an estimated 60-80% of compile time per run. - Add concurrency groups to all three workflows. PR pushes cancel superseded runs to free concurrency slots; tag pushes and manual dispatches are isolated by run_id to avoid mutually canceling release builds. Previously, rapid PR pushes triggered redundant parallel CI runs with no cancellation. - Unify Node version to 22 across release-tauri.yml and android-apk.yml, which were previously pinned to 20. This ensures consistent build environments across all workflows. Submodule and security hardening: - Conditionally fetch the vendor/qwen-asr submodule only on macOS jobs across all three workflows. The submodule is compiled exclusively by build.rs on macOS (build_qwen_asr_macos); Linux, Windows, and Android builds never reference it. Removing the unconditional recursive checkout eliminates a network fetch and a submodule-availability failure point on every non-macOS job. - Set withGlobalTauri to false in tauri.conf.json. The frontend exclusively uses @tauri-apps/api imports and never references the window.__TAURI__ global, so disabling it removes an unnecessary XSS attack surface without functional impact. Dependency hygiene: - Move @types/dompurify from dependencies to devDependencies in package.json. Type declaration packages are build-time only and should not ship in the production bundle. * fix(ci): address review findings * fix(ci): isolate manual CI workflow runs --------- Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
4496eba30b |
feat(marketplace): 支持风格包 ZIP 下载与跨设备导入 (#942)
* feat(marketplace): add style pack ZIP downloads * fix(marketplace): reject file URI download targets |
||
|
|
ead09481bd |
feat(android): 可选 Shizuku 与三层跨应用粘贴回退 (#932)
* feat(android): add optional Shizuku accessibility recovery path Integrate best-effort OpenLessAccessibilityService recovery via Shizuku UserService with safe rollback, manifest/Gradle scaffolding, JNI bridge, settings UI, and contract tests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): add primary constructor for Shizuku UserService Stub Kotlin requires an explicit supertype initializer when extending AIDL Stub; fixes Gradle compile error on CI. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): replace accessibility heartbeat with IPC paste results Remove SharedPreferences cross-process heartbeat gating, use structured PASTE/PING IPC with safe retry rules, tighten editable target validation, and fix Shizuku manifest attribute merging for mixed-quote providers. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): use literal const values in accessibility command receiver Kotlin const val cannot reference enum codes; fixes release APK compile. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 修复无障碍组件短名与全名匹配误判 Settings.Secure 常以 pkg/.Class 存储服务列表,主检测路径改用 ComponentName 规范化比较,避免 enabled=false 误报未授权;同步 Rust JNI 路径并补齐测试。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 为 JNI 调用的无障碍检测方法添加 @Keep Release 构建中 R8 会剥离仅由 JNI 反射调用的 pingAccessibilityProcess,导致 NoSuchMethodError 与 operational=false 误报;同步保护 isEnabled 并补充契约测试。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 恢复无障碍粘贴的可编辑目标回退查找 粘贴失败日志为 NO_FOCUSED_EDITOR:过严的 isFocused 校验与移除树遍历导致找不到输入框。恢复 FOCUS_ACCESSIBILITY、宽松焦点匹配与 findEditableInTree 回退,并保留严格缓存校验。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 跨窗口扫描与缓存优先修复粘贴目标查找 微信等场景下 rootInActiveWindow 非输入窗口导致 NO_FOCUSED_EDITOR;改为缓存优先、遍历全部无障碍窗口,并加深树搜索。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 识别非 isEditable 的粘贴目标节点 微信等 App 不向无障碍暴露 isEditable;扩展为 EditText/ACTION_PASTE/ACTION_SET_TEXT 目标,并订阅文本变更事件以预热缓存。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 三层粘贴回退(无障碍 → Shizuku → 剪贴板) 无障碍失败后尝试 Shizuku 注入 KEYCODE_PASTE,再回退剪贴板;精简 Tier1 为 findFocus + ACTION_PASTE。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 粘贴结果校验与 Shizuku processNameSuffix ACTION_PASTE 返回 true 但文本未写入时降级为 PASTE_REJECTED 并走 Tier2;修复 UserService 绑定 NPE;Tier1 成功后跳过 Shizuku。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 恢复无障碍可编辑目标查找以修复微信等应用粘贴 aaa3fbf2 将 Tier1 简化为 findFocus 导致微信等应用 PASTE_REJECTED;恢复跨窗口/树扫描与焦点缓存,保留粘贴结果校验与 Promise.allSettled 容错。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 通过 IPC 传递粘贴文本修复后台剪贴板读取被拒绝 无障碍子进程在应用未聚焦时无法读剪贴板,导致 Tier1 直接 PASTE_REJECTED;主进程将听写文本经 EXTRA_PASTE_TEXT 传入粘贴流程。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): Tier2 粘贴改用 Shizuku.newProcess 绕过 MTK UserService 崩溃 联发科/小米 ROM 上 UserService 进程在 LoadedApk.makeApplicationInner 因 procName 为 null 而 NPE;粘贴键注入优先走 Shizuku.newProcess,失败再回退 UserService。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): Tier2 粘贴改用 Shizuku daemon UserService newProcess 为私有 API;粘贴键注入改走 daemon(true) 绑定,避免 MTK 上 :shizuku 子进程 Application NPE。 Co-authored-by: Cursor <cursoragent@cursor.com> * test(android): 修正 daemon 粘贴契约断言 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): daemon 粘贴绑定补全 processNameSuffix Shizuku UserServiceArgs 要求 suffix 非空;daemon(true)+suffix=paste 避免此前 null NPE 与 MTK :shizuku 子进程崩溃。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): Tier2 粘贴经反射调用 Shizuku.newProcess MTK 上 UserService 子进程仍触发 LoadedApk NPE;粘贴键注入优先反射 newProcess 执行 input keyevent 279,UserService 作回退。 Co-authored-by: Cursor <cursoragent@cursor.com> * chore(android): 移除粘贴调试日志 微信等应用三层粘贴回退已在真机验证通过,清理 DBG-21a66f/DBG-53a00d 临时 instrumentation。 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(android): 修复 CI 单元测试在无 Robolectric 下失败 ComponentIds 改为纯 Kotlin 规范化(对齐 Rust);isValidServiceComponent 拒绝首尾空白;Target 测试改用 action id 常量。 Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
cd48a037a4 |
fix(less-computer): restore OpenCode model discovery and execution (#840)
* fix(less-computer): restore OpenCode model execution * fix(capsule): gate set_ignore_cursor_events behind #[cfg(not(mobile))] #887 introduced desktop-only capsule cursor-passthrough logic into the unconditionally-compiled capsule_focus module, breaking Android cargo check (E0599: no method set_ignore_cursor_events on tauri::WebviewWindow). Gate the block to desktop; variables it consumes (classic_style, selection_polish, visible) are still used on mobile outside the block. --------- Co-authored-by: sim <sim@local> Co-authored-by: Chris233 <h-chris233@outlook.com> |
||
|
|
2409036ca9 | fix(android): use app cache for Marketplace installs (#879) | ||
|
|
7ad24d3e94 |
fix(android): persist prefs/logs under app filesDir (#877)
* fix(android): use app filesDir for persistence and SAF log export Honor devices reject writes under /data/local/tmp, which caused PreferencesStore to fall back to openless_prefs_fallback.json and settings saves to fail. Initialize JNI storage roots before Coordinator and file logging, route logs to files/logs/openless.log, and export via ContentResolver for SAF content:// URIs. * fix(android): guard StylePack empty asset_root and Android-only coord logs Reject persisting style pack icons when asset_root is empty to avoid writing under cwd; no-op cleanup on empty root. Limit Coordinator persistence degrade log suffix to Android only. Add unit test. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
857ea6dc21 |
Merge pull request #839 from Open-Less/release/1.3.15
fix(updater): prepare 1.3.15-Beta.2 after repository transfer |
||
|
|
6b33ba21e0 | fix(updater): prepare 1.3.15-Beta.2 after repository transfer | ||
|
|
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> |
||
|
|
4bdef5d7a3 |
ci: run all frontend and contract tests (#832)
Co-authored-by: sim <sim@local> |
||
|
|
eb099c4f41 |
build(macos): 校验打包产物声明语音识别用途 (#825)
* build(macos): 校验打包产物声明语音识别用途 build-mac.sh 已在校验 NSMicrophoneUsageDescription 与 audio-input entitlement,但 Apple Speech provider 引入的 NSSpeechRecognitionUsage- Description 没有对应护栏。该 key 一旦缺失,构建静默成功,而选择 apple-speech 的用户会在调用 Speech.framework 时被 macOS TCC 以 SIGABRT 直接杀掉进程(崩溃报告 termination namespace = TCC)。 对打包产物 .app/Contents/Info.plist(而非源码 Info.plist)新增一行 PlistBuddy 校验:脚本开头的 set -euo pipefail 会让 key 缺失时非零退出、 当场中止构建。key 存在时输出被丢弃,行为与现在完全一致,产物逐字节不变。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(macos): enforce speech usage description contract * fix(macos): reject Unicode-only speech descriptions --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: sim <sim@local> |
||
|
|
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> |
||
|
|
9991b325b8 |
test(windows): repair UI config contract (#829)
Co-authored-by: sim <sim@local> |