Compare commits

...

53 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
bb34f71b62 Merge remote-tracking branch 'origin/main' into dev/duhowett/ws2025
# Conflicts:
#	.pipelines/v2/oneFuzz.yml
#	.pipelines/v2/release.yml
#	.pipelines/v2/templates/pipeline-ci-build.yml
#	.pipelines/v2/templates/pipeline-ui-tests-full-build.yml

Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-01 09:55:36 +00:00
PesBandi
addebb8126 [QuickAccent] Add capitalization for Superscript Latin Small Letter N (#46571)
## Summary of the Pull Request
Adds capitalization for ⁿ (U+207F Superscript Latin Small Letter N -> ᴺ
(U+1D3A Modifier Letter Capital N).
This technically isn't a Unicode case pair, however there isn't any
official capitalization for ⁿ and also where else would a user expect ᴺ
if not on Shift+N.
## PR Checklist

- [x] Closes: #26060
- [ ] **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
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** No need
- [x] **New binaries:** None
- [x] **Documentation updated:** No need

## 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
Manually tested
2026-04-01 11:13:59 +02:00
Gijs Reijn
3b6453c932 Improve DSC documentation (#42554)
<!-- 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 documentation enhances the DSC documentation by incorporating
reference documents and providing examples.

Closes #42552.

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

- [x] 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: Niels Laute <niels.laute@live.nl>
2026-04-01 16:56:40 +08:00
Niels Laute
2c97e04019 Add missing telem events (#46371)
<!-- 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
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-04-01 16:44:50 +08:00
Kai Tao
d27594c4f7 Settings: Fix settings arrow render (#46454)
<!-- 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
Existing: 
<img width="1201" height="150" alt="image"
src="https://github.com/user-attachments/assets/5e764875-bed8-45b5-97a8-60e5f475c296"
/>

A box icon for whatever up, down, left, right, they are not treated as
Glyph icon rendered, instead as normal text

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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

<img width="1273" height="714" alt="image"
src="https://github.com/user-attachments/assets/4d2ffa92-e0ca-44f4-8eda-9c4a7e05bbde"
/>
2026-04-01 10:29:53 +02:00
moooyo
ac28b1c29f Migrate ImageResizer to WinUI3 (#45288)
<!-- 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
Migrate WPF/WinForms utility to WinUI3 can give us many benefit.
1. Only WinUI3 support AOT. By this change, we can remove the blocker to
make imageResizer publish with AOT enabled to improve the performance.
Through the previous testing in CmdPal, it can improve about 1.5x to 3x
perf.
2. WinUI 3 provides a modern UI and makes sure that our experiences fit
in with the Windows 11 look and feel.
3. We can merge many redundant code to the same one and reduce more
codebase and installed size in the future if we successfully migrate all
remaining WPF/WinForms utility to WinUI3.


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

- [x] Closes: #46465
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **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
1. Set up the ImageResizer as the startup project.
2. Start in visual studio.

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:22:33 +08:00
Niels Laute
ee70b3ceca Fix for CmdPal settings page offset (#46568)
<!-- 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 is a weird bug in WinUI where a child panel gets a weird offset
when wrapped in a `ScrollViewer` and if it has a `MaxWidth`. The
solution is to wrap it with another panel 🤷

Top: with fix
Bottom: 0.98.1
<img width="1351" height="1259" alt="image"
src="https://github.com/user-attachments/assets/076ac947-58d1-4031-8be3-300a8511a636"
/>


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

- [x] Closes: #46238
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jiří Polášek <me@jiripolasek.com>
2026-04-01 05:46:06 +00:00
Copilot
7cc4a16aa7 Add setting to select network speed units in Command Palette (#46320)
## Summary of the Pull Request

Adds a unit selection setting to the Performance Monitor extension in
Command Palette that allows users to choose how network transmission
speed is displayed. The setting offers three options: bits per second
(Kbps/Mbps/Gbps, the default), bytes per second (KB/s/MB/s/GB/s), and
binary bytes per second using IEC prefixes (KiB/s/MiB/s/GiB/s).

## PR Checklist

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

## Detailed Description of the Pull Request / Additional comments

The following files were changed:

- **`NetworkSpeedUnit.cs`** (new): Enum defining the three supported
unit modes — `BitsPerSecond`, `BytesPerSecond`, and
`BinaryBytesPerSecond`.

- **`SettingsManager.cs`** (new): A `JsonSettingsManager` subclass for
the Performance Monitor extension. Defines a `ChoiceSetSetting` for
`NetworkSpeedUnit` (default: `BitsPerSecond`), stored under the
`performanceMonitor` namespace in the shared CmdPal `settings.json`.

- **`PerformanceWidgetsPage.cs`**:
- `PerformanceWidgetsPage` constructor now accepts `SettingsManager` and
passes it to `SystemNetworkUsageWidgetPage`.
- `SystemNetworkUsageWidgetPage` stores the settings manager and calls
`SpeedToString()`, which dispatches via a switch expression to
`FormatAsBitsPerSecString()` (e.g. `12.5 Mbps`),
`FormatAsBytesPerSecString()` (e.g. `1.6 MB/s`), or
`FormatAsBinaryBytesPerSecString()` (e.g. `1.5 MiB/s`) based on the
selected unit.

- **`PerformanceMonitorCommandsProvider.cs`**: Holds the
`SettingsManager`, passes it to both the list page and dock band via
`SetEnabledState()`, exposes `Settings` on the provider, and adds the
settings page to the command's `MoreCommands`. The crash-recovery
hardening from main (`ProviderCrashSentinel`,
`TryReactivateImmediately`, `SetDisabledState`, thread-safe locking) is
fully preserved and integrated with the settings manager.

- **`Resources.resw`**: Updated `Network_Speed_Unit_Setting_Title` and
`Network_Speed_Unit_Setting_Description` to neutral language; added
three choice label strings (`Network_Speed_Unit_BitsPerSec`,
`Network_Speed_Unit_BytesPerSec`,
`Network_Speed_Unit_BinaryBytesPerSec`). Also includes the
disabled-state strings merged from main.

## Validation Steps Performed

- Manually verified that the setting appears in the Performance Monitor
extension's settings page (accessible via the ⋯ context menu on the
Performance Monitor command item).
- Verified that network speed values display in Kbps/Mbps/Gbps by
default, switch to KB/s/MB/s/GB/s when the bytes option is selected, and
switch to KiB/s/MiB/s/GiB/s when the binary bytes option is selected.
- Setting persists across Command Palette restarts via the shared CmdPal
settings file.
- Verified that the crash-recovery re-enable flow correctly restores
pages with the settings manager wired up.

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[Command Palette] Allows network transmission speed units
to be switched between bits per second and bytes per
second</issue_title>
> <issue_description>### Description of the new feature / enhancement
> 
> Add a setting that allows network monitoring in the Command Palette
Dock to be displayed in bytes per second.
> 
> ### Scenario when this would be used?
> 
> In everyday usage, byte units are more commonly used.
> 
> ### Supporting information
> 
> _No response_</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> <comment_new><author>@niels9001</author><body>
> @copilot Can you add a setting for this?</body></comment_new>
> </comments>
> 


</details>



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

- Fixes microsoft/PowerToys#46271

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

 Let Copilot coding agent [set things up for
you](https://github.com/microsoft/PowerToys/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-03-31 20:25:03 -05:00
Salehnaz
8c8c99c382 Add subscript & superscript characters for Quick Accent #41922 (#45540)
<!-- 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 PR adds subscript and superscript characters to the "Special
Characters" set in Quick Accent. This addresses the request in issue
#41922, allowing users to easily type common mathematical notations
(e.g., x², H₂O) using the Quick Accent menu.

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

- [x] Closes: #41922
- [ ] **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

The following Unicode subscript and superscript variants were added to
[GetDefaultLetterKeySPECIAL](cci:1://file:///c:/Users/user/Desktop/salehcode/opensource/contribution/Linuxcontrib/PowerToys/src/modules/poweraccent/PowerAccent.Core/Languages.cs:185:8-236:9)
in
[Languages.cs](cci:7://file:///c:/Users/user/Desktop/salehcode/opensource/contribution/Linuxcontrib/PowerToys/src/modules/poweraccent/PowerAccent.Core/Languages.cs:0:0-0:0):

- **0**: ₀, ⁰, ⁾, ₎
- **9**: ₉, ⁹, ⁽, ₍
- **A**: ᵃ, ₐ
- **E**: ᵉ, ₑ
- **N**: ₙ
- **X**: ˣ, ₓ
- **Y**: ʸ
- **Z**: ᶻ
- **+**: ₌, ⁼ (equals signs)
- **-**: ₋, ⁻ (minus signs)
- *****: ˣ, ₓ (multiplication sign variants)

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Verified that the Unicode codepoints correspond to the correct subscript
and superscript glyphs. This change only modifies the static data list
used by Quick Accent and does not alter any logic.

---------

Co-authored-by: saleh <saleh@interlandtech.com>
2026-03-31 17:35:28 +08:00
Jiří Polášek
feae285c40 CmdPal: Revert focus restoration on Extensions settings page (#46642)
<!-- 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 PR reverts focus restoration to the previously selected item in the
list on the Extensions page in the Settings window, as it
unintentionally caused the wrong item to open on click.

This reverts commit cb9d54317a.

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

- [x] Closes: #46641
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-30 21:28:36 -05:00
Jiří Polášek
c34fb7f953 CmdPal: Harden ListViewModel fetch synchronization (#46429)
## Summary of the Pull Request

This PR improves fetching of list items in ListViewModel:
- Fixes _vmCache concurrency with copy-on-write cache publication.
- Preserves latest-fetch-wins behavior across overlapping RPC GetItems()
calls.
- Prevents stale or canceled fetches from publishing and makes them
abort promptly.
- Improves cancellation cleanup for abandoned item view models and
replaced token sources.
- Updates empty-state tracking to follow overlapping fetch activity
correctly.
- Reduces hot-path cache overhead by removing per-item cache locking and
full cache rebuilds.
- Adds guard against re-entry, to prevent situations like #46329:
- Defers ItemsChanged-triggered fetches raised during GetItems() until
the call unwinds;
- Uses a thread-local reentry guard so unrelated cross-thread fetches
are not delayed;
- Adds a regression test covering recursive GetItems() refresh behavior.
- Make sure we never invoke FetchItems on UI thread, and be loud in
debug when we are.

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

- [x] Closes: #46331
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-30 21:27:08 -05:00
Jiří Polášek
7d171a4428 Repository: Add .claude local settings to .gitignore (#46630)
## Summary of the Pull Request

This PR adds rule to .gitignore to exclude Claude AI local settings
files (settings.local.json under .claude directories) from version
control. This prevents accidental commits of local-only configuration.

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-30 21:02:01 -05:00
Jiří Polášek
2d037c4e91 CmdPal: Fix bad merge (#46639)
## Summary of the Pull Request

This PR fix merge inconsistency caused by concurrent changes
(0b7d780980 and
a022a9f024).

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-30 20:40:28 +00:00
Jiří Polášek
0a69c93b87 PowerToys Extension: Include deps in Microsoft.CmdPal.Ext.PowerToys slnf (#46136)
<!-- 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 PR updates Microsoft.CmdPal.Ext.PowerToys solution filter (slnf) to
include new dependencies.

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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: vanzue <vanzue@outlook.com>
2026-03-30 22:12:56 +02:00
Jiří Polášek
a022a9f024 CmdPal: Make Dock stay on top of all other windows (#46163)
<!-- 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 PR makes Dock stay on top of all other windows:

- Marks the Dock window as topmost when there is no full-screen window.
- Adds a new option that allows the user to disable this behavior.
- Adds a timer that polls the system API to determine whether a
full-screen window is present.

## Pictures? Pictures!

<img width="560" height="283" alt="image"
src="https://github.com/user-attachments/assets/55346005-2fac-4357-88bd-60c899565fac"
/>



https://github.com/user-attachments/assets/b81bff6d-4616-4d17-a1b0-063d254022ed


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

- [x] Closes: #46161
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-30 12:11:40 -05:00
Jiří Polášek
0b7d780980 CmdPal: improve full-screen detection (#45891)
## Summary of the Pull Request

This PR improves fullscreen detection for the Command Palette activation
shortcut, adds a separate "busy state" guard, surfaces live notification
state diagnostics in Settings, and provides an opt-in rapid-press
breakthrough to bypass suppression.

The existing fullscreen guard lumped D3D fullscreen, presentation mode,
and the heuristic QUNS_BUSY state into a single check. This made it
impossible to opt into guarding against only true fullscreen while
ignoring false positives from apps like NVIDIA overlay. This PR splits
those concerns, adds diagnostic visibility, and gives users an escape
hatch.

The problem with the detection is that QUNS_RUNNING_D3D_FULL_SCREEN is
intended for exclusive D3D full-screen apps (some games), but it
overlaps with QUNS_BUSY for other games and apps.

  - Splits the fullscreen guard into two separate settings
- IsWindowFullscreen() now only checks QUNS_RUNNING_D3D_FULL_SCREEN and
QUNS_PRESENTATION_MODE
    - New IsAppBusy() handles the heuristic QUNS_BUSY state separately
- New IgnoreShortcutWhenBusy setting (off by default) so users aren't
silently blocked by false positives
- Migrates from hand-written P/Invoke (NativeMethods.cs, deleted) to
CsWin32-generated bindings
- Adds a live InfoBar in Activation settings when the shortcut is
limited
- Polls SHQueryUserNotificationState every 2 seconds via DispatcherTimer
- Displays a warning describing which state is active (D3D fullscreen,
presentation mode, or busy)
- New GetUserNotificationState() in WindowHelper exposes the raw state
for the UI
  - Attributes QUNS_BUSY to known trigger apps
- New FindVisibleTriggerApps() enumerates windows by class name and
process name against a known-app list
- When NVIDIA Overlay (or other known apps) are detected, the InfoBar
message names the likely culprit
  - Adds an opt-in rapid-press breakthrough to bypass suppression
    - New AllowBreakthroughShortcut setting (off by default)
- Pressing the activation shortcut 3 times within 2 seconds overrides
the guard
- The suppression is automatically bypassed when the Command Palette is
visible - to allow dismissal

## Pictures? Pictures!

<img width="1112" height="769" alt="image"
src="https://github.com/user-attachments/assets/e1d64ace-cfb2-4ba1-a436-3d2d77c18c76"
/>

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

- [x] Closes: #45548
- [x] Closes: #41225
- [x] Closes: #42716
- [x] Closes: #45875
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-29 21:39:11 -05:00
Jiří Polášek
7685cd1226 CmdPal: Fix binary file corruption in Create Extension (#46490)
<!-- 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 PR fixes a problem with invisible icons in newly create Command
Palette extensions, when created through built-in command:

- Avoids modifying binary files during extension creation from the
template to prevent corruption.
- Refactors template expansion and physical extension creation into a
separate ExtensionTemplateService.
- Adds unit tests.

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

- [x] Closes: #46448
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-28 16:11:07 -05:00
Jiří Polášek
72bdfb073b CmdPal: Fix exception when converting calc result to different bases (#46176)
<!-- 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 PR fixes an exception that prevents showing result for big items:
- Uses `BigInteger` and custom base converter for secondary results menu
items.
- Adds extra error handler to prevent exception when creating a
secondary menu item from showing the main result to the user.
- Adds some unit tests for the new base converter.

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

- [x] Closes: #46167
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-28 16:09:06 -05:00
Jiří Polášek
96f97064be CmdPal: Fix type rename missed during merge gap (#46599)
<!-- 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

See title

Regressed by: 4cb3359314

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-28 13:33:31 +00:00
Jessica Dene Earley-Cha
75a0fe1d2f [CmdPal] Fix keyboard navigation double Tab stop in details panel (#46346)
<!-- 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

Fixes a keyboard accessibility issue in Command Palette where Tab
navigation would stop twice on container elements in the details panel
instead of navigating to details panel and then directly to interactive
controls. The items in the Details Panel aren't really interactable
anyways (the AllAppsPage & ClipBoard History just displays details)

This is part of the a11y bug batch.
User Impact:
Keyboard-only and assistive-technology users may experience confusion,
extra navigation effort, and reduced efficiency when interacting with
the Search Apps pane.

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

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

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

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



https://github.com/user-attachments/assets/0d5f0f20-040c-4d22-b769-3fe318c66697
2026-03-27 18:51:54 -05:00
Jiří Polášek
5792d32d32 CmdPal: Make Window Walker Close window command respect "Keep open" option (#45721)
## Summary of the Pull Request

This PR fixes the "Close window" command in the Window Walker built-in
extension and makes it obey the "Keep open after closing window" option
in the extension's settings.

It also adds a message that forces Window Walker to refresh its list of
windows, so that the closed window disappears from the list (and
similarly for end task commands).

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

- [x] Closes: #43256 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-27 18:46:36 -05:00
Jiří Polášek
4cb3359314 CmdPal: Plain text viewer and image viewer IContent (#43964)
<!-- 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 PR introduces new types of IContent:  
- Plain text content – simple and straightforward, with options to
switch between UI and monospace fonts and toggle word wrap.
- It's super safe to display any random text content without having to
worry about escaping the pesky markdown.
- Image viewer content – a more polished way to display images:  
- When placed in the ContentPage, the component automatically resizes to
fit the viewport, ensuring the entire image is visible at once.
- Images can be opened in a built-in mini-viewer that lets you view,
pan, and zoom without leaving the Command Palette. (Doing this directly
on the page proved to be a UX and development headache.) Fully
keyboard-controllable, so there’s no need to take your hands off the
keys.

## Pictures? Pictures!

Plain text content:

<img width="960" height="604" alt="image"
src="https://github.com/user-attachments/assets/a4ec36f3-2f7f-4a2a-a646-53056c512023"
/>

Image viewer content:

<img width="939" height="605" alt="image"
src="https://github.com/user-attachments/assets/c87f5726-8cd0-4015-b2d9-f1457fa1ec96"
/>



https://github.com/user-attachments/assets/915cd9d2-e4e3-4baf-8df6-6a328a3592ba


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

- [x] Closes: #41038
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-27 18:45:41 -05:00
Jiří Polášek
943c2a1ff5 CmdPal: Harden performance monitor and enable crash recovery (#46541)
<!-- 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 PR has two parts:

1. Hardens the managed paths in the Performance Monitor extension to
catch everything we can.
1. Adds crash recovery for cases where something fails in a way we
cannot handle.

## Pictures? Pictures!

<img width="1060" height="591" alt="image"
src="https://github.com/user-attachments/assets/ee91c610-32eb-4117-b9b8-6bbc40b9b426"
/>


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

- [x] Closes: #46522
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 18:39:26 -05:00
Jiří Polášek
f686155d9b CmdPal: Fix Dock context menu following active item in Command Bar (#46420)
## Summary of the Pull Request

This PR decouples the Dock control context menu from the item selected
in the Shell Page list / Command Bar.

- Adds a new property to the context menu to control whether it should
react to messages like `UpdateCommandBarMessage`
- The `DockControl` context menu no longer follows those messages

Additional changes:

- Ensures the context menu for Dock-selected search box position
reflects the Dock position (when the Dock is at the bottom, the search
box is also at the bottom)
- Consistently displays the dock item context menu even for items with a
single context menu action (instead of showing the Dock menu, which was
confusing)

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

- [x] Closes: #46404
- [x] Closes: #45892
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-27 18:38:09 -05:00
Jiří Polášek
9afa1ec71d CmdPal: Remove workaround for FontIconSource.CreateIconElement (#45790)
## Summary of the Pull Request

This PR removes workaround for FontIconSource.CreateIconElement icon not
being visible, as it looks like it was fixed in WASDK 1.8.4.

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

- [x] Closes: #45788
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-27 18:37:31 -05:00
Michael Jolley
4337f8e5ff CmdPal: Make settings and app state immutable (#46451)
## Summary
 
 This PR refactors CmdPal settings/state to be immutable end-to-end.
 
 ### Core changes
 - Convert model types to immutable records / init-only properties:
   - `SettingsModel`
   - `AppStateModel`
   - `ProviderSettings`
   - `DockSettings`
   - `RecentCommandsManager`
- supporting settings types (fallback/hotkey/alias/top-level
hotkey/history items, etc.)
- Replace mutable collections with immutable equivalents where
appropriate:
   - `ImmutableDictionary<,>`
   - `ImmutableList<>`
 - Move mutation flow to atomic service updates:
- `ISettingsService.UpdateSettings(Func<SettingsModel, SettingsModel>)`
   - `IAppStateService.UpdateState(Func<AppStateModel, AppStateModel>)`
- Update ViewModels/managers/services to use copy-on-write (`with`)
patterns instead of in-place
mutation.
- Update serialization context + tests for immutable model graph
compatibility.
 
 ## Why
 
Issue #46437 is caused by mutable shared state being updated from
different execution paths/threads,
leading to race-prone behavior during persistence/serialization.
 
By making settings/app state immutable and using atomic swap/update
patterns, we remove in-place
mutation and eliminate this class of concurrency bug.
 
 ## Validation
 
 - Built successfully:
   - `Microsoft.CmdPal.UI.ViewModels`
   - `Microsoft.CmdPal.UI`
   - `Microsoft.CmdPal.UI.ViewModels.UnitTests`
 - Updated unit tests for immutable update patterns.
 
 Fixes #46437

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:54:58 +00:00
Dave Rayment
ed47bceac2 [Settings] Fix Quick Accent language list being cropped and not reflowing contents (#45986)
## Summary of the Pull Request
The Character sets list on the Quick Accent settings page had a fixed
3-column layout. This caused two negative user experience issues that
this PR solves:

1. The contents were clipped. When the settings window was resized to be
smaller, the rightmost column(s) were cut off rather than reflowing.
2. The control displayed unnecessary horizontal and vertical scrollbars
nested within the page.

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

- [x] Closes: #45964
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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

I _believe_ the root cause is that the `ItemsWrapGrid` is contained
within the `ListView`'s built-in `ScrollViewer` which was able to expand
infinitely horizontally. During initial layout, the `MaxWidth` binding
to the parent `SettingsGroup`'s `ActualWidth` was respected and the
layout clamped the measurement appropriately, resulting in the correct
number of columns. However, on resize the unbounded `ScrollViewer`'s
infinite horizontal constraint took precedence and the reflow into fewer
columns never happened - the `ScrollViewer` never invalidated its
children's measure because, from its perspective, their available width
(infinite) had not changed. (I think - WinUI's layout and measure cycle
melts my brain.)

The fix required replacing the `MaxWidth` binding on `ItemsWrapGrid`
with a `SizeChanged` handler on the parent `SettingsCard`. The handler
reads the parent card's padding (58 pixels left and 44 pixels right) and
explicitly sets the language set `ListView.MaxWidth` accordingly. A
`Loaded` handler for the card ensures the correct layout on first
render.

The HorizontalScrollbar that caused the layout issue has been removed.

### Screenshots

3-column view:
<img width="1674" height="730" alt="image"
src="https://github.com/user-attachments/assets/890b0f4d-82ef-4147-a220-55941ae5ebc5"
/>


Resized to 2-columns:
<img width="1343" height="730" alt="image"
src="https://github.com/user-attachments/assets/d09aa295-9641-4c19-ab94-597e107614be"
/>

Resized to single-column:
<img width="726" height="823" alt="image"
src="https://github.com/user-attachments/assets/8baa507f-6e03-4f3c-a0ef-2bc2c59ed2e3"
/>


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

(All manual tests.)

Verified that:
- The 3-column layout is shown when there is enough space (this is the
maximum number of columns because of the page-level constraint.
- The 3-column layout correctly resizes to 2-column then to a
single-column layout when the window is resized, then back again when
made larger.
- The single-column list is shown when the Settings window is opened at
minimum size.
- Selection behaviour performed identically.
2026-03-27 17:48:58 +08:00
Zach Teutsch
df23546c0b [README] Update links and release notes for version 0.98.1 (#46539)
Title.
2026-03-26 15:07:30 -05:00
Alex Mihaiuc
25f44bc6d9 Emulate ZoomIt _mm_cvtsi128_si64 with _mm_storel_epi64 for x86 (#46529)
<!-- 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

Added this to ensure that ZoomIt can still build for 32 bit, even though
PowerToys doesn't ship such binaries.

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

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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

Ensured that the ZoomIt subproject compiles fine for 32 bit, this inside
the Sysinternals build process, also tested that the panoramic
screenshot functionality works. This change is isolated through the
preprocessor for AMD64 and ARM64.
2026-03-26 18:33:25 +01:00
Zach Teutsch
dc533fbdb3 [Keyboard Manager] Remove service enable/disable separate from module, fix editor clear shortcut (#46530)
Two changes to shortcuts here:
1) Remove toggling the KBM service with a shortcut or via command
palette
2) Ensure that shortcut is disabled for editor when shortcut is cleared
2026-03-26 10:20:50 -07:00
Zach Teutsch
c05ba4e2c8 [Keyboard Manager] Allow whitespace-only TextRemappings (#46510)
Title.

Closes #46453
2026-03-26 10:14:13 -07:00
Alex Mihaiuc
c83dd972a0 Add ZoomIt panoramic screenshot functionality (#46506)
<!-- 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 adds several ZoomIt features:

- Panorama / scrolling screenshots. The image reconstruction happens
based on visual cues and accuracy depends on scroll speed during the
capture.
- Text extraction when snipping.
- Break timer improvements (the break timer is now a screen saver,
offering the possibility to lock the computer).
- Functionality for standalone clip trimming is present but not exposed
in the XAML UI.


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

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

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

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

The build is successful both with PowerToys and as a standalone
Sysinternals executable. We ensured that the features behave as expected
and that no regressions are introduced.

---------

Co-authored-by: Mark Russinovich <markruss@ntdev.microsoft.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: markrussinovich <markrussinovich@users.noreply.github.com>
Co-authored-by: MarioHewardt <marioh@microsoft.com>
2026-03-26 13:21:43 +01:00
previously contributed as ttenbergen
c33053b26b Add missing Icelandic character í (VK_I) (#46424)
## Summary of the Pull Request
The Icelandic language definition was missing `í` entirely. This adds it
to `VK_I`.

Closes: Add missing Icelandic character í (VK_I) #46423

## Validation Steps Performed
Code review only. The change is a single line addition to a data-only
switch statement, consistent in structure with all other language
entries in the file. No binaries, pipelines, or localization files are
affected.
2026-03-26 16:22:44 +08:00
Niels Laute
2cf7d0f5ec Fix for duplicated dockbands (#46438)
<!-- 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

CommandProviderWrapper.PinDockBand had no duplicate check and it blindly
called .Add() on the settings list every time. This allowed the same
extension to be pinned multiple times to the dock. Once duplicates
existed in settings, LoadTopLevelCommands would faithfully re-create all
of them on every CommandsChanged reload, making edit-mode unpin appear
broken (unpin removes one, reload brings them all back).

 **Fix**
- CommandProviderWrapper.PinDockBand: Check all three band lists
(Start/Center/End) for an existing (ProviderId, CommandId) match before
adding; early-return if already pinned.
- CommandProviderWrapper.LoadTopLevelCommands: Track seen command IDs in
a HashSet when iterating AllPinnedCommands; skip duplicates so they
never materialize in the UI even if present in settings.
  
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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: Jiří Polášek <me@jiripolasek.com>
2026-03-25 12:29:55 -05:00
Copilot
7cb0f3861a CmdPal: Fix duplicate "Pin to..." context commands on top-level items (#46458)
Top-level commands on the home page showed duplicate pin context entries
— e.g., "Pin to Dock" appearing twice, or contradictory "Pin to Dock" +
"Unpin from dock" on the same command.

## Summary of the Pull Request

When the window is hidden while a sub-page is active,
`ShellViewModel.Receive(WindowHiddenMessage)` re-navigates to the root
page while `CurrentPage` still points to the sub-page.
`GetProviderContextForCommand` therefore returns the sub-page's
`ProviderContext` (which has `SupportsPinning = true`, `ProviderId =
<extension>`) for the new home-page `ListViewModel`.

With that wrong context, `UnsafeBuildAndInitMoreCommands` runs for each
`ListItemViewModel` wrapping a `TopLevelViewModel` and injects a second
set of pin commands — using the wrong provider's dock/top-level state —
on top of the ones `TopLevelViewModel.BuildContextMenu()` already
injected via `AddMoreCommandsToTopLevel` with the correct per-item
provider context.

**Changes:**

- **`ShellViewModel.cs` (root cause):** Move `isMainPage` evaluation
before `providerContext` is computed; use `CommandProviderContext.Empty`
when navigating to the root page, regardless of what `CurrentPage` is at
that moment.

  ```csharp
  var isMainPage = command == _rootPage;
  var providerContext = isMainPage
      ? CommandProviderContext.Empty
: _appHostService.GetProviderContextForCommand(message.Context,
CurrentPage.ProviderContext);
  ```

- **`CommandPaletteContextMenuFactory.cs` (defensive guard):** In
`UnsafeBuildAndInitMoreCommands`, bail early when the page context
supports pinning and `commandItem.Model.Unsafe is TopLevelViewModel`.
`BuildContextMenu()` on `TopLevelViewModel` already populates pin
commands via `AddMoreCommandsToTopLevel` with the item's own provider
context; adding them again here is always wrong regardless of how the
page context ended up. The `SupportsPinning` check is evaluated first to
skip the `.Unsafe` type-test in the common non-pinning case.

## PR Checklist

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

## Detailed Description of the Pull Request / Additional comments

The two fixes are complementary: the `ShellViewModel` change prevents
the wrong `ProviderContext` from ever reaching the home-page
`ListViewModel`; the `CommandPaletteContextMenuFactory` guard ensures
`TopLevelViewModel`-backed items are never double-processed even if some
other future code path sets the page context incorrectly.

The guard in
`CommandPaletteContextMenuFactory.UnsafeBuildAndInitMoreCommands` is
ordered so that `providerContext.SupportsPinning` (a cheap bool property
read) is evaluated before `commandItem.Model.Unsafe is
TopLevelViewModel`. This means the field access and type check are
skipped entirely for the common non-pinning case, addressing reviewer
feedback about unnecessary work on the hot path.

## Validation Steps Performed

Manually reproduced by opening CmdPal, navigating into a sub-page (e.g.,
"Search the Web"), closing the window, reopening, and verifying the
context menu for top-level commands shows exactly one "Pin to Dock" (or
"Unpin from dock") entry and at most one top-level pin action — no
duplicates or contradictory pairs.

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

📍 Connect Copilot coding agent with [Jira](https://gh.io/cca-jira-docs),
[Azure Boards](https://gh.io/cca-azure-boards-docs) or
[Linear](https://gh.io/cca-linear-docs) to delegate work to Copilot in
one click without leaving your project management tool.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: michaeljolley <1228996+michaeljolley@users.noreply.github.com>
2026-03-25 18:16:18 +01:00
Jiří Polášek
1106ac61f5 CmdPal: Guard Frame.GoBack to prevent crash (#46493)
## Summary of the Pull Request

This PR adds a guard to the shell page that prevents navigating back
with empty nav stack (which leads to exception, an ultimately to a
crash).

> You should check that the
[CanGoBack](https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.frame.cangoback?view=windows-app-sdk-1.8#microsoft-ui-xaml-controls-frame-cangoback)
property is true before you call GoBack. If you call GoBack while
CanGoBack is false, an exception is thrown.

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

- [x] Closes: #46492
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-25 12:12:11 -05:00
Niels Laute
107bf3882c Make KBM Editor pinnable (#46482) 2026-03-24 18:52:04 -05:00
Jiří Polášek
3f35b11cee CmdPal: Fix missing primary context command for late-bound items (#46131)
This PR does fix a bug where an item that starts with a null or empty
primary command never adds that primary action to the context menu after
the extension later provides a real command.

- Creates the default primary context-menu item lazily when `Command` or
`Command.Name` becomes available after `SlowInitializeProperties()`
- Refreshes `AllCommands`, `SecondaryCommand`, and `HasMoreCommands`
notifications for late command materialization and Show Details updates.
- Adds unit tests to cover the fixed issue.


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

- [x] Closes: #46129 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 17:24:04 -05:00
Jiří Polášek
1a9fcdcd1f CmdPal: Ensure DockWindow property cleans up after itself (#46303)
## Summary of the Pull Request

This PR improves cleanup of DockWindow after itself (since it can be
created and destroyed multiple times during app lifetime).

- Disposes its ViewModel (which it creates).
- Unregisters itself explicitly from WeakReferenceMessenger.
- Ensures that ShellPage closes the dock window when disposed and can't
spawn more.

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

- [x] Closes: #46302
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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: Zach Teutsch <88554871+zateutsch@users.noreply.github.com>
2026-03-24 17:24:00 -05:00
Jiří Polášek
6cf1d32e5a CmdPal: Hotfix commonCallbacks array initial count to prevent negative number (#46215)
## Summary of the Pull Request

This PR ensures that requested initial capacity is not a negative
number. `TopLevelCommandManager.TopLevelCommands` state is not is sync
with `globalFallbacks` here, plus `globalFallbacks` includes providers
that are disabled.

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

- [x] Closes: #46210
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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: Zach Teutsch <88554871+zateutsch@users.noreply.github.com>
Co-authored-by: Zachary Teutsch <zteutsch@microsoft.com>
2026-03-24 21:15:22 +00:00
Copilot
33497e59cc Update 'Ignore Shortcut' text to 'Ignore Conflict' for clarity (#46318)
## Summary of the Pull Request

Updates the checkbox label in the ShortcutConflictWindow (shown in the
Settings Dashboard when a hotkey conflict is detected) from "Ignore
shortcut" to "Ignore conflict". This change clarifies that checking the
box ignores the *conflict*, not the shortcut itself.

## PR Checklist

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

## Detailed Description of the Pull Request / Additional comments

**File changed:**
`src/settings-ui/Settings.UI/Strings/en-us/Resources.resw`

- Updated `ShortcutConflictWindow_IgnoreShortcut.Content` from `"Ignore
shortcut"` to `"Ignore conflict"`.

The checkbox appears in the ShortcutConflictWindow header row next to
the conflicting hotkey. The previous label "Ignore shortcut" was
ambiguous — it was unclear whether it meant "ignore the conflict" (allow
the shortcut to coexist) or "ignore the shortcut" (disable it). The new
label "Ignore conflict" makes the intent unambiguous.

## Validation Steps Performed

- Manually verified the resource string change in `Resources.resw`.
- Confirmed the `x:Uid="ShortcutConflictWindow_IgnoreShortcut"` binding
in `ShortcutConflictWindow.xaml` picks up the updated `.Content` value.

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>"Ignore Shortcut" is ambiguous; Suggest change to "Ignore
Conflict"</issue_title>
> <issue_description>### Description of the new feature / enhancement
> 
> When ignoring shortcut conflicts, it is unclear (to me) if the "Ignore
shortcut" check box "ignores the conflict" (good) or "ignores the
shortcut" ... so it can't be used (bad). A change to the wording to
"Ignore Conflict" would clarify the intent.
> 
> ### Scenario when this would be used?
> 
> Renaming avoids ambiguity and avoids (me) wasting time checking the
AIs for what the check box actually does.
> 
> ### Supporting information
> 
> Not needed (I think)</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



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

- Fixes microsoft/PowerToys#46296

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

📱 Kick off Copilot coding agent tasks wherever you are with [GitHub
Mobile](https://gh.io/cca-mobile-docs), available on iOS and Android.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-03-24 22:12:05 +01:00
Niels Laute
3d2f069c43 [CmdPal Dock] New pin UX (#46436)
<!-- 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 PR introduces a new dialog that gives you more control on how a
command gets pinned to the Dock.


![PinUX](https://github.com/user-attachments/assets/c270e93f-3fd9-42d5-aaa9-95c08efb8bac)


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

- [x] Closes: #46433
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 15:19:28 -05:00
Jiří Polášek
79d9b0e667 CmdPal: Keep TimeDateExtensionPage simple and update every time (#46396)
## Summary of the Pull Request

This PR updates Time & Date extension page to calculate current results
every time. This breaks possible infinite loop.

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

- [x] Closes: #46329
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 14:51:04 -05:00
Jiří Polášek
e2f611a7fc CmdPal: Prevent PgUp/PgDown from selecting non-internactive items (#46439)
## Summary of the Pull Request

This PR prevents paging (<kbd>PgUp</kbd>/<kbd>PgDown</kbd>) in the item
list from selecting non-interactive items (such as separators or section
headers).

It adds `FindSelectableIndex` and `FindSelectableIndexForPageNavigation`
helper methods, which locate the next interactive item in the given
direction. These methods are used to guard paging navigation and to
consolidate related logic elsewhere.

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

- [x] Closes: #46283 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 14:15:44 -05:00
Jiří Polášek
84ce86c573 CmdPal: Fix missing app context menu actions on the main page (#46293)
## Summary of the Pull Request

This PR fixes missing _Pin to ..._ menu items on app search result.

`MainListPage` reuses `AppListItem` instances from the All Apps page,
but their context menus were being built with the main page provider
context instead of the All Apps provider context.


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

- [x] Closes: #45848
- [x] Closes: #46285
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 13:30:52 -05:00
Jiří Polášek
735ea01a93 CmdPal: Fix dock popup XamlRoot handling on DockControl (#46305)
## Summary of the Pull Request

This PR handles situations when app can crash because a popup control is
being touched before XamlRoot is set.

- Registers message handlers in DockControl only while controls are
loaded.
- Guards the edit-mode TeachingTip until the dock is rooted.
- Sets XamlRoot for dock flyouts and tooltips before showing.
- Ensures that tooltips in DockItemControl are set only after XamlRoot
is explicitly set.
- Unregisteres messages and CenterItems_CollectionChanged when unloaded.

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

- [x] Closes: #46228
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 14:18:59 -04:00
Jiří Polášek
93f80f5f61 CmdPal: Reduce DockWindow backdrop switching and visual artifacts (#46309)
<!-- 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 PR reduces "blinking" of dock when (any) CmdPal settings changes.
It handles only backdrop, not icons.

- Avoids recreating the acrylic controller when the effective backdrop
parameters have not changed.
- Reuses the transparent backdrop instead of reassigning it during dock
refreshes.
- Cleans up backdrop controllers only when switching backdrop modes or
disposing the window.
- Removes obsolete dock-specific backdrop helper logic now handled
directly in DockWindow.


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

- [x] Closes: #46308
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 14:17:51 -04:00
Kai Tao
21f06b8bd0 Always On Top: The opacity should be able to configure the hotkey individually (#46410)
<!-- 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 pull request adds support for customizing the hotkeys used to
increase and decrease the opacity of pinned windows in the Always On Top
module.
Previously, these shortcuts were hardcoded to use the same modifiers as
the main pin hotkey.

With these changes, users can now independently configure the increase
and decrease opacity shortcuts via the settings UI, and the backend has
been updated to respect and store these new settings.

Another change: If window is not Always On Topped, the opacity change
take no effect, so we should not intercept, we should pass through to
minimize the impact.

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

- [X] Closes: #46391, #46387
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
<img width="1184" height="351" alt="image"
src="https://github.com/user-attachments/assets/5d20ffae-9f0c-4ce3-9d85-2ba1efea6301"
/>

<img width="336" height="244" alt="image"
src="https://github.com/user-attachments/assets/a78cc4a3-9eb3-49f1-bbb9-d6db37554e53"
/>

Verified locally that transparency hotkey will not intercept the normal
hotkey in window if Always on top not enabled

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
2026-03-24 14:02:36 -04:00
Dave Rayment
fa78cc8ea7 [OOBE] Ensure the Settings button on the SCOOBE page opens Home, not a blank page (#46203)
## Summary of the Pull Request
This PR fixes an issue where selecting the **Settings** button on the
What's New page for a new or upgraded installation of PowerToys would
show the Settings application but with a blank contents page instead of
the Home page.

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

- [x] Closes: #46202
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
When the current version of PowerToys doesn't match the
`last_version_run`, the What's New (SCOOBE) page is displayed. The
Settings page is loaded at the same time in a hidden state.

If the user selects the Settings button in the bottom-left of the What's
New page, `OpenSettingsItem_Tapped()` is called, which calls:

```csharp
App.OpenSettingsWindow();
```

This unhides the Settings window, but Settings has not navigated to an
initial page, resulting in a blank display.

The solution is to instead call:

```csharp
App.OpenSettingsWindow(ensurePageIsSelected: true);
```

## Validation Steps Performed

Manual tests, following the instructions given in the original issue,
i.e. setting the `last_version_run` JSON manually and retrying to
simulate the upgrade/new install.
2026-03-24 16:33:19 +00:00
Jessica Dene Earley-Cha
cb9d54317a Add ItemsRepeater focus restoration on Extensions settings page (part deux) (#45903)
<!-- 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

Fixes focus management in the Command Palette Extensions settings page.
After user moves through the extension list, when using Shift or
Shift+Tab to navigate into the extensions list, focus now properly
returns to the previously selected extension card instead of jumping to
the first item or end of the list.

This is part of the a11y bug batch.
User Impact:
Keyboard-only and assistive-technology users may lose context and
experience confusion due to unexpected focus movement, increasing
navigation effort and reducing usability of the Extensions page.

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

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

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

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



https://github.com/user-attachments/assets/2ebe25e4-015d-4804-8ae9-9a0107f39b8e

---------

Co-authored-by: Jiří Polášek <me@jiripolasek.com>
2026-03-24 09:00:05 -07:00
Jiří Polášek
5d0eabed15 CmdPal: Fix scroller scrolling and down glyph (#46447)
## Summary of the Pull Request

This PR restores scrolling to scroller (sic!) and updates a glyph on
scroll down button to caret down symbol.

Regressed in https://github.com/microsoft/PowerToys/pull/45873

## Pictures? Pictures!

Updated glyph:

<img width="170" height="59" alt="image"
src="https://github.com/user-attachments/assets/8b81f883-40e0-47b5-9d49-8523bd1b3cfb"
/>

Horizontal scrolling:


https://github.com/user-attachments/assets/a6b682e9-8439-4966-9837-c234fcc986d5

Vertical scrolling:


https://github.com/user-attachments/assets/166e14ed-374c-414b-9005-8cd7f60a48ba



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

## PR Checklist

- [x] Closes: #46441 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **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
2026-03-24 16:42:41 +01:00
Dustin L. Howett
6fdff7a10f this is easier than mucking around 2025-09-30 12:14:54 -05:00
Dustin L. Howett
b78fa62ba3 Move to Windows Server 2025 build agent images 2025-09-30 12:12:48 -05:00
337 changed files with 38938 additions and 7002 deletions

View File

@@ -339,6 +339,7 @@ SETAUTOHIDEBAR
WINDOWPOS
WINEVENTPROC
WORKERW
FULLSCREENAPP
# PowerRename metadata pattern abbreviations (used in tests and regex patterns)
DDDD

View File

@@ -223,6 +223,7 @@ Moq
mozilla
mspaint
Newtonsoft
NVIDIA
onenote
openai
Quickime

View File

@@ -1,9 +1,23 @@
accelscroll
acq
adr
Adr
APPLYTOSUBMENUS
AUDCLNT
axisdefer
axisflip
axisstart
bitmaps
BREAKSCR
BUFFERFLAGS
Cands
capturepath
centiseconds
CLASSW
coeffs
coprime
CREATEDIBSECTION
crossfades
Ctl
CTLCOLOR
CTLCOLORBTN
@@ -11,53 +25,163 @@ CTLCOLORDLG
CTLCOLOREDIT
CTLCOLORLISTBOX
CTrim
ddy
DFCS
dlg
dlu
DONTCARE
downsample
DRAWITEM
DRAWITEMSTRUCT
droppedband
Droppedband
dsum
dupburst
dupsegments
DWLP
EDITCONTROL
ENABLEHOOK
expectedlock
fastscroll
FDE
GETCHANNELRECT
GETCHECK
GETSCREENSAVEACTIVE
GETSCREENSAVETIMEOUT
GETTHUMBRECT
GIFs
hcfdark
hcfwhitespace
HTBOTTOMRIGHT
HTHEME
htol
ICONINFORMATION
ICONWARNING
Inj
jumprecover
KSDATAFORMAT
latestcapture
ldx
LEFTNOWORDWRAP
legitjumps
letterbox
lld
llu
llums
logfont
lookback
lround
lte
luma
Luma
manualdrop
maskcache
maxstep
MENUINFO
mic
middledrop
middledrop
MMRESULT
momentumreversal
mrate
mrt
narrowstrip
ncapture
ncm
nduplicates
niterations
nmonitor
NONCLIENTMETRICS
nonvle
nredraw
nstop
nsubpixel
ntorn
nvw
osc
OWNERDRAW
PBGRA
periodictrap
pfdc
playhead
pointerreuse
pwfx
Qpc
quantums
RCZOOMITSCR
realcapture
REFKNOWNFOLDERID
reposted
SCREENSAVE
SCRNSAVE
SCRNSAVECONFIGURE
scrnsavw
Scrnsavw
scrollramp
SCROLLSIZEGRIP
selftest
SETBARCOLOR
SETBKCOLOR
SETDEFID
SETRECT
SETSCREENSAVETIMEOUT
SHAREMODE
SHAREVIOLATION
shortlist
slowthenfast
smallstart
SNIPOCR
ssi
startuprecovery
stf
stopafter
STREAMFLAGS
submix
sxx
sxy
syy
tallportal
tci
tcsicmp
TEXTMETRIC
tinystep
tme
toolbars
TRACKMOUSEEVENT
Unadvise
vaddq
vaddvq
vandq
vcgeq
vdup
vld
vle
Vle
VLE
vminq
vmlal
vmull
vqaddq
vshrn
vsntprintf
vsnwprintf
vsync
WASAPI
WAVEFORMATEX
WAVEFORMATEXTENSIBLE
wfopen
wideportal
wil
WMU
wrapjump
wtol
WTSSESSION
WTSUn
XEnd
XStart
XStep
YInternal
ZMBS
zncc
Zncc
ZNCC

View File

@@ -35,6 +35,7 @@ Allman
ALLOWUNDO
ALLVIEW
ALPHATYPE
altkey
AModifier
amr
ANDSCANS
@@ -52,6 +53,7 @@ Apm
APPBARDATA
APPEXECLINK
APPLICATIONFRAMEHOST
apphost
appmanifest
APPMODEL
APPNAME
@@ -455,6 +457,9 @@ fdx
FErase
fesf
FFFF
fffffffzzz
FInc
FFh
Figma
FILEEXPLORER
FILEFLAGS
@@ -576,6 +581,7 @@ HIBYTE
hicon
HIDEWINDOW
Hif
highcontrast
highlightbackground
highlightthickness
HIMAGELIST
@@ -849,6 +855,8 @@ MDL
mdtext
mdtxt
mdwn
measuretool
mccs
meme
memicmp
MENUITEMINFO
@@ -892,6 +900,7 @@ MONITORINFOEX
MONITORINFOEXW
monitorinfof
MOUSEACTIVATE
mousecrosshairs
MOUSEDATA
MOUSEEVENTF
MOUSEHWHEEL
@@ -923,6 +932,7 @@ mstsc
msvcp
MT
MTND
multimonitor
MULTIPLEUSE
multizone
muxc
@@ -1016,6 +1026,9 @@ NORMALDISPLAY
NORMALUSER
NOSEARCH
NOSENDCHANGING
NOSIZE
notdefault
Nosize
NOTHOUSANDS
NOTICKS
NOTIFICATIONSDLL
@@ -1106,6 +1119,7 @@ PCIDLIST
PCTSTR
PCWSTR
PDEVMODE
PDFs
pdisp
PDLL
pdo
@@ -1154,6 +1168,10 @@ Pokedex
Popups
POPUPWINDOW
POSITIONITEM
powerocr
POWERBROADCAST
powerdisplay
POWERDISPLAYMODULEINTERFACE
POWERRENAMECONTEXTMENU
powerrenameinput
POWERRENAMETEST
@@ -1329,6 +1347,7 @@ rundll
rungameid
RUNLEVEL
runtimeclass
runtimeconfig
runtimepack
ruuid
rvm
@@ -1516,6 +1535,9 @@ STYLECHANGING
subkeys
sublang
SUBMODULEUPDATE
subresource
suntimes
swp
sug
Superbar
sut
@@ -1583,6 +1605,7 @@ TILEDWINDOW
TILLSON
timedate
timediff
timespan
timeutil
TITLEBARINFO
Titlecase
@@ -1733,6 +1756,7 @@ wdm
wdp
wdupenv
webbrowsers
webdev
webpage
websites
wekyb

3
.gitignore vendored
View File

@@ -361,6 +361,9 @@ src/common/Telemetry/*.etl
installer/*/*.wxs.bk
/src/modules/awake/.claude
# Claude AI local settings - local-only, not committed
**/.claude/settings.local.json
# Squad / Copilot agents — local-only, not committed
.squad/
.squad-workstream

View File

@@ -141,13 +141,13 @@
"WinUI3Apps\\PowerToys.EnvironmentVariables.dll",
"WinUI3Apps\\PowerToys.EnvironmentVariables.exe",
"PowerToys.ImageResizer.exe",
"PowerToys.ImageResizer.dll",
"WinUI3Apps\\PowerToys.ImageResizer.exe",
"WinUI3Apps\\PowerToys.ImageResizer.dll",
"WinUI3Apps\\PowerToys.ImageResizerCLI.exe",
"WinUI3Apps\\PowerToys.ImageResizerCLI.dll",
"PowerToys.ImageResizerExt.dll",
"PowerToys.ImageResizerContextMenu.dll",
"ImageResizerContextMenuPackage.msix",
"WinUI3Apps\\PowerToys.ImageResizerExt.dll",
"WinUI3Apps\\PowerToys.ImageResizerContextMenu.dll",
"WinUI3Apps\\ImageResizerContextMenuPackage.msix",
"PowerToys.LightSwitchModuleInterface.dll",
"LightSwitchService\\PowerToys.LightSwitchService.exe",

View File

@@ -15,6 +15,11 @@ parameters:
type: boolean
default: false
- name: codeSign
displayName: "Enable Code Signing"
type: boolean
default: true
- name: versionNumber
displayName: "Version Number"
type: string
@@ -89,7 +94,7 @@ extends:
versionNumber: ${{ parameters.versionNumber }}
publishArtifacts: false # 1ES PT handles publication for us.
official: true
codeSign: true
codeSign: ${{ parameters.codeSign }}
runTests: false
buildTests: false
signingIdentity:
@@ -130,7 +135,7 @@ extends:
name: SHINE-INT-L
os: windows
official: true
codeSign: true
codeSign: ${{ parameters.codeSign }}
signingIdentity:
serviceName: $(SigningServiceName)
appId: $(SigningAppId)

View File

@@ -59,6 +59,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.Repair_Fail</td>
<td>Triggered when the PowerToys repair operation fails to complete successfully due to an error.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.PowerToys_ModuleLaunchedFromSettings</td>
<td>Logs when a module editor is launched from the Settings UI or Quick Access panel, including which module was launched.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.Runner_Launch</td>
<td>Indicates when the PowerToys Runner is launched.</td>
@@ -392,6 +396,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.CmdPal_SessionDuration</td>
<td>Logs session metrics from launch to dismissal including duration, commands executed, pages visited, search queries, navigation depth, and errors.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.CmdPal_DockConfiguration</td>
<td>Tracks dock configuration at startup including whether the dock is enabled, dock side (top, bottom, left, or right), and the list of extension commands pinned to the start, center, and end sections of the dock.</td>
</tr>
</table>
### Crop And Lock
@@ -856,6 +864,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.KeyboardManager_KeyToShortcutRemapInvoked</td>
<td>Logs each instance of a key-to-shortcut remap being used.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.KeyboardManager_LaunchEditor</td>
<td>Logs when the Keyboard Manager editor is launched, including whether it was opened via hotkey or through settings.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.KeyboardManager_OSLevelShortcutRemapCount</td>
<td>Logs the total number of OS-level shortcut remaps configured by the user.</td>

View File

@@ -427,7 +427,7 @@
</Project>
</Folder>
<Folder Name="/modules/FileLocksmith/">
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49D456D3-F485-45AF-8875-45B44F193DDC" />
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49d456d3-f485-45af-8875-45b44f193ddc" />
<Project Path="src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj" Id="799a50d8-de89-4ed1-8ff8-ad5a9ed8c0ca" />
<Project Path="src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj" Id="57175ec7-92a5-4c1e-8244-e3fbca2a81de" />
<Project Path="src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj" Id="9d52fd25-ef90-4f9a-a015-91efc5daf54f" />
@@ -438,7 +438,7 @@
</Project>
</Folder>
<Folder Name="/modules/FileLocksmith/Tests/">
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="A1B2C3D4-E5F6-7890-1234-567890ABCDEF" />
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="a1b2c3d4-e5f6-7890-1234-567890abcdef" />
</Folder>
<Folder Name="/modules/Hosts/">
<Project Path="src/modules/Hosts/Hosts/Hosts.csproj">
@@ -464,13 +464,13 @@
</Folder>
<Folder Name="/modules/imageresizer/">
<Project Path="src/modules/imageresizer/dll/ImageResizerExt.vcxproj" Id="0b43679e-edfa-4da0-ad30-f4628b308b1b" />
<Project Path="src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj" Id="93b72a06-c8bd-484f-a6f7-c9f280b150bf" />
<Project Path="src/modules/imageresizer/ImageResizerLib/ImageResizerLib.vcxproj" Id="18b3db45-4ffe-4d01-97d6-5223feee1853" />
<Project Path="src/modules/imageresizer/ui/ImageResizerUI.csproj">
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
<Project Path="src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj" Id="93b72a06-c8bd-484f-a6f7-c9f280b150bf" />
<Project Path="src/modules/imageresizer/ImageResizerLib/ImageResizerLib.vcxproj" Id="18b3db45-4ffe-4d01-97d6-5223feee1853" />
<Project Path="src/modules/imageresizer/ui/ImageResizerUI.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
@@ -1027,7 +1027,10 @@
<File Path="src/modules/Workspaces/workspaces-common/WindowUtils.h" />
</Folder>
<Folder Name="/modules/ZoomIt/">
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b" />
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b">
<BuildDependency Project="src/modules/ZoomIt/ZoomItBreak/ZoomItBreak.vcxproj" />
</Project>
<Project Path="src/modules/ZoomIt/ZoomItBreak/ZoomItBreak.vcxproj" Id="94ba3051-c8d7-454a-9d46-1a7c78e228a3" />
<Project Path="src/modules/ZoomIt/ZoomItModuleInterface/ZoomItModuleInterface.vcxproj" Id="e4585179-2ac1-4d5f-a3ff-cfc5392f694c" />
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
</Folder>

View File

@@ -53,17 +53,17 @@ Go to the <a href="https://aka.ms/installPowerToys">PowerToys GitHub releases</a
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.99%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.98%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.0/PowerToysUserSetup-0.98.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.0/PowerToysUserSetup-0.98.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.0/PowerToysSetup-0.98.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.0/PowerToysSetup-0.98.0-arm64.exe
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-arm64.exe
| Description | Filename |
|----------------|----------|
| Per user - x64 | [PowerToysUserSetup-0.98.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.98.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.98.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.98.0-arm64.exe][ptMachineArm64] |
| Per user - x64 | [PowerToysUserSetup-0.98.1-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.98.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.98.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.98.1-arm64.exe][ptMachineArm64] |
</details>
@@ -106,7 +106,7 @@ There are <a href="https://learn.microsoft.com/windows/powertoys/install#communi
[![What's new image](doc/images/readme/Release-Banner.png)](https://github.com/microsoft/PowerToys/releases)
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.98.0).
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.98.1).
## 🛣️ Roadmap
We are planning some nice new features and improvements for the next releases PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.99][github-next-release-work]!

View File

@@ -1,83 +0,0 @@
# Settings resource
Manage the settings for PowerToys modules
## Commands
### ✨ Modules
List all the modules supported by the settings resource.
```shell
PS C:\> PowerToys.DSC.exe modules --resource 'settings'
AdvancedPaste
AlwaysOnTop
App
Awake
ColorPicker
CropAndLock
EnvironmentVariables
FancyZones
FileLocksmith
FindMyMouse
Hosts
ImageResizer
KeyboardManager
MeasureTool
MouseHighlighter
MouseJump
MousePointerCrosshairs
Peek
PowerAccent
PowerOCR
PowerRename
RegistryPreview
ShortcutGuide
Workspaces
ZoomIt
```
### 📄 Get
Get the settings for a specific module.
```shell
PS C:\> PowerToys.DSC.exe get --resource 'settings' --module EnvironmentVariables
{"settings":{"properties":{"LaunchAdministrator":{"value":true}},"name":"EnvironmentVariables","version":"1.0"}}
```
### 🖨️ Export
Export the settings for a specific module.
Settings resource Get and Export operation output states are identical.
```shell
PS C:\> PowerToys.DSC.exe get --resource 'settings' --module EnvironmentVariables
{"settings":{"properties":{"LaunchAdministrator":{"value":true}},"name":"EnvironmentVariables","version":"1.0"}}
```
### 📝 Set
Set the settings for a specific module. This command will update the settings to the specified values.
```shell
PS C:\> PowerToys.DSC.exe set --resource 'settings' --module Awake --input '{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000001-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"}}'
{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000001-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"}}
["settings"]
```
### 🧪 Test
Test the settings for a specific module. This command will check if the current settings match the desired state.
```shell
PS C:\> PowerToys.DSC.exe test --resource 'settings' --module Awake --input '{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000002-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"}}'
{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000001-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"},"_inDesiredState":false}
["settings"]
```
### 🛠️ Schema
Generates the JSON schema for the settings resource of a specific module.
```shell
PS C:\> PowerToys.DSC.exe schema --resource 'settings' --module Awake
{"$schema":"http://json-schema.org/draft-04/schema#","title":"SettingsResourceObjectOfAwakeSettings","type":"object","additionalProperties":false,"required":["settings"],"properties":{"_inDesiredState":{"type":["boolean","null"],"description":"Indicates whether an instance is in the desired state"},"settings":{"description":"The settings content for the module."}}}
PS E:\src\powertoys> PowerToys.DSC.exe schema --resource 'settings' --module Awake | Format-Json
```
### 📦 Manifest
Generates a manifest dsc resource JSON file for the specified module.
- If the module is not specified, it will generate a manifest for all modules.
- If the output directory is not specified, it will print the manifest to the console.
```shell
PS C:\> PowerToys.DSC.exe manifest --resource settings --module 'Awake' --outputDir "C:\manifests"
```

View File

@@ -0,0 +1,263 @@
---
description: DSC configuration reference for PowerToys AdvancedPaste module
ms.date: 10/18/2025
ms.topic: reference
title: AdvancedPaste Module
---
# AdvancedPaste Module
## Synopsis
Manages configuration for the Advanced Paste utility, which provides advanced clipboard operations and custom paste formats.
## Description
The `AdvancedPaste` module configures PowerToys Advanced Paste, a utility
that extends clipboard functionality with AI-powered transformations, custom
formats, and advanced paste options. It allows you to paste clipboard content
with transformations like plain text conversion, markdown formatting, JSON
formatting, and AI-based text processing.
## Properties
The AdvancedPaste module supports the following configurable properties:
### IsAdvancedAIEnabled
Controls whether AI-powered paste transformations are enabled.
**Type:** boolean
**Default:** `false`
**Description:** Enables AI-based clipboard transformations such as
summarization, translation, and content reformatting.
### PasteAsPlainTextHotkey
Sets the keyboard shortcut for pasting as plain text.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier.
- `ctrl` (boolean) - Ctrl key modifier.
- `alt` (boolean) - Alt key modifier.
- `shift` (boolean) - Shift key modifier.
- `code` (integer) - Virtual key code.
- `key` (string) - Key name.
**Default:** `Ctrl+Win+V`
### PasteAsMarkdownHotkey
Sets the keyboard shortcut for pasting as markdown.
**Type:** object (same structure as PasteAsPlainTextHotkey)
**Default:** `Ctrl+Win+Shift+V`
### PasteAsJsonHotkey
Sets the keyboard shortcut for pasting as JSON.
**Type:** object (same structure as PasteAsPlainTextHotkey)
### ShowCustomPreview
Controls whether a preview window is shown before pasting custom formats.
**Type:** boolean
**Default:** `true`
### CloseAfterLosingFocus
Controls whether the Advanced Paste window closes when it loses focus.
**Type:** boolean
**Default:** `false`
## Examples
### Example 1 - Enable AI features with direct execution
This example enables AI-powered paste transformations.
```powershell
$config = @{
settings = @{
properties = @{
IsAdvancedAIEnabled = $true
}
name = "AdvancedPaste"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module AdvancedPaste `
--input $config
```
### Example 2 - Configure paste hotkeys with DSC
This example customizes keyboard shortcuts for different paste formats.
```bash
dsc config set --file advancedpaste-hotkeys.dsc.yaml
```
```yaml
# advancedpaste-hotkeys.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Advanced Paste hotkeys
type: Microsoft.PowerToys/AdvancedPasteSettings
properties:
settings:
properties:
PasteAsPlainTextHotkey:
win: true
ctrl: true
alt: false
shift: false
code: 86
key: V
PasteAsMarkdownHotkey:
win: true
ctrl: true
alt: false
shift: true
code: 86
key: V
name: AdvancedPaste
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Advanced Paste with AI
enabled.
```bash
winget configure winget-advancedpaste.yaml
```
```yaml
# winget-advancedpaste.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Advanced Paste
type: Microsoft.PowerToys/AdvancedPasteSettings
properties:
settings:
properties:
IsAdvancedAIEnabled: true
ShowCustomPreview: true
CloseAfterLosingFocus: true
name: AdvancedPaste
version: 1.0
```
### Example 4 - Enable with custom preview settings
This example configures preview behavior for custom paste formats.
```bash
dsc config set --file advancedpaste-preview.dsc.yaml
```
```yaml
# advancedpaste-preview.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure preview settings
type: Microsoft.PowerToys/AdvancedPasteSettings
properties:
settings:
properties:
ShowCustomPreview: true
CloseAfterLosingFocus: false
name: AdvancedPaste
version: 1.0
```
### Example 5 - Test AI enablement
This example tests whether AI features are enabled.
```powershell
$desired = @{
settings = @{
properties = @{
IsAdvancedAIEnabled = $true
}
name = "AdvancedPaste"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
$result = PowerToys.DSC.exe test --resource 'settings' `
--module AdvancedPaste --input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "AI features are enabled"
} else {
Write-Host "AI features need to be enabled"
}
```
## Use cases
### Development workflow
Enable AI transformations for code snippets and documentation:
```yaml
resources:
- name: Developer paste settings
type: Microsoft.PowerToys/AdvancedPasteSettings
properties:
settings:
properties:
IsAdvancedAIEnabled: true
ShowCustomPreview: true
name: AdvancedPaste
version: 1.0
```
### Content creation
Configure for markdown and formatted text operations:
```yaml
resources:
- name: Content creator settings
type: Microsoft.PowerToys/AdvancedPasteSettings
properties:
settings:
properties:
ShowCustomPreview: true
CloseAfterLosingFocus: false
name: AdvancedPaste
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [ColorPicker Module][03] - System-wide color picker utility
- [PowerToys Advanced Paste Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./ColorPicker.md
[04]: https://learn.microsoft.com/windows/powertoys/advanced-paste

View File

@@ -0,0 +1,299 @@
---
description: DSC configuration reference for PowerToys AlwaysOnTop module
ms.date: 10/18/2025
ms.topic: reference
title: AlwaysOnTop Module
---
# AlwaysOnTop Module
## Synopsis
Manages configuration for the Always On Top utility, which pins windows to stay on top of other windows.
## Description
The `AlwaysOnTop` module configures PowerToys Always On Top, a utility that
allows you to pin any window to remain visible above all other windows. This
is useful for keeping reference materials, chat windows, or monitoring tools
visible while working with other applications.
## Properties
The AlwaysOnTop module supports the following configurable properties:
### Hotkey
Sets the keyboard shortcut to toggle Always On Top for the active window.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier.
- `ctrl` (boolean) - Ctrl key modifier.
- `alt` (boolean) - Alt key modifier.
- `shift` (boolean) - Shift key modifier.
- `code` (integer) - Virtual key code.
- `key` (string) - Key name.
**Default:** `Win+Ctrl+T`
### FrameEnabled
Controls whether a colored border is displayed around pinned windows.
**Type:** boolean
**Default:** `true`
### FrameThickness
Sets the thickness of the border around pinned windows (in pixels).
**Type:** integer
**Range:** `1` to `100`
**Default:** `5`
### FrameColor
Sets the color of the border around pinned windows.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#FF0000"` (red)
### FrameOpacity
Sets the opacity of the border (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `100`
### FrameAccentColor
Controls whether to use the Windows accent color for the frame.
**Type:** boolean
**Default:** `false`
### SoundEnabled
Controls whether a sound plays when toggling Always On Top.
**Type:** boolean
**Default:** `false`
### DoNotActivateOnGameMode
Controls whether Always On Top is automatically disabled during game mode.
**Type:** boolean
**Default:** `true`
### RoundCornersEnabled
Controls whether the frame has rounded corners.
**Type:** boolean
**Default:** `true`
### ExcludedApps
List of applications excluded from Always On Top functionality.
**Type:** string (newline-separated list of executable names)
## Examples
### Example 1 - Enable with default settings using direct execution
This example enables Always On Top with default border appearance.
```powershell
$config = @{
settings = @{
properties = @{
FrameEnabled = $true
FrameThickness = 5
FrameColor = "#FF0000"
FrameOpacity = 100
}
name = "AlwaysOnTop"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module AlwaysOnTop `
--input $config
```
### Example 2 - Customize frame appearance with DSC
This example configures a custom border color and thickness.
```bash
dsc config set --file alwaysontop-appearance.dsc.yaml
```
```yaml
# alwaysontop-appearance.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Customize Always On Top frame
type: Microsoft.PowerToys/AlwaysOnTopSettings
properties:
settings:
properties:
FrameEnabled: true
FrameThickness: 8
FrameColor: "#0078D7"
FrameOpacity: 80
RoundCornersEnabled: true
name: AlwaysOnTop
version: 1.0
```
### Example 3 - Configure with accent color using WinGet
This example installs PowerToys and configures Always On Top to use the
Windows accent color.
```bash
winget configure winget-alwaysontop.yaml
```
```yaml
# winget-alwaysontop.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Always On Top
type: Microsoft.PowerToys/AlwaysOnTopSettings
properties:
settings:
properties:
FrameEnabled: true
FrameAccentColor: true
FrameThickness: 6
SoundEnabled: true
name: AlwaysOnTop
version: 1.0
```
### Example 4 - Disable for gaming
This example ensures Always On Top is disabled during game mode.
```yaml
# alwaysontop-gaming.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure for gaming
type: Microsoft.PowerToys/AlwaysOnTopSettings
properties:
settings:
properties:
DoNotActivateOnGameMode: true
name: AlwaysOnTop
version: 1.0
```
### Example 5 - Minimal border configuration
This example configures a subtle, thin border.
```powershell
$config = @{
settings = @{
properties = @{
FrameEnabled = $true
FrameThickness = 2
FrameOpacity = 50
RoundCornersEnabled = true
}
name = "AlwaysOnTop"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module AlwaysOnTop --input $config
```
### Example 6 - Exclude specific applications
This example excludes certain applications from Always On Top.
```yaml
# alwaysontop-exclusions.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Exclude apps from Always On Top
type: Microsoft.PowerToys/AlwaysOnTopSettings
properties:
settings:
properties:
ExcludedApps: |
Game.exe
FullScreenApp.exe
name: AlwaysOnTop
version: 1.0
```
## Use cases
### Reference material
Keep documentation or reference windows visible:
```yaml
resources:
- name: Reference window settings
type: Microsoft.PowerToys/AlwaysOnTopSettings
properties:
settings:
properties:
FrameEnabled: true
FrameColor: "#00FF00"
FrameOpacity: 60
name: AlwaysOnTop
version: 1.0
```
### Monitoring dashboards
Pin monitoring tools and dashboards:
```yaml
resources:
- name: Monitoring settings
type: Microsoft.PowerToys/AlwaysOnTopSettings
properties:
settings:
properties:
FrameEnabled: true
FrameAccentColor: true
SoundEnabled: false
name: AlwaysOnTop
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [FancyZones Module][03] - Window layout manager
- [PowerToys Always On Top Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./FancyZones.md
[04]: https://learn.microsoft.com/windows/powertoys/always-on-top

279
doc/dsc/modules/App.md Normal file
View File

@@ -0,0 +1,279 @@
---
description: DSC configuration reference for PowerToys App module (general settings)
ms.date: 10/18/2025
ms.topic: reference
title: App module
---
# App Module
## Synopsis
Manages general PowerToys application settings, including utility enable/disable states, startup behavior, and theme preferences.
## Description
The `App` module controls global PowerToys settings that affect the entire
application. This includes which utilities are enabled, whether PowerToys
runs at startup, the application theme, and other general preferences.
Unlike other modules that configure specific utilities, the App module
manages PowerToys-wide settings and the enabled state of all utilities.
## Properties
The App module supports the following configurable properties:
### Enabled
Controls which PowerToys utilities are enabled or disabled.
**Type:** Object
**Properties:**
- `AdvancedPaste` (boolean) - Enable/disable Advanced Paste utility.
- `AlwaysOnTop` (boolean) - Enable/disable Always On Top utility.
- `Awake` (boolean) - Enable/disable Awake utility.
- `ColorPicker` (boolean) - Enable/disable Color Picker utility.
- `CropAndLock` (boolean) - Enable/disable Crop And Lock utility.
- `EnvironmentVariables` (boolean) - Enable/disable Environment Variables
utility.
- `FancyZones` (boolean) - Enable/disable FancyZones utility.
- `FileLocksmith` (boolean) - Enable/disable File Locksmith utility.
- `FindMyMouse` (boolean) - Enable/disable Find My Mouse utility.
- `Hosts` (boolean) - Enable/disable Hosts File Editor utility.
- `ImageResizer` (boolean) - Enable/disable Image Resizer utility.
- `KeyboardManager` (boolean) - Enable/disable Keyboard Manager utility.
- `MeasureTool` (boolean) - Enable/disable Measure Tool utility.
- `MouseHighlighter` (boolean) - Enable/disable Mouse Highlighter utility.
- `MouseJump` (boolean) - Enable/disable Mouse Jump utility.
- `MousePointerCrosshairs` (boolean) - Enable/disable Mouse Pointer
Crosshairs utility.
- `Peek` (boolean) - Enable/disable Peek utility.
- `PowerAccent` (boolean) - Enable/disable Power Accent utility.
- `PowerOCR` (boolean) - Enable/disable Power OCR utility.
- `PowerRename` (boolean) - Enable/disable Power Rename utility.
- `RegistryPreview` (boolean) - Enable/disable Registry Preview utility.
- `ShortcutGuide` (boolean) - Enable/disable Shortcut Guide utility.
- `Workspaces` (boolean) - Enable/disable Workspaces utility.
- `ZoomIt` (boolean) - Enable/disable ZoomIt utility.
### startup
Controls whether PowerToys starts automatically when you sign in.
**Type:** boolean
**Default:** `true`
### run_elevated
Controls whether PowerToys runs with administrator privileges.
**Type:** boolean
**Default:** `false`
### theme
Sets the application theme.
**Type:** string
**Allowed values:** `"light"`, `"dark"`, `"system"`
**Default:** `"system"`
## Examples
### Example 1 - Enable specific utilities with direct execution
This example enables only FancyZones, PowerRename, and ColorPicker while
disabling all others.
```powershell
$config = @{
settings = @{
properties = @{
Enabled = @{
AdvancedPaste = $false
AlwaysOnTop = $false
Awake = $false
ColorPicker = $true
CropAndLock = $false
EnvironmentVariables = $false
FancyZones = $true
FileLocksmith = $false
FindMyMouse = $false
Hosts = $false
ImageResizer = $false
KeyboardManager = $false
MeasureTool = $false
MouseHighlighter = $false
MouseJump = $false
MousePointerCrosshairs = $false
Peek = $false
PowerAccent = $false
PowerOCR = $false
PowerRename = $true
RegistryPreview = $false
ShortcutGuide = $false
Workspaces = $false
ZoomIt = $false
}
}
name = "App"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module App --input $config
```
### Example 2 - Configure startup and theme with DSC
This example configures PowerToys to run at startup with elevated privileges
and use dark theme.
```bash
dsc config set --file app-config.dsc.yaml
```
```yaml
# app-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure PowerToys general settings
type: Microsoft.PowerToys/AppSettings
properties:
settings:
properties:
startup: true
run_elevated: true
theme: dark
name: App
version: 1.0
```
### Example 3 - Enable all utilities with WinGet
This example installs PowerToys and enables all available utilities.
```bash
winget configure winget-enable-all.yaml
```
```yaml
# winget-enable-all.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Enable all utilities
type: Microsoft.PowerToys/AppSettings
properties:
settings:
properties:
Enabled:
AdvancedPaste: true
AlwaysOnTop: true
Awake: true
ColorPicker: true
CropAndLock: true
EnvironmentVariables: true
FancyZones: true
FileLocksmith: true
FindMyMouse: true
Hosts: true
ImageResizer: true
KeyboardManager: true
MeasureTool: true
MouseHighlighter: true
MouseJump: true
MousePointerCrosshairs: true
Peek: true
PowerAccent: true
PowerOCR: true
PowerRename: true
RegistryPreview: true
ShortcutGuide: true
Workspaces: true
ZoomIt: true
name: App
version: 1.0
```
### Example 4 - Test if specific utilities are enabled
This example tests whether FancyZones and PowerRename are enabled.
```powershell
$desired = @{
settings = @{
properties = @{
Enabled = @{
FancyZones = $true
PowerRename = $true
}
}
name = "App"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
$result = PowerToys.DSC.exe test --resource 'settings' --module App `
--input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "FancyZones and PowerRename are enabled"
} else {
Write-Host "Configuration needs to be updated"
}
```
### Example 5 - Individual resource for each utility
This example shows enabling utilities individually, which provides better granularity for complex configurations.
```powershell
# Get current state
PowerToys.DSC.exe get --resource 'settings' --module App
# Enable individual utilities
$config = @{
settings = @{
properties = @{
Enabled = @{
FancyZones = $true
}
}
name = "App"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module App --input $config
```
### Example 6 - Get schema for App module
This example retrieves the complete JSON schema for the App module.
```powershell
PowerToys.DSC.exe schema --resource 'settings' --module App | `
ConvertFrom-Json | ConvertTo-Json -Depth 10
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [Awake][03]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./Awake.md

342
doc/dsc/modules/Awake.md Normal file
View File

@@ -0,0 +1,342 @@
---
description: DSC configuration reference for PowerToys Awake module
ms.date: 10/18/2025
ms.topic: reference
title: Awake Module
---
# Awake Module
## Synopsis
Manages configuration for the Awake utility, which keeps your computer awake without changing power settings.
## Description
The `Awake` module configures PowerToys Awake, a utility that prevents your
computer from going to sleep or turning off the display. This is useful
during installations, presentations, or any scenario where you need to
temporarily override power settings without permanently changing them.
Awake supports multiple modes including indefinite keep-awake, timed
intervals, and scheduled expiration. The display can be kept on or allowed
to turn off independently of the system sleep state.
## Properties
The Awake module supports the following configurable properties:
### keepDisplayOn
Controls whether the display remains on while Awake is active.
**Type:** boolean
**Default:** `true`
**Description:** When `true`, prevents the display from turning off. When
`false`, only prevents system sleep while allowing the display to turn off
according to power settings.
### mode
Specifies the Awake operating mode.
**Type:** integer
**Allowed values:**
- `0` - Off (Awake is disabled).
- `1` - Keep awake indefinitely.
- `2` - Keep awake for a timed interval.
- `3` - Keep awake until a specific date/time.
**Default:** `0`
### intervalHours
Number of hours to keep the system awake (used when mode is `2`).
**Type:** integer
**Range:** `0` to `999`
**Default:** `0`
### intervalMinutes
Number of minutes to keep the system awake (used when mode is `2`).
**Type:** integer
**Range:** `0` to `59`
**Default:** `1`
### expirationDateTime
The date and time when Awake should automatically disable (used when mode is `3`).
**Type:** string (ISO 8601 datetime format)
**Format:** `"YYYY-MM-DDTHH:mm:ss.fffffffzzz"`
**Example:** `"2025-12-31T23:59:59.0000000-08:00"`
### customTrayTimes
Custom time intervals displayed in the system tray context menu for quick activation.
**Type:** object
**Description:** A dictionary of custom time presets for quick access. Keys
are display names, values are time specifications.
## Examples
### Example 1 - Keep awake indefinitely with display on
This example configures Awake to keep the system and display awake
indefinitely using direct execution.
```powershell
$config = @{
settings = @{
properties = @{
keepDisplayOn = $true
mode = 1
}
name = "Awake"
version = "0.0.1"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $config
```
### Example 2 - Keep awake for 2 hours with DSC
This example configures a timed keep-awake period.
```bash
dsc config set --file awake-timed.dsc.yaml
```
```yaml
# awake-timed.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Awake for 2 hours
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 2
intervalHours: 2
intervalMinutes: 0
name: Awake
version: 0.0.1
```
### Example 3 - Keep awake until specific time with WinGet
This example configures Awake to stay active until a specific date and time.
```bash
winget configure winget-awake-scheduled.yaml
```
```yaml
# winget-awake-scheduled.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Keep awake until end of workday
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 3
expirationDateTime: "2025-10-18T17:00:00.0000000-07:00"
name: Awake
version: 0.0.1
```
### Example 4 - Disable Awake
This example disables Awake using direct execution.
```powershell
$config = @{
settings = @{
properties = @{
mode = 0
}
name = "Awake"
version = "0.0.1"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $config
```
### Example 5 - Keep system awake but allow display to sleep
This example keeps the system awake while allowing the display to turn off.
```bash
dsc config set --file awake-system-only.dsc.yaml
```
```yaml
# awake-system-only.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Keep system awake only
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: false
mode: 1
name: Awake
version: 0.0.1
```
### Example 6 - Configure for presentation (4 hours)
This example configures Awake for a presentation scenario using WinGet.
```bash
winget configure presentation-mode.yaml
```
```yaml
# presentation-mode.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Enable Awake for presentation
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 2
intervalHours: 4
intervalMinutes: 0
name: Awake
version: 0.0.1
```
### Example 7 - Test current configuration
This example tests whether Awake is configured for indefinite keep-awake.
```powershell
$desired = @{
settings = @{
properties = @{
keepDisplayOn = $true
mode = 1
}
name = "Awake"
version = "0.0.1"
}
} | ConvertTo-Json -Depth 10 -Compress
$result = PowerToys.DSC.exe test --resource 'settings' --module Awake --input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "Awake is configured for indefinite keep-awake"
} else {
Write-Host "Awake configuration differs from desired state"
}
```
### Example 8 - Get current Awake configuration
This example retrieves the current Awake settings.
```powershell
PowerToys.DSC.exe get --resource 'settings' --module Awake | ConvertFrom-Json | ConvertTo-Json -Depth 10
```
### Example 9 - Get Awake schema
This example retrieves the JSON schema for Awake module properties.
```powershell
PowerToys.DSC.exe schema --resource 'settings' --module Awake | ConvertFrom-Json | ConvertTo-Json -Depth 10
```
## Use cases
### Development and builds
Keep the system awake during long-running builds or installations:
```yaml
resources:
- name: Keep awake during build
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
mode: 2
intervalHours: 8
intervalMinutes: 0
keepDisplayOn: false
name: Awake
version: 0.0.1
```
### Presentations and demos
Ensure the system stays awake during presentations:
```yaml
resources:
- name: Presentation mode
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
mode: 1
keepDisplayOn: true
name: Awake
version: 0.0.1
```
### Scheduled maintenance
Keep the system awake until a specific time for scheduled tasks:
```yaml
resources:
- name: Maintenance window
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
mode: 3
expirationDateTime: "2025-10-19T02:00:00.0000000-07:00"
keepDisplayOn: false
name: Awake
version: 0.0.1
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [PowerRename][03]
- [PowerToys Awake Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./PowerRename.md
[04]: https://learn.microsoft.com/windows/powertoys/awake

View File

@@ -0,0 +1,307 @@
---
description: DSC configuration reference for PowerToys ColorPicker module
ms.date: 10/18/2025
ms.topic: reference
title: ColorPicker Module
---
# ColorPicker Module
## Synopsis
Manages configuration for the Color Picker utility, a system-wide color selection and identification tool.
## Description
The `ColorPicker` module configures PowerToys Color Picker, a utility that allows you to pick colors from anywhere on your screen and copy them to the clipboard in various formats. It's useful for designers, developers, and anyone working with colors.
## Properties
The ColorPicker module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to activate the color picker.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier.
- `ctrl` (boolean) - Ctrl key modifier.
- `alt` (boolean) - Alt key modifier.
- `shift` (boolean) - Shift key modifier.
- `code` (integer) - Virtual key code.
- `key` (string) - Key name.
**Default:** `Win+Shift+C`
### changecursor
Controls whether the cursor changes when the color picker is activated.
**Type:** boolean
**Default:** `true`
### copiedcolorrepresentation
Sets the default color format copied to the clipboard.
**Type:** string
**Allowed values:** `"HEX"`, `"RGB"`, `"HSL"`, `"HSV"`, `"CMYK"`, `"HSB"`,
`"HSI"`, `"HWB"`, `"NCol"`
**Default:** `"HEX"`
### activationaction
Controls the action when the color picker activation shortcut is pressed.
**Type:** integer
**Allowed values:**
- `0` - Open color picker and show editor
- `1` - Open color picker only
- `2` - Open editor only
**Default:** `0`
### showColorName
Controls whether color names are displayed in the color picker.
**Type:** boolean
**Default:** `false`
### VisibleColorFormats
Defines which color formats are visible in the picker interface.
**Type:** object with boolean properties for each format:
- `HEX` (boolean)
- `RGB` (boolean)
- `HSL` (boolean)
- `HSV` (boolean)
- `CMYK` (boolean)
- `HSB` (boolean)
- `HSI` (boolean)
- `HWB` (boolean)
- `NCol` (boolean)
- `Decimal` (boolean)
## Examples
### Example 1 - Configure default color format with direct execution
This example sets the default copied color format to RGB.
```powershell
$config = @{
settings = @{
properties = @{
copiedcolorrepresentation = "RGB"
}
name = "ColorPicker"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ColorPicker --input $config
```
### Example 2 - Configure activation behavior with DSC
This example configures the color picker to open directly without the
editor.
```bash
dsc config set --file colorpicker-activation.dsc.yaml
```
```yaml
# colorpicker-activation.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Color Picker activation
type: Microsoft.PowerToys/ColorPickerSettings
properties:
settings:
properties:
activationaction: 1
changecursor: true
copiedcolorrepresentation: HEX
name: ColorPicker
version: 1.0
```
### Example 3 - Install and configure for web development with WinGet
This example installs PowerToys and configures Color Picker for web
developers.
```bash
winget configure winget-colorpicker-webdev.yaml
```
```yaml
# winget-colorpicker-webdev.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Color Picker for web development
type: Microsoft.PowerToys/ColorPickerSettings
properties:
settings:
properties:
copiedcolorrepresentation: HEX
showColorName: true
changecursor: true
VisibleColorFormats:
HEX: true
RGB: true
HSL: true
HSV: false
CMYK: false
name: ColorPicker
version: 1.0
```
### Example 4 - Configure visible formats
This example enables only HEX, RGB, and HSL formats.
```bash
dsc config set --file colorpicker-formats.dsc.yaml
```
```yaml
# colorpicker-formats.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure visible color formats
type: Microsoft.PowerToys/ColorPickerSettings
properties:
settings:
properties:
VisibleColorFormats:
HEX: true
RGB: true
HSL: true
HSV: false
CMYK: false
HSB: false
HSI: false
HWB: false
NCol: false
Decimal: false
name: ColorPicker
version: 1.0
```
### Example 5 - Configure for graphic design
This example configures Color Picker for graphic designers with CMYK support.
```powershell
$config = @{
settings = @{
properties = @{
copiedcolorrepresentation = "CMYK"
showColorName = $true
VisibleColorFormats = @{
HEX = $true
RGB = $true
CMYK = $true
HSL = $true
HSV = $true
}
}
name = "ColorPicker"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ColorPicker --input $config
```
### Example 6 - Test configuration
This example tests whether HEX is the default format.
```powershell
$desired = @{
settings = @{
properties = @{
copiedcolorrepresentation = "HEX"
}
name = "ColorPicker"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
$result = PowerToys.DSC.exe test --resource 'settings' --module ColorPicker `
--input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "HEX is the default format"
}
```
## Use cases
### Web development
Configure for HTML/CSS color codes:
```yaml
resources:
- name: Web developer settings
type: Microsoft.PowerToys/ColorPickerSettings
properties:
settings:
properties:
copiedcolorrepresentation: HEX
VisibleColorFormats:
HEX: true
RGB: true
HSL: true
name: ColorPicker
version: 1.0
```
### Print design
Configure for CMYK color space:
```yaml
resources:
- name: Print designer settings
type: Microsoft.PowerToys/ColorPickerSettings
properties:
settings:
properties:
copiedcolorrepresentation: CMYK
showColorName: true
name: ColorPicker
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [ImageResizer][03]
- [PowerToys Color Picker Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./ImageResizer.md
[04]: https://learn.microsoft.com/windows/powertoys/color-picker

View File

@@ -0,0 +1,195 @@
---
description: DSC configuration reference for PowerToys CropAndLock module
ms.date: 10/18/2025
ms.topic: reference
title: CropAndLock Module
---
# CropAndLock Module
## Synopsis
Manages configuration for the Crop And Lock utility, which crops and locks portions of windows.
## Description
The `CropAndLock` module configures PowerToys Crop And Lock, a utility that allows you to crop a portion of any window and keep it visible as a thumbnail. This is useful for monitoring specific parts of applications, keeping reference information visible, or focusing on particular UI elements.
## Properties
The CropAndLock module supports the following configurable properties:
### Hotkey
Sets the keyboard shortcut to activate Crop And Lock for the active window.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier.
- `ctrl` (boolean) - Ctrl key modifier.
- `alt` (boolean) - Alt key modifier.
- `shift` (boolean) - Shift key modifier.
- `code` (integer) - Virtual key code.
- `key` (string) - Key name.
**Default:** `Win+Ctrl+Shift+T`
### ReparentHotkey
Sets the keyboard shortcut to change the parent window of a cropped thumbnail.
**Type:** object (same structure as Hotkey)
### ThumbnailOpacity
Sets the opacity of cropped thumbnails (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `100`
## Examples
### Example 1 - Configure basic settings with direct execution
This example sets the Crop And Lock hotkey and thumbnail opacity.
```powershell
$config = @{
settings = @{
properties = @{
ThumbnailOpacity = 90
}
name = "CropAndLock"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module CropAndLock --input $config
```
### Example 2 - Configure hotkeys with DSC
This example configures custom hotkeys for cropping and reparenting.
```bash
dsc config set --file cropandlock-hotkeys.dsc.yaml
```
```yaml
# cropandlock-hotkeys.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Crop And Lock hotkeys
type: Microsoft.PowerToys/CropAndLockSettings
properties:
settings:
properties:
Hotkey:
win: true
ctrl: true
shift: true
alt: false
code: 84
key: T
ThumbnailOpacity: 85
name: CropAndLock
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Crop And Lock.
```bash
winget configure winget-cropandlock.yaml
```
```yaml
# winget-cropandlock.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Crop And Lock
type: Microsoft.PowerToys/CropAndLockSettings
properties:
settings:
properties:
ThumbnailOpacity: 75
name: CropAndLock
version: 1.0
```
### Example 4 - Semi-transparent thumbnails
This example configures thumbnails to be semi-transparent for overlay use.
```yaml
# cropandlock-transparent.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Semi-transparent thumbnails
type: Microsoft.PowerToys/CropAndLockSettings
properties:
settings:
properties:
ThumbnailOpacity: 60
name: CropAndLock
version: 1.0
```
## Use cases
### Monitoring dashboards
Keep portions of monitoring tools visible:
```yaml
resources:
- name: Monitoring configuration
type: Microsoft.PowerToys/CropAndLockSettings
properties:
settings:
properties:
ThumbnailOpacity: 80
name: CropAndLock
version: 1.0
```
### Reference material
Crop and display reference information:
```yaml
resources:
- name: Reference display
type: Microsoft.PowerToys/CropAndLockSettings
properties:
settings:
properties:
ThumbnailOpacity: 95
name: CropAndLock
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [MouseJump][03]
- [PowerToys Crop And Lock Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./MouseJump.md
[04]: https://learn.microsoft.com/windows/powertoys/crop-and-lock

View File

@@ -0,0 +1,193 @@
---
description: DSC configuration reference for PowerToys EnvironmentVariables module
ms.date: 10/18/2025
ms.topic: reference
title: EnvironmentVariables Module
---
# EnvironmentVariables Module
## Synopsis
Manages configuration for the Environment Variables utility, a quick editor for system and user environment variables.
## Description
The `EnvironmentVariables` module configures PowerToys Environment Variables, a utility that provides an enhanced interface for viewing and editing Windows environment variables. It offers a more user-friendly alternative to the standard Windows environment variable editor.
## Properties
The EnvironmentVariables module supports the following configurable properties:
### LaunchAdministrator
Controls whether the Environment Variables editor launches with administrator privileges by default.
**Type:** boolean
**Default:** `false`
**Description:** When enabled, the editor will always attempt to launch with elevated permissions, allowing editing of system-wide environment variables.
## Examples
### Example 1 - Enable admin launch with direct execution
This example configures the Environment Variables editor to always launch with admin rights.
```powershell
$config = @{
settings = @{
properties = @{
LaunchAdministrator = $true
}
name = "EnvironmentVariables"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module EnvironmentVariables --input $config
```
### Example 2 - Configure with DSC
This example enables administrator launch through DSC configuration.
```bash
dsc config set --file environmentvariables-config.dsc.yaml
```
```yaml
# environmentvariables-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Environment Variables editor
type: Microsoft.PowerToys/EnvironmentVariablesSettings
properties:
settings:
properties:
LaunchAdministrator: true
name: EnvironmentVariables
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Environment Variables for
admin launch.
```bash
winget configure winget-envvars.yaml
```
```yaml
# winget-envvars.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Environment Variables
type: Microsoft.PowerToys/EnvironmentVariablesSettings
properties:
settings:
properties:
LaunchAdministrator: true
name: EnvironmentVariables
version: 1.0
```
### Example 4 - Standard user mode
This example configures for standard user access (no elevation).
```bash
dsc config set --file envvars-user.dsc.yaml
```
```yaml
# envvars-user.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: User-level Environment Variables
type: Microsoft.PowerToys/EnvironmentVariablesSettings
properties:
settings:
properties:
LaunchAdministrator: false
name: EnvironmentVariables
version: 1.0
```
### Example 5 - Test admin launch configuration
This example tests whether admin launch is enabled.
```powershell
$desired = @{
settings = @{
properties = @{
LaunchAdministrator = $true
}
name = "EnvironmentVariables"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
$result = PowerToys.DSC.exe test --resource 'settings' --module EnvironmentVariables --input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "Admin launch is enabled"
}
```
## Use cases
### System administration
Configure for system-wide environment variable management:
```yaml
resources:
- name: Admin configuration
type: Microsoft.PowerToys/EnvironmentVariablesSettings
properties:
settings:
properties:
LaunchAdministrator: true
name: EnvironmentVariables
version: 1.0
```
### Development workstations
Configure for user-level variable management:
```yaml
resources:
- name: Developer configuration
type: Microsoft.PowerToys/EnvironmentVariablesSettings
properties:
settings:
properties:
LaunchAdministrator: false
name: EnvironmentVariables
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [Hosts][03]
- [PowerToys Environment Variables Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./Hosts.md
[04]: https://learn.microsoft.com/windows/powertoys/environment-variables

View File

@@ -0,0 +1,545 @@
---
description: DSC configuration reference for PowerToys FancyZones module
ms.date: 10/18/2025
ms.topic: reference
title: FancyZones Module
---
# FancyZones Module
## Synopsis
Manages configuration for the FancyZones utility, a window layout manager that arranges and snaps windows into efficient layouts.
## Description
The `FancyZones` module configures PowerToys FancyZones, a window manager
utility that helps organize windows into custom layouts called zones.
FancyZones allows you to create multiple zone layouts for different displays
and quickly snap windows into position using keyboard shortcuts or mouse
actions.
This module controls activation methods, window behavior, zone appearance, editor settings, and other FancyZones preferences.
## Properties
The FancyZones module supports the following configurable properties:
### fancyzones_shiftDrag
Controls whether holding Shift while dragging a window activates zone snapping.
**Type:** boolean
**Default:** `true`
### fancyzones_mouseSwitch
Controls whether moving a window across monitors triggers zone selection.
**Type:** boolean
**Default:** `false`
### fancyzones_overrideSnapHotkeys
Controls whether FancyZones overrides the Windows Snap hotkeys (Win+Arrow keys).
**Type:** boolean
**Default:** `false`
### fancyzones_moveWindowsAcrossMonitors
Controls whether moving windows between monitors is enabled.
**Type:** boolean
**Default:** `false`
### fancyzones_moveWindowsBasedOnPosition
Controls whether windows move to zones based on cursor position rather than window position.
**Type:** boolean
**Default:** `false`
### fancyzones_overlappingZonesAlgorithm
Determines the algorithm used when multiple zones overlap.
**Type:** integer
**Allowed values:**
- `0` - Smallest zone
- `1` - Largest zone
- `2` - Positional (based on cursor/window position)
**Default:** `0`
### fancyzones_displayOrWorkAreaChange_moveWindows
Controls whether windows are moved to fit when display or work area changes.
**Type:** boolean
**Default:** `false`
### fancyzones_zoneSetChange_flashZones
Controls whether zones flash briefly when the zone set changes.
**Type:** boolean
**Default:** `false`
### fancyzones_zoneSetChange_moveWindows
Controls whether windows are automatically moved when the zone set changes.
**Type:** boolean
**Default:** `false`
### fancyzones_appLastZone_moveWindows
Controls whether windows are moved to their last known zone when reopened.
**Type:** boolean
**Default:** `true`
### fancyzones_openWindowOnActiveMonitor
Controls whether newly opened windows appear on the currently active monitor.
**Type:** boolean
**Default:** `false`
### fancyzones_spanZonesAcrossMonitors
Controls whether zones can span across multiple monitors.
**Type:** boolean
**Default:** `false`
### fancyzones_makeDraggedWindowTransparent
Controls whether dragged windows become transparent to show zones underneath.
**Type:** boolean
**Default:** `true`
### fancyzones_zoneColor
Sets the color of zone areas.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Example:** `"#0078D7"`
**Default:** `"#0078D7"`
### fancyzones_zoneBorderColor
Sets the color of zone borders.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Example:** `"#FFFFFF"`
**Default:** `"#FFFFFF"`
### fancyzones_zoneHighlightColor
Sets the highlight color when a zone is activated.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Example:** `"#0078D7"`
**Default:** `"#0078D7"`
### fancyzones_highlightOpacity
Sets the opacity of zone highlights (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `50`
### fancyzones_editorHotkey
Sets the keyboard shortcut to open the FancyZones editor.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Shift+~`
### fancyzones_windowSwitching
Controls whether window switching with arrow keys is enabled.
**Type:** boolean
**Default:** `true`
### fancyzones_nextTabHotkey
Sets the keyboard shortcut to switch to the next tab/window in a zone.
**Type:** object (same structure as fancyzones_editorHotkey)
### fancyzones_prevTabHotkey
Sets the keyboard shortcut to switch to the previous tab/window in a zone.
**Type:** object (same structure as fancyzones_editorHotkey)
### fancyzones_excludedApps
List of applications excluded from FancyZones snapping.
**Type:** string (newline-separated list of executable names)
**Example:** `"Notepad.exe\nCalc.exe"`
## Examples
### Example 1 - Enable basic zone snapping with direct execution
This example enables Shift-drag zone snapping and mouse-based monitor switching.
```powershell
$config = @{
settings = @{
properties = @{
fancyzones_shiftDrag = $true
fancyzones_mouseSwitch = $true
}
name = "FancyZones"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module FancyZones --input $config
```
### Example 2 - Configure window movement behavior with DSC
This example configures how windows behave when displays or zones change.
```bash
dsc config set --file fancyzones-window-behavior.dsc.yaml
```
```yaml
# fancyzones-window-behavior.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure FancyZones window behavior
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_displayOrWorkAreaChange_moveWindows: true
fancyzones_zoneSetChange_moveWindows: true
fancyzones_appLastZone_moveWindows: true
fancyzones_moveWindowsAcrossMonitors: true
name: FancyZones
version: 1.0
```
### Example 3 - Customize zone appearance with WinGet
This example installs PowerToys and configures custom zone colors and
opacity.
```bash
winget configure winget-fancyzones-appearance.yaml
```
```yaml
# winget-fancyzones-appearance.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Customize FancyZones appearance
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_zoneColor: "#2D2D30"
fancyzones_zoneBorderColor: "#007ACC"
fancyzones_zoneHighlightColor: "#007ACC"
fancyzones_highlightOpacity: 75
fancyzones_makeDraggedWindowTransparent: true
name: FancyZones
version: 1.0
```
### Example 4 - Override Windows Snap hotkeys
This example configures FancyZones to replace Windows default snap
functionality.
```bash
dsc config set --file fancyzones-snap-override.dsc.yaml
```
```yaml
# fancyzones-snap-override.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Override Windows Snap
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_overrideSnapHotkeys: true
fancyzones_moveWindowsBasedOnPosition: true
name: FancyZones
version: 1.0
```
### Example 5 - Configure editor hotkey
This example changes the FancyZones editor hotkey to Ctrl+Shift+Alt+F.
```powershell
$config = @{
settings = @{
properties = @{
fancyzones_editorHotkey = @{
win = $false
ctrl = $true
alt = $true
shift = $true
code = 70 # F key
key = "F"
}
}
name = "FancyZones"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module FancyZones --input $config
```
### Example 6 - Exclude applications from zone snapping
This example configures FancyZones to ignore specific applications.
```yaml
# fancyzones-exclusions.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Exclude apps from FancyZones
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_excludedApps: |
Notepad.exe
Calculator.exe
mspaint.exe
name: FancyZones
version: 1.0
```
### Example 7 - Multi-monitor configuration
This example configures FancyZones for optimal multi-monitor workflow.
```bash
dsc config set --file fancyzones-multimonitor.dsc.yaml
```
```yaml
# fancyzones-multimonitor.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Multi-monitor FancyZones setup
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_shiftDrag: true
fancyzones_mouseSwitch: true
fancyzones_moveWindowsAcrossMonitors: true
fancyzones_spanZonesAcrossMonitors: false
fancyzones_openWindowOnActiveMonitor: true
fancyzones_displayOrWorkAreaChange_moveWindows: true
name: FancyZones
version: 1.0
```
### Example 8 - Complete FancyZones configuration with WinGet
This example shows a comprehensive FancyZones setup with installation.
```bash
winget configure winget-fancyzones-complete.yaml
```
```yaml
# winget-fancyzones-complete.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Enable FancyZones
type: Microsoft.PowerToys/AppSettings
properties:
settings:
properties:
Enabled:
FancyZones: true
name: App
version: 1.0
- name: Configure FancyZones
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
# Activation
fancyzones_shiftDrag: true
fancyzones_mouseSwitch: true
fancyzones_overrideSnapHotkeys: false
# Window behavior
fancyzones_moveWindowsAcrossMonitors: true
fancyzones_moveWindowsBasedOnPosition: false
fancyzones_displayOrWorkAreaChange_moveWindows: true
fancyzones_zoneSetChange_moveWindows: false
fancyzones_appLastZone_moveWindows: true
# Appearance
fancyzones_makeDraggedWindowTransparent: true
fancyzones_zoneColor: "#0078D7"
fancyzones_zoneBorderColor: "#FFFFFF"
fancyzones_zoneHighlightColor: "#0078D7"
fancyzones_highlightOpacity: 50
# Multi-monitor
fancyzones_openWindowOnActiveMonitor: true
fancyzones_spanZonesAcrossMonitors: false
name: FancyZones
version: 1.0
```
### Example 9 - Test FancyZones configuration
This example tests whether FancyZones is configured for multi-monitor use.
```powershell
$desired = @{
settings = @{
properties = @{
fancyzones_moveWindowsAcrossMonitors = $true
fancyzones_openWindowOnActiveMonitor = $true
}
name = "FancyZones"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
$result = PowerToys.DSC.exe test --resource 'settings' --module FancyZones `
--input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "FancyZones is configured for multi-monitor"
} else {
Write-Host "FancyZones configuration needs updating"
}
```
### Example 10 - Get FancyZones schema
This example retrieves the complete JSON schema for FancyZones properties.
```powershell
PowerToys.DSC.exe schema --resource 'settings' --module FancyZones | `
ConvertFrom-Json | ConvertTo-Json -Depth 10
```
## Use cases
### Development workflow
Configure FancyZones for efficient development with IDE, browser, and terminal windows:
```yaml
resources:
- name: Developer layout
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_shiftDrag: true
fancyzones_overrideSnapHotkeys: true
fancyzones_appLastZone_moveWindows: true
name: FancyZones
version: 1.0
```
### Presentation mode
Optimize window management for presentations and screen sharing:
```yaml
resources:
- name: Presentation layout
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_openWindowOnActiveMonitor: true
fancyzones_highlightOpacity: 30
fancyzones_makeDraggedWindowTransparent: false
name: FancyZones
version: 1.0
```
### Home office setup
Configure for docking/undocking laptop scenarios:
```yaml
resources:
- name: Home office configuration
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_displayOrWorkAreaChange_moveWindows: true
fancyzones_moveWindowsAcrossMonitors: true
fancyzones_appLastZone_moveWindows: true
name: FancyZones
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [Peek][03]
- [PowerToys FancyZones Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./Peek.md
[04]: https://learn.microsoft.com/windows/powertoys/fancyzones

View File

@@ -0,0 +1,179 @@
---
description: DSC configuration reference for PowerToys FileLocksmith module
ms.date: 10/18/2025
ms.topic: reference
title: FileLocksmith Module
---
# FileLocksmith Module
## Synopsis
Manages configuration for the File Locksmith utility, which identifies
processes that are locking files or folders.
## Description
The `FileLocksmith` module configures PowerToys File Locksmith, a Windows
shell extension that helps identify which processes are using (locking)
specific files or folders. It integrates with the Windows Explorer context
menu for easy access.
## Properties
The FileLocksmith module supports the following configurable properties:
### ExtendedContextMenuOnly
Controls whether File Locksmith appears only in the extended context menu.
**Type:** boolean
**Default:** `false`
**Description:** When `true`, File Locksmith only appears in the context menu
when you hold Shift while right-clicking. When `false`, it appears in the
standard context menu.
## Examples
### Example 1 - Show in standard context menu with direct execution
This example configures File Locksmith to appear in the standard context menu.
```powershell
$config = @{
settings = @{
properties = @{
ExtendedContextMenuOnly = $false
}
name = "FileLocksmith"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module FileLocksmith `
--input $config
```
### Example 2 - Extended menu only with DSC
This example configures File Locksmith to appear only in the extended
context menu.
```bash
dsc config set --file filelocksmith-extended.dsc.yaml
```
```yaml
# filelocksmith-extended.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure File Locksmith for extended menu
type: Microsoft.PowerToys/FileLocksmithSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: true
name: FileLocksmith
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures File Locksmith for standard
menu access.
```bash
winget configure winget-filelocksmith.yaml
```
```yaml
# winget-filelocksmith.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure File Locksmith
type: Microsoft.PowerToys/FileLocksmithSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: false
name: FileLocksmith
version: 1.0
```
### Example 4 - Minimize context menu clutter
This example configures for extended menu to reduce clutter.
```bash
dsc config set --file filelocksmith-minimal.dsc.yaml
```
```yaml
# filelocksmith-minimal.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Minimal context menu
type: Microsoft.PowerToys/FileLocksmithSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: true
name: FileLocksmith
version: 1.0
```
## Use cases
### System administration
Quick access for troubleshooting file locks:
```yaml
resources:
- name: Admin quick access
type: Microsoft.PowerToys/FileLocksmithSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: false
name: FileLocksmith
version: 1.0
```
### Clean context menu
Reduce menu clutter for casual users:
```yaml
resources:
- name: Clean menu
type: Microsoft.PowerToys/FileLocksmithSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: true
name: FileLocksmith
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [RegistryPreview][03]
- [PowerToys File Locksmith Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./RegistryPreview.md
[04]: https://learn.microsoft.com/windows/powertoys/file-locksmith

View File

@@ -0,0 +1,288 @@
---
description: DSC configuration reference for PowerToys FindMyMouse module
ms.date: 10/18/2025
ms.topic: reference
title: FindMyMouse Module
---
# FindMyMouse Module
## Synopsis
Manages configuration for the Find My Mouse utility, which helps locate your
mouse cursor on the screen.
## Description
The `FindMyMouse` module configures PowerToys Find My Mouse, a utility that
highlights your mouse cursor location when you press the Ctrl key. This is
particularly useful on large or multiple displays where the cursor can be
difficult to locate.
## Properties
The FindMyMouse module supports the following configurable properties:
### DoNotActivateOnGameMode
Controls whether Find My Mouse is disabled during game mode.
**Type:** boolean
**Default:** `true`
**Description:** When enabled, Find My Mouse will not activate when Windows
game mode is active.
### BackgroundColor
Sets the background color of the spotlight effect.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#000000"` (black)
### SpotlightColor
Sets the color of the spotlight circle around the cursor.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#FFFFFF"` (white)
### OverlayOpacity
Sets the opacity of the background overlay (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `50`
### SpotlightRadius
Sets the radius of the spotlight in pixels.
**Type:** integer
**Range:** `50` to `500`
**Default:** `100`
### AnimationDurationMs
Sets the duration of the spotlight animation in milliseconds.
**Type:** integer
**Range:** `0` to `5000`
**Default:** `500`
### SpotlightInitialZoom
Sets the initial zoom level of the spotlight effect.
**Type:** integer
**Range:** `100` to `1000`
**Default:** `200`
### ExcludedApps
List of applications where Find My Mouse is disabled.
**Type:** string (newline-separated list of executable names)
## Examples
### Example 1 - Configure spotlight appearance with direct execution
This example customizes the spotlight colors and radius.
```powershell
$config = @{
settings = @{
properties = @{
BackgroundColor = "#000000"
SpotlightColor = "#00FF00"
SpotlightRadius = 150
OverlayOpacity = 60
}
name = "FindMyMouse"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module FindMyMouse --input $config
```
### Example 2 - Configure animation with DSC
This example customizes the spotlight animation behavior.
```bash
dsc config set --file findmymouse-animation.dsc.yaml
```
```yaml
# findmymouse-animation.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Find My Mouse animation
type: Microsoft.PowerToys/FindMyMouseSettings
properties:
settings:
properties:
AnimationDurationMs: 750
SpotlightInitialZoom: 300
SpotlightRadius: 120
name: FindMyMouse
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Find My Mouse with custom
colors.
```bash
winget configure winget-findmymouse.yaml
```
```yaml
# winget-findmymouse.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Find My Mouse
type: Microsoft.PowerToys/FindMyMouseSettings
properties:
settings:
properties:
BackgroundColor: "#000000"
SpotlightColor: "#0078D7"
OverlayOpacity: 70
SpotlightRadius: 140
DoNotActivateOnGameMode: true
name: FindMyMouse
version: 1.0
```
### Example 4 - Subtle configuration
This example creates a subtle, less intrusive spotlight effect.
```bash
dsc config set --file findmymouse-subtle.dsc.yaml
```
```yaml
# findmymouse-subtle.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Subtle spotlight
type: Microsoft.PowerToys/FindMyMouseSettings
properties:
settings:
properties:
OverlayOpacity: 30
SpotlightRadius: 100
AnimationDurationMs: 300
name: FindMyMouse
version: 1.0
```
### Example 5 - High visibility configuration
This example creates a high-visibility spotlight for accessibility.
```powershell
$config = @{
settings = @{
properties = @{
BackgroundColor = "#000000"
SpotlightColor = "#FFFF00"
OverlayOpacity = 80
SpotlightRadius = 200
SpotlightInitialZoom = 400
}
name = "FindMyMouse"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module FindMyMouse --input $config
```
### Example 6 - Disable during gaming
This example ensures Find My Mouse doesn't interfere with games.
```bash
dsc config set --file findmymouse-gaming.dsc.yaml
```
```yaml
# findmymouse-gaming.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Gaming configuration
type: Microsoft.PowerToys/FindMyMouseSettings
properties:
settings:
properties:
DoNotActivateOnGameMode: true
name: FindMyMouse
version: 1.0
```
## Use cases
### Large displays
Configure for high visibility on large screens:
```yaml
resources:
- name: Large display configuration
type: Microsoft.PowerToys/FindMyMouseSettings
properties:
settings:
properties:
SpotlightRadius: 180
OverlayOpacity: 70
SpotlightColor: "#FFFFFF"
name: FindMyMouse
version: 1.0
```
### Accessibility
Configure for maximum visibility:
```yaml
resources:
- name: Accessibility configuration
type: Microsoft.PowerToys/FindMyMouseSettings
properties:
settings:
properties:
SpotlightColor: "#FFFF00"
OverlayOpacity: 80
SpotlightRadius: 200
AnimationDurationMs: 1000
name: FindMyMouse
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [MouseHighlighter][03]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./MouseHighlighter.md

200
doc/dsc/modules/Hosts.md Normal file
View File

@@ -0,0 +1,200 @@
---
description: DSC configuration reference for PowerToys Hosts module
ms.date: 10/18/2025
ms.topic: reference
title: Hosts Module
---
# Hosts Module
## Synopsis
Manages configuration for the Hosts File Editor utility, a quick editor for
the Windows hosts file.
## Description
The `Hosts` module configures PowerToys Hosts File Editor, a utility that
provides a user-friendly interface for viewing and editing the Windows hosts
file. It simplifies the process of adding, modifying, and managing DNS
entries in the hosts file.
## Properties
The Hosts module supports the following configurable properties:
### LaunchAdministrator
Controls whether the Hosts File Editor launches with administrator privileges
by default.
**Type:** boolean
**Default:** `false`
**Description:** When enabled, the editor will always attempt to launch with
elevated permissions, which is required to edit the hosts file.
### LoopbackDuplicates
Controls how duplicate loopback addresses are handled.
**Type:** boolean
**Default:** `false`
### AdditionalLinesPosition
Controls where additional lines are positioned when editing entries.
**Type:** integer
**Allowed values:**
- `0` - Top
- `1` - Bottom
**Default:** `0`
## Examples
### Example 1 - Enable admin launch with direct execution
This example configures the Hosts editor to always launch with admin rights.
```powershell
$config = @{
settings = @{
properties = @{
LaunchAdministrator = $true
}
name = "Hosts"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module Hosts --input $config
```
### Example 2 - Configure with DSC
This example enables administrator launch and configures line positioning.
```bash
dsc config set --file hosts-config.dsc.yaml
```
```yaml
# hosts-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Hosts File Editor
type: Microsoft.PowerToys/HostsSettings
properties:
settings:
properties:
LaunchAdministrator: true
AdditionalLinesPosition: 1
name: Hosts
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures the Hosts editor for admin
launch.
```bash
winget configure winget-hosts.yaml
```
```yaml
# winget-hosts.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Hosts File Editor
type: Microsoft.PowerToys/HostsSettings
properties:
settings:
properties:
LaunchAdministrator: true
LoopbackDuplicates: false
name: Hosts
version: 1.0
```
### Example 4 - Development configuration
This example configures for development use with new entries at the bottom.
```bash
dsc config set --file hosts-development.dsc.yaml
```
```yaml
# hosts-development.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Development hosts configuration
type: Microsoft.PowerToys/HostsSettings
properties:
settings:
properties:
LaunchAdministrator: true
AdditionalLinesPosition: 1
name: Hosts
version: 1.0
```
## Use cases
### System administration
Configure for frequent hosts file editing:
```yaml
resources:
- name: Admin configuration
type: Microsoft.PowerToys/HostsSettings
properties:
settings:
properties:
LaunchAdministrator: true
name: Hosts
version: 1.0
```
### Web development
Configure for development environment management:
```yaml
resources:
- name: Developer configuration
type: Microsoft.PowerToys/HostsSettings
properties:
settings:
properties:
LaunchAdministrator: true
AdditionalLinesPosition: 1
name: Hosts
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [KeyboardManager][03]
- [PowerToys Hosts File Editor Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./KeyboardManager.md
[04]: https://learn.microsoft.com/windows/powertoys/hosts-file-editor

View File

@@ -0,0 +1,349 @@
---
description: DSC configuration reference for PowerToys ImageResizer module
ms.date: 10/18/2025
ms.topic: reference
title: ImageResizer Module
---
# ImageResizer Module
## Synopsis
Manages configuration for the Image Resizer utility, which provides quick
image resizing from the Windows Explorer context menu.
## Description
The `ImageResizer` module configures PowerToys Image Resizer, a Windows shell
extension that allows you to resize one or multiple images directly from the
File Explorer context menu. It supports custom size presets and various
resize options.
## Properties
The ImageResizer module supports the following configurable properties:
### ImageResizerSizes
Defines the preset sizes available in the Image Resizer interface.
**Type:** array of objects
**Object properties:**
- `Name` (string) - Display name for the preset
- `Width` (integer) - Width value
- `Height` (integer) - Height value
- `Unit` (string) - Unit of measurement: `"Pixel"`, `"Percent"`, `"Centimeter"`, `"Inch"`
- `Fit` (string) - Resize mode: `"Fit"`, `"Fill"`, `"Stretch"`
### ImageresizerSelectedSizeIndex
Sets the default selected size preset (0-based index).
**Type:** integer
**Default:** `0`
### ImageresizerShrinkOnly
Controls whether images are only resized if they're larger than the target size.
**Type:** boolean
**Default:** `false`
### ImageresizerReplace
Controls whether resized images replace the original files.
**Type:** boolean
**Default:** `false`
### ImageresizerIgnoreOrientation
Controls whether EXIF orientation data is ignored.
**Type:** boolean
**Default:** `true`
### ImageresizerJpegQualityLevel
Sets the JPEG quality level for resized images (1-100).
**Type:** integer
**Range:** `1` to `100`
**Default:** `90`
### ImageresizerPngInterlaceOption
Sets the PNG interlace option.
**Type:** integer
**Allowed values:**
- `0` - No interlacing
- `1` - Interlaced
**Default:** `0`
### ImageresizerTiffCompressOption
Sets the TIFF compression option.
**Type:** integer
**Allowed values:**
- `0` - No compression
- `1` - LZW compression
- `2` - ZIP compression
**Default:** `0`
### ImageresizerFileName
Sets the naming pattern for resized images.
**Type:** string
**Default:** `"%1 (%2)"`
**Placeholders:**
- `%1` - Original filename
- `%2` - Size name
- `%3` - Selected width
- `%4` - Selected height
- `%5` - Actual width
- `%6` - Actual height
### ImageresizerKeepDateModified
Controls whether the original file's modified date is preserved.
**Type:** boolean
**Default:** `false`
### ImageresizerFallbackEncoder
Sets the fallback encoder for unsupported formats.
**Type:** string
**Allowed values:** `"png"`, `"jpg"`, `"bmp"`, `"tiff"`, `"gif"`
**Default:** `"png"`
## Examples
### Example 1 - Configure custom size presets with direct execution
This example defines custom image resize presets.
```powershell
$config = @{
settings = @{
properties = @{
ImageResizerSizes = @(
@{
Name = "Small"
Width = 640
Height = 480
Unit = "Pixel"
Fit = "Fit"
},
@{
Name = "Medium"
Width = 1280
Height = 720
Unit = "Pixel"
Fit = "Fit"
},
@{
Name = "Large"
Width = 1920
Height = 1080
Unit = "Pixel"
Fit = "Fit"
}
)
}
name = "ImageResizer"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ImageResizer `
--input $config
```
### Example 2 - Configure quality settings with DSC
This example configures image quality and format options.
```bash
dsc config set --file imageresizer-quality.dsc.yaml
```
```yaml
# imageresizer-quality.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Image Resizer quality
type: Microsoft.PowerToys/ImageResizerSettings
properties:
settings:
properties:
ImageresizerJpegQualityLevel: 95
ImageresizerShrinkOnly: true
ImageresizerKeepDateModified: true
name: ImageResizer
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Image Resizer with
web-optimized presets.
```bash
winget configure winget-imageresizer.yaml
```
```yaml
# winget-imageresizer.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Image Resizer
type: Microsoft.PowerToys/ImageResizerSettings
properties:
settings:
properties:
ImageResizerSizes:
- Name: Thumbnail
Width: 320
Height: 240
Unit: Pixel
Fit: Fit
- Name: Web Small
Width: 800
Height: 600
Unit: Pixel
Fit: Fit
- Name: Web Large
Width: 1920
Height: 1080
Unit: Pixel
Fit: Fit
ImageresizerJpegQualityLevel: 85
ImageresizerFileName: "%1_resized_%2"
name: ImageResizer
version: 1.0
```
### Example 4 - Photography workflow
This example configures for photography with high quality and metadata
preservation.
```bash
dsc config set --file imageresizer-photo.dsc.yaml
```
```yaml
# imageresizer-photography.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Photography configuration
type: Microsoft.PowerToys/ImageResizerSettings
properties:
settings:
properties:
ImageresizerJpegQualityLevel: 100
ImageresizerKeepDateModified: true
ImageresizerIgnoreOrientation: false
ImageresizerShrinkOnly: true
name: ImageResizer
version: 1.0
```
### Example 5 - Social media presets
This example defines presets for social media platforms.
```powershell
$config = @{
settings = @{
properties = @{
ImageResizerSizes = @(
@{ Name = "Instagram Square"; Width = 1080; Height = 1080; Unit = "Pixel"; Fit = "Fill" },
@{ Name = "Instagram Portrait"; Width = 1080; Height = 1350; Unit = "Pixel"; Fit = "Fill" },
@{ Name = "Facebook Cover"; Width = 820; Height = 312; Unit = "Pixel"; Fit = "Fill" },
@{ Name = "Twitter Header"; Width = 1500; Height = 500; Unit = "Pixel"; Fit = "Fill" }
)
ImageresizerJpegQualityLevel = 90
}
name = "ImageResizer"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ImageResizer `
--input $config
```
## Use cases
### Web development
Configure for web-optimized images:
```yaml
resources:
- name: Web optimization
type: Microsoft.PowerToys/ImageResizerSettings
properties:
settings:
properties:
ImageresizerJpegQualityLevel: 85
ImageresizerShrinkOnly: true
name: ImageResizer
version: 1.0
```
### Content creation
Configure for social media and content platforms:
```yaml
resources:
- name: Content creation
type: Microsoft.PowerToys/ImageResizerSettings
properties:
settings:
properties:
ImageResizerSizes:
- Name: HD
Width: 1920
Height: 1080
Unit: Pixel
Fit: Fit
ImageresizerJpegQualityLevel: 90
name: ImageResizer
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [MeasureTool][03]
- [PowerToys Image Resizer Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./MeasureTool.md
[04]: https://learn.microsoft.com/windows/powertoys/image-resizer

View File

@@ -0,0 +1,145 @@
---
description: DSC configuration reference for PowerToys KeyboardManager module
ms.date: 10/18/2025
ms.topic: reference
title: KeyboardManager Module
---
# KeyboardManager Module
## Synopsis
Manages configuration for the Keyboard Manager utility, which allows key
remapping and custom keyboard shortcuts.
## Description
The `KeyboardManager` module configures PowerToys Keyboard Manager, a utility
that enables you to remap keys and create custom keyboard shortcuts. It
allows reassigning keys, creating application-specific remappings, and
defining shortcuts that run programs or commands.
## Properties
The KeyboardManager module supports the following configurable properties:
### Enabled
Controls whether Keyboard Manager is enabled.
**Type:** boolean
**Default:** `true`
## Examples
### Example 1 - Enable Keyboard Manager with direct execution
This example enables the Keyboard Manager utility.
```powershell
$config = @{
settings = @{
properties = @{
Enabled = $true
}
name = "KeyboardManager"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module KeyboardManager --input $config
```
### Example 2 - Configure with DSC
This example enables Keyboard Manager through DSC configuration.
```bash
dsc config set --file keyboardmanager-config.dsc.yaml
```
```yaml
# keyboardmanager-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Enable Keyboard Manager
type: Microsoft.PowerToys/KeyboardManagerSettings
properties:
settings:
properties:
Enabled: true
name: KeyboardManager
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and enables Keyboard Manager.
```bash
winget configure winget-keyboardmanager.yaml
```
```yaml
# winget-keyboardmanager.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Enable Keyboard Manager
type: Microsoft.PowerToys/KeyboardManagerSettings
properties:
settings:
properties:
Enabled: true
name: KeyboardManager
version: 1.0
```
## Important notes
> **Note:** The Keyboard Manager module DSC configuration controls the enabled state only. Key remappings and shortcut definitions are managed through the Keyboard Manager UI and stored separately. This design ensures that complex remapping configurations are not accidentally overwritten by DSC operations.
To configure key remappings:
1. Enable Keyboard Manager using DSC
2. Open PowerToys Settings
3. Navigate to Keyboard Manager
4. Use "Remap a key" or "Remap a shortcut" to configure specific mappings
## Use cases
### Enable for deployment
Enable Keyboard Manager on new workstations:
```yaml
resources:
- name: Enable Keyboard Manager
type: Microsoft.PowerToys/KeyboardManagerSettings
properties:
settings:
properties:
Enabled: true
name: KeyboardManager
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [PowerOCR][03]
- [PowerToys Keyboard Manager Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./PowerOCR.md
[04]: https://learn.microsoft.com/windows/powertoys/keyboard-manager

View File

@@ -0,0 +1,254 @@
---
description: DSC configuration reference for PowerToys MeasureTool module
ms.date: 10/18/2025
ms.topic: reference
title: MeasureTool Module
---
# MeasureTool Module
## Synopsis
Manages configuration for the Measure Tool (Screen Ruler) utility, which
measures pixels on your screen.
## Description
The `MeasureTool` module configures PowerToys Measure Tool (also known as
Screen Ruler), a utility that allows you to measure the distance between two
points on your screen in pixels. It's useful for designers, developers, and
anyone who needs to measure UI elements or screen distances.
## Properties
The MeasureTool module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to activate the measure tool.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Shift+M`
### ContinuousCapture
Controls whether continuous capture mode is enabled.
**Type:** boolean
**Default:** `false`
### DrawFeetOnCross
Controls whether measurement lines extend to screen edges.
**Type:** boolean
**Default:** `true`
### PerColorChannelEdgeDetection
Controls whether edge detection is per-color-channel or luminosity-based.
**Type:** boolean
**Default:** `false`
### PixelTolerance
Sets the pixel tolerance for edge detection (0-255).
**Type:** integer
**Range:** `0` to `255`
**Default:** `30`
### MeasureCrossColor
Sets the color of the measurement crosshair.
**Type:** string (hex color)
**Format:** `"#RRGGBBAA"` (with alpha)
**Default:** `"#FF4500FF"`
## Examples
### Example 1 - Configure activation shortcut with direct execution
This example customizes the measure tool activation shortcut.
```powershell
$config = @{
settings = @{
properties = @{
ActivationShortcut = @{
win = $true
ctrl = $false
alt = $false
shift = $true
code = 77
key = "M"
}
}
name = "MeasureTool"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MeasureTool `
--input $config
```
### Example 2 - Configure measurement appearance with DSC
This example customizes the crosshair color and measurement behavior.
```bash
dsc config set --file measuretool-appearance.dsc.yaml
```
```yaml
# measuretool-appearance.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Measure Tool appearance
type: Microsoft.PowerToys/MeasureToolSettings
properties:
settings:
properties:
MeasureCrossColor: "#00FF00FF"
DrawFeetOnCross: true
ContinuousCapture: false
name: MeasureTool
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Measure Tool with edge
detection.
```bash
winget configure winget-measuretool.yaml
```
```yaml
# winget-measuretool.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Measure Tool
type: Microsoft.PowerToys/MeasureToolSettings
properties:
settings:
properties:
PixelTolerance: 20
PerColorChannelEdgeDetection: true
DrawFeetOnCross: true
name: MeasureTool
version: 1.0
```
### Example 4 - High contrast configuration
This example configures for high visibility measurements.
```bash
dsc config set --file measuretool-highcontrast.dsc.yaml
```
```yaml
# measuretool-highcontrast.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: High contrast Measure Tool
type: Microsoft.PowerToys/MeasureToolSettings
properties:
settings:
properties:
MeasureCrossColor: "#FFFF00FF"
DrawFeetOnCross: true
name: MeasureTool
version: 1.0
```
### Example 5 - Continuous capture mode
This example enables continuous capture for repeated measurements.
```powershell
$config = @{
settings = @{
properties = @{
ContinuousCapture = $true
PixelTolerance = 25
}
name = "MeasureTool"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MeasureTool --input $config
```
## Use cases
### UI/UX design
Configure for design work with precise measurements:
```yaml
resources:
- name: Design configuration
type: Microsoft.PowerToys/MeasureToolSettings
properties:
settings:
properties:
PixelTolerance: 15
DrawFeetOnCross: true
name: MeasureTool
version: 1.0
```
### Web development
Configure for layout debugging:
```yaml
resources:
- name: Developer configuration
type: Microsoft.PowerToys/MeasureToolSettings
properties:
settings:
properties:
ContinuousCapture: true
MeasureCrossColor: "#0078D7FF"
name: MeasureTool
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [PowerAccent][03]
- [PowerToys Screen Ruler Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./PowerAccent.md
[04]: https://learn.microsoft.com/windows/powertoys/screen-ruler

View File

@@ -0,0 +1,276 @@
---
description: DSC configuration reference for PowerToys MouseHighlighter module
ms.date: 10/18/2025
ms.topic: reference
title: MouseHighlighter Module
---
# MouseHighlighter Module
## Synopsis
Manages configuration for the Mouse Highlighter utility, which highlights
your mouse cursor and clicks.
## Description
The `MouseHighlighter` module configures PowerToys Mouse Highlighter, a
utility that adds visual highlights to your mouse cursor and click locations.
This is useful for presentations, tutorials, screen recordings, or
accessibility purposes.
## Properties
The MouseHighlighter module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to toggle mouse highlighting.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Shift+H`
### LeftButtonClickColor
Sets the color for left mouse button clicks.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#FFFF00"` (yellow)
### RightButtonClickColor
Sets the color for right mouse button clicks.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#0000FF"` (blue)
### HighlightOpacity
Sets the opacity of click highlights (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `160`
### HighlightRadius
Sets the radius of click highlights in pixels.
**Type:** integer
**Range:** `1` to `500`
**Default:** `20`
### HighlightFadeDelayMs
Sets how long highlights remain visible in milliseconds.
**Type:** integer
**Range:** `0` to `10000`
**Default:** `500`
### HighlightFadeDurationMs
Sets the duration of the highlight fade animation in milliseconds.
**Type:** integer
**Range:** `0` to `10000`
**Default:** `250`
### AutoActivate
Controls whether Mouse Highlighter activates automatically during presentations.
**Type:** boolean
**Default:** `false`
## Examples
### Example 1 - Configure highlight colors with direct execution
This example customizes the click highlight colors.
```powershell
$config = @{
settings = @{
properties = @{
LeftButtonClickColor = "#00FF00"
RightButtonClickColor = "#FF0000"
HighlightOpacity = 200
}
name = "MouseHighlighter"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MouseHighlighter `
--input $config
```
### Example 2 - Configure highlight animation with DSC
This example customizes the animation timing and appearance.
```bash
dsc config set --file mousehighlighter-animation.dsc.yaml
```
```yaml
# mousehighlighter-animation.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Mouse Highlighter animation
type: Microsoft.PowerToys/MouseHighlighterSettings
properties:
settings:
properties:
HighlightRadius: 30
HighlightFadeDelayMs: 750
HighlightFadeDurationMs: 400
name: MouseHighlighter
version: 1.0
```
### Example 3 - Install and configure for presentations with WinGet
This example installs PowerToys and configures Mouse Highlighter for
presentations.
```bash
winget configure winget-mousehighlighter.yaml
```
```yaml
# winget-mousehighlighter.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Mouse Highlighter for presentations
type: Microsoft.PowerToys/MouseHighlighterSettings
properties:
settings:
properties:
LeftButtonClickColor: "#FFD700"
RightButtonClickColor: "#FF4500"
HighlightOpacity: 220
HighlightRadius: 25
AutoActivate: true
name: MouseHighlighter
version: 1.0
```
### Example 4 - Subtle highlighting
This example configures subtle, less distracting highlights.
```bash
dsc config set --file mousehighlighter-subtle.dsc.yaml
```
```yaml
# mousehighlighter-subtle.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Subtle mouse highlighting
type: Microsoft.PowerToys/MouseHighlighterSettings
properties:
settings:
properties:
HighlightOpacity: 100
HighlightRadius: 15
HighlightFadeDelayMs: 300
name: MouseHighlighter
version: 1.0
```
### Example 5 - High visibility for accessibility
This example configures high-contrast, long-lasting highlights.
```powershell
$config = @{
settings = @{
properties = @{
LeftButtonClickColor = "#FFFFFF"
RightButtonClickColor = "#FF0000"
HighlightOpacity = 255
HighlightRadius = 40
HighlightFadeDelayMs = 1500
HighlightFadeDurationMs = 500
}
name = "MouseHighlighter"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MouseHighlighter --input $config
```
## Use cases
### Presentations and demos
Configure for clear visibility during presentations:
```yaml
resources:
- name: Presentation highlighting
type: Microsoft.PowerToys/MouseHighlighterSettings
properties:
settings:
properties:
LeftButtonClickColor: "#FFD700"
HighlightOpacity: 200
HighlightRadius: 25
AutoActivate: true
name: MouseHighlighter
version: 1.0
```
### Screen recording
Configure for video tutorials and recordings:
```yaml
resources:
- name: Recording configuration
type: Microsoft.PowerToys/MouseHighlighterSettings
properties:
settings:
properties:
HighlightOpacity: 180
HighlightFadeDelayMs: 600
name: MouseHighlighter
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [MousePointerCrosshairs][03]
- [PowerToys Mouse Utilities Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./MousePointerCrosshairs.md
[04]: https://learn.microsoft.com/windows/powertoys/mouse-utilities

View File

@@ -0,0 +1,220 @@
---
description: DSC configuration reference for PowerToys MouseJump module
ms.date: 10/18/2025
ms.topic: reference
title: MouseJump Module
---
# MouseJump Module
## Synopsis
Manages configuration for the Mouse Jump utility, which enables quick
navigation across large or multiple displays.
## Description
The `MouseJump` module configures PowerToys Mouse Jump, a utility that
provides a miniature preview of all your displays, allowing you to quickly
jump your mouse cursor to any location. This is particularly useful with
large monitors or multi-monitor setups.
## Properties
The MouseJump module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to activate Mouse Jump.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Shift+D`
### ThumbnailSize
Sets the size of the screen thumbnail preview.
**Type:** string
**Allowed values:**
- `"small"` - Smaller thumbnail for faster performance
- `"medium"` - Balanced size and performance
- `"large"` - Larger thumbnail for better visibility
**Default:** `"medium"`
## Examples
### Example 1 - Configure activation shortcut with direct execution
This example customizes the Mouse Jump activation shortcut.
```powershell
$config = @{
settings = @{
properties = @{
ActivationShortcut = @{
win = $true
ctrl = $false
alt = $false
shift = $true
code = 68
key = "D"
}
}
name = "MouseJump"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MouseJump `
--input $config
```
### Example 2 - Configure thumbnail size with DSC
This example sets a larger thumbnail for better visibility.
```bash
dsc config set --file mousejump-size.dsc.yaml
```
```yaml
# mousejump-size.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Mouse Jump thumbnail
type: Microsoft.PowerToys/MouseJumpSettings
properties:
settings:
properties:
ThumbnailSize: large
name: MouseJump
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Mouse Jump for multi-monitor
setups.
```bash
winget configure winget-mousejump.yaml
```
```yaml
# winget-mousejump.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Mouse Jump
type: Microsoft.PowerToys/MouseJumpSettings
properties:
settings:
properties:
ThumbnailSize: medium
name: MouseJump
version: 1.0
```
### Example 4 - Performance-optimized configuration
This example uses a smaller thumbnail for better performance.
```bash
dsc config set --file mousejump-performance.dsc.yaml
```
```yaml
# mousejump-performance.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Performance-optimized Mouse Jump
type: Microsoft.PowerToys/MouseJumpSettings
properties:
settings:
properties:
ThumbnailSize: small
name: MouseJump
version: 1.0
```
### Example 5 - Large display configuration
This example configures for large or high-DPI displays.
```powershell
$config = @{
settings = @{
properties = @{
ThumbnailSize = "large"
}
name = "MouseJump"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MouseJump --input $config
```
## Use cases
### Multi-monitor workstations
Configure for efficient navigation across multiple displays:
```yaml
resources:
- name: Multi-monitor configuration
type: Microsoft.PowerToys/MouseJumpSettings
properties:
settings:
properties:
ThumbnailSize: medium
name: MouseJump
version: 1.0
```
### Large displays
Configure for ultra-wide or 4K+ displays:
```yaml
resources:
- name: Large display configuration
type: Microsoft.PowerToys/MouseJumpSettings
properties:
settings:
properties:
ThumbnailSize: large
name: MouseJump
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [FindMyMouse][03]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./FindMyMouse.md

View File

@@ -0,0 +1,290 @@
---
description: DSC configuration reference for PowerToys MousePointerCrosshairs module
ms.date: 10/18/2025
ms.topic: reference
title: MousePointerCrosshairs Module
---
# MousePointerCrosshairs Module
## Synopsis
Manages configuration for the Mouse Pointer Crosshairs utility, which
displays crosshairs centered on your mouse pointer.
## Description
The `MousePointerCrosshairs` module configures PowerToys Mouse Pointer
Crosshairs, a utility that displays customizable crosshairs overlaid on your
screen, centered on the mouse cursor. This is useful for presentations,
design work, or improving cursor visibility.
## Properties
The MousePointerCrosshairs module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to toggle crosshairs display.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Alt+P`
### CrosshairsColor
Sets the color of the crosshairs.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#FF0000"` (red)
### CrosshairsOpacity
Sets the opacity of the crosshairs (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `75`
### CrosshairsRadius
Sets the length of the crosshair lines in pixels.
**Type:** integer
**Range:** `0` to `9999`
**Default:** `100`
### CrosshairsThickness
Sets the thickness of the crosshair lines in pixels.
**Type:** integer
**Range:** `1` to `50`
**Default:** `5`
### CrosshairsBorderColor
Sets the border color of the crosshairs.
**Type:** string (hex color)
**Format:** `"#RRGGBB"`
**Default:** `"#FFFFFF"` (white)
### CrosshairsBorderSize
Sets the width of the crosshair border in pixels.
**Type:** integer
**Range:** `0` to `50`
**Default:** `1`
### CrosshairsAutoHide
Controls whether crosshairs automatically hide when the mouse is not moving.
**Type:** boolean
**Default:** `false`
### CrosshairsIsFixedLengthEnabled
Controls whether crosshairs have a fixed length or extend to screen edges.
**Type:** boolean
**Default:** `true`
### CrosshairsFixedLength
Sets the fixed length of crosshairs when fixed length mode is enabled.
**Type:** integer
**Range:** `0` to `9999`
**Default:** `100`
## Examples
### Example 1 - Configure crosshair appearance with direct execution
This example customizes the crosshair color and size.
```powershell
$config = @{
settings = @{
properties = @{
CrosshairsColor = "#00FF00"
CrosshairsOpacity = 85
CrosshairsThickness = 3
CrosshairsRadius = 150
}
name = "MousePointerCrosshairs"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MousePointerCrosshairs `
--input $config
```
### Example 2 - Configure with border with DSC
This example adds a border to the crosshairs for better visibility.
```bash
dsc config set --file mousecrosshairs-border.dsc.yaml
```
```yaml
# mousecrosshairs-border.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure crosshairs with border
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
properties:
settings:
properties:
CrosshairsColor: "#FF0000"
CrosshairsBorderColor: "#FFFFFF"
CrosshairsBorderSize: 2
CrosshairsThickness: 4
name: MousePointerCrosshairs
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures crosshairs for presentations.
```bash
winget configure winget-mousecrosshairs.yaml
```
```yaml
# winget-mousecrosshairs.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Mouse Crosshairs
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
properties:
settings:
properties:
CrosshairsColor: "#FFFF00"
CrosshairsOpacity: 90
CrosshairsRadius: 120
CrosshairsThickness: 5
CrosshairsBorderSize: 2
name: MousePointerCrosshairs
version: 1.0
```
### Example 4 - Full-screen crosshairs
This example configures crosshairs that extend to screen edges.
```bash
dsc config set --file mousecrosshairs-fullscreen.dsc.yaml
```
```yaml
# mousecrosshairs-fullscreen.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Full-screen crosshairs
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
properties:
settings:
properties:
CrosshairsIsFixedLengthEnabled: false
CrosshairsOpacity: 60
name: MousePointerCrosshairs
version: 1.0
```
### Example 5 - Subtle crosshairs with auto-hide
This example creates subtle crosshairs that hide when idle.
```powershell
$config = @{
settings = @{
properties = @{
CrosshairsColor = "#FFFFFF"
CrosshairsOpacity = 50
CrosshairsThickness = 2
CrosshairsRadius = 80
CrosshairsAutoHide = $true
}
name = "MousePointerCrosshairs"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module MousePointerCrosshairs --input $config
```
## Use cases
### Presentations and demos
Configure for clear cursor tracking during presentations:
```yaml
resources:
- name: Presentation crosshairs
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
properties:
settings:
properties:
CrosshairsColor: "#FFFF00"
CrosshairsOpacity: 85
CrosshairsRadius: 150
name: MousePointerCrosshairs
version: 1.0
```
### Design and alignment
Configure for precise alignment work:
```yaml
resources:
- name: Design crosshairs
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
properties:
settings:
properties:
CrosshairsIsFixedLengthEnabled: false
CrosshairsThickness: 1
CrosshairsOpacity: 70
name: MousePointerCrosshairs
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [MouseHighlighter][03]
- [PowerToys Mouse Utilities Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./MouseHighlighter.md
[04]: https://learn.microsoft.com/windows/powertoys/mouse-utilities

200
doc/dsc/modules/Peek.md Normal file
View File

@@ -0,0 +1,200 @@
---
description: DSC configuration reference for PowerToys Peek module
ms.date: 10/18/2025
ms.topic: reference
title: Peek Module
---
# Peek Module
## Synopsis
Manages configuration for the Peek utility, a quick file preview tool.
## Description
The `Peek` module configures PowerToys Peek, a utility that provides quick
file previews without opening files. Activate it with a keyboard shortcut to
preview documents, images, videos, and more in a popup window.
## Properties
The Peek module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to activate Peek for the selected file.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Ctrl+Space`
### CloseAfterLosingFocus
Controls whether Peek window closes when it loses focus.
**Type:** boolean
**Default:** `true`
## Examples
### Example 1 - Configure activation shortcut with direct execution
This example customizes the Peek activation shortcut.
```powershell
$config = @{
settings = @{
properties = @{
ActivationShortcut = @{
win = $false
ctrl = $true
alt = $false
shift = $false
code = 32
key = "Space"
}
}
name = "Peek"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module Peek --input $config
```
### Example 2 - Configure focus behavior with DSC
This example configures Peek to remain open after losing focus.
```bash
dsc config set --file peek-focus.dsc.yaml
```
```yaml
# peek-focus.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Peek focus behavior
type: Microsoft.PowerToys/PeekSettings
properties:
settings:
properties:
CloseAfterLosingFocus: false
name: Peek
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Peek.
```bash
winget configure winget-peek.yaml
```
```yaml
# winget-peek.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Peek
type: Microsoft.PowerToys/PeekSettings
properties:
settings:
properties:
CloseAfterLosingFocus: true
name: Peek
version: 1.0
```
### Example 4 - Alternative activation shortcut
This example uses Ctrl+Shift+Space as the activation shortcut.
```bash
dsc config set --file peek-altkey.dsc.yaml
```
```yaml
# peek-altkey.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Alternative Peek shortcut
type: Microsoft.PowerToys/PeekSettings
properties:
settings:
properties:
ActivationShortcut:
win: false
ctrl: true
alt: false
shift: true
code: 32
key: Space
name: Peek
version: 1.0
```
## Use cases
### File browsing
Configure for quick file preview during browsing:
```yaml
resources:
- name: File browsing configuration
type: Microsoft.PowerToys/PeekSettings
properties:
settings:
properties:
CloseAfterLosingFocus: true
name: Peek
version: 1.0
```
### Content review
Configure for extended content review:
```yaml
resources:
- name: Review configuration
type: Microsoft.PowerToys/PeekSettings
properties:
settings:
properties:
CloseAfterLosingFocus: false
name: Peek
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [ShortcutGuide][03]
- [PowerToys Peek Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./ShortcutGuide.md
[04]: https://learn.microsoft.com/windows/powertoys/peek

View File

@@ -0,0 +1,257 @@
---
description: DSC configuration reference for PowerToys PowerAccent module
ms.date: 10/18/2025
ms.topic: reference
title: PowerAccent Module
---
# PowerAccent Module
## Synopsis
Manages configuration for the Power Accent utility, a quick accent character selector.
## Description
The `PowerAccent` module configures PowerToys Power Accent (Quick Accent), a
utility that provides quick access to accented characters. Hold down a key
and use arrow keys or numbers to select from available accent variations.
## Properties
The PowerAccent module supports the following configurable properties:
### ActivationKey
Sets which key triggers the accent selection.
**Type:** string
**Allowed values:**
- `"LeftRightArrow"` - Hold left or right arrow keys
- `"Space"` - Hold spacebar
- `"Both"` - Hold either left/right arrows or spacebar
**Default:** `"Both"`
### InputTime
Sets how long the activation key must be held (in milliseconds) before showing accents.
**Type:** integer
**Range:** `100` to `1000`
**Default:** `300`
### ExcludedApps
List of applications where Power Accent is disabled.
**Type:** string (newline-separated list of executable names)
### ToolbarPosition
Sets the position of the accent selection toolbar.
**Type:** string
**Allowed values:**
- `"Top"` - Above the cursor
- `"Bottom"` - Below the cursor
- `"Left"` - To the left of cursor
- `"Right"` - To the right of cursor
- `"Center"` - Centered on screen
**Default:** `"Top"`
### ShowUnicodeDescription
Controls whether Unicode descriptions are shown for each accent character.
**Type:** boolean
**Default:** `false`
### SortByUsageFrequency
Controls whether accent characters are sorted by usage frequency.
**Type:** boolean
**Default:** `false`
### StartSelectionFromTheLeft
Controls whether selection starts from the left side.
**Type:** boolean
**Default:** `false`
## Examples
### Example 1 - Configure activation method with direct execution
This example sets spacebar as the activation key.
```powershell
$config = @{
settings = @{
properties = @{
ActivationKey = "Space"
InputTime = 250
}
name = "PowerAccent"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module PowerAccent `
--input $config
```
### Example 2 - Configure toolbar appearance with DSC
This example customizes the toolbar position and display options.
```bash
dsc config set --file poweraccent-toolbar.dsc.yaml
```
```yaml
# poweraccent-toolbar.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Power Accent toolbar
type: Microsoft.PowerToys/PowerAccentSettings
properties:
settings:
properties:
ToolbarPosition: Bottom
ShowUnicodeDescription: true
SortByUsageFrequency: true
name: PowerAccent
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Power Accent for multilingual
typing.
```bash
winget configure winget-poweraccent.yaml
```
```yaml
# winget-poweraccent.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Power Accent
type: Microsoft.PowerToys/PowerAccentSettings
properties:
settings:
properties:
ActivationKey: Space
InputTime: 300
ToolbarPosition: Top
SortByUsageFrequency: true
name: PowerAccent
version: 1.0
```
### Example 4 - Fast activation configuration
This example configures for quick accent selection.
```bash
dsc config set --file poweraccent-fast.dsc.yaml
```
```yaml
# poweraccent-fast.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Fast accent activation
type: Microsoft.PowerToys/PowerAccentSettings
properties:
settings:
properties:
InputTime: 150
SortByUsageFrequency: true
name: PowerAccent
version: 1.0
```
### Example 5 - Exclude applications
This example excludes specific applications from Power Accent.
```powershell
$config = @{
settings = @{
properties = @{
ExcludedApps = "notepad.exe`nWordPad.exe"
}
name = "PowerAccent"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module PowerAccent --input $config
```
## Use cases
### Multilingual content creation
Configure for efficient multilingual typing:
```yaml
resources:
- name: Multilingual configuration
type: Microsoft.PowerToys/PowerAccentSettings
properties:
settings:
properties:
ActivationKey: Space
SortByUsageFrequency: true
ShowUnicodeDescription: false
name: PowerAccent
version: 1.0
```
### Language learning
Configure for language learning with Unicode descriptions:
```yaml
resources:
- name: Learning configuration
type: Microsoft.PowerToys/PowerAccentSettings
properties:
settings:
properties:
ShowUnicodeDescription: true
InputTime: 400
name: PowerAccent
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [Workspaces][03]
- [PowerToys Quick Accent Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./Workspaces.md
[04]: https://learn.microsoft.com/windows/powertoys/quick-accent

197
doc/dsc/modules/PowerOCR.md Normal file
View File

@@ -0,0 +1,197 @@
---
description: DSC configuration reference for PowerToys PowerOCR module
ms.date: 10/18/2025
ms.topic: reference
title: PowerOCR Module
---
# PowerOCR Module
## Synopsis
Manages configuration for the Power OCR (Text Extractor) utility, which
extracts text from images and screen regions.
## Description
The `PowerOCR` module configures PowerToys Power OCR (Text Extractor), a
utility that uses optical character recognition (OCR) to extract text from
any screen region and copy it to the clipboard. It's useful for capturing
text from images, videos, PDFs, or any on-screen content.
## Properties
The PowerOCR module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to activate text extraction.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Shift+T`
### PreferredLanguage
Sets the preferred language for OCR recognition.
**Type:** string
**Default:** System language
## Examples
### Example 1 - Configure activation shortcut with direct execution
This example customizes the OCR activation shortcut.
```powershell
$config = @{
settings = @{
properties = @{
ActivationShortcut = @{
win = $true
ctrl = $false
alt = $false
shift = $true
code = 84
key = "T"
}
}
name = "PowerOCR"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module PowerOCR `
--input $config
```
### Example 2 - Configure language with DSC
This example sets the preferred OCR language.
```bash
dsc config set --file powerocr-language.dsc.yaml
```
```yaml
# powerocr-language.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Power OCR language
type: Microsoft.PowerToys/PowerOCRSettings
properties:
settings:
properties:
PreferredLanguage: en-US
name: PowerOCR
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Power OCR.
```bash
winget configure winget-powerocr.yaml
```
```yaml
# winget-powerocr.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Power OCR
type: Microsoft.PowerToys/PowerOCRSettings
properties:
settings:
properties:
PreferredLanguage: en-US
name: PowerOCR
version: 1.0
```
### Example 4 - Multilingual configuration
This example configures for multilingual text extraction.
```bash
dsc config set --file powerocr-multilingual.dsc.yaml
```
```yaml
# powerocr-multilingual.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Multilingual OCR
type: Microsoft.PowerToys/PowerOCRSettings
properties:
settings:
properties:
PreferredLanguage: fr-FR
name: PowerOCR
version: 1.0
```
## Use cases
### Document digitization
Configure for extracting text from documents:
```yaml
resources:
- name: Document OCR
type: Microsoft.PowerToys/PowerOCRSettings
properties:
settings:
properties:
PreferredLanguage: en-US
name: PowerOCR
version: 1.0
```
### International content
Configure for multilingual content extraction:
```yaml
resources:
- name: Multilingual OCR
type: Microsoft.PowerToys/PowerOCRSettings
properties:
settings:
properties:
PreferredLanguage: es-ES
name: PowerOCR
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [ZoomIt][03]
- [PowerToys Text Extractor Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./ZoomIt.md
[04]: https://learn.microsoft.com/windows/powertoys/text-extractor

View File

@@ -0,0 +1,230 @@
---
description: DSC configuration reference for PowerToys PowerRename module
ms.date: 10/18/2025
ms.topic: reference
title: PowerRename Module
---
# PowerRename Module
## Synopsis
Manages configuration for the Power Rename utility, a bulk file and folder renaming tool.
## Description
The `PowerRename` module configures PowerToys Power Rename, a Windows shell
extension that enables bulk renaming of files and folders with advanced
features like regular expressions, preview, and undo functionality. It
integrates with the Windows Explorer context menu.
## Properties
The PowerRename module supports the following configurable properties:
### MRUEnabled
Controls whether the most recently used (MRU) search and replace terms are saved.
**Type:** boolean
**Default:** `true`
### MaxMRUSize
Sets the maximum number of MRU entries to remember.
**Type:** integer
**Range:** `0` to `20`
**Default:** `10`
### ShowIcon
Controls whether the Power Rename icon appears in the Explorer context menu.
**Type:** boolean
**Default:** `true`
### ExtendedContextMenuOnly
Controls whether Power Rename appears only in the extended context menu (Shift+right-click).
**Type:** boolean
**Default:** `false`
### UseBoostLib
Controls whether the Boost library is used for regular expression processing.
**Type:** boolean
**Default:** `false`
## Examples
### Example 1 - Configure MRU settings with direct execution
This example configures the most recently used list behavior.
```powershell
$config = @{
settings = @{
properties = @{
MRUEnabled = $true
MaxMRUSize = 15
}
name = "PowerRename"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module PowerRename --input $config
```
### Example 2 - Configure context menu with DSC
This example configures Power Rename to appear in the extended context menu
only.
```bash
dsc config set --file powerrename-context.dsc.yaml
```
```yaml
# powerrename-context.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Power Rename context menu
type: Microsoft.PowerToys/PowerRenameSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: true
ShowIcon: true
name: PowerRename
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Power Rename.
```bash
winget configure winget-powerrename.yaml
```
```yaml
# winget-powerrename.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Power Rename
type: Microsoft.PowerToys/PowerRenameSettings
properties:
settings:
properties:
MRUEnabled: true
MaxMRUSize: 20
ShowIcon: true
UseBoostLib: true
name: PowerRename
version: 1.0
```
### Example 4 - Clean context menu configuration
This example minimizes context menu clutter.
```bash
dsc config set --file powerrename-minimal.dsc.yaml
```
```yaml
# powerrename-minimal.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Minimal context menu
type: Microsoft.PowerToys/PowerRenameSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: true
ShowIcon: false
name: PowerRename
version: 1.0
```
### Example 5 - Advanced regex configuration
This example enables the Boost library for advanced regex features.
```powershell
$config = @{
settings = @{
properties = @{
UseBoostLib = $true
MRUEnabled = $true
MaxMRUSize = 15
}
name = "PowerRename"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module PowerRename --input $config
```
## Use cases
### Content management
Configure for frequent file renaming tasks:
```yaml
resources:
- name: Content management
type: Microsoft.PowerToys/PowerRenameSettings
properties:
settings:
properties:
MRUEnabled: true
MaxMRUSize: 20
ShowIcon: true
name: PowerRename
version: 1.0
```
### Clean interface
Configure for minimal context menu presence:
```yaml
resources:
- name: Clean interface
type: Microsoft.PowerToys/PowerRenameSettings
properties:
settings:
properties:
ExtendedContextMenuOnly: true
name: PowerRename
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [AdvancedPaste][03]
- [PowerToys PowerRename Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./AdvancedPaste.md
[04]: https://learn.microsoft.com/windows/powertoys/powerrename

View File

@@ -0,0 +1,173 @@
---
description: DSC configuration reference for PowerToys RegistryPreview module
ms.date: 10/18/2025
ms.topic: reference
title: RegistryPreview Module
---
# RegistryPreview Module
## Synopsis
Manages configuration for the Registry Preview utility, which visualizes and edits Windows registry files (.reg).
## Description
The `RegistryPreview` module configures PowerToys Registry Preview, a utility
that provides a visual preview and editing interface for Windows registry
(.reg) files. It helps you understand and safely edit registry files before
applying them to your system.
## Properties
The RegistryPreview module supports the following configurable properties:
### DefaultRegApp
Controls whether Registry Preview is set as the default application for .reg files.
**Type:** boolean
**Default:** `false`
## Examples
### Example 1 - Set as default .reg handler with direct execution
This example sets Registry Preview as the default application for .reg files.
```powershell
$config = @{
settings = @{
properties = @{
DefaultRegApp = $true
}
name = "RegistryPreview"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module RegistryPreview --input $config
```
### Example 2 - Configure with DSC
This example configures Registry Preview as the default handler.
```bash
dsc config set --file registrypreview-default.dsc.yaml
```
```yaml
# registrypreview-default.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Set Registry Preview as default
type: Microsoft.PowerToys/RegistryPreviewSettings
properties:
settings:
properties:
DefaultRegApp: true
name: RegistryPreview
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and sets Registry Preview as the default .reg
handler.
```bash
winget configure winget-registrypreview.yaml
```
```yaml
# winget-registrypreview.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Registry Preview
type: Microsoft.PowerToys/RegistryPreviewSettings
properties:
settings:
properties:
DefaultRegApp: true
name: RegistryPreview
version: 1.0
```
### Example 4 - Disable as default handler
This example ensures Registry Preview is not the default .reg handler.
```bash
dsc config set --file registrypreview-notdefault.dsc.yaml
```
```yaml
# registrypreview-notdefault.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Do not use as default
type: Microsoft.PowerToys/RegistryPreviewSettings
properties:
settings:
properties:
DefaultRegApp: false
name: RegistryPreview
version: 1.0
```
## Use cases
### System administration
Configure as default for safe registry file handling:
```yaml
resources:
- name: Admin configuration
type: Microsoft.PowerToys/RegistryPreviewSettings
properties:
settings:
properties:
DefaultRegApp: true
name: RegistryPreview
version: 1.0
```
### Optional tool
Keep as optional tool without default file association:
```yaml
resources:
- name: Optional tool
type: Microsoft.PowerToys/RegistryPreviewSettings
properties:
settings:
properties:
DefaultRegApp: false
name: RegistryPreview
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [FileLocksmith][03]
- [PowerToys Registry Preview Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./FileLocksmith.md
[04]: https://learn.microsoft.com/windows/powertoys/registry-preview

View File

@@ -0,0 +1,259 @@
---
description: DSC configuration reference for PowerToys ShortcutGuide module
ms.date: 10/18/2025
ms.topic: reference
title: ShortcutGuide Module
---
# ShortcutGuide Module
## Synopsis
Manages configuration for the Shortcut Guide utility, which displays available keyboard shortcuts.
## Description
The `ShortcutGuide` module configures PowerToys Shortcut Guide, a utility that
displays an overlay showing available Windows keyboard shortcuts when you hold
the Windows key. It helps users discover and learn keyboard shortcuts.
## Properties
The ShortcutGuide module supports the following configurable properties:
### OpenShortcutGuide
Sets the keyboard shortcut or method to open the shortcut guide.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** Hold Windows key for 900ms
### OverlayOpacity
Sets the opacity of the shortcut guide overlay (0-100).
**Type:** integer
**Range:** `0` to `100`
**Default:** `90`
### Theme
Sets the theme for the shortcut guide.
**Type:** string
**Allowed values:** `"light"`, `"dark"`, `"system"`
**Default:** `"dark"`
### PressTime
Sets how long the Windows key must be held before showing the guide (in milliseconds).
**Type:** integer
**Range:** `100` to `10000`
**Default:** `900`
### ExcludedApps
List of applications where Shortcut Guide is disabled.
**Type:** string (newline-separated list of executable names)
## Examples
### Example 1 - Configure activation time with direct execution
This example sets a faster activation time for the shortcut guide.
```powershell
$config = @{
settings = @{
properties = @{
PressTime = 600
}
name = "ShortcutGuide"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ShortcutGuide `
--input $config
```
### Example 2 - Configure appearance with DSC
This example customizes the overlay appearance.
```bash
dsc config set --file shortcutguide-appearance.dsc.yaml
```
```yaml
# shortcutguide-appearance.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Shortcut Guide appearance
type: Microsoft.PowerToys/ShortcutGuideSettings
properties:
settings:
properties:
OverlayOpacity: 95
Theme: light
name: ShortcutGuide
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Shortcut Guide.
```bash
winget configure winget-shortcutguide.yaml
```
```yaml
# winget-shortcutguide.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Shortcut Guide
type: Microsoft.PowerToys/ShortcutGuideSettings
properties:
settings:
properties:
PressTime: 700
OverlayOpacity: 90
Theme: dark
name: ShortcutGuide
version: 1.0
```
### Example 4 - Quick activation
This example configures for quick activation with a short press time.
```bash
dsc config set --file shortcutguide-quick.dsc.yaml
```
```yaml
# shortcutguide-quick.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Quick activation
type: Microsoft.PowerToys/ShortcutGuideSettings
properties:
settings:
properties:
PressTime: 400
name: ShortcutGuide
version: 1.0
```
### Example 5 - High opacity for visibility
This example maximizes opacity for better visibility.
```powershell
$config = @{
settings = @{
properties = @{
OverlayOpacity = 100
Theme = "dark"
}
name = "ShortcutGuide"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ShortcutGuide --input $config
```
### Example 6 - Exclude applications
This example excludes Shortcut Guide from specific applications.
```bash
dsc config set --file shortcutguide-exclusions.dsc.yaml
```
```yaml
# shortcutguide-exclusions.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Exclude apps
type: Microsoft.PowerToys/ShortcutGuideSettings
properties:
settings:
properties:
ExcludedApps: |
Game.exe
FullScreenApp.exe
name: ShortcutGuide
version: 1.0
```
## Use cases
### New users
Configure for easy keyboard shortcut discovery:
```yaml
resources:
- name: New user configuration
type: Microsoft.PowerToys/ShortcutGuideSettings
properties:
settings:
properties:
PressTime: 800
OverlayOpacity: 95
name: ShortcutGuide
version: 1.0
```
### Power users
Configure for quick access without accidental activation:
```yaml
resources:
- name: Power user configuration
type: Microsoft.PowerToys/ShortcutGuideSettings
properties:
settings:
properties:
PressTime: 1200
OverlayOpacity: 85
name: ShortcutGuide
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [Peek][03]
- [PowerToys Keyboard Shortcut Guide Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./Peek.md
[04]: https://learn.microsoft.com/windows/powertoys/shortcut-guide

View File

@@ -0,0 +1,238 @@
---
description: DSC configuration reference for PowerToys Workspaces module
ms.date: 10/18/2025
ms.topic: reference
title: Workspaces Module
---
# Workspaces Module
## Synopsis
Manages configuration for the Workspaces utility, which launches application sets and arranges windows.
## Description
The `Workspaces` module configures PowerToys Workspaces, a utility that allows
you to save and restore sets of applications with their window positions. It
enables you to quickly switch between different work contexts by launching and
arranging multiple applications at once.
## Properties
The Workspaces module supports the following configurable properties:
### LaunchHotkey
Sets the keyboard shortcut to launch the Workspaces editor.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Win+Shift+;` (VK code 186)
### MoveExistingWindows
Controls whether existing application windows are moved when launching a workspace.
**Type:** boolean
**Default:** `false`
### SpanZonesAcrossMonitors
Controls whether workspace zones can span across multiple monitors.
**Type:** boolean
**Default:** `false`
## Examples
### Example 1 - Configure launch hotkey with direct execution
This example sets a custom hotkey to launch the Workspaces editor.
```powershell
$config = @{
settings = @{
properties = @{
LaunchHotkey = @{
win = $true
ctrl = $true
alt = $false
shift = $false
code = 87
key = "W"
}
}
name = "Workspaces"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module Workspaces --input $config
```
### Example 2 - Configure window behavior with DSC
This example enables moving existing windows when launching workspaces.
```bash
dsc config set --file workspaces-behavior.dsc.yaml
```
```yaml
# workspaces-behavior.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Workspaces window behavior
type: Microsoft.PowerToys/WorkspacesSettings
properties:
settings:
properties:
MoveExistingWindows: true
SpanZonesAcrossMonitors: false
name: Workspaces
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures Workspaces.
```bash
winget configure winget-workspaces.yaml
```
```yaml
# winget-workspaces.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure Workspaces
type: Microsoft.PowerToys/WorkspacesSettings
properties:
settings:
properties:
LaunchHotkey:
win: true
ctrl: false
alt: false
shift: true
code: 186
key: ";"
MoveExistingWindows: true
name: Workspaces
version: 1.0
```
### Example 4 - Multi-monitor setup
This example configures for multi-monitor workspace management.
```bash
dsc config set --file workspaces-multimonitor.dsc.yaml
```
```yaml
# workspaces-multimonitor.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Multi-monitor configuration
type: Microsoft.PowerToys/WorkspacesSettings
properties:
settings:
properties:
SpanZonesAcrossMonitors: true
MoveExistingWindows: true
name: Workspaces
version: 1.0
```
### Example 5 - Simple hotkey
This example sets a simple single-key hotkey combination.
```powershell
$config = @{
settings = @{
properties = @{
LaunchHotkey = @{
win = $true
ctrl = $false
alt = $true
shift = $false
code = 192
key = "~"
}
}
name = "Workspaces"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module Workspaces --input $config
```
## Use cases
### Development environments
Configure for quick switching between development workspaces:
```yaml
resources:
- name: Development workspace
type: Microsoft.PowerToys/WorkspacesSettings
properties:
settings:
properties:
MoveExistingWindows: true
SpanZonesAcrossMonitors: true
name: Workspaces
version: 1.0
```
### Single monitor usage
Configure for single-monitor workflow:
```yaml
resources:
- name: Single monitor setup
type: Microsoft.PowerToys/WorkspacesSettings
properties:
settings:
properties:
SpanZonesAcrossMonitors: false
MoveExistingWindows: false
name: Workspaces
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [ColorPicker Module][03] - For additional PowerToys configuration
- [PowerToys Workspaces Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./ColorPicker.md
[04]: https://learn.microsoft.com/windows/powertoys/workspaces

215
doc/dsc/modules/ZoomIt.md Normal file
View File

@@ -0,0 +1,215 @@
---
description: DSC configuration reference for PowerToys ZoomIt module
ms.date: 10/18/2025
ms.topic: reference
title: ZoomIt Module
---
# ZoomIt Module
## Synopsis
Manages configuration for the ZoomIt utility, which provides screen zoom, annotation, and presentation tools.
## Description
The `ZoomIt` module configures PowerToys ZoomIt, a screen zoom and annotation utility for presentations and demonstrations. It provides live zoom, screen drawing, a break timer, and other presentation features activated through customizable keyboard shortcuts.
## Properties
The ZoomIt module supports the following configurable properties:
### ActivationShortcut
Sets the keyboard shortcut to activate the zoom mode.
**Type:** object
**Properties:**
- `win` (boolean) - Windows key modifier
- `ctrl` (boolean) - Ctrl key modifier
- `alt` (boolean) - Alt key modifier
- `shift` (boolean) - Shift key modifier
- `code` (integer) - Virtual key code
- `key` (string) - Key name
**Default:** `Ctrl+1` (VK code 49)
## Examples
### Example 1 - Configure activation shortcut with direct execution
This example sets a custom keyboard shortcut to activate ZoomIt.
```powershell
$config = @{
settings = @{
properties = @{
ActivationShortcut = @{
win = $false
ctrl = $true
alt = $false
shift = $true
code = 90
key = "Z"
}
}
name = "ZoomIt"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
PowerToys.DSC.exe set --resource 'settings' --module ZoomIt --input $config
```
### Example 2 - Configure with Microsoft DSC
This example configures the ZoomIt activation shortcut using Microsoft DSC.
```bash
dsc config set --file zoomit-config.dsc.yaml
```
```yaml
# zoomit-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure ZoomIt shortcut
type: Microsoft.PowerToys/ZoomItSettings
properties:
settings:
properties:
ActivationShortcut:
win: false
ctrl: true
alt: false
shift: false
code: 49
key: "1"
name: ZoomIt
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and configures ZoomIt using WinGet.
```bash
winget configure winget-zoomit.yaml
```
```yaml
# winget-zoomit.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure ZoomIt
type: Microsoft.PowerToys/ZoomItSettings
properties:
settings:
properties:
ActivationShortcut:
win: false
ctrl: true
alt: false
shift: true
code: 90
key: Z
name: ZoomIt
version: 1.0
```
### Example 4 - Presentation mode hotkey
This example configures an easy-to-remember presentation hotkey.
```bash
dsc config set --file zoomit-presentation.dsc.yaml
```
```yaml
# zoomit-presentation.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Presentation hotkey
type: Microsoft.PowerToys/ZoomItSettings
properties:
settings:
properties:
ActivationShortcut:
win: true
ctrl: false
alt: false
shift: false
code: 187
key: "="
name: ZoomIt
version: 1.0
```
## Use cases
### Presentations
Configure for easy screen zooming during presentations:
```yaml
resources:
- name: Presentation setup
type: Microsoft.PowerToys/ZoomItSettings
properties:
settings:
properties:
ActivationShortcut:
win: false
ctrl: true
alt: false
shift: false
code: 49
key: "1"
name: ZoomIt
version: 1.0
```
### Screen recording
Configure for quick access during screen recording sessions:
```yaml
resources:
- name: Recording setup
type: Microsoft.PowerToys/ZoomItSettings
properties:
settings:
properties:
ActivationShortcut:
win: true
ctrl: false
alt: false
shift: true
code: 90
key: Z
name: ZoomIt
version: 1.0
```
## See also
- [Settings Resource][01]
- [PowerToys DSC Overview][02]
- [CropAndLock Module][03] - For additional PowerToys configuration
- [PowerToys ZoomIt Documentation][04]
<!-- Link reference definitions -->
[01]: ../settings-resource.md
[02]: ../overview.md
[03]: ./CropAndLock.md
[04]: https://learn.microsoft.com/windows/powertoys/zoomit

244
doc/dsc/overview.md Normal file
View File

@@ -0,0 +1,244 @@
---
description: Overview of PowerToys Desired State Configuration (DSC) support
ms.date: 10/18/2025
ms.topic: overview
title: PowerToys DSC Overview
---
# PowerToys DSC Overview
## Synopsis
PowerToys supports Desired State Configuration (DSC) v3 for declarative configuration management of PowerToys settings.
## Description
PowerToys includes Microsoft Desired State Configuration (DSC) support
through the `PowerToys.DSC.exe` command-line tool, enabling you to:
- Declare and enforce desired configuration states for PowerToys
utilities.
- Automate PowerToys configuration across multiple systems.
- Integrate PowerToys configuration with WinGet and other DSC-compatible
tools.
- Version control your PowerToys settings as code.
The PowerToys DSC implementation provides a **settings** resource that
manages configuration for all PowerToys utilities (modules). Each utility
can be configured independently, allowing granular control over your
PowerToys environment.
## Usage methods
PowerToys DSC can be used in three ways:
### 1. Direct execution with PowerToys.DSC.exe
Execute DSC operations directly using the PowerToys.DSC.exe command-line
tool:
```powershell
# Get current settings for a module
PowerToys.DSC.exe get --resource 'settings' --module Awake
# Set settings for a module
$input = '{"settings":{...}}'
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $input
# Test if settings match desired state
PowerToys.DSC.exe test --resource 'settings' --module Awake --input $input
```
For detailed information, see [PowerToys.DSC.exe command reference][01].
### 2. Microsoft Desired State Configuration (DSC)
Use PowerToys DSC resources in standard DSC configuration documents:
```yaml
# powertoys-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Awake
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 1
name: Awake
version: 0.0.1
```
### 3. WinGet Configuration
Integrate PowerToys configuration with WinGet package installation:
```yaml
# winget-powertoys.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure FancyZones
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_shiftDrag: true
fancyzones_mouseSwitch: true
name: FancyZones
version: 1.0
```
## Available resources
PowerToys DSC provides the following resource:
| Resource | Description |
| ---------- | ---------------------------------------------------- |
| `settings` | Manages configuration for PowerToys utility modules. |
For detailed information about the settings resource, see [Settings
Resource Reference][03].
## Available modules
The settings resource supports configuration for the following PowerToys
utilities:
| Module | Description | Documentation |
| ---------------------- | -------------------------------------------- | ------------------------------------- |
| App | General PowerToys application settings. | [App module][04] |
| AdvancedPaste | Advanced clipboard operations. | [AdvancedPaste module][05] |
| AlwaysOnTop | Pin windows to stay on top. | [AlwaysOnTop module][06] |
| Awake | Keep computer awake. | [Awake module][07] |
| ColorPicker | System-wide color picker utility. | [ColorPicker module][08] |
| CropAndLock | Crop and lock portions of windows. | [CropAndLock module][09] |
| EnvironmentVariables | Manage environment variables. | [EnvironmentVariables module][10] |
| FancyZones | Window layout manager. | [FancyZones module][11] |
| FileLocksmith | Identify what's locking files. | [FileLocksmith module][12] |
| FindMyMouse | Locate your mouse cursor. | [FindMyMouse module][13] |
| Hosts | Quick hosts file editor. | [Hosts module][14] |
| ImageResizer | Resize images from context menu. | [ImageResizer module][15] |
| KeyboardManager | Remap keys and create shortcuts. | [KeyboardManager module][16] |
| MeasureTool | Measure pixels on screen. | [MeasureTool module][17] |
| MouseHighlighter | Highlight mouse cursor. | [MouseHighlighter module][18] |
| MouseJump | Jump across large or multiple displays. | [MouseJump module][19] |
| MousePointerCrosshairs | Display crosshairs centered on mouse. | [MousePointerCrosshairs module][20] |
| Peek | Quick file previewer. | [Peek module][21] |
| PowerAccent | Quick accent character selector. | [PowerAccent module][22] |
| PowerOCR | Extract text from images. | [PowerOCR module][23] |
| PowerRename | Bulk rename files. | [PowerRename module][24] |
| RegistryPreview | Visualize and edit registry files. | [RegistryPreview module][25] |
| ShortcutGuide | Display keyboard shortcuts. | [ShortcutGuide module][26] |
| Workspaces | Save and restore application sets. | [Workspaces module][27] |
| ZoomIt | Screen zoom and annotation tool. | [ZoomIt module][28] |
## Common operations
### List all supported modules
```powershell
PowerToys.DSC.exe modules --resource 'settings'
```
### Get current configuration
```powershell
# Get configuration for a specific module.
PowerToys.DSC.exe get --resource 'settings' --module FancyZones
# Export configuration (identical to get).
PowerToys.DSC.exe export --resource 'settings' --module FancyZones
```
### Apply configuration
```powershell
# Set configuration for a module.
$input = '{"settings":{...}}'
PowerToys.DSC.exe set --resource 'settings' --module FancyZones --input $input
```
### Validate configuration
```powershell
# Test if current state matches desired state.
$input = '{"settings":{...}}'
PowerToys.DSC.exe test --resource 'settings' --module FancyZones --input $input
```
### Generate schema
```powershell
# Get JSON schema for a module's settings.
PowerToys.DSC.exe schema --resource 'settings' --module FancyZones
```
### Generate DSC manifest
```powershell
# Generate manifest for a specific module.
$outputDir = "C:\manifests"
PowerToys.DSC.exe manifest --resource 'settings' --module FancyZones `
--outputDir $outputDir
# Generate manifests for all modules.
PowerToys.DSC.exe manifest --resource 'settings' --outputDir $outputDir
```
## Examples
For complete examples, see:
- [Settings Resource Examples][29]
- Individual module documentation in the [modules][30] folder
## See also
- [Settings Resource Reference][03]
- [PowerToys.DSC.exe Command Reference][01]
- [Module Documentation][30]
- [Microsoft DSC Documentation][31]
- [WinGet Configuration Documentation][32]
<!-- Link reference definitions -->
[01]: ./modules/
[03]: ./settings-resource.md
[04]: ./modules/App.md
[05]: ./modules/AdvancedPaste.md
[06]: ./modules/AlwaysOnTop.md
[07]: ./modules/Awake.md
[08]: ./modules/ColorPicker.md
[09]: ./modules/CropAndLock.md
[10]: ./modules/EnvironmentVariables.md
[11]: ./modules/FancyZones.md
[12]: ./modules/FileLocksmith.md
[13]: ./modules/FindMyMouse.md
[14]: ./modules/Hosts.md
[15]: ./modules/ImageResizer.md
[16]: ./modules/KeyboardManager.md
[17]: ./modules/MeasureTool.md
[18]: ./modules/MouseHighlighter.md
[19]: ./modules/MouseJump.md
[20]: ./modules/MousePointerCrosshairs.md
[21]: ./modules/Peek.md
[22]: ./modules/PowerAccent.md
[23]: ./modules/PowerOCR.md
[24]: ./modules/PowerRename.md
[25]: ./modules/RegistryPreview.md
[26]: ./modules/ShortcutGuide.md
[27]: ./modules/Workspaces.md
[28]: ./modules/ZoomIt.md
[29]: ./settings-resource.md#examples
[30]: ./modules/
[31]: https://learn.microsoft.com/powershell/dsc/overview
[32]: https://learn.microsoft.com/windows/package-manager/configuration/

View File

@@ -0,0 +1,458 @@
---
description: Reference for the PowerToys DSC settings resource
ms.date: 10/18/2025
ms.topic: reference
title: Settings Resource
---
# Settings Resource
## Synopsis
Manages configuration settings for PowerToys utilities (modules).
## Description
The `settings` resource provides Microsoft Desired State Configuration (DSC)
support for managing PowerToys configuration. It enables declarative
configuration of PowerToys utilities, allowing you to define, test, and
enforce desired states for each module.
Each PowerToys utility (module) has its own configurable properties that can
be managed through this resource. The settings resource supports standard DSC
operations: get, set, test, export, schema, and manifest generation.
## Supported modules
The settings resource supports the following PowerToys modules:
- **App** - General application settings (enable/disable utilities, run at
startup, theme, etc.).
- **AdvancedPaste** - Advanced clipboard and paste operations.
- **AlwaysOnTop** - Window pinning configuration.
- **Awake** - Keep-awake timer settings.
- **ColorPicker** - Color picker activation and format settings.
- **CropAndLock** - Window cropping settings.
- **EnvironmentVariables** - Environment variable editor settings.
- **FancyZones** - Window layout and zone configuration.
- **FileLocksmith** - File lock detection settings.
- **FindMyMouse** - Mouse locator settings.
- **Hosts** - Hosts file editor settings.
- **ImageResizer** - Image resize configuration.
- **KeyboardManager** - Key remapping and shortcut settings.
- **MeasureTool** - Screen measurement tool settings.
- **MouseHighlighter** - Mouse highlighting configuration.
- **MouseJump** - Mouse jump navigation settings.
- **MousePointerCrosshairs** - Crosshair display settings.
- **Peek** - File preview settings.
- **PowerAccent** - Accent character selection settings.
- **PowerOCR** - Text extraction settings.
- **PowerRename** - Bulk rename configuration.
- **RegistryPreview** - Registry file preview settings.
- **ShortcutGuide** - Keyboard shortcut overlay settings.
- **Workspaces** - Application workspace settings.
- **ZoomIt** - Screen zoom and annotation settings.
For detailed property information for each module, see the individual [module
documentation][01].
## Operations
### List supported modules
List all modules that can be configured with the settings resource.
**Direct execution:**
```powershell
# List all configurable modules.
PowerToys.DSC.exe modules --resource 'settings'
```
### Get current state
Retrieve the current configuration state for a module.
**Direct execution:**
```powershell
# Get current settings for a module.
PowerToys.DSC.exe get --resource 'settings' --module <ModuleName>
```
**DSC configuration:**
```yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Get Awake settings
type: Microsoft.PowerToys/AwakeSettings
properties: {}
```
**WinGet configuration:**
```yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Get FancyZones settings
type: Microsoft.PowerToys/FancyZonesSettings
properties: {}
```
### Export current state
Export the current configuration state. The output is identical to the `get`
operation.
**Direct execution:**
```powershell
# Export current settings for a module.
PowerToys.DSC.exe export --resource 'settings' --module <ModuleName>
```
### Set desired state
Apply a configuration to a module, updating only the properties that differ
from the desired state.
**Direct execution:**
```powershell
# Set desired configuration for a module.
$input = '{
"settings": {
"properties": {
"keepDisplayOn": true,
"mode": 1
},
"name": "Awake",
"version": "0.0.1"
}
}'
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $input
```
**DSC configuration:**
```yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Configure Awake
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 1
name: Awake
version: 0.0.1
```
**WinGet configuration:**
```yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install and configure PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
- name: Configure FancyZones
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_shiftDrag: true
fancyzones_mouseSwitch: true
fancyzones_displayOrWorkAreaChange_moveWindows: true
name: FancyZones
version: 1.0
```
### Test desired state
Verify whether the current configuration matches the desired state.
**Direct execution:**
```powershell
# Test if current state matches desired state.
$input = '{
"settings": {
"properties": {
"keepDisplayOn": true,
"mode": 1
},
"name": "Awake",
"version": "0.0.1"
}
}'
PowerToys.DSC.exe test --resource 'settings' --module Awake --input $input
```
The output includes an `_inDesiredState` property indicating whether the
configuration matches (`true`) or differs (`false`).
**DSC configuration:**
```yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Test Awake configuration
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 1
name: Awake
version: 0.0.1
```
### Get schema
Generate the JSON schema for a module's settings, describing all configurable
properties and their types.
**Direct execution:**
```powershell
# Get JSON schema for a module.
PowerToys.DSC.exe schema --resource 'settings' --module Awake
# Format for readability.
PowerToys.DSC.exe schema --resource 'settings' --module Awake `
| ConvertFrom-Json | ConvertTo-Json -Depth 10
```
### Generate manifest
Create a DSC resource manifest file for one or all modules.
**Direct execution:**
```powershell
# Generate manifest for a specific module.
$outputDir = "C:\manifests"
PowerToys.DSC.exe manifest --resource 'settings' --module Awake `
--outputDir $outputDir
# Generate manifests for all modules.
PowerToys.DSC.exe manifest --resource 'settings' --outputDir $outputDir
# Print manifest to console (omit --outputDir).
PowerToys.DSC.exe manifest --resource 'settings' --module Awake
```
## Examples
### Example 1 - Enable and configure FancyZones
This example enables FancyZones and configures window dragging behavior using
direct execution.
```powershell
# Get current FancyZones settings.
$current = PowerToys.DSC.exe get --resource 'settings' --module FancyZones `
| ConvertFrom-Json
# Modify settings.
$desired = @{
settings = @{
properties = @{
fancyzones_shiftDrag = $true
fancyzones_mouseSwitch = $true
fancyzones_displayOrWorkAreaChange_moveWindows = $true
}
name = "FancyZones"
version = "1.0"
}
} | ConvertTo-Json -Depth 10 -Compress
# Apply configuration.
PowerToys.DSC.exe set --resource 'settings' --module FancyZones `
--input $desired
```
### Example 2 - Configure multiple utilities with DSC
This example configures multiple PowerToys utilities in a single DSC
configuration.
```yaml
# powertoys-multi.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Enable PowerToys utilities
type: Microsoft.PowerToys/AppSettings
properties:
settings:
properties:
Enabled:
Awake: true
FancyZones: true
PowerRename: true
ColorPicker: true
name: App
version: 1.0
- name: Configure Awake
type: Microsoft.PowerToys/AwakeSettings
properties:
settings:
properties:
keepDisplayOn: true
mode: 1
name: Awake
version: 0.0.1
- name: Configure ColorPicker
type: Microsoft.PowerToys/ColorPickerSettings
properties:
settings:
properties:
changecursor: true
copiedcolorrepresentation: "HEX"
name: ColorPicker
version: 1.0
```
### Example 3 - Install and configure with WinGet
This example installs PowerToys and applies configuration using WinGet.
```yaml
# winget-powertoys-setup.yaml
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
metadata:
winget:
processor: dscv3
resources:
- name: Install PowerToys
type: Microsoft.WinGet.DSC/WinGetPackage
properties:
id: Microsoft.PowerToys
source: winget
ensure: Present
- name: Configure general settings
type: Microsoft.PowerToys/AppSettings
properties:
settings:
properties:
run_elevated: true
startup: true
theme: "dark"
name: App
version: 1.0
- name: Configure FancyZones
type: Microsoft.PowerToys/FancyZonesSettings
properties:
settings:
properties:
fancyzones_shiftDrag: true
fancyzones_zoneSetChange_moveWindows: true
name: FancyZones
version: 1.0
- name: Configure ImageResizer
type: Microsoft.PowerToys/ImageResizerSettings
properties:
settings:
properties:
ImageResizerSizes:
- Name: Small
Width: 854
Height: 480
Unit: Pixel
Fit: Fit
- Name: Medium
Width: 1920
Height: 1080
Unit: Pixel
Fit: Fit
name: ImageResizer
version: 1.0
```
Apply the configuration:
```powershell
winget configure winget-powertoys-setup.yaml
```
### Example 4 - Test configuration drift
This example tests whether the current configuration matches the desired
state.
```powershell
# Define desired state.
$desired = @{
settings = @{
properties = @{
keepDisplayOn = $true
mode = 1
}
name = "Awake"
version = "0.0.1"
}
} | ConvertTo-Json -Depth 10 -Compress
# Test for drift.
$result = PowerToys.DSC.exe test --resource 'settings' --module Awake `
--input $desired | ConvertFrom-Json
if ($result._inDesiredState) {
Write-Host "Configuration is in desired state"
} else {
Write-Host "Configuration has drifted from desired state"
# Apply configuration.
PowerToys.DSC.exe set --resource 'settings' --module Awake `
--input $desired
}
```
### Example 5 - Export all module configurations
This example exports configuration for all modules.
```powershell
# Get list of all modules.
$modules = PowerToys.DSC.exe modules --resource 'settings'
# Export each module's configuration.
$configurations = @{}
foreach ($module in $modules) {
$config = PowerToys.DSC.exe export --resource 'settings' `
--module $module | ConvertFrom-Json
$configurations[$module] = $config
}
# Save to file.
$configurations | ConvertTo-Json -Depth 10 `
| Out-File "powertoys-backup.json"
```
## See also
- [PowerToys DSC Overview][02]
- [Module Documentation][01]
- [WinGet Configuration][03]
<!-- Link reference definitions -->
[01]: ./modules/
[02]: ./overview.md
[03]: https://learn.microsoft.com/windows/package-manager/configuration/

View File

@@ -9,7 +9,7 @@
<Fragment>
<!-- Resource directories should be added only if the installer is built on the build farm -->
<?ifdef env.IsPipeline?>
<?foreach ParentDirectory in INSTALLFOLDER;WinUI3AppsInstallFolder;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
<?foreach ParentDirectory in INSTALLFOLDER;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
<DirectoryRef Id="$(var.ParentDirectory)">
<!-- Resource file directories -->
<?foreach Language in $(var.LocLanguageList)?>
@@ -171,12 +171,6 @@
</RegistryKey>
<File Id="FancyZonesEditor_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.FancyZonesEditor.resources.dll" />
</Component>
<Component Id="ImageResizer_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" Guid="$(var.CompGUIDPrefix)02">
<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)\WinUI3Apps\$(var.Language)\PowerToys.ImageResizer.resources.dll" />
</Component>
<Component Id="ColorPicker_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER" Guid="$(var.CompGUIDPrefix)03">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="ColorPicker_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes" />
@@ -459,7 +453,6 @@
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)HistoryPluginFolder" Directory="Resource$(var.IdSafeLanguage)HistoryPluginFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)PowerToysPluginFolder" Directory="Resource$(var.IdSafeLanguage)PowerToysPluginFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)ValueGeneratorPluginFolder" Directory="Resource$(var.IdSafeLanguage)ValueGeneratorPluginFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" On="uninstall"/>
<?undef IdSafeLanguage?>
<?endforeach?>
</Component>

View File

@@ -131,7 +131,25 @@ if ($platform -ceq "arm64") {
}
#BaseApplications
# WORKAROUND: Exclude ImageResizer files that leak into the root output directory.
# ImageResizerCLI (Exe, SelfContained) has a ProjectReference to ImageResizerUI (WinExe, SelfContained).
# MSBuild copies the referenced WinExe's apphost (.exe, .deps.json, .runtimeconfig.json) to the root
# output directory as a side effect. These files are incomplete (missing the managed .dll) and should
# not be included in the installer. The complete ImageResizer files are in WinUI3Apps/ and are handled
# by WinUI3ApplicationsFiles. TODO: Refactor ImageResizer to use a shared Library project instead.
Generate-FileList -fileDepsJson "" -fileListName BaseApplicationsFiles -wxsFilePath $PSScriptRoot\BaseApplications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release"
# Remove leaked ImageResizer artifacts from BaseApplications
$baseAppWxsPath = "$PSScriptRoot\BaseApplications.wxs"
$baseAppWxs = Get-Content $baseAppWxsPath -Raw
$baseAppWxs = $baseAppWxs -replace 'PowerToys\.ImageResizer\.exe;?', ''
$baseAppWxs = $baseAppWxs -replace 'PowerToys\.ImageResizer\.deps\.json;?', ''
$baseAppWxs = $baseAppWxs -replace 'PowerToys\.ImageResizer\.runtimeconfig\.json;?', ''
# Clean up trailing/double semicolons left after removal
$baseAppWxs = $baseAppWxs -replace ';;+', ';'
$baseAppWxs = $baseAppWxs -replace '=;', '='
$baseAppWxs = $baseAppWxs -replace ';"', '"'
Set-Content -Path $baseAppWxsPath -Value $baseAppWxs
Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs
#WinUI3Applications

View File

@@ -105,6 +105,22 @@ namespace Microsoft.PowerToys.Common.UI.Controls
{
switch (key)
{
case nameof(VirtualKey.Up):
SetGlyphOrText("\uE0E4", VirtualKey.Up);
break;
case nameof(VirtualKey.Down):
SetGlyphOrText("\uE0E5", VirtualKey.Down);
break;
case nameof(VirtualKey.Left):
SetGlyphOrText("\uE0E2", VirtualKey.Left);
break;
case nameof(VirtualKey.Right):
SetGlyphOrText("\uE0E3", VirtualKey.Right);
break;
case "Copilot":
_keyPresenter.Style = (Style)Application.Current.Resources["CopilotKeyCharPresenterStyle"];
break;
@@ -141,19 +157,19 @@ namespace Microsoft.PowerToys.Common.UI.Controls
break;
case VirtualKey.Up:
_keyPresenter.Content = "\uE0E4";
SetGlyphOrText("\uE0E4", virtualKey);
break;
case VirtualKey.Down:
_keyPresenter.Content = "\uE0E5";
SetGlyphOrText("\uE0E5", virtualKey);
break;
case VirtualKey.Left:
_keyPresenter.Content = "\uE0E2";
SetGlyphOrText("\uE0E2", virtualKey);
break;
case VirtualKey.Right:
_keyPresenter.Content = "\uE0E3";
SetGlyphOrText("\uE0E3", virtualKey);
break;
case VirtualKey.LeftWindows:

View File

@@ -300,10 +300,6 @@ namespace winrt::PowerToys::Interop::implementation
{
return CommonSharedConstants::OPEN_NEW_KEYBOARD_MANAGER_EVENT;
}
hstring Constants::ToggleKeyboardManagerActiveEvent()
{
return CommonSharedConstants::TOGGLE_KEYBOARD_MANAGER_ACTIVE_EVENT;
}
hstring Constants::KeyboardManagerEngineInstanceMutex()
{
return CommonSharedConstants::KEYBOARD_MANAGER_ENGINE_INSTANCE_MUTEX;

View File

@@ -78,7 +78,6 @@ namespace winrt::PowerToys::Interop::implementation
static hstring MWBToggleEasyMouseEvent();
static hstring MWBReconnectEvent();
static hstring OpenNewKeyboardManagerEvent();
static hstring ToggleKeyboardManagerActiveEvent();
static hstring KeyboardManagerEngineInstanceMutex();
};
}

View File

@@ -75,7 +75,6 @@ namespace PowerToys
static String MWBToggleEasyMouseEvent();
static String MWBReconnectEvent();
static String OpenNewKeyboardManagerEvent();
static String ToggleKeyboardManagerActiveEvent();
static String KeyboardManagerEngineInstanceMutex();
}
}

View File

@@ -151,6 +151,7 @@ namespace CommonSharedConstants
const wchar_t ZOOMIT_BREAK_EVENT[] = L"Local\\PowerToysZoomIt-BreakEvent-17f2e63c-4c56-41dd-90a0-2d12f9f50c6b";
const wchar_t ZOOMIT_LIVEZOOM_EVENT[] = L"Local\\PowerToysZoomIt-LiveZoomEvent-390bf0c7-616f-47dc-bafe-a2d228add20d";
const wchar_t ZOOMIT_SNIP_EVENT[] = L"Local\\PowerToysZoomIt-SnipEvent-2fd9c211-436d-4f17-a902-2528aaae3e30";
const wchar_t ZOOMIT_SNIPOCR_EVENT[] = L"Local\\PowerToysZoomIt-SnipOcrEvent-a7c3b1d2-9e4f-4a6b-8d5c-1f2e3a4b5c6d";
const wchar_t ZOOMIT_RECORD_EVENT[] = L"Local\\PowerToysZoomIt-RecordEvent-74539344-eaad-4711-8e83-23946e424512";
// Path to the events used by PowerDisplay
@@ -172,7 +173,6 @@ namespace CommonSharedConstants
// Path to events used by Keyboard Manager
const wchar_t OPEN_NEW_KEYBOARD_MANAGER_EVENT[] = L"Local\\PowerToysOpenNewKeyboardManagerEvent-9c1d2e3f-4b5a-6c7d-8e9f-0a1b2c3d4e5f";
const wchar_t TOGGLE_KEYBOARD_MANAGER_ACTIVE_EVENT[] = L"Local\\PowerToysToggleKeyboardManagerActiveEvent-7f3a1d5c-2e94-4ff4-8b6a-90fd2bc4d2a7";
const wchar_t KEYBOARD_MANAGER_ENGINE_INSTANCE_MUTEX[] = L"Local\\PowerToys_KBMEngine_InstanceMutex";
// used from quick access window

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
//============================================================================
//
// PanoramaCapture.h
//
// Panorama (scrolling) screen capture and stitching.
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#pragma once
#include <windows.h>
#include <vector>
// Globals shared with the main ZoomIt module.
extern bool g_PanoramaCaptureActive;
extern bool g_PanoramaStopRequested;
extern bool g_PanoramaDebugMode;
// Run the panorama capture flow: select a region, capture frames while
// scrolling, stitch them together, and copy the result to the clipboard.
bool RunPanoramaCaptureToClipboard( HWND hWnd );
// Run the panorama capture flow and save the result to a file via a
// Save As dialog instead of copying to the clipboard.
bool RunPanoramaCaptureToFile( HWND hWnd );
// Run a synthetic, non-interactive self-test for panorama frame stitching.
// Returns true when stitching output matches expected dimensions/content.
#ifdef _DEBUG
bool RunPanoramaStitchSelfTest();
// Re-stitch frames from a specific debug dump directory.
bool RunPanoramaStitchDumpDirectory( const wchar_t* path );
// Re-stitch accepted panorama frames from the latest debug dump session and
// save output into that same session directory.
bool RunPanoramaStitchLatestDebugDump();
#endif

View File

@@ -11,6 +11,23 @@
#include "Utility.h"
#include "WindowsVersions.h"
static void SelectRectangleDebugLog( const wchar_t* format, ... )
{
#if _DEBUG
wchar_t message[1024]{};
va_list args;
#pragma warning( push )
#pragma warning( disable : 26492 )
va_start( args, format );
#pragma warning( pop )
vswprintf_s( message, format, args );
va_end( args );
OutputDebugStringW( message );
#else
UNREFERENCED_PARAMETER( format );
#endif
}
//----------------------------------------------------------------------------
//
// SelectRectangle::Start
@@ -18,6 +35,12 @@
//----------------------------------------------------------------------------
bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
{
m_stopping = false;
SelectRectangleDebugLog( L"[SelectRectangle] Start owner=%p fullMonitor=%d minSize=%d alpha=%u\n",
ownerWindow,
fullMonitor ? 1 : 0,
MinSize(),
Alpha() );
WNDCLASSW windowClass{};
windowClass.lpfnWndProc = []( HWND window, UINT message, WPARAM wordParam, LPARAM longParam ) -> LRESULT
{
@@ -46,10 +69,16 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
m_cancel = false;
auto rect = GetMonitorRectFromCursor();
SelectRectangleDebugLog( L"[SelectRectangle] Monitor rect=(%ld,%ld)-(%ld,%ld)\n",
rect.left,
rect.top,
rect.right,
rect.bottom );
m_window = wil::unique_hwnd( CreateWindowExW( WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, m_className, nullptr, WS_POPUP,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, ownerWindow,
nullptr, nullptr, this ) );
THROW_LAST_ERROR_IF_NULL( m_window.get() );
SelectRectangleDebugLog( L"[SelectRectangle] Window created hwnd=%p\n", m_window.get() );
if( fullMonitor )
{
@@ -58,7 +87,11 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
}
else
{
SetLayeredWindowAttributes( m_window.get(), 0, Alpha(), LWA_ALPHA );
const BOOL layered = SetLayeredWindowAttributes( m_window.get(), 0, Alpha(), LWA_ALPHA );
SelectRectangleDebugLog( L"[SelectRectangle] SetLayeredWindowAttributes(alpha=%u) success=%d err=%lu\n",
Alpha(),
layered ? 1 : 0,
layered ? 0 : GetLastError() );
}
ShowWindow( m_window.get(), SW_SHOW );
@@ -69,6 +102,7 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
GetClipCursor( &m_oldClipRect );
ClipCursor( &rect );
m_setClip = true;
SelectRectangleDebugLog( L"[SelectRectangle] Cursor clipped to monitor bounds\n" );
}
MSG message;
@@ -78,13 +112,20 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
DispatchMessageW( &message );
if( m_cancel )
{
SelectRectangleDebugLog( L"[SelectRectangle] Start cancelled via Stop()\n" );
return false;
}
if( m_selected )
{
SelectRectangleDebugLog( L"[SelectRectangle] Selection finalized rect=(%ld,%ld)-(%ld,%ld)\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
break;
}
}
SelectRectangleDebugLog( L"[SelectRectangle] Start complete selected=%d cancel=%d\n", m_selected ? 1 : 0, m_cancel ? 1 : 0 );
return true;
}
@@ -95,15 +136,38 @@ bool SelectRectangle::Start( HWND ownerWindow, bool fullMonitor )
//----------------------------------------------------------------------------
void SelectRectangle::Stop()
{
if( m_stopping )
{
SelectRectangleDebugLog( L"[SelectRectangle] Stop ignored due to reentrancy\n" );
return;
}
m_stopping = true;
SelectRectangleDebugLog( L"[SelectRectangle] Stop hwnd=%p selected=%d cancel=%d clip=%d rect=(%ld,%ld)-(%ld,%ld)\n",
m_window.get(),
m_selected ? 1 : 0,
m_cancel ? 1 : 0,
m_setClip ? 1 : 0,
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
if( m_setClip )
{
ClipCursor( &m_oldClipRect );
m_setClip = false;
}
m_window.reset();
HWND window = m_window.release();
if( window != nullptr && IsWindow( window ) )
{
DestroyWindow( window );
}
m_selected = false;
m_selectedRect = {};
m_cancel = true;
m_stopping = false;
}
//----------------------------------------------------------------------------
@@ -114,11 +178,20 @@ void SelectRectangle::Stop()
void SelectRectangle::ShowSelected()
{
m_selected = true;
SelectRectangleDebugLog( L"[SelectRectangle] ShowSelected rect=(%ld,%ld)-(%ld,%ld) dpi=%u\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom,
m_dpi );
// Set the alpha to match the Windows graphics capture API yellow border
// and set the window to be transparent and disabled, so it will be skipped
// for hit testing and as a candidate for the next foreground window.
SetLayeredWindowAttributes( m_window.get(), 0, 191, LWA_ALPHA );
const BOOL layered = SetLayeredWindowAttributes( m_window.get(), 0, 191, LWA_ALPHA );
SelectRectangleDebugLog( L"[SelectRectangle] ShowSelected SetLayeredWindowAttributes(alpha=191) success=%d err=%lu\n",
layered ? 1 : 0,
layered ? 0 : GetLastError() );
SetWindowLong( m_window.get(), GWL_EXSTYLE, GetWindowLong( m_window.get(), GWL_EXSTYLE ) | WS_EX_TRANSPARENT );
EnableWindow( m_window.get(), FALSE );
@@ -144,6 +217,12 @@ void SelectRectangle::ShowSelected()
point.x += windowRect.left;
point.y += windowRect.top;
MoveWindow( m_window.get(), point.x, point.y, rect.right, rect.bottom, true );
SelectRectangleDebugLog( L"[SelectRectangle] Border window moved to (%ld,%ld) size=%ldx%ld borderWidth=%d\n",
point.x,
point.y,
rect.right,
rect.bottom,
width );
// Use a region to keep everything but the border transparent.
wil::unique_hrgn region{CreateRectRgnIndirect( &rect )};
@@ -151,6 +230,11 @@ void SelectRectangle::ShowSelected()
wil::unique_hrgn insideRegion{CreateRectRgnIndirect( &rect )};
CombineRgn( region.get(), region.get(), insideRegion.get(), RGN_XOR );
SetWindowRgn( m_window.get(), region.release(), true );
SelectRectangleDebugLog( L"[SelectRectangle] Border window region applied\n" );
// Force immediate paint so the yellow border is visible instead of a
// transient black frame from the class background brush.
RedrawWindow( m_window.get(), nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME );
}
//----------------------------------------------------------------------------
@@ -162,6 +246,7 @@ void SelectRectangle::UpdateOwner( HWND window )
{
if( m_window != nullptr )
{
SelectRectangleDebugLog( L"[SelectRectangle] UpdateOwner hwnd=%p newOwner=%p\n", m_window.get(), window );
SetWindowLongPtr( m_window.get(), GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(window) );
SetWindowPos( m_window.get(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE );
}
@@ -179,10 +264,24 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
case WM_CREATE:
m_dpi = GetDpiForWindowHelper( window );
SetWindowDisplayAffinity( window, WDA_EXCLUDEFROMCAPTURE );
SelectRectangleDebugLog( L"[SelectRectangle] WM_CREATE hwnd=%p dpi=%u\n", window, m_dpi );
return 0;
case WM_DESTROY:
Stop();
SelectRectangleDebugLog( L"[SelectRectangle] WM_DESTROY hwnd=%p\n", window );
if( m_window.get() == window )
{
m_window.release();
}
if( m_setClip )
{
ClipCursor( &m_oldClipRect );
m_setClip = false;
}
m_selected = false;
m_selectedRect = {};
m_cancel = true;
m_stopping = false;
return 0;
case WM_LBUTTONDOWN:
@@ -190,6 +289,7 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
SetCapture( window );
m_startPoint = { GET_X_LPARAM( longParam ), GET_Y_LPARAM( longParam ) };
SelectRectangleDebugLog( L"[SelectRectangle] WM_LBUTTONDOWN startPoint=(%ld,%ld)\n", m_startPoint.x, m_startPoint.y );
[[fallthrough]];
}
case WM_MOUSEMOVE:
@@ -199,6 +299,11 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
GetClientRect( window, &rect );
POINT point{ GET_X_LPARAM( longParam ), GET_Y_LPARAM( longParam ) };
m_selectedRect = ForceRectInBounds( RectFromPointsMinSize( m_startPoint, point, MinSize() ), rect );
SelectRectangleDebugLog( L"[SelectRectangle] Drag rect=(%ld,%ld)-(%ld,%ld)\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
// Use a region to carve out the selected rectangle.
wil::unique_hrgn region{CreateRectRgnIndirect( &m_selectedRect )};
@@ -211,6 +316,7 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
case WM_KEYDOWN:
if( wordParam == VK_ESCAPE )
{
SelectRectangleDebugLog( L"[SelectRectangle] WM_KEYDOWN Escape pressed\n" );
Stop();
}
return 0;
@@ -218,12 +324,18 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
case WM_KILLFOCUS:
if( !m_selected )
{
SelectRectangleDebugLog( L"[SelectRectangle] WM_KILLFOCUS before selection complete\n" );
Stop();
}
return 0;
case WM_LBUTTONUP:
{
SelectRectangleDebugLog( L"[SelectRectangle] WM_LBUTTONUP selectedRect=(%ld,%ld)-(%ld,%ld)\n",
m_selectedRect.left,
m_selectedRect.top,
m_selectedRect.right,
m_selectedRect.bottom );
if( m_setClip )
{
ClipCursor( &m_oldClipRect );
@@ -249,6 +361,11 @@ LRESULT SelectRectangle::WindowProc( HWND window, UINT message, WPARAM wordParam
RECT rect;
GetClientRect( window, &rect );
SelectRectangleDebugLog( L"[SelectRectangle] WM_PAINT selected border rect=(%ld,%ld)-(%ld,%ld)\n",
rect.left,
rect.top,
rect.right,
rect.bottom );
// Draw a border matching the Windows graphics capture API border.
// The outer frame is yellow and two logical pixels wide, while the

View File

@@ -20,10 +20,14 @@ public:
void MinSize( int minSize ) { m_minSize = minSize; }
int MinSize() const { return m_minSize; }
RECT SelectedRect() const { return m_selectedRect; }
bool IsActive() const { return m_window != nullptr; }
bool Start( HWND ownerWindow = nullptr, bool fullMonitor = false );
void Stop();
void UpdateOwner( HWND window );
void Hide() { if( m_window ) ShowWindow( m_window.get(), SW_HIDE ); }
void Show() { if( m_window ) ShowWindow( m_window.get(), SW_SHOWNA ); }
void SetExcludeFromCapture( bool exclude ) { if( m_window ) SetWindowDisplayAffinity( m_window.get(), exclude ? WDA_EXCLUDEFROMCAPTURE : WDA_NONE ); }
private:
BYTE m_alpha = 176;
@@ -36,6 +40,7 @@ private:
RECT m_oldClipRect{};
bool m_selected{ false };
bool m_setClip{ false };
bool m_stopping{ false };
POINT m_startPoint{};
wil::unique_hwnd m_window;

View File

@@ -406,7 +406,10 @@ static bool LoadGifFrames(const std::wstring& gifPath, VideoRecordingSession::Tr
const auto& lastFrame = pData->gifFrames.back();
pData->videoDuration = winrt::TimeSpan{ lastFrame.start.count() + lastFrame.duration.count() };
pData->trimEnd = pData->videoDuration;
if( pData->trimEnd.count() <= 0 )
{
pData->trimEnd = pData->videoDuration;
}
pData->gifFramesLoaded = true;
pData->gifLastFrameIndex = 0;
@@ -721,13 +724,9 @@ namespace
SetDlgItemText(hDlg, IDC_TRIM_DURATION_LABEL, durationText.c_str());
}
// Enable OK when trimming is active (even if unchanged since dialog opened),
// or when the user changed the selection (including reverting to full length).
const bool trimChanged = (pData->trimStart.count() != pData->originalTrimStart.count()) ||
(pData->trimEnd.count() != pData->originalTrimEnd.count());
const bool trimIsActive = (pData->trimStart.count() > 0) ||
(pData->videoDuration.count() > 0 && pData->trimEnd.count() < pData->videoDuration.count());
EnableWindow(GetDlgItem(hDlg, IDOK), trimChanged || trimIsActive);
// Always enable OK so users can close the dialog after previewing
// without being forced to use Cancel.
EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
}
RECT GetTimelineTrackRect(const RECT& clientRect, UINT dpi)
@@ -1345,7 +1344,10 @@ public:
auto trimResult = VideoRecordingSession::ShowTrimDialog(hParent, m_videoPath, *m_pTrimStart, *m_pTrimEnd);
if (trimResult == IDOK)
{
*m_pShouldTrim = true;
// Trim values are only written back when the user actually
// changed the selection, so a non-zero trimEnd means a
// real trim is requested.
*m_pShouldTrim = (m_pTrimEnd->count() > 0);
}
else if( trimResult == IDCANCEL )
{
@@ -1502,12 +1504,13 @@ INT_PTR VideoRecordingSession::ShowTrimDialog(
HWND hParent,
const std::wstring& videoPath,
winrt::TimeSpan& trimStart,
winrt::TimeSpan& trimEnd)
winrt::TimeSpan& trimEnd,
bool standaloneMode)
{
std::promise<INT_PTR> resultPromise;
auto resultFuture = resultPromise.get_future();
std::thread staThread([hParent, videoPath, &trimStart, &trimEnd, promise = std::move(resultPromise)]() mutable
std::thread staThread([hParent, videoPath, &trimStart, &trimEnd, standaloneMode, promise = std::move(resultPromise)]() mutable
{
bool coInitialized = false;
try
@@ -1525,7 +1528,7 @@ INT_PTR VideoRecordingSession::ShowTrimDialog(
try
{
INT_PTR dlgResult = ShowTrimDialogInternal(hParent, videoPath, trimStart, trimEnd);
INT_PTR dlgResult = ShowTrimDialogInternal(hParent, videoPath, trimStart, trimEnd, standaloneMode);
promise.set_value(dlgResult);
}
catch (const winrt::hresult_error& e)
@@ -1584,7 +1587,8 @@ INT_PTR VideoRecordingSession::ShowTrimDialogInternal(
HWND hParent,
const std::wstring& videoPath,
winrt::TimeSpan& trimStart,
winrt::TimeSpan& trimEnd)
winrt::TimeSpan& trimEnd,
bool standaloneMode)
{
TrimDialogData data;
data.videoPath = videoPath;
@@ -1592,6 +1596,7 @@ INT_PTR VideoRecordingSession::ShowTrimDialogInternal(
data.trimStart = trimStart;
data.trimEnd = trimEnd;
data.isGif = IsGifPath(videoPath);
data.standaloneMode = standaloneMode;
if (data.isGif)
{
@@ -1786,8 +1791,17 @@ INT_PTR VideoRecordingSession::ShowTrimDialogInternal(
if (result == IDOK)
{
trimStart = data.trimStart;
trimEnd = data.trimEnd;
// Only write back trim values when the user actually changed the
// selection. This lets the caller distinguish "confirmed without
// trimming" (preview-only) from a real trim operation.
const bool selectionChanged =
(data.trimStart.count() != data.originalTrimStart.count()) ||
(data.trimEnd.count() != data.originalTrimEnd.count());
if (selectionChanged)
{
trimStart = data.trimStart;
trimEnd = data.trimEnd;
}
}
return result;
@@ -3890,6 +3904,12 @@ INT_PTR CALLBACK VideoRecordingSession::TrimDialogProc(HWND hDlg, UINT message,
// Make OK the default button
SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
// In standalone mode, change OK button text to "Save As"
if (pData->standaloneMode)
{
SetDlgItemText(hDlg, IDOK, L"Save As");
}
// Subclass the dialog to handle resize grip hit testing
SetWindowSubclass(hDlg, TrimDialogSubclassProc, 0, reinterpret_cast<DWORD_PTR>(pData));
@@ -5029,6 +5049,115 @@ INT_PTR CALLBACK VideoRecordingSession::TrimDialogProc(HWND hDlg, UINT message,
case IDOK:
pData = reinterpret_cast<TrimDialogData*>(GetWindowLongPtr(hDlg, DWLP_USER));
StopPlayback(hDlg, pData);
if (pData->standaloneMode)
{
// In standalone mode, "Save As" shows a save dialog and performs the trim
auto saveDialog = wil::CoCreateInstance<::IFileSaveDialog>(CLSID_FileSaveDialog);
FILEOPENDIALOGOPTIONS options;
if (SUCCEEDED(saveDialog->GetOptions(&options)))
saveDialog->SetOptions(options | FOS_FORCEFILESYSTEM);
wil::com_ptr<::IShellItem> videosItem;
if (SUCCEEDED(SHGetKnownFolderItem(FOLDERID_Videos, KF_FLAG_DEFAULT, nullptr,
IID_IShellItem, (void**)videosItem.put())))
saveDialog->SetDefaultFolder(videosItem.get());
// Derive suggested filename from source
std::wstring suggestedName;
{
auto pos = pData->videoPath.find_last_of(L"\\/");
suggestedName = (pos != std::wstring::npos) ? pData->videoPath.substr(pos + 1) : pData->videoPath;
auto dot = suggestedName.find_last_of(L'.');
if (dot != std::wstring::npos)
suggestedName.insert(dot, L"_trimmed");
else
suggestedName += L"_trimmed";
}
if (pData->isGif)
{
saveDialog->SetDefaultExtension(L".gif");
COMDLG_FILTERSPEC fileTypes[] = { { L"GIF Animation", L"*.gif" } };
saveDialog->SetFileTypes(_countof(fileTypes), fileTypes);
}
else
{
saveDialog->SetDefaultExtension(L".mp4");
COMDLG_FILTERSPEC fileTypes[] = { { L"MP4 Video", L"*.mp4" } };
saveDialog->SetFileTypes(_countof(fileTypes), fileTypes);
}
saveDialog->SetFileName(suggestedName.c_str());
saveDialog->SetTitle(L"ZoomIt: Save Trimmed Video As...");
HRESULT hr = saveDialog->Show(hDlg);
if (FAILED(hr))
{
// User cancelled save dialog — return to trim editor
return TRUE;
}
wil::com_ptr<::IShellItem> resultItem;
THROW_IF_FAILED(saveDialog->GetResult(resultItem.put()));
wil::unique_cotaskmem_string savePath;
THROW_IF_FAILED(resultItem->GetDisplayName(SIGDN_FILESYSPATH, savePath.put()));
// Capture what we need before closing the dialog
std::wstring videoPath = pData->videoPath;
bool isGif = pData->isGif;
auto trimStart = pData->trimStart;
auto trimEnd = pData->trimEnd;
std::wstring savePathStr(savePath.get());
// Close the trim dialog immediately
EndDialog(hDlg, IDOK);
// Perform the trim after the dialog is closed
try
{
auto trimOp = isGif
? TrimGifAsync(videoPath, trimStart, trimEnd)
: TrimVideoAsync(videoPath, trimStart, trimEnd);
// Pump messages while waiting for async operation
while (trimOp.Status() == winrt::AsyncStatus::Started)
{
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
auto trimmedPath = std::wstring(trimOp.GetResults());
if (trimmedPath.empty())
{
MessageBox(nullptr, L"Failed to trim video.", L"Error", MB_OK | MB_ICONERROR);
return TRUE;
}
// Copy trimmed file to the user-chosen save location
if (!CopyFile(trimmedPath.c_str(), savePathStr.c_str(), FALSE))
{
MessageBox(nullptr, L"Failed to save the trimmed file.", L"Error", MB_OK | MB_ICONERROR);
DeleteFile(trimmedPath.c_str());
return TRUE;
}
// Clean up temp file
DeleteFile(trimmedPath.c_str());
}
catch (...)
{
MessageBox(nullptr, L"Failed to trim video.", L"Error", MB_OK | MB_ICONERROR);
}
return TRUE;
}
// Trim times are already set by mouse dragging
EndDialog(hDlg, IDOK);
return TRUE;

View File

@@ -132,6 +132,7 @@ public:
bool isDragging{ false };
int lastPlayheadX{ -1 }; // Track last playhead pixel position for efficient invalidation
MMRESULT mmTimerId{ 0 }; // Multimedia timer for smooth MP4 playback
bool standaloneMode{ false }; // When true, OK becomes "Save As" and handles file saving directly
// Helper to convert time to pixel position
int TimeToPixel(winrt::Windows::Foundation::TimeSpan time, int timelineWidth) const
@@ -162,7 +163,8 @@ public:
HWND hParent,
const std::wstring& videoPath,
winrt::Windows::Foundation::TimeSpan& trimStart,
winrt::Windows::Foundation::TimeSpan& trimEnd);
winrt::Windows::Foundation::TimeSpan& trimEnd,
bool standaloneMode = false);
private:
static INT_PTR CALLBACK TrimDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
@@ -179,7 +181,8 @@ private:
HWND hParent,
const std::wstring& videoPath,
winrt::Windows::Foundation::TimeSpan& trimStart,
winrt::Windows::Foundation::TimeSpan& trimEnd);
winrt::Windows::Foundation::TimeSpan& trimEnd,
bool standaloneMode = false);
private:
VideoRecordingSession(

View File

@@ -113,26 +113,26 @@ END
// Dialog
//
OPTIONS DIALOGEX 0, 0, 299, 325
OPTIONS DIALOGEX 0, 0, 299, 331
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "ZoomIt - Sysinternals: www.sysinternals.com"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,186,306,50,14
PUSHBUTTON "Cancel",IDCANCEL,243,306,50,14
LTEXT "ZoomIt v10.1",IDC_VERSION,42,7,73,10
DEFPUSHBUTTON "OK",IDOK,184,308,50,14
PUSHBUTTON "Cancel",IDCANCEL,241,308,50,14
LTEXT "ZoomIt v11.0",IDC_VERSION,42,7,73,10
LTEXT "Copyright \251 2006-2026 Mark Russinovich",IDC_COPYRIGHT,42,17,251,8
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
"SysLink",WS_TABSTOP,42,26,150,9
ICON "APPICON",IDC_STATIC,12,9,20,20
CONTROL "Show tray icon",IDC_SHOW_TRAY_ICON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,295,105,10
CONTROL "",IDC_TAB,"SysTabControl32",TCS_MULTILINE | WS_TABSTOP,8,46,285,247
CONTROL "Run ZoomIt when Windows starts",IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,309,122,10
CONTROL "Show tray icon",IDC_SHOW_TRAY_ICON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,302,105,10
CONTROL "",IDC_TAB,"SysTabControl32",TCS_MULTILINE | WS_TABSTOP,8,45,285,255
CONTROL "Run ZoomIt when Windows starts",IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,316,122,10
END
ADVANCED_BREAK DIALOGEX 0, 0, 209, 225
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
ADVANCED_BREAK DIALOGEX 0, 0, 209, 223
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Advanced Break Options"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@@ -158,8 +158,8 @@ BEGIN
EDITTEXT IDC_BACKGROUND_FILE,62,164,125,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "&...",IDC_BACKGROUND_BROWSE,188,164,13,11
CONTROL "Scale to screen:",IDC_CHECK_BACKGROUND_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,58,180,67,10,WS_EX_RIGHT
DEFPUSHBUTTON "OK",IDOK,97,199,50,14
PUSHBUTTON "Cancel",IDCANCEL,150,199,50,14
DEFPUSHBUTTON "OK",IDOK,97,202,50,14
PUSHBUTTON "Cancel",IDCANCEL,150,202,50,14
LTEXT "Alarm Sound File:",IDC_STATIC_SOUND_FILE,61,26,56,8
LTEXT "Timer Opacity:",IDC_STATIC,8,59,48,8
LTEXT "Timer Position:",IDC_STATIC,8,77,48,8
@@ -215,21 +215,23 @@ BEGIN
GROUPBOX "Sample",IDC_TEXT_FONT,8,61,99,28
END
BREAK DIALOGEX 0, 0, 260, 123
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_SYSMENU
BREAK DIALOGEX 0, 0, 260, 159
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_BREAK_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,52,67,80,12
EDITTEXT IDC_TIMER,52,86,31,13,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_TIMER,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,66,86,11,12
LTEXT "minutes",IDC_STATIC,88,88,25,8
PUSHBUTTON "&Advanced",IDC_ADVANCED_BREAK,192,102,41,14
LTEXT "Enter timer mode by using the ZoomIt tray icon's Break menu item. Increase and decrease time with the arrow keys. If you Alt-Tab away from the timer window, reactivate it by left-clicking on the ZoomIt tray icon. Exit timer mode with Escape. ",IDC_STATIC,7,7,230,33
LTEXT "Start Timer:",IDC_STATIC,7,70,39,8
LTEXT "Timer:",IDC_STATIC,7,88,20,8
LTEXT "Change the break timer color using the same keys that the drawing color. The break timer font is the same as text font.",IDC_STATIC,7,45,230,20
CONTROL "",IDC_BREAK_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,52,74,80,12
EDITTEXT IDC_TIMER,52,93,31,13,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_TIMER,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,66,93,11,12
LTEXT "minutes",IDC_STATIC,88,95,25,8
PUSHBUTTON "&Advanced",IDC_ADVANCED_BREAK,213,140,41,14
LTEXT "Enter timer mode by using the ZoomIt tray icon's Break menu item. Increase and decrease time with the arrow keys. If you Alt-Tab away from the timer window, reactivate it by left-clicking on the ZoomIt tray icon. Exit timer mode with Escape. ",IDC_STATIC,7,7,242,33
LTEXT "Start Timer:",IDC_STATIC,7,77,39,8
LTEXT "Timer:",IDC_STATIC,7,95,20,8
LTEXT "Change the break timer color using the same keys that the drawing color, including background color. The break timer font is the same as text font.",IDC_STATIC,7,45,241,26
CONTROL "Show Time Elapsed After Expiration:",IDC_CHECK_SHOW_EXPIRED,
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,8,104,132,10
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,111,130,10
CONTROL "Lock Workstation During Break:",IDC_CHECK_LOCK_WORKSTATION,
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,126,113,10
END
1543 DIALOGEX 100, 50, 216, 131
@@ -249,19 +251,19 @@ BEGIN
CTEXT "AaBbYyZz",1092,16,88,127,31,SS_NOPREFIX | NOT WS_VISIBLE
END
LIVEZOOM DIALOGEX 0, 0, 260, 134
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_SYSMENU
LIVEZOOM DIALOGEX 0, 0, 317, 136
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_LIVE_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,69,108,80,12
LTEXT "LiveZoom mode is supported on Windows 7 and higher where window updates show while zoomed. ",IDC_STATIC,7,7,230,18
LTEXT "LiveZoom mode is supported on Windows 7 and higher where window updates show while zoomed. ",IDC_STATIC,7,7,255,18
LTEXT "LiveZoom Toggle:",IDC_STATIC,7,110,62,8
LTEXT "To enter and exit LiveZoom, enter the hotkey specified below.",IDC_STATIC,7,94,230,13
LTEXT "Note that in LiveZoom you must use Ctrl+Up and Ctrl+Down to control the zoom level. To enter drawing mode, use the standard zoom-without-draw hotkey and then escape to go back to LiveZoom.",IDC_STATIC,7,30,230,27
LTEXT "Use LiveDraw to draw and annotate the live desktop. To activate LiveDraw, enter the hotkey with the Shift key in the opposite mode. You can remove LiveDraw annotations by activating LiveDraw and enter the escape key",IDC_STATIC,7,62,230,32
LTEXT "Use LiveDraw to draw and annotate the live desktop. To activate LiveDraw, enter the hotkey with the Shift key in the opposite mode. You can remove LiveDraw annotations by activating LiveDraw and enter the escape key",IDC_STATIC,7,62,249,32
LTEXT "Note that in LiveZoom you must use Ctrl+Up and Ctrl+Down to control the zoom level. To enter drawing mode, use the standard zoom-without-draw hotkey and then escape to go back to LiveZoom.",IDC_STATIC,7,30,255,27
END
RECORD DIALOGEX 0, 0, 260, 181
RECORD DIALOGEX 0, 0, 263, 224
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@@ -282,19 +284,33 @@ BEGIN
CONTROL "Mono",IDC_MIC_MONO_MIX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,98,161,30,10
COMBOBOX IDC_MICROPHONE,81,176,152,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,32,178,47,8
PUSHBUTTON "&Trim",IDC_TRIM_FILE,207,209,53,14
END
SNIP DIALOGEX 0, 0, 260, 68
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
SNIP DIALOGEX 0, 0, 260, 80
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_SNIP_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,55,32,80,12
LTEXT "Copy a region of the screen to the clipboard or enter the hotkey with the Shift key in the opposite mode to save it to a file.",IDC_STATIC,7,7,230,19
LTEXT "Snip Toggle:",IDC_STATIC,7,33,45,8
LTEXT "Copy a region of the screen to the clipboard or enter the hotkey with the Shift key in the opposite mode to save it to a file. ",IDC_STATIC,7,7,230,19
CONTROL "",IDC_SNIP_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,67,32,80,12
LTEXT "Copy text from the selected region to the clipboard:",IDC_STATIC,7,50,230,10
LTEXT "Text Toggle:",IDC_STATIC,7,65,55,8
CONTROL "",IDC_SNIP_OCR_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,67,63,80,12
END
PANORAMA DIALOGEX 0, 0, 260, 105
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Capture a scrolling panorama of a selected screen region. Select the area, then scroll the content. Move slowly and consistently, and do not rewind to previously covered areas. Press the hotkey again or with Shift to save to a file.",IDC_STATIC,7,7,245,33
LTEXT "Panorama Toggle:",IDC_STATIC,7,74,63,8
CONTROL "",IDC_SNIP_PANORAMA_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,73,72,80,12
LTEXT "For the best results, scroll slowly and at a constant rate, do not include stationary content (like scrollbars) in the capture area, and avoid content that is changing (e.g., animations or videos). ",IDC_STATIC,7,41,245,30
END
DEMOTYPE DIALOGEX 0, 0, 260, 249
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_DEMOTYPE_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,74,154,80,12
@@ -308,11 +324,11 @@ BEGIN
LTEXT "Fast",IDC_DEMOTYPE_STATIC2,186,213,17,8
EDITTEXT IDC_DEMOTYPE_FILE,44,137,167,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Input file:",IDC_STATIC,7,139,32,8
LTEXT "When you reach the end of the file, ZoomIt will reload the file and start at the beginning. Enter the hotkey with the Shift key in the opposite mode to step back to the last [end].",IDC_STATIC,7,108,230,24
LTEXT "DemoType has ZoomIt type text specified in the input file when you enter the DemoType toggle. Simply separate snippets with the [end] keyword, or you can insert text from the clipboard if it is prefixed with the [start].",IDC_STATIC,7,7,230,24
LTEXT "When you reach the end of the file, ZoomIt will reload the file and start at the beginning. Enter the hotkey with the Shift key in the opposite mode to step back to the last [end].",IDC_STATIC,7,108,249,24
LTEXT "DemoType has ZoomIt type text specified in the input file when you enter the DemoType toggle. Simply separate snippets with the [end] keyword, or you can insert text from the clipboard if it is prefixed with the [start].",IDC_STATIC,7,7,247,24
LTEXT " - Insert pauses with the [pause:n] keyword where 'n' is seconds. ",IDC_STATIC,19,34,218,11
LTEXT "You can have ZoomIt send text automatically, or select the option to drive input with typing. ZoomIt will block keyboard input while sending output.",IDC_STATIC,7,68,230,16
LTEXT "When driving input, hit the space bar to unblock keyboard input at the end of a snippet. In auto mode, control will be returned upon completion.",IDC_STATIC,7,88,230,16
LTEXT "You can have ZoomIt send text automatically, or select the option to drive input with typing. ZoomIt will block keyboard input while sending output.",IDC_STATIC,7,68,245,16
LTEXT "When driving input, hit the space bar to unblock keyboard input at the end of a snippet. In auto mode, control will be returned upon completion.",IDC_STATIC,7,88,243,16
LTEXT "- Send text via the clipboard with [paste] and [/paste]. ",IDC_STATIC,23,45,210,8
LTEXT "- Send keystrokes with [enter], [up], [down], [left], and [right].",IDC_STATIC,23,56,210,8
END
@@ -349,13 +365,13 @@ BEGIN
"OPTIONS", DIALOG
BEGIN
RIGHTMARGIN, 293
BOTTOMMARGIN, 320
BOTTOMMARGIN, 326
END
"ADVANCED_BREAK", DIALOG
BEGIN
RIGHTMARGIN, 207
BOTTOMMARGIN, 215
BOTTOMMARGIN, 214
END
"ZOOM", DIALOG
@@ -383,7 +399,7 @@ BEGIN
BEGIN
LEFTMARGIN, 7
TOPMARGIN, 7
BOTTOMMARGIN, 116
BOTTOMMARGIN, 154
END
1543, DIALOG
@@ -395,22 +411,27 @@ BEGIN
"LIVEZOOM", DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 181
TOPMARGIN, 7
BOTTOMMARGIN, 127
BOTTOMMARGIN, 89
END
"RECORD", DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 260
TOPMARGIN, 7
BOTTOMMARGIN, 164
BOTTOMMARGIN, 223
END
"SNIP", DIALOG
BEGIN
LEFTMARGIN, 7
TOPMARGIN, 7
BOTTOMMARGIN, 61
END
"PANORAMA", DIALOG
BEGIN
END
"DEMOTYPE", DIALOG
@@ -496,6 +517,16 @@ BEGIN
0
END
ADVANCED_BREAK AFX_DIALOG_LAYOUT
BEGIN
0
END
PANORAMA AFX_DIALOG_LAYOUT
BEGIN
0
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

View File

@@ -68,8 +68,8 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100;4091;4245</DisableSpecificWarnings>
<AdditionalIncludeDirectories>..\..\..\;$(MSBuildThisFileDirectory)..\..\..\common\sysinternals;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<DisableSpecificWarnings>26451;4100;4091;4245</DisableSpecificWarnings>
<AdditionalIncludeDirectories>..\..\..\;$(MSBuildThisFileDirectory)..\..\..\common\sysinternals;..\ZoomItBreak;$(MSBuildThisFileDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Create</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpplatest</LanguageStandard>
@@ -90,7 +90,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(InterPlatformDir)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
@@ -109,10 +109,10 @@
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;_M_X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
@@ -132,10 +132,10 @@
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;_M_ARM64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<FixedBaseAddress>
@@ -156,7 +156,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(InterPlatformDir)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
@@ -174,10 +174,10 @@
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_X64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -196,10 +196,10 @@
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;_M_ARM64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -208,6 +208,14 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\ZoomItBreak\BreakTimer.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="AudioSampleGenerator.cpp">
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</MultiProcessorCompilation>
<MultiProcessorCompilation Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</MultiProcessorCompilation>
@@ -249,6 +257,14 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="GifRecordingSession.cpp" />
<ClCompile Include="PanoramaCapture.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="pch.cpp" />
<ClCompile Include="SelectRectangle.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
@@ -300,11 +316,13 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\ZoomItBreak\BreakTimer.h" />
<ClInclude Include="AudioSampleGenerator.h" />
<ClInclude Include="LoopbackCapture.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\Eula\Eula.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\ZoomItModuleInterface\Trace.h" />
<ClInclude Include="GifRecordingSession.h" />
<ClInclude Include="PanoramaCapture.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Registry.h" />
<ClInclude Include="resource.h" />
@@ -378,4 +396,4 @@
<Import Project="..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets" Condition="Exists('..\..\..\..\packages\robmikh.common.0.0.23-beta\build\native\robmikh.common.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</Project>
</Project>

View File

@@ -60,6 +60,12 @@
<ClCompile Include="GifRecordingSession.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PanoramaCapture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ZoomItBreak\BreakTimer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Registry.h">
@@ -107,6 +113,12 @@
<ClInclude Include="GifRecordingSession.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PanoramaCapture.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ZoomItBreak\BreakTimer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="appicon.ico">

View File

@@ -17,6 +17,8 @@ DWORD g_BreakToggleKey = ((HOTKEYF_CONTROL) << 8)| '3';
DWORD g_DemoTypeToggleKey = ((HOTKEYF_CONTROL) << 8) | '7';
DWORD g_RecordToggleKey = ((HOTKEYF_CONTROL) << 8) | '5';
DWORD g_SnipToggleKey = ((HOTKEYF_CONTROL) << 8) | '6';
DWORD g_SnipPanoramaToggleKey = ((HOTKEYF_CONTROL) << 8) | '8';
DWORD g_SnipOcrToggleKey = ((HOTKEYF_CONTROL | HOTKEYF_ALT) << 8) | '6';
DWORD g_ShowExpiredTime = 1;
DWORD g_SliderZoomLevel = 3;
@@ -24,6 +26,7 @@ BOOLEAN g_AnimateZoom = TRUE;
BOOLEAN g_SmoothImage = TRUE;
DWORD g_PenColor = COLOR_RED;
DWORD g_BreakPenColor = COLOR_RED;
DWORD g_BreakBackgroundColor = 0;
DWORD g_RootPenWidth = PEN_WIDTH;
int g_FontScale = 10;
DWORD g_BreakTimeout = 10;
@@ -40,6 +43,7 @@ BOOLEAN g_ShowTrayIcon = TRUE;
BOOLEAN g_SnapToGrid = TRUE;
BOOLEAN g_TelescopeZoomOut = TRUE;
BOOLEAN g_BreakOnSecondary = FALSE;
BOOLEAN g_BreakLockWorkstation = FALSE;
LOGFONT g_LogFont;
BOOLEAN g_DemoTypeUserDriven = false;
TCHAR g_DemoTypeFile[MAX_PATH] = {0};
@@ -66,10 +70,13 @@ REG_SETTING RegSettings[] = {
{ L"DrawToggleKey", SETTING_TYPE_DWORD, 0, &g_DrawToggleKey, static_cast<DOUBLE>(g_DrawToggleKey) },
{ L"RecordToggleKey", SETTING_TYPE_DWORD, 0, &g_RecordToggleKey, static_cast<DOUBLE>(g_RecordToggleKey) },
{ L"SnipToggleKey", SETTING_TYPE_DWORD, 0, &g_SnipToggleKey, static_cast<DOUBLE>(g_SnipToggleKey) },
{ L"SnipPanoramaToggleKey", SETTING_TYPE_DWORD, 0, &g_SnipPanoramaToggleKey, static_cast<DOUBLE>(g_SnipPanoramaToggleKey) },
{ L"SnipOcrToggleKey", SETTING_TYPE_DWORD, 0, &g_SnipOcrToggleKey, static_cast<DOUBLE>(g_SnipOcrToggleKey) },
{ L"PenColor", SETTING_TYPE_DWORD, 0, &g_PenColor, static_cast<DOUBLE>(g_PenColor) },
{ L"PenWidth", SETTING_TYPE_DWORD, 0, &g_RootPenWidth, static_cast<DOUBLE>(g_RootPenWidth) },
{ L"OptionsShown", SETTING_TYPE_BOOLEAN, 0, &g_OptionsShown, static_cast<DOUBLE>(g_OptionsShown) },
{ L"BreakPenColor", SETTING_TYPE_DWORD, 0, &g_BreakPenColor, static_cast<DOUBLE>(g_BreakPenColor) },
{ L"BreakBackgroundColor", SETTING_TYPE_DWORD, 0, &g_BreakBackgroundColor, static_cast<DOUBLE>(g_BreakBackgroundColor) },
{ L"BreakTimerKey", SETTING_TYPE_DWORD, 0, &g_BreakToggleKey, static_cast<DOUBLE>(g_BreakToggleKey) },
{ L"DemoTypeToggleKey", SETTING_TYPE_DWORD, 0, &g_DemoTypeToggleKey, static_cast<DOUBLE>(g_DemoTypeToggleKey) },
{ L"DemoTypeFile", SETTING_TYPE_STRING, sizeof( g_DemoTypeFile ), g_DemoTypeFile, static_cast<DOUBLE>(0) },
@@ -85,6 +92,7 @@ REG_SETTING RegSettings[] = {
{ L"BreakTimerPosition", SETTING_TYPE_DWORD, 0, &g_BreakTimerPosition, static_cast<DOUBLE>(g_BreakTimerPosition) },
{ L"BreakShowDesktop", SETTING_TYPE_BOOLEAN, 0, &g_BreakShowDesktop, static_cast<DOUBLE>(g_BreakShowDesktop) },
{ L"BreakOnSecondary", SETTING_TYPE_BOOLEAN, 0, &g_BreakOnSecondary,static_cast<DOUBLE>(g_BreakOnSecondary) },
{ L"BreakLockWorkstation", SETTING_TYPE_BOOLEAN, 0, &g_BreakLockWorkstation, static_cast<DOUBLE>(g_BreakLockWorkstation) },
{ 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) },

File diff suppressed because it is too large Load Diff

View File

@@ -15,4 +15,14 @@ RCZOOMIT64 BINRES MOVEABLE PURE RCZOOMIT_x64_path
#endif
// Embed the break timer screensaver for the current platform.
// The .scr is built by the ZoomItBreak project into the shared output directory.
#ifdef _M_IX86
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak.scr"
#elif defined(_M_X64)
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak64.scr"
#elif defined(_M_ARM64)
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak64a.scr"
#endif
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "ZoomIt.exe.manifest"

View File

@@ -53,6 +53,9 @@
#include <winrt/Windows.Storage.Pickers.h>
#include <winrt/Windows.Storage.FileProperties.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Media.Ocr.h>
#include <Windows.Graphics.Imaging.Interop.h>
#include <filesystem>

View File

@@ -78,6 +78,8 @@
#define IDC_RECORD_FRAME_RATE2 1059
#define IDC_RECORD_SCALING 1059
#define IDC_SNIP_HOTKEY 1060
#define IDC_SNIP_OCR_HOTKEY 1112
#define IDC_SNIP_PANORAMA_HOTKEY 1114
#define IDC_CAPTURE_AUDIO 1061
#define IDC_MICROPHONE 1062
#define IDC_PEN_CONTROL 1063
@@ -111,12 +113,15 @@
#define IDC_SMOOTH_IMAGE 1107
#define IDC_CAPTURE_SYSTEM_AUDIO 1108
#define IDC_MICROPHONE_LABEL 1109
#define IDC_MIC_MONO_MIX 1110
#define IDC_TRIM_FILE 1110
#define IDC_MIC_MONO_MIX 1111
#define IDC_CHECK_LOCK_WORKSTATION 1112
#define IDC_SAVE 40002
#define IDC_COPY 40004
#define IDC_RECORD 40006
#define IDC_RECORD_HOTKEY 40007
#define IDC_COPY_CROP 40008
#define IDC_COPY_OCR 40014
#define IDC_SAVE_CROP 40009
#define IDC_DEMOTYPE_HOTKEY 40011
@@ -125,8 +130,8 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 120
#define _APS_NEXT_COMMAND_VALUE 40013
#define _APS_NEXT_CONTROL_VALUE 1099
#define _APS_NEXT_COMMAND_VALUE 40015
#define _APS_NEXT_CONTROL_VALUE 1113
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,520 @@
//============================================================================
//
// BreakTimer.cpp
//
// Shared break timer rendering module used by both ZoomIt and the
// ZoomItBreak screensaver (.scr).
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
// When built inside ZoomIt (with PCH), pch.h is included automatically.
// When built for the screensaver project, we include the headers we need.
#ifndef __ZOOMIT_SCREENSAVER__
#include "pch.h"
#endif
#include "BreakTimer.h"
#include <stdio.h>
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "Msimg32.lib")
#pragma comment(lib, "Winmm.lib")
//----------------------------------------------------------------------------
//
// BreakTimer_UpdateMonitorInfo
//
// Determine monitor geometry for the given screen point.
//
//----------------------------------------------------------------------------
void BreakTimer_UpdateMonitorInfo( POINT point, MONITORINFO* monInfo )
{
HMONITOR hMon = MonitorFromPoint( point, MONITOR_DEFAULTTONEAREST );
if( hMon != nullptr )
{
monInfo->cbSize = sizeof *monInfo;
GetMonitorInfo( hMon, monInfo );
}
else
{
*monInfo = {};
HDC hdcScreen = CreateDC( L"DISPLAY", nullptr, nullptr, nullptr );
if( hdcScreen != nullptr )
{
monInfo->rcMonitor.right = GetDeviceCaps( hdcScreen, HORZRES );
monInfo->rcMonitor.bottom = GetDeviceCaps( hdcScreen, VERTRES );
DeleteDC( hdcScreen );
}
}
}
//----------------------------------------------------------------------------
//
// BreakTimer_LoadImageFile
//
// Use GDI+ to load an image file and return an HBITMAP.
//
//----------------------------------------------------------------------------
HBITMAP BreakTimer_LoadImageFile( PTCHAR Filename )
{
HBITMAP hBmp;
Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile( Filename );
if( bitmap == nullptr || bitmap->GetHBITMAP( NULL, &hBmp ) != Gdiplus::Ok )
{
delete bitmap;
return NULL;
}
delete bitmap;
return hBmp;
}
//----------------------------------------------------------------------------
//
// BreakTimer_CreateFadedDesktopBackground
//
// Creates a snapshot of the desktop that is faded and alpha-blended
// with black.
//
//----------------------------------------------------------------------------
HBITMAP BreakTimer_CreateFadedDesktopBackground( HDC hdc, LPRECT rcScreen, LPRECT rcCrop )
{
int width = rcScreen->right - rcScreen->left;
int height = rcScreen->bottom - rcScreen->top;
HDC hdcScreen = hdc;
HDC hdcMem = CreateCompatibleDC( hdcScreen );
HBITMAP hBitmap = CreateCompatibleBitmap( hdcScreen, width, height );
HBITMAP hOld = static_cast<HBITMAP>( SelectObject( hdcMem, hBitmap ) );
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 0 ) );
// Start with black background.
FillRect( hdcMem, rcScreen, hBrush );
if( rcCrop != NULL && rcCrop->left != -1 )
{
// Copy screen contents that are not cropped.
BitBlt( hdcMem, rcCrop->left, rcCrop->top,
rcCrop->right - rcCrop->left,
rcCrop->bottom - rcCrop->top,
hdcScreen, rcCrop->left, rcCrop->top, SRCCOPY );
}
// Blend screen contents into the black background.
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 0x4F;
blend.AlphaFormat = 0;
AlphaBlend( hdcMem, 0, 0, width, height,
hdcScreen, rcScreen->left, rcScreen->top,
width, height, blend );
SelectObject( hdcMem, hOld );
DeleteDC( hdcMem );
DeleteObject( hBrush );
return hBitmap;
}
//----------------------------------------------------------------------------
//
// BreakTimer_Init
//
// Create fonts, backing bitmap, and optionally load background.
// Returns TRUE on success.
//
//----------------------------------------------------------------------------
BOOLEAN BreakTimer_Init(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings,
int timeoutSeconds,
HBITMAP hExistingBackground,
HDC hExistingBackgroundDC )
{
state->active = TRUE;
state->timeoutSeconds = timeoutSeconds;
// Get screen DC.
state->hdcScreen = CreateDC( L"DISPLAY", static_cast<PTCHAR>( NULL ),
static_cast<PTCHAR>( NULL ),
static_cast<CONST DEVMODE*>( NULL ) );
if( !state->hdcScreen )
return FALSE;
// Determine monitor.
POINT cursorPos;
GetCursorPos( &cursorPos );
BreakTimer_UpdateMonitorInfo( cursorPos, &state->monInfo );
state->width = state->monInfo.rcMonitor.right - state->monInfo.rcMonitor.left;
state->height = state->monInfo.rcMonitor.bottom - state->monInfo.rcMonitor.top;
// Manage background bitmap.
if( hExistingBackground )
{
// Caller supplied a pre-captured background (e.g. from command line).
state->hBackgroundBmp = hExistingBackground;
state->hDcBackgroundFile = hExistingBackgroundDC;
}
else if( settings->showBackgroundFile && !settings->showDesktop )
{
// Load image file.
state->hBackgroundBmp = BreakTimer_LoadImageFile(
const_cast<PTCHAR>( settings->backgroundFile ) );
if( !state->hBackgroundBmp )
return FALSE;
state->hDcBackgroundFile = CreateCompatibleDC( state->hdcScreen );
SelectObject( state->hDcBackgroundFile, state->hBackgroundBmp );
}
else if( settings->showBackgroundFile && settings->showDesktop )
{
// Faded desktop screenshot.
HDC hDcDesktop = GetDC( NULL );
state->hBackgroundBmp = BreakTimer_CreateFadedDesktopBackground(
hDcDesktop, &state->monInfo.rcMonitor, NULL );
ReleaseDC( NULL, hDcDesktop );
state->hDcBackgroundFile = CreateCompatibleDC( state->hdcScreen );
SelectObject( state->hDcBackgroundFile, state->hBackgroundBmp );
}
else
{
state->hBackgroundBmp = NULL;
state->hDcBackgroundFile = NULL;
}
// Create fonts.
LOGFONT lf = settings->logFont;
lf.lfHeight = state->height / 5;
state->hTimerFont = CreateFontIndirect( &lf );
lf.lfHeight = state->height / 8;
state->hNegativeTimerFont = CreateFontIndirect( &lf );
// Create backing bitmap for double buffering.
state->hdcScreenCompat = CreateCompatibleDC( state->hdcScreen );
state->bmp.bmBitsPixel = static_cast<BYTE>( GetDeviceCaps( state->hdcScreen, BITSPIXEL ) );
state->bmp.bmPlanes = static_cast<BYTE>( GetDeviceCaps( state->hdcScreen, PLANES ) );
state->bmp.bmWidth = state->width;
state->bmp.bmHeight = state->height;
state->bmp.bmWidthBytes = ( ( state->bmp.bmWidth + 15 ) & ~15 ) / 8;
state->hbmpCompat = CreateBitmap( state->bmp.bmWidth, state->bmp.bmHeight,
state->bmp.bmPlanes, state->bmp.bmBitsPixel, static_cast<CONST VOID*>( NULL ) );
SelectObject( state->hdcScreenCompat, state->hbmpCompat );
SetTextColor( state->hdcScreenCompat, settings->penColor );
SetBkMode( state->hdcScreenCompat, TRANSPARENT );
SelectObject( state->hdcScreenCompat, state->hTimerFont );
return TRUE;
}
//----------------------------------------------------------------------------
//
// BreakTimer_Tick
//
// Decrement counter, invalidate window, play sound at zero.
//
//----------------------------------------------------------------------------
void BreakTimer_Tick(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings )
{
state->timeoutSeconds -= 1;
InvalidateRect( hWnd, NULL, FALSE );
if( state->timeoutSeconds == 0 && settings->playSound )
{
PlaySound( settings->soundFile, NULL, SND_FILENAME | SND_ASYNC );
}
}
//----------------------------------------------------------------------------
//
// BreakTimer_Paint
//
// Render the break timer into the back buffer and blit to the paint DC.
//
//----------------------------------------------------------------------------
void BreakTimer_Paint(
HDC hdc,
BreakTimerState* state,
const BreakTimerSettings* settings )
{
RECT rc, rc1;
TCHAR timerText[16];
TCHAR negativeTimerText[16];
// Fill background (white by default, black if backgroundColor == 1).
rc.top = rc.left = 0;
rc.bottom = state->height;
rc.right = state->width;
if( settings->backgroundColor )
{
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 0 ) );
FillRect( state->hdcScreenCompat, &rc, hBrush );
DeleteObject( hBrush );
}
else
{
FillRect( state->hdcScreenCompat, &rc, GetSysColorBrush( COLOR_WINDOW ) );
}
// Draw background bitmap if present.
if( state->hBackgroundBmp )
{
BITMAP local_bmp;
GetObject( state->hBackgroundBmp, sizeof( local_bmp ), &local_bmp );
SetStretchBltMode( state->hdcScreenCompat,
settings->smoothImage ? HALFTONE : COLORONCOLOR );
if( settings->backgroundStretch )
{
StretchBlt( state->hdcScreenCompat, 0, 0, state->width, state->height,
state->hDcBackgroundFile, 0, 0,
local_bmp.bmWidth, local_bmp.bmHeight, SRCCOPY | CAPTUREBLT );
}
else
{
BitBlt( state->hdcScreenCompat,
state->width / 2 - local_bmp.bmWidth / 2,
state->height / 2 - local_bmp.bmHeight / 2,
local_bmp.bmWidth, local_bmp.bmHeight,
state->hDcBackgroundFile, 0, 0, SRCCOPY | CAPTUREBLT );
}
}
// Format timer text.
if( state->timeoutSeconds > 0 )
{
_stprintf( timerText, L"% 2d:%02d",
state->timeoutSeconds / 60, state->timeoutSeconds % 60 );
}
else
{
_tcscpy( timerText, L"0:00" );
}
// Measure timer text.
rc.left = rc.top = 0;
DrawText( state->hdcScreenCompat, timerText, -1, &rc,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX | DT_CALCRECT );
// Measure expired text if needed.
rc1.left = rc1.right = rc1.bottom = rc1.top = 0;
if( settings->showExpiredTime && state->timeoutSeconds < 0 )
{
_stprintf( negativeTimerText, L"(-% 2d:%02d)",
-state->timeoutSeconds / 60, -state->timeoutSeconds % 60 );
HFONT prevFont = static_cast<HFONT>(
SelectObject( state->hdcScreenCompat, state->hNegativeTimerFont ) );
DrawText( state->hdcScreenCompat, negativeTimerText, -1, &rc1,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX | DT_CALCRECT );
SelectObject( state->hdcScreenCompat, prevFont );
}
// Position vertically.
switch( settings->timerPosition )
{
case 0: case 1: case 2:
rc.top = 50;
break;
case 3: case 4: case 5:
rc.top = ( state->height - ( rc.bottom - rc.top ) ) / 2;
break;
case 6: case 7: case 8:
rc.top = state->height - rc.bottom - 50 - rc1.bottom;
break;
}
// Position horizontally.
switch( settings->timerPosition )
{
case 0: case 3: case 6:
rc.left = 50;
break;
case 1: case 4: case 7:
rc.left = ( state->width - ( rc.right - rc.left ) ) / 2;
break;
case 2: case 5: case 8:
rc.left = state->width - rc.right - 50;
break;
}
rc.bottom += rc.top;
rc.right += rc.left;
// Draw timer text.
DrawText( state->hdcScreenCompat, timerText, -1, &rc,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX );
// Draw expired text below the timer.
if( settings->showExpiredTime && state->timeoutSeconds < 0 )
{
rc1.top = rc.bottom + 10;
rc1.left = rc.left + ( ( rc.right - rc.left ) - ( rc1.right - rc1.left ) ) / 2;
HFONT prevFont = static_cast<HFONT>(
SelectObject( state->hdcScreenCompat, state->hNegativeTimerFont ) );
DrawText( state->hdcScreenCompat, negativeTimerText, -1, &rc1,
DT_NOCLIP | DT_LEFT | DT_NOPREFIX );
SelectObject( state->hdcScreenCompat, prevFont );
}
// Copy to screen.
BitBlt( hdc, 0, 0, state->width, state->height,
state->hdcScreenCompat, 0, 0, SRCCOPY | CAPTUREBLT );
}
//----------------------------------------------------------------------------
//
// BreakTimer_Cleanup
//
// Free the GDI resources used by the break timer.
//
//----------------------------------------------------------------------------
void BreakTimer_Cleanup(
BreakTimerState* state,
BOOLEAN freeBackground )
{
if( freeBackground && state->hBackgroundBmp )
{
DeleteObject( state->hBackgroundBmp );
DeleteDC( state->hDcBackgroundFile );
state->hBackgroundBmp = NULL;
state->hDcBackgroundFile = NULL;
}
if( state->hTimerFont )
{
DeleteObject( state->hTimerFont );
state->hTimerFont = NULL;
}
if( state->hNegativeTimerFont )
{
DeleteObject( state->hNegativeTimerFont );
state->hNegativeTimerFont = NULL;
}
if( state->hdcScreen )
{
DeleteDC( state->hdcScreen );
state->hdcScreen = NULL;
}
if( state->hdcScreenCompat )
{
DeleteDC( state->hdcScreenCompat );
state->hdcScreenCompat = NULL;
}
if( state->hbmpCompat )
{
DeleteObject( state->hbmpCompat );
state->hbmpCompat = NULL;
}
state->active = FALSE;
}
//----------------------------------------------------------------------------
//
// BreakTimer_AdjustTime
//
// Round to the nearest minute boundary and adjust by deltaMinutes.
// Resets the 1-second timer on the window.
//
//----------------------------------------------------------------------------
void BreakTimer_AdjustTime(
HWND hWnd,
BreakTimerState* state,
int deltaMinutes )
{
int breakTimeout = state->timeoutSeconds;
if( deltaMinutes > 0 )
{
if( breakTimeout < 0 ) breakTimeout = 0;
if( breakTimeout % 60 )
{
breakTimeout += ( 60 - breakTimeout % 60 );
deltaMinutes--;
}
breakTimeout += deltaMinutes * 60;
}
else
{
int absDelta = -deltaMinutes;
if( breakTimeout % 60 )
{
breakTimeout -= breakTimeout % 60;
absDelta--;
}
breakTimeout -= absDelta * 60;
}
if( breakTimeout < 0 ) breakTimeout = 0;
state->timeoutSeconds = breakTimeout;
KillTimer( hWnd, 0 );
SetTimer( hWnd, 0, 1000, NULL );
InvalidateRect( hWnd, NULL, TRUE );
}
//----------------------------------------------------------------------------
//
// BreakScrConfig_GetPath
//
// Build the full path to the config file in %TEMP%.
//
//----------------------------------------------------------------------------
static void BreakScrConfig_GetPath( TCHAR* path, size_t cch )
{
GetTempPath( static_cast<DWORD>( cch ), path );
_tcscat( path, BREAKSCR_CONFIG_FILE );
}
//----------------------------------------------------------------------------
//
// BreakScrConfig_Write
//
//----------------------------------------------------------------------------
BOOLEAN BreakScrConfig_Write( const BreakScrConfig* config )
{
TCHAR path[MAX_PATH];
BreakScrConfig_GetPath( path, MAX_PATH );
HANDLE hFile = CreateFile( path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
DWORD written;
BOOL ok = WriteFile( hFile, config, sizeof( *config ), &written, NULL );
CloseHandle( hFile );
return ok && written == sizeof( *config );
}
//----------------------------------------------------------------------------
//
// BreakScrConfig_Read
//
//----------------------------------------------------------------------------
BOOLEAN BreakScrConfig_Read( BreakScrConfig* config )
{
TCHAR path[MAX_PATH];
BreakScrConfig_GetPath( path, MAX_PATH );
HANDLE hFile = CreateFile( path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
DWORD bytesRead;
BOOL ok = ReadFile( hFile, config, sizeof( *config ), &bytesRead, NULL );
CloseHandle( hFile );
if( !ok || bytesRead != sizeof( *config ) )
return FALSE;
if( config->magic != BREAKSCR_CONFIG_MAGIC )
return FALSE;
return TRUE;
}

View File

@@ -0,0 +1,141 @@
//============================================================================
//
// BreakTimer.h
//
// Shared break timer rendering module used by both ZoomIt and the
// ZoomItBreak screensaver (.scr).
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#pragma once
#include <windows.h>
#include <tchar.h>
#define GDIPVER 0x0110
#include <gdiplus.h>
//----------------------------------------------------------------------------
// BreakTimerSettings — read-only configuration, populated from globals
// or from command-line arguments in the screensaver.
//----------------------------------------------------------------------------
struct BreakTimerSettings
{
DWORD penColor;
DWORD backgroundColor; // 0 = white, 1 = black
DWORD timerPosition; // 08 (3×3 grid)
DWORD opacity; // 0100
DWORD showExpiredTime; // 0 or 1
BOOLEAN smoothImage;
BOOLEAN backgroundStretch;
BOOLEAN playSound;
TCHAR soundFile[MAX_PATH];
BOOLEAN showDesktop;
BOOLEAN showBackgroundFile;
TCHAR backgroundFile[MAX_PATH];
LOGFONT logFont;
};
//----------------------------------------------------------------------------
// BreakTimerState — runtime state for an active break timer.
//----------------------------------------------------------------------------
struct BreakTimerState
{
BOOLEAN active;
int timeoutSeconds; // counts down; goes negative if expired
HFONT hTimerFont;
HFONT hNegativeTimerFont;
HBITMAP hBackgroundBmp;
HDC hDcBackgroundFile;
HDC hdcScreen;
HDC hdcScreenCompat;
HBITMAP hbmpCompat;
BITMAP bmp;
int width;
int height;
MONITORINFO monInfo;
};
//----------------------------------------------------------------------------
// Shared utility functions
//----------------------------------------------------------------------------
// Determine monitor geometry for the given screen point.
void BreakTimer_UpdateMonitorInfo( POINT point, MONITORINFO* monInfo );
// Load an image file via GDI+; returns an HBITMAP or NULL on failure.
HBITMAP BreakTimer_LoadImageFile( PTCHAR Filename );
// Capture a faded (alpha-blended with black) screenshot of the desktop.
HBITMAP BreakTimer_CreateFadedDesktopBackground( HDC hdc, LPRECT rcScreen, LPRECT rcCrop );
//----------------------------------------------------------------------------
// Break timer lifecycle
//----------------------------------------------------------------------------
// Create fonts, backing bitmap, and load background.
// The caller is responsible for creating/showing the window itself.
// |timeoutSeconds| is already in seconds (e.g. g_BreakTimeout * 60 + 1).
BOOLEAN BreakTimer_Init(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings,
int timeoutSeconds,
HBITMAP hExistingBackground, // optional pre-captured background
HDC hExistingBackgroundDC // optional DC for above
);
// Called every second; decrements the counter and invalidates the window.
void BreakTimer_Tick(
HWND hWnd,
BreakTimerState* state,
const BreakTimerSettings* settings
);
// Render the timer into hdcScreenCompat then BitBlt to hdc (from BeginPaint).
void BreakTimer_Paint(
HDC hdc,
BreakTimerState* state,
const BreakTimerSettings* settings
);
// Free fonts, DCs, bitmaps. If |freeBackground| is false the background
// bitmap/DC are left for the caller to manage (e.g. shallow destroy).
void BreakTimer_Cleanup(
BreakTimerState* state,
BOOLEAN freeBackground
);
// Adjust the remaining time by |deltaMinutes| (positive = add time).
// Resets the 1-second timer on hWnd.
void BreakTimer_AdjustTime(
HWND hWnd,
BreakTimerState* state,
int deltaMinutes
);
//----------------------------------------------------------------------------
// BreakScrConfig — binary blob written to a temp file by ZoomIt and
// read by the screensaver on startup. This avoids command-line arg
// issues since Windows launches screensavers with only /s.
//----------------------------------------------------------------------------
#define BREAKSCR_CONFIG_MAGIC 0x5A4D4253 // 'ZMBS'
#define BREAKSCR_CONFIG_FILE L"ZoomItBreakConfig.dat"
struct BreakScrConfig
{
DWORD magic; // must be BREAKSCR_CONFIG_MAGIC
int timeoutSeconds;
BOOL resumed; // set TRUE by screensaver on first launch
BreakTimerSettings settings;
TCHAR screenshotPath[MAX_PATH];
};
// Write config to %TEMP%\BREAKSCR_CONFIG_FILE.
BOOLEAN BreakScrConfig_Write( const BreakScrConfig* config );
// Read config from %TEMP%\BREAKSCR_CONFIG_FILE.
BOOLEAN BreakScrConfig_Read( BreakScrConfig* config );

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

@@ -0,0 +1,27 @@
//============================================================================
//
// ZoomItBreak.rc
//
// Minimal resources required by Scrnsavw.lib.
//
//============================================================================
#include <windows.h>
#include <scrnsave.h>
// Embed DPI-awareness manifest so the screensaver sees native resolution.
// This ensures the pre-captured desktop screenshot (saved at physical pixels
// by the DPI-aware ZoomIt process) matches the screensaver window dimensions.
1 RT_MANIFEST "ZoomItBreak.manifest"
// IDS_DESCRIPTION is used by scrnsavw.lib as the window class name.
STRINGTABLE
BEGIN
IDS_DESCRIPTION, "ZoomIt Break Timer"
END
// Stub configuration dialog - never shown (ScreenSaverConfigureDialog returns FALSE).
DLG_SCRNSAVECONFIGURE DIALOG 0, 0, 200, 60
STYLE WS_DLGFRAME | WS_POPUP | WS_VISIBLE | DS_MODALFRAME | WS_CAPTION
CAPTION "ZoomIt Break Timer"
BEGIN
END

View File

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>18.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{94ba3051-c8d7-454a-9d46-1a7c78e228a3}</ProjectGuid>
<RootNamespace>ZoomItBreak</RootNamespace>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<!--
Output .scr instead of .exe. A screensaver is a renamed executable.
Use a separate intermediate dir to avoid colliding with ZoomIt.
-->
<PropertyGroup>
<TargetExt>.scr</TargetExt>
<GenerateManifest>false</GenerateManifest>
<IntDir>$(Platform)\$(Configuration)\ZoomItBreak\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64a</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<OutDir>$(MsBuildProjectDirectory)\$(Platform)\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)64a</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v145</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100;4091;4245</DisableSpecificWarnings>
<AdditionalIncludeDirectories>..\..\..\;$(MSBuildThisFileDirectory)..\..\..\common\sysinternals;..\ZoomIt;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x0602;_DEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x600;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PreprocessorDefinitions>__ZOOMIT_SCREENSAVER__;_UNICODE;UNICODE;WINVER=0x602;NDEBUG;_WIN32_WINNT=0x602;_WIN32_WINDOWS=0x501;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="BreakTimer.cpp" />
<ClCompile Include="ZoomItBreakScr.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ZoomItBreak.rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BreakTimer.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</Project>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BreakTimer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZoomItBreakScr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="BreakTimer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ZoomItBreak.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,283 @@
//============================================================================
//
// ZoomItBreakScr.cpp
//
// ZoomIt break timer screensaver (.scr). When launched by Winlogon on the
// Screen-saver desktop with password protection, the user must authenticate
// to dismiss it. The break timer countdown and rendering are provided by
// the shared BreakTimer module.
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//============================================================================
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <scrnsave.h>
#define GDIPVER 0x0110
#include <gdiplus.h>
#include "BreakTimer.h"
static void DbgPrint( LPCTSTR fmt, ... )
{
TCHAR buf[512];
va_list ap;
#pragma warning( push )
#pragma warning( disable : 26492 )
va_start( ap, fmt );
#pragma warning( pop )
_vsntprintf( buf, _countof(buf), fmt, ap );
va_end( ap );
buf[_countof(buf)-1] = 0;
OutputDebugString( buf );
}
#pragma comment(lib, "scrnsavw.lib")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "Msimg32.lib")
#pragma comment(lib, "Winmm.lib")
//----------------------------------------------------------------------------
// Globals
//----------------------------------------------------------------------------
static BreakTimerSettings g_Settings;
static BreakTimerState g_State;
static ULONG_PTR g_GdiplusToken;
static TCHAR g_ScreenshotPath[MAX_PATH] = { 0 };
static int g_LastSavedTimeout = 0; // For state persistence
//----------------------------------------------------------------------------
// Load settings from the binary config file written by ZoomIt,
// falling back to hard-coded defaults if the file is missing.
//----------------------------------------------------------------------------
static void LoadSettings( void )
{
BreakScrConfig config;
if( BreakScrConfig_Read( &config ) )
{
g_Settings = config.settings;
g_State.timeoutSeconds = config.timeoutSeconds;
_tcscpy( g_ScreenshotPath, config.screenshotPath );
DbgPrint( L"[BreakScr] Config loaded: timeout=%d, bgFile=%d, showDesktop=%d, screenshot=%s\n",
config.timeoutSeconds, config.settings.showBackgroundFile,
config.settings.showDesktop, config.screenshotPath );
return;
}
DbgPrint( L"[BreakScr] Config file not found, using fallback defaults\n" );
// Fallback defaults (for testing the .scr directly).
memset( &g_Settings, 0, sizeof( g_Settings ) );
g_Settings.penColor = RGB( 255, 0, 0 );
g_Settings.timerPosition = 4;
g_Settings.opacity = 100;
g_Settings.showExpiredTime = 1;
g_Settings.smoothImage = TRUE;
g_Settings.backgroundStretch = FALSE;
g_Settings.showDesktop = TRUE;
g_Settings.showBackgroundFile = FALSE;
g_State.timeoutSeconds = 600;
NONCLIENTMETRICS ncm = { sizeof( ncm ) };
SystemParametersInfo( SPI_GETNONCLIENTMETRICS, sizeof( ncm ), &ncm, 0 );
g_Settings.logFont = ncm.lfMessageFont;
}
//----------------------------------------------------------------------------
//
// ScreenSaverProc
//
// Main window procedure for the screensaver, called by Scrnsavw.lib.
//
//----------------------------------------------------------------------------
LRESULT WINAPI ScreenSaverProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CREATE:
{
DbgPrint( L"[BreakScr] WM_CREATE: hwnd=%p\n", hWnd );
// Initialize GDI+.
Gdiplus::GdiplusStartupInput startupIn;
Gdiplus::GdiplusStartup( &g_GdiplusToken, &startupIn, NULL );
LoadSettings();
// Check if a previous screensaver instance already ran (resumed == TRUE).
// On first launch, ZoomIt sets resumed = FALSE, so we skip the deduction.
BreakScrConfig resumeConfig;
if( BreakScrConfig_Read( &resumeConfig ) && resumeConfig.resumed )
{
// Subtract the screensaver idle timeout to compensate for
// the time the screensaver wasn't running on the lock screen.
UINT scrTimeout = 0;
SystemParametersInfo( SPI_GETSCREENSAVETIMEOUT, 0, &scrTimeout, 0 );
g_State.timeoutSeconds -= static_cast<int>( scrTimeout );
if( g_State.timeoutSeconds < 0 && !g_Settings.showExpiredTime )
g_State.timeoutSeconds = 0;
DbgPrint( L"[BreakScr] Resumption: subtracted %u sec idle, timeout=%d\n",
scrTimeout, g_State.timeoutSeconds );
}
// Mark as resumed so subsequent screensaver launches know to deduct idle time.
{
BreakScrConfig markConfig;
if( BreakScrConfig_Read( &markConfig ) )
{
markConfig.resumed = TRUE;
BreakScrConfig_Write( &markConfig );
}
}
// Load pre-captured screenshot if provided.
HBITMAP hBgBmp = NULL;
HDC hBgDC = NULL;
if( g_ScreenshotPath[0] )
{
hBgBmp = BreakTimer_LoadImageFile( g_ScreenshotPath );
DbgPrint( L"[BreakScr] LoadImageFile(%s) => %p\n", g_ScreenshotPath, hBgBmp );
if( hBgBmp )
{
HDC hdcScreen = CreateDC( L"DISPLAY", NULL, NULL, NULL );
hBgDC = CreateCompatibleDC( hdcScreen );
SelectObject( hBgDC, hBgBmp );
DeleteDC( hdcScreen );
}
}
int timeout = g_State.timeoutSeconds;
memset( &g_State, 0, sizeof( g_State ) );
DbgPrint( L"[BreakScr] Calling BreakTimer_Init, timeout=%d\n", timeout );
if( !BreakTimer_Init( hWnd, &g_State, &g_Settings, timeout, hBgBmp, hBgDC ) )
{
DbgPrint( L"[BreakScr] BreakTimer_Init FAILED\n" );
PostMessage( hWnd, WM_CLOSE, 0, 0 );
return 0;
}
DbgPrint( L"[BreakScr] BreakTimer_Init OK, active=%d\n", g_State.active );
// Prevent the monitor from blanking due to power management.
SetThreadExecutionState( ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED );
// Kick off the first tick and start the 1-second timer.
SendMessage( hWnd, WM_TIMER, 1, 0 );
SetTimer( hWnd, 1, 1000, NULL );
return 0;
}
case WM_TIMER:
if( wParam == 1 )
{
BreakTimer_Tick( hWnd, &g_State, &g_Settings );
// Periodically save state (every 5 seconds) for resumption after
// credential provider timeout. This allows the screensaver to continue
// from where it left off if a student triggers the login screen but
// doesn't authenticate.
if( g_State.timeoutSeconds != g_LastSavedTimeout &&
g_State.timeoutSeconds % 5 == 0 )
{
BreakScrConfig config;
if( BreakScrConfig_Read( &config ) )
{
config.timeoutSeconds = g_State.timeoutSeconds;
if( BreakScrConfig_Write( &config ) )
{
g_LastSavedTimeout = g_State.timeoutSeconds;
DbgPrint( L"[BreakScr] Saved state: %d seconds remaining\n",
g_State.timeoutSeconds );
}
}
}
}
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hWnd, &ps );
if( g_State.active )
{
BreakTimer_Paint( hdc, &g_State, &g_Settings );
}
EndPaint( hWnd, &ps );
return 0;
}
case WM_DESTROY:
DbgPrint( L"[BreakScr] WM_DESTROY\n" );
SetThreadExecutionState( ES_CONTINUOUS ); // Restore default power behavior
KillTimer( hWnd, 1 );
BreakTimer_Cleanup( &g_State, TRUE );
Gdiplus::GdiplusShutdown( g_GdiplusToken );
return 0;
//------------------------------------------------------------------
// Prevent DefScreenSaverProc from auto-closing on user input.
// The screensaver must stay up until the break timer expires or
// the user authenticates via Ctrl+Alt+Del. DefScreenSaverProc
// would close the window on mouse movement, clicks, keyboard,
// or deactivation.
//------------------------------------------------------------------
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_KEYDOWN:
if( wParam == 'W' || wParam == 'K' )
{
g_Settings.backgroundColor = ( wParam == 'K' ) ? 1 : 0;
InvalidateRect( hWnd, NULL, FALSE );
}
return 0;
case WM_KEYUP:
case WM_SYSKEYDOWN:
return 0;
case WM_ACTIVATE:
case WM_ACTIVATEAPP:
// Don't close on deactivation (e.g. LockWorkStation switches desktop).
return 0;
case WM_SYSCOMMAND:
// Block SC_CLOSE from Alt+F4 etc.
if( ( wParam & 0xFFF0 ) == SC_CLOSE )
return 0;
break;
}
return DefScreenSaverProc( hWnd, msg, wParam, lParam );
}
//----------------------------------------------------------------------------
//
// ScreenSaverConfigureDialog
//
// No configuration — ZoomIt handles all settings.
//
//----------------------------------------------------------------------------
BOOL WINAPI ScreenSaverConfigureDialog( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
return FALSE;
}
//----------------------------------------------------------------------------
//
// RegisterDialogClasses
//
// Nothing to register.
//
//----------------------------------------------------------------------------
BOOL WINAPI RegisterDialogClasses( HANDLE hInst )
{
return TRUE;
}

View File

@@ -91,3 +91,12 @@ void Trace::ZoomItActivateSnip() noexcept
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::ZoomItActivateSnipOcr() noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"ZoomIt_ActivateSnipOcr",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -14,4 +14,5 @@ public:
static void ZoomItActivateDemoType() noexcept;
static void ZoomItActivateRecord() noexcept;
static void ZoomItActivateSnip() noexcept;
static void ZoomItActivateSnipOcr() noexcept;
};

View File

@@ -70,6 +70,8 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
{ L"DrawToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"RecordToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"SnipToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"SnipOcrToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"SnipPanoramaToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"BreakTimerKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"DemoTypeToggleKey", SPECIAL_SEMANTICS_SHORTCUT },
{ L"PenColor", SPECIAL_SEMANTICS_COLOR },

View File

@@ -76,7 +76,7 @@ bool isExcluded(HWND window)
}
AlwaysOnTop::AlwaysOnTop(bool useLLKH, DWORD mainThreadId) :
SettingsObserver({SettingId::FrameEnabled, SettingId::Hotkey, SettingId::ExcludeApps, SettingId::ShowInSystemMenu}),
SettingsObserver({ SettingId::FrameEnabled, SettingId::Hotkey, SettingId::IncreaseOpacityHotkey, SettingId::DecreaseOpacityHotkey, SettingId::ExcludeApps, SettingId::ShowInSystemMenu }),
m_hinstance(reinterpret_cast<HINSTANCE>(&__ImageBase)),
m_useCentralizedLLKH(useLLKH),
m_mainThreadId(mainThreadId),
@@ -150,6 +150,8 @@ void AlwaysOnTop::SettingsUpdate(SettingId id)
switch (id)
{
case SettingId::Hotkey:
case SettingId::IncreaseOpacityHotkey:
case SettingId::DecreaseOpacityHotkey:
{
RegisterHotkey();
}
@@ -360,10 +362,8 @@ void AlwaysOnTop::RegisterHotkey() const
// Register pin hotkey
RegisterHotKey(m_window, static_cast<int>(HotkeyId::Pin), settings->hotkey.get_modifiers(), settings->hotkey.get_code());
// Register transparency hotkeys using the same modifiers as the pin hotkey
UINT modifiers = settings->hotkey.get_modifiers();
RegisterHotKey(m_window, static_cast<int>(HotkeyId::IncreaseOpacity), modifiers, VK_OEM_PLUS);
RegisterHotKey(m_window, static_cast<int>(HotkeyId::DecreaseOpacity), modifiers, VK_OEM_MINUS);
RegisterHotKey(m_window, static_cast<int>(HotkeyId::IncreaseOpacity), settings->increaseOpacityHotkey.get_modifiers(), settings->increaseOpacityHotkey.get_code());
RegisterHotKey(m_window, static_cast<int>(HotkeyId::DecreaseOpacity), settings->decreaseOpacityHotkey.get_modifiers(), settings->decreaseOpacityHotkey.get_code());
}
void AlwaysOnTop::RegisterLLKH()

View File

@@ -13,6 +13,8 @@ namespace NonLocalizable
const static wchar_t* SettingsFileName = L"settings.json";
const static wchar_t* HotkeyID = L"hotkey";
const static wchar_t* IncreaseOpacityHotkeyID = L"increase-opacity-hotkey";
const static wchar_t* DecreaseOpacityHotkeyID = L"decrease-opacity-hotkey";
const static wchar_t* SoundEnabledID = L"sound-enabled";
const static wchar_t* ShowInSystemMenuID = L"show-in-system-menu";
const static wchar_t* FrameEnabledID = L"frame-enabled";
@@ -100,16 +102,21 @@ void AlwaysOnTopSettings::LoadSettings()
const auto currentSettings = AlwaysOnTopSettings::settings();
auto updatedSettings = std::make_shared<Settings>(*currentSettings);
std::vector<SettingId> changedSettings;
if (const auto jsonVal = values.get_json(NonLocalizable::HotkeyID))
{
auto val = PowerToysSettings::HotkeyObject::from_json(*jsonVal);
if (updatedSettings->hotkey.get_modifiers() != val.get_modifiers() || updatedSettings->hotkey.get_key() != val.get_key() || updatedSettings->hotkey.get_code() != val.get_code())
const auto updateHotkeySetting = [&](const wchar_t* hotkeyName, auto& currentHotkey, SettingId settingId) {
if (const auto jsonVal = values.get_json(hotkeyName))
{
updatedSettings->hotkey = val;
changedSettings.push_back(SettingId::Hotkey);
auto val = PowerToysSettings::HotkeyObject::from_json(*jsonVal);
if (currentHotkey.get_modifiers() != val.get_modifiers() || currentHotkey.get_key() != val.get_key() || currentHotkey.get_code() != val.get_code())
{
currentHotkey = val;
changedSettings.push_back(settingId);
}
}
}
};
updateHotkeySetting(NonLocalizable::HotkeyID, updatedSettings->hotkey, SettingId::Hotkey);
updateHotkeySetting(NonLocalizable::IncreaseOpacityHotkeyID, updatedSettings->increaseOpacityHotkey, SettingId::IncreaseOpacityHotkey);
updateHotkeySetting(NonLocalizable::DecreaseOpacityHotkeyID, updatedSettings->decreaseOpacityHotkey, SettingId::DecreaseOpacityHotkey);
if (const auto jsonVal = values.get_bool_value(NonLocalizable::SoundEnabledID))
{

View File

@@ -18,6 +18,8 @@ class SettingsObserver;
struct Settings
{
PowerToysSettings::HotkeyObject hotkey = PowerToysSettings::HotkeyObject::from_settings(true, true, false, false, 84); // win + ctrl + T
PowerToysSettings::HotkeyObject increaseOpacityHotkey = PowerToysSettings::HotkeyObject::from_settings(true, true, false, false, VK_OEM_PLUS); // win + ctrl + '+'
PowerToysSettings::HotkeyObject decreaseOpacityHotkey = PowerToysSettings::HotkeyObject::from_settings(true, true, false, false, VK_OEM_MINUS); // win + ctrl + '-'
static constexpr int minTransparencyPercentage = 20; // minimum transparency (can't go below 20%)
static constexpr int maxTransparencyPercentage = 100; // maximum (fully opaque)
static constexpr int transparencyStep = 10; // step size for +/- adjustment

View File

@@ -3,6 +3,8 @@
enum class SettingId
{
Hotkey = 0,
IncreaseOpacityHotkey,
DecreaseOpacityHotkey,
SoundEnabled,
ShowInSystemMenu,
FrameEnabled,

View File

@@ -16,6 +16,8 @@
namespace NonLocalizable
{
const wchar_t ModulePath[] = L"PowerToys.AlwaysOnTop.exe";
// Keep in sync with src\modules\alwaysontop\AlwaysOnTop\AlwaysOnTop.cpp
const wchar_t PinnedWindowProp[] = L"AlwaysOnTop_Pinned";
}
namespace
@@ -27,6 +29,8 @@ namespace
const wchar_t JSON_KEY_SHIFT[] = L"shift";
const wchar_t JSON_KEY_CODE[] = L"code";
const wchar_t JSON_KEY_HOTKEY[] = L"hotkey";
const wchar_t JSON_KEY_INCREASE_OPACITY_HOTKEY[] = L"increase-opacity-hotkey";
const wchar_t JSON_KEY_DECREASE_OPACITY_HOTKEY[] = L"decrease-opacity-hotkey";
const wchar_t JSON_KEY_VALUE[] = L"value";
}
@@ -107,27 +111,38 @@ public:
virtual bool on_hotkey(size_t hotkeyId) override
{
if (m_enabled)
if (!m_enabled)
{
return false;
}
Logger::trace(L"AlwaysOnTop hotkey pressed, id={}", hotkeyId);
if (hotkeyId == 0)
{
Logger::trace(L"AlwaysOnTop hotkey pressed, id={}", hotkeyId);
if (!is_process_running())
{
Enable();
}
if (hotkeyId == 0)
SetEvent(m_hPinEvent);
return true;
}
if (hotkeyId == 1 || hotkeyId == 2)
{
const HWND foregroundWindow = GetForegroundWindow();
if (!foregroundWindow || !IsWindow(foregroundWindow) || !GetPropW(foregroundWindow, NonLocalizable::PinnedWindowProp))
{
SetEvent(m_hPinEvent);
}
else if (hotkeyId == 1)
{
SetEvent(m_hIncreaseOpacityEvent);
}
else if (hotkeyId == 2)
{
SetEvent(m_hDecreaseOpacityEvent);
return false;
}
if (!is_process_running())
{
Enable();
}
SetEvent(hotkeyId == 1 ? m_hIncreaseOpacityEvent : m_hDecreaseOpacityEvent);
return true;
}
@@ -136,48 +151,48 @@ public:
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
{
size_t count = 0;
// Hotkey 0: Pin/Unpin (e.g., Win+Ctrl+T)
if (m_hotkey.key)
constexpr size_t hotkeyCount = 3;
Hotkey configuredHotkeys[hotkeyCount] = { m_hotkey, m_increaseOpacityHotkey, m_decreaseOpacityHotkey };
for (size_t i = 0; i < hotkeyCount; ++i)
{
if (hotkeys && buffer_size > count)
{
hotkeys[count] = m_hotkey;
Logger::trace(L"AlwaysOnTop hotkey[0]: win={}, ctrl={}, shift={}, alt={}, key={}",
m_hotkey.win, m_hotkey.ctrl, m_hotkey.shift, m_hotkey.alt, m_hotkey.key);
}
count++;
configuredHotkeys[i].id = static_cast<int>(i);
configuredHotkeys[i].isShown = configuredHotkeys[i].key != 0;
}
// Hotkey 1: Increase opacity (same modifiers + VK_OEM_PLUS '=')
if (m_hotkey.key)
if (hotkeys)
{
if (hotkeys && buffer_size > count)
const size_t countToCopy = (buffer_size < hotkeyCount) ? buffer_size : hotkeyCount;
for (size_t i = 0; i < countToCopy; ++i)
{
hotkeys[count] = m_hotkey;
hotkeys[count].key = VK_OEM_PLUS; // '=' key
Logger::trace(L"AlwaysOnTop hotkey[1] (increase opacity): win={}, ctrl={}, shift={}, alt={}, key={}",
hotkeys[count].win, hotkeys[count].ctrl, hotkeys[count].shift, hotkeys[count].alt, hotkeys[count].key);
hotkeys[i] = configuredHotkeys[i];
}
count++;
}
// Hotkey 2: Decrease opacity (same modifiers + VK_OEM_MINUS '-')
if (m_hotkey.key)
{
if (hotkeys && buffer_size > count)
{
hotkeys[count] = m_hotkey;
hotkeys[count].key = VK_OEM_MINUS; // '-' key
Logger::trace(L"AlwaysOnTop hotkey[2] (decrease opacity): win={}, ctrl={}, shift={}, alt={}, key={}",
hotkeys[count].win, hotkeys[count].ctrl, hotkeys[count].shift, hotkeys[count].alt, hotkeys[count].key);
}
count++;
}
Logger::trace(L"AlwaysOnTop hotkey[0]: win={}, ctrl={}, shift={}, alt={}, key={}, shown={}",
configuredHotkeys[0].win,
configuredHotkeys[0].ctrl,
configuredHotkeys[0].shift,
configuredHotkeys[0].alt,
configuredHotkeys[0].key,
configuredHotkeys[0].isShown);
Logger::trace(L"AlwaysOnTop hotkey[1] (increase opacity): win={}, ctrl={}, shift={}, alt={}, key={}, shown={}",
configuredHotkeys[1].win,
configuredHotkeys[1].ctrl,
configuredHotkeys[1].shift,
configuredHotkeys[1].alt,
configuredHotkeys[1].key,
configuredHotkeys[1].isShown);
Logger::trace(L"AlwaysOnTop hotkey[2] (decrease opacity): win={}, ctrl={}, shift={}, alt={}, key={}, shown={}",
configuredHotkeys[2].win,
configuredHotkeys[2].ctrl,
configuredHotkeys[2].shift,
configuredHotkeys[2].alt,
configuredHotkeys[2].key,
configuredHotkeys[2].isShown);
Logger::trace(L"AlwaysOnTop get_hotkeys returning count={}", count);
return count;
Logger::trace(L"AlwaysOnTop get_hotkeys returning count={}", hotkeyCount);
return hotkeyCount;
}
// Enable the powertoy
@@ -279,21 +294,34 @@ private:
void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
{
const auto parseSingleHotkey = [](const winrt::Windows::Data::Json::JsonObject& propertiesObject, const wchar_t* hotkeyName, Hotkey& hotkey) {
try
{
auto jsonHotkeyObject = propertiesObject.GetNamedObject(hotkeyName).GetNamedObject(JSON_KEY_VALUE);
hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
}
catch (...)
{
}
};
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
try
{
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HOTKEY).GetNamedObject(JSON_KEY_VALUE);
m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES);
parseSingleHotkey(propertiesObject, JSON_KEY_HOTKEY, m_hotkey);
parseSingleHotkey(propertiesObject, JSON_KEY_INCREASE_OPACITY_HOTKEY, m_increaseOpacityHotkey);
parseSingleHotkey(propertiesObject, JSON_KEY_DECREASE_OPACITY_HOTKEY, m_decreaseOpacityHotkey);
}
catch (...)
{
Logger::error("Failed to initialize AlwaysOnTop start shortcut");
Logger::error("Failed to initialize AlwaysOnTop shortcuts");
}
}
else
@@ -329,7 +357,9 @@ private:
bool m_enabled = false;
HANDLE m_hProcess = nullptr;
Hotkey m_hotkey;
Hotkey m_hotkey{ .win = true, .ctrl = true, .shift = false, .alt = false, .key = 'T' };
Hotkey m_increaseOpacityHotkey{ .win = true, .ctrl = true, .shift = false, .alt = false, .key = VK_OEM_PLUS };
Hotkey m_decreaseOpacityHotkey{ .win = true, .ctrl = true, .shift = false, .alt = false, .key = VK_OEM_MINUS };
// Handle to event used to pin/unpin windows
HANDLE m_hPinEvent;

View File

@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.Common;
/// <summary>
/// Shared constants for the extension load sentinel file used by
/// <c>ProviderLoadGuard</c> and provider-specific crash sentinels to
/// coordinate crash detection across process lifetimes.
/// </summary>
public static class ExtensionLoadState
{
/// <summary>
/// Name of the sentinel JSON file written to the config directory.
/// Both the app-level guard and individual extension sentinels must
/// read and write the same file for crash detection to work.
/// </summary>
public const string SentinelFileName = "extensionLoadState.json";
/// <summary>
/// JSON property name storing the owning provider id for a guarded block.
/// </summary>
public const string ProviderIdKey = "providerId";
/// <summary>
/// JSON property name indicating a guarded block was active when the
/// process exited.
/// </summary>
public const string LoadingKey = "loading";
/// <summary>
/// JSON property name storing the consecutive crash count for a guarded
/// block.
/// </summary>
public const string CrashCountKey = "crashCount";
/// <summary>
/// Shared lock that must be held around every read-modify-write cycle
/// on the sentinel file. Both <c>ProviderLoadGuard</c> and
/// provider-specific crash sentinels run in the same process and would
/// otherwise race on the file, silently dropping entries.
/// </summary>
public static readonly object SentinelFileLock = new();
}

View File

@@ -0,0 +1,241 @@
// 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.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Common.Helpers;
/// <summary>
/// Tracks guarded provider blocks in the shared extension-load sentinel file so
/// callers can fail closed after repeated native crashes that bypass managed
/// exception handling.
/// </summary>
public sealed class ProviderCrashSentinel
{
private readonly string _providerId;
private readonly Lock _sentinelLock = new();
private readonly HashSet<string> _completedBlocks = [];
private readonly HashSet<string> _activeBlocks = [];
public ProviderCrashSentinel(string providerId)
{
ArgumentException.ThrowIfNullOrWhiteSpace(providerId);
_providerId = providerId;
}
public bool BeginBlock(string blockSuffix)
{
var blockId = CreateBlockId(blockSuffix);
lock (_sentinelLock)
{
if (_completedBlocks.Contains(blockId) || !_activeBlocks.Add(blockId))
{
return false;
}
UpdateState(
state =>
{
var entry = GetOrCreateEntry(state, blockId);
entry[ExtensionLoadState.LoadingKey] = true;
});
return true;
}
}
public void CompleteBlock(string blockSuffix)
{
var blockId = CreateBlockId(blockSuffix);
lock (_sentinelLock)
{
if (!_activeBlocks.Remove(blockId))
{
return;
}
_completedBlocks.Add(blockId);
UpdateState(state => state.Remove(blockId));
}
}
public void CancelBlock(string blockSuffix)
{
var blockId = CreateBlockId(blockSuffix);
lock (_sentinelLock)
{
if (!_activeBlocks.Remove(blockId))
{
return;
}
UpdateState(state => state.Remove(blockId));
}
}
public void ClearProviderState()
{
lock (_sentinelLock)
{
_completedBlocks.RemoveWhere(blockId => blockId.StartsWith(_providerId + ".", StringComparison.Ordinal));
_activeBlocks.RemoveWhere(blockId => blockId.StartsWith(_providerId + ".", StringComparison.Ordinal));
UpdateState(
state =>
{
var keysToRemove = state
.Where(kvp => IsProviderEntry(kvp.Key, kvp.Value as JsonObject))
.Select(kvp => kvp.Key)
.ToArray();
foreach (var key in keysToRemove)
{
state.Remove(key);
}
});
}
}
private void UpdateState(Action<JsonObject> update)
{
try
{
lock (ExtensionLoadState.SentinelFileLock)
{
var sentinelPath = GetSentinelPath();
var state = LoadState(sentinelPath);
update(state);
SaveState(sentinelPath, state);
}
}
catch (Exception ex)
{
CoreLogger.LogError($"Failed to update crash sentinel state for provider '{_providerId}'.", ex);
}
}
private static JsonObject LoadState(string sentinelPath)
{
if (!File.Exists(sentinelPath))
{
return [];
}
try
{
var json = File.ReadAllText(sentinelPath);
if (string.IsNullOrWhiteSpace(json))
{
CoreLogger.LogWarning($"Crash sentinel state file '{sentinelPath}' was empty. Treating as empty state.");
DeleteInvalidStateFile(sentinelPath);
return [];
}
if (JsonNode.Parse(json) is JsonObject state)
{
return state;
}
CoreLogger.LogError($"Crash sentinel state file '{sentinelPath}' did not contain a JSON object. Treating as empty state.");
DeleteInvalidStateFile(sentinelPath);
}
catch (JsonException ex)
{
CoreLogger.LogError($"Failed to parse crash sentinel state from '{sentinelPath}'. Treating as empty state.", ex);
DeleteInvalidStateFile(sentinelPath);
}
catch (IOException ex)
{
CoreLogger.LogError($"Failed to read crash sentinel state from '{sentinelPath}'. Treating as empty state.", ex);
}
catch (UnauthorizedAccessException ex)
{
CoreLogger.LogError($"Access denied reading crash sentinel state from '{sentinelPath}'. Treating as empty state.", ex);
}
return [];
}
private static void DeleteInvalidStateFile(string sentinelPath)
{
try
{
File.Delete(sentinelPath);
}
catch (IOException ex)
{
CoreLogger.LogError($"Failed to delete invalid crash sentinel state file '{sentinelPath}'.", ex);
}
catch (UnauthorizedAccessException ex)
{
CoreLogger.LogError($"Access denied deleting invalid crash sentinel state file '{sentinelPath}'.", ex);
}
}
private static void SaveState(string sentinelPath, JsonObject state)
{
if (state.Count == 0)
{
if (File.Exists(sentinelPath))
{
File.Delete(sentinelPath);
}
return;
}
var directory = Path.GetDirectoryName(sentinelPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
var tempPath = sentinelPath + ".tmp";
File.WriteAllText(tempPath, state.ToJsonString());
File.Move(tempPath, sentinelPath, overwrite: true);
}
private JsonObject GetOrCreateEntry(JsonObject state, string blockId)
{
if (state[blockId] is JsonObject existing)
{
existing[ExtensionLoadState.ProviderIdKey] = _providerId;
return existing;
}
var entry = new JsonObject
{
[ExtensionLoadState.ProviderIdKey] = _providerId,
[ExtensionLoadState.LoadingKey] = false,
[ExtensionLoadState.CrashCountKey] = 0,
};
state[blockId] = entry;
return entry;
}
private bool IsProviderEntry(string blockId, JsonObject? entry)
{
var providerId = entry?[ExtensionLoadState.ProviderIdKey]?.GetValue<string>();
if (string.Equals(providerId, _providerId, StringComparison.Ordinal))
{
return true;
}
return blockId.StartsWith(_providerId + ".", StringComparison.Ordinal);
}
private string CreateBlockId(string blockSuffix)
{
return $"{_providerId}.{blockSuffix}";
}
private static string GetSentinelPath()
{
return Path.Combine(Utilities.BaseSettingsPath("Microsoft.CmdPal"), ExtensionLoadState.SentinelFileName);
}
}

View File

@@ -0,0 +1,231 @@
// 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.Text.Json.Nodes;
namespace Microsoft.CmdPal.Common.Helpers;
/// <summary>
/// Reads the shared provider crash sentinel file at startup, increments crash
/// counts for blocks left marked as active, and determines which providers
/// should be soft-disabled for the current session.
/// </summary>
public sealed class ProviderLoadGuard
{
private const int MaxConsecutiveCrashes = 2;
private readonly string _sentinelPath;
private readonly HashSet<string> _disabledProviders = [];
public ProviderLoadGuard(string configDirectory)
{
_sentinelPath = Path.Combine(configDirectory, ExtensionLoadState.SentinelFileName);
DetectCrashes();
}
/// <summary>
/// Returns true if the provider has been disabled due to repeated crashes
/// in one of its tracked guarded blocks.
/// </summary>
public bool IsProviderDisabled(string providerId) => _disabledProviders.Contains(providerId);
/// <summary>
/// Call immediately before attempting a guarded operation.
/// Marks the block as "loading" in the sentinel file so that a
/// subsequent native crash leaves evidence on disk.
/// </summary>
public void Enter(string blockId, string providerId)
{
UpdateState(state =>
{
var entry = GetOrCreateEntry(state, blockId, providerId);
entry[ExtensionLoadState.LoadingKey] = true;
});
}
/// <summary>
/// Call after a guarded operation succeeds or fails gracefully via managed
/// exception. Clears the loading flag and removes the block entry.
/// </summary>
public void Exit(string blockId)
{
UpdateState(state => state.Remove(blockId));
}
/// <summary>
/// Removes any persisted crash state for a provider so it can be retried
/// on the next launch.
/// </summary>
public void ClearProvider(string providerId)
{
_disabledProviders.Remove(providerId);
UpdateState(state =>
{
var keysToRemove = state
.Where(kvp => TryGetProviderId(kvp.Key, kvp.Value as JsonObject) == providerId)
.Select(kvp => kvp.Key)
.ToArray();
foreach (var key in keysToRemove)
{
state.Remove(key);
}
});
}
private void DetectCrashes()
{
// Read the sentinel file once at startup to detect providers that
// crashed on the previous launch, then write back the updated state.
lock (ExtensionLoadState.SentinelFileLock)
{
var state = ReadState();
var keysToCheck = state.Select(kvp => kvp.Key).ToArray();
foreach (var key in keysToCheck)
{
if (state[key] is not JsonObject entry)
{
continue;
}
var providerId = TryGetProviderId(key, entry);
var wasLoading = entry[ExtensionLoadState.LoadingKey]?.GetValue<bool>() ?? false;
if (wasLoading)
{
// The guarded block was active when the process died.
var crashCount = (entry[ExtensionLoadState.CrashCountKey]?.GetValue<int>() ?? 0) + 1;
entry[ExtensionLoadState.CrashCountKey] = crashCount;
entry[ExtensionLoadState.LoadingKey] = false;
if (crashCount >= MaxConsecutiveCrashes)
{
_disabledProviders.Add(providerId);
CoreLogger.LogError($"Provider '{providerId}' disabled after {crashCount} consecutive crash(es) in guarded block '{key}'.");
}
else
{
CoreLogger.LogWarning($"Guarded block '{key}' for provider '{providerId}' crashed on previous launch (crash {crashCount}/{MaxConsecutiveCrashes}). Will retry.");
}
}
var currentCrashCount = entry[ExtensionLoadState.CrashCountKey]?.GetValue<int>() ?? 0;
if (currentCrashCount >= MaxConsecutiveCrashes)
{
// Persist disabled state from a previous session.
_disabledProviders.Add(providerId);
}
if (!(entry[ExtensionLoadState.LoadingKey]?.GetValue<bool>() ?? false) && currentCrashCount == 0)
{
state.Remove(key);
}
}
WriteState(state);
}
}
/// <summary>
/// Reads the sentinel file, applies a mutation, and writes it back
/// under <see cref="ExtensionLoadState.SentinelFileLock"/> to prevent
/// concurrent writers from clobbering each other's entries.
/// </summary>
private void UpdateState(Action<JsonObject> mutate)
{
try
{
lock (ExtensionLoadState.SentinelFileLock)
{
var state = ReadState();
mutate(state);
WriteState(state);
}
}
catch (Exception ex)
{
CoreLogger.LogError("Failed to update extension load sentinel file.", ex);
}
}
private static JsonObject GetOrCreateEntry(JsonObject state, string blockId, string providerId)
{
if (state[blockId] is JsonObject existing)
{
existing[ExtensionLoadState.ProviderIdKey] = providerId;
return existing;
}
var entry = new JsonObject
{
[ExtensionLoadState.ProviderIdKey] = providerId,
[ExtensionLoadState.LoadingKey] = false,
[ExtensionLoadState.CrashCountKey] = 0,
};
state[blockId] = entry;
return entry;
}
private static string TryGetProviderId(string blockId, JsonObject? entry)
{
var providerId = entry?[ExtensionLoadState.ProviderIdKey]?.GetValue<string>();
if (!string.IsNullOrWhiteSpace(providerId))
{
return providerId;
}
var separatorIndex = blockId.IndexOf('.');
return separatorIndex > 0 ? blockId[..separatorIndex] : blockId;
}
private JsonObject ReadState()
{
try
{
if (File.Exists(_sentinelPath))
{
var json = File.ReadAllText(_sentinelPath);
return JsonNode.Parse(json)?.AsObject() ?? [];
}
}
catch (Exception ex)
{
CoreLogger.LogError("Failed to read extension load sentinel file.", ex);
}
return [];
}
private void WriteState(JsonObject state)
{
try
{
if (state.Count == 0)
{
if (File.Exists(_sentinelPath))
{
File.Delete(_sentinelPath);
}
return;
}
var directory = Path.GetDirectoryName(_sentinelPath);
if (directory != null)
{
Directory.CreateDirectory(directory);
}
var tempPath = _sentinelPath + ".tmp";
File.WriteAllText(tempPath, state.ToJsonString());
File.Move(tempPath, _sentinelPath, overwrite: true);
}
catch (Exception ex)
{
CoreLogger.LogError("Failed to write extension load sentinel file.", ex);
}
}
}

View File

@@ -5,6 +5,7 @@
"src\\common\\Common.Search\\Common.Search.csproj",
"src\\common\\Common.UI\\Common.UI.csproj",
"src\\common\\ManagedCommon\\ManagedCommon.csproj",
"src\\common\\ManagedCsWin32\\ManagedCsWin32.csproj",
"src\\common\\ManagedTelemetry\\Telemetry\\ManagedTelemetry.csproj",
"src\\common\\PowerToys.ModuleContracts\\PowerToys.ModuleContracts.csproj",
"src\\common\\SettingsAPI\\SettingsAPI.vcxproj",
@@ -18,8 +19,11 @@
"src\\modules\\ZoomIt\\ZoomItSettingsInterop\\ZoomItSettingsInterop.vcxproj",
"src\\modules\\awake\\Awake.ModuleServices\\Awake.ModuleServices.csproj",
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.PowerToys\\Microsoft.CmdPal.Ext.PowerToys.csproj",
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions.Toolkit\\Microsoft.CommandPalette.Extensions.Toolkit.csproj",
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj",
"src\\modules\\colorPicker\\ColorPicker.ModuleServices\\ColorPicker.ModuleServices.csproj",
"src\\modules\\fancyzones\\FancyZonesEditorCommon\\FancyZonesEditorCommon.csproj",
"src\\modules\\powerdisplay\\PowerDisplay.Lib\\PowerDisplay.Lib.csproj",
"src\\settings-ui\\Settings.UI.Library\\Settings.UI.Library.csproj"
]
}

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.UI.ViewModels.Messages;
@@ -14,26 +15,32 @@ public partial class AliasManager : ObservableObject
private readonly TopLevelCommandManager _topLevelCommandManager;
private readonly ISettingsService _settingsService;
// REMEMBER, CommandAlias.SearchPrefix is what we use as keys
private readonly Dictionary<string, CommandAlias> _aliases;
private static readonly ImmutableList<CommandAlias> _defaultAliases = new List<CommandAlias>
{
new CommandAlias(":", "com.microsoft.cmdpal.registry", true),
new CommandAlias("$", "com.microsoft.cmdpal.windowsSettings", true),
new CommandAlias("=", "com.microsoft.cmdpal.calculator", true),
new CommandAlias(">", "com.microsoft.cmdpal.shell", true),
new CommandAlias("<", "com.microsoft.cmdpal.windowwalker", true),
new CommandAlias("??", "com.microsoft.cmdpal.websearch", true),
new CommandAlias("file", "com.microsoft.indexer.fileSearch", false),
new CommandAlias(")", "com.microsoft.cmdpal.timedate", true),
}.ToImmutableList();
public AliasManager(TopLevelCommandManager tlcManager, ISettingsService settingsService)
{
_topLevelCommandManager = tlcManager;
_settingsService = settingsService;
_aliases = _settingsService.Settings.Aliases;
if (_aliases.Count == 0)
if (_settingsService.Settings.Aliases.Count == 0)
{
PopulateDefaultAliases();
}
}
private void AddAlias(CommandAlias a) => _aliases.Add(a.SearchPrefix, a);
public bool CheckAlias(string searchText)
{
if (_aliases.TryGetValue(searchText, out var alias))
if (_settingsService.Settings.Aliases.TryGetValue(searchText, out var alias))
{
try
{
@@ -56,19 +63,18 @@ public partial class AliasManager : ObservableObject
private void PopulateDefaultAliases()
{
this.AddAlias(new CommandAlias(":", "com.microsoft.cmdpal.registry", true));
this.AddAlias(new CommandAlias("$", "com.microsoft.cmdpal.windowsSettings", true));
this.AddAlias(new CommandAlias("=", "com.microsoft.cmdpal.calculator", true));
this.AddAlias(new CommandAlias(">", "com.microsoft.cmdpal.shell", true));
this.AddAlias(new CommandAlias("<", "com.microsoft.cmdpal.windowwalker", true));
this.AddAlias(new CommandAlias("??", "com.microsoft.cmdpal.websearch", true));
this.AddAlias(new CommandAlias("file", "com.microsoft.indexer.fileSearch", false));
this.AddAlias(new CommandAlias(")", "com.microsoft.cmdpal.timedate", true));
_settingsService.UpdateSettings(
s => s with
{
Aliases = s.Aliases
.AddRange(_defaultAliases.ToDictionary(a => a.SearchPrefix, a => a)),
},
hotReload: false);
}
public string? KeysFromId(string commandId)
{
return _aliases
return _settingsService.Settings.Aliases
.Where(kv => kv.Value.CommandId == commandId)
.Select(kv => kv.Value.Alias)
.FirstOrDefault();
@@ -76,7 +82,7 @@ public partial class AliasManager : ObservableObject
public CommandAlias? AliasFromId(string commandId)
{
return _aliases
return _settingsService.Settings.Aliases
.Where(kv => kv.Value.CommandId == commandId)
.Select(kv => kv.Value)
.FirstOrDefault();
@@ -90,9 +96,11 @@ public partial class AliasManager : ObservableObject
return;
}
var aliases = _settingsService.Settings.Aliases;
// If we already have _this exact alias_, do nothing
if (newAlias is not null &&
_aliases.TryGetValue(newAlias.SearchPrefix, out var existingAlias))
aliases.TryGetValue(newAlias.SearchPrefix, out var existingAlias))
{
if (existingAlias.CommandId == commandId)
{
@@ -100,19 +108,19 @@ public partial class AliasManager : ObservableObject
}
}
List<CommandAlias> toRemove = [];
foreach (var kv in _aliases)
var keysToRemove = new List<string>();
foreach (var kv in aliases)
{
// Look for the old aliases for the command, and remove it
if (kv.Value.CommandId == commandId)
{
toRemove.Add(kv.Value);
keysToRemove.Add(kv.Key);
}
// Look for the alias belonging to another command, and remove it
if (newAlias is not null && kv.Value.Alias == newAlias.Alias && kv.Value.CommandId != commandId)
{
toRemove.Add(kv.Value);
keysToRemove.Add(kv.Key);
// Remove alias from other TopLevelViewModels it may be assigned to
var topLevelCommand = _topLevelCommandManager.LookupCommand(kv.Value.CommandId);
@@ -123,15 +131,16 @@ public partial class AliasManager : ObservableObject
}
}
foreach (var alias in toRemove)
_settingsService.UpdateSettings(s =>
{
// REMEMBER, SearchPrefix is what we use as keys
_aliases.Remove(alias.SearchPrefix);
}
var updatedAliases = s.Aliases.RemoveRange(keysToRemove);
if (newAlias is not null)
{
AddAlias(newAlias);
}
if (newAlias is not null)
{
updatedAliases = updatedAliases.Add(newAlias.SearchPrefix, newAlias);
}
return s with { Aliases = updatedAliases };
});
}
}

View File

@@ -2,20 +2,18 @@
// 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.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.Immutable;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class AppStateModel : ObservableObject
public record AppStateModel
{
///////////////////////////////////////////////////////////////////////////
// STATE HERE
// 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 RecentCommandsManager RecentCommands { get; init; } = new();
public List<string> RunHistory { get; set; } = [];
public ImmutableList<string> RunHistory { get; init; } = ImmutableList<string>.Empty;
// END SETTINGS
///////////////////////////////////////////////////////////////////////////

View File

@@ -112,10 +112,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.Theme != value)
{
_settingsService.Settings.Theme = value;
_settingsService.UpdateSettings(s => s with { Theme = value });
OnPropertyChanged();
OnPropertyChanged(nameof(ThemeIndex));
Save();
DebouncedReapply();
}
}
}
@@ -127,7 +127,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.ColorizationMode != value)
{
_settingsService.Settings.ColorizationMode = value;
_settingsService.UpdateSettings(s => s with { ColorizationMode = value });
OnPropertyChanged();
OnPropertyChanged(nameof(ColorizationModeIndex));
OnPropertyChanged(nameof(IsCustomTintVisible));
@@ -146,7 +146,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
IsColorizationDetailsExpanded = value != ColorizationMode.None;
Save();
DebouncedReapply();
}
}
}
@@ -164,7 +164,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.CustomThemeColor != value)
{
_settingsService.Settings.CustomThemeColor = value;
_settingsService.UpdateSettings(s => s with { CustomThemeColor = value });
OnPropertyChanged();
@@ -173,7 +173,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
ColorIntensity = 100;
}
Save();
DebouncedReapply();
}
}
}
@@ -183,10 +183,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
get => _settingsService.Settings.CustomThemeColorIntensity;
set
{
_settingsService.Settings.CustomThemeColorIntensity = value;
_settingsService.UpdateSettings(s => s with { CustomThemeColorIntensity = value });
OnPropertyChanged();
OnPropertyChanged(nameof(EffectiveTintIntensity));
Save();
DebouncedReapply();
}
}
@@ -195,10 +195,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
get => _settingsService.Settings.BackgroundImageTintIntensity;
set
{
_settingsService.Settings.BackgroundImageTintIntensity = value;
_settingsService.UpdateSettings(s => s with { BackgroundImageTintIntensity = value });
OnPropertyChanged();
OnPropertyChanged(nameof(EffectiveTintIntensity));
Save();
DebouncedReapply();
}
}
@@ -209,7 +209,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.BackgroundImagePath != value)
{
_settingsService.Settings.BackgroundImagePath = value;
_settingsService.UpdateSettings(s => s with { BackgroundImagePath = value });
OnPropertyChanged();
if (BackgroundImageOpacity == 0)
@@ -217,7 +217,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
BackgroundImageOpacity = 100;
}
Save();
DebouncedReapply();
}
}
}
@@ -229,9 +229,9 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.BackgroundImageOpacity != value)
{
_settingsService.Settings.BackgroundImageOpacity = value;
_settingsService.UpdateSettings(s => s with { BackgroundImageOpacity = value });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
}
@@ -243,9 +243,9 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.BackgroundImageBrightness != value)
{
_settingsService.Settings.BackgroundImageBrightness = value;
_settingsService.UpdateSettings(s => s with { BackgroundImageBrightness = value });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
}
@@ -257,9 +257,9 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.BackgroundImageBlurAmount != value)
{
_settingsService.Settings.BackgroundImageBlurAmount = value;
_settingsService.UpdateSettings(s => s with { BackgroundImageBlurAmount = value });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
}
@@ -271,10 +271,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.BackgroundImageFit != value)
{
_settingsService.Settings.BackgroundImageFit = value;
_settingsService.UpdateSettings(s => s with { BackgroundImageFit = value });
OnPropertyChanged();
OnPropertyChanged(nameof(BackgroundImageFitIndex));
Save();
DebouncedReapply();
}
}
}
@@ -305,11 +305,11 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
{
if (_settingsService.Settings.BackdropOpacity != value)
{
_settingsService.Settings.BackdropOpacity = value;
_settingsService.UpdateSettings(s => s with { BackdropOpacity = value });
OnPropertyChanged();
OnPropertyChanged(nameof(EffectiveBackdropStyle));
OnPropertyChanged(nameof(EffectiveImageOpacity));
Save();
DebouncedReapply();
}
}
}
@@ -322,7 +322,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
var newStyle = (BackdropStyle)value;
if (_settingsService.Settings.BackdropStyle != newStyle)
{
_settingsService.Settings.BackdropStyle = newStyle;
_settingsService.UpdateSettings(s => s with { BackdropStyle = newStyle });
OnPropertyChanged();
OnPropertyChanged(nameof(IsBackdropOpacityVisible));
@@ -335,7 +335,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
IsColorizationDetailsExpanded = false;
}
Save();
DebouncedReapply();
}
}
}
@@ -468,9 +468,8 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
}
private void Save()
private void DebouncedReapply()
{
_settingsService.Save();
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
}

View File

@@ -6,13 +6,13 @@ using System.Text.Json.Serialization;
namespace Microsoft.CmdPal.UI.ViewModels;
public class CommandAlias
public record CommandAlias
{
public string CommandId { get; set; }
public string CommandId { get; init; }
public string Alias { get; set; }
public string Alias { get; init; }
public bool IsDirect { get; set; }
public bool IsDirect { get; init; }
[JsonIgnore]
public string SearchPrefix => Alias + (IsDirect ? string.Empty : " ");

View File

@@ -2,7 +2,6 @@
// 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.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
@@ -106,7 +105,8 @@ public sealed partial class CommandBarViewModel : ObservableObject,
{
switch (e.PropertyName)
{
case nameof(SelectedItem.HasMoreCommands):
case nameof(SelectedItem.CanOpenContextMenu):
case nameof(SelectedItem.SecondaryCommand):
UpdateContextItems();
break;
}
@@ -122,9 +122,7 @@ public sealed partial class CommandBarViewModel : ObservableObject,
}
SecondaryCommand = SelectedItem.SecondaryCommand;
var moreCommands = SelectedItem.MoreCommands;
ShouldShowContextMenu = moreCommands.Count > 1 && SelectedItem.HasMoreCommands;
ShouldShowContextMenu = SelectedItem.CanOpenContextMenu;
OnPropertyChanged(nameof(HasSecondaryCommand));
OnPropertyChanged(nameof(SecondaryCommand));

View File

@@ -86,6 +86,8 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
public CommandItemViewModel? SecondaryCommand => _secondaryMoreCommand;
public bool CanOpenContextMenu => AllCommands.Any(item => item is CommandItemViewModel command && command.ShouldBeVisible);
public bool ShouldBeVisible => !string.IsNullOrEmpty(Name);
public bool HasTitle => !string.IsNullOrEmpty(Title);
@@ -213,22 +215,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
BuildAndInitMoreCommands();
if (!string.IsNullOrEmpty(model.Command?.Name))
{
_defaultCommandContextItemViewModel = new CommandContextItemViewModel(new CommandContextItem(model.Command!), PageContext)
{
_itemTitle = Name,
Subtitle = Subtitle,
Command = Command,
// TODO this probably should just be a CommandContextItemViewModel(CommandItemViewModel) ctor, or a copy ctor or whatever
// Anything we set manually here must stay in sync with the corresponding properties on CommandItemViewModel.
};
// Only set the icon on the context item for us if our command didn't
// have its own icon
UpdateDefaultContextItemIcon();
}
TryCreateDefaultCommandContextItem(model);
lock (_moreCommandsLock)
{
@@ -238,6 +225,8 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
Initialized |= InitializedState.SelectionInitialized;
UpdateProperty(nameof(MoreCommands));
UpdateProperty(nameof(AllCommands));
UpdateProperty(nameof(SecondaryCommand), nameof(SecondaryCommandName), nameof(HasMoreCommands));
UpdateProperty(nameof(CanOpenContextMenu));
UpdateProperty(nameof(IsSelectedInitialized));
}
@@ -335,14 +324,22 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
// or Command.Name change. This is a workaround to ensure that the Title is always up-to-date for extensions with old SDK.
_itemTitle = model.Title;
_defaultCommandContextItemViewModel?.Command = Command;
_defaultCommandContextItemViewModel?.UpdateTitle(_itemTitle);
UpdateDefaultContextItemIcon();
if (_defaultCommandContextItemViewModel is not null)
{
_defaultCommandContextItemViewModel.Command = Command;
_defaultCommandContextItemViewModel.UpdateTitle(_itemTitle);
UpdateDefaultContextItemIcon();
}
else
{
TryCreateDefaultCommandContextItem(model);
}
UpdateProperty(nameof(Name));
UpdateProperty(nameof(Title));
UpdateProperty(nameof(Icon));
UpdateProperty(nameof(HasText));
UpdateProperty(nameof(CanOpenContextMenu));
break;
case nameof(Title):
@@ -374,7 +371,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
case nameof(model.MoreCommands):
BuildAndInitMoreCommands();
UpdateProperty(nameof(SecondaryCommand), nameof(SecondaryCommandName), nameof(HasMoreCommands), nameof(AllCommands));
UpdateProperty(nameof(SecondaryCommand), nameof(SecondaryCommandName), nameof(HasMoreCommands), nameof(AllCommands), nameof(CanOpenContextMenu));
break;
case nameof(DataPackage):
@@ -402,8 +399,17 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
_itemTitle = model.Title;
_titleCache.Invalidate();
UpdateProperty(nameof(Title), nameof(Name));
UpdateProperty(nameof(CanOpenContextMenu));
if (_defaultCommandContextItemViewModel is not null)
{
_defaultCommandContextItemViewModel.UpdateTitle(model.Command.Name);
}
else
{
TryCreateDefaultCommandContextItem(model);
}
_defaultCommandContextItemViewModel?.UpdateTitle(model.Command.Name);
break;
case nameof(Command.Icon):
@@ -413,6 +419,46 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
}
}
/// <summary>
/// Creates <see cref="_defaultCommandContextItemViewModel"/> when it does not exist
/// yet and the current command has a non-empty name. This covers the case
/// where an extension initially exposes a <c>NoOpCommand</c> (empty name)
/// and later switches to a concrete command after <see cref="SlowInitializeProperties"/> has already run.
/// When a new instance is created, the snapshot is refreshed and
/// <see cref="AllCommands"/> is notified.
/// </summary>
private void TryCreateDefaultCommandContextItem(ICommandItem model)
{
if (_defaultCommandContextItemViewModel is not null)
{
return;
}
if (string.IsNullOrEmpty(model.Command?.Name))
{
return;
}
_defaultCommandContextItemViewModel = new CommandContextItemViewModel(new CommandContextItem(model.Command!), PageContext)
{
_itemTitle = Name,
Subtitle = Subtitle,
Command = Command,
// TODO this probably should just be a CommandContextItemViewModel(CommandItemViewModel) ctor, or a copy ctor or whatever
// Anything we set manually here must stay in sync with the corresponding properties on CommandItemViewModel.
};
UpdateDefaultContextItemIcon();
lock (_moreCommandsLock)
{
RefreshMoreCommandStateUnsafe();
}
UpdateProperty(nameof(AllCommands));
}
private void UpdateDefaultContextItemIcon() =>
// Command icon takes precedence over our icon on the primary command
@@ -491,6 +537,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
UpdateProperty(nameof(SecondaryCommand));
UpdateProperty(nameof(SecondaryCommandName));
UpdateProperty(nameof(HasMoreCommands));
UpdateProperty(nameof(CanOpenContextMenu));
}
catch (Exception ex)
{

View File

@@ -20,6 +20,8 @@ public partial class CommandPaletteContentPageViewModel : ContentPageViewModel
IFormContent form => new ContentFormViewModel(form, context),
IMarkdownContent markdown => new ContentMarkdownViewModel(markdown, context),
ITreeContent tree => new ContentTreeViewModel(tree, context),
IPlainTextContent plainText => new ContentPlainTextViewModel(plainText, context),
IImageContent image => new ContentImageViewModel(image, context),
_ => null,
};
return viewModel;

View File

@@ -127,7 +127,12 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
private ProviderSettings GetProviderSettings(SettingsModel settings)
{
return settings.GetProviderSettings(this);
if (!settings.ProviderSettings.TryGetValue(ProviderId, out var ps))
{
ps = new ProviderSettings();
}
return ps.WithConnection(this);
}
public async Task LoadTopLevelCommands(IServiceProvider serviceProvider)
@@ -140,9 +145,26 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
}
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
var settings = settingsService.Settings;
var providerSettings = GetProviderSettings(settingsService.Settings);
// Persist the connected provider settings (fallback commands, etc.)
settingsService.UpdateSettings(
s =>
{
if (!s.ProviderSettings.TryGetValue(ProviderId, out var ps))
{
ps = new ProviderSettings();
}
var newPs = ps.WithConnection(this);
return s with
{
ProviderSettings = s.ProviderSettings.SetItem(ProviderId, newPs),
};
},
hotReload: false);
var providerSettings = GetProviderSettings(settings);
IsActive = providerSettings.IsEnabled;
if (!IsActive)
{
@@ -301,8 +323,19 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
var dockSettings = settings.DockSettings;
var allPinnedCommands = dockSettings.AllPinnedCommands;
var pinnedBandsForThisProvider = allPinnedCommands.Where(c => c.ProviderId == ProviderId);
// Track which command IDs we've already added to avoid duplicates
// from settings that were pinned multiple times.
HashSet<string> seenCommandIds = new(bands.Select(b => b.Id));
foreach (var (providerId, commandId) in pinnedBandsForThisProvider)
{
if (!seenCommandIds.Add(commandId))
{
Logger.LogWarning($"Skipping duplicate pinned dock band command {commandId} for provider {providerId}");
continue;
}
Logger.LogDebug($"Looking for pinned dock band command {commandId} for provider {providerId}");
// First, try to lookup the command as one of this provider's
@@ -408,62 +441,125 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
public void PinCommand(string commandId, IServiceProvider serviceProvider)
{
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
var settings = settingsService.Settings;
var providerSettings = GetProviderSettings(settings);
var providerSettings = GetProviderSettings(settingsService.Settings);
if (!providerSettings.PinnedCommandIds.Contains(commandId))
{
providerSettings.PinnedCommandIds.Add(commandId);
settingsService.UpdateSettings(
s =>
{
if (!s.ProviderSettings.TryGetValue(ProviderId, out var ps))
{
ps = new ProviderSettings();
}
var providerSettings = ps.WithConnection(this);
var newPinned = providerSettings.PinnedCommandIds.Add(commandId);
var newPs = providerSettings with { PinnedCommandIds = newPinned };
return s with
{
ProviderSettings = s.ProviderSettings.SetItem(ProviderId, newPs),
};
},
hotReload: false);
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
settingsService.Save(hotReload: false);
}
}
public void UnpinCommand(string commandId, IServiceProvider serviceProvider)
{
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
var settings = settingsService.Settings;
var providerSettings = GetProviderSettings(settings);
if (providerSettings.PinnedCommandIds.Remove(commandId))
{
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
settingsService.UpdateSettings(
s =>
{
if (!s.ProviderSettings.TryGetValue(ProviderId, out var ps))
{
ps = new ProviderSettings();
}
settingsService.Save(hotReload: false);
}
var providerSettings = ps.WithConnection(this);
var newPinned = providerSettings.PinnedCommandIds.Remove(commandId);
var newPs = providerSettings with { PinnedCommandIds = newPinned };
return s with
{
ProviderSettings = s.ProviderSettings.SetItem(ProviderId, newPs),
};
},
hotReload: false);
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
}
public void PinDockBand(string commandId, IServiceProvider serviceProvider)
public void PinDockBand(string commandId, IServiceProvider serviceProvider, Dock.DockPinSide side = Dock.DockPinSide.Start, bool? showTitles = null, bool? showSubtitles = null)
{
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
var settings = settingsService.Settings;
var dockSettings = settings.DockSettings;
// Prevent duplicate pins — check all sections
if (dockSettings.StartBands.Any(b => b.CommandId == commandId && b.ProviderId == this.ProviderId) ||
dockSettings.CenterBands.Any(b => b.CommandId == commandId && b.ProviderId == this.ProviderId) ||
dockSettings.EndBands.Any(b => b.CommandId == commandId && b.ProviderId == this.ProviderId))
{
Logger.LogDebug($"Dock band '{commandId}' from provider '{this.ProviderId}' is already pinned; skipping.");
return;
}
var bandSettings = new DockBandSettings
{
CommandId = commandId,
ProviderId = this.ProviderId,
ShowTitles = showTitles,
ShowSubtitles = showSubtitles,
};
settings.DockSettings.StartBands.Add(bandSettings);
settingsService.UpdateSettings(
s =>
{
var dockSettings = s.DockSettings;
return s with
{
DockSettings = side switch
{
Dock.DockPinSide.Center => dockSettings with { CenterBands = dockSettings.CenterBands.Add(bandSettings) },
Dock.DockPinSide.End => dockSettings with { EndBands = dockSettings.EndBands.Add(bandSettings) },
_ => dockSettings with { StartBands = dockSettings.StartBands.Add(bandSettings) },
},
};
},
hotReload: false);
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
settingsService.Save(hotReload: false);
}
public void UnpinDockBand(string commandId, IServiceProvider serviceProvider)
{
var settingsService = serviceProvider.GetRequiredService<ISettingsService>();
var settings = settingsService.Settings;
settings.DockSettings.StartBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
settings.DockSettings.CenterBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
settings.DockSettings.EndBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId);
settingsService.UpdateSettings(
s =>
{
var dockSettings = s.DockSettings;
return s with
{
DockSettings = dockSettings with
{
StartBands = dockSettings.StartBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId),
CenterBands = dockSettings.CenterBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId),
EndBands = dockSettings.EndBands.RemoveAll(b => b.CommandId == commandId && b.ProviderId == ProviderId),
},
};
},
hotReload: false);
// Raise CommandsChanged so the TopLevelCommandManager reloads our commands
this.CommandsChanged?.Invoke(this, new ItemsChangedEventArgs(-1));
settingsService.Save(hotReload: false);
}
public ICommandProviderContext GetProviderContext() => this;

View File

@@ -365,9 +365,9 @@ public sealed partial class MainListPage : DynamicListPage,
}
// prefilter fallbacks
var globalFallbacks = _settingsService.Settings.GetGlobalFallbacks();
var specialFallbacks = new List<TopLevelViewModel>(globalFallbacks.Length);
var commonFallbacks = new List<TopLevelViewModel>(commands.Count - globalFallbacks.Length);
var configuredGlobalFallbackIds = _settingsService.Settings.GetGlobalFallbacks();
var specialFallbacks = new List<TopLevelViewModel>(configuredGlobalFallbackIds.Length);
var commonFallbacks = new List<TopLevelViewModel>(Math.Max(commands.Count - configuredGlobalFallbackIds.Length, 0));
foreach (var s in commands)
{
@@ -376,7 +376,7 @@ public sealed partial class MainListPage : DynamicListPage,
continue;
}
if (globalFallbacks.Contains(s.Id))
if (configuredGlobalFallbackIds.Contains(s.Id))
{
specialFallbacks.Add(s);
}
@@ -509,7 +509,7 @@ public sealed partial class MainListPage : DynamicListPage,
return;
}
IEnumerable<IListItem> newFallbacksForScoring = commands.Where(s => s.IsFallback && globalFallbacks.Contains(s.Id));
IEnumerable<IListItem> newFallbacksForScoring = commands.Where(s => s.IsFallback && configuredGlobalFallbackIds.Contains(s.Id));
_scoredFallbackItems = InternalListHelpers.FilterListWithScores(newFallbacksForScoring, searchQuery, _scoringFunction);
if (token.IsCancellationRequested)
@@ -679,9 +679,10 @@ public sealed partial class MainListPage : DynamicListPage,
public void UpdateHistory(IListItem topLevelOrAppItem)
{
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
var history = _appStateService.State.RecentCommands;
history.AddHistoryItem(id);
_appStateService.Save();
_appStateService.UpdateState(state => state with
{
RecentCommands = state.RecentCommands.WithHistoryItem(id),
});
}
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)

View File

@@ -2,9 +2,9 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO.Compression;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -13,6 +13,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
internal sealed partial class NewExtensionForm : NewExtensionFormBase
{
private static readonly string _creatingText = "Creating new extension...";
private readonly IExtensionTemplateService _extensionTemplateService;
private readonly StatusMessage _creatingMessage = new()
{
Message = _creatingText,
@@ -20,7 +21,15 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
};
public NewExtensionForm()
: this(new ExtensionTemplateService())
{
}
private NewExtensionForm(IExtensionTemplateService extensionTemplateService)
{
ArgumentNullException.ThrowIfNull(extensionTemplateService);
_extensionTemplateService = extensionTemplateService;
TemplateJson = $$"""
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
@@ -115,7 +124,7 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
try
{
CreateExtension(extensionName, displayName, outputPath);
_extensionTemplateService.CreateExtension(extensionName, displayName, outputPath);
BuiltinsExtensionHost.Instance.HideStatus(_creatingMessage);
@@ -131,57 +140,6 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
return CommandResult.KeepOpen();
}
private void CreateExtension(string extensionName, string newDisplayName, string outputPath)
{
var newGuid = Guid.NewGuid().ToString();
// Unzip `template.zip` to a temp dir:
var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
// Does the output path exist?
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
var assetsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), "Microsoft.CmdPal.UI.ViewModels\\Assets\\template.zip");
ZipFile.ExtractToDirectory(assetsPath, tempDir);
var files = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
foreach (var file in files)
{
var text = File.ReadAllText(file);
// Replace all the instances of `FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF` with a new random guid:
text = text.Replace("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF", newGuid);
// Then replace all the `TemplateCmdPalExtension` with `extensionName`
text = text.Replace("TemplateCmdPalExtension", extensionName);
// Then replace all the `TemplateDisplayName` with `newDisplayName`
text = text.Replace("TemplateDisplayName", newDisplayName);
// We're going to write the file to the same relative location in the output path
var relativePath = Path.GetRelativePath(tempDir, file);
var newFileName = Path.Combine(outputPath, relativePath);
// if the file name had `TemplateCmdPalExtension` in it, replace it with `extensionName`
newFileName = newFileName.Replace("TemplateCmdPalExtension", extensionName);
// Make sure the directory exists
Directory.CreateDirectory(Path.GetDirectoryName(newFileName)!);
File.WriteAllText(newFileName, text);
// Delete the old file
File.Delete(file);
}
// Delete the temp dir
Directory.Delete(tempDir, true);
}
private string FormatJsonString(string str) =>
// Escape the string for JSON

View File

@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ContentImageViewModel : ContentViewModel
{
public ExtensionObject<IImageContent> Model { get; }
public IconInfoViewModel Image { get; protected set; } = new(null);
public double MaxWidth { get; protected set; } = double.PositiveInfinity;
public double MaxHeight { get; protected set; } = double.PositiveInfinity;
public ContentImageViewModel(IImageContent content, WeakReference<IPageContext> context)
: base(context)
{
Model = new ExtensionObject<IImageContent>(content);
}
public override void InitializeProperties()
{
var model = Model.Unsafe;
if (model is null)
{
return;
}
Image = new IconInfoViewModel(model.Image);
Image.InitializeProperties();
MaxWidth = model.MaxWidth <= 0 ? double.PositiveInfinity : model.MaxWidth;
MaxHeight = model.MaxHeight <= 0 ? double.PositiveInfinity : model.MaxHeight;
UpdateProperty(nameof(Image), nameof(MaxWidth), nameof(MaxHeight));
model.PropChanged += Model_PropChanged;
}
private void Model_PropChanged(object sender, IPropChangedEventArgs args)
{
try
{
var propName = args.PropertyName;
FetchProperty(propName);
}
catch (Exception ex)
{
ShowException(ex);
}
}
private void FetchProperty(string propertyName)
{
var model = Model.Unsafe;
if (model is null)
{
return; // throw?
}
switch (propertyName)
{
case nameof(Image):
Image = new IconInfoViewModel(model.Image);
Image.InitializeProperties();
UpdateProperty(propertyName);
break;
case nameof(IImageContent.MaxWidth):
MaxWidth = model.MaxWidth <= 0 ? double.PositiveInfinity : model.MaxWidth;
UpdateProperty(propertyName);
break;
case nameof(IImageContent.MaxHeight):
MaxHeight = model.MaxHeight <= 0 ? double.PositiveInfinity : model.MaxHeight;
UpdateProperty(propertyName);
break;
}
}
protected override void UnsafeCleanup()
{
base.UnsafeCleanup();
var model = Model.Unsafe;
if (model is not null)
{
model.PropChanged -= Model_PropChanged;
}
}
}

View File

@@ -37,6 +37,8 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext
public bool HasMoreCommands => _snapshot.SecondaryCommand is not null;
public bool CanOpenContextMenu => _snapshot.AllCommands.Any(item => item is CommandItemViewModel command && command.ShouldBeVisible);
public string SecondaryCommandName => _snapshot.SecondaryCommand?.Name ?? string.Empty;
public CommandItemViewModel? PrimaryCommand => _snapshot.PrimaryCommand;
@@ -184,6 +186,7 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext
UpdateProperty(nameof(SecondaryCommandName));
UpdateProperty(nameof(HasCommands));
UpdateProperty(nameof(HasMoreCommands));
UpdateProperty(nameof(CanOpenContextMenu));
UpdateProperty(nameof(MoreCommands));
UpdateProperty(nameof(AllCommands));
DoOnUiThread(

View File

@@ -0,0 +1,114 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ContentPlainTextViewModel : ContentViewModel
{
private ExtensionObject<IPlainTextContent> Model { get; }
public string? Text { get; protected set; }
public bool WordWrapEnabled { get; protected set; }
public bool UseMonospace { get; protected set; }
public ContentPlainTextViewModel(IPlainTextContent content, WeakReference<IPageContext> context)
: base(context)
{
Model = new ExtensionObject<IPlainTextContent>(content);
}
public override void InitializeProperties()
{
var model = Model.Unsafe;
if (model is null)
{
return;
}
Text = model.Text;
WordWrapEnabled = model.WrapWords;
UseMonospace = model.FontFamily == CommandPalette.Extensions.FontFamily.Monospace;
UpdateProperty(nameof(Text), nameof(WordWrapEnabled), nameof(UseMonospace));
model.PropChanged += Model_PropChanged;
}
private void Model_PropChanged(object sender, IPropChangedEventArgs args)
{
try
{
var propName = args.PropertyName;
FetchProperty(propName);
}
catch (Exception ex)
{
ShowException(ex);
}
}
private void FetchProperty(string propertyName)
{
var model = Model.Unsafe;
if (model is null)
{
return; // throw?
}
switch (propertyName)
{
case nameof(IPlainTextContent.FontFamily):
// RPC:
var incomingUseMonospace = model.FontFamily == CommandPalette.Extensions.FontFamily.Monospace;
// local:
if (incomingUseMonospace != UseMonospace)
{
UseMonospace = incomingUseMonospace;
UpdateProperty(nameof(UseMonospace));
}
break;
case nameof(IPlainTextContent.WrapWords):
// RPC:
var incomingWrap = model.WrapWords;
// local:
if (WordWrapEnabled != incomingWrap)
{
WordWrapEnabled = model.WrapWords;
UpdateProperty(nameof(WordWrapEnabled));
}
break;
case nameof(IPlainTextContent.Text):
// RPC:
var incomingText = model.Text;
// local:
if (incomingText != Text)
{
Text = incomingText;
UpdateProperty(nameof(Text));
}
break;
}
}
protected override void UnsafeCleanup()
{
base.UnsafeCleanup();
var model = Model.Unsafe;
if (model is not null)
{
model.PropChanged -= Model_PropChanged;
}
}
}

View File

@@ -58,6 +58,8 @@ public partial class ContentTreeViewModel(ITreeContent _tree, WeakReference<IPag
IFormContent form => new ContentFormViewModel(form, context),
IMarkdownContent markdown => new ContentMarkdownViewModel(markdown, context),
ITreeContent tree => new ContentTreeViewModel(tree, context),
IPlainTextContent plainText => new ContentPlainTextViewModel(plainText, context),
IImageContent image => new ContentImageViewModel(image, context),
_ => null,
};
return viewModel;

View File

@@ -47,7 +47,20 @@ public partial class ContextMenuViewModel : ObservableObject,
public ContextMenuViewModel(IFuzzyMatcherProvider fuzzyMatcherProvider)
{
_fuzzyMatcherProvider = fuzzyMatcherProvider;
WeakReferenceMessenger.Default.Register<UpdateCommandBarMessage>(this);
}
public void HookCommandBar()
{
var messenger = WeakReferenceMessenger.Default;
if (!messenger.IsRegistered<UpdateCommandBarMessage>(this))
{
messenger.Register<UpdateCommandBarMessage>(this);
}
}
public void UnhookCommandBar()
{
WeakReferenceMessenger.Default.Unregister<UpdateCommandBarMessage>(this);
}
public void Receive(UpdateCommandBarMessage message)

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Globalization;
using System.Text;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -14,10 +15,11 @@ public partial class DockBandSettingsViewModel : ObservableObject
{
private static readonly CompositeFormat PluralItemsFormatString = CompositeFormat.Parse(Properties.Resources.dock_item_count_plural);
private readonly ISettingsService _settingsService;
private readonly DockBandSettings _dockSettingsModel;
private readonly TopLevelViewModel _adapter;
private readonly DockBandViewModel? _bandViewModel;
private DockBandSettings _dockSettingsModel;
public string Title => _adapter.Title;
public string Description
@@ -54,14 +56,14 @@ public partial class DockBandSettingsViewModel : ObservableObject
if (value != _showLabels)
{
_showLabels = value;
_dockSettingsModel.ShowLabels = value switch
var newShowTitles = value switch
{
ShowLabelsOption.Default => null,
ShowLabelsOption.Default => (bool?)null,
ShowLabelsOption.ShowLabels => true,
ShowLabelsOption.HideLabels => false,
_ => null,
};
Save();
UpdateModel(_dockSettingsModel with { ShowTitles = newShowTitles });
}
}
}
@@ -174,9 +176,38 @@ public partial class DockBandSettingsViewModel : ObservableObject
return bandVm.Items.Count;
}
private void Save()
private void UpdateModel(DockBandSettings newModel)
{
_settingsService.Save();
var commandId = _dockSettingsModel.CommandId;
_settingsService.UpdateSettings(
s =>
{
var dockSettings = s.DockSettings;
return s with
{
DockSettings = dockSettings with
{
StartBands = ReplaceInList(dockSettings.StartBands, commandId, newModel),
CenterBands = ReplaceInList(dockSettings.CenterBands, commandId, newModel),
EndBands = ReplaceInList(dockSettings.EndBands, commandId, newModel),
},
};
},
hotReload: false);
_dockSettingsModel = newModel;
}
private static ImmutableList<DockBandSettings> ReplaceInList(ImmutableList<DockBandSettings> list, string commandId, DockBandSettings newModel)
{
for (var i = 0; i < list.Count; i++)
{
if (list[i].CommandId == commandId)
{
return list.SetItem(i, newModel);
}
}
return list;
}
private void UpdatePinSide(DockPinSide value)
@@ -189,44 +220,31 @@ public partial class DockBandSettingsViewModel : ObservableObject
public void SetBandPosition(DockPinSide side, int? index)
{
var dockSettings = _settingsService.Settings.DockSettings;
var commandId = _dockSettingsModel.CommandId;
// Remove from all sides first
dockSettings.StartBands.RemoveAll(b => b.CommandId == _dockSettingsModel.CommandId);
dockSettings.CenterBands.RemoveAll(b => b.CommandId == _dockSettingsModel.CommandId);
dockSettings.EndBands.RemoveAll(b => b.CommandId == _dockSettingsModel.CommandId);
// Add to the selected side
switch (side)
_settingsService.UpdateSettings(s =>
{
case DockPinSide.Start:
{
var insertIndex = index ?? dockSettings.StartBands.Count;
dockSettings.StartBands.Insert(insertIndex, _dockSettingsModel);
break;
}
var dockSettings = s.DockSettings;
case DockPinSide.Center:
{
var insertIndex = index ?? dockSettings.CenterBands.Count;
dockSettings.CenterBands.Insert(insertIndex, _dockSettingsModel);
break;
}
// Remove from all sides first
var newDock = dockSettings with
{
StartBands = dockSettings.StartBands.RemoveAll(b => b.CommandId == commandId),
CenterBands = dockSettings.CenterBands.RemoveAll(b => b.CommandId == commandId),
EndBands = dockSettings.EndBands.RemoveAll(b => b.CommandId == commandId),
};
case DockPinSide.End:
{
var insertIndex = index ?? dockSettings.EndBands.Count;
dockSettings.EndBands.Insert(insertIndex, _dockSettingsModel);
break;
}
// Add to the selected side
newDock = side switch
{
DockPinSide.Start => newDock with { StartBands = newDock.StartBands.Insert(index ?? newDock.StartBands.Count, _dockSettingsModel) },
DockPinSide.Center => newDock with { CenterBands = newDock.CenterBands.Insert(index ?? newDock.CenterBands.Count, _dockSettingsModel) },
DockPinSide.End => newDock with { EndBands = newDock.EndBands.Insert(index ?? newDock.EndBands.Count, _dockSettingsModel) },
_ => newDock,
};
case DockPinSide.None:
default:
// Do nothing
break;
}
Save();
return s with { DockSettings = newDock };
});
}
private void OnPinSideChanged(DockPinSide value)

View File

@@ -2,8 +2,10 @@
// 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.Immutable;
using System.Collections.ObjectModel;
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.CmdPal.UI.ViewModels.Settings;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -15,11 +17,11 @@ namespace Microsoft.CmdPal.UI.ViewModels.Dock;
public sealed partial class DockBandViewModel : ExtensionObjectViewModel
{
private readonly CommandItemViewModel _rootItem;
private readonly DockBandSettings _bandSettings;
private readonly DockSettings _dockSettings;
private readonly Action _saveSettings;
private readonly ISettingsService _settingsService;
private readonly IContextMenuFactory _contextMenuFactory;
private DockBandSettings _bandSettings;
public ObservableCollection<DockItemViewModel> Items { get; } = new();
private bool _showTitles = true;
@@ -103,8 +105,7 @@ public sealed partial class DockBandViewModel : ExtensionObjectViewModel
/// </summary>
internal void SaveShowLabels()
{
_bandSettings.ShowTitles = _showTitles;
_bandSettings.ShowSubtitles = _showSubtitles;
ReplaceBandInSettings(_bandSettings with { ShowTitles = _showTitles, ShowSubtitles = _showSubtitles });
_showTitlesSnapshot = null;
_showSubtitlesSnapshot = null;
}
@@ -127,21 +128,54 @@ public sealed partial class DockBandViewModel : ExtensionObjectViewModel
}
}
private void ReplaceBandInSettings(DockBandSettings newSettings)
{
var commandId = _bandSettings.CommandId;
_settingsService.UpdateSettings(
s =>
{
var dockSettings = s.DockSettings;
return s with
{
DockSettings = dockSettings with
{
StartBands = ReplaceBandInList(dockSettings.StartBands, commandId, newSettings),
CenterBands = ReplaceBandInList(dockSettings.CenterBands, commandId, newSettings),
EndBands = ReplaceBandInList(dockSettings.EndBands, commandId, newSettings),
},
};
},
false);
_bandSettings = newSettings;
}
private static ImmutableList<DockBandSettings> ReplaceBandInList(ImmutableList<DockBandSettings> list, string commandId, DockBandSettings newSettings)
{
for (var i = 0; i < list.Count; i++)
{
if (list[i].CommandId == commandId)
{
return list.SetItem(i, newSettings);
}
}
return list;
}
internal DockBandViewModel(
CommandItemViewModel commandItemViewModel,
WeakReference<IPageContext> errorContext,
DockBandSettings settings,
DockSettings dockSettings,
Action saveSettings,
ISettingsService settingsService,
IContextMenuFactory contextMenuFactory)
: base(errorContext)
{
_rootItem = commandItemViewModel;
_bandSettings = settings;
_dockSettings = dockSettings;
_saveSettings = saveSettings;
_settingsService = settingsService;
_contextMenuFactory = contextMenuFactory;
var dockSettings = settingsService.Settings.DockSettings;
_showTitles = settings.ResolveShowTitles(dockSettings.ShowLabels);
_showSubtitles = settings.ResolveShowSubtitles(dockSettings.ShowLabels);
}

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
@@ -21,6 +22,7 @@ public sealed partial class DockViewModel
private readonly IContextMenuFactory _contextMenuFactory;
private DockSettings _settings;
private bool _isEditing;
public TaskScheduler Scheduler { get; }
@@ -52,6 +54,12 @@ public sealed partial class DockViewModel
private void DockBands_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (_isEditing)
{
Logger.LogDebug("Skipping DockBands_CollectionChanged during edit mode");
return;
}
Logger.LogDebug("Starting DockBands_CollectionChanged");
SetupBands();
Logger.LogDebug("Ended DockBands_CollectionChanged");
@@ -73,7 +81,7 @@ public sealed partial class DockViewModel
}
private void SetupBands(
List<DockBandSettings> bands,
ImmutableList<DockBandSettings> bands,
ObservableCollection<DockBandViewModel> target)
{
List<DockBandViewModel> newBands = new();
@@ -141,7 +149,7 @@ public sealed partial class DockViewModel
DockBandSettings bandSettings,
CommandItemViewModel commandItem)
{
DockBandViewModel band = new(commandItem, commandItem.PageContext, bandSettings, _settings, SaveSettings, _contextMenuFactory);
DockBandViewModel band = new(commandItem, commandItem.PageContext, bandSettings, _settingsService, _contextMenuFactory);
// the band is NOT initialized here!
return band;
@@ -149,7 +157,7 @@ public sealed partial class DockViewModel
private void SaveSettings()
{
_settingsService.Save();
_settingsService.UpdateSettings(s => s with { DockSettings = _settings });
}
public DockBandViewModel? FindBandByTopLevel(TopLevelViewModel tlc)
@@ -194,7 +202,7 @@ public sealed partial class DockViewModel
public void SyncBandPosition(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
{
var bandId = band.Id;
var dockSettings = _settingsService.Settings.DockSettings;
var dockSettings = _settings;
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
@@ -206,20 +214,30 @@ public sealed partial class DockViewModel
}
// Remove from all settings lists
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId);
dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId);
var newDock = dockSettings with
{
StartBands = dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId),
CenterBands = dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId),
EndBands = dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId),
};
// Add to target settings list at the correct index
var targetSettings = targetSide switch
var targetList = targetSide switch
{
DockPinSide.Start => dockSettings.StartBands,
DockPinSide.Center => dockSettings.CenterBands,
DockPinSide.End => dockSettings.EndBands,
_ => dockSettings.StartBands,
DockPinSide.Start => newDock.StartBands,
DockPinSide.Center => newDock.CenterBands,
DockPinSide.End => newDock.EndBands,
_ => newDock.StartBands,
};
var insertIndex = Math.Min(targetIndex, targetSettings.Count);
targetSettings.Insert(insertIndex, bandSettings);
var insertIndex = Math.Min(targetIndex, targetList.Count);
newDock = targetSide switch
{
DockPinSide.Start => newDock with { StartBands = targetList.Insert(insertIndex, bandSettings) },
DockPinSide.Center => newDock with { CenterBands = targetList.Insert(insertIndex, bandSettings) },
DockPinSide.End => newDock with { EndBands = targetList.Insert(insertIndex, bandSettings) },
_ => newDock with { StartBands = targetList.Insert(insertIndex, bandSettings) },
};
_settings = newDock;
}
/// <summary>
@@ -229,7 +247,7 @@ public sealed partial class DockViewModel
public void MoveBandWithoutSaving(DockBandViewModel band, DockPinSide targetSide, int targetIndex)
{
var bandId = band.Id;
var dockSettings = _settingsService.Settings.DockSettings;
var dockSettings = _settings;
var bandSettings = dockSettings.StartBands.FirstOrDefault(b => b.CommandId == bandId)
?? dockSettings.CenterBands.FirstOrDefault(b => b.CommandId == bandId)
@@ -241,10 +259,15 @@ public sealed partial class DockViewModel
return;
}
// Remove from all sides (settings and UI)
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId);
dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId);
// Remove from all sides (settings)
var newDock = dockSettings with
{
StartBands = dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId),
CenterBands = dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId),
EndBands = dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId),
};
// Remove from UI collections
StartItems.Remove(band);
CenterItems.Remove(band);
EndItems.Remove(band);
@@ -254,8 +277,8 @@ public sealed partial class DockViewModel
{
case DockPinSide.Start:
{
var settingsIndex = Math.Min(targetIndex, dockSettings.StartBands.Count);
dockSettings.StartBands.Insert(settingsIndex, bandSettings);
var settingsIndex = Math.Min(targetIndex, newDock.StartBands.Count);
newDock = newDock with { StartBands = newDock.StartBands.Insert(settingsIndex, bandSettings) };
var uiIndex = Math.Min(targetIndex, StartItems.Count);
StartItems.Insert(uiIndex, band);
@@ -264,8 +287,8 @@ public sealed partial class DockViewModel
case DockPinSide.Center:
{
var settingsIndex = Math.Min(targetIndex, dockSettings.CenterBands.Count);
dockSettings.CenterBands.Insert(settingsIndex, bandSettings);
var settingsIndex = Math.Min(targetIndex, newDock.CenterBands.Count);
newDock = newDock with { CenterBands = newDock.CenterBands.Insert(settingsIndex, bandSettings) };
var uiIndex = Math.Min(targetIndex, CenterItems.Count);
CenterItems.Insert(uiIndex, band);
@@ -274,8 +297,8 @@ public sealed partial class DockViewModel
case DockPinSide.End:
{
var settingsIndex = Math.Min(targetIndex, dockSettings.EndBands.Count);
dockSettings.EndBands.Insert(settingsIndex, bandSettings);
var settingsIndex = Math.Min(targetIndex, newDock.EndBands.Count);
newDock = newDock with { EndBands = newDock.EndBands.Insert(settingsIndex, bandSettings) };
var uiIndex = Math.Min(targetIndex, EndItems.Count);
EndItems.Insert(uiIndex, band);
@@ -283,6 +306,8 @@ public sealed partial class DockViewModel
}
}
_settings = newDock;
Logger.LogDebug($"Moved band {bandId} to {targetSide} at index {targetIndex} (not saved yet)");
}
@@ -298,17 +323,57 @@ public sealed partial class DockViewModel
band.SaveShowLabels();
}
_snapshotStartBands = null;
_snapshotCenterBands = null;
_snapshotEndBands = null;
// Preserve any per-band label edits made while in edit mode. Those edits are
// saved independently of reorder, so merge the latest band settings back into
// the local reordered snapshot before we persist dock settings.
var latestBandSettings = BuildBandSettingsLookup(_settingsService.Settings.DockSettings);
_settings = _settings with
{
StartBands = MergeBandSettings(_settings.StartBands, latestBandSettings),
CenterBands = MergeBandSettings(_settings.CenterBands, latestBandSettings),
EndBands = MergeBandSettings(_settings.EndBands, latestBandSettings),
};
_snapshotDockSettings = null;
_snapshotBandViewModels = null;
_settingsService.Save();
// Save without hotReload to avoid triggering SettingsChanged → SetupBands,
// which could race with stale DockBands_CollectionChanged work items and
// re-add bands that were just unpinned.
_settingsService.UpdateSettings(s => s with { DockSettings = _settings }, false);
_isEditing = false;
Logger.LogDebug("Saved band order to settings");
}
private List<DockBandSettings>? _snapshotStartBands;
private List<DockBandSettings>? _snapshotCenterBands;
private List<DockBandSettings>? _snapshotEndBands;
private static Dictionary<string, DockBandSettings> BuildBandSettingsLookup(DockSettings dockSettings)
{
var lookup = new Dictionary<string, DockBandSettings>(StringComparer.Ordinal);
foreach (var band in dockSettings.StartBands.Concat(dockSettings.CenterBands).Concat(dockSettings.EndBands))
{
lookup[band.CommandId] = band;
}
return lookup;
}
private static ImmutableList<DockBandSettings> MergeBandSettings(
ImmutableList<DockBandSettings> targetBands,
IReadOnlyDictionary<string, DockBandSettings> latestBandSettings)
{
var merged = targetBands;
for (var i = 0; i < merged.Count; i++)
{
var commandId = merged[i].CommandId;
if (latestBandSettings.TryGetValue(commandId, out var latestSettings))
{
merged = merged.SetItem(i, latestSettings);
}
}
return merged;
}
private DockSettings? _snapshotDockSettings;
private Dictionary<string, DockBandViewModel>? _snapshotBandViewModels;
/// <summary>
@@ -317,13 +382,17 @@ public sealed partial class DockViewModel
/// </summary>
public void SnapshotBandOrder()
{
_isEditing = true;
var dockSettings = _settingsService.Settings.DockSettings;
_snapshotStartBands = dockSettings.StartBands.Select(b => b.Clone()).ToList();
_snapshotCenterBands = dockSettings.CenterBands.Select(b => b.Clone()).ToList();
_snapshotEndBands = dockSettings.EndBands.Select(b => b.Clone()).ToList();
var snapshotStartBandsCount = dockSettings.StartBands.Count;
var snapshotCenterBandsCount = dockSettings.CenterBands.Count;
var snapshotEndBandsCount = dockSettings.EndBands.Count;
// Snapshot band ViewModels so we can restore unpinned bands
// Use a dictionary but handle potential duplicates gracefully
_snapshotDockSettings = dockSettings;
_snapshotBandViewModels = new Dictionary<string, DockBandViewModel>();
foreach (var band in StartItems.Concat(CenterItems).Concat(EndItems))
{
@@ -336,7 +405,7 @@ public sealed partial class DockViewModel
band.SnapshotShowLabels();
}
Logger.LogDebug($"Snapshot taken: {_snapshotStartBands.Count} start bands, {_snapshotCenterBands.Count} center bands, {_snapshotEndBands.Count} end bands");
Logger.LogDebug($"Snapshot taken: {snapshotStartBandsCount} start bands, {snapshotCenterBandsCount} center bands, {snapshotEndBandsCount} end bands");
}
/// <summary>
@@ -345,9 +414,7 @@ public sealed partial class DockViewModel
/// </summary>
public void RestoreBandOrder()
{
if (_snapshotStartBands == null ||
_snapshotCenterBands == null ||
_snapshotEndBands == null || _snapshotBandViewModels == null)
if (_snapshotDockSettings == null || _snapshotBandViewModels == null)
{
Logger.LogWarning("No snapshot to restore from");
return;
@@ -359,38 +426,15 @@ public sealed partial class DockViewModel
band.RestoreShowLabels();
}
var dockSettings = _settingsService.Settings.DockSettings;
// Restore settings from snapshot
dockSettings.StartBands.Clear();
dockSettings.CenterBands.Clear();
dockSettings.EndBands.Clear();
foreach (var bandSnapshot in _snapshotStartBands)
{
var bandSettings = bandSnapshot.Clone();
dockSettings.StartBands.Add(bandSettings);
}
foreach (var bandSnapshot in _snapshotCenterBands)
{
var bandSettings = bandSnapshot.Clone();
dockSettings.CenterBands.Add(bandSettings);
}
foreach (var bandSnapshot in _snapshotEndBands)
{
var bandSettings = bandSnapshot.Clone();
dockSettings.EndBands.Add(bandSettings);
}
// Restore settings from snapshot (immutable = just assign back)
_settings = _snapshotDockSettings;
// Rebuild UI collections from restored settings using the snapshotted ViewModels
RebuildUICollectionsFromSnapshot();
_snapshotStartBands = null;
_snapshotCenterBands = null;
_snapshotEndBands = null;
_snapshotDockSettings = null;
_snapshotBandViewModels = null;
_isEditing = false;
Logger.LogDebug("Restored band order from snapshot");
}
@@ -401,7 +445,7 @@ public sealed partial class DockViewModel
return;
}
var dockSettings = _settingsService.Settings.DockSettings;
var dockSettings = _settings;
StartItems.Clear();
CenterItems.Clear();
@@ -434,7 +478,7 @@ public sealed partial class DockViewModel
private void RebuildUICollections()
{
var dockSettings = _settingsService.Settings.DockSettings;
var dockSettings = _settings;
// Create a lookup of all current band ViewModels
var allBands = StartItems.Concat(CenterItems).Concat(EndItems).ToDictionary(b => b.Id);
@@ -511,7 +555,7 @@ public sealed partial class DockViewModel
// Create settings for the new band
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId, ShowLabels = null };
var dockSettings = _settingsService.Settings.DockSettings;
var dockSettings = _settings;
// Create the band view model
var bandVm = CreateBandItem(bandSettings, topLevel.ItemViewModel);
@@ -520,15 +564,15 @@ public sealed partial class DockViewModel
switch (targetSide)
{
case DockPinSide.Start:
dockSettings.StartBands.Add(bandSettings);
_settings = dockSettings with { StartBands = dockSettings.StartBands.Add(bandSettings) };
StartItems.Add(bandVm);
break;
case DockPinSide.Center:
dockSettings.CenterBands.Add(bandSettings);
_settings = dockSettings with { CenterBands = dockSettings.CenterBands.Add(bandSettings) };
CenterItems.Add(bandVm);
break;
case DockPinSide.End:
dockSettings.EndBands.Add(bandSettings);
_settings = dockSettings with { EndBands = dockSettings.EndBands.Add(bandSettings) };
EndItems.Add(bandVm);
break;
}
@@ -551,12 +595,15 @@ public sealed partial class DockViewModel
public void UnpinBand(DockBandViewModel band)
{
var bandId = band.Id;
var dockSettings = _settingsService.Settings.DockSettings;
var dockSettings = _settings;
// Remove from settings
dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId);
dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId);
dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId);
_settings = dockSettings with
{
StartBands = dockSettings.StartBands.RemoveAll(b => b.CommandId == bandId),
CenterBands = dockSettings.CenterBands.RemoveAll(b => b.CommandId == bandId),
EndBands = dockSettings.EndBands.RemoveAll(b => b.CommandId == bandId),
};
// Remove from UI collections
StartItems.Remove(band);
@@ -620,7 +667,7 @@ public sealed partial class DockViewModel
var isDockEnabled = _settingsService.Settings.EnableDock;
var dockSide = isDockEnabled ? _settings.Side.ToString().ToLowerInvariant() : "none";
static string FormatBands(List<DockBandSettings> bands) =>
static string FormatBands(ImmutableList<DockBandSettings> bands) =>
string.Join("\n", bands.Select(b => $"{b.ProviderId}/{b.CommandId}"));
var startBands = isDockEnabled ? FormatBands(_settings.StartBands) : string.Empty;

View File

@@ -47,10 +47,10 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.Theme != value)
{
_settingsService.Settings.DockSettings.Theme = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { Theme = value } });
OnPropertyChanged();
OnPropertyChanged(nameof(ThemeIndex));
Save();
DebouncedReapply();
}
}
}
@@ -68,10 +68,10 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.Backdrop != value)
{
_settingsService.Settings.DockSettings.Backdrop = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { Backdrop = value } });
OnPropertyChanged();
OnPropertyChanged(nameof(BackdropIndex));
Save();
DebouncedReapply();
}
}
}
@@ -83,7 +83,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.ColorizationMode != value)
{
_settingsService.Settings.DockSettings.ColorizationMode = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { ColorizationMode = value } });
OnPropertyChanged();
OnPropertyChanged(nameof(ColorizationModeIndex));
OnPropertyChanged(nameof(IsCustomTintVisible));
@@ -99,7 +99,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
IsColorizationDetailsExpanded = value != ColorizationMode.None;
Save();
DebouncedReapply();
}
}
}
@@ -117,7 +117,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.CustomThemeColor != value)
{
_settingsService.Settings.DockSettings.CustomThemeColor = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { CustomThemeColor = value } });
OnPropertyChanged();
@@ -126,7 +126,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
ColorIntensity = 100;
}
Save();
DebouncedReapply();
}
}
}
@@ -136,9 +136,9 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
get => _settingsService.Settings.DockSettings.CustomThemeColorIntensity;
set
{
_settingsService.Settings.DockSettings.CustomThemeColorIntensity = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { CustomThemeColorIntensity = value } });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
@@ -149,7 +149,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.BackgroundImagePath != value)
{
_settingsService.Settings.DockSettings.BackgroundImagePath = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { BackgroundImagePath = value } });
OnPropertyChanged();
if (BackgroundImageOpacity == 0)
@@ -157,7 +157,7 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
BackgroundImageOpacity = 100;
}
Save();
DebouncedReapply();
}
}
}
@@ -169,9 +169,9 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.BackgroundImageOpacity != value)
{
_settingsService.Settings.DockSettings.BackgroundImageOpacity = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { BackgroundImageOpacity = value } });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
}
@@ -183,9 +183,9 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.BackgroundImageBrightness != value)
{
_settingsService.Settings.DockSettings.BackgroundImageBrightness = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { BackgroundImageBrightness = value } });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
}
@@ -197,9 +197,9 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.BackgroundImageBlurAmount != value)
{
_settingsService.Settings.DockSettings.BackgroundImageBlurAmount = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { BackgroundImageBlurAmount = value } });
OnPropertyChanged();
Save();
DebouncedReapply();
}
}
}
@@ -211,10 +211,10 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
{
if (_settingsService.Settings.DockSettings.BackgroundImageFit != value)
{
_settingsService.Settings.DockSettings.BackgroundImageFit = value;
_settingsService.UpdateSettings(s => s with { DockSettings = s.DockSettings with { BackgroundImageFit = value } });
OnPropertyChanged();
OnPropertyChanged(nameof(BackgroundImageFitIndex));
Save();
DebouncedReapply();
}
}
}
@@ -298,9 +298,8 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
}
private void Save()
private void DebouncedReapply()
{
_settingsService.Save();
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
}

View File

@@ -6,11 +6,11 @@ using System.Text.Json.Serialization;
namespace Microsoft.CmdPal.UI.ViewModels;
public class FallbackSettings
public record FallbackSettings
{
public bool IsEnabled { get; set; } = true;
public bool IsEnabled { get; init; } = true;
public bool IncludeInGlobalResults { get; set; }
public bool IncludeInGlobalResults { get; init; }
public FallbackSettings()
{

View File

@@ -12,7 +12,9 @@ namespace Microsoft.CmdPal.UI.ViewModels;
public partial class FallbackSettingsViewModel : ObservableObject
{
private readonly ISettingsService _settingsService;
private readonly FallbackSettings _fallbackSettings;
private readonly ProviderSettingsViewModel _providerSettingsViewModel;
private FallbackSettings _fallbackSettings;
public string DisplayName { get; private set; } = string.Empty;
@@ -27,15 +29,18 @@ public partial class FallbackSettingsViewModel : ObservableObject
{
if (value != _fallbackSettings.IsEnabled)
{
_fallbackSettings.IsEnabled = value;
var newSettings = _fallbackSettings with { IsEnabled = value };
if (!_fallbackSettings.IsEnabled)
if (!newSettings.IsEnabled)
{
_fallbackSettings.IncludeInGlobalResults = false;
newSettings = newSettings with { IncludeInGlobalResults = false };
}
Save();
_fallbackSettings = newSettings;
_providerSettingsViewModel.UpdateFallbackSettings(Id, _fallbackSettings);
OnPropertyChanged(nameof(IsEnabled));
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
}
}
}
@@ -47,15 +52,18 @@ public partial class FallbackSettingsViewModel : ObservableObject
{
if (value != _fallbackSettings.IncludeInGlobalResults)
{
_fallbackSettings.IncludeInGlobalResults = value;
var newSettings = _fallbackSettings with { IncludeInGlobalResults = value };
if (!_fallbackSettings.IsEnabled)
if (!newSettings.IsEnabled)
{
_fallbackSettings.IsEnabled = true;
newSettings = newSettings with { IsEnabled = true };
}
Save();
_fallbackSettings = newSettings;
_providerSettingsViewModel.UpdateFallbackSettings(Id, _fallbackSettings);
OnPropertyChanged(nameof(IncludeInGlobalResults));
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
}
}
}
@@ -67,6 +75,7 @@ public partial class FallbackSettingsViewModel : ObservableObject
ISettingsService settingsService)
{
_settingsService = settingsService;
_providerSettingsViewModel = providerSettings;
_fallbackSettings = fallbackSettings;
Id = fallback.Id;
@@ -77,10 +86,4 @@ public partial class FallbackSettingsViewModel : ObservableObject
Icon = new(fallback.InitialIcon);
Icon.InitializeProperties();
}
private void Save()
{
_settingsService.Save();
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
}
}

View File

@@ -8,7 +8,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
public record HistoryItem
{
public required string CommandId { get; set; }
public required string CommandId { get; init; }
public required int Uses { get; set; }
public required int Uses { get; init; }
}

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