Compare commits
502 Commits
shawn/Pyth
...
shortcutgu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dad8d00cda | ||
|
|
1b71d64ef8 | ||
|
|
111d5b387e | ||
|
|
47d348f4ec | ||
|
|
4a71f79901 | ||
|
|
79f69b87f2 | ||
|
|
6a0ff6a131 | ||
|
|
9123365993 | ||
|
|
8af6a99136 | ||
|
|
9699d8a802 | ||
|
|
5b16a8c945 | ||
|
|
fa2b7f6e5f | ||
|
|
f117bfc64e | ||
|
|
52bf042df2 | ||
|
|
2fc27b13b6 | ||
|
|
94b7e3eea2 | ||
|
|
82e4705ed6 | ||
|
|
c334f1d997 | ||
|
|
8e74eb2ba8 | ||
|
|
cffddcdcb7 | ||
|
|
e7b6e36987 | ||
|
|
da9a08aa92 | ||
|
|
b893d633d9 | ||
|
|
a7bc09a87a | ||
|
|
6e9b3b1536 | ||
|
|
c07a3d9bd9 | ||
|
|
dcfcd0a699 | ||
|
|
7da07303b3 | ||
|
|
d973bcbcaa | ||
|
|
02fbb916a7 | ||
|
|
9dff42627a | ||
|
|
df4f130023 | ||
|
|
2d0aadee9c | ||
|
|
b02e53dda5 | ||
|
|
95ef94d35f | ||
|
|
8da01581f6 | ||
|
|
c7bb7f7e79 | ||
|
|
97d3b99bc5 | ||
|
|
e0409b0271 | ||
|
|
d9725649bc | ||
|
|
4e8c7aa3a6 | ||
|
|
49cdb9249d | ||
|
|
fb59905d41 | ||
|
|
38882fd392 | ||
|
|
75ac1521a8 | ||
|
|
b58803642d | ||
|
|
87c91c5a2a | ||
|
|
7716010a38 | ||
|
|
e77e7dfe68 | ||
|
|
7aae7343aa | ||
|
|
bf8493225d | ||
|
|
e17454b553 | ||
|
|
703dc92c04 | ||
|
|
012fb4a5c8 | ||
|
|
36f172cedf | ||
|
|
12eddcb320 | ||
|
|
2d3f93537f | ||
|
|
a187bfc2bb | ||
|
|
42902eeba5 | ||
|
|
b99defbc0d | ||
|
|
08caf10d84 | ||
|
|
eaaba455dd | ||
|
|
b68b2a5583 | ||
|
|
34e78bd8c3 | ||
|
|
e932fe6e61 | ||
|
|
c7d458b71d | ||
|
|
c4ff073d01 | ||
|
|
59eefd9581 | ||
|
|
42ff04d4bc | ||
|
|
3948fcc19d | ||
|
|
c5d17913e4 | ||
|
|
5715f694ea | ||
|
|
966c1db76a | ||
|
|
34cebb8285 | ||
|
|
9473657670 | ||
|
|
9948a2831f | ||
|
|
e746675ebf | ||
|
|
c3ec5b529b | ||
|
|
6a8e439847 | ||
|
|
36c30ae2fd | ||
|
|
d09408ce43 | ||
|
|
ab79ae7477 | ||
|
|
b6eb027558 | ||
|
|
9b28e6d5a2 | ||
|
|
ba68b88ca1 | ||
|
|
3e60249326 | ||
|
|
af1c0a313c | ||
|
|
3b76597981 | ||
|
|
e193509e65 | ||
|
|
a33f984027 | ||
|
|
be1f9dd2d8 | ||
|
|
14f2ac1b78 | ||
|
|
3796b244e5 | ||
|
|
5b981666a9 | ||
|
|
8864e64c97 | ||
|
|
df41e322d1 | ||
|
|
0e766bcafa | ||
|
|
a650504640 | ||
|
|
d6e27ae10b | ||
|
|
3d96d52564 | ||
|
|
95b70555bb | ||
|
|
2aedb932e5 | ||
|
|
f0c9a17fd1 | ||
|
|
a2892cd993 | ||
|
|
b93fd97e80 | ||
|
|
b4820c01cb | ||
|
|
87b6ca0ab6 | ||
|
|
1744bdecd8 | ||
|
|
05cd66c9bc | ||
|
|
cd15686416 | ||
|
|
e6d346a59b | ||
|
|
2aece74831 | ||
|
|
7861bc408c | ||
|
|
b835cde4d2 | ||
|
|
f79df0663c | ||
|
|
7211f7ed67 | ||
|
|
215dfaf236 | ||
|
|
f5a294bb66 | ||
|
|
d10203b8ac | ||
|
|
fecd2e72a7 | ||
|
|
fde1599f7d | ||
|
|
c68003c678 | ||
|
|
e5b0667397 | ||
|
|
8536d7b1cd | ||
|
|
7ac16118c8 | ||
|
|
7508e6e794 | ||
|
|
2d9dfff444 | ||
|
|
9de2e5298a | ||
|
|
07beeca9b9 | ||
|
|
234933f6fa | ||
|
|
f8a10550f3 | ||
|
|
ca1ffebc26 | ||
|
|
2e5c7d2ee6 | ||
|
|
949e42c5c7 | ||
|
|
a65266fcad | ||
|
|
656ea91580 | ||
|
|
d9bfc42229 | ||
|
|
11bfb40ec6 | ||
|
|
1eecf46c51 | ||
|
|
eeaf89e481 | ||
|
|
bfc5fea11e | ||
|
|
98c5b45a30 | ||
|
|
5509628f51 | ||
|
|
a255493c68 | ||
|
|
5c15a63846 | ||
|
|
2c95a61bb3 | ||
|
|
dbc390ab0d | ||
|
|
c6a79360f3 | ||
|
|
b0ccc2394a | ||
|
|
c8ffcb73c3 | ||
|
|
fcfbf83b55 | ||
|
|
de6ba922fd | ||
|
|
71cb9bc54e | ||
|
|
8ad571dcde | ||
|
|
bf00c1b94f | ||
|
|
7a89220a91 | ||
|
|
3a541bb3eb | ||
|
|
f4d23c85a6 | ||
|
|
5e302bed79 | ||
|
|
520037e128 | ||
|
|
d80d216bef | ||
|
|
b310c55835 | ||
|
|
999ba28e34 | ||
|
|
03ff49601d | ||
|
|
406b42db64 | ||
|
|
8d4a24cd3f | ||
|
|
0a20b7efb5 | ||
|
|
bf18e7815b | ||
|
|
43b37bc733 | ||
|
|
c011b2223c | ||
|
|
019ee046da | ||
|
|
e8579c39d0 | ||
|
|
911b39256c | ||
|
|
5520ae4cfa | ||
|
|
beddc3b065 | ||
|
|
088da21a70 | ||
|
|
befb5c672e | ||
|
|
578554d157 | ||
|
|
e4f98897ce | ||
|
|
be1e749574 | ||
|
|
dc57d22754 | ||
|
|
99ef5948b0 | ||
|
|
218a01c1a9 | ||
|
|
1c7f3d832c | ||
|
|
ff87dce4a4 | ||
|
|
758a60103c | ||
|
|
f8cadbf7f0 | ||
|
|
0819a6268b | ||
|
|
a31f82fcbd | ||
|
|
daeb2e1ef4 | ||
|
|
0089de33bd | ||
|
|
3e2914a0b2 | ||
|
|
3554f0884b | ||
|
|
ad60090096 | ||
|
|
de6a609d16 | ||
|
|
243255ecea | ||
|
|
1e1bd07087 | ||
|
|
32b4080007 | ||
|
|
47c1fb5418 | ||
|
|
8ee3d64667 | ||
|
|
51c9bc4930 | ||
|
|
ddff66c088 | ||
|
|
e28ed8a566 | ||
|
|
4f693778f2 | ||
|
|
e1ad13ab34 | ||
|
|
cea0497bb9 | ||
|
|
fd5be6d04e | ||
|
|
4dfdf46e0d | ||
|
|
1c4ecc23c6 | ||
|
|
5888f6eb7f | ||
|
|
152f64151b | ||
|
|
76b773b016 | ||
|
|
0da5602f68 | ||
|
|
565094abbe | ||
|
|
1314f68602 | ||
|
|
fbad0dce9c | ||
|
|
36a5b77e6c | ||
|
|
250a8cb5b7 | ||
|
|
4ce451edd0 | ||
|
|
42924e71c7 | ||
|
|
addebb8126 | ||
|
|
3b6453c932 | ||
|
|
2c97e04019 | ||
|
|
d27594c4f7 | ||
|
|
ac28b1c29f | ||
|
|
ee70b3ceca | ||
|
|
7cc4a16aa7 | ||
|
|
8c8c99c382 | ||
|
|
feae285c40 | ||
|
|
c34fb7f953 | ||
|
|
7d171a4428 | ||
|
|
2d037c4e91 | ||
|
|
0a69c93b87 | ||
|
|
a022a9f024 | ||
|
|
0b7d780980 | ||
|
|
7685cd1226 | ||
|
|
72bdfb073b | ||
|
|
96f97064be | ||
|
|
75a0fe1d2f | ||
|
|
5792d32d32 | ||
|
|
4cb3359314 | ||
|
|
943c2a1ff5 | ||
|
|
f686155d9b | ||
|
|
9afa1ec71d | ||
|
|
4337f8e5ff | ||
|
|
ed47bceac2 | ||
|
|
df23546c0b | ||
|
|
25f44bc6d9 | ||
|
|
dc533fbdb3 | ||
|
|
c05ba4e2c8 | ||
|
|
c83dd972a0 | ||
|
|
c33053b26b | ||
|
|
2cf7d0f5ec | ||
|
|
7cb0f3861a | ||
|
|
1106ac61f5 | ||
|
|
107bf3882c | ||
|
|
3f35b11cee | ||
|
|
1a9fcdcd1f | ||
|
|
6cf1d32e5a | ||
|
|
33497e59cc | ||
|
|
3d2f069c43 | ||
|
|
79d9b0e667 | ||
|
|
e2f611a7fc | ||
|
|
84ce86c573 | ||
|
|
735ea01a93 | ||
|
|
93f80f5f61 | ||
|
|
21f06b8bd0 | ||
|
|
fa78cc8ea7 | ||
|
|
cb9d54317a | ||
|
|
5d0eabed15 | ||
|
|
7051b8939b | ||
|
|
0d41d45a64 | ||
|
|
86115a54f6 | ||
|
|
99706d4324 | ||
|
|
ff194c0b5f | ||
|
|
a151d6c8b6 | ||
|
|
7610b77109 | ||
|
|
549b32e273 | ||
|
|
0ccf5986e9 | ||
|
|
87b24afa23 | ||
|
|
74c53c14e6 | ||
|
|
77173cd075 | ||
|
|
149e7b1efe | ||
|
|
0c2d24c3f6 | ||
|
|
b81ea23c68 | ||
|
|
39bbf0593e | ||
|
|
4620f6f381 | ||
|
|
da3b12d536 | ||
|
|
bab77edd11 | ||
|
|
414ee86fb3 | ||
|
|
eeeb6c0c93 | ||
|
|
70e082ce4f | ||
|
|
8404bfbebb | ||
|
|
77412d1961 | ||
|
|
fad5a3ac69 | ||
|
|
52cab7058a | ||
|
|
35a3c55f29 | ||
|
|
ed16ae7b2a | ||
|
|
f049cc5839 | ||
|
|
f82fb2a411 | ||
|
|
90131e35d9 | ||
|
|
77355ef2fb | ||
|
|
a130969d0a | ||
|
|
d1605640ca | ||
|
|
9859fb6196 | ||
|
|
3bd85efc56 | ||
|
|
f8453214fb | ||
|
|
0aca7c292c | ||
|
|
c6f1a09fa2 | ||
|
|
b72224ea0b | ||
|
|
e323da939b | ||
|
|
75fb296bb2 | ||
|
|
3d69785ca4 | ||
|
|
f6b0996c9b | ||
|
|
748d5e485c | ||
|
|
1718cecedb | ||
|
|
4f0c8f476a | ||
|
|
a953a39aec | ||
|
|
8c4ff37a50 | ||
|
|
02062dd023 | ||
|
|
bcbca0d5dd | ||
|
|
f0134e4448 | ||
|
|
f651d1a611 | ||
|
|
d20ae940d5 | ||
|
|
86860df314 | ||
|
|
d28f312b81 | ||
|
|
f6309ac549 | ||
|
|
c23ba227b4 | ||
|
|
ce2e72832c | ||
|
|
c066cc3deb | ||
|
|
9089ca2ede | ||
|
|
798564eea4 | ||
|
|
738b78c406 | ||
|
|
1cb99e32ef | ||
|
|
95835a4cfa | ||
|
|
4146876d88 | ||
|
|
a6e49c941d | ||
|
|
734c738751 | ||
|
|
22b4dda3aa | ||
|
|
fd399045f7 | ||
|
|
7e3f9f0c3f | ||
|
|
9e4bf1e3e0 | ||
|
|
cc3c3c0367 | ||
|
|
637b58b136 | ||
|
|
6c691f59e8 | ||
|
|
7dfe6c0159 | ||
|
|
543399b62b | ||
|
|
90e81cbfd5 | ||
|
|
9f9d221eab | ||
|
|
b9c5c15be2 | ||
|
|
3a4f9c2274 | ||
|
|
a51fe19f82 | ||
|
|
efc814a610 | ||
|
|
1d1ae0d191 | ||
|
|
3df4b45849 | ||
|
|
88357a5a99 | ||
|
|
b4773affa7 | ||
|
|
e82c2d20cb | ||
|
|
e3a1f97ef3 | ||
|
|
b26ded5370 | ||
|
|
2e8ad4827b | ||
|
|
877626ef45 | ||
|
|
e0f72df36c | ||
|
|
552b02d596 | ||
|
|
c184acbada | ||
|
|
55038c3c5e | ||
|
|
f537c43139 | ||
|
|
f867323677 | ||
|
|
d58145eb8c | ||
|
|
486bec0ebd | ||
|
|
e76506dffd | ||
|
|
841a5c5555 | ||
|
|
f646e0328e | ||
|
|
5054a776dd | ||
|
|
9d480c8e2c | ||
|
|
d652285a81 | ||
|
|
200afb5c4b | ||
|
|
e7582ebd6a | ||
|
|
b9c1181d9f | ||
|
|
4aff3418e4 | ||
|
|
e53e1b4376 | ||
|
|
73f718c233 | ||
|
|
0917a64e7d | ||
|
|
a764bf3e0c | ||
|
|
4853bd0345 | ||
|
|
dceb1d7730 | ||
|
|
72be09554e | ||
|
|
19f95066c3 | ||
|
|
bb16ae1709 | ||
|
|
dc0877ebe5 | ||
|
|
cd844e3889 | ||
|
|
ac789a7fbe | ||
|
|
3b7df37ac2 | ||
|
|
387b7e9795 | ||
|
|
b553addcdd | ||
|
|
5c11c751fe | ||
|
|
6d7d5f9cde | ||
|
|
4cb9c53809 | ||
|
|
84d4cbb16d | ||
|
|
97cba618da | ||
|
|
145247c4fb | ||
|
|
84ab12027b | ||
|
|
3458d01d4c | ||
|
|
2e6f80f944 | ||
|
|
f8cc513f9c | ||
|
|
b1d5233622 | ||
|
|
68b7b4183f | ||
|
|
68a10d0488 | ||
|
|
e70ca56e9d | ||
|
|
16c4a56ca1 | ||
|
|
0d5c85a00d | ||
|
|
509ad636fe | ||
|
|
26f76105d4 | ||
|
|
639b29eb8c | ||
|
|
bff3874b5f | ||
|
|
eff58e1df5 | ||
|
|
411f4df2c0 | ||
|
|
7dc8c1000b | ||
|
|
48d8e33375 | ||
|
|
afc27e873f | ||
|
|
3302e61d72 | ||
|
|
e6edca93e7 | ||
|
|
7acab452d5 | ||
|
|
0a07811233 | ||
|
|
9ecf82d2ea | ||
|
|
44d12c6e63 | ||
|
|
6b8a3e65f7 | ||
|
|
2b16068a7d | ||
|
|
440e75184a | ||
|
|
acf510dff5 | ||
|
|
ddd090cc81 | ||
|
|
2f4766df19 | ||
|
|
6558260c53 | ||
|
|
55b3e15f10 | ||
|
|
69c6475e15 | ||
|
|
8e7be164a9 | ||
|
|
f42b3922c7 | ||
|
|
d568d16560 | ||
|
|
0abae1d190 | ||
|
|
271e0c0533 | ||
|
|
6bd5c4c811 | ||
|
|
a41be807a4 | ||
|
|
e11626550e | ||
|
|
7266745124 | ||
|
|
77a5bc2ff5 | ||
|
|
0b6683eb34 | ||
|
|
3796fdb706 | ||
|
|
2d12932e44 | ||
|
|
1da76e55bb | ||
|
|
3c1a6a5b16 | ||
|
|
3b77feb879 | ||
|
|
7e7bb04d48 | ||
|
|
273b50cb16 | ||
|
|
498c8d534f | ||
|
|
0e2f466454 | ||
|
|
1982f2615d | ||
|
|
2f89281178 | ||
|
|
56f056e492 | ||
|
|
71dd8fe83f | ||
|
|
f9183af53d | ||
|
|
0f85f8bad6 | ||
|
|
e0e7bf4df2 | ||
|
|
b12fcf6699 | ||
|
|
2ee02c4bbe | ||
|
|
a306797d21 | ||
|
|
7e50caa04e | ||
|
|
e3b1ec356e | ||
|
|
fa54b49fca | ||
|
|
7e2fc4481d | ||
|
|
201a27d2bb | ||
|
|
fe3d481407 | ||
|
|
3475c92f32 | ||
|
|
82b0ca71fd | ||
|
|
3a8431ae9d | ||
|
|
eed6dc6f8d | ||
|
|
038cd23423 | ||
|
|
be799ddd82 | ||
|
|
e26bf2acd6 | ||
|
|
95e0a20444 | ||
|
|
3e75fb0c52 | ||
|
|
ba4098960c | ||
|
|
1580279be1 | ||
|
|
07760e4730 | ||
|
|
d73ab4f2d3 | ||
|
|
a6b761433e | ||
|
|
da77396da5 | ||
|
|
46df48684d | ||
|
|
7596e965ef | ||
|
|
0b823ea8bd | ||
|
|
54d176c4c4 | ||
|
|
569f07268b | ||
|
|
7ce5182695 | ||
|
|
b16b4579fa | ||
|
|
a292a92f4d | ||
|
|
6952deb4ae | ||
|
|
da7b789bfe | ||
|
|
a14c458f19 | ||
|
|
22dc870991 | ||
|
|
d5f5500347 | ||
|
|
c81a423880 | ||
|
|
7a6189ba3e | ||
|
|
03587ae800 |
11
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -40,7 +40,6 @@ body:
|
||||
- Other (please specify in "Steps to Reproduce")
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Area(s) with issue?
|
||||
@@ -58,6 +57,7 @@ body:
|
||||
- Environment Variables
|
||||
- FancyZones
|
||||
- FancyZones Editor
|
||||
- Grab And Move
|
||||
- File Locksmith
|
||||
- "File Explorer: Preview Pane"
|
||||
- "File Explorer: Thumbnail preview"
|
||||
@@ -70,6 +70,7 @@ body:
|
||||
- Mouse Without Borders
|
||||
- New+
|
||||
- Peek
|
||||
- Power Display
|
||||
- PowerRename
|
||||
- PowerToys Run
|
||||
- Quick Accent
|
||||
@@ -106,7 +107,13 @@ body:
|
||||
placeholder: What happened instead?
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: upload
|
||||
id: bugreportfile
|
||||
attributes:
|
||||
label: Upload Bug Report ZIP-file
|
||||
description: Right-clicking the PowerToys tray icon in the taskbar and selecting “Report bug” generates a ZIP file containing diagnostic information about your setup and PowerToys logs, helping us better understand and troubleshoot the issue.
|
||||
validations:
|
||||
required: false
|
||||
- id: additionalInfo
|
||||
type: textarea
|
||||
attributes:
|
||||
|
||||
68
.github/actions/spell-check/allow/code.txt
vendored
@@ -1,6 +1,7 @@
|
||||
# COLORS
|
||||
|
||||
argb
|
||||
Bgr
|
||||
bgra
|
||||
BLACKONWHITE
|
||||
BLUEGRAY
|
||||
@@ -18,6 +19,7 @@ OLIVEGREEN
|
||||
PALEBLUE
|
||||
PArgb
|
||||
Pbgra
|
||||
SRGBTo
|
||||
WHITEONBLACK
|
||||
|
||||
|
||||
@@ -28,6 +30,7 @@ RUS
|
||||
|
||||
AYUV
|
||||
bak
|
||||
HDP
|
||||
Bcl
|
||||
bgcode
|
||||
Deflatealgorithm
|
||||
@@ -48,6 +51,7 @@ resw
|
||||
resx
|
||||
srt
|
||||
Stereolithography
|
||||
taskmgr
|
||||
terabyte
|
||||
UYVY
|
||||
xbf
|
||||
@@ -124,6 +128,7 @@ HOLDSPACE
|
||||
HOLDBACKSPACE
|
||||
IDIGNORE
|
||||
KBDLLHOOKSTRUCT
|
||||
keydowns
|
||||
keyevent
|
||||
LAlt
|
||||
LBUTTON
|
||||
@@ -182,6 +187,12 @@ xmlutil
|
||||
# Prefix
|
||||
pcs
|
||||
|
||||
# EXPRTK / C++ MATH
|
||||
|
||||
ifunction
|
||||
isinf
|
||||
isnan
|
||||
|
||||
# User32.SYSTEM_METRICS_INDEX.cs
|
||||
|
||||
CLEANBOOT
|
||||
@@ -297,6 +308,8 @@ pwa
|
||||
|
||||
AOT
|
||||
Aot
|
||||
ify
|
||||
TFM
|
||||
|
||||
# YML
|
||||
onefuzz
|
||||
@@ -304,6 +317,7 @@ onefuzz
|
||||
# NameInCode
|
||||
leilzh
|
||||
mengyuanchen
|
||||
contoso
|
||||
|
||||
# DllName
|
||||
testhost
|
||||
@@ -323,17 +337,58 @@ MRUCMPPROC
|
||||
MRUINFO
|
||||
REGSTR
|
||||
|
||||
#Xaml
|
||||
NVI
|
||||
Storyboards
|
||||
|
||||
# Misc Win32 APIs and PInvokes
|
||||
DEFAULTTONEAREST
|
||||
INVOKEIDLIST
|
||||
LCMAP
|
||||
MEMORYSTATUSEX
|
||||
ABE
|
||||
Mdt
|
||||
HTCAPTION
|
||||
POSCHANGED
|
||||
QPC
|
||||
QUERYPOS
|
||||
SETAUTOHIDEBAR
|
||||
ULW
|
||||
WINDOWPOS
|
||||
WINEVENTPROC
|
||||
WORKERW
|
||||
FULLSCREENAPP
|
||||
ACLO
|
||||
CACLI
|
||||
DOENVSUBST
|
||||
FILESYSONLY
|
||||
URLIS
|
||||
WAITTIMEOUT
|
||||
DEFAULTTONEAREST
|
||||
|
||||
|
||||
# COM/WinRT interface prefixes and type fragments
|
||||
BAlt
|
||||
BShift
|
||||
Cmanifest
|
||||
Cmodule
|
||||
Cuuid
|
||||
Dng
|
||||
IApplication
|
||||
IDisposable
|
||||
IEnum
|
||||
IFolder
|
||||
IInitialize
|
||||
IMemory
|
||||
IOle
|
||||
ipreview
|
||||
IProperty
|
||||
IShell
|
||||
ithumbnail
|
||||
IVirtual
|
||||
|
||||
# Test frameworks
|
||||
MSTEST
|
||||
|
||||
# PowerRename metadata pattern abbreviations (used in tests and regex patterns)
|
||||
DDDD
|
||||
@@ -345,6 +400,12 @@ YYY
|
||||
# Unicode
|
||||
precomposed
|
||||
|
||||
# names of characters
|
||||
zwsp
|
||||
|
||||
# mermaid
|
||||
autonumber
|
||||
|
||||
# GitHub issue/PR commands
|
||||
azp
|
||||
feedbackhub
|
||||
@@ -361,3 +422,10 @@ Nonpaged
|
||||
|
||||
# XAML
|
||||
Untargeted
|
||||
|
||||
# Program names
|
||||
SEARCHHOST
|
||||
SHELLEXPERIENCEHOST
|
||||
SHELLHOST
|
||||
STARTMENUEXPERIENCEHOST
|
||||
WIDGETBOARD
|
||||
|
||||
4
.github/actions/spell-check/allow/names.txt
vendored
@@ -178,7 +178,9 @@ Taras
|
||||
TBM
|
||||
Teutsch
|
||||
tilovell
|
||||
traies
|
||||
Triet
|
||||
udit
|
||||
urnotdfs
|
||||
vednig
|
||||
waaverecords
|
||||
@@ -207,6 +209,7 @@ Bilibili
|
||||
BVID
|
||||
capturevideosample
|
||||
cmdow
|
||||
contoso
|
||||
Contoso
|
||||
Controlz
|
||||
cortana
|
||||
@@ -223,6 +226,7 @@ Moq
|
||||
mozilla
|
||||
mspaint
|
||||
Newtonsoft
|
||||
NVIDIA
|
||||
onenote
|
||||
openai
|
||||
Quickime
|
||||
|
||||
149
.github/actions/spell-check/allow/zoomit.txt
vendored
@@ -1,9 +1,26 @@
|
||||
accelscroll
|
||||
acq
|
||||
adr
|
||||
Adr
|
||||
APPLYTOSUBMENUS
|
||||
AUDCLNT
|
||||
axisdefer
|
||||
axisflip
|
||||
axisstart
|
||||
BGRX
|
||||
bitmaps
|
||||
blits
|
||||
BREAKSCR
|
||||
BUFFERFLAGS
|
||||
Cands
|
||||
capturepath
|
||||
centiseconds
|
||||
CLASSW
|
||||
coeffs
|
||||
coprime
|
||||
CREATEDIBSECTION
|
||||
CREATESTRUCTW
|
||||
crossfades
|
||||
Ctl
|
||||
CTLCOLOR
|
||||
CTLCOLORBTN
|
||||
@@ -11,53 +28,185 @@ CTLCOLORDLG
|
||||
CTLCOLOREDIT
|
||||
CTLCOLORLISTBOX
|
||||
CTrim
|
||||
DBuffer
|
||||
ddx
|
||||
ddy
|
||||
DEVSOURCE
|
||||
DFCS
|
||||
dlg
|
||||
dlu
|
||||
DONTCARE
|
||||
downsample
|
||||
DRAWITEM
|
||||
DRAWITEMSTRUCT
|
||||
droppedband
|
||||
Droppedband
|
||||
dsum
|
||||
dupburst
|
||||
dupsegments
|
||||
DWLP
|
||||
EDITCONTROL
|
||||
ENABLEHOOK
|
||||
ENDOFSTREAM
|
||||
expectedlock
|
||||
fabsf
|
||||
fastscroll
|
||||
FDE
|
||||
GETCHANNELRECT
|
||||
GETCHECK
|
||||
GETCOUNT
|
||||
GETSCREENSAVEACTIVE
|
||||
GETSCREENSAVETIMEOUT
|
||||
GETTHUMBRECT
|
||||
GIFs
|
||||
hcfdark
|
||||
hcfwhitespace
|
||||
HTBOTTOMRIGHT
|
||||
HTHEME
|
||||
htol
|
||||
ICONINFORMATION
|
||||
ICONWARNING
|
||||
igc
|
||||
Inj
|
||||
jumprecover
|
||||
KSDATAFORMAT
|
||||
latestcapture
|
||||
ldx
|
||||
LEFTNOWORDWRAP
|
||||
legitjumps
|
||||
letterbox
|
||||
lld
|
||||
llu
|
||||
llums
|
||||
logfont
|
||||
lookback
|
||||
lround
|
||||
lte
|
||||
luma
|
||||
Luma
|
||||
manualdrop
|
||||
maskcache
|
||||
maxstep
|
||||
MENUINFO
|
||||
MFSTARTUP
|
||||
mfxhw
|
||||
mic
|
||||
middledrop
|
||||
MJPEG
|
||||
MMRESULT
|
||||
momentumreversal
|
||||
mrate
|
||||
mrt
|
||||
narrowstrip
|
||||
ncapture
|
||||
ncm
|
||||
nduplicates
|
||||
niterations
|
||||
nmonitor
|
||||
NONCLIENTMETRICS
|
||||
NONOTIFY
|
||||
nonvle
|
||||
nredraw
|
||||
nstop
|
||||
nsubpixel
|
||||
ntorn
|
||||
nvw
|
||||
osc
|
||||
OWNERDRAW
|
||||
PBGRA
|
||||
periodictrap
|
||||
pillarbox
|
||||
pfdc
|
||||
playhead
|
||||
pointerreuse
|
||||
PSWA
|
||||
pwfx
|
||||
qpc
|
||||
Qpc
|
||||
quantums
|
||||
RCZOOMITSCR
|
||||
readback
|
||||
READERF
|
||||
realcapture
|
||||
REFKNOWNFOLDERID
|
||||
reposted
|
||||
RETURNCMD
|
||||
SCREENSAVE
|
||||
SCRNSAVE
|
||||
SCRNSAVECONFIGURE
|
||||
scrnsavw
|
||||
Scrnsavw
|
||||
scrollramp
|
||||
SCROLLSIZEGRIP
|
||||
selftest
|
||||
SETBARCOLOR
|
||||
SETBKCOLOR
|
||||
SETDEFID
|
||||
SETRECT
|
||||
SETSCREENSAVETIMEOUT
|
||||
SHAREMODE
|
||||
SHAREVIOLATION
|
||||
shortlist
|
||||
slowthenfast
|
||||
smallstart
|
||||
SNIPOCR
|
||||
sqrtf
|
||||
ssi
|
||||
startuprecovery
|
||||
stf
|
||||
stopafter
|
||||
STREAMFLAGS
|
||||
submix
|
||||
sxx
|
||||
sxy
|
||||
synthesising
|
||||
syy
|
||||
tallportal
|
||||
tci
|
||||
tcsicmp
|
||||
TEXTMETRIC
|
||||
tinystep
|
||||
tme
|
||||
toolbars
|
||||
TRACKMOUSEEVENT
|
||||
Unadvise
|
||||
vaddq
|
||||
vaddvq
|
||||
vandq
|
||||
vcgeq
|
||||
vdup
|
||||
VIDCAP
|
||||
vld
|
||||
vle
|
||||
Vle
|
||||
VLE
|
||||
vminq
|
||||
vmlal
|
||||
vmull
|
||||
vqaddq
|
||||
vshrn
|
||||
vsntprintf
|
||||
vsnwprintf
|
||||
vsync
|
||||
WASAPI
|
||||
WAVEFORMATEX
|
||||
WAVEFORMATEXTENSIBLE
|
||||
webcam
|
||||
Webcam
|
||||
webcams
|
||||
wfopen
|
||||
wideportal
|
||||
wil
|
||||
WMU
|
||||
wrapjump
|
||||
wtol
|
||||
WTSSESSION
|
||||
WTSUn
|
||||
XEnd
|
||||
XStart
|
||||
XStep
|
||||
YInternal
|
||||
ZMBS
|
||||
zncc
|
||||
Zncc
|
||||
ZNCC
|
||||
|
||||
73
.github/actions/spell-check/candidate.patterns
vendored
@@ -1,6 +1,3 @@
|
||||
# D2D
|
||||
#D?2D
|
||||
|
||||
# Repeated letters
|
||||
\b([a-z])\g{-1}{2,}\b
|
||||
|
||||
@@ -14,7 +11,7 @@
|
||||
^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b
|
||||
|
||||
# copyright
|
||||
Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
|
||||
Copyright (?:\([Cc]\)|©|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
|
||||
|
||||
# patch hunk comments
|
||||
^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .*
|
||||
@@ -22,10 +19,10 @@ Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
|
||||
index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# file permissions
|
||||
['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
|
||||
(?:^|['"`\s])(?!-+\s)[-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
|
||||
|
||||
# css fonts
|
||||
\bfont(?:-family|):[^;}]+
|
||||
\bfont(?:-family(?:[-\w+]*)|):[^;}]+
|
||||
|
||||
# css url wrappings
|
||||
\burl\([^)]+\)
|
||||
@@ -90,6 +87,9 @@ arn:aws:[-/:\w]+
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# Azure AD
|
||||
\baad\.\w{48}\b
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
@@ -171,7 +171,7 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitHub actions
|
||||
\buses:\s+[-\w.]+/[-\w./]+@[-\w.]+
|
||||
\buses:\s+(['"]?)[-\w.]+/[-\w./]+@[-\w.]+\g{-1}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
@@ -240,7 +240,7 @@ accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
\bmedium\.com/@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]*
|
||||
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%?#]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
@@ -414,7 +414,7 @@ ipfs://[0-9a-zA-Z]{3,}
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
|
||||
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[(?:\d+(?:;\d+)*|)m
|
||||
|
||||
# URL escaped characters
|
||||
%[0-9A-F][A-F](?=[A-Za-z])
|
||||
@@ -431,7 +431,7 @@ sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(\\?['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
\b(?=(?:[a-fA-F]{0,2}\d)*[a-fA-F]{3})[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
@@ -455,7 +455,11 @@ LS0tLS1CRUdJT.*
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
|
||||
# unicode escaped characters (4)
|
||||
\\u[0-9a-fA-F]{4}
|
||||
|
||||
# hex digits including css/html color classes
|
||||
(?:[\\0][xX]|\\u\{?|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
|
||||
|
||||
# integrity
|
||||
@@ -478,7 +482,7 @@ Name\[[^\]]+\]=.*
|
||||
(?:(?:\b|_|(?<=[a-z]))I|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
|
||||
|
||||
# python
|
||||
#\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
|
||||
#\b(?i)py(?!gment|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
|
||||
|
||||
# crypt
|
||||
(['"])\$2[ayb]\$.{56}\g{-1}
|
||||
@@ -498,12 +502,21 @@ Name\[[^\]]+\]=.*
|
||||
# go.sum
|
||||
\bh1:\S+
|
||||
|
||||
# golang print-f-style functions
|
||||
#(?i)(?<=append|comma|debug|equal|err|error|exit|fatal|format|info|log|name|panic|print|skip|scan|string|trace|true|warn|warning|wrap|write)(?:f|ln)(?:[ (]|$)
|
||||
|
||||
# golang regular expression
|
||||
(?<!")\br".+?"
|
||||
|
||||
# imports
|
||||
^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+
|
||||
^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+(?:\s+from (['"]).*?\g{-1}|)
|
||||
|
||||
# scala modules
|
||||
#("[^"]+"\s*%%?\s*){2,3}"[^"]+"
|
||||
|
||||
# Dataframes / NumPy
|
||||
#\b(?:df|np)\.\w{3,}
|
||||
|
||||
# container images
|
||||
image: [-\w./:@]+
|
||||
|
||||
@@ -533,12 +546,18 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1}
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!['"])\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)['"](?=[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expression for word breaks
|
||||
#\\b(?=[a-z]{2})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# Java regular expressions
|
||||
Pattern\.(?:compile|matches)\(".*"
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.{3,}/[gim]*\.test\(
|
||||
# javascript exec/test regex
|
||||
/.{3,}?/[gim]*\.(?:exec|test)\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]{3,}/[gim]*\s*
|
||||
# javascript match regex
|
||||
@@ -565,7 +584,7 @@ perl(?:\s+-[a-zA-Z]\w*)+
|
||||
regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\)
|
||||
|
||||
# regex choice
|
||||
# \(\?:[^)]+\|[^)]+\)
|
||||
#\((?:\?:|)[^)|]+(?<! )\|(?!(?:jq|xargs)\b)[^)| ][^)]*\)
|
||||
|
||||
# proto
|
||||
^\s*(\w+)\s\g{-1} =
|
||||
@@ -588,6 +607,9 @@ urn:shemas-jetbrains-com
|
||||
# Debian changelog severity
|
||||
[-\w]+ \(.*\) (?:\w+|baseline|unstable|experimental); urgency=(?:low|medium|high|emergency|critical)\b
|
||||
|
||||
# Red Hat Package management spec file dependencies
|
||||
^(?:Build|)Requires: [-.\w]+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
@@ -642,6 +664,8 @@ PrependWithABINamepsace
|
||||
>[-a-zA-Z=;:/0-9+]{3,}=</
|
||||
# base64 encoded content, possibly wrapped in mime
|
||||
#(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
|
||||
# jwt
|
||||
(?:\be[wy][-a-zA-Z=;:/0-9+]+\.){2}[-_\w]+
|
||||
# base64 encoded json
|
||||
\beyJ[-a-zA-Z=;:/0-9+]+
|
||||
# base64 encoded pkcs
|
||||
@@ -679,9 +703,9 @@ systemd.*?running in system mode \([-+].*\)$
|
||||
|
||||
# Non-English
|
||||
# Even repositories expecting pure English content can unintentionally have Non-English content... People will occasionally mistakenly enter [homoglyphs](https://en.wikipedia.org/wiki/Homoglyph) which are essentially typos, and using this pattern will mean check-spelling will not complain about them.
|
||||
#
|
||||
# .
|
||||
# If the content to be checked should be written in English and the only Non-English items will be people's names, then you can consider adding this.
|
||||
#
|
||||
# .
|
||||
# Alternatively, if you're using check-spelling v0.0.25+, and you would like to _check_ the Non-English content for spelling errors, you can. For information on how to do so, see:
|
||||
# https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#unicode
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,}
|
||||
@@ -693,7 +717,7 @@ systemd.*?running in system mode \([-+].*\)$
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex (check-spelling >= 0.0.22)
|
||||
# LaTeX
|
||||
\\\w{2,}\{
|
||||
|
||||
# American Mathematical Society (AMS) / Doxygen
|
||||
@@ -720,7 +744,6 @@ nolint:\s*[\w,]+
|
||||
# cygwin paths
|
||||
/cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+
|
||||
|
||||
# in check-spelling@v0.0.22+, printf markers aren't automatically consumed
|
||||
# printf markers
|
||||
#(?<!\\)\\[nrt](?=[a-z]{2,})
|
||||
# alternate printf markers if you run into latex and friends
|
||||
@@ -749,12 +772,12 @@ W/"[^"]+"
|
||||
|
||||
# Compiler flags (Unix, Java/Scala)
|
||||
# Use if you have things like `-Pdocker` and want to treat them as `docker`
|
||||
#(?:^|[\t ,>"'`=(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
#(?:^|[\t ,>"'`=\[(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (Windows / PowerShell)
|
||||
# This is a subset of the more general compiler flags pattern.
|
||||
# It avoids matching `-Path` to prevent it from being treated as `ath`
|
||||
#(?:^|[\t ,"'`=(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}))
|
||||
#(?:^|[\t ,"'`=\[(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}))
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
@@ -762,7 +785,7 @@ W/"[^"]+"
|
||||
# Library prefix
|
||||
# e.g., `lib`+`archive`, `lib`+`raw`, `lib`+`unwind`
|
||||
# (ignores some words that happen to start with `lib`)
|
||||
(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
|
||||
(?:\b|_)[Ll]ib(?!era[lt])(?:re(?=office)|era|)(?!ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
|
||||
|
||||
# iSCSI iqn (approximate regex)
|
||||
\biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b
|
||||
@@ -773,9 +796,9 @@ W/"[^"]+"
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\b(?:bash|sh|set)(?:\s+[-+][abefimouxE]{1,2})*\s+[-+][abefimouxE]{3,}(?:\s+[-+][abefimouxE]+)*
|
||||
\b(?:bash|(?<!\.)sh|set)(?:\s+[-+][abefimouxE]{1,2})*\s+[-+][abefimouxE]{3,}(?:\s+[-+][abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:\s-C \S+|(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
|
||||
9
.github/actions/spell-check/excludes.txt
vendored
@@ -105,13 +105,13 @@
|
||||
^src/common/ManagedCommon/ColorFormatHelper\.cs$
|
||||
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
|
||||
^src/common/sysinternals/Eula/
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
|
||||
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^doc/devdocs/modules/cmdpal/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage\.cs$
|
||||
^src/modules/cmdpal/Microsoft\.CmdPal\.UI/Settings/InternalPage\.SampleData\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/.*\.TestData\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/Text/.*\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
|
||||
^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$
|
||||
^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/
|
||||
^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$
|
||||
@@ -140,8 +140,7 @@
|
||||
^tools/project_template/ModuleTemplate/resource\.h$
|
||||
^tools/Verification scripts/Check preview handler registration\.ps1$
|
||||
ignore$
|
||||
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
|
||||
^src/common/CalculatorEngineCommon/exprtk\.hpp$
|
||||
src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage.cs
|
||||
^src/modules/powerrename/unittests/testdata/avif_test\.avif$
|
||||
^src/modules/powerrename/unittests/testdata/heif_test\.heic$
|
||||
^deps/spdlog-msvc-fix/
|
||||
|
||||
1030
.github/actions/spell-check/expect.txt
vendored
497
.github/actions/spell-check/line_forbidden.patterns
vendored
53
.github/actions/spell-check/patterns.txt
vendored
@@ -1,10 +1,30 @@
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
|
||||
|
||||
Inno Setup
|
||||
|
||||
FFmpeg
|
||||
|
||||
# https://github.com/MicrosoftEdge/edge-launcher
|
||||
MIcrosoftEdgeLauncherCsharp
|
||||
|
||||
# x64
|
||||
(?:(?<=[a-df-z])x|(?<=[A-Z]X))64
|
||||
|
||||
# reversed irreversible binomials
|
||||
\b(?:mouse down and up|low and high)\b
|
||||
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# JavaScript regex literals that start with \b can be reported as "b..." words.
|
||||
# Example: /\bclass\s+.../
|
||||
^\s*/\\[b].{3,}?/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
|
||||
# GitHub API header token used in code (not natural language).
|
||||
\bx-ratelimit-reset\b
|
||||
|
||||
# Gaelic
|
||||
Gàidhlig
|
||||
|
||||
@@ -71,11 +91,14 @@ StringComparer.OrdinalIgnoreCase\) \{.*\}
|
||||
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
|
||||
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
|
||||
|
||||
# DateTime Formats
|
||||
Get-Date -Format \w+|DateTime\.Now(?::|\.ToString\(")\w+
|
||||
|
||||
# Automatically suggested patterns
|
||||
|
||||
# hit-count: 5402 file-count: 1339
|
||||
# IServiceProvider / isAThing
|
||||
(?:(?:\b|_|(?<=[a-z]))[IT]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
|
||||
(?:(?:\b|_|(?<=[a-z]))[A-Z]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
|
||||
|
||||
# hit-count: 2073 file-count: 842
|
||||
# #includes
|
||||
@@ -159,6 +182,10 @@ aka\.ms/[a-zA-Z0-9]+
|
||||
# kubernetes crd patterns
|
||||
^\s*pattern: .*$
|
||||
|
||||
# hit-count: 7 file-count: 3
|
||||
# unicode escaped characters (4)
|
||||
\\u[0-9a-fA-F]{4}
|
||||
|
||||
# hit-count: 5 file-count: 3
|
||||
# URL escaped characters
|
||||
%[0-9A-F][A-F](?=[A-Za-z])
|
||||
@@ -171,6 +198,10 @@ aka\.ms/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/@?[^/\s"]+/[-\w:/*.]+
|
||||
|
||||
# hit-count: 2 file-count: 2
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:\s-C \S+|(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
@@ -184,22 +215,9 @@ aka\.ms/[a-zA-Z0-9]+
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
|
||||
# #pragma lib
|
||||
^\s*#pragma comment\(lib, ".*?"\)
|
||||
|
||||
# UnitTests
|
||||
\[DataRow\(.*\)\]
|
||||
|
||||
# AdditionalDependencies
|
||||
<AdditionalDependencies>.*<
|
||||
|
||||
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
|
||||
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
|
||||
|
||||
RegExp\(@?([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|return/.*?/
|
||||
|
||||
# Questionably acceptable forms of `in to`
|
||||
@@ -219,13 +237,15 @@ RegExp\(@?([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|retu
|
||||
# mount
|
||||
\bmount\s+-t\s+(\w+)\s+\g{-1}\b
|
||||
# C types and repeated CSS values
|
||||
\s(auto|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?:\s\g{-1})+\s
|
||||
\s(auto|await|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?:\s\g{-1})+\s
|
||||
# C enum and struct
|
||||
\b(?:enum|struct)\s+(\w+)\s+\g{-1}\b
|
||||
# go templates
|
||||
\s(\w+)\s+\g{-1}\s+\`(?:graphql|inject|json|yaml):
|
||||
# doxygen / javadoc / .net
|
||||
(?:[\\@](?:brief|defgroup|groupname|link|t?param|return|retval)|(?:public|private|\[Parameter(?:\(.+\)|)\])(?:\s+(?:static|override|readonly|required|virtual))*)(?:\s+\{\w+\}|)\s+(\w+)\s+\g{-1}\s
|
||||
# C# getter/setter
|
||||
\s(\w+)\s+\g{-1}\s*\{\s*[gs]et;
|
||||
|
||||
# macOS file path
|
||||
(?:Contents\W+|(?!iOS)/)MacOS\b
|
||||
@@ -289,3 +309,6 @@ St&yle
|
||||
|
||||
# Microsoft Store URLs and product IDs
|
||||
ms-windows-store://\S+
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
|
||||
|
||||
47
.github/actions/spell-check/reject.txt
vendored
@@ -1,23 +1,30 @@
|
||||
^attache$
|
||||
^bellows?$
|
||||
attache
|
||||
aroynt.*
|
||||
bellows?
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^develope$
|
||||
^developement$
|
||||
^developpe
|
||||
^Devers?$
|
||||
^devex
|
||||
^devide
|
||||
^Devinn?[ae]
|
||||
^devisal
|
||||
^devisor
|
||||
^diables?$
|
||||
^oer$
|
||||
.*dnt
|
||||
dependan.*
|
||||
developement
|
||||
developp?e
|
||||
Devers?
|
||||
devex.*
|
||||
devide
|
||||
Devinn?[ae]
|
||||
devisals?
|
||||
devisors?
|
||||
diables?
|
||||
hasta?
|
||||
hastat.*
|
||||
immediatly
|
||||
inisle
|
||||
inital
|
||||
linge
|
||||
oer
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^Teh$
|
||||
^untill$
|
||||
^untilling$
|
||||
^venders?$
|
||||
^wether.*
|
||||
[Ss]pae.*
|
||||
Teh
|
||||
untill
|
||||
untilling
|
||||
venders?
|
||||
wether.*
|
||||
|
||||
2
.github/copilot-instructions.md
vendored
@@ -33,4 +33,4 @@ These are auto-applied based on file location:
|
||||
## Detailed Documentation
|
||||
|
||||
- [Architecture](../doc/devdocs/core/architecture.md)
|
||||
- [Coding Style](../doc/devdocs/development/style.md)
|
||||
- [Coding Style](../doc/devdocs/development/style.md)
|
||||
35
.github/policies/resourceManagement.yml
vendored
@@ -233,6 +233,30 @@ configuration:
|
||||
- addReply:
|
||||
reply: Hi! Thanks for making us aware of the problem. We raised the issue with our internal localization team. This issue should be fixed hopefully in the next version of PowerToys.
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: '\/need-monitor-info'
|
||||
isRegex: True
|
||||
- hasLabel:
|
||||
label: Product-Cursor Wrap
|
||||
- or:
|
||||
- activitySenderHasAssociation:
|
||||
association: Owner
|
||||
- activitySenderHasAssociation:
|
||||
association: Member
|
||||
- activitySenderHasAssociation:
|
||||
association: Collaborator
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
- removeLabel:
|
||||
label: Needs-Team-Response
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- addReply:
|
||||
reply: "To help debug your layout, please run [this script](https://github.com/microsoft/PowerToys/blob/main/src/modules/MouseUtils/CursorWrap/CursorWrapTests/Capture-MonitorLayout.ps1) and attach the generated JSON output to this thread.\n\nThis allows us to better understand the issue and investigate potential fixes."
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
@@ -242,16 +266,5 @@ configuration:
|
||||
- addReply:
|
||||
reply: Hi! Your last comment indicates to our system, that you might want to contribute to this feature/fix this bug. Thank you! Please make us aware on our ["Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769), as we don't see all the comments. <br /><br />_I'm a bot (beep!) so please excuse any mistakes I may make_
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- isAction:
|
||||
action: Opened
|
||||
- bodyContains:
|
||||
pattern: 'Area\(s\) with issue\?\s*\nWorkspaces'
|
||||
isRegex: True
|
||||
then:
|
||||
- addLabel:
|
||||
label: Product-Workspaces
|
||||
description:
|
||||
onFailure:
|
||||
onSuccess:
|
||||
|
||||
324
.github/scripts/telemetry-pr-check.js
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Detects telemetry-event additions/modifications in a pull request and
|
||||
* posts (or updates) a PR comment when telemetry-related changes are found.
|
||||
*
|
||||
* This script is executed by .github/workflows/telemetry-pr-check.yml.
|
||||
* Keep both files aligned when changing trigger behavior, env usage, or messaging.
|
||||
*/
|
||||
|
||||
const fs = require('node:fs');
|
||||
|
||||
const COMMENT_MARKER = '<!-- telemetry-event-check -->';
|
||||
const COMMENT_BODY_WITH_PRIVACY_UPDATE = `${COMMENT_MARKER}
|
||||
THIS IS A TEST | @chatasweetie is testing this functionality
|
||||
Thanks for contributing to PowerToys. This change might include a new or modified telemetry event, and we want to help make sure you can get your data end to end.
|
||||
|
||||
1. Reach out to Jessica (@chatasweetie) to follow up on the next steps to add these telemetry events to our pipelines.`;
|
||||
|
||||
const COMMENT_BODY_WITHOUT_PRIVACY_UPDATE = `${COMMENT_MARKER}
|
||||
THIS IS A TEST | @chatasweetie is testing this functionality
|
||||
Thanks for contributing to PowerToys. This change might include a new or modified telemetry event, and we want to help make sure you can get your data end to end.
|
||||
|
||||
1. Make sure to add your telemetry events to DATA_AND_PRIVACY.md.
|
||||
|
||||
2. Reach out to Jessica (@chatasweetie) to follow up on the next steps to add these telemetry events to our pipelines.`;
|
||||
|
||||
const TELEMETRY_PATH_PATTERNS = [
|
||||
/(^|\/)trace\.(h|hpp|cpp|cs)$/i,
|
||||
/(^|\/)telemetry\//i,
|
||||
/(^|\/)events\/.+event\.cs$/i,
|
||||
/^src\/common\/Telemetry\//i,
|
||||
/^src\/common\/ManagedTelemetry\//i,
|
||||
/^src\/runner\/trace\.(h|cpp)$/i,
|
||||
/^src\/settings-ui\/.+\/Telemetry\//i,
|
||||
];
|
||||
|
||||
const TELEMETRY_LINE_PATTERNS = [
|
||||
/TraceLoggingWriteWrapper\s*\(/,
|
||||
/\bTraceLoggingWrite\s*\(/,
|
||||
/\bTRACELOGGING_DEFINE_PROVIDER\b/,
|
||||
/\bTraceLoggingOptionProjectTelemetry\b/,
|
||||
/\bProjectTelemetryPrivacyDataTag\b/,
|
||||
/\bPROJECT_KEYWORD_MEASURE\b/,
|
||||
/\bRegisterProvider\s*\(/,
|
||||
/\bUnregisterProvider\s*\(/,
|
||||
/\bPowerToysTelemetry\.Log\.WriteEvent\s*\(/,
|
||||
/\bclass\s+\w+\s*:\s*EventBase\s*,\s*IEvent\b/,
|
||||
/\bclass\s+\w+\s*:\s*TelemetryBase\b/,
|
||||
/\bPartA_PrivTags\b/,
|
||||
/\[EventData\]/,
|
||||
/\bEventName\b/,
|
||||
];
|
||||
|
||||
function requireEnv(name) {
|
||||
const value = process.env[name];
|
||||
if (!value) {
|
||||
throw new Error(`Missing required environment variable: ${name}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function validateRepository(repository) {
|
||||
if (!/^[^/]+\/[^/]+$/.test(repository)) {
|
||||
throw new Error(
|
||||
`GITHUB_REPOSITORY must be in owner/repo format, received: ${JSON.stringify(repository)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function readEventPayload(eventPath) {
|
||||
let raw;
|
||||
try {
|
||||
raw = fs.readFileSync(eventPath, 'utf8');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to read event payload at ${eventPath}: ${error.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON from ${eventPath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePullNumber(event) {
|
||||
const fromPullRequest = event?.pull_request?.number;
|
||||
const fromWorkflowDispatch = event?.inputs?.pr_number;
|
||||
const rawPullNumber = fromPullRequest ?? fromWorkflowDispatch;
|
||||
|
||||
if (rawPullNumber === undefined || rawPullNumber === null || rawPullNumber === '') {
|
||||
throw new Error(
|
||||
'Unable to determine pull request number from event payload. Expected pull_request.number or inputs.pr_number.'
|
||||
);
|
||||
}
|
||||
|
||||
const pullNumber = Number.parseInt(String(rawPullNumber), 10);
|
||||
if (!Number.isInteger(pullNumber) || pullNumber <= 0) {
|
||||
throw new Error(`Invalid pull request number: ${JSON.stringify(rawPullNumber)}`);
|
||||
}
|
||||
|
||||
return pullNumber;
|
||||
}
|
||||
|
||||
function isTelemetryPath(filePath) {
|
||||
return TELEMETRY_PATH_PATTERNS.some((pattern) => pattern.test(filePath));
|
||||
}
|
||||
|
||||
function changedLinesFromPatch(patch) {
|
||||
if (!patch) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return patch
|
||||
.split('\n')
|
||||
.filter((line) => {
|
||||
if (line.startsWith('+++') || line.startsWith('---')) {
|
||||
return false;
|
||||
}
|
||||
return line.startsWith('+') || line.startsWith('-');
|
||||
})
|
||||
.map((line) => line.slice(1));
|
||||
}
|
||||
|
||||
function hasTelemetryLineSignal(lines) {
|
||||
return lines.some((line) => TELEMETRY_LINE_PATTERNS.some((pattern) => pattern.test(line)));
|
||||
}
|
||||
|
||||
async function apiRequest(url, method = 'GET', body) {
|
||||
const token = requireEnv('GITHUB_TOKEN');
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: 'application/vnd.github+json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Network error during ${method} ${url}: ${error.message}`);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
const rateLimitReset = response.headers.get('x-ratelimit-reset');
|
||||
const rateLimitHint =
|
||||
response.status === 403 && rateLimitReset
|
||||
? ` (rate limit reset at epoch ${rateLimitReset})`
|
||||
: '';
|
||||
throw new Error(`${method} ${url} failed (${response.status})${rateLimitHint}: ${text}`);
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON response for ${method} ${url}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllPullFiles(apiBaseUrl, repository, pullNumber) {
|
||||
const files = [];
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const url = `${apiBaseUrl}/repos/${repository}/pulls/${pullNumber}/files?per_page=100&page=${page}`;
|
||||
const batch = await apiRequest(url);
|
||||
if (!Array.isArray(batch)) {
|
||||
throw new Error(`Unexpected response while listing PR files on page ${page}.`);
|
||||
}
|
||||
|
||||
if (batch.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
files.push(...batch);
|
||||
|
||||
if (batch.length < 100) {
|
||||
break;
|
||||
}
|
||||
|
||||
page += 1;
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
async function findExistingTelemetryComment(apiBaseUrl, repository, pullNumber) {
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const commentsUrl = `${apiBaseUrl}/repos/${repository}/issues/${pullNumber}/comments?per_page=100&page=${page}`;
|
||||
const comments = await apiRequest(commentsUrl);
|
||||
|
||||
if (!Array.isArray(comments)) {
|
||||
throw new Error(`Unexpected response while listing issue comments on page ${page}.`);
|
||||
}
|
||||
|
||||
const existing = comments.find(
|
||||
(comment) => typeof comment.body === 'string' && comment.body.includes(COMMENT_MARKER)
|
||||
);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (comments.length < 100) {
|
||||
return null;
|
||||
}
|
||||
|
||||
page += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function detectTelemetryChanges(files) {
|
||||
const matches = [];
|
||||
|
||||
for (const file of files) {
|
||||
const filename = file.filename || '';
|
||||
const telemetryPath = isTelemetryPath(filename);
|
||||
const changedLines = changedLinesFromPatch(file.patch);
|
||||
const telemetryLineSignal = hasTelemetryLineSignal(changedLines);
|
||||
|
||||
// Some large diffs omit patch content. If the file path is telemetry-centric,
|
||||
// treat it as a telemetry modification to avoid false negatives.
|
||||
const patchUnavailable = !file.patch && telemetryPath;
|
||||
|
||||
if (telemetryPath || telemetryLineSignal || patchUnavailable) {
|
||||
matches.push({
|
||||
filename,
|
||||
telemetryPath,
|
||||
telemetryLineSignal,
|
||||
patchUnavailable,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
function hasDataAndPrivacyChange(files) {
|
||||
return files.some((file) => {
|
||||
const filename = (file.filename || '').toLowerCase();
|
||||
return filename === 'data_and_privacy.md';
|
||||
});
|
||||
}
|
||||
|
||||
async function upsertPrComment(apiBaseUrl, repository, pullNumber, body) {
|
||||
const existing = await findExistingTelemetryComment(apiBaseUrl, repository, pullNumber);
|
||||
|
||||
if (existing) {
|
||||
const updateUrl = `${apiBaseUrl}/repos/${repository}/issues/comments/${existing.id}`;
|
||||
await apiRequest(updateUrl, 'PATCH', { body });
|
||||
console.log(`Updated existing telemetry comment (id: ${existing.id}).`);
|
||||
return;
|
||||
}
|
||||
|
||||
const createUrl = `${apiBaseUrl}/repos/${repository}/issues/${pullNumber}/comments`;
|
||||
await apiRequest(createUrl, 'POST', { body });
|
||||
console.log('Created telemetry comment on PR.');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const eventPath = requireEnv('GITHUB_EVENT_PATH');
|
||||
const repository = requireEnv('GITHUB_REPOSITORY');
|
||||
const apiBaseUrl = process.env.GITHUB_API_URL || 'https://api.github.com';
|
||||
validateRepository(repository);
|
||||
|
||||
let parsedApiBaseUrl;
|
||||
try {
|
||||
parsedApiBaseUrl = new URL(apiBaseUrl);
|
||||
} catch {
|
||||
throw new Error(`Invalid GITHUB_API_URL: ${JSON.stringify(apiBaseUrl)}`);
|
||||
}
|
||||
|
||||
const event = readEventPayload(eventPath);
|
||||
const pullNumber = resolvePullNumber(event);
|
||||
|
||||
console.log(`Event name: ${process.env.GITHUB_EVENT_NAME || 'unknown'}`);
|
||||
console.log(`Repository: ${repository}`);
|
||||
console.log(`PR number: ${pullNumber}`);
|
||||
|
||||
const files = await getAllPullFiles(parsedApiBaseUrl.origin, repository, pullNumber);
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('No changed files found for PR; skipping telemetry comment update.');
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = detectTelemetryChanges(files);
|
||||
const dataAndPrivacyChanged = hasDataAndPrivacyChange(files);
|
||||
|
||||
console.log(`Scanned ${files.length} changed files.`);
|
||||
console.log(`Telemetry matches found: ${matches.length}.`);
|
||||
console.log(`DATA_AND_PRIVACY.md changed: ${dataAndPrivacyChanged}.`);
|
||||
|
||||
if (matches.length === 0) {
|
||||
console.log('No telemetry-related additions/modifications detected.');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const match of matches) {
|
||||
console.log(
|
||||
`- ${match.filename} (telemetryPath=${match.telemetryPath}, telemetryLineSignal=${match.telemetryLineSignal}, patchUnavailable=${match.patchUnavailable})`
|
||||
);
|
||||
}
|
||||
|
||||
const commentBody = dataAndPrivacyChanged
|
||||
? COMMENT_BODY_WITH_PRIVACY_UPDATE
|
||||
: COMMENT_BODY_WITHOUT_PRIVACY_UPDATE;
|
||||
|
||||
await upsertPrComment(apiBaseUrl, repository, pullNumber, commentBody);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Telemetry PR check failed.');
|
||||
console.error(error instanceof Error ? error.stack || error.message : error);
|
||||
process.exit(1);
|
||||
});
|
||||
30
.github/skills/release-note-generation/SKILL.md
vendored
@@ -1,12 +1,12 @@
|
||||
---
|
||||
name: release-note-generation
|
||||
description: Toolkit for generating PowerToys release notes from GitHub milestone PRs or commit ranges. Use when asked to create release notes, summarize milestone PRs, generate changelog, prepare release documentation, request Copilot reviews for PRs, update README for a new release, manage PR milestones, or collect PRs between commits/tags. Supports PR collection by milestone or commit range, milestone assignment, grouping by label, summarization with external contributor attribution, and README version bumping.
|
||||
description: Toolkit for generating PowerToys release notes from GitHub milestone PRs or commit ranges. Use when asked to create release notes, summarize milestone PRs, generate changelog, prepare release documentation, generate PR review summaries locally for release notes, update README for a new release, manage PR milestones, collect PRs between commits/tags, or prepare release assets (download installers and compute installer hashes).
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
# Release Note Generation Skill
|
||||
|
||||
Generate professional release notes for PowerToys milestones by collecting merged PRs, requesting Copilot code reviews, grouping by label, and producing user-facing summaries.
|
||||
Generate professional release notes for PowerToys milestones by collecting merged PRs, summarizing each PR with the local CLI agent, grouping by label, and producing user-facing summaries.
|
||||
|
||||
## Output Directory
|
||||
|
||||
@@ -26,16 +26,17 @@ Generated Files/ReleaseNotes/
|
||||
|
||||
- Generate release notes for a milestone
|
||||
- Summarize PRs merged in a release
|
||||
- Request Copilot reviews for milestone PRs
|
||||
- Generate per-PR review summaries locally for release-notes copy
|
||||
- Assign milestones to PRs missing them
|
||||
- Collect PRs between two commits/tags
|
||||
- Update README.md for a new version
|
||||
- Prepare GitHub release assets (download installers/symbols + compute hashes)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- GitHub CLI (`gh`) installed and authenticated
|
||||
- MCP Server: github-mcp-server installed
|
||||
- GitHub Copilot code review enabled for the org/repo
|
||||
- **GitHub CLI (`gh`) installed and authenticated** — The collection script uses `gh pr view` and `gh api graphql` to fetch PR metadata and co-author information. Run `gh auth status` to verify; if not logged in, run `gh auth login` first. See [Step 1.0.0](./references/step1-collection.md) for details.
|
||||
- MCP Server: github-mcp-server installed (used to fetch PR diffs/files for the local-agent review step)
|
||||
- For [prepare-release-assets.ps1](./scripts/prepare-release-assets.ps1) only: **Azure CLI** authenticated against the Microsoft tenant (`az login`) with the `azure-devops` extension; access to the `microsoft/Dart` ADO project
|
||||
|
||||
## Required Variables
|
||||
|
||||
@@ -49,6 +50,10 @@ Generated Files/ReleaseNotes/
|
||||
|
||||
```
|
||||
┌────────────────────────────────┐
|
||||
│ 1.0 Verify gh auth + MemberList │
|
||||
└────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────┐
|
||||
│ 1.1 Collect PRs (stable range) │
|
||||
└────────────────────────────────┘
|
||||
↓
|
||||
@@ -61,12 +66,12 @@ Generated Files/ReleaseNotes/
|
||||
└────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────┐
|
||||
│ 3.1 Request Reviews (Copilot) │
|
||||
│ 3.1 Local-agent PR summaries │
|
||||
│ (writes CopilotSummary) │
|
||||
└────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────┐
|
||||
│ 3.2 Refresh PR data │
|
||||
│ (CopilotSummary) │
|
||||
│ 3.2 (Optional) Refresh PR data │
|
||||
└────────────────────────────────┘
|
||||
↓
|
||||
┌────────────────────────────────┐
|
||||
@@ -85,10 +90,11 @@ Generated Files/ReleaseNotes/
|
||||
|
||||
| Step | Action | Details |
|
||||
|------|--------|---------|
|
||||
| 1.0 | Verify prerequisites | `gh auth status` must pass; generate MemberList.md |
|
||||
| 1.1 | Collect PRs | From previous release tag on `stable` branch → `sorted_prs.csv` |
|
||||
| 1.2 | Assign Milestones | Ensure all PRs have correct milestone |
|
||||
| 2.1–2.4 | Label PRs | Auto-suggest + human label low-confidence |
|
||||
| 3.1–3.3 | Reviews & Grouping | Request Copilot reviews → refresh → group by label |
|
||||
| 3.1–3.3 | Reviews & Grouping | Local agent summarizes each PR diff into `CopilotSummary` → (optional refresh) → group by label |
|
||||
| 4.1–4.2 | Summaries & Final | Generate grouped summaries, then consolidate |
|
||||
|
||||
## Detailed workflow docs
|
||||
@@ -109,6 +115,7 @@ Do not read all steps at once—only read the step you are executing.
|
||||
| [group-prs-by-label.ps1](./scripts/group-prs-by-label.ps1) | Group PRs into CSVs |
|
||||
| [collect-or-apply-milestones.ps1](./scripts/collect-or-apply-milestones.ps1) | Assign milestones |
|
||||
| [diff_prs.ps1](./scripts/diff_prs.ps1) | Incremental PR diff |
|
||||
| [prepare-release-assets.ps1](./scripts/prepare-release-assets.ps1) | Download installers + symbols from an ADO build, compute SHA256, emit the "Installer Hashes" markdown table for the GitHub release page |
|
||||
|
||||
## References
|
||||
|
||||
@@ -128,5 +135,6 @@ Do not read all steps at once—only read the step you are executing.
|
||||
|-------|----------|
|
||||
| `gh` command not found | Install GitHub CLI and add to PATH |
|
||||
| No PRs returned | Verify milestone title matches exactly |
|
||||
| Empty CopilotSummary | Request Copilot reviews first, then re-run dump |
|
||||
| Empty `CopilotSummary` for many PRs | Run Step 3.1 (local-agent summaries). Do **not** use `mcp_github_request_copilot_review` from a CLI/coding agent — the GitHub API rejects bot-initiated review requests, so the column will stay empty. |
|
||||
| Many unlabeled PRs | Return to labeling step before grouping |
|
||||
| `prepare-release-assets.ps1` fails with "Failed to acquire ADO access token" | Run `az login` and ensure you have access to the `microsoft/Dart` ADO project |
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
- Added mouse button actions so you can choose what left, right, or middle click does. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Added mouse button actions so you can choose what left, right, or middle click does in [#1234](https://github.com/microsoft/PowerToys/pull/1234) by [@PesBandi](https://github.com/PesBandi)
|
||||
|
||||
- Aligned window styling with current Windows theme for a cleaner look. Thanks [@sadirano](https://github.com/sadirano)!
|
||||
- Aligned window styling with current Windows theme for a cleaner look in [#1235](https://github.com/microsoft/PowerToys/pull/1235) by [@sadirano](https://github.com/sadirano)
|
||||
|
||||
- Ensured screen readers are notified when the selected item in the list changes for better accessibility.
|
||||
- Ensured screen readers are notified when the selected item in the list changes for better accessibility in [#1236](https://github.com/microsoft/PowerToys/pull/1236)
|
||||
|
||||
- Implemented configurable UI test pipeline that can use pre-built official releases instead of building everything from scratch, reducing test execution time from 2+ hours.
|
||||
- Implemented configurable UI test pipeline that can use pre-built official releases instead of building everything from scratch, reducing test execution time from 2+ hours in [#1237](https://github.com/microsoft/PowerToys/pull/1237)
|
||||
|
||||
- Fixed Alt+Left Arrow navigation not working when search box contains text. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed Alt+Left Arrow navigation not working when search box contains text in [#1238](https://github.com/microsoft/PowerToys/pull/1238) by [@jiripolasek](https://github.com/jiripolasek)
|
||||
@@ -1,6 +1,7 @@
|
||||
# Step 1: Collection and Milestones
|
||||
|
||||
## 1.0 To-do
|
||||
- 1.0.0 Verify GitHub CLI authentication (REQUIRED)
|
||||
- 1.0.1 Generate MemberList.md (REQUIRED)
|
||||
- 1.1 Collect PRs
|
||||
- 1.2 Assign Milestones (REQUIRED)
|
||||
@@ -20,6 +21,34 @@
|
||||
|
||||
---
|
||||
|
||||
## 1.0.0 Verify GitHub CLI Authentication (REQUIRED)
|
||||
|
||||
⚠️ **BLOCKING:** The collection script requires an authenticated `gh` CLI to fetch PR metadata and co-author information via GitHub's GraphQL API. Without authentication, PR data and `NeedThanks` attribution will be incomplete.
|
||||
|
||||
### Check authentication status
|
||||
|
||||
```powershell
|
||||
gh auth status
|
||||
```
|
||||
|
||||
**If authenticated:** You'll see `Logged in to github.com account <username>`. Proceed to 1.0.1.
|
||||
|
||||
**If NOT authenticated:** Run the login flow before continuing:
|
||||
|
||||
```powershell
|
||||
# Interactive login (opens browser for OAuth)
|
||||
gh auth login --hostname github.com --web
|
||||
|
||||
# Or use a personal access token
|
||||
gh auth login --with-token <<< "YOUR_GITHUB_TOKEN"
|
||||
```
|
||||
|
||||
**Required scopes:** `repo` (for reading PR data and assigning milestones)
|
||||
|
||||
After login, verify again with `gh auth status` and confirm exit code 0.
|
||||
|
||||
---
|
||||
|
||||
## 1.0.1 Generate MemberList.md (REQUIRED)
|
||||
|
||||
Create `Generated Files/ReleaseNotes/MemberList.md` from the **PowerToys core team** section in [COMMUNITY.md](../../../COMMUNITY.md).
|
||||
@@ -80,6 +109,8 @@ The script detects both merge commits (`Merge pull request #12345`) and squash c
|
||||
**Output** (in `Generated Files/ReleaseNotes/`):
|
||||
- `milestone_prs.json` - raw PR data
|
||||
- `sorted_prs.csv` - sorted PR list with columns: Id, Title, Labels, Author, Url, Body, CopilotSummary, NeedThanks
|
||||
- `Author`: Comma-separated list of all contributors (PR opener + co-authors from commit trailers)
|
||||
- `NeedThanks`: Comma-separated list of external contributors to thank (excludes core team members from MemberList.md). Empty string means no thanks needed.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
# Step 3: Copilot Reviews and Grouping
|
||||
# Step 3: Local Agent Reviews and Grouping
|
||||
|
||||
## 3.0 To-do
|
||||
- 3.1 Request Copilot Reviews (Agent Mode)
|
||||
- 3.2 Refresh PR Data
|
||||
- 3.1 Generate PR Summaries with the Local Agent
|
||||
- 3.2 (Optional) Refresh PR Data
|
||||
- 3.3 Group PRs by Label
|
||||
|
||||
## 3.1 Request Copilot Reviews (Agent Mode)
|
||||
## 3.1 Generate PR Summaries with the Local Agent
|
||||
|
||||
Use MCP tools to request Copilot reviews for all PRs in `Generated Files/ReleaseNotes/sorted_prs.csv`:
|
||||
> ⚠️ **Do not use `mcp_github_request_copilot_review` (or any "request Copilot review" tool that calls the GitHub API).**
|
||||
> When this skill is driven from a CLI / coding agent, the request is made from a bot identity and the GitHub API rejects it ("Bot reviewers cannot be requested"). The PR ends up with no Copilot review and `CopilotSummary` stays empty.
|
||||
>
|
||||
> Instead, **the local agent that is running this skill performs the review itself** and writes the summary directly into `sorted_prs.csv`.
|
||||
|
||||
- Use `mcp_github_request_copilot_review` for each PR ID
|
||||
- Do NOT generate or run scripts for this step
|
||||
For every PR listed in `Generated Files/ReleaseNotes/sorted_prs.csv` whose `CopilotSummary` is empty:
|
||||
|
||||
1. Fetch the PR diff using a tool that does **not** post anything back to GitHub. Any of these works:
|
||||
- `mcp_github_pull_request_read` with `method: get_diff`
|
||||
- `mcp_github_pull_request_read` with `method: get_files` (when the diff is large)
|
||||
- `gh pr diff <PR_NUMBER> --repo microsoft/PowerToys`
|
||||
2. Read the PR title, body, and diff. Produce a 1–3 sentence, user-facing summary in the same style as a Copilot PR review (focus on observable behavior change, not implementation details).
|
||||
3. Write the summary into the `CopilotSummary` column for that PR row in `Generated Files/ReleaseNotes/sorted_prs.csv`. Preserve all other columns and the existing row order.
|
||||
|
||||
**Batching guidance**
|
||||
|
||||
- Process PRs in the order they appear in `sorted_prs.csv`.
|
||||
- Generate summaries for **all** PRs in one pass before continuing to Step 3.3, so the human reviewer can validate them together.
|
||||
- For very large diffs, summarize from `get_files` (filenames + per-file patches) rather than the full diff.
|
||||
- Skip PRs that already have a non-empty `CopilotSummary` (e.g. PRs where a human reviewer already pasted one). Do not overwrite existing summaries.
|
||||
|
||||
**Why not post the summary back to the PR?** Posting a comment from the agent's identity would not be picked up by `dump-prs-since-commit.ps1` (which only matches Copilot bot authors), and it adds noise to the PR. Writing straight into the CSV keeps the artifact self-contained.
|
||||
|
||||
---
|
||||
|
||||
## 3.2 Refresh PR Data
|
||||
## 3.2 (Optional) Refresh PR Data
|
||||
|
||||
Re-run the collection script to capture Copilot review summaries into the `CopilotSummary` column:
|
||||
Only re-run the collection script if PR metadata on GitHub has changed (new labels, retitled PRs, etc.) since Step 1.1. **Skip this step if you only want to preserve the locally generated `CopilotSummary` values from Step 3.1**, because re-running the dump will overwrite the CSV.
|
||||
|
||||
```powershell
|
||||
pwsh ./.github/skills/release-note-generation/scripts/dump-prs-since-commit.ps1 `
|
||||
@@ -24,6 +42,8 @@ pwsh ./.github/skills/release-note-generation/scripts/dump-prs-since-commit.ps1
|
||||
-OutputDir 'Generated Files/ReleaseNotes'
|
||||
```
|
||||
|
||||
If you do refresh, redo Step 3.1 afterwards to repopulate `CopilotSummary`.
|
||||
|
||||
---
|
||||
|
||||
## 3.3 Group PRs by Label
|
||||
@@ -35,3 +55,4 @@ pwsh ./.github/skills/release-note-generation/scripts/group-prs-by-label.ps1 -Cs
|
||||
Creates `Generated Files/ReleaseNotes/grouped_csv/` with one CSV per label combination.
|
||||
|
||||
**Validation:** The `Unlabeled.csv` file should be minimal (ideally empty). If many PRs remain unlabeled, return to Step 2 (see [step2-labeling.md](./step2-labeling.md)).
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ For each CSV in `Generated Files/ReleaseNotes/grouped_csv/`, create a markdown f
|
||||
- Use the “Verb-ed + Scenario + Impact” sentence structure—make readers think, “That’s exactly what I need” or “Yes, that’s an awesome fix.”; The "impact" can be end-user focused (written to convey user excitement) or technical (performance/stability) when user-facing impact is minimal.
|
||||
- If nothing special on impact or unclear impact, mark as needing human summary
|
||||
- Source from Title, Body, and CopilotSummary (prefer CopilotSummary when available)
|
||||
- If the column `NeedThanks` in CSV is `True`, append: `Thanks [@Author](https://github.com/Author)!`
|
||||
- The `NeedThanks` column contains a comma-separated list of external contributor usernames who should be credited (empty = no attribution needed, all authors are core team). For each non-empty `NeedThanks` value, append a `by` attribution that lists **every** contributor, matching GitHub's standard contributor-attribution style: `by [@user1](https://github.com/user1)` for a single contributor, `by [@user1](https://github.com/user1) and [@user2](https://github.com/user2)` for two, or `by [@user1](https://github.com/user1), [@user2](https://github.com/user2), and [@user3](https://github.com/user3)` for three or more. In the final consolidated release notes (Step 4.2), the attribution follows the PR link, e.g. `…in [#1234](url) by [@user](url)`. Do not use "Thanks @user!" phrasing.
|
||||
- Do NOT include PR numbers in bullet lines
|
||||
- Do NOT mention “security” or “privacy” issues, since these are not known and could be leveraged by attackers in earlier versions. Instead, describe the user-facing scenario, usage, or impact.
|
||||
- If confidence < 70%, write: `Human Summary Needed: <PR full link>`
|
||||
@@ -72,13 +72,13 @@ Some items in the Development section may overlap and should be moved to the Mod
|
||||
|
||||
## Advanced Paste
|
||||
|
||||
- Wrapped paste option lists in a single ScrollViewer
|
||||
- Added image input handling for AI-powered transformations
|
||||
- Wrapped paste option lists in a single ScrollViewer in [#5678](https://github.com/microsoft/PowerToys/pull/5678)
|
||||
- Added image input handling for AI-powered transformations in [#5679](https://github.com/microsoft/PowerToys/pull/5679)
|
||||
...
|
||||
|
||||
## Awake
|
||||
|
||||
- Fixed timed mode expiration. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed timed mode expiration in [#5680](https://github.com/microsoft/PowerToys/pull/5680) by [@daverayment](https://github.com/daverayment)
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
@@ -42,30 +42,7 @@ param(
|
||||
[string]$OutputJson = "milestone_prs.json"
|
||||
)
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Dump merged PR information whose merge commits are reachable from EndCommit but not from StartCommit.
|
||||
.DESCRIPTION
|
||||
Uses git rev-list to compute commits in the (StartCommit, EndCommit] range, extracts PR numbers from merge commit messages,
|
||||
queries GitHub (gh CLI) for details, then outputs a CSV.
|
||||
|
||||
PR merge commit messages in PowerToys generally contain patterns like:
|
||||
Merge pull request #12345 from ...
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./dump-prs-since-commit.ps1 -StartCommit 0123abcd -Branch stable
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./dump-prs-since-commit.ps1 -StartCommit 0123abcd -EndCommit 89ef7654 -OutputCsv changes.csv
|
||||
|
||||
.NOTES
|
||||
Requires: gh CLI authenticated; git available in working directory (must be inside PowerToys repo clone).
|
||||
CopilotSummary behavior:
|
||||
- Attempts to locate the latest GitHub Copilot authored review (preferred).
|
||||
- If no review is found, lazily fetches PR comments to look for a Copilot-authored comment.
|
||||
- Normalizes whitespace and strips newlines. Empty when no Copilot activity detected.
|
||||
- Run with -Verbose to see whether the summary came from a 'review' or 'comment' source.
|
||||
#>
|
||||
# (See top-level synopsis above for full documentation)
|
||||
|
||||
function Write-Info($msg) { Write-Host $msg -ForegroundColor Cyan }
|
||||
function Write-Warn($msg) { Write-Host $msg -ForegroundColor Yellow }
|
||||
@@ -151,11 +128,11 @@ catch {
|
||||
}
|
||||
|
||||
Write-Info "Collecting commits between $startSha..$endSha (excluding start, including end)."
|
||||
# Get list of commits reachable from end but not from start.
|
||||
# IMPORTANT: In PowerShell, the .. operator creates a numeric/char range. If $startSha and $endSha look like hex strings,
|
||||
# `$startSha..$endSha` must be passed as a single string argument.
|
||||
$rangeArg = "$startSha..$endSha"
|
||||
$commitList = git rev-list $rangeArg
|
||||
# Get list of commits reachable from end but not from start.
|
||||
# IMPORTANT: In PowerShell, the .. operator creates a numeric/char range. If $startSha and $endSha look like hex strings,
|
||||
# `$startSha..$endSha` must be passed as a single string argument.
|
||||
$rangeArg = "$startSha..$endSha"
|
||||
$commitList = git rev-list $rangeArg
|
||||
|
||||
# Normalize list (filter out empty strings)
|
||||
$normalizedCommits = $commitList | Where-Object { $_ -and $_.Trim() -ne '' }
|
||||
@@ -210,6 +187,63 @@ $prNumbers = $mergeCommits | Select-Object -ExpandProperty Pr -Unique | Sort-Obj
|
||||
Write-Info ("Found {0} unique PRs: {1}" -f $prNumbers.Count, ($prNumbers -join ', '))
|
||||
Write-DebugMsg ("Total merge commits examined: {0}" -f $mergeCommits.Count)
|
||||
|
||||
# Build a map of PR number → list of commit SHAs (for co-author extraction)
|
||||
$prToCommits = @{}
|
||||
foreach ($mc in $mergeCommits) {
|
||||
if (-not $prToCommits.ContainsKey($mc.Pr)) {
|
||||
$prToCommits[$mc.Pr] = @()
|
||||
}
|
||||
$prToCommits[$mc.Pr] += $mc.Sha
|
||||
}
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get all authors (including co-authors) for a set of commits via GitHub GraphQL API.
|
||||
.DESCRIPTION
|
||||
Uses the Commit.authors field in GitHub's GraphQL API which natively includes
|
||||
co-authors (from Co-authored-by trailers). Returns GitHub usernames (login)
|
||||
without any email parsing — GitHub resolves the association for us.
|
||||
|
||||
NOTE: For squash merges this captures all co-authors correctly because GitHub
|
||||
preserves Co-authored-by trailers in the squash commit. For traditional merge
|
||||
commits, only the merger's author is returned — co-authors on individual PR
|
||||
commits are not traversed. This is acceptable because PowerToys primarily uses
|
||||
squash merging.
|
||||
#>
|
||||
function Get-CommitAuthors {
|
||||
param(
|
||||
[string[]]$CommitShas,
|
||||
[string]$RepoFullName = "microsoft/PowerToys"
|
||||
)
|
||||
$parts = $RepoFullName -split '/'
|
||||
$owner = $parts[0]
|
||||
$repoName = $parts[1]
|
||||
$allAuthors = @()
|
||||
|
||||
foreach ($sha in $CommitShas) {
|
||||
try {
|
||||
$query = "{ repository(owner: `"$owner`", name: `"$repoName`") { object(expression: `"$sha`") { ... on Commit { authors(first: 20) { nodes { user { login } name } } } } } }"
|
||||
$result = gh api graphql -f query="$query" 2>$null | ConvertFrom-Json
|
||||
$nodes = $result.data.repository.object.authors.nodes
|
||||
if ($nodes) {
|
||||
foreach ($node in $nodes) {
|
||||
if ($node.user -and $node.user.login) {
|
||||
$allAuthors += $node.user.login
|
||||
} else {
|
||||
# User without a GitHub account (rare) — use display name as fallback
|
||||
Write-DebugMsg "Commit $sha has an author without GitHub account: $($node.name)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-DebugMsg "GraphQL authors query failed for commit ${sha}: $_"
|
||||
}
|
||||
}
|
||||
|
||||
return $allAuthors | Select-Object -Unique
|
||||
}
|
||||
|
||||
# Query GitHub for each PR
|
||||
$prDetails = @()
|
||||
function Get-CopilotSummaryFromPrJson {
|
||||
@@ -307,22 +341,45 @@ foreach ($pr in $prNumbers) {
|
||||
$bodyValue = if ($json.body) { ($json.body -replace "`r", '') -replace "`n", ' ' } else { '' }
|
||||
$bodyValue = $bodyValue -replace '\s+', ' '
|
||||
|
||||
# Determine if author needs thanks (not in member list)
|
||||
# Collect all contributors: PR author + co-authors from commit messages
|
||||
$authorLogin = $json.author.login
|
||||
$needThanks = $true
|
||||
if ($memberList.Count -gt 0 -and $authorLogin) {
|
||||
$needThanks = -not ($memberList -contains $authorLogin)
|
||||
$allContributors = @($authorLogin)
|
||||
|
||||
# Extract all authors (including co-authors) from associated commits via GitHub GraphQL API
|
||||
if ($prToCommits.ContainsKey([int]$pr)) {
|
||||
$commitAuthors = Get-CommitAuthors -CommitShas $prToCommits[[int]$pr] -RepoFullName $Repo
|
||||
if ($commitAuthors) {
|
||||
$allContributors += $commitAuthors
|
||||
}
|
||||
}
|
||||
|
||||
# Deduplicate contributors (case-insensitive)
|
||||
$allContributors = $allContributors | Where-Object { $_ } | Sort-Object -Unique
|
||||
|
||||
# Filter to only external contributors (not in member list) for thanks
|
||||
$externalContributors = @()
|
||||
if ($memberList.Count -gt 0) {
|
||||
$externalContributors = $allContributors | Where-Object { -not ($memberList -contains $_) }
|
||||
} else {
|
||||
$externalContributors = $allContributors
|
||||
}
|
||||
|
||||
# Author column: all contributors (comma-separated)
|
||||
$authorField = ($allContributors -join ', ')
|
||||
|
||||
# NeedThanks column: comma-separated list of external contributors who
|
||||
# deserve thanks attribution. Empty string means no thanks needed.
|
||||
$needThanksField = ($externalContributors -join ', ')
|
||||
|
||||
$prDetails += [PSCustomObject]@{
|
||||
Id = $json.number
|
||||
Title = $json.title
|
||||
Labels = $labelNames
|
||||
Author = $authorLogin
|
||||
Author = $authorField
|
||||
Url = $json.url
|
||||
Body = $bodyValue
|
||||
CopilotSummary = $copilot.Summary
|
||||
NeedThanks = $needThanks
|
||||
NeedThanks = $needThanksField
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
||||
334
.github/skills/release-note-generation/scripts/prepare-release-assets.ps1
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Prepares the binary assets for a PowerToys GitHub release: downloads the
|
||||
four installers (per-user/per-machine x x64/arm64) and the symbol archives
|
||||
from an ADO pipeline build, computes SHA256 hashes, and emits the
|
||||
"Installer Hashes" markdown table.
|
||||
|
||||
.DESCRIPTION
|
||||
Given an ADO Dart pipeline build id (e.g. from
|
||||
https://microsoft.visualstudio.com/Dart/_build/results?buildId=NNN),
|
||||
downloads the four installer EXEs and the per-arch symbol zips into a
|
||||
single per-version folder, then writes a hashes.md alongside them with a
|
||||
markdown table ready to paste into the GitHub release notes.
|
||||
|
||||
Requires: az login (Azure CLI authenticated), az devops extension.
|
||||
|
||||
.EXAMPLE
|
||||
.\prepare-release-assets.ps1 -BuildId 145505247
|
||||
.\prepare-release-assets.ps1 -BuildId 145505247 -OutputFolder D:\Releases
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$BuildId,
|
||||
|
||||
[string]$OutputFolder = "$env:USERPROFILE\Downloads",
|
||||
|
||||
[string]$Organization = "https://dev.azure.com/microsoft",
|
||||
[string]$Project = "Dart",
|
||||
|
||||
[string]$GitHubRepo = "microsoft/PowerToys"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:AZURE_CORE_NO_PROMPT = "true"
|
||||
|
||||
# --- Helpers -----------------------------------------------------------------
|
||||
|
||||
# Invoke an `az` CLI command and capture stderr in $script:LastAzError so
|
||||
# callers can surface the underlying message (expired login, blocked extension,
|
||||
# tenant policy, ...) instead of swallowing it with `2>$null`.
|
||||
function Invoke-Az {
|
||||
$tmpErr = [System.IO.Path]::GetTempFileName()
|
||||
try {
|
||||
$output = & az @args 2>$tmpErr
|
||||
# Get-Content -Raw returns $null for an empty file, and calling .Trim()
|
||||
# on $null throws under $ErrorActionPreference = 'Stop' -- which would
|
||||
# turn every successful (no-stderr) az call into a fatal error. Guard
|
||||
# explicitly so $script:LastAzError is always a (possibly empty) string.
|
||||
$rawErr = Get-Content $tmpErr -Raw -ErrorAction SilentlyContinue
|
||||
$script:LastAzError = if ($null -eq $rawErr) { '' } else { $rawErr.Trim() }
|
||||
return $output
|
||||
}
|
||||
finally {
|
||||
Remove-Item $tmpErr -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# Build an ADO artifact download URL from scratch instead of regex-replacing
|
||||
# the URL returned by `az pipelines runs artifact list`. Preserves any other
|
||||
# query parameters and only swaps `format` and `subPath`, so we don't break if
|
||||
# the upstream URL shape ever changes.
|
||||
function Get-ArtifactDownloadUrl {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$BaseUrl,
|
||||
[Parameter(Mandatory)][string]$SubPath,
|
||||
[Parameter(Mandatory)][ValidateSet('file', 'zip')][string]$Format
|
||||
)
|
||||
$encodedSubPath = [Uri]::EscapeDataString($SubPath)
|
||||
$idx = $BaseUrl.IndexOf('?')
|
||||
if ($idx -lt 0) {
|
||||
return "${BaseUrl}?format=${Format}&subPath=${encodedSubPath}"
|
||||
}
|
||||
$base = $BaseUrl.Substring(0, $idx)
|
||||
$kept = $BaseUrl.Substring($idx + 1) -split '&' | Where-Object {
|
||||
$_ -and -not ($_ -match '^(format|subPath)=')
|
||||
}
|
||||
$kept = @($kept) + @("format=$Format", "subPath=$encodedSubPath")
|
||||
return "${base}?$($kept -join '&')"
|
||||
}
|
||||
|
||||
# Download a single ADO artifact file with bearer auth and a small retry/backoff
|
||||
# loop. A transient network blip on a ~200 MB installer or symbol zip otherwise
|
||||
# aborts the entire release-prep run.
|
||||
function Invoke-AdoDownload {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Url,
|
||||
[Parameter(Mandatory)][string]$DestPath,
|
||||
[Parameter(Mandatory)][string]$Token,
|
||||
[int]$MaxAttempts = 3
|
||||
)
|
||||
$lastError = $null
|
||||
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.Headers.Add("Authorization", "Bearer $Token")
|
||||
try {
|
||||
$webClient.DownloadFile($Url, $DestPath)
|
||||
return
|
||||
}
|
||||
catch {
|
||||
$lastError = $_
|
||||
if (Test-Path $DestPath) {
|
||||
Remove-Item $DestPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
if ($attempt -lt $MaxAttempts) {
|
||||
$backoffSec = [int][Math]::Pow(2, $attempt) # 2, 4, 8 ...
|
||||
Write-Host " Attempt $attempt failed: $($_.Exception.Message). Retrying in ${backoffSec}s..." -ForegroundColor Yellow
|
||||
Start-Sleep -Seconds $backoffSec
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$webClient.Dispose()
|
||||
}
|
||||
}
|
||||
throw "Download failed after $MaxAttempts attempts. Last error: $($lastError.Exception.Message)`nURL: $Url"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Work around broken az extensions: if the default extension dir has
|
||||
# inaccessible files, redirect to a clean directory.
|
||||
$defaultExtDir = "$env:USERPROFILE\.azure\cliextensions"
|
||||
if (-not $env:AZURE_EXTENSION_DIR -and (Test-Path $defaultExtDir)) {
|
||||
$broken = Get-ChildItem "$defaultExtDir\*\*.dist-info" -Directory -ErrorAction SilentlyContinue | Where-Object {
|
||||
try { [System.IO.Directory]::GetFiles($_.FullName) | Out-Null; $false } catch { $true }
|
||||
}
|
||||
if ($broken) {
|
||||
$cleanDir = "$env:USERPROFILE\.azure\cliextensions_clean"
|
||||
Write-Host " Detected broken az extension, redirecting to $cleanDir" -ForegroundColor Yellow
|
||||
$env:AZURE_EXTENSION_DIR = $cleanDir
|
||||
if (-not (Test-Path $cleanDir)) { New-Item -ItemType Directory -Path $cleanDir -Force | Out-Null }
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure azure-devops extension is installed
|
||||
$ext = Invoke-Az extension list --query "[?name=='azure-devops']" -o tsv
|
||||
if (-not $ext) {
|
||||
Write-Host "Installing azure-devops extension..." -ForegroundColor Yellow
|
||||
Invoke-Az extension add --name azure-devops --yes | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Failed to install azure-devops extension. (az: $script:LastAzError)"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Configure az devops defaults
|
||||
Invoke-Az devops configure --defaults organization=$Organization project=$Project | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Failed to configure az devops defaults. (az: $script:LastAzError)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Step 1: Get build info to determine version ---
|
||||
Write-Host "Fetching build $BuildId info..." -ForegroundColor Cyan
|
||||
$buildJson = Invoke-Az pipelines build show --id $BuildId --output json
|
||||
if (-not $buildJson) {
|
||||
Write-Error "Could not fetch build $BuildId. Are you logged in (az login)? (az: $script:LastAzError)"
|
||||
exit 1
|
||||
}
|
||||
$build = $buildJson | ConvertFrom-Json
|
||||
|
||||
$versionParam = $build.templateParameters.VersionNumber
|
||||
if (-not $versionParam) {
|
||||
Write-Error "Could not determine version from build $BuildId"
|
||||
exit 1
|
||||
}
|
||||
Write-Host " Version: $versionParam" -ForegroundColor DarkGray
|
||||
|
||||
# --- Step 2: Get artifact metadata once ---
|
||||
Write-Host "Fetching artifact metadata..." -ForegroundColor Cyan
|
||||
$artifactsJson = Invoke-Az pipelines runs artifact list --run-id $BuildId --output json
|
||||
if (-not $artifactsJson) {
|
||||
Write-Error "Could not list artifacts for build $BuildId. (az: $script:LastAzError)"
|
||||
exit 1
|
||||
}
|
||||
$artifacts = $artifactsJson | ConvertFrom-Json
|
||||
|
||||
# --- Step 3: Prepare destination folder ---
|
||||
$destFolder = Join-Path $OutputFolder "PowerToys-v$versionParam"
|
||||
if (-not (Test-Path $destFolder)) {
|
||||
New-Item -ItemType Directory -Path $destFolder -Force | Out-Null
|
||||
}
|
||||
Write-Host " Destination: $destFolder" -ForegroundColor DarkGray
|
||||
|
||||
# --- Step 4: Get an ADO access token once ---
|
||||
$token = Invoke-Az account get-access-token --resource "499b84ac-1321-427f-aa17-267ca6975798" --query accessToken -o tsv
|
||||
if (-not $token) {
|
||||
Write-Error "Failed to acquire ADO access token. Run 'az login' first. (az: $script:LastAzError)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Step 5: Define the four installers to download ---
|
||||
$targets = @(
|
||||
[pscustomobject]@{ Description = "Per user - x64"; Scope = "perUser"; Arch = "x64"; Artifact = "build-x64-Release"; FileName = "PowerToysUserSetup-$versionParam-x64.exe" }
|
||||
[pscustomobject]@{ Description = "Per user - ARM64"; Scope = "perUser"; Arch = "arm64"; Artifact = "build-arm64-Release"; FileName = "PowerToysUserSetup-$versionParam-arm64.exe" }
|
||||
[pscustomobject]@{ Description = "Machine wide - x64"; Scope = "perMachine"; Arch = "x64"; Artifact = "build-x64-Release"; FileName = "PowerToysSetup-$versionParam-x64.exe" }
|
||||
[pscustomobject]@{ Description = "Machine wide - ARM64"; Scope = "perMachine"; Arch = "arm64"; Artifact = "build-arm64-Release"; FileName = "PowerToysSetup-$versionParam-arm64.exe" }
|
||||
)
|
||||
|
||||
# --- Step 6: Download each installer (skip if already present) ---
|
||||
foreach ($t in $targets) {
|
||||
$destPath = Join-Path $destFolder $t.FileName
|
||||
|
||||
if (Test-Path $destPath) {
|
||||
$sizeMB = [math]::Round((Get-Item $destPath).Length / 1MB, 1)
|
||||
Write-Host "[skip] $($t.FileName) already exists ($sizeMB MB)" -ForegroundColor DarkGray
|
||||
continue
|
||||
}
|
||||
|
||||
$artifact = $artifacts | Where-Object { $_.name -eq $t.Artifact }
|
||||
if (-not $artifact) {
|
||||
Write-Error "Artifact '$($t.Artifact)' not found in build $BuildId. Available: $(($artifacts | ForEach-Object name) -join ', ')"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$fileUrl = Get-ArtifactDownloadUrl -BaseUrl $artifact.resource.downloadUrl -SubPath "/$($t.FileName)" -Format file
|
||||
|
||||
Write-Host "Downloading $($t.FileName) ..." -ForegroundColor Cyan
|
||||
try {
|
||||
Invoke-AdoDownload -Url $fileUrl -DestPath $destPath -Token $token
|
||||
}
|
||||
catch {
|
||||
Write-Error "Download failed for $($t.FileName): $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$sizeMB = [math]::Round((Get-Item $destPath).Length / 1MB, 1)
|
||||
Write-Host " Saved ($sizeMB MB)" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# --- Step 6b: Download symbols (one zip per arch) ---
|
||||
$symbolTargets = @(
|
||||
[pscustomobject]@{ Arch = "x64"; Artifact = "build-x64-Release"; SubPath = "/symbols-x64" }
|
||||
[pscustomobject]@{ Arch = "arm64"; Artifact = "build-arm64-Release"; SubPath = "/symbols-arm64" }
|
||||
)
|
||||
|
||||
foreach ($s in $symbolTargets) {
|
||||
$finalZip = Join-Path $destFolder "symbols-$($s.Arch).zip"
|
||||
if (Test-Path $finalZip) {
|
||||
$sizeMB = [math]::Round((Get-Item $finalZip).Length / 1MB, 1)
|
||||
Write-Host "[skip] symbols-$($s.Arch).zip already exists ($sizeMB MB)" -ForegroundColor DarkGray
|
||||
continue
|
||||
}
|
||||
|
||||
$artifact = $artifacts | Where-Object { $_.name -eq $s.Artifact }
|
||||
if (-not $artifact) {
|
||||
Write-Error "Artifact '$($s.Artifact)' not found in build $BuildId."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Symbols are downloaded as a folder => keep format=zip and append subPath
|
||||
$symbolsUrl = Get-ArtifactDownloadUrl -BaseUrl $artifact.resource.downloadUrl -SubPath $s.SubPath -Format zip
|
||||
|
||||
$tmpZip = Join-Path ([System.IO.Path]::GetTempPath()) ("ptsym-$($s.Arch)-$([Guid]::NewGuid().ToString('N')).zip")
|
||||
$tmpExtract = Join-Path ([System.IO.Path]::GetTempPath()) ("ptsym-$($s.Arch)-$([Guid]::NewGuid().ToString('N'))")
|
||||
$stageRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("ptsym-stage-$([Guid]::NewGuid().ToString('N'))")
|
||||
|
||||
try {
|
||||
Write-Host "Downloading symbols-$($s.Arch).zip ..." -ForegroundColor Cyan
|
||||
try {
|
||||
Invoke-AdoDownload -Url $symbolsUrl -DestPath $tmpZip -Token $token
|
||||
}
|
||||
catch {
|
||||
Write-Error "Symbols download failed for $($s.Arch): $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host " Extracting..." -ForegroundColor DarkGray
|
||||
Expand-Archive -Path $tmpZip -DestinationPath $tmpExtract -Force
|
||||
|
||||
# Walk down while the current dir holds exactly one subfolder and no files.
|
||||
$current = Get-Item $tmpExtract
|
||||
while ($true) {
|
||||
$children = Get-ChildItem -LiteralPath $current.FullName -Force
|
||||
$subDirs = @($children | Where-Object { $_.PSIsContainer })
|
||||
$files = @($children | Where-Object { -not $_.PSIsContainer })
|
||||
if ($subDirs.Count -eq 1 -and $files.Count -eq 0) {
|
||||
$current = $subDirs[0]
|
||||
}
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# Stage to a folder named symbols-<arch> so the zip extracts to that name.
|
||||
$stageInner = Join-Path $stageRoot "symbols-$($s.Arch)"
|
||||
New-Item -ItemType Directory -Path $stageInner -Force | Out-Null
|
||||
Get-ChildItem -LiteralPath $current.FullName -Force | ForEach-Object {
|
||||
Copy-Item -LiteralPath $_.FullName -Destination $stageInner -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Host " Repacking to $finalZip ..." -ForegroundColor DarkGray
|
||||
if (Test-Path $finalZip) { Remove-Item $finalZip -Force }
|
||||
Compress-Archive -Path "$stageInner\*" -DestinationPath $finalZip -CompressionLevel Optimal
|
||||
|
||||
$sizeMB = [math]::Round((Get-Item $finalZip).Length / 1MB, 1)
|
||||
Write-Host " Saved symbols-$($s.Arch).zip ($sizeMB MB)" -ForegroundColor Green
|
||||
}
|
||||
catch {
|
||||
# Don't leave a half-built zip behind if anything in the pipeline blew up.
|
||||
if (Test-Path $finalZip) { Remove-Item $finalZip -Force -ErrorAction SilentlyContinue }
|
||||
throw
|
||||
}
|
||||
finally {
|
||||
Remove-Item $tmpZip -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $tmpExtract -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $stageRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# --- Step 7: Compute SHA256 and build markdown ---
|
||||
Write-Host "`nComputing SHA256 hashes..." -ForegroundColor Cyan
|
||||
|
||||
$sb = [System.Text.StringBuilder]::new()
|
||||
[void]$sb.AppendLine("## Installer Hashes")
|
||||
[void]$sb.AppendLine("")
|
||||
[void]$sb.AppendLine("| Description | Filename | sha256 hash |")
|
||||
[void]$sb.AppendLine("| --- | --- | --- |")
|
||||
|
||||
foreach ($t in $targets) {
|
||||
$destPath = Join-Path $destFolder $t.FileName
|
||||
$hash = (Get-FileHash -Path $destPath -Algorithm SHA256).Hash.ToUpper()
|
||||
[void]$sb.AppendLine("| $($t.Description) | $($t.FileName) | $hash |")
|
||||
Write-Host " $($t.FileName) $hash" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
$markdown = $sb.ToString()
|
||||
$mdPath = Join-Path $destFolder "hashes.md"
|
||||
Set-Content -Path $mdPath -Value $markdown -Encoding UTF8
|
||||
|
||||
Write-Host "`nMarkdown written to: $mdPath" -ForegroundColor Green
|
||||
Write-Host "`n----- Installer Hashes -----`n" -ForegroundColor Yellow
|
||||
Write-Host $markdown
|
||||
|
||||
Write-Host "Draft a new GitHub release at: https://github.com/$GitHubRepo/releases/new?tag=v$versionParam" -ForegroundColor Green
|
||||
21
.github/skills/winmd-api-search/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
192
.github/skills/winmd-api-search/SKILL.md
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
name: winmd-api-search
|
||||
description: 'Find and explore Windows desktop APIs. Use when building features that need platform capabilities — camera, file access, notifications, UI controls, AI/ML, sensors, networking, etc. Discovers the right API for a task and retrieves full type details (methods, properties, events, enumeration values).'
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
# WinMD API Search
|
||||
|
||||
This skill helps you find the right Windows API for any capability and get its full details. It searches a local cache of all WinMD metadata from:
|
||||
|
||||
- **Windows Platform SDK** — all `Windows.*` WinRT APIs (always available, no restore needed)
|
||||
- **WinAppSDK / WinUI** — bundled as a baseline in the cache generator (always available, no restore needed)
|
||||
- **NuGet packages** — any additional packages in restored projects that contain `.winmd` files
|
||||
- **Project-output WinMD** — class libraries (C++/WinRT, C#) that produce `.winmd` as build output
|
||||
|
||||
Even on a fresh clone with no restore or build, you still get full Platform SDK + WinAppSDK coverage.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User wants to build a feature and you need to find which API provides that capability
|
||||
- User asks "how do I do X?" where X involves a platform feature (camera, files, notifications, sensors, AI, etc.)
|
||||
- You need the exact methods, properties, events, or enumeration values of a type before writing code
|
||||
- You're unsure which control, class, or interface to use for a UI or system task
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **.NET SDK 8.0 or later** — required to build the cache generator. Install from [dotnet.microsoft.com](https://dotnet.microsoft.com/download) if not available.
|
||||
|
||||
## Cache Setup (Required Before First Use)
|
||||
|
||||
All query and search commands read from a local JSON cache. **You must generate the cache before running any queries.**
|
||||
|
||||
```powershell
|
||||
# All projects in the repo (recommended for first run)
|
||||
.\.github\skills\winmd-api-search\scripts\Update-WinMdCache.ps1
|
||||
|
||||
# Single project
|
||||
.\.github\skills\winmd-api-search\scripts\Update-WinMdCache.ps1 -ProjectDir <project-folder>
|
||||
```
|
||||
|
||||
No project restore or build is needed for baseline coverage (Platform SDK + WinAppSDK). For additional NuGet packages, the project needs `dotnet restore` (which generates `project.assets.json`) or a `packages.config` file.
|
||||
|
||||
Cache is stored at `Generated Files\winmd-cache\`, deduplicated per-package+version.
|
||||
|
||||
### What gets indexed
|
||||
|
||||
| Source | When available |
|
||||
|--------|----------------|
|
||||
| Windows Platform SDK | Always (reads from local SDK install) |
|
||||
| WinAppSDK (latest) | Always (bundled as baseline in cache generator) |
|
||||
| WinAppSDK Runtime | When installed on the system (detected via `Get-AppxPackage`) |
|
||||
| Project NuGet packages | After `dotnet restore` or with `packages.config` |
|
||||
| Project-output `.winmd` | After project build (class libraries that produce WinMD) |
|
||||
|
||||
> **Note:** This cache directory should be in `.gitignore` — it's generated, not source.
|
||||
|
||||
## How to Use
|
||||
|
||||
Pick the path that matches the situation:
|
||||
|
||||
---
|
||||
|
||||
### Discover — "I don't know which API to use"
|
||||
|
||||
The user describes a capability in their own words. You need to find the right API.
|
||||
|
||||
**0. Ensure the cache exists**
|
||||
|
||||
If the cache hasn't been generated yet, run `Update-WinMdCache.ps1` first — see [Cache Setup](#cache-setup-required-before-first-use) above.
|
||||
|
||||
**1. Translate user language → search keywords**
|
||||
|
||||
Map the user's daily language to programming terms. Try multiple variations:
|
||||
|
||||
| User says | Search keywords to try (in order) |
|
||||
|-----------|-----------------------------------|
|
||||
| "take a picture" | `camera`, `capture`, `photo`, `MediaCapture` |
|
||||
| "load from disk" | `file open`, `picker`, `FileOpen`, `StorageFile` |
|
||||
| "describe what's in it" | `image description`, `Vision`, `Recognition` |
|
||||
| "show a popup" | `dialog`, `flyout`, `popup`, `ContentDialog` |
|
||||
| "drag and drop" | `drag`, `drop`, `DragDrop` |
|
||||
| "save settings" | `settings`, `ApplicationData`, `LocalSettings` |
|
||||
|
||||
Start with simple everyday words. If results are weak or irrelevant, try the more technical variation.
|
||||
|
||||
**2. Run searches**
|
||||
|
||||
```powershell
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action search -Query "<keyword>"
|
||||
```
|
||||
|
||||
This returns ranked namespaces with top matching types and the **JSON file path**.
|
||||
|
||||
If results have **low scores (below 60) or are irrelevant**, fall back to searching online documentation:
|
||||
|
||||
1. Use web search to find the right API on Microsoft Learn, for example:
|
||||
- `site:learn.microsoft.com/uwp/api <capability keywords>` for `Windows.*` APIs
|
||||
- `site:learn.microsoft.com/windows/windows-app-sdk/api/winrt <capability keywords>` for `Microsoft.*` WinAppSDK APIs
|
||||
2. Read the documentation pages to identify which type matches the user's requirement.
|
||||
3. Once you know the type name, come back and use `-Action members` or `-Action enums` to get the exact local signatures.
|
||||
|
||||
**3. Read the JSON to choose the right API**
|
||||
|
||||
Read the file at the path(s) from the top results. The JSON has all types in that namespace — full members, signatures, parameters, return types, enumeration values.
|
||||
|
||||
Read and decide which types and members fit the user's requirement.
|
||||
|
||||
**4. Look up official documentation for context**
|
||||
|
||||
The cache contains only signatures — no descriptions or usage guidance. For explanations, examples, and remarks, look up the type on Microsoft Learn:
|
||||
|
||||
| Namespace prefix | Documentation base URL |
|
||||
|-----------------|----------------------|
|
||||
| `Windows.*` | `https://learn.microsoft.com/uwp/api/{fully.qualified.typename}` |
|
||||
| `Microsoft.*` (WinAppSDK) | `https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/{fully.qualified.typename}` |
|
||||
|
||||
For example, `Microsoft.UI.Xaml.Controls.NavigationView` maps to:
|
||||
`https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.navigationview`
|
||||
|
||||
**5. Use the API knowledge to answer or write code**
|
||||
|
||||
---
|
||||
|
||||
### Lookup — "I know the API, show me the details"
|
||||
|
||||
You already know (or suspect) the type or namespace name. Go direct:
|
||||
|
||||
```powershell
|
||||
# Get all members of a known type
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action members -TypeName "Microsoft.UI.Xaml.Controls.NavigationView"
|
||||
|
||||
# Get enum values
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action enums -TypeName "Microsoft.UI.Xaml.Visibility"
|
||||
|
||||
# List all types in a namespace
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action types -Namespace "Microsoft.UI.Xaml.Controls"
|
||||
|
||||
# Browse namespaces
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action namespaces -Filter "Microsoft.UI"
|
||||
```
|
||||
|
||||
If you need full detail beyond what `-Action members` shows, use `-Action search` to get the JSON file path, then read the JSON file directly.
|
||||
|
||||
---
|
||||
|
||||
### Other Commands
|
||||
|
||||
```powershell
|
||||
# List cached projects
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action projects
|
||||
|
||||
# List packages for a project
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action packages
|
||||
|
||||
# Show stats
|
||||
.\.github\skills\winmd-api-search\scripts\Invoke-WinMdQuery.ps1 -Action stats
|
||||
```
|
||||
|
||||
> If only one project is cached, `-Project` is auto-selected.
|
||||
> If multiple projects exist, add `-Project <name>` (use `-Action projects` to see available names).
|
||||
> In scan mode, manifest names include a short hash suffix to avoid collisions; you can pass the base project name without the suffix if it's unambiguous.
|
||||
|
||||
## Search Scoring
|
||||
|
||||
The search ranks type names and member names against your query:
|
||||
|
||||
| Score | Match type | Example |
|
||||
|-------|-----------|---------|
|
||||
| 100 | Exact name | `Button` → `Button` |
|
||||
| 80 | Starts with | `Navigation` → `NavigationView` |
|
||||
| 60 | Contains | `Dialog` → `ContentDialog` |
|
||||
| 50 | PascalCase initials | `ASB` → `AutoSuggestBox` |
|
||||
| 40 | Multi-keyword AND | `navigation item` → `NavigationViewItem` |
|
||||
| 20 | Fuzzy character match | `NavVw` → `NavigationView` |
|
||||
|
||||
Results are grouped by namespace. Higher-scored namespaces appear first.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Fix |
|
||||
|-------|-----|
|
||||
| "Cache not found" | Run `Update-WinMdCache.ps1` |
|
||||
| "Multiple projects cached" | Add `-Project <name>` |
|
||||
| "Namespace not found" | Use `-Action namespaces` to list available ones |
|
||||
| "Type not found" | Use fully qualified name (e.g., `Microsoft.UI.Xaml.Controls.Button`) |
|
||||
| Stale after NuGet update | Re-run `Update-WinMdCache.ps1` |
|
||||
| Cache in git history | Add `Generated Files/` to `.gitignore` |
|
||||
|
||||
## References
|
||||
|
||||
- [Windows Platform SDK API reference](https://learn.microsoft.com/uwp/api/) — documentation for `Windows.*` namespaces
|
||||
- [Windows App SDK API reference](https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/) — documentation for `Microsoft.*` WinAppSDK namespaces
|
||||
505
.github/skills/winmd-api-search/scripts/Invoke-WinMdQuery.ps1
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Query WinMD API metadata from cached JSON files.
|
||||
|
||||
.DESCRIPTION
|
||||
Reads pre-built JSON cache of WinMD types, members, and namespaces.
|
||||
The cache is organized per-package (deduplicated) with project manifests
|
||||
that map each project to its referenced packages.
|
||||
|
||||
Supports listing namespaces, types, members, searching, enum value lookup,
|
||||
and listing cached projects/packages.
|
||||
|
||||
.PARAMETER Action
|
||||
The query action to perform:
|
||||
- projects : List cached projects
|
||||
- packages : List packages for a project
|
||||
- stats : Show aggregate statistics for a project
|
||||
- namespaces : List all namespaces (optional -Filter prefix)
|
||||
- types : List types in a namespace (-Namespace required)
|
||||
- members : List members of a type (-TypeName required)
|
||||
- search : Search types and members by name (-Query required)
|
||||
- enums : List enum values (-TypeName required)
|
||||
|
||||
.PARAMETER Project
|
||||
Project name to query. Auto-selected if only one project is cached.
|
||||
Use -Action projects to list available projects.
|
||||
|
||||
.PARAMETER Namespace
|
||||
Namespace to query types from (used with -Action types).
|
||||
|
||||
.PARAMETER TypeName
|
||||
Full type name e.g. "Microsoft.UI.Xaml.Controls.Button" (used with -Action members, enums).
|
||||
|
||||
.PARAMETER Query
|
||||
Search query string (used with -Action search).
|
||||
|
||||
.PARAMETER Filter
|
||||
Optional prefix filter for namespaces (used with -Action namespaces).
|
||||
|
||||
.PARAMETER CacheDir
|
||||
Path to the winmd-cache directory. Defaults to "Generated Files\winmd-cache"
|
||||
relative to the workspace root.
|
||||
|
||||
.PARAMETER MaxResults
|
||||
Maximum number of results to return for search. Defaults to 30.
|
||||
|
||||
.EXAMPLE
|
||||
.\Invoke-WinMdQuery.ps1 -Action projects
|
||||
.\Invoke-WinMdQuery.ps1 -Action packages -Project BlankWinUI
|
||||
.\Invoke-WinMdQuery.ps1 -Action stats -Project BlankWinUI
|
||||
.\Invoke-WinMdQuery.ps1 -Action namespaces -Filter "Microsoft.UI"
|
||||
.\Invoke-WinMdQuery.ps1 -Action types -Namespace "Microsoft.UI.Xaml.Controls"
|
||||
.\Invoke-WinMdQuery.ps1 -Action members -TypeName "Microsoft.UI.Xaml.Controls.Button"
|
||||
.\Invoke-WinMdQuery.ps1 -Action search -Query "NavigationView"
|
||||
.\Invoke-WinMdQuery.ps1 -Action enums -TypeName "Microsoft.UI.Xaml.Visibility"
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet('projects', 'packages', 'stats', 'namespaces', 'types', 'members', 'search', 'enums')]
|
||||
[string]$Action,
|
||||
|
||||
[string]$Project,
|
||||
[string]$Namespace,
|
||||
[string]$TypeName,
|
||||
[string]$Query,
|
||||
[string]$Filter,
|
||||
[string]$CacheDir,
|
||||
[int]$MaxResults = 30
|
||||
)
|
||||
|
||||
# ─── Resolve cache directory ─────────────────────────────────────────────────
|
||||
|
||||
if (-not $CacheDir) {
|
||||
# Convention: skill lives at .github/skills/winmd-api-search/scripts/
|
||||
# so workspace root is 4 levels up from $PSScriptRoot.
|
||||
$scriptDir = $PSScriptRoot
|
||||
$root = (Resolve-Path (Join-Path $scriptDir '..\..\..\..')).Path
|
||||
$CacheDir = Join-Path $root 'Generated Files\winmd-cache'
|
||||
}
|
||||
|
||||
if (-not (Test-Path $CacheDir)) {
|
||||
Write-Error "Cache not found at: $CacheDir`nRun: .\Update-WinMdCache.ps1 (from .github\skills\winmd-api-search\scripts\)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ─── Project resolution helpers ──────────────────────────────────────────────
|
||||
|
||||
function Get-CachedProjects {
|
||||
$projectsDir = Join-Path $CacheDir 'projects'
|
||||
if (-not (Test-Path $projectsDir)) { return @() }
|
||||
Get-ChildItem $projectsDir -Filter '*.json' | ForEach-Object { $_.BaseName }
|
||||
}
|
||||
|
||||
function Resolve-ProjectManifest {
|
||||
param([string]$Name)
|
||||
|
||||
$projectsDir = Join-Path $CacheDir 'projects'
|
||||
if (-not (Test-Path $projectsDir)) {
|
||||
Write-Error "No projects cached. Run Update-WinMdCache.ps1 first."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($Name) {
|
||||
$path = Join-Path $projectsDir "$Name.json"
|
||||
if (-not (Test-Path $path)) {
|
||||
# Scan mode appends a hash suffix -- try prefix match
|
||||
$matching = @(Get-ChildItem $projectsDir -Filter "${Name}_*.json" -ErrorAction SilentlyContinue)
|
||||
if ($matching.Count -eq 1) {
|
||||
return Get-Content $matching[0].FullName -Raw | ConvertFrom-Json
|
||||
}
|
||||
if ($matching.Count -gt 1) {
|
||||
$names = ($matching | ForEach-Object { $_.BaseName }) -join ', '
|
||||
Write-Error "Multiple projects match '$Name'. Specify the full name: $names"
|
||||
exit 1
|
||||
}
|
||||
$available = (Get-CachedProjects) -join ', '
|
||||
Write-Error "Project '$Name' not found. Available: $available"
|
||||
exit 1
|
||||
}
|
||||
return Get-Content $path -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
# Auto-select if only one project
|
||||
$manifests = Get-ChildItem $projectsDir -Filter '*.json' -ErrorAction SilentlyContinue
|
||||
if ($manifests.Count -eq 0) {
|
||||
Write-Error "No projects cached. Run Update-WinMdCache.ps1 first."
|
||||
exit 1
|
||||
}
|
||||
if ($manifests.Count -eq 1) {
|
||||
return Get-Content $manifests[0].FullName -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
$available = ($manifests | ForEach-Object { $_.BaseName }) -join ', '
|
||||
Write-Error "Multiple projects cached -- use -Project to specify. Available: $available"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function Get-PackageCacheDirs {
|
||||
param($Manifest)
|
||||
$dirs = @()
|
||||
foreach ($pkg in $Manifest.packages) {
|
||||
$dir = Join-Path (Join-Path (Join-Path $CacheDir 'packages') $pkg.id) $pkg.version
|
||||
if (Test-Path $dir) {
|
||||
$dirs += $dir
|
||||
}
|
||||
}
|
||||
return $dirs
|
||||
}
|
||||
|
||||
# ─── Action: projects ────────────────────────────────────────────────────────
|
||||
|
||||
function Show-Projects {
|
||||
$projects = Get-CachedProjects
|
||||
if ($projects.Count -eq 0) {
|
||||
Write-Output "No projects cached."
|
||||
return
|
||||
}
|
||||
Write-Output "Cached projects ($($projects.Count)):"
|
||||
foreach ($p in $projects) {
|
||||
$manifest = Get-Content (Join-Path (Join-Path $CacheDir 'projects') "$p.json") -Raw | ConvertFrom-Json
|
||||
$pkgCount = $manifest.packages.Count
|
||||
Write-Output " $p ($pkgCount package(s))"
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Action: packages ────────────────────────────────────────────────────────
|
||||
|
||||
function Show-Packages {
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
Write-Output "Packages for project '$($manifest.projectName)' ($($manifest.packages.Count)):"
|
||||
foreach ($pkg in $manifest.packages) {
|
||||
$metaPath = Join-Path (Join-Path (Join-Path (Join-Path $CacheDir 'packages') $pkg.id) $pkg.version) 'meta.json'
|
||||
if (Test-Path $metaPath) {
|
||||
$meta = Get-Content $metaPath -Raw | ConvertFrom-Json
|
||||
Write-Output " $($pkg.id)@$($pkg.version) -- $($meta.totalTypes) types, $($meta.totalMembers) members"
|
||||
} else {
|
||||
Write-Output " $($pkg.id)@$($pkg.version) -- (cache missing)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Action: stats ───────────────────────────────────────────────────────────
|
||||
|
||||
function Show-Stats {
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
$totalTypes = 0
|
||||
$totalMembers = 0
|
||||
$totalNamespaces = 0
|
||||
$totalWinMd = 0
|
||||
|
||||
foreach ($pkg in $manifest.packages) {
|
||||
$metaPath = Join-Path (Join-Path (Join-Path (Join-Path $CacheDir 'packages') $pkg.id) $pkg.version) 'meta.json'
|
||||
if (Test-Path $metaPath) {
|
||||
$meta = Get-Content $metaPath -Raw | ConvertFrom-Json
|
||||
$totalTypes += $meta.totalTypes
|
||||
$totalMembers += $meta.totalMembers
|
||||
$totalNamespaces += $meta.totalNamespaces
|
||||
$totalWinMd += $meta.winMdFiles.Count
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "WinMD Index Statistics -- $($manifest.projectName)"
|
||||
Write-Output "======================================"
|
||||
Write-Output " Packages: $($manifest.packages.Count)"
|
||||
Write-Output " Namespaces: $totalNamespaces (may overlap across packages)"
|
||||
Write-Output " Types: $totalTypes"
|
||||
Write-Output " Members: $totalMembers"
|
||||
Write-Output " WinMD files: $totalWinMd"
|
||||
}
|
||||
|
||||
# ─── Action: namespaces ──────────────────────────────────────────────────────
|
||||
|
||||
function Get-Namespaces {
|
||||
param([string]$Prefix)
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
$dirs = Get-PackageCacheDirs -Manifest $manifest
|
||||
$allNs = @()
|
||||
|
||||
foreach ($dir in $dirs) {
|
||||
$nsFile = Join-Path $dir 'namespaces.json'
|
||||
if (Test-Path $nsFile) {
|
||||
$allNs += (Get-Content $nsFile -Raw | ConvertFrom-Json)
|
||||
}
|
||||
}
|
||||
|
||||
$allNs = $allNs | Sort-Object -Unique
|
||||
if ($Prefix) {
|
||||
$allNs = $allNs | Where-Object { $_ -like "$Prefix*" }
|
||||
}
|
||||
$allNs | ForEach-Object { Write-Output $_ }
|
||||
}
|
||||
|
||||
# ─── Action: types ───────────────────────────────────────────────────────────
|
||||
|
||||
function Get-TypesInNamespace {
|
||||
param([string]$Ns)
|
||||
if (-not $Ns) {
|
||||
Write-Error "-Namespace is required for 'types' action."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
$dirs = Get-PackageCacheDirs -Manifest $manifest
|
||||
$safeFile = $Ns.Replace('.', '_') + '.json'
|
||||
$found = $false
|
||||
$seen = @{}
|
||||
|
||||
foreach ($dir in $dirs) {
|
||||
$filePath = Join-Path $dir "types\$safeFile"
|
||||
if (-not (Test-Path $filePath)) { continue }
|
||||
$found = $true
|
||||
$types = Get-Content $filePath -Raw | ConvertFrom-Json
|
||||
foreach ($t in $types) {
|
||||
if ($seen.ContainsKey($t.fullName)) { continue }
|
||||
$seen[$t.fullName] = $true
|
||||
Write-Output "$($t.kind) $($t.fullName)$(if ($t.baseType) { " : $($t.baseType)" } else { '' })"
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $found) {
|
||||
Write-Error "Namespace not found: $Ns"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Action: members ─────────────────────────────────────────────────────────
|
||||
|
||||
function Get-MembersOfType {
|
||||
param([string]$FullName)
|
||||
if (-not $FullName) {
|
||||
Write-Error "-TypeName is required for 'members' action."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$lastDot = $FullName.LastIndexOf('.')
|
||||
if ($lastDot -lt 0) {
|
||||
Write-Error "-TypeName must include a namespace (for example: 'MyNamespace.MyType'). Provided: $FullName"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$ns = $FullName.Substring(0, $lastDot)
|
||||
$safeFile = $ns.Replace('.', '_') + '.json'
|
||||
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
$dirs = Get-PackageCacheDirs -Manifest $manifest
|
||||
|
||||
foreach ($dir in $dirs) {
|
||||
$filePath = Join-Path $dir "types\$safeFile"
|
||||
if (-not (Test-Path $filePath)) { continue }
|
||||
|
||||
$types = Get-Content $filePath -Raw | ConvertFrom-Json
|
||||
$type = $types | Where-Object { $_.fullName -eq $FullName }
|
||||
if (-not $type) { continue }
|
||||
|
||||
Write-Output "$($type.kind) $($type.fullName)"
|
||||
if ($type.baseType) { Write-Output " Extends: $($type.baseType)" }
|
||||
Write-Output ""
|
||||
foreach ($m in $type.members) {
|
||||
Write-Output " [$($m.kind)] $($m.signature)"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Write-Error "Type not found: $FullName"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ─── Action: search ──────────────────────────────────────────────────────────
|
||||
# Ranks namespaces by best match score on type names and member names.
|
||||
# Outputs: ranked namespaces with top matching types and the JSON file path.
|
||||
# The agent can then read the JSON file to inspect all members intelligently.
|
||||
|
||||
function Search-WinMd {
|
||||
param([string]$SearchQuery, [int]$Max)
|
||||
if (-not $SearchQuery) {
|
||||
Write-Error "-Query is required for 'search' action."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
$dirs = Get-PackageCacheDirs -Manifest $manifest
|
||||
|
||||
# Collect: namespace -> { bestScore, matchingTypes[], filePath }
|
||||
$nsResults = @{}
|
||||
|
||||
foreach ($dir in $dirs) {
|
||||
$nsFile = Join-Path $dir 'namespaces.json'
|
||||
if (-not (Test-Path $nsFile)) { continue }
|
||||
$nsList = Get-Content $nsFile -Raw | ConvertFrom-Json
|
||||
|
||||
foreach ($n in $nsList) {
|
||||
$safeFile = $n.Replace('.', '_') + '.json'
|
||||
$filePath = Join-Path $dir "types\$safeFile"
|
||||
if (-not (Test-Path $filePath)) { continue }
|
||||
|
||||
$types = Get-Content $filePath -Raw | ConvertFrom-Json
|
||||
foreach ($t in $types) {
|
||||
$typeScore = Get-MatchScore -Name $t.name -FullName $t.fullName -Query $SearchQuery
|
||||
|
||||
# Also search member names for matches
|
||||
$bestMemberScore = 0
|
||||
$matchingMember = $null
|
||||
if ($t.members) {
|
||||
foreach ($m in $t.members) {
|
||||
$memberName = $m.name
|
||||
$mScore = Get-MatchScore -Name $memberName -FullName "$($t.fullName).$memberName" -Query $SearchQuery
|
||||
if ($mScore -gt $bestMemberScore) {
|
||||
$bestMemberScore = $mScore
|
||||
$matchingMember = $m.signature
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$score = [Math]::Max($typeScore, $bestMemberScore)
|
||||
if ($score -le 0) { continue }
|
||||
|
||||
if (-not $nsResults.ContainsKey($n)) {
|
||||
$nsResults[$n] = @{ BestScore = 0; Types = @(); FilePaths = @() }
|
||||
}
|
||||
$entry = $nsResults[$n]
|
||||
if ($score -gt $entry.BestScore) { $entry.BestScore = $score }
|
||||
if ($entry.FilePaths -notcontains $filePath) {
|
||||
$entry.FilePaths += $filePath
|
||||
}
|
||||
|
||||
if ($typeScore -ge $bestMemberScore) {
|
||||
$entry.Types += @{ Text = "$($t.kind) $($t.fullName) [$typeScore]"; Score = $typeScore }
|
||||
} else {
|
||||
$entry.Types += @{ Text = "$($t.kind) $($t.fullName) -> $matchingMember [$bestMemberScore]"; Score = $bestMemberScore }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($nsResults.Count -eq 0) {
|
||||
Write-Output "No results found for: $SearchQuery"
|
||||
return
|
||||
}
|
||||
|
||||
$ranked = $nsResults.GetEnumerator() |
|
||||
Sort-Object { $_.Value.BestScore } -Descending |
|
||||
Select-Object -First $Max
|
||||
|
||||
foreach ($r in $ranked) {
|
||||
$ns = $r.Key
|
||||
$info = $r.Value
|
||||
Write-Output "[$($info.BestScore)] $ns"
|
||||
foreach ($fp in $info.FilePaths) {
|
||||
Write-Output " File: $fp"
|
||||
}
|
||||
# Show top 5 highest-scoring matching types in this namespace
|
||||
$info.Types | Sort-Object { $_.Score } -Descending |
|
||||
Select-Object -First 5 |
|
||||
ForEach-Object { Write-Output " $($_.Text)" }
|
||||
Write-Output ""
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Search scoring ──────────────────────────────────────────────────────────
|
||||
# Simple ranked scoring on type names. Higher = better.
|
||||
# 100 = exact name 80 = starts-with 60 = substring
|
||||
# 50 = PascalCase 40 = multi-keyword 20 = fuzzy subsequence
|
||||
|
||||
function Get-MatchScore {
|
||||
param([string]$Name, [string]$FullName, [string]$Query)
|
||||
|
||||
$q = $Query.Trim()
|
||||
if (-not $q) { return 0 }
|
||||
|
||||
if ($Name -eq $q) { return 100 }
|
||||
if ($Name -like "$q*") { return 80 }
|
||||
if ($Name -like "*$q*" -or $FullName -like "*$q*") { return 60 }
|
||||
|
||||
$initials = ($Name.ToCharArray() | Where-Object { [char]::IsUpper($_) }) -join ''
|
||||
if ($initials.Length -ge 2 -and $initials -like "*$q*") { return 50 }
|
||||
|
||||
$words = $q -split '\s+' | Where-Object { $_.Length -gt 0 }
|
||||
if ($words.Count -gt 1) {
|
||||
$allFound = $true
|
||||
foreach ($w in $words) {
|
||||
if ($Name -notlike "*$w*" -and $FullName -notlike "*$w*") {
|
||||
$allFound = $false
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($allFound) { return 40 }
|
||||
}
|
||||
|
||||
if (Test-FuzzySubsequence -Text $Name -Pattern $q) { return 20 }
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function Test-FuzzySubsequence {
|
||||
param([string]$Text, [string]$Pattern)
|
||||
$ti = 0
|
||||
$tLower = $Text.ToLowerInvariant()
|
||||
$pLower = $Pattern.ToLowerInvariant()
|
||||
foreach ($ch in $pLower.ToCharArray()) {
|
||||
$idx = $tLower.IndexOf($ch, $ti)
|
||||
if ($idx -lt 0) { return $false }
|
||||
$ti = $idx + 1
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
# ─── Action: enums ───────────────────────────────────────────────────────────
|
||||
|
||||
function Get-EnumValues {
|
||||
param([string]$FullName)
|
||||
if (-not $FullName) {
|
||||
Write-Error "-TypeName is required for 'enums' action."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$lastDot = $FullName.LastIndexOf('.')
|
||||
if ($lastDot -lt 1) {
|
||||
Write-Error "-TypeName must be a fully-qualified type name including namespace, e.g. 'Namespace.TypeName'. Provided: $FullName"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$ns = $FullName.Substring(0, $lastDot)
|
||||
$safeFile = $ns.Replace('.', '_') + '.json'
|
||||
|
||||
$manifest = Resolve-ProjectManifest -Name $Project
|
||||
$dirs = Get-PackageCacheDirs -Manifest $manifest
|
||||
|
||||
foreach ($dir in $dirs) {
|
||||
$filePath = Join-Path $dir "types\$safeFile"
|
||||
if (-not (Test-Path $filePath)) { continue }
|
||||
|
||||
$types = Get-Content $filePath -Raw | ConvertFrom-Json
|
||||
$type = $types | Where-Object { $_.fullName -eq $FullName }
|
||||
if (-not $type) { continue }
|
||||
|
||||
if ($type.kind -ne 'Enum') {
|
||||
Write-Error "$FullName is not an Enum (kind: $($type.kind))"
|
||||
exit 1
|
||||
}
|
||||
Write-Output "Enum $($type.fullName)"
|
||||
if ($type.enumValues) {
|
||||
$type.enumValues | ForEach-Object { Write-Output " $_" }
|
||||
} else {
|
||||
Write-Output " (no values)"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Write-Error "Type not found: $FullName"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ─── Dispatch ─────────────────────────────────────────────────────────────────
|
||||
|
||||
switch ($Action) {
|
||||
'projects' { Show-Projects }
|
||||
'packages' { Show-Packages }
|
||||
'stats' { Show-Stats }
|
||||
'namespaces' { Get-Namespaces -Prefix $Filter }
|
||||
'types' { Get-TypesInNamespace -Ns $Namespace }
|
||||
'members' { Get-MembersOfType -FullName $TypeName }
|
||||
'search' { Search-WinMd -SearchQuery $Query -Max $MaxResults }
|
||||
'enums' { Get-EnumValues -FullName $TypeName }
|
||||
}
|
||||
208
.github/skills/winmd-api-search/scripts/Update-WinMdCache.ps1
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generate or refresh the WinMD cache for the Agent Skill.
|
||||
|
||||
.DESCRIPTION
|
||||
Builds and runs the standalone cache generator to export cached JSON files
|
||||
from all WinMD metadata found in project NuGet packages and Windows SDK.
|
||||
|
||||
The cache is per-package+version: if two projects reference the same
|
||||
package at the same version, the WinMD data is parsed once and shared.
|
||||
|
||||
Supports single project or recursive scan of an entire repo.
|
||||
|
||||
.PARAMETER ProjectDir
|
||||
Path to a project directory (contains .csproj/.vcxproj), or a project file itself.
|
||||
Defaults to scanning the workspace root.
|
||||
|
||||
.PARAMETER Scan
|
||||
Recursively discover all .csproj/.vcxproj files under ProjectDir.
|
||||
|
||||
.PARAMETER OutputDir
|
||||
Path to the cache output directory. Defaults to "Generated Files\winmd-cache".
|
||||
|
||||
.EXAMPLE
|
||||
.\Update-WinMdCache.ps1
|
||||
.\Update-WinMdCache.ps1 -ProjectDir BlankWinUI
|
||||
.\Update-WinMdCache.ps1 -Scan -ProjectDir .
|
||||
.\Update-WinMdCache.ps1 -ProjectDir "src\MyApp\MyApp.csproj"
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ProjectDir,
|
||||
[switch]$Scan,
|
||||
[string]$OutputDir = 'Generated Files\winmd-cache'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Convention: skill lives at .github/skills/winmd-api-search/scripts/
|
||||
# so workspace root is 4 levels up from $PSScriptRoot.
|
||||
$root = (Resolve-Path (Join-Path $PSScriptRoot '..\..\..\..')).Path
|
||||
$generatorProj = Join-Path (Join-Path $PSScriptRoot 'cache-generator') 'CacheGenerator.csproj'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# WinAppSDK version detection -- look only at the repo root folder (no recursion)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function Get-WinAppSdkVersionFromDirectoryPackagesProps {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Extract Microsoft.WindowsAppSDK version from a Directory.Packages.props
|
||||
(Central Package Management) at the repo root.
|
||||
#>
|
||||
param([string]$RepoRoot)
|
||||
$propsFile = Join-Path $RepoRoot 'Directory.Packages.props'
|
||||
if (-not (Test-Path $propsFile)) { return $null }
|
||||
try {
|
||||
[xml]$xml = Get-Content $propsFile -Raw
|
||||
$node = $xml.SelectNodes('//PackageVersion') |
|
||||
Where-Object { $_.Include -eq 'Microsoft.WindowsAppSDK' } |
|
||||
Select-Object -First 1
|
||||
if ($node) { return $node.Version }
|
||||
} catch {
|
||||
Write-Verbose "Could not parse $propsFile : $_"
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-WinAppSdkVersionFromPackagesConfig {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Extract Microsoft.WindowsAppSDK version from a packages.config at the repo root.
|
||||
#>
|
||||
param([string]$RepoRoot)
|
||||
$configFile = Join-Path $RepoRoot 'packages.config'
|
||||
if (-not (Test-Path $configFile)) { return $null }
|
||||
try {
|
||||
[xml]$xml = Get-Content $configFile -Raw
|
||||
$node = $xml.SelectNodes('//package') |
|
||||
Where-Object { $_.id -eq 'Microsoft.WindowsAppSDK' } |
|
||||
Select-Object -First 1
|
||||
if ($node) { return $node.version }
|
||||
} catch {
|
||||
Write-Verbose "Could not parse $configFile : $_"
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Try Directory.Packages.props first (CPM), then packages.config
|
||||
$winAppSdkVersion = Get-WinAppSdkVersionFromDirectoryPackagesProps -RepoRoot $root
|
||||
if (-not $winAppSdkVersion) {
|
||||
$winAppSdkVersion = Get-WinAppSdkVersionFromPackagesConfig -RepoRoot $root
|
||||
}
|
||||
if ($winAppSdkVersion) {
|
||||
Write-Host "Detected WinAppSDK version from repo: $winAppSdkVersion" -ForegroundColor Cyan
|
||||
} else {
|
||||
Write-Host "No WinAppSDK version found at repo root; will use latest (Version=*)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Default: if no ProjectDir, scan the workspace root
|
||||
if (-not $ProjectDir) {
|
||||
$ProjectDir = $root
|
||||
$Scan = $true
|
||||
}
|
||||
|
||||
Push-Location $root
|
||||
|
||||
try {
|
||||
# Detect installed .NET SDK -- require >= 8.0, prefer stable over preview
|
||||
$dotnetSdks = dotnet --list-sdks 2>$null
|
||||
$bestMajor = $dotnetSdks |
|
||||
Where-Object { $_ -notmatch 'preview|rc|alpha|beta' } |
|
||||
ForEach-Object { if ($_ -match '^(\d+)\.') { [int]$Matches[1] } } |
|
||||
Where-Object { $_ -ge 8 } |
|
||||
Sort-Object -Descending |
|
||||
Select-Object -First 1
|
||||
|
||||
# Fall back to preview SDKs if no stable SDK found
|
||||
if (-not $bestMajor) {
|
||||
$bestMajor = $dotnetSdks |
|
||||
ForEach-Object { if ($_ -match '^(\d+)\.') { [int]$Matches[1] } } |
|
||||
Where-Object { $_ -ge 8 } |
|
||||
Sort-Object -Descending |
|
||||
Select-Object -First 1
|
||||
}
|
||||
|
||||
if (-not $bestMajor) {
|
||||
Write-Error "No .NET SDK >= 8.0 found. Install from https://dotnet.microsoft.com/download"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$targetFramework = "net$bestMajor.0"
|
||||
Write-Host "Using .NET SDK: $targetFramework" -ForegroundColor Cyan
|
||||
|
||||
# Build MSBuild properties -- pass detected WinAppSDK version when available
|
||||
$sdkVersionProp = ''
|
||||
if ($winAppSdkVersion) {
|
||||
$sdkVersionProp = "-p:WinAppSdkVersion=$winAppSdkVersion"
|
||||
}
|
||||
|
||||
Write-Host "Building cache generator..." -ForegroundColor Cyan
|
||||
$restoreArgs = @($generatorProj, "-p:TargetFramework=$targetFramework", '--nologo', '-v', 'q')
|
||||
if ($sdkVersionProp) { $restoreArgs += $sdkVersionProp }
|
||||
dotnet restore @restoreArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Restore failed"
|
||||
exit 1
|
||||
}
|
||||
$buildArgs = @($generatorProj, '-c', 'Release', '--nologo', '-v', 'q', "-p:TargetFramework=$targetFramework", '--no-restore')
|
||||
if ($sdkVersionProp) { $buildArgs += $sdkVersionProp }
|
||||
dotnet build @buildArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Build failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Run the built executable directly (avoids dotnet run target framework mismatch issues)
|
||||
$generatorDir = Join-Path $PSScriptRoot 'cache-generator'
|
||||
$exePath = Join-Path $generatorDir "bin\Release\$targetFramework\CacheGenerator.exe"
|
||||
if (-not (Test-Path $exePath)) {
|
||||
# Fallback: try dll with dotnet
|
||||
$dllPath = Join-Path $generatorDir "bin\Release\$targetFramework\CacheGenerator.dll"
|
||||
if (Test-Path $dllPath) {
|
||||
$exePath = $null
|
||||
} else {
|
||||
Write-Error "Built executable not found at: $exePath"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$runArgs = @()
|
||||
if ($Scan) {
|
||||
$runArgs += '--scan'
|
||||
}
|
||||
|
||||
# Detect installed WinAppSDK runtime via Get-AppxPackage (the WindowsApps
|
||||
# folder is ACL-restricted so C# cannot enumerate it directly).
|
||||
# WinMD files are architecture-independent metadata, so pick whichever arch
|
||||
# matches the current OS to ensure the package is present.
|
||||
$osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
|
||||
$runtimePkg = Get-AppxPackage -Name 'Microsoft.WindowsAppRuntime.*' -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -notmatch 'CBS' -and $_.Architecture -eq $osArch } |
|
||||
Sort-Object -Property Version -Descending |
|
||||
Select-Object -First 1
|
||||
if ($runtimePkg -and $runtimePkg.InstallLocation -and (Test-Path $runtimePkg.InstallLocation)) {
|
||||
Write-Host "Detected WinAppSDK runtime: $($runtimePkg.Name) v$($runtimePkg.Version)" -ForegroundColor Cyan
|
||||
$runArgs += '--winappsdk-runtime'
|
||||
$runArgs += $runtimePkg.InstallLocation
|
||||
}
|
||||
|
||||
$runArgs += $ProjectDir
|
||||
$runArgs += $OutputDir
|
||||
|
||||
Write-Host "Exporting WinMD cache..." -ForegroundColor Cyan
|
||||
if ($exePath) {
|
||||
& $exePath @runArgs
|
||||
} else {
|
||||
dotnet $dllPath @runArgs
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Cache export failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Cache updated at: $OutputDir" -ForegroundColor Green
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
29
.github/skills/winmd-api-search/scripts/cache-generator/CacheGenerator.csproj
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<!-- Default fallback; Update-WinMdCache.ps1 overrides via -p:TargetFramework=net{X}.0 -->
|
||||
<TargetFramework Condition="'$(TargetFramework)' == ''">net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<!-- System.Reflection.Metadata is inbox in net9.0+, only needed for net8.0 -->
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="System.Reflection.Metadata" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Baseline WinAppSDK packages: downloaded during restore so the cache generator
|
||||
can always index WinAppSDK APIs, even if the target project hasn't been restored.
|
||||
ExcludeAssets="all" means they're downloaded but don't affect this tool's build.
|
||||
|
||||
When the repo has a known version (passed via -p:WinAppSdkVersion=X.Y.Z from
|
||||
Update-WinMdCache.ps1), prefer that version to avoid unnecessary NuGet downloads.
|
||||
Falls back to Version="*" (latest) on fresh clones with no restore.
|
||||
-->
|
||||
<ItemGroup Condition="'$(WinAppSdkVersion)' != ''">
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="$(WinAppSdkVersion)" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(WinAppSdkVersion)' == ''">
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="*" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
3
.github/skills/winmd-api-search/scripts/cache-generator/Directory.Build.props
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<Project>
|
||||
<!-- Isolate this standalone tool from the repo-level build configuration -->
|
||||
</Project>
|
||||
3
.github/skills/winmd-api-search/scripts/cache-generator/Directory.Build.targets
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<Project>
|
||||
<!-- Isolate this standalone tool from the repo-level build targets -->
|
||||
</Project>
|
||||
3
.github/skills/winmd-api-search/scripts/cache-generator/Directory.Packages.props
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<Project>
|
||||
<!-- Isolate this standalone tool from the repo-level Central Package Management -->
|
||||
</Project>
|
||||
1222
.github/skills/winmd-api-search/scripts/cache-generator/Program.cs
vendored
Normal file
165
.github/skills/wpf-to-winui3-migration/SKILL.md
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
name: wpf-to-winui3-migration
|
||||
description: Guide for migrating PowerToys modules from WPF to WinUI 3 (Windows App SDK). Use when asked to migrate WPF code, convert WPF XAML to WinUI, replace System.Windows namespaces with Microsoft.UI.Xaml, update Dispatcher to DispatcherQueue, replace DynamicResource with ThemeResource, migrate imaging APIs from System.Windows.Media.Imaging to Windows.Graphics.Imaging, convert WPF Window to WinUI Window, migrate .resx to .resw resources, migrate custom Observable/RelayCommand to CommunityToolkit.Mvvm source generators, handle WPF-UI (Lepo) to WinUI native control migration, or fix installer/build pipeline issues after migration. Keywords: WPF, WinUI, WinUI3, migration, porting, convert, namespace, XAML, Dispatcher, DispatcherQueue, imaging, BitmapImage, Window, ContentDialog, ThemeResource, DynamicResource, ResourceLoader, resw, resx, CommunityToolkit, ObservableProperty, WPF-UI, SizeToContent, AppWindow, SoftwareBitmap.
|
||||
license: Complete terms in LICENSE.txt
|
||||
---
|
||||
|
||||
# WPF to WinUI 3 Migration Skill
|
||||
|
||||
Migrate PowerToys modules from WPF (`System.Windows.*`) to WinUI 3 (`Microsoft.UI.Xaml.*` / Windows App SDK). Based on patterns validated in the ImageResizer module migration.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Migrate a PowerToys module from WPF to WinUI 3
|
||||
- Convert WPF XAML files to WinUI 3 XAML
|
||||
- Replace `System.Windows` namespaces with `Microsoft.UI.Xaml`
|
||||
- Migrate `Dispatcher` usage to `DispatcherQueue`
|
||||
- Migrate custom `Observable`/`RelayCommand` to CommunityToolkit.Mvvm source generators
|
||||
- Replace WPF-UI (Lepo) controls with native WinUI 3 controls
|
||||
- Convert imaging code from `System.Windows.Media.Imaging` to `Windows.Graphics.Imaging`
|
||||
- Handle WPF `Window` vs WinUI `Window` differences (sizing, positioning, SizeToContent)
|
||||
- Migrate resource files from `.resx` to `.resw` with `ResourceLoader`
|
||||
- Fix installer/build pipeline issues after WinUI 3 migration
|
||||
- Update project files, NuGet packages, and signing config
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Visual Studio 2022 17.4+
|
||||
- Windows App SDK NuGet package (`Microsoft.WindowsAppSDK`)
|
||||
- .NET 8+ with `net8.0-windows10.0.19041.0` TFM
|
||||
- Windows 10 1803+ (April 2018 Update or newer)
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Recommended Order
|
||||
|
||||
1. **Project file** — Update TFM, NuGet packages, set `<UseWinUI>true</UseWinUI>`
|
||||
2. **Data models and business logic** — No UI dependencies, migrate first
|
||||
3. **MVVM framework** — Replace custom Observable/RelayCommand with CommunityToolkit.Mvvm
|
||||
4. **Resource strings** — Migrate `.resx` → `.resw`, introduce `ResourceLoaderInstance`
|
||||
5. **Services and utilities** — Replace `System.Windows` types, async-ify imaging code
|
||||
6. **ViewModels** — Update Dispatcher usage, binding patterns
|
||||
7. **Views/Pages** — Starting from leaf pages with fewest dependencies
|
||||
8. **Main page / shell** — Last, since it depends on everything
|
||||
9. **App.xaml / startup code** — Merge carefully (do NOT overwrite WinUI 3 boilerplate)
|
||||
10. **Installer & build pipeline** — Update WiX, signing, build events
|
||||
11. **Tests** — Adapt for WinUI 3 runtime, async patterns
|
||||
|
||||
### Key Principles
|
||||
|
||||
- **Do NOT overwrite `App.xaml` / `App.xaml.cs`** — WinUI 3 has different application lifecycle boilerplate. Merge your resources and initialization code into the generated WinUI 3 App class.
|
||||
- **Do NOT create Exe→WinExe `ProjectReference`** — Extract shared code to a Library project. This causes phantom build artifacts.
|
||||
- **Use `Lazy<T>` for resource-dependent statics** — `ResourceLoader` is not available at class-load time in all contexts.
|
||||
|
||||
## Quick Reference Tables
|
||||
|
||||
### Namespace Mapping
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `System.Windows` | `Microsoft.UI.Xaml` |
|
||||
| `System.Windows.Controls` | `Microsoft.UI.Xaml.Controls` |
|
||||
| `System.Windows.Media` | `Microsoft.UI.Xaml.Media` |
|
||||
| `System.Windows.Media.Imaging` | `Microsoft.UI.Xaml.Media.Imaging` (UI) / `Windows.Graphics.Imaging` (processing) |
|
||||
| `System.Windows.Input` | `Microsoft.UI.Xaml.Input` |
|
||||
| `System.Windows.Data` | `Microsoft.UI.Xaml.Data` |
|
||||
| `System.Windows.Threading` | `Microsoft.UI.Dispatching` |
|
||||
| `System.Windows.Interop` | `WinRT.Interop` |
|
||||
|
||||
### Critical API Replacements
|
||||
|
||||
| WPF | WinUI 3 | Notes |
|
||||
|-----|---------|-------|
|
||||
| `Dispatcher.Invoke()` | `DispatcherQueue.TryEnqueue()` | Different return type (`bool`) |
|
||||
| `Dispatcher.CheckAccess()` | `DispatcherQueue.HasThreadAccess` | Property vs method |
|
||||
| `Application.Current.Dispatcher` | Store `DispatcherQueue` in static field | See [Threading](./references/threading-and-windowing.md) |
|
||||
| `MessageBox.Show()` | `ContentDialog` | Must set `XamlRoot` |
|
||||
| `DynamicResource` | `ThemeResource` | Theme-reactive only |
|
||||
| `clr-namespace:` | `using:` | XAML namespace prefix |
|
||||
| `{x:Static props:Resources.Key}` | `x:Uid` or `ResourceLoader.GetString()` | .resx → .resw |
|
||||
| `DataType="{x:Type m:Foo}"` | Remove or use code-behind | `x:Type` not supported |
|
||||
| `Properties.Resources.MyString` | `ResourceLoaderInstance.ResourceLoader.GetString("MyString")` | Lazy-init pattern |
|
||||
| `Application.Current.MainWindow` | Custom `App.Window` static property | Must track manually |
|
||||
| `SizeToContent="Height"` | Custom `SizeToContent()` via `AppWindow.Resize()` | See [Windowing](./references/threading-and-windowing.md) |
|
||||
| `MouseLeftButtonDown` | `PointerPressed` | Mouse → Pointer events |
|
||||
| `Pack URI (pack://...)` | `ms-appx:///` | Resource URI scheme |
|
||||
| `Observable` (custom base) | `ObservableObject` + `[ObservableProperty]` | CommunityToolkit.Mvvm |
|
||||
| `RelayCommand` (custom) | `[RelayCommand]` source generator | CommunityToolkit.Mvvm |
|
||||
| `JpegBitmapEncoder` | `BitmapEncoder.CreateAsync(JpegEncoderId, stream)` | Async, unified API |
|
||||
| `encoder.QualityLevel = 85` | `BitmapPropertySet { "ImageQuality", 0.85f }` | int 1-100 → float 0-1 |
|
||||
|
||||
### NuGet Package Migration
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `Microsoft.Xaml.Behaviors.Wpf` | `Microsoft.Xaml.Behaviors.WinUI.Managed` |
|
||||
| `WPF-UI` (Lepo) | Remove — use native WinUI 3 controls |
|
||||
| `CommunityToolkit.Mvvm` | `CommunityToolkit.Mvvm` (same) |
|
||||
| `Microsoft.Toolkit.Wpf.*` | `CommunityToolkit.WinUI.*` |
|
||||
| (none) | `Microsoft.WindowsAppSDK` |
|
||||
| (none) | `Microsoft.Windows.SDK.BuildTools` |
|
||||
| (none) | `WinUIEx` (optional, for window helpers) |
|
||||
| (none) | `CommunityToolkit.WinUI.Converters` |
|
||||
|
||||
### XAML Syntax Changes
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `xmlns:local="clr-namespace:MyApp"` | `xmlns:local="using:MyApp"` |
|
||||
| `{DynamicResource Key}` | `{ThemeResource Key}` |
|
||||
| `{x:Static Type.Member}` | `{x:Bind}` or code-behind |
|
||||
| `{x:Type local:MyType}` | Not supported |
|
||||
| `<Style.Triggers>` / `<DataTrigger>` | `VisualStateManager` |
|
||||
| `{Binding}` in `Setter.Value` | Not supported — use `StaticResource` |
|
||||
| `Content="{x:Static p:Resources.Cancel}"` | `x:Uid="Cancel"` with `.Content` in `.resw` |
|
||||
| `<ui:FluentWindow>` / `<ui:Button>` (WPF-UI) | Native `<Window>` / `<Button>` |
|
||||
| `<ui:NumberBox>` / `<ui:ProgressRing>` (WPF-UI) | Native `<NumberBox>` / `<ProgressRing>` |
|
||||
| `BasedOn="{StaticResource {x:Type ui:Button}}"` | `BasedOn="{StaticResource DefaultButtonStyle}"` |
|
||||
| `IsDefault="True"` / `IsCancel="True"` | `Style="{StaticResource AccentButtonStyle}"` / handle via KeyDown |
|
||||
| `<AccessText>` | Not available — use `AccessKey` property |
|
||||
| `<behaviors:Interaction.Triggers>` | Migrate to code-behind or WinUI behaviors |
|
||||
|
||||
## Detailed Reference Docs
|
||||
|
||||
Read only the section relevant to your current task:
|
||||
|
||||
- [Namespace and API Mapping](./references/namespace-api-mapping.md) — Full type mapping, NuGet changes, project file, CsWinRT interop
|
||||
- [XAML Migration Guide](./references/xaml-migration.md) — XAML syntax, WPF-UI removal, markup extensions, styles, resources, data binding
|
||||
- [Threading and Window Management](./references/threading-and-windowing.md) — Dispatcher, DispatcherQueue, SizeToContent, AppWindow, HWND interop, custom entry point
|
||||
- [Imaging API Migration](./references/imaging-migration.md) — BitmapEncoder/Decoder, SoftwareBitmap, CodecHelper, async patterns, int→uint
|
||||
- [PowerToys-Specific Patterns](./references/powertoys-patterns.md) — MVVM migration, ResourceLoader, Lazy init, installer, signing, test adaptation, build pipeline
|
||||
|
||||
## Common Pitfalls (from ImageResizer migration)
|
||||
|
||||
| Pitfall | Solution |
|
||||
|---------|----------|
|
||||
| `ContentDialog` throws "does not have a XamlRoot" | Set `dialog.XamlRoot = this.Content.XamlRoot` before `ShowAsync()` |
|
||||
| `FilePicker` throws error in desktop app | Call `WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd)` |
|
||||
| `Window.Dispatcher` returns null | Use `Window.DispatcherQueue` instead |
|
||||
| Resources on `Window` element not found | Move resources to root layout container (`Grid.Resources`) |
|
||||
| `VisualStateManager` on `Window` fails | Use `UserControl` or `Page` inside the Window |
|
||||
| Satellite assembly installer errors (`WIX0103`) | Remove `.resources.dll` refs from `Resources.wxs`; WinUI 3 uses `.pri` |
|
||||
| Phantom `.exe`/`.deps.json` in root output dir | Avoid Exe→WinExe `ProjectReference`; use Library project |
|
||||
| `ResourceLoader` crash at static init | Wrap in `Lazy<T>` or null-coalescing property — see [Lazy Init](./references/powertoys-patterns.md#lazy-initialization-for-resource-dependent-statics) |
|
||||
| `SizeToContent` not available | Implement manual content measurement + `AppWindow.Resize()` with DPI scaling |
|
||||
| `x:Bind` default mode is `OneTime` | Explicitly set `Mode=OneWay` or `Mode=TwoWay` |
|
||||
| `DynamicResource` / `x:Static` not compiling | Replace with `ThemeResource` / `ResourceLoader` or `x:Uid` |
|
||||
| `IValueConverter.Convert` signature mismatch | Last param: `CultureInfo` → `string` (language tag) |
|
||||
| Test project can't resolve WPF types | Add `<UseWPF>true</UseWPF>` temporarily; remove after imaging migration |
|
||||
| Pixel dimension type mismatch (`int` vs `uint`) | WinRT uses `uint` for pixel sizes — add `u` suffix in test assertions |
|
||||
| `$(SolutionDir)` empty in standalone project build | Use `$(MSBuildThisFileDirectory)` with relative paths instead |
|
||||
| JPEG quality value wrong after migration | WPF: int 1-100; WinRT: float 0.0-1.0 |
|
||||
| MSIX packaging fails in PreBuildEvent | Move to PostBuildEvent; artifacts not ready at PreBuild time |
|
||||
| RC file icon path with forward slashes | Use double-backslash escaping: `..\\ui\\Assets\\icon.ico` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Build fails after namespace rename | Check for lingering `System.Windows` usings; some types have no direct equivalent |
|
||||
| Missing `PresentationCore.dll` at runtime | Ensure ALL imaging code uses `Windows.Graphics.Imaging`, not `System.Windows.Media.Imaging` |
|
||||
| `DataContext` not working on Window | WinUI 3 `Window` is not a `DependencyObject`; use a root `Page` or `UserControl` |
|
||||
| XAML designer not available | WinUI 3 does not support XAML Designer; use Hot Reload instead |
|
||||
| NuGet restore failures | Run `build-essentials.cmd` after adding `Microsoft.WindowsAppSDK` package |
|
||||
| `Parallel.ForEach` compilation error | Migrate to `Parallel.ForEachAsync` for async imaging operations |
|
||||
| Signing check fails on leaked artifacts | Run `generateAllFileComponents.ps1`; verify only `WinUI3Apps\\` paths in signing config |
|
||||
287
.github/skills/wpf-to-winui3-migration/references/imaging-migration.md
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
# Imaging API Migration
|
||||
|
||||
Migrating from WPF (`System.Windows.Media.Imaging` / `PresentationCore.dll`) to WinRT (`Windows.Graphics.Imaging`). Based on the ImageResizer migration.
|
||||
|
||||
## Why This Migration Is Required
|
||||
|
||||
WinUI 3 apps deployed as self-contained do NOT include `PresentationCore.dll`. Any code using `System.Windows.Media.Imaging` will throw `FileNotFoundException` at runtime. ALL imaging code must use WinRT APIs.
|
||||
|
||||
| Purpose | Namespace |
|
||||
|---------|-----------|
|
||||
| UI display (`Image.Source`) | `Microsoft.UI.Xaml.Media.Imaging` |
|
||||
| Image processing (encode/decode/transform) | `Windows.Graphics.Imaging` |
|
||||
|
||||
## Architecture Change: Pipeline vs Declarative
|
||||
|
||||
The fundamental architecture differs:
|
||||
|
||||
**WPF**: In-memory pipeline of bitmap objects. Decode → transform → encode synchronously.
|
||||
```csharp
|
||||
var decoder = BitmapDecoder.Create(stream, ...);
|
||||
var transform = new TransformedBitmap(decoder.Frames[0], new ScaleTransform(...));
|
||||
var encoder = new JpegBitmapEncoder();
|
||||
encoder.Frames.Add(BitmapFrame.Create(transform, ...));
|
||||
encoder.Save(outputStream);
|
||||
```
|
||||
|
||||
**WinRT**: Declarative transform model. Configure transforms on the encoder, which handles pixel manipulation internally. All async.
|
||||
```csharp
|
||||
var decoder = await BitmapDecoder.CreateAsync(winrtStream);
|
||||
var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
|
||||
encoder.BitmapTransform.ScaledWidth = newWidth;
|
||||
encoder.BitmapTransform.ScaledHeight = newHeight;
|
||||
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
|
||||
await encoder.FlushAsync();
|
||||
```
|
||||
|
||||
## Core Type Mapping
|
||||
|
||||
### Decoders
|
||||
|
||||
| WPF | WinRT | Notes |
|
||||
|-----|-------|-------|
|
||||
| `BitmapDecoder.Create(stream, options, cache)` | `BitmapDecoder.CreateAsync(stream)` | Async, auto-detects format |
|
||||
| `JpegBitmapDecoder` / `PngBitmapDecoder` / etc. | `BitmapDecoder.CreateAsync(stream)` | Single unified decoder |
|
||||
| `decoder.Frames[0]` | `await decoder.GetFrameAsync(0)` | Async frame access |
|
||||
| `decoder.Frames.Count` | `decoder.FrameCount` (uint) | `int` → `uint` |
|
||||
| `decoder.CodecInfo.ContainerFormat` | `decoder.DecoderInformation.CodecId` | Different property path |
|
||||
| `decoder.Frames[0].PixelWidth` (int) | `decoder.PixelWidth` (uint) | `int` → `uint` |
|
||||
| `WmpBitmapDecoder` | Not available | WMP/HDP not supported |
|
||||
|
||||
### Encoders
|
||||
|
||||
| WPF | WinRT | Notes |
|
||||
|-----|-------|-------|
|
||||
| `new JpegBitmapEncoder()` | `BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream)` | Async factory |
|
||||
| `new PngBitmapEncoder()` | `BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream)` | No interlace control |
|
||||
| `encoder.Frames.Add(frame)` | `encoder.SetSoftwareBitmap(bitmap)` | Different API |
|
||||
| `encoder.Save(stream)` | `await encoder.FlushAsync()` | Async |
|
||||
|
||||
### Encoder Properties (Strongly-Typed → BitmapPropertySet)
|
||||
|
||||
WPF had type-specific encoder subclasses. WinRT uses a generic property set:
|
||||
|
||||
```csharp
|
||||
// WPF
|
||||
case JpegBitmapEncoder jpeg: jpeg.QualityLevel = 85; // int 1-100
|
||||
case PngBitmapEncoder png: png.Interlace = PngInterlaceOption.On;
|
||||
case TiffBitmapEncoder tiff: tiff.Compression = TiffCompressOption.Lzw;
|
||||
|
||||
// WinRT — JPEG quality (float 0.0-1.0)
|
||||
await encoder.BitmapProperties.SetPropertiesAsync(new BitmapPropertySet
|
||||
{
|
||||
{ "ImageQuality", new BitmapTypedValue(0.85f, PropertyType.Single) }
|
||||
});
|
||||
|
||||
// WinRT — TIFF compression (via BitmapPropertySet at creation time)
|
||||
var props = new BitmapPropertySet
|
||||
{
|
||||
{ "TiffCompressionMethod", new BitmapTypedValue((byte)2, PropertyType.UInt8) }
|
||||
};
|
||||
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.TiffEncoderId, stream, props);
|
||||
```
|
||||
|
||||
**JPEG quality scale change**: WPF int `1-100` → WinRT float `0.0-1.0`. Divide by 100.
|
||||
|
||||
### Bitmap Types
|
||||
|
||||
| WPF | WinRT | Notes |
|
||||
|-----|-------|-------|
|
||||
| `BitmapSource` | `SoftwareBitmap` | Central pixel-data type |
|
||||
| `BitmapImage` | `BitmapImage` (in `Microsoft.UI.Xaml.Media.Imaging`) | UI display only |
|
||||
| `FormatConvertedBitmap` | `SoftwareBitmap.Convert()` | |
|
||||
| `TransformedBitmap` + `ScaleTransform` | `BitmapTransform` via encoder | Declarative |
|
||||
| `CroppedBitmap` | `BitmapTransform.Bounds` | |
|
||||
|
||||
### Metadata
|
||||
|
||||
| WPF | WinRT | Notes |
|
||||
|-----|-------|-------|
|
||||
| `BitmapMetadata` | `BitmapProperties` | Different API surface |
|
||||
| `BitmapMetadata.Clone()` | No equivalent | Cannot selectively clone |
|
||||
| Selective metadata removal | Not supported | All-or-nothing only |
|
||||
|
||||
**Two encoder creation strategies for metadata:**
|
||||
- `CreateForTranscodingAsync()` — preserves ALL metadata from source
|
||||
- `CreateAsync()` — creates fresh encoder with NO metadata
|
||||
|
||||
This eliminated ~258 lines of manual metadata manipulation code (`BitmapMetadataExtension.cs`) in ImageResizer.
|
||||
|
||||
### Interpolation Modes
|
||||
|
||||
| WPF `BitmapScalingMode` | WinRT `BitmapInterpolationMode` |
|
||||
|------------------------|-------------------------------|
|
||||
| `HighQuality` / `Fant` | `Fant` |
|
||||
| `Linear` | `Linear` |
|
||||
| `NearestNeighbor` | `NearestNeighbor` |
|
||||
| `Unspecified` / `LowQuality` | `Linear` |
|
||||
|
||||
## Stream Interop
|
||||
|
||||
WinRT imaging requires `IRandomAccessStream` instead of `System.IO.Stream`:
|
||||
|
||||
```csharp
|
||||
using var stream = File.OpenRead(path);
|
||||
var winrtStream = stream.AsRandomAccessStream(); // Extension method
|
||||
var decoder = await BitmapDecoder.CreateAsync(winrtStream);
|
||||
```
|
||||
|
||||
**Critical**: For transcode, seek the input stream back to 0 before creating the encoder:
|
||||
```csharp
|
||||
winrtStream.Seek(0);
|
||||
var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
|
||||
```
|
||||
|
||||
## CodecHelper Pattern (from ImageResizer)
|
||||
|
||||
WPF stored container format GUIDs in `settings.json`. WinRT uses different codec IDs. Create a `CodecHelper` to bridge them:
|
||||
|
||||
```csharp
|
||||
internal static class CodecHelper
|
||||
{
|
||||
// Maps WPF container format GUIDs (stored in settings JSON) to WinRT encoder IDs
|
||||
private static readonly Dictionary<Guid, Guid> LegacyGuidToEncoderId = new()
|
||||
{
|
||||
[new Guid("19e4a5aa-5662-4fc5-a0c0-1758028e1057")] = BitmapEncoder.JpegEncoderId,
|
||||
[new Guid("1b7cfaf4-713f-473c-bbcd-6137425faeaf")] = BitmapEncoder.PngEncoderId,
|
||||
[new Guid("0af1d87e-fcfe-4188-bdeb-a7906471cbe3")] = BitmapEncoder.BmpEncoderId,
|
||||
[new Guid("163bcc30-e2e9-4f0b-961d-a3e9fdb788a3")] = BitmapEncoder.TiffEncoderId,
|
||||
[new Guid("1f8a5601-7d4d-4cbd-9c82-1bc8d4eeb9a5")] = BitmapEncoder.GifEncoderId,
|
||||
};
|
||||
|
||||
// Maps decoder IDs to corresponding encoder IDs
|
||||
private static readonly Dictionary<Guid, Guid> DecoderIdToEncoderId = new()
|
||||
{
|
||||
[BitmapDecoder.JpegDecoderId] = BitmapEncoder.JpegEncoderId,
|
||||
[BitmapDecoder.PngDecoderId] = BitmapEncoder.PngEncoderId,
|
||||
// ...
|
||||
};
|
||||
|
||||
public static Guid GetEncoderIdFromLegacyGuid(Guid legacyGuid)
|
||||
=> LegacyGuidToEncoderId.GetValueOrDefault(legacyGuid, Guid.Empty);
|
||||
|
||||
public static Guid GetEncoderIdForDecoder(BitmapDecoder decoder)
|
||||
=> DecoderIdToEncoderId.GetValueOrDefault(decoder.DecoderInformation.CodecId, Guid.Empty);
|
||||
}
|
||||
```
|
||||
|
||||
This preserves backward compatibility with existing `settings.json` files that contain WPF-era GUIDs.
|
||||
|
||||
## ImagingEnums Pattern (from ImageResizer)
|
||||
|
||||
WPF-specific enums (`PngInterlaceOption`, `TiffCompressOption`) from `System.Windows.Media.Imaging` are used in settings JSON. Create custom enums with identical integer values for backward-compatible deserialization:
|
||||
|
||||
```csharp
|
||||
// Replace System.Windows.Media.Imaging.PngInterlaceOption
|
||||
public enum PngInterlaceOption { Default = 0, On = 1, Off = 2 }
|
||||
|
||||
// Replace System.Windows.Media.Imaging.TiffCompressOption
|
||||
public enum TiffCompressOption { Default = 0, None = 1, Ccitt3 = 2, Ccitt4 = 3, Lzw = 4, Rle = 5, Zip = 6 }
|
||||
```
|
||||
|
||||
## Async Migration Patterns
|
||||
|
||||
### Method Signatures
|
||||
|
||||
All imaging operations become async:
|
||||
|
||||
| Before | After |
|
||||
|--------|-------|
|
||||
| `void Execute(file, settings)` | `async Task ExecuteAsync(file, settings)` |
|
||||
| `IEnumerable<Error> Process()` | `async Task<IEnumerable<Error>> ProcessAsync()` |
|
||||
|
||||
### Parallel Processing
|
||||
|
||||
```csharp
|
||||
// WPF (synchronous)
|
||||
Parallel.ForEach(Files, new ParallelOptions { MaxDegreeOfParallelism = ... },
|
||||
(file, state, i) => { Execute(file, settings); });
|
||||
|
||||
// WinRT (async)
|
||||
await Parallel.ForEachAsync(Files, new ParallelOptions { MaxDegreeOfParallelism = ... },
|
||||
async (file, ct) => { await ExecuteAsync(file, settings); });
|
||||
```
|
||||
|
||||
### CLI Async Bridge
|
||||
|
||||
CLI entry points must bridge async to sync:
|
||||
```csharp
|
||||
return RunSilentModeAsync(cliOptions).GetAwaiter().GetResult();
|
||||
```
|
||||
|
||||
### Task.Factory.StartNew → Task.Run
|
||||
|
||||
```csharp
|
||||
// WPF
|
||||
_ = Task.Factory.StartNew(StartExecutingWork, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
|
||||
// WinUI 3
|
||||
_ = Task.Run(() => StartExecutingWorkAsync());
|
||||
```
|
||||
|
||||
## SoftwareBitmap as Interface Type
|
||||
|
||||
When modules expose imaging interfaces (e.g., AI super-resolution), change parameter/return types:
|
||||
|
||||
```csharp
|
||||
// WPF
|
||||
BitmapSource ApplySuperResolution(BitmapSource source, int scale, string filePath);
|
||||
|
||||
// WinRT
|
||||
SoftwareBitmap ApplySuperResolution(SoftwareBitmap source, int scale, string filePath);
|
||||
```
|
||||
|
||||
This eliminates manual `BitmapSource ↔ SoftwareBitmap` conversion code (unsafe `IMemoryBufferByteAccess` COM interop).
|
||||
|
||||
## MultiFrame Image Handling
|
||||
|
||||
```csharp
|
||||
// WinRT multi-frame encode (e.g., multi-page TIFF, animated GIF)
|
||||
for (uint i = 0; i < decoder.FrameCount; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
await encoder.GoToNextFrameAsync();
|
||||
|
||||
var frame = await decoder.GetFrameAsync(i);
|
||||
var bitmap = await frame.GetSoftwareBitmapAsync(
|
||||
frame.BitmapPixelFormat,
|
||||
BitmapAlphaMode.Premultiplied,
|
||||
transform,
|
||||
ExifOrientationMode.IgnoreExifOrientation,
|
||||
ColorManagementMode.DoNotColorManage);
|
||||
encoder.SetSoftwareBitmap(bitmap);
|
||||
}
|
||||
await encoder.FlushAsync();
|
||||
```
|
||||
|
||||
## int → uint for Pixel Dimensions
|
||||
|
||||
WinRT uses `uint` for all pixel dimensions. This affects:
|
||||
- `decoder.PixelWidth` / `decoder.PixelHeight` — `uint`
|
||||
- `BitmapTransform.ScaledWidth` / `ScaledHeight` — `uint`
|
||||
- `SoftwareBitmap` constructor — `uint` parameters
|
||||
- Test assertions: `Assert.AreEqual(96, ...)` → `Assert.AreEqual(96u, ...)`
|
||||
|
||||
## Display SoftwareBitmap in UI
|
||||
|
||||
```csharp
|
||||
var source = new SoftwareBitmapSource();
|
||||
// Must convert to Bgra8/Premultiplied for display
|
||||
if (bitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
|
||||
bitmap.BitmapAlphaMode != BitmapAlphaMode.Premultiplied)
|
||||
{
|
||||
bitmap = SoftwareBitmap.Convert(bitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
|
||||
}
|
||||
await source.SetBitmapAsync(bitmap);
|
||||
myImage.Source = source;
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
| Feature | WPF | WinRT | Impact |
|
||||
|---------|-----|-------|--------|
|
||||
| PNG interlace | `PngBitmapEncoder.Interlace` | Not available | Always non-interlaced |
|
||||
| Metadata stripping | Selective via `BitmapMetadata.Clone()` | All-or-nothing | Orientation EXIF also removed |
|
||||
| Pixel formats | Many (`Pbgra32`, `Bgr24`, `Indexed8`, ...) | Primarily `Bgra8`, `Rgba8`, `Gray8/16` | Convert to `Bgra8` |
|
||||
| WMP/HDP format | `WmpBitmapDecoder` | Not available | Not supported |
|
||||
| Pixel differences | WPF scaler | `BitmapInterpolationMode.Fant` | Not bit-identical |
|
||||
226
.github/skills/wpf-to-winui3-migration/references/namespace-api-mapping.md
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
# Namespace and API Mapping Reference
|
||||
|
||||
Complete reference for mapping WPF types to WinUI 3 equivalents, based on the ImageResizer migration.
|
||||
|
||||
## Root Namespace Mapping
|
||||
|
||||
| WPF Namespace | WinUI 3 Namespace |
|
||||
|---------------|-------------------|
|
||||
| `System.Windows` | `Microsoft.UI.Xaml` |
|
||||
| `System.Windows.Automation` | `Microsoft.UI.Xaml.Automation` |
|
||||
| `System.Windows.Automation.Peers` | `Microsoft.UI.Xaml.Automation.Peers` |
|
||||
| `System.Windows.Controls` | `Microsoft.UI.Xaml.Controls` |
|
||||
| `System.Windows.Controls.Primitives` | `Microsoft.UI.Xaml.Controls.Primitives` |
|
||||
| `System.Windows.Data` | `Microsoft.UI.Xaml.Data` |
|
||||
| `System.Windows.Documents` | `Microsoft.UI.Xaml.Documents` |
|
||||
| `System.Windows.Input` | `Microsoft.UI.Xaml.Input` |
|
||||
| `System.Windows.Markup` | `Microsoft.UI.Xaml.Markup` |
|
||||
| `System.Windows.Media` | `Microsoft.UI.Xaml.Media` |
|
||||
| `System.Windows.Media.Animation` | `Microsoft.UI.Xaml.Media.Animation` |
|
||||
| `System.Windows.Media.Imaging` | `Microsoft.UI.Xaml.Media.Imaging` |
|
||||
| `System.Windows.Navigation` | `Microsoft.UI.Xaml.Navigation` |
|
||||
| `System.Windows.Shapes` | `Microsoft.UI.Xaml.Shapes` |
|
||||
| `System.Windows.Threading` | `Microsoft.UI.Dispatching` |
|
||||
| `System.Windows.Interop` | `WinRT.Interop` |
|
||||
|
||||
## Core Type Mapping
|
||||
|
||||
| WPF Type | WinUI 3 Type |
|
||||
|----------|-------------|
|
||||
| `System.Windows.Application` | `Microsoft.UI.Xaml.Application` |
|
||||
| `System.Windows.Window` | `Microsoft.UI.Xaml.Window` (NOT a DependencyObject) |
|
||||
| `System.Windows.DependencyObject` | `Microsoft.UI.Xaml.DependencyObject` |
|
||||
| `System.Windows.DependencyProperty` | `Microsoft.UI.Xaml.DependencyProperty` |
|
||||
| `System.Windows.FrameworkElement` | `Microsoft.UI.Xaml.FrameworkElement` |
|
||||
| `System.Windows.UIElement` | `Microsoft.UI.Xaml.UIElement` |
|
||||
| `System.Windows.Visibility` | `Microsoft.UI.Xaml.Visibility` |
|
||||
| `System.Windows.Thickness` | `Microsoft.UI.Xaml.Thickness` |
|
||||
| `System.Windows.CornerRadius` | `Microsoft.UI.Xaml.CornerRadius` |
|
||||
| `System.Windows.Media.Color` | `Windows.UI.Color` (note: `Windows.UI`, not `Microsoft.UI`) |
|
||||
| `System.Windows.Media.Colors` | `Microsoft.UI.Colors` |
|
||||
|
||||
## Controls Mapping
|
||||
|
||||
### Direct Mapping (namespace-only change)
|
||||
|
||||
These controls exist in both frameworks with the same name — change `System.Windows.Controls` to `Microsoft.UI.Xaml.Controls`:
|
||||
|
||||
`Button`, `TextBox`, `TextBlock`, `ComboBox`, `CheckBox`, `ListBox`, `ListView`, `Image`, `StackPanel`, `Grid`, `Border`, `ScrollViewer`, `ContentControl`, `UserControl`, `Page`, `Frame`, `Slider`, `ProgressBar`, `ToolTip`, `RadioButton`, `ToggleButton`
|
||||
|
||||
### Controls With Different Names or Behavior
|
||||
|
||||
| WPF | WinUI 3 | Notes |
|
||||
|-----|---------|-------|
|
||||
| `MessageBox` | `ContentDialog` | Must set `XamlRoot` before `ShowAsync()` |
|
||||
| `ContextMenu` | `MenuFlyout` | Different API surface |
|
||||
| `TabControl` | `TabView` | Different API |
|
||||
| `Menu` | `MenuBar` | Different API |
|
||||
| `StatusBar` | Custom `StackPanel` layout | No built-in equivalent |
|
||||
| `AccessText` | Not available | Use `AccessKey` property on target control |
|
||||
|
||||
### WPF-UI (Lepo) to Native WinUI 3
|
||||
|
||||
ImageResizer used the `WPF-UI` library (Lepo) for Fluent styling. These must be replaced with native WinUI 3 equivalents:
|
||||
|
||||
| WPF-UI (Lepo) | WinUI 3 Native | Notes |
|
||||
|----------------|---------------|-------|
|
||||
| `<ui:FluentWindow>` | `<Window>` | Native window + `ExtendsContentIntoTitleBar` |
|
||||
| `<ui:Button>` | `<Button>` | Native button |
|
||||
| `<ui:NumberBox>` | `<NumberBox>` | Built into WinUI 3 |
|
||||
| `<ui:ProgressRing>` | `<ProgressRing>` | Built into WinUI 3 |
|
||||
| `<ui:SymbolIcon>` | `<SymbolIcon>` or `<FontIcon>` | Built into WinUI 3 |
|
||||
| `<ui:InfoBar>` | `<InfoBar>` | Built into WinUI 3 |
|
||||
| `<ui:TitleBar>` | Custom title bar via `SetTitleBar()` | Use `ExtendsContentIntoTitleBar` |
|
||||
| `<ui:ThemesDictionary>` | `<XamlControlsResources>` | In merged dictionaries |
|
||||
| `<ui:ControlsDictionary>` | Remove | Not needed — WinUI 3 has its own control styles |
|
||||
| `BasedOn="{StaticResource {x:Type ui:Button}}"` | `BasedOn="{StaticResource DefaultButtonStyle}"` | Named style keys |
|
||||
|
||||
## Input Event Mapping
|
||||
|
||||
| WPF Event | WinUI 3 Event | Notes |
|
||||
|-----------|--------------|-------|
|
||||
| `MouseLeftButtonDown` | `PointerPressed` | Check `IsLeftButtonPressed` on args |
|
||||
| `MouseLeftButtonUp` | `PointerReleased` | Check pointer properties |
|
||||
| `MouseRightButtonDown` | `RightTapped` | Or `PointerPressed` with right button check |
|
||||
| `MouseMove` | `PointerMoved` | Uses `PointerRoutedEventArgs` |
|
||||
| `MouseWheel` | `PointerWheelChanged` | Different event args |
|
||||
| `MouseEnter` | `PointerEntered` | |
|
||||
| `MouseLeave` | `PointerExited` | |
|
||||
| `MouseDoubleClick` | `DoubleTapped` | Different event args |
|
||||
| `KeyDown` | `KeyDown` | Same name, args type: `KeyRoutedEventArgs` |
|
||||
| `PreviewKeyDown` | No direct equivalent | Use `KeyDown` with handled pattern |
|
||||
|
||||
## IValueConverter Signature Change
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `Convert(object value, Type targetType, object parameter, CultureInfo culture)` | `Convert(object value, Type targetType, object parameter, string language)` |
|
||||
| `ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)` | `ConvertBack(object value, Type targetType, object parameter, string language)` |
|
||||
|
||||
Last parameter changes from `CultureInfo` to `string` (BCP-47 language tag). All converter classes must be updated.
|
||||
|
||||
## Types That Moved to Different Hierarchies
|
||||
|
||||
| WPF | WinUI 3 | Notes |
|
||||
|-----|---------|-------|
|
||||
| `System.Windows.Threading.Dispatcher` | `Microsoft.UI.Dispatching.DispatcherQueue` | Completely different API |
|
||||
| `System.Windows.Threading.DispatcherPriority` | `Microsoft.UI.Dispatching.DispatcherQueuePriority` | Only 3 levels: High/Normal/Low |
|
||||
| `System.Windows.Interop.HwndSource` | `WinRT.Interop.WindowNative` | For HWND interop |
|
||||
| `System.Windows.Interop.WindowInteropHelper` | `WinRT.Interop.WindowNative.GetWindowHandle()` | |
|
||||
| `System.Windows.SystemColors` | Resource keys via `ThemeResource` | No direct static class |
|
||||
| `System.Windows.SystemParameters` | Win32 API or `DisplayInformation` | No direct equivalent |
|
||||
|
||||
## NuGet Package Migration
|
||||
|
||||
| WPF | WinUI 3 | Notes |
|
||||
|-----|---------|-------|
|
||||
| Built into .NET (no NuGet needed) | `Microsoft.WindowsAppSDK` | Required |
|
||||
| `PresentationCore` / `PresentationFramework` | `Microsoft.WinUI` (transitive) | |
|
||||
| `Microsoft.Xaml.Behaviors.Wpf` | `Microsoft.Xaml.Behaviors.WinUI.Managed` | |
|
||||
| `WPF-UI` (Lepo) | **Remove** — use native WinUI 3 controls | |
|
||||
| `CommunityToolkit.Mvvm` | `CommunityToolkit.Mvvm` (same) | |
|
||||
| `Microsoft.Toolkit.Wpf.*` | `CommunityToolkit.WinUI.*` | |
|
||||
| (none) | `Microsoft.Windows.SDK.BuildTools` | Required |
|
||||
| (none) | `WinUIEx` | Optional, window helpers |
|
||||
| (none) | `CommunityToolkit.WinUI.Converters` | Optional |
|
||||
| (none) | `CommunityToolkit.WinUI.Extensions` | Optional |
|
||||
| (none) | `Microsoft.Web.WebView2` | If using WebView |
|
||||
|
||||
## Project File Changes
|
||||
|
||||
### WPF .csproj
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
### WinUI 3 .csproj
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<SelfContained>true</SelfContained>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Assets\ImageResizer\ImageResizer.ico</ApplicationIcon>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
|
||||
<ProjectPriFileName>PowerToys.ModuleName.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
Key changes:
|
||||
- `UseWPF` → `UseWinUI`
|
||||
- TFM: `net8.0-windows` → `net8.0-windows10.0.19041.0`
|
||||
- Add `WindowsPackageType=None` for unpackaged desktop apps
|
||||
- Add `SelfContained=true` + `WindowsAppSDKSelfContained=true`
|
||||
- Add `DISABLE_XAML_GENERATED_MAIN` if using custom `Program.cs` entry point
|
||||
- Set `ProjectPriFileName` to match your module's assembly name
|
||||
- Move icon from `Resources/` to `Assets/<Module>/`
|
||||
|
||||
### XAML ApplicationDefinition Setup
|
||||
|
||||
WinUI 3 requires explicit `ApplicationDefinition` declaration:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<Page Remove="ImageResizerXAML\App.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="ImageResizerXAML\App.xaml" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### CsWinRT Interop (for GPO and native references)
|
||||
|
||||
If the module references native C++ projects (like `GPOWrapper`):
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
Change `GPOWrapperProjection.csproj` reference to direct `GPOWrapper.vcxproj` reference.
|
||||
|
||||
### InternalsVisibleTo Migration
|
||||
|
||||
Move from code file to `.csproj`:
|
||||
|
||||
```csharp
|
||||
// DELETE: Properties/InternalsVisibleTo.cs
|
||||
// [assembly: InternalsVisibleTo("ImageResizer.Test")]
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- ADD to .csproj: -->
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="ImageResizer.Test" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### Items to Remove from .csproj
|
||||
|
||||
```xml
|
||||
<!-- DELETE: WPF resource embedding -->
|
||||
<EmbeddedResource Update="Properties\Resources.resx">...</EmbeddedResource>
|
||||
<Resource Include="Resources\ImageResizer.ico" />
|
||||
<Compile Update="Properties\Resources.Designer.cs">...</Compile>
|
||||
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" /> <!-- from CLI project -->
|
||||
```
|
||||
516
.github/skills/wpf-to-winui3-migration/references/powertoys-patterns.md
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
# PowerToys-Specific Migration Patterns
|
||||
|
||||
Patterns and conventions specific to the PowerToys codebase, based on the ImageResizer migration.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Before (WPF Module)
|
||||
|
||||
```
|
||||
src/modules/<module>/
|
||||
├── <Module>UI/
|
||||
│ ├── <Module>UI.csproj # OutputType=WinExe, UseWPF=true
|
||||
│ ├── App.xaml / App.xaml.cs
|
||||
│ ├── MainWindow.xaml / .cs
|
||||
│ ├── Views/
|
||||
│ ├── ViewModels/
|
||||
│ ├── Helpers/
|
||||
│ │ ├── Observable.cs # Custom INotifyPropertyChanged
|
||||
│ │ └── RelayCommand.cs # Custom ICommand
|
||||
│ ├── Properties/
|
||||
│ │ ├── Resources.resx # WPF resource strings
|
||||
│ │ ├── Resources.Designer.cs
|
||||
│ │ └── InternalsVisibleTo.cs
|
||||
│ └── Telemetry/
|
||||
├── <Module>CLI/
|
||||
│ └── <Module>CLI.csproj # OutputType=Exe
|
||||
└── tests/
|
||||
```
|
||||
|
||||
### After (WinUI 3 Module)
|
||||
|
||||
```
|
||||
src/modules/<module>/
|
||||
├── <Module>UI/
|
||||
│ ├── <Module>UI.csproj # OutputType=WinExe, UseWinUI=true
|
||||
│ ├── Program.cs # Custom entry point (DISABLE_XAML_GENERATED_MAIN)
|
||||
│ ├── app.manifest # Single manifest file
|
||||
│ ├── ImageResizerXAML/
|
||||
│ │ ├── App.xaml / App.xaml.cs # WinUI 3 App class
|
||||
│ │ ├── MainWindow.xaml / .cs
|
||||
│ │ └── Views/
|
||||
│ ├── Converters/ # WinUI 3 IValueConverter (string language)
|
||||
│ ├── ViewModels/
|
||||
│ ├── Helpers/
|
||||
│ │ └── ResourceLoaderInstance.cs # Static ResourceLoader accessor
|
||||
│ ├── Utilities/
|
||||
│ │ └── CodecHelper.cs # WPF→WinRT codec ID mapping (if imaging)
|
||||
│ ├── Models/
|
||||
│ │ └── ImagingEnums.cs # Custom enums replacing WPF imaging enums
|
||||
│ ├── Strings/
|
||||
│ │ └── en-us/
|
||||
│ │ └── Resources.resw # WinUI 3 resource strings
|
||||
│ └── Assets/
|
||||
│ └── <Module>/
|
||||
│ └── <Module>.ico # Moved from Resources/
|
||||
├── <Module>Common/ # NEW: shared library for CLI
|
||||
│ └── <Module>Common.csproj # OutputType=Library
|
||||
├── <Module>CLI/
|
||||
│ └── <Module>CLI.csproj # References Common, NOT UI
|
||||
└── tests/
|
||||
```
|
||||
|
||||
### Critical: CLI Dependency Pattern
|
||||
|
||||
**Do NOT** create `ProjectReference` from Exe to WinExe. This causes phantom build artifacts (`.exe`, `.deps.json`, `.runtimeconfig.json`) in the root output directory.
|
||||
|
||||
```
|
||||
WRONG: ImageResizerCLI (Exe) → ImageResizerUI (WinExe) ← phantom artifacts
|
||||
CORRECT: ImageResizerCLI (Exe) → ImageResizerCommon (Library)
|
||||
ImageResizerUI (WinExe) → ImageResizerCommon (Library)
|
||||
```
|
||||
|
||||
Follow the `FancyZonesCLI` → `FancyZonesEditorCommon` pattern.
|
||||
|
||||
### Files to Delete
|
||||
|
||||
| File | Reason |
|
||||
|------|--------|
|
||||
| `Properties/Resources.resx` | Replaced by `Strings/en-us/Resources.resw` |
|
||||
| `Properties/Resources.Designer.cs` | Auto-generated; no longer needed |
|
||||
| `Properties/InternalsVisibleTo.cs` | Moved to `.csproj` `<InternalsVisibleTo>` |
|
||||
| `Helpers/Observable.cs` | Replaced by `CommunityToolkit.Mvvm.ObservableObject` |
|
||||
| `Helpers/RelayCommand.cs` | Replaced by `CommunityToolkit.Mvvm.Input` |
|
||||
| `Resources/*.ico` / `Resources/*.png` | Moved to `Assets/<Module>/` |
|
||||
| WPF `.dev.manifest` / `.prod.manifest` | Replaced by single `app.manifest` |
|
||||
| WPF-specific converters | Replaced by WinUI 3 converters with `string language` |
|
||||
|
||||
---
|
||||
|
||||
## MVVM Migration: Custom → CommunityToolkit.Mvvm Source Generators
|
||||
|
||||
### Observable Base Class → ObservableObject + [ObservableProperty]
|
||||
|
||||
**Before (custom Observable):**
|
||||
```csharp
|
||||
public class ResizeSize : Observable
|
||||
{
|
||||
private int _id;
|
||||
public int Id { get => _id; set => Set(ref _id, value); }
|
||||
|
||||
private ResizeFit _fit;
|
||||
public ResizeFit Fit
|
||||
{
|
||||
get => _fit;
|
||||
set
|
||||
{
|
||||
Set(ref _fit, value);
|
||||
UpdateShowHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _showHeight = true;
|
||||
public bool ShowHeight { get => _showHeight; set => Set(ref _showHeight, value); }
|
||||
private void UpdateShowHeight() { ShowHeight = Fit == ResizeFit.Stretch || Unit != ResizeUnit.Percent; }
|
||||
}
|
||||
```
|
||||
|
||||
**After (CommunityToolkit.Mvvm source generators):**
|
||||
```csharp
|
||||
public partial class ResizeSize : ObservableObject // MUST be partial
|
||||
{
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("Id")]
|
||||
private int _id;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ShowHeight))] // Replaces manual UpdateShowHeight()
|
||||
private ResizeFit _fit;
|
||||
|
||||
// Computed property — no backing field, no manual update method
|
||||
public bool ShowHeight => Fit == ResizeFit.Stretch || Unit != ResizeUnit.Percent;
|
||||
}
|
||||
```
|
||||
|
||||
Key changes:
|
||||
- Class must be `partial` for source generators
|
||||
- `Observable` → `ObservableObject` (from CommunityToolkit.Mvvm)
|
||||
- Manual `Set(ref _field, value)` → `[ObservableProperty]` attribute
|
||||
- `PropertyChanged` dependencies → `[NotifyPropertyChangedFor(nameof(...))]`
|
||||
- Computed properties with manual `UpdateXxx()` → direct expression body
|
||||
|
||||
### Custom Name Setter with Transform
|
||||
|
||||
For properties that transform the value before storing:
|
||||
|
||||
```csharp
|
||||
// Cannot use [ObservableProperty] because of value transformation
|
||||
private string _name;
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => SetProperty(ref _name, ReplaceTokens(value)); // SetProperty from ObservableObject
|
||||
}
|
||||
```
|
||||
|
||||
### RelayCommand → [RelayCommand] Source Generator
|
||||
|
||||
```csharp
|
||||
// DELETE: Helpers/RelayCommand.cs (custom ICommand)
|
||||
|
||||
// Before
|
||||
public ICommand ResizeCommand { get; } = new RelayCommand(Execute);
|
||||
|
||||
// After
|
||||
[RelayCommand]
|
||||
private void Resize() { /* ... */ }
|
||||
// Source generator creates ResizeCommand property automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resource String Migration (.resx → .resw)
|
||||
|
||||
### ResourceLoaderInstance Helper
|
||||
|
||||
```csharp
|
||||
internal static class ResourceLoaderInstance
|
||||
{
|
||||
internal static ResourceLoader ResourceLoader { get; private set; }
|
||||
|
||||
static ResourceLoaderInstance()
|
||||
{
|
||||
ResourceLoader = new ResourceLoader("PowerToys.ImageResizer.pri");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: Use the single-argument `ResourceLoader` constructor. The two-argument version (`ResourceLoader("file.pri", "path/Resources")`) may fail if the resource map path doesn't match the actual PRI structure.
|
||||
|
||||
### Usage
|
||||
|
||||
```csharp
|
||||
// WPF
|
||||
using ImageResizer.Properties;
|
||||
string text = Resources.MyStringKey;
|
||||
|
||||
// WinUI 3
|
||||
string text = ResourceLoaderInstance.ResourceLoader.GetString("MyStringKey");
|
||||
```
|
||||
|
||||
### Lazy Initialization for Resource-Dependent Statics
|
||||
|
||||
`ResourceLoader` is not available at class-load time in all contexts (CLI mode, test harness). Use lazy initialization:
|
||||
|
||||
**Before (crashes at class load):**
|
||||
```csharp
|
||||
private static readonly CompositeFormat _format =
|
||||
CompositeFormat.Parse(Resources.Error_Format);
|
||||
|
||||
private static readonly Dictionary<string, string> _tokens = new()
|
||||
{
|
||||
["$small$"] = Resources.Small,
|
||||
["$medium$"] = Resources.Medium,
|
||||
};
|
||||
```
|
||||
|
||||
**After (lazy, safe):**
|
||||
```csharp
|
||||
private static CompositeFormat _format;
|
||||
private static CompositeFormat Format => _format ??=
|
||||
CompositeFormat.Parse(ResourceLoaderInstance.ResourceLoader.GetString("Error_Format"));
|
||||
|
||||
private static readonly Lazy<Dictionary<string, string>> _tokens = new(() =>
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["$small$"] = ResourceLoaderInstance.ResourceLoader.GetString("Small"),
|
||||
["$medium$"] = ResourceLoaderInstance.ResourceLoader.GetString("Medium"),
|
||||
});
|
||||
// Usage: _tokens.Value.TryGetValue(...)
|
||||
```
|
||||
|
||||
### XAML: x:Static → x:Uid
|
||||
|
||||
```xml
|
||||
<!-- WPF -->
|
||||
<Button Content="{x:Static p:Resources.Cancel}" />
|
||||
<!-- WinUI 3 -->
|
||||
<Button x:Uid="Cancel" />
|
||||
```
|
||||
|
||||
In `.resw`, use property-suffixed keys: `Cancel.Content`, `Header.Text`, etc.
|
||||
|
||||
---
|
||||
|
||||
## CLI Options Migration
|
||||
|
||||
`System.CommandLine.Option<T>` constructor signature changed:
|
||||
|
||||
```csharp
|
||||
// WPF era — string[] aliases
|
||||
public DestinationOption()
|
||||
: base(_aliases, Properties.Resources.CLI_Option_Destination)
|
||||
|
||||
// WinUI 3 — single string name
|
||||
public DestinationOption()
|
||||
: base(_aliases[0], ResourceLoaderInstance.ResourceLoader.GetString("CLI_Option_Destination"))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Installer Updates
|
||||
|
||||
### WiX Changes
|
||||
|
||||
#### 1. Remove Satellite Assembly References
|
||||
|
||||
Remove from `installer/PowerToysSetupVNext/Resources.wxs`:
|
||||
- `<Component>` entries for `<Module>.resources.dll`
|
||||
- `<RemoveFolder>` entries for locale directories
|
||||
- Module from `WinUI3AppsInstallFolder` `ParentDirectory` loop
|
||||
|
||||
#### 2. Update File Component Generation
|
||||
|
||||
Run `generateAllFileComponents.ps1` after migration. For Exe→WinExe dependency issues, add cleanup logic:
|
||||
|
||||
```powershell
|
||||
# Strip phantom ImageResizer files from BaseApplications.wxs
|
||||
$content = $content -replace 'PowerToys\.ImageResizer\.exe', ''
|
||||
$content = $content -replace 'PowerToys\.ImageResizer\.deps\.json', ''
|
||||
$content = $content -replace 'PowerToys\.ImageResizer\.runtimeconfig\.json', ''
|
||||
```
|
||||
|
||||
#### 3. Output Directory
|
||||
|
||||
WinUI 3 modules output to `WinUI3Apps/`:
|
||||
```xml
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutputPath>
|
||||
```
|
||||
|
||||
### ESRP Signing
|
||||
|
||||
Update `.pipelines/ESRPSigning_core.json` — all module binaries must use `WinUI3Apps\\` paths:
|
||||
|
||||
```json
|
||||
{
|
||||
"FileList": [
|
||||
"WinUI3Apps\\PowerToys.ImageResizer.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerExt.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerContextMenu.dll"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build Pipeline Fixes
|
||||
|
||||
### $(SolutionDir) → $(MSBuildThisFileDirectory)
|
||||
|
||||
`$(SolutionDir)` is empty when building individual projects outside the solution. Replace with relative paths from the project file:
|
||||
|
||||
```xml
|
||||
<!-- Before (breaks on standalone project build) -->
|
||||
<Exec Command="powershell $(SolutionDir)tools\build\convert-resx-to-rc.ps1" />
|
||||
|
||||
<!-- After (works always) -->
|
||||
<Exec Command="powershell $(MSBuildThisFileDirectory)..\..\..\..\tools\build\convert-resx-to-rc.ps1" />
|
||||
```
|
||||
|
||||
### MSIX Packaging: PreBuild → PostBuild
|
||||
|
||||
MSIX packaging must happen AFTER the build (artifacts not ready at PreBuild):
|
||||
|
||||
```xml
|
||||
<!-- Before -->
|
||||
<PreBuildEvent>MakeAppx.exe pack /d . /p "$(OutDir)Package.msix" /o</PreBuildEvent>
|
||||
|
||||
<!-- After -->
|
||||
<PostBuildEvent>
|
||||
if exist "$(OutDir)Package.msix" del "$(OutDir)Package.msix"
|
||||
MakeAppx.exe pack /d "$(MSBuildThisFileDirectory)." /p "$(OutDir)Package.msix" /o
|
||||
</PostBuildEvent>
|
||||
```
|
||||
|
||||
### RC File Icon Path Escaping
|
||||
|
||||
Windows Resource Compiler requires double-backslash paths:
|
||||
|
||||
```c
|
||||
// Before (breaks)
|
||||
IDI_ICON1 ICON "..\\ui\Assets\ImageResizer\ImageResizer.ico"
|
||||
// After
|
||||
IDI_ICON1 ICON "..\\ui\\Assets\\ImageResizer\\ImageResizer.ico"
|
||||
```
|
||||
|
||||
### BOM/Encoding Normalization
|
||||
|
||||
Migration may strip UTF-8 BOM from C# files (`// Copyright` → `// Copyright`). This is cosmetic and safe, but be aware it will show as changes in diff.
|
||||
|
||||
---
|
||||
|
||||
## Test Adaptation
|
||||
|
||||
### Tests Requiring WPF Runtime
|
||||
|
||||
If tests still need WPF types (e.g., comparing old vs new output), temporarily add:
|
||||
```xml
|
||||
<UseWPF>true</UseWPF>
|
||||
```
|
||||
Remove this after fully migrating all test code to WinRT APIs.
|
||||
|
||||
### Tests Using ResourceLoader
|
||||
|
||||
Unit tests cannot easily initialize WinUI 3 `ResourceLoader`. Options:
|
||||
- Hardcode expected strings in tests: `"Value must be between '{0}' and '{1}'."`
|
||||
- Delete tests that only verify resource string lookup
|
||||
- Avoid creating `App` instances in test harness (WinUI App cannot be instantiated in tests)
|
||||
|
||||
### Async Test Methods
|
||||
|
||||
All imaging tests become async:
|
||||
```csharp
|
||||
// Before
|
||||
[TestMethod]
|
||||
public void ResizesImage() { ... }
|
||||
|
||||
// After
|
||||
[TestMethod]
|
||||
public async Task ResizesImageAsync() { ... }
|
||||
```
|
||||
|
||||
### uint Assertions
|
||||
|
||||
```csharp
|
||||
// Before
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
// After
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
```
|
||||
|
||||
### Pixel Data Access in Tests
|
||||
|
||||
```csharp
|
||||
// Before (WPF)
|
||||
public static Color GetFirstPixel(this BitmapSource source)
|
||||
{
|
||||
var pixel = new byte[4];
|
||||
new FormatConvertedBitmap(
|
||||
new CroppedBitmap(source, new Int32Rect(0, 0, 1, 1)),
|
||||
PixelFormats.Bgra32, null, 0).CopyPixels(pixel, 4, 0);
|
||||
return Color.FromArgb(pixel[3], pixel[2], pixel[1], pixel[0]);
|
||||
}
|
||||
|
||||
// After (WinRT)
|
||||
public static async Task<(byte R, byte G, byte B, byte A)> GetFirstPixelAsync(
|
||||
this BitmapDecoder decoder)
|
||||
{
|
||||
using var bitmap = await decoder.GetSoftwareBitmapAsync(
|
||||
BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
|
||||
var buffer = new Windows.Storage.Streams.Buffer(
|
||||
(uint)(bitmap.PixelWidth * bitmap.PixelHeight * 4));
|
||||
bitmap.CopyToBuffer(buffer);
|
||||
using var reader = DataReader.FromBuffer(buffer);
|
||||
byte b = reader.ReadByte(), g = reader.ReadByte(),
|
||||
r = reader.ReadByte(), a = reader.ReadByte();
|
||||
return (r, g, b, a);
|
||||
}
|
||||
```
|
||||
|
||||
### Metadata Assertions
|
||||
|
||||
```csharp
|
||||
// Before
|
||||
Assert.AreEqual("Test", ((BitmapMetadata)image.Frames[0].Metadata).Comment);
|
||||
|
||||
// After
|
||||
var props = await decoder.BitmapProperties.GetPropertiesAsync(
|
||||
new[] { "System.Photo.DateTaken" });
|
||||
Assert.IsTrue(props.ContainsKey("System.Photo.DateTaken"),
|
||||
"Metadata should be preserved during transcode");
|
||||
```
|
||||
|
||||
### AllowUnsafeBlocks for SoftwareBitmap Tests
|
||||
|
||||
If tests access pixel data via `IMemoryBufferByteAccess`, add:
|
||||
```xml
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Settings JSON Backward Compatibility
|
||||
|
||||
- Settings are stored in `%LOCALAPPDATA%\Microsoft\PowerToys\<ModuleName>\`
|
||||
- Schema must remain backward-compatible across upgrades
|
||||
- Add new fields with defaults; never remove or rename existing fields
|
||||
- Create custom enums matching WPF enum integer values for deserialization (e.g., `ImagingEnums.cs`)
|
||||
- See: `src/settings-ui/Settings.UI.Library/`
|
||||
|
||||
## IPC Contract
|
||||
|
||||
If the module communicates with the runner or settings UI:
|
||||
1. Update BOTH sides of the IPC contract
|
||||
2. Test settings changes are received by the module
|
||||
3. Test module state changes are reflected in settings UI
|
||||
4. Reference: `doc/devdocs/core/settings/runner-ipc.md`
|
||||
|
||||
---
|
||||
|
||||
## Checklist for PowerToys Module Migration
|
||||
|
||||
### Project & Dependencies
|
||||
- [ ] Update `.csproj`: `UseWPF` → `UseWinUI`, TFM → `net8.0-windows10.0.19041.0`
|
||||
- [ ] Add `WindowsPackageType=None`, `SelfContained=true`, `WindowsAppSDKSelfContained=true`
|
||||
- [ ] Add `DISABLE_XAML_GENERATED_MAIN` if using custom `Program.cs`
|
||||
- [ ] Replace NuGet packages (WPF-UI → remove, add WindowsAppSDK, etc.)
|
||||
- [ ] Update project references (GPOWrapperProjection → GPOWrapper + CsWinRT)
|
||||
- [ ] Move `InternalsVisibleTo` from code to `.csproj`
|
||||
- [ ] Extract CLI shared logic to Library project (avoid Exe→WinExe dependency)
|
||||
|
||||
### MVVM & Resources
|
||||
- [ ] Replace custom `Observable`/`RelayCommand` with CommunityToolkit.Mvvm source generators
|
||||
- [ ] Migrate `.resx` → `.resw` (`Properties/Resources.resx` → `Strings/en-us/Resources.resw`)
|
||||
- [ ] Create `ResourceLoaderInstance` helper
|
||||
- [ ] Wrap resource-dependent statics in `Lazy<T>` or null-coalescing properties
|
||||
- [ ] Delete `Properties/Resources.Designer.cs`, `Observable.cs`, `RelayCommand.cs`
|
||||
|
||||
### XAML
|
||||
- [ ] Replace `clr-namespace:` → `using:` in all xmlns declarations
|
||||
- [ ] Remove WPF-UI (Lepo) xmlns and controls — use native WinUI 3
|
||||
- [ ] Replace `{x:Static p:Resources.Key}` → `x:Uid` with `.resw` keys
|
||||
- [ ] Replace `{DynamicResource}` → `{ThemeResource}`
|
||||
- [ ] Replace `DataType="{x:Type ...}"` → `x:DataType="..."`
|
||||
- [ ] Replace `<Style.Triggers>` → `VisualStateManager`
|
||||
- [ ] Add `<XamlControlsResources/>` to `App.xaml` merged dictionaries
|
||||
- [ ] Move `Window.Resources` to root container's `Resources`
|
||||
- [ ] Run XamlStyler: `.\.pipelines\applyXamlStyling.ps1 -Main`
|
||||
|
||||
### Code-Behind & APIs
|
||||
- [ ] Replace all `System.Windows.*` namespaces with `Microsoft.UI.Xaml.*`
|
||||
- [ ] Replace `Dispatcher` with `DispatcherQueue`
|
||||
- [ ] Store `DispatcherQueue` reference explicitly (no `Application.Current.Dispatcher`)
|
||||
- [ ] Implement `SizeToContent()` via AppWindow if needed
|
||||
- [ ] Update `ContentDialog` calls to set `XamlRoot`
|
||||
- [ ] Update `FilePicker` calls with HWND initialization
|
||||
- [ ] Migrate imaging code to `Windows.Graphics.Imaging` (async, `SoftwareBitmap`)
|
||||
- [ ] Create `CodecHelper` for legacy GUID → WinRT codec ID mapping (if imaging)
|
||||
- [ ] Create custom imaging enums for JSON backward compatibility (if imaging)
|
||||
- [ ] Update all `IValueConverter` signatures (`CultureInfo` → `string`)
|
||||
|
||||
### Build & Installer
|
||||
- [ ] Update WiX installer: remove satellite assembly refs from `Resources.wxs`
|
||||
- [ ] Run `generateAllFileComponents.ps1`; handle phantom artifacts
|
||||
- [ ] Update ESRP signing paths to `WinUI3Apps\\`
|
||||
- [ ] Fix `$(SolutionDir)` → `$(MSBuildThisFileDirectory)` in build events
|
||||
- [ ] Move MSIX packaging from PreBuild to PostBuild
|
||||
- [ ] Fix RC file path escaping (double-backslash)
|
||||
- [ ] Verify output dir is `WinUI3Apps/`
|
||||
|
||||
### Testing & Validation
|
||||
- [ ] Update test project: async methods, `uint` assertions
|
||||
- [ ] Handle ResourceLoader unavailability in tests (hardcode strings or skip)
|
||||
- [ ] Build clean: `cd` to project folder, `tools/build/build.cmd`, exit code 0
|
||||
- [ ] Run tests for affected module
|
||||
- [ ] Verify settings JSON backward compatibility
|
||||
- [ ] Test IPC contracts (runner ↔ settings UI)
|
||||
314
.github/skills/wpf-to-winui3-migration/references/threading-and-windowing.md
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
# Threading and Window Management Migration
|
||||
|
||||
Based on patterns from the ImageResizer migration.
|
||||
|
||||
## Dispatcher → DispatcherQueue
|
||||
|
||||
### API Mapping
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `Dispatcher.Invoke(Action)` | `DispatcherQueue.TryEnqueue(Action)` |
|
||||
| `Dispatcher.BeginInvoke(Action)` | `DispatcherQueue.TryEnqueue(Action)` |
|
||||
| `Dispatcher.Invoke(DispatcherPriority, Action)` | `DispatcherQueue.TryEnqueue(DispatcherQueuePriority, Action)` |
|
||||
| `Dispatcher.CheckAccess()` | `DispatcherQueue.HasThreadAccess` |
|
||||
| `Dispatcher.VerifyAccess()` | Check `DispatcherQueue.HasThreadAccess` (no exception-throwing method) |
|
||||
|
||||
### Priority Mapping
|
||||
|
||||
WinUI 3 has only 3 levels: `High`, `Normal`, `Low`.
|
||||
|
||||
| WPF `DispatcherPriority` | WinUI 3 `DispatcherQueuePriority` |
|
||||
|-------------------------|----------------------------------|
|
||||
| `Send` | `High` |
|
||||
| `Normal` / `Input` / `Loaded` / `Render` / `DataBind` | `Normal` |
|
||||
| `Background` / `ContextIdle` / `ApplicationIdle` / `SystemIdle` | `Low` |
|
||||
|
||||
### Pattern: Global DispatcherQueue Access (from ImageResizer)
|
||||
|
||||
WPF provided `Application.Current.Dispatcher` globally. WinUI 3 requires explicit storage:
|
||||
|
||||
```csharp
|
||||
// Store DispatcherQueue at app startup
|
||||
private static DispatcherQueue _uiDispatcherQueue;
|
||||
|
||||
public static void InitializeDispatcher()
|
||||
{
|
||||
_uiDispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
}
|
||||
```
|
||||
|
||||
Usage with thread-check pattern (from `Settings.Reload()`):
|
||||
```csharp
|
||||
var currentDispatcher = DispatcherQueue.GetForCurrentThread();
|
||||
if (currentDispatcher != null)
|
||||
{
|
||||
// Already on UI thread
|
||||
ReloadCore(jsonSettings);
|
||||
}
|
||||
else if (_uiDispatcherQueue != null)
|
||||
{
|
||||
// Dispatch to UI thread
|
||||
_uiDispatcherQueue.TryEnqueue(() => ReloadCore(jsonSettings));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback (e.g., CLI mode, no UI)
|
||||
ReloadCore(jsonSettings);
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern: DispatcherQueue in ViewModels (from ProgressViewModel)
|
||||
|
||||
```csharp
|
||||
public class ProgressViewModel
|
||||
{
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
|
||||
public ProgressViewModel()
|
||||
{
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
}
|
||||
|
||||
private void OnProgressChanged(double progress)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
Progress = progress;
|
||||
// other UI updates...
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern: Async Dispatch (await)
|
||||
|
||||
```csharp
|
||||
// WPF
|
||||
await this.Dispatcher.InvokeAsync(() => { /* UI work */ });
|
||||
|
||||
// WinUI 3 (using TaskCompletionSource)
|
||||
var tcs = new TaskCompletionSource();
|
||||
this.DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
try { /* UI work */ tcs.SetResult(); }
|
||||
catch (Exception ex) { tcs.SetException(ex); }
|
||||
});
|
||||
await tcs.Task;
|
||||
```
|
||||
|
||||
### C++/WinRT Threading
|
||||
|
||||
| Old API | New API |
|
||||
|---------|---------|
|
||||
| `winrt::resume_foreground(CoreDispatcher)` | `wil::resume_foreground(DispatcherQueue)` |
|
||||
| `CoreDispatcher.RunAsync()` | `DispatcherQueue.TryEnqueue()` |
|
||||
|
||||
Add `Microsoft.Windows.ImplementationLibrary` NuGet for `wil::resume_foreground`.
|
||||
|
||||
---
|
||||
|
||||
## Window Management
|
||||
|
||||
### WPF Window vs WinUI 3 Window
|
||||
|
||||
| Feature | WPF `Window` | WinUI 3 `Window` |
|
||||
|---------|-------------|------------------|
|
||||
| Base class | `ContentControl` → `DependencyObject` | **NOT** a control, NOT a `DependencyObject` |
|
||||
| `Resources` property | Yes | No — use root container's `Resources` |
|
||||
| `DataContext` property | Yes | No — use root `Page`/`UserControl` |
|
||||
| `VisualStateManager` | Yes | No — use inside child controls |
|
||||
| `Load`/`Unload` events | Yes | No |
|
||||
| `SizeToContent` | Yes (`Height`/`Width`/`WidthAndHeight`) | No — must implement manually |
|
||||
| `WindowState` (min/max/normal) | Yes | No — use `AppWindow.Presenter` |
|
||||
| `WindowStyle` | Yes | No — use `AppWindow` title bar APIs |
|
||||
| `ResizeMode` | Yes | No — use `AppWindow.Presenter` |
|
||||
| `WindowStartupLocation` | Yes | No — calculate manually |
|
||||
| `Icon` | `Window.Icon` | `AppWindow.SetIcon()` |
|
||||
| `Title` | `Window.Title` | `AppWindow.Title` (or `Window.Title`) |
|
||||
| Size (Width/Height) | Yes | No — use `AppWindow.Resize()` |
|
||||
| Position (Left/Top) | Yes | No — use `AppWindow.Move()` |
|
||||
| `IsDefault`/`IsCancel` on buttons | Yes | No — handle Enter/Escape in code-behind |
|
||||
|
||||
### Getting AppWindow from Window
|
||||
|
||||
```csharp
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using WinRT.Interop;
|
||||
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
```
|
||||
|
||||
### Pattern: SizeToContent Replacement (from ImageResizer)
|
||||
|
||||
WinUI 3 has no `SizeToContent`. ImageResizer implemented a manual equivalent:
|
||||
|
||||
```csharp
|
||||
private void SizeToContent()
|
||||
{
|
||||
if (Content is not FrameworkElement content)
|
||||
return;
|
||||
|
||||
// Measure desired content size
|
||||
content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
var desiredHeight = content.DesiredSize.Height + WindowChromeHeight + Padding;
|
||||
|
||||
// Account for DPI scaling
|
||||
var scaleFactor = Content.XamlRoot.RasterizationScale;
|
||||
var pixelHeight = (int)(desiredHeight * scaleFactor);
|
||||
var pixelWidth = (int)(WindowWidth * scaleFactor);
|
||||
|
||||
// Resize via AppWindow
|
||||
var hwnd = WindowNative.GetWindowHandle(this);
|
||||
var windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
var appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.Resize(new Windows.Graphics.SizeInt32(pixelWidth, pixelHeight));
|
||||
}
|
||||
```
|
||||
|
||||
**Key details:**
|
||||
- `WindowChromeHeight` ≈ 32px for the title bar
|
||||
- Must multiply by `RasterizationScale` for DPI-aware sizing
|
||||
- Call `SizeToContent()` after page navigation or content changes
|
||||
- Unsubscribe previous event handlers before subscribing new ones to avoid memory leaks
|
||||
|
||||
### Window Positioning (Center Screen)
|
||||
|
||||
```csharp
|
||||
var displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest);
|
||||
var centerX = (displayArea.WorkArea.Width - appWindow.Size.Width) / 2;
|
||||
var centerY = (displayArea.WorkArea.Height - appWindow.Size.Height) / 2;
|
||||
appWindow.Move(new Windows.Graphics.PointInt32(centerX, centerY));
|
||||
```
|
||||
|
||||
### Window State (Minimize/Maximize)
|
||||
|
||||
```csharp
|
||||
(appWindow.Presenter as OverlappedPresenter)?.Maximize();
|
||||
(appWindow.Presenter as OverlappedPresenter)?.Minimize();
|
||||
(appWindow.Presenter as OverlappedPresenter)?.Restore();
|
||||
```
|
||||
|
||||
### Title Bar Customization
|
||||
|
||||
```csharp
|
||||
// Extend content into title bar
|
||||
this.ExtendsContentIntoTitleBar = true;
|
||||
this.SetTitleBar(AppTitleBar); // AppTitleBar is a XAML element
|
||||
|
||||
// Or via AppWindow API
|
||||
if (AppWindowTitleBar.IsCustomizationSupported())
|
||||
{
|
||||
var titleBar = appWindow.TitleBar;
|
||||
titleBar.ExtendsContentIntoTitleBar = true;
|
||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
}
|
||||
```
|
||||
|
||||
### Tracking the Main Window
|
||||
|
||||
```csharp
|
||||
public partial class App : Application
|
||||
{
|
||||
public static Window MainWindow { get; private set; }
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
MainWindow = new MainWindow();
|
||||
MainWindow.Activate();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ContentDialog Requires XamlRoot
|
||||
|
||||
```csharp
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = "Confirm",
|
||||
Content = "Are you sure?",
|
||||
PrimaryButtonText = "Yes",
|
||||
CloseButtonText = "No",
|
||||
XamlRoot = this.Content.XamlRoot // REQUIRED
|
||||
};
|
||||
var result = await dialog.ShowAsync();
|
||||
```
|
||||
|
||||
### File Pickers Require HWND
|
||||
|
||||
```csharp
|
||||
var picker = new FileOpenPicker();
|
||||
picker.FileTypeFilter.Add(".jpg");
|
||||
|
||||
// REQUIRED for desktop apps
|
||||
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
|
||||
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
var file = await picker.PickSingleFileAsync();
|
||||
```
|
||||
|
||||
### Window Close Handling
|
||||
|
||||
```csharp
|
||||
// WPF
|
||||
protected override void OnClosing(CancelEventArgs e) { e.Cancel = true; this.Hide(); }
|
||||
|
||||
// WinUI 3
|
||||
this.AppWindow.Closing += (s, e) => { e.Cancel = true; this.AppWindow.Hide(); };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom Entry Point (DISABLE_XAML_GENERATED_MAIN)
|
||||
|
||||
ImageResizer uses a custom `Program.cs` entry point instead of the WinUI 3 auto-generated `Main`. This is needed for:
|
||||
- CLI mode (process files without showing UI)
|
||||
- Custom initialization before the WinUI 3 App starts
|
||||
- Single-instance enforcement
|
||||
|
||||
### Setup
|
||||
|
||||
In `.csproj`:
|
||||
```xml
|
||||
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
|
||||
```
|
||||
|
||||
Create `Program.cs`:
|
||||
```csharp
|
||||
public static class Program
|
||||
{
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
// CLI mode — no UI
|
||||
return RunCli(args);
|
||||
}
|
||||
|
||||
// GUI mode
|
||||
WinRT.ComWrappersSupport.InitializeComWrappers();
|
||||
Application.Start((p) =>
|
||||
{
|
||||
var context = new DispatcherQueueSynchronizationContext(
|
||||
DispatcherQueue.GetForCurrentThread());
|
||||
SynchronizationContext.SetSynchronizationContext(context);
|
||||
_ = new App();
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### WPF App Constructor Removal
|
||||
|
||||
WPF modules often created `new App()` to initialize the WPF `Application` and get `Application.Current.Dispatcher`. This is no longer needed — the WinUI 3 `Application.Start()` handles this.
|
||||
|
||||
```csharp
|
||||
// DELETE (WPF pattern):
|
||||
_imageResizerApp = new App();
|
||||
// REPLACE with: Store DispatcherQueue explicitly (see Global DispatcherQueue Access above)
|
||||
```
|
||||
365
.github/skills/wpf-to-winui3-migration/references/xaml-migration.md
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
# XAML Migration Guide
|
||||
|
||||
Detailed reference for migrating XAML from WPF to WinUI 3, based on the ImageResizer migration.
|
||||
|
||||
## XML Namespace Declaration Changes
|
||||
|
||||
### Before (WPF)
|
||||
|
||||
```xml
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:MyApp"
|
||||
xmlns:m="clr-namespace:ImageResizer.Models"
|
||||
xmlns:p="clr-namespace:ImageResizer.Properties"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
x:Class="MyApp.MainWindow">
|
||||
```
|
||||
|
||||
### After (WinUI 3)
|
||||
|
||||
```xml
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:MyApp"
|
||||
xmlns:m="using:ImageResizer.Models"
|
||||
xmlns:converters="using:ImageResizer.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Class="MyApp.MainWindow">
|
||||
```
|
||||
|
||||
### Key Changes
|
||||
|
||||
| WPF Syntax | WinUI 3 Syntax | Notes |
|
||||
|------------|---------------|-------|
|
||||
| `clr-namespace:Foo` | `using:Foo` | CLR namespace mapping |
|
||||
| `clr-namespace:Foo;assembly=Bar` | `using:Foo` | Assembly qualification not needed |
|
||||
| `xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"` | **Remove entirely** | WPF-UI namespace no longer needed |
|
||||
| `xmlns:p="clr-namespace:...Properties"` | **Remove** | No more `.resx` string bindings |
|
||||
| `sys:String` (from mscorlib) | `x:String` | XAML intrinsic types |
|
||||
| `sys:Int32` | `x:Int32` | XAML intrinsic types |
|
||||
| `sys:Boolean` | `x:Boolean` | XAML intrinsic types |
|
||||
| `sys:Double` | `x:Double` | XAML intrinsic types |
|
||||
|
||||
## Unsupported Markup Extensions
|
||||
|
||||
| WPF Markup Extension | WinUI 3 Alternative |
|
||||
|----------------------|---------------------|
|
||||
| `{DynamicResource Key}` | `{ThemeResource Key}` (theme-reactive) or `{StaticResource Key}` |
|
||||
| `{x:Static Type.Member}` | `{x:Bind}` to a static property, or code-behind |
|
||||
| `{x:Type local:MyType}` | Not supported; use code-behind |
|
||||
| `{x:Array}` | Not supported; create collections in code-behind |
|
||||
| `{x:Code}` | Not supported |
|
||||
|
||||
### DynamicResource → ThemeResource
|
||||
|
||||
```xml
|
||||
<!-- WPF -->
|
||||
<TextBlock Foreground="{DynamicResource MyBrush}" />
|
||||
|
||||
<!-- WinUI 3 -->
|
||||
<TextBlock Foreground="{ThemeResource MyBrush}" />
|
||||
```
|
||||
|
||||
`ThemeResource` automatically updates when the app theme changes (Light/Dark/HighContrast). For truly dynamic non-theme resources, set values in code-behind or use data binding.
|
||||
|
||||
### x:Static Resource Strings → x:Uid
|
||||
|
||||
This is the most pervasive XAML change. WPF used `{x:Static}` to bind to strongly-typed `.resx` resource strings. WinUI 3 uses `x:Uid` with `.resw` files.
|
||||
|
||||
**WPF:**
|
||||
```xml
|
||||
<Button Content="{x:Static p:Resources.Cancel}" />
|
||||
<TextBlock Text="{x:Static p:Resources.Input_Header}" />
|
||||
```
|
||||
|
||||
**WinUI 3:**
|
||||
```xml
|
||||
<Button x:Uid="Cancel" />
|
||||
<TextBlock x:Uid="Input_Header" />
|
||||
```
|
||||
|
||||
In `Strings/en-us/Resources.resw`:
|
||||
```xml
|
||||
<data name="Cancel.Content" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="Input_Header.Text" xml:space="preserve">
|
||||
<value>Select a size</value>
|
||||
</data>
|
||||
```
|
||||
|
||||
The `x:Uid` suffix (`.Content`, `.Text`, `.Header`, `.PlaceholderText`, etc.) matches the target property name.
|
||||
|
||||
### DataType with x:Type → Remove
|
||||
|
||||
**WPF:**
|
||||
```xml
|
||||
<DataTemplate DataType="{x:Type m:ResizeSize}">
|
||||
```
|
||||
|
||||
**WinUI 3:**
|
||||
```xml
|
||||
<DataTemplate x:DataType="m:ResizeSize">
|
||||
```
|
||||
|
||||
## WPF-UI (Lepo) Controls Removal
|
||||
|
||||
If the module uses the `WPF-UI` library, replace all Lepo controls with native WinUI 3 equivalents.
|
||||
|
||||
### Window
|
||||
|
||||
```xml
|
||||
<!-- WPF (WPF-UI) -->
|
||||
<ui:FluentWindow
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<ui:TitleBar Title="Image Resizer" />
|
||||
...
|
||||
</ui:FluentWindow>
|
||||
|
||||
<!-- WinUI 3 (native) -->
|
||||
<Window>
|
||||
<!-- Title bar managed via code-behind: this.ExtendsContentIntoTitleBar = true; -->
|
||||
...
|
||||
</Window>
|
||||
```
|
||||
|
||||
### App.xaml Resources
|
||||
|
||||
```xml
|
||||
<!-- WPF (WPF-UI) -->
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ThemesDictionary Theme="Dark" />
|
||||
<ui:ControlsDictionary />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
<!-- WinUI 3 (native) -->
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
```
|
||||
|
||||
### Common Control Replacements
|
||||
|
||||
```xml
|
||||
<!-- WPF-UI NumberBox -->
|
||||
<ui:NumberBox Value="{Binding Width}" />
|
||||
<!-- WinUI 3 -->
|
||||
<NumberBox Value="{x:Bind ViewModel.Width, Mode=TwoWay}" />
|
||||
|
||||
<!-- WPF-UI InfoBar -->
|
||||
<ui:InfoBar Title="Warning" Message="..." IsOpen="True" Severity="Warning" />
|
||||
<!-- WinUI 3 -->
|
||||
<InfoBar Title="Warning" Message="..." IsOpen="True" Severity="Warning" />
|
||||
|
||||
<!-- WPF-UI ProgressRing -->
|
||||
<ui:ProgressRing IsIndeterminate="True" />
|
||||
<!-- WinUI 3 -->
|
||||
<ProgressRing IsActive="True" />
|
||||
|
||||
<!-- WPF-UI SymbolIcon -->
|
||||
<ui:SymbolIcon Symbol="Add" />
|
||||
<!-- WinUI 3 -->
|
||||
<SymbolIcon Symbol="Add" />
|
||||
```
|
||||
|
||||
### Button Patterns
|
||||
|
||||
```xml
|
||||
<!-- WPF -->
|
||||
<Button IsDefault="True" Content="OK" />
|
||||
<Button IsCancel="True" Content="Cancel" />
|
||||
|
||||
<!-- WinUI 3 (no IsDefault/IsCancel) -->
|
||||
<Button Style="{StaticResource AccentButtonStyle}" Content="OK" />
|
||||
<Button Content="Cancel" />
|
||||
<!-- Handle Enter/Escape keys in code-behind if needed -->
|
||||
```
|
||||
|
||||
## Style and Template Changes
|
||||
|
||||
### Triggers → VisualStateManager
|
||||
|
||||
WPF `Triggers`, `DataTriggers`, and `EventTriggers` are not supported.
|
||||
|
||||
**WPF:**
|
||||
```xml
|
||||
<Style TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="LightBlue"/>
|
||||
</Trigger>
|
||||
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
```
|
||||
|
||||
**WinUI 3:**
|
||||
```xml
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="LightBlue"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<ContentPresenter />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
```
|
||||
|
||||
### No Binding in Setter.Value
|
||||
|
||||
```xml
|
||||
<!-- WPF (works) -->
|
||||
<Setter Property="Foreground" Value="{Binding TextColor}"/>
|
||||
|
||||
<!-- WinUI 3 (does NOT work — use StaticResource) -->
|
||||
<Setter Property="Foreground" Value="{StaticResource TextColorBrush}"/>
|
||||
```
|
||||
|
||||
### Visual State Name Changes
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `MouseOver` | `PointerOver` |
|
||||
| `Disabled` | `Disabled` |
|
||||
| `Pressed` | `Pressed` |
|
||||
|
||||
## Resource Dictionary Changes
|
||||
|
||||
### Window.Resources → Grid.Resources
|
||||
|
||||
WinUI 3 `Window` is NOT a `DependencyObject` — no `Window.Resources`, `DataContext`, or `VisualStateManager`.
|
||||
|
||||
```xml
|
||||
<!-- WPF -->
|
||||
<Window>
|
||||
<Window.Resources>
|
||||
<SolidColorBrush x:Key="MyBrush" Color="Red"/>
|
||||
</Window.Resources>
|
||||
<Grid>...</Grid>
|
||||
</Window>
|
||||
|
||||
<!-- WinUI 3 -->
|
||||
<Window>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<SolidColorBrush x:Key="MyBrush" Color="Red"/>
|
||||
</Grid.Resources>
|
||||
...
|
||||
</Grid>
|
||||
</Window>
|
||||
```
|
||||
|
||||
### Theme Dictionaries
|
||||
|
||||
```xml
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="MyBrush" Color="#FF000000"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="MyBrush" Color="#FFFFFFFF"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="MyBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
```
|
||||
|
||||
## URI Scheme Changes
|
||||
|
||||
| WPF | WinUI 3 |
|
||||
|-----|---------|
|
||||
| `pack://application:,,,/MyAssembly;component/image.png` | `ms-appx:///Assets/image.png` |
|
||||
| `pack://application:,,,/image.png` | `ms-appx:///image.png` |
|
||||
| Relative path `../image.png` | `ms-appx:///image.png` |
|
||||
|
||||
Assets directory convention: `Resources/` → `Assets/<Module>/`
|
||||
|
||||
## Data Binding Changes
|
||||
|
||||
### {Binding} vs {x:Bind}
|
||||
|
||||
Both are available. Prefer `{x:Bind}` for compile-time safety and performance.
|
||||
|
||||
| Feature | `{Binding}` | `{x:Bind}` |
|
||||
|---------|------------|------------|
|
||||
| Default mode | `OneWay` | **`OneTime`** (explicit `Mode=OneWay` required!) |
|
||||
| Context | `DataContext` | Code-behind class |
|
||||
| Resolution | Runtime | Compile-time |
|
||||
| Performance | Reflection-based | Compiled |
|
||||
| Function binding | No | Yes |
|
||||
|
||||
### WPF-Specific Binding Features to Remove
|
||||
|
||||
```xml
|
||||
<!-- These WPF-only features must be removed or rewritten -->
|
||||
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<!-- WinUI 3: UpdateSourceTrigger not needed; TextBox uses PropertyChanged by default -->
|
||||
<TextBox Text="{x:Bind ViewModel.Value, Mode=TwoWay}" />
|
||||
|
||||
{Binding RelativeSource={RelativeSource Self}, ...}
|
||||
<!-- WinUI 3: Use x:Bind which binds to the page itself, or use ElementName -->
|
||||
|
||||
<ItemsControl ItemsSource="{Binding}" />
|
||||
<!-- WinUI 3: Must specify explicit path -->
|
||||
<ItemsControl ItemsSource="{x:Bind ViewModel.Items}" />
|
||||
```
|
||||
|
||||
## WPF-Only Window Properties to Remove
|
||||
|
||||
These properties exist on WPF `Window` but not WinUI 3:
|
||||
|
||||
```xml
|
||||
<!-- Remove from XAML — handle in code-behind via AppWindow API -->
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ResizeMode="NoResize"
|
||||
ExtendsContentIntoTitleBar="True" <!-- Set in code-behind -->
|
||||
```
|
||||
|
||||
## XAML Control Property Changes
|
||||
|
||||
| WPF Property | WinUI 3 Property | Notes |
|
||||
|-------------|-----------------|-------|
|
||||
| `Focusable` | `IsTabStop` | Different name |
|
||||
| `SnapsToDevicePixels` | Not available | WinUI handles pixel snapping internally |
|
||||
| `UseLayoutRounding` | `UseLayoutRounding` | Same |
|
||||
| `IsHitTestVisible` | `IsHitTestVisible` | Same |
|
||||
| `TextBox.VerticalScrollBarVisibility` | `ScrollViewer.VerticalScrollBarVisibility` (attached) | Attached property |
|
||||
|
||||
## XAML Formatting (XamlStyler)
|
||||
|
||||
After migration, run XamlStyler to normalize formatting:
|
||||
- Alphabetize xmlns declarations and element attributes
|
||||
- Add UTF-8 BOM to all XAML files
|
||||
- Normalize comment spacing: `<!-- text -->` → `<!-- text -->`
|
||||
|
||||
PowerToys command: `.\.pipelines\applyXamlStyling.ps1 -Main`
|
||||
213
.github/workflows/auto-label-issues.yml
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
name: Automatic Triaging on Issue Creation
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
# Manual trigger: go to Actions → "Automatic Triaging on Issue Creation" → Run workflow.
|
||||
# Enter one or more comma-separated issue numbers (e.g. "1234" or "1234,1235,1236")
|
||||
# to apply AI-generated area labels to existing untriaged issues.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_numbers:
|
||||
description: 'Comma-separated issue number(s) to label (e.g. 1234 or 1234,1235)'
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
models: read
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
# Each workflow run gets its own concurrency group.
|
||||
# For issue events, group by issue number so a rapid close+reopen only runs once.
|
||||
# For manual dispatch (which may cover multiple issues), use the unique run ID.
|
||||
group: ${{ github.event_name == 'issues' && format('{0}-issue-{1}', github.workflow, github.event.issue.number) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Apply area labels with AI
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
# actions/github-script does not propagate `github-token` to
|
||||
# process.env. Expose it explicitly so the inline script can
|
||||
# authenticate against the GitHub Models inference endpoint.
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// When triggered manually, process each supplied issue number in turn.
|
||||
// When triggered by an issue event, use the event's issue number.
|
||||
let issueNumbers;
|
||||
if (context.eventName === 'workflow_dispatch') {
|
||||
issueNumbers = String(context.payload.inputs.issue_numbers)
|
||||
.split(',')
|
||||
.map(s => parseInt(s.trim(), 10))
|
||||
.filter(n => Number.isFinite(n) && n > 0);
|
||||
} else {
|
||||
issueNumbers = [context.issue.number];
|
||||
}
|
||||
|
||||
if (issueNumbers.length === 0) {
|
||||
console.log('No valid issue numbers to process; skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const issueNumber of issueNumbers) {
|
||||
console.log(`\n--- Processing issue #${issueNumber} ---`);
|
||||
await labelIssue(issueNumber);
|
||||
}
|
||||
|
||||
async function labelIssue(issueNumber) {
|
||||
// Fetch the issue so both the automatic and manual paths have the same data.
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
|
||||
const title = issue.title ?? '';
|
||||
const body = issue.body ?? '';
|
||||
|
||||
if (!title && !body) {
|
||||
console.log(`Issue #${issueNumber} has no title or body; skipping.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Truncation limit for issue body sent to the model. Keeps the
|
||||
// prompt within the model's context window and avoids high token usage.
|
||||
const MAX_BODY_LENGTH = 4000;
|
||||
|
||||
// Upper bound on model response tokens. A JSON array of label strings
|
||||
// is compact; 200 tokens is more than enough for any realistic response.
|
||||
const MAX_TOKENS = 200;
|
||||
|
||||
// All valid Product-* and Area-* labels the agent may choose from.
|
||||
const VALID_LABELS = [
|
||||
'Product-Advanced Paste',
|
||||
'Product-Always On Top',
|
||||
'Product-Awake',
|
||||
'Product-Color Picker',
|
||||
'Product-CommandNotFound',
|
||||
'Product-Command Palette',
|
||||
'Product-CropAndLock',
|
||||
'Product-Environment Variables',
|
||||
'Product-FancyZones',
|
||||
'Product-File Explorer',
|
||||
'Product-File Locksmith',
|
||||
'Product-Find My Mouse',
|
||||
'Product-Grab And Move',
|
||||
'Product-Hosts File Editor',
|
||||
'Product-Image Resizer',
|
||||
'Product-Keyboard Manager',
|
||||
'Product-LightSwitch',
|
||||
'Product-Mouse Highlighter',
|
||||
'Product-Mouse Jump',
|
||||
'Product-Mouse Pointer Crosshairs',
|
||||
'Product-Mouse Utilities',
|
||||
'Product-Mouse Without Borders',
|
||||
'Product-New+',
|
||||
'Product-Peek',
|
||||
'Product-PowerDisplay',
|
||||
'Product-PowerRename',
|
||||
'Product-PowerToys Run',
|
||||
'Product-Quick Accent',
|
||||
'Product-Registry Preview',
|
||||
'Product-Screen Ruler',
|
||||
'Product-Settings',
|
||||
'Product-Shortcut Guide',
|
||||
'Product-Text Extractor',
|
||||
'Product-Workspaces',
|
||||
'Product-ZoomIt',
|
||||
'Area-Setup/Install',
|
||||
'Area-Localization',
|
||||
];
|
||||
|
||||
const systemPrompt = `You are a GitHub issue triage assistant for the microsoft/PowerToys repository.
|
||||
Your job is to classify issues by assigning the correct area label(s).
|
||||
|
||||
Rules:
|
||||
- Only return labels from the following list, exactly as written:
|
||||
${VALID_LABELS.map(l => ` • ${l}`).join('\n')}
|
||||
- Choose only the labels that clearly match the issue content.
|
||||
- If the issue mentions multiple areas, include a label for each one.
|
||||
- If no label fits, return an empty array.
|
||||
- Respond with ONLY a JSON array of label strings, no explanation.
|
||||
Example: ["Product-FancyZones","Product-Settings"]`;
|
||||
|
||||
const userPrompt = `Issue title: ${title}
|
||||
|
||||
Issue body:
|
||||
${body.slice(0, MAX_BODY_LENGTH)}`;
|
||||
|
||||
// Validate that the token is available before making the API call.
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
if (!token) {
|
||||
console.log('GITHUB_TOKEN is not set; skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the GitHub Models inference endpoint (OpenAI-compatible).
|
||||
const response = await fetch(
|
||||
'https://models.inference.ai.azure.com/chat/completions',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'gpt-4o-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: systemPrompt },
|
||||
{ role: 'user', content: userPrompt },
|
||||
],
|
||||
max_tokens: MAX_TOKENS,
|
||||
// temperature: 0 ensures deterministic, consistent label
|
||||
// classification across similar issues.
|
||||
temperature: 0,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorBody = await response.text();
|
||||
console.log(`GitHub Models API error: ${response.status} ${response.statusText} — ${errorBody}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const text = data.choices?.[0]?.message?.content?.trim() ?? '';
|
||||
console.log(`Model response: ${text}`);
|
||||
|
||||
let suggested;
|
||||
try {
|
||||
suggested = JSON.parse(text);
|
||||
} catch {
|
||||
console.log('Could not parse model response as JSON; skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(suggested) || suggested.length === 0) {
|
||||
console.log('No labels suggested by the model.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Only apply labels that are in the allow-list.
|
||||
const validSet = new Set(VALID_LABELS);
|
||||
const toApply = [...new Set(suggested.filter(l => validSet.has(l)))];
|
||||
|
||||
if (toApply.length === 0) {
|
||||
console.log('Model returned no valid labels.');
|
||||
return;
|
||||
}
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
labels: toApply,
|
||||
});
|
||||
console.log(`Issue #${issueNumber}: added labels: ${toApply.join(', ')}`);
|
||||
}
|
||||
4
.github/workflows/msstore-submissions.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)
|
||||
|
||||
- name: Log in to Azure
|
||||
uses: azure/login@v2
|
||||
uses: azure/login@v3
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
- uses: microsoft/setup-msstore-cli@v1
|
||||
|
||||
- name: Fetch Store Credential
|
||||
uses: azure/cli@v2
|
||||
uses: azure/cli@v3
|
||||
with:
|
||||
azcliversion: latest
|
||||
inlineScript: |-
|
||||
|
||||
31
.github/workflows/spelling2.yml
vendored
@@ -55,7 +55,7 @@ name: Spell checking
|
||||
# spelling:
|
||||
# # remove `security-events: write` and `use_sarif: 1`
|
||||
# # remove `experimental_apply_changes_via_bot: 1`
|
||||
# ... otherwise adjust the `with:` as you wish
|
||||
# ... otherwise, adjust the `with:` as you wish
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -74,6 +74,8 @@ on:
|
||||
types:
|
||||
- "created"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Check Spelling
|
||||
@@ -85,7 +87,7 @@ jobs:
|
||||
outputs:
|
||||
followup: ${{ steps.spelling.outputs.followup }}
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.event_name, 'pull_request') || github.event_name == 'push' }}
|
||||
if: ${{ (contains(github.event_name, 'pull_request') && github.event.pull_request.state == 'open') || github.event_name == 'push' }}
|
||||
concurrency:
|
||||
group: spelling-${{ github.event.pull_request.number || github.ref }}
|
||||
# note: If you use only_check_changed_files, you do not want cancel-in-progress
|
||||
@@ -93,7 +95,7 @@ jobs:
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
config: .github/actions/spell-check
|
||||
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
|
||||
@@ -135,11 +137,12 @@ jobs:
|
||||
cspell:cpp/compiler-msvc.txt
|
||||
cspell:python/common/extra.txt
|
||||
cspell:scala/scala.txt
|
||||
ignored: ignored-expect-variant
|
||||
|
||||
comment-push:
|
||||
name: Report (Push)
|
||||
# If your workflow isn't running on push, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: spelling
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -147,30 +150,23 @@ jobs:
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
config: .github/actions/spell-check
|
||||
checkout: true
|
||||
spell_check_this: microsoft/PowerToys@main
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
comment-pr:
|
||||
name: Report (PR)
|
||||
# If you workflow isn't running on pull_request*, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: spelling
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
config: .github/actions/spell-check
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
|
||||
@@ -180,12 +176,13 @@ jobs:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
actions: read
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
if: ${{
|
||||
github.repository_owner != 'microsoft' &&
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '@check-spelling-bot apply') &&
|
||||
contains(github.event.comment.body, '@check-spelling-bot') &&
|
||||
contains(github.event.comment.body, 'apply') &&
|
||||
contains(github.event.comment.body, 'https://')
|
||||
}}
|
||||
concurrency:
|
||||
@@ -193,7 +190,7 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: apply spelling updates
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
with:
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
checkout: true
|
||||
|
||||
35
.github/workflows/telemetry-pr-check.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# NOTE: This workflow depends on .github/scripts/telemetry-pr-check.js for telemetry detection and PR comments.
|
||||
# Keep this workflow and script behavior in sync when making changes.
|
||||
name: Telemetry PR Check
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: "Pull Request Number to test against"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: telemetry-pr-check-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
detect-telemetry-events:
|
||||
if: ${{ github.event.pull_request.draft == false }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Detect telemetry event changes and comment PR
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: node .github/scripts/telemetry-pr-check.js
|
||||
10
.gitignore
vendored
@@ -360,3 +360,13 @@ src/common/Telemetry/*.etl
|
||||
# PowerToysInstaller Build Temp Files
|
||||
installer/*/*.wxs.bk
|
||||
/src/modules/awake/.claude
|
||||
|
||||
# Claude AI local settings - local-only, not committed
|
||||
**/.claude/settings.local.json
|
||||
|
||||
# Squad / Copilot agents — local-only, not committed
|
||||
.copilot
|
||||
.squad
|
||||
.squad-workstream
|
||||
.github/agents/**squad**.md
|
||||
.github/workflows/**squad**.yml
|
||||
|
||||
@@ -106,7 +106,13 @@
|
||||
"PowerToys.SvgThumbnailProvider.dll",
|
||||
"PowerToys.SvgThumbnailProvider.exe",
|
||||
"PowerToys.SvgThumbnailProviderCpp.dll",
|
||||
"PowerToys.KeyboardManager.dll",
|
||||
|
||||
"KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe",
|
||||
"WinUI3Apps\\PowerToys.KeyboardManagerEditorUI.exe",
|
||||
"WinUI3Apps\\PowerToys.KeyboardManagerEditorUI.dll",
|
||||
"KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe",
|
||||
"PowerToys.KeyboardManagerEditorLibraryWrapper.dll",
|
||||
"WinUI3Apps\\PowerToys.HostsModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.HostsUILib.dll",
|
||||
"WinUI3Apps\\PowerToys.Hosts.dll",
|
||||
@@ -135,13 +141,13 @@
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariables.dll",
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariables.exe",
|
||||
|
||||
"PowerToys.ImageResizer.exe",
|
||||
"PowerToys.ImageResizer.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizer.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizer.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.dll",
|
||||
"PowerToys.ImageResizerExt.dll",
|
||||
"PowerToys.ImageResizerContextMenu.dll",
|
||||
"ImageResizerContextMenuPackage.msix",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerExt.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerContextMenu.dll",
|
||||
"WinUI3Apps\\ImageResizerContextMenuPackage.msix",
|
||||
|
||||
"PowerToys.LightSwitchModuleInterface.dll",
|
||||
"LightSwitchService\\PowerToys.LightSwitchService.exe",
|
||||
@@ -211,7 +217,11 @@
|
||||
"PowerToys.PowerAccentModuleInterface.dll",
|
||||
"PowerToys.PowerAccentKeyboardService.dll",
|
||||
|
||||
"PowerToys.PowerDisplayModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.PowerDisplay.dll",
|
||||
"WinUI3Apps\\PowerToys.PowerDisplay.exe",
|
||||
"PowerDisplay.Lib.dll",
|
||||
"PowerDisplay.Models.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.PowerRenameExt.dll",
|
||||
"WinUI3Apps\\PowerToys.PowerRename.exe",
|
||||
@@ -233,13 +243,20 @@
|
||||
"WinUI3Apps\\PowerToys.RegistryPreview.dll",
|
||||
"WinUI3Apps\\PowerToys.RegistryPreview.exe",
|
||||
|
||||
"PowerToys.ShortcutGuide.exe",
|
||||
"PowerToys.ShortcutGuideModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.exe",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuideModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.dll",
|
||||
"WinUI3Apps\\PowerToys.ShortcutGuide.IndexYmlGenerator.exe",
|
||||
"WinUI3Apps\\ShortcutGuide.CPPProject.dll",
|
||||
|
||||
"PowerToys.ZoomIt.exe",
|
||||
"PowerToys.ZoomItModuleInterface.dll",
|
||||
"PowerToys.ZoomItSettingsInterop.dll",
|
||||
|
||||
"PowerToys.GrabAndMove.exe",
|
||||
"PowerToys.GrabAndMoveModuleInterface.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.Settings.dll",
|
||||
"WinUI3Apps\\PowerToys.Settings.exe",
|
||||
|
||||
@@ -251,8 +268,8 @@
|
||||
"Workspaces.ModuleServices.dll",
|
||||
"Microsoft.CommandPalette.Extensions.dll",
|
||||
"Microsoft.CommandPalette.Extensions.Toolkit.dll",
|
||||
"Microsoft.CmdPal.Ext.PowerToys.dll",
|
||||
"Microsoft.CmdPal.Ext.PowerToys.exe",
|
||||
"WinUI3Apps\\Microsoft.CmdPal.Ext.PowerToys.dll",
|
||||
"WinUI3Apps\\Microsoft.CmdPal.Ext.PowerToys.exe",
|
||||
"*Microsoft.CmdPal.UI_*.msix",
|
||||
|
||||
"PowerToys.DSC.dll",
|
||||
@@ -370,6 +387,10 @@
|
||||
"ColorCode.Core.dll",
|
||||
"Microsoft.SemanticKernel.Connectors.Ollama.dll",
|
||||
"OllamaSharp.dll",
|
||||
"WinUI3Apps\\Google.Apis.dll",
|
||||
"WinUI3Apps\\Google.Apis.Auth.dll",
|
||||
"WinUI3Apps\\Google.Apis.Core.dll",
|
||||
"WinUI3Apps\\Google.GenAI.dll",
|
||||
|
||||
"boost_regex-vc143-mt-gd-x32-1_87.dll",
|
||||
"boost_regex-vc143-mt-gd-x64-1_87.dll",
|
||||
|
||||
@@ -9,7 +9,7 @@ schedules:
|
||||
always: false # only run if there's code changes!
|
||||
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
vmImage: windows-latest
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
|
||||
@@ -16,6 +16,7 @@ pr:
|
||||
include:
|
||||
- main
|
||||
- stable
|
||||
drafts: false
|
||||
# paths:
|
||||
# exclude:
|
||||
# - '**.md'
|
||||
|
||||
@@ -70,7 +70,7 @@ parameters:
|
||||
default: false
|
||||
- name: winAppSDKVersionNumber
|
||||
type: string
|
||||
default: 1.6
|
||||
default: '2.0'
|
||||
- name: useExperimentalVersion
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -200,7 +200,7 @@ jobs:
|
||||
- template: steps-ensure-dotnet-version.yml
|
||||
parameters:
|
||||
sdk: true
|
||||
version: '9.0'
|
||||
version: '10.0'
|
||||
|
||||
- ${{ if eq(parameters.runTests, true) }}:
|
||||
- task: VisualStudioTestPlatformInstaller@1
|
||||
@@ -210,6 +210,9 @@ jobs:
|
||||
& '.pipelines/applyXamlStyling.ps1' -Passive
|
||||
displayName: Verify XAML formatting
|
||||
|
||||
- task: NuGetAuthenticate@1
|
||||
displayName: Authenticate NuGet feeds for verification
|
||||
|
||||
- pwsh: |-
|
||||
& '.pipelines/verifyNugetPackages.ps1' -solution '$(build.sourcesdirectory)\PowerToys.slnx'
|
||||
displayName: Verify Nuget package versions for PowerToys.slnx
|
||||
@@ -415,7 +418,7 @@ jobs:
|
||||
/p:VCRTForwarders-IncludeDebugCRT=false
|
||||
/p:PowerToysRoot=$(Build.SourcesDirectory)
|
||||
/p:PublishProfile=InstallationPublishProfile.pubxml
|
||||
/p:TargetFramework=net9.0-windows10.0.26100.0
|
||||
/p:TargetFramework=net10.0-windows10.0.26100.0
|
||||
/bl:$(LogOutputDirectory)\publish-${{ join('_',split(project, '/')) }}.binlog
|
||||
$(RestoreAdditionalProjectSourcesArg)
|
||||
platform: $(BuildPlatform)
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
- template: steps-ensure-dotnet-version.yml
|
||||
parameters:
|
||||
sdk: true
|
||||
version: '9.0'
|
||||
version: '10.0'
|
||||
|
||||
- template: .\steps-restore-nuget.yml
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ parameters:
|
||||
default: false
|
||||
- name: winAppSDKVersionNumber
|
||||
type: string
|
||||
default: 1.6
|
||||
default: '2.0'
|
||||
- name: useExperimentalVersion
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -27,6 +27,7 @@ stages:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
demands: ImageOverride -equals SHINE-VS18-Latest
|
||||
buildPlatforms:
|
||||
- ${{ parameters.platform }}
|
||||
uiTestModules: ${{ parameters.uiTestModules }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parameters:
|
||||
- name: version
|
||||
type: string
|
||||
default: "9.0"
|
||||
default: "10.0"
|
||||
- name: sdk
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parameters:
|
||||
- name: versionNumber
|
||||
type: string
|
||||
default: 1.6
|
||||
default: '2.0'
|
||||
- name: useExperimentalVersion
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -22,7 +22,7 @@ $totalList = $projFiles | ForEach-Object -Parallel {
|
||||
#Workaround for preventing exit code from dotnet process from reflecting exit code in PowerShell
|
||||
$procInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{
|
||||
FileName = "dotnet.exe";
|
||||
Arguments = "list $csproj package";
|
||||
Arguments = "list $csproj package --no-restore";
|
||||
RedirectStandardOutput = $true;
|
||||
RedirectStandardError = $true;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ $nonDirectoryAssetsItems = Get-ChildItem $targetAssetsDir -Attributes !Directory
|
||||
$directoryAssetsItems = Get-ChildItem $targetAssetsDir -Attributes Directory
|
||||
|
||||
if ($directoryAssetsItems.Count -le 0) {
|
||||
Write-Host -ForegroundColor Red "No directories detected in " $nonDirectoryAssetsItems ". Are you sure this is the right path?`r`n"
|
||||
Write-Host -ForegroundColor Red "ERROR: No directories detected in " $nonDirectoryAssetsItems ". Are you sure this is the right path?`r`n"
|
||||
$totalFailures++;
|
||||
} elseif ($nonDirectoryAssetsItems.Count -gt 0) {
|
||||
Write-Host -ForegroundColor Red "Detected " $nonDirectoryAssetsItems " files in " $targetAssetsDir "`r`n"
|
||||
Write-Host -ForegroundColor Red "ERROR: Detected " $nonDirectoryAssetsItems " files in " $targetAssetsDir ". Each application should use a named subdirectory for assets.`r`n"
|
||||
$totalFailures++;
|
||||
} else {
|
||||
Write-Host -ForegroundColor Green "Only directories detected in " $targetAssetsDir "`r`n"
|
||||
@@ -29,7 +29,7 @@ if ($directoryAssetsItems.Count -le 0) {
|
||||
# Make sure there's no resources.pri file. Each application should use a different name for their own resources file path.
|
||||
$resourcesPriFiles = Get-ChildItem $targetDir -Filter resources.pri
|
||||
if ($resourcesPriFiles.Count -gt 0) {
|
||||
Write-Host -ForegroundColor Red "Detected a resources.pri file in " $targetDir "`r`n"
|
||||
Write-Host -ForegroundColor Red "ERROR: Detected a resources.pri file in " $targetDir ". Each application should use a unique name for its resources file.`r`n"
|
||||
$totalFailures++;
|
||||
} else {
|
||||
Write-Host -ForegroundColor Green "No resources.pri file detected in " $targetDir "`r`n"
|
||||
@@ -38,7 +38,7 @@ if ($resourcesPriFiles.Count -gt 0) {
|
||||
# Each application should have their XAML files in their own paths to avoid these conflicts.
|
||||
$resourcesPriFiles = Get-ChildItem $targetDir -Filter *.xbf
|
||||
if ($resourcesPriFiles.Count -gt 0) {
|
||||
Write-Host -ForegroundColor Red "Detected a .xbf file in " $targetDir "`r`n"
|
||||
Write-Host -ForegroundColor Red "ERROR: Detected a .xbf file in " $targetDir ". Ensure all XAML files are placed in a subdirectory in each application.`r`n"
|
||||
$totalFailures++;
|
||||
} else {
|
||||
Write-Host -ForegroundColor Green "No .xbf files detected in " $targetDir "`r`n"
|
||||
|
||||
3
.vscode/settings.json
vendored
@@ -13,5 +13,6 @@
|
||||
{
|
||||
"file": ".github/prompts/create-pr-summary.prompt.md"
|
||||
}
|
||||
]
|
||||
],
|
||||
"sarif-viewer.connectToGithubCodeScanning": "on"
|
||||
}
|
||||
39
AGENTS.md
@@ -3,7 +3,7 @@ description: 'Top-level AI contributor guidance for developing PowerToys - a col
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
# PowerToys – AI Contributor Guide
|
||||
# PowerToys – AI contributor guide
|
||||
|
||||
This is the top-level guidance for AI contributions to PowerToys. Keep changes atomic, follow existing patterns, and cite exact paths in PRs.
|
||||
|
||||
@@ -26,13 +26,15 @@ For architecture details and module types, see [Architecture Overview](doc/devdo
|
||||
## Conventions
|
||||
|
||||
For detailed coding conventions, see:
|
||||
|
||||
- [Coding Guidelines](doc/devdocs/development/guidelines.md) – Dependencies, testing, PR management
|
||||
- [Coding Style](doc/devdocs/development/style.md) – Formatting, C++/C#/XAML style rules
|
||||
- [Logging](doc/devdocs/development/logging.md) – C++ spdlog and C# Logger usage
|
||||
|
||||
### Component-Specific Instructions
|
||||
### Component-specific instructions
|
||||
|
||||
These instruction files are automatically applied when working in their respective areas:
|
||||
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md) – IPC contracts, schema migrations
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md) – ABI stability, shared code guidelines
|
||||
|
||||
@@ -44,7 +46,7 @@ These instruction files are automatically applied when working in their respecti
|
||||
- Windows 10 1803+ (April 2018 Update or newer)
|
||||
- Initialize submodules once: `git submodule update --init --recursive`
|
||||
|
||||
### Build Commands
|
||||
### Build commands
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
@@ -52,7 +54,7 @@ These instruction files are automatically applied when working in their respecti
|
||||
| Build current folder | `tools\build\build.cmd` |
|
||||
| Build with options | `build.ps1 -Platform x64 -Configuration Release` |
|
||||
|
||||
### Build Discipline
|
||||
### Build discipline
|
||||
|
||||
1. One terminal per operation (build → test). Do not switch or open new ones mid-flow
|
||||
2. After making changes, `cd` to the project folder that changed (`.csproj`/`.vcxproj`)
|
||||
@@ -62,9 +64,10 @@ These instruction files are automatically applied when working in their respecti
|
||||
6. On failure, read the errors log: `build.<config>.<platform>.errors.log`
|
||||
7. Do not start tests or launch Runner until the build succeeds
|
||||
|
||||
### Build Logs
|
||||
### Build logs
|
||||
|
||||
Located next to the solution/project being built:
|
||||
|
||||
- `build.<configuration>.<platform>.errors.log` – errors only (check this first)
|
||||
- `build.<configuration>.<platform>.all.log` – full log
|
||||
- `build.<configuration>.<platform>.trace.binlog` – for MSBuild Structured Log Viewer
|
||||
@@ -73,18 +76,18 @@ For complete details, see [Build Guidelines](tools/build/BUILD-GUIDELINES.md).
|
||||
|
||||
## Tests
|
||||
|
||||
### Test Discovery
|
||||
### Test discovery
|
||||
|
||||
- Find test projects by product code prefix (e.g., `FancyZones`, `AdvancedPaste`)
|
||||
- Look for sibling folders or 1-2 levels up named `<Product>*UnitTests` or `<Product>*UITests`
|
||||
|
||||
### Running Tests
|
||||
### Running tests
|
||||
|
||||
1. **Build the test project first**, wait for exit code 0
|
||||
2. Run via VS Test Explorer (`Ctrl+E, T`) or `vstest.console.exe` with filters
|
||||
3. **Avoid `dotnet test`** in this repo – use VS Test Explorer or vstest.console.exe
|
||||
|
||||
### Test Types
|
||||
### Test types
|
||||
|
||||
| Type | Requirements | Setup |
|
||||
|------|--------------|-------|
|
||||
@@ -92,13 +95,13 @@ For complete details, see [Build Guidelines](tools/build/BUILD-GUIDELINES.md).
|
||||
| UI Tests | WinAppDriver v1.2.1, Developer Mode | Install from [WinAppDriver releases](https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1) |
|
||||
| Fuzz Tests | OneFuzz, .NET 8 | See [Fuzzing Tests](doc/devdocs/tools/fuzzingtesting.md) |
|
||||
|
||||
### Test Discipline
|
||||
### Test discipline
|
||||
|
||||
1. Add or adjust tests when changing behavior
|
||||
2. If tests skipped, state why (e.g., comment-only change, string rename)
|
||||
3. New modules handling file I/O or user input **must** implement fuzzing tests
|
||||
|
||||
### Special Requirements
|
||||
### Special requirements
|
||||
|
||||
- **Mouse Without Borders**: Requires 2+ physical computers (not VMs)
|
||||
- **Multi-monitor utilities**: Test with 2+ monitors, different DPI settings
|
||||
@@ -107,14 +110,14 @@ For UI test setup details, see [UI Tests](doc/devdocs/development/ui-tests.md).
|
||||
|
||||
## Boundaries
|
||||
|
||||
### Ask for Clarification When
|
||||
### Ask for clarification when
|
||||
|
||||
- Ambiguous spec after scanning relevant docs
|
||||
- Cross-module impact (shared enum/struct) is unclear
|
||||
- Security, elevation, or installer changes involved
|
||||
- GPO or policy handling modifications needed
|
||||
|
||||
### Areas Requiring Extra Care
|
||||
### Areas requiring extra care
|
||||
|
||||
| Area | Concern | Reference |
|
||||
|------|---------|-----------|
|
||||
@@ -123,7 +126,7 @@ For UI test setup details, see [UI Tests](doc/devdocs/development/ui-tests.md).
|
||||
| Installer files | Release impact | Careful review required |
|
||||
| Elevation/GPO logic | Security | Confirm no regression in policy handling |
|
||||
|
||||
### What NOT to Do
|
||||
### What not to do
|
||||
|
||||
- Don't merge incomplete features into main (use feature branches)
|
||||
- Don't break IPC/JSON contracts without updating both runner and settings-ui
|
||||
@@ -143,23 +146,27 @@ Before finishing, verify:
|
||||
|
||||
## Documentation Index
|
||||
|
||||
### Core Architecture
|
||||
### Core architecture
|
||||
|
||||
- [Architecture Overview](doc/devdocs/core/architecture.md)
|
||||
- [Runner](doc/devdocs/core/runner.md)
|
||||
- [Settings System](doc/devdocs/core/settings/readme.md)
|
||||
- [Module Interface](doc/devdocs/modules/interface.md)
|
||||
|
||||
### Development
|
||||
|
||||
- [Coding Guidelines](doc/devdocs/development/guidelines.md)
|
||||
- [Coding Style](doc/devdocs/development/style.md)
|
||||
- [Logging](doc/devdocs/development/logging.md)
|
||||
- [UI Tests](doc/devdocs/development/ui-tests.md)
|
||||
- [Fuzzing Tests](doc/devdocs/tools/fuzzingtesting.md)
|
||||
|
||||
### Build & Tools
|
||||
### Build & tools
|
||||
|
||||
- [Build Guidelines](tools/build/BUILD-GUIDELINES.md)
|
||||
- [Tools Overview](doc/devdocs/tools/readme.md)
|
||||
|
||||
### Instructions (Auto-Applied)
|
||||
### Instructions (auto-applied)
|
||||
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md)
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md)
|
||||
|
||||
121
COMMUNITY.md
@@ -1,84 +1,109 @@
|
||||
# Community
|
||||
|
||||
The PowerToys team is extremely grateful to have the support of an amazing active community. The work you do is incredibly important. PowerToys wouldn’t be near what it is without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thanks and to recognize your work. This is a living document dedicated to highlighting the high impact community members and their contributions.
|
||||
The PowerToys team is extremely grateful to have the support of an amazing active community. The work you do is incredibly important. PowerToys wouldn't be near what it is without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thanks and to recognize your work. This is a living document dedicated to highlighting the high impact community members and their contributions.
|
||||
|
||||
Names are in alphabetical order based on first name.
|
||||
Names are in alphabetical order, based on first name.
|
||||
|
||||
## High impact community members
|
||||
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
Christian contributed New+ utility
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
|
||||
Christian contributed the New+ utility
|
||||
|
||||
### [@CleanCodeDeveloper](https://github.com/CleanCodeDeveloper)
|
||||
|
||||
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
|
||||
|
||||
### [@plante-msft](https://github.com/plante-msft) - Connor Plante
|
||||
|
||||
Connor was the creator of Workspaces and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
|
||||
|
||||
Damien has helped out by developing and contributing the Quick Accent utility.
|
||||
|
||||
### [@daverayment](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/)
|
||||
|
||||
Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs.
|
||||
|
||||
### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/)
|
||||
|
||||
Davide has helped fix multiple bugs, added new utilities, features, as well as help us with the ARM64 effort by porting applications to .NET Core.
|
||||
|
||||
### [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang
|
||||
|
||||
Ethan helped run PowerToys and worked on improving and prototyping out next generation PowerToys
|
||||
|
||||
### [@franky920920](https://github.com/franky920920) - [Franky Chen](https://frankychen.net)
|
||||
|
||||
Franky has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes to PowerToys.
|
||||
|
||||
### [@htcfreek](https://github.com/htcfreek) - Heiko
|
||||
|
||||
Heiko has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes to PowerToys.
|
||||
|
||||
### [@Jay-o-Way](https://github.com/Jay-o-Way) - Jay
|
||||
|
||||
Jay has helped triaging, discussing, creating a substantial number of issues and PRs.
|
||||
|
||||
### [@jefflord](https://github.com/Jjefflord) - Jeff Lord
|
||||
Jeff added in multiple new features into Keyboard manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
Jeff added multiple new features to Keyboard Manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
|
||||
Jeremy has helped drive substantial ARM64 support within PowerToys.
|
||||
|
||||
### [@jiripolasek](https://github.com/jiripolasek) - [Jiří Polášek](https://github.com/jiripolasek)
|
||||
|
||||
Jiří has contributed a massive number of features and improvements to Command Palette, including drag & drop support, custom themes, Web Search enhancements, Remote Desktop extension fixes, and many UX improvements.
|
||||
|
||||
### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com)
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
Joe has helped with triaging, discussing issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
### [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie
|
||||
|
||||
Jordi helped innovate amazing new features into Advanced Paste and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
|
||||
### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/)
|
||||
|
||||
Helping keep our spelling correct :)
|
||||
|
||||
### [@martinchrzan](https://github.com/martinchrzan/) - Martin Chrzan
|
||||
|
||||
Color Picker is from Martin.
|
||||
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
|
||||
|
||||
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
|
||||
|
||||
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
|
||||
### [@royvou](https://github.com/royvou)
|
||||
|
||||
Roy has helped out contributing multiple features to PowerToys Run
|
||||
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
ThiefZero has helped contribute features to PowerToys Run, such as the unit converter plugin
|
||||
|
||||
### [@TobiasSekan](https://github.com/TobiasSekan) - Tobias Sekan
|
||||
|
||||
Tobias Sekan has helped out contributing features to PowerToys Run such as Settings plugin, Registry plugin
|
||||
|
||||
## Open source projects
|
||||
@@ -94,7 +119,8 @@ Their fork of Wox was the base of PowerToys Run.
|
||||
Initial base of jjw24's fork, which makes it the base of PowerToys Run.
|
||||
|
||||
### [Text-Grab](https://github.com/TheJoeFin/Text-Grab) - Joseph Finney
|
||||
Joe helped develop and contribute to the Text Extractor utility. It is directly based on his Text Grab application.
|
||||
|
||||
Joe helped develop and contribute to the Text Extractor utility. It is directly based on his Text Grab application.
|
||||
|
||||
## Microsoft community members
|
||||
|
||||
@@ -102,7 +128,7 @@ We would like to also directly call out some extremely helpful Microsoft employe
|
||||
|
||||
### [@betsegaw](https://github.com/betsegaw/) - [Betsegaw Tadele](http://www.dreamsofameaningfullife.com/)
|
||||
|
||||
Window Walker, inside PowerToys Run, is from Beta.
|
||||
Window Walker, inside PowerToys Run, is from Beta.
|
||||
|
||||
### [@TheMrJukes](https://github.com/TheMrJukes/) - Bret Anderson
|
||||
|
||||
@@ -125,6 +151,7 @@ PowerToys Awake is a tool to keep your computer awake.
|
||||
Randy contributed Registry Preview and some very early conversations about keyboard remapping.
|
||||
|
||||
### [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon
|
||||
|
||||
Kayla was a former lead for PowerToys and helped create multiple utilities, maintained the GitHub repo, and collaborated with the community to improve the overall product
|
||||
|
||||
### [@oldnewthing](https://github.com/oldnewthing) - Raymond Chen
|
||||
@@ -135,46 +162,48 @@ Find My Mouse is based on Raymond Chen's SuperSonar.
|
||||
|
||||
Crop And Lock is based on the original work of Robert Mikhayelyan, with Program Manager support from [@kevinguo305](https://github.com/kevinguo305) - Kevin Guo.
|
||||
|
||||
ZoomIt's Video Recording Session code is based on Robert Mikhayelyan's https://github.com/robmikh/capturevideosample code.
|
||||
ZoomIt's Video Recording Session code is based on Robert Mikhayelyan's <https://github.com/robmikh/capturevideosample> code.
|
||||
|
||||
### Microsoft InVEST team
|
||||
|
||||
This amazing team helped PowerToys develop PowerToys Run and Keyboard manager as well as update our Settings to v2. @alekhyareddy28, @arjunbalgovind, @jyuwono @laviusmotileng-ms, @ryanbodrug-microsoft, @saahmedm, @somil55, @traies, @udit3333
|
||||
This amazing team helped PowerToys develop PowerToys Run and Keyboard manager as well as update our Settings to v2. @alekhyareddy28, @arjunbalgovind, @jyuwono @laviusmotileng-ms, @ryanbodrug-microsoft, @saahmedm, @somil55, @traies, @udit3333
|
||||
|
||||
## Mouse Without Borders original contributors
|
||||
*Project creator: Truong Do (Đỗ Đức Trường)*
|
||||
|
||||
Project creator: Truong Do (Đỗ Đức Trường)
|
||||
|
||||
Other contributors:
|
||||
* Microsoft Garage: Quinn Hawkins, Michael Low, Joe Coplen, Nino Yuniardi, Gwyneth Marshall, David Andrews, Karen Luecking
|
||||
* Peter Hauge - Visual Studio
|
||||
* Bruce Dawson - Windows Fundamentals
|
||||
* Alan Myrvold - Office Security
|
||||
* Adrian Garside - WEX
|
||||
* Scott Bradner - Surface
|
||||
* Aleks Gershaft - Windows Azure
|
||||
* Chinh Huynh - Windows Azure
|
||||
* Long Nguyen - Data Center
|
||||
* Triet Le - Cloud Engineering
|
||||
* Luke Schoen - Excel
|
||||
* Bao Nguyen - Bing
|
||||
* Ross Nichols - Windows
|
||||
* Ryan Baltazar - Windows
|
||||
* Ed Essey - The Garage
|
||||
* Mario Madden - The Garage
|
||||
* Karthick Mahalingam - ACE
|
||||
* Pooja Kamra - ACE
|
||||
* Justin White - SA
|
||||
* Chris Ransom - SA
|
||||
* Mike Ricks - Red Team
|
||||
* Randy Santossio - Surface
|
||||
* Ashish Sen Jaswal - Device Health
|
||||
* Zoltan Harmath - Security Tools
|
||||
* Luciano Krigun - Security Products
|
||||
* Jo Hemmerlein - Red Team
|
||||
* Chris Johnson - Surface Hub
|
||||
* Loren Ponten - Surface Hub
|
||||
* Paul Schmitt - WWL
|
||||
* And many other Users!
|
||||
|
||||
- Microsoft Garage: Quinn Hawkins, Michael Low, Joe Coplen, Nino Yuniardi, Gwyneth Marshall, David Andrews, Karen Luecking
|
||||
- Peter Hauge - Visual Studio
|
||||
- Bruce Dawson - Windows Fundamentals
|
||||
- Alan Myrvold - Office Security
|
||||
- Adrian Garside - WEX
|
||||
- Scott Bradner - Surface
|
||||
- Aleks Gershaft - Windows Azure
|
||||
- Chinh Huynh - Windows Azure
|
||||
- Long Nguyen - Data Center
|
||||
- Triet Le - Cloud Engineering
|
||||
- Luke Schoen - Excel
|
||||
- Bao Nguyen - Bing
|
||||
- Ross Nichols - Windows
|
||||
- Ryan Baltazar - Windows
|
||||
- Ed Essey - The Garage
|
||||
- Mario Madden - The Garage
|
||||
- Karthick Mahalingam - ACE
|
||||
- Pooja Kamra - ACE
|
||||
- Justin White - SA
|
||||
- Chris Ransom - SA
|
||||
- Mike Ricks - Red Team
|
||||
- Randy Santossio - Surface
|
||||
- Ashish Sen Jaswal - Device Health
|
||||
- Zoltan Harmath - Security Tools
|
||||
- Luciano Krigun - Security Products
|
||||
- Jo Hemmerlein - Red Team
|
||||
- Chris Johnson - Surface Hub
|
||||
- Loren Ponten - Surface Hub
|
||||
- Paul Schmitt - WWL
|
||||
- And many other Users!
|
||||
|
||||
## ZoomIt original contributors
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# PowerToys Contributor's Guide
|
||||
# PowerToys contributor's guide
|
||||
|
||||
Below is our guidance for reporting issues, proposing new features, and submitting contributions via Pull Requests (PRs). Our philosophy is to understand the problem and scenarios first, which is why we follow this pattern before work starts.
|
||||
|
||||
@@ -6,46 +6,46 @@ Below is our guidance for reporting issues, proposing new features, and submitti
|
||||
2. There has been a conversation.
|
||||
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation).
|
||||
|
||||
## Filing an Issue
|
||||
## Filing an issue
|
||||
|
||||
**Importance of Filing an Issue First**
|
||||
|
||||
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyone’s time:
|
||||
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyone's time:
|
||||
|
||||
> 👉 If you have a question, think you've discovered an issue, or would like to propose a new feature, please find/file an issue **BEFORE** starting work to fix/implement it.
|
||||
|
||||
When requesting new features or enhancements, providing additional evidence, data, tweets, blog posts, or research is extremely helpful. This information gives context to the scenario that may otherwise be lost.
|
||||
|
||||
* Unsure whether it’s an issue or feature request? File an issue.
|
||||
* Have a question that isn't answered in the docs, videos, etc.? File an issue.
|
||||
* Want to know if we’re planning a particular feature? File an issue.
|
||||
* Got a great idea for a new utility or feature? File an issue/request/idea.
|
||||
* Don’t understand how to do something? File an issue/Community Guidance Request.
|
||||
* Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
|
||||
- Unsure whether it's an issue or feature request? File an issue.
|
||||
- Have a question that isn't answered in the docs, videos, etc.? File an issue.
|
||||
- Want to know if we're planning a particular feature? File an issue.
|
||||
- Got a great idea for a new utility or feature? File an issue/request/idea.
|
||||
- Don't understand how to do something? File an issue/Community Guidance Request.
|
||||
- Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
|
||||
|
||||
A quick search before filing an issue could be helpful. It’s likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
|
||||
A quick search before filing an issue could be helpful. It's likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
|
||||
|
||||
### Indicating Interest in Issues
|
||||
### Indicating interest in issues
|
||||
|
||||
To let the team know which issues are important, upvote by clicking the [+😊] button and the 👍 icon on the original issue post. Avoid comments like "+1" or "me too" as they clutter the discussion and make it harder to prioritize requests.
|
||||
|
||||
---
|
||||
|
||||
## Contributing Fixes/Features
|
||||
## Contributing fixes or features
|
||||
|
||||
Please comment on our ["Would you like to contribute to PowerToys?"](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
|
||||
Please comment on our [Would you like to contribute to PowerToys?](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
|
||||
|
||||
### Localization Issues
|
||||
### Localization issues
|
||||
|
||||
For localization issues, please file an issue to notify our internal localization team, as community PRs for localization aren't accepted. Localization is handled exclusively by the internal Microsoft team.
|
||||
|
||||
### To Spec or Not to Spec
|
||||
### To spec or not to spec
|
||||
|
||||
A key point is for everyone to understand the approach that will be taken. We want to be sure that any work done will be accepted. Larger-scope items will require a spec to outline the approach and allow for discussion. Specs help collaborators consider different solutions, describe feature behavior, and plan for errors. Achieving agreement in a spec before writing code often results in simpler code and less wasted effort.
|
||||
|
||||
Once a team member has agreed with your approach, proceed to the "Development" section below. Team members are happy to help review specs and guide them to completion.
|
||||
|
||||
### Help Wanted
|
||||
### Help wanted
|
||||
|
||||
Once the team has approved an issue/spec approach, development can proceed. If no developers are immediately available, the spec may be parked and labeled "Help Wanted," ready for a developer to get started. For development opportunities, visit [Issues labeled Help Wanted](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
|
||||
|
||||
@@ -55,18 +55,18 @@ Once the team has approved an issue/spec approach, development can proceed. If n
|
||||
|
||||
Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md).
|
||||
|
||||
### Naming Features and Functionality
|
||||
### Naming features and functionality
|
||||
|
||||
Names should be descriptive and straightforward, clearly reflecting functionality and usefulness.
|
||||
|
||||
### Becoming a Collaborator on the PowerToys Team
|
||||
### Becoming a collaborator on the PowerToys team
|
||||
|
||||
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
|
||||
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
|
||||
|
||||
When the time comes, Microsoft will reach out to you about becoming a formal team member. Just make sure they have a way to contact you. 😊
|
||||
|
||||
---
|
||||
|
||||
## Thank You
|
||||
## Thank you
|
||||
|
||||
Thank you in advance for your contribution! We appreciate your help in making PowerToys a better tool for everyone.
|
||||
|
||||
1717
DATA_AND_PRIVACY.md
@@ -20,6 +20,7 @@
|
||||
<NuGetAuditMode>direct</NuGetAuditMode>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <!-- Don't add source revision hash to the product version of binaries. -->
|
||||
<PlatformTarget>$(Platform)</PlatformTarget>
|
||||
<RestoreEnablePackagePruning Condition=" '$(VisualStudioVersion)' == '17.0'">false </RestoreEnablePackagePruning>
|
||||
|
||||
<!-- Enable Microsoft.Testing.Platform -->
|
||||
<EnableMSTestRunner>true</EnableMSTestRunner>
|
||||
@@ -170,6 +171,12 @@
|
||||
$(USERPROFILE)\AppData\LocalLow\Microsoft\PowerToys\**;
|
||||
</MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns>
|
||||
|
||||
<!-- dotnet.exe seems to access files after builds. Temporarily putting in this entry for testing if we get further. This looks to be related to a .NET Roslyn Analyzer in .NET 10-->
|
||||
<MSBuildCacheAllowFileAccessAfterProjectFinishProcessPatterns>
|
||||
$(MSBuildCacheAllowFileAccessAfterProjectFinishProcessPatterns);
|
||||
\**\dotnet\dotnet.exe;
|
||||
\**\vbcscompiler.exe;
|
||||
</MSBuildCacheAllowFileAccessAfterProjectFinishProcessPatterns>
|
||||
<!--
|
||||
This repo uses a common output directory with many projects writing duplicate outputs. Allow everything, but note this costs some performance in the form of requiring
|
||||
the cache to use copies instead of hardlinks when pulling from cache.
|
||||
|
||||
@@ -65,4 +65,20 @@
|
||||
<!-- Note: For C++ skipped test projects, build is effectively skipped by removing all compile items above.
|
||||
We don't define empty Build/Rebuild/Clean targets here because MSBuild Target definitions with Condition
|
||||
on the Target element still override the default targets even when condition is false. -->
|
||||
|
||||
<!-- Clean up unused VC++ runtime DLLs that CopyCppRuntimeToOutputDir copies from the full
|
||||
VCRedist tree (MFC, C++ AMP, OpenMP). No PowerToys binary links against these — verified
|
||||
with dumpbin /dependents across all installed binaries. -->
|
||||
<Target Name="RemoveUnusedVCRuntimeDlls"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(CopyCppRuntimeToOutputDir)' == 'true' and '$(MSBuildProjectExtension)' == '.vcxproj'">
|
||||
<ItemGroup>
|
||||
<_UnusedVCRuntimeDlls Include="$(OutDir)mfc140*.dll" />
|
||||
<_UnusedVCRuntimeDlls Include="$(OutDir)mfcm140*.dll" />
|
||||
<_UnusedVCRuntimeDlls Include="$(OutDir)vcamp140*.dll" />
|
||||
<_UnusedVCRuntimeDlls Include="$(OutDir)vcomp140*.dll" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(_UnusedVCRuntimeDlls)" Condition="'@(_UnusedVCRuntimeDlls)' != ''" />
|
||||
<Message Importance="normal" Text="Cleaned up unused VC runtime DLLs: @(_UnusedVCRuntimeDlls)" Condition="'@(_UnusedVCRuntimeDlls)' != ''" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||
@@ -18,15 +18,15 @@
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.12" />
|
||||
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.260116-build.2514" />
|
||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
||||
@@ -39,49 +39,49 @@
|
||||
<PackageVersion Include="Markdig.Signed" Version="0.34.0" />
|
||||
<!-- 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.CommandPalette.Extensions" Version="0.5.250829002" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.10" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.102" />
|
||||
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.9.260303001" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.7" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageVersion Include="Microsoft.Windows.CppWinRT" Version="2.0.250303.1" />
|
||||
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
|
||||
<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.Extensions.AI" Version="10.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.0.1-preview.1.25571.5" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.7" />
|
||||
<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.AzureAIInference" Version="1.66.0-beta" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.66.0-alpha" />
|
||||
<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.SemanticKernel" Version="1.71.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.71.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.71.0-beta" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.71.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.71.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.71.0-alpha" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3719.77" />
|
||||
<!-- 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.10" />
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.10" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.269" />
|
||||
<!-- 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. -->
|
||||
<!--
|
||||
TODO: in Common.Dotnet.CsWinRT.props, on upgrade, verify RemoveCsWinRTPackageAnalyzer is no longer needed.
|
||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.231216.1"/>
|
||||
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.250325.1"/>
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.260209005" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.260203002" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.47" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.260209005" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.1" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="2.0.20" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="2.0.185" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="2.0.1" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||
@@ -90,11 +90,11 @@
|
||||
<PackageVersion Include="MSTest" Version="$(MSTestVersion)" />
|
||||
<PackageVersion Include="MSTest.TestFramework" Version="$(MSTestVersion)" />
|
||||
<PackageVersion Include="NJsonSchema" Version="11.4.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<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.5.0" />
|
||||
<PackageVersion Include="OpenAI" Version="2.7.0" />
|
||||
<PackageVersion Include="Polly.Core" Version="8.6.5" />
|
||||
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
|
||||
<PackageVersion Include="RtfPipe" Version="2.0.7677.4303" />
|
||||
@@ -106,31 +106,28 @@
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.10" />
|
||||
<PackageVersion Include="System.Collections.Immutable" Version="9.0.0" />
|
||||
<PackageVersion Include="System.CodeDom" Version="10.0.7" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<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" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="10.0.7" />
|
||||
<!-- 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.10" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="10.0.7" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.10" />
|
||||
<PackageVersion Include="System.ClientModel" Version="1.7.0" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.10" />
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="10.0.7" />
|
||||
<PackageVersion Include="System.ClientModel" Version="1.8.1" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="10.0.7" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.10" />
|
||||
<PackageVersion Include="System.Management" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.11" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.2" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.10" />
|
||||
<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.Runtime.Caching" Version="10.0.7" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.7" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="ToolGood.Words.Pinyin" Version="3.1.0.3" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
@@ -138,8 +135,8 @@
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.6.0" />
|
||||
<PackageVersion Include="WinUIEx" Version="2.8.0" />
|
||||
<PackageVersion Include="WmiLight" Version="6.14.0" />
|
||||
<PackageVersion Include="WPF-UI" Version="3.0.5" />
|
||||
<PackageVersion Include="WyHash" Version="1.0.5" />
|
||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||
<PackageVersion Include="WixToolset.Heat" Version="5.0.2" />
|
||||
<PackageVersion Include="WixToolset.Firewall.wixext" Version="5.0.2" />
|
||||
<PackageVersion Include="WixToolset.Util.wixext" Version="5.0.2" />
|
||||
|
||||
90
NOTICE.md
@@ -17,7 +17,7 @@ This software incorporates material from third parties.
|
||||
|
||||
### Martin Chrzan's Color Picker
|
||||
|
||||
**Source**: https://github.com/martinchrzan/ColorPicker
|
||||
**Source**: <https://github.com/martinchrzan/ColorPicker>
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -49,7 +49,7 @@ We use the WyHash NuGet package for calculating stable hashes for strings.
|
||||
|
||||
**Source**: [https://github.com/wangyi-fudan/wyhash](https://github.com/wangyi-fudan/wyhash)
|
||||
|
||||
```
|
||||
```text
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
@@ -82,7 +82,7 @@ We use the ToolGood.Words.Pinyin NuGet package for converting Chinese characters
|
||||
|
||||
**Source**: [https://github.com/toolgood/ToolGood.Words.Pinyin](https://github.com/toolgood/ToolGood.Words.Pinyin)
|
||||
|
||||
```
|
||||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 ToolGood
|
||||
@@ -106,8 +106,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
|
||||
## Utility: Command Palette Built-in Extensions
|
||||
## Utility: Command palette built-in extensions
|
||||
|
||||
### Calculator
|
||||
|
||||
@@ -117,7 +116,7 @@ We use the exprtk library (exprtk.hpp) to evaluate mathematical expressions.
|
||||
|
||||
**Source**: [https://github.com/ArashPartow/exprtk](https://github.com/ArashPartow/exprtk)
|
||||
|
||||
```
|
||||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (c) 1999-2024 Arash Partow
|
||||
@@ -144,7 +143,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
```
|
||||
|
||||
## Utility: PowerToys Run Built-in Extensions
|
||||
## Utility: PowerToys Run built-in extensions
|
||||
|
||||
### Calculator
|
||||
|
||||
@@ -154,7 +153,7 @@ We use the Mages NuGet package for calculating the result of expression.
|
||||
|
||||
**Source**: [https://github.com/FlorianRappl/Mages](https://github.com/FlorianRappl/Mages)
|
||||
|
||||
```
|
||||
```text
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 - 2025 Florian Rappl
|
||||
@@ -178,13 +177,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## Utility: File Explorer Add-ins
|
||||
## Utility: File Explorer add-ins
|
||||
|
||||
### Monaco Editor
|
||||
|
||||
**Source**: https://github.com/Microsoft/monaco-editor
|
||||
**Source**: <https://github.com/Microsoft/monaco-editor>
|
||||
|
||||
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
|
||||
**Additional third party notifications:** <https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -208,9 +207,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### The Quite OK Image Format reference decoder
|
||||
### The Quite OK image format reference decoder
|
||||
|
||||
**Source**: https://github.com/phoboslab/qoi
|
||||
**Source**: <https://github.com/phoboslab/qoi>
|
||||
|
||||
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys from the original C++ implementation.
|
||||
|
||||
@@ -240,9 +239,9 @@ SOFTWARE.
|
||||
|
||||
We use the UTF.Unknown NuGet package for detecting encoding in text/code files.
|
||||
|
||||
**Source**: https://github.com/CharsetDetector/UTF-unknown
|
||||
**Source**: <https://github.com/CharsetDetector/UTF-unknown>
|
||||
|
||||
```
|
||||
```text
|
||||
MOZILLA PUBLIC LICENSE
|
||||
Version 1.1
|
||||
|
||||
@@ -716,9 +715,9 @@ EXHIBIT A -Mozilla Public License.
|
||||
|
||||
## Utility: ImageResizer
|
||||
|
||||
### Brice Lams's Image Resizer License
|
||||
### Brice Lams's Image Resizer license
|
||||
|
||||
**Source**: https://github.com/bricelam/ImageResizer/
|
||||
**Source**: <https://github.com/bricelam/ImageResizer/>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -744,10 +743,10 @@ THE SOFTWARE.
|
||||
|
||||
## Utility: PowerToys Run
|
||||
|
||||
### Wox License
|
||||
### Wox license
|
||||
|
||||
**Fork project source**: https://github.com/jjw24/Wox/
|
||||
**Base project source**: https://github.com/Wox-launcher/Wox
|
||||
**Fork project source**: <https://github.com/jjw24/Wox/>
|
||||
**Base project source**: <https://github.com/Wox-launcher/Wox>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -770,9 +769,9 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Beta Tadele's Window Walker License
|
||||
### Beta Tadele's Window Walker license
|
||||
|
||||
**Source**: https://github.com/betsegaw/windowwalker
|
||||
**Source**: <https://github.com/betsegaw/windowwalker>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -786,9 +785,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
## Utility: PowerRename
|
||||
|
||||
### Chris Davis's SmartRename License
|
||||
### Chris Davis's SmartRename license
|
||||
|
||||
**Source**: https://github.com/chrdavis/SmartRename
|
||||
**Source**: <https://github.com/chrdavis/SmartRename>
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -816,7 +815,7 @@ SOFTWARE.
|
||||
|
||||
### spdlog
|
||||
|
||||
**Source**: https://github.com/gabime/spdlog
|
||||
**Source**: <https://github.com/gabime/spdlog>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -841,12 +840,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-- NOTE: Third party dependency used by this software --
|
||||
This software depends on the fmt lib (MIT License),
|
||||
and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst
|
||||
This software depends on the fmt lib (MIT License), and users must comply to its license:
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>
|
||||
|
||||
### expected-lite
|
||||
|
||||
**Source**: https://github.com/martinmoene/expected-lite
|
||||
**Source**: <https://github.com/martinmoene/expected-lite>
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
@@ -874,7 +873,7 @@ DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### zip
|
||||
|
||||
**Source**: https://github.com/kuba--/zip
|
||||
**Source**: <https://github.com/kuba--/zip>
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
@@ -902,7 +901,7 @@ THE SOFTWARE.
|
||||
|
||||
We adopted some functions from it.
|
||||
|
||||
**Source**: https://github.com/DLTcollab/sse2neon
|
||||
**Source**: <https://github.com/DLTcollab/sse2neon>
|
||||
|
||||
sse2neon is freely redistributable under the MIT License.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -925,9 +924,9 @@ SOFTWARE.
|
||||
|
||||
### Monaco Editor
|
||||
|
||||
**Source**: https://github.com/Microsoft/monaco-editor
|
||||
**Source**: <https://github.com/Microsoft/monaco-editor>
|
||||
|
||||
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
|
||||
**Additional third party notifications:** <https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt>
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -951,11 +950,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### The Quite OK Image Format reference decoder
|
||||
### The Quite OK image format reference decoder
|
||||
|
||||
**Source**: https://github.com/phoboslab/qoi
|
||||
**Source**: <https://github.com/phoboslab/qoi>
|
||||
|
||||
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys from the original C++ implementation.
|
||||
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys, from the original C++ implementation.
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -979,13 +978,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### UTF Unknown
|
||||
### UTF unknown
|
||||
|
||||
We use the UTF.Unknown NuGet package for detecting encoding in text/code files.
|
||||
|
||||
**Source**: https://github.com/CharsetDetector/UTF-unknown
|
||||
**Source**: <https://github.com/CharsetDetector/UTF-unknown>
|
||||
|
||||
```
|
||||
```text
|
||||
MOZILLA PUBLIC LICENSE
|
||||
Version 1.1
|
||||
|
||||
@@ -1463,9 +1462,9 @@ EXHIBIT A -Mozilla Public License.
|
||||
|
||||
We use HexBox.WinUI to show a preview of binary values.
|
||||
|
||||
**Source**: https://github.com/hotkidfamily/HexBox.WinUI
|
||||
**Source**: <https://github.com/hotkidfamily/HexBox.WinUI>
|
||||
|
||||
```
|
||||
```text
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Filip Jeremic
|
||||
@@ -1492,11 +1491,11 @@ SOFTWARE.
|
||||
|
||||
### Monaco Editor
|
||||
|
||||
**Source**: https://github.com/Microsoft/monaco-editor
|
||||
**Source**: <https://github.com/Microsoft/monaco-editor>
|
||||
|
||||
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
|
||||
**Additional third party notifications:** <https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt>
|
||||
|
||||
```
|
||||
```text
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 - present Microsoft Corporation
|
||||
@@ -1526,7 +1525,7 @@ SOFTWARE.
|
||||
|
||||
PowerDisplay's DDC/CI implementation references techniques from Twinkle Tray.
|
||||
|
||||
**Source**: https://github.com/xanderfrangos/twinkle-tray
|
||||
**Source**: <https://github.com/xanderfrangos/twinkle-tray>
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -1588,7 +1587,6 @@ SOFTWARE.
|
||||
- NLog.Extensions.Logging
|
||||
- NLog.Schema
|
||||
- OpenAI
|
||||
- Polly.Core
|
||||
- ReverseMarkdown
|
||||
- ScipBe.Common.Office.OneNote
|
||||
- SharpCompress
|
||||
@@ -1602,5 +1600,5 @@ SOFTWARE.
|
||||
- UTF.Unknown
|
||||
- WinUIEx
|
||||
- WmiLight
|
||||
- WPF-UI
|
||||
- WyHash
|
||||
- YamlDotNet
|
||||
100
PowerToys.slnx
@@ -206,6 +206,10 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Actions/Microsoft.CmdPal.Ext.Actions.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -319,6 +323,10 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.Indexer.UnitTests/Microsoft.CmdPal.Ext.Indexer.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.Registry.UnitTests/Microsoft.CmdPal.Ext.Registry.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
@@ -427,7 +435,7 @@
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/FileLocksmith/">
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49D456D3-F485-45AF-8875-45B44F193DDC" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/FileLocksmithCLI.vcxproj" Id="49d456d3-f485-45af-8875-45b44f193ddc" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj" Id="799a50d8-de89-4ed1-8ff8-ad5a9ed8c0ca" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj" Id="57175ec7-92a5-4c1e-8244-e3fbca2a81de" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithLib/FileLocksmithLib.vcxproj" Id="9d52fd25-ef90-4f9a-a015-91efc5daf54f" />
|
||||
@@ -438,7 +446,7 @@
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/FileLocksmith/Tests/">
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="A1B2C3D4-E5F6-7890-1234-567890ABCDEF" />
|
||||
<Project Path="src/modules/FileLocksmith/FileLocksmithCLI/tests/FileLocksmithCLIUnitTests.vcxproj" Id="a1b2c3d4-e5f6-7890-1234-567890abcdef" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/Hosts/">
|
||||
<Project Path="src/modules/Hosts/Hosts/Hosts.csproj">
|
||||
@@ -464,13 +472,13 @@
|
||||
</Folder>
|
||||
<Folder Name="/modules/imageresizer/">
|
||||
<Project Path="src/modules/imageresizer/dll/ImageResizerExt.vcxproj" Id="0b43679e-edfa-4da0-ad30-f4628b308b1b" />
|
||||
<Project Path="src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj" Id="93b72a06-c8bd-484f-a6f7-c9f280b150bf" />
|
||||
<Project Path="src/modules/imageresizer/ImageResizerLib/ImageResizerLib.vcxproj" Id="18b3db45-4ffe-4d01-97d6-5223feee1853" />
|
||||
<Project Path="src/modules/imageresizer/ui/ImageResizerUI.csproj">
|
||||
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
|
||||
<Project Path="src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj" Id="93b72a06-c8bd-484f-a6f7-c9f280b150bf" />
|
||||
<Project Path="src/modules/imageresizer/ImageResizerLib/ImageResizerLib.vcxproj" Id="18b3db45-4ffe-4d01-97d6-5223feee1853" />
|
||||
<Project Path="src/modules/imageresizer/ui/ImageResizerUI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
@@ -497,6 +505,31 @@
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj" Id="ba661f5b-1d5a-4ffc-9bf1-fc39df280bdd" />
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManagerEngineLibrary.vcxproj" Id="e496b7fc-1e99-4bab-849b-0e8367040b02" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/MouseUtils/">
|
||||
<Project Path="src/modules/MouseUtils/CursorWrap/CursorWrap.vcxproj" Id="48a1db8c-5df8-4fb3-9e14-2b67f3f2d8b5" />
|
||||
<Project Path="src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj" Id="e94fd11c-0591-456f-899f-efc0ca548336" />
|
||||
<Project Path="src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.vcxproj" Id="782a61be-9d85-4081-b35c-1ccc9dcc1e88" />
|
||||
<Project Path="src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MouseJump/MouseJump.vcxproj" Id="8a08d663-4995-40e3-b42c-3f910625f284" />
|
||||
<Project Path="src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MousePointerCrosshairs/MousePointerCrosshairs.vcxproj" Id="eae14c0e-7a6b-45da-9080-a7d8c077ba6e" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/MouseUtils/Tests/">
|
||||
<Project Path="src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MouseUtils.UITests/MouseUtils.UITests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/keyboardmanager/Tests/">
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEditorTest/KeyboardManagerEditorTest.vcxproj" Id="62173d9a-6724-4c00-a1c8-fb646480a9ec" />
|
||||
<Project Path="src/modules/keyboardmanager/KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj" Id="7f4b3a60-bc27-45a7-8000-68b0b6ea7466" />
|
||||
@@ -684,17 +717,19 @@
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerDisplay/">
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay.Models/PowerDisplay.Models.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay.Lib/PowerDisplay.Lib.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<!-- TEMPORARILY_DISABLED: PowerDisplay
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplayModuleInterface/PowerDisplayModuleInterface.vcxproj" Id="d1234567-8901-2345-6789-abcdef012345" />
|
||||
-->
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerDisplay/Tests/">
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay.Lib.UnitTests/PowerDisplay.Lib.UnitTests.csproj">
|
||||
@@ -720,31 +755,6 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/MouseUtils/">
|
||||
<Project Path="src/modules/MouseUtils/CursorWrap/CursorWrap.vcxproj" Id="48a1db8c-5df8-4fb3-9e14-2b67f3f2d8b5" />
|
||||
<Project Path="src/modules/MouseUtils/FindMyMouse/FindMyMouse.vcxproj" Id="e94fd11c-0591-456f-899f-efc0ca548336" />
|
||||
<Project Path="src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.vcxproj" Id="782a61be-9d85-4081-b35c-1ccc9dcc1e88" />
|
||||
<Project Path="src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MouseJump/MouseJump.vcxproj" Id="8a08d663-4995-40e3-b42c-3f910625f284" />
|
||||
<Project Path="src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MousePointerCrosshairs/MousePointerCrosshairs.vcxproj" Id="eae14c0e-7a6b-45da-9080-a7d8c077ba6e" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/MouseUtils/Tests/">
|
||||
<Project Path="src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/MouseUtils/MouseUtils.UITests/MouseUtils.UITests.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/MouseWithoutBorders/">
|
||||
<Project Path="src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
@@ -979,9 +989,16 @@
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/shortcutguide/">
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj" Id="2edb3eb4-fa92-4bff-b2d8-566584837231" />
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="2d604c07-51fc-46bb-9eb7-75aecc7f5e81" />
|
||||
<Folder Name="/modules/ShortcutGuide/">
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.IndexYmlGenerator/ShortcutGuide.IndexYmlGenerator.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuide.Ui.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="e487304a-b1fb-4e6b-8e70-014051af5b99" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/Workspaces/">
|
||||
<Project Path="src/modules/Workspaces/Workspaces.ModuleServices/Workspaces.ModuleServices.csproj">
|
||||
@@ -1027,10 +1044,17 @@
|
||||
<File Path="src/modules/Workspaces/workspaces-common/WindowUtils.h" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/ZoomIt/">
|
||||
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomIt/ZoomIt.vcxproj" Id="0a84f764-3a88-44cd-aa96-41bdbd48627b">
|
||||
<BuildDependency Project="src/modules/ZoomIt/ZoomItBreak/ZoomItBreak.vcxproj" />
|
||||
</Project>
|
||||
<Project Path="src/modules/ZoomIt/ZoomItBreak/ZoomItBreak.vcxproj" Id="94ba3051-c8d7-454a-9d46-1a7c78e228a3" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomItModuleInterface/ZoomItModuleInterface.vcxproj" Id="e4585179-2ac1-4d5f-a3ff-cfc5392f694c" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/GrabAndMove/">
|
||||
<Project Path="src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj" Id="568c4c30-2e3c-4c2c-a691-007362073765" />
|
||||
<Project Path="src/modules/GrabAndMove/GrabAndMoveModuleInterface/GrabAndMoveModuleInterface.vcxproj" Id="2c3f7770-4e57-46b7-8dc1-7428a383d0db" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
@@ -1095,6 +1119,8 @@
|
||||
<BuildDependency Project="src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchModuleInterface/LightSwitchModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj" />
|
||||
<BuildDependency Project="src/modules/GrabAndMove/GrabAndMoveModuleInterface/GrabAndMoveModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/dll/PowerRenameExt.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/lib/PowerRenameLib.vcxproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/Common/PreviewHandlerCommon.csproj" />
|
||||
|
||||
319
README.md
@@ -19,51 +19,35 @@
|
||||
<span> · </span>
|
||||
<a href="#-whats-new">Release notes</a>
|
||||
</h3>
|
||||
<br/><br/>
|
||||
|
||||
## 🔨 Utilities
|
||||
|
||||
PowerToys includes over 25 utilities to help you customize and optimize your Windows experience:
|
||||
PowerToys includes over 30 utilities to help you customize and optimize your Windows experience:
|
||||
|
||||
| | | |
|
||||
|---|---|---|
|
||||
| --- | --- | --- |
|
||||
| [<img src="doc/images/icons/AdvancedPaste.png" alt="Advanced Paste icon" height="16"> Advanced Paste](https://aka.ms/PowerToysOverview_AdvancedPaste) | [<img src="doc/images/icons/Always%20On%20Top.png" alt="Always on Top icon" height="16"> Always on Top](https://aka.ms/PowerToysOverview_AoT) | [<img src="doc/images/icons/Awake.png" alt="Awake icon" height="16"> Awake](https://aka.ms/PowerToysOverview_Awake) |
|
||||
| [<img src="doc/images/icons/Color%20Picker.png" alt="Color Picker icon" height="16"> Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [<img src="doc/images/icons/Command%20Not%20Found.png" alt="Command Not Found icon" height="16"> Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | [<img src="doc/images/icons/Command Palette.png" alt="Command Palette icon" height="16"> Command Palette](https://aka.ms/PowerToysOverview_CmdPal) |
|
||||
| [<img src="doc/images/icons/Crop%20And%20Lock.png" alt="Crop and Lock icon" height="16"> Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [<img src="doc/images/icons/Environment%20Manager.png" alt="Environment Variables icon" height="16"> Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [<img src="doc/images/icons/FancyZones.png" alt="FancyZones icon" height="16"> FancyZones](https://aka.ms/PowerToysOverview_FancyZones) |
|
||||
| [<img src="doc/images/icons/File%20Explorer%20Preview.png" alt="File Explorer Add-ons icon" height="16"> File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [<img src="doc/images/icons/File%20Locksmith.png" alt="File Locksmith icon" height="16"> File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [<img src="doc/images/icons/Host%20File%20Editor.png" alt="Hosts File Editor icon" height="16"> Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) |
|
||||
| [<img src="doc/images/icons/Image%20Resizer.png" alt="Image Resizer icon" height="16"> Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [<img src="doc/images/icons/Keyboard%20Manager.png" alt="Keyboard Manager icon" height="16"> Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [<img src="doc/images/icons/Light Switch.png" alt="Light Switch icon" height="16"> Light Switch](https://aka.ms/PowerToysOverview_LightSwitch) |
|
||||
| [<img src="doc/images/icons/Find My Mouse.png" alt="Mouse Utilities icon" height="16"> Mouse Utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [<img src="doc/images/icons/MouseWithoutBorders.png" alt="Mouse Without Borders icon" height="16"> Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [<img src="doc/images/icons/NewPlus.png" alt="New+ icon" height="16"> New+](https://aka.ms/PowerToysOverview_NewPlus) |
|
||||
| [<img src="doc/images/icons/Peek.png" alt="Peek icon" height="16"> Peek](https://aka.ms/PowerToysOverview_Peek) | [<img src="doc/images/icons/PowerRename.png" alt="PowerRename icon" height="16"> PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [<img src="doc/images/icons/PowerToys%20Run.png" alt="PowerToys Run icon" height="16"> PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) |
|
||||
| [<img src="doc/images/icons/PowerAccent.png" alt="Quick Accent icon" height="16"> Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [<img src="doc/images/icons/Registry%20Preview.png" alt="Registry Preview icon" height="16"> Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [<img src="doc/images/icons/MeasureTool.png" alt="Screen Ruler icon" height="16"> Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) |
|
||||
| [<img src="doc/images/icons/Shortcut%20Guide.png" alt="Shortcut Guide icon" height="16"> Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [<img src="doc/images/icons/PowerOCR.png" alt="Text Extractor icon" height="16"> Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [<img src="doc/images/icons/Workspaces.png" alt="Workspaces icon" height="16"> Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
|
||||
| [<img src="doc/images/icons/ZoomIt.png" alt="ZoomIt icon" height="16"> ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) | | |
|
||||
| [<img src="doc/images/icons/File%20Explorer%20Preview.png" alt="File Explorer Add-ons icon" height="16"> File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [<img src="doc/images/icons/File%20Locksmith.png" alt="File Locksmith icon" height="16"> File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [<img src="doc/images/icons/GrabAndMove.png" alt="Grab And Move icon" height="16"> Grab And Move](https://aka.ms/PowerToysOverview_GrabAndMove) |
|
||||
| [<img src="doc/images/icons/Host%20File%20Editor.png" alt="Hosts File Editor icon" height="16"> Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [<img src="doc/images/icons/Image%20Resizer.png" alt="Image Resizer icon" height="16"> Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [<img src="doc/images/icons/Keyboard%20Manager.png" alt="Keyboard Manager icon" height="16"> Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) |
|
||||
| [<img src="doc/images/icons/Light Switch.png" alt="Light Switch icon" height="16"> Light Switch](https://aka.ms/PowerToysOverview_LightSwitch) | [<img src="doc/images/icons/Find My Mouse.png" alt="Mouse Utilities icon" height="16"> Mouse Utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [<img src="doc/images/icons/MouseWithoutBorders.png" alt="Mouse Without Borders icon" height="16"> Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) |
|
||||
| [<img src="doc/images/icons/NewPlus.png" alt="New+ icon" height="16"> New+](https://aka.ms/PowerToysOverview_NewPlus) | [<img src="doc/images/icons/Peek.png" alt="Peek icon" height="16"> Peek](https://aka.ms/PowerToysOverview_Peek) | [<img src="doc/images/icons/PowerDisplay.png" alt="PowerDisplay icon" height="16"> PowerDisplay](https://aka.ms/PowerToysOverview_PowerDisplay) |
|
||||
| [<img src="doc/images/icons/PowerRename.png" alt="PowerRename icon" height="16"> PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [<img src="doc/images/icons/PowerToys%20Run.png" alt="PowerToys Run icon" height="16"> PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [<img src="doc/images/icons/PowerAccent.png" alt="Quick Accent icon" height="16"> Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) |
|
||||
| [<img src="doc/images/icons/Registry%20Preview.png" alt="Registry Preview icon" height="16"> Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [<img src="doc/images/icons/MeasureTool.png" alt="Screen Ruler icon" height="16"> Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [<img src="doc/images/icons/Shortcut%20Guide.png" alt="Shortcut Guide icon" height="16"> Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
|
||||
| [<img src="doc/images/icons/PowerOCR.png" alt="Text Extractor icon" height="16"> Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [<img src="doc/images/icons/Workspaces.png" alt="Workspaces icon" height="16"> Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | [<img src="doc/images/icons/ZoomIt.png" alt="ZoomIt icon" height="16"> ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) |
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
## 📋 Installation
|
||||
|
||||
For detailed installation instructions and system requirements, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
|
||||
For detailed installation instructions and system requirements, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
|
||||
|
||||
But to get started quickly, choose one of the installation methods below:
|
||||
<br/><br/>
|
||||
<details open>
|
||||
<summary><strong>Download .exe from GitHub</strong></summary>
|
||||
<summary><strong>Download the .exe file from GitHub</strong></summary>
|
||||
<br/>
|
||||
Go to the <a href="https://aka.ms/installPowerToys">PowerToys GitHub releases</a>, click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.98%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.97%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysUserSetup-0.97.1-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysUserSetup-0.97.1-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysSetup-0.97.1-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysSetup-0.97.1-arm64.exe
|
||||
|
||||
| Description | Filename |
|
||||
|----------------|----------|
|
||||
| Per user - x64 | [PowerToysUserSetup-0.97.1-x64.exe][ptUserX64] |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.97.1-arm64.exe][ptUserArm64] |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.97.1-x64.exe][ptMachineX64] |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.97.1-arm64.exe][ptMachineArm64] |
|
||||
Go to the [PowerToys GitHub releases](https://aka.ms/installPowerToys), scroll down and select **Assets** to reveal the installation files, and choose the one that matches your architecture and install scope. For most devices, that would be _x64 per-user_.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -83,14 +67,16 @@ You can easily install PowerToys from the Microsoft Store:
|
||||
<details>
|
||||
<summary><strong>WinGet</strong></summary>
|
||||
<br/>
|
||||
Download PowerToys from <a href="https://github.com/microsoft/winget-cli#installing-the-client">WinGet</a>. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
|
||||
Download PowerToys from [WinGet](https://github.com/microsoft/winget-cli#installing-the-client). Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
|
||||
|
||||
- User scope installer (default)
|
||||
|
||||
*User scope installer [default]*
|
||||
```powershell
|
||||
winget install Microsoft.PowerToys -s winget
|
||||
```
|
||||
|
||||
*Machine-wide scope installer*
|
||||
- Machine-wide scope installer
|
||||
|
||||
```powershell
|
||||
winget install --scope machine Microsoft.PowerToys -s winget
|
||||
```
|
||||
@@ -99,265 +85,36 @@ winget install --scope machine Microsoft.PowerToys -s winget
|
||||
<details>
|
||||
<summary><strong>Other methods</strong></summary>
|
||||
<br/>
|
||||
There are <a href="https://learn.microsoft.com/windows/powertoys/install#community-driven-install-tools">community driven install methods</a> such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there.
|
||||
There are [community driven install methods](https://learn.microsoft.com/windows/powertoys/install#community-driven-install-tools) such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there.
|
||||
</details>
|
||||
|
||||
## ✨ What's new
|
||||
## ✨ What's new?
|
||||
|
||||
**Version 0.97.2 (Feb 2026)**
|
||||
[](https://github.com/microsoft/PowerToys/releases)
|
||||
|
||||
This patch release fixes several important stability issues identified in v0.97.0 based on incoming reports. Check out the [v0.97.0](https://github.com/microsoft/PowerToys/releases/tag/v0.97.0) notes for the full list of changes.
|
||||
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/).
|
||||
|
||||
## Advanced Paste
|
||||
- #45207 Fixed a crash in the Advanced Paste settings page caused by null values during JSON deserialization.
|
||||
## 🛣️ Roadmap
|
||||
|
||||
## Color Picker
|
||||
- #45367 Fixed contrast issue in Color picker UI.
|
||||
We are planning some nice new features and improvements for the next releases – a brand-new Shortcut Guide experience, ensuring it's easier to find and install Command Palette extensions and so much more! Stay tuned for [v0.100][github-next-release-work]!
|
||||
|
||||
## Command Palette
|
||||
- #45194 Fixed an issue where some Command Palette PowerToys Extension strings were not localised.
|
||||
## ❤️ PowerToys Community
|
||||
|
||||
## Cursor Wrap
|
||||
- #45210 Fixed "Automatically activate on utility startup" setting not persisting when disabled. Thanks [@ThanhNguyxn](https://github.com/ThanhNguyxn)!
|
||||
- #45303 Added option to disable Cursor Wrapping when only a single monitor is connected. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
|
||||
## Image Resizer
|
||||
- #45184 Fixed Image Resizer not working after upgrading PowerToys on Windows 10 by properly cleaning up legacy sparse app packages.
|
||||
|
||||
## LightSwitch
|
||||
- #45304 Fixed Light Switch startup logic to correctly apply the appropriate theme on launch.
|
||||
|
||||
## Workspaces
|
||||
- #45183 Fixed overlay positioning issue in workspace snapshot draw caused by DPI-aware coordinate mismatch.
|
||||
|
||||
## Quick Access and Measure Tool
|
||||
- #45443 Fixed crash related to `IsShownInSwitchers` property when Explorer is not running.
|
||||
|
||||
**Version 0.97.1 (January 2026)**
|
||||
|
||||
**Highlights**
|
||||
|
||||
### Advanced Paste
|
||||
- #44862: Fixed Settings UI advanced paste page crash by using correct settings repository for null checking.
|
||||
|
||||
### Command Palette
|
||||
- #44886: Fixed personalization section not appearing by using latest MSIX for installation.
|
||||
- #44938: Fixed loading of icons from internet shortcuts. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- #45076: Fixed potential deadlock from lazy-loading AppListItem details. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
|
||||
### Cursor Wrap
|
||||
- #44936: Added improved multi-monitor support; Added laptop lid close detection for dynamic monitor topology updates. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
- #44936: Added new settings dropdown to constrain wrapping to horizontal-only, vertical-only, or both directions. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
|
||||
### Peek
|
||||
- #44995: Fixed Space key triggering Peek during file rename, search, or address bar typing.
|
||||
|
||||
### PowerRename
|
||||
- #44944: Fixed regex `$` not working, preventing users from adding text at the end of filenames.
|
||||
|
||||
### Runner
|
||||
- #44931: Monochrome tray icon now adapts to Windows system theme instead of app theme.
|
||||
- #44982: Fixed right-click menu to dynamically update based on Quick Access enabled/disabled state.
|
||||
|
||||
### GPO / Enterprise
|
||||
- #45028: Added CursorWrap policy definition to ADMX templates. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
|
||||
For the full list of v0.97 changes, visit the [Windows Command Line blog](https://aka.ms/powertoys-releaseblog).
|
||||
|
||||
## Advanced Paste
|
||||
|
||||
- Added hex color previews in clipboard history. Thanks [@crramirez](https://github.com/crramirez)!
|
||||
- Added automatic placeholder endpoints when required fields are left empty.
|
||||
- Fixed a grammar issue in the AI settings description. Thanks [@erik-anderson](https://github.com/erik-anderson)!
|
||||
- Fixed loading order so custom action hotkeys are read correctly.
|
||||
- Updated Advanced Paste descriptions to reflect support for online and local models.
|
||||
- Fixed clipboard history item selection so it doesn’t duplicate entries.
|
||||
- Prevented placeholder endpoints from being saved for providers that don’t need them.
|
||||
- Added image input support for AI transforms and improved clipboard change tracking.
|
||||
|
||||
## Awake
|
||||
|
||||
- Fixed Awake CLI so help, errors, and logs appear correctly in the console. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
|
||||
## Command Palette
|
||||
|
||||
- Fixed background image loading in BlurImageControl. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed SDK packaging paths and added a CI SDK build stage.
|
||||
- Aligned naming and spell-checking with .NET conventions. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added drag-and-drop support for Command Palette items. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added a PowerToys Command Palette extension to discover and launch PowerToys utilities.
|
||||
- Fixed grid view bindings and layout issues. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Fixed a line-break issue in RDC extension toast messages. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Made the Settings button text localizable. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Hid the RDC fallback on the home page and fixed MSTSC working directory handling. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Optimized result list merging for better performance. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Added Small/Medium/Large detail sizes in the extensions API. Thanks [@DevLGuilherme](https://github.com/DevLGuilherme)!
|
||||
- Hid fallback commands on the home page when no query is entered. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added back navigation support in the Settings window. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added a Command Palette solution filter. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Updated Extension SDK documentation links to Microsoft Learn. Thanks [@RubenFricke](https://github.com/RubenFricke)!
|
||||
- Added a custom search engine URL setting for Web Search. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added pinyin matching for Chinese input. Thanks [@frg2089](https://github.com/frg2089)!
|
||||
- Bumped Command Palette version to 0.8.
|
||||
- Removed subtitles from built-in top-level commands. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Refined separator styling in the details pane. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added a built-in Remote Desktop extension.
|
||||
- Added a Peek command to the Indexer extension.
|
||||
- Improved default browser detection using the Windows Shell API. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added Escape key behavior options. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added theme and background customization options. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Improved WinGet package app matching. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added an auto-return-home delay setting. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added fallback ranking and global results settings.
|
||||
- Removed the selection indicator in the context menu list. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added a developer ribbon with build and log info. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Updated the “Learn more” string for Command Palette. Thanks [@pratnala](https://github.com/pratnala)!
|
||||
- Added arrow-key navigation for grid views. Thanks [@samrueby](https://github.com/samrueby)!
|
||||
- Fixed version display when running unpackaged. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added a native debugging launch profile. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Reduced redundant property change notifications in the SDK. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Improved section readability and accessibility. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Made gallery spacing uniform. Thanks [@jiripolasek](https://github.com/jiripolasek)!
|
||||
- Added sections and separators for list and grid pages. Thanks [@DevLGuilherme](https://github.com/DevLGuilherme)!
|
||||
|
||||
## Crop & Lock
|
||||
|
||||
- Added a screenshot mode that freezes a cropped region into its own window. Thanks [@fm-sys](https://github.com/fm-sys)!
|
||||
|
||||
## Cursor Wrap
|
||||
|
||||
- Improved Cursor Wrap behavior on multi-monitor setups by wrapping only at outer edges. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
|
||||
## FancyZones
|
||||
|
||||
- Fixed editor overlay positioning on mixed-DPI multi-monitor setups. Thanks [@Memphizzz](https://github.com/Memphizzz)!
|
||||
- Added a FancyZones CLI for command-line layout management.
|
||||
|
||||
## File Locksmith
|
||||
|
||||
- Added a File Locksmith CLI for querying, waiting on, or killing file locks.
|
||||
|
||||
## Find My Mouse
|
||||
|
||||
- Improved spotlight edge rendering for clearer Find My Mouse visuals.
|
||||
- Added telemetry to track how Find My Mouse is triggered.
|
||||
|
||||
## Image Resizer
|
||||
|
||||
- Fixed Fill mode cropping when Shrink Only is enabled. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Added a dedicated Image Resizer CLI for scripted resizing.
|
||||
|
||||
## Light Switch
|
||||
|
||||
- Added telemetry events for Light Switch usage and settings changes.
|
||||
- Added a Follow Night Light mode to sync theme changes with Night Light.
|
||||
- Clarified LightSwitchService and LightSwitchStateManager roles in docs.
|
||||
- Added a Quick Access dashboard button to toggle Light Switch quickly.
|
||||
- Ensured Light Switch honors GPO policy states with clear status messaging.
|
||||
|
||||
## Mouse Without Borders
|
||||
|
||||
- Continued refactoring Mouse Without Borders by splitting the large Common class into focused components. Thanks [@mikeclayton](https://github.com/mikeclayton)!
|
||||
- Completed the Common class refactor with Core and IPC helper extraction. Thanks [@mikeclayton](https://github.com/mikeclayton)!
|
||||
|
||||
## Peek
|
||||
|
||||
- Hardened Peek previews with strict resource filtering and safer external link warnings.
|
||||
- Improved SVG preview compatibility by rendering via WebView2.
|
||||
|
||||
## PowerRename
|
||||
|
||||
- Added HEIF/AVIF EXIF metadata extraction and extension status guidance for related previews.
|
||||
- Fixed undefined behavior in file time handling. Thanks [@safocl](https://github.com/safocl)!
|
||||
- Optimized memory allocation for depth-based rename processing.
|
||||
- Fixed Unicode normalization and non‑breaking space matching. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed date token replacements followed by capital letters. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
|
||||
## PowerToys Run Plugins
|
||||
|
||||
- Fixed a plugin name typo and added Project Launcher to the third‑party list. Thanks [@artickc](https://github.com/artickc)!
|
||||
- Added the Open With Antigravity plugin to the third‑party list. Thanks [@artickc](https://github.com/artickc)!
|
||||
|
||||
## PowerToys Run
|
||||
|
||||
- Avoided unnecessary hotkey conflict checks when settings change.
|
||||
- Added QuickAI to the third-party PowerToys Run plugin list. Thanks [@ruslanlap](https://github.com/ruslanlap)!
|
||||
|
||||
## Quick Accent
|
||||
|
||||
- Added localized quotation marks to Quick Accent. Thanks [@warquys](https://github.com/warquys)!
|
||||
- Fixed duplicate and redundant characters in Quick Accent sets. Thanks [@noraa-junker](https://github.com/noraa-junker)!
|
||||
- Fixed DPI positioning issues for Quick Accent on mixed-DPI setups. Thanks [@noraa-junker](https://github.com/noraa-junker)!
|
||||
|
||||
## Settings
|
||||
|
||||
- Added a new tray icon that adapts to theme changes. Thanks [@HO-COOH](https://github.com/HO-COOH)!
|
||||
- Centralized module enable/disable logic for cleaner Settings UI updates.
|
||||
- Simplified Settings utilities by removing ISettingsUtils/ISettingsPath interfaces. Thanks [@noraa-junker](https://github.com/noraa-junker)!
|
||||
- Improved Settings UI consistency and disabled-state visuals.
|
||||
- Added semantic headings to the Dashboard for better accessibility.
|
||||
- Introduced Quick Access as a standalone host with updated Settings integration.
|
||||
- Fixed Dashboard toggle flicker and sort menu checkmarks. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Added Native AOT-compatible settings serialization.
|
||||
- Standardized mouse tool description text. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Added a global SettingsUtils singleton to reduce repeated initialization.
|
||||
|
||||
## Development
|
||||
|
||||
- Fixed broken devdocs links to the coding style guide. Thanks [@RubenFricke](https://github.com/RubenFricke)!
|
||||
- Migrated main and installer solutions to .slnx for improved build tooling.
|
||||
- Restored local installer builds after the WiX v5 upgrade with signing and versioning fixes.
|
||||
- Added incremental review tooling and structured AI prompts for PR/issue reviews.
|
||||
- Documented bot commands and cleaned up devdocs structure. Thanks [@noraa-junker](https://github.com/noraa-junker)!
|
||||
- Updated WinAppSDK pipeline defaults to 1.8 and fixed restore handling.
|
||||
- Updated the COMMUNITY list to reflect current roles.
|
||||
- Maintained community member ordering and added a new entry.
|
||||
- Re-enabled centralized PackageReference for native projects with VS auto-restore.
|
||||
- Disabled MSBuild caching by default in CI to avoid build instability.
|
||||
- Updated the latest WinAppSDK daily pipeline for split-dependency restores.
|
||||
- Suppressed experimental build warnings and aligned WrapPanel stretch handling.
|
||||
- Reordered the spell-check expect list for consistent automation.
|
||||
- Migrated native projects to centralized PackageReference management.
|
||||
- Cleaned spell-check dictionary entries and capitalization.
|
||||
- Synced commit/PR prompts and wired VS Code to repo prompt files.
|
||||
- Added VS Code build tasks and improved build script path handling.
|
||||
- Updated Windows App SDK package versions in central package management.
|
||||
- Migrated cmdpal extension native project to PackageReference and fixed outputs.
|
||||
- Reverted PackageReference changes back to packages.config where needed.
|
||||
- Bypassed a release version check for a failing DLL to keep pipelines green.
|
||||
- Consolidated Copilot instructions and fixed prompt frontmatter.
|
||||
- Added signing entries for new Quick Access binaries and CLI version metadata.
|
||||
- Fixed install scope detection to avoid mixed per-user/per-machine installs.
|
||||
- Added a Module Loader tool to quickly test PowerToys modules without full builds. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
|
||||
- Added update telemetry to understand auto-update checks and downloads.
|
||||
- Updated the telemetry package for new compliance requirements. Thanks [@carlos-zamora](https://github.com/carlos-zamora)!
|
||||
- Documented missing telemetry events in DATA_AND_PRIVACY.
|
||||
- Fixed UI test pipeline restores for .slnx solutions.
|
||||
- Added UI automation coverage for Advanced Paste clipboard history flows.
|
||||
- Stabilized FancyZones UI tests with more reliable selectors and screen recordings.
|
||||
|
||||
## 🛣️ Roadmap
|
||||
We are planning some nice new features and improvements for the next releases – PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.98][github-next-release-work]!
|
||||
|
||||
## ❤️ PowerToys Community
|
||||
The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn't be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Your contributions and feedback improve PowerToys month after month!
|
||||
|
||||
## Contributing
|
||||
This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. Most contributions require you to agree to a [Contributor License Agreement (CLA)][oss-CLA] declaring that you grant us the rights to use your contribution and that you have permission to do so. For guidance on developing for PowerToys, please read the [developer docs](./doc/devdocs) for a detailed breakdown. This includes how to setup your computer to compile.
|
||||
## Contributing
|
||||
|
||||
## Code of Conduct
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct-code].
|
||||
This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. Most contributions require you to agree to a [Contributor License Agreement (CLA)][oss-CLA] declaring that you grant us the rights to use your contribution and that you have permission to do so. For guidance on developing for PowerToys, please read the [developer docs](./doc/devdocs) for a detailed breakdown. This includes how to setup your computer to compile.
|
||||
|
||||
## Privacy Statement
|
||||
The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
|
||||
## Code of conduct
|
||||
|
||||
[oss-CLA]: https://cla.opensource.microsoft.com
|
||||
[oss-conduct-code]: CODE_OF_CONDUCT.md
|
||||
[community-link]: COMMUNITY.md
|
||||
[github-release-link]: https://aka.ms/installPowerToys
|
||||
[microsoft-store-link]: https://aka.ms/getPowertoys
|
||||
[winget-link]: https://github.com/microsoft/winget-cli#installing-the-client
|
||||
[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
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct-code].
|
||||
|
||||
## Privacy statement
|
||||
|
||||
The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
|
||||
|
||||
[oss-CLA]: https://cla.opensource.microsoft.com
|
||||
[oss-conduct-code]: CODE_OF_CONDUCT.md
|
||||
[community-link]: COMMUNITY.md
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.100%22
|
||||
|
||||
24
SECURITY.md
@@ -1,36 +1,36 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
|
||||
|
||||
## Security
|
||||
# Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
## Reporting security issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
|
||||
If you prefer to submit without logging in, send an email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
- Full paths of source file(s) related to the manifestation of the issue
|
||||
- The location of the affected source code (tag/branch/commit or direct URL)
|
||||
- Any special configuration required to reproduce the issue
|
||||
- Step-by-step instructions to reproduce the issue
|
||||
- Proof-of-concept or exploit code (if possible)
|
||||
- Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
## Preferred languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
|
||||
17
SUPPORT.md
@@ -1,24 +1,21 @@
|
||||
# Support
|
||||
|
||||
## How to use Microsoft PowerToys
|
||||
|
||||
## How to use Microsoft PowerToys
|
||||
|
||||
For more info on [PowerToys overviews and how to use the utilities][usingPowerToys-docs-link], or any other tools and resources for [Windows development environments](https://learn.microsoft.com/windows/dev-environment/overview), head over to [learn.microsoft.com][usingPowerToys-docs-link]!
|
||||
For more information about PowerToys overviews, how to use the utilities, and other tools and resources for [Windows development environments](https://learn.microsoft.com/windows/dev-environment/overview), visit [learn.microsoft.com][usingPowerToys-docs-link].
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses [GitHub Issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or
|
||||
feature request as a new Issue.
|
||||
This project uses [GitHub Issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue.
|
||||
|
||||
For help and questions about using this project, please look at our Wiki for using PowerToys and our [Contributor's Guide][contributor] if you want to work on PowerToys.
|
||||
For help and questions about using this project, please visit our documentation and [Contributor's Guide][contributor] if you want to contribute to PowerToys.
|
||||
|
||||
## Microsoft Support Policy
|
||||
## Microsoft support policy
|
||||
|
||||
Support for PowerToys is limited to the resources listed above.
|
||||
|
||||
[gh-issue]: https://github.com/microsoft/PowerToys/issues/new/choose
|
||||
[gh-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=Issue-Bug&template=bug_report.md&title=
|
||||
[gh-feature]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=feature_request.md&title=
|
||||
[wiki]: https://github.com/microsoft/PowerToys/wiki
|
||||
[gh-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=Issue-Bug&template=bug_report.md
|
||||
[gh-feature]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=feature_request.md
|
||||
[contributor]: https://github.com/microsoft/PowerToys/blob/main/CONTRIBUTING.md
|
||||
[usingPowerToys-docs-link]: https://aka.ms/powertoys-docs
|
||||
|
||||
54
auto-cherry-pick.ps1
Normal file
@@ -0,0 +1,54 @@
|
||||
# Auto-resolve cherry-pick conflicts
|
||||
param([int]$MaxAttempts = 100)
|
||||
|
||||
$attempts = 0
|
||||
while ($attempts -lt $MaxAttempts) {
|
||||
$attempts++
|
||||
|
||||
# Check if cherry-pick is in progress
|
||||
$status = git status --porcelain
|
||||
if (-not $status) {
|
||||
Write-Host "Cherry-pick complete!" -ForegroundColor Green
|
||||
break
|
||||
}
|
||||
|
||||
# Get conflicted files
|
||||
$conflicts = git diff --name-only --diff-filter=U
|
||||
|
||||
if ($conflicts) {
|
||||
Write-Host "Attempt $attempts`: Resolving conflicts..." -ForegroundColor Yellow
|
||||
|
||||
foreach ($file in $conflicts) {
|
||||
Write-Host " Resolving: $file"
|
||||
git checkout --ours $file 2>$null
|
||||
}
|
||||
|
||||
# Handle deleted files
|
||||
git status --short | Where-Object { $_ -match '^DU' } | ForEach-Object {
|
||||
$file = ($_ -split '\s+', 2)[1]
|
||||
Write-Host " Removing deleted: $file"
|
||||
git rm $file 2>$null
|
||||
}
|
||||
|
||||
git add . 2>$null
|
||||
}
|
||||
|
||||
# Try to continue
|
||||
$result = git cherry-pick --continue 2>&1
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host " Continued successfully" -ForegroundColor Green
|
||||
}
|
||||
elseif ($result -match 'empty') {
|
||||
Write-Host " Skipping empty commit" -ForegroundColor Cyan
|
||||
git cherry-pick --skip 2>&1 | Out-Null
|
||||
}
|
||||
else {
|
||||
Write-Host " Error: $result" -ForegroundColor Red
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
}
|
||||
|
||||
if ($attempts -ge $MaxAttempts) {
|
||||
Write-Host "Max attempts reached. Check status manually." -ForegroundColor Red
|
||||
}
|
||||
94
deps/spdlog-msvc-fix/include/spdlog-msvc-fix.h
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// spdlog-msvc-fix.h
|
||||
//
|
||||
// Workaround for MSVC 14.51 (compiler version 19.51, _MSC_VER >= 1951) removing
|
||||
// stdext::checked_array_iterator. Force-included for all spdlog consumers via
|
||||
// deps/spdlog.props, because spdlog v1.8.5's bundled fmt format.h(357) still
|
||||
// references this type inside #if defined(_SECURE_SCL) && _SECURE_SCL -- a
|
||||
// branch entered in Debug builds where _ITERATOR_DEBUG_LEVEL > 0.
|
||||
//
|
||||
// On MSVC 14.50 and earlier, the type still exists in <iterator>, so this shim
|
||||
// is a no-op via the _MSC_VER guard. On MSVC 14.51+, it provides a minimal
|
||||
// pointer-backed substitute that satisfies the bundled fmt's usage:
|
||||
//
|
||||
// template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
|
||||
// template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
|
||||
// return {p, size};
|
||||
// }
|
||||
// ... return make_checked(get_data(c) + size, n);
|
||||
//
|
||||
// When deps/spdlog is bumped past v1.14 (which ships fmt 10.2 and drops this
|
||||
// dependency), this shim and its <ForcedIncludeFiles> entry in deps/spdlog.props
|
||||
// can be deleted.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER >= 1951
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace stdext
|
||||
{
|
||||
template <typename _Ptr>
|
||||
class checked_array_iterator
|
||||
{
|
||||
_Ptr _Myarray = nullptr;
|
||||
std::size_t _Mysize = 0;
|
||||
std::size_t _Myindex = 0;
|
||||
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = std::remove_cv_t<std::remove_pointer_t<_Ptr>>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = _Ptr;
|
||||
using reference = std::remove_pointer_t<_Ptr>&;
|
||||
|
||||
constexpr checked_array_iterator() = default;
|
||||
|
||||
constexpr checked_array_iterator(_Ptr arr, std::size_t size, std::size_t idx = 0) noexcept
|
||||
: _Myarray(arr), _Mysize(size), _Myindex(idx)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr reference operator*() const noexcept { return _Myarray[_Myindex]; }
|
||||
constexpr pointer operator->() const noexcept { return _Myarray + _Myindex; }
|
||||
constexpr reference operator[](difference_type n) const noexcept
|
||||
{
|
||||
return _Myarray[_Myindex + static_cast<std::size_t>(n)];
|
||||
}
|
||||
|
||||
constexpr checked_array_iterator& operator++() noexcept { ++_Myindex; return *this; }
|
||||
constexpr checked_array_iterator operator++(int) noexcept { auto t = *this; ++_Myindex; return t; }
|
||||
constexpr checked_array_iterator& operator--() noexcept { --_Myindex; return *this; }
|
||||
constexpr checked_array_iterator operator--(int) noexcept { auto t = *this; --_Myindex; return t; }
|
||||
|
||||
constexpr checked_array_iterator& operator+=(difference_type n) noexcept
|
||||
{
|
||||
_Myindex = static_cast<std::size_t>(static_cast<difference_type>(_Myindex) + n);
|
||||
return *this;
|
||||
}
|
||||
constexpr checked_array_iterator& operator-=(difference_type n) noexcept
|
||||
{
|
||||
_Myindex = static_cast<std::size_t>(static_cast<difference_type>(_Myindex) - n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend constexpr checked_array_iterator operator+(checked_array_iterator it, difference_type n) noexcept { it += n; return it; }
|
||||
friend constexpr checked_array_iterator operator+(difference_type n, checked_array_iterator it) noexcept { return it + n; }
|
||||
friend constexpr checked_array_iterator operator-(checked_array_iterator it, difference_type n) noexcept { it -= n; return it; }
|
||||
friend constexpr difference_type operator-(checked_array_iterator a, checked_array_iterator b) noexcept
|
||||
{
|
||||
return static_cast<difference_type>(a._Myindex) - static_cast<difference_type>(b._Myindex);
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(checked_array_iterator a, checked_array_iterator b) noexcept { return a._Myindex == b._Myindex; }
|
||||
friend constexpr bool operator!=(checked_array_iterator a, checked_array_iterator b) noexcept { return !(a == b); }
|
||||
friend constexpr bool operator<(checked_array_iterator a, checked_array_iterator b) noexcept { return a._Myindex < b._Myindex; }
|
||||
friend constexpr bool operator>(checked_array_iterator a, checked_array_iterator b) noexcept { return b < a; }
|
||||
friend constexpr bool operator<=(checked_array_iterator a, checked_array_iterator b) noexcept { return !(b < a); }
|
||||
friend constexpr bool operator>=(checked_array_iterator a, checked_array_iterator b) noexcept { return !(a < b); }
|
||||
};
|
||||
} // namespace stdext
|
||||
|
||||
#endif // __cplusplus && _MSC_VER >= 1951
|
||||
1
deps/spdlog.props
vendored
@@ -3,6 +3,7 @@
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)spdlog\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_COMPILED_LIB;SPDLOG_WCHAR_FILENAMES;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ForcedIncludeFiles>$(MSBuildThisFileDirectory)spdlog-msvc-fix\include\spdlog-msvc-fix.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
|
||||
@@ -56,7 +56,7 @@ After generating the resx file, rename the existing rc and h files to ProjName.b
|
||||
</Target>
|
||||
```
|
||||
|
||||
This event runs a script which generates a resource.h and ProjName.rc in the `Generated Files` folder using the strings in all the resx files along with the existing information in resource.base.h and ProjName.base.rc. The script is [convert-resx-to-rc.ps1](https://github.com/microsoft/PowerToys/blob/main/tools/build/convert-resx-to-rc.ps1). The script uses [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert) to convert the resx file to a string table expected in the .rc file format. When the resources are added to the rc file the `IDS_` prefix is added and resource names are in upper case (as it was originally). Any occurrences of `"` in the string resource is escaped as `""` to prevent build errors. The string tables are added to the rc file in the following format:
|
||||
This event runs a script which generates a resource.h and ProjName.rc in the `Generated Files` folder using the strings in all the resx files along with the existing information in resource.base.h and ProjName.base.rc. The script is [convert-resx-to-rc.ps1](https://github.com/microsoft/PowerToys/blob/main/tools/build/convert-resx-to-rc.ps1). The script uses [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert) to convert the resx file to a string table expected in the .rc file format. When the resources are added to the rc file the `IDS_` prefix is added and resource names are in uppercase (as it was originally). Any occurrences of `"` in the string resource is escaped as `""` to prevent build errors. The string tables are added to the rc file in the following format:
|
||||
```
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 5.3 MiB After Width: | Height: | Size: 5.3 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
@@ -60,7 +60,7 @@ A markdown page is a page inside of command palette that displays markdown conte
|
||||
|
||||
```csharp
|
||||
interface IMarkdownPage requires IPage {
|
||||
String[] Bodies(); // TODO! should this be an IBody, so we can make it observable?
|
||||
String[] Bodies();
|
||||
IDetails Details();
|
||||
IContextItem[] Commands { get; };
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 3.5 MiB |
|
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 491 KiB After Width: | Height: | Size: 491 KiB |
|
Before Width: | Height: | Size: 706 KiB After Width: | Height: | Size: 706 KiB |
|
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 408 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 493 KiB After Width: | Height: | Size: 493 KiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
167
doc/devdocs/modules/cmdpal/extension-gallery.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Command Palette Extension Gallery
|
||||
|
||||
This document describes how Command Palette (CmdPal) discovers extensions for
|
||||
the in-app **Extension gallery** page.
|
||||
|
||||
## At a glance
|
||||
|
||||
- The gallery loads a single JSON feed called `extensions.json` from a remote
|
||||
HTTPS URL, parses it, and renders the entries.
|
||||
- The default feed lives in the external repo
|
||||
**`microsoft/CmdPal-Extensions`** at
|
||||
`https://aka.ms/CmdPal-ExtensionsJson`.
|
||||
- Feed content + icon images are cached on disk so the page works offline and
|
||||
survives short network hiccups.
|
||||
- There is no WinGet discovery, no per-extension `manifest.json` fetch, and no
|
||||
other network call for rendering the list.
|
||||
|
||||
## Implementation pointers
|
||||
|
||||
| Concern | File |
|
||||
| --- | --- |
|
||||
| Fetching, parsing, caching, pruning | `Microsoft.CmdPal.Common/ExtensionGallery/Services/ExtensionGalleryService.cs` |
|
||||
| Resolving which URL to fetch | `Microsoft.CmdPal.Common/ExtensionGallery/Services/GalleryFeedUrlProvider.cs` + `Microsoft.CmdPal.UI/Helpers/GalleryServiceRegistration.cs` |
|
||||
| HTTP + on-disk cache | `Microsoft.CmdPal.Common/ExtensionGallery/Services/ExtensionGalleryHttpClient.cs` (wraps `Microsoft.CmdPal.Common/Services/HttpCaching/HttpCachingClient`) |
|
||||
| Feed + entry models | `Microsoft.CmdPal.Common/ExtensionGallery/Models/` |
|
||||
|
||||
## Feed URL resolution
|
||||
|
||||
`ExtensionGalleryService.GetFeedUrl()` returns, in order:
|
||||
|
||||
1. The user-configured URL from CmdPal settings (`SettingsModel.GalleryFeedUrl`,
|
||||
exposed via the hidden `InternalPage` settings page). Any non-empty value
|
||||
wins. Mostly used for local testing against a custom feed.
|
||||
2. Otherwise, the built-in default
|
||||
`https://aka.ms/CmdPal-ExtensionsJson`.
|
||||
|
||||
Local `file://` URIs are allowed too — `FetchFeedDocumentAsync` reads the file
|
||||
directly and bypasses the HTTP cache.
|
||||
|
||||
## Feed format
|
||||
|
||||
The feed is a single wrapped JSON document with inline entries:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/CmdPal-Extensions/main/.github/schemas/gallery.schema.json",
|
||||
"extensions": [
|
||||
{
|
||||
"id": "sample-extension",
|
||||
"title": "Sample Extension",
|
||||
"description": "A sample extension demonstrating the gallery feed format.",
|
||||
"author": { "name": "Microsoft", "url": "https://github.com/microsoft" },
|
||||
"homepage": "https://github.com/microsoft/CmdPal-Extensions",
|
||||
"iconUrl": "https://.../icon.png",
|
||||
"screenshotUrls": ["https://.../screenshot-1.png"],
|
||||
"tags": ["sample"],
|
||||
"installSources": [
|
||||
{ "type": "winget", "id": "Contoso.SampleExtension" },
|
||||
{ "type": "msstore", "id": "9P…" },
|
||||
{ "type": "url", "uri": "https://github.com/contoso/sample/releases/latest" }
|
||||
],
|
||||
"detection": { "packageFamilyName": "Contoso.SampleExtension_1234567890abc" }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Only the `extensions` array is read at runtime. The authoritative JSON
|
||||
schema for an entry lives in the upstream feed repo
|
||||
([`microsoft/CmdPal-Extensions`](https://github.com/microsoft/CmdPal-Extensions));
|
||||
don't duplicate it here — it drifts.
|
||||
|
||||
### Required + optional entry fields
|
||||
|
||||
| Field | Required | Notes |
|
||||
| --- | --- | --- |
|
||||
| `id` | yes | Lowercase stable identifier; entries with empty id are dropped. |
|
||||
| `title` | yes | Display name. |
|
||||
| `description` | yes | Shown in list and detail views. |
|
||||
| `author.name` | yes | `author.url` optional. |
|
||||
| `installSources` | yes | At least one entry; see [Install sources](#install-sources). |
|
||||
| `homepage`, `iconUrl`, `screenshotUrls`, `tags`, `detection.packageFamilyName` | no | All optional. |
|
||||
|
||||
Relative `iconUrl` / `screenshotUrls` are resolved against the feed URL's
|
||||
directory (useful only for local / `file://` feeds during development).
|
||||
|
||||
## Install sources
|
||||
|
||||
Each entry's `installSources` is consumed by
|
||||
`ExtensionGalleryItemViewModel` to decide which install affordances to show.
|
||||
|
||||
| `type` | Required field | Behaviour |
|
||||
| --- | --- | --- |
|
||||
| `winget` | `id` | Enables the "Install via WinGet" button (uses the shared WinGet service), and joins in-flight install progress + installed/update status. |
|
||||
| `msstore` | `id` | Opens `ms-windows-store://pdp/?ProductId={id}`. |
|
||||
| `url` | `uri` | Shown as a "GitHub" or "Website" link depending on host. |
|
||||
|
||||
An entry can declare any combination. Sources the runtime does not recognise
|
||||
are surfaced as an "unknown source" indicator.
|
||||
|
||||
## Fetching and caching
|
||||
|
||||
`ExtensionGalleryService` uses `ExtensionGalleryHttpClient`, which wraps
|
||||
`HttpCachingClient` over a file-system cache. Both the feed JSON and any
|
||||
cacheable icon URLs are cached.
|
||||
|
||||
| Setting | Value | Defined in |
|
||||
| --- | --- | --- |
|
||||
| Cache root | `{AppCache}\GalleryCache\` | `ExtensionGalleryHttpClient.CacheDirectoryName` |
|
||||
| Feed TTL | 4 hours | `ExtensionGalleryHttpClient.DefaultTimeToLive` |
|
||||
| Icon TTL | 24 hours | `ExtensionGalleryService.IconCacheTtl` |
|
||||
| HTTP timeout | 15 s | `ExtensionGalleryHttpClient` |
|
||||
| `User-Agent` | `PowerToys-CmdPal/1.0` | `ExtensionGalleryHttpClient` |
|
||||
|
||||
`{AppCache}` resolves to `ApplicationData.Current.LocalCacheFolder` when
|
||||
CmdPal runs packaged, and to
|
||||
`%LOCALAPPDATA%\Microsoft\PowerToys\Microsoft.CmdPal\Cache\` when unpackaged
|
||||
(see `ApplicationInfoService.DetermineCacheDirectory`).
|
||||
|
||||
### Fetch flow
|
||||
|
||||
`GetExtensionsAsync` (normal load) and `RefreshAsync` (user-initiated
|
||||
refresh, `forceRefresh: true`) both go through `FetchWrappedFeedAsync`:
|
||||
|
||||
1. Resolve the feed URL (see above).
|
||||
2. If the URL is local, read it from disk. Otherwise, hand it to
|
||||
`HttpCachingClient.GetResourceAsync` which:
|
||||
- Serves a fresh cached copy if one exists and TTL has not elapsed.
|
||||
- Otherwise issues a conditional GET (ETag / `If-None-Match`). On `304
|
||||
Not Modified` it refreshes the cache metadata and returns the cached
|
||||
body.
|
||||
- On network failure it returns the last-known cached body with
|
||||
`UsedFallbackCache = true`, so the UI can show a "stale data" banner.
|
||||
3. Parse the JSON with the source-generated `GallerySerializationContext`
|
||||
(strongly-typed `GalleryRemoteIndex` — no reflection, AOT-friendly).
|
||||
4. Drop entries with missing `id`, normalize relative `iconUrl` and
|
||||
`screenshotUrls`, and resolve remote icon URIs through the same HTTP
|
||||
cache so the UI binds to local `file://` URIs.
|
||||
5. On a successful forced refresh, `PruneCachedResources` deletes cache
|
||||
entries that are no longer referenced by the current feed (old feed URL
|
||||
and icon URLs that dropped out of the feed).
|
||||
|
||||
### Fetch result flags
|
||||
|
||||
`GetExtensionsAsync` returns a `GalleryFetchResult` that the view model uses
|
||||
for UI hints:
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `FromCache` | The feed came from cache without hitting the network (TTL still valid). |
|
||||
| `UsedFallbackCache` | A network request was attempted and failed, and the cached copy was served as fallback. The UI shows a stale-data info bar. |
|
||||
| `RateLimited` | The origin returned `429 Too Many Requests` and no fallback was available. The UI shows a rate-limit error. |
|
||||
|
||||
## Authoring
|
||||
|
||||
- Entries for the production gallery are added to the feed repo
|
||||
`microsoft/CmdPal-Extensions`.
|
||||
- For editor validation of an entry, reference the schema published in the
|
||||
upstream repo via the entry's `$schema` field.
|
||||
- Keep `id` stable once an extension is published — users may have it
|
||||
installed and the gallery keys install status by id.
|
||||
- Prefer providing a `winget` source when the extension ships through App
|
||||
Installer; the gallery uses it both for status ("Installed" / "Update
|
||||
available") and for the in-app install button.
|
||||
- `detection.packageFamilyName` lets the gallery recognise an
|
||||
already-installed packaged extension before WinGet metadata resolves.
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 311 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 297 KiB |
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 284 KiB |
@@ -76,6 +76,13 @@ functionality.
|
||||
- [Status messages](#status-messages)
|
||||
- [Rendering of ICommandItems in Lists and Menus](#rendering-of-icommanditems-in-lists-and-menus)
|
||||
- [Addenda I: API additions (ICommandProvider2)](#addenda-i-api-additions-icommandprovider2)
|
||||
- [Addenda II: Commands with Parameters](#addenda-ii-commands-with-parameters)
|
||||
- [String parameters](#string-parameters)
|
||||
- [Command parameters - Invokable Commands](#command-parameters---invokable-commands)
|
||||
- [Command parameters - List Commands](#command-parameters---list-commands)
|
||||
- [Examples](#examples)
|
||||
- [Addenda III: Rich Search (DRAFT)](#addenda-iii-rich-search-draft)
|
||||
- [Nov 2025 status](#nov-2025-status)
|
||||
- [Addenda IV: Dock bands](#addenda-iv-dock-bands)
|
||||
- [Pinning nested commands to the dock (and top level)](#pinning-nested-commands-to-the-dock-and-top-level)
|
||||
- [Class diagram](#class-diagram)
|
||||
@@ -353,7 +360,7 @@ On a cold launch, DevPal will do the following:
|
||||
* Start it up.
|
||||
* Check if it's fresh or frozen.
|
||||
* Call `TopLevelCommands`, and put all of them in the list
|
||||
* Create a extension cache entry for that app.
|
||||
* Create an extension cache entry for that app.
|
||||
* If the provider is frozen: we can actually release the
|
||||
`ICommandProvider` instance at this point.
|
||||
* And of course, if we don't find all the packages we had cached, then delete
|
||||
@@ -454,7 +461,7 @@ ms-windows-store://assoc/?Tags=AppExtension-com.microsoft.commandpalette
|
||||
|
||||
to open the store to a list of extensions. However, we can't list those
|
||||
ourselves directly. Our friends in DevHome suggested it could be possible to
|
||||
stand up a azure service which could query the store for us, and return a list
|
||||
stand up an azure service which could query the store for us, and return a list
|
||||
of extensions. This is not something that they currently have planned, nor would
|
||||
it be cheap from an engineering standpoint.
|
||||
|
||||
@@ -1780,7 +1787,7 @@ class MyAppSettings {
|
||||
/* You can save the settings to the file here */
|
||||
var mySettingsFilePath = /* whatever */;
|
||||
string mySettingsJson = mySettings.Settings.GetState();
|
||||
// Or you could raise a event to indicate to the rest of your app that settings have changed.
|
||||
// Or you could raise an event to indicate to the rest of your app that settings have changed.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2006,7 +2013,7 @@ class CommandWithOnlyProperties : IExtendedAttributesProvider { ... }
|
||||
|
||||
will populate the WinRT type cache in Command Palette with the type information
|
||||
for `ICommandWithProperties`. In fact, if Command Palette has the
|
||||
`IExtendedAttributesProvider` type info in it's cache, and then later receives a new
|
||||
`IExtendedAttributesProvider` type info in its cache, and then later receives a new
|
||||
`MyCommandWithProperties` object, it'll actually be able to know that
|
||||
`MyCommandWithProperties` is an `IExtendedAttributesProvider`. WinRT is just weird
|
||||
like that some times.
|
||||
@@ -2048,6 +2055,183 @@ Fortunately, we can put all of that (`GetApiExtensionStubs`,
|
||||
developers won't have to do anything. The toolkit will just do the right thing
|
||||
for them.
|
||||
|
||||
## Addenda II: Commands with Parameters
|
||||
|
||||
Extensions will often want to provide commands that accept parameters from the
|
||||
user.
|
||||
|
||||
To support this, we're adding a new page type. The `IParametersPage` is a page
|
||||
that allows an extension to define a set of parameters that the user can fill.
|
||||
These parameters can be of different types, such as:
|
||||
* Labels: static text that provides context or instructions.
|
||||
* String parameters: text input fields where the user can type a string.
|
||||
* Command parameters: interactive fields that allow the user to select from a
|
||||
list of predefined commands, or just press a button to select an input.
|
||||
|
||||
Interleaving labels with parameters allows extensions to create rich, guided
|
||||
input forms for their commands. These are a more lightweight solution than the
|
||||
current adaptive card content.
|
||||
|
||||
```csharp
|
||||
[uuid("a2590cc9-510c-4af7-b562-a6b56fe37f55")]
|
||||
interface IParameterRun requires INotifyPropChanged
|
||||
{
|
||||
};
|
||||
|
||||
interface ILabelRun requires IParameterRun
|
||||
{
|
||||
String Text{ get; };
|
||||
};
|
||||
|
||||
interface IParameterValueRun requires IParameterRun
|
||||
{
|
||||
String PlaceholderText{ get; };
|
||||
Boolean NeedsValue{ get; }; // TODO! name is weird
|
||||
};
|
||||
|
||||
interface IStringParameterRun requires IParameterValueRun
|
||||
{
|
||||
String Text{ get; set; };
|
||||
|
||||
// TODO! do we need a way to validate string inputs?
|
||||
};
|
||||
|
||||
interface ICommandParameterRun requires IParameterValueRun
|
||||
{
|
||||
String DisplayText{ get; };
|
||||
ICommand GetSelectValueCommand(UInt64 hostHwnd);
|
||||
IIconInfo Icon{ get; }; // ? maybe
|
||||
|
||||
};
|
||||
|
||||
interface IParametersPage requires IPage
|
||||
{
|
||||
IParameterRun[] Parameters{ get; };
|
||||
IListItem Command{ get; };
|
||||
};
|
||||
```
|
||||
|
||||
When we open a `IParametersPage`, we will render the `Parameters` in the search
|
||||
box. We'll move focus to the first `IParameterRun` that is not a `ILabelRun`.
|
||||
What those interactions looks like depends on the type of `IParameterRun`.
|
||||
|
||||
There are three basic types of inputs: strings, invokable commands, and lists.
|
||||
Strings are a special case that doesn't require a command to set the value.
|
||||
Lists and invokable commands are picked based on the type of the
|
||||
`SelectValueCommand`. Each of these are detailed below.
|
||||
|
||||
When all the parameters have `NeedsValue` set to `false`, we will display a
|
||||
single item to the user - the `Command` item.
|
||||
|
||||
### String parameters
|
||||
|
||||
These are rendered as a text box within the search box. The user can type into
|
||||
it. Focus is moved to the next parameter when the user presses Enter or tab.
|
||||
|
||||
### Command parameters - Invokable Commands
|
||||
|
||||
These are used when the `SelectValueCommand` is an `IInvokableCommand`.
|
||||
|
||||
These are rendered as a button within the search box. The button text is
|
||||
`DisplayText` if it is set. If it is not, we will display the
|
||||
`PlaceholderText`. If the user clicks the button, we invoke the
|
||||
`SelectValueCommand` (and ignore the `CommandResult`).
|
||||
|
||||
This is good for file pickers, date pickers, color pickers, etc. Anything that
|
||||
requires a custom UI to pick a value.
|
||||
|
||||
When the extension has picked a value, it should set the `NeedsValue` to false.
|
||||
The extension can also set the `DisplayText` and `Icon` to reflect the chosen value.
|
||||
|
||||
When the user presses enter with the button focused, we will also invoke the
|
||||
`SelectValueCommand`.
|
||||
|
||||
When the user presses tab, we will move focus to the next parameter.
|
||||
|
||||
If the `NeedsValue` property is changed to `false` while it's focused, we will
|
||||
move focus to the next parameter.
|
||||
|
||||
### Command parameters - List Commands
|
||||
|
||||
These are used when the `SelectValueCommand` is an `IListPage` - both static and
|
||||
dynamic lists work similarly.
|
||||
|
||||
These are rendered as a text box within the search box. When the user focuses
|
||||
the text box, we will display the items from the `IListPage` in the body of
|
||||
CmdPal. The user can then type to filter the list. This filtering will work the
|
||||
same way as any other list page in CmdPal - CmdPal will filter static lists, or
|
||||
pass the query to a dynamic list.
|
||||
|
||||
The items in this list should all be `IListItem` objects with
|
||||
`IInvokableCommands`. Putting a `IPage` into one of these items will cause the
|
||||
user to navigate away from the parameters page, which would probably be
|
||||
unexpected.
|
||||
|
||||
When the user picks an item from the list, the extension should handle that
|
||||
command by bubbling an event up to the `CommandRun`, and setting the `Value`,
|
||||
`DisplayText`, and `Icon` properties, and setting `NeedsValue` to false.
|
||||
|
||||
When the user presses enter with the text box focused, we will invoke the
|
||||
command of the selected item in the list.
|
||||
|
||||
When the user presses tab, we will move focus to the next parameter.
|
||||
|
||||
If the `NeedsValue` property is changed to `false` while it's focused, we will
|
||||
move focus to the next parameter.
|
||||
|
||||
### Examples
|
||||
|
||||
Lets say you had a command like "Create a note \${title} in \${folder}".
|
||||
`title` is a string input, and `folder` is a static list of folders.
|
||||
|
||||
The extension author can then define a `IParametersPage` with four runs in it:
|
||||
* A `ILabelRun` for "Create a note"
|
||||
* A `IStringParameterRun` for the `title`
|
||||
* A `ILabelRun` for "in"
|
||||
* A `ICommandParameterRun` for the `folder`. The `Command` will be a
|
||||
`IListPage`, where the items are possible folders
|
||||
|
||||
In this example, the user can pick the "create note" command, then type the
|
||||
title, hit enter/tab, and then pick a folder from the list, then hit enter to
|
||||
run the command.
|
||||
|
||||
Samples for the parameters page are implemented over in
|
||||
[the sample extension](../../ext/SamplePagesExtension/Pages/ParameterSamples.cs)
|
||||
|
||||
|
||||
## Addenda III: Rich Search (DRAFT)
|
||||
|
||||
> [!NOTE]
|
||||
> _Mike_: Rich search and parameters were prototyped together, but ultimately we used two different solutions.
|
||||
>
|
||||
> Currently, we have a dummy implementation of draft C (ZWSP tokens), but without full API changes. Detailed [below](#nov-2025-status).
|
||||
|
||||
Extensions will often want to provide rich search experiences for their users.
|
||||
|
||||
This addenda is broken into multiple draft specs currently. These represent
|
||||
different approaches to the same goals.
|
||||
|
||||
* **A**: [Rich Search Box](./drafts/RichSearchBox-draft-A.md)
|
||||
* **B**: [Prefix Search](./drafts/PrefixSearch-draft-B.md)
|
||||
* **C**: [ZWSP tokens](./drafts/PlainRichSearch-draft-C.md)
|
||||
|
||||
### Nov 2025 status
|
||||
|
||||
As of Nov 2025, we're implementing a simple version of draft C in the host.
|
||||
|
||||
In this version, if the extension implements `IDynamicListPage`, and also
|
||||
implements `IExtendedAttributesProvider`, then they can set the `TokenSearch`
|
||||
property. This will enlighten CmdPal to treat ZWSP-separated tokens in the
|
||||
search text specially.
|
||||
|
||||
For an example, see
|
||||
[this sample implementation](../../ext/SamplePagesExtension/Pages/SampleSuggestionsPage.cs).
|
||||
|
||||
In my head, I am still leaning towards a more full-featured version of draft C,
|
||||
but with full CommandItem's in the `ISearchUpdateArgs` instead of just strings.
|
||||
We'd almost need a new page type to support that, where the extension can add
|
||||
`ICommandItem`s to the search box directly.
|
||||
|
||||
## Addenda IV: Dock bands
|
||||
|
||||
The "dock" is another way to surface commands to the user. This is a
|
||||
@@ -2158,7 +2342,6 @@ because that method is was designed for two main purposes:
|
||||
In neither of those scenarios was the full "display" of the item needed. In
|
||||
pinning scenarios, however, we need everything that the user would see in the UI
|
||||
for that item, which is all in the `ICommandItem`.
|
||||
|
||||
## Class diagram
|
||||
|
||||
This is a diagram attempting to show the relationships between the various types we've defined for the SDK. Some elements are omitted for clarity. (Notably, `IconData` and `IPropChanged`, which are used in many places.)
|
||||
@@ -2350,7 +2533,7 @@ follow - these are not part of the current SDK spec.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> A thought: what if a action returns a `CommandResult.Entity`, then that takes
|
||||
> A thought: what if an action returns a `CommandResult.Entity`, then that takes
|
||||
> devpal back home, but leaves the entity in the query box. This would allow for
|
||||
> a Quicksilver-like "thing, do" flow. That command would prepopulate the
|
||||
> parameters. So we would then filter top-level commands based on things that can
|
||||