Compare commits

...

47 Commits

Author SHA1 Message Date
Jeremy Sinclair
3bc1cf4e4c Merge branch 'main' into dev/snickler/net910_patch 2025-11-05 10:30:20 -05:00
Niels Laute
b7b6ae6485 Tweaking the focus state for AP (#43306)
Adding the AI underline to the inputbox

<img width="656" height="583" alt="image"
src="https://github.com/user-attachments/assets/998a4203-61d7-4228-a682-391c6d78b3a5"
/>
2025-11-05 21:23:17 +08:00
Dave Rayment
b5b7361855 [General] Include high-volume bugs in Issue Template header (#43134)
<!-- 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
Adds references to 3 bugs which are logged by those who are unfamiliar
with the issue search function. It is hoped that this will help ease the
amount of triaging.

<!-- 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
- Checked the YML preview on my own fork.
2025-11-05 13:26:55 +01:00
Mike Hall
cd988b798b Add Cursor Wrap functionality to Powertoys Mouse Utils (#41826)
## Summary of the Pull Request

Cursor Wrap makes it simple to move the mouse from one edge of a display
(or set of displays) to the opposite edge of the display stack - on a
single display Cursor Wrap will wrap top/bottom and left/right edges.


https://github.com/user-attachments/assets/3feb606c-142b-4dab-9824-7597833d3ba4


## PR Checklist

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

## Detailed Description of the Pull Request / Additional comments
PR adds a new mouse utils module, this is 'Cursor Wrap' - Cursor Wrap
works with 1-9 monitors based on the logical monitor layout of the PC -
for a single monitor device the cursor is wrapped for the top/bottom and
left/right edges of the display - for a multi-monitor setup the cursor
is wrapped on the top/bottom left/right of the displays in the logical
display layout.

## Validation Steps Performed
Validation has been performed on a Surface Laptop 7 Pro (Intel) with a
single display and with an HDMI USB-C second display configured to be a
second monitor in top/left/right/bottom configuration - there are also
tests that run as part of the build to validate logical monitor layout
and cursor positioning.

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Kai Tao (from Dev Box) <kaitao@microsoft.com>
Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 19:28:25 +08:00
Niels Laute
1ad468641b [UX] Dashboard utilities sorting (#42065)
## Summary of the Pull Request

This PR adds a sorting button to the module list so it can be sorted
alphabetically (default) or by status.

Fixes: #41837

<img width="760" height="933" alt="image"
src="https://github.com/user-attachments/assets/69c831a9-ae9e-4849-b630-772d03e4a4b1"
/>


@yeelam-gordon When running the runner, I do see the settings value is
being updated. But when running the runner again the setting doesn't
seem to be saved? Is that because of a bug in my implementation, or am I
testing it wrong :)?

<img width="286" height="86" alt="image"
src="https://github.com/user-attachments/assets/3a90997f-c40c-4f50-8361-b1ae3aa02052"
/>



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

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

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

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

---------

Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
Co-authored-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
2025-11-05 17:42:24 +08:00
Davide Giacometti
3176eb94a9 [Hosts] Backup Settings (#37778)
<!-- 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

Add backup settings for the Hosts File Editor to allow users to
customize the existing hardcoded logic.

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

- [x] **Closes:** #37666
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [x] **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:
https://github.com/MicrosoftDocs/windows-dev-docs/pull/5342

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

<img width="707" alt="image"
src="https://github.com/user-attachments/assets/e114431e-60e0-4b8c-bba7-df23f7af0182"
/>

<img width="707" alt="image"
src="https://github.com/user-attachments/assets/a02b591e-eb46-4964-bee7-548ec175b3aa"
/>

<img width="707" alt="image"
src="https://github.com/user-attachments/assets/6eb0ff21-74fa-4229-8832-df2df877b5cd"
/>

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

- Backup on: verified that backup isn't executed
- Backups off: Verified that only one backup is executed
- Verified that backup is located in the expected path
- Auto delete is set to "never": verified that no backups are deleted
- Auto delete is set to "based on count": verified that backups are
deleted according to count value
- Auto delete is set to "based on age and count": verified that backups
are deleted according to days and count values
- Verified that files without the backup pattern aren't deleted
- There is also adequate test coverage for these scenarios 🚀

---------

Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 16:42:31 +08:00
Antonín Procházka
31a0deee35 Peek a file using command line and named pipe. (#26422) (#38754)
This is a PoC. It adds the ability to peek a file using a named pipe and
a command line.

Usage/testing before this gets merged and released:

1. Build release configuration of Peek.UI and Peek.CLI.
2. Terminate PowerToys.Peek.UI.exe if running.
3. Back up and replace PowerToys.Peek.UI[.dll;.exe;.pdb;.pri]. Use
[Everything](https://www.voidtools.com/downloads/) to find the source
and destination folders.
4. Call `PowerToys.Peek.CLI.exe <path>` or send the path to peek to the
`PeekPipe` named pipe.

If this solution is OK, documentation and installer need to be updated
and a follow-up issue needs to be filed to support navigation.

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
Co-authored-by: Leilei Zhang <leilzh@microsoft.com>
Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-05 16:38:07 +08:00
Kai Tao
20b5ca79a3 light switch: Remove newinfobadge for light switch (#43300)
<!-- 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 new tag for light switch

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

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

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

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

<img width="773" height="332" alt="image"
src="https://github.com/user-attachments/assets/79f89994-8b6e-431e-a5f1-30909940e996"
/>
2025-11-05 16:36:46 +08:00
Shawn Yuan
a3b8dc6cb8 Advanced Paste: AI pasting enhancement (#42374)
<!-- 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
* Add multiple endpoint support for paste with AI
* Add Local AI support for paste AI
* Advanced AI implementation

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

- [x] Closes: #32960
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** Added/updated
- [x] **New binaries:** Added on the required places
- [x] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [x] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [x] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [x] [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

### GPO
- [x] Paste with AI should not be available if the original GPO for
paste AI is set to false
   - [x] Paste with AI should be controlled within endpoint granularity
- [x] Advanced Paste UI should disable AI ability if GPO is set to
disable for any llm
### Paste AI
   - [x] Every AI endpoint should work as expected
   - [x] Default prompt should be able to give a reasonable result
   - [x] Local AI should work as expected
### Advanced AI
- [x] Open AI and Azure OPENAI should be able to configure as advanced
AI endpoint
- [x] Advanced AI should be able to pick up functions correctly to do
the transformation and give reasonable result

---------

Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
Signed-off-by: Shuai Yuan <shuai.yuan.zju@gmail.com>
Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
Co-authored-by: Leilei Zhang <leilzh@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Kai Tao <kaitao@microsoft.com>
Co-authored-by: Kai Tao <69313318+vanzue@users.noreply.github.com>
Co-authored-by: vanzue <vanzue@outlook.com>
Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
2025-11-05 16:13:55 +08:00
Jeremy Sinclair
426405b530 [Deps] bump Microsoft.* and System.* package versions to 9.0.10 2025-11-04 22:16:17 -05:00
Kai Tao
c364aa7c70 Cmdpal: Bring vcruntime in cmdpal (#43255)
<!-- 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
Cmdpal has dependency on vcruntime appx package due to adaptive card,
bring that dependency in

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
<img width="1873" height="1183" alt="image"
src="https://github.com/user-attachments/assets/d416d09f-f4d6-456c-a625-ca724a69b5ee"
/>

---------

Co-authored-by: Jiří Polášek <me@jiripolasek.com>
2025-11-05 09:51:25 +08:00
leileizhang
229bedd09f [UI Tests] Add complete OCR UI test coverage (#41947)
<!-- 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
* Enable Text Extractor. Then:
   - [x] Press the activation shortcut and verify the overlay appears.
   - [x] Press Escape and verify the overlay disappears.
   - [x] Press the activation shortcut and verify the overlay appears.
   - [x] Right-click and select Cancel. Verify the overlay disappears.
- [x] Disable Text Extractor and verify that the activation shortuct no
longer activates the utility.
 * With Text Extractor enabled and activated:
   - [x] Try to select text and verify it is copied to the clipboard.
- [x] Try to select a different OCR language by right-clicking and
verify the change is applied.
 * Test the different settings and verify they are applied:
   - [x] Activation shortcut
   - [x] OCR Language
 
<!-- 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-04 11:59:10 +08:00
moooyo
70e1177a6a [PowerRename] Support using photo metadata to replace in the PowerRename (#41728)
<!-- 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. Introduce WIC for power rename and add new class WICMetadataExtractor
to use WIC to extract metadata.
2. Add some patterns for metadata extract.
3. Support XMP and EXIF metadata extract.
4. Add test data for xmp and exif extractor
5. Add attribution for the test data uploader.

UI:
<img width="2052" height="1415" alt="image"
src="https://github.com/user-attachments/assets/9051b12e-4e66-4fdc-a4d4-3bada661c235"
/>
<img width="284" height="170" alt="image"
src="https://github.com/user-attachments/assets/2fd67193-77a7-48f0-a5ac-08a69fe64e55"
/>
<img width="715" height="1160" alt="image"
src="https://github.com/user-attachments/assets/5fa68a8c-d129-44dd-b747-099dfbcded12"
/>

demo:


https://github.com/user-attachments/assets/e90bc206-62e5-4101-ada2-3187ee7e2039



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

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

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

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

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-11-04 09:27:16 +08:00
Jiří Polášek
957b653210 CmdPal: Add a micro global error handler (#41392)
## Summary of the Pull Request

This PR introduces a scaled-down version of the global error handler
from #41061.

- Catches and logs virtually all unhandled exceptions.
- For UI thread exceptions, generates an additional error report:
    - One copy is saved in app's log folder for the Bug Report Tool.
- Another copy can be placed to the user’s desktop to increase
visibility and encourage report submission (disabled for now).
- Displays a message box that tells the user where to find the saved
report.

This PR is intentionally minimal and focused. The complete, more
polished solution is still planned in #41061, which should replace this
implementation in a follow-up.

<details><summary>Report example</summary>
<pre>

This is an error report generated by Windows Command Palette.
If you are seeing this message, it means the application has encountered
an unexpected issue.
You can help us fix it by filing a report at
https://aka.ms/powerToysReportBug.
============================================================
😢 An unexpected error occurred in the application.

Summary:
  Message:    NamedResource Not Found.

NamedResource Not Found.

  Type:       System.Runtime.InteropServices.COMException
  Source:     WinRT.Runtime
  Time:       2025-08-26 20:22:53.5752505
  HRESULT:    0x80073B17 (-2147009769)

Stack Trace:
at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
at
ABI.Microsoft.Windows.ApplicationModel.Resources.IResourceLoaderMethods.GetString(IObjectReference
_obj, String resourceId)
at
Microsoft.Windows.ApplicationModel.Resources.ResourceLoader.GetString(String
resourceId)
at Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance.GetString(String
resourceId)
at
Microsoft.CmdPal.UI.Settings.SettingsWindow.AnnounceNavigationPaneStateChanged(DependencyObject
sender, DependencyProperty dp)
at
ABI.Microsoft.UI.Xaml.DependencyPropertyChangedCallback.Do_Abi_Invoke(IntPtr
thisPtr, IntPtr sender, IntPtr dp)

------------------ Full Exception Details ------------------
System.Runtime.InteropServices.COMException (0x80073B17): NamedResource
Not Found.

NamedResource Not Found.

at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
at
ABI.Microsoft.Windows.ApplicationModel.Resources.IResourceLoaderMethods.GetString(IObjectReference
_obj, String resourceId)
at
Microsoft.Windows.ApplicationModel.Resources.ResourceLoader.GetString(String
resourceId)
at Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance.GetString(String
resourceId)
at
Microsoft.CmdPal.UI.Settings.SettingsWindow.AnnounceNavigationPaneStateChanged(DependencyObject
sender, DependencyProperty dp)
at
ABI.Microsoft.UI.Xaml.DependencyPropertyChangedCallback.Do_Abi_Invoke(IntPtr
thisPtr, IntPtr sender, IntPtr dp)

ℹ️ If you need further assistance, please include this information in
your support request.
ℹ️ Before sending, take a quick look to make sure it doesn't contain any
personal or sensitive information.
============================================================


</pre>
</details> 

Message:
<img width="2309" height="1341" alt="image"
src="https://github.com/user-attachments/assets/3ec20054-402f-421d-bebd-bf848a8eb504"
/>

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

- [x] Partially handles: #41606
- [ ] **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-10-31 12:35:37 -05:00
Dustin L. Howett
0b0ad68b60 build: build the Machine and User installers at the same time (#42888) 2025-10-31 00:28:12 -05:00
Dustin L. Howett
b87be7263d Tests: do not use *relative drive letter* (#43135)
Since we switched to running the build on C:\, this test started
failing. C: means "current directory on the C drive"!
2025-10-31 11:33:44 +08:00
Jaylyn Barbee
1783812f1f Light Switch is not clearing manual override if the boundary happens while offline (#43072)
Bug: If light switch is transitioning while the computer is offline,
Light Switch will not clear the manual override at the boundary.
Fix: Ensure the "Clear override" logic checks if we are at or past a
boundary point.
2025-10-29 09:23:43 -07:00
Shawn Yuan
94b5bc62de Fix title bar overlapping issue (#43022)
<!-- 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 introduces a small UI improvement to the
`ShellPage.xaml` file. The change sets a breakpoint for the compact
state of the title bar, which will help the interface adapt better to
different window sizes.

* UI responsiveness: Added `CompactStateBreakpoint="900"` to the
`AppTitleBar` control in `ShellPage.xaml` to improve layout adaptation
for smaller window widths.

**Before:**


https://github.com/user-attachments/assets/5ec424f4-b441-4eff-967f-a97cc92e180a


**After:**


https://github.com/user-attachments/assets/a1487c39-dce7-4055-90e9-aa6ed95e4beb



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

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

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

---------

Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
2025-10-29 10:32:09 +08:00
Shawn Yuan
0d18727e81 [CmdPal] Added recenter window support. (#38943)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Added a new setting option to Command Palette that allows users to
choose whether the window should be recentered on every launch or
remember its last position. This enhancement improves user experience by
maintaining window positioning preferences across sessions.

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

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

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

This PR adds a "Recenter window on launch" setting to Command Palette
with the following changes:

1. Added a new `RecenterWindow` property to `SettingsModel` (default is
true for backward compatibility)
2. Added corresponding property to `SettingsViewModel` for binding
3. Created a new `WindowPosition` class to track window position and
size
4. Modified `MainWindow.xaml.cs` to:
    - Track window position and size changes
    - Update position memory when window is modified or dismissed
    - Respect the recenter setting when showing the window
5. Added UI controls in the settings page with proper localization
strings

![image](https://github.com/user-attachments/assets/1c1db9f6-b23e-47c0-9a03-93906d4c9a42)



This feature allows users who prefer to have the Command Palette appear
in a specific screen location to maintain that preference, while others
can continue using the centered window behavior.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
1. Manually verified that with the setting enabled (default), the window
centers on launch
2. Verified that with the setting disabled, the window appears at its
last position

---------

Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
2025-10-28 14:37:45 -05:00
Jiří Polášek
1e40d6b15b CmdPal: Extension list search (#41453)
## Summary of the Pull Request

Adds a rough implementation that appears to work, but it hasn’t been
tested. Don’t rely on it yet—I’m too tired to test properly or write a
polished commit message.

Pictures? Pictures!

<img width="2720" height="1624" alt="image"
src="https://github.com/user-attachments/assets/4f9c3360-8d08-4a34-888f-c6890afd0159"
/>

<img width="2647" height="1558" alt="image"
src="https://github.com/user-attachments/assets/3d6c23bc-f26d-4e31-ae09-d91efd304048"
/>

<img width="2683" height="1536" alt="image"
src="https://github.com/user-attachments/assets/6017b2d4-ccd9-4555-b81f-e2b1f78c48e5"
/>

<img width="577" height="334" alt="image"
src="https://github.com/user-attachments/assets/987fea99-05b2-4d31-b90c-327122daa241"
/>


https://github.com/user-attachments/assets/65a8f8ce-ae09-4272-881d-0b6f56cecfae

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

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

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

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

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Michael Jolley <mike@baldbeardedbuilder.com>
2025-10-28 14:36:42 -05:00
Guilherme
de00cbf20a [CmdPal] Fix filters visibility on non-ListPage (#42828)
<!-- 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 aims to fix the issue where filters from a ListPage remain
visible when navigating to other pages.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

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

![FiltersIssue](https://github.com/user-attachments/assets/b0ad6059-9a11-4e12-821d-7202358e25bb)

### After:

![FiltersFix](https://github.com/user-attachments/assets/b9ee71ee-cb5d-4ef9-b9fc-bc2e2a710b5c)

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

---------

Co-authored-by: Jiří Polášek <me@jiripolasek.com>
2025-10-28 14:32:09 -05:00
Jiří Polášek
c4e96c7ee9 CmdPal: Add hints about bookmark placeholders to the Add/Edit Bookmark form (#42793)
## Summary of the Pull Request

This PR adds a short explanation to the Add/Edit Bookmark form,
describing how to use placeholders in bookmark URLs or paths

<img width="823" height="525" alt="image"
src="https://github.com/user-attachments/assets/b66adfc4-2dbc-4934-8796-9d4ad46d9f5f"
/>

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

- [x] Closes: #42265 
- [ ] **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-10-28 14:28:46 -05:00
Jiří Polášek
103429b4d7 CmdPal: Add hidden window as owner for tool windows (#42902)
## Summary of the Pull Request

This PR changes the method used to hide tool windows from the taskbar
and Alt+Tab to a more reliable approach.
Previously, this was achieved by adding `WS_EX_TOOLWINDOW` to an unowned
top-level window, which proved unreliable in several scenarios.

The new implementation assigns a hidden window as the owner of each tool
window.
This ensures that the window does not appear on the taskbar even when
the Windows setting
**Settings → System → Multitasking → On the taskbar, show all opened
windows** is set to **On all desktops**.

## Change log one-liner

Fixes Command Palette windows occasionally appearing on the taskbar
under certain system settings.

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

- [x] Closes: #42395
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** Added/updated
- [x] **New binaries:** none
- [x] **Documentation updated:** no need

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

Tested alongside the stable CmdPal on a system with
2025-10-28 14:26:01 -05:00
Jaylyn Barbee
01fb831e4e [Light Switch] Light Switch should detect changes in Windows Settings and treat as manual override (same as using shortcut) (#42882)
<!-- 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 ensures that Light Switch detects changes to app/system theme
from Windows Settings. This PR also introduces new behavior where
switching the schedule will cause an instant update to the theme if
necessary.

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

- [x] Closes: #42878
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
2025-10-28 12:00:38 -07:00
Sam Rueby
d197af3da9 CmdPal's search bar now accepts page up/down keyboard strokes. (#41886)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
The page up/down keys now function while the search box is focused.

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

- [ X ] Closes: #41877
- [ ] **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
Previously, the page up/down keys only performed any action while an
item in the list was focused. The page up/down keys did not have any
effect while the search box was focused, however the up/down arrows do
have effect. This PR enables the page up/down keys while the search box
is focused.

There is a caveat here. The page up/down behavior is not consistent. I
do not see a way to tell the ListView to perform its native page up/down
function. Instead, I manually calculate roughly which item to scroll-to.
Because of this, the amount of scroll between when the search box is
focused and when an item in the ListView is focused is not consistent.

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


![pageupdown](https://github.com/user-attachments/assets/b30f6e4e-03de-45bd-8570-0b06850bef24)
In this GIF:
1. CmdPal appears
2. SearchBar focused, down/up arrow keys.
3. SearchBar focused, page down/up keys.
4. Tab to item in ListView
5. ListView item focused down/up arrow keys.
6. ListView item focused page down/up keys.
7. SearchBar focused
8. Filter "abc"
9. SearchBar focused page down/up keys.
2025-10-28 12:56:17 -05:00
Jiří Polášek
a4791cc493 CmdPal: Ensure CommandItemViewModel reacts to changes of replaced Command (#42982)
## Summary of the Pull Request

This PR resolves the issue with CommandItemViewModel's subscription to
changes in the associated Command when it gets replaced by another
Command. The current implementation removes the handler from the old
command but fails to attach a new one.

## Change log one-liner


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

- [x] Closes: #42981 
- [ ] **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-10-28 11:22:09 -05:00
leileizhang
1a1894472a Fix package identity build issue (#43019)
<!-- 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
<img width="1988" height="360" alt="image"
src="https://github.com/user-attachments/assets/85d994a9-3799-4f16-a836-85ca264b873c"
/>

Add an EntryPoint to the AppxManifest to fix the issue.
<!-- 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-10-28 17:16:03 +08:00
Jiří Polášek
5d6f96559c Multiple toys: Exclude TitleBars from tab navigation (#42030)
## Summary of the Pull Request

This PR removes title bar controls from tab navigation, solving one of
hidden tab stops (the other being #40637).

Affected apps:
- Command Palette - Settings
- Environment Variables
- File Locksmith
- Hosts File Editor
- Registry Preview
- Settings (the search box in the title bar is still tab navigable)

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

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

All apps were manually tests, by pressing tab and shift + tab.

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
2025-10-28 09:40:20 +08:00
Kai Tao
b774e13176 Fix the foreground style for find my mouse (#42865)
<!-- 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

Find my mouse should use full transparent window

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

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

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

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


https://github.com/user-attachments/assets/75c73eb3-04bb-438c-8823-3c9f18923cc6
2025-10-27 09:33:26 +08:00
Jiří Polášek
e256e79685 CmdPal: Fix search box text selection in ShellPage.GoHome (#42937)
## Summary of the Pull Request

This PR fixes an issue where `ShellPage.GoHome` wouldn’t select the
search box text when the current page was already the home page.

In that case, the navigation stack was empty, and no code was executed
because focusing the text had been delegated to the `GoBack` operation.

## Change log one-liner

Ensured search text is selected when Go home when activated and
Highlight search on activate are both enabled.

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

- [x] Closes: #42443
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** Added/updated
- [x] **New binaries:** Added on the required places
- [x] **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-10-25 19:51:53 -05:00
Jiří Polášek
623c804093 ManagedCommon: Log correct HRESULT for the inner exception (#42178)
## Summary of the Pull Request

This PR fixes incorrect HRESULT for inner exception when an error is
logged.

<!-- 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-10-25 19:50:07 -05:00
Michael Jolley
20188bda9b File search now has filters (#42141)
Closes #39260

Search for all files & folders, folders only, or files only.

Enjoy.


https://github.com/user-attachments/assets/43ba93f5-dfc5-4e73-8414-547cf99dcfcf
2025-10-24 19:16:21 -05:00
Jiří Polášek
6e5ad11bc3 CmdpPal: SearchBox visibility and async loading race (#42783)
## Summary of the Pull Request

This PR introduces two related fixes to improve the stability and
reliability of navigation and search UI behavior in the shell:

- **Ensure search box visibility is correctly updated**  
- `ShellViewModel` previously set `IsSearchBoxVisible` after navigation
to the page, but didn’t update it when the value changed. While the
value isn’t expected to change dynamically, the property initialization
is asynchronous, which could cause a race condition.
- As a defensive measure, this also changes the default value of
uninitialized property to make it visible by default.

- **Cancel asynchronous focus placement if navigation changes**  
- Ensures that any pending asynchronous focus operation is cancelled
when another navigation occurs before it completes.


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

- [x] Closes: #42782
- [ ] **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-10-24 19:15:34 -05:00
Jiří Polášek
0e36e7e7a7 CmdPal: Add keyboard shortcut (Ctrl+,) to open Settings (#42787)
## Summary of the Pull Request

This PR introduces a new keyboard shortcut `Ctrl + ,` that opens the
Settings window directly.


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

- [x] Closes: #42785 
- [ ] **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-10-24 19:12:59 -05:00
moooyo
82dc4cdc18 [CmdPal] Replace complex cancellation token mechanism with a simple task queue. (#42356)
<!-- 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
Just consider user are trying to search a long name such as "Visual
Studio Code"
The old mechanism:
User input: V
Task: V
Then input: i
Task cancel for V and start task i
etc...

The problem is:
1. I don't think we can really cancel the most time-cost part (Find
packages from WinGet).
2. User cannot see anything before they really end the input and the
last task complete. UX exp is so bad.
3. It's so complex to maintain. Hard to understand for the new
contributor.

New mechanism:
User input: V
Task: V
Then input: i 
Prev Task is still running but mark the next task is i
Input: s
Prev Task is still running but override the next task to s
etc...

We can get:
1. User can see some results if prev task complete.
2. It's simple to understand
3. The extra time cost I think will not too much. Because we ignored the
middle input.

Compare:


https://github.com/user-attachments/assets/f45f4073-efab-4f43-87f0-f47b727f36dc



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

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

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

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

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-10-24 14:28:30 -05:00
Kai Tao
c71fdca277 Hybrid CRT for powertys (#42073)
<!-- 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-->
Hybrid CRT across powertoys for better bundle size

## 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
Bundle Size comparasion:

| bundle        | Before Hybrid CRT | After Hybrid CRT | diff |
|---------------|-------------------|------------------|------|
| x64-user      | 317M              | 310M             | 7M   |
| x64-machine   | 317M              |310M             | 7M   |
| arm64-user    | 305M              | 299M             | 6M   |
| arm64-machine | 305M              | 299M             | 6M   |


Did verification on a sandbox machine, every module launches as
expected, no dependency issue
2025-10-24 10:24:39 +08:00
Jiří Polášek
a69f7fa806 CmdPal: Update top-level item view model to reflect change of the associated command (#42806)
## Summary of the Pull Request

This PR implements a fix that ensures the top-level command's alias,
hotkey, and tags are automatically updated whenever the associated
command is modified, as the command defines the actual identity of the
item.

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

- [x] Closes: #42796
- [x] Related to: #42807
- [ ] **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-10-23 16:57:12 -05:00
Jiří Polášek
cd5f753140 CmdPal: Migrate bookmarks manually (#42814)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR fixes the migration of bookmarks from versions prior to 0.95,
resolving an issue where hotkeys and aliases wouldn’t persist on
bookmarks created with Command Palette 0.94 or earlier.

It removes ID auto-fixing from `BookmarkData` in favor of an explicit
migration step handled by `BookmarkManager`.

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

- [x] Closes: #42796
- [ ] **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-10-23 16:53:06 -05:00
Clint Rutkas
c628b4901d Added open tag to default expand open (#42824)
By default, none of the 4 methods are expanded. this will expand the top
item by default for users.

<img width="974" height="414" alt="image"
src="https://github.com/user-attachments/assets/f328dee6-9206-47a1-b45f-1f951142eef4"
/>
2025-10-23 22:07:16 +02:00
Jaylyn Barbee
c6c7bfb861 Updated installer hashes for 0.95.1 (#42820)
Title, updating installer links for next release.
2025-10-23 12:18:45 -04:00
Mike Hall
d64f06906c Enable switching to and from MousePointerCrosshairs and Gliding Cursor (#42105)
## Summary of the Pull Request
This PR enables a user to switch between Mouse Pointer Crosshairs and
Gliding Cursor (or the other way round!). The primary change is to the
underlying state machine that's shared between Mouse Pointer Crosshairs
and Gliding Cursor, both are implemented in the same Mouse Module.

## PR Checklist

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

## Detailed Description of the Pull Request / Additional comments
See above - this is primarily a change to the shared state machine
between Mouse Pointer Crosshairs and Gliding Cursor - this change
enables transition between Mouse Pointer Crosshairs and Gliding Cursor,
the underlying state is reset when a user transitions from Gliding to
Mouse Pointer and back again.

## Validation Steps Performed
Validation on a Windows Surface Laptop 7 Pro for the following states.
- Mouse Pointer Crosshairs and Gliding Cursor NOT active -
enable/disable Mouse Pointer Crosshairs
- Mouse Pointer Crosshairs and Gliding Cursor NOT active - enable/step
states for Gliding Cursor
- Activate and disable Mouse Pointer Crosshairs
- Activate and step through Gliding Cursor
- Mouse Pointer Crosshairs Active - Switch to Gliding Cursor
- Gliding Cursor Active - Switch to Mouse Pointer Crosshairs
2025-10-23 18:21:01 +08:00
Kai Tao
c26dfef81b Find My Mouse: Cursor should not go busy & window should not be active (#42795)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fix two issue in find my mouse:
 #42758  #42765 
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #42758 and #42765
- [ ] **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
| Problem | Before | After this fix |
|----------|---------|----------------|
| Mouse Loading status | html<br><video
src="https://github.com/user-attachments/assets/a07b3fe7-a9e1-4176-a33f-4836bf7f7e0a"
controls width="300"></video> | html<br><video
src="https://github.com/user-attachments/assets/a78848b0-6f34-4d68-b470-a4a3adbd8a5e"
controls width="300"></video> |
| Current window lose focus | html<br><video
src="https://github.com/user-attachments/assets/5122262c-6c98-4fe5-a107-5db2d96a6341"
controls width="300"></video> | html<br><video
src="https://github.com/user-attachments/assets/b12391c8-d148-4498-b37f-c8c1bc9e381a"
controls width="300"></video> |


The window lose focus test: Currently after activate the find my mouse,
the window lose focus, after the fix, foreground window is still the
focused window, And my keystroke will directly apply in the foreground
window.
Maybe hard to see in the video.
2025-10-23 16:20:40 +08:00
Mason Bergstrom
dd420509ab [Mouse Without Borders] Adding Horizontal Scrolling Support (#42179)
## Summary of the Pull Request
Added support for horizontal scrolling to Mouse Without Borders, instead
of being a no-op.

## PR Checklist

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

## Detailed Description of the Pull Request / Additional comments
Works in a backward compatible fashion, continuing to be a no-op when
forwarded to an older version, but works once both devices are updated.

## Validation Steps Performed
Built on two separate devices that are paired with each other. First
tested with one device updated and one on the old code, confirming
backwards compatibility support. Second tested both devices updated,
confirming horizontal scroll is now working on remote device.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-23 12:06:33 +08:00
Jaylyn Barbee
2c4aab9d87 Light Switch Hotfixes v2 (#42774)
Should fix: #42627
Issue: Suntimes not updating within the day if the mode changes to
SunsetToSunrise
Fix: Update suntimes in the service if the mode is changed to Sun mode.

Other: small bug fixes (brackets, etc)
2025-10-22 11:59:36 -07:00
Dustin L. Howett
52ce33d438 build: run all builds out of a new (huge) P:\ drive (#42739)
The new agents have much smaller temporary disks (which I verified with
another test run to clock in at about 87GB). For right now, let's move
our release builds to a larger drive while we troubleshoot.
2025-10-21 18:32:34 -05:00
Gordon Lam
fc1307418e Update Copilot guidance and PR prompt workflow (This PR summary and commit are generated accordingly) (#42634)
## Summary of the Pull Request
- Added new Copilot agent prompts in
`.github/prompts/create-commit-title.prompt.md`,
`.github/prompts/create-pr.prompt.md`, and
`.github/prompts/fix-spelling.prompt.md` to streamline contributor
workflows.
- Refreshed `.github/copilot-instructions.md` with front matter and
clarified build/test guidance.
And added a new section titled `Language Style Rules` to help reduce
style issues in auto-generated code.

## Screenshot
a. For the new prompts, in VSCode, just type "/" and its name
<img width="477" height="124" alt="image"
src="https://github.com/user-attachments/assets/37c7330d-6fdc-45b4-9030-95920920c964"
/>
<img width="446" height="105" alt="image"
src="https://github.com/user-attachments/assets/55d3bb49-46cc-4441-9fb5-fed3a22983d9"
/>
b. All the spelling check problem, PR description, Commit title, are
generated and fixed by above prompts as demonstration (Except this
section)

## PR Checklist
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** N/A on doc updates.
- [x] **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 (N/A)

## Detailed Description of the Pull Request / Additional comments
- Introduced prompt definitions so local tooling can generate commit
titles, PR descriptions, and resolve spell-check alerts using consistent
instructions.
- Added GitHub CLI prerequisites inside
`.github/prompts/fix-spelling.prompt.md` to ensure `gh` commands succeed
for new contributors.
- Converted `.github/copilot-instructions.md` to YAML-front-matter
format while aligning headings and terminology with current contributor
guidance.

## Validation Steps Performed
- No automated tests were run; changes are documentation and
configuration only.
2025-10-21 16:05:16 +08:00
Jaylyn Barbee
f45d54abdf [Light Switch] Hotfixes (#42434)
<!-- 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
Adds new "Off" mode for the schedule mode options which disable the
schedule.
Adds explicit function to disable light switch by default.

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

Closes: 
- #42402 
New behavior: "Off" mode added. when off, the regular service loop stops
and all actions are event driven to either resume the loop or listen for
hotkey.
- #42386
New behavior: Disabled explicitly by default
- #42389
New behavior: When switching from dark to light mode the system theme
will remove the accent color.
- #42513
New behavior: Manual mode no longer gets reset. It was being overridden
by the sun calculations that were invertedly running when in manual
mode.

Todo:
- [ ] **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 @alvinashcraft we will need to add this new mode
to the documentation.

## Validation Steps Performed
- Removed all default settings and tested new logic. Light Switch is set
to off by default.
- Updated UI and tested new "Off" mode, logs indicate mode switched and
ticker stopped. Polling resumes on mode change. (need to check that the
shortcut still works)

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
2025-10-20 15:57:03 -07:00
349 changed files with 20023 additions and 2421 deletions

View File

@@ -7,6 +7,13 @@ body:
- type: markdown
attributes:
value: Please make sure to [search for existing issues](https://github.com/microsoft/PowerToys/issues) before filing a new one!
- type: markdown
attributes:
value: |
We are aware of the following high-volume issues and are actively working on them. Please check if your issue is one of these before filing a new bug report:
* **PowerToys Run crash related to "Desktop composition is disabled"**: This may appear as `COMException: 0x80263001`. For more details, see issue [#31226](https://github.com/microsoft/PowerToys/issues/31226).
* **PowerToys Run crash with `COMException (0xD0000701)`**: For more details, see issue [#30769](https://github.com/microsoft/PowerToys/issues/30769).
* **PowerToys Run crash with a "Cyclic reference" error**: This `System.InvalidOperationException` is detailed in issue [#36451](https://github.com/microsoft/PowerToys/issues/36451).
- id: version
type: input
attributes:

View File

@@ -321,3 +321,10 @@ REGSTR
# Misc Win32 APIs and PInvokes
INVOKEIDLIST
# PowerRename metadata pattern abbreviations (used in tests and regex patterns)
DDDD
FFF
HHH
riday
YYY

View File

@@ -22,6 +22,7 @@ ADate
ADDSTRING
ADDUNDORECORD
ADifferent
adjacents
ADMINS
adml
admx
@@ -34,6 +35,7 @@ AFX
AGGREGATABLE
AHK
AHybrid
AIUI
akv
ALarger
ALIGNRIGHT
@@ -45,6 +47,7 @@ Allmodule
ALLOWUNDO
ALLVIEW
ALPHATYPE
amazonbedrock
AModifier
amr
ANDSCANS
@@ -94,8 +97,10 @@ ASSOCSTR
ASYNCWINDOWPLACEMENT
ASYNCWINDOWPOS
atl
ATX
ATRIOX
aumid
authenticode
Authenticode
AUTOBUDDY
AUTOCHECKBOX
@@ -110,9 +115,13 @@ AValid
AWAYMODE
azcliversion
azman
azureaiinference
azureinference
azureopenai
bbwe
BCIE
bck
backticks
BESTEFFORT
bezelled
bhid
@@ -140,6 +149,7 @@ bmi
BNumber
BODGY
BOklab
Bootstrappers
BOOTSTRAPPERINSTALLFOLDER
BOTTOMALIGN
boxmodel
@@ -167,9 +177,12 @@ BYPOSITION
CALCRECT
CALG
callbackptr
cabstr
calpwstr
caub
Cangjie
CANRENAME
Carlseibert
Canvascustomlayout
CAPTUREBLT
CAPTURECHANGED
@@ -197,6 +210,7 @@ CIBUILD
cidl
CIELCh
cim
claude
CImage
cla
CLASSDC
@@ -229,6 +243,7 @@ CODENAME
codereview
Codespaces
Coen
cognitiveservices
COINIT
colid
colorconv
@@ -268,12 +283,14 @@ countof
covrun
cpcontrols
cph
cppcoreguidelines
cplusplus
CPower
cpptools
cppvsdbg
cppwinrt
createdump
creativecommons
CREATEPROCESS
CREATESCHEDULEDTASK
CREATESTRUCT
@@ -297,6 +314,8 @@ CURRENTDIR
CURSORINFO
cursorpos
CURSORSHOWING
CURSORWRAP
CursorWrap
customaction
CUSTOMACTIONTEST
CUSTOMFORMATPLACEHOLDER
@@ -336,6 +355,7 @@ Deact
debugbreak
decryptor
Dedup
dfx
Deduplicator
Deeplink
DEFAULTBOOTSTRAPPERINSTALLFOLDER
@@ -410,6 +430,7 @@ DROPFILES
DSTINVERT
DString
DSVG
dto
DTo
DUMMYUNIONNAME
dutil
@@ -509,10 +530,12 @@ EXTRINSICPROPERTIES
eyetracker
FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
FNumber
FARPROC
fdx
fesf
FFFF
Figma
FILEEXPLORER
fileexploreraddons
fileexplorerpreview
@@ -539,6 +562,7 @@ FIXEDSYS
flac
flyouts
FMask
foundrylocal
fmtid
FOF
FOFX
@@ -603,6 +627,8 @@ GValue
gwl
GWLP
GWLSTYLE
googleai
googlegemini
hangeul
Hanzi
Hardlines
@@ -665,6 +691,7 @@ hmonitor
homies
homljgmgpmcbpjbnjpfijnhipfkiclkd
HOOKPROC
huggingface
HORZRES
HORZSIZE
Hostbackdropbrush
@@ -691,6 +718,7 @@ HTCLIENT
hthumbnail
HTOUCHINPUT
HTTRANSPARENT
hutchinsoniana
HVal
HValue
Hvci
@@ -712,7 +740,9 @@ IDCANCEL
IDD
idk
idl
IIM
idlist
ifd
IDOK
IDOn
IDR
@@ -729,6 +759,7 @@ Ijwhost
ILD
IMAGEHLP
IMAGERESIZERCONTEXTMENU
IPTC
IMAGERESIZEREXT
imageresizerinput
imageresizersettings
@@ -747,7 +778,7 @@ INITDIALOG
INITGUID
INITTOLOGFONTSTRUCT
INLINEPREFIX
Inlines
inlines
INPC
inproc
INPUTHARDWARE
@@ -868,6 +899,7 @@ LOCKTYPE
LOGFONT
LOGFONTW
logon
lon
LOGMSG
LOGPIXELSX
LOGPIXELSY
@@ -959,6 +991,7 @@ MENUITEMINFOW
MERGECOPY
MERGEPAINT
Metadatas
metadatamatters
metafile
mfc
Mgmt
@@ -1026,6 +1059,7 @@ msiexec
MSIFASTINSTALL
MSIHANDLE
MSIRESTARTMANAGERCONTROL
MSIs
msixbundle
MSIXCA
MSLLHOOKSTRUCT
@@ -1117,9 +1151,11 @@ NONCLIENTMETRICSW
NONELEVATED
nonspace
nonstd
nullrefs
NOOWNERZORDER
NOPARENTNOTIFY
NOPREFIX
NPU
NOREDIRECTIONBITMAP
NOREDRAW
NOREMOVE
@@ -1179,6 +1215,9 @@ opencode
OPENFILENAME
opensource
openxmlformats
ollama
Olllama
onnx
OPTIMIZEFORINVOKE
ORPHANEDDIALOGTITLE
ORSCANS
@@ -1273,6 +1312,7 @@ pnid
PNMLINK
Poc
Podcasts
Photoshop
POINTERID
POINTERUPDATE
Pokedex
@@ -1367,6 +1407,8 @@ pwsz
pwtd
QDC
qit
QNN
Qualcomm
QITAB
QITABENT
qoi
@@ -1475,6 +1517,7 @@ sacl
safeprojectname
SAMEKEYPREVIOUSLYMAPPED
SAMESHORTCUTPREVIOUSLYMAPPED
samsung
sancov
SAVEFAILED
scanled
@@ -1837,6 +1880,7 @@ USEINSTALLERFORTEST
USESHOWWINDOW
USESTDHANDLES
USRDLL
utm
UType
uuidv
uwp
@@ -1913,6 +1957,7 @@ wcsicmp
wcsncpy
wcsnicmp
WCT
WCRAPI
WDA
wdm
wdp
@@ -1926,6 +1971,7 @@ wgpocpl
WHEREID
wic
wifi
wikimedia
wikipedia
WIL
winapi
@@ -1955,6 +2001,8 @@ WINL
winlogon
winmd
WINNT
windowsml
winml
winres
winrt
winsdk
@@ -2020,7 +2068,9 @@ XAxis
XButton
xclip
xcopy
xap
XDeployment
XDimension
xdf
XDocument
XElement
@@ -2038,6 +2088,7 @@ xsi
XSpeed
XStr
xstyler
xmp
XTimer
XUP
XVIRTUALSCREEN
@@ -2045,6 +2096,7 @@ xxxxxx
YAxis
ycombinator
YIncrement
YDimension
yinle
yinyue
YPels

View File

@@ -1,43 +1,59 @@
# PowerToys Copilot guide (concise)
---
description: PowerToys AI contributor guidance.
applyTo: pullRequests
---
# PowerToys - Copilot guide (concise)
This is the top-level guide for AI changes. Keep edits small, follow existing patterns, and cite exact paths in PRs.
Repo map (1line per area)
# Repo map (1-line per area)
- Core apps: `src/runner/**` (tray/loader), `src/settings-ui/**` (Settings app)
- Shared libs: `src/common/**`
- Modules: `src/modules/*` (one per utility; Command Palette in `src/modules/cmdpal/**`)
- Build tools/docs: `tools/**`, `doc/devdocs/**`
Build and test (defaults)
# Build and test (defaults)
- Prerequisites: Visual Studio 2022 17.4+, minimal Windows 10 1803+.
- Build discipline:
- One terminal per operation (build test). Dont switch/open new ones mid-flow.
- One terminal per operation (build -> test). Do not switch or open new ones mid-flow.
- After making changes, `cd` to the project folder that changed (`.csproj`/`.vcxproj`).
- Use script(s) to build, synchronously block and wait in foreground for it to finish: `tools/build/build.ps1|.cmd` (current folder), `build-essentials.*` (once per brand new build for missing nuget packages)
- Treat build **exit code 0** as success; any non-zero exit code is a failure, have Copilot read the errors log in the build folder (e.g., `build.*.*.errors.log`) and surface problems.
- Dont start tests or launch Runner until the previous step succeeded.
- Tests (fast + targeted):
- Find the test project by product code prefix (e.g., FancyZones, AdvancedPaste). Look for a sibling folder or 12 levels up named like `<Product>*UnitTests` or `<Product>*UITests`.
- Build the test project, wait for **exit**, then run only those tests via VS Test Explorer or `vstest.console.exe` with filters. Avoid `dotnet test` in this repo.
- Add/adjust tests when changing behavior; if skipped, state why (e.g., comment-only, string rename).
- Use scripts to build, synchronously block and wait in foreground for completion: `tools/build/build.ps1|.cmd` (current folder), `build-essentials.*` (once per brand new build for missing nuget packages).
- Treat build exit code 0 as success; any non-zero exit code is a failure. Read the errors log in the build folder (such as `build.*.*.errors.log`) and surface problems.
- Do not start tests or launch Runner until the previous step succeeded.
- Tests (fast and targeted):
- Find the test project by product code prefix (for example FancyZones, AdvancedPaste). Look for a sibling folder or one to two levels up named like `<Product>*UnitTests` or `<Product>*UITests`.
- Build the test project, wait for exit, then run only those tests via VS Test Explorer or `vstest.console.exe` with filters. Avoid `dotnet test` in this repo.
- Add or adjust tests when changing behavior; if skipped, state why (for example comment-only or string rename).
Pull requests (expectations)
- Atomic: one logical change; no driveby refactors.
- Describe: problem / approach / risk / test evidence.
# Pull requests (expectations)
- Atomic: one logical change; no drive-by refactors.
- Describe: problem, approach, risk, test evidence.
- List: touched paths if not obvious.
When to ask for clarification
# When to ask for clarification
- Ambiguous spec after scanning relevant docs (see below).
- Cross-module impact (shared enum/struct) not clear.
- Security / elevation / installer changes.
- Cross-module impact (shared enum or struct) not clear.
- Security, elevation, or installer changes.
Logging (use existing stacks)
- C++: `src/common/logger/**` (`Logger::info|warn|error|debug`). Keep hot paths quiet (hooks, tight loops).
- C#: `ManagedCommon.Logger` (`LogInfo|LogWarning|LogError|LogDebug|LogTrace`). Some UIs use injected `ILogger` via `LoggerInstance.Logger`.
# Logging (use existing stacks)
- C++ logging lives in `src/common/logger/**` (`Logger::info`, `Logger::warn`, `Logger::error`, `Logger::debug`). Keep hot paths quiet (hooks, tight loops).
- C# logging goes through `ManagedCommon.Logger` (`LogInfo`, `LogWarning`, `LogError`, `LogDebug`, `LogTrace`). Some UIs use injected `ILogger` via `LoggerInstance.Logger`.
Docs to consult
# Docs to consult
- `tools/build/BUILD-GUIDELINES.md`
- `doc/devdocs/core/architecture.md`, `doc/devdocs/core/runner.md`, `doc/devdocs/core/settings/readme.md`, `doc/devdocs/modules/readme.md`
- `doc/devdocs/core/architecture.md`
- `doc/devdocs/core/runner.md`
- `doc/devdocs/core/settings/readme.md`
- `doc/devdocs/modules/readme.md`
Done checklist (self review before finishing)
- Build clean? Tests updated/passed? No unintended formatting? Any new dependency? Documented skips?
# Language style rules
- Always enforce repo analyzers: root `.editorconfig` plus any `stylecop.json`.
- C# code follows StyleCop.Analyzers and Microsoft.CodeAnalysis.NetAnalyzers.
- C++ code honors `.clang-format` plus `.clang-tidy` (modernize/cppcoreguidelines/readability).
- Markdown files wrap at 80 characters and use ATX headers with fenced code blocks that include language tags.
- YAML files indent two spaces and add comments for complex settings while keeping keys clear.
- PowerShell scripts use Verb-Noun names and prefer single-quoted literals while documenting parameters and satisfying PSScriptAnalyzer.
# Done checklist (self review before finishing)
- Build clean? Tests updated or passed? No unintended formatting? Any new dependency? Documented skips?

View File

@@ -0,0 +1,16 @@
---
mode: 'agent'
model: GPT-5-Codex (Preview)
description: 'Generate an 80-character git commit title for the local diff.'
---
**Goal:** Provide a ready-to-paste git commit title (<= 80 characters) that captures the most important local changes since `HEAD`.
**Workflow:**
1. Run a single command to view the local diff since the last commit:
```@terminal
git diff HEAD
```
2. From that diff, identify the dominant area (reference key paths like `src/modules/*`, `doc/devdocs/**`, etc.), the type of change (bug fix, docs update, config tweak), and any notable impact.
3. Draft a concise, imperative commit title summarizing the dominant change. Keep it plain ASCII, <= 80 characters, and avoid trailing punctuation. Mention the primary component when obvious (for example `FancyZones:` or `Docs:`).
4. Respond with only the final commit title on a single line so it can be pasted directly into `git commit`.

View File

@@ -0,0 +1,22 @@
---
mode: 'agent'
model: GPT-5-Codex (Preview)
description: 'Generate a PowerToys-ready pull request description from the local diff.'
---
**Goal:** Produce a ready-to-paste PR title and description that follows PowerToys conventions by comparing the current branch against a user-selected target branch.
**Repo guardrails:**
- Treat `.github/pull_request_template.md` as the single source of truth; load it at runtime instead of embedding hardcoded content in this prompt.
- Preserve section order from the template but only surface checklist lines that are relevant for the detected changes, filling them with `[x]`/`[ ]` as appropriate.
- Cite touched paths with inline backticks, matching the guidance in `.github/copilot-instructions.md`.
- Call out test coverage explicitly: list automated tests run (unit/UI) or state why they are not applicable.
**Workflow:**
1. Determine the target branch from user context; default to `main` when no branch is supplied.
2. Run `git status --short` once to surface uncommitted files that may influence the summary.
3. Run `git diff <target-branch>...HEAD` a single time to review the detailed changes. Only when confidence stays low dig deeper with focused calls such as `git diff <target-branch>...HEAD -- <path>`.
4. From the diff, capture impacted areas, key file changes, behavioral risks, migrations, and noteworthy edge cases.
5. Confirm validation: list tests executed with results or state why tests were skipped in line with repo guidance.
6. Load `.github/pull_request_template.md`, mirror its section order, and populate it with the gathered facts. Include only relevant checklist entries, marking them `[x]/[ ]` and noting any intentional omissions as "N/A".
7. Present the filled template inside a fenced ```markdown code block with no extra commentary so it is ready to paste into a PR, clearly flagging any placeholders that still need user input.

22
.github/prompts/fix-spelling.prompt.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
mode: 'agent'
model: GPT-5-Codex (Preview)
description: 'Resolve Code scanning / check-spelling comments on the active PR.'
---
**Goal:** Clear every outstanding GitHub pull request comment created by the `Code scanning / check-spelling` workflow by explicitly allowing intentional terms.
**Guardrails:**
- Update only discussion threads authored by `github-actions` or `github-actions[bot]` that mention `Code scanning results / check-spelling`.
- Resolve findings solely by editing `.github/actions/spell-check/expect.txt`; reuse existing entries.
- Leave all other files and topics untouched.
**Prerequisites:**
- Install GitHub CLI if it is not present: `winget install GitHub.cli`.
- Run `gh auth login` once before the first CLI use.
**Workflow:**
1. Determine the active pull request with a single `gh pr view --json number` call (default to the current branch).
2. Fetch all PR discussion data once via `gh pr view --json comments,reviews` and filter to check-spelling comments authored by `github-actions` or `github-actions[bot]` that are not minimized; when several remain, process only the most recent comment body.
3. For each flagged token, review `.github/actions/spell-check/expect.txt` for an equivalent term (for example an existing lowercase variant); when found, reuse that normalized term rather than adding a new entry, even if the flagged token differs only by casing. Only add a new entry after confirming no equivalent already exists.
4. Add any remaining missing token to `.github/actions/spell-check/expect.txt`, keeping surrounding formatting intact.

View File

@@ -5,7 +5,6 @@
{
"MatchedPath": [
"*.resources.dll",
"WinUI3Apps\\Assets\\Settings\\Scripts\\*.ps1",
"PowerToys.ActionRunner.exe",
@@ -27,6 +26,7 @@
"PowerToys.GPOWrapper.dll",
"PowerToys.GPOWrapperProjection.dll",
"PowerToys.AllExperiments.dll",
"LanguageModelProvider.dll",
"Common.Search.dll",
@@ -181,6 +181,7 @@
"PowerToys.MousePointerCrosshairs.dll",
"PowerToys.MouseJumpUI.dll",
"PowerToys.MouseJumpUI.exe",
"PowerToys.CursorWrap.dll",
"PowerToys.MouseWithoutBorders.dll",
"PowerToys.MouseWithoutBorders.exe",
@@ -346,6 +347,8 @@
"Testably.Abstractions.FileSystem.Interface.dll",
"WinUI3Apps\\Testably.Abstractions.FileSystem.Interface.dll",
"ColorCode.Core.dll",
"Microsoft.SemanticKernel.Connectors.Ollama.dll",
"OllamaSharp.dll",
"UnitsNet.dll",
"UtfUnknown.dll",

View File

@@ -1,53 +0,0 @@
{
"Version": "1.0.0",
"UseMinimatch": false,
"SignBatches": [
{
"MatchedPath": [
"PowerToysSetupCustomActionsVNext.dll",
"SilentFilesInUseBAFunction.dll",
"PowerToys*Setup-*.exe",
"PowerToys*Setup-*.msi"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
}
]
}

View File

@@ -32,7 +32,7 @@ parameters:
- name: enableMsBuildCaching
type: boolean
displayName: "Enable MSBuild Caching"
default: true
default: false
- name: runTests
type: boolean
displayName: "Run Tests"

View File

@@ -73,10 +73,11 @@ extends:
parameters:
pool:
name: SHINE-INT-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
${{ else }}:
image: SHINE-VS17-Latest
demands:
# Our INT agents have a large disk mounted at P:\
- WorkFolder -equals P:\_work
- ${{ if eq(parameters.useVSPreview, true) }}:
- ImageOverride -equals SHINE-VS17-Preview
os: windows
variables:
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations

View File

@@ -266,6 +266,26 @@ jobs:
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: VSBuild@1
displayName: Generate DSC artifacts for ARM64
condition: and(succeeded(), eq(variables['BuildPlatform'], 'arm64'))
inputs:
solution: PowerToys.sln
vsVersion: 17.0
msbuildArgs: >-
-restore
/p:Configuration=$(BuildConfiguration)
/p:Platform=x64
/t:DSC\PowerToys_Settings_DSC_Schema_Generator
/bl:$(LogOutputDirectory)\build-dsc-generator.binlog
${{ parameters.additionalBuildOptions }}
$(MSBuildCacheParameters)
$(RestoreAdditionalProjectSourcesArg)
platform: x64
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
# Build PowerToys.DSC.exe for ARM64 (x64 uses existing binary from previous build)
- task: VSBuild@1
displayName: Build PowerToys.DSC.exe (x64 for generating manifests)
@@ -512,14 +532,6 @@ jobs:
versionNumber: ${{ parameters.versionNumber }}
additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
- template: steps-build-installer-vnext.yml
parameters:
codeSign: ${{ parameters.codeSign }}
signingIdentity: ${{ parameters.signingIdentity }}
versionNumber: ${{ parameters.versionNumber }}
additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
buildUserInstaller: true # NOTE: This is the distinction between the above and below rules
# This saves ~1GiB per architecture. We won't need these later.
# Removes:
# - All .pdb files from any static libs .libs (which were only used during linking)

View File

@@ -2,9 +2,6 @@ parameters:
- name: versionNumber
type: string
default: "0.0.1"
- name: buildUserInstaller
type: boolean
default: false
- name: codeSign
type: boolean
default: false
@@ -25,43 +22,26 @@ steps:
arguments: 'install --global wix --version 5.0.2'
- pwsh: |-
& git clean -xfd -e *exe -- .\installer\
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Clean installer to reduce cross-contamination
- pwsh: |-
# Determine whether this is a per-user build
$IsPerUser = $${{ parameters.buildUserInstaller }}
# Build slug used to locate the artifacts
$InstallerBuildSlug = if ($IsPerUser) { 'UserSetup' } else { 'MachineSetup' }
# VNext bundle folder; base name intentionally omits the VNext suffix
$InstallerFolder = 'PowerToysSetupVNext'
if ($IsPerUser) {
$InstallerBasename = "PowerToysUserSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
}
else {
$InstallerBasename = "PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
}
# Export variables for downstream steps
Write-Host "##vso[task.setvariable variable=InstallerBuildSlug]$InstallerBuildSlug"
Write-Host "##vso[task.setvariable variable=InstallerRelativePath]$(BuildPlatform)\$(BuildConfiguration)\$InstallerBuildSlug"
Write-Host "##vso[task.setvariable variable=InstallerBasename]$InstallerBasename"
Write-Host "##vso[task.setvariable variable=InstallerFolder]$InstallerFolder"
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Prepare Installer variables
Write-Host "##vso[task.setvariable variable=InstallerMachineRoot]installer\PowerToysSetupVNext\$(BuildPlatform)\$(BuildConfiguration)\MachineSetup"
Write-Host "##vso[task.setvariable variable=InstallerUserRoot]installer\PowerToysSetupVNext\$(BuildPlatform)\$(BuildConfiguration)\UserSetup"
Write-Host "##vso[task.setvariable variable=InstallerMachineBasename]PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
Write-Host "##vso[task.setvariable variable=InstallerUserBasename]PowerToysUserSetup-${{ parameters.versionNumber }}-$(BuildPlatform)"
displayName: Prepare Installer variables
# This dll needs to be built and signed before building the MSI.
# The Custom Actions project contains a pre-build event that prepares the .wxs files
# by filling them out with all our components. We pass RunBuildEvents=true to force
# that logic to run.
- task: VSBuild@1
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build PowerToysSetupCustomActionsVNext
displayName: Build Shared Support DLLs
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysSetupCustomActionsVNext
/p:RunBuildEvents=true;PerUser=${{parameters.buildUserInstaller}};RestorePackagesConfig=true;CIBuild=true
/t:PowerToysSetupCustomActionsVNext;SilentFilesInUseBAFunction
/p:RunBuildEvents=true;RestorePackagesConfig=true;CIBuild=true
-restore -graph
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-actions.binlog
/bl:$(LogOutputDirectory)\installer-actions.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
@@ -70,28 +50,53 @@ steps:
maximumCpuCount: true
- ${{ if eq(parameters.codeSign, true) }}:
- template: steps-esrp-signing.yml
- template: steps-esrp-sign-files-authenticode.yml
parameters:
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign PowerToysSetupCustomActionsVNext
displayName: Sign Shared Support DLLs
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'installer/PowerToysSetupCustomActionsVNext/$(InstallerRelativePath)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
folder: 'installer'
pattern: |-
**/PowerToysSetupCustomActionsVNext.dll
**/SilentFilesInUseBAFunction.dll
## INSTALLER START
#### MSI BUILDING AND SIGNING
#
# The MSI build contains code that reverts the .wxs files to their in-tree versions.
# This is only supposed to happen during local builds. Since this build system is
# supposed to run side by side--machine and then user--we do NOT want to destroy
# the .wxs files. Therefore, we pass RunBuildEvents=false to suppress all of that
# logic.
#
# We pass BuildProjectReferences=false so that it does not recompile the DLLs we just built.
# We only pass -restore on the first one because the second run should already have all
# of the dependencies.
- task: VSBuild@1
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build VNext MSI
displayName: 💻 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
-restore
/t:PowerToysInstallerVNext
/p:RunBuildEvents=false;PerUser=${{parameters.buildUserInstaller}};BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-msi.binlog
/p:RunBuildEvents=false;PerUser=false;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-machine-msi.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: false # don't undo our hard work above by deleting the CustomActions dll
msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
displayName: 👤 Build VNext MSI
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysInstallerVNext
/p:RunBuildEvents=false;PerUser=true;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-user-msi.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
@@ -100,77 +105,66 @@ steps:
maximumCpuCount: true
- script: |-
wix msi decompile installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).msi -x $(build.sourcesdirectory)\extractedMsi
dir $(build.sourcesdirectory)\extractedMsi
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} WiX5: Extract and verify MSI"
wix msi decompile $(InstallerMachineRoot)\$(InstallerMachineBasename).msi -x $(build.sourcesdirectory)\extractedMachineMsi
wix msi decompile $(InstallerUserRoot)\$(InstallerUserBasename).msi -x $(build.sourcesdirectory)\extractedUserMsi
dir $(build.sourcesdirectory)\extractedMachineMsi
dir $(build.sourcesdirectory)\extractedUserMsi
displayName: "WiX5: Extract and verify MSIs"
# Check if deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Audit deps.json in MSI extracted files
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMachineMsi\File'
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedUserMsi\File'
displayName: Audit deps.json in MSI extracted files
- ${{ if eq(parameters.codeSign, true) }}:
- pwsh: |-
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMsi\Binary'
git clean -xfd ./extractedMsi
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Verify all binaries are signed and versioned
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMachineMsi\File'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMachineMsi\Binary'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedUserMsi\File'
& .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedUserMsi\Binary'
git clean -xfd ./extractedMachineMsi ./extractedUserMsi
displayName: Verify all binaries are signed and versioned
- template: steps-esrp-signing.yml
- template: steps-esrp-sign-files-authenticode.yml
parameters:
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign VNext MSI
displayName: Sign VNext MSIs
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'installer/$(InstallerFolder)/$(InstallerRelativePath)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
folder: 'installer'
pattern: '**/PowerToys*Setup-*.msi'
#### END MSI
#### BUILDING AND SIGNING SilentFilesInUseBAFunction DLL
- task: VSBuild@1
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build SilentFilesInUseBAFunction
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:SilentFilesInUseBAFunction
/p:RunBuildEvents=true;PerUser=${{parameters.buildUserInstaller}};RestorePackagesConfig=true;CIBuild=true
-restore -graph
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-SilentFilesInUseBAFunction.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: false # don't undo our hard work above by deleting the msi
msbuildArchitecture: x64
maximumCpuCount: true
- ${{ if eq(parameters.codeSign, true) }}:
- template: steps-esrp-signing.yml
parameters:
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign SilentFilesInUseBAFunction
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'installer/$(BuildPlatform)/$(BuildConfiguration)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
#### END BUILDING AND SIGNING SilentFilesInUseBAFunction DLL
#### BOOTSTRAP BUILDING AND SIGNING
# We pass BuildProjectReferences=false so that it does not recompile the DLLs we just built.
# We only pass -restore on the first one because the second run should already have all
# of the dependencies.
- task: VSBuild@1
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build VNext Bootstrapper
displayName: 💻 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
-restore
/t:PowerToysBootstrapperVNext
/p:PerUser=${{parameters.buildUserInstaller}};CIBuild=true
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-bootstrapper.binlog
-restore -graph
/p:PerUser=false;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-machine-bootstrapper.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: false # don't undo our hard work above by deleting the MSI nor SilentFilesInUseBAFunction
msbuildArchitecture: x64
maximumCpuCount: true
- task: VSBuild@1
displayName: 👤 Build VNext Bootstrapper
inputs:
solution: "**/installer/PowerToysSetup.sln"
vsVersion: 17.0
msbuildArgs: >-
/t:PowerToysBootstrapperVNext
/p:PerUser=true;BuildProjectReferences=false;CIBuild=true
/bl:$(LogOutputDirectory)\installer-user-bootstrapper.binlog
${{ parameters.additionalBuildOptions }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
@@ -181,54 +175,41 @@ steps:
# The entirety of bundle unpacking/re-packing is unnecessary if we are not code signing it.
- ${{ if eq(parameters.codeSign, true) }}:
- script: |-
wix burn detach installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe -engine installer\engine.exe
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} WiX5: Extract Engine from Bundle"
wix burn detach $(InstallerMachineRoot)\$(InstallerMachineBasename).exe -engine installer\machine-engine.exe
wix burn detach $(InstallerUserRoot)\$(InstallerUserBasename).exe -engine installer\user-engine.exe
displayName: "WiX5: Extract Engines from Bundles"
- template: steps-esrp-signing.yml
- template: steps-esrp-sign-files-authenticode.yml
parameters:
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign WiX Engine
displayName: Sign WiX Engines
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: "installer"
Pattern: engine.exe
signConfigType: inlineSignParams
inlineOperation: |
[
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolSign",
"Parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \"SHA256\"",
"PageHash": "/NPH",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
folder: "installer"
pattern: '*-engine.exe'
- script: |-
wix burn reattach installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe -engine installer\engine.exe -o installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe
displayName: "${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} WiX5: Reattach Engine to Bundle"
wix burn reattach $(InstallerMachineRoot)\$(InstallerMachineBasename).exe -engine installer\machine-engine.exe -o $(InstallerMachineRoot)\$(InstallerMachineBasename).exe
wix burn reattach $(InstallerUserRoot)\$(InstallerUserBasename).exe -engine installer\user-engine.exe -o $(InstallerUserRoot)\$(InstallerUserBasename).exe
displayName: "WiX5: Reattach Engines to Bundles"
- template: steps-esrp-signing.yml
- pwsh: |-
& wix burn extract -oba installer\ba\m "$(InstallerMachineRoot)\$(InstallerMachineBasename).exe"
& wix burn extract -oba installer\ba\u "$(InstallerUserRoot)\$(InstallerUserBasename).exe"
Get-ChildItem installer\ba -Recurse -Include *.exe,*.dll | Get-AuthenticodeSignature | ForEach-Object {
If ($_.Status -Ne "Valid") {
Write-Error $_.StatusMessage
} Else {
Write-Host $_.StatusMessage
}
}
& git clean -fdx installer\ba
displayName: "WiX5: Verify Bootstrapper content is signed"
- template: steps-esrp-sign-files-authenticode.yml
parameters:
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign Final Bootstrapper
displayName: Sign Final Bootstrappers
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'installer/$(InstallerFolder)/$(InstallerRelativePath)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
folder: 'installer'
pattern: '**/PowerToys*Setup-*.exe'
#### END BOOTSTRAP
## END INSTALLER

View File

@@ -0,0 +1,45 @@
parameters:
- name: displayName
type: string
default: Sign Specific Files
- name: folder
type: string
- name: pattern
type: string
- name: signingIdentity
type: object
default: {}
steps:
- template: steps-esrp-signing.yml
parameters:
displayName: ${{ parameters.displayName }}
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: ${{ parameters.folder }}
Pattern: ${{ parameters.pattern }}
UseMinimatch: true
signConfigType: inlineSignParams
inlineOperation: |-
[
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolSign",
"Parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \"SHA256\"",
"PageHash": "/NPH",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]

13
.vscode/launch.json vendored
View File

@@ -38,6 +38,17 @@
"env": {},
"console": "internalConsole",
"stopAtEntry": false
}
},
{
"name": "Run AdvancedPaste (managed, no build, ARCH configurable)",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}\\${input:arch}\\Debug\\WinUI3Apps\\PowerToys.AdvancedPaste.exe",
"args": [],
"cwd": "${workspaceFolder}",
"env": {},
"console": "internalConsole",
"stopAtEntry": false
},
]
}

View File

@@ -1,6 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Hybrid CRT configuration -->
<PropertyGroup Condition="'$(HybridCrtConfiguration)'==''">
<HybridCrtConfiguration>$(Configuration)</HybridCrtConfiguration>
</PropertyGroup>
<!-- Skip Hybrid CRT for AppContainer/UWP projects as they require MultiThreadedDLL -->
<PropertyGroup Condition="'$(AppContainerApplication)'=='true' and '$(_VC_Target_Library_Platform)'!='Desktop'">
<HybridCrtConfiguration></HybridCrtConfiguration>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(HybridCrtConfiguration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(HybridCrtConfiguration)'=='Release'">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- AppContainer/UWP projects must use MultiThreadedDLL -->
<ItemDefinitionGroup Condition="'$(AppContainerApplication)'=='true' and '$(_VC_Target_Library_Platform)'!='Desktop' And '$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(AppContainerApplication)'=='true' and '$(_VC_Target_Library_Platform)'!='Desktop' And '$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<!-- Project configurations -->
<ItemGroup Label="ProjectConfigurations">
@@ -73,7 +123,6 @@
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -83,7 +132,6 @@
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>

View File

@@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
@@ -9,7 +9,6 @@
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.5" />
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageVersion Include="CoenM.ImageSharp.ImageHash" Version="1.3.6" />
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
@@ -35,23 +34,35 @@
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.8" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.10" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.8" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
<PackageVersion Include="Microsoft.Windows.CppWinRT" Version="2.0.240111.5" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.8" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.9.1" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="9.9.1-preview.1.25474.6" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.10" />
<PackageVersion Include="Microsoft.AI.Foundry.Local" Version="0.3.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.66.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.66.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Amazon" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.66.0-beta" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.HuggingFace" Version="1.66.0-preview" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.8" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.10" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.8" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.10" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
@@ -74,7 +85,7 @@
<PackageVersion Include="NLog" Version="5.2.8" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
<PackageVersion Include="OpenAI" Version="2.0.0" />
<PackageVersion Include="OpenAI" Version="2.5.0" />
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
<PackageVersion Include="RtfPipe" Version="2.0.7677.4303" />
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
@@ -84,28 +95,29 @@
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.CodeDom" Version="9.0.8" />
<PackageVersion Include="System.CodeDom" Version="9.0.10" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.8" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.8" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.8" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.10" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.10" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.10" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.8" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.10" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.8" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.8" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.10" />
<PackageVersion Include="System.ClientModel" Version="1.7.0" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.10" />
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="9.0.8" />
<PackageVersion Include="System.Management" Version="9.0.10" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.8" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.8" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.8" />
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.10" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.10" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
<PackageVersion Include="System.Text.Json" Version="9.0.10" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />
@@ -127,4 +139,4 @@
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1495,7 +1495,6 @@ SOFTWARE.
- AdaptiveCards.Rendering.WinUI3
- AdaptiveCards.Templating
- Appium.WebDriver
- Azure.AI.OpenAI
- CoenM.ImageSharp.ImageHash
- CommunityToolkit.Common
- CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock

View File

@@ -822,12 +822,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSea
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Shell.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.Shell.UnitTests\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj", "{E816D7B4-4688-4ECB-97CC-3D8E798F3833}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CursorWrap", "src\modules\MouseUtils\CursorWrap\CursorWrap.vcxproj", "{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3DCCD936-D085-4869-A1DE-CA6A64152C94}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightSwitch.UITests", "src\modules\LightSwitch\Tests\LightSwitch.UITests\LightSwitch.UITests.csproj", "{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj", "{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageModelProvider", "src\common\LanguageModelProvider\LanguageModelProvider.csproj", "{45354F4F-1414-45CE-B600-51CD1209FD19}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.UI.ViewModels.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.UI.ViewModels.UnitTests\Microsoft.CmdPal.UI.ViewModels.UnitTests.csproj", "{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}"
EndProject
Global
@@ -2988,6 +2992,14 @@ Global
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|ARM64.Build.0 = Release|ARM64
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|x64.ActiveCfg = Release|x64
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|x64.Build.0 = Release|x64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Debug|ARM64.ActiveCfg = Debug|ARM64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Debug|ARM64.Build.0 = Debug|ARM64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Debug|x64.ActiveCfg = Debug|x64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Debug|x64.Build.0 = Debug|x64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Release|ARM64.ActiveCfg = Release|ARM64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Release|ARM64.Build.0 = Release|ARM64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Release|x64.ActiveCfg = Release|x64
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5}.Release|x64.Build.0 = Release|x64
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|ARM64.Build.0 = Debug|ARM64
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F}.Debug|ARM64.Deploy.0 = Debug|ARM64
@@ -3008,6 +3020,14 @@ Global
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|ARM64.Build.0 = Release|ARM64
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|x64.ActiveCfg = Release|x64
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA}.Release|x64.Build.0 = Release|x64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Debug|ARM64.ActiveCfg = Debug|ARM64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Debug|ARM64.Build.0 = Debug|ARM64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Debug|x64.ActiveCfg = Debug|x64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Debug|x64.Build.0 = Debug|x64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Release|ARM64.ActiveCfg = Release|ARM64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Release|ARM64.Build.0 = Release|ARM64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Release|x64.ActiveCfg = Release|x64
{45354F4F-1414-45CE-B600-51CD1209FD19}.Release|x64.Build.0 = Release|x64
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|ARM64.Build.0 = Debug|ARM64
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C}.Debug|x64.ActiveCfg = Debug|x64
@@ -3341,9 +3361,11 @@ Global
{E816D7B3-4688-4ECB-97CC-3D8E798F3832} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B2-4688-4ECB-97CC-3D8E798F3831} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B4-4688-4ECB-97CC-3D8E798F3833} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{48A1DB8C-5DF8-4FB3-9E14-2B67F3F2D8B5} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{3DCCD936-D085-4869-A1DE-CA6A64152C94} = {5B201255-53C8-490B-A34F-01F05D48A477}
{F5333ED7-06D8-4AB3-953A-36D63F08CB6F} = {3DCCD936-D085-4869-A1DE-CA6A64152C94}
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{45354F4F-1414-45CE-B600-51CD1209FD19} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

View File

@@ -48,7 +48,7 @@ Before you begin, make sure your device meets the system requirements:
Choose one of the installation methods below:
<details>
<details open>
<summary>Download .exe from GitHub</summary>
Go to the [PowerToys GitHub releases][github-release-link], click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
@@ -56,17 +56,17 @@ Go to the [PowerToys GitHub releases][github-release-link], click Assets to reve
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.96%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.95%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.0/PowerToysUserSetup-0.95.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.0/PowerToysUserSetup-0.95.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.0/PowerToysSetup-0.95.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.0/PowerToysSetup-0.95.0-arm64.exe
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysUserSetup-0.95.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysUserSetup-0.95.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysSetup-0.95.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.95.1/PowerToysSetup-0.95.1-arm64.exe
| Description | Filename |
|----------------|----------|
| Per user - x64 | [PowerToysUserSetup-0.95.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.95.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.95.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.95.0-arm64.exe][ptMachineArm64] |
| Per user - x64 | [PowerToysUserSetup-0.95.1-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.95.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.95.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.95.1-arm64.exe][ptMachineArm64] |
</details>
@@ -281,4 +281,4 @@ The application logs basic diagnostic data (telemetry). For more privacy informa
[roadmap]: https://github.com/microsoft/PowerToys/wiki/Roadmap
[privacy-link]: http://go.microsoft.com/fwlink/?LinkId=521839
[loc-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=translation_issue.md&title=
[usingPowerToys-docs-link]: https://aka.ms/powertoys-docs
[usingPowerToys-docs-link]: https://aka.ms/powertoys-docs

View File

@@ -1,87 +0,0 @@
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True)]
[string]$dscWxsFile,
[Parameter(Mandatory = $True)]
[string]$Platform,
[Parameter(Mandatory = $True)]
[string]$Configuration
)
$ErrorActionPreference = 'Stop'
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$buildOutputDir = Join-Path $scriptDir "..\..\$Platform\$Configuration"
if (-not (Test-Path $buildOutputDir)) {
Write-Error "Build output directory not found: '$buildOutputDir'"
exit 1
}
$dscFiles = Get-ChildItem -Path $buildOutputDir -Filter "microsoft.powertoys.*.settings.dsc.resource.json" -File
if (-not $dscFiles) {
Write-Warning "No DSC manifest files found in '$buildOutputDir'"
$wxsContent = @"
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include `$(sys.CURRENTDIR)\Common.wxi?>
<Fragment>
<ComponentGroup Id="DscResourcesComponentGroup">
</ComponentGroup>
</Fragment>
</Wix>
"@
Set-Content -Path $dscWxsFile -Value $wxsContent
exit 0
}
Write-Host "Found $($dscFiles.Count) DSC manifest file(s)"
$wxsContent = @"
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include `$(sys.CURRENTDIR)\Common.wxi?>
<Fragment>
<DirectoryRef Id="DSCModulesReferenceFolder">
"@
$componentRefs = @()
foreach ($file in $dscFiles) {
$componentId = "DscResource_" + ($file.BaseName -replace '[^A-Za-z0-9_]', '_')
$fileId = $componentId + "_File"
$guid = [System.Guid]::NewGuid().ToString().ToUpper()
$componentRefs += $componentId
$wxsContent += @"
<Component Id="$componentId" Guid="{$guid}">
<RegistryKey Root="`$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="$componentId" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="$fileId" Source="`$(var.BinDir)$($file.Name)" Vital="no"/>
</Component>
"@
}
$wxsContent += @"
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DscResourcesComponentGroup">
"@
foreach ($componentId in $componentRefs) {
$wxsContent += @"
<ComponentRef Id="$componentId"/>
"@
}
$wxsContent += @"
</ComponentGroup>
</Fragment>
</Wix>
"@
Set-Content -Path $dscWxsFile -Value $wxsContent
Write-Host "Generated DSC resources WiX fragment: '$dscWxsFile'"

View File

@@ -34,13 +34,8 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir Condition=" '$(PerUser)' != 'true' ">$(Platform)\$(Configuration)\MachineSetup\</OutDir>
<OutDir Condition=" '$(PerUser)' == 'true' ">$(Platform)\$(Configuration)\UserSetup\</OutDir>
<IntDir Condition=" '$(PerUser)' != 'true' ">$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\MachineSetup\obj\</IntDir>
<IntDir Condition=" '$(PerUser)' == 'true' ">$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\UserSetup\obj\</IntDir>
<!-- The CMD script below checks this value, and it is **CASE SENSITIVE** -->
<NormalizedPerUserValue>false</NormalizedPerUserValue>
<NormalizedPerUserValue Condition=" '$(PerUser)' == 'true' ">true</NormalizedPerUserValue>
<OutDir>$(Platform)\$(Configuration)\SetupShared\</OutDir>
<IntDir>$(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\SetupShared\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
@@ -80,8 +75,7 @@
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\WinAppSDK.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\WinAppSDK.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\WinUI3Applications.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\WinUI3Applications.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Workspaces.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Workspaces.wxs.bk""""
if not "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform)
if "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform) -installscopeperuser $(NormalizedPerUserValue)
call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform)
</Command>
<Message>Backing up original files and populating .NET and WPF Runtime dependencies for WiX3 based installer</Message>
</PreBuildEvent>
@@ -115,7 +109,6 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -128,7 +121,6 @@
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
@@ -181,4 +173,4 @@
<Error Condition="!Exists('..\packages\WixToolset.DUtil.5.0.2\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WixToolset.DUtil.5.0.2\build\WixToolset.DUtil.props'))" />
<Error Condition="!Exists('..\packages\WixToolset.WcaUtil.5.0.2\build\WixToolset.WcaUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WixToolset.WcaUtil.5.0.2\build\WixToolset.WcaUtil.props'))" />
</Target>
</Project>
</Project>

View File

@@ -4,6 +4,13 @@
<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
<?if $(sys.BUILDARCH) = x64 ?>
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
<?else?>
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
<?endif?>
</Directory>
</Directory>
</DirectoryRef>
<DirectoryRef Id="CmdPalInstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test">
@@ -18,14 +25,40 @@
<?endif?>
</Component>
</DirectoryRef>
<?if $(sys.BUILDARCH) = x64 ?>
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.VCLibs.x64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?else?>
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.VCLibs.ARM64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?endif?>
<ComponentGroup Id="CmdPalComponentGroup">
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall" />
<?if $(sys.BUILDARCH) = x64 ?>
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall" />
<?else?>
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall" />
<?endif?>
</Component>
<ComponentRef Id="Module_CmdPal" />
<ComponentRef Id="Module_CmdPal_Deps" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -1,4 +1,5 @@
<Project>
<Import Project="..\..\Directory.Build.props" Condition="Exists('..\..\Directory.Build.props')" />
<Import Project="..\..\src\Version.props" Condition="Exists('..\..\src\Version.props')" />
<PropertyGroup>
<!-- Set BaseIntermediateOutputPath for each project to avoid conflicts -->
@@ -8,4 +9,4 @@
<!-- Set MSBuildProjectExtensionsPath to use the BaseIntermediateOutputPath -->
<MSBuildProjectExtensionsPath Condition="'$(BaseIntermediateOutputPath)' != ''">$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>
</PropertyGroup>
</Project>
</Project>

View File

@@ -60,6 +60,12 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition="'$(RunBuildEvents)'=='false'">
<PostBuildEvent></PostBuildEvent>
<RunPostBuildEvent></RunPostBuildEvent>
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
</PropertyGroup>
<PropertyGroup Label="UserMacros" Condition=" '$(PerUser)' == 'true' ">
<DefineConstants>$(DefineConstants);PerUser=true</DefineConstants>
</PropertyGroup>

View File

@@ -14,11 +14,16 @@
<?define SettingsV2OOBEAssetsFluentIconsFiles=?>
<?define SettingsV2OOBEAssetsFluentIconsFilesPath=$(var.BinDir)WinUI3Apps\Assets\Settings\Icons\?>
<?define SettingsV2IconsModelsFiles=?>
<?define SettingsV2IconsModelsFilesPath=$(var.BinDir)WinUI3Apps\Assets\Settings\Icons\Models\?>
<Fragment>
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<Directory Id="SettingsV2AssetsInstallFolder" Name="Settings">
<Directory Id="SettingsAppAssetsScriptsFolder" Name="Scripts" />
<Directory Id="SettingsV2OOBEAssetsFluentIconsInstallFolder" Name="Icons" />
<Directory Id="SettingsV2OOBEAssetsFluentIconsInstallFolder" Name="Icons">
<Directory Id="SettingsV2IconsModelsInstallFolder" Name="Models" />
</Directory>
<Directory Id="SettingsV2AssetsModulesInstallFolder" Name="Modules">
<Directory Id="SettingsV2OOBEAssetsModulesInstallFolder" Name="OOBE" />
</Directory>
@@ -45,6 +50,11 @@
<!--SettingsV2OOBEAssetsFluentIconsFiles_Component_Def-->
</DirectoryRef>
<DirectoryRef Id="SettingsV2IconsModelsInstallFolder" FileSource="$(var.SettingsV2IconsModelsFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--SettingsV2IconsModelsFiles_Component_Def-->
</DirectoryRef>
<DirectoryRef Id="SettingsAppAssetsScriptsFolder" FileSource="$(var.SettingsV2AssetsFilesPath)\Scripts\">
<Component Id="CommandNotFound_Scripts" Guid="898EFA1E-EDD3-4F4B-8C7F-4A14B0D05B02" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
@@ -67,6 +77,7 @@
</RegistryKey>
<RemoveFolder Id="RemoveFolderSettingsV2AssetsInstallFolder" Directory="SettingsV2AssetsInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2OOBEAssetsFluentIconsInstallFolder" Directory="SettingsV2OOBEAssetsFluentIconsInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2IconsModelsInstallFolder" Directory="SettingsV2IconsModelsInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2AssetsModulesInstallFolder" Directory="SettingsV2AssetsModulesInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2OOBEAssetsModulesInstallFolder" Directory="SettingsV2OOBEAssetsModulesInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsAppAssetsScriptsFolder" Directory="SettingsAppAssetsScriptsFolder" On="uninstall" />

View File

@@ -1,30 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}</ProjectGuid>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<TargetName>SilentFilesInUseBAFunction</TargetName>
<ProjectName>PowerToysSetupCustomActionsVNext</ProjectName>
<ProjectModuleDefinitionFile>bafunctions.def</ProjectModuleDefinitionFile>
@@ -33,7 +10,6 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<!-- Configuration-specific property groups -->
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
@@ -65,7 +41,10 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="SilentFilesInUseBAFunctions.cpp" />
<ClCompile Include="SilentFilesInUseBAFunctions.cpp">
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="bafunctions.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
@@ -92,31 +71,5 @@
</Link>
</ItemDefinitionGroup>
<!-- C++ source compile-specific things for Debug/Release configurations -->
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>

View File

@@ -18,7 +18,6 @@ public: // IBootstrapperApplication
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running detect begin BA function. fCached=%d, registrationType=%d, cPackages=%u, fCancel=%d", fCached, registrationType, cPackages, *pfCancel);
LExit:
return hr;
}
@@ -32,12 +31,6 @@ public: // IBAFunctions
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running plan begin BA function. cPackages=%u, fCancel=%d", cPackages, *pfCancel);
//-------------------------------------------------------------------------------------------------
// YOUR CODE GOES HERE
// BalExitOnFailure(hr, "Change this message to represent real error handling.");
//-------------------------------------------------------------------------------------------------
LExit:
return hr;
}
@@ -63,6 +56,7 @@ public: // IBAFunctions
)
{
HRESULT hr = S_OK;
UNREFERENCED_PARAMETER(source);
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION CALLED *** Running OnExecuteFilesInUse BA function. packageId=%ls, cFiles=%u, recommendation=%d", wzPackageId, cFiles, nRecommendation);

View File

@@ -1,9 +1,7 @@
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, Position = 1)]
[string]$platform,
[Parameter(Mandatory = $False, Position = 2)]
[string]$installscopeperuser = "false"
[string]$platform
)
Function Generate-FileList() {
@@ -77,9 +75,7 @@ Function Generate-FileComponents() {
[Parameter(Mandatory = $True, Position = 1)]
[string]$fileListName,
[Parameter(Mandatory = $True, Position = 2)]
[string]$wxsFilePath,
[Parameter(Mandatory = $True, Position = 3)]
[string]$regroot
[string]$wxsFilePath
)
$wxsFile = Get-Content $wxsFilePath;
@@ -100,7 +96,7 @@ Function Generate-FileComponents() {
$componentDefs +=
@"
<Component Id="$($componentId)" Guid="$((New-Guid).ToString().ToUpper())">
<RegistryKey Root="$($regroot)" Key="Software\Classes\powertoys\components">
<RegistryKey Root="`$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="$($componentId)" Value="" KeyPath="yes"/>
</RegistryKey>`r`n
"@
@@ -134,194 +130,190 @@ if ($platform -ceq "arm64") {
$platform = "ARM64"
}
if ($installscopeperuser -eq "true") {
$registryroot = "HKCU"
} else {
$registryroot = "HKLM"
}
#BaseApplications
Generate-FileList -fileDepsJson "" -fileListName BaseApplicationsFiles -wxsFilePath $PSScriptRoot\BaseApplications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release"
Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs -regroot $registryroot
Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs
#WinUI3Applications
Generate-FileList -fileDepsJson "" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs
#AdvancedPaste
Generate-FileList -fileDepsJson "" -fileListName AdvancedPasteAssetsFiles -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\AdvancedPaste"
Generate-FileComponents -fileListName "AdvancedPasteAssetsFiles" -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -regroot $registryroot
Generate-FileComponents -fileListName "AdvancedPasteAssetsFiles" -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs
#AwakeFiles
Generate-FileList -fileDepsJson "" -fileListName AwakeImagesFiles -wxsFilePath $PSScriptRoot\Awake.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Awake"
Generate-FileComponents -fileListName "AwakeImagesFiles" -wxsFilePath $PSScriptRoot\Awake.wxs -regroot $registryroot
Generate-FileComponents -fileListName "AwakeImagesFiles" -wxsFilePath $PSScriptRoot\Awake.wxs
#ColorPicker
Generate-FileList -fileDepsJson "" -fileListName ColorPickerAssetsFiles -wxsFilePath $PSScriptRoot\ColorPicker.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ColorPicker"
Generate-FileComponents -fileListName "ColorPickerAssetsFiles" -wxsFilePath $PSScriptRoot\ColorPicker.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ColorPickerAssetsFiles" -wxsFilePath $PSScriptRoot\ColorPicker.wxs
#Environment Variables
Generate-FileList -fileDepsJson "" -fileListName EnvironmentVariablesAssetsFiles -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\EnvironmentVariables"
Generate-FileComponents -fileListName "EnvironmentVariablesAssetsFiles" -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs -regroot $registryroot
Generate-FileComponents -fileListName "EnvironmentVariablesAssetsFiles" -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs
#FileExplorerAdd-ons
Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerMonacoAssetsFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Monaco"
Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerCustomLanguagesFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Monaco\customLanguages"
Generate-FileComponents -fileListName "MonacoPreviewHandlerMonacoAssetsFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot
Generate-FileComponents -fileListName "MonacoPreviewHandlerCustomLanguagesFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot
Generate-FileComponents -fileListName "MonacoPreviewHandlerMonacoAssetsFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs
Generate-FileComponents -fileListName "MonacoPreviewHandlerCustomLanguagesFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs
#FileLocksmith
Generate-FileList -fileDepsJson "" -fileListName FileLocksmithAssetsFiles -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\FileLocksmith"
Generate-FileComponents -fileListName "FileLocksmithAssetsFiles" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -regroot $registryroot
Generate-FileComponents -fileListName "FileLocksmithAssetsFiles" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs
#Hosts
Generate-FileList -fileDepsJson "" -fileListName HostsAssetsFiles -wxsFilePath $PSScriptRoot\Hosts.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Hosts"
Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot
Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs
#ImageResizer
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ImageResizer"
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs
# Light Switch Service
Generate-FileList -fileDepsJson "" -fileListName LightSwitchFiles -wxsFilePath $PSScriptRoot\LightSwitch.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\LightSwitchService"
Generate-FileComponents -fileListName "LightSwitchFiles" -wxsFilePath $PSScriptRoot\LightSwitch.wxs -regroot $registryroot
Generate-FileComponents -fileListName "LightSwitchFiles" -wxsFilePath $PSScriptRoot\LightSwitch.wxs
#New+
Generate-FileList -fileDepsJson "" -fileListName NewPlusAssetsFiles -wxsFilePath $PSScriptRoot\NewPlus.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\NewPlus"
Generate-FileComponents -fileListName "NewPlusAssetsFiles" -wxsFilePath $PSScriptRoot\NewPlus.wxs -regroot $registryroot
Generate-FileComponents -fileListName "NewPlusAssetsFiles" -wxsFilePath $PSScriptRoot\NewPlus.wxs
#Peek
Generate-FileList -fileDepsJson "" -fileListName PeekAssetsFiles -wxsFilePath $PSScriptRoot\Peek.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Peek\"
Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRoot\Peek.wxs -regroot $registryroot
Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRoot\Peek.wxs
#PowerRename
Generate-FileList -fileDepsJson "" -fileListName PowerRenameAssetsFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerRename\"
Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs -regroot $registryroot
Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs
#RegistryPreview
Generate-FileList -fileDepsJson "" -fileListName RegistryPreviewAssetsFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\RegistryPreview\"
Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -regroot $registryroot
Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs
#Run
Generate-FileList -fileDepsJson "" -fileListName launcherImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\PowerLauncher"
Generate-FileComponents -fileListName "launcherImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "launcherImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
## Plugins
###Calculator
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Calculator\Microsoft.PowerToys.Run.Plugin.Calculator.deps.json" -fileListName calcComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName calcImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Calculator\Images"
Generate-FileComponents -fileListName "calcComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "calcImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "calcComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "calcImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Folder
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Folder\Microsoft.Plugin.Folder.deps.json" -fileListName FolderComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName FolderImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Folder\Images"
Generate-FileComponents -fileListName "FolderComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "FolderImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "FolderComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "FolderImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Program
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Program\Microsoft.Plugin.Program.deps.json" -fileListName ProgramComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ProgramImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Program\Images"
Generate-FileComponents -fileListName "ProgramComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ProgramImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ProgramComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ProgramImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Shell
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Shell\Microsoft.Plugin.Shell.deps.json" -fileListName ShellComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ShellImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Shell\Images"
Generate-FileComponents -fileListName "ShellComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ShellImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ShellComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ShellImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Indexer
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Indexer\Microsoft.Plugin.Indexer.deps.json" -fileListName IndexerComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName IndexerImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Indexer\Images"
Generate-FileComponents -fileListName "IndexerComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "IndexerImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "IndexerComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "IndexerImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###UnitConverter
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.deps.json" -fileListName UnitConvCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName UnitConvImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\UnitConverter\Images"
Generate-FileComponents -fileListName "UnitConvCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "UnitConvImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "UnitConvCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "UnitConvImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###WebSearch
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WebSearch\Community.PowerToys.Run.Plugin.WebSearch.deps.json" -fileListName WebSrchCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WebSrchImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WebSearch\Images"
Generate-FileComponents -fileListName "WebSrchCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WebSrchImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WebSrchCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WebSrchImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###History
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\History\Microsoft.PowerToys.Run.Plugin.History.deps.json" -fileListName HistoryPluginComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName HistoryPluginImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\History\Images"
Generate-FileComponents -fileListName "HistoryPluginComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "HistoryPluginImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "HistoryPluginComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "HistoryPluginImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Uri
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Uri\Microsoft.Plugin.Uri.deps.json" -fileListName UriComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName UriImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Uri\Images"
Generate-FileComponents -fileListName "UriComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "UriImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "UriComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "UriImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###VSCodeWorkspaces
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.deps.json" -fileListName VSCWrkCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName VSCWrkImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\VSCodeWorkspaces\Images"
Generate-FileComponents -fileListName "VSCWrkCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "VSCWrkImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "VSCWrkCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "VSCWrkImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###WindowWalker
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowWalker\Microsoft.Plugin.WindowWalker.deps.json" -fileListName WindowWlkrCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WindowWlkrImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowWalker\Images"
Generate-FileComponents -fileListName "WindowWlkrCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WindowWlkrImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WindowWlkrCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WindowWlkrImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###OneNote
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\OneNote\Microsoft.PowerToys.Run.Plugin.OneNote.deps.json" -fileListName OneNoteComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName OneNoteImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\OneNote\Images"
Generate-FileComponents -fileListName "OneNoteComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "OneNoteImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "OneNoteComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "OneNoteImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Registry
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Registry\Microsoft.PowerToys.Run.Plugin.Registry.deps.json" -fileListName RegistryComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName RegistryImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Registry\Images"
Generate-FileComponents -fileListName "RegistryComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "RegistryImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "RegistryComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "RegistryImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###Service
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Service\Microsoft.PowerToys.Run.Plugin.Service.deps.json" -fileListName ServiceComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ServiceImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Service\Images"
Generate-FileComponents -fileListName "ServiceComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ServiceImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ServiceComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ServiceImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###System
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\System\Microsoft.PowerToys.Run.Plugin.System.deps.json" -fileListName SystemComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName SystemImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\System\Images"
Generate-FileComponents -fileListName "SystemComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SystemImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SystemComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "SystemImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###TimeDate
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.deps.json" -fileListName TimeDateComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName TimeDateImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\TimeDate\Images"
Generate-FileComponents -fileListName "TimeDateComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "TimeDateImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "TimeDateComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "TimeDateImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###WindowsSettings
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json" -fileListName WinSetCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WinSetImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsSettings\Images"
Generate-FileComponents -fileListName "WinSetCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinSetImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinSetCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WinSetImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###WindowsTerminal
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsTerminal\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.deps.json" -fileListName WinTermCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName WinTermImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsTerminal\Images"
Generate-FileComponents -fileListName "WinTermCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinTermImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WinTermCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "WinTermImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###PowerToys
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\PowerToys\Microsoft.PowerToys.Run.Plugin.PowerToys.deps.json" -fileListName PowerToysCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName PowerToysImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\PowerToys\Images"
Generate-FileComponents -fileListName "PowerToysCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "PowerToysImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "PowerToysCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "PowerToysImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
###ValueGenerator
Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.deps.json" -fileListName ValueGeneratorCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
Generate-FileList -fileDepsJson "" -fileListName ValueGeneratorImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\ValueGenerator\Images"
Generate-FileComponents -fileListName "ValueGeneratorCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ValueGeneratorCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs
## Plugins
#ShortcutGuide
Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ShortcutGuide\"
Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot
Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs
#Settings
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\OOBE\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsFluentIconsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Icons\"
Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
Generate-FileList -fileDepsJson "" -fileListName SettingsV2IconsModelsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Icons\Models\"
Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2IconsModelsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
#Workspaces
Generate-FileList -fileDepsJson "" -fileListName WorkspacesImagesComponentFiles -wxsFilePath $PSScriptRoot\Workspaces.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Workspaces\"
Generate-FileComponents -fileListName "WorkspacesImagesComponentFiles" -wxsFilePath $PSScriptRoot\Workspaces.wxs -regroot $registryroot
Generate-FileComponents -fileListName "WorkspacesImagesComponentFiles" -wxsFilePath $PSScriptRoot\Workspaces.wxs

View File

@@ -36,7 +36,7 @@
</Capabilities>
<Applications>
<Application Id="PowerToys.OCR" Executable="PowerToys.PowerOCR.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<Application Id="PowerToys.OCR" Executable="PowerToys.PowerOCR.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.OCR"
Description="PowerToys OCR Module"
@@ -45,7 +45,7 @@
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.SettingsUI" Executable="PowerToys.Settings.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<Application Id="PowerToys.SettingsUI" Executable="WinUI3Apps\PowerToys.Settings.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.SettingsUI"
Description="PowerToys Settings UI"
@@ -54,7 +54,7 @@
Square44x44Logo="Images\Square44x44Logo.png">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.ImageResizer"
Description="PowerToys Image Resizer UI"

View File

@@ -17,7 +17,7 @@
<CIBuildParam Condition="'$(CIBuild)' != 'true'"></CIBuildParam>
</PropertyGroup>
<Exec Command="powershell -NonInteractive -ExecutionPolicy Bypass -File &quot;$(MSBuildThisFileDirectory)BuildSparsePackage.ps1&quot; -Platform $(Platform) -Configuration $(Configuration) $(NoSignParam) $(CIBuildParam)"
<Exec Command="pwsh -NonInteractive -ExecutionPolicy Bypass -File &quot;$(MSBuildThisFileDirectory)BuildSparsePackage.ps1&quot; -Platform $(Platform) -Configuration $(Configuration) $(NoSignParam) $(CIBuildParam)"
ContinueOnError="false"
WorkingDirectory="$(MSBuildThisFileDirectory)" />
</Target>

View File

@@ -9,12 +9,6 @@
<RootNamespace>CalculatorEngineCommon</RootNamespace>
<AppxPackage>false</AppxPackage>
</PropertyGroup>
<!-- BEGIN common.build.pre.props -->
<PropertyGroup Label="Configuration">
<EnableHybridCRT>true</EnableHybridCRT>
<UseCrtSDKReferenceStaticWarning Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReferenceStaticWarning>
</PropertyGroup>
<!-- END common.build.pre.props -->
<!-- BEGIN cppwinrt.build.pre.props -->
<PropertyGroup Label="Globals">
<CppWinRTEnabled>true</CppWinRTEnabled>
@@ -25,11 +19,9 @@
</PropertyGroup>
<PropertyGroup>
<MinimalCoreWin>true</MinimalCoreWin>
<AppContainerApplication>true</AppContainerApplication>
<AppContainerApplication>false</AppContainerApplication>
<WindowsStoreApp>true</WindowsStoreApp>
<ApplicationType>Windows Store</ApplicationType>
<UseCrtSDKReference Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReference>
<!-- The SDK reference breaks the Hybrid CRT -->
</PropertyGroup>
<PropertyGroup>
<!-- We have to use the Desktop platform for Hybrid CRT to work. -->
@@ -148,43 +140,5 @@
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
<!-- BEGIN common.build.post.props -->
<!--
The Hybrid CRT model statically links the runtime and STL and dynamically
links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
WinAppSDK asserts that this is "supported according to the CRT maintainer."
This must come before Microsoft.Cpp.targets because it manipulates ClCompile.RuntimeLibrary.
-->
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and '$(Configuration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and ('$(Configuration)'=='Release' or '$(Configuration)'=='AuditMode')">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- END common.build.post.props -->
</Target> <!-- END common.build.post.props -->
</Project>

View File

@@ -112,6 +112,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMousePointerCrosshairsEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredCursorWrapEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredCursorWrapEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredPowerRenameEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredPowerRenameEnabledValue());
@@ -192,6 +196,38 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOnlineAIModelsValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteOpenAIValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOpenAIValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteAzureOpenAIValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteAzureOpenAIValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteAzureAIInferenceValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteAzureAIInferenceValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteMistralValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteMistralValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteGoogleValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteGoogleValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteAnthropicValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteAnthropicValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteOllamaValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOllamaValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteFoundryLocalValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteFoundryLocalValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredNewPlusEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredNewPlusEnabledValue());

View File

@@ -35,6 +35,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredMouseHighlighterEnabledValue();
static GpoRuleConfigured GetConfiguredMouseJumpEnabledValue();
static GpoRuleConfigured GetConfiguredMousePointerCrosshairsEnabledValue();
static GpoRuleConfigured GetConfiguredCursorWrapEnabledValue();
static GpoRuleConfigured GetConfiguredPowerRenameEnabledValue();
static GpoRuleConfigured GetConfiguredPowerLauncherEnabledValue();
static GpoRuleConfigured GetConfiguredQuickAccentEnabledValue();
@@ -54,6 +55,14 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredQoiPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredQoiThumbnailsEnabledValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOnlineAIModelsValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOpenAIValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAzureOpenAIValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAzureAIInferenceValue();
static GpoRuleConfigured GetAllowedAdvancedPasteMistralValue();
static GpoRuleConfigured GetAllowedAdvancedPasteGoogleValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAnthropicValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();
static GpoRuleConfigured GetConfiguredWorkspacesEnabledValue();
static GpoRuleConfigured GetConfiguredMwbClipboardSharingEnabledValue();

View File

@@ -38,6 +38,7 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredMouseHighlighterEnabledValue();
static GpoRuleConfigured GetConfiguredMouseJumpEnabledValue();
static GpoRuleConfigured GetConfiguredMousePointerCrosshairsEnabledValue();
static GpoRuleConfigured GetConfiguredCursorWrapEnabledValue();
static GpoRuleConfigured GetConfiguredMouseWithoutBordersEnabledValue();
static GpoRuleConfigured GetConfiguredPowerRenameEnabledValue();
static GpoRuleConfigured GetConfiguredPowerLauncherEnabledValue();
@@ -58,6 +59,14 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredQoiPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredQoiThumbnailsEnabledValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOnlineAIModelsValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOpenAIValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAzureOpenAIValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAzureAIInferenceValue();
static GpoRuleConfigured GetAllowedAdvancedPasteMistralValue();
static GpoRuleConfigured GetAllowedAdvancedPasteGoogleValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAnthropicValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();
static GpoRuleConfigured GetConfiguredWorkspacesEnabledValue();
static GpoRuleConfigured GetConfiguredMwbClipboardSharingEnabledValue();

View File

@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace LanguageModelProvider.FoundryLocal;
internal sealed record FoundryCachedModel(string Name, string? Id);

View File

@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace LanguageModelProvider.FoundryLocal;
internal sealed record FoundryCatalogModel
{
[JsonPropertyName("name")]
public string Name { get; init; } = string.Empty;
[JsonPropertyName("displayName")]
public string DisplayName { get; init; } = string.Empty;
[JsonPropertyName("providerType")]
public string ProviderType { get; init; } = string.Empty;
[JsonPropertyName("uri")]
public string Uri { get; init; } = string.Empty;
[JsonPropertyName("version")]
public string Version { get; init; } = string.Empty;
[JsonPropertyName("modelType")]
public string ModelType { get; init; } = string.Empty;
[JsonPropertyName("promptTemplate")]
public PromptTemplate PromptTemplate { get; init; } = default!;
[JsonPropertyName("publisher")]
public string Publisher { get; init; } = string.Empty;
[JsonPropertyName("task")]
public string Task { get; init; } = string.Empty;
[JsonPropertyName("runtime")]
public Runtime Runtime { get; init; } = default!;
[JsonPropertyName("fileSizeMb")]
public long FileSizeMb { get; init; }
[JsonPropertyName("modelSettings")]
public ModelSettings ModelSettings { get; init; } = default!;
[JsonPropertyName("alias")]
public string Alias { get; init; } = string.Empty;
[JsonPropertyName("supportsToolCalling")]
public bool SupportsToolCalling { get; init; }
[JsonPropertyName("license")]
public string License { get; init; } = string.Empty;
[JsonPropertyName("licenseDescription")]
public string LicenseDescription { get; init; } = string.Empty;
[JsonPropertyName("parentModelUri")]
public string ParentModelUri { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,208 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using ManagedCommon;
using Microsoft.AI.Foundry.Local;
namespace LanguageModelProvider.FoundryLocal;
internal sealed class FoundryClient
{
public static async Task<FoundryClient?> CreateAsync()
{
try
{
Logger.LogInfo("[FoundryClient] Creating Foundry Local client");
var manager = new FoundryLocalManager();
// Check if service is already running
if (manager.IsServiceRunning)
{
Logger.LogInfo("[FoundryClient] Foundry service is already running");
return new FoundryClient(manager);
}
// Start the service using SDK's method
Logger.LogInfo("[FoundryClient] Starting Foundry service using manager.StartServiceAsync()");
await manager.StartServiceAsync().ConfigureAwait(false);
Logger.LogInfo("[FoundryClient] Foundry service started successfully");
return new FoundryClient(manager);
}
catch (Exception ex)
{
Logger.LogError($"[FoundryClient] Error creating client: {ex.Message}");
if (ex.InnerException != null)
{
Logger.LogError($"[FoundryClient] Inner exception: {ex.InnerException.Message}");
}
return null;
}
}
private readonly FoundryLocalManager _foundryManager;
private readonly List<FoundryCatalogModel> _catalogModels = [];
private FoundryClient(FoundryLocalManager foundryManager)
{
_foundryManager = foundryManager;
}
public Task<string?> GetServiceUrl()
{
try
{
return Task.FromResult(_foundryManager.Endpoint?.ToString());
}
catch
{
return Task.FromResult<string?>(null);
}
}
public Uri? GetServiceUri()
{
try
{
return _foundryManager.ServiceUri;
}
catch
{
return null;
}
}
public async Task<List<FoundryCatalogModel>> ListCatalogModels()
{
if (_catalogModels.Count > 0)
{
return _catalogModels;
}
try
{
Logger.LogInfo("[FoundryClient] Listing catalog models");
var models = await _foundryManager.ListCatalogModelsAsync().ConfigureAwait(false);
if (models != null)
{
foreach (var model in models)
{
_catalogModels.Add(new FoundryCatalogModel
{
Name = model.ModelId ?? string.Empty,
DisplayName = model.DisplayName ?? string.Empty,
ProviderType = model.ProviderType ?? string.Empty,
Uri = model.Uri ?? string.Empty,
Version = model.Version ?? string.Empty,
ModelType = model.ModelType ?? string.Empty,
Publisher = model.Publisher ?? string.Empty,
Task = model.Task ?? string.Empty,
FileSizeMb = model.FileSizeMb,
Alias = model.Alias ?? string.Empty,
License = model.License ?? string.Empty,
LicenseDescription = model.LicenseDescription ?? string.Empty,
ParentModelUri = model.ParentModelUri ?? string.Empty,
SupportsToolCalling = model.SupportsToolCalling,
});
}
Logger.LogInfo($"[FoundryClient] Found {_catalogModels.Count} catalog models");
}
}
catch (Exception ex)
{
Logger.LogError($"[FoundryClient] Error listing catalog models: {ex.Message}");
// Surfacing errors here prevents listing other providers; swallow and return cached list instead.
}
return _catalogModels;
}
public async Task<List<FoundryCachedModel>> ListCachedModels()
{
try
{
Logger.LogInfo("[FoundryClient] Listing cached models");
var cachedModels = await _foundryManager.ListCachedModelsAsync().ConfigureAwait(false);
var catalogModels = await ListCatalogModels().ConfigureAwait(false);
List<FoundryCachedModel> models = [];
foreach (var model in cachedModels)
{
var catalogModel = catalogModels.FirstOrDefault(m => m.Name == model.ModelId);
var alias = catalogModel?.Alias ?? model.Alias;
models.Add(new FoundryCachedModel(model.ModelId ?? string.Empty, alias));
}
Logger.LogInfo($"[FoundryClient] Found {models.Count} cached models");
return models;
}
catch (Exception ex)
{
Logger.LogError($"[FoundryClient] Error listing cached models: {ex.Message}");
return [];
}
}
public async Task<bool> IsModelLoaded(string modelId)
{
try
{
var loadedModels = await _foundryManager.ListLoadedModelsAsync().ConfigureAwait(false);
var isLoaded = loadedModels.Any(m => m.ModelId == modelId);
Logger.LogInfo($"[FoundryClient] IsModelLoaded({modelId}): {isLoaded}");
Logger.LogInfo($"[FoundryClient] Loaded models: {string.Join(", ", loadedModels.Select(m => m.ModelId))}");
return isLoaded;
}
catch (Exception ex)
{
Logger.LogError($"[FoundryClient] IsModelLoaded exception: {ex.Message}");
return false;
}
}
public async Task<bool> EnsureModelLoaded(string modelId)
{
try
{
Logger.LogInfo($"[FoundryClient] EnsureModelLoaded called with: {modelId}");
// Check if already loaded
if (await IsModelLoaded(modelId).ConfigureAwait(false))
{
Logger.LogInfo($"[FoundryClient] 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;
}
}
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace LanguageModelProvider.FoundryLocal;
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = false)]
[JsonSerializable(typeof(FoundryCatalogModel))]
[JsonSerializable(typeof(List<FoundryCatalogModel>))]
internal sealed partial class FoundryJsonContext : JsonSerializerContext
{
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace LanguageModelProvider.FoundryLocal;
internal sealed record ModelSettings
{
// The sample shows an empty array; keep it open-ended.
[JsonPropertyName("parameters")]
public List<JsonElement> Parameters { get; init; } = [];
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace LanguageModelProvider.FoundryLocal;
internal sealed record PromptTemplate
{
[JsonPropertyName("assistant")]
public string Assistant { get; init; } = string.Empty;
[JsonPropertyName("prompt")]
public string Prompt { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace LanguageModelProvider.FoundryLocal;
internal sealed record Runtime
{
[JsonPropertyName("deviceType")]
public string DeviceType { get; init; } = string.Empty;
[JsonPropertyName("executionProvider")]
public string ExecutionProvider { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,185 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.ClientModel;
using LanguageModelProvider.FoundryLocal;
using ManagedCommon;
using Microsoft.Extensions.AI;
using OpenAI;
namespace LanguageModelProvider;
public sealed class FoundryLocalModelProvider : ILanguageModelProvider
{
private IEnumerable<ModelDetails>? _downloadedModels;
private FoundryClient? _foundryManager;
private string? _serviceUrl;
public static FoundryLocalModelProvider Instance { get; } = new();
public string Name => "FoundryLocal";
public string ProviderDescription => "The model will run locally via Foundry Local";
public string UrlPrefix => "fl://";
public IChatClient? GetIChatClient(string url)
{
try
{
Logger.LogInfo($"[FoundryLocal] GetIChatClient called with url: {url}");
InitializeAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Logger.LogError($"[FoundryLocal] Failed to initialize: {ex.Message}");
return null;
}
if (string.IsNullOrWhiteSpace(_serviceUrl) || _foundryManager == null)
{
Logger.LogError("[FoundryLocal] Service URL or manager is null");
return null;
}
// Extract model ID from URL (format: fl://modelname)
var modelId = url.Replace(UrlPrefix, string.Empty).Trim('/');
if (string.IsNullOrWhiteSpace(modelId))
{
Logger.LogError("[FoundryLocal] Model ID is empty after extraction");
return null;
}
Logger.LogInfo($"[FoundryLocal] Extracted model ID: {modelId}");
// Ensure the model is loaded before returning chat client
try
{
var isLoaded = _foundryManager.EnsureModelLoaded(modelId).GetAwaiter().GetResult();
if (!isLoaded)
{
Logger.LogError($"[FoundryLocal] Failed to load model: {modelId}");
return null;
}
Logger.LogInfo($"[FoundryLocal] Model is loaded: {modelId}");
}
catch (Exception ex)
{
Logger.LogError($"[FoundryLocal] Exception ensuring model loaded: {ex.Message}");
return null;
}
// Use ServiceUri instead of Endpoint since Endpoint already includes /v1
var baseUri = _foundryManager.GetServiceUri();
if (baseUri == null)
{
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 })
.GetChatClient(modelId)
.AsIChatClient();
}
public string GetIChatClientString(string url)
{
try
{
InitializeAsync().GetAwaiter().GetResult();
}
catch
{
return string.Empty;
}
var modelId = url.Split('/').LastOrDefault();
if (string.IsNullOrWhiteSpace(_serviceUrl) || string.IsNullOrWhiteSpace(modelId))
{
return string.Empty;
}
return $"new OpenAIClient(new ApiKeyCredential(\"none\"), new OpenAIClientOptions{{ Endpoint = new Uri(\"{_serviceUrl}/v1\") }}).GetChatClient(\"{modelId}\").AsIChatClient()";
}
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 (_foundryManager != null && _downloadedModels != null && _downloadedModels.Any())
{
return;
}
Logger.LogInfo("[FoundryLocal] Initializing provider");
_foundryManager ??= await FoundryClient.CreateAsync();
if (_foundryManager == null)
{
Logger.LogError("[FoundryLocal] Failed to create Foundry client");
return;
}
_serviceUrl ??= await _foundryManager.GetServiceUrl();
Logger.LogInfo($"[FoundryLocal] Service URL: {_serviceUrl}");
var cachedModels = await _foundryManager.ListCachedModels();
Logger.LogInfo($"[FoundryLocal] Found {cachedModels.Count} cached models");
List<ModelDetails> downloadedModels = [];
foreach (var model in cachedModels)
{
Logger.LogInfo($"[FoundryLocal] Adding unmatched cached model: {model.Name}");
downloadedModels.Add(new ModelDetails
{
Id = $"fl-{model.Name}",
Name = model.Name,
Url = $"{UrlPrefix}{model.Name}",
Description = $"{model.Name} running locally with Foundry Local",
HardwareAccelerators = [HardwareAccelerator.FOUNDRYLOCAL],
SupportedOnQualcomm = true,
ProviderModelDetails = model,
});
}
_downloadedModels = downloadedModels;
Logger.LogInfo($"[FoundryLocal] Initialization complete. Total downloaded models: {downloadedModels.Count}");
}
public async Task<bool> IsAvailable()
{
Logger.LogInfo("[FoundryLocal] Checking availability");
await InitializeAsync();
var available = _foundryManager != null;
Logger.LogInfo($"[FoundryLocal] Available: {available}");
return available;
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace LanguageModelProvider;
public enum HardwareAccelerator
{
CPU,
DML,
QNN,
WCRAPI,
OLLAMA,
OPENAI,
FOUNDRYLOCAL,
LEMONADE,
NPU,
GPU,
VitisAI,
OpenVINO,
NvTensorRT,
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Extensions.AI;
namespace LanguageModelProvider;
public interface ILanguageModelProvider
{
string Name { get; }
string UrlPrefix { get; }
string ProviderDescription { get; }
Task<IEnumerable<ModelDetails>> GetModelsAsync(bool ignoreCached = false, CancellationToken cancelationToken = default);
IChatClient? GetIChatClient(string url);
string GetIChatClientString(string url);
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
<PackageReference Include="Microsoft.AI.Foundry.Local" />
<PackageReference Include="OpenAI" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ManagedCommon\ManagedCommon.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace LanguageModelProvider;
public class ModelDetails
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public long Size { get; set; }
public bool IsUserAdded { get; set; }
public string Icon { get; set; } = string.Empty;
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

@@ -26,6 +26,16 @@ namespace ManagedCommon
private static readonly string Version = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version ?? "Unknown";
/// <summary>
/// Gets the path to the log directory for the current version of the app.
/// </summary>
public static string CurrentVersionLogDirectoryPath { get; private set; }
/// <summary>
/// Gets the path to the log directory for the app.
/// </summary>
public static string AppLogDirectoryPath { get; private set; }
/// <summary>
/// Initializes the logger and sets the path for logging.
/// </summary>
@@ -42,6 +52,9 @@ namespace ManagedCommon
Directory.CreateDirectory(versionedPath);
}
AppLogDirectoryPath = basePath;
CurrentVersionLogDirectoryPath = versionedPath;
var logFilePath = Path.Combine(versionedPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
@@ -130,7 +143,7 @@ namespace ManagedCommon
{
exMessage +=
"Inner exception: " + Environment.NewLine +
ex.InnerException.GetType() + " (" + ex.HResult + "): " + ex.InnerException.Message + Environment.NewLine;
ex.InnerException.GetType() + " (" + ex.InnerException.HResult + "): " + ex.InnerException.Message + Environment.NewLine;
}
exMessage +=

View File

@@ -12,6 +12,7 @@ namespace ManagedCommon
ColorPicker,
CmdPal,
CropAndLock,
CursorWrap,
EnvironmentVariables,
FancyZones,
FileLocksmith,

View File

@@ -63,14 +63,12 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>

View File

@@ -59,6 +59,7 @@ struct LogSettings
inline const static std::string mouseHighlighterLoggerName = "mouse-highlighter";
inline const static std::string mouseJumpLoggerName = "mouse-jump";
inline const static std::string mousePointerCrosshairsLoggerName = "mouse-pointer-crosshairs";
inline const static std::string cursorWrapLoggerName = "cursor-wrap";
inline const static std::string imageResizerLoggerName = "imageresizer";
inline const static std::string powerRenameLoggerName = "powerrename";
inline const static std::string alwaysOnTopLoggerName = "always-on-top";

View File

@@ -46,16 +46,6 @@
<PropertyGroup>
<TargetName>notifications</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>

View File

@@ -3,6 +3,7 @@
#include <Windows.h>
#include <optional>
#include <vector>
#include <string>
namespace powertoys_gpo
{
@@ -51,6 +52,7 @@ namespace powertoys_gpo
const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_HIGHLIGHTER = L"ConfigureEnabledUtilityMouseHighlighter";
const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_JUMP = L"ConfigureEnabledUtilityMouseJump";
const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_POINTER_CROSSHAIRS = L"ConfigureEnabledUtilityMousePointerCrosshairs";
const std::wstring POLICY_CONFIGURE_ENABLED_CURSOR_WRAP = L"ConfigureEnabledUtilityCursorWrap";
const std::wstring POLICY_CONFIGURE_ENABLED_POWER_RENAME = L"ConfigureEnabledUtilityPowerRename";
const std::wstring POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER = L"ConfigureEnabledUtilityPowerLauncher";
const std::wstring POLICY_CONFIGURE_ENABLED_QUICK_ACCENT = L"ConfigureEnabledUtilityQuickAccent";
@@ -82,6 +84,14 @@ namespace powertoys_gpo
const std::wstring POLICY_CONFIGURE_RUN_AT_STARTUP = L"ConfigureRunAtStartup";
const std::wstring POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER_ALL_PLUGINS = L"PowerLauncherAllPluginsEnabledState";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_ONLINE_AI_MODELS = L"AllowPowerToysAdvancedPasteOnlineAIModels";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_OPENAI = L"AllowAdvancedPasteOpenAI";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_AZURE_OPENAI = L"AllowAdvancedPasteAzureOpenAI";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_AZURE_AI_INFERENCE = L"AllowAdvancedPasteAzureAIInference";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_MISTRAL = L"AllowAdvancedPasteMistral";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_GOOGLE = L"AllowAdvancedPasteGoogle";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_ANTHROPIC = L"AllowAdvancedPasteAnthropic";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_OLLAMA = L"AllowAdvancedPasteOllama";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_FOUNDRY_LOCAL = L"AllowAdvancedPasteFoundryLocal";
const std::wstring POLICY_MWB_CLIPBOARD_SHARING_ENABLED = L"MwbClipboardSharingEnabled";
const std::wstring POLICY_MWB_FILE_TRANSFER_ENABLED = L"MwbFileTransferEnabled";
const std::wstring POLICY_MWB_USE_ORIGINAL_USER_INTERFACE = L"MwbUseOriginalUserInterface";
@@ -401,6 +411,11 @@ namespace powertoys_gpo
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_MOUSE_POINTER_CROSSHAIRS);
}
inline gpo_rule_configured_t getConfiguredCursorWrapEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_CURSOR_WRAP);
}
inline gpo_rule_configured_t getConfiguredPowerRenameEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_POWER_RENAME);
@@ -575,6 +590,46 @@ namespace powertoys_gpo
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_ONLINE_AI_MODELS);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteOpenAIValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_OPENAI);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteAzureOpenAIValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_AZURE_OPENAI);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteAzureAIInferenceValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_AZURE_AI_INFERENCE);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteMistralValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_MISTRAL);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteGoogleValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_GOOGLE);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteAnthropicValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_ANTHROPIC);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteOllamaValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_OLLAMA);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteFoundryLocalValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_FOUNDRY_LOCAL);
}
inline gpo_rule_configured_t getConfiguredMwbClipboardSharingEnabledValue()
{
return getConfiguredValue(POLICY_MWB_CLIPBOARD_SHARING_ENABLED);

View File

@@ -33,9 +33,4 @@
<Target Name="PostBuildAction" AfterTargets="Build" Outputs="$(GeneratedDSCModule)" Condition="'$(Platform)'!='ARM64'">
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;..\..\..\x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule) $(GeneratedDSCManifest)" />
</Target>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Platform)'=='ARM64'">
<Exec Command="&quot;$(MSBuildToolsPath)\msbuild.exe&quot; PowerToys.sln -p:Configuration=&quot;$(Configuration)&quot; -p:Platform=&quot;x64&quot; -verbosity:m -t:DSC\PowerToys_Settings_DSC_Schema_Generator" WorkingDirectory="..\..\..\" />
</Target>
</Project>

View File

@@ -23,7 +23,8 @@ public sealed class SettingsResourceAdvancedPasteModuleTest : SettingsResourceMo
{
s.Properties.ShowCustomPreview = !s.Properties.ShowCustomPreview;
s.Properties.CloseAfterLosingFocus = !s.Properties.CloseAfterLosingFocus;
s.Properties.IsAdvancedAIEnabled = !s.Properties.IsAdvancedAIEnabled;
// s.Properties.IsAdvancedAIEnabled = !s.Properties.IsAdvancedAIEnabled;
s.Properties.AdvancedPasteUIShortcut = new HotkeySettings
{
Key = "mock",

View File

@@ -41,8 +41,8 @@
</ItemGroup>
<!-- In debug mode, generate the DSC resource JSON files -->
<Target Name="GenerateDscResourceJsonFiles" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
<Message Text="Generating DSC resource JSON files inside ..." Importance="high" />
<Exec Command="dotnet &quot;$(TargetPath)&quot; manifest --resource settings --outputDir &quot;$(TargetDir)\&quot;" />
<Target Name="GenerateDscResourceJsonFiles" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
<Message Text="Generating DSC resource JSON files inside ..." Importance="high" />
<Exec Command="dotnet &quot;$(TargetPath)&quot; manifest --resource settings --outputDir &quot;$(TargetDir)\&quot;" />
</Target>
</Project>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.17" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.18" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyNamespaces>
<target prefix="powertoys" namespace="Microsoft.Policies.PowerToys" />
</policyNamespaces>
<resources minRequiredRevision="1.17"/><!-- Last changed with PowerToys v0.90.0 -->
<resources minRequiredRevision="1.18"/><!-- Last changed with PowerToys v0.96.0 -->
<supportedOn>
<definitions>
<definition name="SUPPORTED_POWERTOYS_0_64_0" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0)"/>
@@ -26,6 +26,7 @@
<definition name="SUPPORTED_POWERTOYS_0_88_0" displayName="$(string.SUPPORTED_POWERTOYS_0_88_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_89_0" displayName="$(string.SUPPORTED_POWERTOYS_0_89_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_90_0" displayName="$(string.SUPPORTED_POWERTOYS_0_90_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_96_0" displayName="$(string.SUPPORTED_POWERTOYS_0_96_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1)"/>
</definitions>
</supportedOn>
@@ -614,6 +615,86 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteOpenAI" class="Both" displayName="$(string.AllowAdvancedPasteOpenAI)" explainText="$(string.AllowAdvancedPasteOpenAIDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteOpenAI">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteAzureOpenAI" class="Both" displayName="$(string.AllowAdvancedPasteAzureOpenAI)" explainText="$(string.AllowAdvancedPasteAzureOpenAIDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteAzureOpenAI">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteAzureAIInference" class="Both" displayName="$(string.AllowAdvancedPasteAzureAIInference)" explainText="$(string.AllowAdvancedPasteAzureAIInferenceDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteAzureAIInference">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteMistral" class="Both" displayName="$(string.AllowAdvancedPasteMistral)" explainText="$(string.AllowAdvancedPasteMistralDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteMistral">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteGoogle" class="Both" displayName="$(string.AllowAdvancedPasteGoogle)" explainText="$(string.AllowAdvancedPasteGoogleDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteGoogle">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteAnthropic" class="Both" displayName="$(string.AllowAdvancedPasteAnthropic)" explainText="$(string.AllowAdvancedPasteAnthropicDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteAnthropic">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteOllama" class="Both" displayName="$(string.AllowAdvancedPasteOllama)" explainText="$(string.AllowAdvancedPasteOllamaDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteOllama">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="AllowAdvancedPasteFoundryLocal" class="Both" displayName="$(string.AllowAdvancedPasteFoundryLocal)" explainText="$(string.AllowAdvancedPasteFoundryLocalDescription)" key="Software\Policies\PowerToys" valueName="AllowAdvancedPasteFoundryLocal">
<parentCategory ref="AdvancedPaste" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_96_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="MwbClipboardSharingEnabled" class="Both" displayName="$(string.MwbClipboardSharingEnabled)" explainText="$(string.MwbClipboardSharingEnabledDescription)" key="Software\Policies\PowerToys" valueName="MwbClipboardSharingEnabled">
<parentCategory ref="MouseWithoutBorders" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.17" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.18" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<displayName>PowerToys</displayName>
<description>PowerToys</description>
<resources>
@@ -33,6 +33,7 @@
<string id="SUPPORTED_POWERTOYS_0_88_0">PowerToys version 0.88.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_89_0">PowerToys version 0.89.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_90_0">PowerToys version 0.90.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_96_0">PowerToys version 0.96.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1">From PowerToys version 0.64.0 until PowerToys version 0.87.1</string>
<string id="ConfigureAllUtilityGlobalEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
@@ -291,6 +292,54 @@ If you don't configure this policy, the user will be able to control the setting
<string id="ConfigureEnabledUtilityFileExplorerQOIPreview">QOI file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerQOIThumbnails">QOI file thumbnail: Configure enabled state</string>
<string id="AllowPowerToysAdvancedPasteOnlineAIModels">Allow using online AI models</string>
<string id="AllowAdvancedPasteOpenAI">Advanced Paste: Allow OpenAI endpoint</string>
<string id="AllowAdvancedPasteOpenAIDescription">This policy controls whether users can use the OpenAI endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use OpenAI as their AI provider.
If you disable this policy, users will not be able to select or use OpenAI endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteAzureOpenAI">Advanced Paste: Allow Azure OpenAI endpoint</string>
<string id="AllowAdvancedPasteAzureOpenAIDescription">This policy controls whether users can use the Azure OpenAI endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Azure OpenAI as their AI provider.
If you disable this policy, users will not be able to select or use Azure OpenAI endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteAzureAIInference">Advanced Paste: Allow Azure AI Inference endpoint</string>
<string id="AllowAdvancedPasteAzureAIInferenceDescription">This policy controls whether users can use the Azure AI Inference endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Azure AI Inference as their AI provider.
If you disable this policy, users will not be able to select or use Azure AI Inference endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteMistral">Advanced Paste: Allow Mistral endpoint</string>
<string id="AllowAdvancedPasteMistralDescription">This policy controls whether users can use the Mistral AI endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Mistral as their AI provider.
If you disable this policy, users will not be able to select or use Mistral endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteGoogle">Advanced Paste: Allow Google endpoint</string>
<string id="AllowAdvancedPasteGoogleDescription">This policy controls whether users can use the Google (Gemini) endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Google as their AI provider.
If you disable this policy, users will not be able to select or use Google endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteAnthropic">Advanced Paste: Allow Anthropic endpoint</string>
<string id="AllowAdvancedPasteAnthropicDescription">This policy controls whether users can use the Anthropic (Claude) endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Anthropic as their AI provider.
If you disable this policy, users will not be able to select or use Anthropic endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteOllama">Advanced Paste: Allow Ollama endpoint</string>
<string id="AllowAdvancedPasteOllamaDescription">This policy controls whether users can use the Ollama local model endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Ollama as their AI provider.
If you disable this policy, users will not be able to select or use Ollama endpoint in Advanced Paste settings.</string>
<string id="AllowAdvancedPasteFoundryLocal">Advanced Paste: Allow Foundry Local endpoint</string>
<string id="AllowAdvancedPasteFoundryLocalDescription">This policy controls whether users can use the Foundry Local model endpoint in Advanced Paste.
If you enable or don't configure this policy, users can configure and use Foundry Local as their AI provider.
If you disable this policy, users will not be able to select or use Foundry Local endpoint in Advanced Paste settings.</string>
<string id="MwbClipboardSharingEnabled">Clipboard sharing enabled</string>
<string id="MwbFileTransferEnabled">File transfer enabled</string>
<string id="MwbUseOriginalUserInterface">Original user interface is available</string>

View File

@@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.UnitTests.Mocks;
/// <summary>
/// Minimal <see cref="IUserSettings"/> implementation used by integration tests that
/// need to construct the runtime Advanced Paste services.
/// </summary>
internal sealed class IntegrationTestUserSettings : IUserSettings
{
private readonly PasteAIConfiguration _configuration;
private readonly IReadOnlyList<AdvancedPasteCustomAction> _customActions;
private readonly IReadOnlyList<PasteFormats> _additionalActions;
public IntegrationTestUserSettings()
{
var provider = new PasteAIProviderDefinition
{
Id = "integration-openai",
EnableAdvancedAI = true,
ServiceTypeKind = AIServiceType.OpenAI,
ModelName = "gpt-4o",
ModerationEnabled = true,
};
_configuration = new PasteAIConfiguration
{
ActiveProviderId = provider.Id,
Providers = new ObservableCollection<PasteAIProviderDefinition> { provider },
};
_customActions = Array.Empty<AdvancedPasteCustomAction>();
_additionalActions = Array.Empty<PasteFormats>();
}
public bool IsAIEnabled => true;
public bool ShowCustomPreview => false;
public bool CloseAfterLosingFocus => false;
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions => _customActions;
public IReadOnlyList<PasteFormats> AdditionalActions => _additionalActions;
public PasteAIConfiguration PasteAIConfiguration => _configuration;
public event EventHandler Changed;
public Task SetActiveAIProviderAsync(string providerId)
{
_configuration.ActiveProviderId = providerId ?? string.Empty;
Changed?.Invoke(this, EventArgs.Empty);
return Task.CompletedTask;
}
}

View File

@@ -13,6 +13,8 @@ using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services;
using AdvancedPaste.Services.CustomActions;
using AdvancedPaste.Services.OpenAI;
using AdvancedPaste.UnitTests.Mocks;
using ManagedCommon;
@@ -79,7 +81,9 @@ public sealed class AIServiceBatchIntegrationTests
Assert.IsTrue(results.Count <= inputs.Count);
CollectionAssert.AreEqual(results.Select(result => result.ToInput()).ToList(), inputs.Take(results.Count).ToList());
#pragma warning disable IL2026, IL3050 // The tests rely on runtime JSON serialization for ad-hoc data files.
async Task WriteResultsAsync() => await File.WriteAllTextAsync(resultsFile, JsonSerializer.Serialize(results, SerializerOptions));
#pragma warning restore IL2026, IL3050
Logger.LogInfo($"Starting {nameof(TestGenerateBatchResults)}; Count={inputs.Count}, InCache={results.Count}");
@@ -101,8 +105,12 @@ public sealed class AIServiceBatchIntegrationTests
await WriteResultsAsync();
}
private static async Task<List<T>> GetDataListAsync<T>(string filePath) =>
File.Exists(filePath) ? JsonSerializer.Deserialize<List<T>>(await File.ReadAllTextAsync(filePath)) : [];
private static async Task<List<T>> GetDataListAsync<T>(string filePath)
{
#pragma warning disable IL2026, IL3050 // Tests only run locally and can depend on runtime JSON serialization.
return File.Exists(filePath) ? JsonSerializer.Deserialize<List<T>>(await File.ReadAllTextAsync(filePath)) : [];
#pragma warning restore IL2026, IL3050
}
private static async Task<string> GetTextOutputAsync(BatchTestInput input, PasteFormats format)
{
@@ -130,23 +138,35 @@ public sealed class AIServiceBatchIntegrationTests
private static async Task<DataPackage> GetOutputDataPackageAsync(BatchTestInput batchTestInput, PasteFormats format)
{
VaultCredentialsProvider credentialsProvider = new();
PromptModerationService promptModerationService = new(credentialsProvider);
var services = CreateServices();
NoOpProgress progress = new();
CustomTextTransformService customTextTransformService = new(credentialsProvider, promptModerationService);
switch (format)
{
case PasteFormats.CustomTextTransformation:
return DataPackageHelpers.CreateFromText(await customTextTransformService.TransformTextAsync(batchTestInput.Prompt, batchTestInput.Clipboard, CancellationToken.None, progress));
var transformResult = await services.CustomActionTransformService.TransformTextAsync(batchTestInput.Prompt, batchTestInput.Clipboard, CancellationToken.None, progress);
return DataPackageHelpers.CreateFromText(transformResult.Content ?? string.Empty);
case PasteFormats.KernelQuery:
var clipboardData = DataPackageHelpers.CreateFromText(batchTestInput.Clipboard).GetView();
KernelService kernelService = new(new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, customTextTransformService);
return await kernelService.TransformClipboardAsync(batchTestInput.Prompt, clipboardData, isSavedQuery: false, CancellationToken.None, progress);
return await services.KernelService.TransformClipboardAsync(batchTestInput.Prompt, clipboardData, isSavedQuery: false, CancellationToken.None, progress);
default:
throw new InvalidOperationException($"Unexpected format {format}");
}
}
private static IntegrationTestServices CreateServices()
{
IntegrationTestUserSettings userSettings = new();
EnhancedVaultCredentialsProvider credentialsProvider = new(userSettings);
PromptModerationService promptModerationService = new(credentialsProvider);
PasteAIProviderFactory providerFactory = new();
ICustomActionTransformService customActionTransformService = new CustomActionTransformService(promptModerationService, providerFactory, credentialsProvider, userSettings);
IKernelService kernelService = new AdvancedAIKernelService(credentialsProvider, new NoOpKernelQueryCacheService(), promptModerationService, userSettings, customActionTransformService);
return new IntegrationTestServices(customActionTransformService, kernelService);
}
private readonly record struct IntegrationTestServices(ICustomActionTransformService CustomActionTransformService, IKernelService KernelService);
}

View File

@@ -11,6 +11,8 @@ using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services;
using AdvancedPaste.Services.CustomActions;
using AdvancedPaste.Services.OpenAI;
using AdvancedPaste.Telemetry;
using AdvancedPaste.UnitTests.Mocks;
@@ -27,16 +29,19 @@ namespace AdvancedPaste.UnitTests.ServicesTests;
public sealed class KernelServiceIntegrationTests : IDisposable
{
private const string StandardImageFile = "image_with_text_example.png";
private KernelService _kernelService;
private IKernelService _kernelService;
private AdvancedPasteEventListener _eventListener;
[TestInitialize]
public void TestInitialize()
{
VaultCredentialsProvider credentialsProvider = new();
IntegrationTestUserSettings userSettings = new();
EnhancedVaultCredentialsProvider credentialsProvider = new(userSettings);
PromptModerationService promptModerationService = new(credentialsProvider);
PasteAIProviderFactory providerFactory = new();
CustomActionTransformService customActionTransformService = new(promptModerationService, providerFactory, credentialsProvider, userSettings);
_kernelService = new KernelService(new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, new CustomTextTransformService(credentialsProvider, promptModerationService));
_kernelService = new AdvancedAIKernelService(credentialsProvider, new NoOpKernelQueryCacheService(), promptModerationService, userSettings, customActionTransformService);
_eventListener = new();
}

View File

@@ -33,11 +33,14 @@
</PropertyGroup>
<ItemGroup>
<None Remove="AdvancedPasteXAML\Controls\ClipboardHistoryItemPreviewControl.xaml" />
<None Remove="AdvancedPasteXAML\Controls\PromptBox.xaml" />
<None Remove="AdvancedPasteXAML\Styles\Button.xaml" />
<None Remove="Assets\AdvancedPaste\AIIcon.png" />
<None Remove="Assets\AdvancedPaste\Gradient.png" />
<None Remove="AdvancedPasteXAML\Controls\AnimatedContentControl\AnimatedBorderBrush.xaml" />
<None Remove="AdvancedPasteXAML\Views\MainPage.xaml" />
<None Remove="Assets\AdvancedPaste\SemanticKernel.svg" />
</ItemGroup>
<ItemGroup>
@@ -49,7 +52,6 @@
<ItemGroup>
<PackageReference Include="OpenAI" />
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
@@ -57,10 +59,17 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageReference Include="MessagePack" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Amazon" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Google" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.HuggingFace" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.MistralAI" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.SemanticKernel" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="System.ClientModel" />
<PackageReference Include="Microsoft.Windows.Compatibility" />
<PackageReference Include="Microsoft.Windows.CsWin32" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
@@ -102,6 +111,7 @@
<!-- HACK: Common.UI is referenced, even if it is not used, to force dll versions to be the same as in other projects that use it. It's still unclear why this is the case, but this is need for flattening the install directory. -->
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\common\LanguageModelProvider\LanguageModelProvider.csproj" />
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
@@ -114,9 +124,38 @@
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
<ItemGroup>
<Page Update="AdvancedPasteXAML\Controls\ClipboardHistoryItemPreviewControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="AdvancedPasteXAML\Controls\PromptBox.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="AdvancedPasteXAML\Styles\Button.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<!-- Share AI Model Provider Icons from Settings.UI to avoid duplication -->
<!-- These icons are included from Settings.UI project -->
<Content Include="..\..\..\settings-ui\Settings.UI\Assets\Settings\Icons\Models\*.svg">
<Link>Assets\Settings\Icons\Models\%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- AdvancedPaste specific assets -->
<Content Include="Assets\AdvancedPaste\*.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\AdvancedPaste\*.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- Keep SemanticKernel.svg as it's specific to AdvancedPaste -->
<Content Include="Assets\AdvancedPaste\SemanticKernel.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -9,6 +9,7 @@
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="ms-appx:///AdvancedPasteXAML/Controls/AnimatedContentControl/AnimatedContentControl.xaml" />
<ResourceDictionary Source="ms-appx:///AdvancedPasteXAML/Styles/Button.xaml" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->

View File

@@ -10,10 +10,10 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services;
using AdvancedPaste.Services.CustomActions;
using AdvancedPaste.Settings;
using AdvancedPaste.ViewModels;
using ManagedCommon;
@@ -77,11 +77,12 @@ namespace AdvancedPaste
{
services.AddSingleton<IFileSystem, FileSystem>();
services.AddSingleton<IUserSettings, UserSettings>();
services.AddSingleton<IAICredentialsProvider, Services.OpenAI.VaultCredentialsProvider>();
services.AddSingleton<IAICredentialsProvider, EnhancedVaultCredentialsProvider>();
services.AddSingleton<IPromptModerationService, Services.OpenAI.PromptModerationService>();
services.AddSingleton<ICustomTextTransformService, Services.OpenAI.CustomTextTransformService>();
services.AddSingleton<IKernelQueryCacheService, CustomActionKernelQueryCacheService>();
services.AddSingleton<IKernelService, Services.OpenAI.KernelService>();
services.AddSingleton<IPasteAIProviderFactory, PasteAIProviderFactory>();
services.AddSingleton<ICustomActionTransformService, CustomActionTransformService>();
services.AddSingleton<IKernelService, AdvancedAIKernelService>();
services.AddSingleton<IPasteFormatExecutor, PasteFormatExecutor>();
services.AddSingleton<OptionsViewModel>();
}).Build();
@@ -111,7 +112,11 @@ namespace AdvancedPaste
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
#if DEBUG
protected async override void OnLaunched(LaunchActivatedEventArgs args)
#else
protected override void OnLaunched(LaunchActivatedEventArgs args)
#endif
{
var cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs?.Length > 1)
@@ -133,6 +138,10 @@ namespace AdvancedPaste
{
ProcessNamedPipe(cmdArgs[2]);
}
#if DEBUG
await ShowWindow(); // This allows for direct access without using PowerToys Runner, not all functionality might work
#endif
}
private void ProcessNamedPipe(string pipeName)

View File

@@ -11,7 +11,7 @@
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="CornerRadius" Value="16" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AnimatedContentControl">

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="AdvancedPaste.Controls.ClipboardHistoryItemPreviewControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:AdvancedPaste.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:AdvancedPaste.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
mc:Ignorable="d">
<UserControl.Resources>
<converters:DateTimeToFriendlyStringConverter x:Key="DateTimeToFriendlyStringConverter" />
<tkconverters:BoolToVisibilityConverter x:Name="BoolToVisibilityConverter" />
</UserControl.Resources>
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource SubtleFillColorSecondaryBrush}" CornerRadius="16,0,0,16">
<Grid>
<!-- Image preview -->
<Image
Source="{x:Bind ClipboardItem.Image, Mode=OneWay}"
Stretch="UniformToFill"
Visibility="{x:Bind HasImage, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<!-- Text preview -->
<TextBlock
Margin="8,0,0,0"
VerticalAlignment="Center"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ClipboardItem.Content, Mode=OneWay}"
TextWrapping="Wrap"
Visibility="{x:Bind HasText, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<!-- Icon glyph fallback -->
<FontIcon
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="48"
Glyph="{x:Bind IconGlyph, Mode=OneWay}"
Visibility="{x:Bind HasGlyph, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
</Border>
<StackPanel
Grid.Column="1"
VerticalAlignment="Center"
Spacing="2">
<TextBlock
Style="{StaticResource BodyTextBlockStyle}"
Text="{x:Bind Header, Mode=OneWay}"
TextWrapping="NoWrap" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Timestamp, Converter={StaticResource DateTimeToFriendlyStringConverter}, Mode=OneWay}" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
namespace AdvancedPaste.Controls
{
public sealed partial class ClipboardHistoryItemPreviewControl : UserControl
{
public static readonly DependencyProperty ClipboardItemProperty = DependencyProperty.Register(
nameof(ClipboardItem),
typeof(ClipboardItem),
typeof(ClipboardHistoryItemPreviewControl),
new PropertyMetadata(defaultValue: null, OnClipboardItemChanged));
public ClipboardItem ClipboardItem
{
get => (ClipboardItem)GetValue(ClipboardItemProperty);
set => SetValue(ClipboardItemProperty, value);
}
// Computed properties for display
public string Header => ClipboardItem != null ? GetHeaderFromFormat(ClipboardItem.Format) : string.Empty;
public string IconGlyph => ClipboardItem != null ? GetGlyphFromFormat(ClipboardItem.Format) : string.Empty;
public string ContentText => ClipboardItem?.Content ?? string.Empty;
public ImageSource ContentImage => ClipboardItem?.Image;
public DateTimeOffset? Timestamp => ClipboardItem?.Timestamp ?? ClipboardItem?.Item?.Timestamp;
public bool HasImage => ContentImage is not null;
public bool HasText => !string.IsNullOrEmpty(ContentText) && !HasImage;
public bool HasGlyph => !HasImage && !HasText && !string.IsNullOrEmpty(IconGlyph);
public ClipboardHistoryItemPreviewControl()
{
InitializeComponent();
}
private static void OnClipboardItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ClipboardHistoryItemPreviewControl control)
{
// Notify bindings that all computed properties may have changed
control.Bindings.Update();
}
}
private static string GetHeaderFromFormat(ClipboardFormat format)
{
// Check flags in priority order (most specific first)
if (format.HasFlag(ClipboardFormat.Image))
{
return GetStringOrFallback("ClipboardPreviewCategoryImage", "Image");
}
if (format.HasFlag(ClipboardFormat.Video))
{
return GetStringOrFallback("ClipboardPreviewCategoryVideo", "Video");
}
if (format.HasFlag(ClipboardFormat.Audio))
{
return GetStringOrFallback("ClipboardPreviewCategoryAudio", "Audio");
}
if (format.HasFlag(ClipboardFormat.File))
{
return GetStringOrFallback("ClipboardPreviewCategoryFile", "File");
}
if (format.HasFlag(ClipboardFormat.Text) || format.HasFlag(ClipboardFormat.Html))
{
return GetStringOrFallback("ClipboardPreviewCategoryText", "Text");
}
return GetStringOrFallback("ClipboardPreviewCategoryUnknown", "Clipboard");
}
private static string GetGlyphFromFormat(ClipboardFormat format)
{
// Check flags in priority order (most specific first)
if (format.HasFlag(ClipboardFormat.Image))
{
return "\uEB9F"; // Image icon
}
if (format.HasFlag(ClipboardFormat.Video))
{
return "\uE714"; // Video icon
}
if (format.HasFlag(ClipboardFormat.Audio))
{
return "\uE189"; // Audio icon
}
if (format.HasFlag(ClipboardFormat.File))
{
return "\uE8A5"; // File icon
}
if (format.HasFlag(ClipboardFormat.Text) || format.HasFlag(ClipboardFormat.Html))
{
return "\uE8D2"; // Text icon
}
return "\uE77B"; // Generic clipboard icon
}
private static string GetStringOrFallback(string resourceKey, string fallback)
{
var value = ResourceLoaderInstance.ResourceLoader.GetString(resourceKey);
return string.IsNullOrEmpty(value) ? fallback : value;
}
}
}

View File

@@ -7,34 +7,21 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:AdvancedPaste.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:settings="using:Microsoft.PowerToys.Settings.UI.Library"
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
xmlns:ui="using:CommunityToolkit.WinUI"
x:Name="PromptBoxControl"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Color x:Key="AccentGradientColor">#65C8F2</Color>
<LinearGradientBrush x:Key="AccentGradientBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0.0" Color="#98EFFE" />
<GradientStop Offset="0.25" Color="#48B1E9" />
<GradientStop Offset="1.0" Color="{StaticResource AccentGradientColor}" />
</LinearGradientBrush>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Color x:Key="AccentGradientColor">#005FB8</Color>
<LinearGradientBrush x:Key="AccentGradientBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0.0" Color="#4992C7" />
<GradientStop Offset="0.25" Color="#1353A0" />
<GradientStop Offset="1.0" Color="{StaticResource AccentGradientColor}" />
</LinearGradientBrush>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Color x:Key="AccentGradientColor">#48B1E9</Color>
<SolidColorBrush x:Key="AccentGradientBrush" Color="{StaticResource AccentGradientColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<LinearGradientBrush x:Name="IntelligentUnderlineGradient" StartPoint="0,0.5" EndPoint="1,0.5">
<GradientStop Offset="0.0" Color="#FF0078D4" />
<GradientStop Offset="0.42" Color="#FF464FEB" />
<GradientStop Offset="0.87" Color="#FFD660FF" />
<GradientStop Offset="1.0" Color="#FFFEA874" />
</LinearGradientBrush>
<x:Double x:Key="ModelSelectorButtonWidth">44</x:Double>
<Style x:Key="CustomTextBoxStyle" TargetType="TextBox">
<Setter Property="Foreground" Value="{ThemeResource TextControlForeground}" />
<Setter Property="Background" Value="{ThemeResource TextControlBackground}" />
@@ -155,6 +142,7 @@
Foreground="{ThemeResource TextControlHeaderForeground}"
TextWrapping="Wrap"
Visibility="Collapsed" />
<Border
x:Name="BorderElement"
Grid.Row="1"
@@ -168,48 +156,32 @@
BorderThickness="{TemplateBinding BorderThickness}"
Control.IsTemplateFocusTarget="True"
CornerRadius="{TemplateBinding CornerRadius}" />
<Viewbox
<Rectangle
x:Name="FocusHighlighter"
Grid.Row="1"
Width="16"
Height="16"
Margin="8,0,0,0">
<StackPanel
Margin="0"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ProgressRing
Width="30"
Height="30"
HorizontalAlignment="Right"
VerticalAlignment="Center"
IsActive="{Binding DataContext.IsBusy, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
IsIndeterminate="{Binding DataContext.HasIndeterminateTransformProgress, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Maximum="100"
Minimum="0"
Visibility="{Binding DataContext.IsBusy, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}}"
Value="{Binding DataContext.TransformProgress, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<StackPanel
Margin="0"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Visibility="{Binding DataContext.IsBusy, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToInvertedVisibilityConverter}}">
<Image
x:Name="AIGlyphImage"
AutomationProperties.AccessibilityView="Raw"
Source="/Assets/AdvancedPaste/SemanticKernel.svg"
Visibility="{Binding DataContext.IsAdvancedAIEnabled, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}}" />
<PathIcon
x:Name="AIGlyph"
AutomationProperties.AccessibilityView="Raw"
Data="M128 766q0-42 24-77t65-48l178-57q32-11 61-30t52-42q50-50 71-114l58-179q13-40 48-65t78-26q42 0 77 24t50 65l58 177q21 66 72 117 49 50 117 72l176 58q43 14 69 48t26 80q0 41-25 76t-64 49l-178 58q-66 21-117 72-32 32-51 73t-33 84-26 83-30 73-45 51-71 20q-42 0-77-24t-49-65l-58-178q-8-25-19-47t-28-43q-34-43-77-68t-89-41-89-27-78-29-55-45-21-75zm1149 7q-76-29-145-53t-129-60-104-88-73-138l-57-176-67 176q-18 48-42 89t-60 78q-34 34-76 61t-89 43l-177 57q75 29 144 53t127 60 103 89 73 137l57 176 67-176q37-97 103-168t168-103l177-57zm-125 759q0-31 20-57t49-36l99-32q34-11 53-34t30-51 20-59 20-54 33-41 58-16q32 0 59 19t38 50q6 20 11 40t13 40 17 38 25 34q16 17 39 26t48 18 49 16 44 20 31 32 12 50q0 33-18 60t-51 38q-19 6-39 11t-41 13-39 17-34 25q-24 25-35 62t-24 73-35 61-68 25q-32 0-59-19t-38-50q-6-18-11-39t-13-41-17-40-24-33q-18-17-41-27t-47-17-49-15-43-20-30-33-12-54zm583 4q-43-13-74-30t-55-41-40-55-32-74q-12 41-29 72t-42 55-55 42-71 31q81 23 128 71t71 129q15-43 31-74t40-54 53-40 75-32z"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Visibility="{Binding DataContext.IsAdvancedAIEnabled, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BoolToInvertedVisibilityConverter}}" />
</StackPanel>
</StackPanel>
</Viewbox>
Grid.RowSpan="1"
Grid.ColumnSpan="4"
Height="2"
Margin="12,0,12,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Fill="{StaticResource IntelligentUnderlineGradient}"
RadiusX="1"
RadiusY="1"
Visibility="Collapsed" />
<Grid Grid.Row="1" Width="{StaticResource ModelSelectorButtonWidth}">
<ProgressRing
Width="20"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsActive="{Binding DataContext.IsBusy, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
IsIndeterminate="{Binding DataContext.HasIndeterminateTransformProgress, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Maximum="100"
Minimum="0"
Visibility="{Binding DataContext.IsBusy, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}}"
Value="{Binding DataContext.TransformProgress, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</Grid>
<ScrollViewer
x:Name="ContentElement"
Grid.Row="1"
@@ -279,12 +251,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AIGlyph" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AIGlyphImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0.4" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundDisabled}}" />
</ObjectAnimationUsingKeyFrames>
@@ -314,7 +280,10 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundFocused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusHighlighter" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
</ObjectAnimationUsingKeyFrames>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<LinearGradientBrush MappingMode="Absolute" StartPoint="0,0" EndPoint="0,2">
@@ -331,7 +300,7 @@
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderThickness">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderThemeThicknessFocused}" />
</ObjectAnimationUsingKeyFrames>
</ObjectAnimationUsingKeyFrames>-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundFocused}" />
</ObjectAnimationUsingKeyFrames>
@@ -364,6 +333,8 @@
FalseValue="Visible"
TrueValue="Collapsed" />
<converters:CountToVisibilityConverter x:Key="CountToVisibilityConverter" />
<converters:CountToInvertedVisibilityConverter x:Key="CountToInvertedVisibilityConverter" />
<converters:ServiceTypeToIconConverter x:Key="ServiceTypeToIconConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="PromptBoxGrid" Loaded="Grid_Loaded">
@@ -374,18 +345,19 @@
<local:AnimatedContentControl
x:Name="Loader"
MinHeight="48"
CornerRadius="8">
CornerRadius="16">
<Grid>
<TextBox
x:Name="InputTxtBox"
HorizontalAlignment="Stretch"
x:FieldModifier="public"
CornerRadius="16"
DataContext="{x:Bind ViewModel}"
IsEnabled="{x:Bind ViewModel.ClipboardHasData, Mode=OneWay}"
KeyDown="InputTxtBox_KeyDown"
PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}"
Style="{StaticResource CustomTextBoxStyle}"
TabIndex="0"
TabIndex="1"
Text="{x:Bind ViewModel.Query, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="InputTxtBoxTooltip" />
@@ -545,6 +517,136 @@
</Flyout>
</FlyoutBase.AttachedFlyout>
</TextBox>
<DropDownButton
x:Name="AIProviderButton"
x:Uid="AIProviderButton"
MinWidth="{StaticResource ModelSelectorButtonWidth}"
Margin="1,1,0,2"
Padding="0,0,4,0"
VerticalAlignment="Stretch"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="0,0,1,0"
CornerRadius="16,0,0,16"
Style="{StaticResource SubtleDropDownButtonStyle}"
TabIndex="0"
Visibility="{x:Bind ViewModel.IsBusy, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}">
<ToolTipService.ToolTip>
<TextBlock Text="{x:Bind ViewModel.ActiveAIProviderTooltip, Mode=OneWay}" TextWrapping="WrapWholeWords" />
</ToolTipService.ToolTip>
<DropDownButton.Content>
<Image
x:Name="AIProviderIcon"
Width="16"
Height="16"
AutomationProperties.AccessibilityView="Raw"
Source="{x:Bind ViewModel.ActiveAIProvider?.ServiceType, Mode=OneWay, Converter={StaticResource ServiceTypeToIconConverter}}" />
</DropDownButton.Content>
<DropDownButton.Flyout>
<Flyout Placement="Bottom" ShouldConstrainToRootBounds="False">
<Grid
Width="386"
Margin="-4"
RowSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
x:Uid="AIProvidersFlyoutHeader"
Grid.Row="0"
Style="{StaticResource BodyStrongTextBlockStyle}" />
<ListView
x:Name="AIProviderListView"
Grid.Row="1"
MaxHeight="320"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="{StaticResource OverlayCornerRadius}"
ItemsSource="{x:Bind ViewModel.AIProviders, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto"
SelectedItem="{x:Bind ViewModel.ActiveAIProvider, Mode=OneWay}"
SelectionChanged="AIProviderListView_SelectionChanged"
SelectionMode="Single"
Visibility="{x:Bind ViewModel.AIProviders.Count, Mode=OneWay, Converter={StaticResource CountToVisibilityConverter}}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Spacing="2" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate x:DataType="settings:PasteAIProviderDefinition">
<Grid Padding="0,8,0,8" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Width="20"
Height="20"
AutomationProperties.AccessibilityView="Raw"
Source="{x:Bind ServiceType, Mode=OneWay, Converter={StaticResource ServiceTypeToIconConverter}}" />
<StackPanel
Grid.Column="1"
VerticalAlignment="Center"
Spacing="2">
<TextBlock Text="{x:Bind DisplayName, Mode=OneWay}" TextTrimming="CharacterEllipsis" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ServiceType, Mode=OneWay}" />
</StackPanel>
<Border
Grid.Column="2"
Padding="2,0,2,0"
VerticalAlignment="Center"
BorderBrush="{ThemeResource ControlStrokeColorSecondary}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}"
Visibility="{x:Bind IsLocalModel, Mode=OneWay}">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="Local" />
</Border>
<!--<Border
Grid.Column="2"
Padding="2,0,2,0"
VerticalAlignment="Center"
BorderBrush="{ThemeResource TertiaryButtonBorderBrush}"
BorderThickness="1"
CornerRadius="{StaticResource ControlCornerRadius}"
Visibility="{x:Bind EnableAdvanceAI}">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="ADVANCED" />
</Border>-->
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock
x:Uid="AIProvidersEmptyText"
Grid.Row="1"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
TextWrapping="WrapWholeWords"
Visibility="{x:Bind ViewModel.AIProviders.Count, Mode=OneWay, Converter={StaticResource CountToInvertedVisibilityConverter}}" />
<HyperlinkButton
x:Uid="AIProvidersManageButtonContent"
Grid.Row="2"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.OpenSettingsCommand, Mode=OneWay}" />
</Grid>
</Flyout>
</DropDownButton.Flyout>
</DropDownButton>
<Grid
Width="32"
Height="32"
@@ -562,10 +664,9 @@
Command="{x:Bind GenerateCustomAICommand}"
Content="{ui:FontIcon Glyph=&#xE724;,
FontSize=16}"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
IsEnabled="{x:Bind ViewModel.IsCustomAIAvailable, Mode=OneWay}"
Style="{StaticResource SubtleButtonStyle}"
TabIndex="1"
TabIndex="2"
Visibility="{x:Bind ViewModel.Query.Length, Mode=OneWay, Converter={StaticResource CountToVisibilityConverter}}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="SendBtnToolTip" TextWrapping="WrapWholeWords" />
@@ -640,8 +741,8 @@
x:Name="LoadingText"
x:Uid="LoadingText"
Grid.Row="1"
FontWeight="SemiBold"
Foreground="{ThemeResource AccentGradientBrush}"
Margin="4,4,0,0"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Visibility="Collapsed">
<animations:Implicit.ShowAnimations>

View File

@@ -10,9 +10,11 @@ using AdvancedPaste.Models;
using AdvancedPaste.ViewModels;
using CommunityToolkit.Mvvm.Input;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
namespace AdvancedPaste.Controls
{
@@ -44,6 +46,18 @@ namespace AdvancedPaste.Controls
set => SetValue(FooterProperty, value);
}
public static readonly DependencyProperty ModelSelectorProperty = DependencyProperty.Register(
nameof(ModelSelector),
typeof(object),
typeof(PromptBox),
new PropertyMetadata(defaultValue: null));
public object ModelSelector
{
get => GetValue(ModelSelectorProperty);
set => SetValue(ModelSelectorProperty, value);
}
public PromptBox()
{
InitializeComponent();
@@ -111,5 +125,19 @@ namespace AdvancedPaste.Controls
{
Loader.IsLoading = loading;
}
private async void AIProviderListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (AIProviderListView.SelectedItem is PasteAIProviderDefinition provider)
{
if (ViewModel.SetActiveProviderCommand.CanExecute(provider))
{
await ViewModel.SetActiveProviderCommand.ExecuteAsync(provider);
}
var flyout = FlyoutBase.GetAttachedFlyout(AIProviderButton);
flyout?.Hide();
}
}
}
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace AdvancedPaste.Converters;
public sealed partial class CountToInvertedVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
bool hasItems = ((value is int intValue) && intValue > 0) || (value is IEnumerable enumerable && enumerable.GetEnumerator().MoveNext());
return targetType == typeof(Visibility)
? (hasItems ? Visibility.Collapsed : Visibility.Visible)
: !hasItems;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
=> throw new NotSupportedException();
}

View File

@@ -0,0 +1,103 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.UI.Xaml.Data;
using Microsoft.Windows.ApplicationModel.Resources;
namespace AdvancedPaste.Converters
{
public sealed partial class DateTimeToFriendlyStringConverter : IValueConverter
{
private static readonly ResourceLoader _resources = new ResourceLoader();
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is not DateTimeOffset dto)
{
return string.Empty;
}
// Use local times to calculate relative values and formatting
var now = DateTimeOffset.Now;
var localValue = dto.ToLocalTime();
var culture = !string.IsNullOrEmpty(language)
? new CultureInfo(language)
: CultureInfo.CurrentCulture;
var delta = now - localValue;
// Future dates: fall back to date/time formatting
if (delta < TimeSpan.Zero)
{
return FormatDateAndTime(localValue, culture);
}
// < 1 minute
if (delta.TotalSeconds < 60)
{
return _resources.GetString("Relative_JustNow"); // "Just now"
}
// < 60 minutes
if (delta.TotalMinutes < 60)
{
var mins = (int)Math.Round(delta.TotalMinutes);
if (mins <= 1)
{
return _resources.GetString("Relative_MinuteAgo"); // "1 minute ago"
}
var fmt = _resources.GetString("Relative_MinutesAgo_Format"); // "{0} minutes ago"
return string.Format(culture, fmt, mins);
}
// Same calendar day → "Today, {time}"
var today = now.Date;
if (localValue.Date == today)
{
var time = localValue.ToString("t", culture); // localized short time
var fmt = _resources.GetString("Relative_Today_TimeFormat"); // "Today, {0}"
return string.Format(culture, fmt, time);
}
// Yesterday → "Yesterday, {time}"
if (localValue.Date == today.AddDays(-1))
{
var time = localValue.ToString("t", culture);
var fmt = _resources.GetString("Relative_Yesterday_TimeFormat"); // "Yesterday, {0}"
return string.Format(culture, fmt, time);
}
// Within last 7 days → "{Weekday}, {time}"
if (delta.TotalDays < 7)
{
var weekday = localValue.ToString("dddd", culture); // localized weekday
var time = localValue.ToString("t", culture);
var fmt = _resources.GetString("Relative_Weekday_TimeFormat"); // "{0}, {1}"
return string.Format(culture, fmt, weekday, time);
}
// Older → localized date + time
return FormatDateAndTime(localValue, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
=> throw new NotSupportedException();
private static string FormatDateAndTime(DateTimeOffset localValue, CultureInfo culture)
{
// Use localized short date + short time
var date = localValue.ToString("d", culture);
var time = localValue.ToString("t", culture);
var fmt = _resources.GetString("Relative_Date_TimeFormat"); // "{0}, {1}"
return string.Format(culture, fmt, date, time);
}
}
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media.Imaging;
namespace AdvancedPaste.Converters;
public sealed partial class ServiceTypeToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
string iconPath = value switch
{
string service when !string.IsNullOrWhiteSpace(service) => AIServiceTypeRegistry.GetIconPath(service),
AIServiceType serviceType => AIServiceTypeRegistry.GetIconPath(serviceType),
_ => null,
};
if (string.IsNullOrEmpty(iconPath))
{
iconPath = AIServiceTypeRegistry.GetIconPath(AIServiceType.Unknown);
}
try
{
return new SvgImageSource(new Uri(iconPath));
}
catch (Exception ex)
{
Logger.LogDebug("Failed to create SvgImageSource for AI service icon", ex.Message);
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
=> throw new NotSupportedException();
}

View File

@@ -7,9 +7,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:AdvancedPaste.Pages"
xmlns:winuiex="using:WinUIEx"
Width="420"
Width="486"
Height="188"
MinWidth="420"
MinWidth="486"
MinHeight="188"
Closed="WindowEx_Closed"
IsAlwaysOnTop="True"

View File

@@ -35,7 +35,7 @@
AutomationProperties.HelpText="{x:Bind Name, Mode=OneWay}"
AutomationProperties.Name="{x:Bind AccessibleName, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26" />
<ColumnDefinition Width="48" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
@@ -52,6 +52,7 @@
Grid.Column="1"
VerticalAlignment="Center"
x:Phase="1"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Name, Mode=OneWay}" />
<TextBlock
Grid.Column="2"
@@ -74,7 +75,7 @@
AutomationProperties.AccessibilityView="Raw"
Opacity="0.5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26" />
<ColumnDefinition Width="48" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
@@ -87,6 +88,7 @@
Grid.Column="1"
VerticalAlignment="Center"
x:Phase="1"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Name, Mode=OneWay}" />
</Grid>
</DataTemplate>
@@ -142,69 +144,158 @@
</Page.KeyboardAccelerators>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid
Margin="8,0,8,16"
Padding="4"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="20"
Visibility="{x:Bind ViewModel.ClipboardHasData, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<controls:ClipboardHistoryItemPreviewControl Height="48" ClipboardItem="{x:Bind ViewModel.CurrentClipboardItem, Mode=OneWay}" />
<Button
x:Uid="ClipboardHistoryButton"
Grid.Column="1"
Margin="0,0,4,0"
VerticalAlignment="Center"
IsEnabled="{x:Bind ViewModel.ClipboardHistoryEnabled, Mode=TwoWay}"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="ClipboardHistoryButtonToolTip" />
</ToolTipService.ToolTip>
<FontIcon
Margin="0,0,0,0"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontSize="16"
Glyph="&#xE81C;" />
<Button.Flyout>
<Flyout
FlyoutPresenterStyle="{StaticResource PaddingLessFlyoutPresenterStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False">
<ItemsView
Width="320"
Margin="8,8,8,0"
IsItemInvokedEnabled="True"
ItemInvoked="ClipboardHistory_ItemInvoked"
ItemsSource="{x:Bind clipboardHistory, Mode=OneWay}"
SelectionMode="None">
<ItemsView.Layout>
<StackLayout Orientation="Vertical" Spacing="8" />
</ItemsView.Layout>
<ItemsView.Transitions />
<ItemsView.ItemTemplate>
<DataTemplate x:DataType="local:ClipboardItem">
<ItemContainer
AutomationProperties.Name="{x:Bind Description, Mode=OneWay}"
CornerRadius="16"
ToolTipService.ToolTip="{x:Bind Content}">
<Grid
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
ColumnSpacing="8"
CornerRadius="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MaxWidth="240" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<controls:ClipboardHistoryItemPreviewControl
Height="64"
x:Phase="0"
ClipboardItem="{x:Bind}" />
<Button
x:Name="ClipboardHistoryItemMoreOptionsButton"
x:Uid="ClipboardHistoryItemMoreOptionsButton"
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource SubtleButtonStyle}">
<Button.Content>
<FontIcon FontSize="16" Glyph="&#xe712;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem
x:Uid="ClipboardHistoryItemDeleteButton"
Click="ClipboardHistoryItemDeleteButton_Click"
CommandParameter="{x:Bind (local:ClipboardItem)}"
Icon="Delete" />
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</ItemContainer>
</DataTemplate>
</ItemsView.ItemTemplate>
</ItemsView>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
<controls:PromptBox
x:Name="CustomFormatTextBox"
x:Uid="CustomFormatTextBox"
Margin="8,4,8,0"
Grid.Row="1"
Margin="20,0,20,0"
x:FieldModifier="public"
IsEnabled="{x:Bind ViewModel.IsCustomAIServiceEnabled, Mode=OneWay}"
TabIndex="0">
<controls:PromptBox.Footer>
<StackPanel Orientation="Horizontal">
<TextBlock
x:Uid="AIMistakeNote"
Margin="0,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}">
<Run x:Uid="AIMistakeNote" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
<TextBlock
Margin="4,0,2,0"
HorizontalAlignment="Left"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<Button
Padding="0"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}">
<Hyperlink
x:Name="TermsHyperlink"
NavigateUri="https://openai.com/policies/terms-of-use"
TabIndex="3">
<Run x:Uid="TermsLink" />
</Hyperlink>
<ToolTipService.ToolTip>
<TextBlock Text="https://openai.com/policies/terms-of-use" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock
Margin="0,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
ToolTipService.ToolTip="">
<Run x:Uid="AIFooterSeparator" Foreground="{ThemeResource TextFillColorSecondaryBrush}">|</Run>
</TextBlock>
<TextBlock
Margin="0,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}">
<Hyperlink NavigateUri="https://openai.com/policies/privacy-policy" TabIndex="3">
<Run x:Uid="PrivacyLink" />
</Hyperlink>
<ToolTipService.ToolTip>
<TextBlock Text="https://openai.com/policies/privacy-policy" />
</ToolTipService.ToolTip>
</TextBlock>
Style="{StaticResource SubtleButtonStyle}">
<FontIcon FontSize="12" Glyph="&#xE946;" />
<Button.Flyout>
<Flyout>
<StackPanel Spacing="8">
<TextBlock TextWrapping="Wrap">
<Run x:Uid="AIMistakeNote" /><LineBreak /><Run
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="You are using a custom endpoint. Verify all answers." />
</TextBlock>
<StackPanel Orientation="Horizontal" Spacing="8">
<HyperlinkButton
x:Name="TermsHyperlink"
x:Uid="TermsLink"
Padding="0"
FontSize="12"
NavigateUri="https://openai.com/policies/terms-of-use" />
<HyperlinkButton
x:Name="PrivacyHyperLink"
x:Uid="PrivacyLink"
Padding="0"
FontSize="12"
NavigateUri="https://openai.com/policies/privacy-policy" />
</StackPanel>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</controls:PromptBox.Footer>
</controls:PromptBox>
<Grid
Grid.Row="2"
Background="{ThemeResource LayerOnAcrylicFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="0,1,0,0"
RowSpacing="4">
<Grid Grid.Row="2" RowSpacing="4">
<Grid.RowDefinitions>
<RowDefinition Height="{x:Bind ViewModel.StandardPasteFormats.Count, Mode=OneWay, Converter={StaticResource standardPasteFormatsToHeightConverter}}" />
<RowDefinition Height="Auto" />
@@ -225,7 +316,6 @@
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="None"
TabIndex="1" />
<Rectangle
Grid.Row="1"
Height="1"
@@ -246,117 +336,6 @@
ScrollViewer.VerticalScrollMode="Auto"
SelectionMode="None"
TabIndex="2" />
<Rectangle
Grid.Row="3"
Height="1"
HorizontalAlignment="Stretch"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
<Button
Grid.Row="4"
Height="32"
Margin="4,0,4,4"
Padding="{StaticResource ButtonPadding}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
AutomationProperties.LabeledBy="{x:Bind ClipboardHistoryButton}"
IsEnabled="{x:Bind ViewModel.ClipboardHistoryEnabled, Mode=TwoWay}"
Style="{StaticResource SubtleButtonStyle}">
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon
Margin="0,0,0,0"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontSize="16"
Glyph="&#xE81C;" />
<TextBlock
x:Name="ClipboardHistoryButton"
x:Uid="ClipboardHistoryButton"
Grid.Column="1"
VerticalAlignment="Center" />
<FontIcon
Grid.Column="2"
AutomationProperties.AccessibilityView="Raw"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE974;" />
</Grid>
<Button.Flyout>
<Flyout
FlyoutPresenterStyle="{StaticResource PaddingLessFlyoutPresenterStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False">
<ListView
Width="320"
IsItemClickEnabled="True"
ItemClick="ClipboardHistory_ItemClick"
ItemsSource="{x:Bind clipboardHistory, Mode=OneWay}"
SelectionMode="None">
<ListView.Transitions />
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ClipboardItem">
<Grid
Height="40"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Description, Mode=OneWay}"
ColumnSpacing="8"
ToolTipService.ToolTip="{x:Bind Content}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MaxWidth="240" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
HorizontalAlignment="Left"
x:Phase="2"
Source="{x:Bind Image}"
Visibility="Visible" />
<TextBlock
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
x:Phase="1"
Text="{x:Bind Content}"
TextTrimming="CharacterEllipsis"
Visibility="Visible" />
<Button
x:Name="ClipboardHistoryItemMoreOptionsButton"
x:Uid="ClipboardHistoryItemMoreOptionsButton"
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource SubtleButtonStyle}">
<Button.Content>
<FontIcon FontSize="16" Glyph="&#xe712;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem
x:Uid="ClipboardHistoryItemDeleteButton"
Click="ClipboardHistoryItemDeleteButton_Click"
CommandParameter="{x:Bind (local:ClipboardItem)}"
Icon="Delete" />
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</Grid>
</Page>

View File

@@ -68,11 +68,22 @@ namespace AdvancedPaste.Pages
if (item.Content.Contains(StandardDataFormats.Text))
{
string text = await item.Content.GetTextAsync();
items.Add(new ClipboardItem { Content = text, Item = item });
items.Add(new ClipboardItem
{
Content = text,
Format = ClipboardFormat.Text,
Timestamp = item.Timestamp,
Item = item,
});
}
else if (item.Content.Contains(StandardDataFormats.Bitmap))
{
items.Add(new ClipboardItem { Item = item });
items.Add(new ClipboardItem
{
Format = ClipboardFormat.Image,
Timestamp = item.Timestamp,
Item = item,
});
}
}
}
@@ -187,10 +198,9 @@ namespace AdvancedPaste.Pages
}
}
private async void ClipboardHistory_ItemClick(object sender, ItemClickEventArgs e)
private async void ClipboardHistory_ItemInvoked(ItemsView sender, ItemsViewItemInvokedEventArgs args)
{
var item = e.ClickedItem as ClipboardItem;
if (item is not null)
if (args.InvokedItem is ClipboardItem item)
{
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteClipboardItemClicked());
if (!string.IsNullOrEmpty(item.Content))

View File

@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals">
<Style x:Key="SubtleDropDownButtonStyle" TargetType="DropDownButton">
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid
x:Name="RootGrid"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid.BackgroundTransition>
<BrushTransition Duration="0:0:0.083" />
</Grid.BackgroundTransition>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter
x:Name="ContentPresenter"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}" />
<AnimatedIcon
xmlns:local="using:Microsoft.UI.Xaml.Controls"
x:Name="ChevronIcon"
Grid.Column="1"
Width="12"
Height="12"
Margin="-4,0,0,0"
local:AnimatedIcon.State="Normal"
AutomationProperties.AccessibilityView="Raw"
Foreground="{ThemeResource DropDownButtonForegroundSecondary}">
<animatedvisuals:AnimatedChevronDownSmallVisualSource />
<AnimatedIcon.FallbackIconSource>
<FontIconSource
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="8"
Glyph="&#xE96E;"
IsTextScaleFactorEnabled="False" />
</AnimatedIcon.FallbackIconSource>
</AnimatedIcon>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChevronIcon" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource DropDownButtonForegroundSecondaryPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ChevronIcon.(AnimatedIcon.State)" Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChevronIcon" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource DropDownButtonForegroundSecondaryPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ChevronIcon.(AnimatedIcon.State)" Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChevronIcon" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
<Setter Target="ChevronIcon.(AnimatedIcon.State)" Value="Normal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using AdvancedPaste.Models;
using Microsoft.SemanticKernel;
namespace AdvancedPaste.Helpers;
/// <summary>
/// Helper class for extracting AI service usage information from chat messages.
/// </summary>
public static class AIServiceUsageHelper
{
/// <summary>
/// Extracts AI service usage information from OpenAI chat message metadata.
/// </summary>
/// <param name="chatMessage">The chat message containing usage metadata.</param>
/// <returns>AI service usage information or AIServiceUsage.None if extraction fails.</returns>
public static AIServiceUsage GetOpenAIServiceUsage(ChatMessageContent chatMessage)
{
// Try to get usage information from metadata
if (chatMessage.Metadata?.TryGetValue("Usage", out var usageObj) == true)
{
// Handle different possible usage types through reflection to be version-agnostic
var usageType = usageObj.GetType();
try
{
// Try common property names for prompt tokens
var promptTokensProp = usageType.GetProperty("PromptTokens") ??
usageType.GetProperty("InputTokens") ??
usageType.GetProperty("InputTokenCount");
var completionTokensProp = usageType.GetProperty("CompletionTokens") ??
usageType.GetProperty("OutputTokens") ??
usageType.GetProperty("OutputTokenCount");
if (promptTokensProp != null && completionTokensProp != null)
{
var promptTokens = (int)(promptTokensProp.GetValue(usageObj) ?? 0);
var completionTokens = (int)(completionTokensProp.GetValue(usageObj) ?? 0);
return new AIServiceUsage(promptTokens, completionTokens);
}
}
catch
{
// If reflection fails, fall back to no usage
}
}
return AIServiceUsage.None;
}
}

View File

@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.Helpers
{
internal static class ClipboardItemHelper
{
/// <summary>
/// Creates a ClipboardItem from current clipboard data.
/// </summary>
public static async Task<ClipboardItem> CreateFromCurrentClipboardAsync(
DataPackageView clipboardData,
ClipboardFormat availableFormats,
DateTimeOffset? timestamp = null,
BitmapImage existingImage = null)
{
if (clipboardData == null || availableFormats == ClipboardFormat.None)
{
return null;
}
var clipboardItem = new ClipboardItem
{
Format = availableFormats,
Timestamp = timestamp,
};
// Text or HTML content
if (availableFormats.HasFlag(ClipboardFormat.Text) || availableFormats.HasFlag(ClipboardFormat.Html))
{
clipboardItem.Content = await clipboardData.GetTextOrEmptyAsync();
}
// Image content
else if (availableFormats.HasFlag(ClipboardFormat.Image))
{
// Reuse existing image if provided
if (existingImage != null)
{
clipboardItem.Image = existingImage;
}
else
{
clipboardItem.Image = await TryCreateBitmapImageAsync(clipboardData);
}
}
return clipboardItem;
}
/// <summary>
/// Creates a BitmapImage from clipboard data.
/// </summary>
private static async Task<BitmapImage> TryCreateBitmapImageAsync(DataPackageView clipboardData)
{
try
{
var imageReference = await clipboardData.GetBitmapAsync();
if (imageReference != null)
{
using (var imageStream = await imageReference.OpenReadAsync())
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(imageStream);
return bitmapImage;
}
}
}
catch
{
// Silently fail - caller can check for null
}
return null;
}
}
}

View File

@@ -6,11 +6,13 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using ManagedCommon;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.Win32;
using Windows.ApplicationModel.DataTransfer;
using Windows.Data.Html;
@@ -180,6 +182,46 @@ internal static class DataPackageHelpers
}
}
internal static async Task<string> GetClipboardTextOrThrowAsync(this DataPackageView dataPackageView, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(dataPackageView);
try
{
if (dataPackageView.Contains(StandardDataFormats.Text))
{
return await dataPackageView.GetTextAsync();
}
if (dataPackageView.Contains(StandardDataFormats.Html))
{
var html = await dataPackageView.GetHtmlFormatAsync();
return HtmlUtilities.ConvertToText(html);
}
if (dataPackageView.Contains(StandardDataFormats.Bitmap))
{
var bitmap = await dataPackageView.GetImageContentAsync();
if (bitmap != null)
{
return await OcrHelpers.ExtractTextAsync(bitmap, cancellationToken);
}
}
}
catch (Exception ex) when (ex is COMException or InvalidOperationException)
{
throw CreateClipboardTextMissingException(ex);
}
throw CreateClipboardTextMissingException();
}
private static PasteActionException CreateClipboardTextMissingException(Exception innerException = null)
{
var message = ResourceLoaderInstance.ResourceLoader.GetString("ClipboardEmptyWarning");
return new PasteActionException(message, innerException ?? new InvalidOperationException("Clipboard does not contain text content."));
}
internal static async Task<string> GetHtmlContentAsync(this DataPackageView dataPackageView) =>
dataPackageView.Contains(StandardDataFormats.Html) ? await dataPackageView.GetHtmlFormatAsync() : string.Empty;
@@ -195,6 +237,22 @@ internal static class DataPackageHelpers
return null;
}
internal static async Task<BitmapImage> GetPreviewBitmapAsync(this DataPackageView dataPackageView)
{
var stream = await dataPackageView.GetImageStreamAsync();
if (stream == null)
{
return null;
}
using (stream)
{
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
return bitmapImage;
}
}
private static async Task<IRandomAccessStream> GetImageStreamAsync(this DataPackageView dataPackageView)
{
if (dataPackageView.Contains(StandardDataFormats.StorageItems))

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
@@ -12,7 +13,7 @@ namespace AdvancedPaste.Settings
{
public interface IUserSettings
{
public bool IsAdvancedAIEnabled { get; }
public bool IsAIEnabled { get; }
public bool ShowCustomPreview { get; }
@@ -22,6 +23,10 @@ namespace AdvancedPaste.Settings
public IReadOnlyList<PasteFormats> AdditionalActions { get; }
public PasteAIConfiguration PasteAIConfiguration { get; }
public event EventHandler Changed;
Task SetActiveAIProviderAsync(string providerId);
}
}

View File

@@ -13,6 +13,7 @@ using AdvancedPaste.Models;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Windows.Security.Credentials;
namespace AdvancedPaste.Settings
{
@@ -33,7 +34,7 @@ namespace AdvancedPaste.Settings
public event EventHandler Changed;
public bool IsAdvancedAIEnabled { get; private set; }
public bool IsAIEnabled { get; private set; }
public bool ShowCustomPreview { get; private set; }
@@ -43,13 +44,16 @@ namespace AdvancedPaste.Settings
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions => _customActions;
public PasteAIConfiguration PasteAIConfiguration { get; private set; }
public UserSettings(IFileSystem fileSystem)
{
_settingsUtils = new SettingsUtils(fileSystem);
IsAdvancedAIEnabled = false;
IsAIEnabled = false;
ShowCustomPreview = true;
CloseAfterLosingFocus = false;
PasteAIConfiguration = new PasteAIConfiguration();
_additionalActions = [];
_customActions = [];
_taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
@@ -94,13 +98,16 @@ namespace AdvancedPaste.Settings
var settings = _settingsUtils.GetSettingsOrDefault<AdvancedPasteSettings>(AdvancedPasteModuleName);
if (settings != null)
{
bool migratedLegacyEnablement = TryMigrateLegacyAIEnablement(settings);
void UpdateSettings()
{
var properties = settings.Properties;
IsAdvancedAIEnabled = properties.IsAdvancedAIEnabled;
IsAIEnabled = properties.IsAIEnabled;
ShowCustomPreview = properties.ShowCustomPreview;
CloseAfterLosingFocus = properties.CloseAfterLosingFocus;
PasteAIConfiguration = properties.PasteAIConfiguration ?? new PasteAIConfiguration();
var sourceAdditionalActions = properties.AdditionalActions;
(PasteFormats Format, IAdvancedPasteAction[] Actions)[] additionalActionFormats =
@@ -126,6 +133,11 @@ namespace AdvancedPaste.Settings
Task.Factory
.StartNew(UpdateSettings, CancellationToken.None, TaskCreationOptions.None, _taskScheduler)
.Wait();
if (migratedLegacyEnablement)
{
settings.Save(_settingsUtils);
}
}
retry = false;
@@ -144,6 +156,114 @@ namespace AdvancedPaste.Settings
}
}
private static bool TryMigrateLegacyAIEnablement(AdvancedPasteSettings settings)
{
if (settings?.Properties is null)
{
return false;
}
if (settings.Properties.IsAIEnabled || !LegacyOpenAIKeyExists())
{
return false;
}
settings.Properties.IsAIEnabled = true;
return true;
}
private static bool LegacyOpenAIKeyExists()
{
try
{
PasswordVault vault = new();
return vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey") is not null;
}
catch (Exception)
{
return false;
}
}
public async Task SetActiveAIProviderAsync(string providerId)
{
if (string.IsNullOrWhiteSpace(providerId))
{
return;
}
await Task.Run(() =>
{
lock (_loadingSettingsLock)
{
var settings = _settingsUtils.GetSettingsOrDefault<AdvancedPasteSettings>(AdvancedPasteModuleName);
var configuration = settings?.Properties?.PasteAIConfiguration;
var providers = configuration?.Providers;
if (configuration == null || providers == null || providers.Count == 0)
{
return;
}
var target = providers.FirstOrDefault(provider => string.Equals(provider.Id, providerId, StringComparison.OrdinalIgnoreCase));
if (target == null)
{
return;
}
if (string.Equals(configuration.ActiveProvider?.Id, providerId, StringComparison.OrdinalIgnoreCase))
{
return;
}
configuration.ActiveProviderId = providerId;
foreach (var provider in providers)
{
provider.IsActive = string.Equals(provider.Id, providerId, StringComparison.OrdinalIgnoreCase);
}
try
{
settings.Save(_settingsUtils);
}
catch (Exception ex)
{
Logger.LogError("Failed to set active AI provider", ex);
return;
}
try
{
Task.Factory
.StartNew(
() =>
{
PasteAIConfiguration.ActiveProviderId = providerId;
if (PasteAIConfiguration.Providers is not null)
{
foreach (var provider in PasteAIConfiguration.Providers)
{
provider.IsActive = string.Equals(provider.Id, providerId, StringComparison.OrdinalIgnoreCase);
}
}
Changed?.Invoke(this, EventArgs.Empty);
},
CancellationToken.None,
TaskCreationOptions.None,
_taskScheduler)
.Wait();
}
catch (Exception ex)
{
Logger.LogError("Failed to dispatch active AI provider change", ex);
}
}
});
}
public void Dispose()
{
Dispose(true);

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using AdvancedPaste.Helpers;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel.DataTransfer;
@@ -12,10 +13,15 @@ public class ClipboardItem
{
public string Content { get; set; }
public ClipboardHistoryItem Item { get; set; }
public BitmapImage Image { get; set; }
public ClipboardFormat Format { get; set; }
public DateTimeOffset? Timestamp { get; set; }
// Only used for clipboard history items that have a ClipboardHistoryItem
public ClipboardHistoryItem Item { get; set; }
public string Description => !string.IsNullOrEmpty(Content) ? Content :
Image is not null ? ResourceLoaderInstance.ResourceLoader.GetString("ClipboardHistoryImage") :
string.Empty;

View File

@@ -0,0 +1,227 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services.CustomActions;
using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.Amazon;
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
using Microsoft.SemanticKernel.Connectors.MistralAI;
using Microsoft.SemanticKernel.Connectors.Ollama;
using Microsoft.SemanticKernel.Connectors.OpenAI;
namespace AdvancedPaste.Services;
public sealed class AdvancedAIKernelService : KernelServiceBase
{
private sealed record RuntimeConfiguration(
AIServiceType ServiceType,
string ModelName,
string Endpoint,
string DeploymentName,
string ModelPath,
string SystemPrompt,
bool ModerationEnabled) : IKernelRuntimeConfiguration;
private readonly IAICredentialsProvider credentialsProvider;
public AdvancedAIKernelService(
IAICredentialsProvider credentialsProvider,
IKernelQueryCacheService queryCacheService,
IPromptModerationService promptModerationService,
IUserSettings userSettings,
ICustomActionTransformService customActionTransformService)
: base(queryCacheService, promptModerationService, userSettings, customActionTransformService)
{
ArgumentNullException.ThrowIfNull(credentialsProvider);
this.credentialsProvider = credentialsProvider;
}
protected override string AdvancedAIModelName => GetRuntimeConfiguration().ModelName;
protected override PromptExecutionSettings PromptExecutionSettings => CreatePromptExecutionSettings();
protected override void AddChatCompletionService(IKernelBuilder kernelBuilder)
{
ArgumentNullException.ThrowIfNull(kernelBuilder);
var runtimeConfig = GetRuntimeConfiguration();
var serviceType = runtimeConfig.ServiceType;
var modelName = runtimeConfig.ModelName;
var requiresApiKey = RequiresApiKey(serviceType);
var apiKey = string.Empty;
if (requiresApiKey)
{
this.credentialsProvider.Refresh();
apiKey = (this.credentialsProvider.GetKey() ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(apiKey))
{
throw new InvalidOperationException($"An API key is required for {serviceType} but none was found in the credential vault.");
}
}
var endpoint = string.IsNullOrWhiteSpace(runtimeConfig.Endpoint) ? null : runtimeConfig.Endpoint.Trim();
var deployment = string.IsNullOrWhiteSpace(runtimeConfig.DeploymentName) ? modelName : runtimeConfig.DeploymentName;
switch (serviceType)
{
case AIServiceType.OpenAI:
kernelBuilder.AddOpenAIChatCompletion(modelName, apiKey, serviceId: modelName);
break;
case AIServiceType.AzureOpenAI:
kernelBuilder.AddAzureOpenAIChatCompletion(deployment, RequireEndpoint(endpoint, serviceType), apiKey, serviceId: modelName);
break;
default:
throw new NotSupportedException($"Service type '{runtimeConfig.ServiceType}' is not supported");
}
}
protected override AIServiceUsage GetAIServiceUsage(ChatMessageContent chatMessage)
{
return AIServiceUsageHelper.GetOpenAIServiceUsage(chatMessage);
}
protected override bool ShouldModerateAdvancedAI()
{
if (!TryGetRuntimeConfiguration(out var runtimeConfig))
{
return false;
}
return runtimeConfig.ModerationEnabled && (runtimeConfig.ServiceType == AIServiceType.OpenAI || runtimeConfig.ServiceType == AIServiceType.AzureOpenAI);
}
private static string GetModelName(PasteAIProviderDefinition config)
{
if (!string.IsNullOrWhiteSpace(config?.ModelName))
{
return config.ModelName;
}
return "gpt-4o";
}
protected override IKernelRuntimeConfiguration GetRuntimeConfiguration()
{
if (TryGetRuntimeConfiguration(out var runtimeConfig))
{
return runtimeConfig;
}
throw new InvalidOperationException("No Advanced AI provider is configured.");
}
private bool TryGetRuntimeConfiguration(out IKernelRuntimeConfiguration runtimeConfig)
{
runtimeConfig = null;
if (!TryResolveAdvancedProvider(out var provider))
{
return false;
}
var serviceType = NormalizeServiceType(provider.ServiceTypeKind);
if (!IsServiceTypeSupported(serviceType))
{
return false;
}
runtimeConfig = new RuntimeConfiguration(
serviceType,
GetModelName(provider),
provider.EndpointUrl,
provider.DeploymentName,
provider.ModelPath,
provider.SystemPrompt,
provider.ModerationEnabled);
return true;
}
private bool TryResolveAdvancedProvider(out PasteAIProviderDefinition provider)
{
provider = null;
var configuration = this.UserSettings?.PasteAIConfiguration;
if (configuration is null)
{
return false;
}
var activeProvider = configuration.ActiveProvider;
if (IsAdvancedProvider(activeProvider))
{
provider = activeProvider;
return true;
}
if (activeProvider is not null)
{
return false;
}
var fallback = configuration.Providers?.FirstOrDefault(IsAdvancedProvider);
if (fallback is not null)
{
provider = fallback;
return true;
}
return false;
}
private static bool IsAdvancedProvider(PasteAIProviderDefinition provider)
{
if (provider is null || !provider.EnableAdvancedAI)
{
return false;
}
var serviceType = NormalizeServiceType(provider.ServiceTypeKind);
return IsServiceTypeSupported(serviceType);
}
private static bool IsServiceTypeSupported(AIServiceType serviceType)
{
return serviceType is AIServiceType.OpenAI or AIServiceType.AzureOpenAI;
}
private static AIServiceType NormalizeServiceType(AIServiceType serviceType)
{
return serviceType == AIServiceType.Unknown ? AIServiceType.OpenAI : serviceType;
}
private static bool RequiresApiKey(AIServiceType serviceType)
{
return true;
}
private static string RequireEndpoint(string endpoint, AIServiceType serviceType)
{
if (!string.IsNullOrWhiteSpace(endpoint))
{
return endpoint;
}
throw new InvalidOperationException($"Endpoint is required for {serviceType} configuration but was not provided.");
}
private PromptExecutionSettings CreatePromptExecutionSettings()
{
var serviceType = GetRuntimeConfiguration().ServiceType;
return new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Required(),
Temperature = 0.01,
};
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using AdvancedPaste.Models;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class CustomActionTransformResult
{
public CustomActionTransformResult(string content, AIServiceUsage usage)
{
Content = content;
Usage = usage;
}
public string Content { get; }
public AIServiceUsage Usage { get; }
}
}

View File

@@ -0,0 +1,200 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Settings;
using AdvancedPaste.Telemetry;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class CustomActionTransformService : ICustomActionTransformService
{
private const string DefaultSystemPrompt = """
You are tasked with reformatting user's clipboard data. Use the user's instructions, and the content of their clipboard below to edit their clipboard content as they have requested it.
Do not output anything else besides the reformatted clipboard content.
""";
private readonly IPromptModerationService promptModerationService;
private readonly IPasteAIProviderFactory providerFactory;
private readonly IAICredentialsProvider credentialsProvider;
private readonly IUserSettings userSettings;
public CustomActionTransformService(IPromptModerationService promptModerationService, IPasteAIProviderFactory providerFactory, IAICredentialsProvider credentialsProvider, IUserSettings userSettings)
{
this.promptModerationService = promptModerationService;
this.providerFactory = providerFactory;
this.credentialsProvider = credentialsProvider;
this.userSettings = userSettings;
}
public async Task<CustomActionTransformResult> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress)
{
var pasteConfig = userSettings?.PasteAIConfiguration;
var providerConfig = BuildProviderConfig(pasteConfig);
return await TransformAsync(prompt, inputText, providerConfig, cancellationToken, progress);
}
private async Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, PasteAIConfig providerConfig, CancellationToken cancellationToken, IProgress<double> progress)
{
ArgumentNullException.ThrowIfNull(providerConfig);
if (string.IsNullOrWhiteSpace(prompt))
{
return new CustomActionTransformResult(string.Empty, AIServiceUsage.None);
}
if (string.IsNullOrWhiteSpace(inputText))
{
Logger.LogWarning("Clipboard has no usable text data");
return new CustomActionTransformResult(string.Empty, AIServiceUsage.None);
}
var systemPrompt = providerConfig.SystemPrompt ?? DefaultSystemPrompt;
var fullPrompt = (systemPrompt ?? string.Empty) + "\n\n" + (inputText ?? string.Empty);
if (ShouldModerate(providerConfig))
{
await promptModerationService.ValidateAsync(fullPrompt, cancellationToken);
}
try
{
var provider = providerFactory.CreateProvider(providerConfig);
var request = new PasteAIRequest
{
Prompt = prompt,
InputText = inputText,
SystemPrompt = systemPrompt,
};
var providerContent = await provider.ProcessPasteAsync(
request,
cancellationToken,
progress);
var usage = request.Usage;
var content = providerContent ?? string.Empty;
// Log endpoint usage
var endpointEvent = new AdvancedPasteEndpointUsageEvent(providerConfig.ProviderType);
PowerToysTelemetry.Log.WriteEvent(endpointEvent);
Logger.LogDebug($"{nameof(CustomActionTransformService)}.{nameof(TransformAsync)} complete; ModelName={providerConfig.Model ?? string.Empty}, PromptTokens={usage.PromptTokens}, CompletionTokens={usage.CompletionTokens}");
return new CustomActionTransformResult(content, usage);
}
catch (Exception ex)
{
Logger.LogError($"{nameof(CustomActionTransformService)}.{nameof(TransformAsync)} failed", ex);
if (ex is PasteActionException or OperationCanceledException)
{
throw;
}
var statusCode = ExtractStatusCode(ex);
var failureMessage = providerConfig.ProviderType switch
{
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => ErrorHelpers.TranslateErrorText(statusCode),
_ => ResourceLoaderInstance.ResourceLoader.GetString("PasteError"),
};
throw new PasteActionException(failureMessage, ex);
}
}
private static int ExtractStatusCode(Exception exception)
{
if (exception is HttpOperationException httpOperationException)
{
return (int?)httpOperationException.StatusCode ?? -1;
}
if (exception is HttpRequestException httpRequestException && httpRequestException.StatusCode is HttpStatusCode statusCode)
{
return (int)statusCode;
}
return -1;
}
private static AIServiceType NormalizeServiceType(AIServiceType serviceType)
{
return serviceType == AIServiceType.Unknown ? AIServiceType.OpenAI : serviceType;
}
private PasteAIConfig BuildProviderConfig(PasteAIConfiguration config)
{
config ??= new PasteAIConfiguration();
var provider = config.ActiveProvider ?? config.Providers?.FirstOrDefault() ?? new PasteAIProviderDefinition();
var serviceType = NormalizeServiceType(provider.ServiceTypeKind);
var systemPrompt = string.IsNullOrWhiteSpace(provider.SystemPrompt) ? DefaultSystemPrompt : provider.SystemPrompt;
var apiKey = AcquireApiKey(serviceType);
var modelName = provider.ModelName;
var providerConfig = new PasteAIConfig
{
ProviderType = serviceType,
ApiKey = apiKey,
Model = modelName,
Endpoint = provider.EndpointUrl,
DeploymentName = provider.DeploymentName,
LocalModelPath = provider.ModelPath,
ModelPath = provider.ModelPath,
SystemPrompt = systemPrompt,
ModerationEnabled = provider.ModerationEnabled,
};
return providerConfig;
}
private string AcquireApiKey(AIServiceType serviceType)
{
if (!RequiresApiKey(serviceType))
{
return string.Empty;
}
credentialsProvider.Refresh();
return credentialsProvider.GetKey() ?? string.Empty;
}
private static bool RequiresApiKey(AIServiceType serviceType)
{
return serviceType switch
{
AIServiceType.Onnx => false,
AIServiceType.Ollama => false,
AIServiceType.Anthropic => false,
AIServiceType.AmazonBedrock => false,
_ => true,
};
}
private static bool ShouldModerate(PasteAIConfig providerConfig)
{
if (providerConfig is null || !providerConfig.ModerationEnabled)
{
return false;
}
return providerConfig.ProviderType == AIServiceType.OpenAI || providerConfig.ProviderType == AIServiceType.AzureOpenAI;
}
}
}

View File

@@ -0,0 +1,194 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using LanguageModelProvider;
using Microsoft.Extensions.AI;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Services.CustomActions;
public sealed class FoundryLocalPasteProvider : IPasteAIProvider
{
private static readonly IReadOnlyCollection<AIServiceType> SupportedTypes = new[]
{
AIServiceType.FoundryLocal,
};
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new FoundryLocalPasteProvider(config));
private static readonly LanguageModelService LanguageModels = LanguageModelService.CreateDefault();
private readonly PasteAIConfig _config;
public FoundryLocalPasteProvider(PasteAIConfig config)
{
ArgumentNullException.ThrowIfNull(config);
_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();
return await FoundryLocalModelProvider.Instance.IsAvailable().ConfigureAwait(false);
}
public async Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress)
{
ArgumentNullException.ThrowIfNull(request);
try
{
var systemPrompt = request.SystemPrompt;
if (string.IsNullOrWhiteSpace(systemPrompt))
{
throw new PasteActionException(
"System prompt is required for Foundry Local",
new ArgumentException("System prompt must be provided", nameof(request)));
}
var prompt = request.Prompt;
var inputText = request.InputText;
if (string.IsNullOrWhiteSpace(prompt) || string.IsNullOrWhiteSpace(inputText))
{
throw new PasteActionException(
"Prompt and input text are required",
new ArgumentException("Prompt and input text must be provided", nameof(request)));
}
var modelReference = _config?.Model;
if (string.IsNullOrWhiteSpace(modelReference))
{
throw new PasteActionException(
"No Foundry Local model selected",
new InvalidOperationException("Model identifier is required"),
aiServiceMessage: "Please select a model in the AI provider settings. Model identifier should be in the format 'fl://model-name'.");
}
cancellationToken.ThrowIfCancellationRequested();
var chatClient = LanguageModels.GetClient(modelReference);
if (chatClient is null)
{
throw new PasteActionException(
$"Unable to load Foundry Local model: {modelReference}",
new InvalidOperationException("Chat client resolution failed"),
aiServiceMessage: "The model may not be downloaded or the Foundry Local service may not be running. Please check the model status in settings.");
}
// Extract actual model ID from the URL (format: fl://modelId)
var actualModelId = modelReference.Replace("fl://", string.Empty).Trim('/');
var userMessageContent = $"""
User instructions:
{prompt}
Text:
{inputText}
Output:
""";
var chatMessages = new List<ChatMessage>
{
new(ChatRole.System, systemPrompt),
new(ChatRole.User, userMessageContent),
};
var chatOptions = CreateChatOptions(_config?.SystemPrompt, actualModelId);
progress?.Report(0.1);
var response = await chatClient.GetResponseAsync(chatMessages, chatOptions, cancellationToken).ConfigureAwait(false);
progress?.Report(0.8);
var responseText = GetResponseText(response);
request.Usage = ToUsage(response.Usage);
progress?.Report(1.0);
return responseText ?? string.Empty;
}
catch (OperationCanceledException)
{
// Let cancellation exceptions pass through unchanged
throw;
}
catch (PasteActionException)
{
// Let our custom exceptions pass through unchanged
throw;
}
catch (Exception ex)
{
// Wrap any other exceptions with context
var modelInfo = !string.IsNullOrWhiteSpace(_config?.Model) ? $" (Model: {_config.Model})" : string.Empty;
throw new PasteActionException(
$"Failed to generate response using Foundry Local{modelInfo}",
ex,
aiServiceMessage: $"Error details: {ex.Message}");
}
}
private static ChatOptions CreateChatOptions(string systemPrompt, string modelReference)
{
var options = new ChatOptions
{
ModelId = modelReference,
};
if (!string.IsNullOrWhiteSpace(systemPrompt))
{
options.Instructions = systemPrompt;
}
return options;
}
private static string GetResponseText(ChatResponse response)
{
if (!string.IsNullOrWhiteSpace(response.Text))
{
return response.Text;
}
if (response.Messages is { Count: > 0 })
{
var lastMessage = response.Messages.LastOrDefault(m => !string.IsNullOrWhiteSpace(m.Text));
if (!string.IsNullOrWhiteSpace(lastMessage?.Text))
{
return lastMessage.Text;
}
}
return string.Empty;
}
private static AIServiceUsage ToUsage(UsageDetails usageDetails)
{
if (usageDetails is null)
{
return AIServiceUsage.None;
}
int promptTokens = (int)(usageDetails.InputTokenCount ?? 0);
int completionTokens = (int)(usageDetails.OutputTokenCount ?? 0);
if (promptTokens == 0 && completionTokens == 0)
{
return AIServiceUsage.None;
}
return new AIServiceUsage(promptTokens, completionTokens);
}
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Settings;
namespace AdvancedPaste.Services.CustomActions
{
public interface ICustomActionTransformService
{
Task<CustomActionTransformResult> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress);
}
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Services.CustomActions
{
public interface IPasteAIProvider
{
Task<bool> IsAvailableAsync(CancellationToken cancellationToken);
Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress);
}
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace AdvancedPaste.Services.CustomActions
{
public interface IPasteAIProviderFactory
{
IPasteAIProvider CreateProvider(PasteAIConfig config);
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class LocalModelPasteProvider : IPasteAIProvider
{
private static readonly IReadOnlyCollection<AIServiceType> SupportedTypes = new[]
{
AIServiceType.Onnx,
AIServiceType.ML,
};
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new LocalModelPasteProvider(config));
private readonly PasteAIConfig _config;
public LocalModelPasteProvider(PasteAIConfig config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
}
public Task<bool> IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true);
public Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress)
{
ArgumentNullException.ThrowIfNull(request);
// TODO: Implement local model inference logic using _config.LocalModelPath/_config.ModelPath
var content = request.InputText ?? string.Empty;
request.Usage = AIServiceUsage.None;
return Task.FromResult(content);
}
}
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.SemanticKernel.ChatCompletion;
namespace AdvancedPaste.Services.CustomActions
{
public class PasteAIConfig
{
public AIServiceType ProviderType { get; set; }
public string Model { get; set; }
public string ApiKey { get; set; }
public string Endpoint { get; set; }
public string DeploymentName { get; set; }
public string LocalModelPath { get; set; }
public string ModelPath { get; set; }
public string SystemPrompt { get; set; }
public bool ModerationEnabled { get; set; }
}
}

View File

@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using Microsoft.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class PasteAIProviderFactory : IPasteAIProviderFactory
{
private static readonly IReadOnlyList<PasteAIProviderRegistration> ProviderRegistrations = new[]
{
SemanticKernelPasteProvider.Registration,
LocalModelPasteProvider.Registration,
FoundryLocalPasteProvider.Registration,
};
private static readonly IReadOnlyDictionary<AIServiceType, Func<PasteAIConfig, IPasteAIProvider>> ProviderFactories = CreateProviderFactories();
public IPasteAIProvider CreateProvider(PasteAIConfig config)
{
ArgumentNullException.ThrowIfNull(config);
var serviceType = config.ProviderType;
if (serviceType == AIServiceType.Unknown)
{
serviceType = AIServiceType.OpenAI;
config.ProviderType = serviceType;
}
if (!ProviderFactories.TryGetValue(serviceType, out var factory))
{
throw new NotSupportedException($"Provider {config.ProviderType} not supported");
}
return factory(config);
}
private static IReadOnlyDictionary<AIServiceType, Func<PasteAIConfig, IPasteAIProvider>> CreateProviderFactories()
{
var map = new Dictionary<AIServiceType, Func<PasteAIConfig, IPasteAIProvider>>();
foreach (var registration in ProviderRegistrations)
{
Register(map, registration.SupportedTypes, registration.Factory);
}
return map;
}
private static void Register(Dictionary<AIServiceType, Func<PasteAIConfig, IPasteAIProvider>> map, IReadOnlyCollection<AIServiceType> types, Func<PasteAIConfig, IPasteAIProvider> factory)
{
foreach (var type in types)
{
map[type] = factory;
}
}
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class PasteAIProviderRegistration
{
public PasteAIProviderRegistration(IReadOnlyCollection<Microsoft.PowerToys.Settings.UI.Library.AIServiceType> supportedTypes, Func<PasteAIConfig, IPasteAIProvider> factory)
{
SupportedTypes = supportedTypes ?? throw new ArgumentNullException(nameof(supportedTypes));
Factory = factory ?? throw new ArgumentNullException(nameof(factory));
}
public IReadOnlyCollection<Microsoft.PowerToys.Settings.UI.Library.AIServiceType> SupportedTypes { get; }
public Func<PasteAIConfig, IPasteAIProvider> Factory { get; }
}
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using AdvancedPaste.Models;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class PasteAIRequest
{
public string Prompt { get; init; }
public string InputText { get; init; }
public string SystemPrompt { get; init; }
public AIServiceUsage Usage { get; set; } = AIServiceUsage.None;
}
}

View File

@@ -0,0 +1,203 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.Amazon;
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
using Microsoft.SemanticKernel.Connectors.MistralAI;
using Microsoft.SemanticKernel.Connectors.Ollama;
using Microsoft.SemanticKernel.Connectors.OpenAI;
namespace AdvancedPaste.Services.CustomActions
{
public sealed class SemanticKernelPasteProvider : IPasteAIProvider
{
private static readonly IReadOnlyCollection<AIServiceType> SupportedTypes = new[]
{
AIServiceType.OpenAI,
AIServiceType.AzureOpenAI,
AIServiceType.Mistral,
AIServiceType.Google,
AIServiceType.HuggingFace,
AIServiceType.AzureAIInference,
AIServiceType.Ollama,
AIServiceType.Anthropic,
AIServiceType.AmazonBedrock,
};
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new SemanticKernelPasteProvider(config));
private readonly PasteAIConfig _config;
private readonly AIServiceType _serviceType;
public SemanticKernelPasteProvider(PasteAIConfig config)
{
ArgumentNullException.ThrowIfNull(config);
_config = config;
_serviceType = config.ProviderType;
if (_serviceType == AIServiceType.Unknown)
{
_serviceType = AIServiceType.OpenAI;
_config.ProviderType = _serviceType;
}
}
public IReadOnlyCollection<AIServiceType> SupportedServiceTypes => SupportedTypes;
public Task<bool> IsAvailableAsync(CancellationToken cancellationToken) => Task.FromResult(true);
public async Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress)
{
ArgumentNullException.ThrowIfNull(request);
var systemPrompt = request.SystemPrompt;
if (string.IsNullOrWhiteSpace(systemPrompt))
{
throw new ArgumentException("System prompt must be provided", nameof(request));
}
var prompt = request.Prompt;
var inputText = request.InputText;
if (string.IsNullOrWhiteSpace(prompt) || string.IsNullOrWhiteSpace(inputText))
{
throw new ArgumentException("Prompt and input text must be provided", nameof(request));
}
var userMessageContent = $"""
User instructions:
{prompt}
Clipboard Content:
{inputText}
Output:
""";
var executionSettings = CreateExecutionSettings();
var kernel = CreateKernel();
var modelId = _config.Model;
IChatCompletionService chatService;
if (!string.IsNullOrWhiteSpace(modelId))
{
try
{
chatService = kernel.GetRequiredService<IChatCompletionService>(modelId);
}
catch (Exception)
{
chatService = kernel.GetRequiredService<IChatCompletionService>();
}
}
else
{
chatService = kernel.GetRequiredService<IChatCompletionService>();
}
var chatHistory = new ChatHistory();
chatHistory.AddSystemMessage(systemPrompt);
chatHistory.AddUserMessage(userMessageContent);
var response = await chatService.GetChatMessageContentAsync(chatHistory, executionSettings, kernel, cancellationToken);
chatHistory.Add(response);
request.Usage = AIServiceUsageHelper.GetOpenAIServiceUsage(response);
return response.Content;
}
private Kernel CreateKernel()
{
var kernelBuilder = Kernel.CreateBuilder();
var endpoint = string.IsNullOrWhiteSpace(_config.Endpoint) ? null : _config.Endpoint.Trim();
var apiKey = _config.ApiKey?.Trim() ?? string.Empty;
if (RequiresApiKey(_serviceType) && string.IsNullOrWhiteSpace(apiKey))
{
throw new InvalidOperationException($"API key is required for {_serviceType} but was not provided.");
}
switch (_serviceType)
{
case AIServiceType.OpenAI:
kernelBuilder.AddOpenAIChatCompletion(_config.Model, apiKey, serviceId: _config.Model);
break;
case AIServiceType.AzureOpenAI:
var deploymentName = string.IsNullOrWhiteSpace(_config.DeploymentName) ? _config.Model : _config.DeploymentName;
kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName, RequireEndpoint(endpoint, _serviceType), apiKey, serviceId: _config.Model);
break;
case AIServiceType.Mistral:
kernelBuilder.AddMistralChatCompletion(_config.Model, apiKey: apiKey);
break;
case AIServiceType.Google:
kernelBuilder.AddGoogleAIGeminiChatCompletion(_config.Model, apiKey: apiKey);
break;
case AIServiceType.HuggingFace:
kernelBuilder.AddHuggingFaceChatCompletion(_config.Model, apiKey: apiKey);
break;
case AIServiceType.AzureAIInference:
kernelBuilder.AddAzureAIInferenceChatCompletion(_config.Model, apiKey: apiKey, endpoint: new Uri(endpoint));
break;
case AIServiceType.Ollama:
kernelBuilder.AddOllamaChatCompletion(_config.Model, endpoint: new Uri(endpoint));
break;
case AIServiceType.Anthropic:
kernelBuilder.AddBedrockChatCompletionService(_config.Model);
break;
case AIServiceType.AmazonBedrock:
kernelBuilder.AddBedrockChatCompletionService(_config.Model);
break;
default:
throw new NotSupportedException($"Provider '{_config.ProviderType}' is not supported by {nameof(SemanticKernelPasteProvider)}");
}
return kernelBuilder.Build();
}
private PromptExecutionSettings CreateExecutionSettings()
{
return _serviceType switch
{
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings
{
Temperature = 0.01,
MaxTokens = 2000,
FunctionChoiceBehavior = null,
},
_ => new PromptExecutionSettings(),
};
}
private static bool RequiresApiKey(AIServiceType serviceType)
{
return serviceType switch
{
AIServiceType.Ollama => false,
AIServiceType.Anthropic => false,
AIServiceType.AmazonBedrock => false,
_ => true,
};
}
private static string RequireEndpoint(string endpoint, AIServiceType serviceType)
{
if (!string.IsNullOrWhiteSpace(endpoint))
{
return endpoint;
}
throw new InvalidOperationException($"Endpoint is required for {serviceType} but was not provided.");
}
}
}

View File

@@ -0,0 +1,188 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Threading;
using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
using Windows.Security.Credentials;
namespace AdvancedPaste.Services;
/// <summary>
/// Enhanced credentials provider that supports different AI service types
/// Keys are stored in Windows Credential Vault with service-specific identifiers
/// </summary>
public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider
{
private sealed class CredentialSlot
{
public AIServiceType ServiceType { get; set; } = AIServiceType.Unknown;
public string ProviderId { get; set; } = string.Empty;
public (string Resource, string Username)? Entry { get; set; }
public string Key { get; set; } = string.Empty;
}
private readonly IUserSettings _userSettings;
private readonly CredentialSlot _slot;
private readonly Lock _syncRoot = new();
public EnhancedVaultCredentialsProvider(IUserSettings userSettings)
{
_userSettings = userSettings ?? throw new ArgumentNullException(nameof(userSettings));
_slot = new CredentialSlot();
Refresh();
}
public string GetKey()
{
using (_syncRoot.EnterScope())
{
UpdateSlot(forceRefresh: false);
return _slot.Key;
}
}
public bool IsConfigured()
{
return !string.IsNullOrEmpty(GetKey());
}
public bool Refresh()
{
using (_syncRoot.EnterScope())
{
return UpdateSlot(forceRefresh: true);
}
}
private bool UpdateSlot(bool forceRefresh)
{
var (serviceType, providerId) = ResolveCredentialTarget();
var desiredServiceType = NormalizeServiceType(serviceType);
providerId ??= string.Empty;
var hasChanged = false;
if (_slot.ServiceType != desiredServiceType || !string.Equals(_slot.ProviderId, providerId, StringComparison.Ordinal))
{
_slot.ServiceType = desiredServiceType;
_slot.ProviderId = providerId;
_slot.Entry = BuildCredentialEntry(desiredServiceType, providerId);
forceRefresh = true;
hasChanged = true;
}
if (!forceRefresh)
{
return hasChanged;
}
var newKey = LoadKey(_slot.Entry);
if (!string.Equals(_slot.Key, newKey, StringComparison.Ordinal))
{
_slot.Key = newKey;
hasChanged = true;
}
return hasChanged;
}
private (AIServiceType ServiceType, string ProviderId) ResolveCredentialTarget()
{
var provider = _userSettings.PasteAIConfiguration?.ActiveProvider;
if (provider is null)
{
return (AIServiceType.OpenAI, string.Empty);
}
return (provider.ServiceTypeKind, provider.Id ?? string.Empty);
}
private static AIServiceType NormalizeServiceType(AIServiceType serviceType)
{
return serviceType == AIServiceType.Unknown ? AIServiceType.OpenAI : serviceType;
}
private static string LoadKey((string Resource, string Username)? entry)
{
if (entry is null)
{
return string.Empty;
}
try
{
var credential = new PasswordVault().Retrieve(entry.Value.Resource, entry.Value.Username);
return credential?.Password ?? string.Empty;
}
catch (Exception)
{
return string.Empty;
}
}
private static (string Resource, string Username)? BuildCredentialEntry(AIServiceType serviceType, string providerId)
{
string resource;
string serviceKey;
switch (serviceType)
{
case AIServiceType.OpenAI:
resource = "https://platform.openai.com/api-keys";
serviceKey = "openai";
break;
case AIServiceType.AzureOpenAI:
resource = "https://azure.microsoft.com/products/ai-services/openai-service";
serviceKey = "azureopenai";
break;
case AIServiceType.AzureAIInference:
resource = "https://azure.microsoft.com/products/ai-services/ai-inference";
serviceKey = "azureaiinference";
break;
case AIServiceType.Mistral:
resource = "https://console.mistral.ai/account/api-keys";
serviceKey = "mistral";
break;
case AIServiceType.Google:
resource = "https://ai.google.dev/";
serviceKey = "google";
break;
case AIServiceType.HuggingFace:
resource = "https://huggingface.co/settings/tokens";
serviceKey = "huggingface";
break;
case AIServiceType.FoundryLocal:
case AIServiceType.ML:
case AIServiceType.Onnx:
case AIServiceType.Ollama:
case AIServiceType.Anthropic:
case AIServiceType.AmazonBedrock:
return null;
default:
return null;
}
string username = $"PowerToys_AdvancedPaste_PasteAI_{serviceKey}_{NormalizeProviderIdentifier(providerId)}";
return (resource, username);
}
private static string NormalizeProviderIdentifier(string providerId)
{
if (string.IsNullOrWhiteSpace(providerId))
{
return "default";
}
var filtered = new string(providerId.Where(char.IsLetterOrDigit).ToArray());
return string.IsNullOrWhiteSpace(filtered) ? "default" : filtered.ToLowerInvariant();
}
}

View File

@@ -4,11 +4,26 @@
namespace AdvancedPaste.Services;
/// <summary>
/// Provides access to AI credentials stored for Advanced Paste scenarios.
/// </summary>
public interface IAICredentialsProvider
{
bool IsConfigured { get; }
/// <summary>
/// Gets a value indicating whether any credential is configured.
/// </summary>
/// <returns><see langword="true"/> when a non-empty credential exists for the active AI provider.</returns>
bool IsConfigured();
string Key { get; }
/// <summary>
/// Retrieves the credential for the active AI provider.
/// </summary>
/// <returns>Credential string or <see cref="string.Empty"/> when missing.</returns>
string GetKey();
/// <summary>
/// Refreshes the cached credential for the active AI provider.
/// </summary>
/// <returns><see langword="true"/> when the credential changed.</returns>
bool Refresh();
}

View File

@@ -1,14 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AdvancedPaste.Services;
public interface ICustomTextTransformService
{
Task<string> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress);
}

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