Skip to content

feat(linux): fcitx5 候选框听写状态提示 + AppImage 插件自动安装 + 热键启动容错 - #520

Merged
H-Chris233 merged 11 commits into
Open-Less:betafrom
aeoform:feat/linux-fcitx5-status-popup
May 25, 2026
Merged

feat(linux): fcitx5 候选框听写状态提示 + AppImage 插件自动安装 + 热键启动容错#520
H-Chris233 merged 11 commits into
Open-Less:betafrom
aeoform:feat/linux-fcitx5-status-popup

Conversation

@aeoform

@aeoformaeoform commented May 22, 2026

Copy link
Copy Markdown
Contributor

User description

概述

Linux 上通过 fcitx5 输入法候选框下方显示听写状态文字,替代 macOS/Windows 的胶囊窗口。同时修复 AppImage 不含 fcitx5 插件、热键启动时序等问题。

改动

fcitx5 插件 (openless.cpp)

  • 新增 SetAuxDown/ClearAuxDown DBus 接口
  • 6 种状态提示:🎤 收音中 → 🔄 识别中 → ✨ 润色中 → ✅ 已插入 / — 已取消 / ❌ 出错
  • InputContextFocusIn 监听:切窗口自动跟随焦点
  • 优先当前焦点 IC,失焦 IC 降级兜底
  • flushUI 先排空旧事件再设 auxDown,防止按键竞态覆盖

热键启动容错 (linux_fcitx.rs)

  • start_dictation_signal_listener 启动时等待 fcitx5 最多 30s
  • 监听 NameOwnerChanged,fcitx5 重启后自动重新同步快捷键

AppImage 插件自动安装

  • CI 将 libopenless.so 打入 AppImage 资源
  • 启动时检测插件缺失 → 自动安装到 ~/.local/lib/fcitx5/~/.local/share/fcitx5/addon/
  • deb/rpm 仍通过 files 映射安装到系统路径

其他

  • Linux 胶囊窗口不显示,状态完全走 fcitx5 输入面板
  • coordinator.rs / dictation.rs 移除冗余调用,统一文案

测试

  • fcitx5 候选框下方显示听写状态
  • 切换窗口状态跟随焦点
  • fcitx5 后启动时热键自动补同步
  • AppImage 首次启动自动安装插件到 ~/.local/

🤖 Generated with Claude Code


PR Type

Bug fix, Enhancement, Tests


Description

  • Deduplicate DashScope final transcript segments.

  • Render Linux status through fcitx5 auxDown.

  • Auto-install bundled plugin in AppImage.

  • Resync hotkeys after fcitx5 restarts.


Diagram Walkthrough

flowchart LR
A["DashScope ASR"] -- "deduplicate finals" --> B["Bailian transcript output"]
C["Linux coordinator"] -- "emit auxDown status" --> D["fcitx5 OpenLess plugin"]
E["AppImage startup"] -- "install bundled plugin" --> D
F["fcitx5 restart"] -- "NameOwnerChanged resync" --> G["Hotkey bindings"]
D -- "focus-aware panel" --> H["Candidate list status text"]
Loading

File Walkthrough

Relevant files
Bug fix
2 files
bailian.rs
Deduplicate final ASR sentence segments
+21/-5
dictation.rs
Remove duplicate polishing capsule emission
+3/-0
Enhancement
4 files
coordinator.rs
Route Linux status and hotkey startup
+128/-8
lib.rs
Auto-install bundled fcitx5 plugin
+5/-0
linux_fcitx.rs
Add auxDown APIs and resync logic
+218/-1
openless.cpp
Display status text inside fcitx5
+83/-8
Configuration changes
1 files
release-tauri.yml
Bundle fcitx5 plugin into AppImage
+4/-3

- fcitx5 插件新增 SetAuxDown/ClearAuxDown DBus 接口,在候选词列表下方
显示听写状态文字(收音中→识别中→润色中→已插入),切窗口自动跟随焦点
- 优先使用当前焦点 IC 展示 auxDown,失焦 IC 降级兜底,避免面板不渲染
- flushUI 排空旧事件后再设 auxDown,防止按键事件竞态覆盖状态文字
- start_dictation_signal_listener 启动时等待 fcitx5 最多 30s,监听
NameOwnerChanged 在 fcitx5 重启后自动重新同步快捷键绑定
- AppImage 打包 libopenless.so 为资源,启动时检测缺失自动安装到
~/.local/lib/fcitx5/ 和 ~/.local/share/fcitx5/addon/
- Linux 胶囊窗口不在 Wayland/X11 显示,状态完全走 fcitx5 输入面板
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actionsBot commented May 22, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit 2832437)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Stale binding

The fcitx5 restart handler re-syncs from the hotkey values captured when the listener thread started, not from the current preferences. If the user changes the dictation/QA/translation hotkeys while OpenLess is running and fcitx5 restarts later, the plugin will be pushed the old bindings until OpenLess is restarted.

// NOTE: NameOwnerChanged 捕获的是线程启动时的绑定快照。用户在// OpenLess 运行时改了快捷键且 fcitx5 恰好重启,重连会写入旧绑定。// 这是一个低概率场景(需要两个操作同时发生),暂时保留快照语义。// 要彻底解决需要把 Arc<PreferencesStore> 传给监听线程做实时读取。let binding_for_name = binding.clone();let custom_for_name = custom_trigger_key.clone();let qa_for_name = qa_trigger;let trans_for_name = translation_trigger;let _name_match = match conn.add_match(fcitx_rule,move |args:(String,String,String), _conn, _msg| {let(name, _old_owner, new_owner) = args;if name != "org.fcitx.Fcitx5"{returntrue;}if !new_owner.is_empty(){// fcitx5 已启动(或重启),重新同步所有快捷键绑定。// 把延迟+同步挪到独立线程:add_match 回调跑在 DBus 事件循环// 线程里,sleep 会阻塞所有信号处理。
log::info!("[fcitx-hotkey] fcitx5 appeared on DBus, re-syncing bindings");let b = binding_for_name.clone();let c = custom_for_name.clone();let q = qa_for_name;let t = trans_for_name;
std::thread::spawn(move || {
std::thread::sleep(Duration::from_secs(1));// 等插件完全加载resync_main_binding(&b, c.as_deref());sync_qa_binding(q);sync_translation_binding(t);});
Dropped Segment

Final ASR segments are only retained when sentence_id > 0. If DashScope ever emits a finalized segment with sentence_id = 0 for the first sentence, that sentence is silently dropped from the completed transcript and the user gets an incomplete result.

if is_sentence_final && sentence_id > 0{// 同一 sentence_id 后到覆盖前到:API 对同一句话的累积更新// ("你"→"你好"→"你好吗")只保留最终版本。
st.final_segments.insert(sentence_id, trimmed.to_string());}elseif is_sentence_final && sentence_id == 0{
log::warn!("[bailian-asr] final sentence missing sentence_id, dropping: {trimmed:?}");

aeoformand others added 2 commits May 22, 2026 20:48
Linux 已从 NoActivate 列表移除(走 fcitx5 auxDown 状态提示),测试断言
需同步更新为 FallbackShow。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
两个根因:
1. end_time 判断用 is_some() 而非 > 0:API 对 interim 结果设 end_time:0,
导致所有中间结果被当成 final 推入 segments
2. 累积文本拼接:同一句 API 多次发送("你"→"你好"→"你好吗"),
每次作为新 segment push,join 后变成重复拼接
修复:
- end_time 改用 > 0 判断真正的句子结束
- 引入 sentence_id → BTreeMap 按序存储,同一 sentence_id 后到覆盖前到
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 7635df5

1. clearAuxDown 找不到 IC 时也会清掉 lastAuxText_,避免 FocusIn 时
重放旧状态(如"已插入"→切窗口→突然显示"已插入")
2. 300ms 延迟重试前检查 LAST_AUX,状态已变则跳过,避免旧文字覆盖
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 7403597

1. set_aux_down/clear_aux_down 从 emit_capsule(被 cpal 音频回调调用)
移到独立线程执行,避免同步 DBus I/O 阻塞录音导致卡顿
2. is_plugin_installed_on_disk 同时检查 .so 和 .conf,孤立的 .so
没有 addon 配置 fcitx5 不会加载
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit c4cb3c9

1. start_dictation_signal_listener 新增 custom_trigger_key 参数,
NameOwnerChanged 和初始同步都通过 resync_main_binding 分支处理
自定义组合键 vs 预设修饰键
2. ensure_plugin_installed 去掉"已安装即跳过"检查,AppImage 每次启动
都覆盖最新 .so + .conf,确保新 DBus 方法不会因旧插件丢失
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 163327e

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 163327e

aeoformand others added 2 commits May 22, 2026 21:29
…ux")]
函数体内引用了 crate::linux_fcitx,非 Linux 平台该模块不存在导致 E0433。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 77455cf

@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 1b0e41c

@aeoform

Copy link
Copy Markdown
ContributorAuthor

在测试里我没发现相关问题

@appergb

Copy link
Copy Markdown
Collaborator

@claude 审查一下,然后告诉我结果。

@claude

claudeBot commented May 24, 2026

Copy link
Copy Markdown

Claude finished @appergb's task in 4m 3s —— View job


代码审查结果

  • 初始化任务列表
  • 读取变更文件
  • 分析 linux_fcitx.rs(插件安装 + auxDown + 热键重同步)
  • 分析 coordinator.rs(状态路由)
  • 分析 openless.cpp(C++ 插件端)
  • 分析 bailian.rs(ASR 去重)
  • 分析 CI 配置与 lib.rs
  • 汇总审查结果并输出

总体评价

整体设计合理,功能闭环完整。bailian.rs 的 sentence_id 去重修复、openless.cpp 中的 key.check() 正确 API 使用、Shift 不再 filterAndAccept 的修复,以及 DBus I/O 挪离音频线程等,都是正确且有质量的改动。发现以下几处问题,其中 1 个中等、2 个低优先级:


问题 1 — 中等:NameOwnerChanged 回调内 sleep(1s) 阻塞 DBus 事件循环

文件:openless-all/app/src-tauri/src/linux_fcitx.rsadd_match 闭包内:

// 当前代码let _name_match = match conn.add_match(fcitx_rule,move |args: ...,_conn, _msg| {if !new_owner.is_empty(){
log::info!("...");
std::thread::sleep(Duration::from_secs(1));// ← 阻塞整个 DBus 事件循环线程 1 秒resync_main_binding(...);
...}true}){ ...};

add_match 的闭包运行在 conn.process() 的调用线程(即 DBus 事件循环线程)。在此 sleep 1 秒会让该线程在整秒内无法处理任何其他 DBus 信号,包括 fcitx5 重启后插件刚发来的热键注册回执,导致这段时间内的热键事件全部丢失。

修复方向: 把重同步逻辑 spawn 到独立线程:

let binding_cloned = binding_for_name.clone();let custom_cloned = custom_for_name.clone();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_secs(1));resync_main_binding(&binding_cloned, custom_cloned.as_deref());sync_qa_binding(qa_for_name);sync_translation_binding(trans_for_name);});

Fix this →


问题 2 — 低:emit_capsule 首次 spawn 的 set_aux_down 无状态守卫

文件:openless-all/app/src-tauri/src/coordinator.rsemit_capsule 内 Linux 块:

let text = t.to_string();
std::thread::spawn(move || {// ← 没有在此处检查 LAST_AUX 是否仍等于 textifletErr(e) = crate::linux_fcitx::set_aux_down(&text){ ...}});

理论竞态:状态由 Recording → Idle 切换足够快时:

  1. T1 spawn: set_aux_down("🎤 收音中...")
  2. T2 spawn: clear_aux_down()
  3. T2 先完成(已清除)
  4. T1 后完成 → "🎤 收音中..." 重新出现在已结束的会话里

300ms 重试逻辑已做了守卫(检查 LAST_AUX),但首次调用未做。实测可能很难复现(状态切换通常比 DBus 往返慢),但逻辑上存在漏洞。

修复方向: 首次 spawn 内部也加 LAST_AUX 对比:

let text = t.to_string();
std::thread::spawn(move || {let current = LAST_AUX.lock().unwrap().clone();if current.as_deref() != Some(&text){return;}ifletErr(e) = crate::linux_fcitx::set_aux_down(&text){ ...}});

问题 3 — 低:ensure_plugin_installed 注释与行为不一致

文件:openless-all/app/src-tauri/src/linux_fcitx.rs(函数注释)和 openless-all/app/src-tauri/src/lib.rs(调用处注释):

  • 函数文档说「仅当插件文件在任何已知系统路径和用户路径都不存在时才执行安装
  • lib.rs 调用处注释说「不会覆盖系统已有的插件
  • 实际行为:只要 bundled .so 存在,就无条件~/.local/lib/fcitx5/libopenless.so 覆盖(fs::copy 不检查目标是否已存在)

提交 163327e("总是覆盖旧版插件")表明这是有意为之,确保 AppImage 版本与插件版本一致。但注释应更新为实际语义,避免误导维护者:

// AppImage:总是用 bundled 版本覆盖 ~/.local/ 下的插件,保持版本与 AppImage 一致。
// 系统路径(/usr/lib/fcitx5/ 等)由 deb/rpm 管理,此处不触碰。

正面评价

改动评价
bailian.rs sentence_id 去重干净正确,用 BTreeMap<i64, String> 按 ID 覆盖完美解决累积重复
openless.cppkey.check()比手动比较 sym() + states() 更正确,handles modifiers 顺序差异
Shift 移除 filterAndAccept()正确修复了 Shift 被意外拦截导致 fcitx5 中英切换失效的问题
FocusIn 事件补发 auxDown设计合理;flushUI() 的防竞态处理也细心
DBus I/O 挪离音频线程正确,防止 cpal 录音回调因同步 DBus 调用而卡顿
LAST_AUX 防重复 DBus 调用有效避免 ~30Hz 音频回调频繁触发 DBus 写
CI bundle + 自动安装AppImage 闭环设计完整
Linux 胶囊窗口早退 (return)看到 emit_capsule#[cfg(target_os = "linux")] { return; } 确认 FallbackShow 策略不会导致胶囊窗口意外弹出,逻辑正确

问题 1 建议合并前修复(会在 fcitx5 重启后的 1 秒窗口内丢弃所有热键信号),问题 2、3 可酌情处理。

1. NameOwnerChanged 回调内 sleep 移到独立线程,不再阻塞 DBus 事件循环
2. emit_capsule 首次 set_aux_down spawn 加 LAST_AUX 状态守卫
3. ensure_plugin_installed 注释更新为"总是覆盖"语义
4. NameOwnerChanged match rule 移除不支持的 arg0,改回调内过滤
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 5208499

@appergbappergb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化代码审核 — PR #520

由 Agent 自动审核,最终合并决定由维护者把关。

严重度数量结论
CRITICAL0pass
HIGH2建议合并前修复
MEDIUM3info
LOW3note

[HIGH] _name_match 在 listener 真正运行前就被 drop — NameOwnerChanged 永远收不到

位置openless-all/app/src-tauri/src/linux_fcitx.rsstart_dictation_signal_listener 内新增的 NameOwnerChanged 注册块。

let _name_match = match conn.add_match(fcitx_rule,move |...| { ...}){Ok(m) => m,
...};// 紧接着进入 retry 循环和 `loop {}` 事件循环

dbus crate 的 add_match 返回一个 MsgMatch guard — guard drop 时 match 即被注销。_name_match 是局部变量,作用域在外层 block 结束时就被析构,而真正处理事件的 loop {} 还在后面。结果:fcitx5 重启后自动重新同步快捷键这条核心能力实际上不会工作,尽管源码看上去逻辑齐全。

修复:把 _name_match 的绑定挪到紧邻 loop {} 之前的位置,或者改用一个生命周期能贯穿整个事件循环的容器(例如 Vec<MsgMatch> 放在 thread-local 或外层作用域)。


[HIGH] 仓库里提交了一个空的 libopenless.so 占位文件

位置openless-all/app/src-tauri/linux-fcitx5-plugin/libopenless.so(zero-byte,blob SHA e69de29b

CI 在打包前会用真实编译产物覆盖这个路径,所以 release 流程是好的。但任何开发者在本地 clone 后跑 Linux build,如果没执行完整的 CI 插件构建步骤,ensure_plugin_installed 就会把这个空 .so 拷到 ~/.local/lib/fcitx5/,fcitx5 加载时静默失败,没有可用的错误信息

建议二选一:

  1. 从源码树里删掉这个文件,仅依赖 CI 的 cp 步骤;或
  2. 改成 .gitkeep 哨兵 + 启动时检查文件大小,发现是空文件直接报错而不是拷过去。

[MEDIUM] emit_capsule Linux 分支:两次 spawn 的 retry thread 与 LAST_AUX 之间存在窄竞态

位置coordinator.rsemit_capsule 内的 Linux 块。

was_none 触发时无条件 spawn 一个 300ms 后重试的线程。如果状态在 300ms 内 Recording → Idle → Recording,两个 retry 线程都会看到相同的 LAST_AUX 文本,都通过 current.as_deref() != Some(&text) 守卫,结果是 两次 DBus 调用。建议引入 AtomicBool cancel 标志,或者用单个 debounce task 替代每次都 spawn。

[MEDIUM] setAuxDown 中的 savedIc_ 存在窄析构窗口

位置openless-all/scripts/linux-fcitx5-plugin/openless.cppsetAuxDown / clearAuxDown

注释说"事件处理线程和 DBus 处理线程都是 fcitx5 主事件循环,无竞态"——对从按键事件触发的路径是对的。但现在 setAuxDown 也会由 Rust 侧 std::thread::spawn 出来的线程经 DBus 触发,进入 fcitx5 event loop 后,理论上仍可能在 if (!ic) null-check 与 ic->inputPanel() 解引用之间,被同一 event loop 迭代中 dispatch 的 InputContextDestroyed 事件清空 savedIc_flushUI() 能排空已有事件但挡不住其后到达的 destroy 事件。建议把 IC 访问 + panel 更新包进 instance_->callLaterforeachFocused 闭包里。

[MEDIUM] bailian.rs 去重:sentence_id == 0 的最终句会被静默丢弃

位置openless-all/app/src-tauri/src/asr/bailian.rs 第 +69 行。

if is_sentence_final && sentence_id > 0{
st.final_segments.insert(sentence_id, trimmed.to_string());}

sentence_id 通过 unwrap_or(0) 兜底。如果 API 偶尔返回 end_time > 0 但缺 sentence_id 的最终句,旧代码会 push 到 Vec,新代码会丢。至少应在 is_sentence_final && sentence_id == 0 时打一条 warn 日志


[LOW] retry 循环阻塞了 listener 线程,听 NameOwnerChanged 与 30s 同步串行了

10 × 3s 同步等待结束之前 NameOwnerChanged 是不在跑的(即便修了上面的 drop bug 也存在 3s 窗口)。可接受,但和 HIGH#1 联动,建议先注册 match 再开始 retry。

[LOW] release-tauri.yml 旧注释

PR 已更新文案,无需处理。

[LOW] coordinator.rs 内联 use std::sync::Mutex;

#[cfg(target_os = "linux")] 块内的局部 use,与文件顶部 import 风格不一致,可选清理。


结论:WARNING — 2 个 HIGH 都直接影响 PR 自身宣称的功能(fcitx5 重启后自动同步 + AppImage 自动装插件),建议合并前修复。MEDIUM/LOW 不阻塞但值得跟进。

🤖 Generated by code-reviewer agent.

避免快速状态切换 (Done→Idle→Recording) 时旧 clear 跑到新 set 后面把新文字清掉。
NameOwnerChanged Stale Sync 是已知低概率场景,注释保留。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit de5c55b

@appergbappergb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自动化复审 — PR #520(commits 5208499 + de5c55b 之后)

Follow-up review after author addressed my earlier review.

上次 HIGH 问题当前状态
HIGH #1: _name_match 提前 drop已修复
HIGH #2: 空 libopenless.so 占位仍未修复
MEDIUM #2: setAuxDown / clearAuxDown IC 竞态已修复

✅ HIGH #1 已修复

52084999_name_match 的绑定已挪到外层作用域,生命周期覆盖了完整的 loop { conn.process(...) }。callback 内部增加了手工守卫 if name != "org.fcitx.Fcitx5" { return true; },替代了 dbus crate 不支持的 arg0 过滤。逻辑正确。

附加改进:thread::sleep 从 callback 内挪到 spawn 出来的线程里,顺便修了 reconnect 时事件循环被阻塞的潜在问题,赞。

❌ HIGH #2 仍未修复

openless-all/app/src-tauri/linux-fcitx5-plugin/libopenless.so在当前 PR branch 中仍是 0 字节wc -c 返回 0)。

ensure_plugin_installed 的注释更新为"启动时总会覆盖",但仓库里 bundle 进 AppImage 的 .so 本身还是空的。任何 Linux AppImage 构建仍会把零字节 .so 拷到 ~/.local/lib/fcitx5/,fcitx5 加载时静默失败。

建议二选一:

  1. 把这个文件从仓库里删掉,完全依赖 CI 在打包前编译并 cp 真实产物;或
  2. 启动时检查 .so 文件大小,0 字节直接报错而不是拷贝,避免误以为安装成功。

✅ MEDIUM #2 已修复

de5c55bclear_aux_down 加了对称的状态守卫,在 spawn 出来的线程内重新检查 LAST_AUX.is_none(),与 set_aux_down 的守卫对齐。能正确防止陈旧的 clear 越过新的 set。


结论:HIGH #2 仍阻塞合并 — Linux AppImage 那条主线还是会出问题。

🤖 Generated by code-reviewer agent.

1. _name_match/_match 生命周期注释:Rust 的 `let _name =` 不会提前 drop,
仅有 `let _ =` 才会。两个 guard 与 loop{} 同闭包作用域,自动化审核 HIGH 误判。
2. emit_capsule retry 用 AtomicU64 gen counter 去重,避免快速状态切换时
多个 retry 线程同时生效。
3. bailian.rs sentence_id==0 的 final 句加 warn 日志,方便排查。
4. 删除空 libopenless.so 占位文件,CI 会在构建前实时 cp。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@appergb

Copy link
Copy Markdown
Collaborator

撤回 HIGH #1 —— 我看错了 Rust 语义

@aeoform 抱歉,你 28324376 提交说的是对的,_name_match 那条 HIGH 是我误判,撤回

Rust 里 let _foo = expr;(带名字、下划线前缀)只是抑制 unused-warning,binding 的生命周期和普通 let 一样;只有 let _ = expr;(裸下划线,没有名字)才会立即 drop 临时值。我前面那条审核搞反了,对不起。

第二轮我的 reviewer 又"确认"了你"修复了"它 —— 实际上你只是加了注释解释这个误判,binding 一直是有效的。两次都错,丢人。

剩余情况:

我前面提的项当前状态
HIGH #1_name_match drop我的误判,撤回
HIGH #2 仓库里 0 字节 libopenless.so⚠️仍未修 —— diff 里 libopenless.so +0/-0,文件还是 0 字节
MEDIUM #1 retry 线程竞态✅ AtomicU64 gen counter 去重,已修
MEDIUM #2 setAuxDown/clearAuxDown IC race✅ 已修
MEDIUM bailian dedup 丢句静默✅ 加了 warn 日志

HIGH #2 那条还是想确认下你的意图 —— 是不是想保持空文件作为 placeholder,或者准备让 CI 在打包前覆盖?如果是 CI 来覆盖,建议在 ensure_plugin_installed 启动时加文件大小校验(metadata().len() == 0 时报错而不是 silent copy),防止本地 dev build 误用。

🤖 Generated by code-reviewer agent.

@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 2832437

@aeoform

Copy link
Copy Markdown
ContributorAuthor

HIGH #2 仓库里 0 字节 libopenless.so修好了

@appergb

Copy link
Copy Markdown
Collaborator

HIGH #2 确认已修 — 我又误读了

@aeoform 谢谢澄清。再仔细看了一次 28324376 这个 commit,libopenless.so 的 status 是 removed+0/-0 是因为原本就是零字节、删了也不动行数。我前面看 diff 里的 +0/-0 就以为是"文件未改动",其实那是"删除一个零字节文件"。是我读错了。

当前 PR #520 状态:

我前面提的项当前状态
HIGH #1_name_match drop❌ 我的误判,已撤回
HIGH #2 0 字节 libopenless.so已修复(commit 2832437 中 removed)
MEDIUM #1 retry 线程竞态✅ AtomicU64 gen counter
MEDIUM #2 setAuxDown/clearAuxDown IC race✅ 已加状态守卫
MEDIUM bailian dedup 丢句✅ 加了 warn 日志

从我这边看,PR #520 现在没有阻塞合并的问题了。 合并决定权留给维护者。

抱歉这次审核我自己出了两次错(HIGH #1 Rust 语义 + HIGH #2 误读 diff status),下次会更仔细。

🤖 Generated by code-reviewer agent.

@H-Chris233
H-Chris233 merged commit 7538ac0 into Open-Less:betaMay 25, 2026
4 checks passed
appergb pushed a commit that referenced this pull request May 25, 2026
… Sherpa 本地 ASR 状态同步 + DashScope 重复修复
本次只动版本号,逻辑改动已在以下 PR 落盘:
- #520 Linux fcitx5 候选框听写状态提示 + AppImage 插件自动安装 + 热键启动容错
- #523 Sherpa 本地 ASR 状态同步修复(active settings 顺序 + Qwen3 tokenizer 缓存校验)
- #527 Sherpa 本地 ASR asset 与 state handling 修复
- DashScope (bailian) ASR 部分语句重复输出修复
- pr-agent.yml CI 配置调整
@aeoform
aeoform deleted the feat/linux-fcitx5-status-popup branch May 25, 2026 13:44
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@aeoform@appergb@H-Chris233