完善egui相关内容 - #1117
Open
aeoform wants to merge 37 commits into
Open
完善egui相关内容#1117aeoform wants to merge 37 commits into
aeoform wants to merge 37 commits into
Conversation
Beta.3 merged as 913279c; this revision carries the review fixes (fcitx5 startup barrier, UI bridge protocol v2, field-patch settings saves, history cache) into the deb/rpm channel.
Lists the observable behaviour each review fix introduced: the fcitx5 startup barrier and its failure/recovery path, the UI bridge handshake and reconnect, field-patch settings saves under conflict, history delete identity and caching, plus the previous round's parity regressions and the install/upgrade checks.
…ged daemon The barrier required the `org.fcitx.Fcitx5` owner name to change after a reload, but `restart_fcitx5_via_dbus` reported success whenever the D-Bus call was dispatched. A compositor-launched fcitx5 (`fcitx5-wayland-launcher --reopen`) sets `CanRestart` false and then silently ignores `Restart`, so the owner never changed: every start burned the full 15 second budget and died in the startup error window even though the running addon answered and hotkey registration would have worked. - Readiness is now the addon interface, not a replaced daemon. A reload that the daemon does not perform is a warning (`reload_wait`, `ReloadPlan`). - `CanRestart` is consulted before `Restart`, and a session-managed daemon keeps its running addon copy instead of having a competing `fcitx5 -r` spawned next to it. Only a refused controller request uses the CLI fallback. - `parse_maps_plugin_path` no longer treats the kernel's " (deleted)" suffix as "fcitx5 is not running", so the stale-copy case is reported and judged correctly.
The host's MSRV is 1.95 while CI lints with the current stable (1.98), and clippy 1.95 flags these two dedupe decisions as `nonminimal_bool`. Rewriting them as `is_none_or` (already the shape used in the history cache) is equivalent — signal/local is None, or the stamp is outside the dedupe window — and is clean on both toolchains. Package revision advances to 105 for the rebuilt deb/rpm.
The bridge refused any snapshot over 16 MiB by dropping the window connection. The window treats a dead host as "the host is gone" and exits successfully, and the host only records a window error on a non-zero exit — so an oversized view model turned into: connection dropped, window exits, host respawns after 800 ms, same oversized snapshot, forever. The window blinked about once a second and the app was effectively unusable (it takes ~14k entries at the current average transcript length to get there, but history has no entry cap by default). Now the host trims the oldest history before sending: a ladder of 4000/1000/200/0 entries, newest first, until the payload fits a 12 MiB soft limit. The history list is refilled from the real cache every tick, so trimming only affects the frame being sent, and both sides keep indexing the same list. The count of hidden entries travels in `FrontendViewModel::history_truncated` and the history page paints a warning with that count plus the setting that actually shrinks history. If even the empty history does not fit, the frame is skipped and the connection is kept: the window keeps showing the last good state instead of dying in a loop. `OPENLESS_UI_SNAPSHOT_LIMIT=<bytes>` lowers the soft limit so the degraded path can be exercised on a device without accumulating 12 MiB of history. Verified: with the limit forced to 60 kB the host logged the trim once, the window process was spawned exactly once and never exited, and steady-state publishing stayed at zero frames (the fingerprint gate still works). Package revision 106.
wgpu 28 把 `MemoryHints::Performance` 定为默认值,于是 wgpu-hal 的 Vulkan 子分配器
按 device 128–256 MB 的内存块向驱动申请。每个渲染进程(主窗、胶囊、每个弹窗)都为
此付出固定代价:建 device 后显存 195 MiB,且与窗口大小、UI 内容无关 —— 460×180 的
胶囊要 197 MiB,而裸 Vulkan(vkcube,含交换链)只有 5 MiB。
现在 eframe 窗口与原生 layer-shell 胶囊共用一份设备描述,统一改要
`MemoryHints::MemoryUsage`(8–64 MB 块)。
本机实测(GTX 1660 SUPER / NVIDIA 615.71.09 / 1920×1080@120):
胶囊 460×180,Siri 录音态: 198 MiB → 18 MiB,29.8 fps 不变
设置面板打开(背板 4×MSAA + 模糊 + 动画预览):
209 MiB → 47 MiB,72.7 fps 不变
顺带加入 `frame_stats`:`OPENLESS_FPS_LOG=1` 每秒写一行帧统计,
`OPENLESS_FPS_SETTINGS=1` 启动即打开设置面板,用来量上面这两组数字。
WGPU 迁移把包依赖从 libegl1 / libwayland-egl1(rpm: libglvnd-egl / libwayland-egl.so.1)换成了 libvulkan1(rpm: vulkan-loader)+ Recommends mesa-vulkan-drivers,但 linux-egui-release-contract.test.mjs 还在断言旧的 EGL 依赖,于是 CI 的 macOS/Windows 契约步骤从迁移那次提交起就红: AssertionError: deb must require libegl1 at scripts/linux-egui-release-contract.test.mjs:98 现在断言 Vulkan loader 在 deb/rpm 依赖里、EGL 依赖不再回来(回来了说明 GL 渲染 路径又被引回来),并断言 Recommends 里有 Vulkan 驱动。 顺带更新 docs/linux-egui-dependency-upgrade.md:layer-shell 胶囊已经走 wgpu/Vulkan,「仍存在的 GL 路径」一节以及验收清单里「该 surface 仍走 GL」的说法 都过期了;补上 MemoryHints::MemoryUsage 的显存结论。
`cargo clippy -p openless-linux-egui --all-targets -- -D warnings` 会把 `assert!(MAX_TEXTURE_DIMENSION_2D >= 3840)` 判成 assertions_on_constants。 挪进 const 块,仍然是编译期拦截「把 2D 纹理上限调小」的改动。
历史保留用的是两套时间:`created_at` 由注入的 `Clock` 生成,而保留截止 (`retain_with_policy`)和「最近 N 分钟」(`recent_within_minutes`)自己读 `chrono::Utc::now()`。注入固定时钟的后端(测试、回放)因此在真实时间越过 「固定时间 + retention」之后,会把刚写入的条目在写入瞬间判成过期删掉: api::tests::completed_dictation_persists_history_and_activity_from_the_session_snapshot assertion `left == right` failed left: 0 right: 1 (api.rs:11048) 本 PR 12:13Z 那次 CI 还是绿的,12:55Z 起每次都红在同一行 —— 这个测试注入 2026-08-28T12:34:56Z、retention 30 天,真实时间在 2026-09-27T12:34:56Z 越线。 改法:`HistoryStore` 持有唯一时钟来源,后端组装时用 `attach_clock` 把注入的 `Clock` 交下去;保留裁剪与 `recent_within_minutes` 都改用它。功能、默认值 (7 天)、`0 = 不按天数清理`、只在写入时裁剪的行为一律不变;生产注入的本来 就是 `SystemClock`,行为零变化。 回归测试 `retention_follows_the_injected_clock_instead_of_the_wall_clock` (固定时钟 + 30 天保留:新条目留住、超期条目照删;把 cutoff 换回 `Utc::now()` 时该测试立刻失败,已验证)。 package revision 118。
现场:一个跑了一晚的 UI 进程 RSS 5.8 GB(VSS 7.2 GB),但 21 小时只用了
60 秒 CPU、GPU 显存 47 MiB —— 涨的是纯内存,不是渲染,也不是泄漏式的匀速增长。
成因是三段代码凑在一起:
1. 宿主 `publish_view_model()`:内容没变也**每 2 秒发一份保活快照**。
2. UI 侧收件通道是无界 `mpsc::channel()`(`UiBridgeClient::connect`)。
3. 窗口被遮挡/不可见时,eframe 会**完全跳过 egui pass**
(eframe 0.36 `wgpu_integration.rs` 的 `if !show_ui { /* 不做任何 egui pass */ }`),
`UiClientApp::ui()` 根本不会被调用,也就没人排空那个通道。
于是「窗口不可见」的每一秒都在堆快照:21 小时 ≈ 3.7 万份 × ~154 KB ≈ 5.8 GB,
且窗口恢复可见后要先逐份消化积压再回到正常帧,看起来就是卡死。
旁证:健康实例每 40 秒左右会写一条 `[ui-client] ipc round-trip ...`(UI 每 2 秒
一次 ping),而那个实例 21 小时里一条都没有 —— 它的 egui pass 一直没跑。
改法:快照不再走通道,只进 `UiBridgeClient` 的**单一槽位**(`LatestSnapshot`)。
旧快照本来就会被后一份覆盖,中间那些没有任何价值;窗口每帧 `take_snapshot()`
一次即可。内存因此与「窗口被遮挡多久 / 宿主发过多少份」无关。
控制帧(Ready/Hotkeys/Pong/Focus…)照旧走通道:它们由事件驱动、体积小。
回归测试 `an_undrained_window_keeps_only_the_newest_snapshot`:连发 40 份快照、
一份都不取,断言只有序号 40 留在槽位、取走后清空、通道里一句快照都没有
(把快照改回走通道时该测试立刻超时失败,已验证)。
另外这次同步把 origin/beta 的两个提交(Open-Less#1113 空格/鼠标热键、Open-Less#1115 GPT-6 温度)
合入本分支,无冲突。
package revision 120。
以 Tauri(`src/pages/settings/`)为唯一标准,把「AI 服务与模型 → 语言模型」
这一页补到对齐。用户在 Linux/Windows 两边截图对比后指出「差的有点多」。
新增(Tauri 有、我们以前没有):
1. **识别管线**(`ProvidersSection` 的 pipelineMode 行):`识别管线 ⓘ` +
`[传统模式][多模态模式]` 分段控件 + 「两种模式使用完全独立的凭据配置…」
说明。只在实验性开关 `multimodalPipelineEnabled` 打开后出现 —— 与 Tauri 的
`if (prefs?.multimodalPipelineEnabled !== true) return null` 同条件。
切换只改偏好(`pipeline_mode`),两套凭据都留在凭据库里。
2. **渠道卡片**(`ChannelList` 的行)改成 Tauri 的结构:
- 圆角 12 卡片,仅「当前使用」的渠道有中性灰底 + 0.5px 描边(Tauri 特意
不用蓝底竖条);第二行 `provider · model`(模型等宽),名字为空时标题
回退成供应商名(`channel.name.trim() || providerLabel`)。
- 状态块:`上次验证` + 通过(+耗时)/ 失败(+错误 + 「验证失败不会自动停用…」)
/ 尚未验证,再跟「多久以前」,超过 24 小时追加「结果已超过 24 小时」。
- 右侧只留「重新验证 / 启用 / 编辑 ›」(编辑按钮带铅笔与右尖括号图标)。
- 换顺序靠左侧拖拽手柄(新图标 `Grip`,走 lucide 同步管道),不再用 ↑↓。
- 点「验证」后按钮立刻变「正在验证…」并禁用,结果回包或超时后恢复
(Tauri 的 `testingIds` 也是界面本地状态)。
- 供应商选择与「删除渠道」(两步确认)移进编辑面板 —— Tauri 把它们放在
ChannelModal 里,卡片上不出现;卡片下方保留「凭据保存在系统凭据库中。」
数据管道:`SettingsChannel` 补 `id`,并把原来宿主预格式化的 `last_check` 字符串
换成结构化字段(`last_ok` / `last_error` / `last_latency_ms` /
`last_check_age_seconds`),本地化仍全部走 i18n 目录。
文案:新增 15 个 key(识别管线 5 个、渠道状态 10 个),值取自 Tauri 五语目录,
`sync-egui-i18n.mjs` 报告全部 in sync。
测试:`the_pipeline_mode_row_only_shows_with_the_experimental_switch`、
`a_failed_channel_card_explains_the_failure_and_the_stale_result`、
`a_verify_click_marks_the_channel_as_testing_until_the_result_arrives`;
识别管线那条做过负向验证(去掉开关判断即失败)。
package revision 121。
… links
三处对齐 Tauri(用户逐项指出):
1. **渠道编辑器改成右栏覆盖**(`ChannelModal` 的 embedded 形态)。
之前是卡片下方展开的内嵌面板,后来我先做成了全窗口遮罩 + 居中卡片,用户
指出「应该是在右侧覆盖,不是独立的覆盖层」——Tauri 桌面端确实是
`createPortal` 进右侧 `.ol-settings-content-pane`,用
`.ol-channel-dialog-embedded { position: absolute; inset: 0; background:
var(--ol-sidebar-bg) }` 整栏盖住。现在照此实现:
- 头部:返回按钮(`chevLeft`,新图标走 lucide 同步管道)+ 「编辑渠道」+
渠道种类徽章(文字处理 / 语音识别)+ 自动保存说明;
- 中部滚动区:白底圆角表单卡(`服务连接` → 供应商 → 渠道名称(可选)+ 说明
→ 凭据 → 模型),字段照 Tauri 是整列宽、38px 高(`layout::text_input_sized`);
- 底部:左侧「删除渠道」(垃圾桶图标 + 两步确认),右侧自动保存提示 +
保存字段/清除 Secret + 「完成」(蓝色主按钮)。
`SettingsProviderEditor` 增 `is_asr`(宿主 `ProviderEditorForm.kind`)决定徽章。
2. **设置弹窗左栏几何**:底色以前画在「内容高度」的 Frame 上,内容短时下半截
露出内容区底色;内容宽度又比 Tauri 少了 24px(选项 170 vs 190)。改为按整栏
高度铺底色 + 右侧 0.5px 分隔线(Tauri 的 `borderRight`),内容走 12/16 内边距,
选项与「帮助中心」上方的分隔线因此与 Tauri 同宽同位置;不再用 `ui.separator()`
以免它自己的宽度把右栏挤窄。
3. **外链指错了组织**:帮助中心/发布日志/GitHub/反馈都指向
`github.com/earendil-works/openless`,Tauri 用的是 `Open-Less/openless`
(帮助中心还要带 `#readme`)。抽成常量并加回归测试。
测试:`the_channel_editor_covers_the_content_column_with_tauri_chrome`(断言编辑器
画出编辑渠道/服务连接/渠道名称/删除渠道/完成,且设置页正文不透过)、
`settings_links_match_the_tauri_targets`;i18n 新增 6 个键(值取自 Tauri 五语目录)。
package revision 122。
…gh it 按 Tauri 补齐两处行为(用户要求「都要同步」): 1. **字段修改后自动保存**(Tauri `CredentialInput` 的 300ms 防抖 + 失焦/离开前 flush)。UI 侧记一个「防抖截止时刻」在 egui 临时数据里:任一字段(名称、 Endpoint、模型、Resource ID、Auth 模式、密钥、供应商切换、模型预设)改动即标脏, 到点推一次 `SettingsProviderSave`;`完成`/返回先 flush 再关。 因此底部去掉「保存字段/Secret」按钮,只剩「清除 Secret」与「完成」。 ——保留「清除 Secret」是有意的差异:Tauri 的密钥字段会读回已存值(清空输入框 即删除),而我们的密钥草稿始终是只写的(已存密钥不进 UI 进程),没有这个按钮就 无法擦除已保存的密钥。若要完全对齐,需要把密钥读回界面,安全性上是降级的。 2. **「添加渠道」走同一个编辑器**(Tauri `startCreate`):点按钮先在后台建一张空 渠道(名字留空、供应商取预设第一项),宿主 `ChannelCreated` 后自动打开编辑器并 标记为草稿 → 标题是「添加渠道」;用户完全没交互就关闭时把这张空渠道回收掉 (Tauri `shouldRecycleDraft` + `deleteChannelIfBlank`)。原来的内嵌小表单 (`add_channel_form` + `SettingsChannelFormOpen/Provider/Name/Create` 四个 action 与三个 VM 字段)整块删除。 测试:`editor_auto_save_debounces_and_flushes_before_closing`(防抖窗口内不保存、 到点只保存一次、关闭前 flush、干净关闭不保存)、右栏覆盖测试新增草稿标题断言、 `typed_text_survives...` 里那段内嵌表单断言改成编辑器字段断言。 package revision 123。
用户报「打开以后只能显示模型编辑页面,不能改其他设置了」——上一条把渠道编辑器改成 右栏整栏覆盖时漏了两道闸,两处都补上: 1. **别自动弹编辑器**。面板加载后宿主会为「选中渠道」把编辑器拉起来(`selected_channel_id` 的兜底是 active provider 或第一个渠道),这在编辑器以内嵌小面板呈现时无害,改成 整栏覆盖后就变成「一进设置右栏永远是模型编辑器」。新增 `editor_requested`:只有用户 点了「编辑」(`SettingsChannelSelect`)或刚建出草稿(`ChannelCreated`)才允许打开, 面板刷新(保存 / 验证 / 切页 / 保活)不再顺手打开。 2. **覆盖范围跟着分区走**。`channel_editor_panel` 现在只在「AI 服务与模型」这一页替代 `panel()`;切到别的分区时宿主还会主动收起编辑器(Tauri 的 modal 宿主挂在服务页里, 切页即卸载),未交互的草稿照旧回收。 顺带修好 QA 开关 `OPENLESS_FPS_SETTINGS=1`:它以前只在窗口初始化时置一次 `settings_open`,随后被宿主快照覆盖,等于失效;改成每帧采纳快照之后再压一次。 测试:`the_channel_editor_never_covers_other_settings_sections`(通用/外观/关于三个 分区开着编辑器也不许出现「编辑渠道」)。 对照用户数据确认:这次误判没有造成删除——`credential-metadata.json` 仍列出 `llm: deepseek`,mtime 未变,日志无删除记录。 package revision 124。
用户诉求:「润色时能不能根据大模型的文字输出速度转圈」。审计结论:Tauri 版没有这个反馈
(`VoiceOrbStage` 只按状态给固定 speed:thinking 1.5 / 收尾 1.0;`PolishDelta` 在
`tauri_events.rs` 里甚至被显式忽略,胶囊 payload 也没有任何速率字段),所以这里按用户
要求把它做成一个**两侧都没有过的新行为**,但基线刻意对齐 Tauri。
- 新增 `ui/frontend/stream_rate.rs`:
· `StreamRate`:宿主侧把流式增量的字符数按 0.5s 滑动窗口折算成「字符/秒」;
· `OrbSpeed`:胶囊侧接住宿主下发的速率、只在速率变化时刷新时间戳,然后本地按 0.45s
时间常数衰减 —— 模型吐字快就转快,网关卡住/模型想事情就慢慢回到基线,不需要宿主发心跳;
· `speed_for_rate`:4 字/秒以下不加成、60 字/秒到顶,映射到 [1.5, 2.6] 转/秒。
**没有速率数据时正好是 Tauri 的 1.5**,所以默认观感与 Tauri 一致。
- 宿主(`main.rs`):新增 `stream_rate` 计量。非 final 的 `PolishDelta` 记增量字符数,
final 时清空样本(流结束立刻回落基线);新会话(`Starting`)也清空。胶囊消息在
`HostToPopup::Capsule` 里多带一个 `stream_rate`(可选字段,老宿主缺省即基线)。
- 胶囊窗口(`popups.rs`):orb 的 `SiriDrive.speed` 不再写死 1.3(顺带修掉与 Tauri 1.5 的
漂移),改由 `OrbSpeed` 逐帧给出;离开思考态复位。
测试:`stream_rate` 模块 8 条(窗口折算、过期、基线、clamp、卡住后衰减、恢复后重爬)+
`the_orb_spins_faster_while_the_model_streams`(走真实渲染路径,用 Siri 时钟推进量比较
「无速率」与「60 字/秒」)+ `capsule_carries_translation_active_and_style` 覆盖协议字段。
package revision 125。
用户拍板「Tauri 为准」:先撤掉上一条把思考光点转速接到流式输出速率的功能 (revert 2c0d28b,只保留 1.3→1.5 的漂移修复),然后把审计出的六处差异全部对齐 Tauri。 - **扫光**:经典/typeless 的「思考中」文案现在有 `cap-shine` 扫光(同一段文字在高光带内 用蓝色重绘一遍,三层由窄到宽),进入转写/润色头 2 秒走 0.9s 快周期、之后回落 2.4s。 - **预测式展开**:宿主量「预备 → 就绪」耗时(Tauri 的 `warmupMs`)并按同样的 `clamp(prev*0.7 + observed*0.3, 60, 600)`、同样的 <20ms/>3s 过滤学习,落在 UI 状态文档 (`capsule_warmup_ms`);`siri_wgpu::tick` 用它算 `warmProgress`(未就绪 cap 0.9,就绪后 k=9 补满),光条从收拢态按这个节奏展开。 - **圆点出场 / 收尾**:`gather` 改由时钟驱动 —— 出场全聚圆心 → hold 0.3s → 1.6/s 散开成环; 终态 `merging` 时 4/s 合回中央一颗圆,转速回落 1.0。终态胶囊因此也要画光点(以前不画)。 - **交叉淡出**:切到思考态后 wave 继续画 —— 先保持 0.55s、再用 0.6s ease-out 淡出(预乘 alpha 的 `opacity`,新增 `SiriEffect::with_opacity`,WGSL 用 `tint.a` 同时缩 RGB 与 A)。 - **Siri 舞台不再有文案**(Tauri `VoiceOrbStage` 只有 error 才给字):删掉 orb 下方的 「思考中」白底标签与「已插入 N 字」;错误时按 Tauri 的 `errorGlowTextStyle` 画底部红字 药丸,文案优先用宿主给的 `message`。 - **新会话复位**:两个窗口进程常驻,Tauri 靠组件重新挂载拿到的「入场从头走」现在由 相位时钟算出并显式 `siri_wgpu::reset`。 测试:`capsule_motion` 6 条(wave 淡出曲线、相位会话边界、扫光快/慢周期)+ `siri_wgpu` 4 条(预测式展开、gather 生命周期、reset 重放入场、opacity 写入 tint.a)+ `ui_state` 2 条(学习上界与 EMA 收敛)+ 界面级 3 条(扫光颜色、wave→orb 双回调、 Siri 只在出错时显示宿主的 messenger 文案);另修正 3 条既有测试的期望(终态 Siri 仍画光点、 Siri 无文案、已插入字数只在经典药丸)。 package revision 126。
appergb
added a commit
that referenced
this pull request
Sep 30, 2026
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>
现场:窗口被遮挡时 eframe 完全跳过 egui pass(`if !show_ui`),`ui()` 一次都不跑, `take_snapshot`/`try_recv` 也就没人调;宿主仍每 2 秒发一份保活快照。旧代码把快照塞进 无界通道 —— 21 小时≈3.7 万份全留在内存里(系统账本:21.1 小时那次会话峰值 5.70 GB, 折算 78.8 KB/s;同长度会话另有 0.96/1.30 GB 两个),窗口恢复可见后还要逐份消化,表现 为卡死。单槽位 `LatestSnapshot`(rev 120)修掉了语义,但没有任何测试能证明「内存不再 跟着份数涨」—— 原来那条只断言槽位里只有一份。 新测试 `an_undrained_window_survives_hours_of_keepalive_snapshots` 直接量 RSS:拿裸 socket 当宿主(绕过宿主侧合并槽这另一半修复),连发 1000 份 32 KiB 快照、一份不取, 只在等待「最新一份」可用时顺便断言驻留内存不超过 8 MiB。 验证过它能红:把读线程改回「进无界通道」,0.73 秒即失败 (`300 sent, RSS grew 10 MiB`);装回单槽位后 1.9 秒通过。踩到的两个坑写在注释里 —— 等待 `take_snapshot` 会让旧代码先以超时失败、掩盖内存断言;突发式发送会被宿主侧合并槽 吞掉中间帧,也复现不出客户端堆积。
用户报障「麦克风选择变成了音响选择」。本机实测 `cpal::input_devices()` 给出 8 个节点, 设置页的「首选麦克风」里排着 `TU116 High Definition Audio Controller Pro`(显卡 HDMI 输出的监听)、`内置音频 Pro`(内置扬声器的监听),而真实采集源 `内置音频 Pro 2` 反而在 后面 —— 选中监听就是录到扬声器回环(静音或系统声音),转写自然出不来东西。 成因:PipeWire 给每个 sink 都造一个 monitor source;Pulse 给它的描述是 `Monitor of <sink 描述>`,而 cpal 的 Pulse 后端把 `Monitor of ` 前缀去掉后原样当成设备 名。`pactl list sources` 的 monitor 项带 `Monitor of Sink:` 字段,据此把描述精确还原成 cpal 会显示的名字即可剔除,不需要猜命名规律 —— 真实采集源的描述不带前缀,永远不会被误删。 - `pulse_sources()` 读 `pactl list sources` + `get-default-source`;判定合到纯函数 `keep_microphone()` 里,`enumerate_microphones()` 只负责 cpal 侧枚举。 - 顺手修 `is_default`:`cpal::default_input_device()` 拿到的是 PipeWire 的 `default_input` 合成节点,永远不等于真实设备名,所以「默认」标记此前从来没标上过;改用 pulse 的默认源。 - `pactl` 缺失/失败时返回空值、退回旧行为:宁可多列,也不误删用户的麦克风。 - 以前误选过监听的名字不会再匹配到任何设备,`audio.rs` 会回落到系统默认麦克风并记一条 warning —— 升级后不用手动改,录音即恢复。 真机核对:修复后列表只剩 `内置音频 Pro 2`(is_default=true),5 个监听全部消失。 测试:`monitor_sources_are_not_offered_as_microphones`(按现场 pactl 片段核对剔除集与 默认源)、`real_capture_sources_survive_the_monitor_filter`(现场 8 个节点逐个核对)、 `without_pactl_no_device_is_dropped`。 package revision 127。
merge origin/beta(`6f9af895`,零冲突)后: - `package-revision` → 128,包名进到 2.0.0-Beta.4-128; - `sync-egui-i18n.mjs` 同步 4 条上游改过的文案(settings.selection_workspace.hint、 vocab.filter_auto、modal.descriptions.advanced、marketplace.upload.hint)。 注意 `vocab.filter_auto`:上游这版是「确认收集」(Beta.4 的 confirmation-based vocabulary learning),egui 侧目前还是「自动添加」的行为 —— 标签已对齐,**行为还没对齐**,单独跟进。
上游 Beta.4 把学词改成了「确认收集」:手改产生的候选只进 Core 的待确认队列,用户点
「记住」才落库。Linux egui 侧完全没有这个确认入口 —— 事件被忽略、面板也不画,结果是
**自动学词永远落不了库**(候选到期自己消失),词库页的「确认收集」分段因此永远是空的。
按 Tauri `VocabSuggestionCard` 补齐:
- 协议:`HostToPopup::Capsule` 增 `suggestions`(`CapsuleSuggestion{id,pattern,
replacement}`,`serde(default)` 兼容旧宿主);新增 `PopupToHost::VocabSuggestion{id,
accept}` 承载一次「记住 / 不用」。
- 胶囊:有候选时整窗只画卡片 —— 标题「要记住这个词吗?」+ 每行「改前 → 改后」+ 两颗
28×28 圆钮(Check/Close,与胶囊确认/取消同一套画法),几何抽成 `vocab_card_layout`
纯函数(行多到装不下时压行高并缩按钮,保证 5 条上限内不重叠、不出舞台)。
- 宿主:`VocabularySuggestionsChanged` → 重算并顶卡;卡片只在 `vocab_card_allowed`(没有
会话)时出现 —— 卡片与录音胶囊共用一个窗口,会话在飞时必须让路(照 Tauri 那道闸);
会话一开始就丢掉候选;过期候选每秒重算自己消失(Core 只按需清理、不发事件)。
- i18n:`vocabCard.title/accept/reject` 三键五语,值与 Tauri 目录逐字一致。
测试:卡片渲染 + 点中「记住/不用」各自带回对应候选 id、5 条满卡不重叠不出界、候选
经协议落地且旧宿主无 `suggestions` 仍可解析、闸门只放 Idle。
package revision 129。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
补齐Linux的缺失功能,暂未完成