Compare commits

...

22 Commits

Author SHA1 Message Date
leileizhang
5386572092 Update src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-21 14:28:26 +08:00
leileizhang
0f1ccaee2d Update src/modules/imageresizer/dll/RuntimeRegistration.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-21 14:23:40 +08:00
Leilei Zhang
68b708f2fd update spelling check 2025-08-20 14:25:57 +08:00
Leilei Zhang
3088908202 use runtime register 2025-08-20 14:08:14 +08:00
Jiří Polášek
a5b9a38517 CmdPal: Bring existing Settings window to the foreground when opened (#41087)
## Summary of the Pull Request

Adds extra BringToFront after Activate. Don't ask.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #41086
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-08-13 13:48:07 -05:00
Jiří Polášek
7a3616e996 CmdPal: Replace Clipboard History extension outline icon with colorful icon (#41012)
## Summary of the Pull Request

Replace Clipboard History extension icon with an icon derived from
Fluent UI System Color set
(https://github.com/microsoft/fluentui-system-icons/). Icon is under MIT
license.

<img width="786" height="473" alt="image"
src="https://github.com/user-attachments/assets/6b9471d8-c98f-45c7-85e3-9521ce2a9717"
/>

## PR Checklist

- [x] Closes: #41018
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx


## Detailed Description of the Pull Request / Additional comments


## Validation Steps Performed

I looked at the icon in the top-level list. Looks nice.
I looked at the icon in the settings page. Also looks nice.
2025-08-13 13:45:47 -05:00
Jiří Polášek
b36530bf87 CmdPal: Fix race condition in SupersedingAsyncGate cancellation handling [MSH] (#40983)
## Summary of the Pull Request

Change SetCanceled to TrySetCanceled in OperationCanceledException
handler to prevent InvalidOperationException when external and internal
cancellation tokens complete the TaskCompletionSource simultaneously.


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #40982
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** none
- [ ] **Localization:** nope
- [ ] **Dev docs:** none
- [ ] **New binaries:** none
- [ ] **Documentation updated:** none

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-08-13 13:44:31 -05:00
Jessica Dene Earley-Cha
e260c01553 CmdPal: Setting Activation Shortcut now auto focuses on window & delivers dialog (#40968)
## Summary of the Pull Request
Screen readers now will focus on the activation shortcut windows and
read out the text

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #40967
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments


<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed


https://github.com/user-attachments/assets/d72a9aea-28b8-49d1-b51f-7a7d2a8ff42f
2025-08-13 13:43:54 -05:00
Mike Griese
7f4a97cac5 CmdPal: extension nuget should target a lower windows SDK version (#40902)
related to some #40113 work

The extension SDK shouldn't rely on a preview version of the Windows
SDK. It should use the stable one.

Also moves some messages around that we didn't need
2025-08-13 13:42:52 -05:00
Davide Giacometti
ab76dd1255 [CmdPal] Search PATH starting with ~ / \ (#40887)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Love that we have now environment variables expanding! 🚀 
Porting also few small features I implemented in Run Folder plugin a
long time ago and I love:
- https://github.com/microsoft/PowerToys/pull/7711
- https://github.com/microsoft/PowerToys/pull/9579

Threat `/` and `\` as root of system drive (typically `C:\`)
Threat `~` as user home directory `%USERPROFILE%`

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<img width="591" height="356" alt="image"
src="https://github.com/user-attachments/assets/9c1f196a-bd4d-428c-a4e6-af9f269acd1f"
/>

<img width="591" height="356" alt="image"
src="https://github.com/user-attachments/assets/4295ebca-f12b-43b0-b3d0-c130b6faf419"
/>


<img width="591" height="356" alt="image"
src="https://github.com/user-attachments/assets/87748864-e250-4141-b366-29b45d58edcf"
/>

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

- Tested search starting with `~` `/` `\`
- Tested UNC network path starting with `\\...` and `//...`
2025-08-13 13:42:40 -05:00
Dustin L. Howett
911989bac1 store: update package catalog before running install (#41121)
It's actually failing because we're bad at... Linux?
2025-08-13 11:01:25 -05:00
Gordon Lam
c690cb1bb8 Initial draft for 0.93 release note (#41036)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

## AI Summary
This pull request updates the `README.md` to document the 0.93 (August
2025) release of Microsoft PowerToys. It introduces a new, modern
settings dashboard, details major improvements and new features across
multiple modules, and updates installation links and documentation. The
release focuses on enhanced user experience, accessibility, performance,
stability, and test coverage.

Most important changes:

**Release and Installation Updates**
- Updated all installer links and release references from version 0.92.1
to 0.93.0, and milestone tracking for the next release to 0.94.
- Updated the release highlights and version number to reflect the 0.93
(August 2025) release, with a summary of new features and improvements.

**Settings and User Experience**
- Introduced a completely redesigned, card-based settings dashboard with
clearer descriptions, faster navigation, and improved release notes
formatting for a better user experience.
- Rewrote setting descriptions for clarity and consistency, added deep
link support to specific settings pages, and fixed various UI/UX issues
in the settings module.

**Command Palette and Extensions**
- Resolved over 99 issues in Command Palette, including accessibility
improvements, context menu enhancements, new navigation shortcuts, AOT
compilation mode (reducing install size and memory usage), and
re-enabled Clipboard History.
- Added new settings and features to Command Palette extensions, such as
command history in Run, improved Apps extension handling, and new
context menu options.

**Module Improvements and New Features**
- Mouse Utilities: Added a new spotlight highlighting mode for
presentations.
- Peek: Added instant previews and embedded thumbnail support for Binary
G-code (.bgcode) 3D printing files.
- Quick Accent: Added Vietnamese language support.

**Development, Testing, and Documentation**
- Upgraded .NET libraries and spell check system, improved CI pipelines,
reduced test timeouts, and added over 600 new unit tests (mainly for
Command Palette), doubling UI automation coverage.
- Added detailed developer documentation, fixed broken SDK links, and
documented new community plugins.

Other minor changes:
- Standardized naming, improved spelling, and cleaned up configuration
files for smoother development.
- Minor capitalization fix for "Mouse Utilities" in the utilities table.
2025-08-13 17:19:06 +02:00
Yu Leng
c23dcb0c5a [CmdPal][UT] Refactor some cmdpal ext's ut and improve the test case (#40896)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. Remove some AI generated nonsense case
2. Add ISettingsInterface for those ext for testing purpose.
3. Add query test.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #40897
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-08-12 18:27:10 +08:00
Kai Tao
3682f186e3 Mouse highlighter spotlight mode, fix the gpu perf issue (#41079)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
The big border solution for implementing the spotlight mode is gpu
consuming, switch to a resource friendly implementation.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

| Before | After | 
|----------|----------|
| <img width="739" height="263" alt="image"
src="https://github.com/user-attachments/assets/b369801a-4cda-44e8-968b-d76586931c8c"
/>| <img width="1031" height="319" alt="image"
src="https://github.com/user-attachments/assets/9b21c96d-f5ce-4ff7-8662-0c4e6e075976"
/>|
2025-08-12 10:55:24 +08:00
Yu Leng
1eae1d9a12 [AOT] Remove rd.xml from CmdPal (#41031)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Actually, we don't need to use rd.xml to preserve type. We can add
attribute to do the same things.

Tested in this build:
https://microsoft.visualstudio.com/Dart/_build/results?buildId=127875892&view=artifacts&pathAsName=false&type=publishedArtifacts

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #40633
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
2025-08-12 07:44:23 +08:00
Mike Griese
bc134b344b CmdPal: Make sure to include apps in the ctor (#41081)
A regression from #40132 

We need to set the value of `_includeApps` in the `MainListPage` ctor.
Without it, if the user doesn't have any extensions installed, the value
is never updated.
2025-08-12 07:43:08 +08:00
Jiří Polášek
04b8234192 CmdPal: Fix styles applied to MoreCommandsButton (#41059)
## Summary of the Pull Request

- Apply the same padding to the button as used for primary and secondary
command buttons.
- Use consistent spacing between keycap blocks.
- Match keycap border style and inner text brush with other command
buttons.
- Add min width constraint to shortcut keycap element to make it at
least square.

<img width="961" height="355" alt="image"
src="https://github.com/user-attachments/assets/cff5ef7e-fe67-41ac-9796-063c0e69768a"
/>


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #41052
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

## Validation Steps Performed
👀
2025-08-11 18:12:05 -05:00
Shawn Yuan
d72e0ab20d Fixed toggle switch not working issue. (#41049)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fixed toggle switch not working issue.

## AI Summary
This pull request refactors how `DashboardListItem` objects are created
and added to collections in the `DashboardViewModel`. The main
improvement is to separate the instantiation of each `DashboardListItem`
from the assignment of its `EnabledChangedCallback` property, which is
now set after the object is added to the relevant collection. This
change improves clarity and may help prevent issues related to object
initialization order.

Refactoring of `DashboardListItem` creation and initialization:

* In the `AddDashboardListItem` method, the `DashboardListItem` object
is now created and added to `AllModules` before its
`EnabledChangedCallback` property is set, instead of setting this
property during object initialization.
* In the `GetShortcutModules` method, both `ShortcutModules` and
`ActionModules` collections now receive `DashboardListItem` objects that
are instantiated first, added to the collection, and then have their
`EnabledChangedCallback` property set. This replaces the previous
pattern of setting the callback during object creation.
[[1]](diffhunk://#diff-aea3404667e7a3de2750bf9ab7ee8ff5e717892caa68ee1de86713cf8e21b44cL123-R136)
[[2]](diffhunk://#diff-aea3404667e7a3de2750bf9ab7ee8ff5e717892caa68ee1de86713cf8e21b44cL144-R159)
* 
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #41046
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
It is an regression from
https://github.com/microsoft/PowerToys/pull/40214

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Signed-off-by: Shuai Yuan <shuai.yuan.zju@gmail.com>
2025-08-08 22:55:00 +08:00
Kai Tao
062234c295 Settings: Mouse utils setting crash (#41050)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fix a crash in settings page due to not found converter
<!-- Please review the items on the PR checklist before submitting-->

## AI Summary
This pull request makes a small update to the `MouseUtilsPage.xaml` file
to use the correct resource for converting boolean values to visibility
states in the UI.

- Updated the `Visibility` binding on an `InfoBar` to use the
`ReverseBoolToVisibilityConverter` instead of the incorrect
`BoolToReverseVisibilityConverter` resource.

## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Regression caused by https://github.com/microsoft/PowerToys/pull/40214

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
<img width="1983" height="1072" alt="image"
src="https://github.com/user-attachments/assets/13c806c3-e7af-4615-a649-6d58d8fe877b"
/>
2025-08-08 16:38:46 +08:00
Jeremy Sinclair
0d4f3d851e [Deps] Update .NET packages from 9.0.7 to 9.0.8 (#41039)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Updates .NET 9 Runtime / Library packages to the latest 9.0.8 servicing
release.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-08-08 08:28:01 +08:00
Mike Griese
e93b044f39 CmdPal: Once again, I am asking you to fix form submits (#41010)
Closes #40979

Usually, you're supposed to try to cast the action to a specific
type, and use those objects to get the data you need.
However, there's something weird with AdaptiveCards and the way it
works when we consume it when built in Release, with AOT (and
trimming) enabled. Any sort of `action.As<IAdaptiveSubmitAction>()`
or similar will throw a System.InvalidCastException.

Instead we have this horror show.

The `action.ToJson()` blob ACTUALLY CONTAINS THE `type` field, which
we can use to determine what kind of action it is. Then we can parse
the JSON manually based on the type.
2025-08-06 19:41:02 -05:00
leileizhang
fed6e523b6 Fix: used wrong preview resize event from another handler (#40995)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Bug: Was using GcodePreviewResizeEvent, which will never work — switched
to use Bgcode's own event

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

## AI Summary
This pull request makes a minor update to the event handling in the
preview pane module. The change updates the event constant used for
resizing the preview from `GcodePreviewResizeEvent` to
`BgcodePreviewResizeEvent`, likely to improve naming consistency or to
support a new event type.
2025-08-06 14:12:37 +08:00
113 changed files with 1813 additions and 963 deletions

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- name: BODGY - Set up Gnome Keyring for future Cert Auth
run: |-
sudo apt-get install -y gnome-keyring
sudo apt-get update && sudo apt-get install -y gnome-keyring
export $(dbus-launch --sh-syntax)
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --unlock)
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)

View File

@@ -34,22 +34,22 @@
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.7" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.8" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.7" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.8" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.8" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.7" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.8" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.7" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.8" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
@@ -78,28 +78,28 @@
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.CodeDom" Version="9.0.7" />
<PackageVersion Include="System.CodeDom" Version="9.0.8" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.7" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.7" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.7" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.8" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.8" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.8" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.7" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.8" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.7" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.7" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.8" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.8" />
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="9.0.7" />
<PackageVersion Include="System.Management" Version="9.0.8" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.7" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.7" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.7" />
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.8" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.8" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.8" />
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />

View File

@@ -1519,23 +1519,23 @@ SOFTWARE.
- Mages 3.0.0
- Markdig.Signed 0.34.0
- MessagePack 3.1.3
- Microsoft.Bcl.AsyncInterfaces 9.0.7
- Microsoft.Bcl.AsyncInterfaces 9.0.8
- Microsoft.Bot.AdaptiveExpressions.Core 4.23.0
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
- Microsoft.Data.Sqlite 9.0.7
- Microsoft.Data.Sqlite 9.0.8
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.DotNet.ILCompiler (A)
- Microsoft.Extensions.DependencyInjection 9.0.7
- Microsoft.Extensions.Hosting 9.0.7
- Microsoft.Extensions.Hosting.WindowsServices 9.0.7
- Microsoft.Extensions.Logging 9.0.7
- Microsoft.Extensions.Logging.Abstractions 9.0.7
- Microsoft.Extensions.DependencyInjection 9.0.8
- Microsoft.Extensions.Hosting 9.0.8
- Microsoft.Extensions.Hosting.WindowsServices 9.0.8
- Microsoft.Extensions.Logging 9.0.8
- Microsoft.Extensions.Logging.Abstractions 9.0.8
- Microsoft.NET.ILLink.Tasks (A)
- Microsoft.SemanticKernel 1.15.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2903.40
- Microsoft.Win32.SystemEvents 9.0.7
- Microsoft.Windows.Compatibility 9.0.7
- Microsoft.Win32.SystemEvents 9.0.8
- Microsoft.Windows.Compatibility 9.0.8
- Microsoft.Windows.CsWin32 0.3.183
- Microsoft.Windows.CsWinRT 2.2.0
- Microsoft.Windows.SDK.BuildTools 10.0.26100.4188
@@ -1555,25 +1555,25 @@ SOFTWARE.
- SkiaSharp.Views.WinUI 2.88.9
- StreamJsonRpc 2.21.69
- StyleCop.Analyzers 1.2.0-beta.556
- System.CodeDom 9.0.7
- System.CodeDom 9.0.8
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 9.0.7
- System.Configuration.ConfigurationManager 9.0.7
- System.Data.OleDb 9.0.7
- System.ComponentModel.Composition 9.0.8
- System.Configuration.ConfigurationManager 9.0.8
- System.Data.OleDb 9.0.8
- System.Data.SqlClient 4.9.0
- System.Diagnostics.EventLog 9.0.7
- System.Diagnostics.PerformanceCounter 9.0.7
- System.Drawing.Common 9.0.7
- System.Diagnostics.EventLog 9.0.8
- System.Diagnostics.PerformanceCounter 9.0.8
- System.Drawing.Common 9.0.8
- System.IO.Abstractions 22.0.13
- System.IO.Abstractions.TestingHelpers 22.0.13
- System.Management 9.0.7
- System.Management 9.0.8
- System.Net.Http 4.3.4
- System.Private.Uri 4.3.2
- System.Reactive 6.0.1
- System.Runtime.Caching 9.0.7
- System.ServiceProcess.ServiceController 9.0.7
- System.Text.Encoding.CodePages 9.0.7
- System.Text.Json 9.0.7
- System.Runtime.Caching 9.0.8
- System.ServiceProcess.ServiceController 9.0.8
- System.Text.Encoding.CodePages 9.0.8
- System.Text.Json 9.0.8
- System.Text.RegularExpressions 4.3.1
- UnicodeInformation 2.6.0
- UnitsNet 5.56.0

View File

@@ -262,6 +262,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643
src\common\utils\EventLocker.h = src\common\utils\EventLocker.h
src\common\utils\EventWaiter.h = src\common\utils\EventWaiter.h
src\common\utils\excluded_apps.h = src\common\utils\excluded_apps.h
src\common\utils\shell_ext_registration.h = src\common\utils\shell_ext_registration.h
src\common\utils\exec.h = src\common\utils\exec.h
src\common\utils\game_mode.h = src\common\utils\game_mode.h
src\common\utils\gpo.h = src\common\utils\gpo.h

202
README.md
View File

@@ -14,7 +14,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | [Command Palette](https://aka.ms/PowerToysOverview_CmdPal) |
| [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) |
| [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) |
| [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) |
| [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [Mouse Utilities](https://aka.ms/PowerToysOverview_MouseUtilities) |
| [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [New+](https://aka.ms/PowerToysOverview_NewPlus) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) |
| [Peek](https://aka.ms/PowerToysOverview_Peek) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) |
| [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) |
@@ -35,19 +35,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
<!-- 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.93%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.92%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.92.1/PowerToysUserSetup-0.92.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.92.1/PowerToysUserSetup-0.92.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.92.1/PowerToysSetup-0.92.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.92.1/PowerToysSetup-0.92.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.94%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.93%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysUserSetup-0.93.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysUserSetup-0.93.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysSetup-0.93.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysSetup-0.93.0-arm64.exe
| Description | Filename |
|----------------|----------|
| Per user - x64 | [PowerToysUserSetup-0.92.1-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.92.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.92.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.92.1-arm64.exe][ptMachineArm64] |
| Per user - x64 | [PowerToysUserSetup-0.93.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.93.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.93.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.93.0-arm64.exe][ptMachineArm64] |
This is our preferred method.
@@ -93,139 +93,119 @@ For guidance on developing for PowerToys, please read the [developer docs](./doc
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
### 0.92 - June 2025 Update
### 0.93 - Aug 2025 Update
In this release, we focused on new features, stability, optimization improvements, and automation.
**✨Highlights**
- PowerToys settings now has a toggle for the system tray icon, giving users control over its visibility based on personal preference. Thanks [@BLM16](https://github.com/BLM16)!
- Command Palette now has Ahead-of-Time ([AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot)) compatibility for all first-party extensions, improved extensibility, and core UX fixes, resulting in better performance and stability across commands.
- Color Picker now has customizable mouse button actions, enabling more personalized workflows by assigning functions to left, right, and middle clicks. Thanks [@PesBandi](https://github.com/PesBandi)!
- Bug Report Tool now has a faster and clearer reporting process, with progress indicators, improved compression, auto-cleanup of old trace logs, and inclusion of MSIX installer logs for more efficient diagnostics.
- File Explorer add-ons now have improved rendering stability, resolving issues with PDF previews, blank thumbnails, and text file crashes during file browsing.
### Color Picker
- Added mouse button actions so you can choose what left, right, or middle click does. Thanks [@PesBandi](https://github.com/PesBandi)!
### Crop & Lock
- Aligned window styling with current Windows theme for a cleaner look. Thanks [@sadirano](https://github.com/sadirano)!
- PowerToys settings debuts a modern, card-based dashboard with clearer descriptions and faster navigation for a streamlined user experience.
- Command Palette had over 99 issues resolved, including bringing back Clipboard History, adding context menu shortcuts, pinning favorite apps, and supporting history in Run.
- Command Palette reduced its startup memory usage by ~15%, load time by ~40%, built-in extensions loading time by ~70%, and installation size by ~55%—all due to using the full Ahead-of-Time (AOT) compilation mode in Windows App SDK.
- Peek now supports instant previews and embedded thumbnails for Binary G-code (.bgcode) 3D printing files, making it easy to inspect models at a glance. Thanks [@pedrolamas](https://github.com/pedrolamas)!
- Mouse Utilities introduces a new spotlight highlighting mode that dims the screen and draws attention to your cursor, perfect for presentations.
- Test coverage improvements for multiple PowerToys modules including Command Palette, Advanced Paste, Peek, Text Extractor, and PowerRename — ensuring better reliability and quality, with over 600 new unit tests (mostly for Command Palette) and doubled UI automation coverage.
### Command Palette
- Enhanced performance by resolving a regression in page loading.
- Applied consistent hotkey handling across all Command Palette commands for a smoother user experience.
- Improved graceful closing of Command Palette. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed consistency issue for extensions' alias with "Direct" setting and enabled localization for "Direct" and "Indirect" for better user understanding. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Improved visual clarity by styling critical context items correctly.
- Automatically focused the field when only one is present on the content page.
- Improved stability and efficiency when loading file icons in SDK ThumbnailHelper.cs by removing unnecessary operations. Thanks [@OldUser101](https://github.com/OldUser101)!
- Enhanced details view with commands implementation. (See [Extension sample](./src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs))
- Ensured screen readers are notified when the selected item in the list changes for better accessibility.
- Fixed command title changes not being properly notified to screen readers. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Made icon controls excluded from keyboard navigation by default for better accessibility. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Improved UI design with better text sizing and alignment.
- Fixed keyboard shortcuts to work better in text boxes and context menus.
- Added right-click context menus with critical command styling and separators.
- Improved various context menu issues, improving item selection, handling of long titles, search bar text scaling, initial item behavior, and primary button functionality.
- Fixed context menu crashes with better type handling.
- Fixed "Reload" command to work with both uppercase and lowercase letters.
- Added mouse back button support for easier navigation. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed Alt+Left Arrow navigation not working when search box contains text. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Updated back button tooltip to show keyboard shortcut information. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed Command Palette window not appearing properly when activated. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed Command Palette window staying hidden from taskbar after File Explorer restarts. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed window focus not returning to previous app properly.
- Fixed Command Palette window to always appear on top when shown and move to bottom when hidden. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed window hiding to properly work on UI thread. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed crashes and improved stability with better synchronization of Command list updates. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Improved extension disposal with better error handling to prevent crashes. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Improved stability by fixing a UI threading issue when loading more results, preventing possible crashes and ensuring the loading state resets if loading fails. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Enhanced icon loading stability with better exception handling. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added thread safety to recent commands to prevent crashes. Thanks [@MaoShengelia](https://github.com/MaoShengelia)!
- Fixed acrylic (frosted glass) system backdrop display issues by ensuring proper UI thread handling. Thanks [@jiripolasek](https://github.com/jiripolasek)!
### Command Palette extensions
- Added "Copy Path" command to *App* search results for convenience. Thanks [@PesBandi](https://github.com/PesBandi)!
- Improved *Calculator* input experience by ignoring leading equal signs. Thanks [@PesBandi](https://github.com/PesBandi)!
- Corrected input handling in the *Calculator* extension to avoid showing errors for input with only leading whitespace.
- Improved *New Extension* wizard by validating names to prevent namespace errors.
- Ensured consistent context items display for the *Run* extension between fallback and top-level results.
- Fixed missing *Time & Date* commands in fallback results. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed outdated results in the *Time & Date* extension. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed an issue where *Web Search* always opened Microsoft Edge instead of the user's default browser on Windows 11 24H2 and later. Thanks [@RuggMatt](https://github.com/RuggMatt)!
- Improved ordering of *Windows Settings* extension search results from alphabetical to relevance-based for quicker access.
- Added "Restart Windows Explorer" command to the *Windows System Commands* provider for gracefully terminate and relaunch explorer.exe. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added settings to each provider to control which fallback commands are enabled. Thanks [@jiripolasek](https://github.com/jiripolasek)! for fixing a regression in this feature.
- Added sample code showing how Command Palette extensions can track when their pages are loaded or unloaded. [Check it out here](./src/modules/cmdpal/ext/SamplePagesExtension/OnLoadPage.cs).
- Fixed *Calculator* to accept regular spaces in numbers that use space separators. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added a new setting to *Calculator* to make "Copy" the primary button (replacing “Save”) and enable "Close on Enter", streamlining the workflow. Thanks [@PesBandi](https://github.com/PesBandi)!
- Improved *Apps* indexing error handling and removed obsolete code. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Prevented apps from showing in search when the *Apps* extension is disabled. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added ability to pin/unpin *Apps* using Ctrl+P shortcut.
- Added keyboard shortcuts to the *Apps* context menu items for faster access.
- Added all file context menu options to the *Apps* items context menu, making all file actions available there for better functionality.
- Streamlined All *Apps* extension settings by removing redundant descriptions, making the UI clearer.
- Added command history to the *Run* page for easier access to previous commands.
- Fixed directory path handling in *Run* fallback for better file navigation.
- Fixed URL fallback item hiding properly in *Web Search* extension when search query becomes invalid. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added proper empty state message for *Web Search* extension when no results found. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added fallback command to *Windows Settings* extension for better search results.
- Re-enabled *Clipboard History* feature with proper window handling.
- Improved *Add Bookmark* extension to automatically detect file, folder, or URL types without manual input.
- Updated terminology from "Kill process" to "End task" in *Window Walker* for consistency with Windows.
- Fixed minor grammar error in SamplePagesExtension code comments. Thanks [@purofle](https://github.com/purofle)!
### Command Palette Ahead-of-Time (AOT) readiness
### Mouse Utilities
- Weve made foundational changes to prepare the Command Palette for future Ahead-of-Time (AOT) publishing. This includes replacing the calculator library with ExprTk, improving COM object handling, refining Win32 interop, and correcting trimming behavior—all to ensure compatibility, performance, and reliability under AOT constraints. All first-party extensions are now AOT-compatible. These improvements lay the groundwork for publishing Command Palette as an AOT application in the next release.
- Special thanks to [@Sergio0694](https://github.com/Sergio0694) for guidance on making COM APIs AOT-compatible, [@jtschuster](https://github.com/jtschuster) for fixing COM object handling, [@ArashPartow](https://github.com/ArashPartow) from ExprTk for integration suggestions, and [@tian-lt](https://github.com/tian-lt) from the Windows Calculator team for valuable suggestion throughout the migration journey and review.
- As part of the upcoming release, were also enabling AOT compatibility for key dependencies, including markdown rendering, Adaptive Cards, internal logging and telemetry library, and the core Command Palette UX.
### FancyZones
- Fixed DPI-scaling issues to ensure FancyZones Editor displays crisply on high-resolution monitors. Thanks [@HO-COOH](https://github.com/HO-COOH)! This inspired us a broader review across other PowerToys modules, leading to DPI display optimizations in Awake, Color Picker, PowerAccent, and more.
### File Explorer add-ons
- Fixed potential failures in PDF previewer and thumbnail generation, improving reliability when browsing PDF files. Thanks [@mohiuddin-khan-shiam](https://github.com/mohiuddin-khan-shiam)!
- Prevented Monaco Preview Handler crash when opening UTF-8-BOM text files.
### Hosts File Editor
- Added an in-app *“Learn more”* link to warning dialogs for quick guidance. Thanks [@PesBandi](https://github.com/PesBandi)!
### Mouse Without Borders
- Fixed firewall rule so MWB now accepts connections from IPs outside your local subnet.
- Cleaned legacy logs to reduce disk usage and noise.
- Added a new spotlight highlighting mode that creates a large transparent circle around your cursor with a backdrop effect, providing an alternative to the traditional circle highlight. Perfect for presentations where you want to focus attention on a specific area while dimming the rest of the screen.
### Peek
- Updated QOI reader so 3-channel QOI images preview correctly in Peek and File Explorer. Thanks [@mbartlett21](https://github.com/mbartlett21)!
- Added codec detection with a clear warning when a video cant be previewed, along with a link to the Microsoft Store to download the required codec.
- Added preview and thumbnail support for Binary G-code (.bgcode) files used in 3D printing. You can now see embedded thumbnails and preview these compressed 3D printing files directly in Peek and File Explorer. Thanks [@pedrolamas](https://github.com/pedrolamas)!
### PowerRename
### Quick Accent
- Added support for $YY-$MM-$DD in ModificationTime and AccessTime to enable flexible date-based renaming.
### PowerToys Run
- Suppressed error UI for known WPF-related crashes to reduce user confusion, while retaining diagnostic logging for analysis. This targets COMException 0xD0000701 and 0x80263001 caused by temporary DWM unavailability.
### Registry Preview
- Added "Extended data preview" via magnifier icon and context menu in the Data Grid, enabled easier inspection of complex registry types like REG_BINARY, REG_EXPAND_SZ, and REG_MULTI_SZ, etc. Thanks [@htcfreek](https://github.com/htcfreek)!
- Improved file-saving experience in Registry Preview by aligning with Notepad-like behavior, enhancing user prompts, error handling, and preventing crashes during unsaved or interrupted actions. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added Vietnamese language support to Quick Accent, mappings for Vietnamese vowels (a, e, i, o, u, y) and the letter d. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
### Settings
- Added an option to hide or show the PowerToys system tray icon. Thanks [@BLM16](https://github.com/BLM16)!
- Improved settings to show progress while a bug report package is being generated.
### Workspaces
- Stored Workspaces icons in user AppData to ensure profile portability and prevent loss during temporary folder cleanup.
- Enabled capture and launch of PWAs on non-default Edge or Chrome profiles, ensuring consistent behavior during creation and execution.
- Completely redesigned the Settings dashboard with a modern card-based layout featuring organized sections for quick actions and shortcuts overview, replacing the old module list.
- Rewrote setting descriptions to be more concise and follow Windows writing style guidelines, making them easier to understand.
- Improved formatting and readability of release notes in the "What's New" section with better typography and spacing.
- Added missing deep link support for various settings pages (Peek, Quick Accent, PowerToys Run, etc.) so you can jump directly to specific settings.
- Resolved an issue where the settings page header would drift away from its position when resizing the settings window.
- Resolved a settings crash related to incompatible property names in ZoomIt configuration.
### Documentation
- Added SpeedTest and Dictionary Definition to the third-party plugins documentation for PowerToys Run. Thanks [@ruslanlap](https://github.com/ruslanlap)!
- Corrected sample links and typo in Command Palette documentation. Thanks [@daverayment](https://github.com/daverayment) and [@roycewilliams](https://github.com/roycewilliams)!
- Added detailed step-by-step instructions for first-time developers building the Command Palette module, including prerequisites and Visual Studio setup guidance. Thanks [@chatasweetie](https://github.com/chatasweetie)!
- **Fixed Broken SDK Link**: Corrected a broken markdown link in the Command Palette SDK README that was pointing to an incorrect directory path. Thanks [@ChrisGuzak](https://github.com/ChrisGuzak)!
- Added documentation for the "Open With Cursor" plugin that enables opening Visual Studio and VS Code recent files using Cursor AI. Thanks [@VictorNoxx](https://github.com/VictorNoxx)!
- Added documentation for two new community plugins - Hotkeys plugin for creating custom keyboard shortcuts, and RandomGen plugin for generating random data like passwords, colors, and placeholder text. Thanks [@ruslanlap](https://github.com/ruslanlap)!
### Development
- Updated .NET libraries to 9.0.6 for performance and security. Thanks [@snickler](https://github.com/snickler)!
- Updated WinAppSDK to 1.7.2 for better stability and Windows support.
- Introduced a one-step local build script that generates a signed installer, enhancing developer productivity.
- Generated portable PDBs so cross-platform debuggers can read symbol files, improving debugging experience in VSCode and other tools.
- Simplified WinGet configuration files by using the [Microsoft.Windows.Settings](https://www.powershellgallery.com/packages/Microsoft.Windows.Settings) module to enable Developer Mode. Thanks [@mdanish-kh](https://github.com/mdanish-kh)!
- Adjusted build scripts for the latest Az.Accounts module to keep CI green.
- Streamlined release pipeline by removing hard-coded telemetry version numbers, and unified Command Palette versioning with Windows Terminal's versioning method for consistent updates.
- Enhanced the build validation step to show detailed differences between NOTICE.md and actual package dependencies and versions.
- Improved spell-checking accuracy across the repo. Thanks [@rovercoder](https://github.com/rovercoder)!
- Upgraded CI to TouchdownBuild v5 for faster pipelines.
- Added context comments to *Resources.resw* to help translators.
- Expanded fuzz testing coverage to include FancyZones.
- Integrated all unit tests into the CI pipeline, increasing from ~3,000 to ~5,000 tests.
- Enabled daily UI test automation on the main branch, now covering over 370 UI tests for end-to-end validation.
- Newly added unit tests for WorkspacesLib to improve reliability and maintainability.
- Updated .NET libraries to 9.0.8 for performance and security. Thanks [@snickler](https://github.com/snickler)!
- Updated the spell check system to version 0.0.25 with better GitHub integration and SARIF reporting, plus fixed numerous spelling errors throughout the codebase including property names and documentation. Thanks [@jsoref](https://github.com/jsoref)!
- Cleaned up spelling check configuration to eliminate false positives and excessive noise that was appearing in every pull request, making the development process smoother.
- Replaced NuGet feed with Azure Artifacts for better package management.
- Implemented configurable UI test pipeline that can use pre-built official releases instead of building everything from scratch, reducing test execution time from 2+ hours.
- Replaced brittle pixel-by-pixel image comparison with perceptual hash (pHash) technology that's more robust to minor rendering differences - no more test failures due to anti-aliasing or compression artifacts.
- Reduced CI/fuzzing/UI test timeouts from 4 hours to 90 minutes, dramatically improving developer feedback loops and preventing long waits when builds get stuck.
- Standardized test project naming across the entire codebase and improved pipeline result identification by adding platform/install mode context to test run titles. Thanks [@khmyznikov](https://github.com/khmyznikov)!
- Added comprehensive UI test suites for multiple PowerToys modules including Command Palette, Advanced Paste, Peek, Text Extractor, and PowerRename - ensuring better reliability and quality.
- Enhanced UI test automation with command-line argument support, better session management, and improved element location methods using pattern matching to avoid failures from minor differences in exact matches.
### General
### What is being planned over the next few releases
- Updated bug report compression library (cziplib 0.3.3) for faster and more reliable package creation. Thanks [@Chubercik](https://github.com/Chubercik)!
- Included App Installer (“AppX Deployment Server”) event logs in bug reports for more thorough diagnostics.
### What is being planned for version 0.93
For [v0.93][github-next-release-work], we'll work on the items below:
For [v0.94][github-next-release-work], we'll work on the items below:
- Continued Command Palette polish
- New UI automation tests
- Working on installer upgrades
- Working on Shortcut Guide v2 (Thanks [@noraa-junker](https://github.com/noraa-junker)!)
- Working on upgrading the installer to WiX 5
- Working on shortcut conflict detection
- Working on setting search
- Upgrading Keyboard Manager's editor UI
- New UI automation tests
- Stability, bug fixes
## PowerToys Community

View File

@@ -14,21 +14,6 @@
<DirectoryRef Id="FileLocksmithAssetsInstallFolder" FileSource="$(var.FileLocksmithAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--FileLocksmithAssetsFiles_Component_Def-->
<!-- !Warning! Make sure to change Component Guid if you update something here -->
<Component Id="Module_FileLocksmith" Guid="108D3EC1-E6E0-4E81-88EF-25966133CB41" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{84D68575-E186-46AD-B0CB-BAEB45EE29C0}">
<RegistryValue Type="string" Value="File Locksmith Shell Extension" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.FileLocksmithExt.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\AllFileSystemObjects\ShellEx\ContextMenuHandlers\FileLocksmithExt">
<RegistryValue Type="string" Value="{84D68575-E186-46AD-B0CB-BAEB45EE29C0}"/>
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\Drive\ShellEx\ContextMenuHandlers\FileLocksmithExt">
<RegistryValue Type="string" Value="{84D68575-E186-46AD-B0CB-BAEB45EE29C0}"/>
</RegistryKey>
</Component>
</DirectoryRef>
<ComponentGroup Id="FileLocksmithComponentGroup">
@@ -38,7 +23,6 @@
</RegistryKey>
<RemoveFolder Id="RemoveFolderFileLocksmithAssetsFolder" Directory="FileLocksmithAssetsInstallFolder" On="uninstall"/>
</Component>
<ComponentRef Id="Module_FileLocksmith" />
</ComponentGroup>
</Fragment>

View File

@@ -16,71 +16,6 @@
<!-- Generated by generateFileComponents.ps1 -->
<!--ImageResizerAssetsFiles_Component_Def-->
<Component Id="Module_ImageResizer_Registry" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
<RegistryValue Value="[WinUI3AppsInstallFolder]PowerToys.ImageResizerExt.dll" Type="string" />
<RegistryValue Name="ThreadingModel" Value="Apartment" Type="string" />
</RegistryKey>
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\Directory\ShellEx\DragDropHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<!-- Registry Keys for the context menu handler for each of the following image formats: bmp, dib, gif, jfif, jpe, jpeg, jpg, jxr, png, rle, tif, tiff, wdp -->
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.bmp\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.dib\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.gif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.jfif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.jpe\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.jpeg\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.jpg\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.jxr\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.rle\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.tif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.tiff\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="$(var.RegistryScope)"
Key="SOFTWARE\Classes\SystemFileAssociations\.wdp\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
</Component>
</DirectoryRef>
<ComponentGroup Id="ImageResizerComponentGroup">
@@ -90,7 +25,6 @@
</RegistryKey>
<RemoveFolder Id="RemoveFolderImageResizerAssetsFolder" Directory="ImageResizerAssetsFolder" On="uninstall"/>
</Component>
<ComponentRef Id="Module_ImageResizer_Registry" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -18,19 +18,6 @@
<DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--NewPlusAssetsFiles_Component_Def-->
<!-- NewPlus Shell Extension for Win10 registration -->
<Component Id="NewPlus_ShellExtension_win10" Guid="D5456D4A-6EEC-4B85-944D-6A6A4A74FFA6" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{FF90D477-E32A-4BE8-8CC5-A502A97F5401}">
<RegistryValue Type="string" Value="NewPlus Shell Extension Win10" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.NewPlus.ShellExtension.win10.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\NewPlusShellExtensionWin10">
<RegistryValue Type="string" Value="{FF90D477-E32A-4BE8-8CC5-A502A97F5401}"/>
</RegistryKey>
</Component>
</DirectoryRef>
<ComponentGroup Id="NewPlusComponentGroup">
@@ -40,8 +27,7 @@
</RegistryKey>
<RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/>
</Component>
<ComponentRef Id="NewPlus_ShellExtension_win10" />
</ComponentGroup>
</ComponentGroup>
<!-- Example templates -->
@@ -81,7 +67,7 @@
</Component>
<ComponentRef Id="NewPlusTemplateFiles_Component" />
<ComponentRef Id="NewPlusTemplateSubFiles_Component" />
</ComponentGroup>
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -14,22 +14,6 @@
<DirectoryRef Id="PowerRenameAssetsFolder" FileSource="$(var.PowerRenameAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--PowerRenameAssetsFiles_Component_Def-->
<!-- !Warning! Make sure to change Component Guid if you update something here -->
<Component Id="Module_PowerRename" Guid="40D43079-240E-402D-8CE8-571BFFA71175" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{0440049F-D1DC-4E46-B27B-98393D79486B}">
<RegistryValue Type="string" Value="PowerRename Shell Extension" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.PowerRenameExt.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\AllFileSystemObjects\ShellEx\ContextMenuHandlers\PowerRenameExt">
<RegistryValue Type="string" Value="{0440049F-D1DC-4E46-B27B-98393D79486B}"/>
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\PowerRenameExt">
<RegistryValue Type="string" Value="{0440049F-D1DC-4E46-B27B-98393D79486B}"/>
</RegistryKey>
</Component>
</DirectoryRef>
<ComponentGroup Id="PowerRenameComponentGroup">
@@ -39,7 +23,6 @@
</RegistryKey>
<RemoveFolder Id="RemoveFolderPowerRenameAssetsFolder" Directory="PowerRenameAssetsFolder" On="uninstall"/>
</Component>
<ComponentRef Id="Module_PowerRename" />
</ComponentGroup>
</Fragment>

View File

@@ -176,6 +176,18 @@
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="CleanImageResizerRuntimeRegistry" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="CleanFileLocksmithRuntimeRegistry" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="CleanPowerRenameRuntimeRegistry" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="CleanNewPlusRuntimeRegistry" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="UnRegisterCmdPalPackage" Before="RemoveFiles">
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
@@ -437,6 +449,35 @@
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="UnRegisterContextMenuPackagesCA"
/>
<CustomAction Id="CleanImageResizerRuntimeRegistry"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CleanImageResizerRuntimeRegistryCA"
/>
<CustomAction Id="CleanFileLocksmithRuntimeRegistry"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CleanFileLocksmithRuntimeRegistryCA"
/>
<CustomAction Id="CleanPowerRenameRuntimeRegistry"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CleanPowerRenameRuntimeRegistryCA"
/>
<CustomAction Id="CleanNewPlusRuntimeRegistry"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CleanNewPlusRuntimeRegistryCA"
/>
<CustomAction Id="UnRegisterCmdPalPackage"

View File

@@ -1153,6 +1153,113 @@ UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
return WcaFinalize(er);
}
UINT __stdcall CleanImageResizerRuntimeRegistryCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "CleanImageResizerRuntimeRegistryCA");
try
{
const wchar_t* CLSID_STR = L"{51B4D7E5-7568-4234-B4BB-47FB3C016A69}";
const wchar_t* exts[] = { L".bmp", L".dib", L".gif", L".jfif", L".jpe", L".jpeg", L".jpg", L".jxr", L".png", L".rle", L".tif", L".tiff", L".wdp" };
auto deleteKeyRecursive = [](HKEY root, const std::wstring &path) {
RegDeleteTreeW(root, path.c_str());
};
// InprocServer32 chain root CLSID
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
// DragDrop handler
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\ShellEx\\DragDropHandlers\\ImageResizer");
// Extensions
for (auto ext : exts)
{
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\SystemFileAssociations\\" + std::wstring(ext) + L"\\ShellEx\\ContextMenuHandlers\\ImageResizer");
}
// Sentinel
RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\ImageResizer");
}
catch (...)
{
er = ERROR_INSTALL_FAILURE;
}
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
return WcaFinalize(er);
}
UINT __stdcall CleanFileLocksmithRuntimeRegistryCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "CleanFileLocksmithRuntimeRegistryCA");
try
{
const wchar_t* CLSID_STR = L"{84D68575-E186-46AD-B0CB-BAEB45EE29C0}";
auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
RegDeleteTreeW(root, path.c_str());
};
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt");
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Drive\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt");
RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\FileLocksmith");
}
catch (...)
{
er = ERROR_INSTALL_FAILURE;
}
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
return WcaFinalize(er);
}
UINT __stdcall CleanPowerRenameRuntimeRegistryCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "CleanPowerRenameRuntimeRegistryCA");
try
{
const wchar_t* CLSID_STR = L"{0440049F-D1DC-4E46-B27B-98393D79486B}";
auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
RegDeleteTreeW(root, path.c_str());
};
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\PowerRenameExt");
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\PowerRenameExt");
RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\PowerRename");
}
catch (...)
{
er = ERROR_INSTALL_FAILURE;
}
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
return WcaFinalize(er);
}
UINT __stdcall CleanNewPlusRuntimeRegistryCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "CleanNewPlusRuntimeRegistryCA");
try
{
const wchar_t* CLSID_STR = L"{FF90D477-E32A-4BE8-8CC5-A502A97F5401}";
auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
RegDeleteTreeW(root, path.c_str());
};
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\NewPlusShellExtensionWin10");
RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\NewPlus");
}
catch (...)
{
er = ERROR_INSTALL_FAILURE;
}
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
return WcaFinalize(er);
}
UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;

View File

@@ -28,3 +28,7 @@ EXPORTS
UninstallCommandNotFoundModuleCA
UpgradeCommandNotFoundModuleCA
UnsetAdvancedPasteAPIKeyCA
CleanImageResizerRuntimeRegistryCA
CleanFileLocksmithRuntimeRegistryCA
CleanPowerRenameRuntimeRegistryCA
CleanNewPlusRuntimeRegistryCA

View File

@@ -0,0 +1,266 @@
// Shared runtime shell extension registration utility for PowerToys modules.
// Provides a generic EnsureRegistered function so individual modules only need
// to supply a specification (CLSID, sentinel, handler key paths, etc.).
#pragma once
#include <string>
#include <vector>
#include <windows.h>
#include <shlwapi.h>
#include "../logger/logger.h"
namespace runtime_shell_ext
{
struct Spec
{
// Mandatory
std::wstring clsid; // e.g. {GUID}
std::wstring sentinelKey; // e.g. Software\\Microsoft\\PowerToys\\ModuleName
std::wstring sentinelValue; // e.g. ContextMenuRegistered
std::vector<std::wstring> dllFileCandidates; // relative filenames (pick first existing)
std::vector<std::wstring> contextMenuHandlerKeyPaths; // full HKCU relative paths where default value = CLSID
// Optional
std::wstring friendlyName; // if non-empty written as default under CLSID root
bool writeOptInEmptyValue = true; // write ContextMenuOptIn="" under CLSID root (legacy pattern)
bool writeThreadingModel = true; // write Apartment threading model
std::vector<std::wstring> extraAssociationPaths; // additional key paths (DragDropHandlers etc.) default=CLSID
std::vector<std::wstring> systemFileAssocExtensions; // e.g. .png -> Software\\Classes\\SystemFileAssociations\\.png\\ShellEx\\ContextMenuHandlers\\<HandlerName>
std::wstring systemFileAssocHandlerName; // e.g. ImageResizer
std::wstring representativeSystemExt; // used to decide if associations need repair (.png)
bool logRepairs = true;
};
namespace detail
{
// Minimal RAII wrapper for HKEY
struct unique_hkey
{
HKEY h{ nullptr };
unique_hkey() = default;
explicit unique_hkey(HKEY handle) : h(handle) {}
~unique_hkey() { if (h) RegCloseKey(h); }
unique_hkey(const unique_hkey&) = delete;
unique_hkey& operator=(const unique_hkey&) = delete;
unique_hkey(unique_hkey&& other) noexcept : h(other.h) { other.h = nullptr; }
unique_hkey& operator=(unique_hkey&& other) noexcept { if (this != &other) { if (h) RegCloseKey(h); h = other.h; other.h = nullptr; } return *this; }
HKEY get() const { return h; }
HKEY* put() { if (h) { RegCloseKey(h); h = nullptr; } return &h; }
};
inline std::wstring base_dir_from_module(HMODULE h)
{
wchar_t buf[MAX_PATH];
if (GetModuleFileNameW(h, buf, MAX_PATH))
{
PathRemoveFileSpecW(buf);
return buf;
}
return L"";
}
inline std::wstring pick_existing_dll(const std::wstring& base, const std::vector<std::wstring>& candidates)
{
for (const auto& rel : candidates)
{
std::wstring full = base + L"\\" + rel;
if (GetFileAttributesW(full.c_str()) != INVALID_FILE_ATTRIBUTES)
{
return full;
}
}
if (!candidates.empty())
{
return base + L"\\" + candidates.front();
}
return L"";
}
inline bool sentinel_exists(const Spec& spec)
{
unique_hkey key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, KEY_READ, key.put()) != ERROR_SUCCESS)
return false;
DWORD v = 0; DWORD sz = sizeof(v);
return RegQueryValueExW(key.get(), spec.sentinelValue.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(&v), &sz) == ERROR_SUCCESS && v == 1;
}
inline void write_sentinel(const Spec& spec)
{
unique_hkey key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
{
DWORD one = 1;
RegSetValueExW(key.get(), spec.sentinelValue.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&one), sizeof(one));
}
}
inline void write_inproc_server(const Spec& spec, const std::wstring& dllPath)
{
using namespace std::string_literals;
std::wstring clsidRoot = L"Software\\Classes\\CLSID\\"s + spec.clsid;
std::wstring inprocKey = clsidRoot + L"\\InprocServer32";
{
unique_hkey key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, clsidRoot.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
{
if (!spec.friendlyName.empty())
{
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(spec.friendlyName.c_str()), static_cast<DWORD>((spec.friendlyName.size() + 1) * sizeof(wchar_t)));
}
if (spec.writeOptInEmptyValue)
{
const wchar_t* optIn = L"ContextMenuOptIn";
const wchar_t empty = L'\0';
RegSetValueExW(key.get(), optIn, 0, REG_SZ, reinterpret_cast<const BYTE*>(&empty), sizeof(empty));
}
}
}
unique_hkey key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, inprocKey.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
{
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(dllPath.c_str()), static_cast<DWORD>((dllPath.size() + 1) * sizeof(wchar_t)));
if (spec.writeThreadingModel)
{
const wchar_t* tm = L"Apartment";
RegSetValueExW(key.get(), L"ThreadingModel", 0, REG_SZ, reinterpret_cast<const BYTE*>(tm), static_cast<DWORD>((wcslen(tm) + 1) * sizeof(wchar_t)));
}
}
}
inline std::wstring read_inproc_server(const Spec& spec)
{
using namespace std::string_literals;
std::wstring inprocKey = L"Software\\Classes\\CLSID\\"s + spec.clsid + L"\\InprocServer32";
unique_hkey key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, inprocKey.c_str(), 0, KEY_READ, key.put()) != ERROR_SUCCESS)
return L"";
wchar_t buf[MAX_PATH]; DWORD sz = sizeof(buf);
if (RegQueryValueExW(key.get(), nullptr, nullptr, nullptr, reinterpret_cast<LPBYTE>(buf), &sz) == ERROR_SUCCESS)
return std::wstring(buf);
return L"";
}
inline void write_default_value_key(const std::wstring& keyPath, const std::wstring& value)
{
unique_hkey key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, keyPath.c_str(), 0, nullptr, 0, KEY_WRITE, nullptr, key.put(), nullptr) == ERROR_SUCCESS)
{
RegSetValueExW(key.get(), nullptr, 0, REG_SZ, reinterpret_cast<const BYTE*>(value.c_str()), static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)));
}
}
inline bool representative_association_exists(const Spec& spec)
{
using namespace std::string_literals;
if (spec.representativeSystemExt.empty() || spec.systemFileAssocHandlerName.empty())
return true;
std::wstring keyPath = L"Software\\Classes\\SystemFileAssociations\\"s + spec.representativeSystemExt + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
unique_hkey key;
return RegOpenKeyExW(HKEY_CURRENT_USER, keyPath.c_str(), 0, KEY_READ, key.put()) == ERROR_SUCCESS;
}
}
inline bool EnsureRegistered(const Spec& spec, HMODULE moduleInstance)
{
using namespace std::string_literals;
auto base = detail::base_dir_from_module(moduleInstance);
auto dllPath = detail::pick_existing_dll(base, spec.dllFileCandidates);
if (dllPath.empty())
{
Logger::error(L"Runtime registration: cannot locate dll path for CLSID {}", spec.clsid);
return false;
}
bool exists = detail::sentinel_exists(spec);
bool repaired = false;
if (exists)
{
auto current = detail::read_inproc_server(spec);
if (_wcsicmp(current.c_str(), dllPath.c_str()) != 0)
{
detail::write_inproc_server(spec, dllPath);
repaired = true;
}
if (!detail::representative_association_exists(spec))
{
repaired = true;
}
}
if (!exists)
{
detail::write_inproc_server(spec, dllPath);
}
if (!exists || repaired)
{
for (const auto& path : spec.contextMenuHandlerKeyPaths)
{
detail::write_default_value_key(path, spec.clsid);
}
for (const auto& path : spec.extraAssociationPaths)
{
detail::write_default_value_key(path, spec.clsid);
}
if (!spec.systemFileAssocExtensions.empty() && !spec.systemFileAssocHandlerName.empty())
{
for (const auto& ext : spec.systemFileAssocExtensions)
{
std::wstring path = L"Software\\Classes\\SystemFileAssociations\\"s + ext + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
detail::write_default_value_key(path, spec.clsid);
}
}
}
if (!exists)
{
detail::write_sentinel(spec);
Logger::info(L"Runtime registration completed for CLSID {}", spec.clsid);
}
else if (repaired && spec.logRepairs)
{
Logger::info(L"Runtime registration repaired for CLSID {}", spec.clsid);
}
return true;
}
inline bool Unregister(const Spec& spec)
{
using namespace std::string_literals;
// Remove handler key paths
for (const auto& path : spec.contextMenuHandlerKeyPaths)
{
RegDeleteTreeW(HKEY_CURRENT_USER, path.c_str());
}
// Remove extra association paths (e.g., drag & drop handlers)
for (const auto& path : spec.extraAssociationPaths)
{
RegDeleteTreeW(HKEY_CURRENT_USER, path.c_str());
}
// Remove per-extension system file association handler keys
if (!spec.systemFileAssocExtensions.empty() && !spec.systemFileAssocHandlerName.empty())
{
for (const auto& ext : spec.systemFileAssocExtensions)
{
std::wstring keyPath = L"Software\\Classes\\SystemFileAssociations\\"s + ext + L"\\ShellEx\\ContextMenuHandlers\\" + spec.systemFileAssocHandlerName;
RegDeleteTreeW(HKEY_CURRENT_USER, keyPath.c_str());
}
}
// Remove CLSID branch
if (!spec.clsid.empty())
{
std::wstring clsidRoot = L"Software\\Classes\\CLSID\\"s + spec.clsid;
RegDeleteTreeW(HKEY_CURRENT_USER, clsidRoot.c_str());
}
// Remove sentinel value (not deleting entire key to avoid disturbing other values)
if (!spec.sentinelKey.empty() && !spec.sentinelValue.empty())
{
HKEY hKey{};
if (RegOpenKeyExW(HKEY_CURRENT_USER, spec.sentinelKey.c_str(), 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS)
{
RegDeleteValueW(hKey, spec.sentinelValue.c_str());
RegCloseKey(hKey);
}
}
Logger::info(L"Successfully unregistered CLSID {}", spec.clsid);
return true;
}
}

View File

@@ -73,6 +73,7 @@
<ClInclude Include="ClassFactory.h" />
<ClInclude Include="dllmain.h" />
<ClInclude Include="ExplorerCommand.h" />
<ClInclude Include="RuntimeRegistration.h" />
<ClInclude Include="pch.h" />
<None Include="packages.config" />
<None Include="resource.base.h" />

View File

@@ -27,6 +27,9 @@
<ClInclude Include="dllmain.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RuntimeRegistration.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">

View File

@@ -12,6 +12,7 @@
#include "FileLocksmithLib/Constants.h"
#include "FileLocksmithLib/Settings.h"
#include "FileLocksmithLib/Trace.h"
#include "RuntimeRegistration.h"
#include "dllmain.h"
#include "Generated Files/resource.h"
@@ -82,12 +83,17 @@ public:
{
std::wstring path = get_module_folderpath(globals::instance);
std::wstring packageUri = path + L"\\FileLocksmithContextMenuPackage.msix";
if (!package::IsPackageRegisteredWithPowerToysVersion(constants::nonlocalizable::ContextMenuPackageName))
{
package::RegisterSparsePackage(path, packageUri);
}
}
else
{
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
FileLocksmithRuntimeRegistration::EnsureRegistered();
#endif
}
m_enabled = true;
}
@@ -95,6 +101,13 @@ public:
virtual void disable() override
{
Logger::info(L"File Locksmith disabled");
if (!package::IsWin11OrGreater())
{
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
FileLocksmithRuntimeRegistration::Unregister();
Logger::info(L"File Locksmith context menu unregistered (Win10)");
#endif
}
m_enabled = false;
}

View File

@@ -0,0 +1,36 @@
// Header-only runtime registration for FileLocksmith context menu extension.
#pragma once
#include <common/utils/shell_ext_registration.h>
namespace globals { extern HMODULE instance; }
namespace FileLocksmithRuntimeRegistration
{
namespace
{
inline runtime_shell_ext::Spec BuildSpec()
{
runtime_shell_ext::Spec spec;
spec.clsid = L"{84D68575-E186-46AD-B0CB-BAEB45EE29C0}";
spec.sentinelKey = L"Software\\Microsoft\\PowerToys\\FileLocksmith";
spec.sentinelValue = L"ContextMenuRegistered";
spec.dllFileCandidates = { L"PowerToys.FileLocksmithExt.dll" };
spec.contextMenuHandlerKeyPaths = {
L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt",
L"Software\\Classes\\Drive\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt" };
spec.friendlyName = L"File Locksmith Shell Extension";
return spec;
}
}
inline bool EnsureRegistered()
{
return runtime_shell_ext::EnsureRegistered(BuildSpec(), globals::instance);
}
inline void Unregister()
{
runtime_shell_ext::Unregister(BuildSpec());
}
}

View File

@@ -5,6 +5,7 @@
#include "MouseHighlighter.h"
#include "trace.h"
#include <cmath>
#include <algorithm>
#ifdef COMPOSITION
namespace winrt
@@ -49,6 +50,9 @@ private:
void BringToFront();
HHOOK m_mouseHook = NULL;
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) noexcept;
// Helpers for spotlight overlay
float GetDpiScale() const;
void UpdateSpotlightMask(float cx, float cy, float radius, bool show);
static constexpr auto m_className = L"MouseHighlighter";
static constexpr auto m_windowTitle = L"PowerToys Mouse Highlighter";
@@ -67,7 +71,14 @@ private:
winrt::CompositionSpriteShape m_leftPointer{ nullptr };
winrt::CompositionSpriteShape m_rightPointer{ nullptr };
winrt::CompositionSpriteShape m_alwaysPointer{ nullptr };
winrt::CompositionSpriteShape m_spotlightPointer{ nullptr };
// Spotlight overlay (mask with soft feathered edge)
winrt::SpriteVisual m_overlay{ nullptr };
winrt::CompositionMaskBrush m_spotlightMask{ nullptr };
winrt::CompositionRadialGradientBrush m_spotlightMaskGradient{ nullptr };
winrt::CompositionColorBrush m_spotlightSource{ nullptr };
winrt::CompositionColorGradientStop m_maskStopCenter{ nullptr };
winrt::CompositionColorGradientStop m_maskStopInner{ nullptr };
winrt::CompositionColorGradientStop m_maskStopOuter{ nullptr };
bool m_leftPointerEnabled = true;
bool m_rightPointerEnabled = true;
@@ -123,6 +134,35 @@ bool Highlighter::CreateHighlighter()
m_shape.RelativeSizeAdjustment({ 1.0f, 1.0f });
m_root.Children().InsertAtTop(m_shape);
// Create spotlight overlay (soft feather, DPI-aware)
m_overlay = m_compositor.CreateSpriteVisual();
m_overlay.RelativeSizeAdjustment({ 1.0f, 1.0f });
m_spotlightSource = m_compositor.CreateColorBrush(m_alwaysColor);
m_spotlightMaskGradient = m_compositor.CreateRadialGradientBrush();
m_spotlightMaskGradient.MappingMode(winrt::CompositionMappingMode::Absolute);
// Center region fully transparent
m_maskStopCenter = m_compositor.CreateColorGradientStop();
m_maskStopCenter.Offset(0.0f);
m_maskStopCenter.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
// Inner edge of feather (still transparent)
m_maskStopInner = m_compositor.CreateColorGradientStop();
m_maskStopInner.Offset(0.995f); // will be updated per-radius
m_maskStopInner.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
// Outer edge (opaque mask -> overlay visible)
m_maskStopOuter = m_compositor.CreateColorGradientStop();
m_maskStopOuter.Offset(1.0f);
m_maskStopOuter.Color(winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255));
m_spotlightMaskGradient.ColorStops().Append(m_maskStopCenter);
m_spotlightMaskGradient.ColorStops().Append(m_maskStopInner);
m_spotlightMaskGradient.ColorStops().Append(m_maskStopOuter);
m_spotlightMask = m_compositor.CreateMaskBrush();
m_spotlightMask.Source(m_spotlightSource);
m_spotlightMask.Mask(m_spotlightMaskGradient);
m_overlay.Brush(m_spotlightMask);
m_overlay.IsVisible(false);
m_root.Children().InsertAtTop(m_overlay);
return true;
}
catch (...)
@@ -165,12 +205,8 @@ void Highlighter::AddDrawingPoint(MouseButton button)
// always
if (m_spotlightMode)
{
float borderThickness = static_cast<float>(std::hypot(GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN)));
circleGeometry.Radius({ static_cast<float>(borderThickness / 2.0 + m_radius), static_cast<float>(borderThickness / 2.0 + m_radius) });
circleShape.FillBrush(nullptr);
circleShape.StrokeBrush(m_compositor.CreateColorBrush(m_alwaysColor));
circleShape.StrokeThickness(borderThickness);
m_spotlightPointer = circleShape;
UpdateSpotlightMask(static_cast<float>(pt.x), static_cast<float>(pt.y), m_radius, true);
return;
}
else
{
@@ -209,20 +245,14 @@ void Highlighter::UpdateDrawingPointPosition(MouseButton button)
}
else
{
// always
// always / spotlight idle
if (m_spotlightMode)
{
if (m_spotlightPointer)
{
m_spotlightPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
}
UpdateSpotlightMask(static_cast<float>(pt.x), static_cast<float>(pt.y), m_radius, true);
}
else
else if (m_alwaysPointer)
{
if (m_alwaysPointer)
{
m_alwaysPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
}
m_alwaysPointer.Offset({ static_cast<float>(pt.x), static_cast<float>(pt.y) });
}
}
}
@@ -266,9 +296,9 @@ void Highlighter::ClearDrawingPoint()
{
if (m_spotlightMode)
{
if (m_spotlightPointer)
if (m_overlay)
{
m_spotlightPointer.StrokeBrush().as<winrt::Windows::UI::Composition::CompositionColorBrush>().Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
m_overlay.IsVisible(false);
}
}
else
@@ -421,7 +451,10 @@ void Highlighter::StopDrawing()
m_leftPointer = nullptr;
m_rightPointer = nullptr;
m_alwaysPointer = nullptr;
m_spotlightPointer = nullptr;
if (m_overlay)
{
m_overlay.IsVisible(false);
}
ShowWindow(m_hwnd, SW_HIDE);
UnhookWindowsHookEx(m_mouseHook);
ClearDrawing();
@@ -452,6 +485,16 @@ void Highlighter::ApplySettings(MouseHighlighterSettings settings)
m_rightPointerEnabled = false;
}
// Keep spotlight overlay color updated
if (m_spotlightSource)
{
m_spotlightSource.Color(m_alwaysColor);
}
if (!m_spotlightMode && m_overlay)
{
m_overlay.IsVisible(false);
}
if (instance->m_visible)
{
instance->StopDrawing();
@@ -563,6 +606,43 @@ void Highlighter::Terminate()
}
}
float Highlighter::GetDpiScale() const
{
return static_cast<float>(GetDpiForWindow(m_hwnd)) / 96.0f;
}
// Update spotlight radial mask center/radius with DPI-aware feather
void Highlighter::UpdateSpotlightMask(float cx, float cy, float radius, bool show)
{
if (!m_spotlightMaskGradient)
{
return;
}
m_spotlightMaskGradient.EllipseCenter({ cx, cy });
m_spotlightMaskGradient.EllipseRadius({ radius, radius });
const float dpiScale = GetDpiScale();
// Target a very fine edge: ~1 physical pixel, convert to DIPs: 1 / dpiScale
const float featherDip = 1.0f / (dpiScale > 0.0f ? dpiScale : 1.0f);
const float safeRadius = (std::max)(radius, 1.0f);
const float featherRel = (std::min)(0.25f, featherDip / safeRadius);
if (m_maskStopInner)
{
m_maskStopInner.Offset((std::max)(0.0f, 1.0f - featherRel));
}
if (m_spotlightSource)
{
m_spotlightSource.Color(m_alwaysColor);
}
if (m_overlay)
{
m_overlay.IsVisible(show);
}
}
#pragma region MouseHighlighter_API
void MouseHighlighterApplySettings(MouseHighlighterSettings settings)

View File

@@ -123,6 +123,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
<ClInclude Include="settings.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="new_utilities.h" />
<ClInclude Include="RuntimeRegistration.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="template_folder.h" />
<ClInclude Include="pch.h" />

View File

@@ -84,6 +84,9 @@
<ClInclude Include="helpers_variables.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RuntimeRegistration.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -0,0 +1,36 @@
// Header-only runtime registration for New+ Win10 context menu.
#pragma once
#include <windows.h>
#include <string>
#include <common/utils/shell_ext_registration.h>
// Provided by dll_main.cpp
extern HMODULE module_instance_handle;
namespace NewPlusRuntimeRegistration
{
namespace {
inline runtime_shell_ext::Spec BuildSpec()
{
runtime_shell_ext::Spec spec;
spec.clsid = L"{FF90D477-E32A-4BE8-8CC5-A502A97F5401}";
spec.sentinelKey = L"Software\\Microsoft\\PowerToys\\NewPlus";
spec.sentinelValue = L"ContextMenuRegisteredWin10";
spec.dllFileCandidates = { L"PowerToys.NewPlus.ShellExtension.win10.dll" };
spec.contextMenuHandlerKeyPaths = { L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\NewPlusShellExtensionWin10" };
spec.friendlyName = L"NewPlus Shell Extension Win10";
return spec;
}
}
inline bool EnsureRegisteredWin10()
{
return runtime_shell_ext::EnsureRegistered(BuildSpec(), module_instance_handle);
}
inline void Unregister()
{
runtime_shell_ext::Unregister(BuildSpec());
}
}

View File

@@ -16,6 +16,7 @@
#include "trace.h"
#include "new_utilities.h"
#include "Generated Files/resource.h"
#include "RuntimeRegistration.h"
// Note: Settings are managed via Settings and UI Settings
class NewModule : public PowertoyModuleIface
@@ -93,8 +94,16 @@ public:
// Log telemetry
Trace::EventToggleOnOff(true);
newplus::utilities::register_msix_package();
if (package::IsWin11OrGreater())
{
newplus::utilities::register_msix_package();
}
else
{
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
NewPlusRuntimeRegistration::EnsureRegisteredWin10();
#endif
}
powertoy_new_enabled = true;
}
@@ -141,6 +150,13 @@ private:
{
Trace::EventToggleOnOff(false);
}
if (!package::IsWin11OrGreater())
{
#if defined(ENABLE_REGISTRATION) || defined(NDEBUG)
NewPlusRuntimeRegistration::Unregister();
Logger::info(L"New+ context menu unregistered (Win10)");
#endif
}
powertoy_new_enabled = false;
}

View File

@@ -12,6 +12,6 @@
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
</ItemGroup>
</Project>

View File

@@ -89,7 +89,7 @@ public class SupersedingAsyncGate : IDisposable
}
catch (OperationCanceledException)
{
CompleteIfCurrent(currentTcs, currentCallId, tcs => tcs.SetCanceled(currentCts.Token));
CompleteIfCurrent(currentTcs, currentCallId, tcs => tcs.TrySetCanceled(currentCts.Token));
}
catch (Exception ex)
{

View File

@@ -2,11 +2,14 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Core.ViewModels;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference<IPageContext> context) : CommandItemViewModel(new(contextItem), context), IContextItemViewModel
{
private readonly KeyChord nullKeyChord = new(0, 0, 0);

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
@@ -9,6 +10,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Core.ViewModels;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBarContext
{
public ExtensionObject<ICommandItem> Model => _commandItemModel;

View File

@@ -4,12 +4,14 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Core.ViewModels;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public interface IContextItemViewModel
{
}

View File

@@ -2,11 +2,13 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Core.ViewModels;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public partial class SeparatorContextItemViewModel() : IContextItemViewModel, ISeparatorContextItem
{
}

View File

@@ -9,6 +9,7 @@ using ManagedCommon;
using Microsoft.CmdPal.Common.Helpers;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.Ext.Apps;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.Extensions.DependencyInjection;
@@ -60,6 +61,7 @@ public partial class MainListPage : DynamicListPage,
var settings = _serviceProvider.GetService<SettingsModel>()!;
settings.SettingsChanged += SettingsChangedHandler;
HotReloadSettings(settings);
_includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId);
IsLoading = true;
}

View File

@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;

View File

@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;

View File

@@ -4,6 +4,7 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;

View File

@@ -98,35 +98,107 @@ public partial class ContentFormViewModel(IFormContent _form, WeakReference<IPag
public void HandleSubmit(IAdaptiveActionElement action, JsonObject inputs)
{
if (action is AdaptiveOpenUrlAction openUrlAction)
{
WeakReferenceMessenger.Default.Send<LaunchUriMessage>(new(openUrlAction.Url));
return;
}
// BODGY circa GH #40979
// Usually, you're supposed to try to cast the action to a specific
// type, and use those objects to get the data you need.
// However, there's something weird with AdaptiveCards and the way it
// works when we consume it when built in Release, with AOT (and
// trimming) enabled. Any sort of `action.As<IAdaptiveSubmitAction>()`
// or similar will throw a System.InvalidCastException.
//
// Instead we have this horror show.
//
// The `action.ToJson()` blob ACTUALLY CONTAINS THE `type` field, which
// we can use to determine what kind of action it is. Then we can parse
// the JSON manually based on the type.
var actionJson = action.ToJson();
if (action is AdaptiveSubmitAction or AdaptiveExecuteAction)
if (actionJson.TryGetValue("type", out var actionTypeValue))
{
// Get the data and inputs
var dataString = (action as AdaptiveSubmitAction)?.DataJson.Stringify() ?? string.Empty;
var inputString = inputs.Stringify();
var actionTypeString = actionTypeValue.GetString();
Logger.LogTrace($"atString={actionTypeString}");
_ = Task.Run(() =>
var actionType = actionTypeString switch
{
try
{
var model = _formModel.Unsafe!;
if (model != null)
"Action.Submit" => ActionType.Submit,
"Action.Execute" => ActionType.Execute,
"Action.OpenUrl" => ActionType.OpenUrl,
_ => ActionType.Unsupported,
};
Logger.LogDebug($"{actionTypeString}->{actionType}");
switch (actionType)
{
case ActionType.OpenUrl:
{
var result = model.SubmitForm(inputString, dataString);
WeakReferenceMessenger.Default.Send<HandleCommandResultMessage>(new(new(result)));
HandleOpenUrlAction(action, actionJson);
}
}
catch (Exception ex)
{
ShowException(ex);
}
});
break;
case ActionType.Submit:
case ActionType.Execute:
{
HandleSubmitAction(action, actionJson, inputs);
}
break;
default:
Logger.LogError($"{actionType} was an unexpected action `type`");
break;
}
}
else
{
Logger.LogError($"actionJson.TryGetValue(type) failed");
}
}
private void HandleOpenUrlAction(IAdaptiveActionElement action, JsonObject actionJson)
{
if (actionJson.TryGetValue("url", out var actionUrlValue))
{
var actionUrl = actionUrlValue.GetString() ?? string.Empty;
if (Uri.TryCreate(actionUrl, default(UriCreationOptions), out var uri))
{
WeakReferenceMessenger.Default.Send<LaunchUriMessage>(new(uri));
}
else
{
Logger.LogError($"Failed to produce URI for {actionUrlValue}");
}
}
}
private void HandleSubmitAction(
IAdaptiveActionElement action,
JsonObject actionJson,
JsonObject inputs)
{
var dataString = string.Empty;
if (actionJson.TryGetValue("data", out var actionDataValue))
{
dataString = actionDataValue.Stringify() ?? string.Empty;
}
var inputString = inputs.Stringify();
_ = Task.Run(() =>
{
try
{
var model = _formModel.Unsafe!;
if (model != null)
{
var result = model.SubmitForm(inputString, dataString);
Logger.LogDebug($"SubmitForm() returned {result}");
WeakReferenceMessenger.Default.Send<HandleCommandResultMessage>(new(new(result)));
}
}
catch (Exception ex)
{
ShowException(ex);
}
});
}
private static readonly string ErrorCardJson = """

View File

@@ -2,7 +2,7 @@
// 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.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.Messages;
public record OpenSettingsMessage()
{

View File

@@ -2,7 +2,7 @@
// 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.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
/// <summary>
/// Message which closes the application. Used by <see cref="QuitCommand"/> via <see cref="BuiltInsCommandProvider"/>.

View File

@@ -2,7 +2,7 @@
// 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.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
public record ReloadCommandsMessage()
{

View File

@@ -2,7 +2,7 @@
// 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.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
public record UpdateFallbackItemsMessage()
{

View File

@@ -7,7 +7,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Properties;
using Microsoft.Extensions.DependencyInjection;

View File

@@ -12,7 +12,7 @@ using ManagedCommon;
using Microsoft.CmdPal.Common.Helpers;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.Extensions.DependencyInjection;
@@ -151,7 +151,7 @@ public partial class TopLevelCommandManager : ObservableObject,
WeakReference<IPageContext> weakSelf = new(this);
await sender.LoadTopLevelCommands(_serviceProvider, weakSelf);
List<TopLevelViewModel> newItems = [..sender.TopLevelItems];
List<TopLevelViewModel> newItems = [.. sender.TopLevelItems];
foreach (var i in sender.FallbackItems)
{
if (i.IsEnabled)

View File

@@ -8,6 +8,7 @@ using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Settings;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;

View File

@@ -64,6 +64,9 @@ public partial class App : Application
this.InitializeComponent();
// Ensure types used in XAML are preserved for AOT compilation
TypePreservation.PreserveTypes();
NativeEventWaiter.WaitForEventLoop(
"Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd", () =>
{

View File

@@ -59,8 +59,24 @@
<Setter Property="BorderBrush" Value="{ThemeResource DividerStrokeColorDefaultBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="6" />
<Setter Property="MinWidth" Value="20" />
</Style.Setters>
</Style>
<Style x:Key="HotkeyTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="10" />
<Setter Property="CharacterSpacing" Value="4" />
<Setter Property="Foreground" Value="{ThemeResource TextFillColorPrimaryBrush}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style x:Key="HotkeyFontIconStyle" TargetType="FontIcon">
<Setter Property="FontSize" Value="10" />
<Setter Property="Foreground" Value="{ThemeResource TextFillColorPrimaryBrush}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
@@ -155,12 +171,7 @@
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.PrimaryCommand.Name, Mode=OneWay}" />
<Border Style="{StaticResource HotkeyStyle}">
<FontIcon
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="10"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Glyph="&#xE751;" />
<FontIcon Glyph="&#xE751;" Style="{StaticResource HotkeyFontIconStyle}" />
</Border>
</StackPanel>
</Button>
@@ -179,19 +190,10 @@
Text="{x:Bind ViewModel.SecondaryCommand.Name, Mode=OneWay}" />
<StackPanel Orientation="Horizontal" Spacing="4">
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
<TextBlock
CharacterSpacing="4"
FontSize="10"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Text="Ctrl" />
<TextBlock Style="{StaticResource HotkeyTextBlockStyle}" Text="Ctrl" />
</Border>
<Border Style="{StaticResource HotkeyStyle}">
<FontIcon
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="10"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Glyph="&#xE751;" />
<FontIcon Glyph="&#xE751;" Style="{StaticResource HotkeyFontIconStyle}" />
</Border>
</StackPanel>
</StackPanel>
@@ -199,7 +201,7 @@
<Button
x:Name="MoreCommandsButton"
x:Uid="MoreCommandsButton"
Padding="4"
Padding="6,4,4,4"
Click="MoreCommandsButton_Clicked"
Style="{StaticResource SubtleButtonStyle}"
ToolTipService.ToolTip="Ctrl+K"
@@ -209,32 +211,12 @@
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="More" />
<StackPanel Orientation="Horizontal" Spacing="2">
<Border
Padding="4,2,4,2"
VerticalAlignment="Center"
Background="{ThemeResource SubtleFillColorSecondaryBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="4">
<TextBlock
CharacterSpacing="4"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Ctrl" />
<StackPanel Orientation="Horizontal" Spacing="4">
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
<TextBlock Style="{StaticResource HotkeyTextBlockStyle}" Text="Ctrl" />
</Border>
<Border
Padding="4,2,4,2"
VerticalAlignment="Center"
Background="{ThemeResource SubtleFillColorSecondaryBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="4">
<TextBlock
VerticalAlignment="Center"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="K" />
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
<TextBlock Style="{StaticResource HotkeyTextBlockStyle}" Text="K" />
</Border>
</StackPanel>
</StackPanel>

View File

@@ -15,7 +15,7 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" />
<TextBlock Grid.Row="0" IsTabStop="True" />
<ItemsControl
x:Name="KeysControl"

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -10,6 +11,7 @@ using Microsoft.UI.Xaml.Data;
namespace Microsoft.CmdPal.UI;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
internal sealed partial class ContextItemTemplateSelector : DataTemplateSelector
{
public DataTemplate? Default { get; set; }

View File

@@ -5,8 +5,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Xaml;
using Windows.Win32;
using Windows.Win32.Foundation;

View File

@@ -10,11 +10,12 @@ using ManagedCommon;
using Microsoft.CmdPal.Common.Helpers;
using Microsoft.CmdPal.Common.Messages;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Events;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Composition;

View File

@@ -2,7 +2,7 @@
// 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.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.Messages;
public record HotkeySummonMessage(string CommandId, IntPtr Hwnd)
{

View File

@@ -2,7 +2,7 @@
// 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.CmdPal.Core.ViewModels.Messages;
namespace Microsoft.CmdPal.UI.Messages;
public record SettingsWindowClosedMessage
{

View File

@@ -109,10 +109,6 @@
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<RdXmlFile Include="rd.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\ext\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj" />

View File

@@ -9,6 +9,7 @@ using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Events;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.Settings;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CommandPalette.Extensions;
@@ -17,7 +18,6 @@ using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
@@ -242,6 +242,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
}
_settingsWindow.Activate();
_settingsWindow.BringToFront();
}
public void Receive(ShowDetailsMessage message)

View File

@@ -4,9 +4,10 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

View File

@@ -6,8 +6,8 @@ using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Windows.Win32;

View File

@@ -0,0 +1,40 @@
// 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 System.Diagnostics.CodeAnalysis;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.CmdPal.UI;
/// <summary>
/// This class ensures types used in XAML are preserved during AOT compilation.
/// Framework types cannot have attributes added directly to their definitions since they're external types.
/// Application types that require runtime type checking should also be preserved here if needed.
/// </summary>
internal static class TypePreservation
{
/// <summary>
/// This method ensures critical types are preserved for AOT compilation.
/// These types are used dynamically in XAML and would otherwise be trimmed.
/// </summary>
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Microsoft.UI.Xaml.Controls.FontIconSource))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Microsoft.UI.Xaml.Controls.PathIcon))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Microsoft.UI.Xaml.DataTemplate))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Microsoft.UI.Xaml.Controls.DataTemplateSelector))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Microsoft.UI.Xaml.Controls.ListViewItem))]
public static void PreserveTypes()
{
// This method exists only to hold the DynamicDependency attributes above.
// It must be called to ensure the types are not trimmed during AOT compilation.
// Note: We cannot add [DynamicallyAccessedMembers] directly to framework types
// since we don't own their source code. DynamicDependency is the correct approach
// for preserving external types that are used dynamically (e.g., in XAML).
// For application types that require runtime type checking (e.g., in template selectors),
// prefer adding [DynamicallyAccessedMembers] attributes directly on the type definitions.
// Only use DynamicDependency here for types we cannot modify directly.
}
}

View File

@@ -1,23 +0,0 @@
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="Microsoft.WinUI">
<Type Name="Microsoft.UI.Xaml.Controls.FontIconSource" Dynamic="Required All" />
<Type Name="Microsoft.UI.Xaml.DataTemplate" Dynamic="Required All" />
<Type Name="Microsoft.UI.Xaml.Controls.DataTemplateSelector" Dynamic="Required All" />
<Type Name="Microsoft.UI.Xaml.Controls.ListViewItem" Dynamic="Required All" />
</Assembly>
<!-- Add ViewModel types for AOT compatibility -->
<Assembly Name="Microsoft.CmdPal.Core.ViewModels">
<Type Name="Microsoft.CmdPal.Core.ViewModels.CommandContextItemViewModel" Dynamic="Required All" />
<Type Name="Microsoft.CmdPal.Core.ViewModels.SeparatorContextItemViewModel" Dynamic="Required All" />
<Type Name="Microsoft.CmdPal.Core.ViewModels.IContextItemViewModel" Dynamic="Required All" />
<Type Name="Microsoft.CmdPal.Core.ViewModels.CommandItemViewModel" Dynamic="Required All" />
</Assembly>
<!-- Add UI types for AOT compatibility -->
<Assembly Name="Microsoft.CmdPal.UI">
<Type Name="Microsoft.CmdPal.UI.ContextItemTemplateSelector" Dynamic="Required All" />
</Assembly>
</Application>
</Directives>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// 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.
@@ -6,12 +6,15 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.CmdPal.Ext.Calc.Helper;
using Microsoft.CmdPal.Ext.UnitTestBase;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.Calc.UnitTests;
[TestClass]
public class ExtendedCalculatorParserTests
public class ExtendedCalculatorParserTests : CommandPaletteUnitTestBase
{
[DataTestMethod]
[DataRow(null)]
@@ -28,7 +31,7 @@ public class ExtendedCalculatorParserTests
[DataRow("[10,10]")] // '[10,10]' is interpreted as array by mages engine
public void Interpret_NoResult_WhenCalled(string input)
{
var settings = new SettingsManager();
var settings = new Settings();
var result = CalculateEngine.Interpret(settings, input, CultureInfo.CurrentCulture, out _);
@@ -68,7 +71,7 @@ public class ExtendedCalculatorParserTests
[DynamicData(nameof(Interpret_NoErrors_WhenCalledWithRounding_Data))]
public void Interpret_NoErrors_WhenCalledWithRounding(string input, decimal expectedResult)
{
var settings = new SettingsManager();
var settings = new Settings();
// Act
// Using InvariantCulture since this is internal
@@ -90,7 +93,7 @@ public class ExtendedCalculatorParserTests
public void Interpret_GreaterPrecision_WhenCalled(string input, decimal expectedResult)
{
// Arrange
var settings = new SettingsManager();
var settings = new Settings();
// Act
// Using InvariantCulture since this is internal
@@ -114,7 +117,7 @@ public class ExtendedCalculatorParserTests
{
// Arrange
var cultureInfo = CultureInfo.GetCultureInfo(cultureName);
var settings = new SettingsManager();
var settings = new Settings();
// Act
var result = CalculateEngine.Interpret(settings, input, cultureInfo, out _);
@@ -175,7 +178,7 @@ public class ExtendedCalculatorParserTests
public void Interpret_MustReturnResult_WhenResultIsZero(string input)
{
// Arrange
var settings = new SettingsManager();
var settings = new Settings();
// Act
// Using InvariantCulture since this is internal
@@ -203,7 +206,7 @@ public class ExtendedCalculatorParserTests
public void Interpret_MustReturnExpectedResult_WhenCalled(string input, decimal expectedResult)
{
// Arrange
var settings = new SettingsManager();
var settings = new Settings();
// Act
// Using en-us culture to have a fixed number style
@@ -226,7 +229,7 @@ public class ExtendedCalculatorParserTests
{
// Arrange
var translator = NumberTranslator.Create(new CultureInfo(sourceCultureName, false), new CultureInfo("en-US", false));
var settings = new SettingsManager();
var settings = new Settings();
// Act
// Using en-us culture to have a fixed number style

View File

@@ -14,5 +14,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ext\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj" />
<ProjectReference Include="..\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,86 @@
// 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 System.Linq;
using Microsoft.CmdPal.Ext.Calc.Helper;
using Microsoft.CmdPal.Ext.Calc.Pages;
using Microsoft.CmdPal.Ext.UnitTestBase;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.Calc.UnitTests;
[TestClass]
public class QueryTests : CommandPaletteUnitTestBase
{
[DataTestMethod]
[DataRow("2+2", "4")]
[DataRow("5*3", "15")]
[DataRow("10/2", "5")]
[DataRow("sqrt(16)", "4")]
[DataRow("2^3", "8")]
public void TopLevelPageQueryTest(string input, string expectedResult)
{
var settings = new Settings();
var page = new CalculatorListPage(settings);
// Simulate query execution
page.UpdateSearchText(string.Empty, input);
var result = page.GetItems();
Assert.IsTrue(result.Length == 1, "Valid input should always return result");
var firstResult = result.FirstOrDefault();
Assert.IsNotNull(result);
Assert.IsTrue(
firstResult.Title.Contains(expectedResult),
$"Expected result to contain '{expectedResult}' but got '{firstResult.Title}'");
}
[TestMethod]
public void EmptyQueryTest()
{
var settings = new Settings();
var page = new CalculatorListPage(settings);
page.UpdateSearchText("abc", string.Empty);
var results = page.GetItems();
Assert.IsNotNull(results);
var firstItem = results.FirstOrDefault();
Assert.AreEqual("Type an equation...", firstItem.Title);
}
[TestMethod]
public void InvalidExpressionTest()
{
var settings = new Settings();
var page = new CalculatorListPage(settings);
// Simulate query execution
page.UpdateSearchText(string.Empty, "invalid expression");
var result = page.GetItems().FirstOrDefault();
Assert.AreEqual("Type an equation...", result.Title);
}
[DataTestMethod]
[DataRow("sin(60)", "-0.30481", CalculateEngine.TrigMode.Radians)]
[DataRow("sin(60)", "0.866025", CalculateEngine.TrigMode.Degrees)]
[DataRow("sin(60)", "0.809016", CalculateEngine.TrigMode.Gradians)]
public void TrigModeSettingsTest(string input, string expected, CalculateEngine.TrigMode trigMode)
{
var settings = new Settings(trigUnit: trigMode);
var page = new CalculatorListPage(settings);
page.UpdateSearchText(string.Empty, input);
var result = page.GetItems().FirstOrDefault();
Assert.IsNotNull(result);
Assert.IsTrue(result.Title.Contains(expected, System.StringComparison.Ordinal), $"Calc trigMode convert result isn't correct. Current result: {result.Title}");
}
}

View File

@@ -0,0 +1,35 @@
// 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.CmdPal.Ext.Calc.Helper;
namespace Microsoft.CmdPal.Ext.Calc.UnitTests;
public class Settings : ISettingsInterface
{
private readonly CalculateEngine.TrigMode trigUnit;
private readonly bool inputUseEnglishFormat;
private readonly bool outputUseEnglishFormat;
private readonly bool closeOnEnter;
public Settings(
CalculateEngine.TrigMode trigUnit = CalculateEngine.TrigMode.Radians,
bool inputUseEnglishFormat = false,
bool outputUseEnglishFormat = false,
bool closeOnEnter = true)
{
this.trigUnit = trigUnit;
this.inputUseEnglishFormat = inputUseEnglishFormat;
this.outputUseEnglishFormat = outputUseEnglishFormat;
this.closeOnEnter = closeOnEnter;
}
public CalculateEngine.TrigMode TrigUnit => trigUnit;
public bool InputUseEnglishFormat => inputUseEnglishFormat;
public bool OutputUseEnglishFormat => outputUseEnglishFormat;
public bool CloseOnEnter => closeOnEnter;
}

View File

@@ -0,0 +1,55 @@
// 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.CmdPal.Ext.Calc.Helper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.Calc.UnitTests;
[TestClass]
public class SettingsManagerTests
{
[TestMethod]
public void SettingsManagerInitializationTest()
{
// Act
var settingsManager = new SettingsManager();
// Assert
Assert.IsNotNull(settingsManager);
Assert.IsNotNull(settingsManager.Settings);
}
[TestMethod]
public void SettingsInterfaceTest()
{
// Act
ISettingsInterface settings = new SettingsManager();
// Assert
Assert.IsNotNull(settings);
Assert.IsTrue(settings.TrigUnit == CalculateEngine.TrigMode.Radians);
Assert.IsFalse(settings.InputUseEnglishFormat);
Assert.IsFalse(settings.OutputUseEnglishFormat);
Assert.IsTrue(settings.CloseOnEnter);
}
[TestMethod]
public void MockSettingsTest()
{
// Act
var settings = new Settings(
trigUnit: CalculateEngine.TrigMode.Degrees,
inputUseEnglishFormat: true,
outputUseEnglishFormat: true,
closeOnEnter: false);
// Assert
Assert.IsNotNull(settings);
Assert.AreEqual(CalculateEngine.TrigMode.Degrees, settings.TrigUnit);
Assert.IsTrue(settings.InputUseEnglishFormat);
Assert.IsTrue(settings.OutputUseEnglishFormat);
Assert.IsFalse(settings.CloseOnEnter);
}
}

View File

@@ -18,5 +18,6 @@
<ItemGroup>
<ProjectReference Include="..\..\ext\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj" />
<ProjectReference Include="..\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,74 @@
// 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 System.Linq;
using Microsoft.CmdPal.Ext.Registry.Helpers;
using Microsoft.CmdPal.Ext.UnitTestBase;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.Registry.UnitTests;
[TestClass]
public class QueryTests : CommandPaletteUnitTestBase
{
[DataTestMethod]
[DataRow("HKLM", "HKEY_LOCAL_MACHINE")]
[DataRow("HKCU", "HKEY_CURRENT_USER")]
[DataRow("HKCR", "HKEY_CLASSES_ROOT")]
[DataRow("HKU", "HKEY_USERS")]
[DataRow("HKCC", "HKEY_CURRENT_CONFIG")]
public void TopLevelPageQueryTest(string input, string expectedKeyName)
{
var settings = new Settings();
var page = new RegistryListPage(settings);
var results = page.Query(input);
Assert.IsNotNull(results);
Assert.IsTrue(results.Count > 0, "No items matched the query.");
var firstItem = results.FirstOrDefault();
Assert.IsNotNull(firstItem, "No items matched the query.");
Assert.IsTrue(
firstItem.Title.Contains(expectedKeyName, System.StringComparison.OrdinalIgnoreCase),
$"Expected to match '{expectedKeyName}' but got '{firstItem.Title}'");
}
[TestMethod]
public void EmptyQueryTest()
{
var settings = new Settings();
var page = new RegistryListPage(settings);
var results = page.Query(string.Empty);
Assert.IsNotNull(results);
// Empty query should return all base keys
Assert.IsTrue(results.Count >= 5, "Expected at least 5 base registry keys.");
}
[TestMethod]
public void NullQueryTest()
{
var settings = new Settings();
var page = new RegistryListPage(settings);
var results = page.Query(null);
Assert.IsNotNull(results);
Assert.AreEqual(0, results.Count, "Null query should return empty results.");
}
[TestMethod]
public void InvalidBaseKeyTest()
{
var settings = new Settings();
var page = new RegistryListPage(settings);
var results = page.Query("INVALID_KEY");
Assert.IsNotNull(results);
Assert.AreEqual(0, results.Count, "Invalid query should return empty results.");
}
}

View File

@@ -0,0 +1,15 @@
// 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.CmdPal.Ext.Registry.Helpers;
namespace Microsoft.CmdPal.Ext.Registry.UnitTests;
public class Settings : ISettingsInterface
{
public Settings()
{
// Currently no specific settings for Registry extension
}
}

View File

@@ -142,11 +142,7 @@ public class QueryTests : CommandPaletteUnitTestBase
// UEFI Firmware Settings command should exist
Assert.IsNotNull(result);
var firstItem = result.FirstOrDefault();
Assert.IsNotNull(firstItem, "No items matched the query.");
var containsFirmwareSettings = firstItem.Title.Contains("UEFI Firmware Settings", StringComparison.OrdinalIgnoreCase);
Assert.IsTrue(
containsFirmwareSettings == hasCommand,
$"Expected to match 'UEFI Firmware Settings' but got '{firstItem.Title}'");
var firstItemIsUefiCommand = firstItem?.Title.Contains("UEFI", StringComparison.OrdinalIgnoreCase) ?? false;
Assert.AreEqual(hasCommand, firstItemIsUefiCommand, $"Expected to match (or not match) 'UEFI Firmware Settings' but got '{firstItem?.Title}'");
}
}

View File

@@ -367,7 +367,7 @@ public class AvailableResultsListTests
public void UnixTimestampSecondsFormat()
{
// Setup
string formatLabel = "Unix epoch time";
var formatLabel = "Unix epoch time";
DateTime timeValue = DateTime.Now.ToUniversalTime();
var settings = new SettingsManager();
var helperResults = AvailableResultsList.GetList(true, settings, null, null, timeValue);
@@ -384,7 +384,7 @@ public class AvailableResultsListTests
public void UnixTimestampMillisecondsFormat()
{
// Setup
string formatLabel = "Unix epoch time in milliseconds";
var formatLabel = "Unix epoch time in milliseconds";
DateTime timeValue = DateTime.Now.ToUniversalTime();
var settings = new SettingsManager();
var helperResults = AvailableResultsList.GetList(true, settings, null, null, timeValue);
@@ -401,7 +401,7 @@ public class AvailableResultsListTests
public void WindowsFileTimeFormat()
{
// Setup
string formatLabel = "Windows file time (Int64 number)";
var formatLabel = "Windows file time (Int64 number)";
DateTime timeValue = DateTime.Now;
var settings = new SettingsManager();
var helperResults = AvailableResultsList.GetList(true, settings, null, null, timeValue);
@@ -418,7 +418,7 @@ public class AvailableResultsListTests
public void ValidateEraResult()
{
// Setup
string formatLabel = "Era";
var formatLabel = "Era";
DateTime timeValue = DateTime.Now;
var settings = new SettingsManager();
var helperResults = AvailableResultsList.GetList(true, settings, null, null, timeValue);
@@ -435,7 +435,7 @@ public class AvailableResultsListTests
public void ValidateEraAbbreviationResult()
{
// Setup
string formatLabel = "Era abbreviation";
var formatLabel = "Era abbreviation";
DateTime timeValue = DateTime.Now;
var settings = new SettingsManager();
var helperResults = AvailableResultsList.GetList(true, settings, null, null, timeValue);

View File

@@ -1,28 +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 System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.TimeDate.UnitTests;
[TestClass]
public class BasicTests
{
[TestMethod]
public void BasicTest()
{
// This is a basic test to verify the test project can run
Assert.IsTrue(true);
}
[TestMethod]
public void DateTimeTest()
{
// Test basic DateTime functionality
var now = DateTime.Now;
Assert.IsNotNull(now);
Assert.IsTrue(now > DateTime.MinValue);
}
}

View File

@@ -40,7 +40,7 @@ public class FallbackTimeDateItemTests
public void FallbackQueryTests(string query, string expectedTitle)
{
// Setup
var settingsManager = new SettingsManager();
var settingsManager = new Settings();
DateTime now = new DateTime(2025, 7, 1, 12, 0, 0); // Fixed date for testing
var fallbackItem = new FallbackTimeDateItem(settingsManager, now);
@@ -66,7 +66,7 @@ public class FallbackTimeDateItemTests
public void InvalidQueryTests(string query)
{
// Setup
var settingsManager = new SettingsManager();
var settingsManager = new Settings();
DateTime now = new DateTime(2025, 7, 1, 12, 0, 0); // Fixed date for testing
var fallbackItem = new FallbackTimeDateItem(settingsManager, now);
@@ -83,4 +83,26 @@ public class FallbackTimeDateItemTests
Assert.Fail($"UpdateQuery should not throw exceptions: {ex.Message}");
}
}
[DataTestMethod]
public void DisableFallbackItemTest()
{
// Setup
var settingsManager = new Settings(enableFallbackItems: false);
DateTime now = new DateTime(2025, 7, 1, 12, 0, 0); // Fixed date for testing
var fallbackItem = new FallbackTimeDateItem(settingsManager, now);
// Act & Assert - Test that UpdateQuery doesn't throw exceptions
try
{
fallbackItem.UpdateQuery("now");
Assert.AreEqual(string.Empty, fallbackItem.Title, "Title should be empty when disable fallback item");
Assert.AreEqual(string.Empty, fallbackItem.Subtitle, "Subtitle should be empty when disable fallback item");
}
catch (Exception ex)
{
Assert.Fail($"UpdateQuery should not throw exceptions: {ex.Message}");
}
}
}

View File

@@ -1,127 +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 System;
using System.Globalization;
using Microsoft.CmdPal.Ext.TimeDate.Helpers;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.TimeDate.UnitTests;
[TestClass]
public class IconTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
// Set culture to 'en-us'
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us", false);
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us", false);
}
[TestCleanup]
public void CleanUp()
{
// Set culture to original value
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
[TestMethod]
public void TimeDateCommandsProvider_HasIcon()
{
// Setup
var provider = new TimeDateCommandsProvider();
// Act
var icon = provider.Icon;
// Assert
Assert.IsNotNull(icon, "Provider should have an icon");
}
[TestMethod]
public void TimeDateCommandsProvider_TopLevelCommands_HaveIcons()
{
// Setup
var provider = new TimeDateCommandsProvider();
// Act
var commands = provider.TopLevelCommands();
// Assert
Assert.IsNotNull(commands);
Assert.IsTrue(commands.Length > 0, "Should have at least one top-level command");
foreach (var command in commands)
{
Assert.IsNotNull(command.Icon, "Each command should have an icon");
}
}
[TestMethod]
public void AvailableResults_HaveIcons()
{
// Setup
var settings = new SettingsManager();
// Act
var results = AvailableResultsList.GetList(true, settings);
// Assert
Assert.IsNotNull(results);
Assert.IsTrue(results.Count > 0, "Should have results");
foreach (var result in results)
{
Assert.IsNotNull(result.GetIconInfo(), $"Result '{result.Label}' should have an icon");
}
}
[DataTestMethod]
[DataRow(ResultIconType.Time, "\uE823")]
[DataRow(ResultIconType.Date, "\uE787")]
[DataRow(ResultIconType.DateTime, "\uEC92")]
public void ResultHelper_CreateListItem_PreservesIcon(ResultIconType resultIconType, string expectedIcon)
{
// Setup
var availableResult = new AvailableResult
{
Label = "Test Label",
Value = "Test Value",
IconType = resultIconType,
};
// Act
var listItem = availableResult.ToListItem();
var icon = listItem.Icon;
// Assert
Assert.IsNotNull(listItem);
Assert.IsNotNull(listItem.Icon, "ListItem should preserve the icon from AvailableResult");
Assert.AreEqual(expectedIcon, icon.Dark.Icon, $"Icon for {resultIconType} should match expected value");
}
[TestMethod]
public void Icons_AreNotEmpty()
{
// Setup
var settings = new SettingsManager();
var results = AvailableResultsList.GetList(true, settings);
// Act & Assert
foreach (var result in results)
{
Assert.IsNotNull(result.GetIconInfo(), $"Result '{result.Label}' should have an icon");
Assert.IsFalse(string.IsNullOrWhiteSpace(result.GetIconInfo().ToString()), $"Icon for '{result.Label}' should not be empty");
}
}
}

View File

@@ -19,5 +19,6 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\ext\Microsoft.CmdPal.Ext.TimeDate\Microsoft.CmdPal.Ext.TimeDate.csproj" />
<ProjectReference Include="..\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj" />
</ItemGroup>
</Project>

View File

@@ -6,13 +6,14 @@ using System;
using System.Globalization;
using System.Linq;
using Microsoft.CmdPal.Ext.TimeDate.Helpers;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.CmdPal.Ext.TimeDate.Pages;
using Microsoft.CmdPal.Ext.UnitTestBase;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.TimeDate.UnitTests;
[TestClass]
public class QueryTests
public class QueryTests : CommandPaletteUnitTestBase
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
@@ -46,7 +47,7 @@ public class QueryTests
public void CountBasicQueries(string query, int expectedMinResultCount)
{
// Setup
var settings = new SettingsManager();
var settings = new Settings();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
@@ -58,30 +59,32 @@ public class QueryTests
}
[DataTestMethod]
[DataRow("time")]
[DataRow("date")]
[DataRow("year")]
[DataRow("now")]
[DataRow("current")]
[DataRow("")]
[DataRow("now::10:10:10")] // Windows file time
public void AllQueriesReturnResults(string query)
[DataRow("time", "time")]
[DataRow("date", "date")]
[DataRow("year", "year")]
[DataRow("now", "now")]
[DataRow("year", "year")]
public void BasicQueryTest(string input, string expectedMatchTerm)
{
// Setup
var settings = new SettingsManager();
var settings = new Settings();
var page = new TimeDateExtensionPage(settings);
page.UpdateSearchText(string.Empty, input);
var resultLists = page.GetItems();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
var result = Query(input, resultLists);
// Assert
Assert.IsNotNull(results);
Assert.IsTrue(results.Count > 0, $"Query '{query}' should return at least one result");
Assert.IsNotNull(result);
Assert.IsTrue(result.Length > 0, "No items matched the query.");
var firstItem = result.FirstOrDefault();
Assert.IsNotNull(firstItem, "No items matched the query.");
Assert.IsTrue(
firstItem.Title.Contains(expectedMatchTerm, System.StringComparison.OrdinalIgnoreCase) ||
firstItem.Subtitle.Contains(expectedMatchTerm, System.StringComparison.OrdinalIgnoreCase),
$"Expected to match '{expectedMatchTerm}' in title or subtitle but got '{firstItem.Title}' - '{firstItem.Subtitle}'");
}
[DataTestMethod]
[DataRow("time", "Time")]
[DataRow("date", "Date")]
[DataRow("now", "Now")]
[DataRow("unix", "Unix epoch time")]
[DataRow("unix epoch time in milli", "Unix epoch time in milliseconds")]
[DataRow("file", "Windows file time (Int64 number)")]
@@ -98,12 +101,8 @@ public class QueryTests
[DataRow("month", "Month")]
[DataRow("month of year", "Month of the year")]
[DataRow("month and d", "Month and day")]
[DataRow("month and y", "Month and year")]
[DataRow("year", "Year")]
[DataRow("era", "Era")]
[DataRow("era a", "Era abbreviation")]
[DataRow("universal", "Universal time format: YYYY-MM-DD hh:mm:ss")]
[DataRow("iso", "ISO 8601")]
[DataRow("rfc", "RFC1123")]
[DataRow("time::12:30", "Time")]
[DataRow("date::10.10.2022", "Date")]
@@ -114,40 +113,19 @@ public class QueryTests
[DataRow("week num", "Week of the year (Calendar week, Week number)")]
[DataRow("days in mo", "Days in month")]
[DataRow("Leap y", "Leap year")]
public void CanFindFormatResult(string query, string expectedSubtitle)
public void FormatDateQueryTest(string input, string expectedMatchTerm)
{
// Setup
var settings = new SettingsManager();
var settings = new Settings();
var page = new TimeDateExtensionPage(settings);
page.UpdateSearchText(string.Empty, input);
var resultLists = page.GetItems();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
// Assert
var matchingResult = results.FirstOrDefault(x => x.Subtitle?.StartsWith(expectedSubtitle, StringComparison.CurrentCulture) == true);
Assert.IsNotNull(matchingResult, $"Could not find result with subtitle starting with '{expectedSubtitle}' for query '{query}'");
}
[DataTestMethod]
[DataRow("12:30", "Time")]
[DataRow("10.10.2022", "Date")]
[DataRow("u1646408119", "Date and time")]
[DataRow("u+1646408119", "Date and time")]
[DataRow("u-1646408119", "Date and time")]
[DataRow("ums1646408119", "Date and time")]
[DataRow("ums+1646408119", "Date and time")]
[DataRow("ums-1646408119", "Date and time")]
[DataRow("ft637820085517321977", "Date and time")]
public void DateTimeNumberOnlyInput(string query, string expectedSubtitle)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
// Assert
var matchingResult = results.FirstOrDefault(x => x.Subtitle?.StartsWith(expectedSubtitle, StringComparison.CurrentCulture) == true);
Assert.IsNotNull(matchingResult, $"Could not find result with subtitle starting with '{expectedSubtitle}' for query '{query}'");
var firstItem = resultLists.FirstOrDefault();
Assert.IsNotNull(firstItem, "No items matched the query.");
Assert.IsTrue(
firstItem.Title.Contains(expectedMatchTerm, System.StringComparison.OrdinalIgnoreCase) ||
firstItem.Subtitle.Contains(expectedMatchTerm, System.StringComparison.OrdinalIgnoreCase),
$"Expected to match '{expectedMatchTerm}' in title or subtitle but got '{firstItem.Title}' - '{firstItem.Subtitle}'");
}
[DataTestMethod]
@@ -157,24 +135,6 @@ public class QueryTests
[DataRow("time:eeee")]
[DataRow("time::eeee")]
[DataRow("time//eeee")]
public void InvalidInputShowsErrorResults(string query)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
// Assert
Assert.IsNotNull(results, $"Results should not be null for query '{query}'");
Assert.IsTrue(results.Count > 0, $"Query '{query}' should return at least one result");
// For invalid input, cmdpal returns an error result
var hasErrorResult = results.Any(r => r.Title?.StartsWith("Error: Invalid input", StringComparison.CurrentCulture) == true);
Assert.IsTrue(hasErrorResult, $"Query '{query}' should return an error result for invalid input");
}
[DataTestMethod]
[DataRow("ug1646408119")] // Invalid prefix
[DataRow("u9999999999999")] // Unix number + prefix is longer than 12 characters
[DataRow("ums999999999999999")] // Unix number in milliseconds + prefix is longer than 17 characters
@@ -194,116 +154,33 @@ public class QueryTests
[DataRow("10.aa.22")]
[DataRow("12::55")]
[DataRow("12:aa:55")]
public void InvalidNumberInputShowsErrorMessage(string query)
public void InvalidInputShowsErrorResults(string query)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
var settings = new Settings();
var page = new TimeDateExtensionPage(settings);
page.UpdateSearchText(string.Empty, query);
var results = page.GetItems();
// Assert
Assert.IsNotNull(results, $"Results should not be null for query '{query}'");
Assert.IsTrue(results.Count > 0, $"Should return at least one result (error message) for invalid query '{query}'");
Assert.IsTrue(results.Length > 0, $"Query '{query}' should return at least one result");
// Check if we get an error result
var errorResult = results.FirstOrDefault(r => r.Title?.StartsWith("Error: Invalid input", StringComparison.CurrentCulture) == true);
Assert.IsNotNull(errorResult, $"Should return an error result for invalid query '{query}'");
var firstItem = results.FirstOrDefault();
Assert.IsTrue(firstItem.Title.StartsWith("Error: Invalid input", StringComparison.CurrentCulture), $"Query '{query}' should return an error result for invalid input");
}
[DataTestMethod]
[DataRow("10.10aa")] // Input contains <Number>.<Number> (Can be part of a date.)
[DataRow("10:10aa")] // Input contains <Number>:<Number> (Can be part of a time.)
[DataRow("10/10aa")] // Input contains <Number>/<Number> (Can be part of a date.)
public void InvalidInputNotShowsErrorMessage(string query)
[DataRow("")]
[DataRow(null)]
public void EmptyQueryReturnsAllResults(string input)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
var settings = new Settings();
var page = new TimeDateExtensionPage(settings);
page.UpdateSearchText("abc", input);
var results = page.GetItems();
// Assert
Assert.IsNotNull(results, $"Results should not be null for query '{query}'");
// These queries are ambiguous and cmdpal returns an error for them
// This test might need to be adjusted based on actual cmdpal behavior
if (results.Count > 0)
{
var hasErrorResult = results.Any(r => r.Title?.StartsWith("Error: Invalid input", StringComparison.CurrentCulture) == true);
// For these ambiguous inputs, cmdpal may return error results, which is acceptable
// We just verify that the system handles them gracefully (doesn't crash)
Assert.IsTrue(true, $"Query '{query}' handled gracefully");
}
}
[DataTestMethod]
[DataRow("time", "time", true)] // Full word match should work
[DataRow("date", "date", true)] // Full word match should work
[DataRow("now", "now", true)] // Full word match should work
[DataRow("year", "year", true)] // Full word match should work
[DataRow("abcdefg", "", false)] // Invalid query should return error
public void ValidateBehaviorOnSearchQueries(string query, string expectedMatchTerm, bool shouldHaveValidResults)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
// Assert
Assert.IsNotNull(results, $"Results should not be null for query '{query}'");
Assert.IsTrue(results.Count > 0, $"Query '{query}' should return at least one result");
if (shouldHaveValidResults)
{
// Should have non-error results
var hasValidResult = results.Any(r => !r.Title?.StartsWith("Error: Invalid input", StringComparison.CurrentCulture) == true);
Assert.IsTrue(hasValidResult, $"Query '{query}' should return valid (non-error) results");
if (!string.IsNullOrEmpty(expectedMatchTerm))
{
var hasMatchingResult = results.Any(r =>
r.Title?.Contains(expectedMatchTerm, StringComparison.CurrentCultureIgnoreCase) == true ||
r.Subtitle?.Contains(expectedMatchTerm, StringComparison.CurrentCultureIgnoreCase) == true);
Assert.IsTrue(hasMatchingResult, $"Query '{query}' should return results containing '{expectedMatchTerm}'");
}
}
else
{
// Should have error results
var hasErrorResult = results.Any(r => r.Title?.StartsWith("Error: Invalid input", StringComparison.CurrentCulture) == true);
Assert.IsTrue(hasErrorResult, $"Query '{query}' should return error results for invalid input");
}
}
[TestMethod]
public void EmptyQueryReturnsAllResults()
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, string.Empty);
// Assert
Assert.IsNotNull(results);
Assert.IsTrue(results.Count > 0, "Empty query should return all available results");
}
[TestMethod]
public void NullQueryReturnsAllResults()
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, null);
// Assert
Assert.IsNotNull(results);
Assert.IsTrue(results.Count > 0, "Null query should return all available results");
Assert.IsTrue(results.Length > 0, $"Empty query should return results");
}
[DataTestMethod]
@@ -312,39 +189,34 @@ public class QueryTests
[DataRow("iso utc", "ISO 8601 UTC")]
[DataRow("iso zone", "ISO 8601 with time zone")]
[DataRow("iso utc zone", "ISO 8601 UTC with time zone")]
public void UTCRelatedQueries(string query, string expectedSubtitle)
public void TimeZoneQuery(string query, string expectedSubtitle)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
var settings = new Settings();
var page = new TimeDateExtensionPage(settings);
page.UpdateSearchText(string.Empty, query);
var resultsList = page.GetItems();
var results = Query(query, resultsList);
// Assert
Assert.IsNotNull(results);
Assert.IsTrue(results.Count > 0, $"Query '{query}' should return results");
var matchingResult = results.FirstOrDefault(x => x.Subtitle?.StartsWith(expectedSubtitle, StringComparison.CurrentCulture) == true);
Assert.IsNotNull(matchingResult, $"Could not find result with subtitle starting with '{expectedSubtitle}' for query '{query}'");
var firstResult = results.FirstOrDefault();
Assert.IsTrue(firstResult.Subtitle.StartsWith(expectedSubtitle, StringComparison.CurrentCulture), $"Could not find result with subtitle starting with '{expectedSubtitle}' for query '{query}'");
}
[DataTestMethod]
[DataRow("time::12:30:45")]
[DataRow("date::2023-12-25")]
[DataRow("now::u1646408119")]
[DataRow("current::ft637820085517321977")]
public void DelimiterQueriesReturnResults(string query)
[DataRow("time::12:30:45", "12:30 PM")]
[DataRow("date::2023-12-25", "12/25/2023")]
[DataRow("now::u1646408119", "132908817190000000")]
public void DelimiterQueriesReturnResults(string query, string expectedResult)
{
// Setup
var settings = new SettingsManager();
// Act
var results = TimeDateCalculator.ExecuteSearch(settings, query);
var settings = new Settings();
var page = new TimeDateExtensionPage(settings);
page.UpdateSearchText(string.Empty, query);
var resultsList = page.GetItems();
// Assert
Assert.IsNotNull(results);
// Delimiter queries should return results even if parsing fails (error results)
Assert.IsTrue(results.Count > 0, $"Delimiter query '{query}' should return at least one result");
Assert.IsNotNull(resultsList);
var firstResult = resultsList.FirstOrDefault();
Assert.IsTrue(firstResult.Title.Contains(expectedResult, StringComparison.CurrentCulture), $"Delimiter query '{query}' result not match {expectedResult} current result {firstResult.Title}");
}
}

View File

@@ -0,0 +1,46 @@
// 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 System.Collections.Generic;
using Microsoft.CmdPal.Ext.TimeDate.Helpers;
namespace Microsoft.CmdPal.Ext.TimeDate.UnitTests;
public class Settings : ISettingsInterface
{
private readonly int firstWeekOfYear;
private readonly int firstDayOfWeek;
private readonly bool enableFallbackItems;
private readonly bool timeWithSecond;
private readonly bool dateWithWeekday;
private readonly List<string> customFormats;
public Settings(
int firstWeekOfYear = -1,
int firstDayOfWeek = -1,
bool enableFallbackItems = true,
bool timeWithSecond = false,
bool dateWithWeekday = false,
List<string> customFormats = null)
{
this.firstWeekOfYear = firstWeekOfYear;
this.firstDayOfWeek = firstDayOfWeek;
this.enableFallbackItems = enableFallbackItems;
this.timeWithSecond = timeWithSecond;
this.dateWithWeekday = dateWithWeekday;
this.customFormats = customFormats ?? new List<string>();
}
public int FirstWeekOfYear => firstWeekOfYear;
public int FirstDayOfWeek => firstDayOfWeek;
public bool EnableFallbackItems => enableFallbackItems;
public bool TimeWithSecond => timeWithSecond;
public bool DateWithWeekday => dateWithWeekday;
public List<string> CustomFormats => customFormats;
}

View File

@@ -1,85 +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 System;
using System.Globalization;
using Microsoft.CmdPal.Ext.TimeDate.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.TimeDate.UnitTests;
[TestClass]
public class SettingsManagerTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
// Set culture to 'en-us'
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us", false);
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us", false);
}
[TestCleanup]
public void Cleanup()
{
// Restore original culture
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
[TestMethod]
public void SettingsManagerInitializationTest()
{
// Act
var settingsManager = new SettingsManager();
// Assert
Assert.IsNotNull(settingsManager);
Assert.IsNotNull(settingsManager.Settings);
}
[TestMethod]
public void DefaultSettingsValidation()
{
// Act
var settingsManager = new SettingsManager();
// Assert - Check that properties are accessible
var enableFallback = settingsManager.EnableFallbackItems;
var timeWithSecond = settingsManager.TimeWithSecond;
var dateWithWeekday = settingsManager.DateWithWeekday;
var firstWeekOfYear = settingsManager.FirstWeekOfYear;
var firstDayOfWeek = settingsManager.FirstDayOfWeek;
var customFormats = settingsManager.CustomFormats;
Assert.IsNotNull(customFormats);
}
[TestMethod]
public void SettingsPropertiesAccessibilityTest()
{
// Setup
var settingsManager = new SettingsManager();
// Act & Assert - Verify all properties are accessible without exception
try
{
_ = settingsManager.EnableFallbackItems;
_ = settingsManager.TimeWithSecond;
_ = settingsManager.DateWithWeekday;
_ = settingsManager.FirstWeekOfYear;
_ = settingsManager.FirstDayOfWeek;
_ = settingsManager.CustomFormats;
}
catch (Exception ex)
{
Assert.Fail($"Settings properties should be accessible: {ex.Message}");
}
}
}

View File

@@ -20,5 +20,6 @@
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj" />
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
<ProjectReference Include="..\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,60 +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 System;
using System.Reflection;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.CmdPal.Ext.WindowWalker.UnitTests;
[TestClass]
public class PluginSettingsTests
{
[DataTestMethod]
[DataRow("ResultsFromVisibleDesktopOnly")]
[DataRow("SubtitleShowPid")]
[DataRow("SubtitleShowDesktopName")]
[DataRow("ConfirmKillProcess")]
[DataRow("KillProcessTree")]
[DataRow("OpenAfterKillAndClose")]
[DataRow("HideKillProcessOnElevatedProcesses")]
[DataRow("HideExplorerSettingInfo")]
[DataRow("InMruOrder")]
public void DoesSettingExist(string name)
{
// Setup
Type settings = SettingsManager.Instance?.GetType();
// Act
var result = settings?.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
// Assert
Assert.IsNotNull(result);
}
[DataTestMethod]
[DataRow("ResultsFromVisibleDesktopOnly", false)]
[DataRow("SubtitleShowPid", false)]
[DataRow("SubtitleShowDesktopName", true)]
[DataRow("ConfirmKillProcess", true)]
[DataRow("KillProcessTree", false)]
[DataRow("OpenAfterKillAndClose", false)]
[DataRow("HideKillProcessOnElevatedProcesses", false)]
[DataRow("HideExplorerSettingInfo", true)]
[DataRow("InMruOrder", true)]
public void DefaultValues(string name, bool valueExpected)
{
// Setup
SettingsManager setting = SettingsManager.Instance;
// Act
PropertyInfo propertyInfo = setting?.GetType()?.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
var result = propertyInfo?.GetValue(setting);
// Assert
Assert.AreEqual(valueExpected, result);
}
}

View File

@@ -0,0 +1,60 @@
// 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.CmdPal.Ext.WindowWalker.Helpers;
namespace Microsoft.CmdPal.Ext.WindowWalker.UnitTests;
public class Settings : ISettingsInterface
{
private readonly bool resultsFromVisibleDesktopOnly;
private readonly bool subtitleShowPid;
private readonly bool subtitleShowDesktopName;
private readonly bool confirmKillProcess;
private readonly bool killProcessTree;
private readonly bool openAfterKillAndClose;
private readonly bool hideKillProcessOnElevatedProcesses;
private readonly bool hideExplorerSettingInfo;
private readonly bool inMruOrder;
public Settings(
bool resultsFromVisibleDesktopOnly = false,
bool subtitleShowPid = false,
bool subtitleShowDesktopName = true,
bool confirmKillProcess = true,
bool killProcessTree = false,
bool openAfterKillAndClose = false,
bool hideKillProcessOnElevatedProcesses = false,
bool hideExplorerSettingInfo = true,
bool inMruOrder = true)
{
this.resultsFromVisibleDesktopOnly = resultsFromVisibleDesktopOnly;
this.subtitleShowPid = subtitleShowPid;
this.subtitleShowDesktopName = subtitleShowDesktopName;
this.confirmKillProcess = confirmKillProcess;
this.killProcessTree = killProcessTree;
this.openAfterKillAndClose = openAfterKillAndClose;
this.hideKillProcessOnElevatedProcesses = hideKillProcessOnElevatedProcesses;
this.hideExplorerSettingInfo = hideExplorerSettingInfo;
this.inMruOrder = inMruOrder;
}
public bool ResultsFromVisibleDesktopOnly => resultsFromVisibleDesktopOnly;
public bool SubtitleShowPid => subtitleShowPid;
public bool SubtitleShowDesktopName => subtitleShowDesktopName;
public bool ConfirmKillProcess => confirmKillProcess;
public bool KillProcessTree => killProcessTree;
public bool OpenAfterKillAndClose => openAfterKillAndClose;
public bool HideKillProcessOnElevatedProcesses => hideKillProcessOnElevatedProcesses;
public bool HideExplorerSettingInfo => hideExplorerSettingInfo;
public bool InMruOrder => inMruOrder;
}

View File

@@ -12,21 +12,21 @@ namespace Microsoft.CmdPal.Ext.Calc;
public partial class CalculatorCommandProvider : CommandProvider
{
private static ISettingsInterface settings = new SettingsManager();
private readonly ListItem _listItem = new(new CalculatorListPage(settings))
{
Subtitle = Resources.calculator_top_level_subtitle,
MoreCommands = [new CommandContextItem(settings.Settings.SettingsPage)],
MoreCommands = [new CommandContextItem(((SettingsManager)settings).Settings.SettingsPage)],
};
private readonly FallbackCalculatorItem _fallback = new(settings);
private static SettingsManager settings = new();
public CalculatorCommandProvider()
{
Id = "Calculator";
DisplayName = Resources.calculator_display_name;
Icon = Icons.CalculatorIcon;
Settings = settings.Settings;
Settings = ((SettingsManager)settings).Settings;
}
public override ICommandItem[] TopLevelCommands() => [_listItem];

View File

@@ -34,7 +34,7 @@ public static class CalculateEngine
/// Interpret
/// </summary>
/// <param name="cultureInfo">Use CultureInfo.CurrentCulture if something is user facing</param>
public static CalculateResult Interpret(SettingsManager settings, string input, CultureInfo cultureInfo, out string error)
public static CalculateResult Interpret(ISettingsInterface settings, string input, CultureInfo cultureInfo, out string error)
{
error = default;

View File

@@ -0,0 +1,18 @@
// 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.CmdPal.Ext.Calc.Helper;
namespace Microsoft.CmdPal.Ext.Calc.Helper;
public interface ISettingsInterface
{
public CalculateEngine.TrigMode TrigUnit { get; }
public bool InputUseEnglishFormat { get; }
public bool OutputUseEnglishFormat { get; }
public bool CloseOnEnter { get; }
}

View File

@@ -12,7 +12,7 @@ namespace Microsoft.CmdPal.Ext.Calc.Helper;
public static partial class QueryHelper
{
public static ListItem Query(string query, SettingsManager settings, bool isFallbackSearch, TypedEventHandler<object, object> handleSave = null)
public static ListItem Query(string query, ISettingsInterface settings, bool isFallbackSearch, TypedEventHandler<object, object> handleSave = null)
{
ArgumentNullException.ThrowIfNull(query);
if (!isFallbackSearch)

View File

@@ -13,7 +13,7 @@ namespace Microsoft.CmdPal.Ext.Calc.Helper;
public static class ResultHelper
{
public static ListItem CreateResult(decimal? roundedResult, CultureInfo inputCulture, CultureInfo outputCulture, string query, SettingsManager settings, TypedEventHandler<object, object> handleSave)
public static ListItem CreateResult(decimal? roundedResult, CultureInfo inputCulture, CultureInfo outputCulture, string query, ISettingsInterface settings, TypedEventHandler<object, object> handleSave)
{
// Return null when the expression is not a valid calculator query.
if (roundedResult == null)

View File

@@ -8,7 +8,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Calc.Helper;
public class SettingsManager : JsonSettingsManager
public class SettingsManager : JsonSettingsManager, ISettingsInterface
{
private static readonly string _namespace = "calculator";

View File

@@ -23,7 +23,7 @@ namespace Microsoft.CmdPal.Ext.Calc.Pages;
public sealed partial class CalculatorListPage : DynamicListPage
{
private readonly Lock _resultsLock = new();
private readonly SettingsManager _settingsManager;
private readonly ISettingsInterface _settingsManager;
private readonly List<ListItem> _items = [];
private readonly List<ListItem> history = [];
private readonly ListItem _emptyItem;
@@ -32,7 +32,7 @@ public sealed partial class CalculatorListPage : DynamicListPage
// We need to avoid the double calculation. This may cause some wierd behaviors.
private string skipQuerySearchText = string.Empty;
public CalculatorListPage(SettingsManager settings)
public CalculatorListPage(ISettingsInterface settings)
{
_settingsManager = settings;
Icon = Icons.CalculatorIcon;

View File

@@ -11,9 +11,9 @@ namespace Microsoft.CmdPal.Ext.Calc.Pages;
public sealed partial class FallbackCalculatorItem : FallbackCommandItem
{
private readonly CopyTextCommand _copyCommand = new(string.Empty);
private readonly SettingsManager _settings;
private readonly ISettingsInterface _settings;
public FallbackCalculatorItem(SettingsManager settings)
public FallbackCalculatorItem(ISettingsInterface settings)
: base(new NoOpCommand(), Resources.calculator_title)
{
Command = _copyCommand;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -14,5 +14,5 @@ internal sealed class Icons
internal static IconInfo PasteIcon { get; } = new("\uE77F");
internal static IconInfo ClipboardListIcon { get; } = new("\uF0E3");
internal static IconInfo ClipboardListIcon { get; } = IconHelpers.FromRelativePath("Assets\\ClipboardHistory.svg");
}

View File

@@ -30,4 +30,13 @@
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Update="Assets\ClipboardHistory.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="Assets\ClipboardHistory.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,17 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Ext.Registry.Helpers;
public interface ISettingsInterface
{
// Add registry-specific settings methods here if needed
// For now, this can be empty if there are no settings for Registry
}

View File

@@ -0,0 +1,37 @@
// 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 System.IO;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Registry.Helpers;
public class SettingsManager : JsonSettingsManager, ISettingsInterface
{
private static readonly string _namespace = "registry";
private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}";
internal static string SettingsJsonPath()
{
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
Directory.CreateDirectory(directory);
// now, the state is just next to the exe
return Path.Combine(directory, "settings.json");
}
public SettingsManager()
{
FilePath = SettingsJsonPath();
// Add settings here when needed
// Settings.Add(setting);
// Load settings from file upon initialization
LoadSettings();
Settings.SettingsChanged += (s, a) => this.SaveSettings();
}
}

View File

@@ -18,12 +18,14 @@ internal sealed partial class RegistryListPage : DynamicListPage
public static IconInfo RegistryIcon { get; } = new("\uE74C"); // OEM
private readonly CommandItem _emptyMessage;
private readonly ISettingsInterface _settingsManager;
public RegistryListPage()
public RegistryListPage(ISettingsInterface settingsManager)
{
Icon = Icons.RegistryIcon;
Name = Title = Resources.Registry_Page_Title;
Id = "com.microsoft.cmdpal.registry";
_settingsManager = settingsManager;
_emptyMessage = new CommandItem()
{
Icon = Icons.RegistryIcon,

View File

@@ -2,6 +2,7 @@
// 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.CmdPal.Ext.Registry.Helpers;
using Microsoft.CmdPal.Ext.Registry.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -10,6 +11,8 @@ namespace Microsoft.CmdPal.Ext.Registry;
public partial class RegistryCommandsProvider : CommandProvider
{
private static readonly ISettingsInterface _settingsManager = new SettingsManager();
public RegistryCommandsProvider()
{
Id = "Windows.Registry";
@@ -20,7 +23,7 @@ public partial class RegistryCommandsProvider : CommandProvider
public override ICommandItem[] TopLevelCommands()
{
return [
new CommandItem(new RegistryListPage())
new CommandItem(new RegistryListPage(_settingsManager))
{
Title = "Registry",
Subtitle = "Navigate the Windows registry",

View File

@@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.Shell.Helpers;
@@ -15,6 +16,8 @@ namespace Microsoft.CmdPal.Ext.Shell;
internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDisposable
{
private static readonly char[] _systemDirectoryRoots = ['\\', '/'];
private readonly Action<string>? _addToHistory;
private CancellationTokenSource? _cancellationTokenSource;
private Task? _currentUpdateTask;
@@ -80,8 +83,8 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDispos
cancellationToken.ThrowIfCancellationRequested();
var searchText = query.Trim();
var expanded = Environment.ExpandEnvironmentVariables(searchText);
searchText = expanded;
Expand(ref searchText);
if (string.IsNullOrEmpty(searchText) || string.IsNullOrWhiteSpace(searchText))
{
Command = null;
@@ -184,8 +187,8 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDispos
internal static bool SuppressFileFallbackIf(string query)
{
var searchText = query.Trim();
var expanded = Environment.ExpandEnvironmentVariables(searchText);
searchText = expanded;
Expand(ref searchText);
if (string.IsNullOrEmpty(searchText) || string.IsNullOrWhiteSpace(searchText))
{
return false;
@@ -197,4 +200,57 @@ internal sealed partial class FallbackExecuteItem : FallbackCommandItem, IDispos
return exeExists || pathIsDir;
}
private static void Expand(ref string searchText)
{
if (searchText.Length == 0)
{
return;
}
var singleCharQuery = searchText.Length == 1;
searchText = Environment.ExpandEnvironmentVariables(searchText);
if (!TryExpandHome(ref searchText))
{
TryExpandRoot(ref searchText);
}
}
private static bool TryExpandHome(ref string searchText)
{
if (searchText[0] == '~')
{
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (searchText.Length == 1)
{
searchText = home;
}
else if (_systemDirectoryRoots.Contains(searchText[1]))
{
searchText = Path.Combine(home, searchText[2..]);
}
return true;
}
return false;
}
private static bool TryExpandRoot(ref string searchText)
{
if (_systemDirectoryRoots.Contains(searchText[0]) && (searchText.Length == 1 || !_systemDirectoryRoots.Contains(searchText[1])))
{
var root = Path.GetPathRoot(Environment.SystemDirectory);
if (root != null)
{
searchText = searchText.Length == 1 ? root : Path.Combine(root, searchText[1..]);
return true;
}
}
return false;
}
}

View File

@@ -16,10 +16,10 @@ namespace Microsoft.CmdPal.Ext.TimeDate;
internal sealed partial class FallbackTimeDateItem : FallbackCommandItem
{
private readonly HashSet<string> _validOptions;
private SettingsManager _settingsManager;
private ISettingsInterface _settingsManager;
private DateTime? _timestamp;
public FallbackTimeDateItem(SettingsManager settings, DateTime? timestamp = null)
public FallbackTimeDateItem(ISettingsInterface settings, DateTime? timestamp = null)
: base(new NoOpCommand(), Resources.Microsoft_plugin_timedate_fallback_display_title)
{
Title = string.Empty;

View File

@@ -22,7 +22,7 @@ internal static class AvailableResultsList
/// <param name="firstWeekOfYear">Required for UnitTest: Use custom first week of the year instead of the plugin setting.</param>
/// <param name="firstDayOfWeek">Required for UnitTest: Use custom first day of the week instead the plugin setting.</param>
/// <returns>List of results</returns>
internal static List<AvailableResult> GetList(bool isKeywordSearch, SettingsManager settings, bool? timeLongFormat = null, bool? dateLongFormat = null, DateTime? timestamp = null, CalendarWeekRule? firstWeekOfYear = null, DayOfWeek? firstDayOfWeek = null)
internal static List<AvailableResult> GetList(bool isKeywordSearch, ISettingsInterface settings, bool? timeLongFormat = null, bool? dateLongFormat = null, DateTime? timestamp = null, CalendarWeekRule? firstWeekOfYear = null, DayOfWeek? firstDayOfWeek = null)
{
var results = new List<AvailableResult>();
var calendar = CultureInfo.CurrentCulture.Calendar;

View File

@@ -0,0 +1,26 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Ext.TimeDate.Helpers;
public interface ISettingsInterface
{
public int FirstWeekOfYear { get; }
public int FirstDayOfWeek { get; }
public bool EnableFallbackItems { get; }
public bool TimeWithSecond { get; }
public bool DateWithWeekday { get; }
public List<string> CustomFormats { get; }
}

View File

@@ -11,7 +11,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.TimeDate.Helpers;
public class SettingsManager : JsonSettingsManager
public class SettingsManager : JsonSettingsManager, ISettingsInterface
{
// Line break character used in WinUI3 TextBox and TextBlock.
private const char TEXTBOXNEWLINE = '\r';

View File

@@ -27,7 +27,7 @@ public sealed partial class TimeDateCalculator
/// </summary>
/// <param name="query">Search query object</param>
/// <returns>List of Wox <see cref="Result"/>s.</returns>
public static List<ListItem> ExecuteSearch(SettingsManager settings, string query)
public static List<ListItem> ExecuteSearch(ISettingsInterface settings, string query)
{
var isEmptySearchInput = string.IsNullOrWhiteSpace(query);
List<AvailableResult> availableFormats = new List<AvailableResult>();

View File

@@ -19,9 +19,9 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
private IList<ListItem> _results = new List<ListItem>();
private bool _dataLoaded;
private SettingsManager _settingsManager;
private ISettingsInterface _settingsManager;
public TimeDateExtensionPage(SettingsManager settingsManager)
public TimeDateExtensionPage(ISettingsInterface settingsManager)
{
Icon = Icons.TimeDateExtIcon;
Title = Resources.Microsoft_plugin_timedate_main_page_title;

View File

@@ -15,7 +15,7 @@ namespace Microsoft.CmdPal.Ext.TimeDate;
public partial class TimeDateCommandsProvider : CommandProvider
{
private readonly CommandItem _command;
private static readonly SettingsManager _settingsManager = new();
private static readonly SettingsManager _settingsManager = new SettingsManager();
private static readonly CompositeFormat MicrosoftPluginTimedatePluginDescription = System.Text.CompositeFormat.Parse(Resources.Microsoft_plugin_timedate_plugin_description);
private static readonly TimeDateExtensionPage _timeDateExtensionPage = new(_settingsManager);
private readonly FallbackTimeDateItem _fallbackTimeDateItem = new(_settingsManager);

View File

@@ -0,0 +1,26 @@
// 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.CmdPal.Ext.WindowWalker.Helpers;
public interface ISettingsInterface
{
public bool ResultsFromVisibleDesktopOnly { get; }
public bool SubtitleShowPid { get; }
public bool SubtitleShowDesktopName { get; }
public bool ConfirmKillProcess { get; }
public bool KillProcessTree { get; }
public bool OpenAfterKillAndClose { get; }
public bool HideKillProcessOnElevatedProcesses { get; }
public bool HideExplorerSettingInfo { get; }
public bool InMruOrder { get; }
}

View File

@@ -8,7 +8,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers;
public class SettingsManager : JsonSettingsManager
public class SettingsManager : JsonSettingsManager, ISettingsInterface
{
private static readonly string _namespace = "windowWalker";

Some files were not shown because too many files have changed in this diff Show More