Compare commits

..

14 Commits

Author SHA1 Message Date
Michael Jolley
449c4ec95c More of the service stuff 2025-11-15 17:03:12 -06:00
Michael Jolley
0873dcf341 broken services 2025-11-14 16:50:36 -06: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
63 changed files with 641 additions and 998 deletions

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,7 +291,6 @@
"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

@@ -7,7 +7,6 @@
<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" />

View File

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

1
deps/cziplib vendored

Submodule deps/cziplib deleted from 81314fff0a

View File

@@ -42,8 +42,7 @@
Description="PowerToys OCR Module"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png"
AppListEntry="none">
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.SettingsUI" Executable="WinUI3Apps\PowerToys.Settings.exe" EntryPoint="Windows.FullTrustApplication">
@@ -52,8 +51,7 @@
Description="PowerToys Settings UI"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png"
AppListEntry="none">
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
@@ -62,8 +60,7 @@
Description="PowerToys Image Resizer UI"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png"
AppListEntry="none">
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
</Applications>

View File

@@ -10,23 +10,6 @@ namespace LanguageModelProvider.FoundryLocal;
internal sealed class FoundryClient
{
public static async Task<FoundryClient?> CreateAsync()
{
// First attempt with current environment
var client = await TryCreateClientAsync().ConfigureAwait(false);
if (client != null)
{
return client;
}
// If failed, refresh PATH from registry and retry once
// This handles cases where PowerToys was launched by MSI installer.
Logger.LogInfo("[FoundryClient] First attempt failed, refreshing PATH and retrying");
RefreshEnvironmentPath();
return await TryCreateClientAsync().ConfigureAwait(false);
}
private static async Task<FoundryClient?> TryCreateClientAsync()
{
try
{
@@ -186,23 +169,41 @@ internal sealed class FoundryClient
public async Task<bool> EnsureModelLoaded(string modelId)
{
Logger.LogInfo($"[FoundryClient] EnsureModelLoaded called with: {modelId}");
// Check if already loaded
if (await IsModelLoaded(modelId).ConfigureAwait(false))
try
{
Logger.LogInfo($"[FoundryClient] Model already loaded: {modelId}");
return true;
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;
}
catch (Exception ex)
{
Logger.LogError($"[FoundryClient] EnsureModelLoaded exception: {ex.Message}");
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;
}
public async Task EnsureRunning()
@@ -212,68 +213,4 @@ internal sealed class FoundryClient
await _foundryManager.StartServiceAsync();
}
}
/// <summary>
/// Refreshes the PATH environment variable from the system registry.
/// This is necessary when tools are installed while PowerToys is running,
/// as the installer updates the system PATH but running processes don't see the change.
/// </summary>
private static void RefreshEnvironmentPath()
{
try
{
Logger.LogInfo("[FoundryClient] Refreshing PATH environment variable from system");
var currentPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) ?? string.Empty;
var machinePath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine) ?? string.Empty;
var userPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User) ?? string.Empty;
var pathsToAdd = new List<string>();
if (!string.IsNullOrWhiteSpace(currentPath))
{
pathsToAdd.AddRange(currentPath.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries));
}
if (!string.IsNullOrWhiteSpace(userPath))
{
var userPaths = userPath.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
foreach (var path in userPaths)
{
if (!pathsToAdd.Contains(path, StringComparer.OrdinalIgnoreCase))
{
pathsToAdd.Add(path);
}
}
}
if (!string.IsNullOrWhiteSpace(machinePath))
{
var machinePaths = machinePath.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
foreach (var path in machinePaths)
{
if (!pathsToAdd.Contains(path, StringComparer.OrdinalIgnoreCase))
{
pathsToAdd.Add(path);
}
}
}
var newPath = string.Join(Path.PathSeparator.ToString(), pathsToAdd);
if (currentPath != newPath)
{
Logger.LogInfo("[FoundryClient] Updating process PATH with latest system values");
Environment.SetEnvironmentVariable("PATH", newPath, EnvironmentVariableTarget.Process);
}
else
{
Logger.LogInfo("[FoundryClient] PATH is already up to date");
}
}
catch (Exception ex)
{
Logger.LogError($"[FoundryClient] Failed to refresh PATH: {ex.Message}");
}
}
}

View File

@@ -12,8 +12,8 @@ namespace LanguageModelProvider;
public sealed class FoundryLocalModelProvider : ILanguageModelProvider
{
private IEnumerable<ModelDetails>? _downloadedModels;
private FoundryClient? _foundryClient;
private IEnumerable<FoundryCatalogModel>? _catalogModels;
private string? _serviceUrl;
public static FoundryLocalModelProvider Instance { get; } = new();
@@ -24,8 +24,22 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
public IChatClient? GetIChatClient(string modelId)
{
Logger.LogInfo($"[FoundryLocal] GetIChatClient called with url: {modelId}");
InitializeAsync().GetAwaiter().GetResult();
try
{
Logger.LogInfo($"[FoundryLocal] GetIChatClient called with url: {modelId}");
InitializeAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Logger.LogError($"[FoundryLocal] Failed to initialize: {ex.Message}");
return null;
}
if (string.IsNullOrWhiteSpace(_serviceUrl) || _foundryClient == null)
{
Logger.LogError("[FoundryLocal] Service URL or manager is null");
return null;
}
if (string.IsNullOrWhiteSpace(modelId))
{
@@ -33,38 +47,39 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
return null;
}
// 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);
}
// Ensure the model is loaded before returning chat client
var isLoaded = _foundryClient!.EnsureModelLoaded(modelId).GetAwaiter().GetResult();
if (!isLoaded)
try
{
Logger.LogError($"[FoundryLocal] Failed to load model: {modelId}");
throw new InvalidOperationException($"Failed to load the model '{modelId}'.");
var isLoaded = _foundryClient.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;
}
// Use ServiceUri instead of Endpoint since Endpoint already includes /v1
var baseUri = _foundryClient.GetServiceUri();
if (baseUri == 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);
Logger.LogError("[FoundryLocal] Service URI is null");
return null;
}
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"),
new OpenAIClientOptions { Endpoint = endpointUri, NetworkTimeout = TimeSpan.FromMinutes(5) })
new OpenAIClientOptions { Endpoint = endpointUri })
.GetChatClient(modelId)
.AsIChatClient();
}
@@ -90,16 +105,49 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
return $"new OpenAIClient(new ApiKeyCredential(\"none\"), new OpenAIClientOptions{{ Endpoint = new Uri(\"{_serviceUrl}/v1\") }}).GetChatClient(\"{modelId}\").AsIChatClient()";
}
public async Task<IEnumerable<ModelDetails>> GetModelsAsync(CancellationToken cancelationToken = default)
public async Task<IEnumerable<ModelDetails>> GetModelsAsync(bool ignoreCached = false, CancellationToken cancelationToken = default)
{
if (ignoreCached)
{
Logger.LogInfo("[FoundryLocal] Ignoring cached models, resetting");
Reset();
}
await InitializeAsync(cancelationToken);
Logger.LogInfo($"[FoundryLocal] Returning {_downloadedModels?.Count() ?? 0} downloaded models");
return _downloadedModels ?? [];
}
private void Reset()
{
_downloadedModels = null;
_ = InitializeAsync();
}
private async Task InitializeAsync(CancellationToken cancelationToken = default)
{
if (_foundryClient != null && _downloadedModels != null && _downloadedModels.Any())
{
await _foundryClient.EnsureRunning().ConfigureAwait(false);
return;
}
Logger.LogInfo("[FoundryLocal] Initializing provider");
_foundryClient ??= await FoundryClient.CreateAsync();
if (_foundryClient == null)
{
return Array.Empty<ModelDetails>();
Logger.LogError("[FoundryLocal] Failed to create Foundry client");
return;
}
_serviceUrl ??= await _foundryClient.GetServiceUrl();
Logger.LogInfo($"[FoundryLocal] Service URL: {_serviceUrl}");
var cachedModels = await _foundryClient.ListCachedModels();
Logger.LogInfo($"[FoundryLocal] Found {cachedModels.Count} cached models");
List<ModelDetails> downloadedModels = [];
foreach (var model in cachedModels)
@@ -112,37 +160,13 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
Url = $"fl://{model.Name}",
Description = $"{model.Name} running locally with Foundry Local",
HardwareAccelerators = [HardwareAccelerator.FOUNDRYLOCAL],
SupportedOnQualcomm = true,
ProviderModelDetails = model,
});
}
return downloadedModels;
}
private async Task InitializeAsync(CancellationToken cancelationToken = default)
{
if (_foundryClient != null && _catalogModels != null && _catalogModels.Any())
{
await _foundryClient.EnsureRunning().ConfigureAwait(false);
return;
}
Logger.LogInfo("[FoundryLocal] Initializing provider");
_foundryClient ??= await FoundryClient.CreateAsync();
if (_foundryClient == null)
{
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 _foundryClient.GetServiceUrl();
Logger.LogInfo($"[FoundryLocal] Service URL: {_serviceUrl}");
var catalogModels = await _foundryClient.ListCatalogModels();
Logger.LogInfo($"[FoundryLocal] Found {catalogModels.Count} catalog models");
_catalogModels = catalogModels;
_downloadedModels = downloadedModels;
Logger.LogInfo($"[FoundryLocal] Initialization complete. Total downloaded models: {downloadedModels.Count}");
}
public async Task<bool> IsAvailable()

View File

@@ -12,7 +12,7 @@ public interface ILanguageModelProvider
string ProviderDescription { get; }
Task<IEnumerable<ModelDetails>> GetModelsAsync(CancellationToken cancelationToken = default);
Task<IEnumerable<ModelDetails>> GetModelsAsync(bool ignoreCached = false, CancellationToken cancelationToken = default);
IChatClient? GetIChatClient(string modelId);

View File

@@ -24,6 +24,8 @@ public class ModelDetails
public List<HardwareAccelerator> HardwareAccelerators { get; set; } = [];
public bool SupportedOnQualcomm { get; set; }
public string License { get; set; } = string.Empty;
public object? ProviderModelDetails { get; set; }

View File

@@ -558,7 +558,7 @@
<TextBlock
x:Uid="AIProvidersFlyoutHeader"
Grid.Row="0"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
Style="{StaticResource BodyStrongTextBlockStyle}" />
<ListView
x:Name="AIProviderListView"
Grid.Row="1"

View File

@@ -299,49 +299,47 @@
</StackPanel>
</controls:PromptBox.Footer>
</controls:PromptBox>
<ScrollViewer Grid.Row="2">
<Grid RowSpacing="4">
<Grid.RowDefinitions>
<RowDefinition Height="{x:Bind ViewModel.StandardPasteFormats.Count, Mode=OneWay, Converter={StaticResource standardPasteFormatsToHeightConverter}}" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" MinHeight="{x:Bind ViewModel.CustomActionPasteFormats.Count, Mode=OneWay, Converter={StaticResource customActionsToMinHeightConverter}}" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView
x:Name="PasteOptionsListView"
Grid.Row="0"
VerticalAlignment="Bottom"
IsItemClickEnabled="True"
ItemClick="PasteFormat_ItemClick"
ItemContainerTransitions="{x:Null}"
ItemTemplateSelector="{StaticResource PasteFormatTemplateSelector}"
ItemsSource="{x:Bind ViewModel.StandardPasteFormats, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
SelectionMode="None"
TabIndex="1" />
<Rectangle
Grid.Row="1"
Height="1"
HorizontalAlignment="Stretch"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}"
Visibility="{x:Bind ViewModel.CustomActionPasteFormats.Count, Mode=OneWay, Converter={StaticResource countToVisibilityConverter}}" />
<Grid Grid.Row="2" RowSpacing="4">
<Grid.RowDefinitions>
<RowDefinition Height="{x:Bind ViewModel.StandardPasteFormats.Count, Mode=OneWay, Converter={StaticResource standardPasteFormatsToHeightConverter}}" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" MinHeight="{x:Bind ViewModel.CustomActionPasteFormats.Count, Mode=OneWay, Converter={StaticResource customActionsToMinHeightConverter}}" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView
x:Name="PasteOptionsListView"
Grid.Row="0"
VerticalAlignment="Bottom"
IsItemClickEnabled="True"
ItemClick="PasteFormat_ItemClick"
ItemContainerTransitions="{x:Null}"
ItemTemplateSelector="{StaticResource PasteFormatTemplateSelector}"
ItemsSource="{x:Bind ViewModel.StandardPasteFormats, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="None"
TabIndex="1" />
<Rectangle
Grid.Row="1"
Height="1"
HorizontalAlignment="Stretch"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}"
Visibility="{x:Bind ViewModel.CustomActionPasteFormats.Count, Mode=OneWay, Converter={StaticResource countToVisibilityConverter}}" />
<ListView
x:Name="CustomActionsListView"
Grid.Row="2"
VerticalAlignment="Top"
IsItemClickEnabled="True"
ItemClick="PasteFormat_ItemClick"
ItemContainerTransitions="{x:Null}"
ItemTemplateSelector="{StaticResource PasteFormatTemplateSelector}"
ItemsSource="{x:Bind ViewModel.CustomActionPasteFormats, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
SelectionMode="None"
TabIndex="2" />
</Grid>
</ScrollViewer>
<ListView
x:Name="CustomActionsListView"
Grid.Row="2"
VerticalAlignment="Top"
IsItemClickEnabled="True"
ItemClick="PasteFormat_ItemClick"
ItemContainerTransitions="{x:Null}"
ItemTemplateSelector="{StaticResource PasteFormatTemplateSelector}"
ItemsSource="{x:Bind ViewModel.CustomActionPasteFormats, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="None"
TabIndex="2" />
</Grid>
</Grid>
</Page>

View File

@@ -215,6 +215,7 @@ public sealed class AdvancedAIKernelService : KernelServiceBase
return new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(),
Temperature = 0.01,
};
}
}

View File

@@ -7,7 +7,6 @@ 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;
@@ -34,6 +33,10 @@ 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();
@@ -73,20 +76,13 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
}
cancellationToken.ThrowIfCancellationRequested();
IChatClient chatClient;
try
var chatClient = _modelProvider.GetIChatClient(modelReference);
if (chatClient is null)
{
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);
$"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.");
}
var userMessageContent = $"""
@@ -146,7 +142,6 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
var options = new ChatOptions
{
ModelId = modelReference,
MaxOutputTokens = 2048,
};
if (!string.IsNullOrWhiteSpace(systemPrompt))

View File

@@ -157,6 +157,8 @@ namespace AdvancedPaste.Services.CustomActions
{
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings
{
Temperature = 0.01,
MaxTokens = 2000,
FunctionChoiceBehavior = null,
},
_ => new PromptExecutionSettings(),

View File

@@ -160,10 +160,10 @@
<value>Active provider: {0}</value>
</data>
<data name="AIProvidersFlyoutHeader.Text" xml:space="preserve">
<value>Configured models</value>
<value>AI providers</value>
</data>
<data name="AIProvidersEmptyText.Text" xml:space="preserve">
<value>No models configured</value>
<value>No AI providers configured</value>
</data>
<data name="AIProvidersManageButtonContent.Content" xml:space="preserve">
<value>Configure models in Settings</value>
@@ -364,12 +364,8 @@
<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">
<data name="LocalModelBadge" 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

@@ -271,6 +271,7 @@ 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,10 +17,12 @@ 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;
}
@@ -31,6 +33,7 @@ void LightSwitchStateManager::OnSettingsChanged()
void LightSwitchStateManager::OnTick(int currentMinutes)
{
std::lock_guard<std::mutex> lock(_stateMutex);
Logger::debug(L"[LightSwitchStateManager] Tick received: {}", currentMinutes);
EvaluateAndApplyIfNeeded();
}
@@ -48,7 +51,7 @@ void LightSwitchStateManager::OnManualOverride()
_state.isAppsLightActive = GetCurrentAppsTheme();
Logger::debug(L"[LightSwitchStateManager] Synced internal theme state to current system theme ({}) and apps theme ({}).",
Logger::info(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"));
}
@@ -76,9 +79,9 @@ void LightSwitchStateManager::SyncInitialThemeState()
std::lock_guard<std::mutex> lock(_stateMutex);
_state.isSystemLightActive = GetCurrentSystemTheme();
_state.isAppsLightActive = GetCurrentAppsTheme();
Logger::debug(L"[LightSwitchStateManager] Synced initial state to current system theme ({})",
Logger::info(L"[LightSwitchStateManager] Synced initial state to current system theme ({})",
_state.isSystemLightActive ? L"light" : L"dark");
Logger::debug(L"[LightSwitchStateManager] Synced initial state to current apps theme ({})",
Logger::info(L"[LightSwitchStateManager] Synced initial state to current apps theme ({})",
_state.isAppsLightActive ? L"light" : L"dark");
}
@@ -124,6 +127,7 @@ 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;
}
@@ -141,6 +145,7 @@ 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;
@@ -183,10 +188,12 @@ 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;
}
@@ -199,7 +206,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,
@@ -208,12 +215,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))
@@ -223,6 +230,10 @@ 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

@@ -39,10 +39,6 @@ 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; // We default to 30 here, but g_RecordFrameRate can be different depending on recording format and gets set accordingly
DWORD g_RecordFrameRate = 30;
DWORD g_RecordScaling = 100;
DWORD g_RecordScalingGIF = 50;
DWORD g_RecordScalingMP4 = 100;
RecordingFormat g_RecordingFormat = RecordingFormat::MP4;
RecordingFormat g_RecordingFormat = RecordingFormat::GIF;
BOOLEAN g_CaptureAudio = FALSE;
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
@@ -87,7 +87,8 @@ 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"RecordingFormat", SETTING_TYPE_DWORD, 0, &g_RecordingFormat, static_cast<DOUBLE>(g_RecordingFormat) },
{ 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"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,7 +168,6 @@ 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;
@@ -2174,10 +2173,7 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO,
g_CaptureAudio ? BST_CHECKED: BST_UNCHECKED );
//
// 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++) {
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),
@@ -2186,7 +2182,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;
@@ -2349,8 +2345,17 @@ 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
@@ -3531,16 +3536,7 @@ void StopRecording()
//----------------------------------------------------------------------------
auto GetUniqueRecordingFilename()
{
std::filesystem::path path;
if (g_RecordingFormat == RecordingFormat::GIF)
{
path = g_RecordingSaveLocationGIF;
}
else
{
path = g_RecordingSaveLocation;
}
std::filesystem::path path{ g_RecordingSaveLocation };
// Chop off index if it's there
auto base = std::regex_replace( path.stem().wstring(), std::wregex( L" [(][0-9]+[)]$" ), L"" );
@@ -3595,7 +3591,6 @@ 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(
@@ -3662,44 +3657,18 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
saveDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
}
// 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)) {
if( g_RecordingSaveLocation.size() == 0) {
wil::com_ptr<IShellItem> shellItem;
wil::unique_cotaskmem_string folderPath;
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;
}
}
}
if (SUCCEEDED(saveDialog->GetFolder(shellItem.put())) && SUCCEEDED(shellItem->GetDisplayName(SIGDN_FILESYSPATH, folderPath.put())))
g_RecordingSaveLocation = folderPath.get();
}
// 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() );
@@ -3727,15 +3696,9 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
}
else {
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);
}
co_await file.MoveAndReplaceAsync( destFile );
g_RecordingSaveLocation = file.Path();
SaveToClipboard(g_RecordingSaveLocation.c_str(), hWnd);
}
g_bSaveInProgress = false;
@@ -4076,10 +4039,8 @@ 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
@@ -6371,17 +6332,6 @@ 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);

View File

@@ -350,7 +350,7 @@ namespace Awake.Core
TrayHelper.TimedIcon,
TrayIconAction.Update);
},
() => HandleTimerCompletion("timed"),
_ => HandleTimerCompletion("timed"),
_tokenSource.Token);
}

View File

@@ -64,8 +64,6 @@ 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;
@@ -144,7 +142,6 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext
return; // throw?
}
Id = page.Id;
Name = page.Name;
ModelIsLoading = page.IsLoading;
Title = page.Title;

View File

@@ -16,7 +16,6 @@ using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.CmdPal.UI.ViewModels.MainPage;
@@ -38,8 +37,10 @@ public partial class MainListPage : DynamicListPage,
"com.microsoft.cmdpal.builtin.datetime",
];
private readonly IServiceProvider _serviceProvider;
private readonly TopLevelCommandManager _tlcManager;
private readonly SettingsModel _settings;
private readonly AliasManager _aliasManager;
private readonly AppStateModel _appState;
private List<Scored<IListItem>>? _filteredItems;
private List<Scored<IListItem>>? _filteredApps;
private List<Scored<IListItem>>? _fallbackItems;
@@ -53,17 +54,18 @@ public partial class MainListPage : DynamicListPage,
private CancellationTokenSource? _cancellationTokenSource;
public MainListPage(IServiceProvider serviceProvider)
public MainListPage(TopLevelCommandManager topLevelCommandManager, SettingsModel settingsModel, AliasManager aliasManager, AppStateModel appStateModel)
{
Title = Resources.builtin_home_name;
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.scale-200.png");
PlaceholderText = Properties.Resources.builtin_main_list_page_searchbar_placeholder;
_serviceProvider = serviceProvider;
_tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
_tlcManager = topLevelCommandManager;
_tlcManager.PropertyChanged += TlcManager_PropertyChanged;
_tlcManager.TopLevelCommands.CollectionChanged += Commands_CollectionChanged;
_appState = appStateModel;
// The all apps page will kick off a BG thread to start loading apps.
// We just want to know when it is done.
var allApps = AllAppsCommandProvider.Page;
@@ -78,12 +80,13 @@ public partial class MainListPage : DynamicListPage,
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
var settings = _serviceProvider.GetService<SettingsModel>()!;
settings.SettingsChanged += SettingsChangedHandler;
HotReloadSettings(settings);
_settings = settingsModel;
_settings.SettingsChanged += SettingsChangedHandler;
HotReloadSettings(_settings);
_includeApps = _tlcManager.IsProviderActive(AllAppsCommandProvider.WellKnownId);
IsLoading = true;
_aliasManager = aliasManager;
}
private void TlcManager_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
@@ -219,14 +222,12 @@ public partial class MainListPage : DynamicListPage,
// Handle changes to the filter text here
if (!string.IsNullOrEmpty(SearchText))
{
var aliases = _serviceProvider.GetService<AliasManager>()!;
if (token.IsCancellationRequested)
{
return;
}
if (aliases.CheckAlias(newSearch))
if (_aliasManager.CheckAlias(newSearch))
{
if (_filteredItemsIncludesApps != _includeApps)
{
@@ -388,7 +389,7 @@ public partial class MainListPage : DynamicListPage,
}
}
var history = _serviceProvider.GetService<AppStateModel>()!.RecentCommands!;
var history = _appState.RecentCommands!;
Func<string, IListItem, int> scoreItem = (a, b) => { return ScoreTopLevelItem(a, b, history); };
// Produce a list of everything that matches the current filter.
@@ -483,9 +484,8 @@ public partial class MainListPage : DynamicListPage,
private bool ActuallyLoading()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
var allApps = AllAppsCommandProvider.Page;
return allApps.IsLoading || tlcManager.IsLoading;
return allApps.IsLoading || _tlcManager.IsLoading;
}
// Almost verbatim ListHelpers.ScoreListItem, but also accounting for the
@@ -580,10 +580,9 @@ public partial class MainListPage : DynamicListPage,
public void UpdateHistory(IListItem topLevelOrAppItem)
{
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
var state = _serviceProvider.GetService<AppStateModel>()!;
var history = state.RecentCommands;
var history = _appState.RecentCommands;
history.AddHistoryItem(id);
AppStateModel.SaveState(state);
AppStateModel.SaveState(_appState);
}
private static string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
@@ -615,10 +614,9 @@ public partial class MainListPage : DynamicListPage,
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
var settings = _serviceProvider.GetService<SettingsModel>();
if (settings is not null)
if (_settings is not null)
{
settings.SettingsChanged -= SettingsChangedHandler;
_settings.SettingsChanged -= SettingsChangedHandler;
}
WeakReferenceMessenger.Default.UnregisterAll(this);

View File

@@ -9,16 +9,14 @@ using Microsoft.CmdPal.Core.Common.Services;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Properties;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ProviderSettingsViewModel(
CommandProviderWrapper _provider,
ProviderSettings _providerSettings,
IServiceProvider _serviceProvider) : ObservableObject
SettingsModel _settings) : ObservableObject
{
private readonly SettingsModel _settings = _serviceProvider.GetService<SettingsModel>()!;
private readonly Lock _initializeSettingsLock = new();
private Task? _initializeSettingsTask;

View File

@@ -5,14 +5,13 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using Microsoft.CmdPal.UI.ViewModels.Settings;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.CmdPal.UI.ViewModels;
public partial class SettingsViewModel : INotifyPropertyChanged
{
private readonly SettingsModel _settings;
private readonly IServiceProvider _serviceProvider;
private readonly TopLevelCommandManager _tlcManager;
public event PropertyChangedEventHandler? PropertyChanged;
@@ -142,10 +141,10 @@ public partial class SettingsViewModel : INotifyPropertyChanged
public SettingsExtensionsViewModel Extensions { get; }
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler)
public SettingsViewModel(SettingsModel settings, TaskScheduler scheduler, TopLevelCommandManager topLevelCommandManager)
{
_settings = settings;
_serviceProvider = serviceProvider;
_tlcManager = topLevelCommandManager;
var activeProviders = GetCommandProviders();
var allProviderSettings = _settings.ProviderSettings;
@@ -154,7 +153,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
{
var providerSettings = settings.GetProviderSettings(item);
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _serviceProvider);
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _settings);
CommandProviders.Add(settingsModel);
}
@@ -163,8 +162,7 @@ public partial class SettingsViewModel : INotifyPropertyChanged
private IEnumerable<CommandProviderWrapper> GetCommandProviders()
{
var manager = _serviceProvider.GetService<TopLevelCommandManager>()!;
var allProviders = manager.CommandProviders;
var allProviders = _tlcManager.CommandProviders;
return allProviders;
}

View File

@@ -34,10 +34,10 @@ public partial class TopLevelCommandManager : ObservableObject,
TaskScheduler IPageContext.Scheduler => _taskScheduler;
public TopLevelCommandManager(IServiceProvider serviceProvider)
public TopLevelCommandManager(IServiceProvider serviceProvider, TaskScheduler taskScheduler)
{
_serviceProvider = serviceProvider;
_taskScheduler = _serviceProvider.GetService<TaskScheduler>()!;
_taskScheduler = taskScheduler;
WeakReferenceMessenger.Default.Register<ReloadCommandsMessage>(this);
_reloadCommandsGate = new(ReloadAllCommandsAsyncCore);
}

View File

@@ -23,6 +23,8 @@ using Microsoft.CmdPal.Ext.WindowsTerminal;
using Microsoft.CmdPal.Ext.WindowWalker;
using Microsoft.CmdPal.Ext.WinGet;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Pages;
using Microsoft.CmdPal.UI.Settings;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
using Microsoft.CmdPal.UI.ViewModels.Models;
@@ -51,10 +53,7 @@ public partial class App : Application
public ETWTrace EtwTrace { get; private set; } = new ETWTrace();
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
private readonly ServiceProvider _services;
/// <summary>
/// Initializes a new instance of the <see cref="App"/> class.
@@ -67,7 +66,7 @@ public partial class App : Application
_globalErrorHandler.Register(this);
#endif
Services = ConfigureServices();
_services = ConfigureServices();
this.InitializeComponent();
@@ -94,7 +93,7 @@ public partial class App : Application
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
AppWindow = new MainWindow();
AppWindow = _services.GetRequiredService<MainWindow>();
var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);
@@ -110,6 +109,11 @@ public partial class App : Application
// Root services
services.AddSingleton(TaskScheduler.FromCurrentSynchronizationContext());
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
services.AddSingleton<ITelemetryService, TelemetryForwarder>();
services.AddSingleton<IPageViewModelFactoryService, CommandPalettePageViewModelFactory>();
// TODO: Register ILogger
// Built-in Commands. Order matters - this is the order they'll be presented by default.
var allApps = new AllAppsCommandProvider();
@@ -152,25 +156,35 @@ public partial class App : Application
services.AddSingleton<ICommandProvider, TimeDateCommandsProvider>();
services.AddSingleton<ICommandProvider, SystemCommandExtensionProvider>();
// Models
services.AddSingleton<TopLevelCommandManager>();
services.AddSingleton<AliasManager>();
services.AddSingleton<HotkeyManager>();
// Settings & state
var sm = SettingsModel.LoadSettings();
services.AddSingleton(sm);
var state = AppStateModel.LoadState();
services.AddSingleton(state);
// Services
services.AddSingleton<TopLevelCommandManager>();
services.AddSingleton<AliasManager>();
services.AddSingleton<HotkeyManager>();
services.AddSingleton<IExtensionService, ExtensionService>();
services.AddSingleton<TrayIconService>();
services.AddSingleton<IRunHistoryService, RunHistoryService>();
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
services.AddSingleton<ITelemetryService, TelemetryForwarder>();
// ViewModels
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
services.AddSingleton<SettingsViewModel>();
services.AddSingleton<ShellViewModel>();
services.AddSingleton<IPageViewModelFactoryService, CommandPalettePageViewModelFactory>();
// Views
services.AddSingleton<MainWindow>();
services.AddSingleton<ShellPage>();
services.AddTransient<ListPage>();
// Settings Pages
services.AddTransient<GeneralPage>();
services.AddTransient<ExtensionPage>();
services.AddTransient<ExtensionsPage>();
return services.BuildServiceProvider();
}

View File

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

View File

@@ -11,7 +11,6 @@ using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
@@ -31,6 +30,7 @@ public sealed partial class ListPage : Page,
IRecipient<ActivateSelectedListItemMessage>,
IRecipient<ActivateSecondaryCommandMessage>
{
private readonly SettingsModel _settings;
private InputSource _lastInputSource;
internal ListViewModel? ViewModel
@@ -51,9 +51,11 @@ public sealed partial class ListPage : Page,
}
}
public ListPage()
public ListPage(SettingsModel settings)
{
this.InitializeComponent();
_settings = settings;
this.NavigationCacheMode = NavigationCacheMode.Disabled;
this.ItemView.Loaded += Items_Loaded;
this.ItemView.PreviewKeyDown += Items_PreviewKeyDown;
@@ -133,8 +135,7 @@ public sealed partial class ListPage : Page,
return;
}
var settings = App.Current.Services.GetService<SettingsModel>()!;
if (settings.SingleClickActivates)
if (_settings.SingleClickActivates)
{
ViewModel?.InvokeItemCommand.Execute(item);
}
@@ -150,8 +151,7 @@ public sealed partial class ListPage : Page,
{
if (ItemView.SelectedItem is ListItemViewModel vm)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
if (!settings.SingleClickActivates)
if (!_settings.SingleClickActivates)
{
ViewModel?.InvokeItemCommand.Execute(vm);
}

View File

@@ -20,7 +20,7 @@ namespace Microsoft.CmdPal.UI.Helpers;
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_*")]
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "Stylistically, window messages are WM_*")]
internal sealed partial class TrayIconService
public sealed partial class TrayIconService
{
private const uint MY_NOTIFY_ID = 1000;
private const uint WM_TRAY_ICON = PInvoke.WM_USER + 1;

View File

@@ -13,6 +13,6 @@
MinHeight="240"
Activated="MainWindow_Activated"
Closed="MainWindow_Closed"
x:Name="Window"
mc:Ignorable="d">
<pages:ShellPage x:Name="RootShellPage" />
</winuiex:WindowEx>

View File

@@ -14,9 +14,9 @@ using Microsoft.CmdPal.Ext.ClipboardHistory.Messages;
using Microsoft.CmdPal.UI.Events;
using Microsoft.CmdPal.UI.Helpers;
using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.Pages;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Composition;
using Microsoft.UI.Composition.SystemBackdrops;
@@ -58,6 +58,10 @@ public sealed partial class MainWindow : WindowEx,
private readonly KeyboardListener _keyboardListener;
private readonly LocalKeyboardListener _localKeyboardListener;
private readonly HiddenOwnerWindowBehavior _hiddenOwnerBehavior = new();
private readonly SettingsModel _settings;
private readonly TrayIconService _trayIconService;
private readonly IExtensionService _extensionService;
private readonly ShellPage _rootShellPage;
private bool _ignoreHotKeyWhenFullScreen = true;
private DesktopAcrylicController? _acrylicController;
@@ -65,10 +69,17 @@ public sealed partial class MainWindow : WindowEx,
private WindowPosition _currentWindowPosition = new();
public MainWindow()
public MainWindow(SettingsModel settingsModel, TrayIconService trayIconService, IExtensionService extensionService, ShellPage shellPage)
{
InitializeComponent();
_settings = settingsModel;
_trayIconService = trayIconService;
_extensionService = extensionService;
_rootShellPage = shellPage;
Window.WindowContent = _rootShellPage;
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
unsafe
@@ -102,7 +113,7 @@ public sealed partial class MainWindow : WindowEx,
ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
SizeChanged += WindowSizeChanged;
RootShellPage.Loaded += RootShellPage_Loaded;
_rootShellPage.Loaded += RootShellPage_Loaded;
WM_TASKBAR_RESTART = PInvoke.RegisterWindowMessage("TaskbarCreated");
@@ -116,10 +127,10 @@ public sealed partial class MainWindow : WindowEx,
// Load our settings, and then also wire up a settings changed handler
HotReloadSettings();
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
_settings.SettingsChanged += SettingsChangedHandler;
// Make sure that we update the acrylic theme when the OS theme changes
RootShellPage.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
_rootShellPage.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
// Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h
NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () =>
@@ -160,8 +171,7 @@ public sealed partial class MainWindow : WindowEx,
private void RestoreWindowPosition()
{
var settings = App.Current.Services.GetService<SettingsModel>();
if (settings?.LastWindowPosition is not WindowPosition savedPosition)
if (_settings.LastWindowPosition is not WindowPosition savedPosition)
{
PositionCentered();
return;
@@ -218,12 +228,10 @@ public sealed partial class MainWindow : WindowEx,
private void HotReloadSettings()
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
SetupHotkey(_settings);
_trayIconService.SetupTrayIcon(_settings.ShowSystemTrayIcon);
SetupHotkey(settings);
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
_ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen;
_ignoreHotKeyWhenFullScreen = _settings.IgnoreShortcutWhenFullscreen;
}
// We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material
@@ -378,9 +386,7 @@ public sealed partial class MainWindow : WindowEx,
public void Receive(ShowWindowMessage message)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
ShowHwnd(message.Hwnd, settings.SummonOn);
ShowHwnd(message.Hwnd, _settings.SummonOn);
}
public void Receive(HideWindowMessage message)
@@ -467,13 +473,11 @@ public sealed partial class MainWindow : WindowEx,
internal void MainWindow_Closed(object sender, WindowEventArgs args)
{
var serviceProvider = App.Current.Services;
UpdateWindowPositionInMemory();
var settings = serviceProvider.GetService<SettingsModel>();
if (settings is not null)
if (_settings is not null)
{
settings.LastWindowPosition = new WindowPosition
_settings.LastWindowPosition = new WindowPosition
{
X = _currentWindowPosition.X,
Y = _currentWindowPosition.Y,
@@ -481,13 +485,12 @@ public sealed partial class MainWindow : WindowEx,
Height = _currentWindowPosition.Height,
};
SettingsModel.SaveSettings(settings);
SettingsModel.SaveSettings(_settings);
}
var extensionService = serviceProvider.GetService<IExtensionService>()!;
extensionService.SignalStopExtensionsAsync();
_extensionService.SignalStopExtensionsAsync();
App.Current.Services.GetService<TrayIconService>()!.Destroy();
_trayIconService.Destroy();
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
// Workaround by turning it off before shutdown.
@@ -513,28 +516,28 @@ public sealed partial class MainWindow : WindowEx,
private void UpdateRegionsForCustomTitleBar()
{
// Specify the interactive regions of the title bar.
var scaleAdjustment = RootShellPage.XamlRoot.RasterizationScale;
var scaleAdjustment = _rootShellPage.XamlRoot.RasterizationScale;
// Get the rectangle around our XAML content. We're going to mark this
// rectangle as "Passthrough", so that the normal window operations
// (resizing, dragging) don't apply in this space.
var transform = RootShellPage.TransformToVisual(null);
var transform = _rootShellPage.TransformToVisual(null);
// Reserve 16px of space at the top for dragging.
var topHeight = 16;
var bounds = transform.TransformBounds(new Rect(
0,
topHeight,
RootShellPage.ActualWidth,
RootShellPage.ActualHeight));
_rootShellPage.ActualWidth,
_rootShellPage.ActualHeight));
var contentRect = GetRect(bounds, scaleAdjustment);
var rectArray = new RectInt32[] { contentRect };
var nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(this.AppWindow.Id);
nonClientInputSrc.SetRegionRects(NonClientRegionKind.Passthrough, rectArray);
// Add a drag-able region on top
var w = RootShellPage.ActualWidth;
_ = RootShellPage.ActualHeight;
var w = _rootShellPage.ActualWidth;
_ = _rootShellPage.ActualHeight;
var dragSides = new RectInt32[]
{
GetRect(new Rect(0, 0, w, topHeight), scaleAdjustment), // the top, {topHeight=16} tall
@@ -624,8 +627,7 @@ public sealed partial class MainWindow : WindowEx,
}
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
{
var settings = App.Current.Services.GetService<SettingsModel>();
if (settings?.AllowExternalReload == true)
if (_settings.AllowExternalReload == true)
{
Logger.LogInfo("External Reload triggered");
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());

View File

@@ -16,7 +16,6 @@ using Microsoft.CmdPal.UI.Messages;
using Microsoft.CmdPal.UI.Settings;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CommandPalette.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Input;
@@ -61,6 +60,9 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
private readonly ToastWindow _toast = new();
private readonly SettingsModel _settings;
private readonly TopLevelCommandManager tlcManager;
private readonly CompositeFormat _pageNavigatedAnnouncement;
private SettingsWindow? _settingsWindow;
@@ -68,14 +70,21 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
private CancellationTokenSource? _focusAfterLoadedCts;
private WeakReference<Page>? _lastNavigatedPageRef;
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
public ShellViewModel ViewModel { get; private set; }
public event PropertyChangedEventHandler? PropertyChanged;
public ShellPage()
public ShellPage(
ShellViewModel shellViewModel,
SettingsModel settingsModel,
TopLevelCommandManager topLevelCommandManager)
{
this.InitializeComponent();
ViewModel = shellViewModel;
_settings = settingsModel;
tlcManager = topLevelCommandManager;
// how we are doing navigation around
WeakReferenceMessenger.Default.Register<NavigateBackMessage>(this);
WeakReferenceMessenger.Default.Register<OpenSettingsMessage>(this);
@@ -111,19 +120,16 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
{
get
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
return settings.DisableAnimations ? _noAnimation : _slideRightTransition;
return _settings.DisableAnimations ? _noAnimation : _slideRightTransition;
}
}
public void Receive(NavigateBackMessage message)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
if (RootFrame.CanGoBack)
{
if (!message.FromBackspace ||
settings.BackspaceGoesBack)
_settings.BackspaceGoesBack)
{
GoBack();
}
@@ -159,7 +165,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, message.Page.Id));
PowerToysTelemetry.Log.WriteEvent(new OpenPage(RootFrame.BackStackDepth));
if (!ViewModel.IsNested)
{
@@ -334,7 +340,6 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
private void SummonOnUiThread(HotkeySummonMessage message)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
var commandId = message.CommandId;
var isRoot = string.IsNullOrEmpty(commandId);
if (isRoot)
@@ -345,11 +350,11 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
// Depending on the settings, either
// * Go home, or
// * Select the search text (if we should remain open on this page)
if (settings.HotkeyGoesHome)
if (_settings.HotkeyGoesHome)
{
GoHome(false);
}
else if (settings.HighlightSearchOnActivate)
else if (_settings.HighlightSearchOnActivate)
{
SearchBox.SelectSearch();
}
@@ -359,8 +364,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
try
{
// For a hotkey bound to a command, first lookup the
// command from our list of toplevel commands.
var tlcManager = App.Current.Services.GetService<TopLevelCommandManager>()!;
// command from our list of top level commands.
var topLevelCommand = tlcManager.LookupCommand(commandId);
if (topLevelCommand is not null)
{

View File

@@ -10,7 +10,6 @@ using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.MainPage;
using Microsoft.CommandPalette.Extensions;
using Microsoft.Extensions.DependencyInjection;
using WinRT;
// To learn more about WinUI, the WinUI project structure,
@@ -19,24 +18,23 @@ namespace Microsoft.CmdPal.UI;
internal sealed class PowerToysRootPageService : IRootPageService
{
private readonly IServiceProvider _serviceProvider;
private readonly TopLevelCommandManager _topLevelCommandManager;
private IExtensionWrapper? _activeExtension;
private Lazy<MainListPage> _mainListPage;
public PowerToysRootPageService(IServiceProvider serviceProvider)
public PowerToysRootPageService(TopLevelCommandManager topLevelCommandManager, SettingsModel settingsModel, AliasManager aliasManager, AppStateModel appStateModel)
{
_serviceProvider = serviceProvider;
_topLevelCommandManager = topLevelCommandManager;
_mainListPage = new Lazy<MainListPage>(() =>
{
return new MainListPage(_serviceProvider);
return new MainListPage(_topLevelCommandManager, settingsModel, aliasManager, appStateModel);
});
}
public async Task PreLoadAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
await tlcManager.LoadBuiltinsAsync();
await _topLevelCommandManager.LoadBuiltinsAsync();
}
public Microsoft.CommandPalette.Extensions.IPage GetRootPage()
@@ -46,13 +44,11 @@ internal sealed class PowerToysRootPageService : IRootPageService
public async Task PostLoadRootPageAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
tlcManager.LoadExtensionsCommand.Execute(null);
_topLevelCommandManager.LoadExtensionsCommand.Execute(null);
await tlcManager.LoadExtensionsCommand.ExecutionTask!;
if (tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
await _topLevelCommandManager.LoadExtensionsCommand.ExecutionTask!;
if (_topLevelCommandManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
{
// TODO: Handle failure case
}

View File

@@ -10,8 +10,6 @@ namespace Microsoft.CmdPal.UI.Settings;
public sealed partial class ExtensionPage : Page
{
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
public ProviderSettingsViewModel? ViewModel { get; private set; }
public ExtensionPage()

View File

@@ -5,7 +5,6 @@
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Controls;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
@@ -14,16 +13,13 @@ namespace Microsoft.CmdPal.UI.Settings;
public sealed partial class ExtensionsPage : Page
{
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
private readonly SettingsViewModel? viewModel;
public ExtensionsPage()
public ExtensionsPage(SettingsViewModel settingsViewModel)
{
this.InitializeComponent();
var settings = App.Current.Services.GetService<SettingsModel>()!;
viewModel = new SettingsViewModel(settings, App.Current.Services, _mainTaskScheduler);
viewModel = settingsViewModel;
}
private void SettingsCard_Click(object sender, RoutedEventArgs e)

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Windows.ApplicationModel;
@@ -11,16 +10,13 @@ namespace Microsoft.CmdPal.UI.Settings;
public sealed partial class GeneralPage : Page
{
private readonly TaskScheduler _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
private readonly SettingsViewModel? viewModel;
public GeneralPage()
public GeneralPage(SettingsViewModel settings, TopLevelCommandManager topLevelCommandManager)
{
this.InitializeComponent();
var settings = App.Current.Services.GetService<SettingsModel>()!;
viewModel = new SettingsViewModel(settings, App.Current.Services, _mainTaskScheduler);
viewModel = settings;
}
public string ApplicationVersion

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

@@ -24,13 +24,13 @@
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
</PropertyGroup>
<!-- <PropertyGroup>
<PropertyGroup>
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(CIBuild)'=='true'">
<ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest>
</PropertyGroup> -->
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">

View File

@@ -161,7 +161,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
L"PowerToys.MouseJump.dll",
L"PowerToys.AlwaysOnTopModuleInterface.dll",
L"PowerToys.MousePointerCrosshairs.dll",
// L"PowerToys.CursorWrap.dll",
L"PowerToys.CursorWrap.dll",
L"PowerToys.PowerAccentModuleInterface.dll",
L"PowerToys.PowerOCRModuleInterface.dll",
L"PowerToys.AdvancedPasteModuleInterface.dll",

View File

@@ -19,7 +19,7 @@ public static class AIServiceTypeRegistry
{
ServiceType = AIServiceType.AzureAIInference,
DisplayName = "Azure AI Inference",
IconPath = "ms-appx:///Assets/Settings/Icons/Models/Azure.svg",
IconPath = "ms-appx:///Assets/Settings/Icons/Models/FoundryLocal.svg", // No icon for Azure AI Inference, use Foundry Local temporarily
IsOnlineService = true,
LegalDescription = "AdvancedPaste_AzureAIInference_LegalDescription",
TermsLabel = "AdvancedPaste_AzureAIInference_TermsLabel",

View File

@@ -1,34 +1,59 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.2663 0H0.231885C0.103572 0 0 0.10358 0 0.231885V2.55072C0 2.67904 0.103572 2.78261 0.231885 2.78261H12.5217C12.9059 2.78261 13.2174 3.09411 13.2174 3.47826V3.18995C13.2174 1.53971 11.9861 0 10.2663 0Z" fill="url(#paint0_linear_178_3940)"/>
<path d="M12.2334 0.81543C12.8633 1.44693 13.2174 2.29872 13.2174 3.19069V15.7689C13.2174 15.8972 13.3209 16.0007 13.4492 16.0007H15.7681C15.8964 16.0007 16 15.8972 16 15.7689V5.73524C16 4.99707 15.707 4.28983 15.1853 3.76732L12.2334 0.81543Z" fill="url(#paint1_linear_178_3940)"/>
<path d="M6.78804 3.47852H0.231885C0.103572 3.47852 0 3.58209 0 3.71039V6.02921C0 6.1575 0.103572 6.26112 0.231885 6.26112H9.04346C9.42759 6.26112 9.7391 6.57263 9.7391 6.95676V6.6685C9.7391 5.01822 8.50778 3.47852 6.78804 3.47852Z" fill="url(#paint2_linear_178_3940)"/>
<path d="M8.75537 4.29297C9.38531 4.92446 9.73928 5.77628 9.73928 6.6682V15.7681C9.73928 15.8964 9.8429 16 9.97119 16H12.29C12.4183 16 12.5219 15.8964 12.5219 15.7681V9.21281C12.5219 8.47462 12.229 7.76735 11.7072 7.24482L8.75537 4.29297Z" fill="url(#paint3_linear_178_3940)"/>
<path d="M3.30975 6.95703H0.231885C0.103572 6.95703 0 7.06056 0 7.18886V9.50771C0 9.63609 0.103572 9.73962 0.231885 9.73962H5.56521C5.94936 9.73962 6.26087 10.0511 6.26087 10.4353V10.147C6.26087 8.49675 5.02956 6.95703 3.30975 6.95703Z" fill="url(#paint4_linear_178_3940)"/>
<path d="M5.27686 7.77148C5.90677 8.40302 6.26083 9.25477 6.26083 10.1468V15.7684C6.26083 15.8967 6.36436 16.0003 6.49274 16.0003H8.8115C8.93988 16.0003 9.04341 15.8967 9.04341 15.7684V12.6913C9.04341 11.9531 8.75051 11.2459 8.22874 10.7234L5.27686 7.77148Z" fill="url(#paint5_linear_178_3940)"/>
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2092_1741)">
<mask id="mask0_2092_1741" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="17" height="16">
<path d="M16.5 0H0.5V16H16.5V0Z" fill="white"/>
</mask>
<g mask="url(#mask0_2092_1741)">
<mask id="mask1_2092_1741" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-1" y="-2" width="19" height="20">
<path d="M17.8337 -1.33337H-0.833008V17.3333H17.8337V-1.33337Z" fill="white"/>
</mask>
<g mask="url(#mask1_2092_1741)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.1137 0.315668C11.57 0.315668 11.9744 0.657891 12.1196 1.15567C12.2648 1.65345 13.1152 4.73345 13.1152 4.73345V10.852H10.0352L10.0974 0.305298H11.1137V0.315668Z" fill="url(#paint0_linear_2092_1741)"/>
<path d="M15.6352 5.09586C15.6352 4.87808 15.4589 4.71216 15.2515 4.71216H13.4366C12.1611 4.71216 11.124 5.7492 11.124 7.02472V10.8618H13.3226C14.5982 10.8618 15.6352 9.82472 15.6352 8.54919V5.09586Z" fill="url(#paint1_linear_2092_1741)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.1133 0.315674C10.7607 0.315674 10.4807 0.595674 10.4807 0.948265L10.4185 12.5942C10.4185 14.2949 9.0392 15.6742 7.33847 15.6742H1.74885C1.47921 15.6742 1.30292 15.4149 1.38589 15.1661L5.86589 2.37938C6.30144 1.14531 7.46293 0.315674 8.7696 0.315674H11.1237H11.1133Z" fill="url(#paint2_linear_2092_1741)"/>
</g>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_178_3940" x1="13.2174" y1="3.15349" x2="0" y2="3.15349" gradientUnits="userSpaceOnUse">
<stop stop-color="#2C08AC"/>
<stop offset="0.8" stop-color="#4F42FD"/>
<linearGradient id="paint0_linear_2092_1741" x1="12.3996" y1="11.0801" x2="9.80702" y2="0.699373" gradientUnits="userSpaceOnUse">
<stop stop-color="#712575"/>
<stop offset="0.09" stop-color="#9A2884"/>
<stop offset="0.18" stop-color="#BF2C92"/>
<stop offset="0.27" stop-color="#DA2E9C"/>
<stop offset="0.34" stop-color="#EB30A2"/>
<stop offset="0.4" stop-color="#F131A5"/>
<stop offset="0.5" stop-color="#EC30A3"/>
<stop offset="0.61" stop-color="#DF2F9E"/>
<stop offset="0.72" stop-color="#C92D96"/>
<stop offset="0.83" stop-color="#AA2A8A"/>
<stop offset="0.95" stop-color="#83267C"/>
<stop offset="1" stop-color="#712575"/>
</linearGradient>
<linearGradient id="paint1_linear_178_3940" x1="14.2303" y1="0.81543" x2="23.44" y2="11.9747" gradientUnits="userSpaceOnUse">
<stop offset="0.3" stop-color="#7274FF"/>
<stop offset="1" stop-color="#4F42FD"/>
<linearGradient id="paint1_linear_2092_1741" x1="13.3848" y1="0.532897" x2="13.3848" y2="15.1759" gradientUnits="userSpaceOnUse">
<stop stop-color="#DA7ED0"/>
<stop offset="0.08" stop-color="#B17BD5"/>
<stop offset="0.19" stop-color="#8778DB"/>
<stop offset="0.3" stop-color="#6276E1"/>
<stop offset="0.41" stop-color="#4574E5"/>
<stop offset="0.54" stop-color="#2E72E8"/>
<stop offset="0.67" stop-color="#1D71EB"/>
<stop offset="0.81" stop-color="#1471EC"/>
<stop offset="1" stop-color="#1171ED"/>
</linearGradient>
<linearGradient id="paint2_linear_178_3940" x1="9.7391" y1="6.63202" x2="0" y2="6.63202" gradientUnits="userSpaceOnUse">
<stop stop-color="#2C08AC"/>
<stop offset="0.8" stop-color="#4F42FD"/>
</linearGradient>
<linearGradient id="paint3_linear_178_3940" x1="10.7523" y1="4.29297" x2="17.3026" y2="14.5881" gradientUnits="userSpaceOnUse">
<stop offset="0.3" stop-color="#7274FF"/>
<stop offset="1" stop-color="#4F42FD"/>
</linearGradient>
<linearGradient id="paint4_linear_178_3940" x1="6.26087" y1="9.91172" x2="0" y2="9.91172" gradientUnits="userSpaceOnUse">
<stop stop-color="#2C08AC"/>
<stop offset="0.8" stop-color="#4F42FD"/>
</linearGradient>
<linearGradient id="paint5_linear_178_3940" x1="7.2738" y1="7.77148" x2="11.0624" y2="16.243" gradientUnits="userSpaceOnUse">
<stop offset="0.3" stop-color="#7274FF"/>
<stop offset="1" stop-color="#4F42FD"/>
<linearGradient id="paint2_linear_2092_1741" x1="12.5029" y1="0.865306" x2="2.79625" y2="16.4313" gradientUnits="userSpaceOnUse">
<stop stop-color="#DA7ED0"/>
<stop offset="0.05" stop-color="#B77BD4"/>
<stop offset="0.11" stop-color="#9079DA"/>
<stop offset="0.18" stop-color="#6E77DF"/>
<stop offset="0.25" stop-color="#5175E3"/>
<stop offset="0.33" stop-color="#3973E7"/>
<stop offset="0.42" stop-color="#2772E9"/>
<stop offset="0.54" stop-color="#1A71EB"/>
<stop offset="0.68" stop-color="#1371EC"/>
<stop offset="1" stop-color="#1171ED"/>
</linearGradient>
<clipPath id="clip0_2092_1741">
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

View File

@@ -28,8 +28,6 @@
<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" />
@@ -73,7 +71,6 @@
</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,24 +30,6 @@
</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"
@@ -87,12 +69,12 @@
FontSize="24"
Glyph="&#xF158;" />
<TextBlock
x:Uid="AdvancedPaste_FL_NoModelsDownloaded"
x:Uid="AdvancedPaste_FL_NoModelsDownloaded."
HorizontalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}"
TextAlignment="Center" />
<TextBlock
x:Uid="AdvancedPaste_FL_RunFoundryLocalText"
x:Uid="AdvancedPaste_FL_RunFoundryLocalText.Text"
HorizontalAlignment="Center"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
@@ -124,7 +106,7 @@
<ComboBox.Header>
<TextBlock>
<Run x:Uid="AdvancedPaste_FL_LocalModel" /><LineBreak /><Run
x:Uid="AdvancedPaste_FL_UseCliToDownloadModels"
x:Uid="AdvancedPaste_FL_UseCLIToDownloadModels"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
@@ -170,7 +152,7 @@
Spacing="8">
<Image Width="36" Source="ms-appx:///Assets/Settings/Icons/Models/FoundryLocal.svg" />
<TextBlock
x:Uid="AdvancedPaste_FL_FLNotAvailableYet"
x:Uid="AdvancedPaste_FL_FLNotavailableYet"
HorizontalAlignment="Center"
FontWeight="SemiBold"
TextAlignment="Center"

View File

@@ -30,7 +30,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
public delegate void DownloadRequestedEventHandler(object sender, object payload);
public delegate void LoadRequestedEventHandler(object sender);
public delegate void LoadRequestedEventHandler(object sender, FoundryLoadRequestedEventArgs args);
public event ModelSelectionChangedEventHandler SelectionChanged;
@@ -94,7 +94,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
public bool HasDownloadableModels => DownloadableModels?.Cast<object>().Any() ?? false;
public void RequestLoad()
public void RequestLoad(bool refresh)
{
if (IsLoading)
{
@@ -107,7 +107,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
IsAvailable = false;
StatusText = "Loading Foundry Local status...";
LoadRequested?.Invoke(this);
LoadRequested?.Invoke(this, new FoundryLoadRequestedEventArgs(refresh));
}
private static void OnCachedModelsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -310,7 +310,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
private void RefreshModelsButton_Click(object sender, RoutedEventArgs e)
{
RequestLoad();
RequestLoad(refresh: true);
}
private void UpdateVisualStates()
@@ -444,4 +444,14 @@ public sealed partial class FoundryLocalModelPicker : UserControl
{
return string.IsNullOrWhiteSpace(license) ? Visibility.Collapsed : Visibility.Visible;
}
public sealed class FoundryLoadRequestedEventArgs : EventArgs
{
public FoundryLoadRequestedEventArgs(bool refresh)
{
Refresh = refresh;
}
public bool Refresh { get; }
}
}

View File

@@ -82,7 +82,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
{
ViewModel.RefreshEnabledState();
UpdatePasteAIUIVisibility();
_ = UpdateFoundryLocalUIAsync();
_ = UpdateFoundryLocalUIAsync(refreshFoundry: true);
}
private void EnableAdvancedPasteAI() => ViewModel.EnableAI();
@@ -384,7 +384,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
}
private Task UpdateFoundryLocalUIAsync()
private Task UpdateFoundryLocalUIAsync(bool refreshFoundry = false)
{
string selectedType = ViewModel?.PasteAIProviderDraft?.ServiceType ?? string.Empty;
bool isFoundryLocal = string.Equals(selectedType, "FoundryLocal", StringComparison.OrdinalIgnoreCase);
@@ -419,12 +419,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
PasteAIProviderConfigurationDialog.IsPrimaryButtonEnabled = false;
}
FoundryLocalPicker?.RequestLoad();
FoundryLocalPicker?.RequestLoad(refreshFoundry);
return Task.CompletedTask;
}
private async Task LoadFoundryLocalModelsAsync()
private async Task LoadFoundryLocalModelsAsync(bool refresh = false)
{
if (FoundryLocalPanel is null)
{
@@ -456,7 +456,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
return;
}
IEnumerable<ModelDetails> cachedModelsEnumerable = await provider.GetModelsAsync(cancelationToken: cancellationToken).ConfigureAwait(false);
IEnumerable<ModelDetails> cachedModelsEnumerable = refresh
? await provider.GetModelsAsync(ignoreCached: true, cancelationToken: cancellationToken)
: await provider.GetModelsAsync(cancelationToken: cancellationToken);
if (cancellationToken.IsCancellationRequested)
{
@@ -465,12 +467,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
var cachedModels = cachedModelsEnumerable?.ToList() ?? new List<ModelDetails>();
DispatcherQueue.TryEnqueue(() =>
{
UpdateFoundryCollections(cachedModels);
ShowFoundryAvailableState();
RestoreFoundrySelection(cachedModels);
});
UpdateFoundryCollections(cachedModels);
ShowFoundryAvailableState();
RestoreFoundrySelection(cachedModels);
}
catch (OperationCanceledException)
{
@@ -479,18 +478,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
catch (Exception ex)
{
var errorMessage = $"Unable to load Foundry Local models. {ex.Message}";
ShowFoundryUnavailableState(errorMessage);
System.Diagnostics.Debug.WriteLine($"[AdvancedPastePage] Failed to load Foundry Local models: {ex}");
DispatcherQueue.TryEnqueue(() =>
{
ShowFoundryUnavailableState(errorMessage);
});
}
finally
{
DispatcherQueue.TryEnqueue(() =>
{
UpdateFoundrySaveButtonState();
});
UpdateFoundrySaveButtonState();
}
}
@@ -679,9 +672,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
UpdateFoundrySaveButtonState();
}
private async void FoundryLocalPicker_LoadRequested(object sender)
private async void FoundryLocalPicker_LoadRequested(object sender, FoundryLocalModelPicker.FoundryLoadRequestedEventArgs args)
{
await LoadFoundryLocalModelsAsync();
await LoadFoundryLocalModelsAsync(args?.Refresh ?? false);
}
private sealed class FoundryDownloadableModel : INotifyPropertyChanged
@@ -1096,7 +1089,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
PasteAIProviderConfigurationDialog.Title = $"{displayName} provider configuration";
}
await UpdateFoundryLocalUIAsync();
await UpdateFoundryLocalUIAsync(refreshFoundry: true);
UpdatePasteAIUIVisibility();
RefreshDialogBindings();
@@ -1125,7 +1118,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
: $"{titlePrefix} provider configuration";
UpdatePasteAIUIVisibility();
await UpdateFoundryLocalUIAsync();
await UpdateFoundryLocalUIAsync(refreshFoundry: false);
RefreshDialogBindings();
PasteAIApiKeyPasswordBox.Password = ViewModel.GetPasteAIApiKey(provider.Id, provider.ServiceType);
await PasteAIProviderConfigurationDialog.ShowAsync();

View File

@@ -10,202 +10,40 @@
xmlns:ui="using:CommunityToolkit.WinUI"
AutomationProperties.LandmarkType="Main"
mc:Ignorable="d">
<Grid>
<ScrollViewer AutomationProperties.AutomationId="PageScrollViewer">
<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>
<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:SettingsPageControl x:Uid="CmdPal" ModuleImageSource="ms-appx:///Assets/Settings/Modules/CmdPal.png">
<controls:SettingsPageControl.ModuleContent>
<StackPanel ChildrenTransitions="{StaticResource SettingsCardsAnimations}" Orientation="Vertical">
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay}">
<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>
Name="CmdPalEnableCmdPal"
x:Uid="CmdPal_Enable_CmdPal"
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
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>
</Grid>
</ScrollViewer>
</Grid>
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>
</StackPanel>
</controls:SettingsPageControl.ModuleContent>
<controls:SettingsPageControl.PrimaryLinks>
<controls:PageLink x:Uid="LearnMore_CmdPal" Link="https://aka.ms/PowerToysOverview_CmdPal" />
</controls:SettingsPageControl.PrimaryLinks>
</controls:SettingsPageControl>
</local:NavigablePage>

View File

@@ -63,20 +63,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
}
}
private void SettingsCard_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
private void CmdPalSettingsDeeplink_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

@@ -273,7 +273,6 @@
<panels:MouseJumpPanel x:Name="MouseUtils_MouseJump_Panel" x:Uid="MouseUtils_MouseJump_Panel" />
<!--
<controls:SettingsGroup x:Uid="MouseUtils_CursorWrap" AutomationProperties.AutomationId="MouseUtils_CursorWrapTestId">
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsCursorWrapEnabledGpoConfigured, Mode=OneWay}">
<tkcontrols:SettingsCard
@@ -301,7 +300,7 @@
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>-->
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="MouseUtils_MousePointerCrosshairs" AutomationProperties.AutomationId="MouseUtils_MousePointerCrosshairsTestId">
<controls:GPOInfoControl ShowWarning="{x:Bind ViewModel.IsMousePointerCrosshairsEnabledGpoConfigured, Mode=OneWay}">
<tkcontrols:SettingsCard

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

@@ -2703,7 +2703,7 @@ From there, simply click on one of the supported files in the File Explorer and
<value>Mouse Pointer Crosshairs</value>
<comment>Mouse as in the hardware peripheral.</comment>
</data>
<data name="Oobe_MouseUtils_MousePointerCrosshairs_Description.Text" xml:space="preserve">
<data name="Oobe_MouseUtils_MousePointerCrosshairs.Description" xml:space="preserve">
<value>Draw crosshairs centered around the mouse pointer.</value>
<comment>Mouse as in the hardware peripheral.</comment>
</data>
@@ -5159,12 +5159,25 @@ 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 a product name, do not loc</comment>
<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>
</data>
<data name="LearnMore_CmdPal.Text" xml:space="preserve">
<value>Learn more</value>
<value>Learn more about Command Palette</value>
<comment>Command Palette is a product name, do not loc</comment>
</data>
<data name="Shell_CmdPal.Content" xml:space="preserve">
<value>Command Palette</value>
@@ -5172,11 +5185,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, do not loc</comment>
<comment>"Command Palette" is a product name</comment>
</data>
<data name="Oobe_CmdPal.Title" xml:space="preserve">
<value>Command Palette</value>
<comment>Command Palette is a product name, do not loc</comment>
<comment>"Command Palette" is a product name</comment>
</data>
<data name="Oobe_CmdPal_HowToUse.Text" xml:space="preserve">
<value>and start typing!</value>
@@ -5209,8 +5222,14 @@ 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_Settings.Header" xml:space="preserve">
<value>Settings</value>
<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>
<data name="Help_chromaCIE" xml:space="preserve">
<value>chroma (CIE LCh)</value>
@@ -5674,14 +5693,14 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<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">
<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">
<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>
@@ -5734,51 +5753,4 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<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

@@ -40,12 +40,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public ObservableCollection<DashboardListItem> ActionModules { get; set; } = new ObservableCollection<DashboardListItem>();
// Master list of module items that is sorted and projected into AllModules.
private List<DashboardListItem> _moduleItems = new List<DashboardListItem>();
// Flag to prevent circular updates when a UI toggle triggers settings changes.
private bool _isUpdatingFromUI;
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
public AllHotkeyConflictsData AllHotkeyConflictsData
@@ -80,7 +74,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
generalSettingsConfig.DashboardSortOrder = value;
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettingsConfig);
SendConfigMSG(outgoing.ToString());
SortModuleList();
RefreshModuleList();
}
}
}
@@ -102,9 +96,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
// set the callback functions value to handle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
BuildModuleList();
SortModuleList();
RefreshShortcutModules();
RefreshModuleList();
GetShortcutModules();
}
protected override void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
@@ -136,22 +129,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
}
/// <summary>
/// Builds the master list of module items. Called once during initialization.
/// Each module item contains its configuration, enabled state, and GPO lock status.
/// </summary>
private void BuildModuleList()
private void RefreshModuleList()
{
_moduleItems.Clear();
AllModules.Clear();
var moduleItems = new List<DashboardListItem>();
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
{
// Hide CursorWrap from Dashboard
if (moduleType == ModuleType.CursorWrap)
{
continue;
}
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(moduleType);
var newItem = new DashboardListItem()
{
@@ -160,148 +145,51 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
// IsNew = moduleType == ModuleType.CursorWrap,
IsNew = moduleType == ModuleType.CursorWrap,
DashboardModuleItems = GetModuleItems(moduleType),
};
newItem.EnabledChangedCallback = EnabledChangedOnUI;
_moduleItems.Add(newItem);
moduleItems.Add(newItem);
}
// Sort based on current sort order
var sortedItems = DashboardSortOrder switch
{
DashboardSortOrder.ByStatus => moduleItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label),
_ => moduleItems.OrderBy(x => x.Label), // Default alphabetical
};
foreach (var item in sortedItems)
{
AllModules.Add(item);
}
}
/// <summary>
/// Sorts the module list according to the current sort order and updates the AllModules collection.
/// On first call, populates AllModules. On subsequent calls, uses Move() to reorder items in-place
/// to avoid destroying and recreating UI elements.
/// </summary>
private void SortModuleList()
{
var sortedItems = (DashboardSortOrder switch
{
DashboardSortOrder.ByStatus => _moduleItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label),
_ => _moduleItems.OrderBy(x => x.Label), // Default alphabetical
}).ToList();
// If AllModules is empty (first load), just populate it.
if (AllModules.Count == 0)
{
foreach (var item in sortedItems)
{
AllModules.Add(item);
}
return;
}
// Otherwise, update the collection in place using Move to avoid UI glitches.
for (int i = 0; i < sortedItems.Count; i++)
{
var currentItem = sortedItems[i];
var currentIndex = AllModules.IndexOf(currentItem);
if (currentIndex != -1 && currentIndex != i)
{
AllModules.Move(currentIndex, i);
}
}
// Notify that DashboardSortOrder changed to update menu check mark.
OnPropertyChanged(nameof(DashboardSortOrder));
}
/// <summary>
/// Refreshes module enabled/locked states by re-reading GPO configuration. Only
/// updates properties that have actually changed to minimize UI notifications
/// then re-sorts the list according to the current sort order.
/// </summary>
private void RefreshModuleList()
{
foreach (var item in _moduleItems)
{
GpoRuleConfigured gpo = ModuleHelper.GetModuleGpoConfiguration(item.Tag);
// GPO can force-enable (Enabled) or force-disable (Disabled) a module.
// If Enabled: module is on and the user cannot disable it.
// If Disabled: module is off and the user cannot enable it.
// Otherwise, the setting is unlocked and the user can enable/disable it.
bool newEnabledState = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, item.Tag));
// Lock the toggle when GPO is controlling the module.
bool newLockedState = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled;
// Only update if there's an actual change to minimize UI notifications.
if (item.IsEnabled != newEnabledState)
{
item.IsEnabled = newEnabledState;
}
if (item.IsLocked != newLockedState)
{
item.IsLocked = newLockedState;
}
}
SortModuleList();
}
/// <summary>
/// Callback invoked when a user toggles a module's enabled state in the UI.
/// Sets the _isUpdatingFromUI flag to prevent circular updates, then updates
/// settings, re-sorts if needed, and refreshes dependent collections.
/// </summary>
private void EnabledChangedOnUI(DashboardListItem dashboardListItem)
{
_isUpdatingFromUI = true;
try
Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, dashboardListItem.IsEnabled);
if (dashboardListItem.Tag == ModuleType.NewPlus && dashboardListItem.IsEnabled == true)
{
Views.ShellPage.UpdateGeneralSettingsCallback(dashboardListItem.Tag, dashboardListItem.IsEnabled);
if (dashboardListItem.Tag == ModuleType.NewPlus && dashboardListItem.IsEnabled == true)
{
var settingsUtils = new SettingsUtils();
var settings = NewPlusViewModel.LoadSettings(settingsUtils);
NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value);
}
// Re-sort only required if sorting by enabled status.
if (DashboardSortOrder == DashboardSortOrder.ByStatus)
{
SortModuleList();
}
// Always refresh shortcuts/actions to reflect enabled state changes.
RefreshShortcutModules();
// Request updated conflicts after module state change.
RequestConflictData();
}
finally
{
_isUpdatingFromUI = false;
var settingsUtils = new SettingsUtils();
var settings = NewPlusViewModel.LoadSettings(settingsUtils);
NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value);
}
// Request updated conflicts after module state change
RequestConflictData();
}
/// <summary>
/// Callback invoked when module enabled state changes from other parts of the
/// settings UI. Ignores the notification if it was triggered by a UI toggle
/// we're already handling, to prevent circular updates.
/// </summary>
public void ModuleEnabledChangedOnSettingsPage()
{
// Ignore if this was triggered by a UI change that we're already handling.
if (_isUpdatingFromUI)
{
return;
}
try
{
RefreshModuleList();
RefreshShortcutModules();
GetShortcutModules();
OnPropertyChanged(nameof(ShortcutModules));
// Request updated conflicts after module state change.
// Request updated conflicts after module state change
RequestConflictData();
}
catch (Exception ex)
@@ -310,11 +198,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
/// <summary>
/// Rebuilds ShortcutModules and ActionModules collections by filtering AllModules
/// to only include enabled modules and their respective shortcut/action items.
/// </summary>
private void RefreshShortcutModules()
private void GetShortcutModules()
{
ShortcutModules.Clear();
ActionModules.Clear();

View File

@@ -36,12 +36,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
{
// Hide CursorWrap from All Apps flyout
if (moduleType == ModuleType.CursorWrap)
{
continue;
}
AddFlyoutMenuItem(moduleType);
}

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());
}
}
}