mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-30 17:07:23 +01:00
Compare commits
34 Commits
yuleng/cmd
...
stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b62f642184 | ||
|
|
5367e77a9c | ||
|
|
1c3a6ffe63 | ||
|
|
3d63d499da | ||
|
|
bd2967806f | ||
|
|
068cccc22b | ||
|
|
1f0603fb2b | ||
|
|
374dccc475 | ||
|
|
64113a1ca9 | ||
|
|
d858dcc1bb | ||
|
|
3539041b72 | ||
|
|
d9b1ca0fd8 | ||
|
|
883ec9c815 | ||
|
|
4b1f54fff9 | ||
|
|
d130f3596d | ||
|
|
fdd0832eb2 | ||
|
|
fcff4bc056 | ||
|
|
cb542079ff | ||
|
|
ea94bcdd6e | ||
|
|
cd467785f2 | ||
|
|
b79b7f7bf6 | ||
|
|
6ab7e878eb | ||
|
|
064484c77c | ||
|
|
60c886f817 | ||
|
|
db41c5a65c | ||
|
|
8f87058508 | ||
|
|
755c138723 | ||
|
|
8b066cea2e | ||
|
|
2d92ccdf3b | ||
|
|
83ea0c2f28 | ||
|
|
cb81a99c5f | ||
|
|
48a3f4fa87 | ||
|
|
6c05e44680 | ||
|
|
6505cd7a63 |
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@@ -5,7 +5,6 @@
|
||||
## PR Checklist
|
||||
|
||||
- [ ] Closes: #xxx
|
||||
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) -->
|
||||
- [ ] **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
|
||||
|
||||
2
.github/workflows/msstore-submissions.yml
vendored
2
.github/workflows/msstore-submissions.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v5
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "deps/expected-lite"]
|
||||
path = deps/expected-lite
|
||||
url = https://github.com/martinmoene/expected-lite.git
|
||||
[submodule "deps/cziplib"]
|
||||
path = deps/cziplib
|
||||
url = https://github.com/kuba--/zip.git
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<PropertyGroup Condition="'$(SkipCppCodeAnalysis)' == ''">
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>$(MsbuildThisFileDirectory)\CppRuleSet.ruleset</CodeAnalysisRuleSet>
|
||||
<CAExcludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(CAExcludePath)</CAExcludePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- C++ source compile-specific things for all configurations -->
|
||||
@@ -35,7 +34,7 @@
|
||||
<PreferredToolArchitecture Condition="'$(PROCESSOR_ARCHITECTURE)' == 'ARM64' or '$(PROCESSOR_ARCHITEW6432)' == 'ARM64'">arm64</PreferredToolArchitecture>
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
<ReplaceWildcardsInProjectItems>true</ReplaceWildcardsInProjectItems>
|
||||
<ExternalIncludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(ExternalIncludePath)</ExternalIncludePath>
|
||||
<ExternalIncludePath>$(MSBuildThisFileFullPath)\..\deps\;$(MSBuildThisFileFullPath)\..\packages\;$(ExternalIncludePath)</ExternalIncludePath>
|
||||
<!-- Enable control flow guard for C++ projects that don't consume any C++ files -->
|
||||
<!-- This covers the case where a .dll exports a .lib, but doesn't have any ClCompile entries. -->
|
||||
<LinkControlFlowGuard>Guard</LinkControlFlowGuard>
|
||||
|
||||
240
README.md
240
README.md
@@ -51,20 +51,19 @@ But to get started quickly, choose one of the installation methods below:
|
||||
Go to the [PowerToys GitHub releases][github-release-link], click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.97%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.96%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.96.0/PowerToysUserSetup-0.96.0-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.96.0/PowerToysUserSetup-0.96.0-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.96.0/PowerToysSetup-0.96.0-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.96.0/PowerToysSetup-0.96.0-arm64.exe
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.96%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.95%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysUserSetup-0.95.1-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysUserSetup-0.95.1-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysSetup-0.95.1-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysSetup-0.95.1-arm64.exe
|
||||
|
||||
| Description | Filename |
|
||||
|----------------|----------|
|
||||
| Per user - x64 | [PowerToysUserSetup-0.96.0-x64.exe][ptUserX64] |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.96.0-arm64.exe][ptUserArm64] |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.96.0-x64.exe][ptMachineX64] |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.96.0-arm64.exe][ptMachineArm64] |
|
||||
|
||||
| Per user - x64 | [PowerToysUserSetup-0.95.1-x64.exe][ptUserX64] |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.95.1-arm64.exe][ptUserArm64] |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.95.1-x64.exe][ptMachineX64] |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.95.1-arm64.exe][ptMachineArm64] |
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -103,131 +102,156 @@ There are [community driven install methods](./doc/unofficialInstallMethods.md)
|
||||
</details>
|
||||
|
||||
## ✨ What's new
|
||||
**Version 0.96 (November 2025)**
|
||||
**Version 0.95 (October 2025)**
|
||||
|
||||
For an in-depth look at the latest changes, visit the [Windows Command Line blog](https://aka.ms/powertoys-releaseblog).
|
||||
|
||||
**✨ Highlights**
|
||||
- Advanced Paste now supports multiple online and on-device AI model providers: Azure OpenAI, OpenAI, Google Gemini, Mistral, Foundry Local and Ollama.
|
||||
- Command Palette received extensive improvements including file search filters, better clipboard history metadata, context-menu styling, and dozens of bug fixes and enhancements.
|
||||
- PowerRename can now extract and use photo metadata (EXIF, XMP) in renaming patterns like `%Camera`, `%Lens`, and `%ExposureTime`.
|
||||
|
||||
### Advanced Paste
|
||||
- Advanced Paste now lets you connect to multiple AI providers instead of being limited to a single OpenAI provider. See [Advanced Paste documentation](https://learn.microsoft.com/windows/powertoys/advanced-paste) for usage.
|
||||
|
||||
### Awake
|
||||
- The Awake countdown timer now stays accurate over long periods. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed Awake context menu positioning. The fix removed the conversion of the mouse cursor from screen to client-window coordinates, instead using the raw screen coordinates returned by GetCursorPos; the context menu now appears at the correct screen position. Thanks [@lzandman](https://github.com/lzandman)!
|
||||
- **NEW:** The **Light Switch** utility in PowerToys allows you to automatically switch between light and dark themes in Windows based on the time of day.
|
||||
- Command Palette delivered major search performance gains (new fuzzy matcher and smarter fallbacks) improving relevance and speed.
|
||||
- Peek can now be activated using just the Spacebar!
|
||||
- Find My Mouse added transparent spotlight with independent backdrop opacity, boosting focus and accessibility.
|
||||
- Settings now lets you delete shortcuts entirely and ignore conflicts.
|
||||
- Mouse Pointer Crosshairs gained orientation options (vertical / horizontal / both) for customizable accessibility. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
- PowerRename fixed enumeration counter skipping ensuring reliable batch renames. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- ZoomIt restored legacy draw and snipping behaviors, and fixed recording issues, improving reliability. Thanks [@chakrik73](https://github.com/chakrik73)!
|
||||
|
||||
### Command Palette
|
||||
- The search field in context menus now matches the look of the Command Palette, with a smoke backdrop and improved padding.
|
||||
- Fallback items such as math calculations or the Run command now appear in results more quickly. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Ensured the command bar updates correctly after navigating to another page and commands are displayed correctly. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- The Command Palette settings page has been reorganized. Activation-key options are grouped under an expander and extension settings are framed for improved readability.
|
||||
- When you modify a command, its alias, hotkey, and tags now update in the top-level list, keeping the displayed information in sync. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Press `Ctrl + ,` to open Command Palette settings from anywhere. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- You can use `Page Up` and `Page Down` to navigate the list while focus is in the search box. Thanks [@samrueby](https://github.com/samrueby)!
|
||||
- Fixed an issue where the search box could disappear when navigating pages. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Ensured search text is selected when *Go home when activated* and *Highlight search on activate* are both enabled. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed an issue where Command Palette window occasionally appeared on the taskbar under certain Windows settings. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Ensured that labels and icons of list items and menu items update when they change. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed visibility of list filters when navigating to a content page. Thanks [@DevLGuilherme](https://github.com/DevLGuilherme)!
|
||||
- Added search to the extension list and a link to extensions on the Microsoft Store. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added options to open the Command Palette window at its last position or re-center it.
|
||||
- The Command Palette now remembers its window size after restarting.
|
||||
- Added a global error handler that logs fatal errors and provides feedback when unexpected failures force Command Palette to close. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed forms and extension settings not showing on some machines due to a missing VC++ runtime.
|
||||
- Restored ranking of fallback commands for built-in extensions (Sleep, Shutdown, Windows settings, Web search, etc.). Thanks [@jiripolasek](https://github.com/jiripolasek).
|
||||
- Improved and unified labels and texts across the application!
|
||||
- Maintainance: Resolved numerous build warnings in Command Palette projects; no user-visible impact. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Maintainance: Fixed a logging issue so exception messages are properly recorded instead of placeholder text, improving troubleshooting. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Applied conditional margin for icon-only tags to tighten layout. Thanks [@samrueby](https://github.com/samrueby)
|
||||
- Improved the reliability of accessing Command Palette settings through PowerToys Settings and executing other x-cmdpal:// protocol commands. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Enabled AOT by default for improved performance while simplifying publish configs.
|
||||
- Replaced service state color dots with play/pause/stop icons for enhanced accessibility. Thanks [@samrueby](https://github.com/samrueby)
|
||||
- Fixed filter dropdown sync and crash by binding SelectedValue and raising UI-thread notifications. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Ensured long links wrap correctly in details view.
|
||||
- Removed animation and enforced minimum width on filter dropdown for clarity. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Restored focus to More button after ESC closes context menu, improving keyboard flow. Thanks [@chatasweetie](https://github.com/chatasweetie)
|
||||
- Marked main and toast windows as tool windows to keep them out of Alt+Tab while preserving style. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Fixed AOT template and theming issues for filter separators. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Introduced grid layouts (small, medium, gallery) for richer page presentation.
|
||||
- Materialized result lists to avoid rescoring overhead.
|
||||
- Disabled problematic selection TextToSuggest behind environment flag.
|
||||
- Major search performance improvements (new fuzzy matcher, smarter fallbacks, fewer exceptions).
|
||||
- Added context menu "Show Details" command when details pane is hidden.
|
||||
- Reduced window flicker by avoiding unnecessary cloaking. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Restored EmptyContent rendering for blank states. Thanks [@DevLGuilherme](https://github.com/DevLGuilherme)
|
||||
- Saved new state even if prior app state file was corrupt (better resilience). Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Migrated settings window to WinUI TitleBar control. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Prevented crash on duplicate keybindings and simplified matching. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Hotkeys now always respect the “Ignore shortcut in fullscreen” setting. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Hid search box on content pages, improving focus and accessibility, and added Home title. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Blocked Ctrl+I from inserting stray tabs in search box.
|
||||
- Logged HRESULT codes in error logs for deeper diagnostics. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Advanced font and emoji icon classification and alignment improvements. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Ensured that fallback command icons are visible on the extension settings page. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Fixed breadcrumb margin misalignment (visual polish). Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Truncated overly long command labels with ellipsis to prevent overflow.
|
||||
- Added a setting to configure the page transition animation.
|
||||
- Collection of small improvements and nits for Run Commands.
|
||||
- Improved bookmarks performance and experience. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Added Ctrl+O shortcut in Clipboard History to open links directly.
|
||||
- Resolved conflict with external software that blocked Command Palette from hiding.
|
||||
- Updated context menu items to reflect name and icon changes, and ensured application icons are displayed correctly. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Added Alt+Home shortcut to return immediately to the Command Palette home page. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Fixed a crash when displaying code blocks in markdown on detail or content pages. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Fixed an issue where the search bar icon and title were not updated when rapidly switching pages. Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Improved the appearance of the search box in the context menu.
|
||||
|
||||
|
||||
### Command Palette Extensions
|
||||
- Bookmarks: Added hints about bookmark placeholders to the Add/Edit Bookmark form. — Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Bookmarks: Improved migration of bookmarks from older versions and fixed an issue where aliases or keyboard shortcuts could be lost after restart. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Clipboard history: Items shown in Command Palette’s clipboard history now include helpful metadata. For example, image items show dimensions, text files show names and sizes, web links include page titles, and text entries display word counts. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- File search: Added filter buttons to show *all items*, *files only*, or *folders only*. Selecting a filter adds `kind:folders` or `kind:not folders` to narrow results.
|
||||
- System commands: Replaced the `:red_circle:` placeholder with an actual red-circle emoji so the correct icon appears in the UI. Thanks [@samrueby](https://github.com/samrueby)!
|
||||
- WinGet: Search performance feels more responsive because typed input is now processed via a task queue rather than complex cancellation tokens!
|
||||
- Window Walker: UWP apps no longer show a "not responding" label when suspended. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Window Walker: Now displays the actual icon of each window rather than using the process icon, improving recognition of PWAs and Python GUIs. Thanks [@Lee-WonJun](https://github.com/Lee-WonJun)!
|
||||
- Windows Terminal profiles: Fixed a rare crash in the Windows Terminal extension when the `LOCALAPPDATA` environment variable was missing. The path is now retrieved via a reliable API. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Replaced localized WebSearch setting keys with stable literals and numeric history count. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Enabled advanced markdown tables and emphasis extensions. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added setting to choose Clipboard History primary action (Paste vs Copy). Thanks [@jiripolasek](https://github.com/jiripolasek)
|
||||
- Added actionable empty-state hints for File Search (search PC / open indexing settings). Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Ensured all WinGet extension assets copy reliably to output. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Improved Run command line parsing for paths with spaces; sped up related tests.
|
||||
- Updated WebSearch extension icon set for enhanced clarity and contrast. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added Terminal profile sort order setting including MRU tracking. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added Uninstall Application command (UWP direct, Win32 via Settings). Thanks [@mKpwnz](https://github.com/mKpwnz)!
|
||||
- Deferred WinGet details loading and added timing logs.
|
||||
- Removed LINQ from All Apps extension for performance.
|
||||
- Added standardized key chord system + shortcuts to File Search commands. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added Terminal channel filter & remembered selection option. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Enabled loading local/data/app images in markdown with sizing hints. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added external extension reload via x-cmdpal://reload (configurable). Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Instant WebSearch history updates with in-memory store & events. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added keep-after-paste option and safe delete with confirmation for Clipboard History. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
|
||||
### Environment Variables
|
||||
- Replaced custom window chrome with WinUI TitleBar for cleaner, maintainable Environment Variables UI.
|
||||
|
||||
### File Locksmith
|
||||
- Adopted WinUI TitleBar to simplify window chrome while preserving appearance.
|
||||
|
||||
### Find My Mouse
|
||||
- Activating Find My Mouse no longer makes the cursor change to the busy (hourglass) icon or steals focus from your active application.
|
||||
- Added transparent spotlight support with separate backdrop opacity; migrated to Windows App SDK composition APIs.
|
||||
|
||||
### Hosts File Editor
|
||||
- Added customizable backup settings allowing users to configure backup frequency, location, and auto-deletion policies. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
|
||||
### Image Resizer
|
||||
- Fixed settings consistency during batch resize operations by capturing settings once before processing. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Migrated to native WinUI TitleBar for cleaner, maintainable window chrome.
|
||||
|
||||
### Light Switch
|
||||
- Introduced new UI to allow users to manually enter their latitude and longitude in Sunrise to Sunset mode.
|
||||
- Refactored service with cleaner state management for stability.
|
||||
- Removed logs from every tick, only logging key events to largely reduce log size.
|
||||
- Introduced as a brand-new PowerToy module.
|
||||
- Automatically switches between light and dark themes.
|
||||
- Supports time-based scheduling or location-based sunrise/sunset switching.
|
||||
- Supports using a keyboard shortcut to force a change.
|
||||
- Supports filtering changes for Apps and/or System Theme.
|
||||
|
||||
### Mouse Pointer Crosshairs
|
||||
- Enabled switching between Mouse Pointer Crosshairs and Gliding Cursor modes. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
- Added Esc key to cancel active gliding cursor sequence. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
- Added orientation option (vertical / horizontal / both) for crosshairs customization. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
|
||||
### Mouse Without Borders
|
||||
- Added horizontal scrolling support. Thanks [@MasonBergstrom](https://github.com/MasonBergstrom)!
|
||||
- Continued Common class refactor (part 5/7) by extracting clipboard and init/cleanup logic into focused classes. Thanks [@mikeclayton](https://github.com/mikeclayton)!
|
||||
|
||||
- Fix connection failures caused by conflicting MachineId across machines. Thanks [@noraa-junker](https://github.com/noraa-junker) for troubleshooting!
|
||||
|
||||
### Peek
|
||||
- Fixed media files remaining locked after preview window closes. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Added a command-line interface for file previewing. See the [Peek documentation](https://learn.microsoft.com/windows/powertoys/peek) for usage. Thanks [@prochan2](https://github.com/prochan2)!
|
||||
- Added the option to activate Peek with just the Spacebar.
|
||||
|
||||
### PowerRename
|
||||
- PowerRename no longer crashes due to a missing resources file.
|
||||
- Added photo metadata extraction support using EXIF and XMP for pattern-based renaming with camera info, GPS coordinates, and date taken. See [PowerRename Documentation](https://learn.microsoft.com/en-us/windows/powertoys/powerrename).
|
||||
- Fixed enumeration counter skipping when regex replacement equals original filename (counters now advance reliably). Thanks [@daverayment](https://github.com/daverayment)!
|
||||
|
||||
### PowerToys Run
|
||||
- Added retry logic with exponential backoff to handle DWM composition errors during theme changes. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Updated OneNote icons to reflect new Microsoft 365 design. Thanks [@trevorNgo](https://github.com/trevorNgo)!
|
||||
### Quick Accent
|
||||
- Expanded Welsh layout with acute, grave, and dieresis variants for vowels (consistent ordering). Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
|
||||
### Quick Accent
|
||||
- Added diameter symbol (⌀) for Shift+O in Special Characters mode, thanks to [@anselumjuju](https://github.com/anselumjuju)!
|
||||
### Registry Preview
|
||||
- Migrated to native TitleBar and AppWindow APIs for cleaner window chrome.
|
||||
|
||||
### Zoomit
|
||||
- Smoothed out zoom-animation in ZoomIt by coalescing mouse-move and timer events, thanks to [@foxmsft](https://github.com/foxmsft)!
|
||||
- Enabled GIF support for ZoomIt, thanks to [@MarioHewardt](https://github.com/MarioHewardt)!
|
||||
- Fixed spelling mistakes, and refactored some literal strings to string constants, thanks to [@lzandman](https://github.com/lzandman)!
|
||||
- Fixed inaccurate "actual size" screenshots in ZoomIt and resolves a GDI handle leak, improving capture fidelity and long-session stability. thanks to [@daverayment](https://github.com/daverayment)!
|
||||
### Screen Ruler
|
||||
- Fixed ARM64 crash by aligning cursor position structure to 8-byte boundary.
|
||||
|
||||
### Settings
|
||||
- Fixed title bar overlapping issue at smaller window sizes.
|
||||
- Refined shortcut control visual design with improved consistency and spacing.
|
||||
- Added dashboard utilities sorting by name or status.
|
||||
- Made update notification InfoBar in flyout clickable for direct navigation to update page.
|
||||
- Expanded installation instructions by default in README.
|
||||
- Improved accessibility for shortcut conflict button with static resource-based automation properties.
|
||||
- Added ScrollViewer to Command Palette page in PowerToys Settings. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed module list glitches and Sort Status checkmark issue. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Added ability to ignore specific hotkey conflicts to reduce noise.
|
||||
- Stopped creating backup directory during dry-run status checks (cleaner first-run).
|
||||
- Standardized casing and localization for ZoomIt and modules header.
|
||||
- Improved search results page accessibility and conditional module grouping.
|
||||
|
||||
### Development
|
||||
- Fixed accessibility by associating controls with labels for screen readers.
|
||||
- Added accessible name to Shortcut Conflicts button for screen readers.
|
||||
- Excluded TitleBars from tab navigation across multiple utilities. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Migrated build infrastructure from Windows Server 2019 to Server 2022 with improved failure logging and predictable NuGet package paths.
|
||||
- Configured build agents to use larger P: drive for release builds to address disk space constraints.
|
||||
- Enhanced DSC v3 support by organizing resource manifests in a dedicated subfolder with PATH configuration.
|
||||
- Reduced installer bundle size by 6-7MB through centralized Hybrid CRT configuration across all C++ projects.
|
||||
- Updated .NET packages to version 9.0.10 for security fixes. Thanks [@snickler](https://github.com/snickler)!
|
||||
- Fixed spell check dictionary entries for consistency.
|
||||
- Restored accidentally deleted NuGet configuration file for Command Palette extensions.
|
||||
- Fixed package identity build by updating AppxManifest entry points to use PowerShell Core.
|
||||
- Optimized CI pipeline by replacing file copy operations with hard links and moves, reducing build time and disk usage by 10-15GB.
|
||||
- Updated Copilot guidance and PR prompt workflow.
|
||||
- Included high-volume bugs in issue template header. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed incorrect HRESULT logging for inner exceptions. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Introduced shared sparse package identity for PowerToys Win32 components to enable access to Windows platform APIs.
|
||||
- Consolidated installer builds to produce both machine and user installers simultaneously, reducing build time and complexity.
|
||||
- Migrated exclusively to WiX v5 installer infrastructure, removing legacy WiX v3 support.
|
||||
- Temporarily removed PowerToys installer path from PATH environment variable to prevent application crashes.
|
||||
- Added complete OCR UI test coverage with automated tests for activation, settings, language selection, and text extraction.
|
||||
- Fixed test input for drive path normalization in bookmark resolver unit tests.
|
||||
- Fixed Peek UI tests by restoring Ctrl+Space activation shortcut for test scenarios.
|
||||
- Hided apps in PowerToys.SpareApps package from Start Menu. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
### ZoomIt
|
||||
- Updated resource file to reflect standalone v9.01 and current copyright year. Thanks [@foxmsft](https://github.com/foxmsft)!
|
||||
- Restored legacy draw/snipping behaviors and fixed recording race conditions. Thanks [@chakrik73](https://github.com/chakrik73)!
|
||||
- Added smooth image option for improved zoom quality using GDI+ for static zoom and Magnifier API for live zoom. Thanks [@markrussinovich](https://github.com/markrussinovich)!
|
||||
|
||||
### Documentation
|
||||
- New Microsoft Learn documentation for the Light Switch module.
|
||||
- New dev docs for the Light Switch module.
|
||||
|
||||
### Development (Area-Build & Area-Tests)
|
||||
- Allowed debug launches to continue when modules fail to load, speeding developer iteration.
|
||||
- Fixed spell checker dictionary entry (advapi) to eliminate false error.
|
||||
- Added VS Code development guide and launch configs to streamline cross-editor workflows.
|
||||
- Upgraded Windows App SDK and related dependencies to 1.8 for newer platform features.
|
||||
- Rewrote YAML comment to resolve new spell checker forbidden pattern. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Corrected solution structure by returning misplaced Common project, reducing build confusion.
|
||||
- Modernized build scripts with shared helpers and VS environment autodetection for simpler CLI builds.
|
||||
- Standardized build scripts and platform detection to improve reliability and reuse.
|
||||
- Added missing Command Palette version bump to align module release cadence.
|
||||
- Added EXECUTEDEFAULT term to dictionary to prevent regression build failures. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Introduced nightly pre-warm pipeline and configurable MSBuild cache mode to improve CI performance.
|
||||
- Resolved CI forbidden pattern spelling complaint to keep pipelines green.
|
||||
- Added AI contributor instruction set to clarify code area expectations.
|
||||
- Added accessibility IDs to settings and FancyZones toggles, stabilizing UI tests.
|
||||
- Added automatic log collection on UI test failures to speed root cause analysis.
|
||||
- Stabilized Mouse Utils tests by switching to AccessibilityId selectors.
|
||||
- Added Screen Ruler UI test coverage to validate core measurement workflows.
|
||||
|
||||
## 🛣️ Roadmap
|
||||
We are planning some nice new features and improvements for the next releases – a revamped Keyboard Manager UI, custom endpoint and local model support for Advanced Paste, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.96][github-next-release-work]!
|
||||
|
||||
1
deps/cziplib
vendored
Submodule
1
deps/cziplib
vendored
Submodule
Submodule deps/cziplib added at 81314fff0a
@@ -33,12 +33,9 @@ The **Light Switch** module lets users automatically transition between light an
|
||||
|
||||
> **Note:** Using the shortcut overrides the current schedule until the next transition event.
|
||||
|
||||
* **LightSwitchService.cpp**
|
||||
is the heart beat of the module. Controls ticking every minute and depending on user actions (manual override, settings changing, etc) triggers the state manager to perform the corresponding operation.
|
||||
|
||||
* **LightSwitchStateManager.cpp**
|
||||
handles updating the state based on the signals sent by LightSwitchService.
|
||||
|
||||
* **LightSwitchService**
|
||||
Reads settings and applies theming. Runs a check every minute to ensure the state is correct.
|
||||
|
||||
* **SettingsXAML/LightSwitch**
|
||||
Provides the settings UI for configuring schedules, syncing location, and customizing shortcuts.
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
||||
LTEXT "ZoomIt v9.21",IDC_VERSION,42,7,73,10
|
||||
LTEXT "ZoomIt v9.20",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
|
||||
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
|
||||
"SysLink",WS_TABSTOP,42,26,150,9
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Services;
|
||||
@@ -28,12 +27,6 @@ public interface IExtensionService
|
||||
|
||||
void DisableExtension(string extensionUniqueId);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies all extensions that support the HostSettings capability about a settings change.
|
||||
/// </summary>
|
||||
/// <param name="settings">The updated host settings.</param>
|
||||
void NotifyHostSettingsChanged(IHostSettings settings);
|
||||
|
||||
///// <summary>
|
||||
///// Gets a boolean indicating whether the extension was disabled due to the corresponding Windows optional feature
|
||||
///// being absent from the machine or in an unknown state.
|
||||
|
||||
@@ -110,10 +110,4 @@ public interface IExtensionWrapper
|
||||
/// <returns>Nullable instance of the provider</returns>
|
||||
Task<IEnumerable<T>> GetListOfProvidersAsync<T>()
|
||||
where T : class;
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the extension that host settings have changed.
|
||||
/// </summary>
|
||||
/// <param name="settings">The updated host settings.</param>
|
||||
void NotifyHostSettingsChanged(IHostSettings settings);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CmdPal.Core.Common;
|
||||
using Microsoft.CmdPal.Core.Common.Services;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
@@ -14,12 +13,6 @@ namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
public abstract partial class AppExtensionHost : IExtensionHost
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a function that returns the current IHostSettings.
|
||||
/// This is set by the application to provide settings to extensions.
|
||||
/// </summary>
|
||||
public static Func<IHostSettings?>? GetHostSettingsFunc { get; set; }
|
||||
|
||||
private static readonly GlobalLogPageContext _globalLogPageContext = new();
|
||||
|
||||
private static ulong _hostingHwnd;
|
||||
|
||||
@@ -214,29 +214,10 @@ public sealed class CommandProviderWrapper
|
||||
Logger.LogDebug($"Provider supports {apiExtensions.Length} extensions");
|
||||
foreach (var a in apiExtensions)
|
||||
{
|
||||
if (a is IExtendedAttributesProvider)
|
||||
if (a is IExtendedAttributesProvider command2)
|
||||
{
|
||||
Logger.LogDebug($"{ProviderId}: Found an IExtendedAttributesProvider");
|
||||
}
|
||||
|
||||
if (a is IHostSettingsChanged handler)
|
||||
{
|
||||
Logger.LogDebug($"{ProviderId}: Found an IHostSettingsChanged, sending initial settings");
|
||||
|
||||
// Send initial settings to the extension
|
||||
try
|
||||
{
|
||||
var settings = AppExtensionHost.GetHostSettingsFunc?.Invoke();
|
||||
if (settings != null)
|
||||
{
|
||||
handler.OnHostSettingsChanged(settings);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogDebug($"Failed to send initial settings to {ProviderId}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Provides conversion between SettingsModel and IHostSettings for extension communication.
|
||||
/// </summary>
|
||||
public static class HostSettingsConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a SettingsModel to an IHostSettings object that can be passed to extensions.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings model to convert.</param>
|
||||
/// <returns>An IHostSettings object with the current settings values.</returns>
|
||||
public static IHostSettings ToHostSettings(this SettingsModel settings)
|
||||
{
|
||||
return new HostSettings
|
||||
{
|
||||
Hotkey = settings.Hotkey?.ToString() ?? string.Empty,
|
||||
ShowAppDetails = settings.ShowAppDetails,
|
||||
HotkeyGoesHome = settings.HotkeyGoesHome,
|
||||
BackspaceGoesBack = settings.BackspaceGoesBack,
|
||||
SingleClickActivates = settings.SingleClickActivates,
|
||||
HighlightSearchOnActivate = settings.HighlightSearchOnActivate,
|
||||
ShowSystemTrayIcon = settings.ShowSystemTrayIcon,
|
||||
IgnoreShortcutWhenFullscreen = settings.IgnoreShortcutWhenFullscreen,
|
||||
DisableAnimations = settings.DisableAnimations,
|
||||
SummonOn = ConvertSummonTarget(settings.SummonOn),
|
||||
};
|
||||
}
|
||||
|
||||
private static SummonTarget ConvertSummonTarget(MonitorBehavior behavior)
|
||||
{
|
||||
return behavior switch
|
||||
{
|
||||
MonitorBehavior.ToMouse => SummonTarget.ToMouse,
|
||||
MonitorBehavior.ToPrimary => SummonTarget.ToPrimary,
|
||||
MonitorBehavior.ToFocusedWindow => SummonTarget.ToFocusedWindow,
|
||||
MonitorBehavior.InPlace => SummonTarget.InPlace,
|
||||
MonitorBehavior.ToLast => SummonTarget.ToLast,
|
||||
_ => SummonTarget.ToMouse,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -400,29 +400,6 @@ public partial class ExtensionService : IExtensionService, IDisposable
|
||||
_enabledExtensions.Remove(extension.First());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies all extensions about a settings change.
|
||||
/// </summary>
|
||||
/// <param name="settings">The updated host settings.</param>
|
||||
public void NotifyHostSettingsChanged(IHostSettings settings)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var extensions = await GetInstalledExtensionsAsync();
|
||||
foreach (var extension in extensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
extension.NotifyHostSettingsChanged(settings);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to notify extension {extension.ExtensionUniqueId} of settings change", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
///// <inheritdoc cref="IExtensionService.DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper)"/>
|
||||
//public async Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension)
|
||||
|
||||
@@ -12,9 +12,6 @@ using Windows.Win32;
|
||||
using Windows.Win32.System.Com;
|
||||
using WinRT;
|
||||
|
||||
#pragma warning disable IDE0005 // Using directive is unnecessary - needed for HostSettings
|
||||
#pragma warning restore IDE0005
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
|
||||
public class ExtensionWrapper : IExtensionWrapper
|
||||
@@ -204,37 +201,4 @@ public class ExtensionWrapper : IExtensionWrapper
|
||||
public void AddProviderType(ProviderType providerType) => _providerTypes.Add(providerType);
|
||||
|
||||
public bool HasProviderType(ProviderType providerType) => _providerTypes.Contains(providerType);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the extension that host settings have changed.
|
||||
/// </summary>
|
||||
/// <param name="settings">The updated host settings.</param>
|
||||
public void NotifyHostSettingsChanged(IHostSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var provider = GetExtensionObject()?.GetProvider(ProviderType.Commands);
|
||||
if (provider is not ICommandProvider2 provider2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the IHostSettingsChanged handler from GetApiExtensionStubs().
|
||||
// This pattern works reliably in OOP scenarios because the stub objects
|
||||
// are properly marshalled across the process boundary.
|
||||
var apiExtensions = provider2.GetApiExtensionStubs();
|
||||
foreach (var stub in apiExtensions)
|
||||
{
|
||||
if (stub is IHostSettingsChanged handler)
|
||||
{
|
||||
handler.OnHostSettingsChanged(settings);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogDebug($"Failed to notify {ExtensionDisplayName} of settings change: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,23 +160,10 @@ public partial class App : Application
|
||||
services.AddSingleton(sm);
|
||||
var state = AppStateModel.LoadState();
|
||||
services.AddSingleton(state);
|
||||
var extensionService = new ExtensionService();
|
||||
services.AddSingleton<IExtensionService>(extensionService);
|
||||
services.AddSingleton<IExtensionService, ExtensionService>();
|
||||
services.AddSingleton<TrayIconService>();
|
||||
services.AddSingleton<IRunHistoryService, RunHistoryService>();
|
||||
|
||||
// Set up host settings for extensions
|
||||
AppExtensionHost.GetHostSettingsFunc = () => sm.ToHostSettings();
|
||||
|
||||
// Subscribe to settings changes and notify extensions
|
||||
sm.SettingsChanged += (sender, _) =>
|
||||
{
|
||||
if (sender is SettingsModel settings)
|
||||
{
|
||||
extensionService.NotifyHostSettingsChanged(settings.ToHostSettings());
|
||||
}
|
||||
};
|
||||
|
||||
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
|
||||
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
|
||||
services.AddSingleton<ITelemetryService, TelemetryForwarder>();
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
"nativeDebugging": false,
|
||||
"doNotLaunchApp": false
|
||||
},
|
||||
"Microsoft.CmdPal.UI (Package) + Native debugging": {
|
||||
"commandName": "MsixPackage",
|
||||
"nativeDebugging": true,
|
||||
"doNotLaunchApp": false
|
||||
},
|
||||
"Microsoft.CmdPal.UI (Unpackaged)": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
|
||||
@@ -1,442 +0,0 @@
|
||||
---
|
||||
author: Yu Leng
|
||||
created on: 2025-01-24
|
||||
last updated: 2025-01-24
|
||||
---
|
||||
|
||||
# Extension Host Settings API
|
||||
|
||||
## Abstract
|
||||
|
||||
This document describes the Host Settings API, which allows Command Palette extensions to access and respond to host application settings. Extensions can read settings like hotkey configuration, UI preferences, and behavior options, enabling them to adapt their behavior and display relevant information to users.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Background](#background)
|
||||
- [Problem Statement](#problem-statement)
|
||||
- [User Scenarios](#user-scenarios)
|
||||
- [Design Goals](#design-goals)
|
||||
- [High-Level Design](#high-level-design)
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
- [Key Design Decisions](#key-design-decisions)
|
||||
- [Detailed Design](#detailed-design)
|
||||
- [Data Flow](#data-flow)
|
||||
- [Cross-Process Communication](#cross-process-communication)
|
||||
- [Implementation Files](#implementation-files)
|
||||
- [API Reference](#api-reference)
|
||||
- [WinRT Interfaces](#winrt-interfaces)
|
||||
- [Toolkit Classes](#toolkit-classes)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Future Considerations](#future-considerations)
|
||||
|
||||
## Background
|
||||
|
||||
### Problem Statement
|
||||
|
||||
Command Palette extensions run in separate processes (out-of-process/OOP) from the host application for security and stability. However, some extensions need to be aware of the host's configuration to provide a better user experience. For example:
|
||||
|
||||
1. An extension displaying keyboard shortcuts needs to know the current hotkey setting
|
||||
2. An extension with animations should respect the "Disable Animations" preference
|
||||
3. A diagnostic extension might want to display all current settings for troubleshooting
|
||||
|
||||
Without a formal API, extensions have no way to access this information, limiting their ability to integrate seamlessly with the host application.
|
||||
|
||||
### User Scenarios
|
||||
|
||||
**Scenario 1: Settings Display Extension**
|
||||
A developer creates a diagnostic page that displays all current Command Palette settings. This helps users understand their configuration and troubleshoot issues.
|
||||
|
||||
**Scenario 2: Adaptive UI Extension**
|
||||
An extension with rich animations checks the `DisableAnimations` setting and disables its own animations when the user prefers reduced motion.
|
||||
|
||||
**Scenario 3: Keyboard Shortcut Helper**
|
||||
An extension that teaches keyboard shortcuts displays the user's configured hotkey so instructions match their actual configuration.
|
||||
|
||||
### Design Goals
|
||||
|
||||
1. **Cross-Process Safety**: Work reliably across the OOP boundary between host and extension
|
||||
2. **Real-Time Updates**: Extensions receive notifications when settings change
|
||||
3. **AOT Compatibility**: All code must be compatible with Ahead-of-Time compilation
|
||||
4. **Minimal API Surface**: Simple, focused interfaces that are easy to use
|
||||
5. **Backward Compatibility**: Old extensions gracefully ignore new capabilities; old hosts don't break new extensions
|
||||
|
||||
## High-Level Design
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Host (CmdPal.UI) │
|
||||
│ │
|
||||
│ SettingsModel ──(SettingsChanged)──► ExtensionService │
|
||||
│ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ ExtensionWrapper │
|
||||
│ │ │ │
|
||||
│ │ GetApiExtensionStubs() │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┴──────────┐ │
|
||||
│ │ ▼ ▼ │
|
||||
│ │ IExtendedAttributesProvider IHostSettingsChanged
|
||||
│ │ │ │
|
||||
│ └──(initial settings)───────────────────────────┤ │
|
||||
│ via CommandProviderWrapper │ │
|
||||
└───────────────────────────────────────────────────────│─────────┘
|
||||
OOP Boundary │
|
||||
┌───────────────────────────────────────────────────────│─────────┐
|
||||
│ Extension Process │ │
|
||||
│ ▼ │
|
||||
│ CommandProvider │
|
||||
│ │ │
|
||||
│ ├── GetApiExtensionStubs() returns: │
|
||||
│ │ • SupportCommandsWithProperties │
|
||||
│ │ • HostSettingsChangedHandler ◄─────────────────┐ │
|
||||
│ │ │ │
|
||||
│ └── OnHostSettingsChanged(settings) ◄─────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ HostSettingsManager.Update(settings) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ SettingsChanged event │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Extension Pages (e.g., HostSettingsPage) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
#### 1. Cross-Process Interface Detection via GetApiExtensionStubs
|
||||
|
||||
**Problem:** The `is` operator for interface detection doesn't work reliably across process boundaries in WinRT/COM scenarios. When the host tries to check `if (provider is IHostSettingsChanged)`, it fails because the interface isn't in the direct inheritance chain of the proxy object.
|
||||
|
||||
**Solution:** Use the `GetApiExtensionStubs()` pattern. The extension returns stub objects that are properly marshalled across the process boundary, allowing reliable interface detection on the host side.
|
||||
|
||||
```csharp
|
||||
// In CommandProvider.cs (Toolkit)
|
||||
public object[] GetApiExtensionStubs()
|
||||
{
|
||||
return [
|
||||
new SupportCommandsWithProperties(), // For IExtendedAttributesProvider
|
||||
new HostSettingsChangedHandler(this) // For IHostSettingsChanged
|
||||
];
|
||||
}
|
||||
|
||||
// In ExtensionWrapper.cs (Host)
|
||||
foreach (var stub in provider2.GetApiExtensionStubs())
|
||||
{
|
||||
if (stub is IHostSettingsChanged handler) // This works across OOP!
|
||||
{
|
||||
handler.OnHostSettingsChanged(settings);
|
||||
return;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Unified Settings Delivery
|
||||
|
||||
Both initial settings and change notifications use the same `OnHostSettingsChanged` path:
|
||||
|
||||
- **Initial Settings:** Sent by `CommandProviderWrapper.UnsafePreCacheApiAdditions()` when discovering the handler during extension startup
|
||||
- **Change Notifications:** Sent by `ExtensionWrapper.NotifyHostSettingsChanged()` when the user modifies settings
|
||||
|
||||
This simplifies the design by having a single code path for settings delivery.
|
||||
|
||||
#### 3. Capability Detection via ICommandProvider2
|
||||
|
||||
Use `is ICommandProvider2` as the capability gate. If an extension implements `ICommandProvider2`, it's using the modern toolkit and may support host settings (if it provides an `IHostSettingsChanged` stub).
|
||||
|
||||
```csharp
|
||||
if (provider is ICommandProvider2 provider2)
|
||||
{
|
||||
// Extension uses modern toolkit, check for settings support
|
||||
var stubs = provider2.GetApiExtensionStubs();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Data Flow
|
||||
|
||||
#### Extension Startup (Initial Settings)
|
||||
|
||||
```
|
||||
Extension starts
|
||||
└─► CommandProviderWrapper created
|
||||
└─► LoadTopLevelCommands()
|
||||
└─► UnsafePreCacheApiAdditions(provider2)
|
||||
└─► GetApiExtensionStubs()
|
||||
└─► Find IHostSettingsChanged handler
|
||||
└─► handler.OnHostSettingsChanged(currentSettings)
|
||||
└─► HostSettingsManager.Update(settings)
|
||||
```
|
||||
|
||||
#### Settings Change Notification
|
||||
|
||||
```
|
||||
User changes settings in CmdPal
|
||||
└─► SettingsModel.SettingsChanged event
|
||||
└─► ExtensionService.NotifyHostSettingsChanged(settings)
|
||||
└─► foreach extension:
|
||||
└─► ExtensionWrapper.NotifyHostSettingsChanged(settings)
|
||||
└─► GetApiExtensionStubs()
|
||||
└─► Find IHostSettingsChanged handler
|
||||
└─► handler.OnHostSettingsChanged(settings)
|
||||
└─► HostSettingsManager.Update(settings)
|
||||
└─► SettingsChanged?.Invoke()
|
||||
```
|
||||
|
||||
### Cross-Process Communication
|
||||
|
||||
The settings are passed as an `IHostSettings` WinRT interface, which is automatically marshalled by the WinRT runtime across the process boundary. The host creates a `HostSettings` object (via `HostSettingsConverter.ToHostSettings()`), and the extension receives a proxy that implements the same interface.
|
||||
|
||||
Key considerations:
|
||||
|
||||
1. **No Reflection:** All type checking uses WinRT's native QueryInterface mechanism
|
||||
2. **AOT Safe:** All types are known at compile time
|
||||
3. **Error Handling:** All cross-process calls are wrapped in try-catch to handle extension crashes gracefully
|
||||
|
||||
### Implementation Files
|
||||
|
||||
#### Host Side
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `App.xaml.cs` | Subscribes to `SettingsModel.SettingsChanged`, sets up `GetHostSettingsFunc` |
|
||||
| `AppExtensionHost.cs` | Provides `GetHostSettingsFunc` static property for settings access |
|
||||
| `HostSettingsConverter.cs` | Extension method to convert `SettingsModel` to `IHostSettings` |
|
||||
| `ExtensionService.cs` | Broadcasts settings changes to all extensions via `NotifyHostSettingsChanged()` |
|
||||
| `ExtensionWrapper.cs` | Sends settings to individual extension via `NotifyHostSettingsChanged()` |
|
||||
| `CommandProviderWrapper.cs` | Sends initial settings when discovering `IHostSettingsChanged` handler |
|
||||
|
||||
#### Extension/Toolkit Side
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `CommandProvider.cs` | Base class providing `IHostSettingsChanged` stub via `GetApiExtensionStubs()` |
|
||||
| `HostSettingsManager.cs` | Static manager with `Current` property and `SettingsChanged` event |
|
||||
| `HostSettings.cs` | Toolkit implementation of `IHostSettings` interface |
|
||||
|
||||
## API Reference
|
||||
|
||||
### WinRT Interfaces
|
||||
|
||||
#### IHostSettings
|
||||
|
||||
Represents the host application settings that can be passed to extensions.
|
||||
|
||||
```idl
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IHostSettings
|
||||
{
|
||||
String Hotkey { get; };
|
||||
Boolean ShowAppDetails { get; };
|
||||
Boolean HotkeyGoesHome { get; };
|
||||
Boolean BackspaceGoesBack { get; };
|
||||
Boolean SingleClickActivates { get; };
|
||||
Boolean HighlightSearchOnActivate { get; };
|
||||
Boolean ShowSystemTrayIcon { get; };
|
||||
Boolean IgnoreShortcutWhenFullscreen { get; };
|
||||
Boolean DisableAnimations { get; };
|
||||
SummonTarget SummonOn { get; };
|
||||
}
|
||||
```
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `Hotkey` | String | The keyboard shortcut to activate Command Palette (e.g., "Alt+Space") |
|
||||
| `ShowAppDetails` | Boolean | Whether to show application details in the UI |
|
||||
| `HotkeyGoesHome` | Boolean | Whether pressing the hotkey returns to the home page |
|
||||
| `BackspaceGoesBack` | Boolean | Whether backspace navigates back when search is empty |
|
||||
| `SingleClickActivates` | Boolean | Whether single-click activates items (vs double-click) |
|
||||
| `HighlightSearchOnActivate` | Boolean | Whether to highlight search text on activation |
|
||||
| `ShowSystemTrayIcon` | Boolean | Whether to show the system tray icon |
|
||||
| `IgnoreShortcutWhenFullscreen` | Boolean | Whether to ignore the hotkey when a fullscreen app is active |
|
||||
| `DisableAnimations` | Boolean | Whether animations are disabled |
|
||||
| `SummonOn` | SummonTarget | Where to position the window when summoned |
|
||||
|
||||
#### SummonTarget
|
||||
|
||||
Enum representing the position behavior when summoning Command Palette.
|
||||
|
||||
```idl
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
enum SummonTarget
|
||||
{
|
||||
ToMouse = 0,
|
||||
ToPrimary = 1,
|
||||
ToFocusedWindow = 2,
|
||||
InPlace = 3,
|
||||
ToLast = 4,
|
||||
};
|
||||
```
|
||||
|
||||
#### IHostSettingsChanged
|
||||
|
||||
Interface for extensions to receive settings change notifications.
|
||||
|
||||
```idl
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IHostSettingsChanged
|
||||
{
|
||||
void OnHostSettingsChanged(IHostSettings settings);
|
||||
}
|
||||
```
|
||||
|
||||
### Toolkit Classes
|
||||
|
||||
#### HostSettingsManager
|
||||
|
||||
Static class providing access to current host settings and change notifications.
|
||||
|
||||
```csharp
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public static class HostSettingsManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when the host settings have changed.
|
||||
/// </summary>
|
||||
public static event Action? SettingsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current host settings, or null if not yet initialized.
|
||||
/// </summary>
|
||||
public static IHostSettings? Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether host settings are available.
|
||||
/// </summary>
|
||||
public static bool IsAvailable { get; }
|
||||
}
|
||||
```
|
||||
|
||||
#### CommandProvider Virtual Method
|
||||
|
||||
Extensions can override this method for custom settings handling:
|
||||
|
||||
```csharp
|
||||
public abstract partial class CommandProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when host settings change. Override to handle settings changes.
|
||||
/// The default implementation updates HostSettingsManager.
|
||||
/// </summary>
|
||||
public virtual void OnHostSettingsChanged(IHostSettings settings)
|
||||
{
|
||||
HostSettingsManager.Update(settings);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Reading Settings in a Page
|
||||
|
||||
```csharp
|
||||
public class MyPage : ListPage
|
||||
{
|
||||
public MyPage()
|
||||
{
|
||||
// Subscribe to changes
|
||||
HostSettingsManager.SettingsChanged += () => RaiseItemsChanged();
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
var settings = HostSettingsManager.Current;
|
||||
if (settings == null)
|
||||
{
|
||||
return [new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = "Settings not available"
|
||||
}];
|
||||
}
|
||||
|
||||
return [
|
||||
new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = $"Hotkey: {settings.Hotkey}"
|
||||
},
|
||||
new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = $"Animations: {(settings.DisableAnimations ? "Off" : "On")}"
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Settings Handler in CommandProvider
|
||||
|
||||
```csharp
|
||||
public class MyCommandProvider : CommandProvider
|
||||
{
|
||||
public override void OnHostSettingsChanged(IHostSettings settings)
|
||||
{
|
||||
base.OnHostSettingsChanged(settings); // Update HostSettingsManager
|
||||
|
||||
// Custom logic
|
||||
if (settings.DisableAnimations)
|
||||
{
|
||||
DisableMyAnimations();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Settings Availability
|
||||
|
||||
```csharp
|
||||
public void DoSomething()
|
||||
{
|
||||
if (!HostSettingsManager.IsAvailable)
|
||||
{
|
||||
// Running with old host, use defaults
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = HostSettingsManager.Current!;
|
||||
// Use settings...
|
||||
}
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
### Extension Compatibility
|
||||
|
||||
| Extension Type | Behavior |
|
||||
|----------------|----------|
|
||||
| Old extensions (no ICommandProvider2) | Won't receive settings, gracefully skipped |
|
||||
| Extensions without IHostSettingsChanged stub | Won't receive settings, gracefully skipped |
|
||||
| New extensions with IHostSettingsChanged | Receive initial settings and change notifications |
|
||||
|
||||
### Host Compatibility
|
||||
|
||||
| Host Version | Behavior |
|
||||
|--------------|----------|
|
||||
| Old hosts (no settings support) | `HostSettingsManager.Current` will be null |
|
||||
| New hosts | Full settings support |
|
||||
|
||||
Extensions should always check `HostSettingsManager.IsAvailable` or handle null `Current` values.
|
||||
|
||||
## Future Considerations
|
||||
|
||||
### Adding New Settings
|
||||
|
||||
To add a new setting property:
|
||||
|
||||
1. Add property to `IHostSettings` interface in IDL
|
||||
2. Add property to `HostSettings.cs` class in Toolkit
|
||||
3. Update `HostSettingsConverter.ToHostSettings()` to include the new property
|
||||
|
||||
### Extension-Specific Settings
|
||||
|
||||
Future versions could allow extensions to register their own settings that the host would store and provide back, enabling a unified settings experience.
|
||||
|
||||
### Bi-Directional Settings
|
||||
|
||||
Currently, settings flow one-way from host to extension. A future enhancement could allow extensions to request settings changes (with user consent).
|
||||
@@ -74,7 +74,6 @@ functionality.
|
||||
- [Settings helpers](#settings-helpers)
|
||||
- [Advanced scenarios](#advanced-scenarios)
|
||||
- [Status messages](#status-messages)
|
||||
- [Host Settings](#host-settings)
|
||||
- [Rendering of ICommandItems in Lists and Menus](#rendering-of-icommanditems-in-lists-and-menus)
|
||||
- [Class diagram](#class-diagram)
|
||||
- [Future considerations](#future-considerations)
|
||||
@@ -1905,160 +1904,6 @@ instance from the host app.
|
||||
to remember that these are x-proc calls, and should be treated asynchronously.
|
||||
Should the other properties be async too?
|
||||
|
||||
### Host Settings
|
||||
|
||||
Extensions may need to be aware of the host application's settings to provide a
|
||||
better user experience. For example, an extension displaying keyboard shortcuts
|
||||
needs to know the current hotkey setting, or an extension with animations should
|
||||
respect the "Disable Animations" preference.
|
||||
|
||||
The Host Settings API allows extensions to read host settings and receive
|
||||
notifications when those settings change. This works across the out-of-process
|
||||
(OOP) boundary using the `GetApiExtensionStubs()` pattern described in
|
||||
[Addenda I](#addenda-i-api-additions-icommandprovider2).
|
||||
|
||||
#### IHostSettings Interface
|
||||
|
||||
The `IHostSettings` interface provides read-only access to the host's current
|
||||
settings:
|
||||
|
||||
```c#
|
||||
enum SummonTarget
|
||||
{
|
||||
ToMouse = 0,
|
||||
ToPrimary = 1,
|
||||
ToFocusedWindow = 2,
|
||||
InPlace = 3,
|
||||
ToLast = 4,
|
||||
};
|
||||
|
||||
interface IHostSettings
|
||||
{
|
||||
String Hotkey { get; };
|
||||
Boolean ShowAppDetails { get; };
|
||||
Boolean HotkeyGoesHome { get; };
|
||||
Boolean BackspaceGoesBack { get; };
|
||||
Boolean SingleClickActivates { get; };
|
||||
Boolean HighlightSearchOnActivate { get; };
|
||||
Boolean ShowSystemTrayIcon { get; };
|
||||
Boolean IgnoreShortcutWhenFullscreen { get; };
|
||||
Boolean DisableAnimations { get; };
|
||||
SummonTarget SummonOn { get; };
|
||||
}
|
||||
```
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `Hotkey` | The keyboard shortcut to activate Command Palette (e.g., "Alt+Space") |
|
||||
| `ShowAppDetails` | Whether to show application details in the UI |
|
||||
| `HotkeyGoesHome` | Whether pressing the hotkey returns to the home page |
|
||||
| `BackspaceGoesBack` | Whether backspace navigates back when search is empty |
|
||||
| `SingleClickActivates` | Whether single-click activates items (vs double-click) |
|
||||
| `HighlightSearchOnActivate` | Whether to highlight search text on activation |
|
||||
| `ShowSystemTrayIcon` | Whether to show the system tray icon |
|
||||
| `IgnoreShortcutWhenFullscreen` | Whether to ignore the hotkey when a fullscreen app is active |
|
||||
| `DisableAnimations` | Whether animations are disabled |
|
||||
| `SummonOn` | Where to position the window when summoned |
|
||||
|
||||
#### IHostSettingsChanged Interface
|
||||
|
||||
Extensions that want to receive settings must provide an `IHostSettingsChanged`
|
||||
stub via the `GetApiExtensionStubs()` pattern:
|
||||
|
||||
```c#
|
||||
interface IHostSettingsChanged
|
||||
{
|
||||
void OnHostSettingsChanged(IHostSettings settings);
|
||||
}
|
||||
```
|
||||
|
||||
The host will call `OnHostSettingsChanged` in two scenarios:
|
||||
1. **Initial settings**: When the extension is first loaded
|
||||
2. **Settings changes**: When the user modifies Command Palette settings
|
||||
|
||||
#### Using Host Settings (Toolkit)
|
||||
|
||||
For extensions using the Toolkit, host settings are automatically managed. The
|
||||
`CommandProvider` base class provides the `IHostSettingsChanged` stub, and
|
||||
settings are cached in `HostSettingsManager`:
|
||||
|
||||
```cs
|
||||
// Access current settings anywhere in your extension
|
||||
var settings = HostSettingsManager.Current;
|
||||
if (settings != null)
|
||||
{
|
||||
var hotkey = settings.Hotkey;
|
||||
var animationsDisabled = settings.DisableAnimations;
|
||||
}
|
||||
|
||||
// Check if settings are available
|
||||
if (HostSettingsManager.IsAvailable)
|
||||
{
|
||||
// Host supports settings
|
||||
}
|
||||
```
|
||||
|
||||
To respond to settings changes, subscribe to the `SettingsChanged` event:
|
||||
|
||||
```cs
|
||||
public class MyPage : ListPage
|
||||
{
|
||||
public MyPage()
|
||||
{
|
||||
// Refresh the page when settings change
|
||||
HostSettingsManager.SettingsChanged += () => RaiseItemsChanged();
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
var settings = HostSettingsManager.Current;
|
||||
if (settings == null)
|
||||
{
|
||||
return [new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = "Settings not available"
|
||||
}];
|
||||
}
|
||||
|
||||
return [
|
||||
new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = $"Current Hotkey: {settings.Hotkey}"
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Custom Settings Handler
|
||||
|
||||
Extensions can override `OnHostSettingsChanged` in their `CommandProvider` to
|
||||
add custom handling logic:
|
||||
|
||||
```cs
|
||||
public class MyCommandProvider : CommandProvider
|
||||
{
|
||||
public override void OnHostSettingsChanged(IHostSettings settings)
|
||||
{
|
||||
base.OnHostSettingsChanged(settings); // Update HostSettingsManager
|
||||
|
||||
// Custom logic
|
||||
if (settings.DisableAnimations)
|
||||
{
|
||||
DisableMyAnimations();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Compatibility
|
||||
|
||||
- **Old extensions** (no `ICommandProvider2`): Won't receive settings, gracefully skipped
|
||||
- **Old hosts** (no settings support): `HostSettingsManager.Current` will be null
|
||||
|
||||
Extensions should always check `HostSettingsManager.IsAvailable` or handle null
|
||||
`Current` values to ensure compatibility with older hosts.
|
||||
|
||||
### Rendering of ICommandItems in Lists and Menus
|
||||
|
||||
When displaying a list item:
|
||||
@@ -2187,31 +2032,19 @@ public partial class SamplePagesCommandsProvider : CommandProvider, ICommandProv
|
||||
|
||||
// Here is where we enable support for future additions to the API
|
||||
public object[] GetApiExtensionStubs() {
|
||||
return [
|
||||
new SupportCommandsWithProperties(),
|
||||
new HostSettingsChangedHandler(this)
|
||||
];
|
||||
return [new SupportCommandsWithProperties()];
|
||||
}
|
||||
private sealed partial class SupportCommandsWithProperties : IExtendedAttributesProvider {
|
||||
public IDictionary<string, object>? GetProperties() => null;
|
||||
}
|
||||
private sealed partial class HostSettingsChangedHandler : IHostSettingsChanged {
|
||||
private readonly SamplePagesCommandsProvider _provider;
|
||||
public HostSettingsChangedHandler(SamplePagesCommandsProvider provider) => _provider = provider;
|
||||
public void OnHostSettingsChanged(IHostSettings settings) => _provider.OnHostSettingsChanged(settings);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This pattern is used for multiple API extensions:
|
||||
- `IExtendedAttributesProvider`: Allows commands to expose additional properties
|
||||
- `IHostSettingsChanged`: Allows extensions to receive host settings updates (see [Host Settings](#host-settings))
|
||||
|
||||
Fortunately, we can put all of that (`GetApiExtensionStubs`,
|
||||
`SupportCommandsWithProperties`, `HostSettingsChangedHandler`) directly in
|
||||
`Toolkit.CommandProvider`, so developers won't have to do anything. The toolkit
|
||||
will just do the right thing for them.
|
||||
`SupportCommandsWithProperties`) directly in `Toolkit.CommandProvider`, so
|
||||
developers won't have to do anything. The toolkit will just do the right thing
|
||||
for them.
|
||||
|
||||
## Class diagram
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace SamplePagesExtension;
|
||||
|
||||
/// <summary>
|
||||
/// A sample page that displays the current Host Settings values.
|
||||
/// This demonstrates how extensions can access and display the Command Palette's global settings.
|
||||
/// </summary>
|
||||
internal sealed partial class HostSettingsPage : ListPage
|
||||
{
|
||||
public HostSettingsPage()
|
||||
{
|
||||
Icon = new IconInfo("\uE713"); // Settings icon
|
||||
Name = "Host Settings";
|
||||
Title = "Current Host Settings";
|
||||
|
||||
// Subscribe to settings changes to refresh the page when settings are updated
|
||||
HostSettingsManager.SettingsChanged += OnSettingsChanged;
|
||||
ExtensionHost.LogMessage($"[HostSettingsPage] Constructor called, subscribed to SettingsChanged");
|
||||
}
|
||||
|
||||
private void OnSettingsChanged()
|
||||
{
|
||||
ExtensionHost.LogMessage($"[HostSettingsPage] OnSettingsChanged called, invoking RaiseItemsChanged");
|
||||
|
||||
// Notify the UI to refresh the items list
|
||||
RaiseItemsChanged();
|
||||
ExtensionHost.LogMessage($"[HostSettingsPage] RaiseItemsChanged completed");
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
ExtensionHost.LogMessage($"[HostSettingsPage] GetItems called");
|
||||
var settings = HostSettingsManager.Current;
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
return [
|
||||
new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = "Host Settings not available",
|
||||
Subtitle = "Settings have not been received from the host yet",
|
||||
Icon = new IconInfo("\uE7BA"), // Warning icon
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
CreateSettingItem("Hotkey", settings.Hotkey, "\uE765"), // Keyboard icon
|
||||
CreateSettingItem("Show App Details", settings.ShowAppDetails, "\uE946"), // View icon
|
||||
CreateSettingItem("Hotkey Goes Home", settings.HotkeyGoesHome, "\uE80F"), // Home icon
|
||||
CreateSettingItem("Backspace Goes Back", settings.BackspaceGoesBack, "\uE72B"), // Back icon
|
||||
CreateSettingItem("Single Click Activates", settings.SingleClickActivates, "\uE8B0"), // Mouse icon
|
||||
CreateSettingItem("Highlight Search On Activate", settings.HighlightSearchOnActivate, "\uE8D6"), // Highlight icon
|
||||
CreateSettingItem("Show System Tray Icon", settings.ShowSystemTrayIcon, "\uE8A5"), // System icon
|
||||
CreateSettingItem("Ignore Shortcut When Fullscreen", settings.IgnoreShortcutWhenFullscreen, "\uE740"), // Fullscreen icon
|
||||
CreateSettingItem("Disable Animations", settings.DisableAnimations, "\uE916"), // Play icon
|
||||
CreateSettingItem("Summon On", settings.SummonOn.ToString(), "\uE7C4"), // Position icon
|
||||
];
|
||||
}
|
||||
|
||||
private static ListItem CreateSettingItem(string name, object value, string iconGlyph)
|
||||
{
|
||||
var displayValue = value switch
|
||||
{
|
||||
bool b => b ? "Enabled" : "Disabled",
|
||||
string s when string.IsNullOrEmpty(s) => "(not set)",
|
||||
_ => value?.ToString() ?? "null",
|
||||
};
|
||||
|
||||
return new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = name,
|
||||
Subtitle = displayValue,
|
||||
Icon = new IconInfo(iconGlyph),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -101,14 +101,6 @@ public partial class SamplesListPage : ListPage
|
||||
Subtitle = "A demo of the settings helpers",
|
||||
},
|
||||
|
||||
// Host Settings
|
||||
new ListItem(new HostSettingsPage())
|
||||
{
|
||||
Title = "Host Settings",
|
||||
Subtitle = "View current Command Palette host settings",
|
||||
Icon = new IconInfo("\uE713"), // Settings icon
|
||||
},
|
||||
|
||||
// Evil edge cases
|
||||
// Anything weird that might break the palette - put that in here.
|
||||
new ListItem(new EvilSamplesPage())
|
||||
|
||||
@@ -6,7 +6,7 @@ using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2, IHostSettingsChanged
|
||||
public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2
|
||||
{
|
||||
public virtual string Id { get; protected set; } = string.Empty;
|
||||
|
||||
@@ -30,16 +30,6 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid
|
||||
|
||||
public virtual void InitializeWithHost(IExtensionHost host) => ExtensionHost.Initialize(host);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the host settings have changed. Override this method to respond to settings changes.
|
||||
/// The base implementation updates the HostSettingsManager cache.
|
||||
/// </summary>
|
||||
/// <param name="settings">The updated host settings.</param>
|
||||
public virtual void OnHostSettingsChanged(IHostSettings settings)
|
||||
{
|
||||
HostSettingsManager.Update(settings);
|
||||
}
|
||||
|
||||
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
|
||||
public virtual void Dispose()
|
||||
{
|
||||
@@ -67,7 +57,7 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid
|
||||
/// <returns>an array of objects that implement all the leaf interfaces we support</returns>
|
||||
public object[] GetApiExtensionStubs()
|
||||
{
|
||||
return [new SupportCommandsWithProperties(), new HostSettingsChangedHandler(this)];
|
||||
return [new SupportCommandsWithProperties()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,18 +69,4 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid
|
||||
{
|
||||
public IDictionary<string, object>? GetProperties() => null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A stub class which implements IHostSettingsChanged. This is marshalled across
|
||||
/// the ABI so that the host can call OnHostSettingsChanged on this object,
|
||||
/// which then delegates to the CommandProvider's OnHostSettingsChanged method.
|
||||
/// </summary>
|
||||
private sealed partial class HostSettingsChangedHandler : IHostSettingsChanged
|
||||
{
|
||||
private readonly CommandProvider _provider;
|
||||
|
||||
public HostSettingsChangedHandler(CommandProvider provider) => _provider = provider;
|
||||
|
||||
public void OnHostSettingsChanged(IHostSettings settings) => _provider.OnHostSettingsChanged(settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
/// <summary>
|
||||
/// Concrete implementation of IHostSettings for the Command Palette host settings.
|
||||
/// This class is used by the host to create settings objects that can be passed to extensions.
|
||||
/// </summary>
|
||||
public partial class HostSettings : IHostSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the global hotkey for summoning the Command Palette.
|
||||
/// </summary>
|
||||
public string Hotkey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show app details in the UI.
|
||||
/// </summary>
|
||||
public bool ShowAppDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether pressing the hotkey goes to the home page.
|
||||
/// </summary>
|
||||
public bool HotkeyGoesHome { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether backspace navigates back.
|
||||
/// </summary>
|
||||
public bool BackspaceGoesBack { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether single click activates items.
|
||||
/// </summary>
|
||||
public bool SingleClickActivates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to highlight the search box on activation.
|
||||
/// </summary>
|
||||
public bool HighlightSearchOnActivate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show the system tray icon.
|
||||
/// </summary>
|
||||
public bool ShowSystemTrayIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to ignore the shortcut when in fullscreen mode.
|
||||
/// </summary>
|
||||
public bool IgnoreShortcutWhenFullscreen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether animations are disabled.
|
||||
/// </summary>
|
||||
public bool DisableAnimations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target monitor behavior when summoning the Command Palette.
|
||||
/// </summary>
|
||||
public SummonTarget SummonOn { get; set; }
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
/// <summary>
|
||||
/// Provides static access to the current Command Palette host settings.
|
||||
/// Extensions can use this class to read the current settings values and respond to changes.
|
||||
/// </summary>
|
||||
public static class HostSettingsManager
|
||||
{
|
||||
private static IHostSettings? _current;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the host settings have changed.
|
||||
/// Extensions can subscribe to this event to refresh their UI when settings are updated.
|
||||
/// </summary>
|
||||
public static event Action? SettingsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current host settings, or null if not yet initialized.
|
||||
/// </summary>
|
||||
public static IHostSettings? Current => _current;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether host settings are available.
|
||||
/// Returns false if the extension is running with an older host that doesn't support settings.
|
||||
/// </summary>
|
||||
public static bool IsAvailable => _current != null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the host settings. Called internally by ExtensionHost during initialization.
|
||||
/// </summary>
|
||||
/// <param name="settings">The initial host settings.</param>
|
||||
internal static void Initialize(IHostSettings settings) => _current = settings;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the cached host settings. Called internally when settings change.
|
||||
/// </summary>
|
||||
/// <param name="settings">The updated host settings.</param>
|
||||
internal static void Update(IHostSettings settings)
|
||||
{
|
||||
_current = settings;
|
||||
#if DEBUG
|
||||
var subscriberCount = SettingsChanged?.GetInvocationList().Length ?? 0;
|
||||
ExtensionHost.LogMessage($"[HostSettingsManager] Update called, subscriber count: {subscriberCount}");
|
||||
#endif
|
||||
SettingsChanged?.Invoke();
|
||||
#if DEBUG
|
||||
ExtensionHost.LogMessage($"[HostSettingsManager] SettingsChanged event invoked");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
[JsonSerializable(typeof(List<Choice>))]
|
||||
[JsonSerializable(typeof(List<ChoiceSetSetting>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
|
||||
[JsonSerializable(typeof(List<Dictionary<string, object>>))]
|
||||
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)]
|
||||
internal sealed partial class JsonSerializationContext : JsonSerializerContext
|
||||
{
|
||||
|
||||
@@ -26,69 +26,15 @@ public sealed class ToggleSetting : Setting<bool>
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
var items = new List<Dictionary<string, object>>();
|
||||
|
||||
if (!string.IsNullOrEmpty(Label))
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
items.Add(
|
||||
new()
|
||||
{
|
||||
{ "type", "TextBlock" },
|
||||
{ "text", Label },
|
||||
{ "wrap", true },
|
||||
});
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(Description) || string.Equals(Description, Label, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
items.Add(
|
||||
new()
|
||||
{
|
||||
{ "type", "TextBlock" },
|
||||
{ "text", Description },
|
||||
{ "isSubtle", true },
|
||||
{ "size", "Small" },
|
||||
{ "spacing", "Small" },
|
||||
{ "wrap", true },
|
||||
});
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
{ "type", "ColumnSet" },
|
||||
{
|
||||
"columns", new List<Dictionary<string, object>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
{ "type", "Column" },
|
||||
{ "width", "20px" },
|
||||
{
|
||||
"items", new List<Dictionary<string, object>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
{ "type", "Input.Toggle" },
|
||||
{ "title", " " },
|
||||
{ "id", Key },
|
||||
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
},
|
||||
}
|
||||
},
|
||||
{ "verticalContentAlignment", "Center" },
|
||||
},
|
||||
new()
|
||||
{
|
||||
{ "type", "Column" },
|
||||
{ "width", "stretch" },
|
||||
{ "items", items },
|
||||
{ "verticalContentAlignment", "Center" },
|
||||
},
|
||||
}
|
||||
},
|
||||
{ "spacing", "Medium" },
|
||||
{ "type", "Input.Toggle" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -391,53 +391,6 @@ namespace Microsoft.CommandPalette.Extensions
|
||||
{
|
||||
Object[] GetApiExtensionStubs();
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Host Settings and Capability System
|
||||
// =========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// Represents the position behavior when summoning the Command Palette.
|
||||
/// </summary>
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
enum SummonTarget
|
||||
{
|
||||
ToMouse = 0,
|
||||
ToPrimary = 1,
|
||||
ToFocusedWindow = 2,
|
||||
InPlace = 3,
|
||||
ToLast = 4,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Interface for host settings that can be passed to extensions across process boundaries.
|
||||
/// Extensions can access these settings to adapt their behavior accordingly.
|
||||
/// The Toolkit provides a concrete HostSettings class that implements this interface.
|
||||
/// </summary>
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IHostSettings
|
||||
{
|
||||
String Hotkey { get; };
|
||||
Boolean ShowAppDetails { get; };
|
||||
Boolean HotkeyGoesHome { get; };
|
||||
Boolean BackspaceGoesBack { get; };
|
||||
Boolean SingleClickActivates { get; };
|
||||
Boolean HighlightSearchOnActivate { get; };
|
||||
Boolean ShowSystemTrayIcon { get; };
|
||||
Boolean IgnoreShortcutWhenFullscreen { get; };
|
||||
Boolean DisableAnimations { get; };
|
||||
SummonTarget SummonOn { get; };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for extensions to receive notifications when host settings change.
|
||||
/// Extensions implementing this interface will be notified whenever the user
|
||||
/// modifies Command Palette settings, including initial settings on startup.
|
||||
/// </summary>
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IHostSettingsChanged
|
||||
{
|
||||
void OnHostSettingsChanged(IHostSettings settings);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
L"PowerToys.MouseJump.dll",
|
||||
L"PowerToys.AlwaysOnTopModuleInterface.dll",
|
||||
L"PowerToys.MousePointerCrosshairs.dll",
|
||||
L"PowerToys.CursorWrap.dll",
|
||||
// L"PowerToys.CursorWrap.dll",
|
||||
L"PowerToys.PowerAccentModuleInterface.dll",
|
||||
L"PowerToys.PowerOCRModuleInterface.dll",
|
||||
L"PowerToys.AdvancedPasteModuleInterface.dll",
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
|
||||
<panels:MouseJumpPanel x:Name="MouseUtils_MouseJump_Panel" x:Uid="MouseUtils_MouseJump_Panel" />
|
||||
|
||||
<!--
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_CursorWrap" AutomationProperties.AutomationId="MouseUtils_CursorWrapTestId">
|
||||
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsCursorWrapEnabledGpoConfigured, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
@@ -300,7 +301,7 @@
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
</controls:SettingsGroup>-->
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_MousePointerCrosshairs" AutomationProperties.AutomationId="MouseUtils_MousePointerCrosshairsTestId">
|
||||
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsMousePointerCrosshairsEnabledGpoConfigured, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard
|
||||
|
||||
@@ -285,9 +285,6 @@
|
||||
AutomationProperties.AutomationId="InputOutputNavItem"
|
||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/InputOutput.png}"
|
||||
SelectsOnInvoked="False">
|
||||
<NavigationViewItem.InfoBadge>
|
||||
<InfoBadge Style="{StaticResource NewInfoBadge}" />
|
||||
</NavigationViewItem.InfoBadge>
|
||||
<NavigationViewItem.MenuItems>
|
||||
<NavigationViewItem
|
||||
x:Name="KeyboardManagerNavigationItem"
|
||||
@@ -302,11 +299,7 @@
|
||||
x:Uid="Shell_MouseUtilities"
|
||||
helpers:NavHelper.NavigateTo="views:MouseUtilsPage"
|
||||
AutomationProperties.AutomationId="MouseUtilitiesNavItem"
|
||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}">
|
||||
<NavigationViewItem.InfoBadge>
|
||||
<InfoBadge Style="{StaticResource NewInfoBadge}" />
|
||||
</NavigationViewItem.InfoBadge>
|
||||
</NavigationViewItem>
|
||||
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}" />
|
||||
<NavigationViewItem
|
||||
x:Name="MouseWithoutBordersNavigationItem"
|
||||
x:Uid="Shell_MouseWithoutBorders"
|
||||
|
||||
@@ -2827,7 +2827,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<comment>Refers to the utility name</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_FindMyMouse.Description" xml:space="preserve">
|
||||
<value>Highlight the position of the cursor when pressing the Ctrl key twice, using a custom shortcut or when shaking the mouse.</value>
|
||||
<value>Find My Mouse highlights the position of the cursor when pressing the Ctrl key twice, using a custom shortcut or when shaking the mouse.</value>
|
||||
<comment>"Ctrl" is a keyboard key. "Find My Mouse" is the name of the utility</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_Enable_FindMyMouse.Header" xml:space="preserve">
|
||||
@@ -2916,7 +2916,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<comment>Refers to the utility name</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseHighlighter.Description" xml:space="preserve">
|
||||
<value>Highlight mouse clicks.</value>
|
||||
<value>Mouse Highlighter mode will highlight mouse clicks.</value>
|
||||
<comment>"Mouse Highlighter" is the name of the utility. Mouse is the hardware mouse.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_Enable_MouseHighlighter.Header" xml:space="preserve">
|
||||
@@ -2961,7 +2961,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<comment>Refers to the utility name</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MousePointerCrosshairs.Description" xml:space="preserve">
|
||||
<value>Draw crosshairs centered on the mouse pointer.</value>
|
||||
<value>Mouse Pointer Crosshairs draws crosshairs centered on the mouse pointer.</value>
|
||||
<comment>"Mouse Pointer Crosshairs" is the name of the utility. Mouse is the hardware mouse.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_Enable_MousePointerCrosshairs.Header" xml:space="preserve">
|
||||
@@ -3410,7 +3410,7 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAISettingsCardDescription.Text" xml:space="preserve">
|
||||
<value>Transform your clipboard content with the power of AI. A cloud or local endpoint is required.</value>
|
||||
<value>Transform your clipboard content with the power of AI. An cloud or local endpoint is required.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAISettingsCardDescriptionLearnMore.Content" xml:space="preserve">
|
||||
<value>Learn more</value>
|
||||
|
||||
@@ -146,6 +146,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
// Hide CursorWrap from Dashboard
|
||||
if (moduleType == ModuleType.CursorWrap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType);
|
||||
var newItem = new DashboardListItem()
|
||||
{
|
||||
@@ -154,7 +160,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
|
||||
IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
|
||||
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
IsNew = moduleType == ModuleType.CursorWrap,
|
||||
|
||||
// IsNew = moduleType == ModuleType.CursorWrap,
|
||||
DashboardModuleItems = GetModuleItems(moduleType),
|
||||
};
|
||||
newItem.EnabledChangedCallback = EnabledChangedOnUI;
|
||||
@@ -198,7 +205,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
// Notify that DashboardSortOrder changed so the menu updates its checked state.
|
||||
// Notify that DashboardSortOrder changed to update menu check mark.
|
||||
OnPropertyChanged(nameof(DashboardSortOrder));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
// Hide CursorWrap from All Apps flyout
|
||||
if (moduleType == ModuleType.CursorWrap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddFlyoutMenuItem(moduleType);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>../../../src/</AdditionalIncludeDirectories>
|
||||
<TreatAngleIncludeAsExternal>true</TreatAngleIncludeAsExternal>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -38,6 +37,10 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c">
|
||||
<!-- Disabling warnings for external code -->
|
||||
<DisableSpecificWarnings>4706;26451;4267;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EventViewer.cpp" />
|
||||
<ClCompile Include="InstallationFolder.cpp" />
|
||||
<ClCompile Include="Package.cpp" />
|
||||
@@ -58,6 +61,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
|
||||
<ClInclude Include="EventViewer.h" />
|
||||
<ClInclude Include="InstallationFolder.h" />
|
||||
<ClInclude Include="Package.h" />
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<ClCompile Include="ZipTools\ZipFolder.cpp">
|
||||
<Filter>ZipTools</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c" />
|
||||
<ClCompile Include="ReportMonitorInfo.cpp" />
|
||||
<ClCompile Include="RegistryUtils.cpp" />
|
||||
<ClCompile Include="EventViewer.cpp" />
|
||||
@@ -27,6 +28,8 @@
|
||||
<Filter>ZipTools</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\common\utils\json.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
|
||||
<ClInclude Include="ReportMonitorInfo.h" />
|
||||
<ClInclude Include="RegistryUtils.h" />
|
||||
<ClInclude Include="EventViewer.h" />
|
||||
|
||||
@@ -1,53 +1,50 @@
|
||||
#include "ZipFolder.h"
|
||||
#include "..\..\..\..\deps\cziplib\src\zip.h"
|
||||
#include <common/utils/timeutil.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <format>
|
||||
#include <wil/stl.h>
|
||||
#include <wil/win32_helpers.h>
|
||||
|
||||
void ZipFolder(std::filesystem::path zipPath, std::filesystem::path folderPath)
|
||||
{
|
||||
const auto reportFilename{
|
||||
std::format("PowerToysReport_{0}.zip",
|
||||
timeutil::format_as_local("%F-%H-%M-%S", timeutil::now()))
|
||||
};
|
||||
const auto finalReportFullPath{ zipPath / reportFilename };
|
||||
std::string reportFilename{ "PowerToysReport_" };
|
||||
reportFilename += timeutil::format_as_local("%F-%H-%M-%S", timeutil::now());
|
||||
reportFilename += ".zip";
|
||||
|
||||
const auto tempReportFilename{ reportFilename + ".tmp" };
|
||||
const auto tempReportFullPath{ zipPath / tempReportFilename };
|
||||
auto tmpZipPath = std::filesystem::temp_directory_path();
|
||||
tmpZipPath /= reportFilename;
|
||||
|
||||
// tar -c --format=zip -f "ReportFile.zip" *
|
||||
const auto executable{ wil::ExpandEnvironmentStringsW<std::wstring>(LR"(%WINDIR%\System32\tar.exe)") };
|
||||
auto commandline{ std::format(LR"("{0}" -c --format=zip -f "{1}" *)", executable, tempReportFullPath.wstring()) };
|
||||
|
||||
const auto folderPathAsString{ folderPath.lexically_normal().wstring() };
|
||||
|
||||
wil::unique_process_information pi;
|
||||
STARTUPINFOW si{ .cb = sizeof(STARTUPINFOW) };
|
||||
if (!CreateProcessW(executable.c_str(),
|
||||
commandline.data() /* must be mutable */,
|
||||
nullptr,
|
||||
nullptr,
|
||||
FALSE,
|
||||
DETACHED_PROCESS,
|
||||
nullptr,
|
||||
folderPathAsString.c_str(),
|
||||
&si,
|
||||
&pi))
|
||||
struct zip_t* zip = zip_open(tmpZipPath.string().c_str(), ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
|
||||
if (!zip)
|
||||
{
|
||||
printf("Cannot open zip.");
|
||||
throw -1;
|
||||
}
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
|
||||
const size_t rootSize = folderPath.wstring().size();
|
||||
for (const auto& dirEntry : recursive_directory_iterator(folderPath))
|
||||
{
|
||||
if (dirEntry.is_regular_file())
|
||||
{
|
||||
auto path = dirEntry.path().string();
|
||||
auto relativePath = path.substr(rootSize, path.size());
|
||||
zip_entry_open(zip, relativePath.c_str());
|
||||
zip_entry_fwrite(zip, path.c_str());
|
||||
zip_entry_close(zip);
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code err{};
|
||||
std::filesystem::rename(tempReportFullPath, finalReportFullPath, err);
|
||||
zip_close(zip);
|
||||
|
||||
std::error_code err;
|
||||
std::filesystem::copy(tmpZipPath, zipPath, err);
|
||||
if (err.value() != 0)
|
||||
{
|
||||
wprintf_s(L"Failed to rename %s. Error code: %d\n", tempReportFullPath.native().c_str(), err.value());
|
||||
wprintf_s(L"Failed to copy %s. Error code: %d\n", tmpZipPath.c_str(), err.value());
|
||||
}
|
||||
}
|
||||
|
||||
err = {};
|
||||
std::filesystem::remove_all(tmpZipPath, err);
|
||||
if (err.value() != 0)
|
||||
{
|
||||
wprintf_s(L"Failed to delete %s. Error code: %d\n", tmpZipPath.c_str(), err.value());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user