Compare commits

...

32 Commits

Author SHA1 Message Date
Shawn Yuan (from Dev Box)
219b0046df Merge branch 'main' into shawn/supportWinML 2025-11-17 10:33:03 +08:00
Kai Tao
34c37f2d38 Add not signed dll (#43631)
<!-- 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 dll is not signed, will fail the pipeline

<!-- 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
2025-11-17 09:48:32 +08:00
Niels Laute
47aed03c03 [AP] Loc fix (#43617)
<!-- 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
2025-11-16 22:13:40 +08:00
Niels Laute
6423c7693d [CmdPal] Settings page refresh (#43487)
- Refreshed the CmdPal page.
- CmdPal can now also be activated from this page by clicking a button
- Added a Preview InfoBar for FL in AP

<img width="1384" height="1067" alt="image"
src="https://github.com/user-attachments/assets/f670e13b-5b4a-4f6a-bcb0-e1dc357afb1e"
/>

## Summary of the Pull Request

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

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If 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>
2025-11-16 22:02:38 +08:00
Kai Tao
3e14d50f65 Advanced paste: Add more error handle for foundry local (#43600)
<!-- 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
Foundry local sdk will not run models that is not in catalog, when
catalog removes some, the old ones will fail executing,
so add error hint for users to re-configure the models 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
<img width="864" height="216" alt="image"
src="https://github.com/user-attachments/assets/654207b3-ff50-4888-a638-82136216de7b"
/>
2025-11-16 15:26:05 +08:00
Jaylyn Barbee
db7c9e180e [Light Switch] Removed logs from every tick, only logging key events. (#43572)
Title
2025-11-15 21:08:20 +08:00
Michael Jolley
bcc3ded280 CmdPal: Adding page Id to OpenPage telemetry event (#43584)
@niels9001 requested this.

As the name says
2025-11-15 21:07:52 +08:00
Mario Hewardt
24a3cdd486 Fixes regressions introduced with GIF support (#43589)
<!-- 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)?
-->
## Fixes regressions introduced with GIF support:
- Switches default recording format back to MP4
- Fixes framerate issues between the two formats
- Fixes file path names

<!-- 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
- Switches default recording format back to MP4
- Fixes framerate issues between the two formats
- Fixes file path names
- 
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manual testing
2025-11-15 09:36:31 +08:00
Shawn Yuan
1884e6abc1 Remove unused properties in AP (#43564)
<!-- 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 removes legacy provider configuration migration logic
and associated data structures from the Advanced Paste AI provider
settings. The changes simplify the codebase by eliminating support for
legacy provider configuration snapshots and related migration methods,
focusing configuration management on the current provider model.

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

---------

Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
2025-11-14 16:56:23 +08:00
leileizhang
ad4b553bb1 Remove all AdvancedPaste stored keys during uninstall (#43563)
<!-- 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
Remove all AdvancedPaste stored keys during uninstall
<!-- 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
2025-11-14 16:15:15 +08:00
Dustin L. Howett
193d9aacbe BugReportTool: replace cziplib with tar.exe (#41127)
BugReportTool is the last consumer in the PowerToys repo of cziplib, a
library we use to produce ZIP files.

This pull request replaces cziplib with a simple CreateProcess call that
spawns `tar.exe`, which comes with Windows as of RS4 and can produce ZIP
files!

I've tested this by producing a bug report archive and attempting to
open it with File Explorer. It works fine.

We have taken every precaution to ensure that we do not allow any
attacker-controlled input to tar's command line. We are *not* using
`system()`, and we are not opening up a vector through which a nefarious
caller can perform shell injection.

We do not pass filenames to tar except that of the final archive. We do
not pass directory names to tar; we rely on the current directory
instead.
2025-11-13 15:59:21 -08:00
Niels Laute
483e773299 [CursorWRap] Revert the shortcut removal (#43537)
See title
2025-11-13 22:37:13 +08:00
Niels Laute
dd4c7ba57e [Advanced Paste] Localization (#43536)
More strings to loc, and re-ordering a few settings.
2025-11-13 22:36:35 +08:00
dependabot[bot]
29534601be Build(deps): Bump actions/setup-dotnet from 4 to 5 (#41693)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet)
from 4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/setup-dotnet/releases">actions/setup-dotnet's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>What's Changed</h2>
<h3>Breaking Changes</h3>
<ul>
<li>Upgrade to Node.js 24 and modernize async usage by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/654">actions/setup-dotnet#654</a></li>
</ul>
<p>Make sure your runner is updated to this version or newer to use this
release. v2.327.1 <a
href="https://github.com/actions/runner/releases/tag/v2.327.1">Release
Notes</a></p>
<h3>Dependency Updates</h3>
<ul>
<li>Upgrade <code>@​action/cache</code> from 4.0.2 to 4.0.3 by <a
href="https://github.com/aparnajyothi-y"><code>@​aparnajyothi-y</code></a>
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/622">actions/setup-dotnet#622</a></li>
<li>Upgrade husky from 8.0.3 to 9.1.7 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/591">actions/setup-dotnet#591</a></li>
<li>Upgrade <code>@​actions/glob</code> from 0.4.0 to 0.5.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/594">actions/setup-dotnet#594</a></li>
<li>Upgrade eslint-config-prettier from 9.1.0 to 10.1.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/639">actions/setup-dotnet#639</a></li>
<li>Upgrade undici from 5.28.5 to 5.29.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/641">actions/setup-dotnet#641</a></li>
<li>Upgrade form-data to bring in fix for critical vulnerability by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-dotnet/pull/652">actions/setup-dotnet#652</a></li>
<li>Upgrade actions/checkout from 4 to 5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/662">actions/setup-dotnet#662</a></li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>Remove Support for older .NET Versions and Update installers scripts
by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-dotnet/pull/647">actions/setup-dotnet#647</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/647">actions/setup-dotnet#647</a></li>
<li><a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/654">actions/setup-dotnet#654</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-dotnet/compare/v4...v5.0.0">https://github.com/actions/setup-dotnet/compare/v4...v5.0.0</a></p>
<h2>v4.3.1</h2>
<h2>What's Changed</h2>
<ul>
<li><code>v4</code> - Remove <code>azureedge.net</code> fallback logic
and update install scripts by <a
href="https://github.com/zaataylor"><code>@​zaataylor</code></a> in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/572">actions/setup-dotnet#572</a>
As outlined in<a
href="https://devblogs.microsoft.com/dotnet/critical-dotnet-install-links-are-changing/#call-to-action">
Critical .NET Install Links Are Changing</a>, remove the storage account
fallback logic added for v4 in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/566">actions/setup-dotnet#566</a>
and update the install scripts accordingly.
<strong>Related issue</strong>: <a
href="https://redirect.github.com/dotnet/install-scripts/issues/559">dotnet/install-scripts#559</a></li>
<li>upgrade <code>@​actions/cache</code> to 4.0.2 by <a
href="https://github.com/HarithaVattikuti"><code>@​HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/615">actions/setup-dotnet#615</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-dotnet/compare/v4...v4.3.1">https://github.com/actions/setup-dotnet/compare/v4...v4.3.1</a></p>
<h2>v4.3.0</h2>
<h2>What's Changed</h2>
<ul>
<li>README update - add permissions section by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/587">actions/setup-dotnet#587</a></li>
<li>Configure Dependabot settings by <a
href="https://github.com/HarithaVattikuti"><code>@​HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/585">actions/setup-dotnet#585</a></li>
<li>Upgrade <strong>cache</strong> from 3.2.4 to 4.0.0 by <a
href="https://github.com/aparnajyothi-y"><code>@​aparnajyothi-y</code></a>
in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/586">actions/setup-dotnet#586</a></li>
<li>Upgrade <strong>actions/publish-immutable-action</strong> from 0.0.3
to 0.0.4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/590">actions/setup-dotnet#590</a></li>
<li>Upgrade <strong><code>@​actions/http-client</code></strong> from
2.2.1 to 2.2.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/592">actions/setup-dotnet#592</a></li>
<li>Upgrade <strong>undici</strong> from 5.28.4 to 5.28.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/596">actions/setup-dotnet#596</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/benwells"><code>@​benwells</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/587">actions/setup-dotnet#587</a></li>
<li><a
href="https://github.com/aparnajyothi-y"><code>@​aparnajyothi-y</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-dotnet/pull/586">actions/setup-dotnet#586</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d4c94342e5"><code>d4c9434</code></a>
Update to Node.js 24 and modernize async usage (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/654">#654</a>)</li>
<li><a
href="5c125af7da"><code>5c125af</code></a>
Bump actions/checkout from 4 to 5 (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/662">#662</a>)</li>
<li><a
href="87c6e11776"><code>87c6e11</code></a>
Bumps form-data (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/652">#652</a>)</li>
<li><a
href="06a5327ecf"><code>06a5327</code></a>
Bump undici from 5.28.5 to 5.29.0 (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/641">#641</a>)</li>
<li><a
href="e8e5b8203e"><code>e8e5b82</code></a>
Bump eslint-config-prettier from 9.1.0 to 10.1.5 (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/639">#639</a>)</li>
<li><a
href="bf4cd79173"><code>bf4cd79</code></a>
Bump <code>@​actions/glob</code> from 0.4.0 to 0.5.0 (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/594">#594</a>)</li>
<li><a
href="4ddad1c881"><code>4ddad1c</code></a>
Bump husky from 8.0.3 to 9.1.7 (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/591">#591</a>)</li>
<li><a
href="0f55b457d2"><code>0f55b45</code></a>
removes end-of-line dotnet versions (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/647">#647</a>)</li>
<li><a
href="267870a9c4"><code>267870a</code></a>
upgrade actions/cache to 4.0.3 (<a
href="https://redirect.github.com/actions/setup-dotnet/issues/622">#622</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/setup-dotnet/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-dotnet&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 06:08:12 -06:00
Guilherme
74225b01ce [CmdPal] Enhance ToggleSetting's Label and Description layout (#43472)
<!-- 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 changes the positioning of Label and Description from
ToggleSetting, making it more similar to
`CheckBoxWithDescriptionControl` used in Settings.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #39283 
- [ ] **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 initially tried solving this by using a Regex on the card JSON to
replace all `Input.Toggle` elements with the new `ColumnSet` structure.
However, since `ToggleSetting.cs` already serves as a wrapper for
creating an Input.Toggle, I decided it was more effective to update the
declaration inside the `ToDictionary()` method directly.

### This is the result:

<img width="797" height="531" alt="Screenshot 2025-11-11 120426"
src="https://github.com/user-attachments/assets/2ae5d15d-699e-45ef-ab52-afd653d82111"
/>


<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 05:47:05 -06:00
Kai Tao
2f001e8150 Advanced paste: Tweak Foundry Local Displayed Model and start server if server is turned on when using AP (#43529)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. Foundry local model name should not prefixed by fl://
2. If foundry service is shutdown, we should not just fail it, we should
start it then call FL to make availability better.

<!-- 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
Verified locally:
1. Manually disable foundry local service, then run AP with foundry
local, it can return result instead of direct failure.
2. 
<img width="659" height="294" alt="image"
src="https://github.com/user-attachments/assets/113da451-7131-4ce7-ae82-0ccf772ad8aa"
/>
<img width="988" height="192" alt="image"
src="https://github.com/user-attachments/assets/aa3650ba-668a-40c4-ad8a-303e09000dd4"
/>
![Uploading image.png…]()
2025-11-13 17:28:23 +08:00
Shawn Yuan
a983a773f3 Fix advanced paste migration issue (#43524)
<!-- 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 refactors how legacy AI enablement settings are
migrated and simplifies the handling of legacy properties in the
settings UI. The main improvements are in the migration logic and the
removal of legacy extension data handling.


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

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

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

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

Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
2025-11-13 16:55:09 +08:00
leileizhang
02eb55e3b9 Refine AdvancedPaste Terms UI and add default system prompt for Advanced AI (#43526)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. when no terms and Privacy links don't show the infobar
<img width="349" height="127" alt="image"
src="https://github.com/user-attachments/assets/b7eeeabf-365f-45f6-adb4-56335c14e8ad"
/>
<img width="410" height="217" alt="image"
src="https://github.com/user-attachments/assets/15e053c4-738d-4bb4-9544-24bdf8a5a584"
/>


2. add add default system prompt for Advanced AI

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 16:51:57 +08:00
Dave Rayment
afaf904016 [General] Update PR template with multiple issues note (#43442)
Added a comment noting that multiple issues should be placed on separate
lines, as per GitHub recommendations.

(When on the same line, issue references after the first are expanded
from their shortcut form, but are _not_ auto-closed once the PR is
merged.)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 09:18:31 +01:00
Jiří Polášek
4425e3ad28 CmdPal: Change captitalization of "Last position" item (#43518)
## Summary of the Pull Request

This PR updates the capitalization of the drop-down item from "Last
Position" to "Last position" to align with the guidelines.

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 09:17:41 +01:00
Dave Rayment
9f95d9b477 [ZoomIt] Fix screenshot save accuracy issue and GDI object leak (#43375)
<!-- 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 fixes two issues with the ZoomIt screenshot save function:

- The "actual size" saved bitmap was recomputed from the zoomed-in
viewport, leading to artefacts and a non-1:1 pixel accurate output if
smooth mode was active.
- Two bitmap objects were not deleted after being selected, leading to a
leak of 1 or 2 GDI objects per screenshot save operation.

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

- [x] Closes: #43323
- [x] Closes: #43352
- [ ] **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
### GDI object leak
In the prior code, bitmap objects were created:

```cpp
            // Capture the screen before displaying the save dialog
            hInterimSaveDc = CreateCompatibleDC( hdcScreen );
            hInterimSaveBitmap = CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight );
            SelectObject( hInterimSaveDc, hInterimSaveBitmap );
...
                    hSaveBitmap = CreateCompatibleBitmap( hdcScreen, saveWidth, saveHeight );
                    SelectObject( hSaveDc, hSaveBitmap );
```

but were not deleted in the clean-up:

```cpp
            DeleteDC( hInterimSaveDc );
            DeleteDC( hSaveDc );
```

Deleting their associated device contexts orphans the bitmaps and the
GDI objects will not be recovered until ZoomIt is closed.

### Fix
The code now uses RAII for handles to DCs and Bitmaps, e.g.:

```cpp
                    wil::unique_hdc hdcZoomed( CreateCompatibleDC(hdcScreen) );
                    wil::unique_hbitmap hbmZoomed(
                        CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight ) );
                    SelectObject( hdcZoomed.get(), hbmZoomed.get() );
```

The handles are automatically cleaned up when the variables go out of
scope.

The existing `DeleteDC` code has been removed.

### Screenshot Actual Size save issue

ZoomIt's save screenshot routine operated on the currently-displayed
view, i.e. the zoomed-in viewport, executing a copy of the full monitor
resolution image to begin:

```cpp
            StretchBlt( hInterimSaveDc,
                        0, 0,
                        copyWidth, copyHeight,
                        hdcScreen,
                        monInfo.rcMonitor.left + copyX,
                        monInfo.rcMonitor.top + copyY,
                        copyWidth, copyHeight,
                        SRCCOPY|CAPTUREBLT );
```
(Here hdcScreen represents the zoomed-in image.)

When "Actual size" was selected by the user, this bitmap was scaled back
down to attempt to reproduce the 1:1 pixel source:

```cpp
                    StretchBlt( hSaveDc,
                                0, 0,
                                saveWidth, saveHeight,
                                hInterimSaveDc,
                                0,
                                0,
                                copyWidth, copyHeight,
                                SRCCOPY | CAPTUREBLT );
				
                    SavePng( targetFilePath, hSaveBitmap );
```

This mostly works if the smooth mode is not applied because the zoom
levels tend to produce integer zoomed pixel sizes, although it still
produces inexact results at times.

The main issue is that the new smooth mode produces a halftone-smoothed
output on the display. Attempting to scale this back down to a
pixel-accurate source removes high frequency detail and does not reflect
the underlying bitmap:

Original source:
<img width="523" height="186" alt="image"
src="https://github.com/user-attachments/assets/7a6dca02-8608-44ed-917f-c6fd1a7b112c"
/>

"Actual size" save result before fix:
<img width="524" height="211" alt="image"
src="https://github.com/user-attachments/assets/29c63018-1e8d-4e74-a572-3615686aaa61"
/>

### Fix

This fix reverses the prior logic. Instead of using the zoomed-in
viewport as the screenshot source, we start by blitting a copy of the
source bitmap itself, from `hdcScreenCompat`. If a zoomed screenshot is
required, this is used as the source of the resize, i.e. we replicate
the zoom the user sees in their viewport.

This approach:

- Is faster and saves memory. It removes the need for an initial
`StretchBlt` operation, and the working bitmap itself is significantly
smaller if the user is zoomed in.
- Saves on the second resize operation if "Actual size" is chosen. We
can simply save the copy as-is.
- Removes the need to care about monitor coords. All calculations are
relative to the top-left of the source bitmap copy.
- Simplifies the code. In addition to being able to remove some code,
locality is improved - the creation of the zoomed image (and the
application of smoothing, if required) is now immediately next to where
it is saved.

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

(Manual validation on standalone ZoomIt build.)

- Confirmed that "Actual size" screenshots are now 1:1 copies of the
underlying bitmap, not scaled copies of the screen display.
- Confirmed that screenshots no longer leak GDI objects in either
"Zoomed" or "Actual size" modes.
- Tested cropped and non-cropped saves, i.e. using
<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>S</kbd> versus
<kbd>Ctrl</kbd>+<kbd>S</kbd>.
- Tested that user-added drawing/text was preserved in screenshots.
- Tested that both smooth and non-smooth zoom modes operate as they did
previously.
- Tested on multiple monitors, including a high-DPI external monitor
running at 175% scaling.

---------

Co-authored-by: Leilei Zhang <leilzh@microsoft.com>
2025-11-13 12:51:54 +08:00
Niels Laute
8fc43e1a22 [CursorWrap] Simplifying settings (#43496)
Removed the shortcut settings based on feedback

<img width="1028" height="249" alt="image"
src="https://github.com/user-attachments/assets/6ffdca64-9c2d-4398-aa77-7116be41f0ca"
/>

---------

Co-authored-by: Leilei Zhang <leilzh@microsoft.com>
2025-11-13 10:33:29 +08:00
Shawn Yuan
42ebf8d992 Fix AP system prompt textbox display issue (#43486)
<!-- 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 refactors how the system prompt is managed and
displayed in the Advanced Paste settings UI. The main improvements
center around normalizing the system prompt value, ensuring placeholder
prompts are handled consistently, and updating the logic for switching
between advanced and simple AI modes. These changes help prevent
placeholder text from being mistakenly saved as a custom system prompt
and improve maintainability.

**System prompt normalization and placeholder handling:**

* Added static normalization for both `AdvancedAISystemPrompt` and
`SimpleAISystemPrompt`, and introduced logic to clear the system prompt
if it matches a placeholder value. This prevents default placeholder
text from being persisted as a custom prompt.
[[1]](diffhunk://#diff-14126907329c7fcd49dd33bab32283296c7dd68ddc3902163a482a3b3ce58d36R39-R40)
[[2]](diffhunk://#diff-14126907329c7fcd49dd33bab32283296c7dd68ddc3902163a482a3b3ce58d36R942-R982)
* Refactored event handlers to use the new normalization logic and
updated the placeholder switching mechanism when toggling between
advanced and simple AI modes.
* Ensured system prompt normalization is applied when saving provider
configuration, further enforcing consistent prompt handling.

**UI improvements:**

* Removed hard-coded placeholder text from the XAML, ensuring that the
placeholder is now dynamically set based on the current mode and
normalized prompt value.
* Updated the logic for setting the system prompt textbox placeholder to
use the new normalization and mode detection methods, improving clarity
and reducing potential user confusion.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

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

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

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

Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
2025-11-13 10:19:43 +08:00
Kai Tao
76b6a25ac4 New info badge for cursor wrap and Advanced paste Tweak (#43490)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
1. New info badge for cursor wrap
2. Advanced paste UI string localization
3. Add ShowPreviewPane Setting to determine whether show the advanced
paste preview

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
<img width="451" height="247" alt="image"
src="https://github.com/user-attachments/assets/920b4b8a-f1f9-4848-b140-3295ff347fdc"
/>

<img width="470" height="383" alt="image"
src="https://github.com/user-attachments/assets/3978ebec-e695-4036-99a8-abf1561d9a28"
/>

<img width="1136" height="392" alt="image"
src="https://github.com/user-attachments/assets/0d474fcf-49a4-4cc7-b849-0231a95d9ea7"
/>
2025-11-13 10:18:13 +08:00
leileizhang
1c646ecb2a Add more detailed telemetry for AdvancedPaste (#43498)
<!-- 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 telemetry to capture the following:

- PasteAI: endpoint, model name, and processing time
- PasteAI Errors
- Advanced AI: endpoint, model name, and token usage
- Advanced AI Errors

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 10:09:50 +08:00
Jaylyn Barbee
a51b2647d9 [Light Switch] Fixed issue where number boxes in select location dialog were sometimes not updating (#43514)
<!-- 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
Ensure that even if the user enters the same values the correct buttons
are enabled and the dialog shows the correct info.

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

- [x] Closes: #43511
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Localization:** All end-user-facing strings can be localized
2025-11-13 09:51:34 +08:00
Niels Laute
b41ed2feb1 Minor styling tweaks in README (#43494)
Added a section for utilities and updated installation instructions.

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 09:36:49 +08:00
Niels Laute
1b742ef817 [AP] Settings UI improvements (#43488)
## Summary of the Pull Request

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 09:32:25 +08:00
Leon Zandman
2a40e1ce4d Minor ZoomIt fixes (#43495)
## Summary of the Pull Request
Regarding #43265 / #43266, I noticed some minor spelling mistakes. I
also refactored some literal strings to string constants.

I didn't discuss this with core contributors or @MarioHewardt. Just a
quick shot-from-the-hip PR ;-) I hope that isn't a problem...

_Note: this is a new Pull Request for this change. I had to close the
previous one (#43443), because I had made a commit using a wrong Github
account._

## PR Checklist

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-13 09:32:05 +08:00
Trevor
b015d6a778 Update onenote png icons to new core10 design (#43506)
<!-- 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
Update the oneNote pngs to October Core 10 icon design 

MSNews article:
https://www.msn.com/en-us/technology/software/microsoft-s-new-office-icons-are-more-curvy-and-colorful/ar-AA1NFYYI

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

- [x] Closes: #43507
- [ ] **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
- [x] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-12 20:28:31 +01:00
Shawn Yuan (from Dev Box)
d2cf2be2cb add ui
Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
2025-11-11 16:37:34 +08:00
Shawn Yuan (from Dev Box)
c953d5146a upgrade winappsdk
Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
2025-11-11 14:54:05 +08:00
87 changed files with 1866 additions and 1049 deletions

View File

@@ -141,7 +141,7 @@ bla
BLACKFRAME
BLENDFUNCTION
Blockquotes
Blt
blt
BLURBEHIND
BLURREGION
bmi
@@ -1873,6 +1873,7 @@ UPDATENOW
UPDATEREGISTRY
updown
UPGRADINGPRODUCTCODE
upscaling
Uptool
urld
Usb

View File

@@ -5,6 +5,7 @@
## 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

View File

@@ -40,7 +40,7 @@ jobs:
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
- name: Setup .NET 9.0
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: '9.0.x'

3
.gitmodules vendored
View File

@@ -4,6 +4,3 @@
[submodule "deps/expected-lite"]
path = deps/expected-lite
url = https://github.com/martinmoene/expected-lite.git
[submodule "deps/cziplib"]
path = deps/cziplib
url = https://github.com/kuba--/zip.git

View File

@@ -291,6 +291,7 @@
"Mono.Cecil.Rocks.dll",
"Newtonsoft.Json.dll",
"CommunityToolkit.WinUI.Controls.TitleBar.dll",
"CommunityToolkit.WinUI.Controls.OpacityMaskView.dll",
"NLog.dll",
"HtmlAgilityPack.dll",

View File

@@ -26,6 +26,7 @@
<PropertyGroup Condition="'$(SkipCppCodeAnalysis)' == ''">
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRuleSet>$(MsbuildThisFileDirectory)\CppRuleSet.ruleset</CodeAnalysisRuleSet>
<CAExcludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(CAExcludePath)</CAExcludePath>
</PropertyGroup>
<!-- C++ source compile-specific things for all configurations -->
@@ -34,7 +35,7 @@
<PreferredToolArchitecture Condition="'$(PROCESSOR_ARCHITECTURE)' == 'ARM64' or '$(PROCESSOR_ARCHITEW6432)' == 'ARM64'">arm64</PreferredToolArchitecture>
<VcpkgEnabled>false</VcpkgEnabled>
<ReplaceWildcardsInProjectItems>true</ReplaceWildcardsInProjectItems>
<ExternalIncludePath>$(MSBuildThisFileFullPath)\..\deps\;$(MSBuildThisFileFullPath)\..\packages\;$(ExternalIncludePath)</ExternalIncludePath>
<ExternalIncludePath>$(MSBuildThisFileDirectory)deps;$(MSBuildThisFileDirectory)packages;$(ExternalIncludePath)</ExternalIncludePath>
<!-- Enable control flow guard for C++ projects that don't consume any C++ files -->
<!-- This covers the case where a .dll exports a .lib, but doesn't have any ClCompile entries. -->
<LinkControlFlowGuard>Guard</LinkControlFlowGuard>

View File

@@ -147,6 +147,18 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.AdvancedPasteSemanticKernelFormatEvent</td>
<td>Triggered when Advanced Paste leverages the Semantic Kernel.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.AdvancedPasteSemanticKernelErrorEvent</td>
<td>Occurs when the Semantic Kernel workflow encounters an error.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.AdvancedPasteEndpointUsageEvent</td>
<td>Logs the AI provider, model, and processing duration for each endpoint call.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.AdvancedPasteCustomActionErrorEvent</td>
<td>Records provider, model, and status details when a custom action fails.</td>
</tr>
</table>
### Always on Top

View File

@@ -7,6 +7,7 @@
<PackageVersion Include="AdaptiveCards.ObjectModel.WinUI3" Version="2.0.0-beta" />
<PackageVersion Include="AdaptiveCards.Rendering.WinUI3" Version="2.1.0-beta" />
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.5" />
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" Version="0.1.251101-build.2372" />
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
<PackageVersion Include="CoenM.ImageSharp.ImageHash" Version="1.3.6" />
@@ -69,9 +70,17 @@
-->
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4948" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.37" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.250907003" />
<PackageVersion Include="Microsoft.ML.OnnxRuntimeGenAI.WinML" Version="0.10.1" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.251003001" />
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.38" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Base" Version="1.8.250831001" />
<PackageVersion Include="Microsoft.WindowsAppSDK.DWrite" Version="1.8.25090401" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.250906002" />
<PackageVersion Include="Microsoft.WindowsAppSDK.InteractiveExperiences" Version="1.8.250906004" />
<PackageVersion Include="Microsoft.WindowsAppSDK.ML" Version="1.8.2095" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.251003001" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Widgets" Version="1.8.250904007" />
<PackageVersion Include="Microsoft.WindowsAppSDK.WinUI" Version="1.8.250906003" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
@@ -110,6 +119,7 @@
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="9.0.10" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.10" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.10" />

View File

@@ -1498,6 +1498,7 @@ SOFTWARE.
- CoenM.ImageSharp.ImageHash
- CommunityToolkit.Common
- CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock
- CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView
- CommunityToolkit.Mvvm
- CommunityToolkit.WinUI.Animations
- CommunityToolkit.WinUI.Collections

View File

@@ -7,7 +7,9 @@
<h1 align="center">
<span>Microsoft PowerToys</span>
</h1>
<p align="center">
<span align="center">Microsoft PowerToys is a collection of utilities that help you customize Windows and streamline everyday tasks.</span>
</p>
<h3 align="center">
<a href="#-installation">Installation</a>
<span> · </span>
@@ -18,8 +20,10 @@
<a href="#-whats-new">Release notes</a>
</h3>
<br/><br/>
Microsoft PowerToys is a collection of utilities that help you customize Windows and streamline everyday tasks.
<br/><br/>
## 🔨 Utilities
PowerToys includes over 25 utilities to help you customize and optimize your Windows experience:
| | | |
|---|---|---|
@@ -37,20 +41,13 @@ Microsoft PowerToys is a collection of utilities that help you customize Windows
## 📋 Installation
For detailed installation instructions, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
Before you begin, make sure your device meets the system requirements:
> [!NOTE]
> - Windows 11 or Windows 10 version 2004 (20H1 / build 19041) or newer
> - 64-bit processor: x64 or ARM64
> - Latest stable version of [Microsoft Edge WebView2 Runtime](https://go.microsoft.com/fwlink/p/?LinkId=2124703) is installed via the bootstrapper during setup
Choose one of the installation methods below:
For detailed installation instructions and system requirements, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
But to get started quickly, choose one of the installation methods below:
<br/><br/>
<details open>
<summary>Download .exe from GitHub</summary>
<summary><strong>Download .exe from GitHub</strong></summary>
<br/>
Go to the [PowerToys GitHub releases][github-release-link], click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
<!-- items that need to be updated release to release -->
@@ -67,11 +64,11 @@ Go to the [PowerToys GitHub releases][github-release-link], click Assets to reve
| Per user - ARM64 | [PowerToysUserSetup-0.95.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.95.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.95.1-arm64.exe][ptMachineArm64] |
</details>
<details>
<summary>Microsoft Store</summary>
<summary><strong>Microsoft Store</strong></summary>
<br/>
You can easily install PowerToys from the Microsoft Store:
<p>
<a style="text-decoration:none" href="https://aka.ms/getPowertoys">
@@ -82,10 +79,9 @@ You can easily install PowerToys from the Microsoft Store:
</p>
</details>
<details>
<summary>WinGet</summary>
<summary><strong>WinGet</strong></summary>
<br/>
Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
*User scope installer [default]*
@@ -100,8 +96,8 @@ winget install --scope machine Microsoft.PowerToys -s winget
</details>
<details>
<summary>Other methods</summary>
<summary><strong>Other methods</strong></summary>
<br/>
There are [community driven install methods](./doc/unofficialInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there.
</details>

1
deps/cziplib vendored

Submodule deps/cziplib deleted from 81314fff0a

View File

@@ -4,6 +4,7 @@
#include <ProjectTelemetry.h>
#include <spdlog/sinks/base_sink.h>
#include <filesystem>
#include <string_view>
#include "../../src/common/logger/logger.h"
#include "../../src/common/utils/gpo.h"
@@ -856,14 +857,69 @@ UINT __stdcall UnsetAdvancedPasteAPIKeyCA(MSIHANDLE hInstall)
try
{
winrt::Windows::Security::Credentials::PasswordVault vault;
winrt::Windows::Security::Credentials::PasswordCredential cred;
hr = WcaInitialize(hInstall, "UnsetAdvancedPasteAPIKey");
ExitOnFailure(hr, "Failed to initialize");
cred = vault.Retrieve(L"https://platform.openai.com/api-keys", L"PowerToys_AdvancedPaste_OpenAIKey");
vault.Remove(cred);
winrt::Windows::Security::Credentials::PasswordVault vault;
auto hasPrefix = [](std::wstring_view value, wchar_t const* prefix) {
std::wstring_view prefixView{ prefix };
return value.compare(0, prefixView.size(), prefixView) == 0;
};
const wchar_t* resourcePrefixes[] = {
L"https://platform.openai.com/api-keys",
L"https://azure.microsoft.com/products/ai-services/openai-service",
L"https://azure.microsoft.com/products/ai-services/ai-inference",
L"https://console.mistral.ai/account/api-keys",
L"https://ai.google.dev/",
};
const wchar_t* usernamePrefixes[] = {
L"PowerToys_AdvancedPaste_",
};
auto credentials = vault.RetrieveAll();
for (auto const& credential : credentials)
{
bool shouldRemove = false;
std::wstring resource{ credential.Resource() };
for (auto const prefix : resourcePrefixes)
{
if (hasPrefix(resource, prefix))
{
shouldRemove = true;
break;
}
}
if (!shouldRemove)
{
std::wstring username{ credential.UserName() };
for (auto const prefix : usernamePrefixes)
{
if (hasPrefix(username, prefix))
{
shouldRemove = true;
break;
}
}
}
if (!shouldRemove)
{
continue;
}
try
{
vault.Remove(credential);
}
catch (...)
{
}
}
}
catch (...)
{

View File

@@ -169,40 +169,30 @@ internal sealed class FoundryClient
public async Task<bool> EnsureModelLoaded(string modelId)
{
try
Logger.LogInfo($"[FoundryClient] EnsureModelLoaded called with: {modelId}");
// Check if already loaded
if (await IsModelLoaded(modelId).ConfigureAwait(false))
{
Logger.LogInfo($"[FoundryClient] EnsureModelLoaded called with: {modelId}");
// Check if already loaded
if (await IsModelLoaded(modelId).ConfigureAwait(false))
{
Logger.LogInfo($"[FoundryClient] Model already loaded: {modelId}");
return true;
}
// Check if model exists in cache
var cachedModels = await ListCachedModels().ConfigureAwait(false);
Logger.LogInfo($"[FoundryClient] Cached models: {string.Join(", ", cachedModels.Select(m => m.Name))}");
if (!cachedModels.Any(m => m.Name == modelId))
{
Logger.LogWarning($"[FoundryClient] Model not found in cache: {modelId}");
return false;
}
// Load the model
Logger.LogInfo($"[FoundryClient] Loading model: {modelId}");
await _foundryManager.LoadModelAsync(modelId).ConfigureAwait(false);
// Verify it's loaded
var loaded = await IsModelLoaded(modelId).ConfigureAwait(false);
Logger.LogInfo($"[FoundryClient] Model load result: {loaded}");
return loaded;
Logger.LogInfo($"[FoundryClient] Model already loaded: {modelId}");
return true;
}
catch (Exception ex)
// Load the model
Logger.LogInfo($"[FoundryClient] Loading model: {modelId}");
await _foundryManager.LoadModelAsync(modelId).ConfigureAwait(false);
// Verify it's loaded
var loaded = await IsModelLoaded(modelId).ConfigureAwait(false);
Logger.LogInfo($"[FoundryClient] Model load result: {loaded}");
return loaded;
}
public async Task EnsureRunning()
{
if (!_foundryManager.IsServiceRunning)
{
Logger.LogError($"[FoundryClient] EnsureModelLoaded exception: {ex.Message}");
return false;
await _foundryManager.StartServiceAsync();
}
}
}

View File

@@ -13,7 +13,8 @@ namespace LanguageModelProvider;
public sealed class FoundryLocalModelProvider : ILanguageModelProvider
{
private IEnumerable<ModelDetails>? _downloadedModels;
private FoundryClient? _foundryManager;
private IEnumerable<FoundryCatalogModel>? _catalogModels;
private FoundryClient? _foundryClient;
private string? _serviceUrl;
public static FoundryLocalModelProvider Instance { get; } = new();
@@ -22,66 +23,54 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
public string ProviderDescription => "The model will run locally via Foundry Local";
public string UrlPrefix => "fl://";
public IChatClient? GetIChatClient(string url)
public IChatClient? GetIChatClient(string modelId)
{
try
{
Logger.LogInfo($"[FoundryLocal] GetIChatClient called with url: {url}");
InitializeAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Logger.LogError($"[FoundryLocal] Failed to initialize: {ex.Message}");
return null;
}
Logger.LogInfo($"[FoundryLocal] GetIChatClient called with url: {modelId}");
InitializeAsync().GetAwaiter().GetResult();
if (string.IsNullOrWhiteSpace(_serviceUrl) || _foundryManager == null)
{
Logger.LogError("[FoundryLocal] Service URL or manager is null");
return null;
}
// Extract model ID from URL (format: fl://modelname)
var modelId = url.Replace(UrlPrefix, string.Empty).Trim('/');
if (string.IsNullOrWhiteSpace(modelId))
{
Logger.LogError("[FoundryLocal] Model ID is empty after extraction");
return null;
}
Logger.LogInfo($"[FoundryLocal] Extracted model ID: {modelId}");
// Check if model is in catalog
var isInCatalog = _catalogModels?.Any(m => m.Name == modelId) ?? false;
if (!isInCatalog)
{
var errorMessage = $"{modelId} is not supported in Foundry Local. Please configure supported models in Settings.";
Logger.LogError($"[FoundryLocal] {errorMessage}");
throw new InvalidOperationException(errorMessage);
}
// Check if model is cached
var isInCache = _downloadedModels?.Any(m => m.ProviderModelDetails is FoundryCachedModel cached && cached.Name == modelId) ?? false;
if (!isInCache)
{
var errorMessage = $"The requested model '{modelId}' is not cached. Please download it using Foundry Local.";
Logger.LogError($"[FoundryLocal] {errorMessage}");
throw new InvalidOperationException(errorMessage);
}
// Ensure the model is loaded before returning chat client
try
var isLoaded = _foundryClient!.EnsureModelLoaded(modelId).GetAwaiter().GetResult();
if (!isLoaded)
{
var isLoaded = _foundryManager.EnsureModelLoaded(modelId).GetAwaiter().GetResult();
if (!isLoaded)
{
Logger.LogError($"[FoundryLocal] Failed to load model: {modelId}");
return null;
}
Logger.LogInfo($"[FoundryLocal] Model is loaded: {modelId}");
}
catch (Exception ex)
{
Logger.LogError($"[FoundryLocal] Exception ensuring model loaded: {ex.Message}");
return null;
Logger.LogError($"[FoundryLocal] Failed to load model: {modelId}");
throw new InvalidOperationException($"Failed to load the model '{modelId}'.");
}
// Use ServiceUri instead of Endpoint since Endpoint already includes /v1
var baseUri = _foundryManager.GetServiceUri();
var baseUri = _foundryClient.GetServiceUri();
if (baseUri == null)
{
Logger.LogError("[FoundryLocal] Service URI is null");
return null;
const string message = "Foundry Local service URL is not available. Please make sure Foundry Local is installed and running.";
Logger.LogError($"[FoundryLocal] {message}");
throw new InvalidOperationException(message);
}
var endpointUri = new Uri($"{baseUri.ToString().TrimEnd('/')}/v1");
Logger.LogInfo($"[FoundryLocal] Creating OpenAI client with endpoint: {endpointUri}");
Logger.LogInfo($"[FoundryLocal] Model ID for chat client: {modelId}");
return new OpenAIClient(
new ApiKeyCredential("none"),
@@ -128,29 +117,36 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
private void Reset()
{
_downloadedModels = null;
_catalogModels = null;
_ = InitializeAsync();
}
private async Task InitializeAsync(CancellationToken cancelationToken = default)
{
if (_foundryManager != null && _downloadedModels != null && _downloadedModels.Any())
if (_foundryClient != null && _downloadedModels != null && _downloadedModels.Any() && _catalogModels != null && _catalogModels.Any())
{
await _foundryClient.EnsureRunning().ConfigureAwait(false);
return;
}
Logger.LogInfo("[FoundryLocal] Initializing provider");
_foundryManager ??= await FoundryClient.CreateAsync();
_foundryClient ??= await FoundryClient.CreateAsync();
if (_foundryManager == null)
if (_foundryClient == null)
{
Logger.LogError("[FoundryLocal] Failed to create Foundry client");
return;
const string message = "Foundry Local client could not be created. Please make sure Foundry Local is installed and running.";
Logger.LogError($"[FoundryLocal] {message}");
throw new InvalidOperationException(message);
}
_serviceUrl ??= await _foundryManager.GetServiceUrl();
_serviceUrl ??= await _foundryClient.GetServiceUrl();
Logger.LogInfo($"[FoundryLocal] Service URL: {_serviceUrl}");
var cachedModels = await _foundryManager.ListCachedModels();
var catalogModels = await _foundryClient.ListCatalogModels();
Logger.LogInfo($"[FoundryLocal] Found {catalogModels.Count} catalog models");
_catalogModels = catalogModels;
var cachedModels = await _foundryClient.ListCachedModels();
Logger.LogInfo($"[FoundryLocal] Found {cachedModels.Count} cached models");
List<ModelDetails> downloadedModels = [];
@@ -162,7 +158,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
{
Id = $"fl-{model.Name}",
Name = model.Name,
Url = $"{UrlPrefix}{model.Name}",
Url = $"fl://{model.Name}",
Description = $"{model.Name} running locally with Foundry Local",
HardwareAccelerators = [HardwareAccelerator.FOUNDRYLOCAL],
SupportedOnQualcomm = true,
@@ -178,7 +174,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
{
Logger.LogInfo("[FoundryLocal] Checking availability");
await InitializeAsync();
var available = _foundryManager != null;
var available = _foundryClient != null;
Logger.LogInfo($"[FoundryLocal] Available: {available}");
return available;
}

View File

@@ -10,13 +10,11 @@ public interface ILanguageModelProvider
{
string Name { get; }
string UrlPrefix { get; }
string ProviderDescription { get; }
Task<IEnumerable<ModelDetails>> GetModelsAsync(bool ignoreCached = false, CancellationToken cancelationToken = default);
IChatClient? GetIChatClient(string url);
IChatClient? GetIChatClient(string modelId);
string GetIChatClientString(string url);
}

View File

@@ -1,106 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Concurrent;
using Microsoft.Extensions.AI;
namespace LanguageModelProvider;
public sealed class LanguageModelService
{
private readonly ConcurrentDictionary<string, ILanguageModelProvider> _providersByPrefix;
public LanguageModelService(IEnumerable<ILanguageModelProvider> providers)
{
ArgumentNullException.ThrowIfNull(providers);
_providersByPrefix = new ConcurrentDictionary<string, ILanguageModelProvider>(StringComparer.OrdinalIgnoreCase);
foreach (var provider in providers)
{
if (!string.IsNullOrWhiteSpace(provider.UrlPrefix))
{
_providersByPrefix[provider.UrlPrefix] = provider;
}
}
}
public static LanguageModelService CreateDefault()
{
return new LanguageModelService(new[]
{
FoundryLocalModelProvider.Instance,
});
}
public IReadOnlyCollection<ILanguageModelProvider> Providers => _providersByPrefix.Values.ToArray();
public bool RegisterProvider(ILanguageModelProvider provider)
{
ArgumentNullException.ThrowIfNull(provider);
if (string.IsNullOrWhiteSpace(provider.UrlPrefix))
{
throw new ArgumentException("Provider must supply a URL prefix.", nameof(provider));
}
_providersByPrefix[provider.UrlPrefix] = provider;
return true;
}
public ILanguageModelProvider? GetProviderFor(string? modelReference)
{
if (string.IsNullOrWhiteSpace(modelReference))
{
return null;
}
foreach (var provider in _providersByPrefix.Values)
{
if (modelReference.StartsWith(provider.UrlPrefix, StringComparison.OrdinalIgnoreCase))
{
return provider;
}
}
return null;
}
public async Task<IReadOnlyList<ModelDetails>> GetModelsAsync(bool refresh = false, CancellationToken cancellationToken = default)
{
List<ModelDetails> models = [];
foreach (var provider in _providersByPrefix.Values)
{
cancellationToken.ThrowIfCancellationRequested();
var providerModels = await provider.GetModelsAsync(refresh, cancellationToken).ConfigureAwait(false);
models.AddRange(providerModels);
}
return models;
}
public IChatClient? GetClient(ModelDetails model)
{
if (model is null)
{
return null;
}
var reference = !string.IsNullOrWhiteSpace(model.Url) ? model.Url : model.Id;
return GetClient(reference);
}
public IChatClient? GetClient(string? modelReference)
{
if (string.IsNullOrWhiteSpace(modelReference))
{
return null;
}
var provider = GetProviderFor(modelReference);
return provider?.GetIChatClient(modelReference);
}
}

View File

@@ -49,12 +49,16 @@ internal sealed class IntegrationTestUserSettings : IUserSettings
public bool CloseAfterLosingFocus => false;
public bool EnableClipboardPreview => true;
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions => _customActions;
public IReadOnlyList<PasteFormats> AdditionalActions => _additionalActions;
public PasteAIConfiguration PasteAIConfiguration => _configuration;
public string CustomModelStoragePath => AdvancedPasteProperties.GetDefaultCustomModelStoragePath();
public event EventHandler Changed;
public Task SetActiveAIProviderAsync(string providerId)

View File

@@ -144,7 +144,7 @@ public sealed class AIServiceBatchIntegrationTests
switch (format)
{
case PasteFormats.CustomTextTransformation:
var transformResult = await services.CustomActionTransformService.TransformTextAsync(batchTestInput.Prompt, batchTestInput.Clipboard, CancellationToken.None, progress);
var transformResult = await services.CustomActionTransformService.TransformTextAsync(null, batchTestInput.Prompt, batchTestInput.Clipboard, CancellationToken.None, progress);
return DataPackageHelpers.CreateFromText(transformResult.Content ?? string.Empty);
case PasteFormats.KernelQuery:

View File

@@ -68,6 +68,7 @@
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="System.ClientModel" />
<PackageReference Include="System.Numerics.Tensors" />
<PackageReference Include="Microsoft.Windows.Compatibility" />
<PackageReference Include="Microsoft.Windows.CsWin32" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />

View File

@@ -558,7 +558,7 @@
<TextBlock
x:Uid="AIProvidersFlyoutHeader"
Grid.Row="0"
Style="{StaticResource BodyStrongTextBlockStyle}" />
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<ListView
x:Name="AIProviderListView"
Grid.Row="1"
@@ -611,10 +611,10 @@
CornerRadius="{StaticResource ControlCornerRadius}"
Visibility="{x:Bind IsLocalModel, Mode=OneWay}">
<TextBlock
x:Uid="LocalModelBadge"
AutomationProperties.AccessibilityView="Raw"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Local" />
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</Border>
<!--<Border
Grid.Column="2"

View File

@@ -156,7 +156,7 @@
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="20"
Visibility="{x:Bind ViewModel.ClipboardHasData, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
Visibility="{x:Bind ViewModel.ShowClipboardPreview, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
@@ -168,7 +168,8 @@
Margin="0,0,4,0"
VerticalAlignment="Center"
IsEnabled="{x:Bind ViewModel.ClipboardHistoryEnabled, Mode=TwoWay}"
Style="{StaticResource SubtleButtonStyle}">
Style="{StaticResource SubtleButtonStyle}"
Visibility="{x:Bind ViewModel.ShowClipboardHistoryButton, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="ClipboardHistoryButtonToolTip" />
</ToolTipService.ToolTip>
@@ -263,16 +264,17 @@
<Button
Padding="0"
VerticalAlignment="Center"
Style="{StaticResource SubtleButtonStyle}">
Style="{StaticResource SubtleButtonStyle}"
Visibility="{x:Bind ViewModel.HasLegalLinks, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<FontIcon FontSize="12" Glyph="&#xE946;" />
<Button.Flyout>
<Flyout>
<StackPanel Spacing="8">
<TextBlock TextWrapping="Wrap">
<Run x:Uid="AIMistakeNote" /><LineBreak /><Run
x:Uid="CustomEndpointWarning"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="You are using a custom endpoint. Verify all answers." />
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
<StackPanel Orientation="Horizontal" Spacing="8">
<HyperlinkButton

View File

@@ -18,6 +18,7 @@ namespace AdvancedPaste.Helpers
PromptTokens = semanticKernelFormatEvent.PromptTokens;
CompletionTokens = semanticKernelFormatEvent.CompletionTokens;
ModelName = semanticKernelFormatEvent.ModelName;
ProviderType = semanticKernelFormatEvent.ProviderType;
ActionChain = semanticKernelFormatEvent.ActionChain;
}
@@ -38,6 +39,8 @@ namespace AdvancedPaste.Helpers
public string ModelName { get; set; }
public string ProviderType { get; set; }
public string ActionChain { get; set; }
public string ToJsonString() => JsonSerializer.Serialize(this, SourceGenerationContext.Default.AIServiceFormatEvent);

View File

@@ -19,12 +19,16 @@ namespace AdvancedPaste.Settings
public bool CloseAfterLosingFocus { get; }
public bool EnableClipboardPreview { get; }
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions { get; }
public IReadOnlyList<PasteFormats> AdditionalActions { get; }
public PasteAIConfiguration PasteAIConfiguration { get; }
public string CustomModelStoragePath { get; }
public event EventHandler Changed;
Task SetActiveAIProviderAsync(string providerId);

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Threading;
@@ -40,12 +41,16 @@ namespace AdvancedPaste.Settings
public bool CloseAfterLosingFocus { get; private set; }
public bool EnableClipboardPreview { get; private set; }
public IReadOnlyList<PasteFormats> AdditionalActions => _additionalActions;
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions => _customActions;
public PasteAIConfiguration PasteAIConfiguration { get; private set; }
public string CustomModelStoragePath { get; private set; }
public UserSettings(IFileSystem fileSystem)
{
_settingsUtils = new SettingsUtils(fileSystem);
@@ -53,7 +58,10 @@ namespace AdvancedPaste.Settings
IsAIEnabled = false;
ShowCustomPreview = true;
CloseAfterLosingFocus = false;
EnableClipboardPreview = true;
PasteAIConfiguration = new PasteAIConfiguration();
CustomModelStoragePath = NormalizeDirectoryPath(AdvancedPasteProperties.GetDefaultCustomModelStoragePath());
EnsureDirectoryExists(CustomModelStoragePath);
_additionalActions = [];
_customActions = [];
_taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
@@ -107,7 +115,10 @@ namespace AdvancedPaste.Settings
IsAIEnabled = properties.IsAIEnabled;
ShowCustomPreview = properties.ShowCustomPreview;
CloseAfterLosingFocus = properties.CloseAfterLosingFocus;
EnableClipboardPreview = properties.EnableClipboardPreview;
PasteAIConfiguration = properties.PasteAIConfiguration ?? new PasteAIConfiguration();
CustomModelStoragePath = NormalizeDirectoryPath(properties.CustomModelStoragePath);
EnsureDirectoryExists(CustomModelStoragePath);
var sourceAdditionalActions = properties.AdditionalActions;
(PasteFormats Format, IAdvancedPasteAction[] Actions)[] additionalActionFormats =
@@ -164,6 +175,15 @@ namespace AdvancedPaste.Settings
}
var properties = settings.Properties;
bool legacyAdvancedAIConsumed = properties.TryConsumeLegacyAdvancedAIEnabled(out var advancedFlag);
bool legacyAdvancedAIEnabled = legacyAdvancedAIConsumed && advancedFlag;
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
if (legacyCredential is null)
{
return legacyAdvancedAIConsumed;
}
var configuration = properties.PasteAIConfiguration;
if (configuration is null)
@@ -172,30 +192,11 @@ namespace AdvancedPaste.Settings
properties.PasteAIConfiguration = configuration;
}
bool hasLegacyProviders = configuration.LegacyProviderConfigurations is { Count: > 0 };
bool legacyAdvancedAIConsumed = properties.TryConsumeLegacyAdvancedAIEnabled(out var advancedFlag);
bool legacyAdvancedAIEnabled = legacyAdvancedAIConsumed && advancedFlag;
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
if (!hasLegacyProviders && legacyCredential is null && !legacyAdvancedAIConsumed)
{
return false;
}
bool configurationUpdated = false;
if (hasLegacyProviders)
{
configurationUpdated |= AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(configuration);
}
PasteAIProviderDefinition openAIProvider = null;
if (legacyCredential is not null || hasLegacyProviders || legacyAdvancedAIConsumed)
{
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
openAIProvider = ensureResult.Provider;
configurationUpdated |= ensureResult.Updated;
}
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
PasteAIProviderDefinition openAIProvider = ensureResult.Provider;
configurationUpdated |= ensureResult.Updated;
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
{
@@ -203,16 +204,17 @@ namespace AdvancedPaste.Settings
configurationUpdated = true;
}
if (legacyCredential is not null && openAIProvider is not null)
if (openAIProvider is not null)
{
StoreMigratedOpenAICredential(openAIProvider.Id, openAIProvider.ServiceType, legacyCredential.Password);
RemoveLegacyOpenAICredential();
}
const bool shouldEnableAI = true;
bool enabledUpdated = false;
if (!properties.IsAIEnabled && legacyCredential is not null)
if (properties.IsAIEnabled != shouldEnableAI)
{
properties.IsAIEnabled = true;
properties.IsAIEnabled = shouldEnableAI;
enabledUpdated = true;
}
@@ -403,5 +405,39 @@ namespace AdvancedPaste.Settings
{
Dispose(false);
}
private static string NormalizeDirectoryPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return AdvancedPasteProperties.GetDefaultCustomModelStoragePath();
}
try
{
path = Environment.ExpandEnvironmentVariables(path.Trim());
return Path.GetFullPath(path);
}
catch (Exception)
{
return path;
}
}
private static void EnsureDirectoryExists(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return;
}
try
{
Directory.CreateDirectory(path);
}
catch (Exception)
{
}
}
}
}

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Models;
@@ -32,12 +33,13 @@ public sealed class PasteFormat
IsSavedQuery = false,
};
public static PasteFormat CreateCustomAIFormat(PasteFormats format, string name, string prompt, bool isSavedQuery, ClipboardFormat clipboardFormats, bool isAIServiceEnabled) =>
public static PasteFormat CreateCustomAIFormat(PasteFormats format, string name, string prompt, bool isSavedQuery, ClipboardFormat clipboardFormats, bool isAIServiceEnabled, AdvancedPasteCustomAction customAction = null) =>
new(format, clipboardFormats, isAIServiceEnabled)
{
Name = name,
Prompt = prompt,
IsSavedQuery = isSavedQuery,
CustomAction = customAction,
};
public PasteFormatMetadataAttribute Metadata => MetadataDict[Format];
@@ -52,6 +54,8 @@ public sealed class PasteFormat
public bool IsSavedQuery { get; private init; }
public AdvancedPasteCustomAction CustomAction { get; private init; }
public bool IsEnabled { get; private init; }
public string AccessibleName => $"{Name} ({ShortcutText})";

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -40,10 +41,12 @@ namespace AdvancedPaste.Services.CustomActions
this.userSettings = userSettings;
}
public async Task<CustomActionTransformResult> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress)
public async Task<CustomActionTransformResult> TransformTextAsync(AdvancedPasteCustomAction customAction, string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress)
{
var pasteConfig = userSettings?.PasteAIConfiguration;
var providerConfig = BuildProviderConfig(pasteConfig);
var providerConfig = customAction?.UseCustomModel == true
? BuildCustomModelConfig(customAction)
: BuildProviderConfig(pasteConfig);
return await TransformAsync(prompt, inputText, providerConfig, cancellationToken, progress);
}
@@ -83,32 +86,39 @@ namespace AdvancedPaste.Services.CustomActions
SystemPrompt = systemPrompt,
};
var operationStart = DateTime.UtcNow;
var providerContent = await provider.ProcessPasteAsync(
request,
cancellationToken,
progress);
var durationMs = (int)Math.Round((DateTime.UtcNow - operationStart).TotalMilliseconds);
var usage = request.Usage;
var content = providerContent ?? string.Empty;
// Log endpoint usage
var endpointEvent = new AdvancedPasteEndpointUsageEvent(providerConfig.ProviderType);
// Log endpoint usage (custom action pipeline is not the advanced SK flow)
var endpointEvent = new AdvancedPasteEndpointUsageEvent(providerConfig.ProviderType, providerConfig.Model ?? string.Empty, isAdvanced: false, durationMs: durationMs);
PowerToysTelemetry.Log.WriteEvent(endpointEvent);
Logger.LogDebug($"{nameof(CustomActionTransformService)}.{nameof(TransformAsync)} complete; ModelName={providerConfig.Model ?? string.Empty}, PromptTokens={usage.PromptTokens}, CompletionTokens={usage.CompletionTokens}");
Logger.LogDebug($"{nameof(CustomActionTransformService)}.{nameof(TransformAsync)} complete; ModelName={providerConfig.Model ?? string.Empty}, PromptTokens={usage.PromptTokens}, CompletionTokens={usage.CompletionTokens}, DurationMs={durationMs}");
return new CustomActionTransformResult(content, usage);
}
catch (Exception ex)
{
Logger.LogError($"{nameof(CustomActionTransformService)}.{nameof(TransformAsync)} failed", ex);
var statusCode = ExtractStatusCode(ex);
var modelName = providerConfig.Model ?? string.Empty;
AdvancedPasteCustomActionErrorEvent errorEvent = new(providerConfig.ProviderType, modelName, statusCode, ex is PasteActionModeratedException ? PasteActionModeratedException.ErrorDescription : ex.Message);
PowerToysTelemetry.Log.WriteEvent(errorEvent);
if (ex is PasteActionException or OperationCanceledException)
{
throw;
}
var statusCode = ExtractStatusCode(ex);
var failureMessage = providerConfig.ProviderType switch
{
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => ErrorHelpers.TranslateErrorText(statusCode),
@@ -164,6 +174,24 @@ namespace AdvancedPaste.Services.CustomActions
return providerConfig;
}
private PasteAIConfig BuildCustomModelConfig(AdvancedPasteCustomAction customAction)
{
ArgumentNullException.ThrowIfNull(customAction);
var modelPath = ResolveCustomModelPath(customAction);
var systemPrompt = DetermineCustomModelSystemPrompt();
return new PasteAIConfig
{
ProviderType = AIServiceType.ML,
Model = Path.GetFileNameWithoutExtension(modelPath),
LocalModelPath = modelPath,
ModelPath = modelPath,
SystemPrompt = systemPrompt,
ModerationEnabled = false,
};
}
private string AcquireApiKey(AIServiceType serviceType)
{
if (!RequiresApiKey(serviceType))
@@ -194,5 +222,57 @@ namespace AdvancedPaste.Services.CustomActions
return providerConfig.ProviderType == AIServiceType.OpenAI || providerConfig.ProviderType == AIServiceType.AzureOpenAI;
}
private string ResolveCustomModelPath(AdvancedPasteCustomAction customAction)
{
var modelPath = customAction.CustomModelPath?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(modelPath))
{
throw new PasteActionException(GetCustomModelMissingMessage(), new FileNotFoundException());
}
var storagePath = userSettings?.CustomModelStoragePath;
var expandedPath = Environment.ExpandEnvironmentVariables(modelPath);
if (!Path.IsPathRooted(expandedPath) && !string.IsNullOrEmpty(storagePath))
{
expandedPath = Path.Combine(storagePath, expandedPath);
}
string fullPath;
try
{
fullPath = Path.GetFullPath(expandedPath);
}
catch (Exception ex)
{
throw new PasteActionException(GetCustomModelMissingMessage(), ex);
}
if (!File.Exists(fullPath))
{
throw new PasteActionException(GetCustomModelMissingMessage(), new FileNotFoundException(fullPath));
}
return fullPath;
}
private static string GetCustomModelMissingMessage()
{
var message = ResourceLoaderInstance.ResourceLoader.GetString("AdvancedPasteCustomModelNotFound");
return string.IsNullOrWhiteSpace(message) ? "The selected custom model could not be found." : message;
}
private string DetermineCustomModelSystemPrompt()
{
var customPrompt = userSettings?.PasteAIConfiguration?.ActiveProvider?.SystemPrompt
?? userSettings?.PasteAIConfiguration?.Providers?.FirstOrDefault()?.SystemPrompt;
if (string.IsNullOrWhiteSpace(customPrompt))
{
return DefaultSystemPrompt;
}
return customPrompt;
}
}
}

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using LanguageModelProvider;
using Microsoft.Extensions.AI;
@@ -23,7 +24,7 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new FoundryLocalPasteProvider(config));
private static readonly LanguageModelService LanguageModels = LanguageModelService.CreateDefault();
private static readonly FoundryLocalModelProvider _modelProvider = FoundryLocalModelProvider.Instance;
private readonly PasteAIConfig _config;
@@ -33,10 +34,6 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
_config = config;
}
public string ProviderName => AIServiceType.FoundryLocal.ToNormalizedKey();
public string DisplayName => string.IsNullOrWhiteSpace(_config?.Model) ? "Foundry Local" : _config.Model;
public async Task<bool> IsAvailableAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -72,21 +69,25 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
throw new PasteActionException(
"No Foundry Local model selected",
new InvalidOperationException("Model identifier is required"),
aiServiceMessage: "Please select a model in the AI provider settings. Model identifier should be in the format 'fl://model-name'.");
aiServiceMessage: "Please select a model in the AI provider settings.");
}
cancellationToken.ThrowIfCancellationRequested();
var chatClient = LanguageModels.GetClient(modelReference);
if (chatClient is null)
{
throw new PasteActionException(
$"Unable to load Foundry Local model: {modelReference}",
new InvalidOperationException("Chat client resolution failed"),
aiServiceMessage: "The model may not be downloaded or the Foundry Local service may not be running. Please check the model status in settings.");
}
// Extract actual model ID from the URL (format: fl://modelId)
var actualModelId = modelReference.Replace("fl://", string.Empty).Trim('/');
IChatClient chatClient;
try
{
chatClient = _modelProvider.GetIChatClient(modelReference);
}
catch (InvalidOperationException ex)
{
// GetIChatClient throws InvalidOperationException for user-facing errors
var errorMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture, ResourceLoaderInstance.ResourceLoader.GetString("FoundryLocal_UnableToLoadModel"), modelReference);
throw new PasteActionException(
errorMessage,
ex,
aiServiceMessage: ex.Message);
}
var userMessageContent = $"""
User instructions:
@@ -104,7 +105,7 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
new(ChatRole.User, userMessageContent),
};
var chatOptions = CreateChatOptions(_config?.SystemPrompt, actualModelId);
var chatOptions = CreateChatOptions(_config?.SystemPrompt, modelReference);
progress?.Report(0.1);

View File

@@ -7,11 +7,12 @@ using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Services.CustomActions
{
public interface ICustomActionTransformService
{
Task<CustomActionTransformResult> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress);
Task<CustomActionTransformResult> TransformTextAsync(AdvancedPasteCustomAction customAction, string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress);
}
}

View File

@@ -3,11 +3,15 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Windows.AI.MachineLearning;
namespace AdvancedPaste.Services.CustomActions
{
@@ -23,6 +27,8 @@ namespace AdvancedPaste.Services.CustomActions
private readonly PasteAIConfig _config;
private static readonly ConcurrentDictionary<string, Lazy<Task<LearningModel>>> ModelCache = new(StringComparer.OrdinalIgnoreCase);
public LocalModelPasteProvider(PasteAIConfig config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
@@ -30,14 +36,120 @@ namespace AdvancedPaste.Services.CustomActions
public Task<bool> IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true);
public Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress)
public async Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress)
{
ArgumentNullException.ThrowIfNull(request);
// TODO: Implement local model inference logic using _config.LocalModelPath/_config.ModelPath
var content = request.InputText ?? string.Empty;
request.Usage = AIServiceUsage.None;
return Task.FromResult(content);
cancellationToken.ThrowIfCancellationRequested();
var modelPath = _config.LocalModelPath ?? _config.ModelPath;
if (string.IsNullOrWhiteSpace(modelPath))
{
throw new PasteActionException(GetInferenceErrorMessage(), new InvalidOperationException("Local model path not provided."));
}
try
{
var model = await GetOrLoadModelAsync(modelPath).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
using LearningModelSession session = new(model);
var binding = new LearningModelBinding(session);
BindInputs(session.Model, binding, request);
var correlationId = Guid.NewGuid().ToString();
var evaluation = await session.EvaluateAsync(binding, correlationId).AsTask(cancellationToken).ConfigureAwait(false);
var output = ExtractStringOutput(session.Model, evaluation);
request.Usage = AIServiceUsage.None;
progress?.Report(1.0);
return output ?? string.Empty;
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
throw new PasteActionException(GetInferenceErrorMessage(), ex);
}
}
private static async Task<LearningModel> GetOrLoadModelAsync(string modelPath)
{
var loader = ModelCache.GetOrAdd(modelPath, path => new Lazy<Task<LearningModel>>(() => Task.Run(() => LearningModel.LoadFromFilePath(path))));
return await loader.Value.ConfigureAwait(false);
}
private static void BindInputs(LearningModel model, LearningModelBinding binding, PasteAIRequest request)
{
var stringInputs = model.InputFeatures
.OfType<TensorFeatureDescriptor>()
.Where(descriptor => descriptor.TensorKind == TensorKind.String)
.ToList();
if (stringInputs.Count == 0)
{
throw new PasteActionException(GetInferenceErrorMessage(), new InvalidOperationException("Model does not expose string inputs."));
}
var prompt = request.Prompt ?? string.Empty;
var input = request.InputText ?? string.Empty;
var combined = string.IsNullOrWhiteSpace(prompt) ? input : string.Join(Environment.NewLine + Environment.NewLine, prompt, input);
foreach (var inputDescriptor in stringInputs)
{
var value = ResolveInputValue(inputDescriptor.Name, prompt, input, combined);
var tensor = TensorString.CreateFromArray(new long[] { 1 }, new[] { value });
binding.Bind(inputDescriptor.Name, tensor);
}
}
private static string ResolveInputValue(string featureName, string prompt, string input, string combined)
{
if (string.IsNullOrEmpty(featureName))
{
return combined;
}
switch (featureName.ToLowerInvariant())
{
case "prompt":
case "instruction":
case "instructions":
return prompt;
case "input":
case "text":
case "input_text":
case "inputtext":
return input;
default:
return combined;
}
}
private static string ExtractStringOutput(LearningModel model, LearningModelEvaluationResult evaluation)
{
foreach (var outputDescriptor in model.OutputFeatures.OfType<TensorFeatureDescriptor>().Where(descriptor => descriptor.TensorKind == TensorKind.String))
{
if (evaluation.Outputs.TryGetValue(outputDescriptor.Name, out var value) && value is TensorString tensor)
{
var vector = tensor.GetAsVectorView();
if (vector.Count > 0)
{
return vector[0];
}
}
}
throw new PasteActionException(GetInferenceErrorMessage(), new InvalidOperationException("Model did not return a string output."));
}
private static string GetInferenceErrorMessage()
{
var message = ResourceLoaderInstance.ResourceLoader.GetString("AdvancedPasteCustomModelInferenceError");
return string.IsNullOrWhiteSpace(message) ? "The custom model failed to generate a response." : message;
}
}
}

View File

@@ -29,6 +29,7 @@ public abstract class KernelServiceBase(
ICustomActionTransformService customActionTransformService) : IKernelService
{
private const string PromptParameterName = "prompt";
private const string DefaultSystemPrompt = "You are an agent who is tasked with helping users paste their clipboard data. You have functions available to help you with this task. Call function when necessary to help user finish the transformation task. You never need to ask permission, always try to do as the user asks. The user will only input one message and will not be available for further questions, so try your best. The user will put in a request to format their clipboard data and you will fulfill it. Do not output anything else besides the reformatted clipboard content.";
private readonly IKernelQueryCacheService _queryCacheService = queryCacheService;
private readonly IPromptModerationService _promptModerationService = promptModerationService;
@@ -144,7 +145,8 @@ public abstract class KernelServiceBase(
ChatHistory chatHistory = [];
chatHistory.AddSystemMessage(runtimeConfig.SystemPrompt);
var systemPrompt = string.IsNullOrWhiteSpace(runtimeConfig.SystemPrompt) ? DefaultSystemPrompt : runtimeConfig.SystemPrompt;
chatHistory.AddSystemMessage(systemPrompt);
chatHistory.AddSystemMessage($"Available clipboard formats: {await kernel.GetDataFormatsAsync()}");
chatHistory.AddUserMessage(prompt);
@@ -186,12 +188,20 @@ public abstract class KernelServiceBase(
private void LogResult(bool cacheUsed, bool isSavedQuery, IEnumerable<ActionChainItem> actionChain, AIServiceUsage usage)
{
AdvancedPasteSemanticKernelFormatEvent telemetryEvent = new(cacheUsed, isSavedQuery, usage.PromptTokens, usage.CompletionTokens, AdvancedAIModelName, AdvancedPasteSemanticKernelFormatEvent.FormatActionChain(actionChain));
var runtimeConfig = GetRuntimeConfiguration();
AdvancedPasteSemanticKernelFormatEvent telemetryEvent = new(
cacheUsed,
isSavedQuery,
usage.PromptTokens,
usage.CompletionTokens,
AdvancedAIModelName,
runtimeConfig.ServiceType.ToString(),
AdvancedPasteSemanticKernelFormatEvent.FormatActionChain(actionChain));
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
// Log endpoint usage
var runtimeConfig = GetRuntimeConfiguration();
var endpointEvent = new AdvancedPasteEndpointUsageEvent(runtimeConfig.ServiceType);
var endpointEvent = new AdvancedPasteEndpointUsageEvent(runtimeConfig.ServiceType, AdvancedAIModelName, isAdvanced: true);
PowerToysTelemetry.Log.WriteEvent(endpointEvent);
var logEvent = new AIServiceFormatEvent(telemetryEvent);
@@ -237,7 +247,7 @@ public abstract class KernelServiceBase(
? $"Runs the \"{customAction.Name}\" custom action."
: customAction.Description;
return KernelFunctionFactory.CreateFromMethod(
method: async (Kernel kernel) => await ExecuteCustomActionAsync(kernel, customAction.Prompt),
method: async (Kernel kernel) => await ExecuteCustomActionAsync(kernel, customAction),
functionName: functionName,
description: description,
parameters: null,
@@ -286,14 +296,14 @@ public abstract class KernelServiceBase(
return string.IsNullOrEmpty(sanitized) ? "_CustomAction" : sanitized;
}
private Task<string> ExecuteCustomActionAsync(Kernel kernel, string fixedPrompt) =>
private Task<string> ExecuteCustomActionAsync(Kernel kernel, AdvancedPasteCustomAction customAction) =>
ExecuteTransformAsync(
kernel,
new ActionChainItem(PasteFormats.CustomTextTransformation, Arguments: new() { { PromptParameterName, fixedPrompt } }),
new ActionChainItem(PasteFormats.CustomTextTransformation, Arguments: new() { { PromptParameterName, customAction?.Prompt ?? string.Empty } }),
async dataPackageView =>
{
var input = await dataPackageView.GetClipboardTextOrThrowAsync(kernel.GetCancellationToken());
var result = await _customActionTransformService.TransformTextAsync(fixedPrompt, input, kernel.GetCancellationToken(), kernel.GetProgress());
var result = await _customActionTransformService.TransformTextAsync(customAction, customAction?.Prompt ?? string.Empty, input, kernel.GetCancellationToken(), kernel.GetProgress());
return DataPackageHelpers.CreateFromText(result?.Content ?? string.Empty);
});
@@ -311,7 +321,7 @@ public abstract class KernelServiceBase(
private async Task<string> GetPromptBasedOutput(PasteFormats format, string prompt, string input, CancellationToken cancellationToken, IProgress<double> progress) =>
format switch
{
PasteFormats.CustomTextTransformation => (await _customActionTransformService.TransformTextAsync(prompt, input, cancellationToken, progress))?.Content ?? string.Empty,
PasteFormats.CustomTextTransformation => (await _customActionTransformService.TransformTextAsync(null, prompt, input, cancellationToken, progress))?.Content ?? string.Empty,
_ => throw new ArgumentException($"Unsupported format {format} for prompt transform", nameof(format)),
};

View File

@@ -37,7 +37,7 @@ public sealed class PasteFormatExecutor(IKernelService kernelService, ICustomAct
pasteFormat.Format switch
{
PasteFormats.KernelQuery => await _kernelService.TransformClipboardAsync(pasteFormat.Prompt, clipboardData, pasteFormat.IsSavedQuery, cancellationToken, progress),
PasteFormats.CustomTextTransformation => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformTextAsync(pasteFormat.Prompt, await clipboardData.GetClipboardTextOrThrowAsync(cancellationToken), cancellationToken, progress))?.Content ?? string.Empty),
PasteFormats.CustomTextTransformation => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformTextAsync(pasteFormat.CustomAction, pasteFormat.Prompt, await clipboardData.GetClipboardTextOrThrowAsync(cancellationToken), cancellationToken, progress))?.Content ?? string.Empty),
_ => await TransformHelpers.TransformAsync(format, clipboardData, cancellationToken, progress),
});
}

View File

@@ -160,10 +160,10 @@
<value>Active provider: {0}</value>
</data>
<data name="AIProvidersFlyoutHeader.Text" xml:space="preserve">
<value>AI providers</value>
<value>Configured models</value>
</data>
<data name="AIProvidersEmptyText.Text" xml:space="preserve">
<value>No AI providers configured</value>
<value>No models configured</value>
</data>
<data name="AIProvidersManageButtonContent.Content" xml:space="preserve">
<value>Configure models in Settings</value>
@@ -359,6 +359,17 @@
</data>
<data name="Relative_Date_TimeFormat" xml:space="preserve">
<value>{0}, {1}</value>
<comment>(e.g., 10/20/2025, 17:05 in the users locale)</comment>
<comment>(e.g., "10/20/2025, 17:05" in the user's locale)</comment>
</data>
<data name="CustomEndpointWarning" xml:space="preserve">
<value>You are using a custom endpoint. Verify all answers.</value>
</data>
<data name="LocalModelBadge.Text" xml:space="preserve">
<value>Local</value>
<comment>Badge label displayed next to local AI model providers (e.g., Ollama, Foundry Local) to indicate the model runs locally</comment>
</data>
<data name="FoundryLocal_UnableToLoadModel" xml:space="preserve">
<value>Unable to load Foundry Local model: {0}</value>
<comment>{0} is the model identifier. Do not translate {0}.</comment>
</data>
</root>

View File

@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry;
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public sealed class AdvancedPasteCustomActionErrorEvent : EventBase, IEvent
{
public AdvancedPasteCustomActionErrorEvent(AIServiceType providerType, string modelName, int statusCode, string error)
{
ProviderType = providerType.ToString();
ModelName = modelName;
StatusCode = statusCode;
Error = error;
}
public string ProviderType { get; set; }
public string ModelName { get; set; }
public int StatusCode { get; set; }
public string Error { get; set; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}

View File

@@ -19,9 +19,27 @@ public class AdvancedPasteEndpointUsageEvent : EventBase, IEvent
/// </summary>
public string ProviderType { get; set; }
public AdvancedPasteEndpointUsageEvent(AIServiceType providerType)
/// <summary>
/// Gets or sets the configured model name.
/// </summary>
public string ModelName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the advanced AI pipeline was used.
/// </summary>
public bool IsAdvanced { get; set; }
/// <summary>
/// Gets or sets the total duration in milliseconds, or -1 if unavailable.
/// </summary>
public int DurationMs { get; set; }
public AdvancedPasteEndpointUsageEvent(AIServiceType providerType, string modelName, bool isAdvanced, int durationMs = -1)
{
ProviderType = providerType.ToString();
ModelName = modelName;
IsAdvanced = isAdvanced;
DurationMs = durationMs;
}
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;

View File

@@ -14,7 +14,7 @@ namespace AdvancedPaste.Telemetry;
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class AdvancedPasteSemanticKernelFormatEvent(bool cacheUsed, bool isSavedQuery, int promptTokens, int completionTokens, string modelName, string actionChain) : EventBase, IEvent
public class AdvancedPasteSemanticKernelFormatEvent(bool cacheUsed, bool isSavedQuery, int promptTokens, int completionTokens, string modelName, string providerType, string actionChain) : EventBase, IEvent
{
public static string FormatActionChain(IEnumerable<ActionChainItem> actionChain) => FormatActionChain(actionChain.Select(item => item.Format));
@@ -30,6 +30,8 @@ public class AdvancedPasteSemanticKernelFormatEvent(bool cacheUsed, bool isSaved
public string ModelName { get; set; } = modelName;
public string ProviderType { get; set; } = providerType;
/// <summary>
/// Gets or sets a comma-separated list of paste formats used - in the same order they were executed.
/// Conceptually an array but formatted this way to work around https://github.com/dotnet/runtime/issues/10428

View File

@@ -63,6 +63,7 @@ namespace AdvancedPaste.ViewModels
private ClipboardFormat _availableClipboardFormats;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowClipboardHistoryButton))]
private bool _clipboardHistoryEnabled;
[ObservableProperty]
@@ -76,6 +77,7 @@ namespace AdvancedPaste.ViewModels
[NotifyPropertyChangedFor(nameof(PrivacyLinkUri))]
[NotifyPropertyChangedFor(nameof(HasTermsLink))]
[NotifyPropertyChangedFor(nameof(HasPrivacyLink))]
[NotifyPropertyChangedFor(nameof(HasLegalLinks))]
private bool _isAllowedByGPO;
[ObservableProperty]
@@ -221,10 +223,16 @@ namespace AdvancedPaste.ViewModels
public bool HasPrivacyLink => GetActiveProviderMetadata().HasPrivacyLink;
public bool HasLegalLinks => HasTermsLink || HasPrivacyLink;
public bool ClipboardHasData => AvailableClipboardFormats != ClipboardFormat.None;
public bool ClipboardHasDataForCustomAI => PasteFormat.SupportsClipboardFormats(CustomAIFormat, AvailableClipboardFormats);
public bool ShowClipboardPreview => _userSettings.EnableClipboardPreview;
public bool ShowClipboardHistoryButton => ClipboardHistoryEnabled;
public bool HasIndeterminateTransformProgress => double.IsNaN(TransformProgress);
private PasteFormats CustomAIFormat =>
@@ -310,6 +318,7 @@ namespace AdvancedPaste.ViewModels
OnPropertyChanged(nameof(IsAdvancedAIEnabled));
OnPropertyChanged(nameof(AIProviders));
OnPropertyChanged(nameof(AllowedAIProviders));
OnPropertyChanged(nameof(ShowClipboardPreview));
NotifyActiveProviderChanged();
@@ -334,8 +343,8 @@ namespace AdvancedPaste.ViewModels
private PasteFormat CreateStandardPasteFormat(PasteFormats format) =>
PasteFormat.CreateStandardFormat(format, AvailableClipboardFormats, IsCustomAIServiceEnabled, ResourceLoaderInstance.ResourceLoader.GetString);
private PasteFormat CreateCustomAIPasteFormat(string name, string prompt, bool isSavedQuery) =>
PasteFormat.CreateCustomAIFormat(CustomAIFormat, name, prompt, isSavedQuery, AvailableClipboardFormats, IsCustomAIServiceEnabled);
private PasteFormat CreateCustomAIPasteFormat(string name, string prompt, bool isSavedQuery, AdvancedPasteCustomAction customAction = null) =>
PasteFormat.CreateCustomAIFormat(CustomAIFormat, name, prompt, isSavedQuery, AvailableClipboardFormats, IsCustomAIServiceEnabled, customAction);
private void UpdateAIProviderActiveFlags()
{
@@ -361,6 +370,7 @@ namespace AdvancedPaste.ViewModels
OnPropertyChanged(nameof(PrivacyLinkUri));
OnPropertyChanged(nameof(HasTermsLink));
OnPropertyChanged(nameof(HasPrivacyLink));
OnPropertyChanged(nameof(HasLegalLinks));
}
private void RefreshPasteFormats()
@@ -407,7 +417,7 @@ namespace AdvancedPaste.ViewModels
UpdateFormats(
CustomActionPasteFormats,
IsCustomAIServiceEnabled ? _userSettings.CustomActions.Select(customAction => CreateCustomAIPasteFormat(customAction.Name, customAction.Prompt, isSavedQuery: true)) : []);
IsCustomAIServiceEnabled ? _userSettings.CustomActions.Select(customAction => CreateCustomAIPasteFormat(customAction.Name, customAction.Prompt, isSavedQuery: true, customAction)) : []);
}
public void Dispose()
@@ -732,7 +742,7 @@ namespace AdvancedPaste.ViewModels
if (customAction != null)
{
await ReadClipboardAsync();
await ExecutePasteFormatAsync(CreateCustomAIPasteFormat(customAction.Name, customAction.Prompt, isSavedQuery: true), source);
await ExecutePasteFormatAsync(CreateCustomAIPasteFormat(customAction.Name, customAction.Prompt, isSavedQuery: true, customAction), source);
}
}
@@ -741,7 +751,7 @@ namespace AdvancedPaste.ViewModels
var customAction = _userSettings.CustomActions
.FirstOrDefault(customAction => Models.KernelQueryCache.CacheKey.PromptComparer.Equals(customAction.Prompt, Query));
await ExecutePasteFormatAsync(CreateCustomAIPasteFormat(customAction?.Name ?? "Default", Query, isSavedQuery: customAction != null), triggerSource);
await ExecutePasteFormatAsync(CreateCustomAIPasteFormat(customAction?.Name ?? "Default", Query, isSavedQuery: customAction != null, customAction), triggerSource);
}
private void HideWindow()

View File

@@ -271,7 +271,6 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
if (wait == WAIT_OBJECT_0 + (hParent ? (hManualOverride ? 3 : 2) : 2))
{
Logger::info(L"[LightSwitchService] Settings file changed event detected.");
ResetEvent(hSettingsChanged);
LightSwitchSettings::instance().LoadSettings();
stateManager.OnSettingsChanged();

View File

@@ -17,12 +17,10 @@ LightSwitchStateManager::LightSwitchStateManager()
void LightSwitchStateManager::OnSettingsChanged()
{
std::lock_guard<std::mutex> lock(_stateMutex);
Logger::info(L"[LightSwitchStateManager] Settings changed event received");
// If manual override was active, clear it so new settings take effect
if (_state.isManualOverride)
{
Logger::info(L"[LightSwitchStateManager] Clearing manual override due to settings update.");
_state.isManualOverride = false;
}
@@ -33,7 +31,6 @@ void LightSwitchStateManager::OnSettingsChanged()
void LightSwitchStateManager::OnTick(int currentMinutes)
{
std::lock_guard<std::mutex> lock(_stateMutex);
Logger::debug(L"[LightSwitchStateManager] Tick received: {}", currentMinutes);
EvaluateAndApplyIfNeeded();
}
@@ -51,7 +48,7 @@ void LightSwitchStateManager::OnManualOverride()
_state.isAppsLightActive = GetCurrentAppsTheme();
Logger::info(L"[LightSwitchStateManager] Synced internal theme state to current system theme ({}) and apps theme ({}).",
Logger::debug(L"[LightSwitchStateManager] Synced internal theme state to current system theme ({}) and apps theme ({}).",
(_state.isSystemLightActive ? L"light" : L"dark"),
(_state.isAppsLightActive ? L"light" : L"dark"));
}
@@ -79,9 +76,9 @@ void LightSwitchStateManager::SyncInitialThemeState()
std::lock_guard<std::mutex> lock(_stateMutex);
_state.isSystemLightActive = GetCurrentSystemTheme();
_state.isAppsLightActive = GetCurrentAppsTheme();
Logger::info(L"[LightSwitchStateManager] Synced initial state to current system theme ({})",
Logger::debug(L"[LightSwitchStateManager] Synced initial state to current system theme ({})",
_state.isSystemLightActive ? L"light" : L"dark");
Logger::info(L"[LightSwitchStateManager] Synced initial state to current apps theme ({})",
Logger::debug(L"[LightSwitchStateManager] Synced initial state to current apps theme ({})",
_state.isAppsLightActive ? L"light" : L"dark");
}
@@ -127,7 +124,6 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
// Early exit: OFF mode just pauses activity
if (_currentSettings.scheduleMode == ScheduleMode::Off)
{
Logger::debug(L"[LightSwitchStateManager] Mode is OFF — pausing service logic.");
_state.lastTickMinutes = now;
return;
}
@@ -145,7 +141,6 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
if (newDay || modeChangedToSun)
{
Logger::info(L"[LightSwitchStateManager] Recalculating sun times (mode/day change).");
auto [newLightTime, newDarkTime] = update_sun_times(_currentSettings);
_state.lastEvaluatedDay = st.wDay;
_state.effectiveLightMinutes = newLightTime + _currentSettings.sunrise_offset;
@@ -188,12 +183,10 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
if (crossedBoundary)
{
Logger::info(L"[LightSwitchStateManager] Manual override cleared after crossing boundary.");
_state.isManualOverride = false;
}
else
{
Logger::debug(L"[LightSwitchStateManager] Manual override active — skipping auto apply.");
_state.lastTickMinutes = now;
return;
}
@@ -206,7 +199,7 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
bool appsNeedsToChange = _currentSettings.changeApps && (_state.isAppsLightActive != shouldBeLight);
bool systemNeedsToChange = _currentSettings.changeSystem && (_state.isSystemLightActive != shouldBeLight);
Logger::debug(
/* Logger::debug(
L"[LightSwitchStateManager] now = {:02d}:{:02d}, light boundary = {:02d}:{:02d} ({}), dark boundary = {:02d}:{:02d} ({})",
now / 60,
now % 60,
@@ -215,12 +208,12 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
_state.effectiveLightMinutes,
_state.effectiveDarkMinutes / 60,
_state.effectiveDarkMinutes % 60,
_state.effectiveDarkMinutes);
_state.effectiveDarkMinutes); */
Logger::debug("should be light = {}, apps needs change = {}, system needs change = {}",
/* Logger::debug("should be light = {}, apps needs change = {}, system needs change = {}",
shouldBeLight ? "true" : "false",
appsNeedsToChange ? "true" : "false",
systemNeedsToChange ? "true" : "false");
systemNeedsToChange ? "true" : "false"); */
// Only apply theme if there's a change or no override active
if (!_state.isManualOverride && (appsNeedsToChange || systemNeedsToChange))
@@ -230,10 +223,6 @@ void LightSwitchStateManager::EvaluateAndApplyIfNeeded()
_state.isSystemLightActive = GetCurrentSystemTheme();
_state.isAppsLightActive = GetCurrentAppsTheme();
Logger::debug(L"[LightSwitchStateManager] Synced post-apply theme state — System: {}, Apps: {}",
_state.isSystemLightActive ? L"light" : L"dark",
_state.isAppsLightActive ? L"light" : L"dark");
}
_state.lastTickMinutes = now;

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
@@ -147,13 +148,14 @@
<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')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -165,16 +167,18 @@
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />

View File

@@ -4,14 +4,15 @@
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.ML" version="1.8.2095" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
</packages>

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
@@ -152,11 +152,11 @@
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.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')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
@@ -167,16 +167,16 @@
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets'))" />
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.ML" version="1.8.2095" targetFramework="native" />
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
</packages>

View File

@@ -39,6 +39,10 @@ type_pEnableThemeDialogTexture pEnableThemeDialogTexture;
#define WIN7_VERSION 0x106
#define WIN10_VERSION 0x206
// Default recording format frame rates
#define RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE 15
#define RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE 30
// Time that we'll cache live zoom window to avoid flicker
// of live zooming on Vista/ws2k8
#define LIVEZOOM_WINDOW_TIMEOUT 2*3600*1000

View File

@@ -44,11 +44,11 @@ LOGFONT g_LogFont;
BOOLEAN g_DemoTypeUserDriven = false;
TCHAR g_DemoTypeFile[MAX_PATH] = {0};
DWORD g_DemoTypeSpeedSlider = static_cast<int>(((MIN_TYPING_SPEED - MAX_TYPING_SPEED) / 2) + MAX_TYPING_SPEED);
DWORD g_RecordFrameRate = 30;
DWORD g_RecordFrameRate = 30; // We default to 30 here, but g_RecordFrameRate can be different depending on recording format and gets set accordingly
DWORD g_RecordScaling = 100;
DWORD g_RecordScalingGIF = 50;
DWORD g_RecordScalingMP4 = 100;
RecordingFormat g_RecordingFormat = RecordingFormat::GIF;
RecordingFormat g_RecordingFormat = RecordingFormat::MP4;
BOOLEAN g_CaptureAudio = FALSE;
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
@@ -87,8 +87,7 @@ REG_SETTING RegSettings[] = {
{ L"SnapToGrid", SETTING_TYPE_BOOLEAN, 0, &g_SnapToGrid, static_cast<DOUBLE>(g_SnapToGrid) },
{ L"ZoominSliderLevel", SETTING_TYPE_DWORD, 0, &g_SliderZoomLevel, static_cast<DOUBLE>(g_SliderZoomLevel) },
{ L"Font", SETTING_TYPE_BINARY, sizeof g_LogFont, &g_LogFont, static_cast<DOUBLE>(0) },
{ L"RecordFrameRate", SETTING_TYPE_DWORD, 0, &g_RecordFrameRate, static_cast<DOUBLE>(g_RecordFrameRate) },
{ L"RecordingFormat", SETTING_TYPE_DWORD, 0, &g_RecordingFormat, static_cast<DOUBLE>(0) },
{ L"RecordingFormat", SETTING_TYPE_DWORD, 0, &g_RecordingFormat, static_cast<DOUBLE>(g_RecordingFormat) },
{ L"RecordScalingGIF", SETTING_TYPE_DWORD, 0, &g_RecordScalingGIF, static_cast<DOUBLE>(g_RecordScalingGIF) },
{ L"RecordScalingMP4", SETTING_TYPE_DWORD, 0, &g_RecordScalingMP4, static_cast<DOUBLE>(g_RecordScalingMP4) },
{ L"CaptureAudio", SETTING_TYPE_BOOLEAN, 0, &g_CaptureAudio, static_cast<DOUBLE>(g_CaptureAudio) },

View File

@@ -168,6 +168,7 @@ BOOL g_RecordToggle = FALSE;
BOOL g_RecordCropping = FALSE;
SelectRectangle g_SelectRectangle;
std::wstring g_RecordingSaveLocation;
std::wstring g_RecordingSaveLocationGIF;
winrt::IDirect3DDevice g_RecordDevice{ nullptr };
std::shared_ptr<VideoRecordingSession> g_RecordingSession = nullptr;
std::shared_ptr<GifRecordingSession> g_GifRecordingSession = nullptr;
@@ -1812,7 +1813,7 @@ INT_PTR CALLBACK OptionsTabProc( HWND hDlg, UINT message,
// Check if GIF is selected by comparing the text
bool isGifSelected = (wcscmp(selectedText, L"GIF") == 0);
// if gif is selected set the scaling to the g_recordScaleGIF value otherwise to the g_recordScaleMP4 value
// If GIF is selected, set the scaling to the g_RecordScalingGIF value; otherwise to the g_RecordScalingMP4 value
if (isGifSelected) {
g_RecordScaling = g_RecordScalingGIF;
@@ -2173,7 +2174,10 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO,
g_CaptureAudio ? BST_CHECKED: BST_UNCHECKED );
for (int i = 0; i < _countof(g_FramerateOptions); i++) {
//
// The framerate drop down list is not used in the current version (might be added in the future)
//
/*for (int i = 0; i < _countof(g_FramerateOptions); i++) {
_stprintf(text, L"%d", g_FramerateOptions[i]);
SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), static_cast<UINT>(CB_ADDSTRING),
@@ -2182,7 +2186,7 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), CB_SETCURSEL, static_cast<WPARAM>(i), static_cast<LPARAM>(0));
}
}
}*/
// Add the recording format to the combo box and set the current selection
size_t selection = 0;
@@ -2345,17 +2349,8 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
text[2] = 0;
newTimeout = _tstoi( text );
if( g_RecordingFormat == RecordingFormat::GIF )
{
// Hardcode lower frame rate for GIFs
g_RecordFrameRate = 15;
}
else
{
g_RecordFrameRate = g_FramerateOptions[SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FRAME_RATE), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0))];
}
g_RecordingFormat = static_cast<RecordingFormat>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_FORMAT), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0)));
g_RecordFrameRate = (g_RecordingFormat == RecordingFormat::GIF) ? RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE : RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE;
g_RecordScaling = static_cast<int>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_SCALING), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0)) * 10 + 10);
// Get the selected microphone
@@ -3536,7 +3531,16 @@ void StopRecording()
//----------------------------------------------------------------------------
auto GetUniqueRecordingFilename()
{
std::filesystem::path path{ g_RecordingSaveLocation };
std::filesystem::path path;
if (g_RecordingFormat == RecordingFormat::GIF)
{
path = g_RecordingSaveLocationGIF;
}
else
{
path = g_RecordingSaveLocation;
}
// Chop off index if it's there
auto base = std::regex_replace( path.stem().wstring(), std::wregex( L" [(][0-9]+[)]$" ), L"" );
@@ -3591,6 +3595,7 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
auto stream = co_await file.OpenAsync( winrt::FileAccessMode::ReadWrite );
// Create the appropriate recording session based on format
OutputDebugStringW((L"Starting recording session. Framerate: " + std::to_wstring(g_RecordFrameRate) + L" scaling: " + std::to_wstring(g_RecordScaling) + L" Format: " + (g_RecordingFormat == RecordingFormat::GIF ? L"GIF" : L"MP4") + L"\n").c_str());
if (g_RecordingFormat == RecordingFormat::GIF)
{
g_GifRecordingSession = GifRecordingSession::Create(
@@ -3657,18 +3662,44 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
saveDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
}
if( g_RecordingSaveLocation.size() == 0) {
// Peek the folder Windows has chosen to display
static std::filesystem::path lastSaveFolder;
wil::unique_cotaskmem_string chosenFolderPath;
wil::com_ptr<IShellItem> currentSelectedFolder;
bool bFolderChanged = false;
if (SUCCEEDED(saveDialog->GetFolder(currentSelectedFolder.put())))
{
if (SUCCEEDED(currentSelectedFolder->GetDisplayName(SIGDN_FILESYSPATH, chosenFolderPath.put())))
{
if (lastSaveFolder != chosenFolderPath.get())
{
lastSaveFolder = chosenFolderPath.get() ? chosenFolderPath.get() : std::filesystem::path{};
bFolderChanged = true;
}
}
}
if( (g_RecordingFormat == RecordingFormat::GIF && g_RecordingSaveLocationGIF.size() == 0) || (g_RecordingFormat == RecordingFormat::MP4 && g_RecordingSaveLocation.size() == 0) || (bFolderChanged)) {
wil::com_ptr<IShellItem> shellItem;
wil::unique_cotaskmem_string folderPath;
if (SUCCEEDED(saveDialog->GetFolder(shellItem.put())) && SUCCEEDED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, folderPath.put())))
g_RecordingSaveLocation = folderPath.get();
if (SUCCEEDED(saveDialog->GetFolder(shellItem.put())) && SUCCEEDED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, folderPath.put()))) {
if (g_RecordingFormat == RecordingFormat::GIF) {
g_RecordingSaveLocationGIF = folderPath.get();
std::filesystem::path currentPath{ g_RecordingSaveLocationGIF };
g_RecordingSaveLocationGIF = currentPath / DEFAULT_GIF_RECORDING_FILE;
}
else {
g_RecordingSaveLocation = folderPath.get();
if (g_RecordingFormat == RecordingFormat::MP4) {
std::filesystem::path currentPath{ g_RecordingSaveLocation };
g_RecordingSaveLocation = currentPath / DEFAULT_RECORDING_FILE;
}
}
}
}
// Always use appropriate default filename based on current format
std::filesystem::path currentPath{ g_RecordingSaveLocation };
const wchar_t* defaultFile = (g_RecordingFormat == RecordingFormat::GIF) ? DEFAULT_GIF_RECORDING_FILE : DEFAULT_RECORDING_FILE;
g_RecordingSaveLocation = currentPath.parent_path() / defaultFile;
auto suggestedName = GetUniqueRecordingFilename();
saveDialog->SetFileName( suggestedName.c_str() );
@@ -3696,9 +3727,15 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
}
else {
co_await file.MoveAndReplaceAsync( destFile );
g_RecordingSaveLocation = file.Path();
SaveToClipboard(g_RecordingSaveLocation.c_str(), hWnd);
co_await file.MoveAndReplaceAsync(destFile);
if (g_RecordingFormat == RecordingFormat::GIF) {
g_RecordingSaveLocationGIF = file.Path();
SaveToClipboard(g_RecordingSaveLocationGIF.c_str(), hWnd);
}
else {
g_RecordingSaveLocation = file.Path();
SaveToClipboard(g_RecordingSaveLocation.c_str(), hWnd);
}
}
g_bSaveInProgress = false;
@@ -4039,8 +4076,10 @@ LRESULT APIENTRY MainWndProc(
// Set g_RecordScaling based on the current recording format
if (g_RecordingFormat == RecordingFormat::GIF) {
g_RecordScaling = g_RecordScalingGIF;
g_RecordFrameRate = RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE;
} else {
g_RecordScaling = g_RecordScalingMP4;
g_RecordFrameRate = RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE;
}
// to support migrating from
@@ -6332,6 +6371,17 @@ LRESULT APIENTRY MainWndProc(
{
// Reload the settings. This message is called from PowerToys after a setting is changed by the user.
reg.ReadRegSettings(RegSettings);
if (g_RecordingFormat == RecordingFormat::GIF)
{
g_RecordScaling = g_RecordScalingGIF;
g_RecordFrameRate = RECORDING_FORMAT_GIF_DEFAULT_FRAMERATE;
}
else
{
g_RecordScaling = g_RecordScalingMP4;
g_RecordFrameRate = RECORDING_FORMAT_MP4_DEFAULT_FRAMERATE;
}
// Apply tray icon setting
EnableDisableTrayIcon(hWnd, g_ShowTrayIcon);
@@ -6440,12 +6490,10 @@ LRESULT APIENTRY MainWndProc(
GetCursorPos(&local_savedCursorPos);
}
HBITMAP hInterimSaveBitmap;
HDC hInterimSaveDc;
HBITMAP hSaveBitmap;
HDC hSaveDc;
int copyX, copyY;
int copyWidth, copyHeight;
// Determine the user's desired save area in zoomed viewport coordinates.
// This will be the entire viewport if the user does not select a crop
// rectangle.
int copyX = 0, copyY = 0, copyWidth = width, copyHeight = height;
if ( LOWORD( wParam ) == IDC_SAVE_CROP )
{
@@ -6460,55 +6508,51 @@ LRESULT APIENTRY MainWndProc(
}
break;
}
auto copyRc = selectRectangle.SelectedRect();
selectRectangle.Stop();
g_RecordCropping = FALSE;
copyX = copyRc.left;
copyY = copyRc.top;
copyWidth = copyRc.right - copyRc.left;
copyHeight = copyRc.bottom - copyRc.top;
}
else
{
copyX = 0;
copyY = 0;
copyWidth = width;
copyHeight = height;
}
OutputDebug( L"***x: %d, y: %d, width: %d, height: %d\n", copyX, copyY, copyWidth, copyHeight );
RECT oldClipRect{};
GetClipCursor( &oldClipRect );
ClipCursor( NULL );
// Capture the screen before displaying the save dialog
hInterimSaveDc = CreateCompatibleDC( hdcScreen );
hInterimSaveBitmap = CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight );
SelectObject( hInterimSaveDc, hInterimSaveBitmap );
// Translate the viewport selection into coordinates for the 1:1 source
// bitmap hdcScreenCompat.
int viewportX, viewportY;
GetZoomedTopLeftCoordinates(
zoomLevel, &cursorPos, &viewportX, width, &viewportY, height );
hSaveDc = CreateCompatibleDC( hdcScreen );
#if SCALE_HALFTONE
SetStretchBltMode( hInterimSaveDc, HALFTONE );
SetStretchBltMode( hSaveDc, HALFTONE );
#else
// Use HALFTONE for better quality when smooth image is enabled
if (g_SmoothImage) {
SetStretchBltMode( hInterimSaveDc, HALFTONE );
SetStretchBltMode( hSaveDc, HALFTONE );
} else {
SetStretchBltMode( hInterimSaveDc, COLORONCOLOR );
SetStretchBltMode( hSaveDc, COLORONCOLOR );
}
#endif
StretchBlt( hInterimSaveDc,
0, 0,
copyWidth, copyHeight,
hdcScreen,
monInfo.rcMonitor.left + copyX,
monInfo.rcMonitor.top + copyY,
copyWidth, copyHeight,
SRCCOPY|CAPTUREBLT );
int saveX = viewportX + static_cast<int>( copyX / zoomLevel );
int saveY = viewportY + static_cast<int>( copyY / zoomLevel );
int saveWidth = static_cast<int>( copyWidth / zoomLevel );
int saveHeight = static_cast<int>( copyHeight / zoomLevel );
// Create a pixel-accurate copy of the desired area from the source bitmap.
wil::unique_hdc hdcActualSize( CreateCompatibleDC( hdcScreen ) );
wil::unique_hbitmap hbmActualSize(
CreateCompatibleBitmap( hdcScreen, saveWidth, saveHeight ) );
// Note: we do not need to restore the existing context later. The objects
// are transient and not reused.
SelectObject( hdcActualSize.get(), hbmActualSize.get() );
// Perform a direct 1:1 copy from the backing bitmap.
BitBlt( hdcActualSize.get(),
0, 0,
saveWidth, saveHeight,
hdcScreenCompat,
saveX, saveY,
SRCCOPY | CAPTUREBLT );
// Open the Save As dialog and capture the desired file path and whether to
// save the zoomed display or the source bitmap pixels.
g_bSaveInProgress = true;
memset( &openFileName, 0, sizeof(openFileName ));
openFileName.lStructSize = OPENFILENAME_SIZE_VERSION_400;
@@ -6524,6 +6568,7 @@ LRESULT APIENTRY MainWndProc(
"Actual size PNG\0*.png\0\0";
//"Actual size BMP\0*.bmp\0\0";
openFileName.lpstrFile = filePath;
if( GetSaveFileName( &openFileName ) )
{
TCHAR targetFilePath[MAX_PATH];
@@ -6533,42 +6578,47 @@ LRESULT APIENTRY MainWndProc(
_tcscat( targetFilePath, L".png" );
}
// Save image at screen size
if( openFileName.nFilterIndex == 1 )
if( openFileName.nFilterIndex == 2 )
{
SavePng( targetFilePath, hInterimSaveBitmap );
// Save at actual size.
SavePng( targetFilePath, hbmActualSize.get() );
}
// Save image scaled down to actual size
else
{
int saveWidth = static_cast<int>( copyWidth / zoomLevel );
int saveHeight = static_cast<int>( copyHeight / zoomLevel );
// Save zoomed-in image at screen resolution.
#if SCALE_HALFTONE
const int bltMode = HALFTONE;
#else
// Use HALFTONE for better quality when smooth image is enabled
const int bltMode = g_SmoothImage ? HALFTONE : COLORONCOLOR;
#endif
// Recreate the zoomed-in view by upscaling from our source bitmap.
wil::unique_hdc hdcZoomed( CreateCompatibleDC(hdcScreen) );
wil::unique_hbitmap hbmZoomed(
CreateCompatibleBitmap( hdcScreen, copyWidth, copyHeight ) );
SelectObject( hdcZoomed.get(), hbmZoomed.get() );
hSaveBitmap = CreateCompatibleBitmap( hdcScreen, saveWidth, saveHeight );
SelectObject( hSaveDc, hSaveBitmap );
SetStretchBltMode( hdcZoomed.get(), bltMode );
StretchBlt( hSaveDc,
StretchBlt( hdcZoomed.get(),
0, 0,
copyWidth, copyHeight,
hdcActualSize.get(),
0, 0,
saveWidth, saveHeight,
hInterimSaveDc,
0,
0,
copyWidth, copyHeight,
SRCCOPY | CAPTUREBLT );
SavePng( targetFilePath, hSaveBitmap );
SavePng( targetFilePath, hbmZoomed.get() );
}
}
g_bSaveInProgress = false;
DeleteDC( hInterimSaveDc );
DeleteDC( hSaveDc );
if( lParam != SHALLOW_ZOOM )
{
SetCursorPos(local_savedCursorPos.x, local_savedCursorPos.y);
SetCursorPos( local_savedCursorPos.x, local_savedCursorPos.y );
}
ClipCursor( &oldClipRect );
break;
}

View File

@@ -64,6 +64,8 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext
public string Title { get => string.IsNullOrEmpty(field) ? Name : field; protected set; } = string.Empty;
public string Id { get; protected set; } = string.Empty;
// This property maps to `IPage.IsLoading`, but we want to expose our own
// `IsLoading` property as a combo of this value and `IsInitialized`
public bool ModelIsLoading { get; protected set; } = true;
@@ -142,6 +144,7 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext
return; // throw?
}
Id = page.Id;
Name = page.Name;
ModelIsLoading = page.IsLoading;
Title = page.Title;

View File

@@ -10,7 +10,7 @@
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools.MSIX" Version="1.7.20250829.1" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.251003001" />
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="System.Text.Json" Version="9.0.8" />

View File

@@ -15,9 +15,12 @@ public class OpenPage : EventBase, IEvent
{
public int PageDepth { get; set; }
public OpenPage(int pageDepth)
public string Id { get; set; }
public OpenPage(int pageDepth, string id)
{
PageDepth = pageDepth;
Id = id;
EventName = "CmdPal_OpenPage";
}

View File

@@ -21,7 +21,6 @@ using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
@@ -160,7 +159,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
new AsyncNavigationRequest(message.Page, message.CancellationToken),
message.WithAnimation ? DefaultPageAnimation : _noAnimation);
PowerToysTelemetry.Log.WriteEvent(new OpenPage(RootFrame.BackStackDepth));
PowerToysTelemetry.Log.WriteEvent(new OpenPage(RootFrame.BackStackDepth, message.Page.Id));
if (!ViewModel.IsNested)
{
@@ -655,15 +654,15 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
e.Handled = true;
break;
default:
{
// The CommandBar is responsible for handling all the item keybindings,
// since the bound context item may need to then show another
// context menu
TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key);
WeakReferenceMessenger.Default.Send(msg);
e.Handled = msg.Handled;
break;
}
{
// The CommandBar is responsible for handling all the item keybindings,
// since the bound context item may need to then show another
// context menu
TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key);
WeakReferenceMessenger.Default.Send(msg);
e.Handled = msg.Handled;
break;
}
}
}

View File

@@ -429,7 +429,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>More</value>
</data>
<data name="Run_Radio_Position_LastPosition.Content" xml:space="preserve">
<value>Last Position</value>
<value>Last position</value>
<comment>Reopen the window where it was last closed</comment>
</data>
<data name="TrayMenu_Settings" xml:space="preserve">

View File

@@ -3,13 +3,13 @@
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup>
<PathToRoot>..\..\..\..\</PathToRoot>
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.251003001</WasdkNuget>
</PropertyGroup>
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
<PropertyGroup Label="Globals">
@@ -209,7 +209,7 @@
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
@@ -228,8 +228,8 @@
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />

View File

@@ -4,14 +4,15 @@
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.ML" version="1.8.2095" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
</packages>

View File

@@ -15,6 +15,7 @@ namespace Microsoft.CommandPalette.Extensions.Toolkit;
[JsonSerializable(typeof(List<Choice>))]
[JsonSerializable(typeof(List<ChoiceSetSetting>))]
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
[JsonSerializable(typeof(List<Dictionary<string, object>>))]
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)]
internal sealed partial class JsonSerializationContext : JsonSerializerContext
{

View File

@@ -26,15 +26,69 @@ public sealed class ToggleSetting : Setting<bool>
public override Dictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>
var items = new List<Dictionary<string, object>>();
if (!string.IsNullOrEmpty(Label))
{
{ "type", "Input.Toggle" },
{ "title", Label },
{ "id", Key },
{ "label", Description },
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
{ "isRequired", IsRequired },
{ "errorMessage", ErrorMessage },
items.Add(
new()
{
{ "type", "TextBlock" },
{ "text", Label },
{ "wrap", true },
});
}
if (!(string.IsNullOrEmpty(Description) || string.Equals(Description, Label, StringComparison.OrdinalIgnoreCase)))
{
items.Add(
new()
{
{ "type", "TextBlock" },
{ "text", Description },
{ "isSubtle", true },
{ "size", "Small" },
{ "spacing", "Small" },
{ "wrap", true },
});
}
return new()
{
{ "type", "ColumnSet" },
{
"columns", new List<Dictionary<string, object>>
{
new()
{
{ "type", "Column" },
{ "width", "20px" },
{
"items", new List<Dictionary<string, object>>
{
new()
{
{ "type", "Input.Toggle" },
{ "title", " " },
{ "id", Key },
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
{ "isRequired", IsRequired },
{ "errorMessage", ErrorMessage },
},
}
},
{ "verticalContentAlignment", "Center" },
},
new()
{
{ "type", "Column" },
{ "width", "stretch" },
{ "items", items },
{ "verticalContentAlignment", "Center" },
},
}
},
{ "spacing", "Medium" },
};
}

View File

@@ -2,7 +2,7 @@
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PathToRoot>..\..\..\..\..\</PathToRoot>
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.251003001</WasdkNuget>
<CppWinRTNuget>$(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5</CppWinRTNuget>
<WindowsSdkBuildToolsNuget>$(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188</WindowsSdkBuildToolsNuget>
<WebView2Nuget>$(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40</WebView2Nuget>

View File

@@ -3,15 +3,16 @@
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.ML" version="1.8.2095" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
</packages>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props')" />
@@ -218,13 +219,14 @@
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets')" />
<Import Project="..\..\..\..\packages\boost.1.87.0\build\boost.targets" Condition="Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" />
<Import Project="..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets" Condition="Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" />
</ImportGroup>
@@ -240,20 +242,22 @@
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.MSIX.1.7.20250829.1\build\Microsoft.Windows.SDK.BuildTools.MSIX.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\boost.1.87.0\build\boost.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost.1.87.0\build\boost.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\boost_regex-vc143.1.87.0\build\boost_regex-vc143.targets'))" />
</Target>

View File

@@ -6,14 +6,15 @@
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.ML" version="1.8.2095" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
</packages>

View File

@@ -3,14 +3,15 @@
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<!-- Windows App SDK and all transitive dependencies -->
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.251003001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.ML" version="1.8.2095" targetFramework="native" />
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
</packages>

View File

@@ -14,13 +14,14 @@
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\..\deps\expected.props" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
<Import Project="..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props" Condition="Exists('..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props')" />
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<ImportGroup Label="Shared" />
<PropertyGroup Label="Configuration">
@@ -141,13 +142,14 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
@@ -156,20 +158,22 @@
<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.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.1.8.251003001\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.251003001\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.WindowsAppSDK.ML.1.8.2095\build\native\Microsoft.WindowsAppSDK.ML.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<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'))" />

View File

@@ -1,35 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
/// <summary>
/// Stores provider-specific configuration overrides so each AI service can keep distinct settings.
/// </summary>
public class AIProviderConfigurationSnapshot
{
[JsonPropertyName("model-name")]
public string ModelName { get; set; } = string.Empty;
[JsonPropertyName("endpoint-url")]
public string EndpointUrl { get; set; } = string.Empty;
[JsonPropertyName("api-version")]
public string ApiVersion { get; set; } = string.Empty;
[JsonPropertyName("deployment-name")]
public string DeploymentName { get; set; } = string.Empty;
[JsonPropertyName("model-path")]
public string ModelPath { get; set; } = string.Empty;
[JsonPropertyName("system-prompt")]
public string SystemPrompt { get; set; } = string.Empty;
[JsonPropertyName("moderation-enabled")]
public bool ModerationEnabled { get; set; } = true;
}
}

View File

@@ -23,6 +23,8 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
private bool _isValid;
private bool _hasConflict;
private string _tooltip;
private bool _useCustomModel;
private string _customModelPath = string.Empty;
[JsonPropertyName("id")]
public int Id
@@ -122,6 +124,32 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
set => Set(ref _tooltip, value);
}
[JsonPropertyName("useCustomModel")]
public bool UseCustomModel
{
get => _useCustomModel;
set
{
if (Set(ref _useCustomModel, value))
{
UpdateIsValid();
}
}
}
[JsonPropertyName("customModelPath")]
public string CustomModelPath
{
get => _customModelPath;
set
{
if (Set(ref _customModelPath, value ?? string.Empty))
{
UpdateIsValid();
}
}
}
[JsonIgnore]
public IEnumerable<IAdvancedPasteAction> SubActions => [];
@@ -144,6 +172,8 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
CanMoveDown = other.CanMoveDown;
HasConflict = other.HasConflict;
Tooltip = other.Tooltip;
UseCustomModel = other.UseCustomModel;
CustomModelPath = other.CustomModelPath;
}
private HotkeySettings GetShortcutClone()
@@ -159,6 +189,10 @@ public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction
private void UpdateIsValid()
{
IsValid = !string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Prompt);
bool hasPrompt = !string.IsNullOrWhiteSpace(Prompt);
bool hasName = !string.IsNullOrWhiteSpace(Name);
bool hasModel = !UseCustomModel || !string.IsNullOrWhiteSpace(CustomModelPath);
IsValid = hasName && hasPrompt && hasModel;
}
}

View File

@@ -13,56 +13,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library
/// </summary>
public static class AdvancedPasteMigrationHelper
{
/// <summary>
/// Moves legacy provider configuration snapshots into the strongly-typed providers collection.
/// </summary>
/// <param name="configuration">The configuration instance to migrate.</param>
/// <returns>True if the configuration was modified.</returns>
public static bool MigrateLegacyProviderConfigurations(PasteAIConfiguration configuration)
{
if (configuration is null)
{
return false;
}
configuration.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
bool configurationUpdated = false;
if (configuration.LegacyProviderConfigurations is { Count: > 0 })
{
foreach (var entry in configuration.LegacyProviderConfigurations)
{
var result = EnsureProvider(configuration, entry.Key, entry.Value);
configurationUpdated |= result.Updated;
}
configuration.LegacyProviderConfigurations = null;
}
configurationUpdated |= EnsureActiveProviderIsValid(configuration);
return configurationUpdated;
}
/// <summary>
/// Ensures an OpenAI provider exists in the configuration, creating one if necessary.
/// </summary>
/// <param name="configuration">The configuration instance.</param>
/// <returns>The ensured provider and a flag indicating whether changes were made.</returns>
public static (PasteAIProviderDefinition Provider, bool Updated) EnsureOpenAIProvider(PasteAIConfiguration configuration)
{
return EnsureProvider(configuration, AIServiceType.OpenAI.ToConfigurationString(), null);
}
/// <summary>
/// Ensures a provider for the supplied service type exists, optionally applying a legacy snapshot.
/// </summary>
/// <param name="configuration">The configuration instance.</param>
/// <param name="serviceTypeKey">The persisted service type key.</param>
/// <param name="snapshot">An optional snapshot containing legacy values.</param>
/// <returns>The ensured provider and whether the configuration was updated.</returns>
public static (PasteAIProviderDefinition Provider, bool Updated) EnsureProvider(PasteAIConfiguration configuration, string serviceTypeKey, AIProviderConfigurationSnapshot snapshot)
{
if (configuration is null)
{
@@ -71,101 +27,45 @@ namespace Microsoft.PowerToys.Settings.UI.Library
configuration.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
var normalizedServiceType = NormalizeServiceType(serviceTypeKey);
var existingProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.ServiceType, normalizedServiceType, StringComparison.OrdinalIgnoreCase));
bool configurationUpdated = false;
const string serviceTypeKey = "OpenAI";
var existingProvider = configuration.Providers.FirstOrDefault(provider => string.Equals(provider.ServiceType, serviceTypeKey, StringComparison.OrdinalIgnoreCase));
bool updated = false;
if (existingProvider is null)
{
existingProvider = CreateProvider(normalizedServiceType, snapshot);
existingProvider = CreateProvider(serviceTypeKey);
configuration.Providers.Add(existingProvider);
configurationUpdated = true;
}
else if (snapshot is not null)
{
configurationUpdated |= ApplySnapshot(existingProvider, snapshot);
updated = true;
}
configurationUpdated |= EnsureActiveProviderIsValid(configuration, existingProvider);
updated |= EnsureActiveProviderIsValid(configuration, existingProvider);
return (existingProvider, configurationUpdated);
return (existingProvider, updated);
}
private static string NormalizeServiceType(string serviceTypeKey)
{
var serviceType = serviceTypeKey.ToAIServiceType();
return serviceType.ToConfigurationString();
}
private static PasteAIProviderDefinition CreateProvider(string serviceTypeKey, AIProviderConfigurationSnapshot snapshot)
/// <summary>
/// Creates a provider with default values for the requested service type.
/// </summary>
private static PasteAIProviderDefinition CreateProvider(string serviceTypeKey)
{
var serviceType = serviceTypeKey.ToAIServiceType();
var metadata = AIServiceTypeRegistry.GetMetadata(serviceType);
var provider = new PasteAIProviderDefinition
{
ServiceType = serviceTypeKey,
ModelName = !string.IsNullOrWhiteSpace(snapshot?.ModelName) ? snapshot.ModelName : PasteAIProviderDefaults.GetDefaultModelName(serviceType),
EndpointUrl = snapshot?.EndpointUrl ?? string.Empty,
ApiVersion = snapshot?.ApiVersion ?? string.Empty,
DeploymentName = snapshot?.DeploymentName ?? string.Empty,
ModelPath = snapshot?.ModelPath ?? string.Empty,
SystemPrompt = snapshot?.SystemPrompt ?? string.Empty,
ModerationEnabled = snapshot?.ModerationEnabled ?? true,
ModelName = PasteAIProviderDefaults.GetDefaultModelName(serviceType),
EndpointUrl = string.Empty,
ApiVersion = string.Empty,
DeploymentName = string.Empty,
ModelPath = string.Empty,
SystemPrompt = string.Empty,
ModerationEnabled = serviceType == AIServiceType.OpenAI,
IsLocalModel = metadata.IsLocalModel,
};
return provider;
}
private static bool ApplySnapshot(PasteAIProviderDefinition provider, AIProviderConfigurationSnapshot snapshot)
{
bool updated = false;
if (!string.IsNullOrWhiteSpace(snapshot.ModelName) && !string.Equals(provider.ModelName, snapshot.ModelName, StringComparison.Ordinal))
{
provider.ModelName = snapshot.ModelName;
updated = true;
}
if (!string.IsNullOrWhiteSpace(snapshot.EndpointUrl) && !string.Equals(provider.EndpointUrl, snapshot.EndpointUrl, StringComparison.Ordinal))
{
provider.EndpointUrl = snapshot.EndpointUrl;
updated = true;
}
if (!string.IsNullOrWhiteSpace(snapshot.ApiVersion) && !string.Equals(provider.ApiVersion, snapshot.ApiVersion, StringComparison.Ordinal))
{
provider.ApiVersion = snapshot.ApiVersion;
updated = true;
}
if (!string.IsNullOrWhiteSpace(snapshot.DeploymentName) && !string.Equals(provider.DeploymentName, snapshot.DeploymentName, StringComparison.Ordinal))
{
provider.DeploymentName = snapshot.DeploymentName;
updated = true;
}
if (!string.IsNullOrWhiteSpace(snapshot.ModelPath) && !string.Equals(provider.ModelPath, snapshot.ModelPath, StringComparison.Ordinal))
{
provider.ModelPath = snapshot.ModelPath;
updated = true;
}
if (!string.IsNullOrWhiteSpace(snapshot.SystemPrompt) && !string.Equals(provider.SystemPrompt, snapshot.SystemPrompt, StringComparison.Ordinal))
{
provider.SystemPrompt = snapshot.SystemPrompt;
updated = true;
}
if (provider.ModerationEnabled != snapshot.ModerationEnabled)
{
provider.ModerationEnabled = snapshot.ModerationEnabled;
updated = true;
}
return updated;
}
private static bool EnsureActiveProviderIsValid(PasteAIConfiguration configuration, PasteAIProviderDefinition preferredProvider = null)
{
if (configuration?.Providers is null || configuration.Providers.Count == 0)

View File

@@ -2,7 +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;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -27,58 +29,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
IsAIEnabled = false;
ShowCustomPreview = true;
CloseAfterLosingFocus = false;
EnableClipboardPreview = true;
PasteAIConfiguration = new();
CustomModelStoragePath = GetDefaultCustomModelStoragePath();
}
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool IsAIEnabled { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> ExtensionData
{
get => _extensionData;
set
{
_extensionData = value;
if (_extensionData != null && _extensionData.TryGetValue("IsOpenAIEnabled", out var legacyElement) && legacyElement.ValueKind == JsonValueKind.Object && legacyElement.TryGetProperty("value", out var valueElement))
{
IsAIEnabled = valueElement.ValueKind switch
{
JsonValueKind.True => true,
JsonValueKind.False => false,
_ => IsAIEnabled,
};
_extensionData.Remove("IsOpenAIEnabled");
}
if (_extensionData != null && _extensionData.TryGetValue("IsAdvancedAIEnabled", out var legacyAdvancedElement))
{
bool? legacyValue = legacyAdvancedElement.ValueKind switch
{
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Object when legacyAdvancedElement.TryGetProperty("value", out var advancedValueElement) => advancedValueElement.ValueKind switch
{
JsonValueKind.True => true,
JsonValueKind.False => false,
_ => null,
},
_ => null,
};
if (legacyValue.HasValue)
{
LegacyAdvancedAIEnabled = legacyValue.Value;
}
_extensionData.Remove("IsAdvancedAIEnabled");
}
}
}
private Dictionary<string, JsonElement> _extensionData;
private bool? _legacyAdvancedAIEnabled;
[JsonPropertyName("IsAdvancedAIEnabled")]
@@ -121,6 +79,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool CloseAfterLosingFocus { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool EnableClipboardPreview { get; set; }
[JsonPropertyName("advanced-paste-ui-hotkey")]
public HotkeySettings AdvancedPasteUIShortcut { get; set; }
@@ -145,7 +106,36 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[CmdConfigureIgnoreAttribute]
public PasteAIConfiguration PasteAIConfiguration { get; set; }
[JsonPropertyName("custom-model-storage-path")]
[CmdConfigureIgnoreAttribute]
public string CustomModelStoragePath { get; set; }
public override string ToString()
=> JsonSerializer.Serialize(this);
public static string GetDefaultCustomModelStoragePath()
{
try
{
var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (string.IsNullOrWhiteSpace(userProfile))
{
userProfile = Environment.GetEnvironmentVariable("USERPROFILE");
}
if (string.IsNullOrWhiteSpace(userProfile))
{
var temp = Path.GetTempPath();
return Path.Combine(temp, "PowerToys", "Models");
}
return Path.Combine(userProfile, ".cache", "PowerToys");
}
catch (Exception)
{
var temp = Path.GetTempPath();
return Path.Combine(temp, "PowerToys", "Models");
}
}
}
}

View File

@@ -20,8 +20,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
private string _activeProviderId = string.Empty;
private ObservableCollection<PasteAIProviderDefinition> _providers = new();
private bool _useSharedCredentials = true;
private Dictionary<string, AIProviderConfigurationSnapshot> _legacyProviderConfigurations;
public event PropertyChangedEventHandler PropertyChanged;
@@ -39,21 +37,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
set => SetProperty(ref _providers, value ?? new ObservableCollection<PasteAIProviderDefinition>());
}
[JsonPropertyName("use-shared-credentials")]
public bool UseSharedCredentials
{
get => _useSharedCredentials;
set => SetProperty(ref _useSharedCredentials, value);
}
[JsonPropertyName("provider-configurations")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, AIProviderConfigurationSnapshot> LegacyProviderConfigurations
{
get => _legacyProviderConfigurations;
set => _legacyProviderConfigurations = value;
}
[JsonIgnore]
public PasteAIProviderDefinition ActiveProvider
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View File

@@ -28,6 +28,8 @@
<None Remove="Assets\Settings\Icons\Models\WindowsML.svg" />
<None Remove="Assets\Settings\Modules\APDialog.dark.png" />
<None Remove="Assets\Settings\Modules\APDialog.light.png" />
<None Remove="Assets\Settings\Modules\CmdPal_Background.png" />
<None Remove="Assets\Settings\Modules\CmdPal_Hero.png" />
<None Remove="Assets\Settings\Modules\LightSwitch.png" />
<None Remove="SettingsXAML\Controls\Dashboard\CheckUpdateControl.xaml" />
<None Remove="SettingsXAML\Controls\Dashboard\ShortcutConflictControl.xaml" />
@@ -71,6 +73,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Labs.WinUI.Controls.OpacityMaskView" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />

View File

@@ -30,6 +30,24 @@
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<InfoBar
x:Uid="AdvancedPaste_FL_PreviewMessage"
Grid.Row="1"
Padding="4"
IsClosable="False"
IsOpen="True"
Message="Foundry Local is still in Public Preview">
<InfoBar.ActionButton>
<HyperlinkButton
x:Uid="AdvancedPaste_FL_LearnMoreFoundryLocal"
Content="Learn more"
NavigateUri="https://learn.microsoft.com/azure/ai-foundry/foundry-local/what-is-foundry-local" />
</InfoBar.ActionButton>
</InfoBar>
<StackPanel
x:Name="LoadingPanel"
HorizontalAlignment="Center"
@@ -42,8 +60,9 @@
HorizontalAlignment="Center" />
<TextBlock
x:Name="LoadingStatusTextBlock"
x:Uid="AdvancedPaste_FL_LoadingStatus"
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Loading Foundry Local status..."
TextAlignment="Center"
TextWrapping="Wrap" />
</StackPanel>
@@ -56,7 +75,6 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel
x:Name="NoModelsPanel"
Grid.Row="0"
@@ -64,24 +82,28 @@
HorizontalAlignment="Center"
Orientation="Vertical"
Spacing="4">
<FontIcon FontSize="24" Glyph="&#xE74E;" />
<FontIcon
AutomationProperties.AccessibilityView="Raw"
FontSize="24"
Glyph="&#xF158;" />
<TextBlock
x:Uid="AdvancedPaste_FL_NoModelsDownloaded."
HorizontalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="No models downloaded"
TextAlignment="Center" />
<TextBlock
x:Uid="AdvancedPaste_FL_RunFoundryLocalText.Text"
HorizontalAlignment="Center"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Run Foundry Local to download or add a local model below."
TextAlignment="Center"
TextWrapping="Wrap" />
<Button
x:Name="LaunchFoundryModelListButton"
x:Uid="AdvancedPaste_FL_OpenFoundryModelList"
Margin="0,8,0,0"
HorizontalAlignment="Center"
Click="LaunchFoundryModelListButton_Click"
Content="Open Foundry model list"
Style="{StaticResource AccentButtonStyle}" />
</StackPanel>
@@ -101,10 +123,10 @@
SelectionChanged="CachedModelsComboBox_SelectionChanged">
<ComboBox.Header>
<TextBlock>
<Run Text="Foundry Local model" /><LineBreak /><Run
<Run x:Uid="AdvancedPaste_FL_LocalModel" /><LineBreak /><Run
x:Uid="AdvancedPaste_FL_UseCliToDownloadModels"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Use the Foundry Local CLI to download models that run locally on-device. They'll appear here." />
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
</ComboBox.Header>
</ComboBox>
@@ -114,9 +136,11 @@
MinHeight="32"
VerticalAlignment="Bottom"
Click="RefreshModelsButton_Click"
Style="{StaticResource SubtleButtonStyle}"
ToolTipService.ToolTip="Refresh model list">
Style="{StaticResource SubtleButtonStyle}">
<FontIcon FontSize="16" Glyph="&#xE72C;" />
<ToolTipService.ToolTip>
<TextBlock x:Uid="AdvancedPaste_FL_RefreshModelList" />
</ToolTipService.ToolTip>
</Button>
</Grid>
<StackPanel
@@ -146,24 +170,28 @@
Spacing="8">
<Image Width="36" Source="ms-appx:///Assets/Settings/Icons/Models/FoundryLocal.svg" />
<TextBlock
x:Uid="AdvancedPaste_FL_FLNotAvailableYet"
HorizontalAlignment="Center"
FontWeight="SemiBold"
Text="Foundry Local is not available on this device yet."
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
x:Uid="AdvancedPaste_FL_StartService"
HorizontalAlignment="Center"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
TextAlignment="Center"
TextWrapping="Wrap">
<Run Text="Start the Foundry Local service before returning to PowerToys." />
</TextBlock>
<HyperlinkButton Content="Follow the Foundry Local CLI guide" NavigateUri="https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started" />
TextWrapping="Wrap" />
<HyperlinkButton
x:Uid="AdvancedPaste_FL_CLIGuide"
HorizontalAlignment="Center"
NavigateUri="https://learn.microsoft.com/azure/ai-foundry/foundry-local/get-started" />
<TextBlock
x:Uid="FoundryLocal_RestartRequiredNote"
HorizontalAlignment="Center"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Note: After installing the Foundry Local CLI, restart PowerToys to use it."
TextAlignment="Center"
TextWrapping="Wrap" />
</StackPanel>

View File

@@ -103,10 +103,7 @@
</StackPanel>
</tkcontrols:SettingsExpander.Description>
<tkcontrols:SettingsExpander.ItemsHeader>
<tkcontrols:SettingsCard
Description="Add online or local models"
Header="Model providers"
Style="{StaticResource DefaultSettingsExpanderItemStyle}">
<tkcontrols:SettingsCard x:Uid="AdvancedPaste_ModelProviders" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
<Button Content="Add model" Style="{StaticResource AccentButtonStyle}">
<Button.Flyout>
<MenuFlyout x:Name="AddProviderMenuFlyout" Opening="AddProviderMenuFlyout_Opening" />
@@ -130,16 +127,16 @@
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem
x:Uid="AdvancedPaste_Edit"
Click="EditPasteAIProviderButton_Click"
Icon="{ui:FontIcon Glyph=&#xE70F;}"
Tag="{x:Bind}"
Text="Edit" />
Tag="{x:Bind}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="AdvancedPaste_Remove"
Click="RemovePasteAIProviderButton_Click"
Icon="{ui:FontIcon Glyph=&#xE74D;}"
Tag="{x:Bind}"
Text="Remove" />
Tag="{x:Bind}" />
</MenuFlyout>
</Button.Flyout>
</Button>
@@ -171,6 +168,9 @@
</InfoBar>
</tkcontrols:SettingsExpander.ItemsHeader>
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard Name="AdvancedPasteEnableClipboardPreview" ContentAlignment="Left">
<controls:CheckBoxWithDescriptionControl x:Uid="AdvancedPaste_EnableClipboardPreview" IsChecked="{x:Bind ViewModel.EnableClipboardPreview, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
Name="AdvancedPasteClipboardHistoryEnabledSettingsCard"
ContentAlignment="Left"
@@ -381,6 +381,20 @@
</DataTemplate>
</tkcontrols:SettingsExpander.ItemTemplate>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsCard
x:Uid="AdvancedPaste_CustomModelStoragePath"
HeaderIcon="{ui:FontIcon Glyph=&#xE8B7;}"
IsEnabled="{x:Bind ViewModel.IsAIEnabled, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBox
Width="320"
Text="{x:Bind ViewModel.CustomModelStoragePath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button
x:Uid="AdvancedPaste_CustomModelStoragePath_BrowseButton"
Click="BrowseCustomModelStoragePath_Click"
Style="{ThemeResource AccentButtonStyle}" />
</StackPanel>
</tkcontrols:SettingsCard>
<InfoBar
x:Uid="AdvancedPaste_ShortcutWarning"
IsClosable="False"
@@ -423,18 +437,42 @@
AcceptsReturn="true"
Text="{Binding Prompt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" />
<ToggleSwitch
x:Uid="AdvancedPasteUI_CustomAction_UseCustomModel"
HorizontalAlignment="Left"
IsOn="{Binding UseCustomModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
OffContent=""
OnContent=""
Toggled="CustomActionUseCustomModel_Toggled" />
<StackPanel
Orientation="Vertical"
Spacing="8"
Visibility="{Binding UseCustomModel, Converter={StaticResource BoolToVisibilityConverter}}">
<ComboBox
x:Name="CustomActionModelComboBox"
x:Uid="AdvancedPasteUI_CustomAction_ModelPath"
Width="340"
HorizontalAlignment="Left"
ItemsSource="{x:Bind CustomModelOptions, Mode=OneWay}"
SelectedItem="{Binding CustomModelPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ToolTipService.ToolTip="{Binding CustomModelPath, Mode=OneWay}" />
<Button
x:Uid="AdvancedPasteUI_CustomAction_SelectModelButton"
HorizontalAlignment="Left"
Click="BrowseCustomActionModel_Click"
Style="{ThemeResource AccentButtonStyle}" />
</StackPanel>
</StackPanel>
</ContentDialog>
<!-- Paste AI provider dialog -->
<ContentDialog
x:Name="PasteAIProviderConfigurationDialog"
x:Uid="AdvancedPaste_EndpointDialog"
Title="Paste with AI provider configuration"
Closed="PasteAIProviderConfigurationDialog_Closed"
PrimaryButtonClick="PasteAIProviderConfigurationDialog_PrimaryButtonClick"
PrimaryButtonStyle="{ThemeResource AccentButtonStyle}"
PrimaryButtonText="Save"
SecondaryButtonText="Cancel">
PrimaryButtonStyle="{ThemeResource AccentButtonStyle}">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMaxWidth">900</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">700</x:Double>
@@ -496,46 +534,44 @@
Spacing="16">
<TextBox
x:Name="PasteAIModelNameTextBox"
x:Uid="AdvancedPaste_ModelName"
MinWidth="200"
HorizontalAlignment="Stretch"
Header="Model name"
PlaceholderText="gpt-4o"
Text="{x:Bind ViewModel.PasteAIProviderDraft.ModelName, Mode=TwoWay}" />
<TextBox
x:Name="PasteAIEndpointUrlTextBox"
x:Uid="AdvancedPaste_EndpointURL"
MinWidth="200"
HorizontalAlignment="Stretch"
Header="Endpoint URL"
PlaceholderText="https://your-resource.openai.azure.com/"
Text="{x:Bind ViewModel.PasteAIProviderDraft.EndpointUrl, Mode=TwoWay}" />
<PasswordBox
x:Name="PasteAIApiKeyPasswordBox"
MinWidth="200"
Header="API key"
PlaceholderText="Enter API Key" />
x:Uid="AdvancedPaste_APIKey"
MinWidth="200" />
<TextBox
x:Name="PasteAIApiVersionTextBox"
x:Uid="AdvancedPaste_APIVersion"
MinWidth="200"
HorizontalAlignment="Stretch"
Header="API version"
PlaceholderText="2024-10-01"
Text="{x:Bind ViewModel.PasteAIProviderDraft.ApiVersion, Mode=TwoWay}"
Visibility="Collapsed" />
<TextBox
x:Name="PasteAIDeploymentNameTextBox"
x:Uid="AdvancedPaste_DeploymentName"
MinWidth="200"
Header="Deployment name"
PlaceholderText="gpt-4o"
Text="{x:Bind ViewModel.PasteAIProviderDraft.DeploymentName, Mode=TwoWay}" />
<TextBox
x:Name="PasteAISystemPromptTextBox"
x:Uid="AdvancedPaste_SystemPrompt"
MinWidth="200"
MinHeight="76"
HorizontalAlignment="Stretch"
AcceptsReturn="True"
Header="System prompt"
PlaceholderText="You are tasked with reformatting user's clipboard data. Use the user's instructions, and the content of their clipboard below to edit their clipboard content as they have requested it. Do not output anything else besides the reformatted clipboard content."
Text="{x:Bind ViewModel.PasteAIProviderDraft.SystemPrompt, Mode=TwoWay}"
TextWrapping="Wrap" />
<Grid
@@ -575,13 +611,10 @@
IsOn="{x:Bind ViewModel.PasteAIProviderDraft.EnableAdvancedAI, Mode=TwoWay}"
Toggled="PasteAIEnableAdvancedAICheckBox_Toggled"
Visibility="Collapsed">
<ToolTipService.ToolTip>
<TextBlock Text="" TextWrapping="Wrap" />
</ToolTipService.ToolTip>
<ToggleSwitch.Header>
<TextBlock>
<Run Text="Enable Advanced AI" /> <LineBreak />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Use built-in functions to handle complex tasks. Token consumption may increase." />
<Run x:Uid="AdvancedPaste_EnableAdvancedAI" /> <LineBreak />
<Run x:Uid="AdvancedPaste_EnableAdvancedAIDescription" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
</ToggleSwitch.Header>
</ToggleSwitch>

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
@@ -27,7 +28,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
public sealed partial class AdvancedPastePage : NavigablePage, IRefreshablePage, IDisposable
{
private readonly ObservableCollection<ModelDetails> _foundryCachedModels = new();
private readonly ObservableCollection<FoundryDownloadableModel> _foundryDownloadableModels = new();
private CancellationTokenSource _foundryModelLoadCts;
private bool _suppressFoundrySelectionChanged;
private bool _isFoundryLocalAvailable;
@@ -36,11 +36,17 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private const string AdvancedAISystemPrompt = "You are an agent who is tasked with helping users paste their clipboard data. You have functions available to help you with this task. Call function when necessary to help user finish the transformation task. You never need to ask permission, always try to do as the user asks. The user will only input one message and will not be available for further questions, so try your best. The user will put in a request to format their clipboard data and you will fulfill it. Do not output anything else besides the reformatted clipboard content.";
private const string SimpleAISystemPrompt = "You are tasked with reformatting user's clipboard data. Use the user's instructions, and the content of their clipboard below to edit their clipboard content as they have requested it. Do not output anything else besides the reformatted clipboard content.";
private static readonly string AdvancedAISystemPromptNormalized = AdvancedAISystemPrompt.Trim();
private static readonly string SimpleAISystemPromptNormalized = SimpleAISystemPrompt.Trim();
private readonly ObservableCollection<string> _customModelOptions = new();
private AdvancedPasteViewModel ViewModel { get; set; }
public ICommand EnableAdvancedPasteAICommand => new RelayCommand(EnableAdvancedPasteAI);
public ObservableCollection<string> CustomModelOptions => _customModelOptions;
public AdvancedPastePage()
{
var settingsUtils = new SettingsUtils();
@@ -52,10 +58,11 @@ namespace Microsoft.PowerToys.Settings.UI.Views
DataContext = ViewModel;
InitializeComponent();
ViewModel.PropertyChanged += AdvancedPasteViewModel_PropertyChanged;
if (FoundryLocalPicker is not null)
{
FoundryLocalPicker.CachedModels = _foundryCachedModels;
FoundryLocalPicker.DownloadableModels = _foundryDownloadableModels;
FoundryLocalPicker.SelectionChanged += FoundryLocalPicker_SelectionChanged;
FoundryLocalPicker.LoadRequested += FoundryLocalPicker_LoadRequested;
}
@@ -133,6 +140,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
CustomActionDialog.Title = resourceLoader.GetString("AddCustomAction");
CustomActionDialog.DataContext = ViewModel.GetNewCustomAction(resourceLoader.GetString("AdvancedPasteUI_NewCustomActionPrefix"));
CustomActionDialog.PrimaryButtonText = resourceLoader.GetString("CustomActionSave");
RefreshCustomActionModelOptions(CustomActionDialog.DataContext as AdvancedPasteCustomAction);
await CustomActionDialog.ShowAsync();
}
@@ -143,6 +151,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
CustomActionDialog.Title = resourceLoader.GetString("EditCustomAction");
CustomActionDialog.DataContext = GetBoundCustomAction(sender, e).Clone();
CustomActionDialog.PrimaryButtonText = resourceLoader.GetString("CustomActionUpdate");
RefreshCustomActionModelOptions(CustomActionDialog.DataContext as AdvancedPasteCustomAction);
await CustomActionDialog.ShowAsync();
}
@@ -256,7 +265,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
if (!string.IsNullOrEmpty(selectedFile))
{
PasteAIModelPathTextBox.Text = selectedFile;
if (ViewModel?.PasteAIProviderDraft is not null)
{
ViewModel.PasteAIProviderDraft.ModelPath = selectedFile;
@@ -264,6 +272,37 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
}
private void BrowseCustomActionModel_Click(object sender, RoutedEventArgs e)
{
string selectedFile = PickFileDialog(
"Model Files\0*.onnx;*.zip;*.model\0All Files\0*.*\0",
"Select Model File",
ViewModel?.CustomModelStoragePath);
if (string.IsNullOrEmpty(selectedFile))
{
return;
}
if (CustomActionDialog?.DataContext is AdvancedPasteCustomAction action)
{
action.CustomModelPath = NormalizeCustomModelSelectionPath(selectedFile);
RefreshCustomActionModelOptions(action);
}
}
private void BrowseCustomModelStoragePath_Click(object sender, RoutedEventArgs e)
{
IntPtr hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow());
string selectedFolder = ShellGetFolder.GetFolderDialogWithFlags(hwnd, ShellGetFolder.FolderDialogFlags._BIF_NEWDIALOGSTYLE);
if (!string.IsNullOrEmpty(selectedFolder) && ViewModel is not null)
{
ViewModel.CustomModelStoragePath = selectedFolder;
RefreshCustomActionModelOptions(CustomActionDialog?.DataContext as AdvancedPasteCustomAction);
}
}
private static string PickFileDialog(string filter, string title, string initialDir = null, int initialFilter = 0)
{
// Use Win32 OpenFileName dialog as FileOpenPicker doesn't work with elevated permissions
@@ -293,6 +332,121 @@ namespace Microsoft.PowerToys.Settings.UI.Views
return null;
}
private string NormalizeCustomModelSelectionPath(string selectedFile)
{
if (string.IsNullOrWhiteSpace(selectedFile))
{
return string.Empty;
}
try
{
var fullSelection = Path.GetFullPath(selectedFile);
var storagePath = ViewModel?.CustomModelStoragePath;
if (!string.IsNullOrWhiteSpace(storagePath))
{
var fullStorage = Path.GetFullPath(storagePath);
if (fullSelection.StartsWith(fullStorage, StringComparison.OrdinalIgnoreCase))
{
var relative = Path.GetRelativePath(fullStorage, fullSelection);
if (!string.IsNullOrEmpty(relative) && !relative.StartsWith("..", StringComparison.Ordinal))
{
return relative;
}
}
}
return fullSelection;
}
catch (Exception)
{
return selectedFile;
}
}
private void RefreshCustomActionModelOptions(AdvancedPasteCustomAction action)
{
var options = new List<string>();
var storageRoot = GetCustomModelStorageRoot();
if (!string.IsNullOrWhiteSpace(storageRoot) && Directory.Exists(storageRoot))
{
try
{
foreach (var file in Directory.EnumerateFiles(storageRoot, "*.onnx", SearchOption.AllDirectories))
{
var option = NormalizeCustomModelSelectionPath(file);
if (!string.IsNullOrWhiteSpace(option))
{
options.Add(option);
}
}
}
catch (Exception)
{
// Enumeration failures are non-fatal; leave the list empty and allow manual selection.
}
}
if (action?.UseCustomModel == true && !string.IsNullOrWhiteSpace(action.CustomModelPath))
{
options.Add(action.CustomModelPath);
}
var uniqueOptions = options
.Where(option => !string.IsNullOrWhiteSpace(option))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(option => option, StringComparer.OrdinalIgnoreCase)
.ToList();
_customModelOptions.Clear();
foreach (var option in uniqueOptions)
{
_customModelOptions.Add(option);
}
if (action?.UseCustomModel == true && string.IsNullOrWhiteSpace(action.CustomModelPath) && _customModelOptions.Count > 0)
{
action.CustomModelPath = _customModelOptions[0];
}
}
private string GetCustomModelStorageRoot()
{
var path = ViewModel?.CustomModelStoragePath;
if (string.IsNullOrWhiteSpace(path))
{
return string.Empty;
}
try
{
path = Environment.ExpandEnvironmentVariables(path.Trim());
return Path.GetFullPath(path);
}
catch (Exception)
{
return path;
}
}
private void AdvancedPasteViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (string.Equals(e.PropertyName, nameof(AdvancedPasteViewModel.CustomModelStoragePath), StringComparison.Ordinal))
{
RefreshCustomActionModelOptions(CustomActionDialog?.DataContext as AdvancedPasteCustomAction);
}
}
private void CustomActionUseCustomModel_Toggled(object sender, RoutedEventArgs e)
{
if (CustomActionDialog?.DataContext is AdvancedPasteCustomAction action && action.UseCustomModel)
{
RefreshCustomActionModelOptions(action);
}
}
private void ShowApiKeySavedMessage(string configType)
{
// This would typically show a TeachingTip or InfoBar
@@ -467,7 +621,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
var cachedModels = cachedModelsEnumerable?.ToList() ?? new List<ModelDetails>();
UpdateFoundryCollections(cachedModels, []);
UpdateFoundryCollections(cachedModels);
ShowFoundryAvailableState();
RestoreFoundrySelection(cachedModels);
}
@@ -536,7 +690,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
UpdateFoundrySaveButtonState();
}
private void UpdateFoundryCollections(IReadOnlyCollection<ModelDetails> cachedModels, IReadOnlyCollection<ModelDetails> catalogModels)
private void UpdateFoundryCollections(IReadOnlyCollection<ModelDetails> cachedModels)
{
_foundryCachedModels.Clear();
@@ -545,20 +699,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
_foundryCachedModels.Add(model);
}
var cachedReferences = new HashSet<string>(_foundryCachedModels.Select(m => NormalizeFoundryModelReference(m.Url ?? m.Name)), StringComparer.OrdinalIgnoreCase);
_foundryDownloadableModels.Clear();
foreach (var model in catalogModels.OrderBy(m => m.Name, StringComparer.OrdinalIgnoreCase))
{
var reference = NormalizeFoundryModelReference(model.Url ?? model.Name);
if (cachedReferences.Contains(reference))
{
continue;
}
_foundryDownloadableModels.Add(new FoundryDownloadableModel(model));
}
var cachedReferences = new HashSet<string>(_foundryCachedModels.Select(m => m.Name), StringComparer.OrdinalIgnoreCase);
}
private void RestoreFoundrySelection(IReadOnlyCollection<ModelDetails> cachedModels)
@@ -574,9 +715,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
if (!string.IsNullOrWhiteSpace(currentModelReference))
{
var normalizedReference = NormalizeFoundryModelReference(currentModelReference);
matchingModel = cachedModels.FirstOrDefault(model =>
string.Equals(NormalizeFoundryModelReference(model.Url ?? model.Name), normalizedReference, StringComparison.OrdinalIgnoreCase));
string.Equals(model.Name, currentModelReference, StringComparison.OrdinalIgnoreCase));
}
if (FoundryLocalPicker is null)
@@ -606,7 +746,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{
if (ViewModel?.PasteAIProviderDraft is not null)
{
ViewModel.PasteAIProviderDraft.ModelName = NormalizeFoundryModelReference(matchingModel.Url ?? matchingModel.Name);
ViewModel.PasteAIProviderDraft.ModelName = matchingModel.Name;
}
if (FoundryLocalPicker is not null)
@@ -618,19 +758,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
UpdateFoundrySaveButtonState();
}
private static string NormalizeFoundryModelReference(string modelReference)
{
if (string.IsNullOrWhiteSpace(modelReference))
{
return string.Empty;
}
var prefix = FoundryLocalModelProvider.Instance.UrlPrefix;
return modelReference.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)
? modelReference
: $"{prefix}{modelReference}";
}
private void UpdateFoundrySaveButtonState()
{
if (PasteAIProviderConfigurationDialog is null)
@@ -654,7 +781,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
return;
}
if (!_isFoundryLocalAvailable || _foundryDownloadableModels.Any(model => model.IsDownloading))
if (!_isFoundryLocalAvailable)
{
PasteAIProviderConfigurationDialog.IsPrimaryButtonEnabled = false;
return;
@@ -675,7 +802,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{
if (ViewModel?.PasteAIProviderDraft is not null)
{
ViewModel.PasteAIProviderDraft.ModelName = NormalizeFoundryModelReference(selectedModel.Url ?? selectedModel.Name);
ViewModel.PasteAIProviderDraft.ModelName = selectedModel.Name;
}
if (FoundryLocalPicker is not null)
@@ -804,6 +931,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
return;
}
NormalizeSystemPrompt(draft);
string serviceType = draft.ServiceType ?? "OpenAI";
string apiKey = PasteAIApiKeyPasswordBox.Password;
string trimmedApiKey = apiKey?.Trim() ?? string.Empty;
@@ -834,22 +962,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
return;
}
bool isEmptyOrDefault = string.IsNullOrWhiteSpace(draft.SystemPrompt) ||
draft.SystemPrompt.Trim() == AdvancedAISystemPrompt.Trim() ||
draft.SystemPrompt.Trim() == SimpleAISystemPrompt.Trim();
if (isEmptyOrDefault)
{
if (!draft.EnableAdvancedAI)
{
// Now we'll switch
draft.SystemPrompt = AdvancedAISystemPrompt;
}
else
{
draft.SystemPrompt = SimpleAISystemPrompt;
}
}
NormalizeSystemPrompt(draft);
UpdateSystemPromptPlaceholder();
}
private static bool RequiresApiKeyForService(string serviceType)
@@ -950,15 +1064,47 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private Visibility GetServicePrivacyVisibility(string serviceType) => HasServicePrivacyLink(serviceType) ? Visibility.Visible : Visibility.Collapsed;
private void UpdateSystemPromptPlaceholder()
private static bool IsPlaceholderSystemPrompt(string prompt)
{
var draft = ViewModel?.PasteAIProviderDraft;
if (draft is null || PasteAISystemPromptTextBox is null)
if (string.IsNullOrWhiteSpace(prompt))
{
return true;
}
string trimmedPrompt = prompt.Trim();
return string.Equals(trimmedPrompt, AdvancedAISystemPromptNormalized, StringComparison.Ordinal)
|| string.Equals(trimmedPrompt, SimpleAISystemPromptNormalized, StringComparison.Ordinal);
}
private static void NormalizeSystemPrompt(PasteAIProviderDefinition draft)
{
if (draft is null)
{
return;
}
PasteAISystemPromptTextBox.PlaceholderText = draft.EnableAdvancedAI
if (IsPlaceholderSystemPrompt(draft.SystemPrompt))
{
draft.SystemPrompt = string.Empty;
}
}
private void UpdateSystemPromptPlaceholder()
{
var draft = ViewModel?.PasteAIProviderDraft;
if (draft is null)
{
return;
}
NormalizeSystemPrompt(draft);
if (PasteAISystemPromptTextBox is null)
{
return;
}
bool useAdvancedPlaceholder = PasteAIEnableAdvancedAICheckBox?.IsOn ?? draft.EnableAdvancedAI;
PasteAISystemPromptTextBox.PlaceholderText = useAdvancedPlaceholder
? AdvancedAISystemPrompt
: SimpleAISystemPrompt;
}
@@ -1000,7 +1146,11 @@ namespace Microsoft.PowerToys.Settings.UI.Views
FoundryLocalPicker.LoadRequested -= FoundryLocalPicker_LoadRequested;
}
ViewModel?.Dispose();
if (ViewModel is not null)
{
ViewModel.PropertyChanged -= AdvancedPasteViewModel_PropertyChanged;
ViewModel.Dispose();
}
_disposed = true;
GC.SuppressFinalize(this);

View File

@@ -10,40 +10,200 @@
xmlns:ui="using:CommunityToolkit.WinUI"
AutomationProperties.LandmarkType="Main"
mc:Ignorable="d">
<Grid>
<Grid
MaxWidth="1000"
Padding="16,0,16,0"
VerticalAlignment="Top"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="16"
RowSpacing="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<tkcontrols:OpacityMaskView Margin="-16,0,-16,0" HorizontalAlignment="Stretch">
<tkcontrols:OpacityMaskView.OpacityMask>
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Offset="0.50" Color="Black" />
<GradientStop Offset="0.75" Color="#80000000" />
<GradientStop Offset="0.95" Color="Transparent" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</tkcontrols:OpacityMaskView.OpacityMask>
<Grid Height="560">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image
Grid.RowSpan="3"
HorizontalAlignment="Stretch"
Source="/Assets/Settings/Modules/CmdPal_Background.png"
Stretch="UniformToFill" />
<TextBlock
Margin="0,24,0,12"
HorizontalAlignment="Center"
FontSize="36"
FontWeight="Bold"
Text="Command Palette">
<TextBlock.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0.0" Color="#FFB9EBFF" />
<GradientStop Offset="0.49" Color="#FF86CBFF" />
<GradientStop Offset="1.0" Color="#FFA1E7FF" />
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
Foreground="White"
TextAlignment="Center"
TextWrapping="Wrap">
<Run x:Uid="CmdPal_Description" />
<Hyperlink NavigateUri="">
<Run x:Uid="LearnMore_CmdPal.Text" Foreground="White" />
</Hyperlink>
</TextBlock>
<Image
Grid.Row="2"
Margin="0,16,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Source="/Assets/Settings/Modules/CmdPal_Hero.png"
Stretch="Uniform" />
</Grid>
</tkcontrols:OpacityMaskView>
<controls:SettingsPageControl x:Uid="CmdPal" ModuleImageSource="ms-appx:///Assets/Settings/Modules/CmdPal.png">
<controls:SettingsPageControl.ModuleContent>
<StackPanel ChildrenTransitions="{StaticResource SettingsCardsAnimations}" Orientation="Vertical">
<Grid
Grid.Row="1"
Margin="0,-12,0,24"
ColumnSpacing="32"
RowSpacing="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<FontIcon
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xEB3B;" />
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap">
<Run x:Uid="CmdPal_ExtensibleHeader" FontWeight="SemiBold" /> <LineBreak />
<Run
x:Uid="CmdPal_ExtensibleDescription"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
<FontIcon
Grid.Column="1"
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE945;" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap">
<Run x:Uid="CmdPal_FastHeader" FontWeight="SemiBold" /> <LineBreak />
<Run
x:Uid="CmdPal_FastDescription"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
<FontIcon
Grid.Column="2"
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE790;" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap">
<Run x:Uid="CmdPal_ModernHeader" FontWeight="SemiBold" /> <LineBreak />
<Run
x:Uid="CmdPal_ModernDescription"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
</Grid>
<StackPanel
Grid.Row="2"
Margin="0,8,0,0"
Orientation="Vertical"
Spacing="{StaticResource SettingsCardSpacing}">
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay}">
<tkcontrols:SettingsCard
Name="CmdPalEnableCmdPal"
x:Uid="CmdPal_Enable_CmdPal"
HorizontalAlignment="Stretch"
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/CmdPal.png}">
<ToggleSwitch IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</controls:GPOInfoControl>
<controls:SettingsGroup x:Uid="CmdPal_Activation_GroupSettings" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsCard
Name="CmdPalActivationShortcut"
x:Uid="CmdPal_ActivationShortcut"
HeaderIcon="{ui:FontIcon Glyph=&#xEDA7;}">
<controls:ShortcutControl
MinWidth="{StaticResource SettingActionControlMinWidth}"
HotkeySettings="{x:Bind Path=ViewModel.Hotkey, Mode=OneWay}"
IsEnabled="False" />
<tkcontrols:SettingsCard.Description>
<HyperlinkButton
x:Name="CmdPalSettingsDeeplink"
x:Uid="CmdPal_DeeplinkContent"
Click="CmdPalSettingsDeeplink_Click" />
</tkcontrols:SettingsCard.Description>
</tkcontrols:SettingsCard>
</controls:SettingsGroup>
<tkcontrols:SettingsCard
x:Uid="CmdPal_Launch"
Grid.Row="3"
ActionIcon="{ui:FontIcon Glyph=&#xE8A7;}"
Click="LaunchCard_Click"
Header="Launch Command Palette"
HeaderIcon="{ui:FontIcon Glyph=&#xE945;}"
IsClickEnabled="True"
IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<ItemsControl
AutomationProperties.AccessibilityView="Raw"
IsTabStop="False"
ItemsSource="{x:Bind Path=ViewModel.Hotkey.GetKeysList(), Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:KeyVisual
Padding="8,8,8,8"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Content="{Binding}"
Style="{StaticResource AccentKeyVisualStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="CmdPal_Settings"
Grid.Row="4"
ActionIcon="{ui:FontIcon Glyph=&#xE8A7;}"
Click="SettingsCard_Click"
HeaderIcon="{ui:FontIcon Glyph=&#xE713;}"
IsClickEnabled="True"
IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}" />
</StackPanel>
</controls:SettingsPageControl.ModuleContent>
<controls:SettingsPageControl.PrimaryLinks>
<controls:PageLink x:Uid="LearnMore_CmdPal" Link="https://aka.ms/PowerToysOverview_CmdPal" />
</controls:SettingsPageControl.PrimaryLinks>
</controls:SettingsPageControl>
</Grid>
</Grid>
</local:NavigablePage>

View File

@@ -63,12 +63,20 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
}
private void CmdPalSettingsDeeplink_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
private void SettingsCard_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
// Launch CmdPal settings window as normal user using explorer
string launchPath = "explorer.exe";
string launchArgs = "x-cmdpal://settings";
LaunchApp(launchPath, launchArgs);
}
private void LaunchCard_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
// Launch CmdPal window as normal user using explorer
string launchPath = "explorer.exe";
string launchArgs = "x-cmdpal";
LaunchApp(launchPath, launchArgs);
}
}
}

View File

@@ -199,10 +199,8 @@
<ContentDialog
x:Name="LocationDialog"
x:Uid="LightSwitch_LocationDialog"
Closed="LocationDialog_Closed"
IsPrimaryButtonEnabled="False"
IsSecondaryButtonEnabled="True"
Opened="LocationDialog_Opened"
PrimaryButtonClick="LocationDialog_PrimaryButtonClick"
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
<Grid RowSpacing="16">

View File

@@ -35,9 +35,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private readonly IFileSystemWatcher fileSystemWatcher;
private readonly DispatcherQueue dispatcherQueue;
private bool suppressViewModelUpdates;
private bool suppressLatLonChange = true;
private bool latBoxLoaded;
private bool lonBoxLoaded;
private LightSwitchViewModel ViewModel { get; set; }
@@ -132,8 +129,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
// Since we use this mode, we can remove the selected city data.
this.ViewModel.SelectedCity = null;
this.suppressLatLonChange = false;
// ViewModel.CityTimesText = $"Sunrise: {result.SunriseHour}:{result.SunriseMinute:D2}\n" + $"Sunset: {result.SunsetHour}:{result.SunsetMinute:D2}";
this.SyncButton.IsEnabled = true;
this.SyncLoader.IsActive = false;
@@ -157,23 +152,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views
private void LatLonBox_ValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args)
{
if (this.suppressLatLonChange)
{
return;
}
double latitude = this.LatitudeBox.Value;
double longitude = this.LongitudeBox.Value;
if (double.IsNaN(latitude) || double.IsNaN(longitude))
{
return;
}
double viewModelLatitude = double.TryParse(this.ViewModel.Latitude, out var lat) ? lat : 0.0;
double viewModelLongitude = double.TryParse(this.ViewModel.Longitude, out var lon) ? lon : 0.0;
if (Math.Abs(latitude - viewModelLatitude) < 0.0001 && Math.Abs(longitude - viewModelLongitude) < 0.0001)
if (double.IsNaN(latitude) || double.IsNaN(longitude) || (latitude == 0 && longitude == 0))
{
return;
}
@@ -183,7 +165,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
this.ViewModel.LocationPanelLightTimeMinutes = (result.SunriseHour * 60) + result.SunriseMinute;
this.ViewModel.LocationPanelDarkTimeMinutes = (result.SunsetHour * 60) + result.SunsetMinute;
// Show the panel with these values
this.LocationResultPanel.Visibility = Visibility.Visible;
if (this.LocationDialog != null)
{
@@ -214,37 +195,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
this.SunriseModeChartState();
}
private void LocationDialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args)
{
this.LatitudeBox.Loaded += LatLonBox_Loaded;
this.LongitudeBox.Loaded += LatLonBox_Loaded;
}
private void LocationDialog_Closed(ContentDialog sender, ContentDialogClosedEventArgs args)
{
this.LatitudeBox.Loaded -= LatLonBox_Loaded;
this.LongitudeBox.Loaded -= LatLonBox_Loaded;
this.latBoxLoaded = false;
this.lonBoxLoaded = false;
}
private void LatLonBox_Loaded(object sender, RoutedEventArgs e)
{
if (sender is NumberBox numberBox && numberBox == this.LatitudeBox && this.LatitudeBox.IsLoaded)
{
this.latBoxLoaded = true;
}
else if (sender is NumberBox numberBox2 && numberBox2 == this.LongitudeBox && this.LongitudeBox.IsLoaded)
{
this.lonBoxLoaded = true;
}
if (this.latBoxLoaded && this.lonBoxLoaded)
{
this.suppressLatLonChange = false;
}
}
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (this.suppressViewModelUpdates)

View File

@@ -283,6 +283,7 @@
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsCursorWrapEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</controls:GPOInfoControl>
<tkcontrols:SettingsExpander
Name="MouseUtilsCursorWrapSettingsExpander"
x:Uid="MouseUtils_CursorWrap_ActivationShortcut"
@@ -294,18 +295,7 @@
<tkcontrols:SettingsCard ContentAlignment="Left">
<CheckBox x:Uid="MouseUtils_AutoActivate" IsChecked="{x:Bind ViewModel.CursorWrapAutoActivate, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsExpander
Name="CursorWrapAppearanceBehavior"
x:Uid="Appearance_Behavior"
AutomationProperties.AutomationId="MouseUtils_CursorWrapAppearanceBehaviorId"
HeaderIcon="{ui:FontIcon Glyph=&#xEB3C;}"
IsEnabled="{x:Bind ViewModel.IsCursorWrapEnabled, Mode=OneWay}"
IsExpanded="False">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard ContentAlignment="Left">
<tkcontrols:SettingsCard ContentAlignment="Left" IsEnabled="{x:Bind ViewModel.IsCursorWrapEnabled, Mode=OneWay}">
<CheckBox x:Uid="MouseUtils_CursorWrap_DisableWrapDuringDrag" IsChecked="{x:Bind ViewModel.CursorWrapDisableWrapDuringDrag, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>

View File

@@ -285,6 +285,9 @@
AutomationProperties.AutomationId="InputOutputNavItem"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/InputOutput.png}"
SelectsOnInvoked="False">
<NavigationViewItem.InfoBadge>
<InfoBadge Style="{StaticResource NewInfoBadge}" />
</NavigationViewItem.InfoBadge>
<NavigationViewItem.MenuItems>
<NavigationViewItem
x:Name="KeyboardManagerNavigationItem"
@@ -299,7 +302,11 @@
x:Uid="Shell_MouseUtilities"
helpers:NavHelper.NavigateTo="views:MouseUtilsPage"
AutomationProperties.AutomationId="MouseUtilitiesNavItem"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}" />
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseUtils.png}">
<NavigationViewItem.InfoBadge>
<InfoBadge Style="{StaticResource NewInfoBadge}" />
</NavigationViewItem.InfoBadge>
</NavigationViewItem>
<NavigationViewItem
x:Name="MouseWithoutBordersNavigationItem"
x:Uid="Shell_MouseWithoutBorders"

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -644,17 +644,14 @@ Please review the placeholder content that represents the final terms and usage
<data name="AdvancedPaste_EnablePasteAIModerationToggle.Header" xml:space="preserve">
<value>Enable OpenAI content moderation</value>
</data>
<data name="AdvancedPaste_EnableAdvancedAI_SettingsCard.Header" xml:space="preserve">
<value>Enable Advanced AI</value>
</data>
<data name="AdvancedPaste_EnableAdvancedAI_SettingsCard.Description" xml:space="preserve">
<data name="AdvancedPaste_EnableAdvancedAIDescription.Text" xml:space="preserve">
<value>Use built-in functions to handle complex tasks. Token consumption may increase.</value>
</data>
<data name="AdvancedPaste_Clipboard_History_Enabled_SettingsCard.Header" xml:space="preserve">
<value>Access Clipboard History</value>
</data>
<data name="AdvancedPaste_Clipboard_History_Enabled_SettingsCard.Description" xml:space="preserve">
<value>Clipboard History shows a list of previously copied items.</value>
<value>View and select previously copied items</value>
</data>
<data name="AdvancedPaste_Direct_Access_Hotkeys_GroupSettings.Header" xml:space="preserve">
<value>Actions</value>
@@ -662,6 +659,22 @@ Please review the placeholder content that represents the final terms and usage
<data name="AdvancedPaste_Additional_Actions_GroupSettings.Header" xml:space="preserve">
<value>Custom actions</value>
</data>
<data name="AdvancedPaste_CustomModelStoragePath.Header" xml:space="preserve">
<value>Custom model storage path</value>
<comment>Header for the settings card that lets users choose where custom models are stored.</comment>
</data>
<data name="AdvancedPaste_CustomModelStoragePath.Description" xml:space="preserve">
<value>PowerToys looks in this folder when custom actions use locally downloaded models.</value>
</data>
<data name="AdvancedPaste_CustomModelStoragePath_BrowseButton.Content" xml:space="preserve">
<value>Browse</value>
</data>
<data name="AdvancedPasteCustomModelNotFound" xml:space="preserve">
<value>The custom model could not be found. Verify the path and try again.</value>
</data>
<data name="AdvancedPasteCustomModelInferenceError" xml:space="preserve">
<value>PowerToys couldn't generate a response using the selected custom model.</value>
</data>
<data name="AdvancedPaste_FoundryLocal_LegalDescription" xml:space="preserve">
<value>You're running local models directly on your device. Their behavior may vary or be unpredictable.</value>
</data>
@@ -2047,6 +2060,20 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<data name="AdvancedPasteUI_CustomAction_Prompt.Header" xml:space="preserve">
<value>Prompt</value>
</data>
<data name="AdvancedPasteUI_CustomAction_UseCustomModel.Header" xml:space="preserve">
<value>Use custom model</value>
<comment>Toggle allowing the user to choose a locally stored model for the custom action.</comment>
</data>
<data name="AdvancedPasteUI_CustomAction_ModelPath.Header" xml:space="preserve">
<value>Model path</value>
<comment>Label for the text box showing the selected custom model path.</comment>
</data>
<data name="AdvancedPasteUI_CustomAction_ModelPath.PlaceholderText" xml:space="preserve">
<value>Select a local model file</value>
</data>
<data name="AdvancedPasteUI_CustomAction_SelectModelButton.Content" xml:space="preserve">
<value>Choose model</value>
</data>
<data name="CustomActionDialog.SecondaryButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
@@ -2688,10 +2715,7 @@ From there, simply click on one of the supported files in the File Explorer and
<value>Wrap the mouse cursor between monitor edges</value>
</data>
<data name="MouseUtils_CursorWrap_ActivationShortcut.Header" xml:space="preserve">
<value>Activation shortcut</value>
</data>
<data name="MouseUtils_CursorWrap_ActivationShortcut_Description.Text" xml:space="preserve">
<value>Hotkey to toggle cursor wrapping on/off</value>
<value>Activation and behavior</value>
</data>
<data name="MouseUtils_CursorWrap_ActivationShortcut_Button.Content" xml:space="preserve">
<value>Set shortcut</value>
@@ -4058,11 +4082,8 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="AdvancedPaste_ShowCustomPreviewSettingsCard.Description" xml:space="preserve">
<value>Preview the output of AI formats and Image to text before pasting</value>
</data>
<data name="AdvancedPaste_EnableAdvancedAI.Header" xml:space="preserve">
<value>Advanced AI</value>
</data>
<data name="AdvancedPaste_EnableAdvancedAI.Description" xml:space="preserve">
<value>Supports advanced workflows by chaining transformations and working with files and images. May use additional API credits.</value>
<data name="AdvancedPaste_EnableAdvancedAI.Text" xml:space="preserve">
<value>Enable Advanced AI</value>
</data>
<data name="Oobe_AdvancedPaste.Description" xml:space="preserve">
<value>Advanced Paste is a tool to put your clipboard content into any format you need, focused towards developer workflows. It can paste as plain text, markdown, or json directly with the UX or with a direct keystroke invoke. These are fully locally executed. In addition, it has an AI powered option that is 100% opt-in and requires an Open AI key. Note: this will replace the formatted text in your clipboard with the selected format.</value>
@@ -4604,9 +4625,13 @@ Activate by holding the key for the character you want to add an accent to, then
<value>If you do not have credits you will see an 'API key quota exceeded' error</value>
</data>
<data name="AdvancedPaste_CloseAfterLosingFocus.Content" xml:space="preserve">
<value>Automatically close the Advanced Paste window after it loses focus</value>
<value>Automatically close the window after it loses focus</value>
<comment>Advanced Paste is a product name, do not loc</comment>
</data>
<data name="AdvancedPaste_EnableClipboardPreview.Header" xml:space="preserve">
<value>Show clipboard preview</value>
<comment>Enables display of clipboard contents preview in the Advanced Paste window</comment>
</data>
<data name="GPO_CommandNotFound_ForceDisabled.Title" xml:space="preserve">
<value>The Command Not Found module is disabled by your organization.</value>
<comment>"Command Not Found" is a product name</comment>
@@ -5164,25 +5189,12 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="Shell_TopLevelSystemTools.Content" xml:space="preserve">
<value>System Tools</value>
</data>
<data name="CmdPal.ModuleTitle" xml:space="preserve">
<value>Command Palette</value>
</data>
<data name="CmdPal_ShortDescription" xml:space="preserve">
<value>A better quick launcher</value>
</data>
<data name="CmdPal_ActivationDescription" xml:space="preserve">
<value>Open Command Palette</value>
</data>
<data name="CmdPal_Enable_CmdPal.Header" xml:space="preserve">
<value>Enable Command Palette</value>
<comment>"Command Palette" is the name of the utility.</comment>
</data>
<data name="CmdPal.ModuleDescription" xml:space="preserve">
<value>A fully extensible quick launcher with a richer display and additional capabilities without sacrificing performance.</value>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="LearnMore_CmdPal.Text" xml:space="preserve">
<value>Learn more about Command Palette</value>
<comment>Command Palette is a product name, do not loc</comment>
<value>Learn more</value>
</data>
<data name="Shell_CmdPal.Content" xml:space="preserve">
<value>Command Palette</value>
@@ -5190,11 +5202,11 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
</data>
<data name="Oobe_CmdPal.Description" xml:space="preserve">
<value>A fully extensible quick launcher with a richer display and additional capabilities without sacrificing performance.</value>
<comment>"Command Palette" is a product name</comment>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="Oobe_CmdPal.Title" xml:space="preserve">
<value>Command Palette</value>
<comment>"Command Palette" is a product name</comment>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="Oobe_CmdPal_HowToUse.Text" xml:space="preserve">
<value>and start typing!</value>
@@ -5227,14 +5239,8 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="RetryLabel.Text" xml:space="preserve">
<value>Retry</value>
</data>
<data name="CmdPal_Activation_GroupSettings.Header" xml:space="preserve">
<value>Activation</value>
</data>
<data name="CmdPal_ActivationShortcut.Header" xml:space="preserve">
<value>Activation shortcut</value>
</data>
<data name="CmdPal_DeeplinkContent.Content" xml:space="preserve">
<value>Open Command Palette settings to customize the activation shortcut</value>
<data name="CmdPal_Settings.Header" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Help_chromaCIE" xml:space="preserve">
<value>chroma (CIE LCh)</value>
@@ -5679,4 +5685,130 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="LightSwitch_SetLocationButton.Content" xml:space="preserve">
<value>Set Location</value>
</data>
<data name="AdvancedPaste_FL_OpenFoundryModelList.Content" xml:space="preserve">
<value>Open Foundry Local model list</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_FL_RunFoundryLocalText.Text" xml:space="preserve">
<value>Run Foundry Local to download or add a local model</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_FL_NoModelsDownloaded.Text" xml:space="preserve">
<value>No models downloaded</value>
</data>
<data name="AdvancedPaste_FL_LoadingStatus.Text" xml:space="preserve">
<value>Loading Foundry Local status..</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_FL_LocalModel.Text" xml:space="preserve">
<value>Foundry Local model</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_FL_UseCliToDownloadModels.Text" xml:space="preserve">
<value>Use the Foundry Local CLI to download models that run locally on-device. They'll appear here.</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_FL_RefreshModelList.Text" xml:space="preserve">
<value>Refresh model list</value>
</data>
<data name="AdvancedPaste_FL_FLNotAvailableYet.Text" xml:space="preserve">
<value>Foundry Local is not available on this device yet.</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_FL_StartService.Text" xml:space="preserve">
<value>Start the Foundry Local service before returning to PowerToys.</value>
</data>
<data name="AdvancedPaste_FL_CLIGuide.Content" xml:space="preserve">
<value>Follow the Foundry Local CLI guide</value>
<comment>Do not localize "Foundry Local", it's a product name</comment>
</data>
<data name="AdvancedPaste_ModelProviders.Header" xml:space="preserve">
<value>Model providers</value>
</data>
<data name="AdvancedPaste_ModelProviders.Description" xml:space="preserve">
<value>Add online or local models</value>
</data>
<data name="AdvancedPaste_Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="AdvancedPaste_Remove.Text" xml:space="preserve">
<value>Remove</value>
</data>
<data name="AdvancedPaste_ModelName.Header" xml:space="preserve">
<value>Model name</value>
</data>
<data name="AdvancedPaste_EndpointURL.Header" xml:space="preserve">
<value>Endpoint URL</value>
</data>
<data name="AdvancedPaste_APIKey.Header" xml:space="preserve">
<value>API key</value>
</data>
<data name="AdvancedPaste_APIKey.PlaceholderText" xml:space="preserve">
<value>Enter API key</value>
</data>
<data name="AdvancedPaste_APIVersion.Header" xml:space="preserve">
<value>API version</value>
</data>
<data name="AdvancedPaste_DeploymentName.Header" xml:space="preserve">
<value>Deployment name</value>
</data>
<data name="AdvancedPaste_SystemPrompt.Header" xml:space="preserve">
<value>System prompt</value>
</data>
<data name="AdvancedPaste_EndpointDialog.PrimaryButtonText" xml:space="preserve">
<value>Save</value>
</data>
<data name="AdvancedPaste_EndpointDialog.SecondaryButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="AdvancedPaste_EnableClipboardPreview.Description" xml:space="preserve">
<value>Display a preview of the current clipboard content</value>
</data>
<data name="AdvancedPaste_FL_LearnMoreFoundryLocal.Content" xml:space="preserve">
<value>Learn more</value>
</data>
<data name="AdvancedPaste_FL_PreviewMessage.Message" xml:space="preserve">
<value>Foundry Local is still in public preview</value>
<comment>Do not loc "Foundry Local"</comment>
</data>
<data name="CmdPal_Settings.Description" xml:space="preserve">
<value>Configure the activation shortcut, extensions, behavior and much more</value>
</data>
<data name="CmdPal_Launch.Header" xml:space="preserve">
<value>Open Command Palette</value>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="CmdPal_ShortDescription" xml:space="preserve">
<value>A better quick launcher</value>
</data>
<data name="CmdPal_Description.Text" xml:space="preserve">
<value>Find files, launch apps, and do so much more with the most extensible quick launcher.</value>
</data>
<data name="CmdPal.ModuleTitle" xml:space="preserve">
<value>Command Palette</value>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="CmdPal_ActivationDescription" xml:space="preserve">
<value>Open Command Palette</value>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="CmdPal_ExtensibleDescription.Text" xml:space="preserve">
<value>Powerful extensions help you do more</value>
</data>
<data name="CmdPal_ExtensibleHeader.Text" xml:space="preserve">
<value>Extensible</value>
</data>
<data name="CmdPal_FastDescription.Text" xml:space="preserve">
<value>Find files and launch apps in an instant</value>
</data>
<data name="CmdPal_FastHeader.Text" xml:space="preserve">
<value>Fast</value>
</data>
<data name="CmdPal_ModernHeader.Text" xml:space="preserve">
<value>Beautiful</value>
</data>
<data name="CmdPal_ModernDescription.Text" xml:space="preserve">
<value>A modern UI built with Fluent Design</value>
<comment>Fluent Design is a product name, do not loc</comment>
</data>
</root>

View File

@@ -8,6 +8,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Runtime.Versioning;
@@ -98,6 +99,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
SetupSettingsFileWatcher();
InitializePasteAIProviderState();
EnsureCustomModelStoragePathInitialized();
InitializeEnabledValue();
MigrateLegacyAIEnablement();
@@ -191,6 +193,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return;
}
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
if (legacyCredential is null)
{
if (legacyAdvancedAIConsumed)
{
SaveAndNotifySettings();
}
return;
}
var configuration = properties.PasteAIConfiguration;
if (configuration is null)
{
@@ -198,28 +212,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
properties.PasteAIConfiguration = configuration;
}
bool hasLegacyProviders = configuration.LegacyProviderConfigurations is { Count: > 0 };
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
if (!hasLegacyProviders && legacyCredential is null && !legacyAdvancedAIConsumed)
{
return;
}
bool configurationUpdated = false;
if (hasLegacyProviders)
{
configurationUpdated |= AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(configuration);
}
PasteAIProviderDefinition openAIProvider = null;
if (legacyCredential is not null || hasLegacyProviders || legacyAdvancedAIConsumed)
{
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
openAIProvider = ensureResult.Provider;
configurationUpdated |= ensureResult.Updated;
}
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
PasteAIProviderDefinition openAIProvider = ensureResult.Provider;
configurationUpdated |= ensureResult.Updated;
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
{
@@ -233,10 +230,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
RemoveLegacyOpenAICredential();
}
const bool shouldEnableAI = true;
bool enabledChanged = false;
if (!properties.IsAIEnabled && legacyCredential is not null)
if (properties.IsAIEnabled != shouldEnableAI)
{
properties.IsAIEnabled = true;
properties.IsAIEnabled = shouldEnableAI;
enabledChanged = true;
}
@@ -546,6 +544,37 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public string CustomModelStoragePath
{
get => _advancedPasteSettings.Properties.CustomModelStoragePath;
set
{
var normalized = NormalizeDirectoryPath(value);
var current = NormalizeDirectoryPath(_advancedPasteSettings.Properties.CustomModelStoragePath);
if (!string.Equals(current, normalized, StringComparison.OrdinalIgnoreCase))
{
_advancedPasteSettings.Properties.CustomModelStoragePath = normalized;
EnsureDirectoryExists(normalized);
OnPropertyChanged(nameof(CustomModelStoragePath));
SaveAndNotifySettings();
}
}
}
public bool EnableClipboardPreview
{
get => _advancedPasteSettings.Properties.EnableClipboardPreview;
set
{
if (value != _advancedPasteSettings.Properties.EnableClipboardPreview)
{
_advancedPasteSettings.Properties.EnableClipboardPreview = value;
NotifySettingsChanged();
}
}
}
public bool IsConflictingCopyShortcut =>
_customActions.Select(customAction => customAction.Shortcut)
.Concat([PasteAsPlainTextShortcut, AdvancedPasteUIShortcut, PasteAsMarkdownShortcut, PasteAsJsonShortcut])
@@ -1204,6 +1233,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
OnPropertyChanged(nameof(CloseAfterLosingFocus));
}
if (!string.Equals(NormalizeDirectoryPath(target.CustomModelStoragePath), NormalizeDirectoryPath(source.CustomModelStoragePath), StringComparison.OrdinalIgnoreCase))
{
target.CustomModelStoragePath = NormalizeDirectoryPath(source.CustomModelStoragePath);
EnsureDirectoryExists(target.CustomModelStoragePath);
OnPropertyChanged(nameof(CustomModelStoragePath));
}
if (target.EnableClipboardPreview != source.EnableClipboardPreview)
{
target.EnableClipboardPreview = source.EnableClipboardPreview;
OnPropertyChanged(nameof(EnableClipboardPreview));
}
var incomingConfig = source.PasteAIConfiguration ?? new PasteAIConfiguration();
if (ShouldReplacePasteAIConfiguration(target.PasteAIConfiguration, incomingConfig))
{
@@ -1228,11 +1270,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return true;
}
if (current.UseSharedCredentials != incoming.UseSharedCredentials)
{
return true;
}
var currentProviders = current.Providers ?? new ObservableCollection<PasteAIProviderDefinition>();
var incomingProviders = incoming.Providers ?? new ObservableCollection<PasteAIProviderDefinition>();
@@ -1388,8 +1425,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return;
}
if (string.Equals(e.PropertyName, nameof(PasteAIConfiguration.ActiveProviderId), StringComparison.Ordinal)
|| string.Equals(e.PropertyName, nameof(PasteAIConfiguration.UseSharedCredentials), StringComparison.Ordinal))
if (string.Equals(e.PropertyName, nameof(PasteAIConfiguration.ActiveProviderId), StringComparison.Ordinal))
{
SaveAndNotifySettings();
}
@@ -1406,15 +1442,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
pasteConfig.Providers ??= new ObservableCollection<PasteAIProviderDefinition>();
bool configurationUpdated = AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(pasteConfig);
SubscribeToPasteAIProviders(pasteConfig);
if (configurationUpdated)
{
SaveAndNotifySettings();
OnPropertyChanged(nameof(PasteAIConfiguration));
}
}
private static string RetrieveCredentialValue(string credentialResource, string credentialUserName)
@@ -1446,5 +1474,59 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
customAction.CanMoveDown = index != _customActions.Count - 1;
}
}
private void EnsureCustomModelStoragePathInitialized()
{
var path = NormalizeDirectoryPath(_advancedPasteSettings.Properties.CustomModelStoragePath);
if (string.IsNullOrWhiteSpace(path))
{
path = NormalizeDirectoryPath(AdvancedPasteProperties.GetDefaultCustomModelStoragePath());
_advancedPasteSettings.Properties.CustomModelStoragePath = path;
SaveAndNotifySettings();
}
EnsureDirectoryExists(path);
OnPropertyChanged(nameof(CustomModelStoragePath));
}
private static string NormalizeDirectoryPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return string.Empty;
}
try
{
path = path.Trim();
if (path.Length == 0)
{
return string.Empty;
}
path = Environment.ExpandEnvironmentVariables(path);
return Path.GetFullPath(path);
}
catch (Exception)
{
return path;
}
}
private static void EnsureDirectoryExists(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return;
}
try
{
Directory.CreateDirectory(path);
}
catch (Exception)
{
}
}
}
}

View File

@@ -145,6 +145,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
IsNew = moduleType == ModuleType.CursorWrap,
DashboardModuleItems = GetModuleItems(moduleType),
};
newItem.EnabledChangedCallback = EnabledChangedOnUI;

View File

@@ -562,7 +562,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
SyncButtonInformation = SelectedCity != null
? SelectedCity.City
: $"{Latitude},{Longitude}";
: $"{Latitude}°,{Longitude}°";
double lat = double.Parse(ModuleSettings.Properties.Latitude.Value, CultureInfo.InvariantCulture);
double lon = double.Parse(ModuleSettings.Properties.Longitude.Value, CultureInfo.InvariantCulture);

View File

@@ -24,6 +24,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class ZoomItViewModel : Observable
{
private const string FormatGif = "GIF";
private const string FormatMp4 = "MP4";
private ISettingsUtils SettingsUtils { get; set; }
private GeneralSettings GeneralSettingsConfig { get; set; }
@@ -656,12 +659,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
get
{
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
if (_zoomItSettings.Properties.RecordFormat.Value == FormatGif)
{
return 0;
}
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
if (_zoomItSettings.Properties.RecordFormat.Value == FormatMp4)
{
return 1;
}
@@ -672,19 +675,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
set
{
int format = 0;
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
if (_zoomItSettings.Properties.RecordFormat.Value == FormatGif)
{
format = 0;
}
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
if (_zoomItSettings.Properties.RecordFormat.Value == FormatMp4)
{
format = 1;
}
if (format != value)
{
_zoomItSettings.Properties.RecordFormat.Value = value == 0 ? "GIF" : "MP4";
_zoomItSettings.Properties.RecordFormat.Value = value == 0 ? FormatGif : FormatMp4;
OnPropertyChanged(nameof(RecordFormatIndex));
NotifySettingsChanged();

View File

@@ -30,6 +30,7 @@
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>../../../src/</AdditionalIncludeDirectories>
<TreatAngleIncludeAsExternal>true</TreatAngleIncludeAsExternal>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -37,10 +38,6 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c">
<!-- Disabling warnings for external code -->
<DisableSpecificWarnings>4706;26451;4267;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="EventViewer.cpp" />
<ClCompile Include="InstallationFolder.cpp" />
<ClCompile Include="Package.cpp" />
@@ -61,8 +58,6 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
<ClInclude Include="EventViewer.h" />
<ClInclude Include="InstallationFolder.h" />
<ClInclude Include="Package.h" />

View File

@@ -8,7 +8,6 @@
<ClCompile Include="ZipTools\ZipFolder.cpp">
<Filter>ZipTools</Filter>
</ClCompile>
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c" />
<ClCompile Include="ReportMonitorInfo.cpp" />
<ClCompile Include="RegistryUtils.cpp" />
<ClCompile Include="EventViewer.cpp" />
@@ -28,8 +27,6 @@
<Filter>ZipTools</Filter>
</ClInclude>
<ClInclude Include="..\..\..\common\utils\json.h" />
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
<ClInclude Include="ReportMonitorInfo.h" />
<ClInclude Include="RegistryUtils.h" />
<ClInclude Include="EventViewer.h" />

View File

@@ -1,50 +1,53 @@
#include "ZipFolder.h"
#include "..\..\..\..\deps\cziplib\src\zip.h"
#include <common/utils/timeutil.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <format>
#include <wil/stl.h>
#include <wil/win32_helpers.h>
void ZipFolder(std::filesystem::path zipPath, std::filesystem::path folderPath)
{
std::string reportFilename{ "PowerToysReport_" };
reportFilename += timeutil::format_as_local("%F-%H-%M-%S", timeutil::now());
reportFilename += ".zip";
const auto reportFilename{
std::format("PowerToysReport_{0}.zip",
timeutil::format_as_local("%F-%H-%M-%S", timeutil::now()))
};
const auto finalReportFullPath{ zipPath / reportFilename };
auto tmpZipPath = std::filesystem::temp_directory_path();
tmpZipPath /= reportFilename;
const auto tempReportFilename{ reportFilename + ".tmp" };
const auto tempReportFullPath{ zipPath / tempReportFilename };
struct zip_t* zip = zip_open(tmpZipPath.string().c_str(), ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
if (!zip)
// tar -c --format=zip -f "ReportFile.zip" *
const auto executable{ wil::ExpandEnvironmentStringsW<std::wstring>(LR"(%WINDIR%\System32\tar.exe)") };
auto commandline{ std::format(LR"("{0}" -c --format=zip -f "{1}" *)", executable, tempReportFullPath.wstring()) };
const auto folderPathAsString{ folderPath.lexically_normal().wstring() };
wil::unique_process_information pi;
STARTUPINFOW si{ .cb = sizeof(STARTUPINFOW) };
if (!CreateProcessW(executable.c_str(),
commandline.data() /* must be mutable */,
nullptr,
nullptr,
FALSE,
DETACHED_PROCESS,
nullptr,
folderPathAsString.c_str(),
&si,
&pi))
{
printf("Cannot open zip.");
throw -1;
}
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
const size_t rootSize = folderPath.wstring().size();
for (const auto& dirEntry : recursive_directory_iterator(folderPath))
{
if (dirEntry.is_regular_file())
{
auto path = dirEntry.path().string();
auto relativePath = path.substr(rootSize, path.size());
zip_entry_open(zip, relativePath.c_str());
zip_entry_fwrite(zip, path.c_str());
zip_entry_close(zip);
}
}
WaitForSingleObject(pi.hProcess, INFINITE);
zip_close(zip);
std::error_code err;
std::filesystem::copy(tmpZipPath, zipPath, err);
std::error_code err{};
std::filesystem::rename(tempReportFullPath, finalReportFullPath, err);
if (err.value() != 0)
{
wprintf_s(L"Failed to copy %s. Error code: %d\n", tmpZipPath.c_str(), err.value());
wprintf_s(L"Failed to rename %s. Error code: %d\n", tempReportFullPath.native().c_str(), err.value());
}
err = {};
std::filesystem::remove_all(tmpZipPath, err);
if (err.value() != 0)
{
wprintf_s(L"Failed to delete %s. Error code: %d\n", tmpZipPath.c_str(), err.value());
}
}
}