Compare commits

...

108 Commits

Author SHA1 Message Date
Mike Griese
752ec2deb0 Merge branch 'dev/migrie/b/fix-clipboard' into dev/migrie/rel/fun 2025-07-09 14:57:16 -05:00
Mike Griese
d9c168c3fe Merge branch 'dev/migrie/f/run-page-2-with-history' into dev/migrie/f/bookmark-exes 2025-07-09 14:56:46 -05:00
Mike Griese
210febe270 Merge branch 'dev/migrie/f/run-page-2-with-suggestions' into dev/migrie/f/run-page-2-with-history 2025-07-09 14:56:22 -05:00
Mike Griese
e53cb409b4 Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-07-09 14:56:07 -05:00
Mike Griese
98c771788b Merge branch 'dev/migrie/f/run-page-2-with-history' into dev/migrie/f/bookmark-exes 2025-07-09 14:55:30 -05:00
Mike Griese
5dee7c9d89 Merge branch 'dev/migrie/f/run-page-2-with-suggestions' into dev/migrie/f/run-page-2-with-history 2025-07-09 14:55:02 -05:00
Michael Jolley
100fca4468 CmdPal: Refactoring ContextMenu adding separators, IsCritical styling, and right-click context menus for list items (#40189)
Refactored ContextMenu into it's own control to allow displaying in
CommandBar and in response to right click on list items.

- Adds "critical" styling to context menu items flagged as `IsCritical`.
This will use the theme to style with correct color.
- Added `SeparatorContextItem` and modified `MoreCommands` to allow for
both `CommandContextItem`s and `SeparatorContextItem`s.
- Right clicking a list item with a context menu will open the context
menu at the position of the click and position the filter box at the top
of the context menu.


![image](https://github.com/user-attachments/assets/3bef6b04-28bb-4a17-b731-d9ed20c0566f)


![image](https://github.com/user-attachments/assets/37ed497c-6d98-4f04-8114-d9952127ca2e)


This PR covers:

- closes #38308
- closes #39211
- closes #38307
- closes #38261
2025-07-09 14:53:47 -05:00
Mike Griese
18b61ce9b7 CmdPal: Give FG back to the previous window (#40445)
this is a port of ce15032 onto main, for just the FG change.

When we cloak our window, we want to make sure to _manually_ give FG
back to another window. Because apparently cloaked windows can have FG.
beacause that makes sense 🤦

Closes #39638
supersedes #40431 

Co-authored-by: jiripolasek <me@jiripolasek.com>
2025-07-09 14:50:07 -05:00
Mike Griese
c2ba9af144 Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-07-09 14:47:13 -05:00
Jiří Polášek
09c1575fa0 Ensure Command Palette main window remains hidden on taskbar Windows Explorer restarts (#40406)
## Summary of the Pull Request

Ensures the Command Palette main window stays hidden from the taskbar
after Windows Explorer restarts. Also updates how app switcher
visibility is managed to prevent unhandled exceptions and avoid startup
crashes when Explorer is not running or is not responsive.

## PR Checklist

- [x] Closes: #40334
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** no tests
- [x] **Localization:** nothing to localize
- [x] **Dev docs:** nothing to update
- [x] **New binaries:** no new binaries
- [x] **Documentation updated:** nothing to update

## Detailed Description of the Pull Request / Additional comments
- Settings on the main window are reapplied after the taskbar is
re-created (e.g., after Windows Explorer restarts), restoring visibility
settings for the app switcher and taskbar button.
- Defensive handling of the property AppWindow.IsShownInSwitchers
controlling appearance in system representations such as ALT+TAB and the
taskbar, to avoid errors when Windows Explorer is not running (see
https://github.com/microsoft/microsoft-ui-xaml/issues/8596).
- Prevents application startup failures when the Command Palette is
launched in environments without Windows Explorer.

## Validation Steps Performed
- Verified that the CmdPal main window taskbar button is not visible
after Explorer is restarted (without an attached debugger).
- Verified that CmdPal can be started when Explorer is not running.
- Verified that the CmdPal main window taskbar button is visible after
Explorer is restarted (if a debugger is attached to CmdPal).
2025-07-09 12:35:42 -05:00
Yu Leng
802bc3bd34 [UI automation test] Add basic tests case for powerrename module. (#40393)
<!-- 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. Add command args support in ui test core
2. Add command line parse logic in powerrename
3. Add some test cases.

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

- [ ] **Closes:** #xxx
- [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-07-09 14:32:40 +08:00
Jeremy Sinclair
c4922f1b30 [Deps] Update .NET packages from 9.0.6 to 9.0.7 (#40485)
<!-- 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.7 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-07-09 09:16:52 +08:00
Josh Soref
bf16e10baf Updates for check-spelling v0.0.25 (#40386)
## Summary of the Pull Request

- #39572 updated check-spelling but ignored:
   > 🐣 Breaking Changes
[Code Scanning action requires a Code Scanning
Ruleset](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset)
If you use SARIF reporting, then instead of the workflow yielding an 
when it fails, it will rely on [github-advanced-security
🤖](https://github.com/apps/github-advanced-security) to report the
failure. You will need to adjust your checks for PRs.

This means that check-spelling hasn't been properly doing its job 😦.

I'm sorry, I should have pushed a thing to this repo earlier,...

Anyway, as with most refreshes, this comes with a number of fixes, some
are fixes for typos that snuck in before the 0.0.25 upgrade, some are
for things that snuck in after, some are based on new rules in
spell-check-this, and some are hand written patterns based on running
through this repository a few times.

About the 🐣 **breaking change**: someone needs to create a ruleset for
this repository (see [Code Scanning action requires a Code Scanning
Ruleset: Sample ruleset

](https://github.com/check-spelling/check-spelling/wiki/Breaking-Change:-Code-Scanning-action-requires-a-Code-Scanning-Ruleset#sample-ruleset)).

The alternative to adding a ruleset is to change the condition to not
use sarif for this repository. In general, I think the github
integration from sarif is prettier/more helpful, so I think that it's
the better choice.

You can see an example of it working in:
- https://github.com/check-spelling-sandbox/PowerToys/pull/23

---------

Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
2025-07-08 17:16:52 -05:00
Michael Jolley
f34735edeb Adding fallback command for Windows Settings extension (#40331)
Two changes:

- Added a new fallback command for Windows Settings extension. If only
one setting or one exact match for the query was found, display that
setting in the list and open that setting on <Enter>. If more than one
setting was found, display a message to open Windows Settings to see the
search results for your query.


![image](https://github.com/user-attachments/assets/bd5708a5-b1d5-466e-9c62-cd1cd7bb1f74)


![image](https://github.com/user-attachments/assets/98f4ac20-efe1-4782-8133-30afa17e3b7d)


![image](https://github.com/user-attachments/assets/e5da90e1-f89b-480c-bd26-214c68ac013a)

- Modified the titles/subtitles of the extension to pull from Resources
to aid in internationalization.

Closes: #38548 and possibly #40308
2025-07-08 10:05:02 -05:00
Mike Griese
1368f62aed missed an icon 2025-07-08 09:59:03 -05:00
Mike Griese
1cf28270c2 Loc, icons, cleanup 2025-07-08 09:52:36 -05:00
Mike Griese
0562415044 Merge branch 'dev/migrie/f/foreground-sucks' into dev/migrie/b/fix-clipboard 2025-07-08 09:34:45 -05:00
Mike Griese
cfcaafca03 _NA 2025-07-08 09:24:39 -05:00
Mike Griese
a748f653ac dead code 2025-07-08 06:58:02 -05:00
Mike Griese
b56309efbc NOW THIS works well 2025-07-08 06:55:29 -05:00
Mike Griese
dc439540c3 NOW THIS works well 2025-07-08 06:53:17 -05:00
Niels Laute
c6cee94456 [CmdPal] UX tweaks (#40381)
Based on guidance from the design team, this PR introduces a bunch of
small UX tweaks:

- Standardizing body text on 14px (e.g. for the Adaptive Cards related
code).
- Left align all content in the details pane
- Brush tweaks to the hotkey / tags for better visibility


![image](https://github.com/user-attachments/assets/4d9bf699-29bb-42e0-af96-b9b72c34f259)


![image](https://github.com/user-attachments/assets/905a268b-2e29-408c-a301-10a98b5885f1)


![image](https://github.com/user-attachments/assets/b9050693-f4bb-4d74-8701-fb30b46698e0)


![image](https://github.com/user-attachments/assets/5f6f93a0-1d6e-4476-bad5-dc7a9e179e92)

Closes #38858
2025-07-07 10:31:56 -05:00
Mike Griese
f403e97a0d CmdPal: Give FG back to the previous window
this is a port of ce15032 onto main, for just the FG change.

When we cloak our window, we want to make sure to _manually_ give FG
back to another window. Because apparently cloaked windows can have FG.
beacause that makes sense 🤦

Closes #39638
2025-07-07 10:17:59 -05:00
Mike Griese
01a6cec411 re-enable the thing 2025-07-07 10:12:12 -05:00
Mike Griese
1fafcb06dc Merge remote-tracking branch 'origin/main' into dev/migrie/b/fix-clipboard 2025-07-07 09:33:57 -05:00
Gordon Lam
0323ebea58 Fix Random unitttest failure (#40390)
The test
`Win32ProgramRepositoryMustCallOnAppRenamedForLnkAppsWhenRenamedEventIsRaised`
was experiencing random failures due to object identity mismatches in
the repository's hash-based storage system.

## Root Cause

The test was manually creating `Win32Program` objects:

```csharp
Win32Program olditem = new Win32Program
{
    Name = "oldpath",
    ExecutableName = oldpath,
    FullPath = linkingTo,
};
```

However, the `DoOnAppRenamedAsync` method creates the `oldApp` object
for removal using a different approach for .lnk files:

```csharp
oldApp = new Win32Program() { 
    Name = Path.GetFileNameWithoutExtension(e.OldName), 
    ExecutableName = Path.GetFileName(e.OldName), 
    FullPath = newApp?.FullPath ?? oldPath 
};
```

Since the repository uses `GetHashCode()` (based on `Name`,
`ExecutableName`, and `FullPath`) to identify objects for removal, any
subtle differences in these properties would cause the `Remove()`
operation to fail, leading to test assertion failures.

## Fix

Changed the test to use `Win32Program.GetAppFromPath()` instead of
manual object creation:

```csharp
Win32Program olditem = Win32Program.GetAppFromPath(oldFullPath);
Win32Program newitem = Win32Program.GetAppFromPath(newFullPath);
```

This mirrors the approach used in the working
`Win32ProgramRepositoryMustCallOnAppRenamedForUrlAppsWhenRenamedEventIsRaised`
test and ensures that test objects are created using the same code path
as the production code, eliminating hash code mismatches.

## Why This Was Random

The test failure appeared random because it depended on subtle
differences in object creation that could vary based on timing, mock
setup, or other environmental factors. By using the same object creation
method as the production code, the test becomes deterministic.
2025-07-07 17:35:09 +08:00
Gordon Lam
bd132d9b40 Fix 6 UI Automation on FanzyZone given there is renamed in Setting UI. (#40434)
<!-- 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
It has renamed on the "Make dragged window transparent" => "Make the
dragged window transparent"

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
Kicked off UI automation pipeline

https://microsoft.visualstudio.com/Dart/_build/results?buildId=125833108&view=ms.vss-test-web.build-test-results-tab&runId=924788779&resultId=100072&paneView=debug
No more failure on FancyZone
2025-07-07 17:34:57 +08:00
leileizhang
69064fab99 Resolve spelling check noise (#40433)
<!-- 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
These appear in every PR

![image](https://github.com/user-attachments/assets/06bb02f1-d828-44c7-8c18-624d3e10def3)
Resolve all spelling issues that were generating excessive noise in PRs.

<!-- 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-07-07 13:08:48 +08:00
leileizhang
a64de74f3b [UI Tests] Fix incorrect Settings Page launch method in UI test framework (#40367)
<!-- 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
### Root Cause
The UI test framework previously attempted to launch
PowerToys.Settings.exe directly.
However, this bypasses the PowerToys Runner, which is required for
proper interaction
and communication between runner.

### Fix
1. This change updates the launch mechanism to start PowerToys through
the Runner
with the appropriate argument ("--open-settings").
2. Prevents the Debug dialog from appearing in test runs

<!-- 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-07-07 12:58:49 +08:00
leileizhang
7772bfb777 Fix: File explorer preview didn't work with per-user installation (#40314)
<!-- 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
This Bug started when the Win11 context menu integration was first
introduced by Image Resizer in version v0.60.0.

Move Image Resizer to the WinUI3Apps folder to fix file preview issue
when PowerToys is installed on a non-C:\Program Files.
This aligns with the current structure used by File Locksmith and
PowerRename, which are not WinUI 3 apps either, but are already located
there.

### Root Cause:
When registering an MSIX package, the Windows API adds certain user
permissions to the installation folders. Since Image Resizer was
previously placed under the main PowerToys directory, these permission
changes could prevent Explorer from loading its preview handler properly
in per-user scenarios.


![image](https://github.com/user-attachments/assets/a8626314-19ce-4e25-87d6-d5e74a015e68)

Interestingly, this issue only affects per-user installs, not
machine-wide installs (e.g., to Program Files), even though both
locations receive additional permissions. The exact reason is still
unclear and requires further investigation.

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

- [x] **Closes:** #24384 #29644 #32113 #34139 #37866 #40345
- [ ] **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-07-07 09:41:19 +08:00
Mike Griese
0f279002f8 Fix loading our app state (#40401)
Yea this is dumb. Cant have private setters when json deserializing.
closes #40399
2025-07-06 16:10:15 -05:00
Mike Griese
29c15601f0 Merge branch 'dev/migrie/f/run-page-2-with-history' into dev/migrie/f/bookmark-exes 2025-07-06 14:14:53 -05:00
Mike Griese
558e2af6cb uhg 2025-07-06 14:14:33 -05:00
Mike Griese
70c5e3c9d5 Merge branch 'dev/migrie/f/run-page-2-with-history' into dev/migrie/f/bookmark-exes 2025-07-06 14:11:30 -05:00
Mike Griese
986ffbeede Merge branch 'dev/migrie/f/run-page-2-with-suggestions' into dev/migrie/f/run-page-2-with-history 2025-07-06 14:07:16 -05:00
Mike Griese
8466c47059 delete all this logging we don't need 2025-07-06 10:36:14 -05:00
Mike Griese
229d3b4991 loc, dead code removal 2025-07-06 09:50:49 -05:00
Mike Griese
7ca9798df5 Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-07-06 09:24:35 -05:00
Mike Griese
a6e39d5535 PARENT: omg 2025-07-06 09:24:20 -05:00
Mike Griese
edf02497af tiny note 2025-07-06 09:24:06 -05:00
Mike Griese
02b583267d move it all out 2025-07-06 09:16:04 -05:00
Mike Griese
e2569ec4ee Start pulling this out of IconPathConverter 2025-07-06 09:07:56 -05:00
Mike Griese
c760962573 minor nits 2025-07-05 22:55:53 -05:00
Mike Griese
ec2480385b Tidy up the Type property, which we no longer need 2025-07-05 22:52:44 -05:00
Mike Griese
458d3a2699 I think placeholders should have subtitles too 2025-07-05 10:22:44 -05:00
Mike Griese
a58b802cb9 i'm so good at this 2025-07-05 07:03:57 -05:00
Mike Griese
a72bf5aed7 Bookmarking exes or files or urls is that easy 2025-07-05 06:26:05 -05:00
Mike Griese
fb49f6a5e5 you should be with your friends 2025-07-05 05:47:25 -05:00
Mike Griese
b4a7bb4a7a MAIN: this is dumb 2025-07-04 06:58:50 -05:00
leileizhang
0c425fd1d7 [Fix][CmdPal] CmdPal Apps extension is missing all Win32 applications (#40392)
<!-- 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
### Root Cause
Calling `"CreateFile"` without an explicit `EntryPoint` fails because
kernel32.dll only exports `CreateFileA` and `CreateFileW`.

### Fix
Add EntryPoint = "CreateFileW"

### How to reproduce
The issue is reproducible when the PC is set to Chinese or another
non-English language.

![image](https://github.com/user-attachments/assets/2bdfd644-3ccf-4ad0-a470-2bd7de29049c)

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

- [x] **Closes:** #40389, #40340 #40388 #40378 #40341
- [ ] **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-07-04 16:18:18 +08:00
Kai Tao
03a9ac1ac7 [UI automation] workspaces ui automation (#39812)
<!-- 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

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

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If 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


![image](https://github.com/user-attachments/assets/1be219be-1d06-432c-8acb-e3a2ba56d1b6)


https://microsoft.visualstudio.com/Dart/_build/results?buildId=125637396&view=results

---------

Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
Co-authored-by: Jerry Xu <n.xu@outlook.com>
Co-authored-by: Zhaopeng Wang <zhaopengwang@microsoft.com>
Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
Co-authored-by: Mengyuan <162882040+chenmy77@users.noreply.github.com>
Co-authored-by: yaqingmi <miyaqing01@gmail.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
Co-authored-by: Yaqing Mi (from Dev Box) <yaqingmi@microsoft.com>
Co-authored-by: XiaofengWang <709586527@qq.com>
Co-authored-by: zhaopeng wang <33367956+wang563681252@users.noreply.github.com>
Co-authored-by: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com>
Co-authored-by: RokyZevon <12629919+RokyZevon@users.noreply.github.com>
Co-authored-by: Yu Leng <42196638+moooyo@users.noreply.github.com>
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com>
Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
Co-authored-by: ruslanlap <106077551+ruslanlap@users.noreply.github.com>
Co-authored-by: Muhammad Danish <mdanishkhdev@gmail.com>
Co-authored-by: Bennett Blodinger <benwa@users.noreply.github.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Ionuț Manța <ionut@janeasystems.com>
Co-authored-by: Hao Liu <liuhaobupt@163.com>
Co-authored-by: OlegHarchevkin <40352094+OlegKharchevkin@users.noreply.github.com>
Co-authored-by: dcog989 <89043002+dcog989@users.noreply.github.com>
Co-authored-by: PesBandi <127593627+PesBandi@users.noreply.github.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
Co-authored-by: Typpi <20943337+Nick2bad4u@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
Co-authored-by: Abhyudit <64366765+bitmap4@users.noreply.github.com>
Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>
Co-authored-by: Ved Nig <vednig12@outlook.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Aung Khaing Khant <aungkhaingkhant.dev@gmail.com>
Co-authored-by: Aung Khaing Khant <aungkhaingkhant@advent-soft.com>
Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
Co-authored-by: leileizhang <leilzh@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
Co-authored-by: Shawn Yuan <128874481+shuaiyuanxx@users.noreply.github.com>
Co-authored-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
Co-authored-by: cryolithic <cryolithic@gmail.com>
Co-authored-by: Lemonyte <49930425+lemonyte@users.noreply.github.com>
Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
Co-authored-by: Corey Hayward <72159232+CoreyHayward@users.noreply.github.com>
Co-authored-by: Jerry Xu <nxu@microsoft.com>
Co-authored-by: Shawn Yuan <shuaiyuan@microsoft.com>
Co-authored-by: Kayla Cinnamon <cinnamon@microsoft.com>
Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>
2025-07-04 10:07:37 +08:00
Copilot
837d5ca543 Fix signing configuration: Remove 25 obsolete file references from ESRPSigning_core.json (#40241)
## Summary

This PR fixes the signing pipeline by removing 25 obsolete file
references from `ESRPSigning_core.json` that were causing "0 files out
of: 0 files" errors during the signing process. These references pointed
to files that are either no longer built or were never produced by the
current project structure.

## Root Cause Analysis

The signing configuration contained references to files that fall into
three categories:

1. **Static libraries incorrectly listed as DLLs** - Projects configured
as `StaticLibrary` don't produce `.dll` files
2. **Obsolete/non-existent projects** - References to projects that were
removed or renamed
3. **WinExe projects incorrectly listed as producing DLLs** - C#
projects with `OutputType=WinExe` only produce `.exe` files, not `.dll`
files

## Changes Made

### Static Libraries (3 files removed):
- `Notifications.dll` - notifications project is a StaticLibrary
- `os-detection.dll` - no corresponding project found
- `Telemetry.dll` - telemetry projects are StaticLibraries

### Obsolete Projects (3 files removed):
- `fancyzones.dll` - FancyZones now produces `PowerToys.FancyZones.exe`
- `Wox.dll` - only `Wox.Plugin.dll` and `Wox.Infrastructure.dll` exist
- Duplicate `PowerToys.ManagedTelemetry.dll` entry

### WinExe Projects (19 files removed):
**Preview/Thumbnail Handlers (11 files):**
All preview and thumbnail handler C# projects have `OutputType=WinExe`
and only produce `.exe` files:
- Removed `.dll` entries for: GcodePreviewHandler,
MarkdownPreviewHandler, MonacoPreviewHandler, PdfPreviewHandler,
QoiPreviewHandler, SvgPreviewHandler, GcodeThumbnailProvider,
PdfThumbnailProvider, QoiThumbnailProvider, StlThumbnailProvider,
SvgThumbnailProvider

**Application Modules (8 files):**
- `PowerToys.WorkspacesEditor.dll` and
`PowerToys.WorkspacesLauncherUI.dll`
- `PowerToys.Awake.dll` and `PowerToys.ImageResizer.dll` 
- `PowerToys.ColorPickerUI.dll` and `PowerToys.PowerOCR.dll`
- `PowerToys.PowerAccent.dll` and `PowerToys.PowerLauncher.dll`

## Verification

All removed entries were verified by:
1. Checking project files for `OutputType` and `ConfigurationType`
settings
2. Confirming `AssemblyName` and `TargetName` properties
3. Ensuring no actual built artifacts are affected

The signing process should now successfully find all expected files and
eliminate the "0 files out of: 0 files" pattern.

Fixes #40240.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey.alchemer.com/s3/8343779/Copilot-Coding-agent) to
start the survey.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com>
2025-07-04 09:50:22 +08:00
Kai Tao
2ff5adbdd4 [Settings] Complete the settings deeplink (#40376)
<!-- 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
Many settings page's deep link is not implemented, this PR complete them
and make them aligned with the settings page.

<!-- 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-07-04 09:41:19 +08:00
Mike Griese
8615c48c5c this works better 2025-07-03 20:16:03 -05:00
Mike Griese
1aa78e1b96 PARENT: omg 2025-07-03 15:49:42 -05:00
Mike Griese
b0c862dd67 add items to history 2025-07-03 15:27:50 -05:00
Mike Griese
b6e3b8a3ee rename 2025-07-03 12:46:29 -05:00
Mike Griese
a2d0d3b262 start adding callbacks to commands 2025-07-03 12:46:10 -05:00
Niels Laute
faebd21a2d [Settings] String updates and minor UX tweaks (#40249)
## Summary of the Pull Request

- I asked Copilot nicely to rewrite some of our strings to be inline
with the Windows writing style guide and to be less verbose or provide a
better explaination.
- Since the Sound expander on the Always on Top page had a single
setting, turning that into a card.

Before:

![image](https://github.com/user-attachments/assets/04ec18f1-06a4-4185-ac50-655070dcf3fc)

After:

![image](https://github.com/user-attachments/assets/e5d62e00-5d0a-4a2d-9011-493232689f41)


Simplified UI + less XAML for diagnostics data:

Before:

![image](https://github.com/user-attachments/assets/c0b0468b-7587-4684-8fbf-bad856e4d30c)

After:

![image](https://github.com/user-attachments/assets/185f2ff8-a3b1-49bd-8617-24b2c5a454a2)



<!-- 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

---------

Co-authored-by: Kai Tao <kaitao@microsoft.com>
2025-07-03 16:20:22 +02:00
Niels Laute
6412d5722e [UX] Settings - fix header position (#40160)
## Summary of the Pull Request
Due to a padding in the scrollviewer hosting the settingspage content,
the title drifts away upon resizing the settings window.
This PR fixes that issue by adding the (20px) padding to the maxwidth of
the header textblock.

Before:

![header](https://github.com/user-attachments/assets/6704a59d-843c-49a7-a109-e22e2cecccfe)

After:

![header2](https://github.com/user-attachments/assets/c9b0aa46-fcab-43ac-be87-ea423d810606)



## PR Checklist

- [x] **Closes:** #40159
- [ ] **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-07-03 04:33:56 -05:00
Mike Griese
ee53a6d138 Use a dict for string->listitem for history 2025-07-02 06:57:02 -05:00
Mike Griese
126a3c0de8 load history items with a timeout 2025-07-02 06:20:07 -05:00
Mike Griese
a94bd91dba Merge branch 'dev/migrie/f/run-page-2-with-suggestions' into dev/migrie/f/run-page-2-with-history 2025-07-01 16:32:14 -05:00
Mike Griese
b2f2462ad6 The fallback command should also be cancellable 2025-07-01 16:13:28 -05:00
Mike Griese
b6f0ced53e Exes that are found in a dir should be RunExeItems 2025-07-01 14:46:58 -05:00
Mike Griese
a405f27d19 reuse existing exe item when we can 2025-07-01 14:30:19 -05:00
Mike Griese
86d04cc3bd timeout resolving network paths 2025-07-01 13:57:14 -05:00
Mike Griese
381482e9a0 Revert "try to async the File.Exists, but get sad"
This reverts commit 9e7d212c31.
2025-07-01 13:34:08 -05:00
Mike Griese
9e7d212c31 try to async the File.Exists, but get sad 2025-07-01 13:34:04 -05:00
Mike Griese
11c9d913cc Make run searches async, so subsequent ones cancel the previous.
thanks copilot, this actually worked
2025-07-01 12:36:01 -05:00
Mike Griese
31f5af7e14 URI only if the file doesn't exist 2025-07-01 12:17:38 -05:00
Mike Griese
568c2ca388 Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-07-01 10:04:59 -05:00
Mike Griese
27124972cd Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-06-30 08:39:19 -05:00
Mike Griese
ca9488c875 Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-06-30 05:53:59 -05:00
Mike Griese
68461ab553 This only sometimes didn't work, and never while debugged 2025-06-19 08:59:32 -05:00
Mike Griese
ce150322ed holy shit it worked
I'm POSITIVE there's a lot of places where this won't.
But if we just exclude TOOLWINDOW's from the EnumWindows call, we can
toss FG back pretty consistently?
2025-06-19 08:43:30 -05:00
Mike Griese
d73a8a0a2c Load the run history immediately 2025-06-18 09:37:00 -05:00
Mike Griese
c953ce7eca Add support for using the RunDlg history to initialize the run history 2025-06-18 08:43:48 -05:00
Mike Griese
9fc7a180d4 xamlformat(?) 2025-06-10 06:20:09 -05:00
Mike Griese
ccca007562 Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-06-10 06:15:59 -05:00
Mike Griese
07c85065f2 Add support for URIs too 2025-06-09 20:42:35 -05:00
Mike Griese
c06767b73e Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-06-08 12:46:12 -05:00
Mike Griese
4c639a085c Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2-with-suggestions 2025-06-08 06:32:20 -05:00
Mike Griese
8aa7349f32 handle backspacing suggestions cleaner 2025-06-08 05:56:26 -05:00
Mike Griese
fa86f2223c PARENT: deal with drives and quotes better 2025-06-08 05:56:03 -05:00
Mike Griese
30215e8b4f PARENT: much better env var handling 2025-06-06 17:58:29 -05:00
Mike Griese
ee386e7be4 PARENT: Suppress file fallback when run finds something 2025-06-05 04:52:25 -05:00
Mike Griese
9ba2030b5e OWN BRANCH: Fix the weighting for the web search fallbacks
Closes #39419
This can be moved to its own branch.
2025-06-04 06:34:26 -05:00
Mike Griese
1ba832b732 Make the calc command play better with TextToSuggest 2025-06-02 14:11:05 -05:00
Mike Griese
46f27c1612 make it build 2025-05-30 07:00:29 -05:00
Mike Griese
1d81ef8935 PARENT: Start working on making run fallback hide the file search one 2025-05-23 18:16:47 -07:00
Mike Griese
e19cdba074 PARENT: Update the fallback item too 2025-05-23 11:28:28 -07:00
Mike Griese
4223286061 PARENT: Expand env vars 2025-05-23 09:39:19 -07:00
Mike Griese
8e6bd141ca PARENT: Better deal with spaces 2025-05-23 09:37:29 -07:00
Mike Griese
d29121c3fd PARENT: logging that I don't think I needed 2025-05-18 19:00:54 -05:00
Mike Griese
58e0530980 Accept suggestions better 2025-05-18 19:00:41 -05:00
Mike Griese
9272e3112b This feels pretty great tbh 2025-05-18 18:47:10 -05:00
Mike Griese
a64095c3d3 try messing with selection to indicate text suggestion 2025-05-18 17:00:32 -05:00
Mike Griese
49480041cd this is near perfect 2025-05-18 15:43:28 -05:00
Mike Griese
547b664a8c Better handle removing the path item if the thing is an exe 2025-05-18 15:07:11 -05:00
Mike Griese
72320bea79 Add a single command for running the commandline. Remove history 2025-05-18 14:29:54 -05:00
Mike Griese
910de53a0a Merge remote-tracking branch 'origin/main' into dev/migrie/f/run-page-2 2025-05-18 10:18:20 -05:00
Mike Griese
11f60de543 stash: start moving into the main run page 2025-05-08 06:13:57 -05:00
Mike Griese
e7eb2d0239 Add the TextToSuggest back, just for demo purposes 2025-04-30 20:36:24 -05:00
Mike Griese
aefae2935e heck just use the original shell provider for history 2025-04-30 10:05:16 -05:00
Mike Griese
727367960e Start adding history 2025-04-30 06:01:45 -05:00
Mike Griese
32700658fd Deduplicate a bunch of code 2025-04-30 04:25:59 -05:00
Mike Griese
a7cb535515 What if the run page had the same typeahead that Rundlg had? 2025-04-29 17:01:49 -05:00
331 changed files with 6899 additions and 2079 deletions

View File

@@ -1,6 +1,9 @@
# D2D
#D?2D
# Repeated letters
\b([a-z])\g{-1}{2,}\b
# marker to ignore all code on line
^.*/\* #no-spell-check-line \*/.*$
# marker to ignore all code on line
@@ -10,6 +13,9 @@
# cspell inline
^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b
# copyright
Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
# patch hunk comments
^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .*
# git index header
@@ -18,6 +24,9 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
# file permissions
['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
# css fonts
\bfont(?:-family|):[^;}]+
# css url wrappings
\burl\([^)]+\)
@@ -29,7 +38,7 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
# data url in quotes
([`'"])data:(?:[^ `'"].*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
# data url
\bdata:[-a-zA-Z=;:/0-9+]*,\S*
\bdata:[-a-zA-Z=;:/0-9+_]*,\S*
# https/http/file urls
#(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/*%?=~_|!:,.;]+[-A-Za-z0-9+&@#/*%=~_|]
@@ -68,6 +77,8 @@ magnet:[?=:\w]+
# Amazon
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
# AWS ARN
arn:aws:[-/:\w]+
# AWS S3
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
# AWS execute-api
@@ -94,6 +105,8 @@ vpc-\w+
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
# Google APIs
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
# Google Artifact Registry
\.pkg\.dev(?:/[-\w]+)+(?::[-\w]+|)
# Google Storage
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
# Google Calendar
@@ -129,6 +142,8 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
# Google Colab Research Drive
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
# Google Cloud regions
(?:us|(?:north|south)america|europe|asia|australia|me|africa)-(?:north|south|east|west|central){1,2}\d+
# GitHub SHAs (api)
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
@@ -167,6 +182,12 @@ GHSA(?:-[0-9a-z]{4}){3}
# GitLab commits
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
# #includes
^\s*#include\s*(?:<.*?>|".*?")
# #pragma lib
^\s*#pragma comment\(lib, ".*?"\)
# binance
accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
@@ -219,7 +240,7 @@ accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
\bmedium\.com/@?[^/\s"]+/[-\w]+
# microsoft
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]*
# powerbi
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
# vs devops
@@ -393,7 +414,7 @@ ipfs://[0-9a-zA-Z]{3,}
\bgetopts\s+(?:"[^"]+"|'[^']+')
# ANSI color codes
(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
# URL escaped characters
%[0-9A-F][A-F](?=[A-Za-z])
@@ -429,10 +450,14 @@ sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]*
# pki (base64)
LS0tLS1CRUdJT.*
# C# includes
^\s*using [^;]+;
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
(?:[\\0][xX]|\\u\{?|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
# integrity
integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1}
@@ -450,7 +475,10 @@ integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1}
Name\[[^\]]+\]=.*
# IServiceProvider / isAThing
(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
(?:(?:\b|_|(?<=[a-z]))I|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
# python
#\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
# crypt
(['"])\$2[ayb]\$.{56}\g{-1}
@@ -464,17 +492,14 @@ Name\[[^\]]+\]=.*
# machine learning (?)
#\b(?i)ml(?=[a-z]{2,})
# python
#\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
# scrypt / argon
\$(?:scrypt|argon\d+[di]*)\$\S+
# go.sum
\bh1:\S+
# scala imports
^import (?:[\w.]|\{\w*?(?:,\s*(?:\w*|\*))+\})+
# imports
^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+
# scala modules
#("[^"]+"\s*%%?\s*){2,3}"[^"]+"
@@ -483,13 +508,13 @@ Name\[[^\]]+\]=.*
image: [-\w./:@]+
# Docker images
^\s*FROM\s+\S+:\S+(?:\s+AS\s+\S+|)
^\s*(?i)FROM\s+\S+:\S+(?:\s+AS\s+\S+|)
# `docker images` REPOSITORY TAG IMAGE ID CREATED SIZE
\s*\S+/\S+\s+\S+\s+[0-9a-f]{8,}\s+\d+\s+(?:hour|day|week)s ago\s+[\d.]+[KMGT]B
# Intel intrinsics
_mm_(?!dd)\w+
_mm\d*_(?!dd)\w+
# Input to GitHub JSON
content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1}
@@ -523,7 +548,7 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1}
# javascript replace regex
\.replace\(/[^/\s"]{3,}/[gim]*\s*,
# assign regex
= /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gi]?(?=\W|$)
= /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gim]*(?=\W|$)
# perl regex test
[!=]~ (?:/.*/|m\{.*?\}|m<.*?>|m([|!/@#,;']).*?\g{-1})
@@ -537,7 +562,7 @@ perl(?:\s+-[a-zA-Z]\w*)+
#(?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\()
# Go regular expressions
regexp?\.MustCompile\(`[^`]*`\)
regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\)
# regex choice
\(\?:[^)]+\|[^)]+\)
@@ -585,7 +610,7 @@ urn:shemas-jetbrains-com
# xcode
# xcodeproject scenes
(?:Controller|destination|ID|id)="\w{3}-\w{2}-\w{3}"
(?:Controller|destination|(?:first|second)Item|ID|id)="\w{3}-\w{2}-\w{3}"
# xcode api botches
customObjectInstantitationMethod
@@ -600,27 +625,33 @@ PrependWithABINamepsace
\.fa-[-a-z0-9]+
# bearer auth
(['"])[Bb]ear[e][r] .*?\g{-1}
(['"])[Bb]ear[e][r] .{3,}?\g{-1}
# bearer auth
\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]+
\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]{3,}
# basic auth
(['"])[Bb]asic [-a-zA-Z=;:/0-9+]{3,}\g{-1}
# basic auth
: [Bb]asic [-a-zA-Z=;:/0-9+.]{3,}
# base64 encoded content
#([`'"])[-a-zA-Z=;:/0-9+]{3,}=\g{-1}
# base64 encoded content in xml/sgml
>[-a-zA-Z=;:/0-9+]{3,}=</
# base64 encoded content, possibly wrapped in mime
(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
#(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
# base64 encoded json
\beyJ[-a-zA-Z=;:/0-9+]+
# base64 encoded pkcs
\bMII[-a-zA-Z=;:/0-9+]+
#\bMII[-a-zA-Z=;:/0-9+]+
# uuencoded
#[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_]{40,}
# DNS rr data
(?:\d+\s+){3}(?:[-+/=.\w]{2,}\s*){1,2}
#(?:\d+\s+){3}(?:[-+/=.\w]{2,}\s*){1,2}
# encoded-word
=\?[-a-zA-Z0-9"*%]+\?[BQ]\?[^?]{0,75}\?=
@@ -629,7 +660,7 @@ PrependWithABINamepsace
\bnumer\b(?=.*denom)
# Time Zones
\b(?:Africa|Atlantic|America|Antarctica|Asia|Australia|Europe|Indian|Pacific)(?:/\w+)+
\b(?:Africa|Atlantic|America|Antarctica|Arctic|Asia|Australia|Europe|Indian|Pacific)(?:/[-\w]+)+
# linux kernel info
^(?:bugs|flags|Features)\s+:.*
@@ -669,13 +700,13 @@ systemd.*?running in system mode \([-+].*\)$
TeX/AMS
# File extensions
\*\.[+\w]+,
#\*\.[+\w]+,
# eslint
"varsIgnorePattern": ".+"
# nolint
nolint:\w+
nolint:\s*[\w,]+
# Windows short paths
[/\\][^/\\]{5,6}~\d{1,2}(?=[/\\])
@@ -683,6 +714,9 @@ nolint:\w+
# Windows Resources with accelerators
\b[A-Z]&[a-z]+\b(?!;)
# signed off by
(?i)Signed-off-by: .*
# cygwin paths
/cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+
@@ -715,29 +749,31 @@ W/"[^"]+"
# Compiler flags (Unix, Java/Scala)
# Use if you have things like `-Pdocker` and want to treat them as `docker`
#(?:^|[\t ,>"'`=(])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
#(?:^|[\t ,>"'`=(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# Compiler flags (Windows / PowerShell)
# This is a subset of the more general compiler flags pattern.
# It avoids matching `-Path` to prevent it from being treated as `ath`
#(?:^|[\t ,"'`=(])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}))
#(?:^|[\t ,"'`=(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}))
# Compiler flags (linker)
,-B
# libraries
(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
# WWNN/WWPN (NAA identifiers)
\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b
# Library prefix
# e.g., `lib`+`archive`, `lib`+`raw`, `lib`+`unwind`
# (ignores some words that happen to start with `lib`)
(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
# iSCSI iqn (approximate regex)
\biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b
# WWNN/WWPN (NAA identifiers)
\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b
# curl arguments
\b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
# set arguments
\b(?:bash|sh|set)(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
\b(?:bash|sh|set)(?:\s+[-+][abefimouxE]{1,2})*\s+[-+][abefimouxE]{3,}(?:\s+[-+][abefimouxE]+)*
# tar arguments
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...

View File

@@ -73,7 +73,9 @@
\.qm$
\.s$
\.sig$
\.snk$
\.so$
\.stl$
\.svgz?$
\.sys$
\.tar$
@@ -90,40 +92,41 @@
\.xz$
\.zip$
^\.github/actions/spell-check/
^\.github/workflows/spelling\d*\.yml$
^\.gitmodules$
^\Q.github/workflows/spelling2.yml\E$
^\Q.pipelines/272MSSharedLibSN2048.snk\E$
^\Q.pipelines/ESRPSigning_core.json\E$
^\Qdoc/devdocs/localization.md\E$
^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$
^\Qsrc/common/notifications/BackgroundActivatorDLL/cpp.hint\E$
^\Qsrc/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002.pdn\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Assets/ColorPicker/colorPicker.cur\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$
^\Qsrc/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs\E$
^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmAbout.cs\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmInputCallback.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmLogon.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMatrix.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMessage.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMouseCursor.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmScreen.resx\E$
^\Qsrc/modules/MouseWithoutBorders/ModuleInterface/generateSecurityDescriptor.h\E$
^\Qsrc/modules/peek/Peek.Common/NativeMethods.txt\E$
^\Qsrc/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator.cs\E$
^\Qsrc/modules/previewpane/UnitTests-StlThumbnailProvider/HelperFiles/sample.stl\E$
^\Qtools/project_template/ModuleTemplate/resource.h\E$
^doc/devdocs/akaLinks\.md$
^NOTICE\.md$
^src/common/CalculatorEngineCommon/exprtk\.hpp$
^src/common/ManagedCommon/ColorFormatHelper\.cs$
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
^src/common/sysinternals/Eula/
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$
^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/
^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$
^src/modules/MouseWithoutBorders/App/.*/NativeMethods\.cs$
^src/modules/MouseWithoutBorders/App/Form/.*\.Designer\.cs$
^src/modules/MouseWithoutBorders/App/Form/.*\.resx$
^src/modules/MouseWithoutBorders/App/Form/frmAbout\.cs$
^src/modules/MouseWithoutBorders/App/Form/frmInputCallback\.resx$
^src/modules/MouseWithoutBorders/App/Form/frmLogon\.resx$
^src/modules/MouseWithoutBorders/App/Form/frmMatrix\.resx$
^src/modules/MouseWithoutBorders/App/Form/frmMessage\.resx$
^src/modules/MouseWithoutBorders/App/Form/frmMouseCursor\.resx$
^src/modules/MouseWithoutBorders/App/Form/frmScreen\.resx$
^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$
^src/modules/MouseWithoutBorders/ModuleInterface/generateSecurityDescriptor\.h$
^src/modules/peek/Peek.Common/NativeMethods\.txt$
^src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator\.cs$
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$
^src/Monaco/
^src/common/sysinternals/Eula/
^tools/project_template/ModuleTemplate/resource\.h$
^tools/Verification scripts/Check preview handler registration\.ps1$
ignore$
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
^src/common/CalculatorEngineCommon/exprtk\.hpp$

View File

@@ -1,4 +1,4 @@
aaaa
AAAAs
abcdefghjkmnpqrstuvxyz
abgr
ABlocked
@@ -9,23 +9,24 @@ ACCEPTFILES
ACCESSDENIED
ACCESSTOKEN
acfs
ACIE
AClient
AColumn
acrt
ACTIVATEAPP
activationaction
ACTIVATEOPTIONS
ACVS
activationaction
adaptivecards
ADate
ADDSTRING
ADDUNDORECORD
ADifferent
ADMINS
adml
admx
advancedpaste
advfirewall
AFeature
AFFINETRANSFORM
affordances
AFX
AGGREGATABLE
@@ -43,12 +44,11 @@ ALPHATYPE
AModifier
amr
ANDSCANS
animatedvisuals
Animnate
ansicolor
ANull
AOC
aocfnapldcnfbofgmbbllojgocaelgdd
AOklab
APARTMENTTHREADED
APeriod
apicontract
@@ -66,10 +66,10 @@ appref
appsettings
appwindow
appwiz
appxpackage
APSTUDIO
AQS
ARandom
Arash
ARCHITEW
ARemapped
ARPINSTALLLOCATION
@@ -77,6 +77,7 @@ ARPPRODUCTICON
ARRAYSIZE
ARROWKEYS
asf
Ashcraft
AShortcut
ASingle
ASSOCCHANGED
@@ -85,7 +86,6 @@ ASSOCSTR
ASYNCWINDOWPLACEMENT
ASYNCWINDOWPOS
atl
atleast
ATRIOX
aumid
Authenticode
@@ -99,12 +99,11 @@ Autorun
AUTOTICKS
AUTOUPDATE
AValid
awakeness
AWAYMODE
azcliversion
azman
backtracer
bbwe
BCIE
bck
BESTEFFORT
bezelled
@@ -130,18 +129,16 @@ Blt
BLURBEHIND
BLURREGION
bmi
bms
BNDBk
BNumber
BODGY
BOklab
BOOTSTRAPPERINSTALLFOLDER
bostrot
BOTTOMALIGN
boxmodel
BPBF
bpmf
bpp
Breadcrumb
breadcrumb
Browsable
BROWSEINFO
bsd
@@ -164,7 +161,6 @@ callbackptr
calpwstr
Cangjie
CANRENAME
Cantarell
CAPTUREBLT
CAPTURECHANGED
CARETBLINKING
@@ -175,6 +171,7 @@ CCHFORMNAME
CCom
CContext
CDeclaration
CDPX
CElems
CENTERALIGN
cer
@@ -187,12 +184,12 @@ CHILDACTIVATE
CHILDWINDOW
CHOOSEFONT
cidl
CIELCh
cim
CImage
cla
CLASSDC
CLASSNOTAVAILABLE
cleanmgr
clickable
clickonce
CLIENTEDGE
@@ -204,7 +201,6 @@ CLIPSIBLINGS
closesocket
clp
CLSCTX
CLSCTXLOCALSERVER
clsids
Clusion
cmder
@@ -218,6 +214,7 @@ CMONITORS
cmph
CNF
coclass
CODENAME
codereview
Codespaces
COINIT
@@ -231,6 +228,7 @@ comctl
comdlg
comexp
cominterop
commandnotfound
commandpalette
compmgmt
COMPOSITIONFULL
@@ -238,15 +236,18 @@ CONFIGW
CONFLICTINGMODIFIERKEY
CONFLICTINGMODIFIERSHORTCUT
CONOUT
constexpr
contentdialog
contentfiles
CONTEXTHELP
CONTEXTMENUHANDLER
contractversion
CONTROLL
CONTROLPARENT
copiedcolorrepresentation
copyable
COPYPEN
COREWINDOW
Corpor
cotaskmem
COULDNOT
countof
@@ -263,6 +264,7 @@ CREATEWINDOWFAILED
CRECT
CRH
critsec
cropandlock
Crossdevice
CSearch
CSettings
@@ -272,9 +274,7 @@ CStyle
cswin
CTest
CTEXT
Ctl
CTLCOLORSTATIC
currentculture
CURRENTDIR
CURSORINFO
cursorpos
@@ -286,17 +286,17 @@ CVal
cvd
CVirtual
CVS
cxfksword
CXSCREEN
CXSMICON
CXVIRTUALSCREEN
cyberrex
CYSCREEN
CYSMICON
CYVIRTUALSCREEN
cziplib
Dac
dacl
DAffine
DAFFINETRANSFORM
datareader
datatracker
dataversion
@@ -307,18 +307,13 @@ DBLEPSILON
DBPROP
DBPROPIDSET
DBPROPSET
DCapture
DCBA
DCOM
DComposition
DCR
ddd
DDEIf
DDevice
DDxgi
Deact
debugbreak
declatory
decryptor
Dedup
Deeplink
@@ -334,10 +329,8 @@ DEFAULTTOPRIMARY
DEFERERASE
DEFPUSHBUTTON
deinitialization
DELA
DELETEDKEYIMAGE
DELETESCANS
deletethis
DEMOTYPE
DENORMAL
depersist
@@ -358,7 +351,6 @@ devpal
DFX
DIALOGEX
digicert
dimm
DINORMAL
DISABLEASACTIONKEY
DISABLENOSCROLL
@@ -382,7 +374,6 @@ DONTVALIDATEPATH
dotnet
DPICHANGED
DPIs
DPolicy
DPSAPI
DQTAT
DQTYPE
@@ -391,13 +382,12 @@ DRAWFRAME
drawingcolor
dreamsofameaningfullife
drivedetectionwarning
Droid
DROPFILES
DSTINVERT
DSurface
DTexture
DString
DSVG
DTo
DUMMYUNIONNAME
Dutil
DVASPECT
DVASPECTINFO
DVD
@@ -440,6 +430,7 @@ ENDSESSION
ENSUREVISIBLE
ENTERSIZEMOVE
ENU
environmentvariables
EOAC
EPO
epu
@@ -448,7 +439,6 @@ EREOF
EResize
ERRORIMAGE
ERRORTITLE
erwrite
ESettings
esrp
ETDT
@@ -456,7 +446,6 @@ etl
etw
eula
eurochange
eventlog
eventvwr
evt
EWXFORCE
@@ -491,12 +480,15 @@ eyetracker
FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
FARPROC
fesf
fff
FFFF
FILEEXPLORER
fileexploreraddons
fileexplorerpreview
FILEFLAGS
FILEFLAGSMASK
FILELOCKSMITH
filelocksmith
FILELOCKSMITHCONTEXTMENU
FILELOCKSMITHEXT
FILELOCKSMITHLIBINTEROP
@@ -509,10 +501,9 @@ FILESUBTYPE
FILESYSPATH
Filetime
FILEVERSION
Filterkeyboard
FILTERMODE
findfast
Fira
findmymouse
FIXEDFILEINFO
FIXEDSYS
flac
@@ -529,13 +520,13 @@ FORCEMINIMIZE
FORMATDLGORD
formatetc
FORPARSING
Fqc
FRAMECHANGED
frm
Froml
FROMTOUCH
fsanitize
fsmgmt
fxf
fuzzingtesting
FZE
gacutil
Gaeilge
@@ -547,7 +538,6 @@ gdi
gdiplus
GDIPVER
GDISCALED
GEmoji
GETCLIENTAREAANIMATION
GETCURSEL
GETDESKWALLPAPER
@@ -572,7 +562,6 @@ GPOCA
gpp
gpu
gradians
gsl
GSM
gtm
guiddata
@@ -620,7 +609,6 @@ hicon
HIDEREADONLY
HIDEWINDOW
Hif
hightlight
HIMAGELIST
himl
hinst
@@ -644,12 +632,14 @@ homljgmgpmcbpjbnjpfijnhipfkiclkd
HORZRES
HORZSIZE
Hostbackdropbrush
hostsfileeditor
Hostx
hotfixes
hotkeycontrol
HOTKEYF
hotkeys
hotlight
hotspot
Hostx
HPAINTBUFFER
HRAWINPUT
HREDRAW
@@ -678,7 +668,6 @@ HWNDPARENT
HWNDPREV
hyjiacan
IAI
IBeam
icf
ICONERROR
ICONLOCATION
@@ -688,13 +677,13 @@ idk
idl
idlist
IDOK
IDOn
IDR
IDXGI
ietf
IEXPLORE
IFACEMETHOD
IFACEMETHODIMP
IFile
IGNOREUNKNOWN
IGo
iid
@@ -720,7 +709,7 @@ INITDIALOG
INITGUID
INITTOLOGFONTSTRUCT
INLINEPREFIX
inorder
Inlines
INPC
inproc
INPUTHARDWARE
@@ -745,28 +734,26 @@ Interlop
INTRESOURCE
INVALIDARG
invalidoperatioexception
invokecommand
ipcmanager
IPREVIEW
ipreviewhandlervisualssetfont
irow
irprops
isbi
ISearch
ISettings
isocpp
isfinite
iss
issecret
ISSEPARATOR
issuecomment
istep
ith
ITHUMBNAIL
IUI
IUnknown
IUse
IWIC
iwr
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jjw
JLO
jobject
jpe
jpnime
@@ -787,16 +774,19 @@ KEYEVENTF
KEYIMAGE
keynum
keyremaps
keyring
keyvault
KILLFOCUS
killrunner
kmph
Kybd
lastcodeanalysissucceeded
Lastdevice
LASTEXITCODE
LAYOUTRTL
LCh
lcid
LCIDTo
lcl
Lclean
Ldone
Ldr
@@ -807,6 +797,7 @@ LError
LEVELID
LExit
lhwnd
LIBFUZZER
LIBID
LIMITSIZE
LIMITTEXT
@@ -820,10 +811,8 @@ LLKH
llkhf
LMEM
LMENU
lnks
LOADFROMFILE
LOBYTE
LOCALDISPLAY
localpackage
LOCALSYSTEM
LOCATIONCHANGE
@@ -833,6 +822,7 @@ LOGFONTW
logon
LOGPIXELSX
LOGPIXELSY
LOn
longdate
LONGNAMES
lowlevel
@@ -877,11 +867,9 @@ lstrcmpi
lstrcpyn
lstrlen
LTEXT
LTk
LTRREADING
luid
LUMA
LUQ
lusrmgr
LVal
lvm
@@ -895,6 +883,7 @@ MAKEINTRESOURCEW
MAKELANGID
MAKELONG
MAKELPARAM
makepri
MAKEWPARAM
manifestdependency
MAPPEDTOSAMEKEY
@@ -926,12 +915,13 @@ Microwaved
middleclickaction
midl
mii
MIIM
mikeclayton
mindaro
Minimizable
MINIMIZEBOX
MINIMIZEEND
MINIMIZESTART
miniz
MINMAXINFO
minwindef
Mip
@@ -955,13 +945,14 @@ monitorinfof
MOUSEACTIVATE
MOUSEDATA
MOUSEEVENTF
mousehighlighter
MOUSEHWHEEL
MOUSEINPUT
mousejump
mousepointer
mouseutils
MOVESIZEEND
MOVESIZESTART
MOZILLAPL
MOZPL
mpmc
MRM
MRT
mru
@@ -984,15 +975,14 @@ msixbundle
MSIXCA
MSLLHOOKSTRUCT
Mso
msp
msrc
msstore
mst
msvcp
MTND
MULTIPLEUSE
multizone
muxc
MVPs
mvvm
MVVMTK
MWBEx
@@ -1014,7 +1004,6 @@ NCMBUTTONDOWN
NCMBUTTONUP
NCMOUSELEAVE
NCMOUSEMOVE
nconsectetur
ncpa
NCPAINT
NCRENDERING
@@ -1035,8 +1024,6 @@ newplus
NEWPLUSCONTEXTMENU
NEWPLUSSHELLEXTENSIONWIN
newrow
newsgroups
NGQt
nicksnettravels
NIF
NLog
@@ -1056,6 +1043,7 @@ NOCOPYBITS
NOCOPYSECURITYATTRIBS
NOCRLF
nodeca
nodiscard
NODRAWCAPTION
NODRAWICON
NOINHERITLAYOUT
@@ -1071,7 +1059,6 @@ NONANTIALIASED
nonclient
NONCLIENTMETRICSW
NONELEVATED
NONINFRINGEMENT
nonspace
nonstd
NOOWNERZORDER
@@ -1099,18 +1086,17 @@ NOTOPMOST
NOTRACK
NOTSRCCOPY
NOTSRCERASE
NOTXORPEN
notwindows
NOTXORPEN
NOZORDER
NPH
npmjs
NResize
nsunt
NTAPI
ntdll
ntfs
NTSTATUS
NTSYSAPI
NTZm
NULLCURSOR
nullonfailure
numberbox
@@ -1128,6 +1114,7 @@ oldpath
oldtheme
oleaut
OLECHAR
onebranch
openas
opencode
OPENFILENAME
@@ -1137,7 +1124,6 @@ OPTIMIZEFORINVOKE
ORPHANEDDIALOGTITLE
ORSCANS
oss
ostr
OSVERSIONINFO
OSVERSIONINFOEX
OSVERSIONINFOEXW
@@ -1145,14 +1131,13 @@ OSVERSIONINFOW
osvi
OUTOFCONTEXT
Outptr
outputtype
outsettings
OVERLAPPEDWINDOW
Oversampling
OVERWRITEPROMPT
OWMt
OWNDC
OWNERDRAWFIXED
OWRj
Packagemanager
PACL
PAINTSTRUCT
@@ -1166,7 +1151,6 @@ PARTIALCONFIRMATIONDIALOGTITLE
PATCOPY
PATHMUSTEXIST
PATINVERT
partow
PATPAINT
pbc
pbi
@@ -1179,7 +1163,7 @@ pchast
PCIDLIST
PCTSTR
PCWSTR
pdbs
PDBs
PDEVMODE
pdisp
PDLL
@@ -1212,13 +1196,13 @@ pinfo
pinvoke
pipename
PKBDLLHOOKSTRUCT
Playbadge
plib
ploc
ploca
plocm
pluginsmodel
PMAGTRANSFORM
PMs
PMSIHANDLE
pnid
PNMLINK
@@ -1251,6 +1235,7 @@ ppstm
ppsz
pptal
ppv
ppwsz
prc
Prefixer
prependpath
@@ -1272,6 +1257,7 @@ prm
proactively
PROCESSENTRY
PROCESSKEY
processthreadsapi
PROCESSTRACE
procmon
PRODEXT
@@ -1287,10 +1273,10 @@ prvpane
psapi
pscid
PSECURITY
psexec
psfgao
psfi
PSMODULEPATH
Psr
psrm
psrree
pstatstg
@@ -1308,14 +1294,12 @@ PTOKEN
PToy
ptstr
pui
Puser
PWAs
pwcs
PWSTR
pwsz
pwtd
QDC
qianlifeng
qit
QITAB
QITABENT
@@ -1324,11 +1308,10 @@ Quarternary
QUERYENDSESSION
QUERYOPEN
QUEUESYNC
quickaccent
QUNS
QXZ
RAII
RAlt
Rappl
randi
Rasterization
Rasterize
@@ -1355,11 +1338,11 @@ REGCLS
regfile
REGISTERCLASSFAILED
REGISTRYHEADER
registrypath
REGISTRYPREVIEWEXT
registryroot
regkey
regroot
regsvr
REINSTALLMODE
reloadable
Relogger
@@ -1384,7 +1367,6 @@ RESIZETOFIT
resmimetype
RESOURCEID
RESTORETOMAXIMIZED
resultlist
RETURNONLYFSDIRS
RGBQUAD
rgbs
@@ -1393,10 +1375,10 @@ rgf
rgh
rgn
rgs
rguid
RIDEV
RIGHTSCROLLBAR
riid
ringbuffer
RKey
RNumber
rop
@@ -1404,7 +1386,6 @@ ROUNDSMALL
ROWSETEXT
rpcrt
RRF
rrr
rsop
Rsp
rstringalnum
@@ -1412,6 +1393,7 @@ rstringalpha
rstringdigit
RTB
RTLREADING
rtm
runas
rundll
rungameid
@@ -1430,10 +1412,9 @@ SAVEFAILED
scanled
schedtasks
SCID
Scip
scipbe
Scode
SCREENFONTS
screenruler
screensaver
screenshots
scrollviewer
@@ -1479,7 +1460,7 @@ SFGAOF
SHACF
SHANDLE
sharepoint
sharpkeys
sharpfuzz
SHCNE
SHCNF
SHCONTF
@@ -1505,7 +1486,6 @@ SHORTCUTMAXONEACTIONKEY
SHORTCUTNOREPEATEDMODIFIER
SHORTCUTONEACTIONKEY
SHORTCUTSTARTWITHMODIFIER
Shortcuttool
shortdate
SHORTPATH
shortsplit
@@ -1545,13 +1525,13 @@ SLGP
sln
SMALLICON
smartphone
smileys
SMTO
SNAPPROCESS
snk
snwprintf
softline
SOURCECLIENTAREAONLY
sourced
sourcedoc
SOURCEHEADER
sourcesdirectory
@@ -1615,11 +1595,13 @@ strret
stscanf
sttngs
Stubless
stuttery
STYLECHANGED
STYLECHANGING
subkeys
sublang
SUBMODULEUPDATE
subresource
Superbar
sut
svchost
@@ -1649,21 +1631,19 @@ SYSMENU
SYSTEMAPPS
SYSTEMMODAL
SYSTEMTIME
TApplication
TApplied
targ
TARG
TARGETAPPHEADER
TARGETDIR
targetentrypoint
TARGETHEADER
targetver
taskbar
taskkill
taskschd
TCHAR
TCIF
TCITEM
TCN
Tcollab
tcs
tcscat
tcschr
@@ -1672,17 +1652,14 @@ tcscpy
tcsdup
tcslen
tcsrchr
TCustom
tdbuild
TDefault
TDevice
telephon
templatenamespace
TESTONLY
testprocess
TEXCOORD
TEXTBOXNEWLINE
TEXTEXTRACTOR
textextractor
TEXTINCLUDE
tfopen
tgz
@@ -1690,9 +1667,7 @@ themeresources
THH
THICKFRAME
THISCOMPONENT
THotkey
throughs
TIcon
TILEDWINDOW
TILLSON
timedate
@@ -1703,7 +1678,6 @@ TITLEBARINFO
Titlecase
tkcontrols
tkconverters
TLayout
tlb
tlbimp
tlc
@@ -1721,20 +1695,14 @@ tracelogging
tracerpt
trackbar
trafficmanager
traies
transicc
TRAYMOUSEMESSAGE
TResult
triaging
trl
trx
tsa
TSender
TServer
tskill
tstoi
TStr
tweakme
TWF
tymed
TYPEKEYBOARD
@@ -1748,7 +1716,6 @@ UBR
UCallback
ucrt
ucrtd
udit
uefi
uesc
UFlags
@@ -1760,7 +1727,6 @@ ums
uncompilable
UNCPRIORITY
UNDNAME
unhiding
UNICODETEXT
uninstalls
Uniquifies
@@ -1774,7 +1740,6 @@ unwide
unzoom
UOffset
UOI
Updatelayout
UPDATENOW
UPDATEREGISTRY
updown
@@ -1796,6 +1761,7 @@ vabdq
validmodulename
valuegenerator
variantassignment
VARTYPE
vcamp
VCENTER
vcgtq
@@ -1815,12 +1781,11 @@ VERTSIZE
VFT
vget
vgetq
viewmodel
viewmodels
VIRTKEY
VIRTUALDESK
VISEGRADRELAY
visiblecolorformats
Visibletrue
visualeffects
vkey
vmovl
@@ -1872,13 +1837,12 @@ wekyb
wft
wgpocpl
WHEREID
Wholegrain
wic
wifi
wil
wikipedia
WIL
winapi
winappsdk
wincolor
windir
WINDOWCREATED
WINDOWEDGE
@@ -1890,11 +1854,12 @@ WINDOWPOSCHANGING
WINDOWSBUILDNUMBER
windowssearch
windowssettings
windowsterminal
WINDOWSTYLES
WINDOWSTYLESICON
winerror
WINEVENT
winexe
winforms
winget
wingetcreate
Winhook
@@ -1950,7 +1915,6 @@ WRITEOBJECTS
Wrk
wrl
wscui
wsf
wsh
wstr
wsz
@@ -1971,6 +1935,7 @@ XElement
xfd
XFile
XIncrement
XLoc
XNamespace
Xoshiro
XPels
@@ -1984,17 +1949,15 @@ XVIRTUALSCREEN
xxxxxx
YAxis
ycombinator
Yeet
YIncrement
yinle
yinyue
YPels
YResolution
YStr
YTM
YVIRTUALSCREEN
ZEROINIT
Zhiwei
zonability
zonable
zoneset
Zoneszonabletester
@@ -2002,21 +1965,3 @@ Zoomin
zoomit
ZOOMITX
Zorder
ZXk
ZXNs
zzz
ACIE
AOklab
BCIE
BOklab
culori
Evercoder
LCh
CIELCh
CLSCTXINPROCALL
IIDI
irow
lcid
ppwsz
rguid
VARTYPE

View File

@@ -8,6 +8,31 @@
# you might not want to check in code where you skip all the other tests.
#\bfit\(
# English does not use a hyphen between adverbs and nouns
# https://twitter.com/nyttypos/status/1894815686192685239
(?:^|\s)[A-Z]?[a-z]+ly-(?=[a-z]{3,})(?:[.,?!]?\s|$)
# Smart quotes should match
\s[^.?!]+[^.?!]+[^.?!]+|\s[^.?!]+[^.?!]+[^.?!]+|\s”[^.?!“”]+”[^.?!“”]+“[^.?!“”]+”|\s“[^.?!“”]+”[^.?!“”]+”[^.?!“”]+”
# Don't use `requires that` + `to be`
# https://twitter.com/nyttypos/status/1894816551435641027
\brequires that \w+\b[^.]+to be\b
# A fully parenthetical sentences period goes inside the parentheses, not outside.
# https://twitter.com/nyttypos/status/1898844061873639490
\([A-Z][a-z]{2,}(?: [a-z]+){3,}\)\.\s
# Complete sentences shouldn't be in the middle of another sentence as a parenthetical.
(?<!\.)(?<!\betc)\.\),
# Complete sentences in parentheticals should not have a space before the period.
\s\.\)(?!.*\}\})
# This probably indicates Mojibake https://en.wikipedia.org/wiki/Mojibake
# You probably should try to unbake this content
Ã(?:Â[¤¶¥]|[£¢])|Ã
# Should be `HH:MM:SS`
\bHH:SS:MM\b
@@ -24,18 +49,74 @@
# Should be `a priori` or `and prior`
(?i)(?<!posteriori)\sand priori\s
# Should be `a`
\san (?=(?:[b-df-gj-npqtv-xz]|h(?!our|tml|ttp)|r(?!c\b)|s(?!sh|vg))[a-z])
# Articles generally shouldn't be used without a noun and a verb
# - Perhaps you're missing a verb between the noun and the second article.
# - Or, perhaps you should remove the first verb and treat the intervening word as a verb?
# - In some cases you should add a `,` between the noun and the second article.
\s(?:an?|the(?! action))\s(?!way|wh|how\b)[A-Za-z][a-z]+[a-qs-z]\s(?:a(?! bit)n?|the)\s
# Should only be one of `a`, `an`, or `the`
\b(?:(?:an?|the)\s+){2,}\b
# Should be a list `something, a second thing, or a third thing` or `something, a thing to do a thing`
# -- This rule is experimental, if you find it has a high false-positive rate, please let the maintainer know
#(?:^|[?!.] )[^()?!;,.]+, a(?:\s+(?!to\b)\w+)+?\s+an?\b
# Should only be `are` or `can`, not both
\b(?:(?:are|can)\s+){2,}\b
# Should probably be `ABCDEFGHIJKLMNOPQRSTUVWXYZ`
(?i)(?!ABCDEFGHIJKLMNOPQRSTUVWXYZ)ABC[A-Z]{21}YZ
# Should be `an`
#(?<!\b[Ii] |\.)\bam\b
# Should be `anymore`
\bany more[,.]
# Should be `Ask`
(?:^|[.?]\s+)As\s+[A-Z][a-z]{2,}\s[^.?]*?(?:how|if|wh\w+)\b
# Should be `at one fell swoop`
# and only when talking about killing, not some other completion
# Act 4 Scene 3, Macbeth
# https://www.opensourceshakespeare.org/views/plays/play_view.php?WorkID=macbeth&Act=4&Scene=3&Scope=scene
\bin one fell s[lw]?oop\b
# Should be `'`
(?i)\b(?:(?:i|s?he|they|what|who|you)"ll|(?:are|ca|did|do|does|ha[ds]|have|is|should|were|wo|would)n"t|(?:s?he|let|that|there|what|where|who)"s|(?:i|they|we|what|who|you)"ve)\b
(?i)\b(?:(?:i|s?he|they|what|who|you)[`"]ll|(?:are|ca|did|do|does|ha[ds]|have|is|should|were|wo|would)n[`"]t|(?:s?he|let|that|there|what|where|who)[`"]s|(?:i|they|we|what|who|you)[`"]ve)\b
# Should be `background` / `intro text` / `introduction` / `prologue` unless it's a brand or relates to _subterfuge_
(?i)\bpretext\b
# Should be `bearer`
\b(?<=the )burden(?= of bad news\b)
# Should be `bona`
# unless talking about bones
\bbone(?= fide\b)
# Should be `branches`
# ... unless it's really about the meal that replaces breakfast and lunch.
\b[Bb]runches\b
# Should be `briefcase`
\bbrief-case\b
# Should be `by far` or `far and away`
\bby far and away\b
# Should be `by and large`
\bby in large\b
# Should be `bytes`
# unless talking about sports where a team gets to skip a game, or
# saying `goodbyes` (even this is questionable)
(?<!\\)\bbyes\b
# Should be `can, not only ..., ... also...`
\bcan not only.*can also\b
@@ -46,7 +127,10 @@
# > In formal writing and where contractions are frowned upon, use `cannot`.
# > It is possible to write `can not`, but you generally find it only as part of some other construction, such as `not only . . . but also.`
# - if you encounter such a case, add a pattern for that case to patterns.txt.
\b[Cc]an not\b
\b[Cc]an not\b(?! only\b)
# Should be `chart`
(?i)\bhelm\b.*\bchard\b
# Do not use `(click) here` links
# For more information, see:
@@ -56,19 +140,49 @@
# * https://heyoka.medium.com/dont-use-click-here-f32f445d1021
(?i)(?:>|\[)(?:(?:click |)here|this(?=\]\([^\)]+:/)|link|(?:read |)more(?!</value))(?:</|\]\()
# Including "image of" or "picture of" in alt text is unnecessary.
\balt=['"](?:an? |)(?:image|picture) of
# Alt text should be short
\balt=(?:'[^']{126,}'|"[^"]{126,}")
# Should be `effect`
(?<=\btake )affect\b
# Should be `-endian`
\b(?i)(?<=big|little) endian\b
# Should be `equals` to `is equal to`
\bequals to\b
# Should be `ECMA` 262 (JavaScript)
(?i)\bTS\/EMCA\b|\bEMCA(?: \d|\s*Script)|\bEMCA\b(?=.*\bTS\b)
# Should be `ECMA` 340 (Near Field Communications)
(?i)EMCA[- ]340
# Should be `fall back`
\bfallback(?= to)\b
# Should be `for`, `for, to` or `to`
\b(?:for to|to for)\b
# Should be `GitHub`
(?<![&*.]|// |\b(?:from|import|type) )\bGithub\b(?![{()])
# Should be `GitLab`
(?<![&*.]|// |\b(?:from|import|type) )\bGitlab\b(?![{()])
# Should be `heartrending` unless talking about drawing hearts
\b(?i)heart[- ]rendering\b(?![^.?!]*(?:hearts|quirk))
# Should probably be `https://`...
# Markdown generally doesn't assume that links are to urls
\]\(www\.\w
# Should be `intents and purposes`
(?<=[Ff]or all )intensive purposes\b
# Should be `JavaScript`
\bJavascript\b
@@ -84,11 +198,14 @@
# Should be `RabbitMQ`
\bRabbitmq\b
# Should be `TensorFlow`
\bTensorflow\b
# Should be `TypeScript`
\bTypescript\b
# Should be `another`
\ban[- ]other\b
\ban[- ]other(?!-)\b
# Should be `case-(in)sensitive`
\bcase (?:in|)sensitive\b
@@ -108,11 +225,14 @@
# Should be `here-in`, `the`, `them`, `this`, `these` or reworded in some other way
\bthe here(?:\.|,| (?!and|defined))
# Should be `greater than`
\bhigher than\b
# Should be `going to bed` or `going to a bad`
\bgoing to bad(?!-)\b
# Should be `ID`
#\bId\b
# Should be `greater than`
#\bhigher than\b
# Should be `ID` (unless it's a flag/property)
#(?<![-\.])\bId\b(?![(])
# Should be `in front of`
\bin from of\b
@@ -125,14 +245,26 @@
# Should be `use`
\sin used by\b
# Should be `in-depth` if used as an adjective (but `in depth` when used as an adverb)
\bin depth\s(?!rather\b)\w{6,}
# Should be `in-flight` or `on the fly` (unless actually talking about airline flights)
\bon[- ]flight\b(?!=\s+(?:(?:\w{2}|)\d+|availability|booking|computer|data|delay|departure|management|performance|radar|reservation|scheduling|software|status|ticket|time|type|.*(?:hotel|taxi)))
# Should be `is obsolete`
\bis obsolescent\b
# Should be `it's` or `its`
\bits[']
(?<![.'])\bits[']
# Should be `its`
\bit's(?= own\b)
\bit's(?= (?:child|only purpose|own(?:er|)|parent|sibling)\b)
# Should be `for its` (possessive) or `because it is`
\bfor it(?:'s| is)\b
# Should be `lends`
\bleads(?= credence)
# Should be `log in`
\blogin to the
@@ -140,12 +272,34 @@
# Should be `long-standing`
\blong standing\b
# Should be `lose`
(?<=\bwill )loose\b
# `apt-key` is deprecated
# ... instead you should be writing a pair of files:
# ... * the gpg key added to a distinct key ring file based on your project/distro/key...
# ... * the sources.list in a district file -- not simply appended to `/etc/apt/sources.list` -- (there is a newer format [DEB822](https://manpages.debian.org/bookworm/dpkg-dev/deb822.5.en.html)) that references the gpg key.
# Consider:
# ````sh
# curl http://download.something.example.com/$DISTRO/Release.key | \
# gpg --dearmor --yes --output /usr/share/keyrings/something-distro.gpg
# echo "deb [signed-by=/usr/share/keyrings/something-distro.gpg] http://download.something.example.com/repositories/home:/$DISTRO ./" \
# >> /etc/apt/sources.list.d/something-distro.list
# ````
\bapt-key add\b
# Should be `nearby`
\bnear by\b
# Should probably be a person named `Nick` or the abbreviation `NIC`
\bNic\b
# Should be `not supposed`
\bsupposed not\b
# Should be `Once this` or `On this` or even `One that`. Rarely `One, this`
[?!.] One this\b
# Should probably be `much more`
\bmore much\b
@@ -153,7 +307,10 @@
\bperform it's\b
# Should be `opt-in`
(?<!\scan|for)(?<!\sif)\sopt in\s
(?<!\scan|for)(?<!\smust)(?<!\sif)\sopt in\s
# Should be `out-of-date` if acting as an adjective before a noun
\bout of date(?= \w{3,}\b)
# Should be `less than`
\bless then\b
@@ -170,24 +327,89 @@
# Should be `on the other hand`
\b(?i)on another hand\b
# Reword to `on at runtime` or `enabled at launch`
# The former if you mean it can be changed dynamically.
# The latter if you mean that it can be changed without recompiling but not after the program starts.
\bswitched on runtime\b
# Should be `Of course,`
[?.!]\s+Of course\s(?=[-\w\s]+[.?;!,])
# Most people only have two hands. Reword.
\b(?i)on the third hand\b
# Should be `Open Graph`
# unless talking about a specific Open Graph implementation:
# - Java
# - Node
# - Py
# - Ruby
\bOpenGraph\b
# Should be `OpenShift`
\bOpenshift\b
# Should be `otherwise`
\bother[- ]wise\b
# Should be `; otherwise` or `. Otherwise`
# https://study.com/learn/lesson/otherwise-in-a-sentence.html
, [Oo]therwise\b
# Should probably be `Otherwise,`
(?<=\. )Otherwise\s
# Should be `or (more|less)`
\bore (?:more|less)\b
# Should be `or`
\b(?i)true of .*false\b
# Should be `pale`
\b(?<=beyond the )pail\b
# Should be reworded.
# `passthrough` is an adjective
# `pass-through` could be a noun
# `pass through` would be a verb phrase
\b(?i)passthrough(?= an?\b)
# Should be `rather than`
\brather then\b
# Should be `Red Hat`
\bRed[Hh]at\b
# Should be `regardless, ...` or `regardless of (whether)`
\b[Rr]egardless if you\b
# Should be `self-signed`
\bself signed\b
# Should be `SendGrid`
\bSendgrid\b
# Should be `set up` (`setup` is a noun / `set up` is a verb)
\b[Ss]etup(?= (?:an?|the)\b)
# Should be `state`
\bsate(?=\b|[A-Z])|(?<=[a-z])Sate(?=\b|[A-Z])|(?<=[A-Z]{2})Sate(?=\b|[A-Z])
# Should be `this`
\b[Tt]oday(?= morning\b)
# Should be `let's` or `let us`
\b[Ll]ets (?=throw\.)
# Should be `no longer needed`
\bno more needed\b(?! than\b)
# Should be `<see|look> below for the`
(?i)\bfind below the\b
# Should be `then any` unless there's a comparison before the `,`
, than any\b
# Should be `did not exist`
\bwere not existent\b
@@ -197,9 +419,18 @@
# Should be `nonexistent`
\b[Nn]o[nt][- ]existent\b
# Should be `our`
\bspending out time\b
# Should be `@brief` / `@details` / `@param` / `@return` / `@retval`
(?:^\s*|(?:\*|//|/*)\s+`)[\\@](?:breif|(?:detail|detials)|(?:params(?!\.)|prama?)|ret(?:uns?)|retvl)\b
# Should be `more than` or `more, then`
\bmore then\b
# Should be `Pipeline`/`pipeline`
(?:(?<=\b|[A-Z])p|P)ipeLine(?:\b|(?=[A-Z]))
# Should be `preexisting`
[Pp]re[- ]existing
@@ -215,6 +446,9 @@
# Should be `prerequisite`
[Pp]re[- ]requisite
# Should be `QuickTime`
\bQuicktime\b
# Should be `recently changed` or `recent changes`
[Rr]ecent changed
@@ -224,14 +458,30 @@
# Should be `reentrant`
[Rr]e[- ]entrant
# Should be `room for`
\brooms for (?!lease|rent|sale)
# Should be `socioeconomic`
# https://dictionary.cambridge.org/us/dictionary/english/socioeconomic
socio-economic
# Should be `strong suit`
\b(?:my|his|her|their) strong suite\b
# Should probably be `temperatures` unless actually talking about thermal drafts (things birds may fly on)
\bthermals\b
# Should be `there are` or `they are` (or `they're`)
(?i)\btheir are\b
# Should be `understand`
\bunder stand\b
# Should be `URI` or `uri` unless it refers to a person named `Uri`
#(?<!\.)\bUri\b(?![(])
# Should be `URI` or `uri` unless it refers to a person named `Uri` (or a flag)
#(?<![-\.])\bUri\b(?![(])
# Should be `true`
(?i)(?<![\[\]()])\brue(?:= or false)
# Should be `it uses is`
/\bis uses is\b/
@@ -245,12 +495,43 @@
# Should be `where`
\bwere they are\b
# Should be `why`
, way(?= is [^.]*\?)
# should be `vCenter`
\bV[Cc]enter\b
# Should be `VM`
\bVm\b
# Should be `walkthrough(s)`
\bwalk-throughs?\b
# Should be `want`
\bdon't ant\b
# Should be `we'll`
\bwe 'll\b
# Should be `week`
# unless you're really talking about people or pointers
\bevery weak[.,?!]
# Should be `well`
\b[Yy]ou(?:'re| are) doing good\b
# Should be `whereas`
\bwhere as\b
# Should be `WinGet`
\bWinget\b
# Should be `without` (unless `out` is a modifier of the next word)
\bwith out\b(?!-)
# Should be `work around`
\b[Ww]orkaround(?= an?\b)
# Should be `workarounds`
#\bwork[- ]arounds\b
@@ -267,15 +548,15 @@
\b(?:coarse|fine) grained\b
# Homoglyph (Cyrillic) should be `A`/`B`/`C`/`E`/`H`/`I`/`I`/`J`/`K`/`M`/`O`/`P`/`S`/`T`/`Y`
# It's possible that your content is intentionally mixing Cyrllic and Latin scripts, but if it isn't, you definitely want to correct this.
# It's possible that your content is intentionally mixing Cyrillic and Latin scripts, but if it isn't, you definitely want to correct this.
(?<=[A-Z]{2})[АВСЕНІӀЈКМОРЅТУ]|[АВСЕНІӀЈКМОРЅТУ](?=[A-Z]+(?:\b|[a-z]+)|[a-z]+(?:[^a-z]|$))
# Homoglyph (Cyrillic) should be `a`/`b`/`e`
# It's possible that your content is intentionally mixing Cyrllic and Latin scripts, but if it isn't, you definitely want to correct this.
[аве](?=[A-Za-z]{2,})|(?<=[A-Za-z]{2})[аве]|(?<=[A-Za-z])[аве](?=[A-Za-z])
# Homoglyph (Cyrillic) should be `a`/`b`/`c`/`e`/`o`/`p`/`x`/`y`
# It's possible that your content is intentionally mixing Cyrillic and Latin scripts, but if it isn't, you definitely want to correct this.
[авсеорху](?=[A-Za-z]{2,})|(?<=[A-Za-z]{2})[авсеорху]|(?<=[A-Za-z])[авсеорху](?=[A-Za-z])
# Should be `neither/nor` -- or reword
#(?!<do )\bnot\b([^.?!"/(](?!neither|,.*?,))+\bnor\b
#(?<!do )\bnot\b([^.?!"/(](?!neither|,.*?,))+\bnor\b
# Should be `neither/nor` (plus rewording the beginning)
# This is probably a double negative...

View File

@@ -1,31 +1,36 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
# #includes
^\s*#include\s*(?:<.*?>|".*?")
# Gaelic
Gàidhlig
# #pragma lib
^\s*#pragma comment\(lib, ".*?"\)
Ov_erwrite
# languageHashTable
"\w+(?:-\w+|)"\s+=\s+@\(".*"\)
# wikipedia
\b\w\w\.wikipedia\.org/wiki/[-\w%.#]+
# Regular expression with `\b`
\\b(?=[a-z]\S*\{)
# css fonts
\bfont-family:[^;}]+
# .github/policies/resourceManagement.yml
pattern: '.*'
# long lorem
L"Lorem.*"
# tabs in c#
\$"\\t
# Hexadecimal character pattern in code
\\x[0-9a-fA-F][0-9a-fA-F]
\\x[0-9a-fA-F]{4}
fontFamily": ".*"
D[23]D(?=[A-Z][a-z])
(?<=[a-z])3D(?=[A-Z])
\.monitorId = \{ .*\}
json::value\(L"\S+"
# windows line breaks in strings
\\r\\n
\\r\\n(?=[A-Za-z])
# power shell gallery website
\bpowershellgallery.com/[-_a-zA-Z0-9()=./%]*
@@ -35,9 +40,22 @@ L?(["']|[-<({>]|\b)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{10,12}(?:\g{
(?:L"[abAB]+", ){3}L"[abAB]+"
# hit-count: 1 file-count: 1
# marker to ignore all code on line
^.*/\* #no-spell-check-line \*/.*$
\. (?: @[-A-Za-z\d]+\b(?!\.[A-Z]),?)+
auto deviceId = L".*"
deviceId(?:\.id|) = L".*"
StringComparer.OrdinalIgnoreCase\) \{.*\}
# namespaces
\b[a-z]+::
"Author": ".+"
(?:Include|Link)=".*?"
# You could ignore `xmlns`, but it's probably better to enforce rules about them...
#\s(?:xmlns:[a-z]+(?:[A-Z][a-z]+|)=|[a-z]+(?:[A-Z][a-z]+):(?=[a-z]+=))
# UnitTests
\[DataRow\(.*\)\]
@@ -50,142 +68,135 @@ L?(["']|[-<({>]|\b)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{10,12}(?:\g{
# Automatically suggested patterns
# hit-count: 3715 file-count: 992
# hit-count: 5402 file-count: 1339
# IServiceProvider / isAThing
(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
(?:(?:\b|_|(?<=[a-z]))[IT]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
# hit-count: 404 file-count: 42
# base64 encoded content, possibly wrapped in mime
(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
# hit-count: 2073 file-count: 842
# #includes
^\s*#include\s*(?:<.*?>|".*?")
# hit-count: 402 file-count: 160
# hit-count: 1639 file-count: 855
# C# includes
^\s*using [^;]+;
# hit-count: 1491 file-count: 693
# microsoft
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]*
# hit-count: 398 file-count: 133
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u\{?|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
# hit-count: 339 file-count: 146
# hex runs
\b[0-9a-fA-F]{16,}\b
# hit-count: 337 file-count: 110
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
# hit-count: 311 file-count: 43
# D2D
D?2D(?!efault)
# hit-count: 272 file-count: 75
# hit-count: 253 file-count: 100
# GitHub SHAs (markdown)
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
# hit-count: 146 file-count: 27
# hit-count: 241 file-count: 37
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# hit-count: 105 file-count: 103
# hit-count: 141 file-count: 6
# Contributor / Project
\[[^\]\s]+\]\(https://github\.com/[^)]+\)(?: -(?: [A-Z]\S+)+|)|\[[^\]]+\]\(https://github\.com/(?:[^/\s"]+/?){1,2}\)
https://github.com/(?:[-\w]+/?){1,2}
# hit-count: 131 file-count: 125
# Repeated letters
\b([a-z])\g{-1}{2,}\b
# hit-count: 99 file-count: 97
# w3
\bw3\.org/[-0-9a-zA-Z/#.]+
# hit-count: 94 file-count: 6
# Contributor
\[[^\]]+\]\(https://github\.com/[^/\s"]+/?\)
RegExp\(([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|return/.*?/
# hit-count: 65 file-count: 38
# regex choice
\(\?:[^)]+\|[^)]+\)
# hit-count: 37 file-count: 14
# hit-count: 59 file-count: 11
# Markdown anchor links
\(#\S*?[a-zA-Z]\S*?\)
# hit-count: 33 file-count: 5
# base64 encoded pkcs
\bMII[-a-zA-Z=;:/0-9+]+
# hit-count: 28 file-count: 22
# hit-count: 29 file-count: 23
# stackexchange -- https://stackexchange.com/feeds/sites
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
# hit-count: 14 file-count: 3
# node packages
(["'])@[^/'" ]+/[^/'" ]+\g{-1}
# hit-count: 24 file-count: 11
# Library prefix
# e.g., `lib`+`archive`, `lib`+`raw`, `lib`+`unwind`
# (ignores some words that happen to start with `lib`)
(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
# hit-count: 13 file-count: 1
# hit-count: 20 file-count: 2
# Intel intrinsics
_mm_(?!dd)\w+
_mm\d*_(?!dd)\w+
# hit-count: 11 file-count: 5
# URL escaped characters
%[0-9A-F][A-F](?=[A-Za-z])
# hit-count: 9 file-count: 5
# hit-count: 15 file-count: 8
# Wikipedia
\ben\.wikipedia\.org/wiki/[-\w%.#]+
# hit-count: 5 file-count: 4
# hit-count: 14 file-count: 10
# vs devops
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
# hit-count: 5 file-count: 4
# Alternatively, if you're using check-spelling v0.0.25+, and you would like to _check_ the Non-English content for spelling errors, you can. For information on how to do so, see:
# https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#unicode
[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,}
# hit-count: 8 file-count: 2
# copyright
Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
# hit-count: 4 file-count: 4
# microsoft
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|developer|docs|learn|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%#]*
# hit-count: 8 file-count: 1
# css fonts
\bfont(?:-family|):[^;}]+
aka\.ms/[a-zA-Z0-9]+
# hit-count: 8 file-count: 1
# kubernetes crd patterns
^\s*pattern: .*$
# hit-count: 5 file-count: 3
# URL escaped characters
%[0-9A-F][A-F](?=[A-Za-z])
# hit-count: 3 file-count: 3
# githubusercontent
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
# hit-count: 3 file-count: 2
# css url wrappings
\burl\([^)]+\)
# hit-count: 3 file-count: 1
# kubernetes crd patterns
^\s*pattern: .*$
# hit-count: 3 file-count: 1
# Lorem
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
# ... Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
# ... You could manually change `(?i)X...` to use `[Xx]...`
# ... or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
(?:(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*)
# hit-count: 3 file-count: 1
# libraries
(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
# hit-count: 2 file-count: 2
# medium
\bmedium\.com/@?[^/\s"]+/[-\w:/*.]+
# hit-count: 2 file-count: 1
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
# YouTube url
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
# hit-count: 1 file-count: 1
# GHSA
GHSA(?:-[0-9a-z]{4}){3}
# hit-count: 1 file-count: 1
# hit-count: 2 file-count: 1
# GitHub actions
\buses:\s+[-\w.]+/[-\w./]+@[-\w.]+
# hit-count: 1 file-count: 1
# medium
\bmedium\.com/@?[^/\s"]+/[-\w]+
# hit-count: 1 file-count: 1
# sha-... -- uses a fancy capture
(\\?['"]|&quot;)[0-9a-f]{40,}\g{-1}
# curl arguments
\b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
# hit-count: 1 file-count: 1
# tar arguments
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
# #pragma lib
^\s*#pragma comment\(lib, ".*?"\)
# UnitTests
\[DataRow\(.*\)\]
# AdditionalDependencies
<AdditionalDependencies>.*<
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
RegExp\(@?([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|return/.*?/
# Questionably acceptable forms of `in to`
# Personally, I prefer `log into`, but people object
# https://www.tprteaching.com/log-into-log-in-to-login/
@@ -194,13 +205,16 @@ GHSA(?:-[0-9a-z]{4}){3}
# to opt in
\bto opt in\b
# pass(ed|ing) in
\bpass(?:ed|ing) in\b
# acceptable duplicates
# ls directory listings
[-bcdlpsw](?:[-r][-w][-SsTtx]){3}[\.+*]?\s+\d+\s+\S+\s+\S+\s+[.\d]+(?:[KMGT]|)\s+
# mount
\bmount\s+-t\s+(\w+)\s+\g{-1}\b
# C types and repeated CSS values
\s(auto|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?: \g{-1})+\s
\s(auto|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?:\s\g{-1})+\s
# C enum and struct
\b(?:enum|struct)\s+(\w+)\s+\g{-1}\b
# go templates
@@ -232,9 +246,11 @@ _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
# hit-count: 1 file-count: 1
# Amazon
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)[^"'\s]+
# hit-count: 3 file-count: 3
# imgur
\bimgur\.com/[^.]+

View File

@@ -1,8 +1,17 @@
^attache$
^bellow$
^bellows?$
benefitting
occurences?
^dependan.*
^develope$
^developement$
^developpe
^Devers?$
^devex
^devide
^Devinn?[ae]
^devisal
^devisor
^diables?$
^oer$
Sorce
@@ -10,4 +19,5 @@ Sorce
^Teh$
^untill$
^untilling$
^venders?$
^wether.*

View File

@@ -105,7 +105,7 @@ jobs:
report-timing: 1
warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check,unclosed-block-ignore-begin,unclosed-block-ignore-end
experimental_apply_changes_via_bot: 1
use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }}
use_sarif: 1
check_extra_dictionaries: ""
dictionary_source_prefixes: >
{
@@ -113,37 +113,28 @@ jobs:
}
extra_dictionaries: |
cspell:software-terms/softwareTerms.txt
cspell:cpp/stdlib-c.txt
cspell:cpp/stdlib-cpp.txt
cspell:filetypes/filetypes.txt
cspell:cpp/stdlib-c.txt
cspell:lorem-ipsum/dictionary.txt
cspell:python/python/python-lib.txt
cspell:php/php.txt
cspell:fullstack/fullstack.txt
cspell:dotnet/dotnet.txt
cspell:swift/swift.txt
cspell:node/node.txt
cspell:dart/dart.txt
cspell:django/django.txt
cspell:python/python/python.txt
cspell:dotnet/dotnet.txt
cspell:powershell/powershell.txt
cspell:npm/npm.txt
cspell:golang/go.txt
cspell:cpp/compiler-msvc.txt
cspell:csharp/csharp.txt
cspell:html/html.txt
cspell:python/python/python-lib.txt
cspell:node/node.txt
cspell:golang/go.txt
cspell:npm/npm.txt
cspell:fullstack/fullstack.txt
cspell:css/css.txt
cspell:java/java.txt
cspell:aws/aws.txt
cspell:typescript/typescript.txt
cspell:cpp/lang-keywords.txt
cspell:html/html.txt
cspell:r/r.txt
cspell:aws/aws.txt
cspell:cpp/compiler-msvc.txt
cspell:python/common/extra.txt
cspell:scala/scala.txt
cspell:shell/shell-all-words.txt
cspell:css/css.txt
cspell:r/r.txt
cspell:java/java-terms.txt
cspell:cpp/stdlib-cerrno.txt
cspell:k8s/k8s.txt
comment-push:
name: Report (Push)

View File

@@ -11,14 +11,13 @@
"PowerToys.ActionRunner.exe",
"PowerToys.Update.exe",
"PowerToys.BackgroundActivatorDLL.dll",
"Notifications.dll",
"os-detection.dll",
"PowerToys.exe",
"PowerToys.FilePreviewCommon.dll",
"PowerToys.Interop.dll",
"Tools\\PowerToys.BugReportTool.exe",
"StylesReportTool\\PowerToys.StylesReportTool.exe",
"Telemetry.dll",
"CalculatorEngineCommon.dll",
"PowerToys.ManagedTelemetry.dll",
"PowerToys.ManagedCommon.dll",
@@ -33,7 +32,7 @@
"PowerToys.AlwaysOnTopModuleInterface.dll",
"PowerToys.CmdNotFoundModuleInterface.dll",
"PowerToys.CmdNotFound.dll",
"PowerToys.ColorPicker.dll",
"PowerToys.ColorPickerUI.dll",
@@ -54,7 +53,7 @@
"PowerToys.Awake.exe",
"PowerToys.Awake.dll",
"fancyzones.dll",
"PowerToys.FancyZonesEditor.exe",
"PowerToys.FancyZonesEditor.dll",
"PowerToys.FancyZonesEditorCommon.dll",
@@ -136,7 +135,7 @@
"PowerToys.PowerLauncher.dll",
"PowerToys.PowerLauncher.exe",
"PowerToys.PowerLauncher.Telemetry.dll",
"Wox.dll",
"Wox.Infrastructure.dll",
"Wox.Plugin.dll",
"RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll",
@@ -275,16 +274,16 @@
"Mono.Cecil.Pdb.dll",
"Mono.Cecil.Rocks.dll",
"Newtonsoft.Json.dll",
"Newtonsoft.Json.Bson.dll",
"NLog.dll",
"HtmlAgilityPack.dll",
"Markdig.Signed.dll",
"HelixToolkit.dll",
"HelixToolkit.Core.Wpf.dll",
"Mages.Core.dll",
"JetBrains.Annotations.dll",
"NLog.Extensions.Logging.dll",
"getfilesiginforedist.dll",
"concrt140_app.dll",
"msvcp140_1_app.dll",
"msvcp140_2_app.dll",
@@ -294,22 +293,8 @@
"vcomp140_app.dll",
"vcruntime140_1_app.dll",
"vcruntime140_app.dll",
"WinUI3Apps\\CommunityToolkit.Labs.WinUI.SettingsControls.dll",
"UnicodeInformation.dll",
"Vanara.Core.dll",
"Vanara.PInvoke.ComCtl32.dll",
"Vanara.PInvoke.Cryptography.dll",
"Vanara.PInvoke.Gdi32.dll",
"Vanara.PInvoke.Kernel32.dll",
"Vanara.PInvoke.Ole.dll",
"Vanara.PInvoke.Rpc.dll",
"Vanara.PInvoke.Security.dll",
"Vanara.PInvoke.Shared.dll",
"Vanara.PInvoke.Shell32.dll",
"Vanara.PInvoke.ShlwApi.dll",
"Vanara.PInvoke.User32.dll",
"WinUI3Apps\\clrcompression.dll",
"WinUI3Apps\\Microsoft.Graphics.Canvas.Interop.dll",
"Microsoft.Web.WebView2.Core.dll",
"Microsoft.Web.WebView2.WinForms.dll",
"Microsoft.Web.WebView2.Wpf.dll",
@@ -336,7 +321,7 @@
"Testably.Abstractions.FileSystem.Interface.dll",
"WinUI3Apps\\Testably.Abstractions.FileSystem.Interface.dll",
"ColorCode.Core.dll",
"ColorCode.UWP.dll",
"UnitsNet.dll",
"UtfUnknown.dll",
"Wpf.Ui.dll"

View File

@@ -472,7 +472,7 @@ jobs:
# This saves ~1GiB per architecture. We won't need these later.
# Removes:
# - All .pdbs from any static libs .libs (which were only used during linking)
# - All .pdb files from any static libs .libs (which were only used during linking)
- pwsh: |-
$binDir = '$(Build.SourcesDirectory)'
$ImportLibs = Get-ChildItem $binDir -Recurse -File -Filter '*.exp' | ForEach-Object { $_.FullName -Replace "exp$","lib" }

View File

@@ -183,9 +183,9 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
- [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev Lead
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev Lead
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
@@ -225,4 +225,4 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev Lead

View File

@@ -47,7 +47,7 @@
<!-- Add ability to run tests via "msbuild /t:Test" -->
<!--
Workaround an MSBuild bug where Microsoft.Common.Test.targets is missing from the Arm64 installation.
Work around an MSBuild bug where Microsoft.Common.Test.targets is missing from the Arm64 installation.
See: https://github.com/dotnet/msbuild/pull/9984
NB 1: This means that using "/t:Test" is not supported for Arm64 builds and tests will need to be run in an alternate way,
eg running tests in VS or invoking vstest.console directly.

View File

@@ -32,22 +32,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.6" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.7" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.6" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.7" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.6" />
<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.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.6" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.7" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.6" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.7" />
<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. -->
<!--
@@ -75,28 +75,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.6" />
<PackageVersion Include="System.CodeDom" Version="9.0.7" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.6" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.6" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.6" />
<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" />
<!-- 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.6" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.7" />
<!-- 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.6" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.6" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.7" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.7" />
<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.6" />
<PackageVersion Include="System.Management" Version="9.0.7" />
<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.6" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.6" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.6" />
<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.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />

View File

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

View File

@@ -605,9 +605,6 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLib", "src\modules\Workspaces\WorkspacesLib\WorkspacesLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLibUnitTests", "src\modules\Workspaces\WorkspacesLib.UnitTests\WorkspacesLibUnitTests.vcxproj", "{A85D4D9F-9A39-4B5D-8B5A-9F2D5C9A8B4C}"
ProjectSection(ProjectDependencies) = postProject
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkspacesLauncherUI", "src\modules\Workspaces\WorkspacesLauncherUI\WorkspacesLauncherUI.csproj", "{9C53CC25-0623-4569-95BC-B05410675EE3}"
EndProject
@@ -725,6 +722,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorEngineCommon", "s
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCsWin32", "src\common\ManagedCsWin32\ManagedCsWin32.csproj", "{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerRenameUITest", "src\modules\powerrename\PowerRenameUITest\PowerRenameUITest.csproj", "{9D3F3793-EFE3-4525-8782-238015DABA62}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2605,22 +2604,6 @@ Global
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64
{64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64
@@ -2643,6 +2626,22 @@ Global
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.Build.0 = Debug|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.ActiveCfg = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|ARM64.Build.0 = Release|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.ActiveCfg = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Release|x64.Build.0 = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|ARM64.Build.0 = Debug|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.ActiveCfg = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Debug|x64.Build.0 = Debug|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.ActiveCfg = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.Build.0 = Debug|ARM64
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.ActiveCfg = Debug|x64
@@ -2659,6 +2658,14 @@ Global
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|ARM64.Build.0 = Release|ARM64
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|x64.ActiveCfg = Release|x64
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A}.Release|x64.Build.0 = Release|x64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|ARM64.ActiveCfg = Debug|ARM64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|ARM64.Build.0 = Debug|ARM64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|x64.ActiveCfg = Debug|x64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Debug|x64.Build.0 = Debug|x64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|ARM64.ActiveCfg = Release|ARM64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|ARM64.Build.0 = Release|ARM64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|x64.ActiveCfg = Release|x64
{9D3F3793-EFE3-4525-8782-238015DABA62}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2798,7 +2805,7 @@ Global
{25C91A4E-BA4E-467A-85CD-8B62545BF674} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
{6AB6A2D6-F859-4A82-9184-0BD29C9F07D1} = {A50C70A6-2DA0-4027-B90E-B1A40755A8A5}
{212AD910-8488-4036-BE20-326931B75FB2} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{7AC943C9-52E8-44CF-9083-744D8049667B} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{7AC943C9-52E8-44CF-9083-744D8049667B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{54A93AF7-60C7-4F6C-99D2-FBB1F75F853A} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{92C39820-9F84-4529-BC7D-22AAE514D63B} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{515554D1-D004-4F7F-A107-2211FC0F6B2C} = {7AC943C9-52E8-44CF-9083-744D8049667B}
@@ -2930,13 +2937,14 @@ Global
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
{64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0217E86E-3476-9946-DE8E-9D200CEBD47A} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79}
{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{9D3F3793-EFE3-4525-8782-238015DABA62} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -9,4 +9,4 @@ The following must be kept in mind regarding compatibility with settings v1 and
- The status of each of the modules is communicated with the runner in the form of a json object. The names of all the powerToys is set in the [`EnableModules.cs`](src/settings-ui/Settings.UI.Library/EnabledModules.cs) file. The `JsonPropertyName` must not be changed to ensure that the information is dispatched properly to all the modules by the runner.
### ImageResizer anomaly
All the powertoys have the same folder name as well as JsonPropertyName to communicate information with the runner. However that is not the case with ImageResizer. The folder name is `ImageResizer` whereas the JsonPropertyName is `Image Resizer`(Note the additional space). This should not be changed to ensure backward compatibility as well as proper functioning of the module.
All the powertoys have the same folder name as well as JsonPropertyName to communicate information with the runner. However that is not the case with ImageResizer. The folder name is `ImageResizer` whereas the JsonPropertyName has an additional space: `Image Resizer`. This should not be changed to ensure backward compatibility as well as proper functioning of the module.

View File

@@ -1,6 +1,6 @@
# What is it
We would like to enable our users to use [`winget configure`](https://learn.microsoft.com/en-us/windows/package-manager/winget/configure) command to install PowerToys and configure its settings with a [Winget configuration file](https://learn.microsoft.com/en-us/windows/package-manager/configuration/create). For example:
We would like to enable our users to use [`winget configure`](https://learn.microsoft.com/en-us/windows/package-manager/winget/configure) command to install PowerToys and configure its settings with a [WinGet configuration file](https://learn.microsoft.com/en-us/windows/package-manager/configuration/create). For example:
```yaml
properties:
@@ -35,7 +35,7 @@ This should install PowerToys and make `PowerToysConfigure` resource available.
PowerToys.Settings.exe set <ModuleName>.<SettingName> <SettingValue>
```
So for the example the config above should perform 3 following invocations:
So for example the config above should perform 3 following invocations:
```
PowerToys.Settings.exe set ShortcutGuide.Enabled false
PowerToys.Settings.exe set FancyZones.Enabled true

View File

@@ -1,25 +1,25 @@
# Viewmodels
The viewmodels are located within the [`Settings.UI.Library`](/src/settings-ui/Settings.UI.Library) project.
# View Models
The view models are located within the [`Settings.UI.Library`](/src/settings-ui/Settings.UI.Library) project.
## Components
- Each viewmodel takes in the general `settingsRepository`, the `moduleSettingsRepository` if it exists and the delegates for IPC communication.
- Each view model takes in the general `settingsRepository`, the `moduleSettingsRepository` if it exists and the delegates for IPC communication.
- The general `settingsRepository` contains the general configurations of all powertoys whereas the `moduleSettingsRepository` is specific to the module. This is to ensure that the configuration details are shared amongst the viewmodels without having to re-open the `settings.json` file.
- Whenever there is a change in the UI, the `OnPropertyChanged` event is invoked and the viewmodel sends a corresponding IPC message to the runner which would perform the designated action such as dispatching the change to the modules or enabling/disabling the powertoy etc.
- Whenever there is a change in the UI, the `OnPropertyChanged` event is invoked and the view model sends a corresponding IPC message to the runner which would perform the designated action such as dispatching the change to the modules or enabling/disabling the powertoy, etc.
#### Difference between viewmodels
#### Difference between view models
- The [`GeneralViewModel`](/src/settings-ui/Settings.UI.Library/ViewModels/GeneralViewModel.cs) is different from the rest of the view models with regard to the IPC communication wherein it sends special IPC messages to the runner to check for updates and to restart as admin.
- Each of the powerToy viewmodels have two types of IPC communications, one for the general status of the powerToy and the other for communication powerToy specific change in properties to the runner.
- Each of the powerToy view models have two types of IPC communications, one for the general status of the powerToy and the other for communication powerToy specific change in properties to the runner.
## [`SettingsRepository`](src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs)
- The [`SettingsRepository`](src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs) is a generic singleton which contains the configurations for each viewmodel.
- As it is a generic singleton, there can only be one instance of the settings repository of a particular type. This ensures that all the viewmodels are modifying a common object and a change made in one locations reflects everywhere.
- The [`SettingsRepository`](src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs) is a generic singleton which contains the configurations for each view model.
- As it is a generic singleton, there can only be one instance of the settings repository of a particular type. This ensures that all the view models are modifying a common object and a change made in one locations reflects everywhere.
- The singleton implementation is thread-safe. Unit tests have been added for the same.
### Settings viewmodel anomalies
- The reason behind using the `SettingsRepository` is to ensure that the settings process does not try to access the `settings.json` files directly but rather does it through this class which encapsulates all the file operations from the viewmodels.
- However, this could not be expanded to all the viewmodels directly for the following reasons. Some refactoring must be done to unify these cases and to bring them under the same model:
- The PowerRename viewmodel does not save the settings configurations in the same format as the rest of the powertoys, ie. {name, version, properties}. However, it only stores the properties directly.
- Some viewmodels expect the runner to create the file instead of creating the file themselves, like in keyboard manager.
### Settings view model anomalies
- The reason behind using the `SettingsRepository` is to ensure that the settings process does not try to access the `settings.json` files directly but rather does it through this class which encapsulates all the file operations from the view models.
- However, this could not be expanded to all the view models directly for the following reasons. Some refactoring must be done to unify these cases and to bring them under the same model:
- The PowerRename view model does not save the settings configurations in the same format as the rest of the powertoys, i.e. {name, version, properties}. However, it only stores the properties directly.
- Some view models expect the runner to create the file instead of creating the file themselves, like in keyboard manager.
- The colorpicker powertoy creates the `settings.json` within the module. This must be taken care of when encapsulated within the settingsRepository.
- Currently, all modules use the `SettingsRepository` to access the General Settings config.
- However, only FancyZones, ShortcutGuide and PowerPreview use the `SettingsRepository` to access the module properties.

View File

@@ -14,7 +14,7 @@
## Localization on the pipeline (CDPX)
[The localization step](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L45-L52) is run on the pipeline before the solution is built. This step runs the [build-localization](https://github.com/microsoft/PowerToys/blob/main/.pipelines/build-localization.cmd) script, which generates resx files for all the projects with localization enabled using the `Localization.XLoc` package.
The [`Localization.XLoc`](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/build-localization.cmd#L24-L25) tool is run on the repo root, and it checks for all occurrences of `LocProject.json`. Each localized project has a `LocProject.json` file in the project root, which contains the location of the English resx file, list of languages for localization, and the output path where the localized resx files are to be copied to. In addition to this, some other parameters can be set, such as whether the language ID should be added as a folder in the file path or in the file name. When the CDPX pipeline is run, the localization team is notified of changes in the English resx files. For each project with localization enabled, a `loc` folder (see [this](https://github.com/microsoft/PowerToys/tree/main/src/modules/launcher/Microsoft.Launcher/loc) for example) is created in the same directory as the `LocProject.json` file. The folder contains language specific folders which in turn have a nested folder path equivalent to `OutputPath` in the `LocProject.json`. Each of these folders contain one `lcl` file. The `lcl` files contain the English resources along with their translation for that language. These are described in more detail in the [Lcl files section](#lcl-files). Once the `.resx` files are generated, they will be used during the `Build PowerToys` step for localized versions of the modules.
The [`Localization.XLoc`](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/build-localization.cmd#L24-L25) tool is run on the repo root, and it checks for all occurrences of `LocProject.json`. Each localized project has a `LocProject.json` file in the project root, which contains the location of the English resx file, list of languages for localization, and the output path where the localized resx files are to be copied to. In addition to this, some other parameters can be set, such as whether the language ID should be added as a folder in the file path or in the file name. When the CDPX pipeline is run, the localization team is notified of changes in the English resx files. For each project with localization enabled, a `loc` folder (see [the loc folder in the Microsoft.Launcher module](https://github.com/microsoft/PowerToys/tree/main/src/modules/launcher/Microsoft.Launcher/loc) for example) is created in the same directory as the `LocProject.json` file. The folder contains language specific folders which in turn have a nested folder path equivalent to `OutputPath` in the `LocProject.json`. Each of these folders contain one `lcl` file. The `lcl` files contain the English resources along with their translation for that language. These are described in more detail in the [Lcl files section](#lcl-files). Once the `.resx` files are generated, they will be used during the `Build PowerToys` step for localized versions of the modules.
Since the localization script requires certain nuget packages, the [`restore-localization`](https://github.com/microsoft/PowerToys/blob/main/.pipelines/restore-localization.cmd) script is run before running `build-localization` to install all the required packages. This script must [run in the `restore` step](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L37-L39) of pipeline because [the host is network isolated](https://onebranch.visualstudio.com/Pipeline/_wiki/wikis/Pipeline.wiki/2066/Consuming-Packages-in-a-CDPx-Pipeline?anchor=overview) at the `build` step. The [Toolset package source](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L23) is used for this.
@@ -159,7 +159,7 @@ This can be done by adding the directory name of the project to [Product.wxs nea
We should also ensure the new dlls are signed by the pipeline. Currently all dlls of the form [`*.resources.dll` are signed](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/.pipelines/pipeline.user.windows.yml#L68).
**Note:** The resource dlls should be added to the MSI project only after the initial commit with the lcl files has been done by the Localization team. Otherwise the pipeline will fail as there wouldn't be any resx files to generate the dlls.
**Note:** The resource dlls should be added to the MSI project only after the initial commit with the lcl files has been done by the Localization team. Otherwise, the pipeline will fail as there wouldn't be any resx files to generate the dlls.
## Working With Strings

View File

@@ -56,7 +56,7 @@ string validUIDisplayString = Resources.ValidUIDisplayString;
```
## More On Coding Guidance
Please review these brief docs below relating to our coding standards etc.
Please review these brief docs below relating to our coding standards, etc.
* [Coding Style](./style.md)
* [Code Organization](./readme.md)

View File

@@ -162,4 +162,4 @@ This can be done by adding the directory name of the project to [Product.wxs nea
We should also ensure the new dlls are signed by the pipeline. Currently all dlls of the form [`*.resources.dll` are signed](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/.pipelines/pipeline.user.windows.yml#L68).
**Note:** The resource dlls should be added to the MSI project only after the initial commit with the lcl files has been done by the Localization team. Otherwise the pipeline will fail as there wouldn't be any resx files to generate the dlls.
**Note:** The resource dlls should be added to the MSI project only after the initial commit with the lcl files has been done by the Localization team. Otherwise, the pipeline will fail as there wouldn't be any resx files to generate the dlls.

View File

@@ -55,7 +55,7 @@ The module is initialized in the AlwaysOnTop class. During initialization, the f
The AlwaysOnTop class handles the pinning and unpinning of windows. Key methods include:
- **PinTopmostWindow**: Pins the specified window on top of others and applies visual indicators
- **UnpinTopmostWindowss**: Removes the pinning status and visual indicators from the specified window
- **UnpinTopmostWindows**: Removes the pinning status and visual indicators from the specified window
- **AssignBorder**: Applies a colored border around the pinned window based on user settings
### Settings Management

View File

@@ -68,4 +68,4 @@ EnvironmentVariableUILib # Abstracted UI methods and implementations
- The module reads and writes variables directly to the registry instead of using the Environment API
- This direct registry access approach is used because the Environment API automatically expands variables and has a timeout for notifications
- When a profile variable has the same name as an existing User variable, a backup is created with a naming pattern: `VARIABLE_NAME_powertoys_PROFILE_NAME`
- When a profile variable has the same name as an existing User variable, a backup is created with the naming pattern: `VARIABLE_NAME_powertoys_PROFILE_NAME`

View File

@@ -444,10 +444,12 @@ PowerToys/doc/releases/tests-checklist-template.md at releaseChecklist · micros
- ### First Run FancyZones error
![Debug Step Image](../images/fancyzones/16.png)
If you encounter this situation, you need to launch the FancyZones Editor once in the powertoys settings UI (Refer to the image below). The reason is that running the Editor directly within the project will not initialize various configuration files.
If you encounter this situation, you need to launch the FancyZones Editor once in the powertoys settings UI:
![Debug Step Image](../images/fancyzones/17.png)
The reason is that running the Editor directly within the project will not initialize various configuration files.
- ### How are layouts stored and loaded? Is there a central configuration handler?
There is no central configuration handler.
@@ -471,7 +473,7 @@ When the Editor starts, it will load the config data, and when FancyZones starts
About monitor detection you can find "FancyZones::MoveSizeUpdate" function.
I believe that in the case without DPI scaling, FancyZones retrieves the window's position and does not need to know what the mouse's DPI scaling is like. If you are referring to window scaling, it is called through the system interface, and you can see the detailed code in "WindowMouseSnap::MoveSizeEnd()" fucntion.
I believe that in the case without DPI scaling, FancyZones retrieves the window's position and does not need to know what the mouse's DPI scaling is like. If you are referring to window scaling, it is called through the system interface, and you can see the detailed code in "WindowMouseSnap::MoveSizeEnd()" function.
- ### How does FancyZones track which windows belong to which zones?

View File

@@ -50,7 +50,7 @@ For more details on the implementation approach, see the [Dual Registration sect
Image Resizer dynamically determines when to show the context menu option:
- `AppxManifest.xml` registers the extension for all file types (`Type="*"`)
- The shell extension checks if the selected files are images using `AssocGetPerceivedType()`
- The menu appears only for image files (returns `ECS_ENABLED`), otherwise it remains hidden (returns `ECS_HIDDEN`)
- The menu appears only for image files (returns `ECS_ENABLED`); otherwise, it remains hidden (returns `ECS_HIDDEN`)
This approach provides flexibility to support additional file types by modifying only the detection logic without changing the system-level registration.

View File

@@ -86,7 +86,7 @@ Returns true if successful.
virtual void set_config(const wchar_t* config)
```
After the user has changed the module settings in the Settings editor, the runner calls this method to pass to the module the updated values. It's a good place to save the settings as well.
After the user has changed the module settings in the Settings editor, the runner calls this method to pass the updated values to the module. It's a good place to save the settings as well.
## call_custom_action

View File

@@ -58,7 +58,7 @@ This file contains documentation for all the methods involved in key/shortcut re
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L754-L809) is used for handling app-specific shortcut to shortcut and shortcut to key remaps. The general logic is as follows:
- Check if the `dwExtraInfo` field is set to `KEYBOARDMANAGER_SHORTCUT_FLAG`. This indicates that the key event was generated by the KBM shortcut remap method using `SendInput`. This ensures that we don't read events generated by the shortcut remap method, but we still read events which are generated by the key remap method.
- Get the name of the process in the foreground. This is done using `GetCurrentApplication` which uses `GetForegroundWindow` to get the window handle and `get_process_path` from the common lib. This approach can fail for UWP apps in full screen, so for that scenario we use the `GetGUIThreadInfo` approach to find the correct window handle, and hence the correct process name. This method is [described in more detail](keyboardmanagercommon.md#Foreground-app-detection)
- By checking `KeyboardManagerState.GetActivatedApp` we check if an app-specific shortcut is currently invoked. If so, we consider this application to be the activated app. This is required because some shortcut remaps could cause the current app to lose focus and hence until the shortcut is completely released we should allow that remap to continue, otherwise the user could end up in a state where some keys do not get released. For example: remap <kbd>Ctrl+A</kbd> to <kbd>Alt+Tab</kbd> for Edge, when a user presses <kbd>Ctrl+A</kbd> the window loses focus as <kbd>Alt+Tab</kbd> gets executed.
- By checking `KeyboardManagerState.GetActivatedApp` we check if an app-specific shortcut is currently invoked. If so, we consider this application to be the activated app. This is required because some shortcut remaps could cause the current app to lose focus and hence until the shortcut is completely released we should allow that remap to continue; otherwise, the user could end up in a state where some keys do not get released. For example: remap <kbd>Ctrl+A</kbd> to <kbd>Alt+Tab</kbd> for Edge, when a user presses <kbd>Ctrl+A</kbd> the window loses focus as <kbd>Alt+Tab</kbd> gets executed.
- If there is no app-specific shortcut currently invoked, we check if the foreground process is present in the list of app-specific remaps, either with or without the file extension and case-insensitive. If it is, this is considered to be the activated app.
- Call `HandleShortcutRemapEvent` with the `activatedApp` argument so that app-specific shortcut remapping takes place if it applies for the current key event.
@@ -73,8 +73,8 @@ The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules
[To mock the `SendInput` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L10-L110), the steps for processing the input are as follows. This implementation is based on public documentation for SendInput and the behavior of key messages and keyboard hooks:
- Iterate over all the inputs in the `INPUT` vector argument.
- If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down, otherwise it is `WM_KEYUP`.
- If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10, otherwise it is `WM_KEYDOWN`.
- If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down; otherwise, it is `WM_KEYUP`.
- If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10; otherwise, it is `WM_KEYDOWN`.
- An optional function which can be set on the `MockedInput` handler can be used to test for the number of times a key event is received by the system with a particular condition using [`sendVirtualInputCallCondition`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L48-L52).
- The hook logic for a low level hook which returns 0 or 1 can be set on the `MockedInput` handler such that it behaves like a low level hook would behave with actual keyboard input. If the method returns 1, then the keyboard state is not updated, and if it returns 0 the corresponding key event is used to update the key state. This works in the recursive way as well similar to low level hooks, as `SendVirtualInput` can be called from within the hook, thus simulating identical behavior to calling `SendInput` in a low level hook (as soon as SendInput is called, the low level hook is called for the new input event, and only after those are processed it returns back to the current event, check this [blog](https://devblogs.microsoft.com/oldnewthing/20140213-00/?p=1773) for more details).
- For updating the keyboard state, KEYUP messages result in the state for that key code being set to false, and KEYDOWN result in the state for that key code being set to true.

View File

@@ -127,12 +127,12 @@ The [`HandleKeyboardHookEvent`](https://github.com/microsoft/PowerToys/blob/b805
**Note:** Single key remaps need to be executed before shortcut remaps, because otherwise there can be several logical issues. For example if a user has Ctrl remapped to X and Ctrl+A remapped to Y, we can't detect Ctrl+A because the moment Ctrl is pressed it would be remapped to X before the system ever sees Ctrl+A. This is why the design decision was made to separate Remap keys and Remap shortcuts, and all key remaps are reflected in the shortcut remaps.
## Custom Action to launch KBM UI
KBM uses the [`call_custom_action`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L249-L280) method from the `PowertoyModuleIface` in order to launch the KBM UI when the user clicks the Remap a key or Remap a shortcut button from the KBM settings page. On clicking the button, we check if there is already any active KBM UI window, and if there is it is brought to the foreground. If not, the corresponding KBM UI window is launched on a separate detached thread. The UI is described in more detail in [Keyboard Manager UI](keyboardmanagerui.md).
KBM uses the [`call_custom_action`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L249-L280) method from the `PowertoyModuleIface` in order to launch the KBM UI when the user clicks "Remap a key" or "Remap a shortcut" from the KBM settings page. On clicking the button, we check if there is already any active KBM UI window, and if there is it is brought to the foreground. If not, the corresponding KBM UI window is launched on a separate detached thread. The UI is described in more detail in [Keyboard Manager UI](keyboardmanagerui.md).
## SendInput Special Scenarios
### Extended keys
Certain keys such as the arrow keys, <kbd>right Ctrl/Alt</kbd>, and <kbd>Del/Home/Ins</kbd>, etc need to be sent with the `KEYEVENTF_EXTENDEDKEY` flag because otherwise the NumPad versions get sent, which can cause weird behavior when NumLock is on. The code can be found where [`SetKeyEvent` checks `IsExtendedKey(keyCode)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L190-L194) and the list of extended keys in code can be found in [`IsExtendedKey`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L73-L98). Docs about extended keys can be found in [Keyboard Input Overview: Extended-Key Flag
Certain keys such as the arrow keys, <kbd>right Ctrl/Alt</kbd>, and <kbd>Del/Home/Ins</kbd>, etc. need to be sent with the `KEYEVENTF_EXTENDEDKEY` flag because otherwise the NumPad versions get sent, which can cause weird behavior when NumLock is on. The code can be found where [`SetKeyEvent` checks `IsExtendedKey(keyCode)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L190-L194) and the list of extended keys in code can be found in [`IsExtendedKey`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L73-L98). Docs about extended keys can be found in [Keyboard Input Overview: Extended-Key Flag
](https://learn.microsoft.com/windows/win32/inputdev/about-keyboard-input#extended-key-flag).
The weird behavior that is caused by this can be found at these issues:
@@ -177,13 +177,13 @@ For example, while [remapping <kbd>Ctrl</kbd> to <kbd>Caps Lock</kbd>](https://g
While the above work around fixes most of the cases, there are still some scenarios where the modifier can get stuck, mentioned at this [comment](https://github.com/microsoft/PowerToys/issues/3397#issuecomment-663729278), which is why the issue is still open. This occurs if a modifier is pressed after the remap has been invoked before releasing the remapped key and it is a harder scenario to solve which requires refactoring the single key remap code.
### UIPI Issues (not resolved)
`SendInput` does not work directly with certain key codes such as Play/Pause Media, Calculator key, etc as it requires UAC privileges to be injected to the OS and accordingly play the active media app or launch the Calculator app. In order to resolve this the correct approach is that the executable which calls `SendInput` needs to have the [UIAccess flag](https://learn.microsoft.com/windows/win32/winauto/uiauto-securityoverview) set to true, which will also avoid the requirement of KBM having to run as administrator to intercept key events when an elevated window is in focus. The UIAccess flag has many constraints such as it must be a signed executable and must be located in a protected path like Program Files. Since KBM currently runs out of the runner process, it would make more sense to do this work after KBM is moved to a separate executable, and it could be enabled by a separate toggle in settings only if PowerToys is installed in Program Files. [This comment](https://github.com/microsoft/PowerToys/issues/3192#issuecomment-646323661) has more details on this approach and (this)[https://github.com/microsoft/PowerToys/issues/3255] is the tracking issue.
`SendInput` does not work directly with certain key codes such as Play/Pause Media, Calculator key, etc. as it requires UAC privileges to be injected to the OS and accordingly play the active media app or launch the Calculator app. In order to resolve this the correct approach is that the executable which calls `SendInput` needs to have the [UIAccess flag](https://learn.microsoft.com/windows/win32/winauto/uiauto-securityoverview) set to true, which will also avoid the requirement of KBM having to run as administrator to intercept key events when an elevated window is in focus. The UIAccess flag has many constraints such as it must be a signed executable and must be located in a protected path like Program Files. Since KBM currently runs out of the runner process, it would make more sense to do this work after KBM is moved to a separate executable, and it could be enabled by a separate toggle in settings only if PowerToys is installed in Program Files. [This comment](https://github.com/microsoft/PowerToys/issues/3192#issuecomment-646323661) has more details on this approach and (this)[https://github.com/microsoft/PowerToys/issues/3255] is the tracking issue.
## Other remapping approaches
Other approaches for remapping which were deprioritized are:
### Registry approach
This method is used by [SharpKeys](https://github.com/randyrants/sharpkeys) and involves using the [Microsoft Keyboard Scancode mapper registry key](https://github.com/randyrants/sharpkeys) to remap keys based on their scan codes. This has the advantage of being applied in all scenarios and not facing any elevation or UAC issues, however the disadvantages are that for modifying the settings the process must run elevated (as it modifies HKLM registry) and it requires a reboot to get applied. Another issue which is an advantage/disadvantage for users is that the process does not need to be running, so the remaps are applied all the time, including at the password prompt on logging into the user's Windows account, which could get a user stuck if they orphaned a key in their password. This registry doesn't have any support for remapping shortcuts either, so the hook approach was prioritized over this.
This method is used by [SharpKeys](https://github.com/randyrants/sharpkeys) and involves using the [Microsoft Keyboard Scancode mapper registry key](https://github.com/randyrants/sharpkeys) to remap keys based on their scan codes. This has the advantage of being applied in all scenarios and not facing any elevation or UAC issues, however the disadvantages are that for modifying the settings, the process must run elevated (as it modifies HKLM registry) and it requires a reboot to get applied. Another issue which is an advantage/disadvantage for users is that the process does not need to be running, so the remaps are applied all the time, including at the password prompt on logging into the user's Windows account, which could get a user stuck if they orphaned a key in their password. This registry doesn't have any support for remapping shortcuts either, so the hook approach was prioritized over this.
### Driver approach
Using a driver approach has the benefit of not depending on precedence orders as KBM could always run before low level hooks, and it also has the benefit of differentiating between different keyboards, allowing [multi keyboard-specific remaps](https://github.com/microsoft/PowerToys/issues/1460). The disadvantages are however that any bug or crash could have system level consequences. [Interception](https://github.com/oblitum/Interception) is an open source driver that could be used for implementing this. The approach was deprioritized due to the potential side effects.
@@ -191,7 +191,7 @@ Using a driver approach has the benefit of not depending on precedence orders as
## Telemetry
Keyboard Manager emits the following telemetry events (implemented in [trace.h](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.h) and [trace.cpp](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.cpp)):
- **`KeyboardManager_EnableKeyboardManager`:** Logs a `boolean` value storing the KBM toggle state. It is logged whenever KBM is enabled or disabled (emitted in [`enable`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L305-L306) and [`disable`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L315-L316)).
- **`KeyboardManager_KeyRemapCount`:** Logs the number of key to key and key to shortcut remaps (i.e. all the remaps on the Remap a key window). This gets logged on saving new settings in the Remap a key window (emitted at [the end of `ApplySingleKeyRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L159-L163)).
- **`KeyboardManager_OSLevelShortcutRemapCount`:** Logs the number of global shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the Remap a shortcut window (emitted at [the end of `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L220)).
- **`KeyboardManager_AppSpecificShortcutRemapCount`:** Logs the number of app-specific shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the Remap a shortcut window (emitted [after calling `OSLevelShortcutRemapCount` in `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L221)).
- **`KeyboardManager_KeyRemapCount`:** Logs the number of key to key and key to shortcut remaps (i.e. all the remaps on the "Remap a key" window). This gets logged on saving new settings in the "Remap a key" window (emitted at [the end of `ApplySingleKeyRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L159-L163)).
- **`KeyboardManager_OSLevelShortcutRemapCount`:** Logs the number of global shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the "Remap a shortcut" window (emitted at [the end of `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L220)).
- **`KeyboardManager_AppSpecificShortcutRemapCount`:** Logs the number of app-specific shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the "Remap a shortcut" window (emitted [after calling `OSLevelShortcutRemapCount` in `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L221)).
- **`KeyboardManager_Error`:** Logs the occurrence of an error in KBM with the name of the method, error code and the corresponding error message. This is currently used only for logging `SetWindowsHookEx` failures (emitted [at the end of `start_lowlevel_keyboard_hook`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L364-L369)).

View File

@@ -32,7 +32,7 @@ The KBM UI consists of a [`Grid` with several columns](https://github.com/micros
When the UI windows are activated the `KeyboardManagerState` object [sets the `UIState` variable](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L251-L252) which is used for distinguishing if the UI is up from the keyboard hook thread. The [states are also updated](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L53) on opening and closing the Type window.
Clicking the Type Button [opens a content dialog](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L206-L380) which registers key delays using the [`KeyDelay` class](keyboardmanagercommon.md#KeyDelay) for Enter and Esc keys and sets the UI states such that when a key event occurs the TextBlocks on the ContentDialog are updated accordingly. On accepting the dialog the selected keys are copied into the ComboBoxes from the TextBlocks, and on closing the window the key delays are unregistered and UI states are reset.
Clicking the Type Button [opens a content dialog](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L206-L380) which registers key delays using the [`KeyDelay` class](keyboardmanagercommon.md#KeyDelay) for Enter and Esc keys and sets the UI states such that when a key event occurs the TextBlocks on the ContentDialog are updated accordingly. On accepting the dialog, the selected keys are copied into the ComboBoxes from the TextBlocks, and on closing the window, the key delays are unregistered and UI states are reset.
Since ComboBoxes are added dynamically, handlers have been added which [update the accessible names for these controls](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L69-L74), which get executed whenever a drop down is added or removed.

View File

@@ -38,7 +38,7 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
- [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1
- `System.Guid.NewGuid()` for version 4
- `System.Guid.CreateVersion7()` for version 7
- Versions 3 and 5 take two parameters, a namespace and a name
- Versions 3 and 5 take two parameters: a namespace and a name
- The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C)
- The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces
- The name can be any string
@@ -72,10 +72,10 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
### [`InputParser`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs)
- It is responsible only for parsing the query from the user
- Based on the user query, the `ParseInput()` method must return an object that implements the `IComputeRequest` interface or it must throw one of `FormatException` or `ArgumentException`
- Throwing an `ArgumentException` should signal the fact the query contains a mistake that the user can fix (eg. an unsupported hash function, an invalid GUID version, an invalid namespace, etc)
- Throwing an `ArgumentException` should signal the fact that the query contains a mistake that the user can fix (e.g. an unsupported hash function, an invalid GUID version, an invalid namespace, etc.)
> The error message will be shown to the user and no log message will be created
- Throwing a `FormatException` should signal either:
- that the query may become valid, and so it does not make sense to show an error just yet (eg. the query does not contain a request yet, a hash request without a string to hash)
- that the query may become valid, and so it does not make sense to show an error just yet (e.g. the query does not contain a request yet, a hash request without a string to hash)
- that the query is completely invalid
> The error message will not be shown to the user but a log message will be created

View File

@@ -6,7 +6,7 @@ The History Plugin allows users to search or display results they have used (sel
The plugin uses data that was already being captured which is, what results were clicked, and how many times. We do add a little more data to this set now.
When this plugin is queried, it creates results based on this previously selected results data.
In order to make sure selected results in the history are still valid, we re-query the plugin the relevant plug using the PluginManager. If there are no results,
In order to make sure selected results in the history are still valid, we re-query the relevant plugin using the PluginManager. If there are no results,
this history item is not included. This usually means that the result is no longer valid. For instance, if a file was deleted, but it's still in the selected history
we don't want to show it as a selectable result.

View File

@@ -9,7 +9,7 @@ The code itself is very simple, basically just a call into OneNote interop via t
var pages = OneNoteProvider.FindPages(query.Search);
```
The query results will be cached for 1 day, and if cached results are found they'll be returned in the initial `Query()` call, otherwise OneNote itself will be queried in the `delayedExecution:true` overload.
The query results will be cached for 1 day, and if cached results are found they'll be returned in the initial `Query()` call; otherwise, OneNote itself will be queried in the `delayedExecution:true` overload.
If the user actions on a result, it'll open it in the OneNote app, and restore and/or focus the app as well if necessary.

View File

@@ -8,7 +8,7 @@ The user can switch to the found windows, close them or kill their process.
## Remarks
### UWP Apps
- The process of an UWP app can't be detected correctly for windows that are minimized while searching. At this time they are assigned to the generic process `ApplicationFrameHost.exe`. If the user searches for such an window while it is not minimized, then the process gets assigned correctly/updated.
- The process of an UWP app can't be detected correctly for windows that are minimized while searching. At this time they are assigned to the generic process `ApplicationFrameHost.exe`. If the user searches for such a window while it is not minimized, then the process gets assigned correctly/updated.
### Killing processes
- Killing the Explorer process is only allowed, if each folder window is running in its own process. (See section `File Explorer setting` below.)
@@ -17,7 +17,7 @@ The user can switch to the found windows, close them or kill their process.
- Windows of UWP apps don't know their process, until they are searched in non-minimized state.
### File Explorer setting
- To kill the Process of an Explorer window, each window has to run in a separate process. Otherwise the process is the same one as the shell process and killing the shell process will crash the shell (Windows ui).
- To kill the Process of an Explorer window, each window has to run in a separate process. Otherwise, the process is the same one as the shell process and killing the shell process will crash the shell (Windows ui).
- To enable this behavior the setting `Launch folder windows in a separate process` under `Folder Options > View` has to be enabled.
- From PowerToys Run you can open the `Folder options` dialog by clicking the information message in the search results. The information message is only shown when searching with action keyword for explorer windows and can be hidden in the plugin settings.
- Note: The folder option/process is evaluated in real time. After changing the setting it is enough to search again for the windows.

View File

@@ -31,7 +31,7 @@ This document outlines the process for preparing and publishing PowerToys releas
- Uses semantic versioning: `MAJOR.MINOR.PATCH`
- MINOR version increases with regular releases (e.g., 0.89.0)
- PATCH version increases for hotfixes (e.g., 0.87.0 → 0.87.1)
- Each release version must be higher than the previous one for proper updating
- Each release version must be greater than the previous one for proper updating
## Testing Process

View File

@@ -5,10 +5,10 @@
<?include $(sys.CURRENTDIR)\Common.wxi?>
<?define ImageResizerAssetsFiles=?>
<?define ImageResizerAssetsFilesPath=$(var.BinDir)Assets\ImageResizer\?>
<?define ImageResizerAssetsFilesPath=$(var.BinDir)WinUI3Apps\Assets\ImageResizer\?>
<Fragment>
<DirectoryRef Id="BaseApplicationsAssetsFolder">
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<Directory Id="ImageResizerAssetsFolder" Name="ImageResizer" />
</DirectoryRef>
@@ -18,7 +18,7 @@
<Component Id="Module_ImageResizer_Registry" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
<RegistryValue Value="[INSTALLFOLDER]PowerToys.ImageResizerExt.dll" Type="string" />
<RegistryValue Value="[WinUI3AppsInstallFolder]PowerToys.ImageResizerExt.dll" Type="string" />
<RegistryValue Name="ThreadingModel" Value="Apartment" Type="string" />
</RegistryKey>

View File

@@ -186,7 +186,7 @@
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="ImageResizer_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="ImageResizer_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.ImageResizer.resources.dll" />
<File Id="ImageResizer_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\WinUI3Apps\$(var.Language)\PowerToys.ImageResizer.resources.dll" />
</Component>
<Component
Id="ColorPicker_$(var.IdSafeLanguage)_Component"

View File

@@ -179,7 +179,7 @@ Generate-FileList -fileDepsJson "" -fileListName HostsAssetsFiles -wxsFilePath $
Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot
#ImageResizer
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ImageResizer"
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ImageResizer"
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot
#New+

View File

@@ -11,26 +11,35 @@ namespace Common.UI
{
public enum SettingsWindow
{
Overview = 0,
Dashboard = 0,
Overview,
AlwaysOnTop,
Awake,
ColorPicker,
CmdNotFound,
FancyZones,
FileLocksmith,
Run,
ImageResizer,
KBM,
MouseUtils,
MouseWithoutBorders,
Peek,
PowerAccent,
PowerLauncher,
PowerPreview,
PowerRename,
FileExplorer,
ShortcutGuide,
Hosts,
MeasureTool,
PowerOCR,
Workspaces,
RegistryPreview,
CropAndLock,
EnvironmentVariables,
Dashboard,
AdvancedPaste,
Workspaces,
NewPlus,
CmdPal,
ZoomIt,
}
@@ -39,14 +48,22 @@ namespace Common.UI
{
switch (value)
{
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.Overview:
return "Overview";
case SettingsWindow.AlwaysOnTop:
return "AlwaysOnTop";
case SettingsWindow.Awake:
return "Awake";
case SettingsWindow.ColorPicker:
return "ColorPicker";
case SettingsWindow.CmdNotFound:
return "CmdNotFound";
case SettingsWindow.FancyZones:
return "FancyZones";
case SettingsWindow.FileLocksmith:
return "FileLocksmith";
case SettingsWindow.Run:
return "Run";
case SettingsWindow.ImageResizer:
@@ -55,6 +72,16 @@ namespace Common.UI
return "KBM";
case SettingsWindow.MouseUtils:
return "MouseUtils";
case SettingsWindow.MouseWithoutBorders:
return "MouseWithoutBorders";
case SettingsWindow.Peek:
return "Peek";
case SettingsWindow.PowerAccent:
return "PowerAccent";
case SettingsWindow.PowerLauncher:
return "PowerLauncher";
case SettingsWindow.PowerPreview:
return "PowerPreview";
case SettingsWindow.PowerRename:
return "PowerRename";
case SettingsWindow.FileExplorer:
@@ -67,18 +94,18 @@ namespace Common.UI
return "MeasureTool";
case SettingsWindow.PowerOCR:
return "PowerOcr";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.RegistryPreview:
return "RegistryPreview";
case SettingsWindow.CropAndLock:
return "CropAndLock";
case SettingsWindow.EnvironmentVariables:
return "EnvironmentVariables";
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.AdvancedPaste:
return "AdvancedPaste";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.NewPlus:
return "NewPlus";
case SettingsWindow.CmdPal:
return "CmdPal";
case SettingsWindow.ZoomIt:

View File

@@ -21,7 +21,7 @@ public static partial class Kernel32
out int pBytesReturned,
IntPtr lpOverlapped);
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16, EntryPoint = "CreateFileW")]
public static partial int CreateFile(
string lpFileName,
FileAccessType dwDesiredAccess,

View File

@@ -31,6 +31,7 @@ namespace Microsoft.PowerToys.UITest
Hosts,
Runner,
Workspaces,
PowerRename,
}
/// <summary>
@@ -97,6 +98,7 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.Hosts] = "Hosts File Editor",
[PowerToysModule.Runner] = "PowerToys",
[PowerToysModule.Workspaces] = "Workspaces Editor",
[PowerToysModule.PowerRename] = "PowerRename",
};
// Exe start path for the module if it exists.
@@ -107,6 +109,7 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.Hosts] = @"\..\..\..\WinUI3Apps\PowerToys.Hosts.exe",
[PowerToysModule.Runner] = @"\..\..\..\PowerToys.exe",
[PowerToysModule.Workspaces] = @"\..\..\..\PowerToys.WorkspacesEditor.exe",
[PowerToysModule.PowerRename] = @"\..\..\..\WinUI3Apps\PowerToys.PowerRename.exe",
};
}

View File

@@ -129,7 +129,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -141,7 +141,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne(By by, int timeoutMS = 5000, bool global = false)
{
return this.HasOne<Element>(by, timeoutMS, global);
@@ -153,7 +153,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -165,7 +165,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne(string name, int timeoutMS = 5000, bool global = false)
{
return this.HasOne<Element>(By.Name(name), timeoutMS, global);
@@ -177,7 +177,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -189,7 +189,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has(By by, int timeoutMS = 5000, bool global = false)
{
return this.Has<Element>(by, timeoutMS, global);
@@ -201,7 +201,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -213,7 +213,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has(string name, int timeoutMS = 5000, bool global = false)
{
return this.Has<Element>(name, timeoutMS, global);

View File

@@ -5,10 +5,13 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -32,27 +35,17 @@ namespace Microsoft.PowerToys.UITest
private Process? runner;
private PowerToysModule scope;
private string[]? commandLineArgs;
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public SessionHelper(PowerToysModule scope)
public SessionHelper(PowerToysModule scope, string[]? commandLineArgs = null)
{
this.scope = scope;
this.commandLineArgs = commandLineArgs;
this.sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
CheckWinAppDriverAndRoot();
var runnerProcessInfo = new ProcessStartInfo
{
FileName = locationPath + this.runnerPath,
Verb = "runas",
};
if (scope == PowerToysModule.PowerToysSettings)
{
this.ExitExe(runnerProcessInfo.FileName);
this.runner = Process.Start(runnerProcessInfo);
}
}
/// <summary>
@@ -76,7 +69,8 @@ namespace Microsoft.PowerToys.UITest
public SessionHelper Init()
{
this.ExitExe(this.locationPath + this.sessionPath);
this.StartExe(this.locationPath + this.sessionPath);
this.StartExe(this.locationPath + this.sessionPath, this.commandLineArgs);
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
@@ -89,19 +83,6 @@ namespace Microsoft.PowerToys.UITest
public void Cleanup()
{
ExitScopeExe();
try
{
if (this.scope == PowerToysModule.PowerToysSettings)
{
runner?.Kill();
runner?.WaitForExit(); // Optional: Wait for the process to exit
}
}
catch (Exception ex)
{
// Handle exceptions if needed
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
}
}
/// <summary>
@@ -132,17 +113,81 @@ namespace Microsoft.PowerToys.UITest
/// Starts a new exe and takes control of it.
/// </summary>
/// <param name="appPath">The path to the application executable.</param>
public void StartExe(string appPath)
/// <param name="args">Optional command line arguments to pass to the application.</param>
public void StartExe(string appPath, string[]? args = null)
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
if (args != null && args.Length > 0)
{
// Build command line arguments string
string argsString = string.Join(" ", args.Select(arg =>
{
// Quote arguments that contain spaces
if (arg.Contains(' '))
{
return $"\"{arg}\"";
}
return arg;
}));
opts.AddAdditionalCapability("appArguments", argsString);
}
this.Driver = NewWindowsDriver(opts);
}
private void TryLaunchPowerToysSettings(AppiumOptions opts)
{
CheckWinAppDriverAndRoot();
var runnerProcessInfo = new ProcessStartInfo
{
FileName = locationPath + this.runnerPath,
Verb = "runas",
Arguments = "--open-settings",
};
this.ExitExe(runnerProcessInfo.FileName);
this.runner = Process.Start(runnerProcessInfo);
Thread.Sleep(5000);
if (root != null)
{
const int maxRetries = 3;
const int delayMs = 5000;
var windowName = "PowerToys Settings";
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
var settingsWindow = ApiHelper.FindDesktopWindowHandler(
new[] { windowName, AdministratorPrefix + windowName });
if (settingsWindow.Count > 0)
{
var hexHwnd = settingsWindow[0].HWnd.ToString("x");
opts.AddAdditionalCapability("appTopLevelWindow", hexHwnd);
return;
}
if (attempt < maxRetries)
{
Thread.Sleep(delayMs);
}
else
{
throw new TimeoutException("Failed to find PowerToys Settings window after multiple attempts.");
}
}
}
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
/// <param name="info">The path to the application executable.</param>
/// <param name="info">The AppiumOptions for the application.</param>
private WindowsDriver<WindowsElement> NewWindowsDriver(AppiumOptions info)
{
// Create driver with retry
@@ -176,6 +221,19 @@ namespace Microsoft.PowerToys.UITest
public void ExitScopeExe()
{
ExitExe(sessionPath);
try
{
if (this.scope == PowerToysModule.PowerToysSettings)
{
runner?.Kill();
runner?.WaitForExit(); // Optional: Wait for the process to exit
}
}
catch (Exception ex)
{
// Handle exceptions if needed
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
}
}
/// <summary>
@@ -184,7 +242,7 @@ namespace Microsoft.PowerToys.UITest
public void RestartScopeExe()
{
ExitScopeExe();
StartExe(locationPath + sessionPath);
StartExe(locationPath + sessionPath, this.commandLineArgs);
}
public WindowsDriver<WindowsElement> GetRoot()

View File

@@ -4,18 +4,9 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using Windows.Devices.Display.Core;
using Windows.Foundation.Metadata;
using static Microsoft.PowerToys.UITest.UITestBase.NativeMethods;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -35,11 +26,12 @@ namespace Microsoft.PowerToys.UITest
private readonly PowerToysModule scope;
private readonly WindowSize size;
private readonly string[]? commandLineArgs;
private SessionHelper? sessionHelper;
private System.Threading.Timer? screenshotTimer;
private string? screenshotDirectory;
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified)
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings, WindowSize size = WindowSize.UnSpecified, string[]? commandLineArgs = null)
{
this.IsInPipeline = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("platform"));
Console.WriteLine($"Running tests on platform: {Environment.GetEnvironmentVariable("platform")}");
@@ -54,6 +46,7 @@ namespace Microsoft.PowerToys.UITest
this.scope = scope;
this.size = size;
this.commandLineArgs = commandLineArgs;
}
/// <summary>
@@ -75,18 +68,8 @@ namespace Microsoft.PowerToys.UITest
System.Windows.Forms.SendKeys.SendWait("{ESC}");
}
this.sessionHelper = new SessionHelper(scope).Init();
this.sessionHelper = new SessionHelper(scope, commandLineArgs).Init();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), scope, size);
if (this.scope == PowerToysModule.PowerToysSettings)
{
// close Debug warning dialog if any
// Such debug warning dialog seems only appear in PowerToys Settings
if (this.FindAll("DEBUG").Count > 0)
{
this.Find("DEBUG").Find<Button>("Close").Click();
}
}
}
/// <summary>
@@ -173,7 +156,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -185,7 +168,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="by">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne(By by, int timeoutMS = 5000, bool global = false)
{
return this.Session.HasOne<Element>(by, timeoutMS, global);
@@ -197,7 +180,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -209,7 +192,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if only has one element, otherwise false.</returns>
/// <returns>True if only has one element; otherwise, false.</returns>
public bool HasOne(string name, int timeoutMS = 5000, bool global = false)
{
return this.Session.HasOne<Element>(name, timeoutMS, global);
@@ -221,7 +204,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has<T>(By by, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -233,7 +216,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has(By by, int timeoutMS = 5000, bool global = false)
{
return this.Session.Has<Element>(by, timeoutMS, global);
@@ -245,7 +228,7 @@ namespace Microsoft.PowerToys.UITest
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has<T>(string name, int timeoutMS = 5000, bool global = false)
where T : Element, new()
{
@@ -257,7 +240,7 @@ namespace Microsoft.PowerToys.UITest
/// </summary>
/// <param name="name">The name of the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 5000).</param>
/// <returns>True if has one or more element, otherwise false.</returns>
/// <returns>True if has one or more element; otherwise, false.</returns>
public bool Has(string name, int timeoutMS = 5000, bool global = false)
{
return this.Session.Has<Element>(name, timeoutMS, global);

View File

@@ -24,7 +24,7 @@ namespace Microsoft.PowerToys.UITest
/// <param name="c1">base color</param>
/// <param name="c2">test color</param>
/// <param name="fuzz">fuzz factor, default is 10</param>
/// <returns>true if same, otherwise is false</returns>
/// <returns>true if same; otherwise, is false</returns>
public static bool PixIsSame(Color c1, Color c2, int fuzz = 10)
{
return Math.Abs(c1.A - c2.A) <= fuzz && Math.Abs(c1.R - c2.R) <= fuzz && Math.Abs(c1.G - c2.G) <= fuzz && Math.Abs(c1.B - c2.B) <= fuzz;

View File

@@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
internal static class WindowHelper
public static class WindowHelper
{
internal const string AdministratorPrefix = "Administrator: ";

View File

@@ -83,7 +83,7 @@ namespace winrt::PowerToys::Interop::implementation
ev.key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->vkCode;
ev.dwExtraInfo = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)->dwExtraInfo;
// Ignore the keyboard hook if the FilterkeyboardEvent returns false.
// Ignore the keyboard hook if the FilterKeyboardEvent returns false.
if ((s_instance->filterKeyboardEvent != nullptr && !s_instance->filterKeyboardEvent(ev)))
{
continue;

View File

@@ -12,7 +12,7 @@ namespace winrt::PowerToys::Interop::implementation
{
return _map->GetKeyFromName(std::wstring(name));
}
void LayoutMapManaged::Updatelayout()
void LayoutMapManaged::UpdateLayout()
{
_map->UpdateLayout();
}

View File

@@ -10,7 +10,7 @@ namespace winrt::PowerToys::Interop::implementation
hstring GetKeyName(uint32_t key);
uint32_t GetKeyValue(hstring const& name);
void Updatelayout();
void UpdateLayout();
private:
std::unique_ptr<LayoutMap> _map = std::make_unique<LayoutMap>();

View File

@@ -6,7 +6,7 @@ namespace PowerToys
LayoutMapManaged();
String GetKeyName(UInt32 key);
UInt32 GetKeyValue(String name);
void Updatelayout();
void UpdateLayout();
}
}
}

View File

@@ -117,7 +117,7 @@ void LayoutMap::LayoutMapImpl::UpdateLayout()
}
}
// Override special key names like Shift, Ctrl etc because they don't have unicode mappings and key names like Enter, Space as they appear as "\r", " "
// Override special key names like Shift, Ctrl, etc. because they don't have unicode mappings and key names like Enter, Space as they appear as "\r", " "
// To do: localization
keyboardLayoutMap[VK_CANCEL] = L"Break";
keyboardLayoutMap[VK_BACK] = L"Backspace";

View File

@@ -190,7 +190,7 @@ void notifications::show_toast_with_activations(std::wstring message,
// We must set toast's title and contents immediately, because some of the toasts we send could be snoozed.
// Windows instantiates the snoozed toast from scratch before showing it again, so all bindings that were set
// using NotificationData would be empty.
// Add the launch attribute if launch_uri is provided, otherwise omit it
// Add the launch attribute if launch_uri is provided; otherwise, omit it
toast_xml += LR"(<?xml version="1.0"?>)";
if (!launch_uri.empty())
{

View File

@@ -207,7 +207,7 @@ namespace powertoys_gpo
inline std::optional<std::wstring> getPolicyListValue(const std::wstring& registry_list_path, const std::wstring& registry_list_value_name)
{
// This function returns the value of an entry of an policy list. The user scope is only checked, if the list is not enabled for the machine to not mix the lists.
// This function returns the value of an entry of a policy list. The user scope is only checked, if the list is not enabled for the machine to not mix the lists.
HKEY key{};

View File

@@ -28,7 +28,7 @@ namespace AdvancedPaste.UnitTests.ServicesTests;
/// Tests that write batch AI outputs against a list of inputs. Connects to OpenAI and uses the full AdvancedPaste action catalog for Semantic Kernel.
/// If queries produce errors, the error message is written to the output file. If queries produce text-file output, their contents are included as though they were text output.
/// To run this test-suite, first:
/// 1. Setup an OpenAI API key using AdvancedPaste Settings.
/// 1. Set up an OpenAI API key using AdvancedPaste Settings.
/// 2. Comment out the [Ignore] attribute above.
/// 3. Ensure the %USERPROFILE% folder contains the required input files (paths are below).
/// These tests are idempotent and resumable, allowing for partial runs and restarts. It's ok to use existing output files as input files - output-related fields will simply be ignored.

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<Application
x:Class="AdvancedPaste.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

View File

@@ -1,4 +1,4 @@
<XamlCompositionBrushBase
<XamlCompositionBrushBase
x:Class="AdvancedPaste.Controls.AnimatedBorderBrush"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

View File

@@ -1,4 +1,4 @@
<ResourceDictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animations="using:CommunityToolkit.WinUI.Animations"

View File

@@ -1,4 +1,4 @@
<Page
<Page
x:Class="AdvancedPaste.Pages.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

View File

@@ -1,4 +1,4 @@
<ResourceDictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdvancedPaste">

View File

@@ -17,7 +17,7 @@ public sealed class PasteActionModeratedException : PasteActionException
}
/// <summary>
/// Non-localized error description for logs, reports, telemetry etc.
/// Non-localized error description for logs, reports, telemetry, etc.
/// </summary>
public const string ErrorDescription = "Paste operation moderated";
}

View File

@@ -60,7 +60,7 @@ OverlayWindow::OverlayWindow(
m_crosshairCursor.reset(winrt::check_pointer(LoadCursorW(nullptr, IDC_CROSS)));
m_cursorType = CursorType::Standard;
// Setup the visual tree
// Set up the visual tree
m_compositor = compositor;
m_target = CreateWindowTarget(m_compositor);
m_rootVisual = m_compositor.CreateContainerVisual();

View File

@@ -147,7 +147,7 @@ void ThumbnailCropAndLockWindow::CropAndLock(HWND windowToCrop, RECT cropRect)
auto adjustedHeight = windowRect.bottom - windowRect.top;
winrt::check_bool(SetWindowPos(m_window, HWND_TOPMOST, 0, 0, adjustedWidth, adjustedHeight, SWP_NOMOVE | SWP_SHOWWINDOW));
// Setup the thumbnail
// Set up the thumbnail
winrt::check_hresult(DwmRegisterThumbnail(m_window, m_currentTarget, m_thumbnail.addressof()));
clientRect = {};

View File

@@ -14,7 +14,7 @@ namespace constants::nonlocalizable
// String key used by PowerToys
constexpr WCHAR PowerToyKey[] = L"File Locksmith";
// Nonlocalized name of this PowerToy, for logs, etc
// Nonlocalized name of this PowerToy, for logs, etc.
constexpr WCHAR PowerToyName[] = L"File Locksmith";
// JSON key used to store whether the module is enabled

View File

@@ -141,7 +141,7 @@ namespace PowerToys.FileLocksmithUI.ViewModels
catch (Exception ex)
{
Logger.LogError($"Couldn't add a waiter to wait for a process to exit. PID = {process.pid} and Name = {process.name}.", ex);
Processes.Remove(process); // If we couldn't get an handle to the process or it has exited in the meanwhile, don't show it.
Processes.Remove(process); // If we couldn't get a handle to the process or it has exited in the meanwhile, don't show it.
}
}
@@ -162,8 +162,8 @@ namespace PowerToys.FileLocksmithUI.ViewModels
}
catch (Exception ex)
{
Logger.LogError($"Couldn't get an handle to kill process {selectedProcess.name} with PID {selectedProcess.pid}. Likely has been killed already.", ex);
Processes.Remove(selectedProcess); // If we couldn't get an handle to the process, remove it from the list, since it's likely been killed already.
Logger.LogError($"Couldn't get a handle to kill process {selectedProcess.name} with PID {selectedProcess.pid}. Likely has been killed already.", ex);
Processes.Remove(selectedProcess); // If we couldn't get a handle to the process, remove it from the list, since it's likely been killed already.
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<Application
x:Class="Hosts.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

View File

@@ -427,7 +427,7 @@ void SuperSonar<D>::DetectShake()
return;
}
// Size of the rectangle the pointer moved in.
// Size of the rectangle that the pointer moved in.
double rectangleWidth = static_cast<double>(maxX) - minX;
double rectangleHeight = static_cast<double>(maxY) - minY;
@@ -521,7 +521,7 @@ void SuperSonar<D>::StartSonar()
Logger::info("Focusing the sonar on the mouse cursor.");
Trace::MousePointerFocused();
// Cover the entire virtual screen.
// HACK: Draw with 1 pixel off. Otherwise Windows glitches the task bar transparency when a transparent window fill the whole screen.
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
m_sonarPos = ptNowhere;
OnMouseTimer();

View File

@@ -185,7 +185,7 @@ void Highlighter::AddDrawingPoint(MouseButton button)
// Perhaps add a task to the Dispatcher every X circles to clean up.
// Get back on top in case other Window is now the topmost.
// HACK: Draw with 1 pixel off. Otherwise Windows glitches the task bar transparency when a transparent window fill the whole screen.
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
}
@@ -402,7 +402,7 @@ void Highlighter::StartDrawing()
m_visible = true;
// HACK: Draw with 1 pixel off. Otherwise Windows glitches the task bar transparency when a transparent window fill the whole screen.
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
ClearDrawing();
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
@@ -461,7 +461,7 @@ void Highlighter::ApplySettings(MouseHighlighterSettings settings)
void Highlighter::BringToFront()
{
// HACK: Draw with 1 pixel off. Otherwise Windows glitches the task bar transparency when a transparent window fill the whole screen.
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
}

View File

@@ -60,7 +60,7 @@ public static class LayoutHelperTests
yield return new object[] { new TestCase(layoutConfig, layoutInfo) };
// check we handle rounding errors in scaling the preview form
// that might make the form a pixel *smaller* than the current screen -
// that might make the form one pixel *smaller* than the current screen -
// e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768
// with a 5px form padding border:
//

View File

@@ -72,7 +72,7 @@ public static class MouseHelper
// | (a) | |
// +---------+----------------+
//
// setting the position a second time seems to fix this and moves the
// setting the position again seems to fix this and moves the
// cursor to the expected location (b)
var target = location.ToPoint();
for (var i = 0; i < 2; i++)

View File

@@ -10,7 +10,7 @@ public interface IImageRegionCopyService
{
/// <summary>
/// Copies the source region from the provider's source image (e.g. the interactive desktop,
/// a static image, etc) to the target region on the specified Graphics object.
/// a static image, etc.) to the target region on the specified Graphics object.
/// </summary>
/// <remarks>
/// Implementations of this interface are used to capture regions of the interactive desktop

View File

@@ -217,7 +217,7 @@ internal sealed partial class MainForm : Form
this.Thumbnail.Image = null;
tmp.Dispose();
// force preview image memory to be released, otherwise
// force preview image memory to be released; otherwise,
// all the disposed images can pile up without being GC'ed
GC.Collect();
}
@@ -250,7 +250,7 @@ internal sealed partial class MainForm : Form
if (!this.Visible)
{
// we seem to need to turn off topmost and then re-enable it again
// when we show the form, otherwise it doesn't always get shown topmost...
// when we show the form; otherwise, it doesn't always get shown topmost...
this.TopMost = false;
this.TopMost = true;
this.Show();

View File

@@ -181,7 +181,7 @@ void InclusiveCrosshairs::UpdateCrosshairsPosition()
{
POINT ptCursor;
// HACK: Draw with 1 pixel off. Otherwise Windows glitches the task bar transparency when a transparent window fill the whole screen.
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
GetCursorPos(&ptCursor);

View File

@@ -117,8 +117,8 @@ namespace MouseUtils.UITests
VerifyMouseHighlighterNotAppears(ref settings, "leftClick");
VerifyMouseHighlighterNotAppears(ref settings, "rightClick");
// [Test Case] With left mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
// [Test Case] With right mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
// [Test Case] With left mouse button pressed, drag the mouse and verify the highlight is dragged with the pointer.
// [Test Case] With right mouse button pressed, drag the mouse and verify the highlight is dragged with the pointer.
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1 - 100, xy.Item2);

View File

@@ -25,9 +25,9 @@ Find My Mouse:
Mouse Highlighter:
* Enable Mouse Highlighter. Then:
- [x] Press the activation shortcut and press left and right click somewhere, verifying the hightlights are applied.
- [x] With left mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
- [x] With right mouse button pressed, drag the mouse and verify the hightlight is dragged with the pointer.
- [x] Press the activation shortcut and press left and right click somewhere, verifying the highlights are applied.
- [x] With left mouse button pressed, drag the mouse and verify the highlight is dragged with the pointer.
- [x] With right mouse button pressed, drag the mouse and verify the highlight is dragged with the pointer.
- [x] Press the activation shortcut again and verify no highlights appear when the mouse buttons are clicked.
- [x] Disable Mouse Highlighter and verify that the module is not activated when you press the activation shortcut.
* Test the different settings and verify they apply:

View File

@@ -970,7 +970,7 @@ namespace MouseWithoutBorders.Class
/// <summary>
/// Use this method to figure out if your code is running on a Microsoft computer.
/// </summary>
/// <returns>True if running on a Microsoft computer, otherwise false.</returns>
/// <returns>True if running on a Microsoft computer; otherwise, false.</returns>
internal static bool IsRunningAtMicrosoft()
{
string domain = GetDNSDomain();

View File

@@ -27,7 +27,7 @@ namespace MouseWithoutBorders.Core;
*
* SEQUENCE OF EVENTS:
* DragDropStep01: MachineX: Remember mouse down state since it could be a start of a dragging
* DragDropStep02: MachineY: Send an message to the MachineX to ask it to check if it is
* DragDropStep02: MachineY: Send a message to the MachineX to ask it to check if it is
* doing drag/drop
* DragDropStep03: MachineX: Got explorerDragDrop, send WM_CHECK_EXPLORER_DRAG_DROP to its mainForm
* DragDropStep04: MachineX: Show Mouse Without Borders Helper form at mouse cursor to get DragEnter event.

View File

@@ -50,7 +50,7 @@ public static class LoggerTests
var lines = log.Split("\r\n");
// some parts of the PrivateDump output are impossible to reproduce -
// e.g. random numbers, system timestamps, thread ids, etc, so we'll mask them
// e.g. random numbers, system timestamps, thread ids, etc., so we'll mask them
var maskPrefixes = new string[]
{
"----_s0 = ",

View File

@@ -808,7 +808,7 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context)
d2d_device_context->FillRectangle(monitor_rect, brush.get());
}
}
// Finalize the overlay - dimm the buttons if no thumbnail is present and show "No active window"
// Finalize the overlay - dim the buttons if no thumbnail is present and show "No active window"
use_overlay->toggle_window_group(miniature_shown || window_state == MINIMIZED);
if (!miniature_shown && window_state != MINIMIZED)
{

View File

@@ -1,4 +1,4 @@
<Page
<Page
x:Class="WorkspacesEditor.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -172,7 +172,7 @@
VerticalContentAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
Visibility="{Binding IsWorkspacesViewEmpty, Mode=OneWay, Converter={StaticResource BooleanToInvertedVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl x:Name="WorkspacesItemsControl" ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
@@ -235,7 +235,10 @@
Grid.Column="1"
Margin="12,12,12,12"
Orientation="Vertical">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<StackPanel
x:Name="WorkspaceActionGroup"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
x:Name="MoreButton"
HorizontalAlignment="Right"
@@ -248,7 +251,8 @@
</Button>
<Popup
AllowsTransparency="True"
IsOpen="{Binding IsPopupVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Closed="PopupClosed"
IsOpen="{Binding IsPopupVisible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Placement="Left"
PlacementTarget="{Binding ElementName=MoreButton}"
StaysOpen="False">

View File

@@ -4,7 +4,8 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using ManagedCommon;
using WorkspacesEditor.Models;
using WorkspacesEditor.ViewModels;
@@ -42,17 +43,28 @@ namespace WorkspacesEditor
e.Handled = true;
Button button = sender as Button;
Project selectedProject = button.DataContext as Project;
selectedProject.IsPopupVisible = false;
_mainViewModel.DeleteProject(selectedProject);
}
private void MoreButton_Click(object sender, RoutedEventArgs e)
{
_mainViewModel.CloseAllPopups();
e.Handled = true;
Button button = sender as Button;
Project project = button.DataContext as Project;
project.IsPopupVisible = true;
}
private void PopupClosed(object sender, object e)
{
if (sender is Popup p && p.DataContext is Project proj)
{
proj.IsPopupVisible = false;
}
}
private void LaunchButton_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;

View File

@@ -33,7 +33,7 @@ namespace WorkspacesEditor.Telemetry
// Number of apps with "Launch as admin" set
public int AdminCount { get; set; }
// True of user checked "Create Shortcut". False if not.
// True if user checked "Create Shortcut". False if not.
public bool ShortcutCreated { get; set; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;

View File

@@ -1,4 +1,4 @@
<ResourceDictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">

View File

@@ -437,7 +437,11 @@ namespace WorkspacesEditor.ViewModels
private void RunSnapshotTool(bool isExistingProjectLaunched)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesSnapshotTool.exe");
var exeDir = Path.GetDirectoryName(Environment.ProcessPath);
var snapshotUtilsPath = Path.Combine(exeDir, "PowerToys.WorkspacesSnapshotTool.exe");
process.StartInfo = new ProcessStartInfo(snapshotUtilsPath);
process.StartInfo.CreateNoWindow = true;
process.StartInfo.Arguments = isExistingProjectLaunched ? $"{(int)InvokePoint.LaunchAndEdit}" : string.Empty;

View File

@@ -51,6 +51,8 @@
MouseLeave="AppBorder_MouseLeave">
<Expander
Margin="0,0,20,0"
AutomationProperties.AutomationId="{Binding AppName}"
AutomationProperties.Name="{Binding AppName}"
FlowDirection="RightToLeft"
IsEnabled="{Binding IsIncluded, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsExpanded="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
@@ -399,7 +401,11 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}" ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<ItemsControl
x:Name="CapturedAppList"
AutomationProperties.Name="Captured Application List"
ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}"
ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</StackPanel>
</ScrollViewer>

View File

@@ -0,0 +1,605 @@
// 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;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
[Ignore("not stable")]
public class WorkspacesEditingPageTests : WorkspacesUiAutomationBase
{
public WorkspacesEditingPageTests()
: base()
{
}
[TestMethod("WorkspacesEditingPage.RemoveApp")]
[TestCategory("Workspaces Editing Page UI")]
public void TestRemoveAppFromWorkspace()
{
// Find app list
var appList = Find<Custom>("AppList");
var initialAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
if (initialAppCount == 0)
{
Assert.Inconclusive("No apps in workspace to remove");
return;
}
// Remove first app
var firstApp = appList.FindAll<Custom>(By.ClassName("AppItem"))[0];
var appName = firstApp.GetAttribute("Name");
var removeButton = firstApp.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(500);
// Verify app removed from list
var finalAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
Assert.AreEqual(initialAppCount - 1, finalAppCount, "App should be removed from list");
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
Assert.AreEqual(finalAppCount, windowPreviews.Count, "Preview should show correct number of windows");
}
[TestMethod("WorkspacesEditingPage.RemoveAndAddBackApp")]
[TestCategory("Workspaces Editing Page UI")]
public void TestRemoveAndAddBackApp()
{
// Find app list
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
var appName = firstApp.GetAttribute("Name");
// Remove app
var removeButton = firstApp.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(500);
// Verify removed app shows in "removed apps" section
Assert.IsTrue(Has<Button>("Add back"), "Should have 'Add back' button for removed apps");
// Add back the app
var addBackButton = Find<Button>("Add back");
addBackButton.Click();
Thread.Sleep(500);
// Verify app is back in the list
var restoredApp = appList.Find<Custom>(By.Name(appName), timeoutMS: 2000);
Assert.IsNotNull(restoredApp, "App should be restored to the list");
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
var currentAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
Assert.AreEqual(currentAppCount, windowPreviews.Count, "Preview should show all windows again");
}
[TestMethod("WorkspacesEditingPage.SetAppMinimized")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetAppMinimized()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find and toggle minimized checkbox
var minimizedCheckbox = firstApp.Find<CheckBox>("Minimized");
bool wasMinimized = minimizedCheckbox.IsChecked;
minimizedCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasMinimized, minimizedCheckbox.IsChecked, "Minimized state should toggle");
// Verify preview reflects the change
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
// The first window preview should indicate minimized state
if (minimizedCheckbox.IsChecked && windowPreviews.Count > 0)
{
var firstPreview = windowPreviews[0];
var opacity = firstPreview.GetAttribute("Opacity");
// Minimized windows might have reduced opacity or other visual indicator
Assert.IsNotNull(opacity, "Minimized window should have visual indication in preview");
}
}
[TestMethod("WorkspacesEditingPage.SetAppMaximized")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetAppMaximized()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find and toggle maximized checkbox
var maximizedCheckbox = firstApp.Find<CheckBox>("Maximized");
bool wasMaximized = maximizedCheckbox.IsChecked;
maximizedCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasMaximized, maximizedCheckbox.IsChecked, "Maximized state should toggle");
// Verify preview reflects the change
var previewPane = Find<Pane>("Preview");
if (maximizedCheckbox.IsChecked)
{
// Maximized window should fill the preview area
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
if (windowPreviews.Count > 0)
{
var firstPreview = windowPreviews[0];
// Check if preview shows maximized state
var width = firstPreview.GetAttribute("Width");
var height = firstPreview.GetAttribute("Height");
Assert.IsNotNull(width, "Maximized window should have width in preview");
Assert.IsNotNull(height, "Maximized window should have height in preview");
}
}
}
[TestMethod("WorkspacesEditingPage.LaunchAsAdmin")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetLaunchAsAdmin()
{
// Find app that supports admin launch
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
bool foundAdminCapableApp = false;
foreach (var app in apps)
{
try
{
var adminCheckbox = app.Find<CheckBox>("Launch as admin", timeoutMS: 1000);
if (adminCheckbox != null && adminCheckbox.IsChecked)
{
foundAdminCapableApp = true;
bool wasAdmin = adminCheckbox.IsChecked;
adminCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasAdmin, adminCheckbox.IsChecked, "Admin launch state should toggle");
break;
}
}
catch
{
// This app doesn't support admin launch
continue;
}
}
if (!foundAdminCapableApp)
{
Assert.Inconclusive("No apps in workspace support admin launch");
}
}
[TestMethod("WorkspacesEditingPage.AddCLIArgs")]
[TestCategory("Workspaces Editing Page UI")]
public void TestAddCommandLineArguments()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find CLI args textbox
var cliArgsTextBox = firstApp.Find<TextBox>("Command line arguments", timeoutMS: 2000);
if (cliArgsTextBox == null)
{
Assert.Inconclusive("App does not support command line arguments");
return;
}
// Add test arguments
string testArgs = "--test-arg value";
cliArgsTextBox.SetText(testArgs);
Thread.Sleep(500);
// Verify arguments were entered
Assert.AreEqual(testArgs, cliArgsTextBox.Text, "Command line arguments should be set");
}
[TestMethod("WorkspacesEditingPage.ChangeAppPosition")]
[TestCategory("Workspaces Editing Page UI")]
public void TestManuallyChangeAppPosition()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find position controls
var xPositionBox = firstApp.Find<TextBox>("X position", timeoutMS: 2000);
var yPositionBox = firstApp.Find<TextBox>("Y position", timeoutMS: 2000);
if (xPositionBox == null || yPositionBox == null)
{
// Try alternate approach with spinners
var positionSpinners = firstApp.FindAll<Custom>(By.ClassName("SpinBox"));
if (positionSpinners.Count >= 2)
{
xPositionBox = positionSpinners[0].Find<TextBox>(By.ClassName("TextBox"));
yPositionBox = positionSpinners[1].Find<TextBox>(By.ClassName("TextBox"));
}
}
if (xPositionBox != null && yPositionBox != null)
{
// Change position
xPositionBox.SetText("200");
Thread.Sleep(500);
yPositionBox.SetText("150");
Thread.Sleep(500);
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
Assert.IsTrue(windowPreviews.Count > 0, "Preview should show window at new position");
}
else
{
Assert.Inconclusive("Could not find position controls");
}
}
[TestMethod("WorkspacesEditingPage.ChangeWorkspaceName")]
[TestCategory("Workspaces Editing Page UI")]
public void TestChangeWorkspaceName()
{
// Find workspace name textbox
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
// Change name
string newName = "Renamed_Workspace_" + DateTime.Now.Ticks;
nameTextBox.SetText(newName);
Thread.Sleep(500);
// Save changes
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify we're back at main list
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list after saving");
// Verify workspace was renamed
var workspacesList = Find<Custom>("WorkspacesList");
var renamedWorkspace = workspacesList.Find<Custom>(By.Name(newName), timeoutMS: 2000);
Assert.IsNotNull(renamedWorkspace, "Workspace should be renamed in the list");
}
[TestMethod("WorkspacesEditingPage.SaveAndCancelWork")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSaveAndCancelButtons()
{
// Make a change
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
string tempName = originalName + "_temp";
nameTextBox.SetText(tempName);
Thread.Sleep(500);
// Test Cancel button
var cancelButton = Find<Button>("Cancel");
cancelButton.Click();
Thread.Sleep(1000);
// Verify returned to main list without saving
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
// Go back to editing
var workspacesList = Find<Custom>("WorkspacesList");
var workspace = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
workspace.Click();
Thread.Sleep(1000);
// Verify name wasn't changed
nameTextBox = Find<TextBox>("Workspace name");
Assert.AreEqual(originalName, nameTextBox.Text, "Name should not be changed after cancel");
// Now test Save button
nameTextBox.SetText(tempName);
Thread.Sleep(500);
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify saved
workspacesList = Find<Custom>("WorkspacesList");
var savedWorkspace = workspacesList.Find<Custom>(By.Name(tempName), timeoutMS: 2000);
Assert.IsNotNull(savedWorkspace, "Workspace should be saved with new name");
}
[TestMethod("WorkspacesEditingPage.NavigateWithoutSaving")]
[TestCategory("Workspaces Editing Page UI")]
public void TestNavigateToMainPageWithoutSaving()
{
// Make a change
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
nameTextBox.SetText(originalName + "_unsaved");
Thread.Sleep(500);
// Click on "Workspaces" navigation/breadcrumb
if (Has<NavigationViewItem>("Workspaces", timeoutMS: 1000))
{
var workspacesNav = Find<NavigationViewItem>("Workspaces");
workspacesNav.Click();
Thread.Sleep(1000);
}
else if (Has<HyperlinkButton>("Workspaces", timeoutMS: 1000))
{
var workspacesBreadcrumb = Find<HyperlinkButton>("Workspaces");
workspacesBreadcrumb.Click();
Thread.Sleep(1000);
}
// If there's a confirmation dialog, handle it
if (Has<Button>("Discard", timeoutMS: 1000))
{
Find<Button>("Discard").Click();
Thread.Sleep(500);
}
// Verify returned to main list
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
// Verify changes weren't saved
var workspacesList = Find<Custom>("WorkspacesList");
var unsavedWorkspace = workspacesList.Find<Custom>(By.Name(originalName + "_unsaved"), timeoutMS: 1000);
Assert.IsNull(unsavedWorkspace, "Unsaved changes should not persist");
}
[TestMethod("WorkspacesEditingPage.CreateDesktopShortcut")]
[TestCategory("Workspaces Editing Page UI")]
public void TestCreateDesktopShortcut()
{
// Find desktop shortcut checkbox
var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
// Get desktop path
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// Get workspace name to check for shortcut
var nameTextBox = Find<TextBox>("Workspace name");
string workspaceName = nameTextBox.Text;
string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
// Clean up any existing shortcut
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
Thread.Sleep(500);
}
// Check the checkbox
if (!shortcutCheckbox.IsChecked)
{
shortcutCheckbox.Click();
Thread.Sleep(500);
}
// Save
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(2000); // Give time for shortcut creation
// Verify shortcut was created
Assert.IsTrue(File.Exists(shortcutPath), "Desktop shortcut should be created");
// Clean up
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
}
}
[TestMethod("WorkspacesEditingPage.DesktopShortcutState")]
[TestCategory("Workspaces Editing Page UI")]
public void TestDesktopShortcutCheckboxState()
{
// Get workspace name
var nameTextBox = Find<TextBox>("Workspace name");
string workspaceName = nameTextBox.Text;
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
// Find checkbox
var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
// Test 1: When shortcut exists
if (!File.Exists(shortcutPath))
{
// Create shortcut first
if (!shortcutCheckbox.IsChecked)
{
shortcutCheckbox.Click();
Thread.Sleep(500);
}
Find<Button>("Save").Click();
Thread.Sleep(2000);
// Navigate back to editing
NavigateToEditingPage();
}
shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
Assert.IsTrue(shortcutCheckbox.IsChecked, "Checkbox should be checked when shortcut exists");
// Test 2: Remove shortcut
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
Thread.Sleep(500);
}
// Re-navigate to refresh state
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
NavigateToEditingPage();
shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
Assert.IsFalse(shortcutCheckbox.IsChecked, "Checkbox should be unchecked when shortcut doesn't exist");
}
[TestMethod("WorkspacesEditingPage.LaunchAndEdit")]
[TestCategory("Workspaces Editing Page UI")]
public void TestLaunchAndEditCapture()
{
// Find Launch and Edit button
var launchEditButton = Find<Button>("Launch and Edit");
launchEditButton.Click();
Thread.Sleep(3000); // Wait for apps to launch
// Open a new application
Process.Start("calc.exe");
Thread.Sleep(2000);
// Click Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Verify new app was added
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
bool foundCalculator = false;
foreach (var app in apps)
{
var appName = app.GetAttribute("Name");
if (appName.Contains("Calculator", StringComparison.OrdinalIgnoreCase))
{
foundCalculator = true;
break;
}
}
Assert.IsTrue(foundCalculator, "Newly opened Calculator should be captured and added");
// Clean up
foreach (var process in Process.GetProcessesByName("CalculatorApp"))
{
process.Kill();
}
foreach (var process in Process.GetProcessesByName("Calculator"))
{
process.Kill();
}
}
// Helper methods
private void NavigateToEditingPage()
{
// Ensure we have at least one workspace
if (!Has<Custom>("WorkspacesList", timeoutMS: 1000))
{
CreateTestWorkspace();
}
// Click on first workspace to edit
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
if (workspaceItems.Count == 0)
{
CreateTestWorkspace();
workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
}
workspaceItems[0].Click();
Thread.Sleep(1000);
}
private void CreateTestWorkspace()
{
// Open a test app
Process.Start("notepad.exe");
Thread.Sleep(1000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save with default name
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Close test app
foreach (var process in Process.GetProcessesByName("notepad"))
{
process.Kill();
}
}
}

View File

@@ -8,10 +8,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesEditorTests : UITestBase
public class WorkspacesEditorTests : WorkspacesUiAutomationBase
{
public WorkspacesEditorTests()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
: base()
{
}
@@ -21,4 +21,217 @@ public class WorkspacesEditorTests : UITestBase
{
Assert.IsTrue(this.Has<Button>("Create Workspace"), "Should have create workspace button");
}
/*
[TestMethod("WorkspacesEditor.Editor.NewWorkspaceAppearsInList")]
[TestCategory("Workspaces UI")]
public void TestNewWorkspaceAppearsInListAfterCapture()
{
// Open test application
OpenNotepad();
Thread.Sleep(2000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save workspace
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify workspace appears in list
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
Assert.IsTrue(workspaceItems.Count > 0, "New workspace should appear in the list");
CloseNotepad();
}
[TestMethod("WorkspacesEditor.Editor.CancelCaptureDoesNotAddWorkspace")]
[TestCategory("Workspaces UI")]
public void TestCancelCaptureDoesNotAddWorkspace()
{
// Count existing workspaces
var workspacesList = Find<Custom>("WorkspacesList");
var initialCount = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem")).Count;
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Cancel
var cancelButton = Find<Button>("Cancel");
cancelButton.Click();
Thread.Sleep(1000);
// Verify count hasn't changed
var finalCount = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem")).Count;
Assert.AreEqual(initialCount, finalCount, "Workspace count should not change after canceling");
}
[TestMethod("WorkspacesEditor.Editor.SearchFiltersWorkspaces")]
[TestCategory("Workspaces UI")]
public void TestSearchFiltersWorkspaces()
{
// Create test workspaces first
CreateTestWorkspace("TestWorkspace1");
CreateTestWorkspace("TestWorkspace2");
CreateTestWorkspace("DifferentName");
// Find search box
var searchBox = Find<TextBox>("Search");
searchBox.SetText("TestWorkspace");
Thread.Sleep(1000);
// Verify filtered results
var workspacesList = Find<Custom>("WorkspacesList");
var visibleItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
// Should only show items matching "TestWorkspace"
Assert.IsTrue(visibleItems.Count >= 2, "Should show at least 2 TestWorkspace items");
// Clear search
searchBox.SetText(string.Empty);
Thread.Sleep(500);
}
[TestMethod("WorkspacesEditor.Editor.SortByWorks")]
[TestCategory("Workspaces UI")]
public void TestSortByFunctionality()
{
// Find sort dropdown
var sortDropdown = Find<ComboBox>("SortBy");
sortDropdown.Click();
Thread.Sleep(500);
// Select different sort options
var sortOptions = FindAll<Custom>(By.ClassName("ComboBoxItem"));
if (sortOptions.Count > 1)
{
sortOptions[1].Click(); // Select second option
Thread.Sleep(1000);
// Verify list is updated (we can't easily verify sort order in UI tests)
var workspacesList = Find<Custom>("WorkspacesList");
Assert.IsNotNull(workspacesList, "Workspaces list should still be visible after sorting");
}
}
[TestMethod("WorkspacesEditor.Editor.SortByPersists")]
[TestCategory("Workspaces UI")]
public void TestSortByPersistsAfterRestart()
{
// Set sort option
var sortDropdown = Find<ComboBox>("SortBy");
sortDropdown.Click();
Thread.Sleep(500);
var sortOptions = FindAll<Custom>(By.ClassName("ComboBoxItem"));
string selectedOption = string.Empty;
if (sortOptions.Count > 1)
{
sortOptions[1].Click(); // Select second option
selectedOption = sortDropdown.Text;
Thread.Sleep(1000);
}
// Restart editor
RestartScopeExe();
Thread.Sleep(2000);
// Verify sort option persisted
sortDropdown = Find<ComboBox>("SortBy");
Assert.AreEqual(selectedOption, sortDropdown.Text, "Sort option should persist after restart");
}
[TestMethod("WorkspacesEditor.Editor.RemoveWorkspace")]
[TestCategory("Workspaces UI")]
public void TestRemoveWorkspace()
{
// Create a test workspace
CreateTestWorkspace("WorkspaceToRemove");
// Find the workspace in the list
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.Find<Custom>(By.Name("WorkspaceToRemove"));
// Click remove button
var removeButton = workspaceItem.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(1000);
// Confirm removal if dialog appears
if (Has<Button>("Yes"))
{
Find<Button>("Yes").Click();
Thread.Sleep(1000);
}
// Verify workspace is removed
Assert.IsFalse(Has(By.Name("WorkspaceToRemove")), "Workspace should be removed from list");
}
[TestMethod("WorkspacesEditor.Editor.EditOpensEditingPage")]
[TestCategory("Workspaces UI")]
public void TestEditOpensEditingPage()
{
// Create a test workspace if none exist
if (!Has<Custom>("WorkspacesList"))
{
CreateTestWorkspace("TestWorkspace");
}
// Find first workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
// Click edit button
var editButton = workspaceItem.Find<Button>("Edit");
editButton.Click();
Thread.Sleep(1000);
// Verify editing page opened
Assert.IsTrue(Has<Button>("Save"), "Should have Save button on editing page");
Assert.IsTrue(Has<Button>("Cancel"), "Should have Cancel button on editing page");
// Go back
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
}
[TestMethod("WorkspacesEditor.Editor.ClickWorkspaceOpensEditingPage")]
[TestCategory("Workspaces UI")]
public void TestClickWorkspaceOpensEditingPage()
{
// Create a test workspace if none exist
if (!Has<Custom>("WorkspacesList"))
{
CreateTestWorkspace("TestWorkspace");
}
// Find first workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
// Click on the workspace item itself
workspaceItem.Click();
Thread.Sleep(1000);
// Verify editing page opened
Assert.IsTrue(Has<Button>("Save"), "Should have Save button on editing page");
Assert.IsTrue(Has<Button>("Cancel"), "Should have Cancel button on editing page");
// Go back
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
}
*/
}

View File

@@ -23,6 +23,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<ProjectReference Include="..\WorkspacesEditor\WorkspacesEditor.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,100 @@
// 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.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
[Ignore("NOT STABLE")]
public class WorkspacesLauncherTest : WorkspacesUiAutomationBase
{
public WorkspacesLauncherTest()
: base()
{
}
[TestMethod("WorkspacesEditor.Launcher.LaunchFromEditor")]
[TestCategory("Workspaces UI")]
public void TestLaunchWorkspaceFromEditor()
{
ClearWorkspaces();
var uuid = Guid.NewGuid().ToString("N").Substring(0, 8);
CreateTestWorkspace(uuid);
CloseNotepad();
var launchButton = Find<Button>(By.Name("Launch"));
launchButton.Click();
Task.Delay(2000).Wait();
var processes = System.Diagnostics.Process.GetProcessesByName("notepad");
Assert.IsTrue(processes?.Length > 0);
}
[TestMethod("WorkspacesEditor.Launcher.CancelLaunch")]
[TestCategory("Workspaces UI")]
public void TestCancelLaunch()
{
// Create workspace with multiple apps
CreateWorkspaceWithApps();
// Launch workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
var launchButton = workspaceItem.Find<Button>("Launch");
launchButton.Click();
Thread.Sleep(1000);
// Cancel launch
if (Has<Button>("Cancel launch"))
{
Find<Button>("Cancel launch").Click();
Thread.Sleep(1000);
// Verify launcher closed
Assert.IsFalse(Has<Window>("Workspaces Launcher"), "Launcher window should close after cancel");
}
// Close any apps that may have launched
CloseTestApplications();
}
[TestMethod("WorkspacesEditor.Launcher.DismissKeepsLaunching")]
[TestCategory("Workspaces UI")]
public void TestDismissKeepsAppsLaunching()
{
// Create workspace with apps
CreateWorkspaceWithApps();
// Launch workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
var launchButton = workspaceItem.Find<Button>("Launch");
launchButton.Click();
Thread.Sleep(1000);
// Dismiss launcher
if (Has<Button>("Dismiss"))
{
Find<Button>("Dismiss").Click();
Thread.Sleep(1000);
// Verify launcher closed but apps continue launching
Assert.IsFalse(Has<Window>("Workspaces Launcher"), "Launcher window should close after dismiss");
// Wait for apps to finish launching
Thread.Sleep(3000);
// Verify apps launched (notepad should be open)
Assert.IsTrue(WindowHelper.IsWindowOpen("Notepad"), "Apps should continue launching after dismiss");
}
// Close launched apps
CloseTestApplications();
}
}

View File

@@ -0,0 +1,195 @@
// 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.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesSettingsTests : UITestBase
{
public WorkspacesSettingsTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Medium)
{
}
[TestMethod("WorkspacesSettings.LaunchFromSettings")]
[TestCategory("Workspaces Settings UI")]
public void TestLaunchEditorFromSettingsPage()
{
GoToSettingsPageAndEnable();
}
[TestMethod("WorkspacesSettings.ActivationShortcut")]
[TestCategory("Workspaces Settings UI")]
public void TestActivationShortcutCustomization()
{
GoToSettingsPageAndEnable();
// Find the activation shortcut control
var shortcutButton = Find<Button>("Activation shortcut");
Assert.IsNotNull(shortcutButton, "Activation shortcut control should exist");
// Test customizing the shortcut
shortcutButton.Click();
Task.Delay(1000).Wait();
// Send new key combination (Win+Ctrl+W)
SendKeys(Key.Win, Key.Ctrl, Key.W);
var saveButton = Find<Button>("Save");
Assert.IsNotNull(saveButton, "Save button should exist after editing shortcut");
saveButton.Click();
var helpText = shortcutButton.HelpText;
Assert.AreEqual("Win + Ctrl + W", helpText, "Activation shortcut should be updated to Win + Ctrl + W");
}
[TestMethod("WorkspacesSettings.EnableToggle")]
[TestCategory("Workspaces Settings UI")]
public void TestEnableDisableModule()
{
GoToSettingsPageAndEnable();
// Find the enable toggle
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
Assert.IsNotNull(enableToggle, "Enable Workspaces toggle should exist");
Assert.IsTrue(enableToggle.IsOn, "Enable Workspaces toggle should be in the 'on' state");
// Toggle the state
enableToggle.Click();
Task.Delay(500).Wait();
// Verify state changed
Assert.IsFalse(enableToggle.IsOn, "Toggle state should change");
// Verify related controls are enabled/disabled accordingly
var launchButton = Find<Button>("Launch editor");
Assert.IsFalse(launchButton.Enabled, "Launch editor button should be disabled when module is disabled");
}
[TestMethod("WorkspacesSettings.LaunchByActivationShortcut")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until settings & runner can be connected in framework")]
public void TestLaunchEditorByActivationShortcut()
{
// Ensure module is enabled
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (!enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
// Close settings window to test shortcut
ExitScopeExe();
Thread.Sleep(1000);
// Default shortcut is Win+Ctrl+`
SendKeys(Key.Win, Key.Ctrl, Key.W);
Thread.Sleep(2000);
// Verify editor opened
Assert.IsTrue(WindowHelper.IsWindowOpen("Workspaces Editor"), "Workspaces Editor should open with activation shortcut");
// Reopen settings for next tests
RestartScopeExe();
NavigateToWorkspacesSettings();
}
[TestMethod("WorkspacesSettings.DisableModuleNoLaunch")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until settings & runner can be connected in framework")]
public void TestDisabledModuleDoesNotLaunchByShortcut()
{
// Disable the module
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
// Close settings to test shortcut
ExitScopeExe();
Thread.Sleep(1000);
// Try to launch with shortcut
SendKeys(Key.Win, Key.Ctrl, Key.W);
Thread.Sleep(2000);
// Verify editor did not open
Assert.IsFalse(WindowHelper.IsWindowOpen("Workspaces Editor"), "Workspaces Editor should not open when module is disabled");
// Reopen settings and re-enable the module
RestartScopeExe();
NavigateToWorkspacesSettings();
enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (!enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
}
[TestMethod("WorkspacesSettings.QuickAccessLaunch")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until tray icon supported is in framework")]
public void TestLaunchFromQuickAccess()
{
// This test verifies the "quick access" mention in settings
// Look for any quick access related UI elements
var quickAccessInfo = FindAll(By.LinkText("quick access"));
if (quickAccessInfo.Count > 0)
{
Assert.IsTrue(quickAccessInfo.Count > 0, "Quick access information should be present in settings");
}
// Note: Actual system tray/quick access interaction would require
// more complex automation that might be platform-specific
}
private void NavigateToWorkspacesSettings()
{
// Find and click Workspaces in the navigation
var workspacesNavItem = Find<NavigationViewItem>("Workspaces");
workspacesNavItem.Click();
Thread.Sleep(1000);
}
private void GoToSettingsPageAndEnable()
{
if (this.FindAll<NavigationViewItem>("Workspaces").Count == 0)
{
this.Find<NavigationViewItem>("Windowing & Layouts").Click();
}
this.Find<NavigationViewItem>("Workspaces").Click();
var enableButton = this.Find<ToggleSwitch>("Enable Workspaces");
Assert.IsNotNull(enableButton, "Enable Workspaces toggle should exist");
if (!enableButton.IsOn)
{
enableButton.Click();
Task.Delay(500).Wait(); // Wait for the toggle animation and state change
}
// Verify it's now enabled
Assert.IsTrue(enableButton.IsOn, "Enable Workspaces toggle should be in the 'on' state");
}
private void AttachWorkspacesEditor()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.Workspaces);
}
}

View File

@@ -0,0 +1,82 @@
// 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.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WorkspacesEditor.Utils;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesSnapshotTests : WorkspacesUiAutomationBase
{
public WorkspacesSnapshotTests()
: base()
{
}
[TestMethod("WorkspacesSnapshot.CancelCapture")]
[TestCategory("Workspaces Snapshot UI")]
public void TestCaptureCancel()
{
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(1000).Wait();
AttachSnapshotWindow();
var cancelButton = Find<Button>("Cancel");
Assert.IsNotNull(cancelButton, "Capture button should exist");
cancelButton.Click();
}
[TestMethod("WorkspacesSnapshot.CapturePackagedApps")]
[TestCategory("Workspaces Snapshot UI")]
public void TestCapturePackagedApplications()
{
OpenCalculator();
// OpenWindowsSettings();
Task.Delay(2000).Wait();
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(1000).Wait();
AttachSnapshotWindow();
var captureButton = Find<Button>("Capture");
captureButton.Click();
Task.Delay(3000).Wait();
// Verify captured windows by reading the temporary workspaces file as the ground truth.
var editorIO = new WorkspacesEditorIO();
var workspace = editorIO.ParseTempProject();
Assert.IsNotNull(workspace, "Workspace data should be deserialized.");
Assert.IsNotNull(workspace.Applications, "Workspace should contain a list of apps.");
bool isCalculatorFound = workspace.Applications.Any(app => app.AppPath.Contains("Calculator", StringComparison.OrdinalIgnoreCase));
// bool isSettingsFound = workspace.Applications.Any(app => app.AppPath.Contains("Settings", StringComparison.OrdinalIgnoreCase));
Assert.IsTrue(isCalculatorFound, "Calculator should be captured in the workspace data.");
// Assert.IsTrue(isSettingsFound, "Settings should be captured in the workspace data.");
// Cancel to clean up
AttachWorkspacesEditor();
Find<Button>("Cancel").Click();
Task.Delay(1000).Wait();
// Close test applications
CloseCalculator();
// CloseWindowsSettings();
}
}

View File

@@ -0,0 +1,253 @@
// 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;
using Microsoft.PowerToys.UITest;
namespace WorkspacesEditorUITest
{
public class WorkspacesUiAutomationBase : UITestBase
{
public WorkspacesUiAutomationBase()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
{
}
protected void CreateTestWorkspace(string name)
{
// Open notepad for capture
OpenNotepad();
Task.Delay(1000).Wait();
// Create workspace
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(500).Wait();
// Capture
AttachSnapshotWindow();
var captureButton = Find<Button>("Capture");
captureButton.Click();
Task.Delay(5000).Wait();
// Set name
var nameTextBox = Find<TextBox>("EditNameTextBox");
nameTextBox.SetText(name);
// Save
Find<Button>("Save Workspace").Click();
// Close notepad
CloseNotepad();
}
// DO NOT USE UNTIL FRAMEWORK AVAILABLE, CAN'T FIND BUTTON FOR SECOND LOOP
protected void ClearWorkspaces()
{
while (true)
{
try
{
var root = Find<Element>(By.AccessibilityId("WorkspacesItemsControl"));
var buttons = root.FindAll<Button>(By.AccessibilityId("MoreButton"));
Debug.WriteLine($"Found {buttons.Count} button");
var button = buttons[^1];
button.Click();
Task.Delay(500).Wait();
var remove = Find<Button>(By.Name("Remove"));
remove.Click();
Task.Delay(500).Wait();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
break;
}
}
}
protected void CreateWorkspaceWithApps()
{
// Open multiple test applications
OpenTestApplications();
Thread.Sleep(3000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save
Find<Button>("Save").Click();
Thread.Sleep(1000);
// Close test applications
CloseTestApplications();
}
protected void OpenTestApplications()
{
OpenNotepad();
// Could add more applications here
Thread.Sleep(1000);
}
protected void CloseTestApplications()
{
CloseNotepad();
}
protected void CloseWorkspacesEditor()
{
// Find and close the Workspaces Editor window
if (WindowHelper.IsWindowOpen("Workspaces Editor"))
{
var editorWindow = Find<Window>("Workspaces Editor");
editorWindow.Close();
Thread.Sleep(1000);
}
}
protected void ResetShortcutToDefault(Custom shortcutControl)
{
// Right-click on the shortcut control to open context menu
shortcutControl.Click(rightClick: true);
Thread.Sleep(500);
// Look for a "Reset to default" or similar option in the context menu
try
{
// Try to find various possible menu item texts for reset option
var resetOption = Find("Reset to default");
resetOption?.Click();
}
catch
{
try
{
// Try alternative text
var resetOption = Find("Reset");
resetOption?.Click();
}
catch
{
try
{
// Try another alternative
var resetOption = Find("Default");
resetOption?.Click();
}
catch
{
// If context menu doesn't have reset option, try keyboard shortcut
// ESC to close any open menus first
SendKeys(Key.Esc);
Thread.Sleep(200);
// Click on the control and try to clear it with standard shortcuts
shortcutControl.Click();
Thread.Sleep(200);
// Try Ctrl+A to select all, then Delete to clear
SendKeys(Key.Ctrl, Key.A);
Thread.Sleep(100);
SendKeys(Key.Delete);
Thread.Sleep(500);
}
}
}
}
protected void OpenNotepad()
{
var process = System.Diagnostics.Process.Start("notepad.exe");
Task.Delay(1000).Wait();
}
protected void CloseNotepad()
{
var processes = System.Diagnostics.Process.GetProcessesByName("notepad");
foreach (var process in processes)
{
try
{
process.Kill();
process.WaitForExit();
}
catch
{
// ignore
}
}
}
private void AttachPowertoySetting()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
protected void AttachWorkspacesEditor()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.Workspaces);
}
protected void AttachSnapshotWindow()
{
Task.Delay(5000).Wait();
this.Session.Attach("Snapshot Creator");
}
protected void OpenCalculator()
{
Process.Start("calc.exe");
Task.Delay(1000).Wait();
}
protected void CloseCalculator()
{
foreach (var process in Process.GetProcessesByName("CalculatorApp"))
{
process.Kill();
}
foreach (var process in Process.GetProcessesByName("Calculator"))
{
process.Kill();
}
}
protected void OpenWindowsSettings()
{
Process.Start(new ProcessStartInfo
{
FileName = "ms-settings:",
UseShellExecute = true,
});
Task.Delay(500).Wait();
}
protected void CloseWindowsSettings()
{
foreach (var process in Process.GetProcessesByName("SystemSettings"))
{
process.Kill();
}
}
}
}

View File

@@ -1,4 +1,4 @@
<ResourceDictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">

View File

@@ -70,6 +70,7 @@ REG_SETTING RegSettings[] = {
{ L"FontScale", SETTING_TYPE_DWORD, 0, &g_FontScale, static_cast<DOUBLE>(g_FontScale) },
{ L"ShowExpiredTime", SETTING_TYPE_BOOLEAN, 0, &g_ShowExpiredTime, static_cast<DOUBLE>(g_ShowExpiredTime) },
{ L"ShowTrayIcon", SETTING_TYPE_BOOLEAN, 0, &g_ShowTrayIcon, static_cast<DOUBLE>(g_ShowTrayIcon) },
// NOTE: AnimateZoom is misspelled, but since it is a user setting stored in the registry we must continue to misspell it.
{ L"AnimnateZoom", SETTING_TYPE_BOOLEAN, 0, &g_AnimateZoom, static_cast<DOUBLE>(g_AnimateZoom) },
{ L"TelescopeZoomOut", SETTING_TYPE_BOOLEAN, 0, &g_TelescopeZoomOut, static_cast<DOUBLE>(g_TelescopeZoomOut) },
{ L"SnapToGrid", SETTING_TYPE_BOOLEAN, 0, &g_SnapToGrid, static_cast<DOUBLE>(g_SnapToGrid) },

View File

@@ -323,7 +323,7 @@ void RestoreForeground()
// If the main window is not visible, move foreground to the next window.
if( !IsWindowVisible( g_hWndMain ) ) {
// Activate the next window by unhiding and hiding the main window.
// Activate the next window by showing and hiding the main window.
MoveWindow( g_hWndMain, 0, 0, 0, 0, FALSE );
ShowWindow( g_hWndMain, SW_SHOWNA );
ShowWindow( g_hWndMain, SW_HIDE );
@@ -5547,7 +5547,7 @@ LRESULT APIENTRY MainWndProc(
}
prevPt = currentPt;
// In liveDraw we an miss the mouse up
// In liveDraw we miss the mouse up
if( GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYERED) {
if((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) {

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.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,27 @@
// 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;
namespace Microsoft.CmdPal.Common.Services;
public interface IRunHistoryService
{
/// <summary>
/// Gets the run history.
/// </summary>
/// <returns>A list of run history items.</returns>
IReadOnlyList<string> GetRunHistory();
/// <summary>
/// Clears the run history.
/// </summary>
void ClearRunHistory();
/// <summary>
/// Adds a run history item.
/// </summary>
/// <param name="item">The run history item to add.</param>
void AddRunHistoryItem(string item);
}

View File

@@ -21,7 +21,11 @@ public partial class AppStateModel : ObservableObject
///////////////////////////////////////////////////////////////////////////
// STATE HERE
public RecentCommandsManager RecentCommands { get; private set; } = new();
// Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
// Make sure that any new types you add are added to JsonSerializationContext!
public RecentCommandsManager RecentCommands { get; set; } = new();
public List<string> RunHistory { get; set; } = [];
// END SETTINGS
///////////////////////////////////////////////////////////////////////////
@@ -86,7 +90,7 @@ public partial class AppStateModel : ObservableObject
{
foreach (var item in newSettings)
{
savedSettings[item.Key] = item.Value != null ? item.Value.DeepClone() : null;
savedSettings[item.Key] = item.Value?.DeepClone();
}
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.AppStateModel.Options);
@@ -121,20 +125,4 @@ public partial class AppStateModel : ObservableObject
// now, the settings is just next to the exe
return Path.Combine(directory, "state.json");
}
// [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
// private static readonly JsonSerializerOptions _serializerOptions = new()
// {
// WriteIndented = true,
// Converters = { new JsonStringEnumConverter() },
// };
// private static readonly JsonSerializerOptions _deserializerOptions = new()
// {
// PropertyNameCaseInsensitive = true,
// IncludeFields = true,
// AllowTrailingCommas = true,
// PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate,
// ReadCommentHandling = JsonCommentHandling.Skip,
// };
}

View File

@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;
namespace Microsoft.CmdPal.UI.ViewModels;
@@ -48,11 +49,6 @@ public partial class CommandBarViewModel : ObservableObject,
[ObservableProperty]
public partial PageViewModel? CurrentPage { get; set; }
[ObservableProperty]
public partial ObservableCollection<ContextMenuStackViewModel> ContextMenuStack { get; set; } = [];
public ContextMenuStackViewModel? ContextMenu => ContextMenuStack.LastOrDefault();
public CommandBarViewModel()
{
WeakReferenceMessenger.Default.Register<UpdateCommandBarMessage>(this);
@@ -101,18 +97,9 @@ public partial class CommandBarViewModel : ObservableObject,
SecondaryCommand = SelectedItem.SecondaryCommand;
if (SelectedItem.MoreCommands.Count() > 1)
{
ShouldShowContextMenu = true;
ContextMenuStack.Clear();
ContextMenuStack.Add(new ContextMenuStackViewModel(SelectedItem));
OnPropertyChanged(nameof(ContextMenu));
}
else
{
ShouldShowContextMenu = false;
}
ShouldShowContextMenu = SelectedItem.MoreCommands
.OfType<CommandContextItemViewModel>()
.Count() > 1;
OnPropertyChanged(nameof(HasSecondaryCommand));
OnPropertyChanged(nameof(SecondaryCommand));
@@ -139,8 +126,18 @@ public partial class CommandBarViewModel : ObservableObject,
public ContextKeybindingResult CheckKeybinding(bool ctrl, bool alt, bool shift, bool win, VirtualKey key)
{
var matchedItem = ContextMenu?.CheckKeybinding(ctrl, alt, shift, win, key);
return matchedItem != null ? PerformCommand(matchedItem) : ContextKeybindingResult.Unhandled;
var keybindings = SelectedItem?.Keybindings();
if (keybindings != null)
{
// Does the pressed key match any of the keybindings?
var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0);
if (keybindings.TryGetValue(pressedKeyChord, out var matchedItem))
{
return matchedItem != null ? PerformCommand(matchedItem) : ContextKeybindingResult.Unhandled;
}
}
return ContextKeybindingResult.Unhandled;
}
private ContextKeybindingResult PerformCommand(CommandItemViewModel? command)
@@ -152,10 +149,6 @@ public partial class CommandBarViewModel : ObservableObject,
if (command.HasMoreCommands)
{
ContextMenuStack.Add(new ContextMenuStackViewModel(command));
OnPropertyChanging(nameof(ContextMenu));
OnPropertyChanged(nameof(ContextMenu));
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(command.Command.Model, command.Model));
return ContextKeybindingResult.KeepOpen;
}
else
@@ -164,33 +157,6 @@ public partial class CommandBarViewModel : ObservableObject,
return ContextKeybindingResult.Hide;
}
}
public bool CanPopContextStack()
{
return ContextMenuStack.Count > 1;
}
public void PopContextStack()
{
if (ContextMenuStack.Count > 1)
{
ContextMenuStack.RemoveAt(ContextMenuStack.Count - 1);
}
OnPropertyChanging(nameof(ContextMenu));
OnPropertyChanged(nameof(ContextMenu));
}
public void ClearContextStack()
{
while (ContextMenuStack.Count > 1)
{
ContextMenuStack.RemoveAt(ContextMenuStack.Count - 1);
}
OnPropertyChanging(nameof(ContextMenu));
OnPropertyChanged(nameof(ContextMenu));
}
}
public enum ContextKeybindingResult

View File

@@ -2,12 +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 CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference<IPageContext> context) : CommandItemViewModel(new(contextItem), context)
public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference<IPageContext> context) : CommandItemViewModel(new(contextItem), context), IContextItemViewModel
{
private readonly KeyChord nullKeyChord = new(0, 0, 0);

View File

@@ -46,25 +46,27 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
public CommandViewModel Command { get; private set; }
public List<CommandContextItemViewModel> MoreCommands { get; private set; } = [];
public List<IContextItemViewModel> MoreCommands { get; private set; } = [];
IEnumerable<CommandContextItemViewModel> IContextMenuContext.MoreCommands => MoreCommands;
IEnumerable<IContextItemViewModel> IContextMenuContext.MoreCommands => MoreCommands;
public bool HasMoreCommands => MoreCommands.Count > 0;
private List<CommandContextItemViewModel> ActualCommands => MoreCommands.OfType<CommandContextItemViewModel>().ToList();
public bool HasMoreCommands => ActualCommands.Count > 0;
public string SecondaryCommandName => SecondaryCommand?.Name ?? string.Empty;
public CommandItemViewModel? PrimaryCommand => this;
public CommandItemViewModel? SecondaryCommand => HasMoreCommands ? MoreCommands[0] : null;
public CommandItemViewModel? SecondaryCommand => HasMoreCommands ? ActualCommands[0] : null;
public bool ShouldBeVisible => !string.IsNullOrEmpty(Name);
public List<CommandContextItemViewModel> AllCommands
public List<IContextItemViewModel> AllCommands
{
get
{
List<CommandContextItemViewModel> l = _defaultCommandContextItem == null ?
List<IContextItemViewModel> l = _defaultCommandContextItem == null ?
new() :
[_defaultCommandContextItem];
@@ -177,18 +179,29 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
if (more != null)
{
MoreCommands = more
.Where(contextItem => contextItem is ICommandContextItem)
.Select(contextItem => (contextItem as ICommandContextItem)!)
.Select(contextItem => new CommandContextItemViewModel(contextItem, PageContext))
.Select(item =>
{
if (item is ICommandContextItem contextItem)
{
return new CommandContextItemViewModel(contextItem, PageContext) as IContextItemViewModel;
}
else
{
return new SeparatorContextItemViewModel() as IContextItemViewModel;
}
})
.ToList();
}
// Here, we're already theoretically in the async context, so we can
// use Initialize straight up
MoreCommands.ForEach(contextItem =>
{
contextItem.SlowInitializeProperties();
});
MoreCommands
.OfType<CommandContextItemViewModel>()
.ToList()
.ForEach(contextItem =>
{
contextItem.SlowInitializeProperties();
});
if (!string.IsNullOrEmpty(model.Command?.Name))
{
@@ -323,19 +336,30 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
if (more != null)
{
var newContextMenu = more
.Where(contextItem => contextItem is ICommandContextItem)
.Select(contextItem => (contextItem as ICommandContextItem)!)
.Select(contextItem => new CommandContextItemViewModel(contextItem, PageContext))
.Select(item =>
{
if (item is CommandContextItem contextItem)
{
return new CommandContextItemViewModel(contextItem, PageContext) as IContextItemViewModel;
}
else
{
return new SeparatorContextItemViewModel() as IContextItemViewModel;
}
})
.ToList();
lock (MoreCommands)
{
ListHelpers.InPlaceUpdateList(MoreCommands, newContextMenu);
}
newContextMenu.ForEach(contextItem =>
{
contextItem.InitializeProperties();
});
newContextMenu
.OfType<CommandContextItemViewModel>()
.ToList()
.ForEach(contextItem =>
{
contextItem.InitializeProperties();
});
}
else
{
@@ -376,7 +400,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
lock (MoreCommands)
{
MoreCommands.ForEach(c => c.SafeCleanup());
MoreCommands.OfType<CommandContextItemViewModel>()
.ToList()
.ForEach(c => c.SafeCleanup());
MoreCommands.Clear();
}

View File

@@ -222,7 +222,7 @@ public partial class MainListPage : DynamicListPage,
{
nameMatch,
descriptionMatch,
isFallback ? 1 : 0, // Always give fallbacks a chance...
isFallback ? 1 : 0, // Always give fallbacks a chance
};
var max = scores.Max();
@@ -232,8 +232,7 @@ public partial class MainListPage : DynamicListPage,
// above "git" from "whatever"
max = max + extensionTitleMatch;
// ... but downweight them
var matchSomething = (max / (isFallback ? 3 : 1))
var matchSomething = max
+ (isAliasMatch ? 9001 : (isAliasSubstringMatch ? 1 : 0));
// If we matched title, subtitle, or alias (something real), then

View File

@@ -32,22 +32,6 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_title)}},
"size": "large"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_text)}},
"wrap": true
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_header)}},
"weight": "bolder",
"size": "default"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}},
"wrap": true
},
{
"type": "Input.Text",
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_label)}},
@@ -59,14 +43,11 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_header)}},
"weight": "bolder",
"size": "default"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}},
"wrap": true
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}},
"wrap": true,
"size": "small",
"isSubtle": true,
"spacing": "none"
},
{
"type": "Input.Text",
@@ -74,18 +55,16 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
"isRequired": true,
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_required)}},
"id": "DisplayName",
"placeholder": "My new extension"
"placeholder": "My new extension",
"spacing": "medium"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_header)}},
"weight": "bolder",
"size": "default"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}},
"wrap": true
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}},
"wrap": true,
"size": "small",
"isSubtle": true,
"spacing": "none"
},
{
"type": "Input.Text",
@@ -93,7 +72,16 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
"isRequired": true,
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_required)}},
"id": "OutputPath",
"placeholder": "C:\\users\\me\\dev"
"placeholder": "C:\\users\\me\\dev",
"spacing": "medium"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}},
"wrap": true,
"size": "small",
"isSubtle": true,
"spacing": "none"
}
],
"actions": [

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