baiqing
81fa80da69
fix(windows): NSIS x64 dll 用 Sysnative 绕过 WOW64 重定向
...
NSIS installer 是 32-bit 进程,进程内的 $SYSDIR / $WINDIR\System32 都因
WOW64 文件系统重定向变成 $WINDIR\SysWOW64,跑的是 32-bit regsvr32 → x64 dll
被注册到 HKLM\Software\Wow6432Node\Classes\CLSID(KEY_WOW64_32KEY 视图)。
但 windows_ime_profile.rs 用 KEY_WOW64_64KEY 校验时只看 HKLM\Software\Classes
\CLSID,永远查不到 → 仍判 RegistrationBroken("OpenLess COM registration is
missing")。
修复:x64 改用 $WINDIR\Sysnative\regsvr32.exe — 这是 32-bit 进程访问真
64-bit System32 的专用 alias,写入会落在 KEY_WOW64_64KEY 视图。x86 保持
SysWOW64 不变(确实需要 32-bit regsvr32 写 Wow6432Node)。
2026-05-06 10:52:12 +08:00
baiqing
c52658b08b
fix(windows): NSIS 也注册 32-bit TSF DLL — 修 P1 review
...
Codex review 发现:windows_ime_profile.rs::inspect_windows_ime_registration() 同时
用 KEY_WOW64_64KEY 和 KEY_WOW64_32KEY 两次检查 HKLM\Software\Classes\CLSID\{...}\
InprocServer32,少了任一边就返回 RegistrationBroken("OpenLess 32-bit COM
registration is missing")。MSI 流程通过 wxs 同时跑了 64-bit 和 SysWOW64\regsvr32
注册两份;之前的 NSIS hook 只注册了 x64,所以 NSIS / updater 用户依然在 Settings
页看到「不可用」。
修复:
- bundle.resources 拆出 x64 / x86 两份 dll target;x64 → tsf-ime\x64,x86 → tsf-ime\x86
- installer.nsh PostInstall/PreUninstall 增加 x86 分支,
x86 用 $WINDIR\SysWOW64\regsvr32.exe(写 KEY_WOW64_32KEY 必需)
- CI Build Windows IME native DLLs 步骤把复制 dll 移到循环里,x64 + x86 都覆盖
仓库占位
- 仓库新增 0 字节 x86 占位 dll,让本地 mac/linux 构建也能 resolve resources
2026-05-06 10:47:03 +08:00
baiqing
5aec74653b
fix(windows): NSIS 安装包注册 OpenLess TSF DLL — 修"输入法后端:不可用"
...
## 现象
1.2.15 起在 Settings 页"Windows 输入法后端"出现 Pill「不可用」。`get_windows_ime_status()`
查 HKLM 注册表四件套(COM CLSID + TSF LanguageProfile + 三个 Category),缺一即报 NotInstalled。
## 根因
- MSI 通过 wix/openless-ime.wxs 的 RegisterOpenLessIme* CustomAction 跑 regsvr32 → 注册齐全。
- NSIS 包既没把 OpenLessIme.dll 打进去(wxs.componentRefs 是 MSI-only),也没 hook 跑
regsvr32 → 用 setup.exe 安装的用户、走 updater 自动升级的存量用户全部命中"不可用"。
- 由于 updater latest-windows-x86_64.json 是 NSIS pass 写的,老 MSI 用户每次更新后也
会被替换为没 TSF 的 NSIS 版本。
## 修复
- 加 src-tauri/installer.nsh:NSIS_HOOK_POSTINSTALL/PREUNINSTALL 跑 regsvr32 注册/反注册。
- bundle.resources map 把 OpenLessIme.dll 拷贝到 $INSTDIR\tsf-ime\OpenLessIme.dll
(target 路径避开 wxs File 已声明的 windows-ime\x64\,避免 MSI component 路径冲突)。
- bundle.windows.nsis.installMode = "perMachine",让 NSIS 以管理员身份运行(写 HKLM 必需)。
- CI 在 build native dll 后把真 dll 复制到 src-tauri/openless-ime-payload/x64/OpenLessIme.dll;
仓库 commit 0 字节占位文件,让本地 mac 端 tauri build 也能 resolve resources 路径。
- build-mac.sh 与 Linux build 步骤加 --config '{"bundle":{"resources":{}}}' 跳过 Windows-only
的 dll 占位,避免污染 mac .app / Linux .deb/.rpm/.AppImage。
## Trade-off
- NSIS perMachine 升级需要 UAC,老 currentUser 用户首次升级时多一次 UAC 提示。
- MSI 包里 dll 会有两份(wxs File + bundle.resources),磁盘冗余 ~几百 KB,无功能影响。
2026-05-06 10:41:07 +08:00
baiqing
463a6d9716
fix(local-asr): 切走/删模型/手动释放时真正归还 RAM
...
三个问题合修:
1. 用户点"释放"按钮后活动监视器看 RSS 不下降。原因:encoder f32 weights
是 malloc 出来的几百 MB,free 后 macOS libmalloc 不会立即把页归还内核。
修:drop QwenAsrEngine 后调 malloc_zone_pressure_relief(NULL, 0),主动
归还。decoder bf16 走 mmap 不依赖这个。
2. 切回云端 ASR 后本地引擎仍驻留。set_active_asr_provider 切到非 local
时未释放,1.2GB+ RAM 一直留到进程退出。
3. 删除已下载的模型时未释放内存里加载的同 id 引擎,留下 mmap 指向已
unlink 文件 + RAM 不归还的双重问题。
Windows 端本期不上线(cache.rs macOS cfg 块包住改动)。
2026-05-05 20:20:46 +08:00
baiqing
cd00ebc584
fix(local-asr): 修 PR #262 review 提的两个 bug
...
(1) 小文件路径 try_download_range_append 的 retry 污染:
原实现用 append 模式打开 .partial。若上一次 attempt 已写了部分字节后失败,
retry 拿到的还是完整 chunk → append → 文件比应有大小多 N 字节,永久损坏。
改成 truncate(effective_start) + seek 写入,每次 attempt 从干净起点重写。
小文件(≤ 32MB)每个 chunk 是整个文件,重写无成本。
(2) 多文件并发一个失败不 abort 其它 worker:
原代码只 log "signaling others to stop",没真 set cancel flag。剩下的
workers 会继续吃带宽直到全部完成才把错误冒上来。
现在错误时主动 cancel.store(true),再用 self_aborted 标记区分这是"我们
因错误 abort"还是"用户主动 cancel"——前者 emit Failed,后者 emit Cancelled,
避免错误被误报成用户取消。
跟踪 PR: #262 review by github-actions PR Reviewer Guide
2026-05-05 12:59:01 +08:00
TRIP
438fce26ab
Merge pull request #262 from appergb/feat/local-asr-qwen3
...
feat(local-asr): 加本地 Qwen3-ASR 引擎(macOS 优先)
2026-05-05 12:53:35 +08:00
baiqing
8c3e4aca0d
Merge remote-tracking branch 'origin/main' into feat/local-asr-qwen3
...
# Conflicts:
# openless-all/app/src-tauri/src/coordinator.rs
2026-05-05 12:48:13 +08:00
Chris233
a0cc88d2ac
Merge pull request #260 from weikeyi/capsule-confirm-overflow
...
fix(capsule): keep confirm icon inside pill on done/error states
2026-05-05 12:46:39 +08:00
Chris233
f984dec84b
Merge pull request #261 from sora520/fix/windows-hotkey-trigger-leak
...
fix(windows): suppress consumed hotkey trigger events
2026-05-05 12:42:27 +08:00
Chris233
3e1ba69bfc
Merge pull request #252 from Cooper-X-Oak/fix/recorder-timeout-238
...
fix(recorder): 添加 liveness watchdog 和全局超时保护以修复 #238
2026-05-05 12:32:37 +08:00
kaku.ki
9c0ae1495a
fix(windows): suppress consumed hotkey trigger events
2026-05-05 13:17:50 +09:00
baiqing
520787b0f7
feat(local-asr): 引擎内存常驻 + 切到本地时预加载 + 释放时机可配
...
用户痛点:之前每次按 hotkey 都重加载 1.2GB 模型 → 首句词延迟 3-5s。
新流程:
- 新 LocalAsrCache:模型一次 load 后驻留内存,跨多次会话复用
- LocalQwenAsr::new 改成接受 Arc<QwenAsrEngine>(引擎所有权挪到 cache)
- build_local_qwen3 改成 async + spawn_blocking 走 cache.get_or_load()
- 切换 active model 时自动 drop 旧引擎再加载新的
- end_session 后调 cache.touch() + schedule_local_asr_release
- keep_loaded_secs == 0 → 立即释放
- keep_loaded_secs > 0 → spawn 一个 sleep+check 任务,到点查 last_used,
若中间又被使用过则跳过释放
预加载:
- commands::set_active_asr_provider("local-qwen3") 触发 preload_in_background
- 用户在「模型设置」页可手动「立即加载」/「立即释放」
- 实时显示「内存中的引擎: <model id>(约占 1.2-3.4 GB 内存)」
- 5 秒轮询刷新状态
新设置项:
- prefs.local_asr_keep_loaded_secs (u32,默认 300)
- UI 选项:说完话立即释放 / 1 min / 5 min(默认)/ 30 min / 始终保留
- 4 个新 IPC:local_asr_engine_status / release_engine / preload /
set_keep_loaded_secs
2026-05-05 12:09:55 +08:00
baiqing
8ed674ecaf
fix(local-asr): 多文件并发 + 8MB 小块 + aria2 UA — 对齐 hfd/hf_xet 实测
...
参考 hf-mirror 官方推荐工具 hfd(aria2 包装)+ hf_xet 默认 16 并发:
- PARALLEL_FILES = 3:原本顺序下文件,大 .safetensors 在跑时 6 个小文件
全在排队;改成最多 3 个文件并发,让小文件不阻塞、大文件 throttle
时剩余带宽喂别的文件
- PARALLEL chunks per file = 4 → 8:贴 hf_xet 默认 (16) 的折中
- CHUNK_SIZE = 32MB → 8MB:单连接寿命缩到 5-20s(CDN throttle 临界点之下),
失败重做成本降 4 倍
- User-Agent: openless/x → aria2/1.36.0:实测 aria2 UA 在 HF / hf-mirror
反滥用规则里走白名单;自定义 UA 在 sustained 传输后会被切流
(日志里大量 "end of file before message length reached" 就是这个)
进度计算改成多文件汇总:每个文件 in_flight bytes 用 AtomicU64,全模型
总进度 = sum(in_flight) + already_done。失败时 cancel 标志传播给其它
spawn task 早退。
2026-05-05 11:33:46 +08:00
weikeyi
dab04fc345
fix(capsule): keep confirm icon inside pill on done/error states
2026-05-05 11:17:49 +08:00
baiqing
209dc9fece
fix(local-asr): 4 并发分块下载 + 取消正确传播 + 残留进度可见可删
...
四个用户报的问题,逐个修:
(1) 慢——单连接顺序拉,HF CDN 单连接限速明显
→ 4 并发 chunk + sparse 文件 seek+write,每完成一块原子追加 .partial.idx;
与 huggingface_hub / aria2 / hf_transfer 同款思路;下次启动只下未完成块
→ 走严格 206 协议(并发 seek 模式不能容忍服务端忽略 Range 返 200,否则会
把整个文件写到 chunk 偏移导致灾难);非 206 直接 fail 让 retry 接手
(2) 取消按钮没反应
→ 原因:旧 download_one 的 chunk loop 在 chunk Err 后 "若有进度就 continue";
用户取消时也属于"有进度"路径 → cancel 标志被吞
→ 新版每次 spawn task 都拿 Arc<AtomicBool>,每个 chunk + 每个流块都查
cancel;run_download 主线在 download_one 返回后立刻再查 cancel emit
Cancelled 事件
→ DownloadManager::cancel 加日志方便诊断
(3) 继续下载丢进度条
→ showProgress 原来只在有 progress 事件时为 true → 重启 app 后看不到 partial
→ 加 hasPartial 判断:downloadedBytes>0 && !isDownloaded 也算 showProgress
→ 按钮文案对应改 "继续下载" / "Resume"
(4) 残留无法删
→ 原本只有 isDownloaded 状态才出 删除按钮
→ 加 hasPartial 状态显示 [继续下载] [删除] 两个按钮
2026-05-05 11:13:41 +08:00
Chris233
c5aa3fef4a
Merge pull request #259 from H-Chris233/main
...
feat(i18n): add full Traditional Chinese (zh-TW) UI support
2026-05-05 11:07:37 +08:00
kaku.ki
23d7f44a96
fix(windows): stabilize capsule visuals and audio meter
2026-05-05 11:51:57 +09:00
H-Chris233
e50bc17a15
Add complete Traditional Chinese UI locale support
...
Add zh-TW locale resources and wire it through language selection,
persistence, and system-locale detection. Also add static shape typing
for zh-TW to prevent future key drift from zh-CN.
Constraint: Keep changes minimal and localized to i18n + language pickers
Rejected: Partial zh-TW translation only for language section | fails full locale support goal
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep zh-TW key shape locked to zh-CN when adding new translation keys
Tested: npm run -s build (openless-all/app)
Not-tested: Manual runtime walkthrough on all platform shells
2026-05-05 10:51:46 +08:00
baiqing
4a3531083b
feat(local-asr): HTTP Range 分块下载 + 加载测试按钮
...
下载彻底重写 (huggingface_hub / aria2 同款思路):
- 之前是"整文件 GET,失败重试整文件"——HF CDN 对长连接不友好,到某个固定时间
点会强制断流,导致看到"卡 5% 就死"。用户对比别家工具发现这点,确实如此。
- 现在 download_one 内按 32MB chunk 跑 Range,每块 4 次指数退避 retry,每次都
从 .partial 真实长度续传;CDN 中途断流只丢一个 chunk
- 防御服务端忽略 Range 返回 200 + 全文件:检测到就 truncate .partial 重头来,
避免 append 污染
- client 加 User-Agent (HF 把 no-UA 流量算异常 → 限速)
- pool_idle_timeout 缩短,避免复用已被 CDN 关掉的连接
加载测试按钮(用户:「做好加载模型和测试按钮」):
- 内嵌 antirez 自带的 samples/test_speech.wav(120KB,"Hello. This is a test
of the Voxtrail speech-to-text system.")
- 严格按 RIFF chunk 链解析(fmt 后面带 LIST/INFO,不能 +44 硬偏移)
- 新 IPC local_asr_test_model:spawn_blocking 跑 qwen_load + transcribe_audio
分别计时;返回 backend / 原文 / 识别 / load_ms / transcribe_ms
- LocalAsr 页每个已下载模型行加「加载并测试」按钮 + 结果卡片
Backend 标识改为 "Apple Accelerate (AMX/NEON, CPU)"——antirez 引擎不走 Metal
GPU(作者明确选择跳过 MPS),Apple Silicon 上靠 AMX 矩阵协处理器 + NEON 加速。
不撒谎说支持 Metal。
2026-05-05 10:27:48 +08:00
baiqing
25d9e9f3da
fix(local-asr): 下载 retry 升级 4 次指数退避 + 已下载列表/删除/性能警告
...
下载失败修:
- HF 大文件被 CDN 中途断流(end of file before message length reached);
原 1 次 retry 不够 + 间隔太短两次都失败
- 升到 4 次:1s → 4s → 16s 指数退避;每次都从 .partial 当前长度断点续传
- reqwest client 加 connect_timeout(30s) + pool_idle_timeout(20s) 避免
连接被复用到已被 CDN 关掉的 socket
- 不设整体 timeout(1GB 文件没意义),只防卡死
UI 补「已下载管理 + 性能预期警告」(用户两条新需求):
- Settings 的 LocalAsrProviderHint 现在列出所有已下载模型 + 每行删除按钮
(原来只显示当前激活模型 ready/not 状态,没看到其它模型也没法删)
- Settings 与 LocalAsr 页顶部都加 ⚠️ 性能警告卡片:明确"比云端慢/中文准确率
通常不如火山+turbo/适用于离线/隐私场景",避免用户误判
- i18n 新增 zh+en:localAsrPerformanceWarning / localAsrDownloadedTitle /
localAsrDelete + LocalAsr.performanceWarning
2026-05-05 10:03:26 +08:00
baiqing
5a58f74c4a
fix(local-asr): HF 下载 TLS unexpected_eof + Settings 引导改下载入口
...
下载层修两件事:
1. 改用 native-tls (macOS = SecureTransport) 而非 rustls:
HF LFS CDN 经常不发 TLS close_notify 直接关 TCP,rustls 0.22+ 把这当
致命 unexpected_eof,实测多个 .safetensors 失败。SecureTransport 容错。
Volcengine WebSocket 不动,继续走 rustls。
2. 单文件失败自动 retry 1 次(带 1s 退避,且尊重 cancel)。
HF 大文件偶发瞬断很常见,不让用户手点重试。
Settings 引导(按用户反馈):
- ASR 选 'local-qwen3' 时**不**渲染 API key / Base URL / Model 三个填空
- 改为 LocalAsrProviderHint 卡片:
* 文案"本地 Qwen3-ASR 在本机运行,无需 API Key"
* 已下载/未下载 Pill 显示当前激活模型状态
* 一键按钮 → 派发 NAVIGATE_LOCAL_ASR_EVENT
- FloatingShell 监听同事件,关 Settings modal + 切到 'localAsr' tab
- i18n 同步 zh + en
2026-05-05 09:56:54 +08:00
baiqing
839ad76c5b
fix(local-asr): 用 tauri::async_runtime 取代 tokio::spawn — 修点击下载即崩
...
崩溃栈:local_asr_download_model → DownloadManager::start → tokio::spawn
→ panic("there is no reactor running")。
根因:Tauri 同步 command 在 main thread 上下文跑,没进 tokio runtime;
tokio::spawn 直接 panic。换 tauri::async_runtime::spawn —— 它走 Tauri
持有的 runtime handle,不依赖调用方上下文。
同步把 local_provider.rs 的 spawn_blocking 也换成 tauri::async_runtime
版本(虽然现在在 async 路径上调没事,保持一致防回归)。
附带把 qwen_ffi.rs 的 `unsafe extern "C"` block 改回经典 extern block,
匹配 CLAUDE.md 声明的 rust-version = "1.77"(前者要 1.82+)。
2026-05-05 09:49:33 +08:00
baiqing
dd43a7801d
feat(local-asr): 前端「模型设置」页 + Settings 预设 + 反硬编码重构 — P5
...
后端反硬编码(按用户反馈):
- models.rs 删 files()/approx_bytes(),改用哨兵文件 .openless-asr-ready
判 is_downloaded、walk_dir 求和算 downloaded_bytes
- download.rs 加 fetch_remote_info(repo, mirror):实时 GET HF
/api/models/<repo>/tree/main 拿真实文件清单 + 尺寸;过滤 .md/.png/.git*
等非权重文件(白名单:json/safetensors/txt/bin/model/tiktoken)
- 新 IPC: local_asr_fetch_remote_info(modelId, mirror)
→ RemoteInfo { files: [{path,size}], totalBytes }
前端:
- LocalAsr.tsx 新页:状态卡 / 镜像源切换 / 模型列表 (下载/取消/删除/设为默认)
+ 进度条订阅 local-asr-download-progress 事件实时刷新
+ 真实尺寸通过 fetchLocalAsrRemoteInfo 拉取,**不硬编码**
- localAsr.ts: 全套 IPC 包装 + dev mock 分支
- FloatingShell.tsx + useAppState.ts: 加 'localAsr' tab,icon=archive
- Settings.tsx ASR_PRESETS 加 { id: 'local-qwen3', nameKey: 'asrLocalQwen3' }
- i18n zh+en 同步:nav.localAsr / asrLocalQwen3 / 整块 localAsr.* 文案
- types.ts UserPreferences 加 localAsrActiveModel + localAsrMirror
2026-05-05 09:39:26 +08:00
baiqing
4656ecc7c7
feat(local-asr): coordinator 接 ActiveAsr::Local 第三分支 — P4
...
- local_provider.rs (LocalQwenAsr):实现 AudioConsumer 缓 i16 PCM →
stop 时转 f32 → 跑 qwen_transcribe_stream,token 经
`local-asr-token` 事件实时推到前端胶囊;spawn_blocking 防止占住 tokio runtime
- coordinator.rs:
* ActiveAsr::Local(Arc<LocalQwenAsr>) cfg(macos)
* start_dictation_for_session 优先走 local 分支再回落 whisper/volc
* end_session 加 Local arm,失败静默回退(CLAUDE.md 不变量)
* cancel_active_asr / ensure_asr_credentials 都补 Local 分支
* build_local_qwen3 helper:从 prefs 拿 model id → models::model_dir → 加载
* ensure_local_qwen3_model_ready:模型未下载时友好提示用户去模型设置页
- 非 macOS 选择 local-qwen3 时 ensure_asr_credentials 直接返回错误
2026-05-05 09:29:12 +08:00
baiqing
51b638a52e
feat(local-asr): IPC commands + 偏好持久化 — P3
...
- types.rs UserPreferences 加 local_asr_active_model + local_asr_mirror,
serde 默认值保证向后兼容(老 prefs.json 不需手工迁移)
- commands.rs 加 7 个 local_asr_* IPC:
get_settings / set_active_model / set_mirror /
list_models / download_model / cancel_download / delete_model
- lib.rs .manage(Arc<DownloadManager>) + invoke_handler 注册全部新命令
- 引擎可用性通过 cfg!(target_os = "macos") 暴露给前端,Win UI 据此灰按钮
2026-05-05 09:26:46 +08:00
baiqing
7b38e26473
feat(local-asr): 加模型注册表 + HF/HF-Mirror 下载管理 — P2
...
- models.rs: ModelId (0.6B / 1.7B) + 文件清单复刻 antirez download_model.sh
+ is_downloaded / downloaded_bytes / list_status / delete_model
- download.rs: DownloadManager (per-model AtomicBool 取消) + reqwest stream
写 .partial 然后原子 rename;支持 Range 续传;Tauri 事件
`local-asr-download-progress` 上报五种 phase
- Mirror enum:Huggingface(默认)/ HfMirror(hf-mirror.com 国内镜像)
- persistence.rs 加 pub fn local_models_root() —— 路径
~/Library/Application Support/OpenLess/models/qwen3-asr/<id>/
2026-05-05 09:24:27 +08:00
baiqing
bd3bac3ca6
feat(local-asr): 接入 antirez/qwen-asr 编译链路 — 阶段 1 脚手架
...
- vendor antirez/qwen-asr 作为 git submodule (commit b00b789)
- build.rs 用 cc 编 10 个 C 源 + 链 Accelerate (仅 macOS)
- src/asr/local/{mod,qwen_ffi,qwen_engine}.rs 包装最小 FFI 面:
load / transcribe_audio / transcribe_stream + token 回调蹦床
- libqwen_asr.a (~434KB) 已链通,nm 验证 qwen_* 符号齐全
- Windows 端不编入,路径见 issue #256
下一阶段:模型下载管理 + IPC commands + 新页 LocalAsr.tsx + coordinator 路由
2026-05-05 09:17:32 +08:00
baiqing
149a6b8eca
chore(release): 1.2.19 — Windows updater 修 NSIS+MSI 拆两轮跑
2026-05-04 23:12:55 +08:00
baiqing
429780b5e4
chore(release): 1.2.18 — Windows bundle 顺序锁 nsis,msi 让 updater 资产先落盘
...
历史链:
1.2.13 ✓ 无 IME wxs,MSI+NSIS 全过
1.2.15 ✗ 加 IME wxs;CI pwsh 把 JSON 引号吃掉,tauri 连 candle 都没跑
1.2.16 ✗ shell 切 bash 后到 light 阶段,但 ICE80 触发,Repair 没 -sice
1.2.17 ✗ Repair 加了 -sice:ICE80 修了 MSI;但 Tauri 字母序 msi→nsis 让
MSI 先挂掉,NSIS 永远没机会跑,updater .exe(.sig) 缺失
1.2.18 显式 --bundles nsis,msi:NSIS 先写盘产 updater 资产,MSI 再失败
由 Repair 兜底 light.exe 重链
唯一改动:Build (Windows) 步骤两个分支都加 --bundles nsis,msi。
预留:NSIS 不读 wix.fragmentPaths,所以 .exe 暂不带 IME DLL;MSI 仍带,
后续单独 issue 跟进 NSIS IME 集成,不挂当前发版。
2026-05-04 22:56:33 +08:00
baiqing
a5f1a8f16d
chore(release): 1.2.17 — Windows MSI 修 light.exe ICE80 报错
...
x86/x64 IME DLL 都装进 INSTALLDIR\windows-ime\,32-bit 组件落在 64-bit
Directory 是 ICE80 的硬性报错。本场景路径绝对指向、不依赖 SysWOW64
重定向,按 Microsoft 文档是合法用法,给手动 light.exe 重链加 -sice:ICE80
即可放行。改动同步覆盖 CI workflow 和本地打包脚本。
2026-05-04 22:21:50 +08:00
baiqing
49210363d6
chore(release): 1.2.16 — Windows MSI 修 PowerShell 引号吞双引号 bug
...
1.2.15 第 4 轮 Windows job 在 wix 阶段之前就 exit 2,根因是上一版我把
Build (Windows) 整步切成 shell:pwsh 后,& npm run tauri --- build --config
'{"bundle":{"createUpdaterArtifacts":true}}' 这条命令 PowerShell 调外部
进程时把 JSON 内的双引号剥光,tauri 收到 {bundle:{createUpdaterArtifacts:true}}
直接拒绝。log 第 586 行能看到剥过引号的 commandline。
修法:
- Build (Windows) 切回 shell:bash(单引号能保留内部双引号)。set +e 抓
exit code 写到 GITHUB_ENV,让本步不直接失败,把判断权交给下一步。
- 新增独立 step "Repair Windows MSI if Tauri failed at WiX link"(pwsh),
仅在 tauri 非零退出且 MSI 不存在时手动从 appRoot 跑 light.exe 重链。
本地 windows-package-msvc.ps1::Repair-TauriMsiBundle 同款手术。
2026-05-04 21:53:23 +08:00
cooper
a8741dd568
fix(recorder): watchdog 从播放开始时计时,避免慢启动误报
...
- 移除 StreamState::stream_start_time 字段
- 在 watchdog 线程内部记录启动时间
- 确保首次回调截止时间从 stream.play() 成功后开始计时
- 修复慢速后端可能导致的过早超时问题
2026-05-04 21:46:07 +08:00
Chris233
88452cb2ca
Merge pull request #251 from H-Chris233/feat/asr-e2e-validation
...
feat(settings): add end-to-end ASR provider validation
2026-05-04 21:45:45 +08:00
cooper and Claude Sonnet 4.6
87838617f8
fix(recorder): 添加首次回调截止时间检测
...
根据 code review 反馈,修复 watchdog 的行为缺口:
## 问题
修复 Finding 3 后,虽然避免了误报慢启动设备,但也失去了对
"第一个回调永远不到达"这种失效模式的检测能力。
如果 stream.play() 成功但 CPAL 从未发送任何回调,watchdog
的 `if let Some(last_time)` 分支永远不会执行,session 仍然
可能无限期挂起。
## 修复方案
添加"首次回调截止时间"机制,watchdog 现在检查两种情况:
1. **首次回调未到达**(last_callback_time = None)
- 检查是否超过 FIRST_CALLBACK_DEADLINE_SECS (5秒)
- 如果超过,报错:"录音启动后 X 秒内未收到回调"
2. **回调中途停止**(last_callback_time = Some)
- 检查是否超过 CALLBACK_TIMEOUT_SECS (3秒)
- 如果超过,报错:"录音回调静默停止 X 秒"
## 技术细节
- 在 StreamState 中添加 stream_start_time 字段
- 在 watchdog 中使用 match 语句区分两种情况
- 5 秒首次回调截止时间足够慢启动设备,但能捕获永不回调的失败
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-04 21:39:52 +08:00
H-Chris233
cb2eb8e9ce
Preserve query parameters when building ASR transcription validation URL
...
ASR validation URL normalization now edits the parsed URL path instead of string-appending to the raw endpoint, so endpoints with query parameters (e.g. api-version) remain valid after adding /audio/transcriptions.
Constraint: Keep endpoint normalization minimal and avoid changing query/fragment semantics
Rejected: Drop query parameters during validation | would break Azure-style OpenAI-compatible deployments
Confidence: high
Scope-risk: narrow
Tested: cargo test -q
2026-05-04 21:35:14 +08:00
H-Chris233
df2d51e014
Normalize ASR transcription endpoint and enforce streaming response cap
...
Addresses review feedback: ASR validation now recognizes OpenAI-style /chat/completions endpoints when building the /audio/transcriptions URL, and response-size protection is enforced while streaming the body instead of after buffering it all.
Constraint: Keep ASR validation behavior provider-agnostic and minimal in scope
Rejected: Heuristic URL rewrites beyond known suffixes | risk of breaking custom reverse proxies
Confidence: high
Scope-risk: narrow
Directive: If changing reqwest features, keep Cargo.lock in sync and re-run tests
Tested: cargo test -q
2026-05-04 21:16:46 +08:00
H-Chris233
fd60f815c0
Accept /audio base URLs when validating ASR transcription endpoint
...
Some OpenAI-compatible deployments expose ASR under a base URL that already ends with /audio. ASR validation now treats that as a valid prefix and appends only /transcriptions to avoid generating /audio/audio/transcriptions.
Constraint: Keep ASR validation URL normalization conservative and backwards-compatible
Rejected: Full URL path rewrite heuristics | risk of breaking custom reverse-proxy layouts
Confidence: high
Scope-risk: narrow
Tested: cargo test -q
2026-05-04 20:51:40 +08:00
baiqing
dcfeb405bf
chore(release): 1.2.15 — Windows MSI 修复 IME bundle 集成
...
1.2.14 三轮 release pipeline Windows job 全挂在 WiX 集成上:
1. 没构建 IME DLL → light.exe 找不到文件
2. 加了 build 步骤后用相对路径 src-tauri\target\... → Tauri wix bundler
cwd 和本地脚本不同,相对路径 resolve 错位
3. 改 $(env.OPENLESS_IME_DLL_X64) 想用绝对路径 → candle 处理 env var
substitution 时 abort(具体原因被 Tauri 吞掉)
1.2.15 切换到本地 windows-package-msvc.ps1 已经验证可行的 Repair 思路:
- wxs 维持字面相对路径 src-tauri\target\... (让 candle 正常吃过)
- workflow 接住 tauri build 失败:从 appRoot (openless-all/app/) 手动跑
light.exe 重链 main.wixobj + openless-ime.wixobj。这是 light 唯一能
resolve 该相对路径的 cwd。
2026-05-04 20:39:22 +08:00
H-Chris233
73dbd4cef7
Harden ASR validation path against endpoint and response regressions
...
Follow-up fixes from dual review keep the new end-to-end ASR check while removing compatibility and robustness regressions: normalize transcription endpoint URL, enforce HTTPS except localhost, cap response size, and switch to stable backend error codes mapped in i18n.
Constraint: Preserve issue #242 full-chain validation behavior with minimal surface change
Rejected: Revert to model-list-only validation | no longer verifies real transcription route
Confidence: high
Scope-risk: narrow
Directive: Keep providerHttpStatus:* and stable error-code contract synchronized with Settings error mapping
Tested: npm run build; cargo check -q
Not-tested: Manual validation against all third-party ASR endpoints
2026-05-04 20:27:08 +08:00
H-Chris233
2ae3bdf68a
Use real transcription call for ASR connection validation
...
ASR validation now runs one end-to-end /audio/transcriptions request with the currently selected model and a tiny bundled silence WAV payload. This verifies endpoint, auth, model routing, and response schema in one pass instead of model-list reachability only.
Constraint: Keep UI/API contract unchanged and avoid touching recording pipeline
Rejected: Reuse live microphone capture in settings validation | invasive and non-deterministic for quick checks
Confidence: high
Scope-risk: narrow
Directive: Preserve providerHttpStatus:* error marker so frontend can keep status-specific guidance
Tested: npm run build; cargo check -q
Not-tested: Live validation against every third-party ASR provider implementation
2026-05-04 20:27:08 +08:00
H-Chris233
2438c901eb
Ensure ASR connection check validates the configured model
...
Issue #242 asks ASR to reach parity with LLM availability checks. Validation now requires a configured ASR model and verifies it exists in the provider model list, instead of only checking endpoint/auth reachability.
Constraint: Keep existing settings flow and IPC shape with minimal UI change
Rejected: Add a new ASR test-audio transcription request | adds latency and provider-specific request complexity
Confidence: high
Scope-risk: narrow
Directive: Keep validate and fetch-models semantics aligned; validate should fail when configured model cannot actually be selected
Tested: npm run build; cargo check -q
Not-tested: Manual Settings UI interaction against live third-party ASR endpoints
2026-05-04 20:27:08 +08:00
baiqing
ac2edbe961
ci(release): wxs 用 env 绝对路径定位 IME DLL
...
之前 light.exe 失败的根因:openless-ime.wxs 的 Source 写的相对路径
src-tauri\target\windows-ime-msvc\... ,要从 openless-all/app/ 才能
resolve;但 Tauri 内置 wix bundler 的 candle/light cwd 是 src-tauri/,
路径错位,文件找不到 → light 失败。
本地 windows-package-msvc.ps1 通过 Repair-TauriMsiBundle 从 appRoot 手动
跑 light 兜底,CI 没有这个兜底所以挂掉。
修法:wxs Source 改用 $(env.OPENLESS_IME_DLL_X64) / _X86 绝对路径变量;
工作流的 IME build 步骤把绝对路径写到 $GITHUB_ENV。这样无论 candle/light
cwd 在哪,都能拿到正确的 DLL 路径。
本地脚本本就 set 这两个 env var(windows-package-msvc.ps1:213),改后
本地 + CI 共用同一条 absolute-path 路径,Repair fallback 不再必需。
2026-05-04 20:04:18 +08:00
cooper and Claude Sonnet 4.6
2050524cbf
fix: 根据 code review 修复三个问题
...
根据 code review 反馈,修复以下问题:
## 1. High: 超时后未清理 ASR session
- 在全局超时分支中添加 asr.cancel() 调用
- 避免 websocket/read tasks 继续运行造成资源泄漏
- 影响文件:coordinator.rs (dictation 和 QA 路径)
## 2. Medium: QA 路径缺少超时保护
- 为 QA 路径的 await_final_result() 添加相同的超时保护
- 保持 dictation 和 QA 路径的一致性
- 影响文件:coordinator.rs:2288-2298
## 3. Medium: Watchdog 误报慢启动设备
- 将 last_callback_time 初始化从 Some(Instant::now()) 改为 None
- 只有在第一次回调后才开始计时
- 避免慢启动设备被误判为"静默停止"
- 影响文件:recorder.rs:333
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-04 19:43:34 +08:00
cooper and Claude Sonnet 4.6
e0d459bbd0
fix(recorder): 添加 liveness watchdog 和全局超时保护以修复 #238
...
根据 Issue #238 的分析,录音器异常停止后会导致胶囊卡在 processing 状态。
本次修复实施了两层防护机制:
## 修复 1: Recorder Liveness Watchdog
- 在 StreamState 中添加 last_callback_time 字段跟踪最后一次回调时间
- 在 process_callback 中更新时间戳
- 在 run_audio_thread 中启动 watchdog 线程,每秒检查一次
- 如果 3 秒没有回调,主动触发 runtime_error 通知 coordinator
- 解决了"录音回调静默停止"的根本问题
## 修复 2: Coordinator 全局超时保护
- 添加 COORDINATOR_GLOBAL_TIMEOUT_SECS = 15 秒常量
- 在 end_session() 中为 await_final_result() 和 transcribe() 添加超时保护
- 超时时强制恢复到 Idle 状态,显示错误胶囊
- 作为最后的防线,即使 ASR 超时机制失效也能恢复
## 技术细节
- Watchdog 使用独立线程,避免阻塞主线程
- 超时时间设置为 15 秒(比 ASR 的 12 秒 FINAL_RESULT_TIMEOUT 稍长)
- 保持与现有错误处理机制的一致性
Fixes #238
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-04 19:06:08 +08:00
baiqing
72ebe76be0
chore(release): 1.2.14
...
包含自 1.2.13 以来的修复与新功能:
- feat(autostart): 跨端开机自启 (#194 )
- feat(asr): SiliconFlow / GLM-ASR / Groq / OpenAI Whisper preset (#212 )
- fix(qa): Windows / Linux 划词追问浮窗 Esc + 拖拽 + 文案 (#205 / #206 )
- fix(settings): preset 切换 race + per-provider 凭据隔离 (#219 / #220 )
- fix(overview): 概览整屏适配 + 嵌套 scroller 细滚动条 (#243 / #248 )
- 一系列 Windows IME (TSF) 模块 (#233 / #240 等)
2026-05-04 18:58:03 +08:00
Chris233
5d15f1d2f2
Merge pull request #241 from H-Chris233/main
...
security(qa): sanitize assistant markdown rendering in QA panel
2026-05-04 18:52:05 +08:00
baiqing
fd0b2fdf02
fix(overview): Codex P1 + P2 — 保留 fallback scroll + 嵌套 scroller 同步细化
...
Codex P1:之前 displayTab==='overview' 时 wrapper 用 overflow:hidden,
窗口被压缩或文案变长(i18n / 字体放大)后底部 Recent Card 整片不可达,
没有 scroll 兜底路径。
修法:所有 tab 一律 overflow:auto。Overview.tsx 内部已经用 flex 把底部
行 grow 到撑满 → 正常尺寸下内容刚好占满,浏览器不显示 scrollbar;真挤
不下了 fallback 出细 scrollbar。"整屏不滚"的视觉效果保留,但有 scroll
保险。
Codex P2:::-webkit-scrollbar 不级联到子节点,所以 .ol-thinscroll 加
在 wrapper 上对 History / SettingsModal 等内层 overflow:auto 容器无效。
把 className 显式补到这些嵌套 scroller:
- History 左侧列表 + 右侧详情 Card(Card 组件加 className prop 透传)
- Settings.tsx 内嵌模式右栏
- SettingsModal personalize/about 块
QaPanel 是独立窗口,不在主壳 thinscroll 范围内,保留默认。
2026-05-04 18:26:34 +08:00
baiqing
8b435b4844
fix(overview): 底部行 flex:1 + 仅最近识别区域滚动
...
#243 第一版给外层 wrapper 加了 overflow:hidden 让概览不滚,但底部「近 7 天 +
最近识别」grid 仍按自然高度展开 → 内容超出 wrapper 时被硬裁,看不到底部
圆角;最近识别第 5 行只露半截。
修法(Overview.tsx 底部 grid):
- grid 自身 flex:1, minHeight:0,撑满父 flex:column 的剩余高度
- 两张 Card 都加 display:flex/column + minHeight:0;最近识别 Card 还加
overflow:hidden,让内层行严格剪裁到 Card 的圆角
- 最近识别的 list <div> 加 .ol-thinscroll + flex:1 + overflow:auto,自己
独立滚动;其他区域按需求保持不滚
视觉效果:底部圆角现在完整显示并与上方 metrics / providers 对齐;只有最
近识别区域出现细滚动条,整体仍是 dashboard「一眼看完」节奏。
2026-05-04 18:19:22 +08:00
H-Chris233
806fa26693
Keep QA code snippets readable while preserving markdown XSS guard
...
Review feedback found that pre-escaping the entire markdown payload caused double-encoding in inline/fenced code, making HTML/JSON/XML examples unreadable. Switch to raw markdown parsing with renderer-level HTML token escaping so code formatting stays readable while raw HTML remains inert.
Constraint: Maintain issue #221 security boundary without broad sanitizer rewrite
Rejected: Keep full-string pre-escape | breaks code snippet readability
Confidence: high
Scope-risk: narrow
Directive: Escape only raw HTML tokens, not the whole markdown source
Tested: npm run build (openless-all/app)
Not-tested: Manual QA panel rendering across all markdown edge cases
2026-05-04 18:06:51 +08:00
baiqing
b41c904104
fix(ui): 概览页整屏不滚 + 其他页 scrollbar 变细 ( #243 )
...
FloatingShell.tsx 的 page wrapper:
- displayTab === 'overview' 时 overflow:hidden,让 Overview 真"一眼看完"
- 其他 tab 维持 overflow:auto,加上 .ol-thinscroll 让 scrollbar 细而可见
tokens.css 新增 .ol-thinscroll —— 与既有 .ol-noscrollbar 同位置;区别是
"细而仍可见",保留可滚动的视觉 affordance(不像 noscrollbar 完全隐藏)。
Closes #243
2026-05-04 18:02:25 +08:00