mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-03 09:00:04 +02:00
Compare commits
42 Commits
dev/muyuan
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9679b937d | ||
|
|
36300d3c75 | ||
|
|
1cde68ae04 | ||
|
|
e6d346a59b | ||
|
|
2aece74831 | ||
|
|
7861bc408c | ||
|
|
b835cde4d2 | ||
|
|
f79df0663c | ||
|
|
7211f7ed67 | ||
|
|
215dfaf236 | ||
|
|
f5a294bb66 | ||
|
|
d10203b8ac | ||
|
|
fecd2e72a7 | ||
|
|
fde1599f7d | ||
|
|
c68003c678 | ||
|
|
e5b0667397 | ||
|
|
8536d7b1cd | ||
|
|
7ac16118c8 | ||
|
|
7508e6e794 | ||
|
|
2d9dfff444 | ||
|
|
9de2e5298a | ||
|
|
07beeca9b9 | ||
|
|
234933f6fa | ||
|
|
f8a10550f3 | ||
|
|
ca1ffebc26 | ||
|
|
2e5c7d2ee6 | ||
|
|
949e42c5c7 | ||
|
|
a65266fcad | ||
|
|
656ea91580 | ||
|
|
d9bfc42229 | ||
|
|
11bfb40ec6 | ||
|
|
1eecf46c51 | ||
|
|
eeaf89e481 | ||
|
|
bfc5fea11e | ||
|
|
98c5b45a30 | ||
|
|
5509628f51 | ||
|
|
a255493c68 | ||
|
|
5c15a63846 | ||
|
|
2c95a61bb3 | ||
|
|
dbc390ab0d | ||
|
|
c6a79360f3 | ||
|
|
b0ccc2394a |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -57,6 +57,7 @@ body:
|
||||
- Environment Variables
|
||||
- FancyZones
|
||||
- FancyZones Editor
|
||||
- Grab And Move
|
||||
- File Locksmith
|
||||
- "File Explorer: Preview Pane"
|
||||
- "File Explorer: Thumbnail preview"
|
||||
@@ -69,6 +70,7 @@ body:
|
||||
- Mouse Without Borders
|
||||
- New+
|
||||
- Peek
|
||||
- Power Display
|
||||
- PowerRename
|
||||
- PowerToys Run
|
||||
- Quick Accent
|
||||
|
||||
12
.github/actions/spell-check/allow/code.txt
vendored
12
.github/actions/spell-check/allow/code.txt
vendored
@@ -127,6 +127,7 @@ HOLDSPACE
|
||||
HOLDBACKSPACE
|
||||
IDIGNORE
|
||||
KBDLLHOOKSTRUCT
|
||||
keydowns
|
||||
keyevent
|
||||
LAlt
|
||||
LBUTTON
|
||||
@@ -329,14 +330,18 @@ MRUINFO
|
||||
REGSTR
|
||||
|
||||
# Misc Win32 APIs and PInvokes
|
||||
DEFAULTTONEAREST
|
||||
INVOKEIDLIST
|
||||
LCMAP
|
||||
MEMORYSTATUSEX
|
||||
ABE
|
||||
Mdt
|
||||
HTCAPTION
|
||||
POSCHANGED
|
||||
QPC
|
||||
QUERYPOS
|
||||
SETAUTOHIDEBAR
|
||||
ULW
|
||||
WINDOWPOS
|
||||
WINEVENTPROC
|
||||
WORKERW
|
||||
@@ -391,3 +396,10 @@ Nonpaged
|
||||
|
||||
# XAML
|
||||
Untargeted
|
||||
|
||||
# Program names
|
||||
SEARCHHOST
|
||||
SHELLEXPERIENCEHOST
|
||||
SHELLHOST
|
||||
STARTMENUEXPERIENCEHOST
|
||||
WIDGETBOARD
|
||||
|
||||
73
.github/actions/spell-check/candidate.patterns
vendored
73
.github/actions/spell-check/candidate.patterns
vendored
@@ -1,6 +1,3 @@
|
||||
# D2D
|
||||
#D?2D
|
||||
|
||||
# Repeated letters
|
||||
\b([a-z])\g{-1}{2,}\b
|
||||
|
||||
@@ -14,7 +11,7 @@
|
||||
^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b
|
||||
|
||||
# copyright
|
||||
Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
|
||||
Copyright (?:\([Cc]\)|©|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
|
||||
|
||||
# patch hunk comments
|
||||
^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .*
|
||||
@@ -22,10 +19,10 @@ Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+
|
||||
index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# file permissions
|
||||
['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
|
||||
(?:^|['"`\s])(?!-+\s)[-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s]
|
||||
|
||||
# css fonts
|
||||
\bfont(?:-family|):[^;}]+
|
||||
\bfont(?:-family(?:[-\w+]*)|):[^;}]+
|
||||
|
||||
# css url wrappings
|
||||
\burl\([^)]+\)
|
||||
@@ -90,6 +87,9 @@ arn:aws:[-/:\w]+
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# Azure AD
|
||||
\baad\.\w{48}\b
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
@@ -171,7 +171,7 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitHub actions
|
||||
\buses:\s+[-\w.]+/[-\w./]+@[-\w.]+
|
||||
\buses:\s+(['"]?)[-\w.]+/[-\w./]+@[-\w.]+\g{-1}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
@@ -240,7 +240,7 @@ accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
\bmedium\.com/@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]*
|
||||
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%?#]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
@@ -414,7 +414,7 @@ ipfs://[0-9a-zA-Z]{3,}
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
|
||||
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[(?:\d+(?:;\d+)*|)m
|
||||
|
||||
# URL escaped characters
|
||||
%[0-9A-F][A-F](?=[A-Za-z])
|
||||
@@ -431,7 +431,7 @@ sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(\\?['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
\b(?=(?:[a-fA-F]{0,2}\d)*[a-fA-F]{3})[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
@@ -455,7 +455,11 @@ LS0tLS1CRUdJT.*
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
|
||||
# unicode escaped characters (4)
|
||||
\\u[0-9a-fA-F]{4}
|
||||
|
||||
# hex digits including css/html color classes
|
||||
(?:[\\0][xX]|\\u\{?|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
|
||||
|
||||
# integrity
|
||||
@@ -478,7 +482,7 @@ Name\[[^\]]+\]=.*
|
||||
(?:(?:\b|_|(?<=[a-z]))I|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
|
||||
|
||||
# python
|
||||
#\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
|
||||
#\b(?i)py(?!gment|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
|
||||
|
||||
# crypt
|
||||
(['"])\$2[ayb]\$.{56}\g{-1}
|
||||
@@ -498,12 +502,21 @@ Name\[[^\]]+\]=.*
|
||||
# go.sum
|
||||
\bh1:\S+
|
||||
|
||||
# golang print-f-style functions
|
||||
#(?i)(?<=append|comma|debug|equal|err|error|exit|fatal|format|info|log|name|panic|print|skip|scan|string|trace|true|warn|warning|wrap|write)(?:f|ln)(?:[ (]|$)
|
||||
|
||||
# golang regular expression
|
||||
(?<!")\br".+?"
|
||||
|
||||
# imports
|
||||
^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+
|
||||
^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+(?:\s+from (['"]).*?\g{-1}|)
|
||||
|
||||
# scala modules
|
||||
#("[^"]+"\s*%%?\s*){2,3}"[^"]+"
|
||||
|
||||
# Dataframes / NumPy
|
||||
#\b(?:df|np)\.\w{3,}
|
||||
|
||||
# container images
|
||||
image: [-\w./:@]+
|
||||
|
||||
@@ -533,12 +546,18 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1}
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!['"])\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)['"](?=[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expression for word breaks
|
||||
#\\b(?=[a-z]{2})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# Java regular expressions
|
||||
Pattern\.(?:compile|matches)\(".*"
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.{3,}/[gim]*\.test\(
|
||||
# javascript exec/test regex
|
||||
/.{3,}?/[gim]*\.(?:exec|test)\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]{3,}/[gim]*\s*
|
||||
# javascript match regex
|
||||
@@ -565,7 +584,7 @@ perl(?:\s+-[a-zA-Z]\w*)+
|
||||
regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\)
|
||||
|
||||
# regex choice
|
||||
# \(\?:[^)]+\|[^)]+\)
|
||||
#\((?:\?:|)[^)|]+(?<! )\|(?!(?:jq|xargs)\b)[^)| ][^)]*\)
|
||||
|
||||
# proto
|
||||
^\s*(\w+)\s\g{-1} =
|
||||
@@ -588,6 +607,9 @@ urn:shemas-jetbrains-com
|
||||
# Debian changelog severity
|
||||
[-\w]+ \(.*\) (?:\w+|baseline|unstable|experimental); urgency=(?:low|medium|high|emergency|critical)\b
|
||||
|
||||
# Red Hat Package management spec file dependencies
|
||||
^(?:Build|)Requires: [-.\w]+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
@@ -642,6 +664,8 @@ PrependWithABINamepsace
|
||||
>[-a-zA-Z=;:/0-9+]{3,}=</
|
||||
# base64 encoded content, possibly wrapped in mime
|
||||
#(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
|
||||
# jwt
|
||||
(?:\be[wy][-a-zA-Z=;:/0-9+]+\.){2}[-_\w]+
|
||||
# base64 encoded json
|
||||
\beyJ[-a-zA-Z=;:/0-9+]+
|
||||
# base64 encoded pkcs
|
||||
@@ -679,9 +703,9 @@ systemd.*?running in system mode \([-+].*\)$
|
||||
|
||||
# Non-English
|
||||
# Even repositories expecting pure English content can unintentionally have Non-English content... People will occasionally mistakenly enter [homoglyphs](https://en.wikipedia.org/wiki/Homoglyph) which are essentially typos, and using this pattern will mean check-spelling will not complain about them.
|
||||
#
|
||||
# .
|
||||
# If the content to be checked should be written in English and the only Non-English items will be people's names, then you can consider adding this.
|
||||
#
|
||||
# .
|
||||
# Alternatively, if you're using check-spelling v0.0.25+, and you would like to _check_ the Non-English content for spelling errors, you can. For information on how to do so, see:
|
||||
# https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#unicode
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,}
|
||||
@@ -693,7 +717,7 @@ systemd.*?running in system mode \([-+].*\)$
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex (check-spelling >= 0.0.22)
|
||||
# LaTeX
|
||||
\\\w{2,}\{
|
||||
|
||||
# American Mathematical Society (AMS) / Doxygen
|
||||
@@ -720,7 +744,6 @@ nolint:\s*[\w,]+
|
||||
# cygwin paths
|
||||
/cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+
|
||||
|
||||
# in check-spelling@v0.0.22+, printf markers aren't automatically consumed
|
||||
# printf markers
|
||||
#(?<!\\)\\[nrt](?=[a-z]{2,})
|
||||
# alternate printf markers if you run into latex and friends
|
||||
@@ -749,12 +772,12 @@ W/"[^"]+"
|
||||
|
||||
# Compiler flags (Unix, Java/Scala)
|
||||
# Use if you have things like `-Pdocker` and want to treat them as `docker`
|
||||
#(?:^|[\t ,>"'`=(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
#(?:^|[\t ,>"'`=\[(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (Windows / PowerShell)
|
||||
# This is a subset of the more general compiler flags pattern.
|
||||
# It avoids matching `-Path` to prevent it from being treated as `ath`
|
||||
#(?:^|[\t ,"'`=(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}))
|
||||
#(?:^|[\t ,"'`=\[(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}))
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
@@ -762,7 +785,7 @@ W/"[^"]+"
|
||||
# Library prefix
|
||||
# e.g., `lib`+`archive`, `lib`+`raw`, `lib`+`unwind`
|
||||
# (ignores some words that happen to start with `lib`)
|
||||
(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
|
||||
(?:\b|_)[Ll]ib(?!era[lt])(?:re(?=office)|era|)(?!ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
|
||||
|
||||
# iSCSI iqn (approximate regex)
|
||||
\biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b
|
||||
@@ -773,9 +796,9 @@ W/"[^"]+"
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\b(?:bash|sh|set)(?:\s+[-+][abefimouxE]{1,2})*\s+[-+][abefimouxE]{3,}(?:\s+[-+][abefimouxE]+)*
|
||||
\b(?:bash|(?<!\.)sh|set)(?:\s+[-+][abefimouxE]{1,2})*\s+[-+][abefimouxE]{3,}(?:\s+[-+][abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:\s-C \S+|(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
|
||||
941
.github/actions/spell-check/expect.txt
vendored
941
.github/actions/spell-check/expect.txt
vendored
File diff suppressed because it is too large
Load Diff
497
.github/actions/spell-check/line_forbidden.patterns
vendored
497
.github/actions/spell-check/line_forbidden.patterns
vendored
File diff suppressed because one or more lines are too long
34
.github/actions/spell-check/patterns.txt
vendored
34
.github/actions/spell-check/patterns.txt
vendored
@@ -1,5 +1,18 @@
|
||||
# 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
|
||||
@@ -71,11 +84,14 @@ StringComparer.OrdinalIgnoreCase\) \{.*\}
|
||||
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
|
||||
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
|
||||
|
||||
# DateTime Formats
|
||||
Get-Date -Format \w+|DateTime\.Now(?::|\.ToString\(")\w+
|
||||
|
||||
# Automatically suggested patterns
|
||||
|
||||
# hit-count: 5402 file-count: 1339
|
||||
# IServiceProvider / isAThing
|
||||
(?:(?:\b|_|(?<=[a-z]))[IT]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
|
||||
(?:(?:\b|_|(?<=[a-z]))[A-Z]|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
|
||||
|
||||
# hit-count: 2073 file-count: 842
|
||||
# #includes
|
||||
@@ -159,6 +175,10 @@ aka\.ms/[a-zA-Z0-9]+
|
||||
# kubernetes crd patterns
|
||||
^\s*pattern: .*$
|
||||
|
||||
# hit-count: 7 file-count: 3
|
||||
# unicode escaped characters (4)
|
||||
\\u[0-9a-fA-F]{4}
|
||||
|
||||
# hit-count: 5 file-count: 3
|
||||
# URL escaped characters
|
||||
%[0-9A-F][A-F](?=[A-Za-z])
|
||||
@@ -171,6 +191,10 @@ aka\.ms/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/@?[^/\s"]+/[-\w:/*.]+
|
||||
|
||||
# hit-count: 2 file-count: 2
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:\s-C \S+|(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
@@ -184,10 +208,6 @@ 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, ".*?"\)
|
||||
|
||||
@@ -210,13 +230,15 @@ RegExp\(@?([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|retu
|
||||
# mount
|
||||
\bmount\s+-t\s+(\w+)\s+\g{-1}\b
|
||||
# C types and repeated CSS values
|
||||
\s(auto|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?:\s\g{-1})+\s
|
||||
\s(auto|await|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?:\s\g{-1})+\s
|
||||
# C enum and struct
|
||||
\b(?:enum|struct)\s+(\w+)\s+\g{-1}\b
|
||||
# go templates
|
||||
\s(\w+)\s+\g{-1}\s+\`(?:graphql|inject|json|yaml):
|
||||
# doxygen / javadoc / .net
|
||||
(?:[\\@](?:brief|defgroup|groupname|link|t?param|return|retval)|(?:public|private|\[Parameter(?:\(.+\)|)\])(?:\s+(?:static|override|readonly|required|virtual))*)(?:\s+\{\w+\}|)\s+(\w+)\s+\g{-1}\s
|
||||
# C# getter/setter
|
||||
\s(\w+)\s+\g{-1}\s*\{\s*[gs]et;
|
||||
|
||||
# macOS file path
|
||||
(?:Contents\W+|(?!iOS)/)MacOS\b
|
||||
|
||||
47
.github/actions/spell-check/reject.txt
vendored
47
.github/actions/spell-check/reject.txt
vendored
@@ -1,23 +1,30 @@
|
||||
^attache$
|
||||
^bellows?$
|
||||
attache
|
||||
aroynt.*
|
||||
bellows?
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^develope$
|
||||
^developement$
|
||||
^developpe
|
||||
^Devers?$
|
||||
^devex
|
||||
^devide
|
||||
^Devinn?[ae]
|
||||
^devisal
|
||||
^devisor
|
||||
^diables?$
|
||||
^oer$
|
||||
.*dnt
|
||||
dependan.*
|
||||
developement
|
||||
developp?e
|
||||
Devers?
|
||||
devex.*
|
||||
devide
|
||||
Devinn?[ae]
|
||||
devisals?
|
||||
devisors?
|
||||
diables?
|
||||
hasta?
|
||||
hastat.*
|
||||
immediatly
|
||||
inisle
|
||||
inital
|
||||
linge
|
||||
oer
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^Teh$
|
||||
^untill$
|
||||
^untilling$
|
||||
^venders?$
|
||||
^wether.*
|
||||
[Ss]pae.*
|
||||
Teh
|
||||
untill
|
||||
untilling
|
||||
venders?
|
||||
wether.*
|
||||
|
||||
279
.github/agents/LabelIssues.agent.md
vendored
279
.github/agents/LabelIssues.agent.md
vendored
@@ -1,279 +0,0 @@
|
||||
---
|
||||
name: LabelIssues
|
||||
description: 'Labels GitHub issues and pull requests with Product-* labels based on issue template fields, linked issues, changed files, and content analysis. Accepts natural-language filters like "5 days", "my issues", "Needs-Triage issues", or "unlabeled PRs".'
|
||||
tools: ['execute', 'read', 'github/*']
|
||||
argument-hint: 'Description of issues/PRs to label (e.g., "5 days", "my issues", "unlabeled PRs this month", "#12345")'
|
||||
infer: true
|
||||
---
|
||||
|
||||
# LabelIssues Agent
|
||||
|
||||
You are an **issue and PR triage agent** that applies `Product-*` labels to GitHub issues and pull requests in the PowerToys repository.
|
||||
|
||||
## Goal
|
||||
|
||||
Given a user description of which issues or PRs to process, find matching items that are **missing `Product-*` labels**, determine the correct product label(s), and apply them — with appropriate confidence gating.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Parse the user's request into a search query
|
||||
|
||||
Interpret the user's natural-language input and build a `gh` search query. Determine whether the user wants to process **issues**, **PRs**, or **both**.
|
||||
|
||||
| User says | Interpreted as |
|
||||
|-----------|---------------|
|
||||
| `5 days` | Issues created in the last 5 days |
|
||||
| `my issues` | Issues assigned to the authenticated user |
|
||||
| `Needs-Triage` or `needs triage` | Issues with the `Needs-Triage` label |
|
||||
| `#12345` or `12345` | A single specific issue or PR |
|
||||
| `open issues this week` | Open issues created in the last 7 days |
|
||||
| `closed bugs last month` | Closed issues with `Issue-Bug` label from last month |
|
||||
| `unlabeled PRs` or `PRs this week` | PRs without Product-* labels |
|
||||
| `unlabeled PRs and issues` | Both PRs and issues without Product-* labels |
|
||||
|
||||
**Always add these implicit filters:**
|
||||
- Exclude items that already have any `Product-*` label
|
||||
- For issues: exclude pull requests; for PRs: only pull requests
|
||||
|
||||
**Echo back** the parsed query to the user before executing:
|
||||
```
|
||||
Searching for: [state:open created:>2026-05-06 -label:"Product-*"]
|
||||
```
|
||||
|
||||
### Step 2 — Fetch matching issues and/or PRs
|
||||
|
||||
Use `gh` CLI to fetch items. Example commands:
|
||||
|
||||
```bash
|
||||
# Recent issues (last N days)
|
||||
gh issue list --repo microsoft/PowerToys --state open --json number,title,body,labels --limit 100
|
||||
|
||||
# PRs without product labels
|
||||
gh pr list --repo microsoft/PowerToys --state open --json number,title,body,labels --limit 100
|
||||
|
||||
# Single issue or PR
|
||||
gh issue view 12345 --repo microsoft/PowerToys --json number,title,body,labels
|
||||
gh pr view 12345 --repo microsoft/PowerToys --json number,title,body,labels,closingIssuesReferences,files
|
||||
```
|
||||
|
||||
Filter out items that already have a `Product-*` label in post-processing.
|
||||
|
||||
Report: `Found N issues and M PRs without Product-* labels.`
|
||||
|
||||
If more than 50 items match, warn the user and ask whether to proceed or narrow the scope.
|
||||
|
||||
### Step 2.5 — Dynamically discover labels and template fields
|
||||
|
||||
**Do this once at the start of every run** so the mapping is always current:
|
||||
|
||||
1. **Fetch all `Product-*` labels from the repo:**
|
||||
```bash
|
||||
gh label list --repo microsoft/PowerToys --search "Product-" --json name --limit 200 --jq '.[].name'
|
||||
```
|
||||
Store these as the set of **valid labels**.
|
||||
|
||||
2. **Fetch the current bug report template dropdown values:**
|
||||
```bash
|
||||
gh api repos/microsoft/PowerToys/contents/.github/ISSUE_TEMPLATE/bug_report.yml --jq '.content' | base64 -d
|
||||
```
|
||||
Parse the YAML to extract the `options` list under the "Area(s) with issue?" dropdown field. These are the **template values**.
|
||||
|
||||
3. **Build the live mapping** by matching each template value to a `Product-*` label:
|
||||
- First, check the **override mapping** in `.github/agents/references/product-label-mapping.md` — this file ONLY contains non-obvious name mismatches (e.g., `Keyboard Manager` → `Product-Keyboard Shortcut Manager`)
|
||||
- Then, try direct match: prepend `Product-` to the template value and check if it exists in the valid labels set
|
||||
- If neither matches, the template value has no mapping (treat as needing content analysis)
|
||||
|
||||
This approach ensures new modules and labels are picked up automatically — the only maintenance needed is when a template dropdown value has a **different name** from its `Product-*` label.
|
||||
|
||||
### Step 3 — Determine product labels
|
||||
|
||||
#### For Issues
|
||||
|
||||
Use the following methods in order:
|
||||
|
||||
##### Method A: Deterministic mapping (HIGH confidence)
|
||||
|
||||
Parse the issue body for the structured **"Area(s) with issue?"** field from the bug report template. The field appears in the rendered markdown as:
|
||||
|
||||
```
|
||||
### Area(s) with issue?
|
||||
|
||||
Command Palette, FancyZones
|
||||
```
|
||||
|
||||
Extract the text between `### Area(s) with issue?` and the next `###` heading (or end of body). Split by commas. Map each value using the **live mapping built in Step 2.5**.
|
||||
|
||||
If all selected areas map to known labels → **HIGH confidence**.
|
||||
|
||||
##### Method B: Content analysis (variable confidence)
|
||||
|
||||
When Method A produces no result (e.g., feature requests without the area field, or free-form issues), analyze the issue title and body yourself to infer the product.
|
||||
|
||||
Use the **valid labels list from Step 2.5** as the universe of possible labels — never invent a label that doesn't exist.
|
||||
|
||||
Optionally consult the keyword hints in `.github/agents/references/product-label-mapping.md` for guidance on ambiguous terms.
|
||||
|
||||
#### For Pull Requests
|
||||
|
||||
Use the following methods in priority order. Stop as soon as you get a HIGH confidence result:
|
||||
|
||||
##### Method C: Linked issues (HIGH confidence)
|
||||
|
||||
Fetch linked issues using:
|
||||
```bash
|
||||
gh pr view <number> --repo microsoft/PowerToys --json closingIssuesReferences --jq '.closingIssuesReferences[].number'
|
||||
```
|
||||
|
||||
This returns issues linked via `Fixes #X`, `Closes #X`, or `Resolves #X` keywords in the PR body (including the `- [ ] Closes: #xxx` checklist item from the PR template).
|
||||
|
||||
If linked issues are found:
|
||||
1. Fetch each linked issue's labels
|
||||
2. Copy any `Product-*` labels from the linked issues → **HIGH confidence**
|
||||
|
||||
If linked issues exist but none have `Product-*` labels, apply the issue labeling methods (A/B) to those linked issues first, then copy the result.
|
||||
|
||||
##### Method D: Parse body for issue references (MEDIUM → HIGH confidence)
|
||||
|
||||
If `closingIssuesReferences` is empty, scan the PR body for `#NNNN` patterns that might reference issues (not other PRs). Fetch those issues and check for `Product-*` labels.
|
||||
|
||||
##### Method E: Changed file paths (HIGH confidence)
|
||||
|
||||
If no linked issues are found, fetch the PR's changed files:
|
||||
```bash
|
||||
gh pr view <number> --repo microsoft/PowerToys --json files --jq '[.files[].path]'
|
||||
```
|
||||
|
||||
Map file paths to products using the `src/modules/` directory structure:
|
||||
|
||||
| Path pattern | Product Label |
|
||||
|-------------|---------------|
|
||||
| `src/modules/AdvancedPaste/` | `Product-Advanced Paste` |
|
||||
| `src/modules/alwaysontop/` | `Product-Always On Top` |
|
||||
| `src/modules/awake/` | `Product-Awake` |
|
||||
| `src/modules/cmdNotFound/` | `Product-CommandNotFound` |
|
||||
| `src/modules/cmdpal/` | `Product-Command Palette` |
|
||||
| `src/modules/colorPicker/` | `Product-Color Picker` |
|
||||
| `src/modules/CropAndLock/` | `Product-CropAndLock` |
|
||||
| `src/modules/EnvironmentVariables/` | `Product-Environment Variables` |
|
||||
| `src/modules/fancyzones/` | `Product-FancyZones` |
|
||||
| `src/modules/FileLocksmith/` | `Product-File Locksmith` |
|
||||
| `src/modules/GrabAndMove/` | `Product-Grab And Move` |
|
||||
| `src/modules/Hosts/` | `Product-Hosts File Editor` |
|
||||
| `src/modules/imageresizer/` | `Product-Image Resizer` |
|
||||
| `src/modules/keyboardmanager/` | `Product-Keyboard Shortcut Manager` |
|
||||
| `src/modules/launcher/` | `Product-PowerToys Run` |
|
||||
| `src/modules/LightSwitch/` | `Product-LightSwitch` |
|
||||
| `src/modules/MeasureTool/` | `Product-Screen Ruler` |
|
||||
| `src/modules/MouseUtils/` | `Product-Mouse Utilities` |
|
||||
| `src/modules/MouseWithoutBorders/` | `Product-Mouse Without Borders` |
|
||||
| `src/modules/NewPlus/` | `Product-New+` |
|
||||
| `src/modules/peek/` | `Product-Peek` |
|
||||
| `src/modules/poweraccent/` | `Product-Quick Accent` |
|
||||
| `src/modules/powerdisplay/` | `Product-PowerDisplay` |
|
||||
| `src/modules/PowerOCR/` | `Product-Text Extractor` |
|
||||
| `src/modules/powerrename/` | `Product-PowerRename` |
|
||||
| `src/modules/previewpane/` | `Product-File Explorer` |
|
||||
| `src/modules/registrypreview/` | `Product-Registry Preview` |
|
||||
| `src/modules/ShortcutGuide/` | `Product-Shortcut Guide` |
|
||||
| `src/modules/Workspaces/` | `Product-Workspaces` |
|
||||
| `src/modules/ZoomIt/` | `Product-ZoomIt` |
|
||||
|
||||
Also check `src/settings-ui/` paths — these often contain the product name (e.g., `ZoomItPage.xaml` → `Product-ZoomIt`, `ImageResizerPage.xaml` → `Product-Image Resizer`).
|
||||
|
||||
If **all** changed files map to a single product → **HIGH confidence**.
|
||||
If changed files span exactly 2 products (one being Settings) → HIGH confidence for the non-Settings product.
|
||||
If changed files span 3+ products → **LOW confidence**, present to user.
|
||||
|
||||
##### Method F: PR title/body content analysis (variable confidence)
|
||||
|
||||
As a final fallback, analyze the PR title and body. Many PRs use a `[ProductName]` prefix convention in the title (e.g., `[PowerDisplay] Fix brightness...`, `[ZoomIt] Remove stale...`). This is **HIGH confidence** if the bracketed name matches a known product.
|
||||
|
||||
Otherwise, apply the same content analysis rules as for issues.
|
||||
|
||||
#### Confidence Classification (applies to both issues and PRs)
|
||||
|
||||
**HIGH confidence** — assign automatically when:
|
||||
- The issue has a deterministic template field match (Method A)
|
||||
- A PR's linked issues have `Product-*` labels (Method C)
|
||||
- All changed files in a PR map to one product (Method E)
|
||||
- The PR title uses `[ProductName]` prefix matching a known product (Method F)
|
||||
- The title/body explicitly and unambiguously names a single product
|
||||
|
||||
**LOW confidence** — present to user for approval when:
|
||||
- Multiple products are mentioned and it's unclear which is primary
|
||||
- The item is about cross-cutting infrastructure (installer, settings, system tray)
|
||||
- The item is in a non-English language and you're unsure of the product
|
||||
- The described feature/bug doesn't clearly map to any existing product
|
||||
- Changed files span 3+ products
|
||||
|
||||
**NO LABEL** — skip entirely when:
|
||||
- The item is too vague to determine any product
|
||||
- The item is about the PowerToys project itself (meta discussions, CI/CD, docs, build infra)
|
||||
- You have no meaningful signal from any method
|
||||
|
||||
### Step 4 — Apply labels and report results
|
||||
|
||||
**For HIGH confidence items:** Apply labels automatically using:
|
||||
```bash
|
||||
# For issues:
|
||||
gh issue edit <number> --repo microsoft/PowerToys --add-label "<Product-Label>"
|
||||
# For PRs (same command works):
|
||||
gh pr edit <number> --repo microsoft/PowerToys --add-label "<Product-Label>"
|
||||
```
|
||||
|
||||
**For LOW confidence items:** Do NOT apply labels. Instead, present them in a table:
|
||||
|
||||
```markdown
|
||||
| # | Type | Title | Suggested Label | Method | Reason |
|
||||
|---|------|-------|----------------|--------|--------|
|
||||
| #123 | Issue | ... | Product-FancyZones | Content | Title mentions "zones" but also "settings" |
|
||||
| #456 | PR | ... | Product-ZoomIt | Files | Changed files span ZoomIt and Settings |
|
||||
```
|
||||
|
||||
Ask the user: *"Would you like me to apply any of these? Reply with the numbers to approve, or 'skip' to leave them."*
|
||||
|
||||
If the user approves specific items, apply those labels.
|
||||
|
||||
**For NO LABEL items:** List them briefly:
|
||||
```
|
||||
Skipped (insufficient signal): #456 (issue), #789 (PR)
|
||||
```
|
||||
|
||||
### Step 5 — Summary
|
||||
|
||||
After processing, always output a summary:
|
||||
|
||||
```
|
||||
=== Label Results ===
|
||||
Issues PRs Total
|
||||
Auto-labeled: 12 5 17
|
||||
Needs review: 3 1 4
|
||||
Skipped: 2 0 2
|
||||
Total: 17 6 23
|
||||
```
|
||||
|
||||
## Safety Rules
|
||||
|
||||
1. **Never remove existing labels** — only add `Product-*` labels
|
||||
2. **Never add labels to items that already have a `Product-*` label** — skip them
|
||||
3. **Never add more than 2 `Product-*` labels** to a single item — if you'd infer 3+, mark as LOW confidence
|
||||
4. **Always echo the search query** before fetching items
|
||||
5. **Always ask for confirmation** when processing more than 50 items
|
||||
6. **Prefer false negatives over false positives** — it's better to skip an item than to mislabel it
|
||||
7. **For PRs, prefer linked-issue labels over content inference** — if a linked issue has a Product-* label, use that even if the PR title/files suggest something different
|
||||
|
||||
## Reference
|
||||
|
||||
Read the override mapping and keyword hints from: `.github/agents/references/product-label-mapping.md`
|
||||
|
||||
This file contains:
|
||||
- **Override mappings** for template values whose names don't match their `Product-*` label (e.g., `Keyboard Manager` → `Product-Keyboard Shortcut Manager`)
|
||||
- **Keyword hints** for content analysis when the structured field is absent
|
||||
- **Non-product template values** that need special handling (Installer, System tray, Welcome window)
|
||||
|
||||
The file does NOT need to list every template value — most map directly by prepending `Product-`. Only non-obvious mismatches need entries. Labels and template values are discovered dynamically at runtime (Step 2.5).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- GitHub CLI (`gh`) must be installed and authenticated. Verify with `gh auth status`.
|
||||
- The agent operates on the `microsoft/PowerToys` repository.
|
||||
106
.github/agents/references/product-label-mapping.md
vendored
106
.github/agents/references/product-label-mapping.md
vendored
@@ -1,106 +0,0 @@
|
||||
# Product Label Mapping — Overrides & Hints
|
||||
|
||||
This file contains **only the non-obvious mappings** between the bug report template
|
||||
"Area(s) with issue?" dropdown values and `Product-*` labels. Most template values
|
||||
map directly by prepending `Product-` — only mismatches are listed here.
|
||||
|
||||
Labels and template values are discovered dynamically at runtime by the agent.
|
||||
|
||||
## Override Mappings (template value ≠ label name)
|
||||
|
||||
These template dropdown values have `Product-*` labels with **different names**:
|
||||
|
||||
| Template Dropdown Value | Product Label |
|
||||
|------------------------|---------------|
|
||||
| ColorPicker | `Product-Color Picker` |
|
||||
| Command not found | `Product-CommandNotFound` |
|
||||
| FancyZones Editor | `Product-FancyZones` |
|
||||
| File Explorer: Preview Pane | `Product-File Explorer` |
|
||||
| File Explorer: Thumbnail preview | `Product-File Explorer` |
|
||||
| Hosts File Editor | `Product-Hosts File Editor` |
|
||||
| Keyboard Manager | `Product-Keyboard Shortcut Manager` |
|
||||
| Power Display | `Product-PowerDisplay` |
|
||||
| TextExtractor | `Product-Text Extractor` |
|
||||
| Screen ruler | `Product-Screen Ruler` |
|
||||
|
||||
## Non-Product Template Values
|
||||
|
||||
These template values do NOT map to a product label. Use content analysis instead:
|
||||
|
||||
| Template Value | Guidance |
|
||||
|---------------|----------|
|
||||
| Installer | Consider `Product-General` or infer from context |
|
||||
| System tray interaction | Consider `Product-Settings` or `Product-General` |
|
||||
| Welcome / PowerToys Tour window | Consider `Product-General` |
|
||||
|
||||
## Keyword Hints for Content Analysis
|
||||
|
||||
When the structured field is not available, use these keyword patterns to infer products:
|
||||
|
||||
| Keywords / Patterns | Suggested Label |
|
||||
|--------------------|-----------------|
|
||||
| CmdPal, cmdpal, command palette, dock | `Product-Command Palette` |
|
||||
| zones, layout, snap, window arrangement | `Product-FancyZones` |
|
||||
| grab, move, drag window | `Product-Grab And Move` |
|
||||
| zoom, screen annotation, draw on screen | `Product-ZoomIt` |
|
||||
| settings-ui, flyout, quick access, tray | `Product-Settings` |
|
||||
| paste, clipboard, AI paste | `Product-Advanced Paste` |
|
||||
| MWB, mouse without borders, cross-machine | `Product-Mouse Without Borders` |
|
||||
| rename, regex, bulk rename | `Product-PowerRename` |
|
||||
| peek, file preview, preview pane | `Product-Peek` |
|
||||
| resize, image resizer, bulk resize | `Product-Image Resizer` |
|
||||
| theme, dark mode, light switch | `Product-LightSwitch` |
|
||||
| accent, diacritics, special characters | `Product-Quick Accent` |
|
||||
| awake, keep awake, caffeine, screen on | `Product-Awake` |
|
||||
| color picker, eyedropper, hex color | `Product-Color Picker` |
|
||||
| hosts, hosts file, DNS | `Product-Hosts File Editor` |
|
||||
| remap, key remap, shortcut remap | `Product-Keyboard Shortcut Manager` |
|
||||
| mouse highlighter, click highlight | `Product-Mouse Highlighter` |
|
||||
| mouse jump, teleport mouse | `Product-Mouse Jump` |
|
||||
| find my mouse, locate cursor | `Product-Find My Mouse` |
|
||||
| crosshairs, cursor crosshair | `Product-Mouse Pointer Crosshairs` |
|
||||
| shortcut guide, keyboard overlay | `Product-Shortcut Guide` |
|
||||
| OCR, text extractor, screen text | `Product-Text Extractor` |
|
||||
| workspace, save layout, restore windows | `Product-Workspaces` |
|
||||
| file locksmith, who is using, file lock | `Product-File Locksmith` |
|
||||
| crop and lock, crop, thumbnail window | `Product-CropAndLock` |
|
||||
| environment variable, env var, PATH | `Product-Environment Variables` |
|
||||
| new+, file template, new file | `Product-New+` |
|
||||
| registry, registry preview, .reg | `Product-Registry Preview` |
|
||||
| screen ruler, measure, pixel ruler | `Product-Screen Ruler` |
|
||||
| run, launcher, powertoys run, plugin | `Product-PowerToys Run` |
|
||||
| command not found, winget, install suggestion | `Product-CommandNotFound` |
|
||||
| brightness, monitor, display, DDC | `Product-PowerDisplay` |
|
||||
| cursor wrap, edge wrap, multi-monitor cursor | `Product-Cursor Wrap` |
|
||||
|
||||
## PR Title Prefix Conventions
|
||||
|
||||
Many PRs use `[ProductName]` prefixes. Common variants:
|
||||
|
||||
| Title prefix | Product Label |
|
||||
|-------------|---------------|
|
||||
| `[CmdPal]` | `Product-Command Palette` |
|
||||
| `[PowerDisplay]` | `Product-PowerDisplay` |
|
||||
| `[ZoomIt]` | `Product-ZoomIt` |
|
||||
| `[Image Resizer]` | `Product-Image Resizer` |
|
||||
| `[GPO]` | `Product-General` |
|
||||
| `[MWB]` | `Product-Mouse Without Borders` |
|
||||
|
||||
Most other prefixes match the label directly (e.g., `[FancyZones]` → `Product-FancyZones`).
|
||||
|
||||
## Source Directory → Label Mapping
|
||||
|
||||
Non-obvious `src/modules/` directory name mappings:
|
||||
|
||||
| Directory | Product Label |
|
||||
|----------|---------------|
|
||||
| `launcher/` | `Product-PowerToys Run` |
|
||||
| `MeasureTool/` | `Product-Screen Ruler` |
|
||||
| `poweraccent/` | `Product-Quick Accent` |
|
||||
| `PowerOCR/` | `Product-Text Extractor` |
|
||||
| `previewpane/` | `Product-File Explorer` |
|
||||
| `interface/` | `Product-General` (runner/settings host) |
|
||||
|
||||
Most other directories match by prepending `Product-` to the directory name.
|
||||
|
||||
<!-- Valid Product-* labels are discovered dynamically at runtime via gh label list -->
|
||||
@@ -16,7 +16,7 @@ For each CSV in `Generated Files/ReleaseNotes/grouped_csv/`, create a markdown f
|
||||
- Use the “Verb-ed + Scenario + Impact” sentence structure—make readers think, “That’s exactly what I need” or “Yes, that’s an awesome fix.”; The "impact" can be end-user focused (written to convey user excitement) or technical (performance/stability) when user-facing impact is minimal.
|
||||
- If nothing special on impact or unclear impact, mark as needing human summary
|
||||
- Source from Title, Body, and CopilotSummary (prefer CopilotSummary when available)
|
||||
- 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.
|
||||
- The `NeedThanks` column contains a comma-separated list of external contributor usernames who should be credited (empty = no attribution needed, all authors are core team). For each non-empty `NeedThanks` value, append a `by` attribution that lists **every** contributor, matching GitHub's standard contributor-attribution style: `by [@user1](https://github.com/user1)` for a single contributor, `by [@user1](https://github.com/user1) and [@user2](https://github.com/user2)` for two, or `by [@user1](https://github.com/user1), [@user2](https://github.com/user2), and [@user3](https://github.com/user3)` for three or more. In the final consolidated release notes (Step 4.2), the attribution follows the PR link, e.g. `…in [#1234](url) by [@user](url)`. Do not use "Thanks @user!" phrasing.
|
||||
- Do NOT include PR numbers in bullet lines
|
||||
- Do NOT mention “security” or “privacy” issues, since these are not known and could be leveraged by attackers in earlier versions. Instead, describe the user-facing scenario, usage, or impact.
|
||||
- If confidence < 70%, write: `Human Summary Needed: <PR full link>`
|
||||
|
||||
18
.github/workflows/spelling2.yml
vendored
18
.github/workflows/spelling2.yml
vendored
@@ -55,7 +55,7 @@ name: Spell checking
|
||||
# spelling:
|
||||
# # remove `security-events: write` and `use_sarif: 1`
|
||||
# # remove `experimental_apply_changes_via_bot: 1`
|
||||
# ... otherwise adjust the `with:` as you wish
|
||||
# ... otherwise, adjust the `with:` as you wish
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -74,6 +74,8 @@ on:
|
||||
types:
|
||||
- "created"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Check Spelling
|
||||
@@ -85,7 +87,7 @@ jobs:
|
||||
outputs:
|
||||
followup: ${{ steps.spelling.outputs.followup }}
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.event_name, 'pull_request') || github.event_name == 'push' }}
|
||||
if: ${{ (contains(github.event_name, 'pull_request') && github.event.pull_request.state == 'open') || github.event_name == 'push' }}
|
||||
concurrency:
|
||||
group: spelling-${{ github.event.pull_request.number || github.ref }}
|
||||
# note: If you use only_check_changed_files, you do not want cancel-in-progress
|
||||
@@ -140,7 +142,7 @@ jobs:
|
||||
comment-push:
|
||||
name: Report (Push)
|
||||
# If your workflow isn't running on push, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: spelling
|
||||
permissions:
|
||||
actions: read
|
||||
@@ -150,24 +152,21 @@ 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-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: spelling
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@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 }}
|
||||
|
||||
@@ -177,12 +176,13 @@ jobs:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
actions: read
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
if: ${{
|
||||
github.repository_owner != 'microsoft' &&
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '@check-spelling-bot apply') &&
|
||||
contains(github.event.comment.body, '@check-spelling-bot') &&
|
||||
contains(github.event.comment.body, 'apply') &&
|
||||
contains(github.event.comment.body, 'https://')
|
||||
}}
|
||||
concurrency:
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -365,6 +365,8 @@ installer/*/*.wxs.bk
|
||||
**/.claude/settings.local.json
|
||||
|
||||
# Squad / Copilot agents — local-only, not committed
|
||||
.squad/
|
||||
.copilot
|
||||
.squad
|
||||
.squad-workstream
|
||||
.github/agents/
|
||||
.github/agents/**squad**.md
|
||||
.github/workflows/**squad**.yml
|
||||
|
||||
@@ -264,8 +264,8 @@
|
||||
"Workspaces.ModuleServices.dll",
|
||||
"Microsoft.CommandPalette.Extensions.dll",
|
||||
"Microsoft.CommandPalette.Extensions.Toolkit.dll",
|
||||
"Microsoft.CmdPal.Ext.PowerToys.dll",
|
||||
"Microsoft.CmdPal.Ext.PowerToys.exe",
|
||||
"WinUI3Apps\\Microsoft.CmdPal.Ext.PowerToys.dll",
|
||||
"WinUI3Apps\\Microsoft.CmdPal.Ext.PowerToys.exe",
|
||||
"*Microsoft.CmdPal.UI_*.msix",
|
||||
|
||||
"PowerToys.DSC.dll",
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -13,5 +13,6 @@
|
||||
{
|
||||
"file": ".github/prompts/create-pr-summary.prompt.md"
|
||||
}
|
||||
]
|
||||
],
|
||||
"sarif-viewer.connectToGithubCodeScanning": "on"
|
||||
}
|
||||
22
README.md
22
README.md
@@ -50,18 +50,18 @@ But to get started quickly, choose one of the installation methods below:
|
||||
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
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.100%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysUserSetup-0.99.0-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysUserSetup-0.99.0-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysSetup-0.99.0-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.99.0/PowerToysSetup-0.99.0-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] |
|
||||
| Per user - x64 | [PowerToysUserSetup-0.99.0-x64.exe][ptUserX64] |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.99.0-arm64.exe][ptUserArm64] |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.99.0-x64.exe][ptMachineX64] |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.99.0-arm64.exe][ptMachineArm64] |
|
||||
|
||||
</details>
|
||||
|
||||
@@ -106,11 +106,11 @@ There are [community driven install methods](https://learn.microsoft.com/windows
|
||||
|
||||
[](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).
|
||||
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.99.0).
|
||||
|
||||
## 🛣️ Roadmap
|
||||
|
||||
We are planning some nice new features and improvements for the next releases – PowerDisplay, Command Palette improvements and a brand-new Shortcut Guide experience! Stay tuned for [v0.99][github-next-release-work]!
|
||||
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]!
|
||||
|
||||
## ❤️ PowerToys Community
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ After generating the resx file, rename the existing rc and h files to ProjName.b
|
||||
</Target>
|
||||
```
|
||||
|
||||
This event runs a script which generates a resource.h and ProjName.rc in the `Generated Files` folder using the strings in all the resx files along with the existing information in resource.base.h and ProjName.base.rc. The script is [convert-resx-to-rc.ps1](https://github.com/microsoft/PowerToys/blob/main/tools/build/convert-resx-to-rc.ps1). The script uses [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert) to convert the resx file to a string table expected in the .rc file format. When the resources are added to the rc file the `IDS_` prefix is added and resource names are in upper case (as it was originally). Any occurrences of `"` in the string resource is escaped as `""` to prevent build errors. The string tables are added to the rc file in the following format:
|
||||
This event runs a script which generates a resource.h and ProjName.rc in the `Generated Files` folder using the strings in all the resx files along with the existing information in resource.base.h and ProjName.base.rc. The script is [convert-resx-to-rc.ps1](https://github.com/microsoft/PowerToys/blob/main/tools/build/convert-resx-to-rc.ps1). The script uses [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert) to convert the resx file to a string table expected in the .rc file format. When the resources are added to the rc file the `IDS_` prefix is added and resource names are in uppercase (as it was originally). Any occurrences of `"` in the string resource is escaped as `""` to prevent build errors. The string tables are added to the rc file in the following format:
|
||||
```
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
@@ -353,7 +353,7 @@ On a cold launch, DevPal will do the following:
|
||||
* Start it up.
|
||||
* Check if it's fresh or frozen.
|
||||
* Call `TopLevelCommands`, and put all of them in the list
|
||||
* Create a extension cache entry for that app.
|
||||
* Create an extension cache entry for that app.
|
||||
* If the provider is frozen: we can actually release the
|
||||
`ICommandProvider` instance at this point.
|
||||
* And of course, if we don't find all the packages we had cached, then delete
|
||||
@@ -454,7 +454,7 @@ ms-windows-store://assoc/?Tags=AppExtension-com.microsoft.commandpalette
|
||||
|
||||
to open the store to a list of extensions. However, we can't list those
|
||||
ourselves directly. Our friends in DevHome suggested it could be possible to
|
||||
stand up a azure service which could query the store for us, and return a list
|
||||
stand up an azure service which could query the store for us, and return a list
|
||||
of extensions. This is not something that they currently have planned, nor would
|
||||
it be cheap from an engineering standpoint.
|
||||
|
||||
@@ -1780,7 +1780,7 @@ class MyAppSettings {
|
||||
/* You can save the settings to the file here */
|
||||
var mySettingsFilePath = /* whatever */;
|
||||
string mySettingsJson = mySettings.Settings.GetState();
|
||||
// Or you could raise a event to indicate to the rest of your app that settings have changed.
|
||||
// Or you could raise an event to indicate to the rest of your app that settings have changed.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2006,7 +2006,7 @@ class CommandWithOnlyProperties : IExtendedAttributesProvider { ... }
|
||||
|
||||
will populate the WinRT type cache in Command Palette with the type information
|
||||
for `ICommandWithProperties`. In fact, if Command Palette has the
|
||||
`IExtendedAttributesProvider` type info in it's cache, and then later receives a new
|
||||
`IExtendedAttributesProvider` type info in its cache, and then later receives a new
|
||||
`MyCommandWithProperties` object, it'll actually be able to know that
|
||||
`MyCommandWithProperties` is an `IExtendedAttributesProvider`. WinRT is just weird
|
||||
like that some times.
|
||||
@@ -2350,7 +2350,7 @@ follow - these are not part of the current SDK spec.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> A thought: what if a action returns a `CommandResult.Entity`, then that takes
|
||||
> A thought: what if an action returns a `CommandResult.Entity`, then that takes
|
||||
> devpal back home, but leaves the entity in the query box. This would allow for
|
||||
> a Quicksilver-like "thing, do" flow. That command would prepopulate the
|
||||
> parameters. So we would then filter top-level commands based on things that can
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This guide is for iterating on `src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj`.
|
||||
|
||||
The extension is registered through the shared sparse package defined in `src/PackageIdentity/AppxManifest.xml`. That manifest declares `Microsoft.CmdPal.Ext.PowerToys.exe` at the sparse package root, so the sparse package and the extension must be built for the same platform and configuration, for example `x64\Debug`.
|
||||
The extension is registered through the shared sparse package defined in `src/PackageIdentity/AppxManifest.xml`. That manifest declares `Microsoft.CmdPal.Ext.PowerToys.exe` relative to the sparse package's ExternalLocation (`WinUI3Apps\` subfolder), so the sparse package and the extension must be built for the same platform and configuration, for example `x64\Debug`.
|
||||
|
||||
## Local development loop
|
||||
|
||||
@@ -30,12 +30,12 @@ The extension is registered through the shared sparse package defined in `src/Pa
|
||||
The command will look like this:
|
||||
|
||||
```powershell
|
||||
Add-AppxPackage -Path "<repo>\<Platform>\<Configuration>\PowerToysSparse.msix" -ExternalLocation "<repo>\<Platform>\<Configuration>"
|
||||
Add-AppxPackage -Path "<repo>\<Platform>\<Configuration>\PowerToysSparse.msix" -ExternalLocation "<repo>\<Platform>\<Configuration>\WinUI3Apps"
|
||||
```
|
||||
|
||||
4. Build `src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Microsoft.CmdPal.Ext.PowerToys.csproj` in the same platform and configuration.
|
||||
|
||||
This project writes `Microsoft.CmdPal.Ext.PowerToys.exe` directly into the sparse package root, such as `x64\Debug` or `ARM64\Debug`. That matches the `Executable="Microsoft.CmdPal.Ext.PowerToys.exe"` entry in `src/PackageIdentity/AppxManifest.xml`.
|
||||
This project writes `Microsoft.CmdPal.Ext.PowerToys.exe` into the `WinUI3Apps\` subfolder of the output root, such as `x64\Debug\WinUI3Apps` or `ARM64\Debug\WinUI3Apps`. That matches the `Executable="Microsoft.CmdPal.Ext.PowerToys.exe"` entry in `src/PackageIdentity/AppxManifest.xml` resolved relative to the sparse package's ExternalLocation.
|
||||
|
||||
5. Restart Command Palette.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
However, the files write and read those are C:\Users\“xxxxxx”\AppData\Local\Microsoft\PowerToys\FancyZones
|
||||
However, the files read from and written to are those in `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.
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ There are three different score types with different start values.
|
||||
| Medium score | 5000 |
|
||||
| Low score | 1000 |
|
||||
|
||||
Each score will decreased by one when a condition match.
|
||||
Each score will be 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 a object as root type, instead of a array.
|
||||
Because the JSON file must have an object as root type, instead of an array.
|
||||
|
||||
### Important project values (*.csproj)
|
||||
|
||||
|
||||
93
doc/devdocs/tools/installer-diagnostics.md
Normal file
93
doc/devdocs/tools/installer-diagnostics.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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: 256 KiB After Width: | Height: | Size: 258 KiB |
@@ -639,7 +639,7 @@ UINT __stdcall InstallPackageIdentityMSIXCA(MSIHANDLE hInstall)
|
||||
try
|
||||
{
|
||||
|
||||
std::wstring externalLocation = installFolderPath; // External content location (PowerToys install folder)
|
||||
std::wstring externalLocation = installFolderPath + L"WinUI3Apps\\"; // External content location (WinUI3Apps subfolder to isolate DACL changes from preview handler DLLs)
|
||||
Uri externalUri{ externalLocation }; // External location URI for sparse package content
|
||||
Uri packageUri{ msixPath }; // The MSIX file URI
|
||||
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
<?define BaseApplicationsFilesPath=$(var.BinDir)\?>
|
||||
|
||||
<Fragment>
|
||||
<DirectoryRef Id="INSTALLFOLDER">
|
||||
<!-- winmd must be in WinUI3Apps (ExternalLocation) for WinRT COM proxy/stub resolution -->
|
||||
<DirectoryRef Id="WinUI3AppsInstallFolder">
|
||||
<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)Microsoft.CommandPalette.Extensions.winmd" />
|
||||
<File Id="Microsoft.CommandPalette.Extensions.winmd" Source="$(var.BinDir)WinUI3Apps\Microsoft.CommandPalette.Extensions.winmd" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="INSTALLFOLDER">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--BaseApplicationsFiles_Component_Def-->
|
||||
</DirectoryRef>
|
||||
|
||||
@@ -67,8 +67,11 @@
|
||||
<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>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
<?define KeyboardManagerAssetsFiles=?>
|
||||
<?define KeyboardManagerAssetsWinUI3Files=?>
|
||||
<?define KeyboardManagerAssetsFilesPath=$(var.BinDir)\Assets\KeyboardManager\?>
|
||||
<?define KeyboardManagerAssetsFilesPath=$(var.BinDir)\WinUI3Apps\Assets\KeyboardManager\?>
|
||||
<?define KeyboardManagerAssetsWinUI3FilesPath=$(var.BinDir)\WinUI3Apps\Assets\KeyboardManagerEditor\?>
|
||||
|
||||
<Fragment>
|
||||
<DirectoryRef Id="BaseApplicationsAssetsFolder">
|
||||
<DirectoryRef Id="WinUI3AppsAssetsFolder">
|
||||
<Directory Id="KeyboardManagerAssetsInstallFolder" Name="KeyboardManager" />
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="WinUI3AppsAssetsFolder">
|
||||
|
||||
@@ -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;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
|
||||
<?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?>
|
||||
<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)INSTALLFOLDER" Guid="$(var.CompGUIDPrefix)23">
|
||||
<Component Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" 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)\$(var.Language)\Microsoft.CmdPal.Ext.PowerToys.resources.dll" />
|
||||
<File Id="CmdPalExtPowerToys_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\WinUI3Apps\$(var.Language)\Microsoft.CmdPal.Ext.PowerToys.resources.dll" />
|
||||
</Component>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?undef CompGUIDPrefix?>
|
||||
@@ -433,6 +433,7 @@
|
||||
<?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" />
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
<?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">
|
||||
@@ -27,6 +30,7 @@
|
||||
<Directory Id="SettingsV2AssetsModulesInstallFolder" Name="Modules">
|
||||
<Directory Id="SettingsV2OOBEAssetsModulesInstallFolder" Name="OOBE" />
|
||||
</Directory>
|
||||
<Directory Id="SettingsV2AssetsCmdPalInstallFolder" Name="CmdPal" />
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
|
||||
@@ -55,6 +59,11 @@
|
||||
<!--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">
|
||||
@@ -80,6 +89,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -191,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\Assets\KeyboardManager"
|
||||
Generate-FileList -fileDepsJson "" -fileListName KeyboardManagerAssetsFiles -wxsFilePath $PSScriptRoot\KeyboardManager.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\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
|
||||
@@ -336,11 +336,13 @@ 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\"
|
||||
|
||||
@@ -38,17 +38,7 @@
|
||||
</Capabilities>
|
||||
|
||||
<Applications>
|
||||
<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">
|
||||
<Application Id="PowerToys.SettingsUI" Executable="PowerToys.Settings.exe" EntryPoint="Windows.FullTrustApplication">
|
||||
<uap:VisualElements
|
||||
DisplayName="PowerToys.SettingsUI"
|
||||
Description="PowerToys Settings UI"
|
||||
@@ -58,7 +48,7 @@
|
||||
AppListEntry="none">
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
<Application Id="PowerToys.ImageResizerUI" Executable="WinUI3Apps\PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
|
||||
<Application Id="PowerToys.ImageResizerUI" Executable="PowerToys.ImageResizer.exe" EntryPoint="Windows.FullTrustApplication">
|
||||
<uap:VisualElements
|
||||
DisplayName="PowerToys.ImageResizer"
|
||||
Description="PowerToys Image Resizer UI"
|
||||
|
||||
@@ -417,6 +417,7 @@ 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 `"$outDir`"" -Level Warning
|
||||
Write-BuildLog "(If already installed and you changed manifest only): Add-AppxPackage -Register `"$manifestPath`" -ExternalLocation `"$outDir`" -ForceApplicationShutdown" -Level Warning
|
||||
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
|
||||
|
||||
@@ -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, PowerOCR, Image Resizer).
|
||||
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 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`).
|
||||
> 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.
|
||||
|
||||
## Building the sparse package locally
|
||||
|
||||
@@ -53,16 +53,17 @@ After `PowerToysSparse.msix` is generated:
|
||||
# First time registration
|
||||
$repoRoot = "C:/git/PowerToys"
|
||||
$outputRoot = Join-Path $repoRoot "x64/Release"
|
||||
Add-AppxPackage -Path (Join-Path $outputRoot "PowerToysSparse.msix") -ExternalLocation $outputRoot
|
||||
$externalLocation = Join-Path $outputRoot "WinUI3Apps"
|
||||
Add-AppxPackage -Path (Join-Path $outputRoot "PowerToysSparse.msix") -ExternalLocation $externalLocation
|
||||
|
||||
# Re-register after manifest tweaks only
|
||||
Add-AppxPackage -Register (Join-Path $repoRoot "src/PackageIdentity/AppxManifest.xml") -ExternalLocation $outputRoot -ForceApplicationShutdown
|
||||
Add-AppxPackage -Register (Join-Path $repoRoot "src/PackageIdentity/AppxManifest.xml") -ExternalLocation $externalLocation -ForceApplicationShutdown
|
||||
|
||||
# Remove the sparse identity
|
||||
Get-AppxPackage -Name Microsoft.PowerToys.SparseApp | Remove-AppxPackage
|
||||
```
|
||||
|
||||
`-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.
|
||||
`-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.
|
||||
|
||||
## CI-specific guidance
|
||||
|
||||
@@ -72,7 +73,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` root.
|
||||
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).
|
||||
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 manifest’s 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.
|
||||
|
||||
@@ -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 a infinite loop function
|
||||
// prevent the warning after the call off an infinite loop function
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702)
|
||||
DWORD WINAPI _checkTheme(LPVOID lpParam)
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UI element is Enabled or not.
|
||||
/// Gets a value indicating whether or not the UI element is Enabled.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exit a exe by Name.
|
||||
/// Exit an 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 a exe.
|
||||
/// Exit an exe.
|
||||
/// </summary>
|
||||
/// <param name="appPath">The path to the application executable.</param>
|
||||
public void ExitExe(string appPath)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.UITest
|
||||
public static class VisualAssert
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts current visual state of the element is equal with base line image.
|
||||
/// Asserts current visual state of the element is equal to 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>
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
}
|
||||
if (this->interrupted)
|
||||
{
|
||||
//Just returns a empty string if the queue was interrupted.
|
||||
//Just returns an empty string if the queue was interrupted.
|
||||
return std::wstring(L"");
|
||||
}
|
||||
std::wstring message = this->message_queue.front();
|
||||
|
||||
@@ -78,7 +78,7 @@ If enabled, per-user installation is not allowed.
|
||||
|
||||
If disabled or not configured, per-user installation is allowed.
|
||||
</string>
|
||||
<string id="DisableAutomaticUpdateDownloadDescription">This policy configures whether the automatic download and installation of available updates is disabled or not. (On metered connections updates are never downloaded.)
|
||||
<string id="DisableAutomaticUpdateDownloadDescription">This policy configures whether or not the automatic download and installation of available updates is disabled. (On metered connections updates are never downloaded.)
|
||||
|
||||
If enabled, automatic download and installation is disabled.
|
||||
|
||||
@@ -94,7 +94,7 @@ Note: The notification about new major versions is always displayed.
|
||||
|
||||
This policy has no effect if the update notification is disabled by the policy "Disable Action Center notification for new updates" or the user setting.
|
||||
</string>
|
||||
<string id="DisableNewUpdateToastDescription">This policy configures whether the action center notification for new updates is shown or not.
|
||||
<string id="DisableNewUpdateToastDescription">This policy configures whether or not the action center notification for new updates is shown.
|
||||
|
||||
If enabled, the notification is disabled.
|
||||
|
||||
|
||||
@@ -910,6 +910,12 @@ public:
|
||||
return powertoys_gpo::getConfiguredAdvancedPasteEnabledValue();
|
||||
}
|
||||
|
||||
// Returns whether the PowerToys should be enabled by default
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
{
|
||||
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
@@ -75,6 +75,12 @@ public:
|
||||
return powertoys_gpo::getConfiguredCropAndLockEnabledValue();
|
||||
}
|
||||
|
||||
// Returns whether the PowerToys should be enabled by default
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return JSON with the configuration options.
|
||||
// These are the settings shown on the settings page along with their current values.
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
|
||||
@@ -226,6 +226,12 @@ public:
|
||||
return powertoys_gpo::getConfiguredEnvironmentVariablesEnabledValue();
|
||||
}
|
||||
|
||||
// Returns whether the PowerToys should be enabled by default
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool get_config(wchar_t* /*buffer*/, int* /*buffer_size*/) override
|
||||
{
|
||||
return false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,12 @@
|
||||
#include <shellapi.h>
|
||||
#include <commctrl.h>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -243,6 +243,12 @@ public:
|
||||
return powertoys_gpo::getConfiguredHostsFileEditorEnabledValue();
|
||||
}
|
||||
|
||||
// Returns whether the PowerToys should be enabled by default
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool get_config(wchar_t* /*buffer*/, int* /*buffer_size*/) override
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -45,21 +45,18 @@ void LightSwitchStateManager::OnManualOverride()
|
||||
Logger::info(L"[LightSwitchStateManager] Manual override triggered");
|
||||
_state.isManualOverride = !_state.isManualOverride;
|
||||
|
||||
// When entering manual override, sync internal theme state to match the current system
|
||||
// The hotkey handler in ModuleInterface has already toggled the theme, so we read the new state
|
||||
if (_state.isManualOverride)
|
||||
{
|
||||
_state.isSystemLightActive = GetCurrentSystemTheme();
|
||||
_state.isAppsLightActive = GetCurrentAppsTheme();
|
||||
// ModuleInterface has already flipped the Windows theme before signaling this event,
|
||||
// regardless of which direction isManualOverride just toggled. Sync cached state and
|
||||
// notify PowerDisplay on every call so the profile follows every hotkey press — the
|
||||
// previous "if entering" gate silently dropped every even-numbered press.
|
||||
_state.isSystemLightActive = GetCurrentSystemTheme();
|
||||
_state.isAppsLightActive = GetCurrentAppsTheme();
|
||||
|
||||
Logger::debug(L"[LightSwitchStateManager] Synced internal theme state to current system theme ({}) and apps theme ({}).",
|
||||
(_state.isSystemLightActive ? L"light" : L"dark"),
|
||||
(_state.isAppsLightActive ? L"light" : L"dark"));
|
||||
Logger::debug(L"[LightSwitchStateManager] Synced internal theme state to current system theme ({}) and apps theme ({}).",
|
||||
(_state.isSystemLightActive ? L"light" : L"dark"),
|
||||
(_state.isAppsLightActive ? L"light" : L"dark"));
|
||||
|
||||
// Notify PowerDisplay about the theme change triggered by hotkey
|
||||
// The theme has already been applied by ModuleInterface, we just need to notify PowerDisplay
|
||||
NotifyPowerDisplay(_state.isSystemLightActive);
|
||||
}
|
||||
NotifyPowerDisplay(_state.isSystemLightActive);
|
||||
|
||||
EvaluateAndApplyIfNeeded();
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace LightSwitch.UITests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update time test operation
|
||||
/// Perform an update time test operation
|
||||
/// </summary>
|
||||
public static void PerformUpdateTimeTest(UITestBase testBase)
|
||||
{
|
||||
@@ -257,7 +257,7 @@ namespace LightSwitch.UITests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update manual location test operation
|
||||
/// Perform an update manual location test operation
|
||||
/// </summary>
|
||||
public static void PerformUserSelectedLocationTest(UITestBase testBase)
|
||||
{
|
||||
@@ -300,7 +300,7 @@ namespace LightSwitch.UITests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update geolocation test operation
|
||||
/// Perform an update geolocation test operation
|
||||
/// </summary>
|
||||
public static void PerformGeolocationTest(UITestBase testBase)
|
||||
{
|
||||
@@ -335,7 +335,7 @@ namespace LightSwitch.UITests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a update time test operation
|
||||
/// Perform an update time test operation
|
||||
/// </summary>
|
||||
public static void PerformOffsetTest(UITestBase testBase)
|
||||
{
|
||||
|
||||
@@ -368,7 +368,7 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa
|
||||
}
|
||||
instance->AddDrawingPoint(MouseButton::Right);
|
||||
instance->m_rightButtonPressed = true;
|
||||
// same as for the left button, start a timer for reposition ourselves to topmost position
|
||||
// same as for the left button, start a timer to reposition ourselves to topmost position
|
||||
if (instance->m_timer_id == 0)
|
||||
{
|
||||
instance->m_timer_id = SetTimer(instance->m_hwnd, BRING_TO_FRONT_TIMER_ID, 10, nullptr);
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace
|
||||
// for a window, it is just limited. If there is no WS_MAXIMIZEBOX using
|
||||
// WinKey + Up just won't maximize the window. Similarly, without
|
||||
// WS_MINIMIZEBOX the window will not get minimized. A "Save As..." dialog
|
||||
// is a example of such window - it can be snapped to both sides and to
|
||||
// is an example of such window - it can be snapped to both sides and to
|
||||
// all screen corners, but will not get maximized nor minimized.
|
||||
// For now, since ShortcutGuide can only disable entire "Windows Controls"
|
||||
// group, we require that the window supports all the options.
|
||||
|
||||
@@ -92,6 +92,12 @@ public:
|
||||
return powertoys_gpo::getConfiguredWorkspacesEnabledValue();
|
||||
}
|
||||
|
||||
// Returns whether the PowerToys should be enabled by default
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return JSON with the configuration options.
|
||||
// These are the settings shown on the settings page along with their current values.
|
||||
virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override
|
||||
|
||||
@@ -9784,7 +9784,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
{
|
||||
if (!RegisterHotKey(hWnd, ZOOM_HOTKEY, g_ToggleMod, g_ToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9793,7 +9796,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
if (!RegisterHotKey(hWnd, LIVE_HOTKEY, g_LiveZoomToggleMod, g_LiveZoomToggleKey & 0xFF) ||
|
||||
!RegisterHotKey(hWnd, LIVE_DRAW_HOTKEY, g_LiveZoomToggleMod ^ MOD_SHIFT, g_LiveZoomToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified live-zoom toggle hotkey is already in use.\nSelect a different zoom toggle hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9801,7 +9807,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
{
|
||||
if (!RegisterHotKey(hWnd, DRAW_HOTKEY, g_DrawToggleMod, g_DrawToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified draw w/out zoom hotkey is already in use.\nSelect a different draw w/out zoom hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9809,7 +9818,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
{
|
||||
if (!RegisterHotKey(hWnd, BREAK_HOTKEY, g_BreakToggleMod, g_BreakToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified break timer hotkey is already in use.\nSelect a different break timer hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9818,7 +9830,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
if (!RegisterHotKey(hWnd, DEMOTYPE_HOTKEY, g_DemoTypeToggleMod, g_DemoTypeToggleKey & 0xFF) ||
|
||||
!RegisterHotKey(hWnd, DEMOTYPE_RESET_HOTKEY, (g_DemoTypeToggleMod ^ MOD_SHIFT), g_DemoTypeToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified live-type hotkey is already in use.\nSelect a different live-type hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9827,7 +9842,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
if (!RegisterHotKey(hWnd, SNIP_HOTKEY, g_SnipToggleMod, g_SnipToggleKey & 0xFF) ||
|
||||
!RegisterHotKey(hWnd, SNIP_SAVE_HOTKEY, (g_SnipToggleMod ^ MOD_SHIFT), g_SnipToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified snip hotkey is already in use.\nSelect a different snip hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9837,7 +9855,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
if (!RegisterHotKey(hWnd, SNIP_PANORAMA_HOTKEY, g_SnipPanoramaToggleMod | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF) ||
|
||||
!RegisterHotKey(hWnd, SNIP_PANORAMA_SAVE_HOTKEY, ( g_SnipPanoramaToggleMod ^ MOD_SHIFT ) | MOD_NOREPEAT, g_SnipPanoramaToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified panorama snip hotkey is already in use.\nSelect a different panorama snip hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9845,7 +9866,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
{
|
||||
if (!RegisterHotKey(hWnd, SNIP_OCR_HOTKEY, g_SnipOcrToggleMod, g_SnipOcrToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified snip OCR hotkey is already in use.\nSelect a different snip OCR hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -9855,7 +9879,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
!RegisterHotKey(hWnd, RECORD_CROP_HOTKEY, (g_RecordToggleMod ^ MOD_SHIFT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF) ||
|
||||
!RegisterHotKey(hWnd, RECORD_WINDOW_HOTKEY, (g_RecordToggleMod ^ MOD_ALT) | MOD_NOREPEAT, g_RecordToggleKey & 0xFF))
|
||||
{
|
||||
MessageBox(hWnd, L"The specified record hotkey is already in use.\nSelect a different record hotkey.", APPNAME, MB_ICONERROR);
|
||||
if(!g_StartedByPowerToys)
|
||||
{
|
||||
MessageBox(hWnd, L"The specified record hotkey is already in use.\nSelect a different record hotkey.", APPNAME, MB_ICONERROR);
|
||||
}
|
||||
showOptions = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
@@ -11,9 +12,21 @@ public record AppStateModel
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// STATE HERE
|
||||
// Make sure that any new types you add are added to JsonSerializationContext!
|
||||
public RecentCommandsManager RecentCommands { get; init; } = new();
|
||||
private RecentCommandsManager? _recentCommands = new();
|
||||
|
||||
public ImmutableList<string> RunHistory { get; init; } = ImmutableList<string>.Empty;
|
||||
public RecentCommandsManager RecentCommands
|
||||
{
|
||||
get => _recentCommands ?? new();
|
||||
init => _recentCommands = value;
|
||||
}
|
||||
|
||||
private ImmutableList<string>? _runHistory = ImmutableList<string>.Empty;
|
||||
|
||||
public ImmutableList<string> RunHistory
|
||||
{
|
||||
get => _runHistory ?? ImmutableList<string>.Empty;
|
||||
init => _runHistory = value;
|
||||
}
|
||||
|
||||
// END SETTINGS
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -46,45 +46,6 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
||||
|
||||
public IconInfoViewModel Icon => _adapter.IconViewModel;
|
||||
|
||||
private ShowLabelsOption _showLabels;
|
||||
|
||||
public ShowLabelsOption ShowLabels
|
||||
{
|
||||
get => _showLabels;
|
||||
set
|
||||
{
|
||||
if (value != _showLabels)
|
||||
{
|
||||
_showLabels = value;
|
||||
var newShowTitles = value switch
|
||||
{
|
||||
ShowLabelsOption.Default => (bool?)null,
|
||||
ShowLabelsOption.ShowLabels => true,
|
||||
ShowLabelsOption.HideLabels => false,
|
||||
_ => null,
|
||||
};
|
||||
UpdateModel(_dockSettingsModel with { ShowTitles = newShowTitles });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ShowLabelsOption FetchShowLabels()
|
||||
{
|
||||
if (_dockSettingsModel.ShowLabels == null)
|
||||
{
|
||||
return ShowLabelsOption.Default;
|
||||
}
|
||||
|
||||
return _dockSettingsModel.ShowLabels.Value ? ShowLabelsOption.ShowLabels : ShowLabelsOption.HideLabels;
|
||||
}
|
||||
|
||||
// used to map to ComboBox selection
|
||||
public int ShowLabelsIndex
|
||||
{
|
||||
get => (int)ShowLabels;
|
||||
set => ShowLabels = (ShowLabelsOption)value;
|
||||
}
|
||||
|
||||
private DockPinSide PinSide
|
||||
{
|
||||
get => _pinSide;
|
||||
@@ -138,7 +99,6 @@ public partial class DockBandSettingsViewModel : ObservableObject
|
||||
_bandViewModel = bandViewModel;
|
||||
_settingsService = settingsService;
|
||||
_pinSide = FetchPinSide();
|
||||
_showLabels = FetchShowLabels();
|
||||
}
|
||||
|
||||
private DockPinSide FetchPinSide()
|
||||
|
||||
@@ -61,6 +61,11 @@ public sealed partial class DockViewModel
|
||||
}
|
||||
|
||||
Logger.LogDebug("Starting DockBands_CollectionChanged");
|
||||
|
||||
// Refresh settings so newly pinned/unpinned bands are visible.
|
||||
// Pin/unpin operations save with hotReload:false (to avoid
|
||||
// double-updates), so _settings can be stale here.
|
||||
_settings = _settingsService.Settings.DockSettings;
|
||||
SetupBands();
|
||||
Logger.LogDebug("Ended DockBands_CollectionChanged");
|
||||
}
|
||||
@@ -554,7 +559,7 @@ public sealed partial class DockViewModel
|
||||
}
|
||||
|
||||
// Create settings for the new band
|
||||
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId, ShowLabels = null };
|
||||
var bandSettings = new DockBandSettings { ProviderId = topLevel.CommandProviderId, CommandId = bandId };
|
||||
var dockSettings = _settings;
|
||||
|
||||
// Create the band view model
|
||||
|
||||
@@ -44,6 +44,15 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
private readonly Lock _listLock = new();
|
||||
private readonly IContextMenuFactory _contextMenuFactory;
|
||||
|
||||
// Reentrancy guard for FilteredItems mutations. WinUI3's ListView processes
|
||||
// CollectionChanged synchronously, and its layout pass can pump the message
|
||||
// loop — which lets a second DoOnUiThread task start mutating FilteredItems
|
||||
// while the first is still mid-update. C# lock is reentrant (same thread
|
||||
// re-acquires), so _listLock cannot prevent this. Instead we use a boolean
|
||||
// flag and defer the latest update until the in-flight one finishes.
|
||||
private bool _isUpdatingFilteredItems;
|
||||
private Action? _pendingFilteredItemsUpdate;
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ListViewModel, int>? _getItemsDepthByViewModel;
|
||||
|
||||
@@ -185,7 +194,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
// But for all normal pages, we should run our fuzzy match on them.
|
||||
lock (_listLock)
|
||||
{
|
||||
ApplyFilterUnderLock();
|
||||
RunFilteredItemsUpdate(ApplyFilterUnderLock);
|
||||
}
|
||||
|
||||
ItemsUpdated?.Invoke(this, new ItemsUpdatedEventArgs(true));
|
||||
@@ -502,14 +511,14 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
if (!_isDynamic)
|
||||
{
|
||||
// A static list? Great! Just run the filter.
|
||||
ApplyFilterUnderLock();
|
||||
RunFilteredItemsUpdate(ApplyFilterUnderLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A dynamic list? Even better! Just stick everything into
|
||||
// FilteredItems. The extension already did any filtering it cared about.
|
||||
var snapshot = Items.Where(i => !i.IsInErrorState).ToList();
|
||||
ListHelpers.InPlaceUpdateList(FilteredItems, snapshot);
|
||||
RunFilteredItemsUpdate(() => ListHelpers.InPlaceUpdateList(FilteredItems, snapshot));
|
||||
}
|
||||
|
||||
UpdateEmptyContent();
|
||||
@@ -572,6 +581,50 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
/// </summary>
|
||||
private void ApplyFilterUnderLock() => ListHelpers.InPlaceUpdateList(FilteredItems, FilterList(Items, SearchTextBox));
|
||||
|
||||
/// <summary>
|
||||
/// Executes an action that mutates <see cref="FilteredItems"/> with a
|
||||
/// reentrancy guard. WinUI3's native XAML renderer can pump the
|
||||
/// message loop while processing a <c>CollectionChanged</c>
|
||||
/// notification, which allows a second queued UI-thread task to begin
|
||||
/// mutating the same collection before the first task finishes. This
|
||||
/// causes heap corruption inside the native ItemsRepeater / ListView
|
||||
/// and manifests as an access-violation in ntdll.dll.
|
||||
///
|
||||
/// The guard detects reentrancy (same UI thread re-entering) and
|
||||
/// stores only the <em>latest</em> pending action. Once the
|
||||
/// in-flight mutation completes, the pending action (if any) executes
|
||||
/// immediately, ensuring the UI always converges to the newest state
|
||||
/// without overlapping mutations.
|
||||
/// </summary>
|
||||
private void RunFilteredItemsUpdate(Action updateAction)
|
||||
{
|
||||
if (_isUpdatingFilteredItems)
|
||||
{
|
||||
// Reentrant call — store only the latest; earlier stale
|
||||
// updates are intentionally dropped.
|
||||
_pendingFilteredItemsUpdate = updateAction;
|
||||
return;
|
||||
}
|
||||
|
||||
_isUpdatingFilteredItems = true;
|
||||
try
|
||||
{
|
||||
updateAction();
|
||||
|
||||
// Drain any update that was enqueued while we were running.
|
||||
while (_pendingFilteredItemsUpdate is not null)
|
||||
{
|
||||
var pending = _pendingFilteredItemsUpdate;
|
||||
_pendingFilteredItemsUpdate = null;
|
||||
pending();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdatingFilteredItems = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<IListItem, ListItemViewModel> ReadVmCache() => Volatile.Read(ref _vmCache);
|
||||
|
||||
private static bool IsCurrentThreadUiThread()
|
||||
@@ -1068,12 +1121,15 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
|
||||
Items.Clear();
|
||||
foreach (var item in FilteredItems)
|
||||
RunFilteredItemsUpdate(() =>
|
||||
{
|
||||
item.SafeCleanup();
|
||||
}
|
||||
foreach (var item in FilteredItems)
|
||||
{
|
||||
item.SafeCleanup();
|
||||
}
|
||||
|
||||
FilteredItems.Clear();
|
||||
FilteredItems.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
PublishVmCache(new(VmCacheComparer));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
@@ -18,12 +18,24 @@ public record ProviderSettings
|
||||
|
||||
public bool IsEnabled { get; init; } = true;
|
||||
|
||||
public ImmutableDictionary<string, FallbackSettings> FallbackCommands { get; init; }
|
||||
private ImmutableDictionary<string, FallbackSettings>? _fallbackCommands
|
||||
= ImmutableDictionary<string, FallbackSettings>.Empty;
|
||||
|
||||
public ImmutableList<string> PinnedCommandIds { get; init; }
|
||||
public ImmutableDictionary<string, FallbackSettings> FallbackCommands
|
||||
{
|
||||
get => _fallbackCommands ?? ImmutableDictionary<string, FallbackSettings>.Empty;
|
||||
init => _fallbackCommands = value;
|
||||
}
|
||||
|
||||
private ImmutableList<string>? _pinnedCommandIds
|
||||
= ImmutableList<string>.Empty;
|
||||
|
||||
public ImmutableList<string> PinnedCommandIds
|
||||
{
|
||||
get => _pinnedCommandIds ?? ImmutableList<string>.Empty;
|
||||
init => _pinnedCommandIds = value;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string ProviderId { get; init; } = string.Empty;
|
||||
|
||||
@@ -37,7 +49,6 @@ public record ProviderSettings
|
||||
{
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
public ProviderSettings(bool isEnabled)
|
||||
{
|
||||
IsEnabled = isEnabled;
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public record RecentCommandsManager : IRecentCommandsManager
|
||||
{
|
||||
[JsonInclude]
|
||||
internal ImmutableList<HistoryItem> History { get; init; } = ImmutableList<HistoryItem>.Empty;
|
||||
private ImmutableList<HistoryItem>? _history = ImmutableList<HistoryItem>.Empty;
|
||||
|
||||
internal ImmutableList<HistoryItem> History
|
||||
{
|
||||
get => _history ?? ImmutableList<HistoryItem>.Empty;
|
||||
init => _history = value;
|
||||
}
|
||||
|
||||
public RecentCommandsManager()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Windows.UI;
|
||||
|
||||
@@ -44,7 +45,7 @@ public record DockSettings
|
||||
public string? BackgroundImagePath { get; init; }
|
||||
|
||||
// </Theme settings>
|
||||
public ImmutableList<DockBandSettings> StartBands { get; init; } = ImmutableList.Create(
|
||||
private ImmutableList<DockBandSettings>? _startBands = ImmutableList.Create(
|
||||
new DockBandSettings
|
||||
{
|
||||
ProviderId = "com.microsoft.cmdpal.builtin.core",
|
||||
@@ -54,12 +55,24 @@ public record DockSettings
|
||||
{
|
||||
ProviderId = "WinGet",
|
||||
CommandId = "com.microsoft.cmdpal.winget",
|
||||
ShowLabels = false,
|
||||
ShowTitles = false,
|
||||
});
|
||||
|
||||
public ImmutableList<DockBandSettings> CenterBands { get; init; } = ImmutableList<DockBandSettings>.Empty;
|
||||
public ImmutableList<DockBandSettings> StartBands
|
||||
{
|
||||
get => _startBands ?? ImmutableList<DockBandSettings>.Empty;
|
||||
init => _startBands = value;
|
||||
}
|
||||
|
||||
public ImmutableList<DockBandSettings> EndBands { get; init; } = ImmutableList.Create(
|
||||
private ImmutableList<DockBandSettings>? _centerBands = ImmutableList<DockBandSettings>.Empty;
|
||||
|
||||
public ImmutableList<DockBandSettings> CenterBands
|
||||
{
|
||||
get => _centerBands ?? ImmutableList<DockBandSettings>.Empty;
|
||||
init => _centerBands = value;
|
||||
}
|
||||
|
||||
private ImmutableList<DockBandSettings>? _endBands = ImmutableList.Create(
|
||||
new DockBandSettings
|
||||
{
|
||||
ProviderId = "PerformanceMonitor",
|
||||
@@ -71,6 +84,12 @@ public record DockSettings
|
||||
CommandId = "com.microsoft.cmdpal.timedate.dockBand",
|
||||
});
|
||||
|
||||
public ImmutableList<DockBandSettings> EndBands
|
||||
{
|
||||
get => _endBands ?? ImmutableList<DockBandSettings>.Empty;
|
||||
init => _endBands = value;
|
||||
}
|
||||
|
||||
public bool ShowLabels { get; init; } = true;
|
||||
|
||||
[JsonIgnore]
|
||||
@@ -102,16 +121,6 @@ public record DockBandSettings
|
||||
/// </summary>
|
||||
public bool? ShowSubtitles { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value for backward compatibility. Maps to ShowTitles.
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public bool? ShowLabels
|
||||
{
|
||||
get => ShowTitles;
|
||||
init => ShowTitles = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the effective value of <see cref="ShowTitles"/> for this band.
|
||||
/// If this band doesn't have a specific value set, we'll fall back to the
|
||||
@@ -135,12 +144,52 @@ public enum DockSide
|
||||
Bottom = 3,
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(DockSizeJsonConverter))]
|
||||
public enum DockSize
|
||||
{
|
||||
Default,
|
||||
Compact,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom converter for <see cref="DockSize"/> that preserves backward
|
||||
/// compatibility with previously-persisted values. Earlier builds shipped a
|
||||
/// <c>Small</c>/<c>Medium</c>/<c>Large</c> enum; those values are migrated to
|
||||
/// <see cref="DockSize.Default"/> so existing settings.json files continue to
|
||||
/// load instead of failing the entire deserialization.
|
||||
/// </summary>
|
||||
internal sealed class DockSizeJsonConverter : JsonConverter<DockSize>
|
||||
{
|
||||
public override DockSize Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
var value = reader.GetString();
|
||||
if (Enum.TryParse<DockSize>(value, ignoreCase: true, out var parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// Legacy values from the original Small/Medium/Large enum, or any
|
||||
// other unknown string — fall back to Default so the user's
|
||||
// settings file remains loadable after upgrading.
|
||||
return DockSize.Default;
|
||||
}
|
||||
|
||||
if (reader.TokenType == JsonTokenType.Number && reader.TryGetInt32(out var number))
|
||||
{
|
||||
return Enum.IsDefined(typeof(DockSize), number) ? (DockSize)number : DockSize.Default;
|
||||
}
|
||||
|
||||
return DockSize.Default;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, DockSize value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public enum DockBackdrop
|
||||
{
|
||||
Transparent,
|
||||
|
||||
@@ -55,8 +55,14 @@ public record HotkeySettings// : ICmdLineRepresentable
|
||||
|
||||
// This is currently needed for FancyZones, we need to unify these two objects
|
||||
// see src\common\settings_objects.h
|
||||
private string? _key = string.Empty;
|
||||
|
||||
[JsonPropertyName("key")]
|
||||
public string Key { get; init; } = string.Empty;
|
||||
public string Key
|
||||
{
|
||||
get => _key ?? string.Empty;
|
||||
init => _key = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -39,17 +39,41 @@ public record SettingsModel
|
||||
|
||||
public bool AllowExternalReload { get; init; }
|
||||
|
||||
public ImmutableDictionary<string, ProviderSettings> ProviderSettings { get; init; }
|
||||
private ImmutableDictionary<string, ProviderSettings>? _providerSettings
|
||||
= ImmutableDictionary<string, ProviderSettings>.Empty;
|
||||
|
||||
public string[] FallbackRanks { get; init; } = [];
|
||||
public ImmutableDictionary<string, ProviderSettings> ProviderSettings
|
||||
{
|
||||
get => _providerSettings ?? ImmutableDictionary<string, ProviderSettings>.Empty;
|
||||
init => _providerSettings = value;
|
||||
}
|
||||
|
||||
public ImmutableDictionary<string, CommandAlias> Aliases { get; init; }
|
||||
private string[]? _fallbackRanks = [];
|
||||
|
||||
public string[] FallbackRanks
|
||||
{
|
||||
get => _fallbackRanks ?? [];
|
||||
init => _fallbackRanks = value;
|
||||
}
|
||||
|
||||
private ImmutableDictionary<string, CommandAlias>? _aliases
|
||||
= ImmutableDictionary<string, CommandAlias>.Empty;
|
||||
|
||||
public ImmutableList<TopLevelHotkey> CommandHotkeys { get; init; }
|
||||
public ImmutableDictionary<string, CommandAlias> Aliases
|
||||
{
|
||||
get => _aliases ?? ImmutableDictionary<string, CommandAlias>.Empty;
|
||||
init => _aliases = value;
|
||||
}
|
||||
|
||||
private ImmutableList<TopLevelHotkey>? _commandHotkeys
|
||||
= ImmutableList<TopLevelHotkey>.Empty;
|
||||
|
||||
public ImmutableList<TopLevelHotkey> CommandHotkeys
|
||||
{
|
||||
get => _commandHotkeys ?? ImmutableList<TopLevelHotkey>.Empty;
|
||||
init => _commandHotkeys = value;
|
||||
}
|
||||
|
||||
public MonitorBehavior SummonOn { get; init; } = MonitorBehavior.ToMouse;
|
||||
|
||||
public bool DisableAnimations { get; init; } = true;
|
||||
@@ -62,7 +86,13 @@ public record SettingsModel
|
||||
|
||||
public bool EnableDock { get; init; }
|
||||
|
||||
public DockSettings DockSettings { get; init; } = new();
|
||||
private DockSettings? _dockSettings = new();
|
||||
|
||||
public DockSettings DockSettings
|
||||
{
|
||||
get => _dockSettings ?? new();
|
||||
init => _dockSettings = value;
|
||||
}
|
||||
|
||||
// Theme settings
|
||||
public UserTheme Theme { get; init; } = UserTheme.Default;
|
||||
|
||||
@@ -196,7 +196,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
||||
{
|
||||
ProviderId = this.CommandProviderId,
|
||||
CommandId = this.Id,
|
||||
ShowLabels = true,
|
||||
ShowTitles = true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem, IEx
|
||||
IsFallback = topLevelType == TopLevelType.Fallback;
|
||||
IsDockBand = topLevelType == TopLevelType.DockBand;
|
||||
ExtensionHost = extensionHost;
|
||||
if (IsFallback && commandItem is FallbackCommandItem fallback)
|
||||
if (IsFallback && commandItem is IFallbackCommandItem2 fallback)
|
||||
{
|
||||
_fallbackId = fallback.Id;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,10 @@ public sealed partial class DockWindow : WindowEx,
|
||||
overlappedPresenter.IsResizable = false;
|
||||
}
|
||||
|
||||
// immediately when we're created: make sure to remove our window frame
|
||||
// and shadow. We don't _always_ get an Activated when we're first
|
||||
// created.
|
||||
UpdateWindowFrame();
|
||||
this.Activated += DockWindow_Activated;
|
||||
|
||||
WeakReferenceMessenger.Default.Register<BringToTopMessage>(this);
|
||||
@@ -144,6 +148,12 @@ public sealed partial class DockWindow : WindowEx,
|
||||
}
|
||||
|
||||
private void DockWindow_Activated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
UpdateWindowFrame();
|
||||
UpdateTopmostState();
|
||||
}
|
||||
|
||||
private void UpdateWindowFrame()
|
||||
{
|
||||
// These are used for removing the very subtle shadow/border that we get from Windows 11
|
||||
HwndExtensions.ToggleWindowStyle(_hwnd, false, WindowStyle.TiledWindow);
|
||||
@@ -152,8 +162,6 @@ public sealed partial class DockWindow : WindowEx,
|
||||
BOOL value = false;
|
||||
PInvoke.DwmSetWindowAttribute(_hwnd, DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, &value, (uint)sizeof(BOOL));
|
||||
}
|
||||
|
||||
UpdateTopmostState();
|
||||
}
|
||||
|
||||
private HWND GetWindowHandle(Window window)
|
||||
|
||||
@@ -25,7 +25,7 @@ public class IndexerTests : CommandPaletteTestBase
|
||||
public IndexerTests()
|
||||
: base()
|
||||
{
|
||||
// create a empty file in Downloads folder
|
||||
// create an empty file in Downloads folder
|
||||
// to ensure that the indexer has something to search for
|
||||
var downloadsPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\Downloads";
|
||||
var emptyFilePath = System.IO.Path.Combine(downloadsPath, TestFileName);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2025</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>9</VersionMinor>
|
||||
<VersionMinor>10</VersionMinor>
|
||||
<VersionInfoProductName>Microsoft Command Palette</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -14,7 +14,7 @@ public class PinStateChangedEventArgs : EventArgs
|
||||
public string AppIdentifier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the specified app identifier was pinned or not.
|
||||
/// Gets a value indicating whether or not the specified app identifier was pinned.
|
||||
/// </summary>
|
||||
public bool IsPinned { get; }
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ internal static class SearchCatalogStatusReader
|
||||
try
|
||||
{
|
||||
var catalogManager = CreateCatalogManager();
|
||||
var pendingItemsCount = catalogManager.NumberOfItemsToIndex();
|
||||
catalogManager.NumberOfItemsToIndex(out var pendingItemsCount, out _, out _);
|
||||
ResetFailureLoggingState();
|
||||
return new SearchCatalogStatus(pendingItemsCount, null);
|
||||
}
|
||||
|
||||
@@ -30,17 +30,17 @@ public partial interface ISearchCatalogManager
|
||||
|
||||
void ReindexSearchRoot(string pszRoot);
|
||||
|
||||
uint get_ConnectTimeout();
|
||||
|
||||
void put_ConnectTimeout(uint dwTimeout);
|
||||
|
||||
uint get_DataTimeout();
|
||||
uint get_ConnectTimeout();
|
||||
|
||||
void put_DataTimeout(uint dwTimeout);
|
||||
|
||||
uint get_DataTimeout();
|
||||
|
||||
uint NumberOfItems();
|
||||
|
||||
uint NumberOfItemsToIndex();
|
||||
void NumberOfItemsToIndex(out uint plIncrementalCount, out uint plNotificationQueue, out uint plHighPriorityQueue);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.LPWStr)]
|
||||
string URLBeingIndexed();
|
||||
@@ -51,16 +51,20 @@ public partial interface ISearchCatalogManager
|
||||
|
||||
void RegisterViewForNotification(string pszView, IntPtr pViewNotify, out uint pdwCookie);
|
||||
|
||||
IntPtr GetItemsChangedSink();
|
||||
void GetItemsChangedSink(
|
||||
IntPtr pISearchNotifyInlineSite,
|
||||
in Guid riid,
|
||||
out IntPtr ppv,
|
||||
out Guid pGUIDCatalogResetSignature,
|
||||
out Guid pGUIDCheckPointSignature,
|
||||
out uint pdwLastCheckPointNumber);
|
||||
|
||||
void UnregisterViewForNotification(uint dwCookie);
|
||||
|
||||
void SetExtensionClusion(string pszExtension, [MarshalAs(UnmanagedType.Bool)] bool fExclude);
|
||||
|
||||
void EnumerateExcludedExtensions();
|
||||
IntPtr EnumerateExcludedExtensions();
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
ISearchQueryHelper GetQueryHelper();
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace PowerToysExtension.Helpers;
|
||||
internal static class PowerToysResourcesHelper
|
||||
{
|
||||
private const string AssetsRoot = "Assets\\";
|
||||
private const string SettingsIconRoot = "WinUI3Apps\\Assets\\Settings\\Icons\\";
|
||||
private const string SettingsIconRoot = "Assets\\Settings\\Icons\\";
|
||||
|
||||
internal static IconInfo IconFromSettingsIcon(string fileName) => IconHelpers.FromRelativePath($"{SettingsIconRoot}{fileName}");
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<EnableMsixTooling>false</EnableMsixTooling>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<GenerateAppxPackageOnBuild>false</GenerateAppxPackageOnBuild>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\</OutputPath>
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<Nullable>enable</Nullable>
|
||||
@@ -27,11 +27,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\..\..\settings-ui\Settings.UI\Assets\Settings\Icons\*.png" Link="WinUI3Apps\Assets\Settings\Icons\%(Filename)%(Extension)">
|
||||
<Content Include="..\..\..\..\settings-ui\Settings.UI\Assets\Settings\Icons\*.png" Link="Assets\Settings\Icons\%(Filename)%(Extension)">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<!-- Monochrome icons from PowerToys Run Plugin for debug mode differentiation -->
|
||||
<Content Include="..\..\..\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.PowerToys\Images\PowerToys.dark.png" Link="WinUI3Apps\Assets\Settings\Icons\PowerToys.dark.png">
|
||||
<Content Include="..\..\..\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.PowerToys\Images\PowerToys.dark.png" Link="Assets\Settings\Icons\PowerToys.dark.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -15,7 +15,7 @@ internal static class KeyName
|
||||
internal const string FirstPart = "HKEY";
|
||||
|
||||
/// <summary>
|
||||
/// The first name part of each base key follow by a underscore
|
||||
/// The first name part of each base key follow by an underscore
|
||||
/// </summary>
|
||||
internal const string FirstPartUnderscore = "HKEY_";
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ internal sealed partial class ShellListPage : DynamicListPage, IDisposable
|
||||
|
||||
internal static ListItem CreateExeItem(string exe, string args, string fullExePath, Action<string>? addToHistory, ITelemetryService? telemetryService)
|
||||
{
|
||||
// PathToListItem will return a RunExeItem if it can find a executable.
|
||||
// PathToListItem will return a RunExeItem if it can find an executable.
|
||||
// It will ALSO add the file search commands to the RunExeItem.
|
||||
return PathToListItem(fullExePath, exe, args, addToHistory, telemetryService);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ internal static class WinGetStatics
|
||||
createCompositePackageCatalogOptions.Catalogs.Add(_wingetCatalog);
|
||||
}
|
||||
|
||||
// Searches only the catalogs provided, but will correlated with installed items
|
||||
// Searches only the catalogs provided, but will be correlated with installed items
|
||||
createCompositePackageCatalogOptions.CompositeSearchBehavior = CompositeSearchBehavior.RemotePackagesFromAllCatalogs;
|
||||
|
||||
var catalogRef = WinGetStatics.Manager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions);
|
||||
|
||||
@@ -231,7 +231,7 @@ internal sealed class WindowProcess
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean value indicating whether the access to a process using the AllAccess flag is denied or not.
|
||||
/// Gets a boolean value indicating whether or not the access to a process using the AllAccess flag is denied.
|
||||
/// </summary>
|
||||
/// <param name="pid">The process ID of the process</param>
|
||||
/// <returns>True if denied and false if not.</returns>
|
||||
|
||||
@@ -49,7 +49,7 @@ internal sealed class WindowsSetting
|
||||
public IEnumerable<string>? AltNames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a additional note of this settings.
|
||||
/// Gets or sets an additional note of this settings.
|
||||
/// <para>(e.g. why is not supported on your system)</para>
|
||||
/// </summary>
|
||||
public string? Note { get; set; }
|
||||
|
||||
@@ -58,7 +58,7 @@ internal static class UnsupportedSettingsHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a unsigned numeric value from given registry value name inside the given registry key.
|
||||
/// Return an unsigned numeric value from given registry value name inside the given registry key.
|
||||
/// </summary>
|
||||
/// <param name="registryKey">The registry key.</param>
|
||||
/// <param name="valueName">The name of the registry value.</param>
|
||||
|
||||
@@ -878,7 +878,7 @@
|
||||
</data>
|
||||
<data name="Id" xml:space="preserve">
|
||||
<value>ID</value>
|
||||
<comment>MEans The "Windows Identifier"</comment>
|
||||
<comment>Means The "Windows Identifier"</comment>
|
||||
</data>
|
||||
<data name="Image" xml:space="preserve">
|
||||
<value>Image</value>
|
||||
|
||||
@@ -16,15 +16,15 @@ namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands;
|
||||
|
||||
internal sealed partial class LaunchProfileCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _id;
|
||||
private readonly string _appUserModelId;
|
||||
private readonly string _profile;
|
||||
private readonly bool _openNewTab;
|
||||
private readonly bool _openQuake;
|
||||
private readonly AppSettingsManager _appSettingsManager;
|
||||
|
||||
internal LaunchProfileCommand(string id, string profile, string iconPath, bool openNewTab, bool openQuake, AppSettingsManager appSettingsManager)
|
||||
internal LaunchProfileCommand(string appUserModelId, string profile, string iconPath, bool openNewTab, bool openQuake, AppSettingsManager appSettingsManager)
|
||||
{
|
||||
this._id = id;
|
||||
this._appUserModelId = appUserModelId;
|
||||
this._profile = profile;
|
||||
this._openNewTab = openNewTab;
|
||||
this._openQuake = openQuake;
|
||||
@@ -32,9 +32,12 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand
|
||||
|
||||
this.Name = Resources.launch_profile;
|
||||
this.Icon = new IconInfo(iconPath);
|
||||
this.Id = MakeId(appUserModelId, profile);
|
||||
}
|
||||
|
||||
private void Launch(string id, string profile)
|
||||
internal static string MakeId(string appUserModelId, string profileName) => $"terminal/{appUserModelId}/{profileName}";
|
||||
|
||||
private void Launch(string appUserModelId, string profile)
|
||||
{
|
||||
IApplicationActivationManager appManager;
|
||||
|
||||
@@ -52,7 +55,7 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand
|
||||
var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake);
|
||||
try
|
||||
{
|
||||
appManager.ActivateApplication(id, queryArguments, noFlags, out var unusedPid);
|
||||
appManager.ActivateApplication(appUserModelId, queryArguments, noFlags, out var unusedPid);
|
||||
}
|
||||
#pragma warning disable IDE0059, CS0168
|
||||
catch (Exception ex)
|
||||
@@ -67,7 +70,7 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand
|
||||
|
||||
try
|
||||
{
|
||||
_appSettingsManager.Current.AddRecentlyUsedProfile(id, profile);
|
||||
_appSettingsManager.Current.AddRecentlyUsedProfile(appUserModelId, profile);
|
||||
_appSettingsManager.Save();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -82,7 +85,7 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand
|
||||
{
|
||||
try
|
||||
{
|
||||
Launch(_id, _profile);
|
||||
Launch(_appUserModelId, _profile);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
|
||||
@@ -92,11 +95,100 @@ public static class TerminalHelper
|
||||
var hidden = (hiddenElement.ValueKind == JsonValueKind.False || hiddenElement.ValueKind == JsonValueKind.True) && hiddenElement.GetBoolean();
|
||||
|
||||
profileElement.TryGetProperty("guid", out var guidElement);
|
||||
var guid = guidElement.ValueKind == JsonValueKind.String ? Guid.Parse(guidElement.GetString()) : null as Guid?;
|
||||
Guid? guid = null;
|
||||
if (guidElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var guidString = guidElement.GetString();
|
||||
if (!string.IsNullOrWhiteSpace(guidString) && Guid.TryParse(guidString, out var parsedGuid))
|
||||
{
|
||||
guid = parsedGuid;
|
||||
}
|
||||
}
|
||||
|
||||
profileElement.TryGetProperty("icon", out var iconElement);
|
||||
var icon = iconElement.ValueKind == JsonValueKind.String ? iconElement.GetString() : null;
|
||||
|
||||
return new TerminalProfile(terminal, name, guid, hidden, icon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve a profile icon to a usable path. For built-in profiles without
|
||||
/// an explicit icon, looks up the GUID-based icon in the Terminal package's
|
||||
/// ProfileIcons folder. Handles ms-appx:/// URIs by mapping them to the
|
||||
/// package install directory. Passes through file paths and font glyphs
|
||||
/// as-is. Falls back to the terminal package logo as a last resort.
|
||||
/// </summary>
|
||||
public static string ResolveProfileIcon(TerminalProfile profile)
|
||||
{
|
||||
var icon = profile.Icon;
|
||||
|
||||
if (string.IsNullOrEmpty(icon))
|
||||
{
|
||||
// Built-in profiles don't have an icon in settings.json.
|
||||
// Their icons are stored by GUID in the Terminal package.
|
||||
if (profile.Identifier.HasValue)
|
||||
{
|
||||
var guidIcon = TryResolveGuidIcon(profile.Terminal.InstallPath, profile.Identifier.Value);
|
||||
if (guidIcon is not null)
|
||||
{
|
||||
return guidIcon;
|
||||
}
|
||||
}
|
||||
|
||||
return profile.Terminal.LogoPath;
|
||||
}
|
||||
|
||||
if (icon.StartsWith("ms-appx:///", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ResolveAppxPath(profile.Terminal.InstallPath, icon) ?? profile.Terminal.LogoPath;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static string? TryResolveGuidIcon(string installPath, Guid guid)
|
||||
{
|
||||
var profileIconsDir = Path.Combine(installPath, "ProfileIcons");
|
||||
if (!Directory.Exists(profileIconsDir))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var guidStr = guid.ToString("B");
|
||||
foreach (var scale in new[] { ".scale-200", ".scale-150", ".scale-100" })
|
||||
{
|
||||
var path = Path.Combine(profileIconsDir, $"{guidStr}{scale}.png");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? ResolveAppxPath(string installPath, string msAppxUri)
|
||||
{
|
||||
var relativePath = msAppxUri.Substring("ms-appx:///".Length).Replace('/', '\\');
|
||||
var resolved = Path.Combine(installPath, relativePath);
|
||||
if (File.Exists(resolved))
|
||||
{
|
||||
return resolved;
|
||||
}
|
||||
|
||||
var dir = Path.GetDirectoryName(resolved);
|
||||
var name = Path.GetFileNameWithoutExtension(resolved);
|
||||
var ext = Path.GetExtension(resolved);
|
||||
|
||||
foreach (var scale in new[] { ".scale-200", ".scale-150", ".scale-100" })
|
||||
{
|
||||
var scaled = Path.Combine(dir!, $"{name}{scale}{ext}");
|
||||
if (File.Exists(scaled))
|
||||
{
|
||||
return scaled;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class TerminalQuery : ITerminalQuery
|
||||
var aumid = appListEntries.Single().AppUserModelId;
|
||||
var version = new Version(p.Id.Version.Major, p.Id.Version.Minor, p.Id.Version.Build, p.Id.Version.Revision);
|
||||
var settingsPath = Path.Combine(localAppDataPath, "Packages", p.Id.FamilyName, "LocalState", "settings.json");
|
||||
yield return new TerminalPackage(aumid, version, p.DisplayName, settingsPath, p.Logo.LocalPath);
|
||||
yield return new TerminalPackage(aumid, version, p.DisplayName, settingsPath, p.Logo.LocalPath, p.InstalledPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +105,13 @@ internal sealed partial class ProfilesListPage : ListPage, INotifyItemsChanged
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(new ListItem(new LaunchProfileCommand(profile.Terminal.AppUserModelId, profile.Name, profile.Terminal.LogoPath, openNewTab, openQuake, _appSettingsManager))
|
||||
var iconPath = TerminalHelper.ResolveProfileIcon(profile);
|
||||
|
||||
result.Add(new ListItem(new LaunchProfileCommand(profile.Terminal.AppUserModelId, profile.Name, iconPath, openNewTab, openQuake, _appSettingsManager))
|
||||
{
|
||||
Title = profile.Name,
|
||||
Subtitle = profile.Terminal.DisplayName,
|
||||
Icon = new IconInfo(iconPath),
|
||||
MoreCommands = [
|
||||
new CommandContextItem(new LaunchProfileAsAdminCommand(profile.Terminal.AppUserModelId, profile.Name, openNewTab, openQuake, _appSettingsManager)),
|
||||
],
|
||||
|
||||
@@ -21,12 +21,15 @@ public class TerminalPackage
|
||||
|
||||
public string LogoPath { get; }
|
||||
|
||||
public TerminalPackage(string appUserModelId, Version version, string displayName, string settingsPath, string logoPath)
|
||||
public string InstallPath { get; }
|
||||
|
||||
public TerminalPackage(string appUserModelId, Version version, string displayName, string settingsPath, string logoPath, string installPath)
|
||||
{
|
||||
AppUserModelId = appUserModelId;
|
||||
Version = version;
|
||||
DisplayName = displayName;
|
||||
SettingsPath = settingsPath;
|
||||
LogoPath = logoPath;
|
||||
InstallPath = installPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
|
||||
using Microsoft.CmdPal.Ext.WindowsTerminal.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
@@ -31,4 +33,18 @@ public partial class WindowsTerminalCommandsProvider : CommandProvider
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() => [_terminalCommand];
|
||||
|
||||
public override ICommandItem? GetCommandItem(string id)
|
||||
{
|
||||
var items = _terminalCommand.Command is Pages.ProfilesListPage page ? page.GetItems() : [];
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Command.Id == id)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ internal sealed partial class SampleListPage : ListPage
|
||||
|
||||
public ListItemChangingCommandInTime()
|
||||
{
|
||||
Subtitle = "I change my command every 10 seconds, and the command changes it's icon every 2 seconds";
|
||||
Subtitle = "I change my command every 10 seconds, and the command changes its icon every 2 seconds";
|
||||
var timer = new Timer(OnTimerElapsed, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
|
||||
this.Command = _commands[0];
|
||||
}
|
||||
|
||||
@@ -642,10 +642,10 @@ public static class FuzzyStringMatcher
|
||||
// Text folding
|
||||
// ============================================================
|
||||
|
||||
// Folding: slash normalization + upper case + optional diacritics stripping
|
||||
// Folding: slash normalization + uppercase + optional diacritics stripping
|
||||
internal static class Folding
|
||||
{
|
||||
// Cache maps an upper case char to its diacritics-stripped upper case char.
|
||||
// Cache maps an uppercase char to its diacritics-stripped uppercase char.
|
||||
// '\0' means "not cached yet".
|
||||
private static readonly char[] StripCacheUpper = new char[char.MaxValue + 1];
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace BufferValidationHelpers
|
||||
std::get<Shortcut>(tempShortcut).SetKeyCodes(selectedCodes);
|
||||
}
|
||||
|
||||
// Convert app name to lower case
|
||||
// Convert app name to lowercase
|
||||
std::transform(appName.begin(), appName.end(), appName.begin(), towlower);
|
||||
std::wstring lowercaseDefAppName = KeyboardManagerEditorStrings::DefaultAppName();
|
||||
std::transform(lowercaseDefAppName.begin(), lowercaseDefAppName.end(), lowercaseDefAppName.begin(), towlower);
|
||||
|
||||
@@ -1569,7 +1569,7 @@ namespace KeyboardEventHandlers
|
||||
|
||||
if (hwnd == GetForegroundWindow())
|
||||
{
|
||||
// only hide if this was a call from a already open program, don't make small if we just opened it.
|
||||
// only hide if this was a call from an already open program, don't make small if we just opened it.
|
||||
if (!isNewProcess && minimizeIfVisible)
|
||||
{
|
||||
Logger::trace(L"ChordKeyboardHandler:{}, got GetForegroundWindow, doing SW_MINIMIZE", programName);
|
||||
@@ -1723,7 +1723,7 @@ namespace KeyboardEventHandlers
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert process name to lower case
|
||||
// Convert process name to lowercase
|
||||
std::transform(process_name.begin(), process_name.end(), process_name.begin(), towlower);
|
||||
|
||||
std::wstring query_string;
|
||||
|
||||
@@ -93,7 +93,7 @@ bool MappingConfiguration::AddSingleKeyToTextRemap(const DWORD originalKey, cons
|
||||
// Function to add a new App specific shortcut remapping
|
||||
bool MappingConfiguration::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutTextUnion& newSC)
|
||||
{
|
||||
// Convert app name to lower case
|
||||
// Convert app name to lowercase
|
||||
std::wstring process_name;
|
||||
process_name.resize(app.length());
|
||||
std::transform(app.begin(), app.end(), process_name.begin(), towlower);
|
||||
|
||||
@@ -156,6 +156,12 @@ public:
|
||||
return powertoys_gpo::getConfiguredPowerLauncherEnabledValue();
|
||||
}
|
||||
|
||||
// Returns whether the PowerToys should be enabled by default
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return JSON with the configuration options.
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
{
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramLogger.Exception($"|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version {FullName} from location {Location} is returned.", new FormatException(), GetType(), Location);
|
||||
ProgramLogger.Exception($"|Trying to get the package version of the UWP program, but an unknown UWP appmanifest version {FullName} from location {Location} is returned.", new FormatException(), GetType(), Location);
|
||||
|
||||
Version = PackageVersion.Unknown;
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean value indicating whether the access to a process using the AllAccess flag is denied or not.
|
||||
/// Gets a boolean value indicating whether or not the access to a process using the AllAccess flag is denied.
|
||||
/// </summary>
|
||||
/// <param name="pid">The process ID of the process</param>
|
||||
/// <returns>True if denied and false if not.</returns>
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
|
||||
internal bool HideKillProcessOnElevatedProcesses { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether we show the explorer settings info or not.
|
||||
/// Gets a value indicating whether or not we show the explorer settings info.
|
||||
/// </summary>
|
||||
internal bool HideExplorerSettingInfo { get; private set; }
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Registry.Constants
|
||||
internal const string FirstPart = "HKEY";
|
||||
|
||||
/// <summary>
|
||||
/// The first name part of each base key follow by a underscore
|
||||
/// The first name part of each base key follow by an underscore
|
||||
/// </summary>
|
||||
internal const string FirstPartUnderscore = "HKEY_";
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System
|
||||
IsBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi;
|
||||
|
||||
// Log info if the system hasn't boot in uefi mode.
|
||||
// (Because this is only going into the log we can ignore the fact that normally UEFI and BIOS are written upper case. No need to convert the enumeration value to upper case.)
|
||||
// (Because this is only going into the log we can ignore the fact that normally UEFI and BIOS are written uppercase. No need to convert the enumeration value to uppercase.)
|
||||
if (!IsBootedInUefiMode)
|
||||
{
|
||||
Wox.Plugin.Logger.Log.Info($"The UEFI command will not show to the user. The system has not booted in UEFI mode or the system does not have an UEFI firmware! (Detected type: {Win32Helpers.GetSystemFirmwareType()})", typeof(Main));
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings
|
||||
public IEnumerable<string>? AltNames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a additional note of this settings.
|
||||
/// Gets or sets an additional note of this settings.
|
||||
/// <para>(e.g. why is not supported on your system)</para>
|
||||
/// </summary>
|
||||
public string? Note { get; set; }
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Helper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a unsigned numeric value from given registry value name inside the given registry key.
|
||||
/// Return an unsigned numeric value from given registry value name inside the given registry key.
|
||||
/// </summary>
|
||||
/// <param name="registryKey">The registry key.</param>
|
||||
/// <param name="valueName">The name of the registry value.</param>
|
||||
|
||||
@@ -878,7 +878,7 @@
|
||||
</data>
|
||||
<data name="Id" xml:space="preserve">
|
||||
<value>ID</value>
|
||||
<comment>MEans The "Windows Identifier"</comment>
|
||||
<comment>Means The "Windows Identifier"</comment>
|
||||
</data>
|
||||
<data name="Image" xml:space="preserve">
|
||||
<value>Image</value>
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace PowerLauncher.Helper
|
||||
string uVarKey = (string)uVar.Key;
|
||||
string uVarValue = (string)uVar.Value;
|
||||
|
||||
// The variable name of the path variable can be upper case, lower case ore mixed case. So we have to compare case-insensitive.
|
||||
// The variable name of the path variable can be uppercase, lowercase or mixed case. So we have to compare case-insensitive.
|
||||
if (!uVarKey.Equals(PathVariableName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
environment[uVarKey] = uVarValue;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Wox.Infrastructure.Storage
|
||||
{
|
||||
/// <summary>
|
||||
/// Save plugin settings/cache,
|
||||
/// todo should be merged into a abstract class instead of separate interface
|
||||
/// todo should be merged into an abstract class instead of separate interface
|
||||
/// </summary>
|
||||
public interface ISavable
|
||||
{
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Wox.Plugin
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
// This property is used in PT Run only to decide whether to updated the Disabled property or not.
|
||||
// This property is used in PT Run only to decide whether or not to updated the Disabled property.
|
||||
[JsonIgnore]
|
||||
public bool IsEnabledPolicyConfigured { get; set; }
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Wox.Plugin
|
||||
/// <summary>
|
||||
/// Gets search part of a query.
|
||||
/// This will not include action keyword if exclusive plugin gets it; otherwise, it should be same as RawQuery.
|
||||
/// Since we allow user to switch a exclusive plugin to generic plugin,
|
||||
/// Since we allow user to switch an exclusive plugin to generic plugin,
|
||||
/// so this property will always give you the "real" query part of the query
|
||||
/// </summary>
|
||||
public string Search
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
using Windows.Win32.Foundation;
|
||||
using static PowerDisplay.Common.Drivers.NativeConstants;
|
||||
using static PowerDisplay.Common.Drivers.PInvoke;
|
||||
@@ -27,32 +28,55 @@ namespace PowerDisplay.Common.Drivers.DDC
|
||||
{
|
||||
if (hPhysicalMonitor == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogWarning("DDC: Monitor ignored - null physical monitor handle");
|
||||
return DdcCiValidationResult.Invalid;
|
||||
}
|
||||
|
||||
var handleHex = $"0x{hPhysicalMonitor:X}";
|
||||
|
||||
try
|
||||
{
|
||||
// Try to get capabilities string (slow I2C operation)
|
||||
var capsString = TryGetCapabilitiesString(hPhysicalMonitor);
|
||||
if (string.IsNullOrEmpty(capsString))
|
||||
{
|
||||
Logger.LogWarning($"DDC: Monitor ignored (handle={handleHex}) - empty capabilities string from DDC/CI");
|
||||
return DdcCiValidationResult.Invalid;
|
||||
}
|
||||
|
||||
Logger.LogInfo($"DDC: Capabilities raw (handle={handleHex}, length={capsString.Length}): {capsString}");
|
||||
|
||||
// Parse the capabilities string
|
||||
var parseResult = Utils.MccsCapabilitiesParser.Parse(capsString);
|
||||
var capabilities = parseResult.Capabilities;
|
||||
|
||||
if (capabilities == null || capabilities.SupportedVcpCodes.Count == 0)
|
||||
{
|
||||
Logger.LogWarning($"DDC: Monitor ignored (handle={handleHex}) - parsed capabilities have no VCP codes (parseErrors={parseResult.Errors.Count})");
|
||||
return DdcCiValidationResult.Invalid;
|
||||
}
|
||||
|
||||
// Check if brightness (VCP 0x10) is supported - determines DDC/CI validity
|
||||
bool supportsBrightness = capabilities.SupportsVcpCode(NativeConstants.VcpCodeBrightness);
|
||||
bool supportsContrast = capabilities.SupportsVcpCode(NativeConstants.VcpCodeContrast);
|
||||
bool supportsColorTemperature = capabilities.SupportsVcpCode(NativeConstants.VcpCodeSelectColorPreset);
|
||||
bool supportsVolume = capabilities.SupportsVcpCode(NativeConstants.VcpCodeVolume);
|
||||
|
||||
Logger.LogInfo(
|
||||
$"DDC: Capabilities parsed (handle={handleHex}) - " +
|
||||
$"Brightness={supportsBrightness} Contrast={supportsContrast} " +
|
||||
$"ColorTemperature={supportsColorTemperature} Volume={supportsVolume}");
|
||||
|
||||
if (!supportsBrightness)
|
||||
{
|
||||
Logger.LogWarning($"DDC: Monitor ignored (handle={handleHex}) - brightness (VCP 0x10) not advertised in capabilities");
|
||||
}
|
||||
|
||||
return new DdcCiValidationResult(supportsBrightness, capsString, capabilities);
|
||||
}
|
||||
catch (Exception ex) when (ex is not OutOfMemoryException)
|
||||
{
|
||||
Logger.LogError($"DDC: Monitor ignored (handle={handleHex}) - exception during FetchCapabilities: {ex.Message}");
|
||||
return DdcCiValidationResult.Invalid;
|
||||
}
|
||||
}
|
||||
@@ -74,6 +98,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
||||
// Get capabilities string length
|
||||
if (!GetCapabilitiesStringLength(hPhysicalMonitor, out uint length) || length == 0)
|
||||
{
|
||||
Logger.LogWarning($"DDC: GetCapabilitiesStringLength failed (handle=0x{hPhysicalMonitor:X}, length={length})");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -83,6 +108,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
||||
{
|
||||
if (!CapabilitiesRequestAndCapabilitiesReply(hPhysicalMonitor, buffer, length))
|
||||
{
|
||||
Logger.LogWarning($"DDC: CapabilitiesRequestAndCapabilitiesReply failed (handle=0x{hPhysicalMonitor:X})");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,6 +121,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
||||
}
|
||||
catch (Exception ex) when (ex is not OutOfMemoryException)
|
||||
{
|
||||
Logger.LogError($"DDC: TryGetCapabilitiesString exception (handle=0x{hPhysicalMonitor:X}): {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -189,7 +216,7 @@ namespace PowerDisplay.Common.Drivers.DDC
|
||||
// EDID manufacturer ID requires byte order swap first
|
||||
manufacturerId = (ushort)(((manufacturerId & 0xff00) >> 8) | ((manufacturerId & 0x00ff) << 8));
|
||||
|
||||
// Extract 3 5-bit characters (each character is A-Z, where A=1, B=2, ..., Z=26)
|
||||
// Extract three 5-bit characters (each character is A-Z, where A=1, B=2, ..., Z=26)
|
||||
var char1 = (char)('A' - 1 + ((manufacturerId >> 0) & 0x1f));
|
||||
var char2 = (char)('A' - 1 + ((manufacturerId >> 5) & 0x1f));
|
||||
var char3 = (char)('A' - 1 + ((manufacturerId >> 10) & 0x1f));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user