Commit Graph
1380 Commits
Author SHA1 Message Date
浩彬(^-^)andCursor 761e6c5c80 fix(android): sync mobile style group nav with desktop sidebar (#864)
Expose polish mode and marketplace via a bottom-sheet under the Style tab, matching desktop NAV_TREE grouping for issue #862.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 19:30:01 +08:00
Chris233 cfd99be1a9 fix(windows): 主窗口改为不透明——消除录音时 DWM/GPU 饱和导致的系统卡顿 (#882)
主窗口 transparent:true 使 WebView2 内容整页走 per-pixel alpha 分层合成,
但 UI 所有大面积层(winchrome/shell/sidebar/内容区)都是不透明背景,
Mica 磨砂实际被完全盖住,透明是纯 GPU 成本零视觉收益。

录音期间叠加胶囊 60fps WebGL + 30Hz 事件,DWM 被拖到饱和时:
- 其他软件合成帧送不出去 → 全局无响应
- 任务栏右键触发缩略图/重合成 → GPU 飙升

新增 tauri.windows.conf.json 平台覆盖(json_patch 数组整体替换,
必须完整列出窗口),main 窗口 transparent:false,capsule 保留透明。
macOS/Linux 不受影响。参考 tauri.linux.conf.json 既有先例。
2026-08-02 19:08:53 +08:00
浩彬(^-^)andCursor c498746599 fix(android): hide overview activity heatmap on mobile layout (#863)
Skip getActivityStats IPC and ActivityHeatmapCard when useMobileLayout()
is true so narrow screens avoid horizontal overflow and unnecessary work.

Closes #861

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 19:05:44 +08:00
8f49971874 feat(asr): support additional Bailian speech models (#866)
* feat(asr): support more Bailian speech models

* test(asr): cover final 8k downsample phase

* fix(asr): address review findings on Bailian model routing (#866)

- route paraformer-8k-realtime / sensevoice-8k-realtime to classic WebSocket,
  matching the 8k downsample naming space in bailian.rs
- downsample 16k->8k with pairwise averaging (first-order low-pass) instead of
  pure decimation to reduce aliasing; drop the lone tail sample on flush
- cap async provider validation at 120s (60s poll) instead of 660s so the
  validate button cannot block for nearly 11 minutes
- join async transcript segments with a space so latin-script words do not
  get glued together

* fix(asr): drop qwen3-asr-flash-filetrans and harden async polling (#866)

- qwen3-asr-flash-filetrans only accepts public URLs per DashScope docs; it
  is incompatible with the local-recording temporary OSS + oss:// path, so
  reject it in protocol_for_model / routing / picker / docs instead of
  shipping a model that would silently fail on real recordings
- simplify async_transcription_body / extract_async_result_url to the
  single fun-asr results shape
- add bounded retry (exponential backoff, deadline-capped) to async task
  polling and result download so one transient network blip does not
  discard an entire recording

* docs(asr): list qwen-audio-3.0-asr-flash as a synchronous model (#866)

Merge with beta #876 brought qwen-audio-3.0-asr-flash into the model
picker and protocol routing; keep the model reference doc in sync and
note that -streaming variants are not supported.

---------

Co-authored-by: LauraGPT <lauragpt@users.noreply.github.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-08-02 18:55:03 +08:00
Murphy 10ff058626 feat(asr): support qwen audio batch transcription (#876) 2026-08-02 11:53:53 +08:00
bigsongandClaude Fable 5 f0ffa8a873 fix(capsule): 入场帧强制重注册 Space 贴附——胶囊不再被 macOS 26 钉死在单个桌面 (#875)
实测(2026-07-31):macOS 26 会在运行中把胶囊窗口从「全 Space 贴附」剥离,
之后它表现为普通受管窗口:只在某一个桌面可见,其它桌面上听写全程无任何
UI 指示(后端 30Hz orderFrontRegardless 全部无效,窗口 onscreen=0,最长观测
到 78 秒无提示录音)。用户侧症状即「微信里不弹胶囊、Claude 里又有」——
实际按桌面分,不按 App 分。

CGS 取证 + 体外复现(CGSRemoveWindowsFromSpaces 人工制造同款坏状态)跑遍
四种救法,结论:
  · 重写同值 273:无效(AppKit 同值写入是 no-op,这正是坏状态救不回的原因)
  · 隐藏时翻转 0→273 再显示:无效(隐藏期间的值变化不触发重注册)
  · 同一 tick 连写 0、273:无效(被合并)
  · 窗口可见时发生 CanJoinAllSpaces 位 0→1 的转变:有效,完整恢复贴附
  · 跨显示器移动:有效(与野外观察到的「外接屏上意外自愈」一致)

修法:入场帧(隐藏→可见)先以 272(保留 Stationary|FullScreenAuxiliary,仅去
CanJoinAllSpaces 位)上屏,30ms 后的下一个主线程 tick 写回 273——可见状态下
的位翻转强制 WindowServer 重新注册贴附。30Hz level 帧不参与。写前读当前值,
发现漂移打 warn 留证据。glow 窗口同款写法一并加固。

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 11:53:17 +08:00
bigsongandClaude Fable 5 95edf8943b fix(capsule): 润色/插入失败改走 error 态——失败提示不再被前端丢掉 (#874)
胶囊只在 error 态渲染 message,done 态按设计是「冻结光效淡出、不带文字」
(Capsule.tsx 的 VoiceOrbStage:`state === 'error' && <span>{message}</span>`)。

但 end_session 无论成败都发 CapsuleState::Done。于是 default_done_message
辛辛苦苦拼出来的失败文案,全部在前端被丢弃——算了、传了、不显示。

最典型的受害者是润色失败。它会静默回退成未润色的原文,而胶囊照常显示成功态,
界面上没有任何痕迹。实测后果:LLM 凭证失效后连着十几个小时每一句都在拿原文,
用户只能靠「今天出来的字怎么变笨了」察觉,而日志里其实每一句都 ERROR 了。
default_done_message 的注释写着「polish 失败优先告知用户」——意图是对的,
只是这句话从来没能显示出来。

改成按结果选状态:润色失败 / 插入失败 / TSF 未上屏走 error 态,成功仍走 done。
文案生成逻辑一行没动,只改用哪个状态发出去。

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 11:53:01 +08:00
LeiYangandChris233 83c4f7f4e9 feat(asr): 火山引擎 ASR 新增方舟 API Key 鉴权模式 (#867)
* feat(asr): add Ark API Key auth mode for Volcengine ASR

* refactor(asr): isolate Ark API Key slot and harden auth-mode handling

Review fixes for #867:
- P2-1: store Ark API Key in a dedicated volcengine.api_key slot so the
  two auth modes never pollute each other's credentials
- P2-2: unify per-mode credential validation in
  VolcengineAuthMode::auth_ok (single trim semantics across open_session /
  overview configured status / dictation preflight)
- P2-3: await auth-mode persistence on switch, roll back with a failure
  toast when the write fails
- P2-4: add unit tests for endpoint/header selection per mode and
  per-mode credential validation

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-08-02 00:58:46 +08:00
5fd353de93 fix(asr): WebSocket 建连补超时——握手挂死不再让热键彻底失灵 (#871)
三个实时 ASR 供应商的 `connect_async` 没有任何超时:握手打到一半断网、公司网关 /
酒店门户静默丢包、服务端黑洞掉连接,这个 await 就永远不返回。

后果不止是「这次听写失败」。`open_session` 是在 hotkey bridge 线程上 `block_on` 等的
(那条 bridge 为修 #468/#475 的 latch 竞态改成了串行,一次只处理一个事件),所以它
一挂,后续的按下 / 松开全都排在 channel 里没人取:

- 按热键开不了录音,也停不了
- 没有任何提示,胶囊不出现,用户只会以为「热键坏了」
- 只能退出 app 重开

真机复现(dogfood 构建,StepFun 实时):9 秒内连续开 6 次会话,第 6 次的建连没返回,
之后热键完全无响应。抓栈确认 `openless-hotkey-bridge` 停在 `__psynch_cvwait`,而
主线程与 tap 线程都正常 —— tap 仍在记录按键,只是没人处理。

四个 WS 供应商里只有 volcengine 当初写对了(CONNECT_TIMEOUT + 有限重试),另外三个
一直是裸 connect_async:

- asr/stepfun_realtime.rs
- asr/qwen_realtime.rs
- asr/bailian.rs

各自补 `CONNECT_TIMEOUT`(5s,与 volcengine 取值一致),超时报
`ConnectionFailed("连接超时(N ms)")`,走既有的错误路径弹胶囊,热键继续可用。

注意与 SESSION_READY_TIMEOUT 的区别:那个管「连上之后等 session.updated 回应」,
本次补的是「连上」本身,之前完全没人管。

只改「连不上」这条已经坏掉的路径,连得上时行为不变。加了一个用例:服务端收下 TCP
但永不完成握手时,open_session 必须有界返回而不是永远挂着。

深层问题(不在本 PR):把 `begin_session` 从串行 hotkey bridge 上挪开,这样任何一个
慢 IO 都不该有能力冻住热键。那处改动风险高(串行正是为修 latch 竞态而来),单独立项。

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-08-01 23:39:51 +08:00
1ab2d52484 fix(asr): StepFun 实时转写不再随机白等 12 秒(13% 的听写中招) (#870)
* fix(asr): StepFun 实时转写不再随机白等 12 秒

真机日志(1059 次听写)统计「松手 → 出结果」耗时:中位数 0.87s,但有 117 次
精确落在 12.00~12.97s,8~12s 之间一次都没有 —— 是硬超时兜底,不是网络抖动。
按 provider 拆分后只有 StepFun 中招:117/887 = 13.2%,bailian / qwen3-realtime
/ siliconflow 合计 0/172。三条语音链路(听写、语音问答、重新转录)共用同一个
send_last_frame,全都受影响,且全程不打任何日志。

根因在收尾状态机:

1. contains_non_silent_pcm 用「任意字节 != 0」判静音。麦克风底噪(实测
   RMS≈0.0008)每个采样都非零,于是尾帧永远被判成「有真实语音」,把
   last_non_silent_audio_written_at 刷到最后一个 completed 之后 ——
   has_audio_after_last_completed 从此恒为真。
2. 宽限任务只在 FINISH_GRACE 到点查一次。这一次因上述原因不通过,就再没有第二
   次机会;而尾帧写出后客户端不再发音频,服务端也不会再吐事件(松手前停顿
   ≥VAD 阈值时,最后一段的 completed 早在尾帧之前就到了),于是一路干等到
   FINAL_RESULT_TIMEOUT。12s 后带着 partial 成功返回,所以文字是对的、也没报错。

对应三处修改:

- contains_non_silent_pcm 改成按振幅判(>1% 满量程、且至少 8 个采样,避开单点
  尖峰),并且只对尾帧的真实录音部分求值,补的静音不参与。
- 宽限任务改成有界轮询(100ms 一次)。收尾前提保持不变 —— 「无未关句段 + 最后
  一个 completed 之后没有未确认的真实音频」那条保护是对的,不为修卡顿而丢;
  新增 FINISH_HARD_DEADLINE=3s 兜住服务端始终不关句段的情况。
- 12s 兜底分支补 log::warn!,这种事不该在日志里完全隐身。

行为学佐证:卡顿会话录音末段峰值 RMS<0.005(已静音)的占 52%,正常会话仅 16%;
卡顿会话原始转写 66% 以「嗯。」结尾,正常会话仅 8% —— 都指向「说完停顿一下再
松手」这个触发条件。

测试:三条新增测试在修复前均失败(两条重现 12.00s 卡死),修复后 19/19 通过。
既有的 non_silent_audio_waits_for_delayed_vad_before_finishing 保持通过,只换了
夹具([1u8; N] 在新判据下属于底噪,改用语音级振幅)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(asr): avoid empty StepFun hard-deadline success

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-08-01 23:01:39 +08:00
93c6f6f448 fix(hotkey): 修饰键热键被当组合键用时不再误唤起听写(150ms 仲裁 + 即时撤销,胶囊不再赖着) (#858)
* fix(hotkey): 修饰键热键被当组合键用时不再唤起听写

modifier-only 触发键(如 Option)按住期间只要按下普通键,就说明用户在打
Option+任意字母/数字键这类组合键、不是想说话。此前 tap/hook 只看修饰键自身的边
沿:按下即 Pressed → 开录音,Auto 模式下快速松手还会被判成「短按锁存」,录音一直
开着停不下来。

- hotkey.rs:macOS CGEventTap / Windows WH_KEYBOARD_LL 在触发键按住期间收到普通
  键 KEY_DOWN 时发一次新的 TriggerCombined 边沿(每次按住只发一次;修饰键叠加不
  算,Shift 翻译修饰键行为不变),之后仍照常发 Released。
- coordinator:TriggerCombined 清掉按住态(随后的 Released 被 was_held 吞掉,不
  再走 Hold 松手 / Auto 短按锁存),并只取消「这一次按下开出来的」会话 ——
  toggle 停止 / 被冷却拦下 / 路由给 QA 的按下什么都不动,绝不误杀正在转写的上一
  条。同时清掉冷却与防抖时间戳,紧接着那次真想说话的按下不会被静默吞掉。
- Less Computer 的 modifier 触发键走同一条撤销路径(顺带熄灭整屏描边)。

* feat(hotkey): 修饰键热键加 150ms 组合键仲裁窗口——组合键不再闪一下胶囊

上一提交是「事后撤销」:Option+任意字母/数字键仍会真的开麦、弹胶囊、烧一次 ASR
建连,然后被取消——功能对了,观感上闪一下。

改为按下后先等 150ms 再开会话,期间监听器若已报告叠加了普通键,这次按下整条作废:
麦克风不开、胶囊不弹。等待只加在 modifier-only 触发键的「开录」分支上:

- 自定义组合键(Cmd+Shift+D)本身没有歧义,不等;
- toggle 停止 / QA 路由 / 防抖与冷却判定都在窗口之前,时序不变(尤其不挤占
  is_queued_chain_press 的 120ms 排队接力窗口);
- 窗口没盖住的慢速组合键(按住 Option 半秒再按 Tab)仍由 TriggerCombined 事后撤销
  兜底,两条路径互补。

150ms 覆盖绝大多数组合键的「修饰键→普通键」间隔,又低于人从按键到开口的反应时间,
不吃首字。

* fix(hotkey): 补上移动端 hotkey stub 的 TriggerCombined / trigger_combined_since_press

Android CI(cargo check --target android)红:`mobile_stubs/hotkey.rs` 是移动端的
HotkeyEvent / HotkeyMonitor 替身,桌面端新增的枚举变体与方法必须同步补上,否则
coordinator 里那两条 match 臂和仲裁窗口的调用在移动端找不到符号。

移动端没有键盘监听器,`trigger_combined_since_press` 恒为 false(仲裁窗口等价于
直接放行)。

* fix(hotkey): 组合键撤销改走独立通道——按下第二个键的那一帧胶囊就消失

150ms 仲裁窗口只盖住了「修饰键→普通键」间隔够快的组合键;慢一点的(实测日志里
press→combined 中位数 428ms)仍然走事后撤销兜底,本该只是「闪一下」,实际却晚了
几百毫秒才收——观感上像录音真的起来了。

原因不是手速,是排队:TriggerCombined 和 Pressed/Released 挤同一条 channel,而
bridge 为修 #468/#475 的 latch 竞态改成了串行 block_on —— 这次按下自己的
begin_session(开麦 + ASR 握手)正卡在 bridge 线程上,撤销只能在队列里等它跑完。
日志里那个 428ms 因此是上界,真实键间隔被 begin_session 的耗时撑大了。

修法同 #853(Esc 取消):把撤销从 HotkeyEvent 枚举拆出,tap/hook 回调改发独立的
Sender<()>,由专用 combo-abort-bridge 线程消费。macOS CGEventTap 与 Windows
WH_KEYBOARD_LL 同步修改;Less Computer 的修饰键 monitor 走同一条通道,只是 handler
换成 cancel_less_computer_press。

并发面两处:
- 撤销与 Released 之间不再有先后保证。改为只看 hotkey_press_began_session
  (每个 Pressed 边沿都会重置)判断要不要撤销,不再看 hotkey_trigger_held ——
  否则 Released 抢先跑完清了按住态,撤销会认不出这条会话是自己开的、放着不管,
  Auto 模式下就是一条停不下来的录音。清 hotkey_trigger_held 只为吞掉后面的
  Released,与撤销与否无关。加了回归测试覆盖这个倒序。
- 撤销落在 begin_session 还在 await 的中途,由既有的
  startup_race_status_for_starting / CancelRaced 检查点接住(audit HIGH #1),
  与 Esc 取消同一条路径。

至此两条路径分工:仲裁窗口内的快组合键胶囊根本不出现;窗口没盖住的慢组合键胶囊
冒个头,按下第二个键即刻消失。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(hotkey): 取消先收胶囊再拆麦克风 + watchdog 碎觉——撤销不再拖着胶囊一秒

上一提交把撤销决策做到了「按下第二个键的那一帧」(tap → handle_trigger_combined
实测 0.14ms),但真机日志显示胶囊仍然晚 0.8~1 秒才消失。11 次组合键测试分成干净的
两组:这次按下已经把 recorder 开起来的 5 次是 769/900/931/965/986ms,还没开起来的
6 次是 0ms。

链路:cancel_session 先 stop_recorder_for_session 才 emit_capsule。
Recorder::stop() 要 join 音频线程,音频线程退出前要 join liveness watchdog,
watchdog 睡在 1000ms 的检查间隔里 —— 所以停采要等它当前那一觉睡完,胶囊排在后面。
按 Esc 取消走同一个函数,同样拖一秒。

两处一起改(缺一不可,见下):

- coordinator/dictation.rs:cancel_session 把 UI 收尾(finish_cancel_session_state
  → emit_capsule → schedule_capsule_idle → 熄描边)整块提到三个拆资源调用之前。
  emit_capsule 仍排在 finish_cancel_session_state 之后 —— 它要读 state.voice_agent /
  phase 拼 payload,再提前会发出「还在进行中」的那一帧。拆资源都按 session_id 取,
  不依赖 phase,挪到后面语义不变。
- recorder.rs:watchdog 的一个检查间隔切成 50ms 的碎觉来睡,每觉醒来重看 stop_flag。
  停采的等待从最坏 1000ms 降到 50ms。判据(回调静默 3s / 首帧 5s)用的是真实时间差
  而非醒来次数,灵敏度不变;代价是录音期间线程多醒几次,醒来只读一个时间戳。

为什么必须成对:UI 先收之后,胶囊消失到麦克风真正释放之间有一段窗口,其间旧
recorder 还占着设备。`Recorder` 没有 Drop 停采,recorder 槽被下一条会话覆盖后旧音频
线程会继续跑、抓着麦克风不放。只做第一处会把这段窗口留在 ~1 秒,紧接着那次真想说话
的按下(最快也要 250ms 防抖 + 150ms 仲裁)就可能撞上;watchdog 切碎后窗口降到几十
毫秒,够不着。

副作用:正常说完话松手结束(end_session 也走 Recorder::stop())同样少等将近一秒。
可观察的代价:胶囊消失后系统菜单栏的录音小圆点会多亮几十毫秒。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(hotkey): resolve modifier combo abort after beta merge

* test(hotkey): initialize mac companion press id

* fix(hotkey): clone Linux fcitx combo channel

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-08-01 18:01:11 +08:00
52e6ef46fc feat(hotkey): 会话激活期间 Esc 由 OpenLess 独占——不再透传宿主应用 (#855)
* feat(hotkey): 会话激活期间 Esc 由 OpenLess 独占——不再透传宿主应用

胶囊转圈时按 Esc,取消会话的同时宿主应用也收到了这个键——一次按键双重
生效(如顺带取消了 Claude 正在生成的回复)。参照输入法的交互模型:组合窗
激活时 Esc 只取消候选词、宿主应用收不到;胶囊显示进行中的会话时同理,
Esc 的语义就是「取消它」,应当被独占消费。

实现:hotkey.rs 新增进程级 ESC_EXCLUSIVE 旗标,由 emit_capsule(所有会话
状态变化的单一出口)维护——胶囊为 Recording/Transcribing/Polishing 且
dictation phase 非 Idle 时置位,终止帧清除。phase 条件排除 QA 会话:QA
的 Esc 由聚焦的浮窗窗口处理,吞键反而会挡掉。置位期间 macOS active tap
对 Esc keydown 返回 null 删除事件,Windows 底层钩子返回 LRESULT(1)——
Windows 本就吞听写触发键,Esc 是补齐同一语义。keyup 不吞:宿主应用几乎
都在 keydown 响应 Esc,孤儿 keyup 无害。

依赖 #853(独立取消通道):没有它,Processing 期间吞掉的 Esc 无法真正
触发取消,会变成「吃掉按键但什么都不做」。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(hotkey): esc-exclusive 判定提取纯函数并补表格测试

- 提取 esc_exclusive_for_capsule(state, phase) 纯函数,emit_capsule 调用
- 表格测试覆盖:进行中胶囊非 Idle 独占、终止帧清除、QA(phase=Idle)不独占

* fix(hotkey): 修复 voice agent 审批挂起 Esc 失效与胶囊双 emit 竞争

- maybe_request_approval 增加取消感知:会话取消(Esc)时按 Deny 处理并清理
  审批注册表,避免审批挂起期间 Esc 被独占吞掉却毫无效果(PR #855 场景)
- run_voice_agent_transcript 审批后若会话已取消,强制结果为 Cancelled,
  避免把第一轮拦截文本当 Done 收尾
- schedule_capsule_idle 记录触发时胶囊状态,到点时若期间有更新的 emit 则
  跳过隐藏(旧 schedule 不再覆盖新状态,消除取消路径双 emit 竞争)
- 补测试:审批取消感知、schedule 新旧状态竞争

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-08-01 12:30:10 +08:00
b16950d13e fix(hotkey): Esc 取消改走独立通道——修复转写/润色期间按 Esc 停不下来 (#853)
* fix(hotkey): Esc 取消改走独立通道——修复转写/润色期间按 Esc 停不下来

#798 给 end_session 加了「转写 vs 取消」的 select! 赛跑,但取消信号本身
送不进来:hotkey bridge 线程为修 #468/#475 的 latch 竞态改成了串行
block_on,Hold 松手后 end_session 在 bridge 线程上同步跑完整段转写 +
润色,期间 Esc 的 HotkeyEvent::Cancelled 只能在同一条 channel 里排队。
流程跑完后才被取出,此时 phase 已回 Idle,cancel 变 no-op —— 用户按
Esc 毫无反应,必须干等转写完成。听写键为修饰键(默认 fn 等)时必现;
组合键用户不受影响(按键事件走 ComboHotkeyMonitor 的独立线程),这也是
#798 验证时没暴露的原因。

修法:把 Cancelled 从 HotkeyEvent 枚举拆出,Esc 在 tap/hook 回调里改发
独立的 Sender<()>,由专用 esc-cancel-bridge 线程消费并调 cancel_session
(纯同步快路径,不 await)。cancelled 旗标即时置位,end_session 的
select! 赛跑与润色流的 should_cancel 立即命中。macOS CGEventTap 与
Windows WH_KEYBOARD_LL 同步修改;Less Computer 修饰键 monitor 的 Esc
转发同样迁移。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(hotkey): esc-cancel-bridge 启动失败留 error 日志并补单元测试

- spawn_esc_cancel_bridge 线程创建失败时 log::error,避免 Esc 取消静默失效
- 新增 esc_cancel_bridge_loop 单元测试:Processing 阶段置 cancelled、
  录制快捷键期间跳过、重复信号幂等

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-31 21:47:59 +08:00
Duyi-WangandChris233 f4bb056fe6 fix(llm): make custom provider temperature configurable (#849)
* fix(llm): make custom temperature configurable

Allow custom OpenAI-compatible LLM providers to omit or override the temperature parameter instead of always sending the default 0.3.

- Send temperature only when configured in OpenAI-compatible chat bodies
- Keep built-in OpenAI-compatible providers on the existing 0.3 default
- Store active custom LLM temperature via ark.temperature
- Expose a Temperature field for custom LLM providers in Settings
- Reuse the same temperature config for provider validation and real polish
- Add tests for omitted/configured temperature and input validation

* docs(settings): show custom temperature range

* fix(llm): honor temperature for legacy custom providers

* fix(llm): harden custom temperature handling

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-22 08:52:09 +08:00
Shawn Shen 8263f6f46f fix(asr): resolve Bailian ASR validation deadlock and surface error details (#848) 2026-07-21 22:26:28 +08:00
nb213andbinyangzhu000-sudo ef66246623 Add Atlas Cloud LLM preset (#844)
Co-authored-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com>
2026-07-21 21:56:40 +08:00
TRIP 857ea6dc21 Merge pull request #839 from Open-Less/release/1.3.15
fix(updater): prepare 1.3.15-Beta.2 after repository transfer
2026-07-21 18:43:09 +08:00
aeoformandChris233 a1fe38730d fix(history): 音频播放改用 Blob URL + 导出走 Tauri dialog save (#841)
* fix(history): 音频播放改用 Blob URL + 导出走 Tauri dialog save

* fix(history): 给 export_audio_recording 加最小目标路径校验

* fix(history): export 对话框移到后端,消除 IPC 路径注入风险

* fix(history): 将阻塞 dialog 调用移入 spawn_blocking 避免阻塞异步线程池

* fix(history): support mobile export destinations and blob cleanup

* fix(history): use mobile fs handles for recording export

* fix(build): sync mobile fs dependency lockfile

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-20 15:51:41 +08:00
sim 6b33ba21e0 fix(updater): prepare 1.3.15-Beta.2 after repository transfer 2026-07-19 00:11:34 +08:00
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>
2026-07-18 18:13:34 +08:00
bigsongandClaude Fable 5 ce3fd8d847 feat(llm): 新增 StepFun 供应商预设;修复 Codex OAuth 默认模型不可用 (#836)
StepFun 预设:
- 供应商下拉新增 StepFun(阶跃星辰),预填 https://api.stepfun.com/v1,
  默认模型 step-1o-turbo-vision(无思考,流式首字 ~0.3s;step-3.x-flash
  系列为推理模型且思考无法关闭,正式内容需等隐藏思考结束,不适合听写润色)
- thinking 控制表新增 stepfun → reasoning_effort(开=medium / 关=low),
  base_url 兜底识别同步支持"自定义"preset 接入 StepFun 的场景
- 补默认 endpoint 映射与两个单元测试

Codex OAuth 默认模型:
- gpt-5.3-codex-spark 对 ChatGPT 账号会被后端 400 拒绝
  ("model is not supported when using Codex with a ChatGPT account"),
  导致每次润色静默失败回退原文;默认与占位改为实测可用的 gpt-5.5

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 15:43:39 +08:00
17fe67c59b feat(asr): 接入阶跃星辰 StepAudio 2.5 批式转写,词典走一等 hotwords (#835)
* feat(asr): 接入阶跃星辰 StepAudio 2.5 批式转写,词典走一等 hotwords

StepFun 的 /v1/audio/transcriptions 是标准 OpenAI multipart(实测 2026-07),
归入 Whisper 兼容路由即可,无需新协议客户端:

- 前端新增 stepfun preset(https://api.stepfun.com/v1 + stepaudio-2.5-asr),
  i18n 五语言补齐;
- StepFun 静默忽略 prompt(实测返回 200 但不参与偏置),词汇偏置走专门的
  hotwords 参数(JSON 数组字符串)——WhisperBatchASR 新增 with_hotwords,
  wiring 按 whisper_uses_hotwords 在 prompt / hotwords 间二选一路由,
  用户词典在 StepFun 下才真正生效;
- response_format 只认 json/text → verbose_json 幻听过滤保持关闭;
- 修「验证」按钮对静音敏感厂商的假阴性:探针是 250ms 纯静音,StepFun 返回
  400 "no speech found",但走到内容拒收说明鉴权(错 key=401)与模型名
  (错模型=404)均已通过——判为验证成功而非凭据错误。

实测矩阵(真实 key 逐项打点):真实语音+json/缺省/带 prompt/带 hotwords 均
200 且转写准确(5.5s 音频约 0.95s 往返);verbose_json 400;静音 400
no speech;错 key 401;错模型 404。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(asr): StepFun 流式实时转写——同一入口按模型名自动切协议

照百炼「一个入口、按模型二次路由」的先例:stepfun preset 下模型填
`stepaudio-2.5-asr-stream`(或任意 `*-stream`)即走新的实时 WS 客户端
(asr/stepfun_realtime.rs),其余模型维持批式 Whisper 兼容路径。凭据与
「已配置」判定仍看真实 active `stepfun`,一把 key、同一个 https base
(wss URL 由 client 自动派生)。

协议与 qwen_realtime 同为 OpenAI Realtime 风格,但四处关键差异
(2026-07-16 真实接口逐项实测确认):

- 模型在 session.update 里传(session.audio.input.transcription.model),
  session 配置是 audio.input.{format,transcription,turn_detection} 嵌套形状;
- delta 的 text/stash 是「已确定前缀 + 未定尾巴」的拼接关系(Qwen 是互斥取一);
- 没有服务端结束事件:session.finish 回 transcript.response.error,server_vad
  下 commit 被静默忽略。收尾靠客户端状态机:松手时冲刷尾音频 + 补送 700ms
  静音帧逼 VAD 关段,finishing && open_segments==0 即完成;纯静音会话由
  1.2s 宽限期兜底空文本成功(验证按钮复用该路径,全程 ~2s);
- 实时协议词汇偏置走 transcription.prompt(批式恰相反只认 hotwords),
  词典按 effective 协议自动选通道。

真机端到端(真实 key、模拟 recorder 时序):session 建立 186ms,
松手→最终结果 251ms(批式 ~950ms),5.5s 中文语音转写一字不差,
多句段 VAD 断句由 join_segments 复用 qwen 拼接规则。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(asr): wait for StepFun tail frame before finishing

* fix(asr): guard StepFun completion before tail ack

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-18 15:43:24 +08:00
Chris233 07f394f5a2 fix: include selected text in follow-up context (#830)
* fix: include selected text in follow-up context

* fix: address selection follow-up review

* chore: preserve beta CI workflow

* fix: invalidate closed QA turns

* fix: guard voice QA sessions

* fix: reset QA session tokens on reopen

* fix: guard stale QA end cleanup
2026-07-16 13:27:12 +08:00
842ae5fd64 feat(history): 详情页展示 ASR/LLM 模型与分步耗时,可观测的模型表现对比 (#826)
* feat(history): 详情页展示 ASR/LLM 模型与分步耗时,复制按钮归位润色卡片

- DictationSession 新增 asr_provider/asr_model/llm_provider/llm_model/asr_ms/polish_ms
  六个可选字段(旧 JSON 兼容);听写收尾实测写入,Raw 直通不记 LLM 字段
- asr_ms 量松键后等待转写结果的时间(流式=收尾延迟,批式=完整转写);
  polish_ms 量 LLM 润色/翻译调用耗时
- 失败/空转写历史也记 ASR provider/model,方便模型对比排查;
  手动重转用当前 ASR 覆盖并清掉旧润色信息
- 详情页底部改为 识别/润色/插入 三行流水线明细;录音时长加「录音」前缀
  并给识别行加悬停说明,避免与分步耗时混淆
- 润色卡片复制按钮移入卡片头部,与原文卡片对称

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(history): 模型归因改为构建时快照 + 亚秒毫秒精度 + volc resource id(review 反馈)

按 #826 review 逐条修复:

1. ASR/LLM 归因改为构建时快照。store_asr_for_session 新增 AsrCallLabel 参数
   (编译期强制 11 个构建分支交出快照),记录实际构建的具体协议 id(含统一百炼
   重定向后的 effective id)与归一化后的模型/alias;LLM 侧 polish 链路新增
   llm_call 出参,在成功构建 provider、即将发起真实调用时才填充。会话中途改
   设置不再污染归因。
2. preflight 失败不再产生伪数据。凭据缺失等构建失败时 llm_call 保持 None,
   不落 llm_*/polish_ms;polish_ms 也只在真实调用发生时记录。
3. 步骤耗时 <1s 显示整数毫秒(durationMillis,5 语言包),流式收尾几十毫秒
   不再被 0.1s 精度拍平。
4. Volcengine 经 allowlist(volc. 命名空间、ASCII 安全字符集、≤64)脱敏后
   把 resource id 落入 asr_model,支持 issue #373 的引擎对比排障;异常值不落盘。
5. 识别行提示改用可聚焦 Tooltip 组件(hover/键盘 focus/触摸三通道 + 虚线下划线)。
6. 重转录命令改用 retranscribe_pcm 返回的构建时快照;抽出 apply_retranscription
   纯函数。新增测试:旧 JSON 缺字段反序列化、camelCase 序列化、volc allowlist、
   重转覆盖归因并清 LLM 字段、Raw 直通不产生调用快照。

UI 验收:深/浅色 × 桌面/移动 × 超长 provider/model 换行 × 键盘聚焦均通过
(mock 数据已扩成对应压力画像)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(history): preserve actual model timing attribution

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-16 11:56:00 +08:00
TRIPandsim 033eb5e731 chore(release): prepare 1.3.15-Beta.1 (#834)
Co-authored-by: sim <sim@local>
2026-07-15 17:22:02 +08:00
TRIPandsim 3143892daa fix(updater): discover modern Beta release tags (#833)
* fix(updater): discover modern Beta release tags

Recognize the current -Beta.N-tauri convention while preserving historical lowercase beta tags so desktop and Android resolve the newest prerelease manifest.

* fix(updater): validate complete modern Beta tags

Reject malformed modern feed tags before they can preempt a valid release, while preserving the historical lowercase beta suffix.

---------

Co-authored-by: sim <sim@local>
2026-07-15 16:51:02 +08:00
664633f32d fix(asr): Apple Speech 保留停顿分段,修复停顿前内容丢失 (#824)
* fix(asr): Apple Speech 保留停顿分段,修复停顿前内容丢失

Apple 设备端识别会在语音停顿处把音频切成多个话段(utterance),每个话段
的回调只携带该话段的文本,随后 partial 从空重新累计;isFinal 通常只跟着
最后一个话段出现。原实现「拿到第一个 isFinal 就返回」,导致停顿之前的所有
话段被整段丢弃——用户表现为「说话中途停顿思考,前面说的全没了」。

- 新增 SegmentAccumulator:以 speechRecognitionMetadata 非空作为话段边界
  信号,逐话段落袋,结束后按 CJK / 空格语言规则拼接返回。
- 结束判定由「见到第一个 final 就收工」改为轮询 SFSpeechRecognitionTask
  .state == completed(辅以 isFinal 后静默的后备条件),兼容部分系统按话段
  多次发 isFinal;completed 后留 250ms 收尾窗口,避免竞态截掉尾段。
- 三重防丢 / 防重:partial 骤缩时判定静默话段重置并抢救上一段;引擎重放
  累计全文时按前缀 / 空白不敏感规则去重;识别中途报错时返回已累积话段而非
  整段作废。
- 等待预算由固定 60s 硬顶改为 max(60s, 音频时长 + 30s),长录音不再被
  「等待语音识别结果超时」截断。
- 新增 12 个单元测试覆盖多话段累积、逐话段 final、静默重置抢救、去重、
  拼接规则与等待预算。

单话段(无停顿)场景输出与修复前逐字节相同,云端识别路径不受影响,因此不
存在回归风险。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(asr): harden Apple Speech segment lifecycle

* fix(asr): preserve repeated final-only utterances

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: baiqing <lbx12309@icloud.com>
2026-07-15 16:01:02 +08:00
TRIPandsim 4bdef5d7a3 ci: run all frontend and contract tests (#832)
Co-authored-by: sim <sim@local>
2026-07-15 15:55:37 +08:00
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>
2026-07-15 15:26:48 +08:00
TRIPandsim 07b2e6a141 fix(security): redact provider URL log secrets (#831)
Co-authored-by: sim <sim@local>
2026-07-15 15:18:10 +08:00
TRIPandsim 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>
2026-07-15 14:46:06 +08:00
TRIPandsim 472b914985 fix(security): authenticate marketplace writes with GitHub OAuth (#816)
* fix(security): authenticate marketplace writes with OAuth

* fix(security): harden marketplace OAuth lifecycle

* fix(security): enforce marketplace credential invalidation

* fix(security): reject public marketplace redirects

---------

Co-authored-by: sim <sim@local>
2026-07-15 14:20:29 +08:00
TRIPandsim 9991b325b8 test(windows): repair UI config contract (#829)
Co-authored-by: sim <sim@local>
2026-07-15 13:53:09 +08:00
TRIPandsim a5a9c7f0b5 test: pin local tsx runner (#828)
Co-authored-by: sim <sim@local>
2026-07-15 13:44:39 +08:00
TRIPandsim d6ca07a6b2 fix(android): break IPC settings barrel cycle (#827)
Co-authored-by: sim <sim@local>
2026-07-15 13:20:57 +08:00
Chris233 c645b16851 Disable background scrolling when settings open (#776)
Fixes #580: prevent background thin-scroll areas from scrolling/bleeding through when the Settings modal is open. Adds data-ol-settings-open={settingsOpen ? 'true' : undefined} to the main FloatingShell div and corresponding CSS in tokens.css to set overflow:hidden and hide WebKit scrollbars when the attribute is present. Also adds settings-modal-scroll-isolation.test.mjs to assert the attribute placement and CSS selectors.
2026-07-14 19:34:11 +08:00
TRIPandsim fa53f47f08 fix(security): redact Less Computer approval tokens (#820)
Co-authored-by: sim <sim@local>
2026-07-14 19:07:20 +08:00
TRIPandsim 6aaae670b0 fix(security): bound style-pack ZIP imports (#819)
* fix(security): bound style pack archive imports

* fix(security): validate physical ZIP directory entries

---------

Co-authored-by: sim <sim@local>
2026-07-14 19:02:03 +08:00
145a0ea17d feat(asr): add ElevenLabs Scribe v2 provider (#807)
* fix(asr): 完善 ElevenLabs Scribe 接入

* fix(asr): omit ElevenLabs word timestamps

* fix(qa): treat ElevenLabs cancellation as idle

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
Co-authored-by: sim <sim@local>
2026-07-14 15:29:16 +08:00
TRIPandsim 66d084792f fix(security): patch RustSec dependency advisories (#809)
Co-authored-by: sim <sim@local>
2026-07-14 14:54:05 +08:00
TRIPandsim ef6f1fadb4 fix(cask): update Homebrew package to v1.3.14 (#802)
* fix(cask): point Homebrew to v1.3.14

* fix(cask): declare Monterey requirement

---------

Co-authored-by: sim <sim@local>
2026-07-14 14:43:36 +08:00
yuanchengluandChris233 6140a4b243 fix(linux): prevent startup panic from unrealized capsule GDK window | fix(linux): 修复胶囊窗口因 GDK 未实例化导致的启动崩溃 (#792)
* fix(linux): prevent startup panic from unrealized capsule GDK window | fix(linux): 修复胶囊窗口因 GDK 未实例化导致的启动崩溃

On Linux X11, when the capsule window is created with `visible: false` +
`transparent: true` in tauri.conf.json, its underlying GDK window is never
realized. The subsequent call to `capsule.set_ignore_cursor_events(true)`
triggers a panic inside tao's Linux event loop (event_loop.rs:457) because
`window.window().unwrap()` returns `None` for an unrealized GDK window.

The fix follows the same approach as voicebox PR#837 (PhamBit):
force GDK window realization before calling set_ignore_cursor_events, by
first moving the window off-screen and calling show(). The window is then
repositioned correctly by the existing position_capsule_bottom_center()
call and hidden as normal.

Refs:
  - https://github.com/jamiepine/voicebox/pull/837
  - https://github.com/tauri-apps/tao/issues/635

在 Linux X11 上,tauri.conf.json 中定义的 capsule 窗口设置了
visible: false + transparent: true,导致其底层 GDK 窗口从未被实例化。
随后调用 capsule.set_ignore_cursor_events(true) 时,tao 的 Linux 事件循环
(event_loop.rs:457) 因 window.window().unwrap() 返回 None 而 panic 崩溃。

修复方法参考 voicebox PR#837:在设置鼠标穿透前先将窗口移出屏幕并
调用 show() 强制 GDK 实例化,后续由原有的 position_capsule_bottom_center()
重新定位并隐藏。

* fix(linux): realize hidden capsule without showing it

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-14 10:52:39 +08:00
bigsongandChris233 6e79a0c289 feat(asr): 百炼 ASR 支持 fun-asr-flash 并统一三协议为单一入口 (#793)
* feat(asr): 百炼 ASR 支持 fun-asr-flash 并统一三协议为单一入口

* fix(asr): harden DashScope model routing and transcription

* fix(asr): address Bailian protocol review feedback

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-14 10:51:36 +08:00
bigsongandChris233 3f51edafab fix(local-asr): 本地 Qwen3 模型在云端 provider 下误标「当前使用」 (#794)
* fix(local-asr): 本地 Qwen3 模型在云端 provider 下误标「当前使用」

* fix(local-asr): sync active provider preference

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-14 10:51:00 +08:00
a77cbd4722 fix(dictation): 转写阶段可中断——按 Esc 立即取消在途 ASR 请求 (#798)
* fix(dictation): 转写阶段可中断——按 Esc 立即取消在途 ASR 请求

进入 Processing(转写)后,end_session 已把 ASR 句柄从 inner.asr 槽 take 走,
cancel_session 走的 cancel_asr_for_session 是 no-op,够不到在途请求:用户按 Esc
后仍要等整段转写跑完才停,且结果被静默丢弃,白等一场。

改为把转写与「用户取消」轮询用 tokio::select! 赛跑:命中取消即 drop 掉 transcribe
future(中断 reqwest HTTP / 停止等待流式最终结果 / 停止本地转写),再显式
cancel_active_asr 促流式 WebSocket 立即关闭,然后 finish_cancelled_processing 收尾
静默返回。覆盖云端流式 / 云端批量 / 本地引擎全部 provider。

顺带删除 Foundry / Sherpa / Apple Speech 分支里各自的 if cancelled 早退补丁——
取消现在由外层 select! 统一处理,那些分支已冗余。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(dictation): clean up cancelled ASR sessions

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-14 10:05:36 +08:00
a49d02b908 fix(settings/overview): 清理 Windows 上不适用的 UI 选项/标签/布局 (#799)
* fix(hotkey): Windows 移除 Command 单键预设

Windows 没有 Command 键,leftCommand/rightCommand 会被映射到 Win 键,而单按 Win
弹出开始菜单,实际无法作为录音热键。从 Windows 的 available_triggers 里删掉这两个
预设,前端「常用单键」chip 便不再展示;不影响 HotkeyTrigger 枚举本身与已有绑定。

Fixes #784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(settings/overview): 清理 Windows 上不适用的 UI 选项/标签/布局

- #783 补齐 hotkey.triggers 缺失的 leftShift/rightShift/leftCommand i18n(5 语言);
  之前缺 key 导致预设 chip 渲染成裸串 "hotkey.triggers.xxx"(用户看到的 "host…")
- #786 移除「粘贴快捷键」里的 Shift+Insert (xterm/urxvt) 选项——这组设置只在 Windows
  出现,而该选项是 X11 终端专用,放 Windows 上纯误导
- #785 删除「自动更新」下方错位的 hotkey statusHint(本属快捷键区,误挂到启动组)
- #780 「胶囊确认插入 ✓」速查行改为仅 macOS——Windows/Linux 胶囊无此按钮
- #781 「年度活动」卡片补 marginBottom,与上方卡片 18px 节奏一致
- #782 「近 7 天」卡片加 overflow:hidden,窗口过小时柱状图不再溢出卡片圆角外

Fixes #783, #786, #785, #780, #781, #782

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(settings): migrate removed Windows paste shortcut

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-14 10:04:56 +08:00
01bff73cbe fix(prefs): 解析失败时逐字段抢救+备份,避免升级改 schema 静默清空设置 (#791)
* fix(prefs): 解析失败时逐字段抢救+备份,避免重构改 schema 静默清空设置

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(prefs): harden corrupt preference recovery

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-13 10:57:44 +08:00
414acf0dc7 feat(settings): 设置项悬停显示功能说明,降低 Less Computer / 录音胶囊等的理解门槛 (#777)
* feat(settings): 设置项悬停显示功能说明,降低 Less Computer / 录音胶囊等的理解门槛

页面瘦身后设置页描述小字全部隐藏,Less Computer、录音胶囊这类光看名字
猜不出用途的功能只剩一个标签,理解成本高。本次把已有的 desc 文案以
Tooltip 形式挂回标签文字上:

- Tooltip 新增 wrap 模式,整句说明允许换行(默认 nowrap 不变)
- SettingRow 的 desc 恢复生效:悬停标签文字弹出说明,虚线下划线 +
  help 光标做「可悬停」暗示,布局保持紧凑
- SectionTitle 新增 hint,Less Computer / Claude 控制台 / 录音与输入
  三个板块标题接上已有的 desc 文案
- 录音与输入里录音胶囊 / 录音方式 / 首选麦克风 / 录音时静音 /
  插入剪贴板等行补传现成的 i18n desc

文案全部复用现有 i18n key,五种语言无需新增翻译。

* feat(settings): Less Computer 快捷键提示补一句「功能说明参见高级设置页」

通用页 Less Computer 那行原提示只讲快捷键用法,补一句指向「高级」设置页
的功能说明入口,降低理解门槛。5 语言同步。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(settings): make hints accessible and viewport-safe

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-12 21:44:09 +08:00
695813074f feat(ux): 三个小改进——Esc 立即收起胶囊 / 概览复制按钮 / 历史页 ⌘R 刷新 (#790)
* feat(capsule): Esc/主动取消时胶囊立即消失(取消延迟 2000ms→0)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(overview): 「最近识别」每行加复制按钮(Btn children 改可选)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(history): ⌘R 刷新历史列表(拦截整页 reload,仅重拉 listHistory)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(dictation): finish cancelled processing sessions

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-12 21:42:50 +08:00
bigsongandChris233 9ed34dabea feat(asr): 新增百炼 Qwen3-ASR-Flash 实时供应商(OpenAI Realtime WS 协议) (#775)
* feat(asr): 新增百炼 Qwen3-ASR-Flash 实时供应商(OpenAI Realtime WS 协议)

bailian.rs 头注释预留的 follow-up 协议线:/api-ws/v1/realtime,与经典
/api-ws/v1/inference 并列为独立供应商 bailian-qwen3-realtime。

- asr/qwen_realtime.rs:新客户端。base64 JSON 事件流 + server_vad 自动断句,
  按序收集 completed 句段;session.finish 冲刷尾段后回 session.finished。
  关键协议行为均经 2026-07 线上实测:尾段冲刷(说到一半松手不丢字)、
  纯静音验证干净返回、language 省略走自动检测、业务空间专属域名
  (wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com)同样承载此路径。
- coordinator / asr_wiring / dictation / qa_session / resources:与 Bailian
  分支同构接线(流式 WS + DeferredAsrBridge)。
- 验证与拉取模型:真实 WS 连通性检查;协议头校验(qwen3EndpointSchemeInvalid)。
- 前端:ASR 预设下拉新增一项;i18n 5 语言。

测试:cargo test 573 passed(新增 18 个客户端单测);npm run build 通过。

* fix(asr): harden qwen realtime protocol

---------

Co-authored-by: Chris233 <h-chris233@outlook.com>
2026-07-12 20:15:04 +08:00