Compare commits

..

16 Commits

Author SHA1 Message Date
Clint Rutkas
ab08e318d8 Fix MathHelper.Modulo for non-positive divisors; improve test coverage
- Add ArgumentOutOfRangeException guard for b<=0 in MathHelper.Modulo
  (previously threw raw DivideByZeroException for b=0, returned wrong
  results for negative divisors)
- Add test for invalid Position enum (only untested code branch)
- Add test for negative Modulo divisor
- Update zero-divisor test to expect ArgumentOutOfRangeException
- Remove redundant AllPositions smoke test (covered by 9 exact-value tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-13 17:20:31 -07:00
Clint Rutkas
ce0439f580 Enhance PowerAccent.Core and Peek.Common tests: fix bugs, strengthen assertions
- Fixed Rect.cs: Added Width/Height zero-division guards (was producing Infinity)
- Fixed Size.cs: Removed duplicate condition in division operator
- Strengthened Currency test with specific symbol assertions (euro, dollar, pound)
- Added cache identity check with Assert.AreSame
- Renamed misleading UNC test method (UncFileUri -> StandardUncWithFile)
- Added 10 new tests: Rect zero-Width/Height/AllZero, int.MinValue overflow,
  Modulo zero divisor, TopRight/BottomLeft exact values, null UNC path,
  Point both-zero, Size both-zero
- Added XML doc comments to all 123 test methods

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 19:09:21 -07:00
Clint Rutkas
5b7d3c0a82 Fix deps.json version conflicts in Peek.Common.UnitTests
Remove Common.SelfContained.props (only 3 of 72 test projects use it) and add
UseWindowsForms to bring in the WindowsDesktop shared framework. This ensures
runtime DLLs (System.CodeDom, Microsoft.VisualBasic, WindowsBase, System.Drawing)
resolve to the same versions as all other projects in the solution.

Without UseWindowsForms, Peek.Common (which uses WinUI, not WPF/WinForms) doesn't
transitively provide the WindowsDesktop framework reference, causing NuGet package
versions to appear in deps.json instead of the shared runtime versions.

Verified: All 487 libraries pass verifyDepsJsonLibraryVersions.ps1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 14:04:38 -07:00
Clint Rutkas
5e12d2476d Merge remote-tracking branch 'origin/copilot/upgrade-microsoft-semantic-kernel-core' into copilot/improve-test-coverage 2026-04-11 15:23:42 -07:00
Clint Rutkas
3b6bf9c0ce Fix deps.json version conflicts for System.Diagnostics.EventLog and System.Threading.Channels
Add PackageReference for System.Diagnostics.EventLog and System.Threading.Channels
to all C# projects via Directory.Build.props, ensuring the NuGet 10.x versions
take precedence over the 9.x versions bundled in the .NET 9 runtime. This resolves
the verifyDepsJsonLibraryVersions CI check failure where different projects would
reference different DLL versions of these libraries.

- Add System.Threading.Channels 10.0.4 to Directory.Packages.props
- Add PackageReference for both packages in Directory.Build.props (C# only)
- Remove ExcludeAssets=runtime from System.Diagnostics.EventLog in 6 projects
  (no longer needed since we want the 10.x NuGet version to override the runtime)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 14:56:16 -07:00
Clint Rutkas
9020b18a9f Upgrade transitive dependencies for SemanticKernel 1.74.0
Upgrade Microsoft.Extensions.*, OpenAI, System.Text.Json, and other
transitive dependencies to versions compatible with SemanticKernel 1.74.0:

- Microsoft.Extensions.AI: 9.9.1 -> 10.4.1
- Microsoft.Extensions.AI.OpenAI: 9.9.1-preview -> 10.4.1
- Microsoft.Extensions.Caching.Abstractions: 9.0.10 -> 10.0.5
- Microsoft.Extensions.Caching.Memory: 9.0.10 -> 10.0.5
- Microsoft.Extensions.DependencyInjection: 9.0.10 -> 10.0.5
- Microsoft.Extensions.Logging: 9.0.10 -> 10.0.5
- Microsoft.Extensions.Logging.Abstractions: 9.0.10 -> 10.0.5
- Microsoft.Extensions.Hosting: 9.0.10 -> 10.0.5
- Microsoft.Extensions.Hosting.WindowsServices: 9.0.10 -> 10.0.5
- Microsoft.Bcl.AsyncInterfaces: 9.0.10 -> 10.0.5
- Newtonsoft.Json: 13.0.3 -> 13.0.4
- OpenAI: 2.5.0 -> 2.9.1
- System.ClientModel: 1.7.0 -> 1.9.0
- System.Diagnostics.EventLog: 9.0.10 -> 10.0.5
- System.Numerics.Tensors: 9.0.11 -> 10.0.4
- System.ServiceProcess.ServiceController: 9.0.10 -> 10.0.5
- System.Text.Json: 9.0.10 -> 10.0.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 13:04:10 -07:00
Clint Rutkas
f59f67cb08 Merge remote-tracking branch 'origin/main' into copilot/improve-test-coverage 2026-04-11 12:24:44 -07:00
copilot-swe-agent[bot]
890ea40f8a Upgrade Microsoft.SemanticKernel packages from 1.66.0 to 1.74.0
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/39021150-d623-45a0-98e1-6eb8a65d44c0

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-11 04:10:23 +00:00
copilot-swe-agent[bot]
70dd9db67a Fix deps.json version mismatch: add SelfContained props to test projects
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/a7787ea5-42eb-4e09-96c8-c295385faa9c

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-05 23:14:55 +00:00
copilot-swe-agent[bot]
fb7c945a2c Fix SA1508 and CS0246: remove blank lines before closing braces, add missing using System
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/a6ae0e3d-68ed-4a4f-8b91-e039e2f05689

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-05 16:37:15 +00:00
copilot-swe-agent[bot]
c72580c8f2 Fix build error: add missing using System.Linq to LanguagesTests.cs
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/0b83d8fc-328d-47ec-a746-685956604afe

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-04 05:08:11 +00:00
copilot-swe-agent[bot]
ce6debf68b Add traies and udit to spell-check allow list
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/1daa557f-51f2-454e-a7f9-aacc3912d36f

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-03 01:41:54 +00:00
copilot-swe-agent[bot]
1eca1713e1 Remove all #region/#endregion directives to fix SA1124 errors
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/00075017-9397-4cb6-ad53-1afda756d401

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-01 20:20:30 +00:00
copilot-swe-agent[bot]
1254cba088 Add test projects to PowerToys.slnx so CI discovers and runs them
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/9ffea101-795b-4b8c-817a-ab5af7004f72

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-04-01 04:40:47 +00:00
copilot-swe-agent[bot]
b5373cbb2b Update spell-check: add Ene to allow list, remove unused entries from expect list
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/717795b3-9c5f-484e-9b9c-63de37852f81

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-03-31 20:39:18 +00:00
copilot-swe-agent[bot]
b82e6c508d Add unit tests for PowerAccent.Core and Peek.Common helpers
Add PowerAccent.Core.UnitTests project with tests for:
- Point, Size, Rect struct constructors and division operators
- Calculation position logic with boundary conditions and DPI
- Languages character mapping for multiple languages

Add Peek.Common.UnitTests project with tests for:
- MathHelper.Modulo with positive/negative values
- MathHelper.NumberOfDigits with edge cases
- PathHelper.IsUncPath with various path formats

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/643cb57b-c352-45a6-83d0-d6b09011290b

Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
2026-03-31 19:10:40 +00:00
883 changed files with 10481 additions and 47700 deletions

View File

@@ -57,7 +57,6 @@ body:
- Environment Variables
- FancyZones
- FancyZones Editor
- Grab And Move
- File Locksmith
- "File Explorer: Preview Pane"
- "File Explorer: Thumbnail preview"
@@ -70,7 +69,6 @@ body:
- Mouse Without Borders
- New+
- Peek
- Power Display
- PowerRename
- PowerToys Run
- Quick Accent

View File

@@ -127,7 +127,6 @@ HOLDSPACE
HOLDBACKSPACE
IDIGNORE
KBDLLHOOKSTRUCT
keydowns
keyevent
LAlt
LBUTTON
@@ -186,12 +185,6 @@ xmlutil
# Prefix
pcs
# EXPRTK / C++ MATH
ifunction
isinf
isnan
# User32.SYSTEM_METRICS_INDEX.cs
CLEANBOOT
@@ -316,7 +309,6 @@ onefuzz
# NameInCode
leilzh
mengyuanchen
contoso
# DllName
testhost
@@ -336,31 +328,22 @@ MRUCMPPROC
MRUINFO
REGSTR
# Quick Accent
Ene
# 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
@@ -395,12 +378,6 @@ YYY
# Unicode
precomposed
# names of characters
zwsp
# mermaid
autonumber
# GitHub issue/PR commands
azp
feedbackhub
@@ -415,12 +392,9 @@ nostdin
engtype
Nonpaged
# Spell-check fragments
traies
udit
# XAML
Untargeted
# Program names
SEARCHHOST
SHELLEXPERIENCEHOST
SHELLHOST
STARTMENUEXPERIENCEHOST
WIDGETBOARD

View File

@@ -209,7 +209,6 @@ Bilibili
BVID
capturevideosample
cmdow
contoso
Contoso
Controlz
cortana

View File

@@ -7,9 +7,7 @@ AUDCLNT
axisdefer
axisflip
axisstart
BGRX
bitmaps
blits
BREAKSCR
BUFFERFLAGS
Cands
@@ -19,7 +17,6 @@ CLASSW
coeffs
coprime
CREATEDIBSECTION
CREATESTRUCTW
crossfades
Ctl
CTLCOLOR
@@ -28,10 +25,7 @@ CTLCOLORDLG
CTLCOLOREDIT
CTLCOLORLISTBOX
CTrim
DBuffer
ddx
ddy
DEVSOURCE
DFCS
dlg
dlu
@@ -47,14 +41,11 @@ dupsegments
DWLP
EDITCONTROL
ENABLEHOOK
ENDOFSTREAM
expectedlock
fabsf
fastscroll
FDE
GETCHANNELRECT
GETCHECK
GETCOUNT
GETSCREENSAVEACTIVE
GETSCREENSAVETIMEOUT
GETTHUMBRECT
@@ -66,7 +57,6 @@ HTHEME
htol
ICONINFORMATION
ICONWARNING
igc
Inj
jumprecover
KSDATAFORMAT
@@ -88,11 +78,9 @@ manualdrop
maskcache
maxstep
MENUINFO
MFSTARTUP
mfxhw
mic
middledrop
MJPEG
middledrop
MMRESULT
momentumreversal
mrate
@@ -104,7 +92,6 @@ nduplicates
niterations
nmonitor
NONCLIENTMETRICS
NONOTIFY
nonvle
nredraw
nstop
@@ -115,22 +102,16 @@ osc
OWNERDRAW
PBGRA
periodictrap
pillarbox
pfdc
playhead
pointerreuse
PSWA
pwfx
qpc
Qpc
quantums
RCZOOMITSCR
readback
READERF
realcapture
REFKNOWNFOLDERID
reposted
RETURNCMD
SCREENSAVE
SCRNSAVE
SCRNSAVECONFIGURE
@@ -150,7 +131,6 @@ shortlist
slowthenfast
smallstart
SNIPOCR
sqrtf
ssi
startuprecovery
stf
@@ -159,7 +139,6 @@ STREAMFLAGS
submix
sxx
sxy
synthesising
syy
tallportal
tci
@@ -175,7 +154,6 @@ vaddvq
vandq
vcgeq
vdup
VIDCAP
vld
vle
Vle
@@ -191,9 +169,6 @@ vsync
WASAPI
WAVEFORMATEX
WAVEFORMATEXTENSIBLE
webcam
Webcam
webcams
wfopen
wideportal
wil

View File

@@ -1,3 +1,6 @@
# D2D
#D?2D
# Repeated letters
\b([a-z])\g{-1}{2,}\b
@@ -11,7 +14,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+|) @@ .*
@@ -19,10 +22,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])(?!-+\s)[-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
# css fonts
\bfont(?:-family(?:[-\w+]*)|):[^;}]+
\bfont(?:-family|):[^;}]+
# css url wrappings
\burl\([^)]+\)
@@ -87,9 +90,6 @@ 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.]+\g{-1}
\buses:\s+[-\w.]+/[-\w./]+@[-\w.]+
# 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
(\\?['"]|&quot;)[0-9a-f]{40,}\g{-1}
# hex runs
\b(?=(?:[a-fA-F]{0,2}\d)*[a-fA-F]{3})[0-9a-fA-F]{16,}\b
\b[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,11 +455,7 @@ LS0tLS1CRUdJT.*
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# unicode escaped characters (4)
\\u[0-9a-fA-F]{4}
# hex digits including css/html color classes
# 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
@@ -482,7 +478,7 @@ Name\[[^\]]+\]=.*
(?:(?:\b|_|(?<=[a-z]))I|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
# python
#\b(?i)py(?!gment|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
#\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
# crypt
(['"])\$2[ayb]\$.{56}\g{-1}
@@ -502,21 +498,12 @@ 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*\})+(?:\s+from (['"]).*?\g{-1}|)
^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+
# scala modules
#("[^"]+"\s*%%?\s*){2,3}"[^"]+"
# Dataframes / NumPy
#\b(?:df|np)\.\w{3,}
# container images
image: [-\w./:@]+
@@ -546,18 +533,12 @@ 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 exec/test regex
/.{3,}?/[gim]*\.(?:exec|test)\(
# javascript test regex
/.{3,}/[gim]*\.test\(
# javascript match regex
\.match\(/[^/\s"]{3,}/[gim]*\s*
# javascript match regex
@@ -584,7 +565,7 @@ perl(?:\s+-[a-zA-Z]\w*)+
regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\)
# regex choice
#\((?:\?:|)[^)|]+(?<! )\|(?!(?:jq|xargs)\b)[^)| ][^)]*\)
# \(\?:[^)]+\|[^)]+\)
# proto
^\s*(\w+)\s\g{-1} =
@@ -607,9 +588,6 @@ 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+
@@ -664,8 +642,6 @@ 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
@@ -703,9 +679,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,}
@@ -717,7 +693,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
# latex (check-spelling >= 0.0.22)
\\\w{2,}\{
# American Mathematical Society (AMS) / Doxygen
@@ -744,6 +720,7 @@ 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
@@ -772,12 +749,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
@@ -785,7 +762,7 @@ W/"[^"]+"
# Library prefix
# e.g., `lib`+`archive`, `lib`+`raw`, `lib`+`unwind`
# (ignores some words that happen to start with `lib`)
(?:\b|_)[Ll]ib(?!era[lt])(?:re(?=office)|era|)(?!ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|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
@@ -796,9 +773,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-C \S+|(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\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

View File

@@ -105,13 +105,13 @@
^src/common/ManagedCommon/ColorFormatHelper\.cs$
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
^src/common/sysinternals/Eula/
^doc/devdocs/modules/cmdpal/initial-sdk-spec/list-elements-mock-002\.pdn$
^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$
^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$
@@ -143,4 +143,3 @@ ignore$
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/

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +1,5 @@
# 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
@@ -84,14 +71,11 @@ 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]))[A-Z]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
(?:(?:\b|_|(?<=[a-z]))[IT]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
# hit-count: 2073 file-count: 842
# #includes
@@ -175,10 +159,6 @@ 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])
@@ -191,10 +171,6 @@ 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
@@ -208,6 +184,10 @@ 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, ".*?"\)
@@ -230,15 +210,13 @@ 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|await|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?:\s\g{-1})+\s
\s(auto|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

View File

@@ -1,30 +1,23 @@
attache
aroynt.*
bellows?
^attache$
^bellows?$
benefitting
occurences?
.*dnt
dependan.*
developement
developp?e
Devers?
devex.*
devide
Devinn?[ae]
devisals?
devisors?
diables?
hasta?
hastat.*
immediatly
inisle
inital
linge
oer
^dependan.*
^develope$
^developement$
^developpe
^Devers?$
^devex
^devide
^Devinn?[ae]
^devisal
^devisor
^diables?$
^oer$
Sorce
[Ss]pae.*
Teh
untill
untilling
venders?
wether.*
^[Ss]pae.*
^Teh$
^untill$
^untilling$
^venders?$
^wether.*

View File

@@ -266,5 +266,16 @@ 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:

View File

@@ -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, 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).
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.
license: Complete terms in LICENSE.txt
---
# Release Note Generation Skill
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.
Generate professional release notes for PowerToys milestones by collecting merged PRs, requesting Copilot code reviews, grouping by label, and producing user-facing summaries.
## Output Directory
@@ -26,17 +26,16 @@ Generated Files/ReleaseNotes/
- Generate release notes for a milestone
- Summarize PRs merged in a release
- Generate per-PR review summaries locally for release-notes copy
- Request Copilot reviews for milestone PRs
- 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** — 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
- MCP Server: github-mcp-server installed
- GitHub Copilot code review enabled for the org/repo
## Required Variables
@@ -66,12 +65,12 @@ Generated Files/ReleaseNotes/
└────────────────────────────────┘
┌────────────────────────────────┐
│ 3.1 Local-agent PR summaries
│ (writes CopilotSummary) │
│ 3.1 Request Reviews (Copilot)
└────────────────────────────────┘
┌────────────────────────────────┐
│ 3.2 (Optional) Refresh PR data │
│ 3.2 Refresh PR data
│ (CopilotSummary) │
└────────────────────────────────┘
┌────────────────────────────────┐
@@ -94,7 +93,7 @@ Generated Files/ReleaseNotes/
| 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.12.4 | Label PRs | Auto-suggest + human label low-confidence |
| 3.13.3 | Reviews & Grouping | Local agent summarizes each PR diff into `CopilotSummary` → (optional refresh) → group by label |
| 3.13.3 | Reviews & Grouping | Request Copilot reviews → refresh → group by label |
| 4.14.2 | Summaries & Final | Generate grouped summaries, then consolidate |
## Detailed workflow docs
@@ -115,7 +114,6 @@ 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
@@ -135,6 +133,5 @@ 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` 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. |
| Empty CopilotSummary | Request Copilot reviews first, then re-run dump |
| 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 |

View File

@@ -1,40 +1,22 @@
# Step 3: Local Agent Reviews and Grouping
# Step 3: Copilot Reviews and Grouping
## 3.0 To-do
- 3.1 Generate PR Summaries with the Local Agent
- 3.2 (Optional) Refresh PR Data
- 3.1 Request Copilot Reviews (Agent Mode)
- 3.2 Refresh PR Data
- 3.3 Group PRs by Label
## 3.1 Generate PR Summaries with the Local Agent
## 3.1 Request Copilot Reviews (Agent Mode)
> ⚠️ **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 tools to request Copilot reviews for all PRs in `Generated Files/ReleaseNotes/sorted_prs.csv`:
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 13 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.
- Use `mcp_github_request_copilot_review` for each PR ID
- Do NOT generate or run scripts for this step
---
## 3.2 (Optional) Refresh PR Data
## 3.2 Refresh PR Data
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.
Re-run the collection script to capture Copilot review summaries into the `CopilotSummary` column:
```powershell
pwsh ./.github/skills/release-note-generation/scripts/dump-prs-since-commit.ps1 `
@@ -42,8 +24,6 @@ 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
@@ -55,4 +35,3 @@ 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)).

View File

@@ -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, “Thats exactly what I need” or “Yes, thats 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)
- 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.
- The `NeedThanks` column contains a comma-separated list of external contributor usernames who should be thanked (empty = no thanks needed, all authors are core team). For each non-empty `NeedThanks` value, append thanks for **every** listed contributor: `Thanks [@user1](https://github.com/user1)!` for a single contributor, or `Thanks [@user1](https://github.com/user1) and [@user2](https://github.com/user2)!` for two, or `Thanks [@user1](https://github.com/user1), [@user2](https://github.com/user2), and [@user3](https://github.com/user3)!` for three or more.
- 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>`

View File

@@ -1,334 +0,0 @@
<#
.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

View File

@@ -1,213 +0,0 @@
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@v9
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-ColorPicker',
'Product-Command Not Found',
'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',
'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-Power Display',
'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(', ')}`);
}

View File

@@ -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,8 +74,6 @@ on:
types:
- "created"
permissions: {}
jobs:
spelling:
name: Check Spelling
@@ -87,7 +85,7 @@ jobs:
outputs:
followup: ${{ steps.spelling.outputs.followup }}
runs-on: ubuntu-latest
if: ${{ (contains(github.event_name, 'pull_request') && github.event.pull_request.state == 'open') || github.event_name == 'push' }}
if: ${{ contains(github.event_name, 'pull_request') || 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
@@ -142,7 +140,7 @@ jobs:
comment-push:
name: Report (Push)
# If your workflow isn't running on push, you can remove this job
runs-on: ubuntu-slim
runs-on: ubuntu-latest
needs: spelling
permissions:
actions: read
@@ -152,21 +150,24 @@ jobs:
- name: comment
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
with:
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-slim
runs-on: ubuntu-latest
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@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
with:
spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }}
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
@@ -176,13 +177,12 @@ jobs:
contents: write
pull-requests: write
actions: read
runs-on: ubuntu-slim
runs-on: ubuntu-latest
if: ${{
github.repository_owner != 'microsoft' &&
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '@check-spelling-bot') &&
contains(github.event.comment.body, 'apply') &&
contains(github.event.comment.body, '@check-spelling-bot apply') &&
contains(github.event.comment.body, 'https://')
}}
concurrency:

6
.gitignore vendored
View File

@@ -365,8 +365,6 @@ installer/*/*.wxs.bk
**/.claude/settings.local.json
# Squad / Copilot agents — local-only, not committed
.copilot
.squad
.squad/
.squad-workstream
.github/agents/**squad**.md
.github/workflows/**squad**.yml
.github/agents/

View File

@@ -250,9 +250,6 @@
"PowerToys.ZoomItModuleInterface.dll",
"PowerToys.ZoomItSettingsInterop.dll",
"PowerToys.GrabAndMove.exe",
"PowerToys.GrabAndMoveModuleInterface.dll",
"WinUI3Apps\\PowerToys.Settings.dll",
"WinUI3Apps\\PowerToys.Settings.exe",
@@ -264,8 +261,8 @@
"Workspaces.ModuleServices.dll",
"Microsoft.CommandPalette.Extensions.dll",
"Microsoft.CommandPalette.Extensions.Toolkit.dll",
"WinUI3Apps\\Microsoft.CmdPal.Ext.PowerToys.dll",
"WinUI3Apps\\Microsoft.CmdPal.Ext.PowerToys.exe",
"Microsoft.CmdPal.Ext.PowerToys.dll",
"Microsoft.CmdPal.Ext.PowerToys.exe",
"*Microsoft.CmdPal.UI_*.msix",
"PowerToys.DSC.dll",

View File

@@ -16,7 +16,6 @@ pr:
include:
- main
- stable
drafts: false
# paths:
# exclude:
# - '**.md'

View File

@@ -70,7 +70,7 @@ parameters:
default: false
- name: winAppSDKVersionNumber
type: string
default: '2.0'
default: 1.6
- name: useExperimentalVersion
type: boolean
default: false
@@ -200,7 +200,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '10.0'
version: '9.0'
- ${{ if eq(parameters.runTests, true) }}:
- task: VisualStudioTestPlatformInstaller@1
@@ -418,7 +418,7 @@ jobs:
/p:VCRTForwarders-IncludeDebugCRT=false
/p:PowerToysRoot=$(Build.SourcesDirectory)
/p:PublishProfile=InstallationPublishProfile.pubxml
/p:TargetFramework=net10.0-windows10.0.26100.0
/p:TargetFramework=net9.0-windows10.0.26100.0
/bl:$(LogOutputDirectory)\publish-${{ join('_',split(project, '/')) }}.binlog
$(RestoreAdditionalProjectSourcesArg)
platform: $(BuildPlatform)

View File

@@ -64,7 +64,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '10.0'
version: '9.0'
- template: .\steps-restore-nuget.yml

View File

@@ -30,7 +30,7 @@ parameters:
default: false
- name: winAppSDKVersionNumber
type: string
default: '2.0'
default: 1.6
- name: useExperimentalVersion
type: boolean
default: false

View File

@@ -27,7 +27,6 @@ stages:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
demands: ImageOverride -equals SHINE-VS18-Latest
buildPlatforms:
- ${{ parameters.platform }}
uiTestModules: ${{ parameters.uiTestModules }}

View File

@@ -1,7 +1,7 @@
parameters:
- name: version
type: string
default: "10.0"
default: "9.0"
- name: sdk
type: boolean
default: false

View File

@@ -1,7 +1,7 @@
parameters:
- name: versionNumber
type: string
default: '2.0'
default: 1.6
- name: useExperimentalVersion
type: boolean
default: false

View File

@@ -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 --no-restore";
Arguments = "list $csproj package";
RedirectStandardOutput = $true;
RedirectStandardError = $true;
}

View File

@@ -13,6 +13,5 @@
{
"file": ".github/prompts/create-pr-summary.prompt.md"
}
],
"sarif-viewer.connectToGithubCodeScanning": "on"
]
}

View File

@@ -108,7 +108,6 @@ Thank you for using PowerToys!
| Microsoft.PowerToys.AwakeIndefinitelyKeepAwakeEvent | Triggered when the system is set to stay awake indefinitely. |
| Microsoft.PowerToys.AwakeNoKeepAwakeEvent | Occurs when Awake is turned off, allowing the computer to enter sleep mode. |
| Microsoft.PowerToys.AwakeTimedKeepAwakeEvent | Triggered when the system is kept awake for a specified time duration. |
| Microsoft.PowerToys.Awake_CLICommand | Triggered when an Awake CLI command is executed, logging the command name and success status. |
### Color Picker
@@ -205,7 +204,6 @@ Thank you for using PowerToys!
| Microsoft.PowerToys.FileLocksmith_Invoked | Occurs when File Locksmith is invoked. |
| Microsoft.PowerToys.FileLocksmith_InvokedRet | Triggered when File Locksmith invocation returns a result. |
| Microsoft.PowerToys.FileLocksmith_QueryContextMenuError | Occurs when there is an error querying the context menu for File Locksmith. |
| Microsoft.PowerToys.FileLocksmith_CLICommand | Triggered when a File Locksmith CLI command is executed, logging the operation mode (query, kill, query-wait, query-json, or help) and success status. |
### FileExplorerAddOns
@@ -242,13 +240,6 @@ Thank you for using PowerToys!
| Microsoft.PowerToys.FindMyMouse_EnableFindMyMouse | Triggered when Find My Mouse is enabled. |
| Microsoft.PowerToys.FindMyMouse_MousePointerFocused | Occurs when the mouse pointer is focused using Find My Mouse, including the activation method (double-tap left/right Ctrl, shake mouse, or shortcut). |
### Grab And Move
| Event Name | Description |
| --- | --- |
| Microsoft.PowerToys.GrabAndMove_EnableGrabAndMove | Triggered when Grab And Move is enabled or disabled. |
| Microsoft.PowerToys.GrabAndMove_ShortcutUse | Logs an attempt to move or resize a window via the Alt+Drag shortcut, including whether it succeeded, the action type (move or resize), and the reason (e.g., started, blocked by game mode). |
### Hosts File Editor
| Event Name | Description |
@@ -267,7 +258,6 @@ Thank you for using PowerToys!
| Microsoft.PowerToys.ImageResizer_Invoked | Occurs when Image Resizer is invoked by the user. |
| Microsoft.PowerToys.ImageResizer_InvokedRet | Fires when the Image Resizer operation is completed and returns a result. |
| Microsoft.PowerToys.ImageResizer_QueryContextMenuError | Triggered when there is an error querying the context menu for Image Resizer. |
| Microsoft.PowerToys.ImageResizer_CLICommand | Triggered when an Image Resizer CLI command is executed, logging the command name and success status. |
### Keyboard Manager
@@ -369,15 +359,6 @@ Thank you for using PowerToys!
| Microsoft.PowerToys.Peek_Settings | Triggered when the settings for Peek are modified. |
| Microsoft.PowerToys.Peek_SpaceModeEnabled | Triggered when the Space key activation mode is enabled or disabled in Peek. |
### Power Display
| Event Name | Description |
| --- | --- |
| Microsoft.PowerToys.PowerDisplay_EnablePowerDisplay | Triggered when Power Display is enabled or disabled. |
| Microsoft.PowerToys.PowerDisplay_Activate | Triggered when Power Display is activated via hotkey or tray toggle. |
| Microsoft.PowerToys.PowerDisplay_Start | Triggered when the Power Display application process starts. |
| Microsoft.PowerToys.PowerDisplay_Settings | Periodic snapshot of Power Display settings, including whether the hotkey and tray icon are enabled, the number of detected monitors, and the number of saved profiles. |
### PowerRename
| Event Name | Description |

View File

@@ -85,6 +85,14 @@
<ForceImportBeforeCppProps>$(RepoRoot)Cpp.Build.props</ForceImportBeforeCppProps>
</PropertyGroup>
<!-- Force all C# projects to reference these packages from NuGet so their 10.x versions
take precedence over the 9.x versions bundled in the .NET 9 runtime. Without this,
projects that don't transitively depend on these packages would get the runtime 9.x
version, causing deps.json version conflicts with projects that do. -->
<ItemGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
<PackageReference Include="System.Diagnostics.EventLog" />
<PackageReference Include="System.Threading.Channels" />
</ItemGroup>
<ItemGroup Condition="'$(MSBuildProjectExtension)' == '.csproj' and '$(_IsSkippedTestProject)' != 'true'">
<PackageReference Include="StyleCop.Analyzers">
@@ -171,12 +179,6 @@
$(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.

View File

@@ -65,20 +65,4 @@
<!-- 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>

View File

@@ -1,4 +1,4 @@
<Project>
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
@@ -39,37 +39,36 @@
<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="10.0.102" />
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.9.260303001" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.7" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.10" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.7" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.5" />
<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="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.Extensions.AI" Version="10.4.1" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.4.1" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.5" />
<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.74.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.74.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.74.0-beta" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.74.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.74.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.74.0-alpha" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3719.77" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="10.0.7" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.10" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="10.0.7" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.269" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.10" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
TODO: in Common.Dotnet.CsWinRT.props, on upgrade, verify RemoveCsWinRTPackageAnalyzer is no longer needed.
@@ -78,10 +77,10 @@
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<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="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.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.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 +89,12 @@
<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.9.1" />
<PackageVersion Include="Polly.Core" Version="8.6.5" />
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
<PackageVersion Include="RtfPipe" Version="2.0.7677.4303" />
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
@@ -105,28 +105,35 @@
<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="10.0.7" />
<PackageVersion Include="System.CodeDom" Version="9.0.10" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<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="10.0.7" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.10" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.10" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.10" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
<!-- Package System.Diagnostics.EventLog force-pinned to 10.x to match other Microsoft.Extensions packages;
referenced by all C# projects (via Directory.Build.props) to override the 9.x version from the .NET runtime. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="10.0.5" />
<!-- 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="10.0.7" />
<PackageVersion Include="System.ClientModel" Version="1.7.0" />
<PackageVersion Include="System.Drawing.Common" Version="10.0.7" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.10" />
<PackageVersion Include="System.ClientModel" Version="1.9.0" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.10" />
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="10.0.7" />
<PackageVersion Include="System.Management" Version="9.0.10" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.11" />
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.4" />
<!-- Including System.Threading.Channels to force version across all projects;
referenced by all C# projects (via Directory.Build.props) to override the 9.x version from the .NET runtime. -->
<PackageVersion Include="System.Threading.Channels" Version="10.0.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<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.Runtime.Caching" Version="9.0.10" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="10.0.5" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
<PackageVersion Include="System.Text.Json" Version="10.0.5" />
<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" />

View File

@@ -1587,6 +1587,7 @@ SOFTWARE.
- NLog.Extensions.Logging
- NLog.Schema
- OpenAI
- Polly.Core
- ReverseMarkdown
- ScipBe.Common.Office.OneNote
- SharpCompress

View File

@@ -206,10 +206,6 @@
<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" />
@@ -323,10 +319,6 @@
<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" />
@@ -799,6 +791,10 @@
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/peek/peek/peek.vcxproj" Id="a1425b53-3d61-4679-8623-e64a0d3d0a48" />
<Project Path="src/modules/peek/Peek.Common.UnitTests/Peek.Common.UnitTests.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
</Folder>
<Folder Name="/modules/PowerAccent/">
<Project Path="src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj">
@@ -811,6 +807,10 @@
</Project>
<Project Path="src/modules/poweraccent/PowerAccentKeyboardService/PowerAccentKeyboardService.vcxproj" Id="c97d9a5d-206c-454e-997e-009e227d7f02" />
<Project Path="src/modules/poweraccent/PowerAccentModuleInterface/PowerAccentModuleInterface.vcxproj" Id="34a354c5-23c7-4343-916c-c52daf4fc39d" />
<Project Path="src/modules/poweraccent/PowerAccent.Core.UnitTests/PowerAccent.Core.UnitTests.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
</Folder>
<Folder Name="/modules/PowerOCR/">
<Project Path="src/modules/PowerOCR/PowerOCR/PowerOCR.csproj">
@@ -1044,10 +1044,6 @@
<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" />
@@ -1112,8 +1108,6 @@
<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" />

View File

@@ -29,13 +29,13 @@ PowerToys includes over 30 utilities to help you customize and optimize your Win
| [<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/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) |
| [<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) | | |
## 📦 Installation
@@ -47,7 +47,21 @@ But to get started quickly, choose one of the installation methods below:
<summary><strong>Download the .exe file from GitHub</strong></summary>
<br/>
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_.
Go to the [PowerToys GitHub releases](https://aka.ms/installPowerToys), 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_.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.99%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-arm64.exe
| Description | Filename |
| --- | --- |
| Per user - x64 | [PowerToysUserSetup-0.98.1-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.98.1-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.98.1-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.98.1-arm64.exe][ptMachineArm64] |
</details>
@@ -92,11 +106,11 @@ There are [community driven install methods](https://learn.microsoft.com/windows
[![What's new image](doc/images/readme/Release-Banner.png)](https://github.com/microsoft/PowerToys/releases)
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/).
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.98.1).
## 🛣️ Roadmap
We are planning some nice new features and improvements for the next releases 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]!
We are planning some nice new features and improvements for the next releases PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.99][github-next-release-work]!
## ❤️ PowerToys Community
@@ -117,4 +131,3 @@ The application logs basic diagnostic data (telemetry). For more privacy informa
[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

View File

@@ -1,54 +0,0 @@
# 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
}

View File

@@ -1,94 +0,0 @@
// 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
View File

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

View File

@@ -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 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:
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:
```
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

View File

@@ -1,167 +0,0 @@
# 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.

View File

@@ -461,7 +461,7 @@ Editor read/write config data handler is in FancyZonesEditorCommon project.
FancyZones cpp project read/write config data handler is in FancyZonesLib project.
![Debug Step Image](../images/fancyzones/19.png)
However, the files read from and written to are those in `C:\Users\“xxxxxx”\AppData\Local\Microsoft\PowerToys\FancyZones`
However, the files write and read those are C:\Users\“xxxxxx”\AppData\Local\Microsoft\PowerToys\FancyZones
You can think of the editor as a visual config editor, which is most of its functionality. Another feature is used to set the layout for the monitor displays.

View File

@@ -3,9 +3,9 @@
- [ ] The plugin is a project under `modules\launcher\Plugins`
- [ ] Microsoft plugin project name pattern: `Microsoft.PowerToys.Run.Plugin.{PluginName}`
- [ ] Community plugin project name pattern: `Community.PowerToys.Run.Plugin.{PluginName}`
- [ ] The plugin target framework should be `net10.0-windows10.0.22621.0`
- [ ] The plugin target framework should be `net9.0-windows10.0.22621.0`
- [ ] If the plugin uses any 3rd party dependencies the project file should import `DynamicPlugin.props`
- [ ] 3rd party dependencies must be compatible with .NET 10
- [ ] 3rd party dependencies must be compatible with .NET 9
- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder:
```json

View File

@@ -75,7 +75,7 @@ There are three different score types with different start values.
| Medium score | 5000 |
| Low score | 1000 |
Each score will be decreased by one when a condition match.
Each score will decreased by one when a condition match.
| Priority | Condition | Score type |
| -------- | ----------------------------------------------------------------- | ------------ |
@@ -134,7 +134,7 @@ The plugin use only these interfaces (all inside the `Main.cs`):
| `plugin.json` | All meta-data for this plugin |
1. We need this extra wrapper class to make it possible that the JSON file can have and use a JSON schema file.
Because the JSON file must have an object as root type, instead of an array.
Because the JSON file must have a object as root type, instead of a array.
### Important project values (*.csproj)

View File

@@ -1,93 +0,0 @@
# PowerToys Installer & Update Diagnostics
A step-by-step guide for diagnosing installer and update issues reported by users.
## Quick Reference: Key Files
| File/Folder | Path | Contains |
|---|---|---|
| UpdateState.json | `%LOCALAPPDATA%\Microsoft\PowerToys\UpdateState.json` | Persisted update state machine |
| Runner logs | `%LOCALAPPDATA%\Microsoft\PowerToys\RunnerLogs\runner-log_*.log` | Startup, update checks, cleanup |
| Update logs | `%LOCALAPPDATA%\Microsoft\PowerToys\UpdateLogs\update-log_*.log` | PowerToys.Update.exe activity |
| Updates folder | `%LOCALAPPDATA%\Microsoft\PowerToys\Updates\` | Downloaded installer files |
> **Note:** These paths use `%LOCALAPPDATA%` (per-user AppData) regardless of whether PowerToys was installed per-user or per-machine. The data/settings location is always per-user.
## Update State Values
From `src/common/updating/updateState.h` (`UpdateState::State` enum):
| Value | Name | Meaning |
|---|---|---|
| 0 | upToDate | No update needed |
| 1 | errorDownloading | Download or install failed, will retry |
| 2 | readyToDownload | New version found, not yet downloaded |
| 3 | readyToInstall | Installer downloaded, waiting for user action |
| 4 | networkError | GitHub API call failed |
---
## Symptom: Old update installers accumulating on disk
### What to ask the user for
1. Contents of `UpdateState.json`
2. Runner logs (last few days from `RunnerLogs\`)
3. Update logs (from `UpdateLogs\`, if they exist)
4. List of files in `Updates\` folder (names + sizes)
### Step 1: Check the running version
In runner logs, look for the startup line:
```
[info] Scoobe: product_version=v0.XX.X last_version_run=v0.XX.X
```
- **If version < v0.73.0**: The pre-download cleanup (PR #27908) is missing. Each downloaded installer accumulates because cleanup only runs at startup when state is `upToDate`. Ask the user to manually upgrade to the latest version.
- **If version >= v0.73.0**: The pre-download cleanup exists. Accumulation should not happen under normal conditions. Continue to Step 2.
### Step 2: Check UpdateState.json
```jsonc
{"state": 3, "downloadedInstallerFilename": "powertoyssetup-0.98.1-x64.exe" /* additional fields may be present */}
```
- **state = 0 (upToDate)**: Cleanup should run at startup. If files are accumulating, check runner logs for "Failed to delete" warnings (Step 4).
- **state = 3 (readyToInstall)**: An installer is downloaded but never installed. Cleanup at startup is skipped (by design, to preserve the pending installer). On v0.73+, cleanup can still occur when a future update check triggers a new download (pre-download cleanup path).
- **state = 1 (errorDownloading)**: A previous download or install failed. Startup cleanup is skipped (state is not `upToDate`). On v0.73+, cleanup runs before the next installer download is attempted.
- **state = 2 or 4**: Startup cleanup is skipped. On v0.73+, cleanup runs before the next installer download is attempted.
### Step 3: Check if PowerToys.Update.exe has ever run
- **UpdateLogs directory missing**: This suggests `PowerToys.Update.exe` may never have been launched, or it did not progress far enough to create logs. The user may never have triggered an install, or Stage 1 may have failed before Stage 2 could run.
- **UpdateLogs exist but show only "logger is initialized"**: The exe launched but the command-line argument didn't match any action (possible argument parsing issue).
- **UpdateLogs show install activity**: The update process ran. Check for success/failure.
### Step 4: Check runner logs for cleanup evidence
Search for these patterns:
| Log pattern | Meaning |
|---|---|
| `Failed to delete installer file ... Access is denied` | File locked by AV, another process, or permissions issue |
| `Failed to delete log file ...` | Same, for old log files |
| `Discovered new version` | Periodic update check ran |
| `New version is already downloaded` | State is `readyToInstall` and filename matches — no re-download, no cleanup |
| No cleanup-related entries at all | Inconclusive by itself — `cleanup_updates()` is silent on success. Corroborate with the Updates folder contents (Step 5) and the running version (Step 1). |
### Step 5: Check the Updates folder contents
- **All different versions**: Cleanup likely did not run across multiple update cycles. Confirm with the running version (Step 1) and update state before concluding a state gate issue.
- **Duplicate filenames**: Unusual — would suggest repeated download without cleanup.
- **Single file matching `downloadedInstallerFilename`**: Normal for `readyToInstall` state.
### Common root causes
| Root cause | Evidence | Fix |
|---|---|---|
| Running pre-v0.73.0 binary | `product_version` < v0.73.0 in runner log | Manually upgrade to latest |
| State stuck at `readyToInstall` (pre-v0.73) | `"state": 3` in UpdateState.json, no UpdateLogs | Manually upgrade to latest |
| File lock preventing deletion | "Failed to delete ... Access is denied" in runner logs | Check AV software, reboot and retry |
| Update installer never launched | No UpdateLogs directory | Check if update notifications are disabled by GPO or setting |
| Install fails silently | UpdateLogs show init but no install activity | Check related issues: #46966, #46967, #46969 |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -4,7 +4,6 @@
#include <ProjectTelemetry.h>
#include <spdlog/sinks/base_sink.h>
#include <filesystem>
#include <fstream>
#include <string_view>
#include "../../src/common/logger/logger.h"
@@ -640,7 +639,7 @@ UINT __stdcall InstallPackageIdentityMSIXCA(MSIHANDLE hInstall)
try
{
std::wstring externalLocation = installFolderPath + L"WinUI3Apps\\"; // External content location (WinUI3Apps subfolder to isolate DACL changes from preview handler DLLs)
std::wstring externalLocation = installFolderPath; // External content location (PowerToys install folder)
Uri externalUri{ externalLocation }; // External location URI for sparse package content
Uri packageUri{ msixPath }; // The MSIX file URI
@@ -1808,223 +1807,6 @@ void initSystemLogger()
} });
}
// Naming note: the *Hardlinks* names in this CA, the matching WiX CustomAction Ids
// in Product.wxs, and the manifest filename "hardlinks.txt" are kept for continuity
// with the original PR design. The implementation uses fs::copy_file -- not
// CreateHardLinkW -- because hard-links share an inode (and DACL) between root and
// WinUI3Apps, which lets MSIX sparse-package registration propagate a rich DACL onto
// the root copy of files like hostfxr.dll and break LOW-IL prevhost.exe loads,
// turning the Monaco preview pane blank. Copies create a fresh inode in WinUI3Apps so the root
// copy keeps its simple DACL. See the in-body comment for the full RCA reference.
UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
std::wstring installationFolder;
hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinks");
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
{
namespace fs = std::filesystem;
const fs::path installDir(installationFolder);
const fs::path winui3Dir = installDir / L"WinUI3Apps";
const fs::path manifestPath = winui3Dir / L"hardlinks.txt";
if (!fs::exists(manifestPath))
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: No hardlinks.txt manifest found, skipping.");
goto LExit;
}
std::ifstream manifestFile(manifestPath); // Read as bytes, then convert UTF-8 -> wide explicitly.
std::string narrowLine;
int created = 0;
int failed = 0;
// INSTALLFOLDER from MSI typically arrives with a trailing backslash. lexically_normal
// preserves that as an empty trailing path component, which would later make the
// per-component std::mismatch containment check below reject every legitimate entry.
// Strip any trailing separators before normalizing.
auto stripTrailingSep = [](fs::path p) {
auto s = p.native();
while (s.size() > 1 && (s.back() == L'\\' || s.back() == L'/')) s.pop_back();
return fs::path(s);
};
// Normalize once so the per-line containment check below is cheap.
const fs::path installDirNorm = stripTrailingSep(installDir).lexically_normal();
const fs::path winui3DirNorm = stripTrailingSep(winui3Dir).lexically_normal();
while (std::getline(manifestFile, narrowLine))
{
if (narrowLine.empty())
{
continue;
}
// Strip CR if the manifest uses CRLF line endings.
if (narrowLine.back() == '\r')
{
narrowLine.pop_back();
if (narrowLine.empty()) continue;
}
// Manifest is written as UTF-8 (no BOM) -- convert to wide string explicitly
// rather than relying on the locale-default codecvt of std::wifstream, which is
// the ANSI code page on Windows and would silently mangle any non-ASCII path.
const int wideLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, nullptr, 0);
if (wideLen <= 0)
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Skipping non-UTF-8 entry: %hs", narrowLine.c_str());
failed++;
continue;
}
std::wstring fileName(static_cast<size_t>(wideLen) - 1, L'\0');
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, fileName.data(), wideLen);
// Defense-in-depth: reject manifest entries that would escape the install root
// via "..", absolute paths, or alternate stream syntax. lexically_normal collapses
// any "." / ".." / repeated separators, then std::mismatch verifies the resolved
// path is still rooted at installDir / winui3Dir respectively.
const fs::path source = (installDir / fileName).lexically_normal();
const fs::path target = (winui3Dir / fileName).lexically_normal();
const auto sourceIn = std::mismatch(installDirNorm.begin(), installDirNorm.end(), source.begin(), source.end());
const auto targetIn = std::mismatch(winui3DirNorm.begin(), winui3DirNorm.end(), target.begin(), target.end());
if (sourceIn.first != installDirNorm.end() || targetIn.first != winui3DirNorm.end())
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Rejecting entry outside install root: %ls", fileName.c_str());
failed++;
continue;
}
if (!fs::exists(source))
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Source not found: %ls", source.c_str());
failed++;
continue;
}
// Remove existing file if present (leftover from previous install)
std::error_code ec;
fs::remove(target, ec);
// Use a regular file copy (not a hard-link). Hard-links share an
// NTFS inode -- and therefore one DACL -- between root and
// WinUI3Apps, which lets MSIX sparse-package registration
// propagate the WinUI3Apps parent's rich (Capability/Package SID)
// DACL onto the root path. That trips a kernel "stricter access
// evaluation" path that blocks LOW-IL prevhost.exe from loading
// hostfxr.dll, so File Explorer Monaco preview goes blank on
// Windows 11 23H2. Copying creates a fresh inode in WinUI3Apps,
// so the root copy keeps its simple DACL while the WinUI3Apps
// copy inherits the rich DACL from its parent (matches 0.99.1
// behaviour). See Documents\PR-47233-Handoff.md for full RCA.
fs::copy_file(source, target, fs::copy_options::overwrite_existing, ec);
if (ec)
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Failed to copy: %ls (%hs)", fileName.c_str(), ec.message().c_str());
failed++;
}
else
{
created++;
}
}
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Copied %d files, %d failures", created, failed);
// Catastrophic-case escalation: if every copy failed, the WinUI3Apps tree is
// unusable (Monaco preview / context-menu shells will break). Surface this rather
// than reporting install success. Per-file failures remain tolerated.
if (created == 0 && failed > 0)
{
hr = E_FAIL;
ExitOnFailure(hr, "All WinAppSDK file copies failed; aborting install.");
}
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
std::wstring installationFolder;
hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinks");
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
{
namespace fs = std::filesystem;
const fs::path winui3Dir = fs::path(installationFolder) / L"WinUI3Apps";
const fs::path manifestPath = winui3Dir / L"hardlinks.txt";
if (!fs::exists(manifestPath))
{
goto LExit;
}
std::ifstream manifestFile(manifestPath); // Read as bytes; convert UTF-8 -> wide explicitly.
std::string narrowLine;
// INSTALLFOLDER from MSI typically arrives with a trailing backslash; strip it before
// normalizing so the per-line containment check doesn't false-reject every entry.
auto stripTrailingSep = [](fs::path p) {
auto s = p.native();
while (s.size() > 1 && (s.back() == L'\\' || s.back() == L'/')) s.pop_back();
return fs::path(s);
};
const fs::path winui3DirNorm = stripTrailingSep(winui3Dir).lexically_normal();
while (std::getline(manifestFile, narrowLine))
{
if (narrowLine.empty())
{
continue;
}
if (narrowLine.back() == '\r')
{
narrowLine.pop_back();
if (narrowLine.empty()) continue;
}
const int wideLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, nullptr, 0);
if (wideLen <= 0)
{
WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Skipping non-UTF-8 entry: %hs", narrowLine.c_str());
continue;
}
std::wstring fileName(static_cast<size_t>(wideLen) - 1, L'\0');
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, fileName.data(), wideLen);
// Defense-in-depth: reject entries whose resolved target escapes WinUI3Apps.
const fs::path target = (winui3Dir / fileName).lexically_normal();
const auto inWinui3 = std::mismatch(winui3DirNorm.begin(), winui3DirNorm.end(), target.begin(), target.end());
if (inWinui3.first != winui3DirNorm.end())
{
WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Rejecting entry outside WinUI3Apps: %ls", fileName.c_str());
continue;
}
std::error_code ec;
fs::remove(target, ec);
}
WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Cleaned up deduplicated copy files");
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
{

View File

@@ -36,5 +36,3 @@ EXPORTS
SetBundleInstallLocationCA
InstallPackageIdentityMSIXCA
UninstallPackageIdentityMSIXCA
CreateWinAppSDKHardlinksCA
DeleteWinAppSDKHardlinksCA

View File

@@ -6,16 +6,13 @@
<?define BaseApplicationsFilesPath=$(var.BinDir)\?>
<Fragment>
<!-- winmd must be in WinUI3Apps (ExternalLocation) for WinRT COM proxy/stub resolution -->
<DirectoryRef Id="WinUI3AppsInstallFolder">
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="Microsoft_CommandPalette_Extensions_winmd" Guid="304AD25A-A986-4058-940E-61DB79EBD78C" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Microsoft_CommandPalette_Extensions_winmd" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.CommandPalette.Extensions.winmd" Source="$(var.BinDir)WinUI3Apps\Microsoft.CommandPalette.Extensions.winmd" />
<File Id="Microsoft.CommandPalette.Extensions.winmd" Source="$(var.BinDir)Microsoft.CommandPalette.Extensions.winmd" />
</Component>
</DirectoryRef>
<DirectoryRef Id="INSTALLFOLDER">
<!-- Generated by generateFileComponents.ps1 -->
<!--BaseApplicationsFiles_Component_Def-->
</DirectoryRef>

View File

@@ -67,11 +67,8 @@
<RegistryValue Type="string" Name="svgs_icons" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="icon.ico" Source="$(var.BinDir)svgs\icon.ico" />
<File Id="iconUpdate.ico" Source="$(var.BinDir)svgs\iconUpdate.ico" />
<File Id="PowerToysWhite.ico" Source="$(var.BinDir)svgs\PowerToysWhite.ico" />
<File Id="PowerToysWhiteUpdate.ico" Source="$(var.BinDir)svgs\PowerToysWhiteUpdate.ico" />
<File Id="PowerToysDark.ico" Source="$(var.BinDir)svgs\PowerToysDark.ico" />
<File Id="PowerToysDarkUpdate.ico" Source="$(var.BinDir)svgs\PowerToysDarkUpdate.ico" />
</Component>
</Directory>
</DirectoryRef>

View File

@@ -4,11 +4,11 @@
<?define KeyboardManagerAssetsFiles=?>
<?define KeyboardManagerAssetsWinUI3Files=?>
<?define KeyboardManagerAssetsFilesPath=$(var.BinDir)\WinUI3Apps\Assets\KeyboardManager\?>
<?define KeyboardManagerAssetsFilesPath=$(var.BinDir)\Assets\KeyboardManager\?>
<?define KeyboardManagerAssetsWinUI3FilesPath=$(var.BinDir)\WinUI3Apps\Assets\KeyboardManagerEditor\?>
<Fragment>
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<DirectoryRef Id="BaseApplicationsAssetsFolder">
<Directory Id="KeyboardManagerAssetsInstallFolder" Name="KeyboardManager" />
</DirectoryRef>
<DirectoryRef Id="WinUI3AppsAssetsFolder">

View File

@@ -112,8 +112,6 @@
<Custom Action="SetInstallCmdPalPackageParam" Before="InstallCmdPalPackage" />
<Custom Action="SetUninstallCommandNotFoundParam" Before="UninstallCommandNotFound" />
<Custom Action="SetUpgradeCommandNotFoundParam" Before="UpgradeCommandNotFound" />
<Custom Action="SetCreateWinAppSDKHardlinksParam" Before="CreateWinAppSDKHardlinks" />
<Custom Action="SetDeleteWinAppSDKHardlinksParam" Before="DeleteWinAppSDKHardlinks" />
<Custom Action="SetApplyModulesRegistryChangeSetsParam" Before="ApplyModulesRegistryChangeSets" />
<Custom Action="SetInstallPackageIdentityMSIXParam" Before="InstallPackageIdentityMSIX" />
@@ -126,7 +124,6 @@
<Custom Action="SetBundleInstallLocationData" Before="SetBundleInstallLocation" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
<Custom Action="SetBundleInstallLocation" After="InstallFiles" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
<Custom Action="ApplyModulesRegistryChangeSets" After="InstallFiles" Condition="NOT Installed" />
<Custom Action="CreateWinAppSDKHardlinks" After="InstallFiles" Condition="NOT Installed OR WIX_UPGRADE_DETECTED OR REINSTALL" />
<Custom Action="InstallCmdPalPackage" After="InstallFiles" Condition="NOT Installed" />
<Custom Action="InstallPackageIdentityMSIX" After="InstallFiles" Condition="NOT Installed AND WINDOWSBUILDNUMBER &gt;= 22000" />
<Custom Action="override Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Before="RemoveFiles" />
@@ -140,7 +137,6 @@
<?endif?>
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize" Condition="NOT Installed" />
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize" Condition="Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="DeleteWinAppSDKHardlinks" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="UnApplyModulesRegistryChangeSets" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="CleanImageResizerRuntimeRegistry" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
@@ -193,10 +189,8 @@
<CustomAction Id="SetUpgradeCommandNotFoundParam" Property="UpgradeCommandNotFound" Value="[INSTALLFOLDER]" />
<CustomAction Id="SetCreateWinAppSDKHardlinksParam" Property="CreateWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
<CustomAction Id="CreateWinAppSDKHardlinks" Return="check" Impersonate="yes" Execute="deferred" DllEntry="CreateWinAppSDKHardlinksCA" BinaryRef="PTCustomActions" />
<CustomAction Id="SetDeleteWinAppSDKHardlinksParam" Property="DeleteWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
<CustomAction Id="DeleteWinAppSDKHardlinks" Return="ignore" Impersonate="yes" Execute="deferred" DllEntry="DeleteWinAppSDKHardlinksCA" BinaryRef="PTCustomActions" />
<CustomAction Id="SetCreatePTInteropHardlinksParam" Property="CreatePTInteropHardlinks" Value="[INSTALLFOLDER]" />

View File

@@ -9,7 +9,7 @@
<Fragment>
<!-- Resource directories should be added only if the installer is built on the build farm -->
<?ifdef env.IsPipeline?>
<?foreach ParentDirectory in INSTALLFOLDER;WinUI3AppsInstallFolder;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
<?foreach ParentDirectory in INSTALLFOLDER;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
<DirectoryRef Id="$(var.ParentDirectory)">
<!-- Resource file directories -->
<?foreach Language in $(var.LocLanguageList)?>
@@ -361,11 +361,11 @@
</RegistryKey>
<File Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.BgcodePreviewHandler.resources.dll" />
</Component>
<Component Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" Guid="$(var.CompGUIDPrefix)23">
<Component Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER" Guid="$(var.CompGUIDPrefix)23">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="CmdPalExtPowerToys_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\WinUI3Apps\$(var.Language)\Microsoft.CmdPal.Ext.PowerToys.resources.dll" />
<File Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\Microsoft.CmdPal.Ext.PowerToys.resources.dll" />
</Component>
<?undef IdSafeLanguage?>
<?undef CompGUIDPrefix?>
@@ -433,7 +433,6 @@
<?define IdSafeLanguage = $(var.Language)?>
<?endif?>
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)INSTALLFOLDER" Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)CalculatorPluginFolder" Directory="Resource$(var.IdSafeLanguage)CalculatorPluginFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)FolderPluginFolder" Directory="Resource$(var.IdSafeLanguage)FolderPluginFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)ProgramPluginFolder" Directory="Resource$(var.IdSafeLanguage)ProgramPluginFolder" On="uninstall" />

View File

@@ -17,9 +17,6 @@
<?define SettingsV2IconsModelsFiles=?>
<?define SettingsV2IconsModelsFilesPath=$(var.BinDir)WinUI3Apps\Assets\Settings\Icons\Models\?>
<?define SettingsV2AssetsCmdPalFiles=?>
<?define SettingsV2AssetsCmdPalFilesPath=$(var.BinDir)WinUI3Apps\Assets\Settings\CmdPal\?>
<Fragment>
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<Directory Id="SettingsV2AssetsInstallFolder" Name="Settings">
@@ -30,7 +27,6 @@
<Directory Id="SettingsV2AssetsModulesInstallFolder" Name="Modules">
<Directory Id="SettingsV2OOBEAssetsModulesInstallFolder" Name="OOBE" />
</Directory>
<Directory Id="SettingsV2AssetsCmdPalInstallFolder" Name="CmdPal" />
</Directory>
</DirectoryRef>
@@ -59,11 +55,6 @@
<!--SettingsV2IconsModelsFiles_Component_Def-->
</DirectoryRef>
<DirectoryRef Id="SettingsV2AssetsCmdPalInstallFolder" FileSource="$(var.SettingsV2AssetsCmdPalFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--SettingsV2AssetsCmdPalFiles_Component_Def-->
</DirectoryRef>
<DirectoryRef Id="SettingsAppAssetsScriptsFolder" FileSource="$(var.SettingsV2AssetsFilesPath)\Scripts\">
<Component Id="CommandNotFound_Scripts" Guid="898EFA1E-EDD3-4F4B-8C7F-4A14B0D05B02" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
@@ -89,7 +80,6 @@
<RemoveFolder Id="RemoveFolderSettingsV2IconsModelsInstallFolder" Directory="SettingsV2IconsModelsInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2AssetsModulesInstallFolder" Directory="SettingsV2AssetsModulesInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2OOBEAssetsModulesInstallFolder" Directory="SettingsV2OOBEAssetsModulesInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsV2AssetsCmdPalInstallFolder" Directory="SettingsV2AssetsCmdPalInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveFolderSettingsAppAssetsScriptsFolder" Directory="SettingsAppAssetsScriptsFolder" On="uninstall" />
</Component>
<ComponentRef Id="CommandNotFound_Scripts" />

View File

@@ -7,18 +7,11 @@
<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Component Id="WinUI3Apps_Hardlinks_Manifest" Guid="F7A2C3D1-8E4B-4F6A-9D2E-1B3C5A7F8E90" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="WinUI3Apps_Hardlinks_Manifest" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="WinUI3Apps_hardlinks_txt" Source="$(var.BinDir)\WinUI3Apps\hardlinks.txt" />
</Component>
<!-- Generated by generateFileComponents.ps1 -->
<!--WinUI3ApplicationsFiles_Component_Def-->
</DirectoryRef>
<ComponentGroup Id="WinUI3ApplicationsComponentGroup">
<ComponentRef Id="WinUI3Apps_Hardlinks_Manifest" />
</ComponentGroup>
</Fragment>

View File

@@ -30,10 +30,6 @@ Function Generate-FileList() {
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")
# MFC DLLs leak into the output via WindowsAppSDKSelfContained but no PowerToys binary imports them.
# Verified with dumpbin /dependents across all 2176 binaries — zero consumers.
$fileExclusionList += @("mfc140.dll", "mfc140u.dll", "mfcm140.dll", "mfcm140u.dll")
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
if ($fileDepsJson -eq [string]::Empty) {
@@ -89,16 +85,11 @@ Function Generate-FileComponents() {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList',
Justification = 'variable is used in another scope')]
$fileList = $matches[2] -split ';' | Where-Object { $_ -ne '' }
$fileList = $matches[2] -split ';'
return
}
}
if ($null -eq $fileList -or $fileList.Count -eq 0) {
# No files to generate components for — leave placeholder intact
return
}
$componentId = "$($fileListName)_Component"
$componentDefs = "`r`n"
@@ -163,67 +154,6 @@ Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSSc
#WinUI3Applications
Generate-FileList -fileDepsJson "" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
# Deduplicate: Remove files from WinUI3Apps that are identical to root (same name + same hash).
# These will be re-created as plain file copies at install time by CreateWinAppSDKHardlinksCA.
# (The CA's name is historical: it now uses fs::copy_file rather than CreateHardLinkW to avoid
# DACL contamination across the shared inode -- see CustomAction.cpp for details.)
$rootPath = "$PSScriptRoot..\..\..\$platform\Release"
$winui3Path = "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
$winui3WxsPath = "$PSScriptRoot\WinUI3Applications.wxs"
$winui3Wxs = Get-Content $winui3WxsPath -Raw
$manifestPath = Join-Path $winui3Path "hardlinks.txt"
if ($winui3Wxs -match "\<\?define WinUI3ApplicationsFiles=([^?]*)\?\>") {
$winui3FileList = $matches[1] -split ';' | Where-Object { $_ -ne '' }
$hardlinkFiles = @()
# Read the BaseApplications WXS file list so we only deduplicate files that the MSI
# is actually deploying to the install root. If a file was stripped from BaseApplications
# by an earlier step (e.g., the ImageResizer leaked-apphost workaround above), the
# install-time CA's source would be missing and both copies would disappear.
$baseAppsWxs = Get-Content $baseAppWxsPath -Raw
$baseAppsFileList = @()
if ($baseAppsWxs -match "\<\?define BaseApplicationsFiles=([^?]*)\?\>") {
$baseAppsFileList = $matches[1] -split ';' | Where-Object { $_ -ne '' }
}
foreach ($file in $winui3FileList) {
# Skip files that were intentionally not deployed to root by the build
if ($baseAppsFileList -notcontains $file) { continue }
$rootFile = Join-Path $rootPath $file
$winui3File = Join-Path $winui3Path $file
if ((Test-Path $rootFile) -and (Test-Path $winui3File)) {
$rootHash = (Get-FileHash $rootFile -Algorithm SHA256).Hash
$winui3Hash = (Get-FileHash $winui3File -Algorithm SHA256).Hash
if ($rootHash -eq $winui3Hash) {
$hardlinkFiles += $file
}
}
}
if ($hardlinkFiles.Count -gt 0) {
# Remove deduplicated files from WinUI3Apps file list
$remainingFiles = $winui3FileList | Where-Object { $_ -notin $hardlinkFiles }
if ($remainingFiles.Count -eq 0) {
# All files are duplicates — keep at least a dummy entry won't be emitted
# Generate-FileComponents handles empty defines by producing no <File> entries
$winui3Wxs = $winui3Wxs -replace "\<\?define WinUI3ApplicationsFiles=[^?]*\?\>", "<?define WinUI3ApplicationsFiles=?>"
} else {
$winui3Wxs = $winui3Wxs -replace "\<\?define WinUI3ApplicationsFiles=[^?]*\?\>", "<?define WinUI3ApplicationsFiles=$($remainingFiles -join ';')?>"
}
Set-Content -Path $winui3WxsPath -Value $winui3Wxs
Write-Host "Deduplicated $($hardlinkFiles.Count) files from WinUI3Apps (will be copied at install time)"
}
# Always write hardlinks.txt (may be empty — CA handles that gracefully)
# Write as UTF-8 without BOM so the install-time CA can read it via std::ifstream
# + MultiByteToWideChar(CP_UTF8) without dealing with PS-version-dependent default
# encodings or a leading BOM.
[System.IO.File]::WriteAllLines($manifestPath, [string[]]$hardlinkFiles, (New-Object System.Text.UTF8Encoding($false)))
}
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs
#AdvancedPaste
@@ -261,7 +191,7 @@ Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFil
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs
#KeyboardManager
Generate-FileList -fileDepsJson "" -fileListName KeyboardManagerAssetsFiles -wxsFilePath $PSScriptRoot\KeyboardManager.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\KeyboardManager"
Generate-FileList -fileDepsJson "" -fileListName KeyboardManagerAssetsFiles -wxsFilePath $PSScriptRoot\KeyboardManager.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\KeyboardManager"
Generate-FileList -fileDepsJson "" -fileListName KeyboardManagerAssetsWinUI3Files -wxsFilePath $PSScriptRoot\KeyboardManager.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\KeyboardManagerEditor"
Generate-FileComponents -fileListName "KeyboardManagerAssetsFiles" -wxsFilePath $PSScriptRoot\KeyboardManager.wxs
Generate-FileComponents -fileListName "KeyboardManagerAssetsWinUI3Files" -wxsFilePath $PSScriptRoot\KeyboardManager.wxs
@@ -406,13 +336,11 @@ Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsModulesFiles -w
Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\OOBE\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsFluentIconsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Icons\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2IconsModelsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Icons\Models\"
Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsCmdPalFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\CmdPal\"
Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2IconsModelsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
Generate-FileComponents -fileListName "SettingsV2AssetsCmdPalFiles" -wxsFilePath $PSScriptRoot\Settings.wxs
#Workspaces
Generate-FileList -fileDepsJson "" -fileListName WorkspacesImagesComponentFiles -wxsFilePath $PSScriptRoot\Workspaces.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Workspaces\"

View File

@@ -8,10 +8,10 @@ SET VCToolsVersion=!VCToolsVersion!
SET ClearDevCommandPromptEnvVars=false
rem In case of Release we should not use Debug CRT in VCRT forwarders
msbuild !PTRoot!\src\modules\previewpane\MonacoPreviewHandler\MonacoPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net10.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\MonacoPreviewHandler\MonacoPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\MarkdownPreviewHandler\MarkdownPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net10.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\MarkdownPreviewHandler\MarkdownPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net10.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net10.0-windows10.0.26100.0
msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0

View File

@@ -62,7 +62,7 @@
<Import Project="$(RepoRoot)deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -70,6 +70,6 @@
</PropertyGroup>
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.250303.1" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.260126.7" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -4,7 +4,7 @@
<Import Project=".\Common.Dotnet.PrepareGeneratedFolder.targets" />
<PropertyGroup>
<CoreTargetFramework>net10.0</CoreTargetFramework>
<CoreTargetFramework>net9.0</CoreTargetFramework>
<WindowsSdkPackageVersion>10.0.26100.68-preview</WindowsSdkPackageVersion>
<TargetFramework>$(CoreTargetFramework)-windows10.0.26100.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>

View File

@@ -38,7 +38,17 @@
</Capabilities>
<Applications>
<Application Id="PowerToys.SettingsUI" Executable="PowerToys.Settings.exe" EntryPoint="Windows.FullTrustApplication">
<Application Id="PowerToys.OCR" Executable="PowerToys.PowerOCR.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.OCR"
Description="PowerToys OCR Module"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png"
AppListEntry="none">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.SettingsUI" Executable="WinUI3Apps\PowerToys.Settings.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.SettingsUI"
Description="PowerToys Settings UI"
@@ -48,7 +58,7 @@
AppListEntry="none">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.ImageResizerUI" Executable="PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.ImageResizer"
Description="PowerToys Image Resizer UI"

View File

@@ -417,7 +417,6 @@ if ($NoSign) {
Write-BuildLog "Identity Name: $($script:Config.IdentityName)" -Level Info
}
$winUI3AppsDir = Join-Path $outDir "WinUI3Apps"
Write-BuildLog "Register sparse package:" -Level Info
Write-BuildLog " Add-AppxPackage -Path `"$msixPath`" -ExternalLocation `"$winUI3AppsDir`"" -Level Warning
Write-BuildLog "(If already installed and you changed manifest only): Add-AppxPackage -Register `"$manifestPath`" -ExternalLocation `"$winUI3AppsDir`" -ForceApplicationShutdown" -Level Warning
Write-BuildLog " Add-AppxPackage -Path `"$msixPath`" -ExternalLocation `"$outDir`"" -Level Warning
Write-BuildLog "(If already installed and you changed manifest only): Add-AppxPackage -Register `"$manifestPath`" -ExternalLocation `"$outDir`" -ForceApplicationShutdown" -Level Warning

View File

@@ -4,9 +4,9 @@ This document describes how to build, sign, register, and consume the shared spa
## Package overview
The sparse package lives under `src/PackageIdentity`. It produces a payload-free MSIX whose `Identity` matches `Microsoft.PowerToys.SparseApp`. The manifest contains one entry per Win32 surface that should run with identity (for example Settings, Image Resizer, CmdPal Extension).
The sparse package lives under `src/PackageIdentity`. It produces a payload-free MSIX whose `Identity` matches `Microsoft.PowerToys.SparseApp`. The manifest contains one entry per Win32 surface that should run with identity (for example Settings, PowerOCR, Image Resizer).
> The MSIX contains only metadata. When the package is registered you must point `-ExternalLocation` to the `WinUI3Apps` subfolder of the output folder that hosts the Win32 binaries (for example `x64\Release\WinUI3Apps`). This isolates the DACL changes that MSIX registration applies on Windows 23H2/24H2 to the `WinUI3Apps` folder, keeping the root install folder clean for preview handler DLLs.
> The MSIX contains only metadata. When the package is registered you must point `-ExternalLocation` to the output folder that hosts the Win32 binaries (for example `x64\Release`).
## Building the sparse package locally
@@ -53,17 +53,16 @@ After `PowerToysSparse.msix` is generated:
# First time registration
$repoRoot = "C:/git/PowerToys"
$outputRoot = Join-Path $repoRoot "x64/Release"
$externalLocation = Join-Path $outputRoot "WinUI3Apps"
Add-AppxPackage -Path (Join-Path $outputRoot "PowerToysSparse.msix") -ExternalLocation $externalLocation
Add-AppxPackage -Path (Join-Path $outputRoot "PowerToysSparse.msix") -ExternalLocation $outputRoot
# Re-register after manifest tweaks only
Add-AppxPackage -Register (Join-Path $repoRoot "src/PackageIdentity/AppxManifest.xml") -ExternalLocation $externalLocation -ForceApplicationShutdown
Add-AppxPackage -Register (Join-Path $repoRoot "src/PackageIdentity/AppxManifest.xml") -ExternalLocation $outputRoot -ForceApplicationShutdown
# Remove the sparse identity
Get-AppxPackage -Name Microsoft.PowerToys.SparseApp | Remove-AppxPackage
```
`-ExternalLocation` should match the `WinUI3Apps` subfolder that contains the Win32 executables declared in the manifest. Re-run registration whenever the manifest or executable layout changes.
`-ExternalLocation` should match the output folder that contains the Win32 executables declared in the manifest. Re-run registration whenever the manifest or executable layout changes.
## CI-specific guidance
@@ -73,7 +72,7 @@ Get-AppxPackage -Name Microsoft.PowerToys.SparseApp | Remove-AppxPackage
## Consuming the identity from other components
1. Add a new `<Application>` entry inside `src/PackageIdentity/AppxManifest.xml`. Use a unique `Id` (for example `PowerToys.MyModuleUI`) and set `Executable` to the Win32 binary relative to the `-ExternalLocation` (`WinUI3Apps` subfolder).
1. Add a new `<Application>` entry inside `src/PackageIdentity/AppxManifest.xml`. Use a unique `Id` (for example `PowerToys.MyModuleUI`) and set `Executable` to the Win32 binary relative to the `-ExternalLocation` root.
2. Ensure the binary is copied into the platform/configuration output folder (`x64\Release`, `ARM64\Debug`, etc.) so the sparse package can locate it.
3. Embed a sparse identity manifest in the Win32 binary so it binds to the MSIX identity at runtime. The manifest must declare an `<msix>` element with `packageName="Microsoft.PowerToys.SparseApp"`, `applicationId` matching the `<Application Id>`, and a `publisher` that matches the sparse package. Keep the manifests publisher in sync with `src/PackageIdentity/.user/PowerToysSparse.publisher.txt` (emitted by `BuildSparsePackage.ps1`). See `src/modules/imageresizer/ui/ImageResizerUI.csproj` for an example that points `ApplicationManifest` to `ImageResizerUI.dev.manifest` for local builds and switches to `ImageResizerUI.prod.manifest` when `$(CIBuild)` is `true`.
4. Register or re-register the sparse package so Windows learns about the new application Id.

View File

@@ -68,7 +68,7 @@
<Import Project="$(RepoRoot)deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -76,6 +76,6 @@
</PropertyGroup>
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.250303.1" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.260126.7" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -36,12 +36,12 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -5,17 +5,11 @@
#include <sstream>
#include <cmath>
#include <limits>
#include <random>
namespace ExprtkCalculator::internal
{
static double factorial(const double n)
{
if (std::isnan(n) || std::isinf(n))
{
return std::numeric_limits<double>::quiet_NaN();
}
// Only allow non-negative integers
if (n < 0.0 || std::floor(n) != n)
{
@@ -26,82 +20,14 @@ namespace ExprtkCalculator::internal
static double sign(const double n)
{
// The sign of NaN is undefined.
if (std::isnan(n))
{
return std::numeric_limits<double>::quiet_NaN();
}
if (n > 0.0) return 1.0;
if (n < 0.0) return -1.0;
return 0.0;
}
// rand(): returns a uniformly distributed random double in [0, 1)
struct rand_func : public exprtk::ifunction<double>
{
std::mt19937_64 rng;
std::uniform_real_distribution<double> dist;
rand_func() :
exprtk::ifunction<double>(0),
rng(std::random_device{}()),
dist(0.0, 1.0)
{}
inline double operator()() override
{
return dist(rng);
}
};
// randi(n): returns a uniformly distributed random integer in [0, n-1]
struct randi_func : public exprtk::ifunction<double>
{
std::mt19937_64 rng;
randi_func() :
exprtk::ifunction<double>(1),
rng(std::random_device{}())
{}
inline double operator()(const double& n) override
{
if (std::isnan(n) || std::isinf(n))
{
return std::numeric_limits<double>::quiet_NaN();
}
constexpr double maxLongLongAsDouble = static_cast<double>(std::numeric_limits<long long>::max());
if (n < 1.0 || n >= maxLongLongAsDouble)
{
return std::numeric_limits<double>::quiet_NaN();
}
if (std::floor(n) != n)
{
return std::numeric_limits<double>::quiet_NaN();
}
std::uniform_int_distribution<long long> dist(0, static_cast<long long>(n) - 1);
return static_cast<double>(dist(rng));
}
};
std::wstring ToWStringFullPrecision(double value)
{
if (std::isnan(value))
{
return L"NaN";
}
if (std::isinf(value))
{
return value > 0 ? L"inf" : L"-inf";
}
std::wostringstream oss;
oss.imbue(std::locale::classic());
oss << std::fixed << std::setprecision(15) << value;
return oss.str();
}
@@ -120,13 +46,6 @@ namespace ExprtkCalculator::internal
symbol_table.add_function("factorial", factorial);
symbol_table.add_function("sign", sign);
// thread_local ensures each thread has its own RNG instance (seeded once,
// state preserved across calls) without requiring locks.
static thread_local rand_func rand_fn;
static thread_local randi_func randi_fn;
symbol_table.add_function("rand", rand_fn);
symbol_table.add_function("randi", randi_fn);
exprtk::expression<double> expression;
expression.register_symbol_table(symbol_table);
@@ -145,7 +64,7 @@ namespace ExprtkCalculator::internal
parser.settings().disable_all_inequality_ops(); // Disable inequality operators like <, >, <=, >=, !=, etc.
if (!parser.compile(expressionText, expression))
return L"ParseError";
return L"NaN";
return ToWStringFullPrecision(expression.value());
}

View File

@@ -4,7 +4,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
<RootNamespace>Microsoft.PowerToys.Common.UI.Controls</RootNamespace>
<AssemblyName>PowerToys.Common.UI.Controls</AssemblyName>
<UseWinUI>true</UseWinUI>
@@ -17,7 +17,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="WinUIEx" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" />

View File

@@ -1,223 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using ManagedCommon;
using Microsoft.UI.Windowing;
using Windows.Graphics;
using WinUIEx;
namespace Microsoft.PowerToys.Common.UI.Controls.Flyout;
/// <summary>
/// Shared helper for positioning and sizing flyout-style WinUI 3 windows
/// (e.g. Quick Access, PowerDisplay) that are pinned to a corner of the work area.
///
/// The public API takes sizes in device-independent pixels (DIP). The helper resolves the
/// target monitor's effective DPI and converts to physical pixels. All window positioning
/// uses absolute screen physical-pixel coordinates via
/// <see cref="AppWindow.MoveAndResize(RectInt32)"/> — the same pattern used by the original
/// Settings.UI flyout, which proved reliable across multi-monitor and mixed-DPI setups.
/// </summary>
public static partial class FlyoutWindowHelper
{
private const uint MdtEffectiveDpi = 0;
private const int DefaultDpi = 96;
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
}
[LibraryImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool GetCursorPos(out POINT lpPoint);
[LibraryImport("shcore.dll")]
private static partial int GetDpiForMonitor(nint hMonitor, uint dpiType, out uint dpiX, out uint dpiY);
/// <summary>
/// Get the DPI scale factor (1.0 = 100%, 1.25 = 125%, 1.5 = 150%, 2.0 = 200%) for a window.
/// </summary>
public static double GetDpiScale(WindowEx window)
{
ArgumentNullException.ThrowIfNull(window);
return (double)window.GetDpiForWindow() / DefaultDpi;
}
/// <summary>
/// Get the DPI scale factor for a given <see cref="DisplayArea"/>.
/// Resolves DPI from the underlying monitor handle so the value reflects the
/// target display, regardless of which monitor the window is currently on.
/// </summary>
public static double GetDpiScale(DisplayArea displayArea)
{
ArgumentNullException.ThrowIfNull(displayArea);
return (double)GetEffectiveDpi(global::Microsoft.UI.Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId)) / DefaultDpi;
}
/// <summary>
/// Convert device-independent pixels (DIP) to physical pixels (rounding up).
/// </summary>
public static int ScaleToPhysicalPixels(int dip, double dpiScale)
{
return (int)Math.Ceiling(dip * dpiScale);
}
/// <summary>
/// Convert physical pixels to device-independent pixels (DIP) (rounding down).
/// </summary>
public static int ScaleToDip(int physicalPixels, double dpiScale)
{
return (int)Math.Floor(physicalPixels / dpiScale);
}
/// <summary>
/// Look up the <see cref="DisplayArea"/> currently containing the mouse cursor.
/// </summary>
public static bool TryGetDisplayAreaAtCursor(out DisplayArea? displayArea)
{
displayArea = null;
if (!GetCursorPos(out var cursorPos))
{
return false;
}
displayArea = DisplayArea.GetFromPoint(new PointInt32(cursorPos.X, cursorPos.Y), DisplayAreaFallback.Nearest);
return displayArea is not null;
}
/// <summary>
/// Position a flyout-style window at the bottom-right corner of the work area on the
/// monitor under the mouse cursor.
/// </summary>
public static void PositionWindowBottomRight(
WindowEx window,
int widthDip,
int heightDip,
int rightMarginDip = 0,
int bottomMarginDip = 0)
{
ArgumentNullException.ThrowIfNull(window);
if (!TryGetDisplayAreaAtCursor(out var displayArea) || displayArea is null)
{
Logger.LogWarning("FlyoutWindowHelper.PositionWindowBottomRight: unable to determine display from cursor; skipping positioning");
return;
}
PositionWindowBottomRight(window, displayArea, widthDip, heightDip, rightMarginDip, bottomMarginDip);
}
/// <summary>
/// Position a flyout-style window at the bottom-right corner of the specified display
/// area's work area. Use this overload when the caller has already resolved the target
/// <see cref="DisplayArea"/> (e.g. the cursor monitor) so size and placement are computed
/// from the same source.
///
/// Internally moves the window in two steps to avoid <c>WM_DPICHANGED</c> double-scaling
/// when the target monitor has a different DPI than the one the window was previously on:
/// first a 1×1 teleport into the target display, then the real position+size while the
/// window is already on that monitor (no DPI boundary crossing).
/// </summary>
public static void PositionWindowBottomRight(
WindowEx window,
DisplayArea displayArea,
int widthDip,
int heightDip,
int rightMarginDip = 0,
int bottomMarginDip = 0)
{
ArgumentNullException.ThrowIfNull(window);
ArgumentNullException.ThrowIfNull(displayArea);
double dpiScale = GetDpiScale(displayArea);
var work = displayArea.WorkArea;
int w = ScaleToPhysicalPixels(widthDip, dpiScale);
int h = ScaleToPhysicalPixels(heightDip, dpiScale);
int marginRight = ScaleToPhysicalPixels(rightMarginDip, dpiScale);
int marginBottom = ScaleToPhysicalPixels(bottomMarginDip, dpiScale);
// Clamp size so the window never extends past the work area minus margins.
// Guards against the bottom/right edge spilling into the taskbar when rounding
// (Math.Ceiling above) would push it just past the boundary.
int maxW = Math.Max(0, work.Width - marginRight);
int maxH = Math.Max(0, work.Height - marginBottom);
w = Math.Min(w, maxW);
h = Math.Min(h, maxH);
// Absolute screen physical-pixel coordinates. WorkArea is in screen coordinates,
// so for non-primary monitors WorkArea.X/Y will be non-zero (and may be negative).
int x = work.X + work.Width - w - marginRight;
int y = work.Y + work.Height - h - marginBottom;
MoveAndResizeOnDisplay(window, displayArea, new RectInt32(x, y, w, h));
}
/// <summary>
/// Center a window within the specified display area's work area.
/// Uses a 1×1 teleport into the target display first to avoid WM_DPICHANGED
/// double-scaling when crossing monitors with different DPI.
/// </summary>
public static void CenterWindowOnDisplay(
WindowEx window,
DisplayArea displayArea,
int widthDip,
int heightDip)
{
ArgumentNullException.ThrowIfNull(window);
ArgumentNullException.ThrowIfNull(displayArea);
double dpiScale = GetDpiScale(displayArea);
var work = displayArea.WorkArea;
int w = Math.Min(ScaleToPhysicalPixels(widthDip, dpiScale), work.Width);
int h = Math.Min(ScaleToPhysicalPixels(heightDip, dpiScale), work.Height);
int x = work.X + ((work.Width - w) / 2);
int y = work.Y + ((work.Height - h) / 2);
MoveAndResizeOnDisplay(window, displayArea, new RectInt32(x, y, w, h));
}
/// <summary>
/// Two-step move that avoids WM_DPICHANGED double-scaling. First teleports a 1×1
/// window into the target display (which may trigger an auto-rescale, but on a 1×1
/// rect the effect is invisible). Then sets the real position+size while the window
/// is already on the target monitor — no DPI boundary crossing, so WinUI's auto
/// handler doesn't fire and overwrite our computed rect.
///
/// Skips the teleport when the window is already on the target display, since there
/// is no boundary to cross.
/// </summary>
private static void MoveAndResizeOnDisplay(WindowEx window, DisplayArea targetDisplay, RectInt32 finalRect)
{
var currentDisplay = DisplayArea.GetFromWindowId(window.AppWindow.Id, DisplayAreaFallback.Nearest);
bool needsTeleport = currentDisplay is null || currentDisplay.DisplayId.Value != targetDisplay.DisplayId.Value;
if (needsTeleport)
{
var work = targetDisplay.WorkArea;
window.AppWindow.MoveAndResize(new RectInt32(work.X, work.Y, 1, 1));
}
window.AppWindow.MoveAndResize(finalRect);
}
private static int GetEffectiveDpi(nint hMonitor)
{
if (hMonitor == 0)
{
return DefaultDpi;
}
var hr = GetDpiForMonitor(hMonitor, MdtEffectiveDpi, out var dpiX, out _);
return hr >= 0 && dpiX > 0 ? (int)dpiX : DefaultDpi;
}
}

View File

@@ -1,92 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using WinUIEx;
namespace Microsoft.PowerToys.Common.UI.Controls.Window;
/// <summary>
/// Subclasses a window's WndProc and invokes a preprocessor callback for every
/// message before the default window procedure runs. Useful for routing low-level
/// Win32 messages (e.g. <c>WM_HOTKEY</c>) into managed handlers without depending
/// on the WinUI XAML message loop.
/// </summary>
/// <remarks>
/// Usage:
/// <code>
/// _hook = new WindowMessageHook(window, (uMsg, wParam, lParam) =>
/// _hotkeyService.HandleMessage(uMsg, wParam));
/// </code>
/// Dispose to restore the original WndProc.
/// </remarks>
public sealed partial class WindowMessageHook : IDisposable
{
// Called for every message before default processing. Return true to swallow.
private readonly Func<uint, nuint, nint, bool> _preProcessor;
private const int GwlWndProc = -4;
private readonly nint _hwnd;
private nint _originalWndProc;
private WndProcDelegate? _wndProcDelegate;
private bool _disposed;
private delegate nint WndProcDelegate(nint hwnd, uint uMsg, nuint wParam, nint lParam);
[LibraryImport("user32.dll", EntryPoint = "SetWindowLongPtrW")]
private static partial nint SetWindowLongPtr(nint hWnd, int nIndex, nint dwNewLong);
[LibraryImport("user32.dll", EntryPoint = "CallWindowProcW")]
private static partial nint CallWindowProc(nint lpPrevWndFunc, nint hWnd, uint msg, nuint wParam, nint lParam);
/// <summary>
/// Initializes a new instance of the <see cref="WindowMessageHook"/> class
/// and subclasses the supplied window's WndProc.
/// </summary>
/// <param name="window">Window to subclass.</param>
/// <param name="preProcessor">Callback invoked for every message before the
/// default WndProc. Receives <c>(uMsg, wParam, lParam)</c>. Return
/// <see langword="true"/> to swallow the message.</param>
public WindowMessageHook(WindowEx window, Func<uint, nuint, nint, bool> preProcessor)
{
ArgumentNullException.ThrowIfNull(window);
ArgumentNullException.ThrowIfNull(preProcessor);
_hwnd = window.GetWindowHandle();
_preProcessor = preProcessor;
_wndProcDelegate = WndProc;
var ptr = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate);
_originalWndProc = SetWindowLongPtr(_hwnd, GwlWndProc, ptr);
}
private nint WndProc(nint hwnd, uint uMsg, nuint wParam, nint lParam)
{
if (_preProcessor(uMsg, wParam, lParam))
{
return 0;
}
return CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam);
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
if (_originalWndProc != 0)
{
SetWindowLongPtr(_hwnd, GwlWndProc, _originalWndProc);
_originalWndProc = 0;
}
_wndProcDelegate = null;
}
}

View File

@@ -14,6 +14,7 @@
<ItemGroup>
<PackageReference Include="Markdig.Signed" />
<PackageReference Include="System.Text.Encoding.CodePages" />
<PackageReference Include="UTF.Unknown" />
</ItemGroup>
</Project>

View File

@@ -36,10 +36,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredPowerDisplayEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredGrabAndMoveEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGrabAndMoveEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredFancyZonesEnabledValue());

View File

@@ -15,7 +15,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue();
static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();

View File

@@ -19,7 +19,6 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue();
static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();

View File

@@ -71,10 +71,5 @@ namespace PowerToys.GPOWrapperProjection
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
}
public static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue()
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGrabAndMoveEnabledValue();
}
}
}

View File

@@ -36,7 +36,6 @@ namespace ManagedCommon
ShortcutGuide,
PowerOCR,
Workspaces,
GrabAndMove,
ZoomIt,
GeneralSettings,
}

View File

@@ -55,7 +55,7 @@
<Import Project="$(RepoRoot)deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -63,6 +63,6 @@
</PropertyGroup>
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -38,7 +38,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -46,6 +46,6 @@
</PropertyGroup>
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.250303.1" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.260126.7" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -3,7 +3,7 @@
#define HKEY_WINDOWS_THEME L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"
// disabling warning 4702 - unreachable code
// prevent the warning after the call off an infinite loop function
// prevent the warning after the call off a infinite loop function
#pragma warning(push)
#pragma warning(disable : 4702)
DWORD WINAPI _checkTheme(LPVOID lpParam)

View File

@@ -68,7 +68,7 @@ namespace Microsoft.PowerToys.UITest
}
/// <summary>
/// Gets a value indicating whether or not the UI element is Enabled.
/// Gets a value indicating whether the UI element is Enabled or not.
/// </summary>
public bool Enabled
{

View File

@@ -93,7 +93,7 @@ namespace Microsoft.PowerToys.UITest
}
/// <summary>
/// Exit an exe by Name.
/// Exit a exe by Name.
/// </summary>
/// <param name="processName">The path to the application executable.</param>
public void ExitExeByName(string processName)
@@ -114,7 +114,7 @@ namespace Microsoft.PowerToys.UITest
}
/// <summary>
/// Exit an exe.
/// Exit a exe.
/// </summary>
/// <param name="appPath">The path to the application executable.</param>
public void ExitExe(string appPath)

View File

@@ -8,7 +8,7 @@
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
@@ -18,6 +18,7 @@
<!-- Test libraries/utilities should not use the metapackage. -->
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="System.Text.RegularExpressions" />
<PackageReference Include="CoenM.ImageSharp.ImageHash" />
</ItemGroup>

View File

@@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.UITest
public static class VisualAssert
{
/// <summary>
/// Asserts current visual state of the element is equal to base line image.
/// Asserts current visual state of the element is equal with base line image.
/// To use this VisualAssert, you need to set Window Theme to Light-Mode to avoid Theme color difference in baseline image.
/// Such limitation could be removed either Auto-generate baseline image for both Light & Dark mode
/// </summary>

View File

@@ -27,7 +27,7 @@
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\;..\utils;..\Telemetry;..\..\;..\..\..\deps\;..\..\..\deps\spdlog\include;..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.260126.7\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\;..\utils;..\Telemetry;..\..\;..\..\..\deps\;..\..\..\deps\spdlog\include;..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp23</LanguageStandard>
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_HEADER_ONLY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>

View File

@@ -275,10 +275,6 @@ namespace winrt::PowerToys::Interop::implementation
{
return CommonSharedConstants::HOTKEY_UPDATED_POWER_DISPLAY_EVENT;
}
hstring Constants::RescanPowerDisplayMonitorsEvent()
{
return CommonSharedConstants::RESCAN_POWER_DISPLAY_MONITORS_EVENT;
}
hstring Constants::PowerDisplayToggleMessage()
{
return CommonSharedConstants::POWER_DISPLAY_TOGGLE_MESSAGE;

View File

@@ -72,7 +72,6 @@ namespace winrt::PowerToys::Interop::implementation
static hstring SettingsUpdatedPowerDisplayEvent();
static hstring PowerDisplaySendSettingsTelemetryEvent();
static hstring HotkeyUpdatedPowerDisplayEvent();
static hstring RescanPowerDisplayMonitorsEvent();
static hstring PowerDisplayToggleMessage();
static hstring PowerDisplayApplyProfileMessage();
static hstring PowerDisplayTerminateAppMessage();

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