Skip to content

feat(linux-egui): native EGUI parity host - #1060

Open
aeoform wants to merge 87 commits into
Open-Less:betafrom
aeoform:feat/linux-egui-tauri-parity
Open

aeoform wants to merge 87 commits into
Open-Less:betafrom
aeoform:feat/linux-egui-tauri-parity

Conversation

@aeoform

@aeoform aeoform commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Introduces the native Linux EGUI host and parity-oriented desktop integration.
  • Restores Linux desktop services, settings, history, marketplace, Fcitx5 hotkeys, updater, localization, and native popup flows.
  • Adds Linux release packaging and contract coverage.

Follow-up before merge

  • Review the local-model / Qwen ASR surface separately; it should not be introduced as a Linux runtime dependency without an explicit decision.
  • Rebase onto the current beta baseline and run the Linux build/package validation.

egui-agent and others added 30 commits September 13, 2026 15:57
① 不说话报 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。
- 插件侧新增 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…)。
…包 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 返回)。
…ze 语义只比 ctrl/alt/shift/super;近失日志降到 Info 并加可开关逐键 trace;宿主逐项记录注册的 (sym,states)
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。
实测 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。
… 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 前不能动,已作为待办写进文件头注释。
…rl+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 外的修饰位一致」,否则这类失败永远没有日志。
…聚合 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 硬门禁)。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant