Skip to content

feat(windows): SendInput 模式下可选隐藏键盘列表中的 OpenLess (Fixes #738)#740

Open
HKLHaoBin wants to merge 3 commits into
Open-Less:betafrom
HKLHaoBin:feat/issue-738-keyboard-list-visibility
Open

feat(windows): SendInput 模式下可选隐藏键盘列表中的 OpenLess (Fixes #738)#740
HKLHaoBin wants to merge 3 commits into
Open-Less:betafrom
HKLHaoBin:feat/issue-738-keyboard-list-visibility

Conversation

@HKLHaoBin

@HKLHaoBin HKLHaoBin commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • 新增 \windowsShowOpenlessInKeyboardList\ 偏好:在「始终使用 SendInput」模式下,允许用户选择是否在 Win+Space 键盘列表中显示 OpenLess TSF 输入法。
  • 通过用户级 \EnableLanguageProfile\ 应用/隐藏,无需管理员权限;启动与保存设置时同步,并与 ASR provider sync、prefs 写入组成可回滚事务。
  • 设置页主 SendInput 开关与子开关共用失败 toast +
    efresh()\ 回滚 UI;修复 Hook 顺序问题。

本 PR 基于 SendInput-only 插入模式(#733 相关提交),#738 的键盘列表开关仅在 SendInput 开启时可见。

关联 Issue

Fixes #738

Test plan


  • pm run build\ 通过
  • CI workflow_dispatch 全绿(Android / Linux / Windows)
  • Windows 真机:SendInput on + 关闭子开关 → Win+Space 无 OpenLess
  • 关闭 SendInput → OpenLess 恢复出现在列表
  • TSF apply 失败时 toast 提示,偏好不假保存

Made with Cursor


PR Type

Enhancement


Description

  • Allow hiding OpenLess from system keyboard list when SendInput-only mode is active

  • Apply TSF profile visibility on startup and via transactional setting save

  • Add UI toggle in Windows recording settings with error toast on failure

  • Extend SendInput-only mode to skip IME switch and fallback appropriately


Diagram Walkthrough

flowchart LR
  A["User toggles visibility pref"] --> B["persist_settings_with_keyboard_apply"]
  B --> C{"ASR provider changed?"}
  C -->|Yes| D["sync_active_asr_provider"]
  D -->|Failure| E["Rollback keyboard list visibility"]
  C -->|No| F["apply_keyboard_list"]
  F --> G["TSF EnableLanguageProfile"]
  G --> H["Update keyboard list"]
  E --> I["Return error to UI"]
  H --> J["Write prefs"]
  J -->|Failure| K["Rollback keyboard list + ASR if needed"]
Loading

File Walkthrough

Relevant files
Enhancement
7 files
settings.rs
Add transactional save with keyboard list apply                   
+274/-11
dictation.rs
Skip TSF switch and insertion fallback for SendInput-only
+33/-14 
lib.rs
Apply keyboard list visibility on startup                               
+11/-0   
types.rs
Add new preferences with serde serialization                         
+101/-0 
windows_ime_profile.rs
Implement profile enable/disable via TSF API                         
+115/-1 
types.ts
Add new preference fields to TypeScript types                       
+4/-0     
RecordingInputSection.tsx
Add UI toggles for SendInput and keyboard list visibility
+40/-2   
Bug fix
1 files
coordinator.rs
Update dictation_error_code for SendInput-only mode           
+11/-3   
Documentation
5 files
en.ts
Add English i18n strings for new toggles                                 
+5/-0     
ja.ts
Add Japanese i18n strings for new toggles                               
+5/-0     
ko.ts
Add Korean i18n strings for new toggles                                   
+5/-0     
zh-CN.ts
Add Simplified Chinese i18n strings                                           
+5/-0     
zh-TW.ts
Add Traditional Chinese i18n strings                                         
+5/-0     
Tests
2 files
mock-data.ts
Include new preferences in mock data                                         
+2/-0     
stylePrefs.test.ts
Include new preferences in test defaults                                 
+2/-0     

HKLHaoBin and others added 3 commits June 22, 2026 09:27
Add windowsSendInputInsertionOnly so users who cannot restore their IME after dictation can opt into Unicode SendInput without switching to OpenLess TSF at session start.

Open-Less#733

Co-authored-by: Cursor <cursoragent@cursor.com>
Serde camelCase produced windowsSendinputInsertionOnly while the UI sends windowsSendInputInsertionOnly, causing the toggle to revert after save. Add explicit rename/alias on UserPreferences wire types and contract tests.

Refs Open-Less#733

Co-authored-by: Cursor <cursoragent@cursor.com>
…mode (Fixes Open-Less#738)

Add windowsShowOpenlessInKeyboardList pref with TSF EnableLanguageProfile apply on startup/save, transactional rollback with ASR sync, and UI error toast/refresh for both SendInput and keyboard-list toggles.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

733 - Partially compliant

Compliant requirements:

  • New Rust field windows_sendinput_insertion_only with default false, serde rename and alias.
  • New TypeScript field added.
  • begin_session skips prepare_session when flag is true.
  • end_session branches to non-TSF insert when flag is true.
  • dictation_error_code skips TSF required error when flag is true.
  • UI toggle present under Windows condition.
  • i18n strings added in all 5 languages.
  • Unit tests for default, round-trip, deserialization, serialization, error code.

Non-compliant requirements:

(none)

Requires further human verification:

(none)

738 - Partially compliant

Compliant requirements:

  • New preference windows_show_openless_in_keyboard_list default true.
  • Setting only visible when windowsSendInputInsertionOnly is true.
  • Implemented via EnableLanguageProfile in windows_ime_profile.rs.
  • Applied on startup in lib.rs.
  • Applied in persist_settings_with_keyboard_apply with rollback logic.
  • Frontend toggle with error toast via emitSaved.
  • Unit tests for desired_openless_language_profile_enabled and mock transaction tests.

Non-compliant requirements:

(none)

Requires further human verification:

  • Actual behavior on Windows (Win+Space list update) cannot be verified in code review; needs manual test.
⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible rollback double-count bug

In persist_settings_with_keyboard_apply, when active_asr_provider_changed is true and write_settings fails, the code checks if windows_keyboard_list_changed and attempts to rollback the keyboard list. However, if the ASR rollback also fails (outer else branch), it attempts to write prefs again (roll-forward) and returns the combined error. In the case where windows_keyboard_list_changed is false and active_asr_provider_changed is true, the rollback of ASR is attempted as before. But the new code restructures the control flow such that the active_asr_provider_changed branch inside write failure now includes an inner condition for windows_keyboard_list_changed. If windows_keyboard_list_changed is true and ASR rollback succeeds, a return Err(error) is executed, which is fine. However, if windows_keyboard_list_changed is true and ASR rollback fails, the code goes into the Err(rollback_error) arm and does a roll-forward write of prefs. If that second write also fails, the error message will contain three concatenated errors. That is intended and acceptable.
No actual bug, but the logic is complex and error-prone. Consider adding a helper to encapsulate the rollback steps.

if windows_keyboard_list_changed {
    apply_keyboard_list(&prefs)?;
}

if active_asr_provider_changed {
    if let Err(asr_err) = coord.sync_active_asr_provider(&active_asr_provider) {
        if windows_keyboard_list_changed {
            if let Err(kb_rollback_err) = apply_keyboard_list(&previous) {
                return Err(format!(
                    "{asr_err}; additionally failed to rollback keyboard list visibility: {kb_rollback_err}"
                ));
            }
            log::warn!(
                "[windows-ime] rolled back keyboard list visibility after ASR provider sync failure"
            );
        }
        return Err(asr_err);
    }
}

if let Err(error) = coord.write_settings(prefs.clone()) {
    if active_asr_provider_changed {
        match coord.sync_active_asr_provider(&previous.active_asr_provider) {
            Ok(()) => {
                if windows_keyboard_list_changed {
                    if let Err(rollback_err) = apply_keyboard_list(&previous) {
                        return Err(format!(
                            "{error}; additionally failed to rollback keyboard list visibility: {rollback_err}"
                        ));
                    }
                    log::warn!(
                        "[windows-ime] rolled back keyboard list visibility after settings write failure"
                    );
                }
                return Err(error);
            }
            Err(rollback_error) => {
                // ASR vault 无法回滚时 roll-forward prefs;键盘列表保持新状态,避免三者分叉。
                coord.write_settings(prefs).map_err(|roll_forward_error| {
                    format!(
                        "{error}; additionally failed to restore active ASR provider: {rollback_error}; additionally failed to preserve active ASR provider consistency: {roll_forward_error}"
                    )
                })?;
            }
        }
    } else if windows_keyboard_list_changed {
        if let Err(rollback_err) = apply_keyboard_list(&previous) {
            return Err(format!(
                "{error}; additionally failed to rollback keyboard list visibility: {rollback_err}"
            ));
        }
        log::warn!(
            "[windows-ime] rolled back keyboard list visibility after settings write failure"
        );
        return Err(error);
    } else {
        return Err(error);
    }
}
Missing non-Windows guard for apply function

The function apply_windows_openless_keyboard_list_pref on non-Windows platforms simply returns Ok(()) and ignores the desired state. This is acceptable because the setting is only meaningful on Windows. However, if a user on Linux accidentally gets this preference (e.g., through a profile sync), no error is raised and the setting silently succeeds. This could lead to confusion if the user expects it to have an effect. Consider at least logging a warning.

/// 将「SendInput + 键盘列表可见性」偏好同步到当前用户的 TSF 语言配置文件。
pub fn apply_windows_openless_keyboard_list_pref(prefs: &UserPreferences) -> Result<(), String> {
    let desired = desired_openless_language_profile_enabled(prefs);
    #[cfg(target_os = "windows")]
    {
        let status = get_windows_ime_status();
        if status.state != WindowsImeInstallState::Installed {
            if desired {
                return Ok(());
            }
            return Err(
                "OpenLess TSF IME is not installed; cannot hide it from the keyboard list"
                    .to_string(),
            );
        }
        set_openless_language_profile_enabled(desired).map_err(|err| {
            let message = err.to_string();
            log::warn!("[windows-ime] apply keyboard list visibility pref failed: {message}");
            message
        })
    }
    #[cfg(not(target_os = "windows"))]
    {
        let _ = desired;
        Ok(())
    }
}

@HKLHaoBin

Copy link
Copy Markdown
Contributor Author

更改 13 个文件, 485 行插入(+), 15 行删除(-)

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.

能否取消在键盘布局中的显示?

1 participant