Commit Graph
109 Commits
Author SHA1 Message Date
kaku.ki 23d7f44a96 fix(windows): stabilize capsule visuals and audio meter 2026-05-05 11:51:57 +09: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 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
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
H-Chris233 d397f92cd7 Restore QA single-newline rendering behavior
Code review flagged a UX regression after sanitizer refactor: assistant single newlines stopped rendering as hard breaks. Re-enable marked breaks mode in the shared QA renderer to preserve previous panel readability for streamed and lightly formatted answers.

Constraint: Keep sanitizer fix while matching prior QA markdown display behavior

Rejected: Add newline post-processing in QaPanel | duplicates renderer concern and increases drift risk

Confidence: high

Scope-risk: narrow

Directive: QA markdown display flags must be centralized in qaMarkdown.ts to keep streaming/final parity

Tested: npm run build (openless-all/app)

Not-tested: Manual visual QA panel check on desktop runtime
2026-05-04 17:47:59 +08:00
H-Chris233 7edd0cde9e Close reviewer-found QA markdown sanitizer gaps
Two parallel reviews flagged a high-risk fallback path and a link-encoding regression in the new QA sanitizer. This patch keeps the minimal #221 approach while ensuring failure fallback stays inert and safe links preserve query strings correctly.

Constraint: Preserve minimal change scope for issue #221

Rejected: Expand into CSP/capability hardening in same patch | out of issue scope and higher blast radius

Confidence: high

Scope-risk: narrow

Directive: Never return raw model output into dangerouslySetInnerHTML fallback paths

Tested: npm run build (openless-all/app)

Not-tested: Manual desktop exploit reproduction on Windows/macOS
2026-05-04 17:33:52 +08:00
H-Chris233 a57978e739 Harden QA markdown rendering to block WebView script injection
Issue #221 requires closing the QA markdown XSS surface without broad CSP or architecture changes. This switches assistant and streaming markdown to one shared safe renderer that escapes raw HTML and rejects non-http(s)/mailto/tel links.

Constraint: Keep markdown readability while preventing executable HTML in QA WebView

Rejected: Add DOMPurify dependency | avoid new dependency for minimal fix

Confidence: medium

Scope-risk: narrow

Directive: Keep streaming and final assistant rendering on the same sanitizer path

Tested: npm run build (openless-all/app)

Not-tested: Manual malicious payload interaction in packaged desktop app
2026-05-04 17:21:26 +08:00
H-Chris233 647fc2ded0 Fix autostart runtime loading and preset apply disabled-term behavior
Address review feedback by removing fragile runtime import of the autostart
plugin package and invoking plugin commands via Tauri core bridge, so production
bundles no longer depend on unresolved bare imports.

Also update preset batch apply to re-enable existing disabled terms and continue
processing remaining terms/presets when one add operation fails.

Constraint: Minimal changes limited to existing Settings/Vocab flows
Rejected: New backend batch endpoint for preset apply | broader cross-layer change
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Preset apply should be idempotent and restore disabled matching terms
Tested: npm run build
Not-tested: Manual autostart toggle check on packaged desktop build
2026-05-04 16:33:50 +08:00
H-Chris233 47069cea4a Harden preset save/apply against partial failures
Preset edits were applied to local UI state before persistence finished, so
storage failures could show a misleading saved state and then lose changes on
reload. Also, batch apply aborted on first add_vocab error and skipped later
selected presets.

Make preset save await persistence before mutating UI/closing editor, and make
batch apply continue across per-term failures while reporting aggregated errors.

Constraint: Minimal behavior fix within existing Vocab page flow
Rejected: Add transactional backend endpoint for batch apply | larger cross-layer change
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep UI success states aligned with durable persistence completion
Tested: npm run build
Not-tested: Manual readonly-disk simulation for persist failure
2026-05-04 16:25:03 +08:00
H-Chris233 f2122dc4f9 Persist only user preset deltas to keep builtin updates receivable
Saving presets previously wrote the merged list (including built-ins) into user
storage, which froze old builtin copies and shadowed future app updates with the
same preset ids. Switch preset persistence to a delta store: custom presets,
overrides for edited built-ins, and disabled builtin ids.

Constraint: Keep current preset UI and command names unchanged
Rejected: Force builtin presets to always overwrite user file | would discard intentional user edits
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Never persist untouched builtin presets into user store
Tested: cargo test vocab_presets_roundtrip_json_file -q; npm run build
Not-tested: Manual end-to-end UX for builtin delete/restore (no delete UI yet)
2026-05-04 16:10:39 +08:00
H-Chris233 d1b4adbff8 Keep built-in vocab presets available after user saves custom presets
Loading presets from app-data JSON previously replaced bundled defaults entirely,
so adding any custom preset made shipped scenarios disappear on the next load.
Merge user presets onto bundled defaults by id so built-ins always remain
available while still allowing user overrides.

Constraint: Minimal change in preset loading path without altering storage schema
Rejected: Persist defaults into user file on first run | unnecessary write churn and migration complexity
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Preserve default+user merge behavior when adding future preset sources
Tested: npm run build
Not-tested: Manual Tauri UI check for merged list ordering
2026-05-04 16:09:45 +08:00
H-Chris233 91dd39932d Prevent unsaved preset drafts from polluting persisted storage
New preset previously persisted an empty entry immediately on click, so cancel
or navigation left permanent junk rows without any delete path. Keep new preset
as in-memory draft and persist only after explicit save.

Also add a regression test for vocab preset JSON persistence roundtrip to cover
the new preset storage workflow with automated verification.

Constraint: Minimal UI change without introducing preset delete UX in this patch
Rejected: Auto-delete blank presets on load | implicit cleanup can remove user-intended drafts
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep create flow non-persistent until user confirms save
Tested: cargo test vocab_presets_roundtrip_json_file -- --nocapture; npm run build
Not-tested: Manual UX walkthrough for preset draft cancel across app restart
2026-05-04 16:09:45 +08:00
H-Chris233 21da4f912d Unblock web build by lazy-loading autostart plugin in Settings
The frontend build failed because the autostart plugin import could not be
resolved in the browser-targeted Vite bundle, and strict TS also flagged
implicit any in the same flow. Switch the autostart integration to runtime
lazy-loading behind the Tauri guard and type the callback parameters explicitly.

Constraint: Keep autostart UX behavior unchanged on Tauri runtime
Rejected: Add Vite external alias for plugin package | hides integration mismatch in app code
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Tauri-only plugin modules should stay runtime-gated to keep web build healthy
Tested: npm run build (openless-all/app) passed
Not-tested: End-to-end autostart toggle interaction on all desktop platforms
2026-05-04 16:09:45 +08:00
H-Chris233 9f8e9650e0 Add vocabulary presets with JSON-based repo and user storage
Issue #224 requires scenario presets with multi-select apply and editable custom
presets, while preparing for import/export. This revision moves built-in presets
into a repository JSON file and stores user presets in the app data directory as
JSON via Tauri commands, so preset persistence uses one JSON shape both in repo
and user space.

Constraint: Keep vocabulary entry backend and UI interaction model unchanged
Rejected: Keep presets in localStorage/TS constants | does not satisfy unified JSON storage requirement
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Preserve vocab-presets JSON schema compatibility for future import/export
Tested: cargo check (src-tauri) passed
Not-tested: npm build currently fails on pre-existing Settings.tsx typing issues unrelated to this change
2026-05-04 16:09:09 +08:00
millionart 104793cf18 merge upstream main into Windows IME PR 2026-05-04 10:23:42 +08:00
baiqing 1eec2fa21c fix(settings): 拆 committed*Provider 让受控 <select> 立即响应 (#220 P2)
Codex P2: 上一版把 setLlmProvider/setAsrProvider 挪到所有 await 之后,
受控 <select> 的 value 现在依赖 IPC 完成顺序——用户连点两次时,先发的
请求晚到会用旧 id 覆盖最新的 setProvider,UI 卡在错误选项上。

修法(state 二分):
- *Provider:立即跟随用户输入,驱动 <select>
- committed*Provider:仅在所有 await 完成后才 commit,驱动 CredentialField
  的 key 与 placeholder/preset 的渲染
- *SwitchSeqRef:每次进入 handler 自增,每段 await 后比对,发现已有更新的
  switch 在飞就 abort 当前 commit,stale write 保护

效果:
- 用户切换 <select> 立刻看到新选项(受控反馈正确)
- CredentialField 等到后端 active 真切完才 remount 读 entry(issue #219 修复保留)
- 100ms 内连点两次:只有最后那次的 commit 落地,UI 不会被旧请求覆盖
2026-05-04 09:04:41 +08:00
baiqing 1accecbd57 fix(settings): 切换 LLM/ASR preset 顺序错导致 UI 显示旧 active 凭据
底层 providers HashMap 已是 per-provider,bug 完全在前端两个 onChange:

1) Race
setLlmProvider(id) / setAsrProvider(id) 是同步 React state 改动,
CredentialField 用 `key={`${provider}:api_key`}` 强制重挂载、读后端
ark.api_key —— 但此时 await setActive*Provider 还没跑,root.active.llm
还是旧值,lookup_account 落到旧 entry。把 await setActive 移到
setLlm/AsrProvider 之前修。

2) 覆盖
旧实现无条件 setCredential('ark.endpoint', preset.baseUrl),把用户在
该 provider 上之前自定义的 baseUrl 覆盖掉。改成「仅在 entry 该字段
为空」才填默认值,保留用户自定义。ASR 端的 endpoint + model 同款修。

不动 persistence schema、不动 IPC、不动 coordinator。

Closes #219
2026-05-04 08:44:37 +08:00
Chris233 bdd4cb1c7d Merge pull request #217 from appergb/feat/autostart-194
feat(autostart): macOS / Linux / Windows 三平台开机自启
2026-05-04 08:09:13 +08:00
TRIP e4f6f2eb43 Merge pull request #206 from appergb/fix/windows-qa-panel-205
fix(qa,windows): 划词追问浮窗读用户当前录音键 + Windows 加 toolbar 拖拽 (#205)
2026-05-04 07:33:28 +08:00
baiqing 07c083d9a1 fix(autostart): toggle 失败给用户看错误,不再吞到 console (#194)
原版 AutostartRow 的 onToggle catch 只 console.error + 回滚,
失败完全不在 UI 上反馈:Windows 写 HKCU\Run 被组策略拦、
macOS LaunchAgent plist 写入失败 这些真实场景下,用户只看到
toggle 弹回去,根本不知道为什么。

补一个 error state:
- onToggle 进入清 null,catch 里 setError(message)。
- SettingRow children 改成 flex column:toggle 上面、错误小字下面。
- 配色 var(--ol-err) 跟 about 卡片的 update error 同款。
- i18n 加 settings.recording.startupAtBootError,{{message}} 插
  入插件抛的具体错。

不动 isEnabled 失败路径(#194 验收没列);不动 prefs 持久化
策略(仍由 OS 持有真相)。
2026-05-04 07:33:00 +08:00
baiqing e5608db21e feat(autostart): macOS / Linux / Windows 三平台开机自启
接入官方 tauri-plugin-autostart:
- macOS:~/Library/LaunchAgents/com.openless.app.plist (LaunchAgent)
- Linux:~/.config/autostart/openless.desktop (XDG)
- Windows:HKCU\Software\Microsoft\Windows\CurrentVersion\Run (per-user,
  不需要 admin)

设置页 → 录制 区追加「开机自启」Toggle;状态以 OS 为唯一真相,
直接调插件 isEnabled / enable / disable,不进 prefs,避免缓存
与系统设置不一致。

Closes #194
2026-05-04 07:07:55 +08:00
baiqing 17bb078f19 fix(qa): 录音键改了立刻刷新浮窗文案,不用等重开 (#205 review)
PR #206 review 反馈:QaPanel 的 recordHotkeyLabel 只在 mount / 语言切换时
重读 prefs;用户在主窗口改了录音键,浮窗如果还开着就一直停留在旧值
("按 右 Option 提问" 之类),误导用户按错键。

修法是补一条事件通路,不动现有的初始读取:
- commands.rs::set_settings 持久化后 `app.emit("prefs:changed", &prefs)`,
  广播给所有 webview。沿用 vocab:updated 同款跨窗口通知模式。
- QaPanel.tsx 在原有 qa:state / qa:dismiss / qa:level 订阅旁边加一条
  prefs:changed listener,收到后用 getHotkeyTriggerLabel 重算 label。
  cleanup 一并处理。

QaPanel 跑在独立 webview(label="qa"),没有 HotkeySettingsContext 注入,
所以必须走 IPC 事件而不是 React context;这也是为什么不能复用 Settings
页面的本地 state 更新。
2026-05-04 06:58:13 +08:00
baiqing d048107abb fix(asr): 把 Qwen3-ASR-Flash 从 V1 拿掉,需独立客户端留给 V2
reviewer 指出 DashScope 的 Qwen3-ASR-Flash 不走 OpenAI Whisper 的
multipart `/audio/transcriptions`,而是 MultiModalConversation 协议
(messages=[{content:[{audio:...}]}])。核实两个独立公开实现
(xinnan-tech/xiaozhi-esp32-server、jianchang512/pyvideotrans)确认
属实——把 qwen 强行塞进 WhisperBatchASR 必然 transcription 失败。

按 PR 描述里"待复核风险 → 不通则降级到 V2"的预案:
- 从 ASR_PRESETS 删除 qwen 项
- 从 is_whisper_compatible_provider 拿掉 "qwen"
- i18n 删掉 asrQwen 文案
- 注释里点名 Qwen 留给 V2 的原因

剩下 SiliconFlow / GLM-ASR / Groq / OpenAI Whisper 四家都有公开
OpenAI 兼容 /audio/transcriptions 端点,继续走原通道。
2026-05-04 06:53:40 +08:00
baiqing 94d6a7199c feat(asr): 接入 Qwen3-ASR / SiliconFlow / GLM-ASR / Groq 作为可选服务商
复用现有 OpenAI 兼容 `/audio/transcriptions` 通道(WhisperBatchASR),无需
新增 Rust 客户端。把 `coordinator.rs` 中的 whisper 分支条件改为
`is_whisper_compatible_provider(id)`,新增厂商时一处即可扩展。

设置页 ASR 下拉新增 4 项预设,切换时自动预填 baseUrl 与 model,避免用户
忘填模型 ID 必然踩坑。

Closes #212
2026-05-03 21:54:34 +08:00
millionart f497cd4eb5 merge Windows TSF IME support 2026-05-03 19:24:21 +08:00
Chris233 6027bb7d6f fix(app): correct browser timeout ref types for build 2026-05-03 17:36:21 +08:00
baiqing 5b8be1664a fix(qa,windows): 划词追问浮窗读用户当前录音键、Windows 加 toolbar 拖拽 (#205)
Windows 端的滑词追问浮窗存在三处问题:(1) qa.* i18n 硬编码 "Option",
Windows 用户没这个键;(2) 拖拽只走 macOS NSWindow.movableByWindowBackground,
Windows 上无等价路径;(3) toolbar 注释明确写「不需要 data-tauri-drag-region」
导致 Tauri 标准拖拽通路也没接。

修复:
- QaPanel mount 时读 prefs.hotkey.trigger,转成 label(如「右 Control」/
  「Right Alt」),通过 i18n {{recordHotkey}} 插值传给 EmptyHint /
  RecordingHeader / TurnIndicator / ErrorRow / StatusBar 五个子组件,平台
  与用户配置都能跟上。
- Toolbar 空白 spacer 加 data-tauri-drag-region:mousedown 走 Tauri 标准
  startDragging() → Windows 上是 WM_NCLBUTTONDOWN(HTCAPTION),在 focus:false
  浮窗上也能拖;macOS 同时保留原 NSWindow movableByWindowBackground,两条
  路径并存不冲突。
2026-05-03 16:49:54 +08:00
millionart a6e49b965d feat: add Windows TSF-only insertion toggle 2026-05-03 16:30:20 +08:00
baiqing 0bf4f7e253 fix(capsule): Windows 翻译模式 host 高度跟随 translation flag
PR Reviewer (PR Agent) 指出:getCapsuleHostMetrics(os, false) 把
translation 参数硬编码成 false,Windows 端 host 在翻译模式下应该从
84 长到 118,硬编码后徽章 bottom 用更短 host 量出来 → 跟 118pt 实际
窗口尺寸错位、徽章可能溢出或被裁。

修:把 useState<translation> 提到 hostMetrics 之前,调用改回
getCapsuleHostMetrics(os, translation)。macOS / Linux 不受影响 —
capsuleLayout 在那两个分支已经忽略 translationActive 返回固定 42。
2026-05-03 15:04:36 +08:00
baiqing d39072a21f Merge remote-tracking branch 'origin/main' into codex/fix-macos-window-ui
# Conflicts:
#	openless-all/app/src-tauri/src/polish.rs
2026-05-03 13:55:44 +08:00
baiqing f06f305774 fix(macos): align with 1.2.11 window/capsule UI + tab cross-fade
红绿灯
- trafficLightPosition x=14 y=20(Apple Notes / Safari 视觉),
  MAC_TITLEBAR_HEIGHT 30→28,paddingTop 30→28,让红绿灯在 titlebar
  内对称居中。

胶囊几何回退到 1.2.11
- macOS / Linux 上 capsule_window_bounds 固定 220×110,capsule_visual_height
  改回 96,emit_capsule 不再随 translation_active 改 size + 位置。修两个
  回归:(a) 录音胶囊 pill 左右被裁;(b) 按 Shift 后窗口高度从 42→110
  导致整体下移。
- 单元测试断言同步更新。

emit_capsule macOS show 路径回退到 1.2.11
- show_capsule_window_no_activate macOS 实现删除(合进 cfg(not(windows))
  stub 返回 false),fallback 走 window.show() + restore_main_window_key_if_active。
  orderFrontRegardless 路径在 webview 未完整初始化时偶发不可见。

Translation 设置页 + i18n 回退到 1.2.12
- 撤回 d7d274f 整页重排,回到 1.2.11/1.2.12 的简洁布局。

tab 切换 cross-fade with blur
- FloatingShell.tsx 加 displayTab + tabPhase;点新 tab 触发旧页
  ol-page-fadeout(opacity 1→0、blur 0→8px、180ms),结束后挂载
  新页走现有 ol-page-slide。修 Windows 徽章 bottom 公式:保留
  hostMetrics 路径(pill 不在 win host 中线,calc(50%) 不对)。

Capsule.tsx 动画化简
- 徽章/capsule-in 撤回 motion-soft / motion-spring 别名 + filter blur,
  回到 1.2.11 的 ease-out / cubic-bezier。
2026-05-03 13:29:53 +08:00
H-Chris233 6545231d83 Restore mac traffic lights without enabling native chrome on Linux/Windows
Keep custom chrome on non-mac platforms while restoring native traffic lights on macOS. Also remove the extra outer mac shell radius/border to avoid double-rounded corners.

Constraint: macOS must show native traffic lights for issue #183
Constraint: Windows/Linux should keep custom titlebar behavior
Rejected: set decorations=true in tauri.conf globally | enables native chrome on Linux
Confidence: high
Scope-risk: narrow
Directive: Platform-specific window decoration behavior should be controlled at runtime, not by global config
Tested: npm run build (openless-all/app)
Not-tested: manual visual verification on real macOS/Linux/Windows runtimes
2026-05-03 12:22:14 +08:00
baiqing d7d274fa87 fix(ui): clean sidebar and translation settings 2026-05-03 10:03:40 +08:00
baiqing 420c5425e6 fix(macos): tune window chrome motion 2026-05-03 09:43:19 +08:00
H-Chris233 e99619948e Treat missing LLM model as unconfigured
The LLM connection check no longer falls back to an Ark-specific default model when the user has not configured one. Instead, it returns a dedicated marker and the settings UI surfaces the empty-state copy, which keeps the validation result aligned with the actual configuration state.

Constraint: Preserve the existing provider-status parsing and keep the change minimal.
Rejected: Keep the deepseek-v3-2 fallback | it creates a false positive for unconfigured non-Ark providers.
Confidence: high
Scope-risk: narrow
Directive: Validation should never invent a model when the user has not saved one.
Tested: npm run build; cargo test --lib
Not-tested: Live provider behavior with a truly empty model field on a real endpoint
2026-05-02 19:59:09 +08:00
Chris233 a259b57d5e Merge branch 'appergb:main' into main 2026-05-02 16:11:42 +08:00
Cooper-X-Oak 802ee653c9 fix(windows): 收紧主窗口 shell 外观契约 2026-05-02 12:12:46 +08:00
Cooper-X-Oak 946d842023 fix(windows): restore custom chrome interactions 2026-05-02 12:12:02 +08:00
Cooper-X-Oak 4ea8389ceb fix(windows): align custom shell rounded frame 2026-05-02 12:12:02 +08:00
Cooper-X-Oak 9875e089d8 fix(windows): 收紧 capsule 几何契约 2026-05-02 12:05:09 +08:00
baiqing dd7c02558f fix(linux): 收敛平台契约错配与 best-effort 降级 (closes #165) 2026-05-02 11:48:15 +08:00
baiqing db27107e7a fix(qa): 修划词追问浮窗 4 个取消 race (closes #161)
issue #161 4 个子修复合并为一个 PR(取消相关,紧密耦合)。

## 1. Esc 路由
coordinator.rs::handle_window_hotkey_event 之前无条件 cancel_session(dictation
取消),即使在 QA 浮窗里按 Esc 也杀的是 dictation。现在先查 qa_state.panel_visible
+ phase != Idle,是 → close_qa_panel;否 → 走 dictation 取消。

## 2. SSE 流取消旗标
- Inner 加 qa_stream_cancelled: Arc<AtomicBool>
- begin_qa_session 重置 false
- cancel_qa_session set true
- polish.rs::chat_completion_history_streaming 的 SSE loop 每帧检查
  should_cancel() flag,true 时立即 break,避免取消后 LLM 继续 drain
  HTTP body 烧 token

## 3. session_id 守卫
on_delta 闭包捕获本会话的 captured_session_id;emit chunk 前比对当前
qa_state.session_id,不等就跳过。修复"取消后开新会话,旧 LLM 流的
chunk 漏进新气泡"。

## 4. loading 帧
types.ts::QaStateKind 加 'loading';QaPanel.tsx switch 加 case:把 status
切到 'thinking' 视图,避免 ASR finalize 期间 UI 卡 'recording' 几百 ms
反馈缺失。

## API 变化
- polish::answer_chat_streaming<F, C> 签名加 should_cancel: C
- polish::chat_completion_history_streaming<F, C> 同
- coordinator::answer_chat_dispatch<F, C> 同

## 测试
- cargo check ✅
- npm run build ✅
2026-05-02 10:56:40 +08:00
TRIP ca7b68053b Merge pull request #173 from appergb/fix/issue-162-qa-level-listener
fix(qa): QaPanel 加 qa:level 监听 + LevelBar (closes #162)
2026-05-02 10:44:48 +08:00
baiqing a8f4b891a0 fix(qa): QaPanel 加 qa:level 监听 + LevelBar 电平条 (closes #162)
后端 coordinator.rs:1743 在 QA 录音时每帧 33ms 推 qa:level 事件,
但 QaPanel 之前只 listen qa:state / qa:dismiss,qa:level 完全无人接 →
QA 录音用户看不到电平反馈,不知道麦克风是否收音。

修复:
- QaPanel 加 listen('qa:level') 累积 level state(cleanup 加 unlistenLevel)
- 新增 LevelBar 组件:4px 高蓝色进度条,宽度 = level × 100%
- RecordingHeader / TurnIndicator(recording) 都加 LevelBar 渲染
- 状态切换非 recording 时 setLevel(0) 避免残留

测试:
- npm run build ✅
- macOS 实测:QA 录音电平条跟随声音起伏(同 capsule 体验)
2026-05-02 10:38:44 +08:00
baiqing 4627eb4615 fix(windows): 启动 gate pollHotkeyStatus 加 10s 超时 (closes #163)
Windows 上 OS 拒绝低层键盘 hook 安装(反作弊/EDR/UAC 拦)时,
hotkey 永远 starting → 前端 while(!cancelled) 永不退出 → UI 卡 "正在启动" 灰屏。

加 50 × 200ms = 10s 上限:超时后强 setGate('ready'),让用户能进 Permissions
页看 hotkey_status.lastError 处理(页面已能显示错误 + 重试入口)。

非 Windows 路径不变。

测试:
- npm run build ✅
2026-05-02 10:36:13 +08:00
TRIP 68298d253c Merge pull request #138 from Cooper-X-Oak/codex/auto-update-download-acceleration
fix(tauri): 加速自动更新下载
2026-05-02 09:51:13 +08:00