Skip to content

fix(settings): 热键冲突不再拒绝保存——升级迁移停用冲突默认键 + 保存自动化解(#904) - #907

Merged
H-Chris233 merged 2 commits into
Open-Less:betafrom
H-Chris233:codex/fix-904-hotkey-collision-save-fallback
Aug 4, 2026
Merged

fix(settings): 热键冲突不再拒绝保存——升级迁移停用冲突默认键 + 保存自动化解(#904)#907
H-Chris233 merged 2 commits into
Open-Less:betafrom
H-Chris233:codex/fix-904-hotkey-collision-save-fallback

Conversation

@H-Chris233

@H-Chris233 H-Chris233 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

User description

背景

#904:1.3.15 新增的「录音方式:自动」无效,切换后重启软件会回退到「按住说话」。

根因(证据链完整)

  1. 1.3.15(PR feat(windows): add selected-text polish workflow #851)新增「选区润色」工作流,Windows 默认热键 = 右 Altdefault_selection_polish_hotkey)。
  2. 旧配置升级时,缺失的 selectionPolishHotkey 被无条件补入默认右 Alt;迁移只保护录音键为历史默认 Right Control 的用户(is_right_control_modifier_shortcut),不覆盖自定义录音键为右 Alt 的用户 → 偏好文件出现持久冲突:dictationHotkey == selectionPolishHotkey == RightAlt
  3. 冲突存在后,任何 set_settings(含切「自动」模式)都被 reject_hotkey_collisions 整体拒绝、不落盘;前端 updatePrefs 乐观更新 + 无 catch 静默吞错 → 界面显示已切换,重启后读回旧值(按住说话)。
  4. issue 附带的日志佐证:整个会话 mode=Hold、从未出现 mode=Auto,且每次按录音键(vk=165=右 Alt)同时触发 selection-polish workflow failed——冲突在运行时显形。

改动

  • types.rs(升级迁移):旧文件升级时,若默认注入的选区润色键与录音键相同,改为停用新功能(None),不再写入冲突值。
  • commands/settings.rs(保存兜底):新增 reconcile_hotkey_collisions——persist_settings 校验到热键冲突/非法时不再整体拒绝,按核心度从高到低自动化解:冲突的非核心热键恢复旧值,旧值本身也是历史冲突则停用(翻译键回退默认 Shift),录音键永不参与调整;其余设置照常落盘并写日志。对受影响的用户,下一次任意设置保存即自动修复历史冲突。
  • HotkeySettingsContext.tsx(前端兜底)updatePrefs 保存失败时回滚乐观状态、弹出失败提示并记日志,杜绝「假保存」。
  • 测试:新增升级迁移停用冲突键、历史冲突下切模式仍可保存、非核心键重叠/非法自动化解的回归测试;4 个旧「保存被拒」测试同步改为新兜底语义。

验证

  • 新增 4 个 Rust 测试全过;settings/hotkeys/types 相关 65 个测试全过。
  • cargo test --lib:924 通过,2 失败与改动前基线完全一致(cooldown 时序抖动 + Windows 符号链接权限环境测试,旧二进制同样失败)。
  • 前端 tsc --noEmitfrontend-test-runner 全过。

行为变更提示

保存遇到热键冲突时不再失败,而是自动恢复/停用冲突的非核心热键(日志留痕),核心录音键始终保留;升级用户首次保存会顺带停用与新功能冲突的默认键。

Fixes #904


PR Type

Bug fix


Description


Diagram Walkthrough

flowchart LR
  A["User saves settings with hotkey conflict"] --> B{"Collision detected"}
  B -- "Yes" --> C["Reconcile non-core hotkeys"]
  C --> D["Restore old value or disable key"]
  D --> E["Persist remaining settings"]
  B -- "No" --> F["Save normally"]
  E --> G["Notify UI / rollback on failure"]
  F --> G
Loading

File Walkthrough

Relevant files
Tests
mod.rs
Adapt hotkey collision tests to reconcile behavior             

openless-all/app/src-tauri/src/commands/mod.rs

  • Updated hotkey collision tests to expect reconciliation instead of
    rejection.
  • Renamed tests to reflect new save-fallback semantics.
  • Verified dictation key is preserved while lower-priority keys are
    disabled or restored.
+27/-25 
Bug fix
settings.rs
Add hotkey collision auto-reconcile on save                           

openless-all/app/src-tauri/src/commands/settings.rs

  • Added reconcile_hotkey_collisions to auto-resolve conflicting or
    invalid non-core hotkeys.
  • Dictation hotkey is treated as core and never adjusted.
  • Persist settings now reconciles collisions before saving instead of
    rejecting the whole save.
  • Adds regression tests for legacy duplication, non-core overlaps, and
    side-specific invalid keys.
+210/-1 
types.rs
Prevent default selection-polish hotkey collision on upgrade

openless-all/app/src-tauri/src/types.rs

  • Improved upgrade migration for the selection-polish hotkey default.
  • Disables selection-polish hotkey when it would overlap the dictation
    hotkey.
  • Detects physical-key overlap, not just string equality, for legacy
    right-Alt configurations.
  • Added Windows-specific migration regression tests.
+49/-9   
HotkeySettingsContext.tsx
Rollback optimistic settings on save failure                         

openless-all/app/src/state/HotkeySettingsContext.tsx

  • Tracks the last persisted preferences in persistedPrefsRef.
  • Rolls back optimistic UI state when settings saving fails.
  • Emits a saved failure event and rethrows the error to surface the
    failure to the user.
+17/-1   

Open-Less#904:1.3.15 新增的选区润色默认键(Windows=右 Alt)在旧配置升级时被
无条件注入。若用户录音键恰为右 Alt,则偏好文件出现持久冲突,之后任何
set_settings(含切「自动」录音方式)都被 reject_hotkey_collisions 整体
拒绝,前端又静默吞错——表现为界面显示已切换、重启后回退按住说话。

修复:
- types.rs:旧文件升级时若默认注入的选区润色键与录音键相同,改为停用
  新功能而非写入冲突值;
- commands/settings.rs:保存兜底 reconcile_hotkey_collisions——热键
  冲突/非法不再拒绝整份保存,按核心度自动恢复旧值或停用冲突的非核心键
  (录音键永不参与调整),其余设置照常落盘并写日志;
- HotkeySettingsContext.tsx:保存失败回滚乐观状态并弹出失败提示,杜绝
  「假保存」;
- 回归测试:升级迁移停用冲突键、历史冲突下切模式仍可保存、非核心键
  重叠/非法自动化解;4 个旧「保存被拒」测试同步改为新兜底语义。

验证:新增测试全过;cargo test --lib 924 通过、2 失败均与改动前基线
一致(cooldown 时序抖动 + Windows 符号链接权限环境测试);前端
tsc --noEmit 与 frontend-test-runner 全过。
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit fd2485f)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

904 - PR Code Verified

Compliant requirements:

  • 保存时热键冲突不再整体拒绝,而是自动化解后继续保存。
  • 1.3.15 升级迁移避免默认选区润色热键与录音键冲突。
  • 前端不再静默吞错,保存失败会回滚乐观状态并提示。

Requires further human verification:

  • 需在真实 Windows 环境验证:升级后录音方式 Auto 重启保持,且被自动停用的选区润色热键在 UI 中正确显示。

851 - Not compliant

Non-compliant requirements:

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

Possible Issue

persistedPrefsRef is updated with the optimistic resolved object after a successful save, but the backend may persist different values (this PR's reconciliation disables a conflicting selectionPolishHotkey while saving the new recording mode). If the prefs:changed event hasn't updated (or is later overwritten by) persistedPrefsRef, a subsequent failed save rolls back to values that were never persisted, so the UI can show a state that differs from disk. Track actual saved values from the command result or prefs:changed payload instead of the optimistic object.

try {
    await queueSetSettings(resolved)
    persistedPrefsRef.current = resolved
} catch (error) {
    // 兜底(#904):保存失败必须回滚乐观状态并可见,
    // 不能出现界面显示已切换、重启后回退的“假保存”。
    const fallback = persistedPrefsRef.current ?? current
    latestPrefsRef.current = fallback
    setPrefs(fallback)
    console.error("[hotkey-settings] save failed, rolled back", error)
    emitSaved("failed", errorMessage(error))
    throw error

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit fd2485f

@H-Chris233
H-Chris233 merged commit 12de016 into Open-Less:beta Aug 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[area] 录音方式无效,重启软件会切换回长按识别

1 participant