Skip to content

[KBM] Use TSF4 API to support text expand feature#46869

Draft
moooyo wants to merge 12 commits intomainfrom
yuleng/kbm/tsf4
Draft

[KBM] Use TSF4 API to support text expand feature#46869
moooyo wants to merge 12 commits intomainfrom
yuleng/kbm/tsf4

Conversation

@moooyo
Copy link
Copy Markdown
Contributor

@moooyo moooyo commented Apr 10, 2026

Summary of the Pull Request

Currently only on MVP stage.

TBD;

Requirement:

  1. Windows 11
  2. OS version > 26H4D (version number: 26100.8311, this is not the final version number and may change future)

PR Checklist

  • Communication: I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected
  • Tests: Added/updated and all pass
  • Localization: All end-user-facing strings can be localized
  • Dev docs: Added/updated
  • New binaries: Added on the required places
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

Validation Steps Performed

Yu Leng (from Dev Box) and others added 10 commits March 19, 2026 10:03
Add a new "Expand" trigger type that allows users to define text expansion
mappings (type abbreviation → expand to full text) in the Keyboard Manager editor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Manually sync SwitchPresenter.Value after programmatic ComboBox item
changes, since WinUI binding on SelectedItem.Tag does not reliably
update when items are cleared and replaced at runtime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace XAML binding on SwitchPresenter.Value with code-behind sync
to avoid binding/direct-assignment conflict when ComboBox items are
dynamically replaced for the Expand trigger type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eWith case

When Text Expand trigger is selected, select the existing "Insert text"
ComboBoxItem, override its label/icon to "Replace with", and disable the
ComboBox. This avoids dynamic ComboBox item replacement that broke the
SwitchPresenter binding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace `new ResourceLoader()` with a string literal since the EditorUI
runs as an unpackaged app where ResourceLoader is not supported.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement the engine-side logic for the Text Expand feature using
Windows TSF4 (Text Services Framework 4) APIs. When a trigger key is
pressed, the engine reads the last N characters from the focused text
box via TSF4 and replaces the abbreviation with expanded text — without
touching the clipboard.

- Add Tsf4TextReplacer (Initialize/TryExpand/Shutdown) with LAF unlock
- Add ExpandMapping data structure, JSON load/save in MappingConfiguration
- Add HandleExpandTextEvent in keyboard event pipeline
- Wire C# editor to persist expand mappings to engine config (default.json)
- Add TSF4 support status card in Settings UI (KBM page)
- Disable Text Expand option in KBM editor when OS lacks TSF4 support
- Register KeyboardManagerEngine in sparse package manifest for identity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set IsTsf4Supported() to Build >= 10000 so all Windows 10/11 machines
pass the check. This enables testing the Text Expand UI flow on any dev
machine. The threshold will be raised to the correct build number before
shipping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EnableShortcut, DisableShortcut, and HandleShortcutDelete did not
recognize ExpandMapping, falling through to incorrect single-key or
multi-key branches. Add explicit ExpandMapping handling that calls
AddExpandMapping/DeleteExpandMapping on the engine config.

Also add DeleteExpandMapping C++ interop (matches by abbreviation +
app name).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
{
if (shortcut is ExpandMapping)
{
ShortcutKeyMapping skm = SettingsManager.EditorSettings.ShortcutSettingsDictionary[shortcut.Id].Shortcut;
ShortcutKeyMapping skm = SettingsManager.EditorSettings.ShortcutSettingsDictionary[shortcut.Id].Shortcut;
if (shortcut.IsActive)
{
_mappingService!.DeleteExpandMapping(skm.OriginalKeys, skm.TargetApp);
ShortcutKeyMapping skm = SettingsManager.EditorSettings.ShortcutSettingsDictionary[shortcut.Id].Shortcut;
if (shortcut.IsActive)
{
_mappingService!.DeleteExpandMapping(skm.OriginalKeys, skm.TargetApp);

if (shortcut is ExpandMapping expandMapping)
{
ShortcutKeyMapping skm = SettingsManager.EditorSettings.ShortcutSettingsDictionary[shortcut.Id].Shortcut;
if (shortcut is ExpandMapping expandMapping)
{
ShortcutKeyMapping skm = SettingsManager.EditorSettings.ShortcutSettingsDictionary[shortcut.Id].Shortcut;
int triggerKeyVk = KeyboardManagerInterop.GetKeyCodeFromName(skm.TargetKeys);
{
try
{
std::wstring attestation = std::wstring(LafPublisherId) + L" has registered their use of " + LafFeatureId + L" with Microsoft and agrees to the terms of use.";
auto result = winrt::Windows::ApplicationModel::LimitedAccessFeatures::TryUnlockFeature(
LafFeatureId, LafToken, attestation);

Logger::info(L"TSF4 LAF unlock status: {}", static_cast<int>(result.Status()));
}
catch (const winrt::hresult_error& ex)
{
Logger::warn(L"TSF4 LAF unlock failed: {}", ex.message().c_str());
}
catch (...)
{
Logger::warn(L"TSF4 LAF unlock failed (no package identity?)");

public ICommand OpenWindowsUpdateCommand => new RelayCommand(() =>
{
Process.Start(new ProcessStartInfo("ms-settings:windowsupdate") { UseShellExecute = true });
@github-actions

This comment has been minimized.

@moooyo moooyo added this to the 0.100 milestone Apr 10, 2026
@moooyo moooyo added the Product-Keyboard Shortcut Manager Issues regarding Keyboard Shortcut Manager label Apr 10, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

This comment has been minimized.

# Conflicts:
#	src/modules/keyboardmanager/KeyboardManagerEditorUI/Controls/UnifiedMappingControl.xaml.cs
#	src/modules/keyboardmanager/KeyboardManagerEditorUI/Helpers/ValidationHelper.cs
#	src/modules/keyboardmanager/KeyboardManagerEditorUI/Pages/MainPage.xaml
#	src/modules/keyboardmanager/KeyboardManagerEditorUI/Pages/MainPage.xaml.cs
#	src/modules/keyboardmanager/KeyboardManagerEditorUI/Strings/en-US/Resources.resw
@github-actions
Copy link
Copy Markdown

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, 👼 SARIF report, or 📝 job summary for details.

Unrecognized words (1)

laf

These words are not needed and should be removed IPREVIEW ITHUMBNAIL Laf LPCFHOOKPROC LUMA MAXDWORD MRT suntimes timespan VSync

To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the git@github.com:microsoft/PowerToys.git repository
on the yuleng/kbm/tsf4 branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/24231427788/attempts/1' &&
git commit -m 'Update check-spelling metadata'

OR

To have the bot accept them for you, comment in the PR quoting the following line:
@check-spelling-bot apply updates.

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE Product-Keyboard Shortcut Manager Issues regarding Keyboard Shortcut Manager

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Text Replacement / expander

2 participants