mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-03 00:49:18 +02:00
Compare commits
3 Commits
copilot/up
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb34f71b62 | ||
|
|
6fdff7a10f | ||
|
|
b78fa62ba3 |
26
.github/actions/spell-check/allow/code.txt
vendored
26
.github/actions/spell-check/allow/code.txt
vendored
@@ -19,7 +19,6 @@ OLIVEGREEN
|
||||
PALEBLUE
|
||||
PArgb
|
||||
Pbgra
|
||||
SRGBTo
|
||||
WHITEONBLACK
|
||||
|
||||
|
||||
@@ -49,6 +48,7 @@ nupkg
|
||||
petabyte
|
||||
resw
|
||||
resx
|
||||
runtimeconfig
|
||||
srt
|
||||
Stereolithography
|
||||
terabyte
|
||||
@@ -332,7 +332,6 @@ REGSTR
|
||||
INVOKEIDLIST
|
||||
MEMORYSTATUSEX
|
||||
ABE
|
||||
Mdt
|
||||
HTCAPTION
|
||||
POSCHANGED
|
||||
QUERYPOS
|
||||
@@ -342,29 +341,6 @@ WINEVENTPROC
|
||||
WORKERW
|
||||
FULLSCREENAPP
|
||||
|
||||
# COM/WinRT interface prefixes and type fragments
|
||||
BAlt
|
||||
BShift
|
||||
Cmanifest
|
||||
Cmodule
|
||||
Cuuid
|
||||
Dng
|
||||
IApplication
|
||||
IDisposable
|
||||
IEnum
|
||||
IFolder
|
||||
IInitialize
|
||||
IMemory
|
||||
IOle
|
||||
ipreview
|
||||
IProperty
|
||||
IShell
|
||||
ithumbnail
|
||||
IVirtual
|
||||
|
||||
# Test frameworks
|
||||
MSTEST
|
||||
|
||||
# PowerRename metadata pattern abbreviations (used in tests and regex patterns)
|
||||
DDDD
|
||||
FFF
|
||||
|
||||
2
.github/actions/spell-check/allow/names.txt
vendored
2
.github/actions/spell-check/allow/names.txt
vendored
@@ -178,9 +178,7 @@ Taras
|
||||
TBM
|
||||
Teutsch
|
||||
tilovell
|
||||
traies
|
||||
Triet
|
||||
udit
|
||||
urnotdfs
|
||||
vednig
|
||||
waaverecords
|
||||
|
||||
4
.github/actions/spell-check/excludes.txt
vendored
4
.github/actions/spell-check/excludes.txt
vendored
@@ -107,7 +107,7 @@
|
||||
^src/common/sysinternals/Eula/
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
|
||||
^doc/devdocs/modules/cmdpal/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/doc/initial-sdk-spec/list-elements-mock-002\.pdn$
|
||||
^src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage\.cs$
|
||||
^src/modules/cmdpal/Microsoft\.CmdPal\.UI/Settings/InternalPage\.SampleData\.cs$
|
||||
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/.*\.TestData\.cs$
|
||||
@@ -140,6 +140,8 @@
|
||||
^tools/project_template/ModuleTemplate/resource\.h$
|
||||
^tools/Verification scripts/Check preview handler registration\.ps1$
|
||||
ignore$
|
||||
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
|
||||
^src/common/CalculatorEngineCommon/exprtk\.hpp$
|
||||
src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownImagesPage.cs
|
||||
^src/modules/powerrename/unittests/testdata/avif_test\.avif$
|
||||
^src/modules/powerrename/unittests/testdata/heif_test\.heic$
|
||||
|
||||
31
.github/actions/spell-check/expect.txt
vendored
31
.github/actions/spell-check/expect.txt
vendored
@@ -87,7 +87,6 @@ AUTOCHECKBOX
|
||||
AUTOHIDE
|
||||
AUTOHSCROLL
|
||||
AUTOMATIONPROPERTIES
|
||||
autopf
|
||||
AUTORADIOBUTTON
|
||||
Autorun
|
||||
AUTOTICKS
|
||||
@@ -242,7 +241,6 @@ CPower
|
||||
cpptools
|
||||
cppvsdbg
|
||||
cppwinrt
|
||||
createallsubdirs
|
||||
createdump
|
||||
CREATEPROCESS
|
||||
CREATESCHEDULEDTASK
|
||||
@@ -279,7 +277,6 @@ CYSMICON
|
||||
CYVIRTUALSCREEN
|
||||
Dac
|
||||
dacl
|
||||
DArchitectures
|
||||
datareader
|
||||
datatracker
|
||||
Dayof
|
||||
@@ -356,7 +353,6 @@ dlib
|
||||
dllhost
|
||||
dllmain
|
||||
Dmdo
|
||||
DMy
|
||||
DNLEN
|
||||
DONOTROUND
|
||||
DONTVALIDATEPATH
|
||||
@@ -476,7 +472,6 @@ FILEMUSTEXIST
|
||||
FILEOP
|
||||
FILEOPENDIALOGOPTIONS
|
||||
FILEOS
|
||||
filesandordirs
|
||||
FILESUBTYPE
|
||||
FILESYSPATH
|
||||
Filetime
|
||||
@@ -660,7 +655,6 @@ IEXPLORE
|
||||
IFACEMETHOD
|
||||
IFACEMETHODIMP
|
||||
IGNOREUNKNOWN
|
||||
ignoreversion
|
||||
IGo
|
||||
iid
|
||||
Iindex
|
||||
@@ -711,8 +705,6 @@ ipcmanager
|
||||
IPREVIEW
|
||||
irprops
|
||||
isbi
|
||||
ISCC
|
||||
isdl
|
||||
iss
|
||||
issecret
|
||||
ISSEPARATOR
|
||||
@@ -727,7 +719,6 @@ jobject
|
||||
JOBOBJECT
|
||||
jpe
|
||||
jpnime
|
||||
jrsoftware
|
||||
Jsons
|
||||
jsonval
|
||||
jxr
|
||||
@@ -948,8 +939,6 @@ muxc
|
||||
mvvm
|
||||
MVVMTK
|
||||
MWBEx
|
||||
mycompany
|
||||
myextension
|
||||
MYICON
|
||||
myorg
|
||||
myrepo
|
||||
@@ -1037,7 +1026,9 @@ NORMALDISPLAY
|
||||
NORMALUSER
|
||||
NOSEARCH
|
||||
NOSENDCHANGING
|
||||
NOSIZE
|
||||
notdefault
|
||||
Nosize
|
||||
NOTHOUSANDS
|
||||
NOTICKS
|
||||
NOTIFICATIONSDLL
|
||||
@@ -1269,7 +1260,6 @@ Quarternary
|
||||
QUERYENDSESSION
|
||||
QUERYOPEN
|
||||
QUEUESYNC
|
||||
quicklinks
|
||||
QUNS
|
||||
RAII
|
||||
RAlt
|
||||
@@ -1290,7 +1280,6 @@ recents
|
||||
RECTDESTINATION
|
||||
rectp
|
||||
RECTSOURCE
|
||||
recursesubdirs
|
||||
recyclebin
|
||||
Redist
|
||||
Reencode
|
||||
@@ -1551,7 +1540,6 @@ suntimes
|
||||
swp
|
||||
sug
|
||||
Superbar
|
||||
SUPPRESSMSGBOXES
|
||||
sut
|
||||
svchost
|
||||
SVGIn
|
||||
@@ -1582,7 +1570,6 @@ sysmenu
|
||||
systemai
|
||||
SYSTEMAPPS
|
||||
SYSTEMMODAL
|
||||
systemroot
|
||||
SYSTEMTIME
|
||||
TARGETAPPHEADER
|
||||
targetentrypoint
|
||||
@@ -1641,7 +1628,6 @@ tracelogging
|
||||
tracerpt
|
||||
trackbar
|
||||
trafficmanager
|
||||
traies
|
||||
transicc
|
||||
TRAYMOUSEMESSAGE
|
||||
triaging
|
||||
@@ -1662,7 +1648,6 @@ UBR
|
||||
UCallback
|
||||
ucrt
|
||||
ucrtd
|
||||
udit
|
||||
uefi
|
||||
uesc
|
||||
UFlags
|
||||
@@ -1678,7 +1663,6 @@ uncompilable
|
||||
UNCPRIORITY
|
||||
UNDNAME
|
||||
UNICODETEXT
|
||||
uninsdeletekey
|
||||
uninstalls
|
||||
Uniquifies
|
||||
unitconverter
|
||||
@@ -1694,7 +1678,6 @@ UOI
|
||||
UPDATENOW
|
||||
updown
|
||||
UPGRADINGPRODUCTCODE
|
||||
upserts
|
||||
Uptool
|
||||
urld
|
||||
Usb
|
||||
@@ -1725,7 +1708,6 @@ VERIFYCONTEXT
|
||||
VERSIONINFO
|
||||
VERTRES
|
||||
VERTSIZE
|
||||
VERYSILENT
|
||||
VFT
|
||||
vget
|
||||
vgetq
|
||||
@@ -1779,6 +1761,7 @@ webpage
|
||||
websites
|
||||
wekyb
|
||||
wgpocpl
|
||||
WIC
|
||||
wic
|
||||
wifi
|
||||
winapi
|
||||
@@ -2176,7 +2159,7 @@ nodiscard
|
||||
nologo
|
||||
nomove
|
||||
nosize
|
||||
NOTOPMOST
|
||||
notopmost
|
||||
Notupdated
|
||||
notwindows
|
||||
nowarn
|
||||
@@ -2343,9 +2326,3 @@ YTimer
|
||||
zamora
|
||||
zonability
|
||||
Zorder
|
||||
LLMHF
|
||||
RIGHTBUTTON
|
||||
SIZEALL
|
||||
grabandmove
|
||||
GRABANDMOVEMODULEINTERFACE
|
||||
INITCOMMONCONTROLSEX
|
||||
|
||||
9
.github/actions/spell-check/patterns.txt
vendored
9
.github/actions/spell-check/patterns.txt
vendored
@@ -191,6 +191,15 @@ aka\.ms/[a-zA-Z0-9]+
|
||||
# #pragma lib
|
||||
^\s*#pragma comment\(lib, ".*?"\)
|
||||
|
||||
# UnitTests
|
||||
\[DataRow\(.*\)\]
|
||||
|
||||
# AdditionalDependencies
|
||||
<AdditionalDependencies>.*<
|
||||
|
||||
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
|
||||
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
|
||||
|
||||
RegExp\(@?([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|return/.*?/
|
||||
|
||||
# Questionably acceptable forms of `in to`
|
||||
|
||||
24
.github/policies/resourceManagement.yml
vendored
24
.github/policies/resourceManagement.yml
vendored
@@ -233,30 +233,6 @@ configuration:
|
||||
- addReply:
|
||||
reply: Hi! Thanks for making us aware of the problem. We raised the issue with our internal localization team. This issue should be fixed hopefully in the next version of PowerToys.
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: '\/need-monitor-info'
|
||||
isRegex: True
|
||||
- hasLabel:
|
||||
label: Product-Cursor Wrap
|
||||
- or:
|
||||
- activitySenderHasAssociation:
|
||||
association: Owner
|
||||
- activitySenderHasAssociation:
|
||||
association: Member
|
||||
- activitySenderHasAssociation:
|
||||
association: Collaborator
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
- removeLabel:
|
||||
label: Needs-Team-Response
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- addReply:
|
||||
reply: "To help debug your layout, please run [this script](https://github.com/microsoft/PowerToys/blob/main/src/modules/MouseUtils/CursorWrap/CursorWrapTests/Capture-MonitorLayout.ps1) and attach the generated JSON output to this thread.\n\nThis allows us to better understand the issue and investigate potential fixes."
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
|
||||
4
.github/workflows/msstore-submissions.yml
vendored
4
.github/workflows/msstore-submissions.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)
|
||||
|
||||
- name: Log in to Azure
|
||||
uses: azure/login@v3
|
||||
uses: azure/login@v2
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
- uses: microsoft/setup-msstore-cli@v1
|
||||
|
||||
- name: Fetch Store Credential
|
||||
uses: azure/cli@v3
|
||||
uses: azure/cli@v2
|
||||
with:
|
||||
azcliversion: latest
|
||||
inlineScript: |-
|
||||
|
||||
232
.github/workflows/scheduled-issue-labeling.yml
vendored
232
.github/workflows/scheduled-issue-labeling.yml
vendored
@@ -1,232 +0,0 @@
|
||||
name: Scheduled Issue Product Labeling
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "20 */6 * * *" # Every 6 hours at :20
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
permissions:
|
||||
models: read
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
label-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label issues missing Product labels
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// ── Product label mapping ──────────────────────────────────
|
||||
// Canonical list of Product-* labels used in this repo,
|
||||
// derived from .github/skills/release-note-generation/references/step2-labeling.md
|
||||
const PRODUCT_LABELS = [
|
||||
"Product-Advanced Paste",
|
||||
"Product-Always on Top",
|
||||
"Product-Awake",
|
||||
"Product-ColorPicker",
|
||||
"Product-Command not found",
|
||||
"Product-Command Palette",
|
||||
"Product-CropAndLock",
|
||||
"Product-Cursor Wrap",
|
||||
"Product-Environment Variables",
|
||||
"Product-FancyZones",
|
||||
"Product-File Explorer",
|
||||
"Product-File Locksmith",
|
||||
"Product-Find My Mouse",
|
||||
"Product-Hosts",
|
||||
"Product-Image Resizer",
|
||||
"Product-Keyboard Manager",
|
||||
"Product-LightSwitch",
|
||||
"Product-Mouse Highlighter",
|
||||
"Product-Mouse Jump",
|
||||
"Product-Mouse Pointer Crosshairs",
|
||||
"Product-Mouse Without Borders",
|
||||
"Product-New+",
|
||||
"Product-Peek",
|
||||
"Product-PowerRename",
|
||||
"Product-PowerToys Run",
|
||||
"Product-Quick Accent",
|
||||
"Product-Registry Preview",
|
||||
"Product-Screen Ruler",
|
||||
"Product-Settings",
|
||||
"Product-Shortcut Guide",
|
||||
"Product-Text Extractor",
|
||||
"Product-Workspaces",
|
||||
"Product-ZoomIt",
|
||||
];
|
||||
|
||||
// Map from bug-report "Area(s) with issue?" dropdown values
|
||||
// to Product-* labels (used as strong hints when the issue body
|
||||
// contains the area dropdown answer).
|
||||
const AREA_TO_LABEL = {
|
||||
"Advanced Paste": "Product-Advanced Paste",
|
||||
"Always on Top": "Product-Always on Top",
|
||||
"Awake": "Product-Awake",
|
||||
"ColorPicker": "Product-ColorPicker",
|
||||
"Command not found": "Product-Command not found",
|
||||
"Command Palette": "Product-Command Palette",
|
||||
"Crop and Lock": "Product-CropAndLock",
|
||||
"Environment Variables": "Product-Environment Variables",
|
||||
"FancyZones": "Product-FancyZones",
|
||||
"FancyZones Editor": "Product-FancyZones",
|
||||
"File Locksmith": "Product-File Locksmith",
|
||||
"File Explorer: Preview Pane": "Product-File Explorer",
|
||||
"File Explorer: Thumbnail preview": "Product-File Explorer",
|
||||
"Hosts File Editor": "Product-Hosts",
|
||||
"Image Resizer": "Product-Image Resizer",
|
||||
"Keyboard Manager": "Product-Keyboard Manager",
|
||||
"Light Switch": "Product-LightSwitch",
|
||||
"Mouse Utilities": "Product-Find My Mouse",
|
||||
"Mouse Without Borders": "Product-Mouse Without Borders",
|
||||
"New+": "Product-New+",
|
||||
"Peek": "Product-Peek",
|
||||
"PowerRename": "Product-PowerRename",
|
||||
"PowerToys Run": "Product-PowerToys Run",
|
||||
"Quick Accent": "Product-Quick Accent",
|
||||
"Registry Preview": "Product-Registry Preview",
|
||||
"Screen ruler": "Product-Screen Ruler",
|
||||
"Shortcut Guide": "Product-Shortcut Guide",
|
||||
"TextExtractor": "Product-Text Extractor",
|
||||
"Workspaces": "Product-Workspaces",
|
||||
"ZoomIt": "Product-ZoomIt",
|
||||
};
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────
|
||||
function hasProductLabel(labels) {
|
||||
return labels.some((l) => l.name.startsWith("Product-"));
|
||||
}
|
||||
|
||||
// Try to extract the area from the structured bug-report body
|
||||
// (the "Area(s) with issue?" dropdown).
|
||||
function extractAreaFromBody(body) {
|
||||
if (!body) return null;
|
||||
// The rendered issue body contains a heading followed by the selected values
|
||||
const areaMatch = body.match(
|
||||
/### Area\(s\) with issue\?\s*\n+(.+?)(?:\n###|\n\n|$)/s
|
||||
);
|
||||
if (!areaMatch) return null;
|
||||
const areaText = areaMatch[1].trim();
|
||||
if (areaText === "_No response_" || areaText === "General") return null;
|
||||
// Could be comma-separated; take the first specific one
|
||||
const areas = areaText.split(",").map((a) => a.trim());
|
||||
for (const area of areas) {
|
||||
if (AREA_TO_LABEL[area]) return AREA_TO_LABEL[area];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use GitHub Models to classify an issue when the dropdown area
|
||||
// is not available or is "General".
|
||||
const MAX_BODY_LENGTH = 3000; // Truncate body to stay within model token limits while keeping enough context
|
||||
const MAX_COMPLETION_TOKENS = 60; // Enough for a Product-* label name with some margin
|
||||
async function classifyWithAI(title, body) {
|
||||
const truncatedBody = (body || "").slice(0, MAX_BODY_LENGTH);
|
||||
const labelList = PRODUCT_LABELS.join("\n- ");
|
||||
|
||||
const prompt = `You are a GitHub issue triager for the microsoft/PowerToys repository.
|
||||
|
||||
Given the issue title and body below, determine which ONE Product label best fits.
|
||||
Reply with ONLY the label name (e.g. "Product-FancyZones") or "UNKNOWN" if you cannot determine it.
|
||||
|
||||
Available labels:
|
||||
- ${labelList}
|
||||
|
||||
Issue title: ${title}
|
||||
|
||||
Issue body:
|
||||
${truncatedBody}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://models.github.ai/inference/chat/completions",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
max_tokens: MAX_COMPLETION_TOKENS,
|
||||
temperature: 0,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
core.warning(`AI classification failed: ${response.status} ${response.statusText}`);
|
||||
return null;
|
||||
}
|
||||
const data = await response.json();
|
||||
const answer = data.choices?.[0]?.message?.content?.trim();
|
||||
if (!answer || answer === "UNKNOWN") return null;
|
||||
// Validate the answer is a known label
|
||||
if (PRODUCT_LABELS.includes(answer)) return answer;
|
||||
// Try fuzzy match (the model may include extra text)
|
||||
const found = PRODUCT_LABELS.find((l) => answer.includes(l));
|
||||
return found || null;
|
||||
} catch (err) {
|
||||
core.warning(`AI classification error: ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main ───────────────────────────────────────────────────
|
||||
const MAX_ISSUES = 50; // Process up to 50 issues per run
|
||||
let labeled = 0;
|
||||
let skipped = 0;
|
||||
|
||||
core.info("Searching for open issues with Needs-Triage but no Product-* label...");
|
||||
|
||||
// Paginate through open issues labeled Needs-Triage
|
||||
for await (const response of github.paginate.iterator(
|
||||
github.rest.issues.listForRepo,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: "open",
|
||||
labels: "Needs-Triage",
|
||||
sort: "created",
|
||||
direction: "desc",
|
||||
per_page: 100,
|
||||
}
|
||||
)) {
|
||||
for (const issue of response.data) {
|
||||
if (labeled + skipped >= MAX_ISSUES) break;
|
||||
// Skip pull requests (the API returns them too)
|
||||
if (issue.pull_request) continue;
|
||||
if (hasProductLabel(issue.labels)) continue;
|
||||
|
||||
core.info(`Processing #${issue.number}: ${issue.title}`);
|
||||
|
||||
// 1) Try structured area dropdown first (fast, no AI needed)
|
||||
let label = extractAreaFromBody(issue.body);
|
||||
|
||||
// 2) Fall back to AI classification
|
||||
if (!label) {
|
||||
label = await classifyWithAI(issue.title, issue.body);
|
||||
}
|
||||
|
||||
if (label) {
|
||||
core.info(` → Applying "${label}" to #${issue.number}`);
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [label],
|
||||
});
|
||||
labeled++;
|
||||
} else {
|
||||
core.info(` → Could not determine product label for #${issue.number}, skipping.`);
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
if (labeled + skipped >= MAX_ISSUES) break;
|
||||
}
|
||||
|
||||
core.info(`Done. Labeled: ${labeled}, Skipped: ${skipped}`);
|
||||
13
.github/workflows/spelling2.yml
vendored
13
.github/workflows/spelling2.yml
vendored
@@ -93,7 +93,7 @@ jobs:
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
config: .github/actions/spell-check
|
||||
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
|
||||
@@ -135,7 +135,6 @@ jobs:
|
||||
cspell:cpp/compiler-msvc.txt
|
||||
cspell:python/common/extra.txt
|
||||
cspell:scala/scala.txt
|
||||
ignored: ignored-expect-variant
|
||||
|
||||
comment-push:
|
||||
name: Report (Push)
|
||||
@@ -148,8 +147,10 @@ jobs:
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
config: .github/actions/spell-check
|
||||
checkout: true
|
||||
spell_check_this: microsoft/PowerToys@main
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
@@ -165,8 +166,10 @@ jobs:
|
||||
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
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
config: .github/actions/spell-check
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
@@ -190,7 +193,7 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: apply spelling updates
|
||||
uses: check-spelling/check-spelling@cfb6f7e75bbfc89c71eaa30366d0c166f1bd9c8c # v0.0.26
|
||||
uses: check-spelling/check-spelling@c635c2f3f714eec2fcf27b643a1919b9a811ef2e # v0.0.25
|
||||
with:
|
||||
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
|
||||
checkout: true
|
||||
|
||||
@@ -217,11 +217,7 @@
|
||||
"PowerToys.PowerAccentModuleInterface.dll",
|
||||
"PowerToys.PowerAccentKeyboardService.dll",
|
||||
|
||||
"PowerToys.PowerDisplayModuleInterface.dll",
|
||||
"WinUI3Apps\\PowerToys.PowerDisplay.dll",
|
||||
"WinUI3Apps\\PowerToys.PowerDisplay.exe",
|
||||
"PowerDisplay.Lib.dll",
|
||||
"PowerDisplay.Models.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.PowerRenameExt.dll",
|
||||
"WinUI3Apps\\PowerToys.PowerRename.exe",
|
||||
@@ -250,9 +246,6 @@
|
||||
"PowerToys.ZoomItModuleInterface.dll",
|
||||
"PowerToys.ZoomItSettingsInterop.dll",
|
||||
|
||||
"PowerToys.GrabAndMove.exe",
|
||||
"PowerToys.GrabAndMoveModuleInterface.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.Settings.dll",
|
||||
"WinUI3Apps\\PowerToys.Settings.exe",
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
- name: codeSign
|
||||
displayName: "Enable Code Signing"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
- name: versionNumber
|
||||
displayName: "Version Number"
|
||||
type: string
|
||||
@@ -89,7 +94,7 @@ extends:
|
||||
versionNumber: ${{ parameters.versionNumber }}
|
||||
publishArtifacts: false # 1ES PT handles publication for us.
|
||||
official: true
|
||||
codeSign: true
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
runTests: false
|
||||
buildTests: false
|
||||
signingIdentity:
|
||||
@@ -130,7 +135,7 @@ extends:
|
||||
name: SHINE-INT-L
|
||||
os: windows
|
||||
official: true
|
||||
codeSign: true
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity:
|
||||
serviceName: $(SigningServiceName)
|
||||
appId: $(SigningAppId)
|
||||
|
||||
39
AGENTS.md
39
AGENTS.md
@@ -3,7 +3,7 @@ description: 'Top-level AI contributor guidance for developing PowerToys - a col
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
# PowerToys – AI contributor guide
|
||||
# PowerToys – AI Contributor Guide
|
||||
|
||||
This is the top-level guidance for AI contributions to PowerToys. Keep changes atomic, follow existing patterns, and cite exact paths in PRs.
|
||||
|
||||
@@ -26,15 +26,13 @@ For architecture details and module types, see [Architecture Overview](doc/devdo
|
||||
## Conventions
|
||||
|
||||
For detailed coding conventions, see:
|
||||
|
||||
- [Coding Guidelines](doc/devdocs/development/guidelines.md) – Dependencies, testing, PR management
|
||||
- [Coding Style](doc/devdocs/development/style.md) – Formatting, C++/C#/XAML style rules
|
||||
- [Logging](doc/devdocs/development/logging.md) – C++ spdlog and C# Logger usage
|
||||
|
||||
### Component-specific instructions
|
||||
### Component-Specific Instructions
|
||||
|
||||
These instruction files are automatically applied when working in their respective areas:
|
||||
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md) – IPC contracts, schema migrations
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md) – ABI stability, shared code guidelines
|
||||
|
||||
@@ -46,7 +44,7 @@ These instruction files are automatically applied when working in their respecti
|
||||
- Windows 10 1803+ (April 2018 Update or newer)
|
||||
- Initialize submodules once: `git submodule update --init --recursive`
|
||||
|
||||
### Build commands
|
||||
### Build Commands
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
@@ -54,7 +52,7 @@ These instruction files are automatically applied when working in their respecti
|
||||
| Build current folder | `tools\build\build.cmd` |
|
||||
| Build with options | `build.ps1 -Platform x64 -Configuration Release` |
|
||||
|
||||
### Build discipline
|
||||
### Build Discipline
|
||||
|
||||
1. One terminal per operation (build → test). Do not switch or open new ones mid-flow
|
||||
2. After making changes, `cd` to the project folder that changed (`.csproj`/`.vcxproj`)
|
||||
@@ -64,10 +62,9 @@ These instruction files are automatically applied when working in their respecti
|
||||
6. On failure, read the errors log: `build.<config>.<platform>.errors.log`
|
||||
7. Do not start tests or launch Runner until the build succeeds
|
||||
|
||||
### Build logs
|
||||
### Build Logs
|
||||
|
||||
Located next to the solution/project being built:
|
||||
|
||||
- `build.<configuration>.<platform>.errors.log` – errors only (check this first)
|
||||
- `build.<configuration>.<platform>.all.log` – full log
|
||||
- `build.<configuration>.<platform>.trace.binlog` – for MSBuild Structured Log Viewer
|
||||
@@ -76,18 +73,18 @@ For complete details, see [Build Guidelines](tools/build/BUILD-GUIDELINES.md).
|
||||
|
||||
## Tests
|
||||
|
||||
### Test discovery
|
||||
### Test Discovery
|
||||
|
||||
- Find test projects by product code prefix (e.g., `FancyZones`, `AdvancedPaste`)
|
||||
- Look for sibling folders or 1-2 levels up named `<Product>*UnitTests` or `<Product>*UITests`
|
||||
|
||||
### Running tests
|
||||
### Running Tests
|
||||
|
||||
1. **Build the test project first**, wait for exit code 0
|
||||
2. Run via VS Test Explorer (`Ctrl+E, T`) or `vstest.console.exe` with filters
|
||||
3. **Avoid `dotnet test`** in this repo – use VS Test Explorer or vstest.console.exe
|
||||
|
||||
### Test types
|
||||
### Test Types
|
||||
|
||||
| Type | Requirements | Setup |
|
||||
|------|--------------|-------|
|
||||
@@ -95,13 +92,13 @@ For complete details, see [Build Guidelines](tools/build/BUILD-GUIDELINES.md).
|
||||
| UI Tests | WinAppDriver v1.2.1, Developer Mode | Install from [WinAppDriver releases](https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1) |
|
||||
| Fuzz Tests | OneFuzz, .NET 8 | See [Fuzzing Tests](doc/devdocs/tools/fuzzingtesting.md) |
|
||||
|
||||
### Test discipline
|
||||
### Test Discipline
|
||||
|
||||
1. Add or adjust tests when changing behavior
|
||||
2. If tests skipped, state why (e.g., comment-only change, string rename)
|
||||
3. New modules handling file I/O or user input **must** implement fuzzing tests
|
||||
|
||||
### Special requirements
|
||||
### Special Requirements
|
||||
|
||||
- **Mouse Without Borders**: Requires 2+ physical computers (not VMs)
|
||||
- **Multi-monitor utilities**: Test with 2+ monitors, different DPI settings
|
||||
@@ -110,14 +107,14 @@ For UI test setup details, see [UI Tests](doc/devdocs/development/ui-tests.md).
|
||||
|
||||
## Boundaries
|
||||
|
||||
### Ask for clarification when
|
||||
### Ask for Clarification When
|
||||
|
||||
- Ambiguous spec after scanning relevant docs
|
||||
- Cross-module impact (shared enum/struct) is unclear
|
||||
- Security, elevation, or installer changes involved
|
||||
- GPO or policy handling modifications needed
|
||||
|
||||
### Areas requiring extra care
|
||||
### Areas Requiring Extra Care
|
||||
|
||||
| Area | Concern | Reference |
|
||||
|------|---------|-----------|
|
||||
@@ -126,7 +123,7 @@ For UI test setup details, see [UI Tests](doc/devdocs/development/ui-tests.md).
|
||||
| Installer files | Release impact | Careful review required |
|
||||
| Elevation/GPO logic | Security | Confirm no regression in policy handling |
|
||||
|
||||
### What not to do
|
||||
### What NOT to Do
|
||||
|
||||
- Don't merge incomplete features into main (use feature branches)
|
||||
- Don't break IPC/JSON contracts without updating both runner and settings-ui
|
||||
@@ -146,27 +143,23 @@ Before finishing, verify:
|
||||
|
||||
## Documentation Index
|
||||
|
||||
### Core architecture
|
||||
|
||||
### Core Architecture
|
||||
- [Architecture Overview](doc/devdocs/core/architecture.md)
|
||||
- [Runner](doc/devdocs/core/runner.md)
|
||||
- [Settings System](doc/devdocs/core/settings/readme.md)
|
||||
- [Module Interface](doc/devdocs/modules/interface.md)
|
||||
|
||||
### Development
|
||||
|
||||
- [Coding Guidelines](doc/devdocs/development/guidelines.md)
|
||||
- [Coding Style](doc/devdocs/development/style.md)
|
||||
- [Logging](doc/devdocs/development/logging.md)
|
||||
- [UI Tests](doc/devdocs/development/ui-tests.md)
|
||||
- [Fuzzing Tests](doc/devdocs/tools/fuzzingtesting.md)
|
||||
|
||||
### Build & tools
|
||||
|
||||
### Build & Tools
|
||||
- [Build Guidelines](tools/build/BUILD-GUIDELINES.md)
|
||||
- [Tools Overview](doc/devdocs/tools/readme.md)
|
||||
|
||||
### Instructions (auto-applied)
|
||||
|
||||
### Instructions (Auto-Applied)
|
||||
- [Runner & Settings UI](.github/instructions/runner-settings-ui.instructions.md)
|
||||
- [Common Libraries](.github/instructions/common-libraries.instructions.md)
|
||||
|
||||
121
COMMUNITY.md
121
COMMUNITY.md
@@ -1,109 +1,84 @@
|
||||
# Community
|
||||
|
||||
The PowerToys team is extremely grateful to have the support of an amazing active community. The work you do is incredibly important. PowerToys wouldn't be near what it is without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thanks and to recognize your work. This is a living document dedicated to highlighting the high impact community members and their contributions.
|
||||
The PowerToys team is extremely grateful to have the support of an amazing active community. The work you do is incredibly important. PowerToys wouldn’t be near what it is without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thanks and to recognize your work. This is a living document dedicated to highlighting the high impact community members and their contributions.
|
||||
|
||||
Names are in alphabetical order, based on first name.
|
||||
Names are in alphabetical order based on first name.
|
||||
|
||||
## High impact community members
|
||||
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
|
||||
Christian contributed the New+ utility
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
Christian contributed New+ utility
|
||||
|
||||
### [@CleanCodeDeveloper](https://github.com/CleanCodeDeveloper)
|
||||
|
||||
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
|
||||
|
||||
### [@plante-msft](https://github.com/plante-msft) - Connor Plante
|
||||
|
||||
Connor was the creator of Workspaces and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
|
||||
|
||||
Damien has helped out by developing and contributing the Quick Accent utility.
|
||||
|
||||
### [@daverayment](https://github.com/daverayment) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/)
|
||||
|
||||
Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs.
|
||||
|
||||
### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/)
|
||||
|
||||
Davide has helped fix multiple bugs, added new utilities, features, as well as help us with the ARM64 effort by porting applications to .NET Core.
|
||||
|
||||
### [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang
|
||||
|
||||
Ethan helped run PowerToys and worked on improving and prototyping out next generation PowerToys
|
||||
|
||||
### [@franky920920](https://github.com/franky920920) - [Franky Chen](https://frankychen.net)
|
||||
|
||||
Franky has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes to PowerToys.
|
||||
|
||||
### [@htcfreek](https://github.com/htcfreek) - Heiko
|
||||
|
||||
Heiko has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes to PowerToys.
|
||||
|
||||
### [@Jay-o-Way](https://github.com/Jay-o-Way) - Jay
|
||||
|
||||
Jay has helped triaging, discussing, creating a substantial number of issues and PRs.
|
||||
|
||||
### [@jefflord](https://github.com/Jjefflord) - Jeff Lord
|
||||
|
||||
Jeff added multiple new features to Keyboard Manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
Jeff added in multiple new features into Keyboard manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
|
||||
Jeremy has helped drive substantial ARM64 support within PowerToys.
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
|
||||
### [@jiripolasek](https://github.com/jiripolasek) - [Jiří Polášek](https://github.com/jiripolasek)
|
||||
|
||||
Jiří has contributed a massive number of features and improvements to Command Palette, including drag & drop support, custom themes, Web Search enhancements, Remote Desktop extension fixes, and many UX improvements.
|
||||
|
||||
### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com)
|
||||
|
||||
Joe has helped with triaging, discussing issues as well as fixing bugs and building features for Text Extractor.
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
### [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie
|
||||
|
||||
Jordi helped innovate amazing new features into Advanced Paste and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
|
||||
### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/)
|
||||
|
||||
Helping keep our spelling correct :)
|
||||
|
||||
### [@martinchrzan](https://github.com/martinchrzan/) - Martin Chrzan
|
||||
|
||||
Color Picker is from Martin.
|
||||
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
|
||||
|
||||
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
|
||||
|
||||
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
|
||||
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
|
||||
### [@royvou](https://github.com/royvou)
|
||||
|
||||
Roy has helped out contributing multiple features to PowerToys Run
|
||||
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
|
||||
ThiefZero has helped contribute features to PowerToys Run, such as the unit converter plugin
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
### [@TobiasSekan](https://github.com/TobiasSekan) - Tobias Sekan
|
||||
|
||||
Tobias Sekan has helped out contributing features to PowerToys Run such as Settings plugin, Registry plugin
|
||||
|
||||
## Open source projects
|
||||
@@ -119,8 +94,7 @@ Their fork of Wox was the base of PowerToys Run.
|
||||
Initial base of jjw24's fork, which makes it the base of PowerToys Run.
|
||||
|
||||
### [Text-Grab](https://github.com/TheJoeFin/Text-Grab) - Joseph Finney
|
||||
|
||||
Joe helped develop and contribute to the Text Extractor utility. It is directly based on his Text Grab application.
|
||||
Joe helped develop and contribute to the Text Extractor utility. It is directly based on his Text Grab application.
|
||||
|
||||
## Microsoft community members
|
||||
|
||||
@@ -128,7 +102,7 @@ We would like to also directly call out some extremely helpful Microsoft employe
|
||||
|
||||
### [@betsegaw](https://github.com/betsegaw/) - [Betsegaw Tadele](http://www.dreamsofameaningfullife.com/)
|
||||
|
||||
Window Walker, inside PowerToys Run, is from Beta.
|
||||
Window Walker, inside PowerToys Run, is from Beta.
|
||||
|
||||
### [@TheMrJukes](https://github.com/TheMrJukes/) - Bret Anderson
|
||||
|
||||
@@ -151,7 +125,6 @@ PowerToys Awake is a tool to keep your computer awake.
|
||||
Randy contributed Registry Preview and some very early conversations about keyboard remapping.
|
||||
|
||||
### [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon
|
||||
|
||||
Kayla was a former lead for PowerToys and helped create multiple utilities, maintained the GitHub repo, and collaborated with the community to improve the overall product
|
||||
|
||||
### [@oldnewthing](https://github.com/oldnewthing) - Raymond Chen
|
||||
@@ -162,48 +135,46 @@ Find My Mouse is based on Raymond Chen's SuperSonar.
|
||||
|
||||
Crop And Lock is based on the original work of Robert Mikhayelyan, with Program Manager support from [@kevinguo305](https://github.com/kevinguo305) - Kevin Guo.
|
||||
|
||||
ZoomIt's Video Recording Session code is based on Robert Mikhayelyan's <https://github.com/robmikh/capturevideosample> code.
|
||||
ZoomIt's Video Recording Session code is based on Robert Mikhayelyan's https://github.com/robmikh/capturevideosample code.
|
||||
|
||||
### Microsoft InVEST team
|
||||
|
||||
This amazing team helped PowerToys develop PowerToys Run and Keyboard manager as well as update our Settings to v2. @alekhyareddy28, @arjunbalgovind, @jyuwono @laviusmotileng-ms, @ryanbodrug-microsoft, @saahmedm, @somil55, @traies, @udit3333
|
||||
This amazing team helped PowerToys develop PowerToys Run and Keyboard manager as well as update our Settings to v2. @alekhyareddy28, @arjunbalgovind, @jyuwono @laviusmotileng-ms, @ryanbodrug-microsoft, @saahmedm, @somil55, @traies, @udit3333
|
||||
|
||||
## Mouse Without Borders original contributors
|
||||
|
||||
Project creator: Truong Do (Đỗ Đức Trường)
|
||||
*Project creator: Truong Do (Đỗ Đức Trường)*
|
||||
|
||||
Other contributors:
|
||||
|
||||
- Microsoft Garage: Quinn Hawkins, Michael Low, Joe Coplen, Nino Yuniardi, Gwyneth Marshall, David Andrews, Karen Luecking
|
||||
- Peter Hauge - Visual Studio
|
||||
- Bruce Dawson - Windows Fundamentals
|
||||
- Alan Myrvold - Office Security
|
||||
- Adrian Garside - WEX
|
||||
- Scott Bradner - Surface
|
||||
- Aleks Gershaft - Windows Azure
|
||||
- Chinh Huynh - Windows Azure
|
||||
- Long Nguyen - Data Center
|
||||
- Triet Le - Cloud Engineering
|
||||
- Luke Schoen - Excel
|
||||
- Bao Nguyen - Bing
|
||||
- Ross Nichols - Windows
|
||||
- Ryan Baltazar - Windows
|
||||
- Ed Essey - The Garage
|
||||
- Mario Madden - The Garage
|
||||
- Karthick Mahalingam - ACE
|
||||
- Pooja Kamra - ACE
|
||||
- Justin White - SA
|
||||
- Chris Ransom - SA
|
||||
- Mike Ricks - Red Team
|
||||
- Randy Santossio - Surface
|
||||
- Ashish Sen Jaswal - Device Health
|
||||
- Zoltan Harmath - Security Tools
|
||||
- Luciano Krigun - Security Products
|
||||
- Jo Hemmerlein - Red Team
|
||||
- Chris Johnson - Surface Hub
|
||||
- Loren Ponten - Surface Hub
|
||||
- Paul Schmitt - WWL
|
||||
- And many other Users!
|
||||
* Microsoft Garage: Quinn Hawkins, Michael Low, Joe Coplen, Nino Yuniardi, Gwyneth Marshall, David Andrews, Karen Luecking
|
||||
* Peter Hauge - Visual Studio
|
||||
* Bruce Dawson - Windows Fundamentals
|
||||
* Alan Myrvold - Office Security
|
||||
* Adrian Garside - WEX
|
||||
* Scott Bradner - Surface
|
||||
* Aleks Gershaft - Windows Azure
|
||||
* Chinh Huynh - Windows Azure
|
||||
* Long Nguyen - Data Center
|
||||
* Triet Le - Cloud Engineering
|
||||
* Luke Schoen - Excel
|
||||
* Bao Nguyen - Bing
|
||||
* Ross Nichols - Windows
|
||||
* Ryan Baltazar - Windows
|
||||
* Ed Essey - The Garage
|
||||
* Mario Madden - The Garage
|
||||
* Karthick Mahalingam - ACE
|
||||
* Pooja Kamra - ACE
|
||||
* Justin White - SA
|
||||
* Chris Ransom - SA
|
||||
* Mike Ricks - Red Team
|
||||
* Randy Santossio - Surface
|
||||
* Ashish Sen Jaswal - Device Health
|
||||
* Zoltan Harmath - Security Tools
|
||||
* Luciano Krigun - Security Products
|
||||
* Jo Hemmerlein - Red Team
|
||||
* Chris Johnson - Surface Hub
|
||||
* Loren Ponten - Surface Hub
|
||||
* Paul Schmitt - WWL
|
||||
* And many other Users!
|
||||
|
||||
## ZoomIt original contributors
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# PowerToys contributor's guide
|
||||
# PowerToys Contributor's Guide
|
||||
|
||||
Below is our guidance for reporting issues, proposing new features, and submitting contributions via Pull Requests (PRs). Our philosophy is to understand the problem and scenarios first, which is why we follow this pattern before work starts.
|
||||
|
||||
@@ -6,46 +6,46 @@ Below is our guidance for reporting issues, proposing new features, and submitti
|
||||
2. There has been a conversation.
|
||||
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation).
|
||||
|
||||
## Filing an issue
|
||||
## Filing an Issue
|
||||
|
||||
**Importance of Filing an Issue First**
|
||||
|
||||
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyone's time:
|
||||
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyone’s time:
|
||||
|
||||
> 👉 If you have a question, think you've discovered an issue, or would like to propose a new feature, please find/file an issue **BEFORE** starting work to fix/implement it.
|
||||
|
||||
When requesting new features or enhancements, providing additional evidence, data, tweets, blog posts, or research is extremely helpful. This information gives context to the scenario that may otherwise be lost.
|
||||
|
||||
- Unsure whether it's an issue or feature request? File an issue.
|
||||
- Have a question that isn't answered in the docs, videos, etc.? File an issue.
|
||||
- Want to know if we're planning a particular feature? File an issue.
|
||||
- Got a great idea for a new utility or feature? File an issue/request/idea.
|
||||
- Don't understand how to do something? File an issue/Community Guidance Request.
|
||||
- Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
|
||||
* Unsure whether it’s an issue or feature request? File an issue.
|
||||
* Have a question that isn't answered in the docs, videos, etc.? File an issue.
|
||||
* Want to know if we’re planning a particular feature? File an issue.
|
||||
* Got a great idea for a new utility or feature? File an issue/request/idea.
|
||||
* Don’t understand how to do something? File an issue/Community Guidance Request.
|
||||
* Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
|
||||
|
||||
A quick search before filing an issue could be helpful. It's likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
|
||||
A quick search before filing an issue could be helpful. It’s likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
|
||||
|
||||
### Indicating interest in issues
|
||||
### Indicating Interest in Issues
|
||||
|
||||
To let the team know which issues are important, upvote by clicking the [+😊] button and the 👍 icon on the original issue post. Avoid comments like "+1" or "me too" as they clutter the discussion and make it harder to prioritize requests.
|
||||
|
||||
---
|
||||
|
||||
## Contributing fixes or features
|
||||
## Contributing Fixes/Features
|
||||
|
||||
Please comment on our [Would you like to contribute to PowerToys?](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
|
||||
Please comment on our ["Would you like to contribute to PowerToys?"](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
|
||||
|
||||
### Localization issues
|
||||
### Localization Issues
|
||||
|
||||
For localization issues, please file an issue to notify our internal localization team, as community PRs for localization aren't accepted. Localization is handled exclusively by the internal Microsoft team.
|
||||
|
||||
### To spec or not to spec
|
||||
### To Spec or Not to Spec
|
||||
|
||||
A key point is for everyone to understand the approach that will be taken. We want to be sure that any work done will be accepted. Larger-scope items will require a spec to outline the approach and allow for discussion. Specs help collaborators consider different solutions, describe feature behavior, and plan for errors. Achieving agreement in a spec before writing code often results in simpler code and less wasted effort.
|
||||
|
||||
Once a team member has agreed with your approach, proceed to the "Development" section below. Team members are happy to help review specs and guide them to completion.
|
||||
|
||||
### Help wanted
|
||||
### Help Wanted
|
||||
|
||||
Once the team has approved an issue/spec approach, development can proceed. If no developers are immediately available, the spec may be parked and labeled "Help Wanted," ready for a developer to get started. For development opportunities, visit [Issues labeled Help Wanted](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
|
||||
|
||||
@@ -55,18 +55,18 @@ Once the team has approved an issue/spec approach, development can proceed. If n
|
||||
|
||||
Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md).
|
||||
|
||||
### Naming features and functionality
|
||||
### Naming Features and Functionality
|
||||
|
||||
Names should be descriptive and straightforward, clearly reflecting functionality and usefulness.
|
||||
|
||||
### Becoming a collaborator on the PowerToys team
|
||||
### Becoming a Collaborator on the PowerToys Team
|
||||
|
||||
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
|
||||
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
|
||||
|
||||
When the time comes, Microsoft will reach out to you about becoming a formal team member. Just make sure they have a way to contact you. 😊
|
||||
|
||||
---
|
||||
|
||||
## Thank you
|
||||
## Thank You
|
||||
|
||||
Thank you in advance for your contribution! We appreciate your help in making PowerToys a better tool for everyone.
|
||||
|
||||
1713
DATA_AND_PRIVACY.md
1713
DATA_AND_PRIVACY.md
File diff suppressed because it is too large
Load Diff
@@ -18,15 +18,15 @@
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="2.1.12" />
|
||||
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.251219" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.260116-build.2514" />
|
||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
||||
@@ -75,7 +75,7 @@
|
||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.250325.1"/>
|
||||
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.231216.1"/>
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.260209005" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.260203002" />
|
||||
|
||||
87
NOTICE.md
87
NOTICE.md
@@ -17,7 +17,7 @@ This software incorporates material from third parties.
|
||||
|
||||
### Martin Chrzan's Color Picker
|
||||
|
||||
**Source**: <https://github.com/martinchrzan/ColorPicker>
|
||||
**Source**: https://github.com/martinchrzan/ColorPicker
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -49,7 +49,7 @@ We use the WyHash NuGet package for calculating stable hashes for strings.
|
||||
|
||||
**Source**: [https://github.com/wangyi-fudan/wyhash](https://github.com/wangyi-fudan/wyhash)
|
||||
|
||||
```text
|
||||
```
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
@@ -82,7 +82,7 @@ We use the ToolGood.Words.Pinyin NuGet package for converting Chinese characters
|
||||
|
||||
**Source**: [https://github.com/toolgood/ToolGood.Words.Pinyin](https://github.com/toolgood/ToolGood.Words.Pinyin)
|
||||
|
||||
```text
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 ToolGood
|
||||
@@ -106,7 +106,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## Utility: Command palette built-in extensions
|
||||
|
||||
## Utility: Command Palette Built-in Extensions
|
||||
|
||||
### Calculator
|
||||
|
||||
@@ -116,7 +117,7 @@ We use the exprtk library (exprtk.hpp) to evaluate mathematical expressions.
|
||||
|
||||
**Source**: [https://github.com/ArashPartow/exprtk](https://github.com/ArashPartow/exprtk)
|
||||
|
||||
```text
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 1999-2024 Arash Partow
|
||||
@@ -143,7 +144,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
```
|
||||
|
||||
## Utility: PowerToys Run built-in extensions
|
||||
## Utility: PowerToys Run Built-in Extensions
|
||||
|
||||
### Calculator
|
||||
|
||||
@@ -153,7 +154,7 @@ We use the Mages NuGet package for calculating the result of expression.
|
||||
|
||||
**Source**: [https://github.com/FlorianRappl/Mages](https://github.com/FlorianRappl/Mages)
|
||||
|
||||
```text
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 - 2025 Florian Rappl
|
||||
@@ -177,13 +178,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## Utility: File Explorer add-ins
|
||||
## Utility: File Explorer Add-ins
|
||||
|
||||
### Monaco Editor
|
||||
|
||||
**Source**: <https://github.com/Microsoft/monaco-editor>
|
||||
**Source**: https://github.com/Microsoft/monaco-editor
|
||||
|
||||
**Additional third party notifications:** <https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt>
|
||||
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -207,9 +208,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### The Quite OK image format reference decoder
|
||||
### The Quite OK Image Format reference decoder
|
||||
|
||||
**Source**: <https://github.com/phoboslab/qoi>
|
||||
**Source**: https://github.com/phoboslab/qoi
|
||||
|
||||
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys from the original C++ implementation.
|
||||
|
||||
@@ -239,9 +240,9 @@ SOFTWARE.
|
||||
|
||||
We use the UTF.Unknown NuGet package for detecting encoding in text/code files.
|
||||
|
||||
**Source**: <https://github.com/CharsetDetector/UTF-unknown>
|
||||
**Source**: https://github.com/CharsetDetector/UTF-unknown
|
||||
|
||||
```text
|
||||
```
|
||||
MOZILLA PUBLIC LICENSE
|
||||
Version 1.1
|
||||
|
||||
@@ -715,9 +716,9 @@ EXHIBIT A -Mozilla Public License.
|
||||
|
||||
## Utility: ImageResizer
|
||||
|
||||
### Brice Lams's Image Resizer license
|
||||
### Brice Lams's Image Resizer License
|
||||
|
||||
**Source**: <https://github.com/bricelam/ImageResizer/>
|
||||
**Source**: https://github.com/bricelam/ImageResizer/
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -743,10 +744,10 @@ THE SOFTWARE.
|
||||
|
||||
## Utility: PowerToys Run
|
||||
|
||||
### Wox license
|
||||
### Wox License
|
||||
|
||||
**Fork project source**: <https://github.com/jjw24/Wox/>
|
||||
**Base project source**: <https://github.com/Wox-launcher/Wox>
|
||||
**Fork project source**: https://github.com/jjw24/Wox/
|
||||
**Base project source**: https://github.com/Wox-launcher/Wox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -769,9 +770,9 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Beta Tadele's Window Walker license
|
||||
### Beta Tadele's Window Walker License
|
||||
|
||||
**Source**: <https://github.com/betsegaw/windowwalker>
|
||||
**Source**: https://github.com/betsegaw/windowwalker
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -785,9 +786,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
## Utility: PowerRename
|
||||
|
||||
### Chris Davis's SmartRename license
|
||||
### Chris Davis's SmartRename License
|
||||
|
||||
**Source**: <https://github.com/chrdavis/SmartRename>
|
||||
**Source**: https://github.com/chrdavis/SmartRename
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -815,7 +816,7 @@ SOFTWARE.
|
||||
|
||||
### spdlog
|
||||
|
||||
**Source**: <https://github.com/gabime/spdlog>
|
||||
**Source**: https://github.com/gabime/spdlog
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -840,12 +841,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-- NOTE: Third party dependency used by this software --
|
||||
This software depends on the fmt lib (MIT License), and users must comply to its license:
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>
|
||||
This software depends on the fmt lib (MIT License),
|
||||
and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst
|
||||
|
||||
### expected-lite
|
||||
|
||||
**Source**: <https://github.com/martinmoene/expected-lite>
|
||||
**Source**: https://github.com/martinmoene/expected-lite
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
@@ -873,7 +874,7 @@ DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### zip
|
||||
|
||||
**Source**: <https://github.com/kuba--/zip>
|
||||
**Source**: https://github.com/kuba--/zip
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
@@ -901,7 +902,7 @@ THE SOFTWARE.
|
||||
|
||||
We adopted some functions from it.
|
||||
|
||||
**Source**: <https://github.com/DLTcollab/sse2neon>
|
||||
**Source**: https://github.com/DLTcollab/sse2neon
|
||||
|
||||
sse2neon is freely redistributable under the MIT License.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -924,9 +925,9 @@ SOFTWARE.
|
||||
|
||||
### Monaco Editor
|
||||
|
||||
**Source**: <https://github.com/Microsoft/monaco-editor>
|
||||
**Source**: https://github.com/Microsoft/monaco-editor
|
||||
|
||||
**Additional third party notifications:** <https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt>
|
||||
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -950,11 +951,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### The Quite OK image format reference decoder
|
||||
### The Quite OK Image Format reference decoder
|
||||
|
||||
**Source**: <https://github.com/phoboslab/qoi>
|
||||
**Source**: https://github.com/phoboslab/qoi
|
||||
|
||||
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys, from the original C++ implementation.
|
||||
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys from the original C++ implementation.
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -978,13 +979,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
### UTF unknown
|
||||
### UTF Unknown
|
||||
|
||||
We use the UTF.Unknown NuGet package for detecting encoding in text/code files.
|
||||
|
||||
**Source**: <https://github.com/CharsetDetector/UTF-unknown>
|
||||
**Source**: https://github.com/CharsetDetector/UTF-unknown
|
||||
|
||||
```text
|
||||
```
|
||||
MOZILLA PUBLIC LICENSE
|
||||
Version 1.1
|
||||
|
||||
@@ -1462,9 +1463,9 @@ EXHIBIT A -Mozilla Public License.
|
||||
|
||||
We use HexBox.WinUI to show a preview of binary values.
|
||||
|
||||
**Source**: <https://github.com/hotkidfamily/HexBox.WinUI>
|
||||
**Source**: https://github.com/hotkidfamily/HexBox.WinUI
|
||||
|
||||
```text
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Filip Jeremic
|
||||
@@ -1491,11 +1492,11 @@ SOFTWARE.
|
||||
|
||||
### Monaco Editor
|
||||
|
||||
**Source**: <https://github.com/Microsoft/monaco-editor>
|
||||
**Source**: https://github.com/Microsoft/monaco-editor
|
||||
|
||||
**Additional third party notifications:** <https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt>
|
||||
**Additional third party notifications:** https://github.com/microsoft/monaco-editor/blob/main/ThirdPartyNotices.txt
|
||||
|
||||
```text
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 - present Microsoft Corporation
|
||||
@@ -1525,7 +1526,7 @@ SOFTWARE.
|
||||
|
||||
PowerDisplay's DDC/CI implementation references techniques from Twinkle Tray.
|
||||
|
||||
**Source**: <https://github.com/xanderfrangos/twinkle-tray>
|
||||
**Source**: https://github.com/xanderfrangos/twinkle-tray
|
||||
|
||||
MIT License
|
||||
|
||||
|
||||
@@ -709,19 +709,17 @@
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerDisplay/">
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay.Models/PowerDisplay.Models.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay.Lib/PowerDisplay.Lib.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<!-- TEMPORARILY_DISABLED: PowerDisplay
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplayModuleInterface/PowerDisplayModuleInterface.vcxproj" Id="d1234567-8901-2345-6789-abcdef012345" />
|
||||
-->
|
||||
</Folder>
|
||||
<Folder Name="/modules/PowerDisplay/Tests/">
|
||||
<Project Path="src/modules/powerdisplay/PowerDisplay.Lib.UnitTests/PowerDisplay.Lib.UnitTests.csproj">
|
||||
@@ -1036,10 +1034,6 @@
|
||||
<Project Path="src/modules/ZoomIt/ZoomItModuleInterface/ZoomItModuleInterface.vcxproj" Id="e4585179-2ac1-4d5f-a3ff-cfc5392f694c" />
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/modules/GrabAndMove/">
|
||||
<Project Path="src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj" Id="568c4c30-2e3c-4c2c-a691-007362073765" />
|
||||
<Project Path="src/modules/GrabAndMove/GrabAndMoveModuleInterface/GrabAndMoveModuleInterface.vcxproj" Id="2c3f7770-4e57-46b7-8dc1-7428a383d0db" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
@@ -1104,8 +1098,6 @@
|
||||
<BuildDependency Project="src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchModuleInterface/LightSwitchModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/LightSwitch/LightSwitchService/LightSwitchService.vcxproj" />
|
||||
<BuildDependency Project="src/modules/GrabAndMove/GrabAndMoveModuleInterface/GrabAndMoveModuleInterface.vcxproj" />
|
||||
<BuildDependency Project="src/modules/GrabAndMove/GrabAndMove/GrabAndMove.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/dll/PowerRenameExt.vcxproj" />
|
||||
<BuildDependency Project="src/modules/powerrename/lib/PowerRenameLib.vcxproj" />
|
||||
<BuildDependency Project="src/modules/previewpane/Common/PreviewHandlerCommon.csproj" />
|
||||
|
||||
70
README.md
70
README.md
@@ -19,13 +19,14 @@
|
||||
<span> · </span>
|
||||
<a href="#-whats-new">Release notes</a>
|
||||
</h3>
|
||||
<br/><br/>
|
||||
|
||||
## 🔨 Utilities
|
||||
|
||||
PowerToys includes over 30 utilities to help you customize and optimize your Windows experience:
|
||||
PowerToys includes over 25 utilities to help you customize and optimize your Windows experience:
|
||||
|
||||
| | | |
|
||||
| --- | --- | --- |
|
||||
|---|---|---|
|
||||
| [<img src="doc/images/icons/AdvancedPaste.png" alt="Advanced Paste icon" height="16"> Advanced Paste](https://aka.ms/PowerToysOverview_AdvancedPaste) | [<img src="doc/images/icons/Always%20On%20Top.png" alt="Always on Top icon" height="16"> Always on Top](https://aka.ms/PowerToysOverview_AoT) | [<img src="doc/images/icons/Awake.png" alt="Awake icon" height="16"> Awake](https://aka.ms/PowerToysOverview_Awake) |
|
||||
| [<img src="doc/images/icons/Color%20Picker.png" alt="Color Picker icon" height="16"> Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [<img src="doc/images/icons/Command%20Not%20Found.png" alt="Command Not Found icon" height="16"> Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | [<img src="doc/images/icons/Command Palette.png" alt="Command Palette icon" height="16"> Command Palette](https://aka.ms/PowerToysOverview_CmdPal) |
|
||||
| [<img src="doc/images/icons/Crop%20And%20Lock.png" alt="Crop and Lock icon" height="16"> Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [<img src="doc/images/icons/Environment%20Manager.png" alt="Environment Variables icon" height="16"> Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [<img src="doc/images/icons/FancyZones.png" alt="FancyZones icon" height="16"> FancyZones](https://aka.ms/PowerToysOverview_FancyZones) |
|
||||
@@ -37,27 +38,28 @@ PowerToys includes over 30 utilities to help you customize and optimize your Win
|
||||
| [<img src="doc/images/icons/Shortcut%20Guide.png" alt="Shortcut Guide icon" height="16"> Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [<img src="doc/images/icons/PowerOCR.png" alt="Text Extractor icon" height="16"> Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [<img src="doc/images/icons/Workspaces.png" alt="Workspaces icon" height="16"> Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
|
||||
| [<img src="doc/images/icons/ZoomIt.png" alt="ZoomIt icon" height="16"> ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) | | |
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
For detailed installation instructions and system requirements, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
|
||||
## 📋 Installation
|
||||
|
||||
For detailed installation instructions and system requirements, visit the [installation docs](https://learn.microsoft.com/windows/powertoys/install).
|
||||
|
||||
But to get started quickly, choose one of the installation methods below:
|
||||
<br/><br/>
|
||||
<details open>
|
||||
<summary><strong>Download the .exe file from GitHub</strong></summary>
|
||||
<summary><strong>Download .exe from GitHub</strong></summary>
|
||||
<br/>
|
||||
|
||||
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_.
|
||||
Go to the <a href="https://aka.ms/installPowerToys">PowerToys GitHub releases</a>, click Assets to reveal the downloads, and choose the installer that matches your architecture and install scope. For most devices, that's the x64 per-user installer.
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.99%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.98%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
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysUserSetup-0.98.1-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.98.1/PowerToysSetup-0.98.1-arm64.exe
|
||||
|
||||
| Description | Filename |
|
||||
| --- | --- |
|
||||
|
||||
| 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] |
|
||||
@@ -81,16 +83,14 @@ You can easily install PowerToys from the Microsoft Store:
|
||||
<details>
|
||||
<summary><strong>WinGet</strong></summary>
|
||||
<br/>
|
||||
Download PowerToys from [WinGet](https://github.com/microsoft/winget-cli#installing-the-client). Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
|
||||
|
||||
- User scope installer (default)
|
||||
Download PowerToys from <a href="https://github.com/microsoft/winget-cli#installing-the-client">WinGet</a>. Updating PowerToys via winget will respect the current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell:
|
||||
|
||||
*User scope installer [default]*
|
||||
```powershell
|
||||
winget install Microsoft.PowerToys -s winget
|
||||
```
|
||||
|
||||
- Machine-wide scope installer
|
||||
|
||||
*Machine-wide scope installer*
|
||||
```powershell
|
||||
winget install --scope machine Microsoft.PowerToys -s winget
|
||||
```
|
||||
@@ -99,7 +99,7 @@ winget install --scope machine Microsoft.PowerToys -s winget
|
||||
<details>
|
||||
<summary><strong>Other methods</strong></summary>
|
||||
<br/>
|
||||
There are [community driven install methods](https://learn.microsoft.com/windows/powertoys/install#community-driven-install-tools) such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there.
|
||||
There are <a href="https://learn.microsoft.com/windows/powertoys/install#community-driven-install-tools">community driven install methods</a> such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there.
|
||||
</details>
|
||||
|
||||
## ✨ What's new?
|
||||
@@ -108,26 +108,28 @@ There are [community driven install methods](https://learn.microsoft.com/windows
|
||||
|
||||
To see what's new, check out the [release notes](https://github.com/microsoft/PowerToys/releases/tag/v0.98.1).
|
||||
|
||||
## 🛣️ Roadmap
|
||||
|
||||
## 🛣️ 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]!
|
||||
|
||||
## ❤️ PowerToys Community
|
||||
|
||||
## ❤️ PowerToys Community
|
||||
The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn't be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Your contributions and feedback improve PowerToys month after month!
|
||||
|
||||
## Contributing
|
||||
## Contributing
|
||||
This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. Most contributions require you to agree to a [Contributor License Agreement (CLA)][oss-CLA] declaring that you grant us the rights to use your contribution and that you have permission to do so. For guidance on developing for PowerToys, please read the [developer docs](./doc/devdocs) for a detailed breakdown. This includes how to setup your computer to compile.
|
||||
|
||||
This project welcomes contributions of all types. Besides coding features / bug fixes, other ways to assist include spec writing, design, documentation, and finding bugs. We are excited to work with the power user community to build a set of tools for helping you get the most out of Windows. We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](CONTRIBUTING.md). We would be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort. Most contributions require you to agree to a [Contributor License Agreement (CLA)][oss-CLA] declaring that you grant us the rights to use your contribution and that you have permission to do so. For guidance on developing for PowerToys, please read the [developer docs](./doc/devdocs) for a detailed breakdown. This includes how to setup your computer to compile.
|
||||
## Code of Conduct
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct-code].
|
||||
|
||||
## Code of conduct
|
||||
## Privacy Statement
|
||||
The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct-code].
|
||||
|
||||
## Privacy statement
|
||||
|
||||
The application logs basic diagnostic data (telemetry). For more privacy information and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
|
||||
|
||||
[oss-CLA]: https://cla.opensource.microsoft.com
|
||||
[oss-conduct-code]: CODE_OF_CONDUCT.md
|
||||
[community-link]: COMMUNITY.md
|
||||
[oss-CLA]: https://cla.opensource.microsoft.com
|
||||
[oss-conduct-code]: CODE_OF_CONDUCT.md
|
||||
[community-link]: COMMUNITY.md
|
||||
[github-release-link]: https://aka.ms/installPowerToys
|
||||
[microsoft-store-link]: https://aka.ms/getPowertoys
|
||||
[winget-link]: https://github.com/microsoft/winget-cli#installing-the-client
|
||||
[roadmap]: https://github.com/microsoft/PowerToys/wiki/Roadmap
|
||||
[privacy-link]: http://go.microsoft.com/fwlink/?LinkId=521839
|
||||
[loc-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=translation_issue.md&title=
|
||||
[usingPowerToys-docs-link]: https://aka.ms/powertoys-docs
|
||||
|
||||
24
SECURITY.md
24
SECURITY.md
@@ -1,36 +1,36 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
|
||||
|
||||
# Security
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
|
||||
|
||||
## Reporting security issues
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send an email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
- Full paths of source file(s) related to the manifestation of the issue
|
||||
- The location of the affected source code (tag/branch/commit or direct URL)
|
||||
- Any special configuration required to reproduce the issue
|
||||
- Step-by-step instructions to reproduce the issue
|
||||
- Proof-of-concept or exploit code (if possible)
|
||||
- Impact of the issue, including how an attacker might exploit the issue
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred languages
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
|
||||
17
SUPPORT.md
17
SUPPORT.md
@@ -1,21 +1,24 @@
|
||||
# Support
|
||||
|
||||
## How to use Microsoft PowerToys
|
||||
|
||||
For more information about PowerToys overviews, how to use the utilities, and other tools and resources for [Windows development environments](https://learn.microsoft.com/windows/dev-environment/overview), visit [learn.microsoft.com][usingPowerToys-docs-link].
|
||||
## How to use Microsoft PowerToys
|
||||
|
||||
For more info on [PowerToys overviews and how to use the utilities][usingPowerToys-docs-link], or any other tools and resources for [Windows development environments](https://learn.microsoft.com/windows/dev-environment/overview), head over to [learn.microsoft.com][usingPowerToys-docs-link]!
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses [GitHub Issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue.
|
||||
This project uses [GitHub Issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or
|
||||
feature request as a new Issue.
|
||||
|
||||
For help and questions about using this project, please visit our documentation and [Contributor's Guide][contributor] if you want to contribute to PowerToys.
|
||||
For help and questions about using this project, please look at our Wiki for using PowerToys and our [Contributor's Guide][contributor] if you want to work on PowerToys.
|
||||
|
||||
## Microsoft support policy
|
||||
## Microsoft Support Policy
|
||||
|
||||
Support for PowerToys is limited to the resources listed above.
|
||||
|
||||
[gh-issue]: https://github.com/microsoft/PowerToys/issues/new/choose
|
||||
[gh-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=Issue-Bug&template=bug_report.md
|
||||
[gh-feature]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=feature_request.md
|
||||
[gh-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=Issue-Bug&template=bug_report.md&title=
|
||||
[gh-feature]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=feature_request.md&title=
|
||||
[wiki]: https://github.com/microsoft/PowerToys/wiki
|
||||
[contributor]: https://github.com/microsoft/PowerToys/blob/main/CONTRIBUTING.md
|
||||
[usingPowerToys-docs-link]: https://aka.ms/powertoys-docs
|
||||
|
||||
@@ -1594,7 +1594,6 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
L"PowerToys.PowerRename.exe",
|
||||
L"PowerToys.ImageResizer.exe",
|
||||
L"PowerToys.LightSwitchService.exe",
|
||||
L"PowerToys.PowerDisplay.exe",
|
||||
L"PowerToys.GcodeThumbnailProvider.exe",
|
||||
L"PowerToys.BgcodeThumbnailProvider.exe",
|
||||
L"PowerToys.PdfThumbnailProvider.exe",
|
||||
|
||||
@@ -47,7 +47,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
call move /Y ..\..\..\NewPlus.wxs.bk ..\..\..\NewPlus.wxs
|
||||
call move /Y ..\..\..\Peek.wxs.bk ..\..\..\Peek.wxs
|
||||
call move /Y ..\..\..\PowerRename.wxs.bk ..\..\..\PowerRename.wxs
|
||||
call move /Y ..\..\..\PowerDisplay.wxs.bk ..\..\..\PowerDisplay.wxs
|
||||
call move /Y ..\..\..\Product.wxs.bk ..\..\..\Product.wxs
|
||||
call move /Y ..\..\..\RegistryPreview.wxs.bk ..\..\..\RegistryPreview.wxs
|
||||
call move /Y ..\..\..\Resources.wxs.bk ..\..\..\Resources.wxs
|
||||
@@ -124,7 +123,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
<Compile Include="KeyboardManager.wxs" />
|
||||
<Compile Include="Peek.wxs" />
|
||||
<Compile Include="PowerRename.wxs" />
|
||||
<Compile Include="PowerDisplay.wxs" />
|
||||
<Compile Include="DscResources.wxs" />
|
||||
<Compile Include="RegistryPreview.wxs" />
|
||||
<Compile Include="Run.wxs" />
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
<ComponentGroupRef Id="LightSwitchComponentGroup" />
|
||||
<ComponentGroupRef Id="PeekComponentGroup" />
|
||||
<ComponentGroupRef Id="PowerRenameComponentGroup" />
|
||||
<ComponentGroupRef Id="PowerDisplayComponentGroup" />
|
||||
<ComponentGroupRef Id="RegistryPreviewComponentGroup" />
|
||||
<ComponentGroupRef Id="RunComponentGroup" />
|
||||
<ComponentGroupRef Id="SettingsComponentGroup" />
|
||||
|
||||
@@ -212,10 +212,6 @@ Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRo
|
||||
Generate-FileList -fileDepsJson "" -fileListName PowerRenameAssetsFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerRename\"
|
||||
Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs
|
||||
|
||||
#PowerDisplay
|
||||
Generate-FileList -fileDepsJson "" -fileListName PowerDisplayAssetsFiles -wxsFilePath $PSScriptRoot\PowerDisplay.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerDisplay\"
|
||||
Generate-FileComponents -fileListName "PowerDisplayAssetsFiles" -wxsFilePath $PSScriptRoot\PowerDisplay.wxs
|
||||
|
||||
#RegistryPreview
|
||||
Generate-FileList -fileDepsJson "" -fileListName RegistryPreviewAssetsFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\RegistryPreview\"
|
||||
Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.250325.1" targetFramework="native" />
|
||||
</packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -28,7 +28,6 @@ namespace ExprtkCalculator::internal
|
||||
std::wstring ToWStringFullPrecision(double value)
|
||||
{
|
||||
std::wostringstream oss;
|
||||
oss.imbue(std::locale::classic());
|
||||
oss << std::fixed << std::setprecision(15) << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
@@ -36,10 +36,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredPowerDisplayEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredGrabAndMoveEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredGrabAndMoveEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredFancyZonesEnabledValue());
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredPowerDisplayEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();
|
||||
|
||||
@@ -71,10 +71,5 @@ namespace PowerToys.GPOWrapperProjection
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredLightSwitchEnabledValue();
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetConfiguredGrabAndMoveEnabledValue()
|
||||
{
|
||||
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredGrabAndMoveEnabledValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace ManagedCommon
|
||||
ShortcutGuide,
|
||||
PowerOCR,
|
||||
Workspaces,
|
||||
GrabAndMove,
|
||||
ZoomIt,
|
||||
GeneralSettings,
|
||||
}
|
||||
|
||||
@@ -145,10 +145,6 @@ namespace CommonSharedConstants
|
||||
|
||||
// Path to the events used by ZoomIt
|
||||
const wchar_t ZOOMIT_REFRESH_SETTINGS_EVENT[] = L"Local\\PowerToysZoomIt-RefreshSettingsEvent-f053a563-d519-4b0d-8152-a54489c13324";
|
||||
|
||||
// Path to the events used by GrabAndMove
|
||||
const wchar_t GRABANDMOVE_REFRESH_SETTINGS_EVENT[] = L"Local\\PowerToysGrabAndMove-RefreshSettingsEvent-a7b3c1d2-4e5f-6a7b-8c9d-0e1f2a3b4c5d";
|
||||
const wchar_t GRABANDMOVE_EXIT_EVENT[] = L"Local\\PowerToysGrabAndMove-ExitEvent-b8c4d2e3-5f6a-7b8c-9d0e-1f2a3b4c5d6e";
|
||||
const wchar_t ZOOMIT_EXIT_EVENT[] = L"Local\\PowerToysZoomIt-ExitEvent-36641ce6-df02-4eac-abea-a3fbf9138220";
|
||||
const wchar_t ZOOMIT_ZOOM_EVENT[] = L"Local\\PowerToysZoomIt-ZoomEvent-1e4190d7-94bc-4ad5-adc0-9a8fd07cb393";
|
||||
const wchar_t ZOOMIT_DRAW_EVENT[] = L"Local\\PowerToysZoomIt-DrawEvent-56338997-404d-4549-bd9a-d132b6766975";
|
||||
|
||||
@@ -84,7 +84,6 @@ struct LogSettings
|
||||
inline const static std::string zoomItLoggerName = "zoom-it";
|
||||
inline const static std::string lightSwitchLoggerName = "light-switch";
|
||||
inline const static std::string powerDisplayLoggerName = "powerdisplay";
|
||||
inline const static std::string grabAndMoveLoggerName = "grabandmove";
|
||||
inline const static int retention = 30;
|
||||
std::wstring logLevel;
|
||||
LogSettings();
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace powertoys_gpo
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH = L"ConfigureEnabledUtilityLightSwitch";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_POWER_DISPLAY = L"ConfigureEnabledUtilityPowerDisplay";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_GRAB_AND_MOVE = L"ConfigureEnabledUtilityGrabAndMove";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH = L"ConfigureEnabledUtilityFileLocksmith";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview";
|
||||
@@ -318,11 +317,6 @@ namespace powertoys_gpo
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_POWER_DISPLAY);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredGrabAndMoveEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_GRAB_AND_MOVE);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredFancyZonesEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_FANCYZONES);
|
||||
|
||||
@@ -26,7 +26,7 @@ public class BaseDscTest
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the resource string.</param>
|
||||
/// <param name="args">The arguments to format the resource string with.</param>
|
||||
/// <returns>The formatted resource string.</returns>
|
||||
/// <returns></returns>
|
||||
public string GetResourceString(string name, params string[] args)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, _resourceManager.GetString(name, CultureInfo.InvariantCulture), args);
|
||||
@@ -35,9 +35,9 @@ public class BaseDscTest
|
||||
/// <summary>
|
||||
/// Execute a dsc command with the provided arguments.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the DSC command to execute.</typeparam>
|
||||
/// <param name="args">The command-line arguments to pass to the DSC command.</param>
|
||||
/// <returns>The result of the DSC command execution.</returns>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
protected DscExecuteResult ExecuteDscCommand<T>(params string[] args)
|
||||
where T : Command, new()
|
||||
{
|
||||
|
||||
@@ -47,6 +47,6 @@ public interface ISettingsFunctionData
|
||||
/// <summary>
|
||||
/// Gets the schema for the settings resource object.
|
||||
/// </summary>
|
||||
/// <returns>The JSON schema string for the settings resource object.</returns>
|
||||
/// <returns></returns>
|
||||
public string Schema();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class BaseResourceObject
|
||||
/// <summary>
|
||||
/// Generates a JSON representation of the resource object.
|
||||
/// </summary>
|
||||
/// <returns>A JSON representation of the resource object.</returns>
|
||||
/// <returns></returns>
|
||||
public JsonNode ToJson()
|
||||
{
|
||||
return JsonSerializer.SerializeToNode(this, GetType(), _options) ?? new JsonObject();
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
<definition name="SUPPORTED_POWERTOYS_0_96_0" displayName="$(string.SUPPORTED_POWERTOYS_0_96_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_97_0" displayName="$(string.SUPPORTED_POWERTOYS_0_97_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_98_0" displayName="$(string.SUPPORTED_POWERTOYS_0_98_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_99_0" displayName="$(string.SUPPORTED_POWERTOYS_0_99_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1)"/>
|
||||
</definitions>
|
||||
</supportedOn>
|
||||
@@ -151,9 +150,10 @@
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<!-- TEMPORARILY_DISABLED: PowerDisplay
|
||||
<policy name="ConfigureEnabledUtilityPowerDisplay" class="Both" displayName="$(string.ConfigureEnabledUtilityPowerDisplay)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityPowerDisplay">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_99_0" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_95_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
@@ -161,6 +161,7 @@
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
-->
|
||||
<policy name="ConfigureEnabledUtilityEnvironmentVariables" class="Both" displayName="$(string.ConfigureEnabledUtilityEnvironmentVariables)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityEnvironmentVariables">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
<string id="SUPPORTED_POWERTOYS_0_96_0">PowerToys version 0.96.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_97_0">PowerToys version 0.97.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_98_0">PowerToys version 0.98.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_99_0">PowerToys version 0.99.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1">From PowerToys version 0.64.0 until PowerToys version 0.87.1</string>
|
||||
|
||||
<string id="ConfigureAllUtilityGlobalEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
|
||||
@@ -250,7 +249,7 @@ If you don't configure this policy, the user will be able to control the setting
|
||||
<string id="ConfigureEnabledUtilityCmdPal">CmdPal: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityCropAndLock">Crop And Lock: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityLightSwitch">Light Switch: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityPowerDisplay">PowerDisplay: Configure enabled state</string>
|
||||
<!-- <string id="ConfigureEnabledUtilityPowerDisplay">PowerDisplay: Configure enabled state</string> --><!-- TEMPORARILY_DISABLED: PowerDisplay -->
|
||||
<string id="ConfigureEnabledUtilityEnvironmentVariables">Environment Variables: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityFancyZones">FancyZones: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityFileLocksmith">File Locksmith: Configure enabled state</string>
|
||||
|
||||
@@ -70,12 +70,12 @@
|
||||
Spacing="2">
|
||||
<TextBlock
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{x:Bind Header, Mode=OneTime}"
|
||||
Text="{x:Bind Header, Mode=OneWay}"
|
||||
TextWrapping="NoWrap" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Timestamp, Converter={StaticResource DateTimeToFriendlyStringConverter}, Mode=OneTime}" />
|
||||
Text="{x:Bind Timestamp, Converter={StaticResource DateTimeToFriendlyStringConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -29,31 +29,31 @@
|
||||
Padding="-9,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind ShortcutText, Mode=OneTime}"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind ShortcutText, Mode=OneWay}"
|
||||
AutomationProperties.AutomationControlType="ListItem"
|
||||
AutomationProperties.FullDescription="{x:Bind ToolTip, Mode=OneTime}"
|
||||
AutomationProperties.HelpText="{x:Bind Name, Mode=OneTime}"
|
||||
AutomationProperties.Name="{x:Bind AccessibleName, Mode=OneTime}">
|
||||
AutomationProperties.FullDescription="{x:Bind ToolTip, Mode=OneWay}"
|
||||
AutomationProperties.HelpText="{x:Bind Name, Mode=OneWay}"
|
||||
AutomationProperties.Name="{x:Bind AccessibleName, Mode=OneWay}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="48" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="{x:Bind ToolTip, Mode=OneTime}" />
|
||||
<TextBlock Text="{x:Bind ToolTip, Mode=OneWay}" />
|
||||
</ToolTipService.ToolTip>
|
||||
<FontIcon
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind IconGlyph, Mode=OneTime}" />
|
||||
Glyph="{x:Bind IconGlyph, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
x:Phase="1"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name, Mode=OneTime}" />
|
||||
Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="0,0,8,0"
|
||||
@@ -61,7 +61,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ShortcutText, Mode=OneTime}" />
|
||||
Text="{x:Bind ShortcutText, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</controls:PasteFormatTemplateSelector.ItemTemplate>
|
||||
@@ -83,13 +83,13 @@
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind IconGlyph, Mode=OneTime}" />
|
||||
Glyph="{x:Bind IconGlyph, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
x:Phase="1"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name, Mode=OneTime}" />
|
||||
Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</controls:PasteFormatTemplateSelector.ItemTemplateDisabled>
|
||||
@@ -198,7 +198,7 @@
|
||||
<ItemsView.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:ClipboardItem">
|
||||
<ItemContainer
|
||||
AutomationProperties.Name="{x:Bind Description, Mode=OneTime}"
|
||||
AutomationProperties.Name="{x:Bind Description, Mode=OneWay}"
|
||||
CornerRadius="16"
|
||||
ToolTipService.ToolTip="{x:Bind Content}">
|
||||
<Grid
|
||||
|
||||
@@ -496,119 +496,23 @@ private:
|
||||
|
||||
if (!GetGUIThreadInfo(0, &gui_info))
|
||||
{
|
||||
Logger::warn(L"Auto-copy: GetGUIThreadInfo failed (error={})", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HWND target = gui_info.hwndFocus ? gui_info.hwndFocus : gui_info.hwndActive;
|
||||
if (!target)
|
||||
{
|
||||
Logger::warn(L"Auto-copy: no focused or active window found");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD_PTR result = 0;
|
||||
auto sendResult = SendMessageTimeout(target, WM_COPY, 0, 0, SMTO_ABORTIFHUNG | SMTO_BLOCK, 50, &result);
|
||||
return sendResult != 0;
|
||||
}
|
||||
|
||||
// Helper: poll clipboard sequence number for a change from initial_sequence.
|
||||
// Returns true if the sequence number changed within the given number of polls.
|
||||
bool poll_clipboard_sequence(DWORD initial_sequence, int poll_attempts, std::chrono::milliseconds poll_delay)
|
||||
{
|
||||
for (int poll = 0; poll < poll_attempts; ++poll)
|
||||
{
|
||||
if (GetClipboardSequenceNumber() != initial_sequence)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
std::this_thread::sleep_for(poll_delay);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper: send Ctrl+C via SendInput, releasing any held modifier keys first
|
||||
// (the hotkey combination may still have modifiers physically pressed).
|
||||
bool send_ctrl_c_input()
|
||||
{
|
||||
std::vector<INPUT> inputs;
|
||||
|
||||
// Release all modifier keys that are currently held down from the hotkey.
|
||||
// Without this, the target app sees e.g. Win+Shift+Ctrl+C instead of just Ctrl+C.
|
||||
try_inject_modifier_key_up(inputs, VK_LCONTROL);
|
||||
try_inject_modifier_key_up(inputs, VK_RCONTROL);
|
||||
try_inject_modifier_key_up(inputs, VK_LWIN);
|
||||
try_inject_modifier_key_up(inputs, VK_RWIN);
|
||||
try_inject_modifier_key_up(inputs, VK_LSHIFT);
|
||||
try_inject_modifier_key_up(inputs, VK_RSHIFT);
|
||||
try_inject_modifier_key_up(inputs, VK_LMENU);
|
||||
try_inject_modifier_key_up(inputs, VK_RMENU);
|
||||
|
||||
// Ctrl down
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// C down
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// C up
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// Ctrl up
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
// Restore modifiers that were held down
|
||||
try_inject_modifier_key_restore(inputs, VK_LCONTROL);
|
||||
try_inject_modifier_key_restore(inputs, VK_RCONTROL);
|
||||
try_inject_modifier_key_restore(inputs, VK_LWIN);
|
||||
try_inject_modifier_key_restore(inputs, VK_RWIN);
|
||||
try_inject_modifier_key_restore(inputs, VK_LSHIFT);
|
||||
try_inject_modifier_key_restore(inputs, VK_RSHIFT);
|
||||
try_inject_modifier_key_restore(inputs, VK_LMENU);
|
||||
try_inject_modifier_key_restore(inputs, VK_RMENU);
|
||||
|
||||
// Prevent Start Menu from activating after Win key release/restore
|
||||
INPUT dummyEvent = {};
|
||||
dummyEvent.type = INPUT_KEYBOARD;
|
||||
dummyEvent.ki.wVk = 0xFF;
|
||||
dummyEvent.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
inputs.push_back(dummyEvent);
|
||||
|
||||
auto uSent = SendInput(static_cast<UINT>(inputs.size()), inputs.data(), sizeof(INPUT));
|
||||
if (uSent != inputs.size())
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
auto errorMessage = get_last_error_message(errorCode);
|
||||
Logger::error(L"SendInput failed for Ctrl+C. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L"");
|
||||
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return SendMessageTimeout(target,
|
||||
WM_COPY,
|
||||
0,
|
||||
0,
|
||||
SMTO_ABORTIFHUNG | SMTO_BLOCK,
|
||||
50,
|
||||
&result) != 0;
|
||||
}
|
||||
|
||||
bool send_copy_selection()
|
||||
@@ -622,30 +526,78 @@ private:
|
||||
for (int attempt = 0; attempt < copy_attempts; ++attempt)
|
||||
{
|
||||
const auto initial_sequence = GetClipboardSequenceNumber();
|
||||
copy_succeeded = try_send_copy_message();
|
||||
|
||||
// Strategy 1: Try WM_COPY message (works for standard Win32 controls)
|
||||
bool wm_copy_sent = try_send_copy_message();
|
||||
|
||||
if (wm_copy_sent)
|
||||
if (!copy_succeeded)
|
||||
{
|
||||
if (poll_clipboard_sequence(initial_sequence, clipboard_poll_attempts, clipboard_poll_delay))
|
||||
std::vector<INPUT> inputs;
|
||||
|
||||
// send Ctrl+C (key downs and key ups)
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
// Avoid triggering detection by the centralized keyboard hook.
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = 0x43; // C
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
// Avoid triggering detection by the centralized keyboard hook.
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
{
|
||||
INPUT input_event = {};
|
||||
input_event.type = INPUT_KEYBOARD;
|
||||
input_event.ki.wVk = VK_CONTROL;
|
||||
input_event.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG;
|
||||
inputs.push_back(input_event);
|
||||
}
|
||||
|
||||
auto uSent = SendInput(static_cast<UINT>(inputs.size()), inputs.data(), sizeof(INPUT));
|
||||
if (uSent != inputs.size())
|
||||
{
|
||||
DWORD errorCode = GetLastError();
|
||||
auto errorMessage = get_last_error_message(errorCode);
|
||||
Logger::error(L"SendInput failed for Ctrl+C. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L"");
|
||||
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput");
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_succeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 2: If WM_COPY didn't work, try SendInput Ctrl+C (works for Electron, browsers, etc.)
|
||||
if (!copy_succeeded)
|
||||
if (copy_succeeded)
|
||||
{
|
||||
const auto sequence_before_ctrl_c = GetClipboardSequenceNumber();
|
||||
|
||||
if (send_ctrl_c_input())
|
||||
bool sequence_changed = false;
|
||||
for (int poll_attempt = 0; poll_attempt < clipboard_poll_attempts; ++poll_attempt)
|
||||
{
|
||||
if (poll_clipboard_sequence(sequence_before_ctrl_c, clipboard_poll_attempts, clipboard_poll_delay))
|
||||
if (GetClipboardSequenceNumber() != initial_sequence)
|
||||
{
|
||||
copy_succeeded = true;
|
||||
sequence_changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(clipboard_poll_delay);
|
||||
}
|
||||
|
||||
copy_succeeded = sequence_changed;
|
||||
}
|
||||
|
||||
if (copy_succeeded)
|
||||
@@ -659,11 +611,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
if (!copy_succeeded)
|
||||
{
|
||||
Logger::warn(L"Auto-copy: all {} copy attempts failed — the target application did not update the clipboard after WM_COPY and Ctrl+C", copy_attempts);
|
||||
}
|
||||
|
||||
return copy_succeeded;
|
||||
}
|
||||
|
||||
@@ -1030,7 +977,6 @@ public:
|
||||
{
|
||||
if (!send_copy_selection())
|
||||
{
|
||||
Logger::warn(L"Auto-copy: failed to copy selection for custom action index {} — aborting action", custom_action_index);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
if (argc < 2)
|
||||
{
|
||||
Logger::warn("No arguments provided");
|
||||
return { 1, get_usage(strings), L"help" };
|
||||
return { 1, get_usage(strings) };
|
||||
}
|
||||
|
||||
bool json_output = false;
|
||||
@@ -156,18 +156,18 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
catch (...)
|
||||
{
|
||||
Logger::error("Invalid timeout value");
|
||||
return { 1, strings.GetString(IDS_ERROR_INVALID_TIMEOUT), L"query-wait" };
|
||||
return { 1, strings.GetString(IDS_ERROR_INVALID_TIMEOUT) };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error("Timeout argument missing");
|
||||
return { 1, strings.GetString(IDS_ERROR_TIMEOUT_ARG), L"query-wait" };
|
||||
return { 1, strings.GetString(IDS_ERROR_TIMEOUT_ARG) };
|
||||
}
|
||||
}
|
||||
else if (arg == L"--help")
|
||||
{
|
||||
return { 0, get_usage(strings), L"help" };
|
||||
return { 0, get_usage(strings) };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -178,7 +178,7 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
if (paths.empty())
|
||||
{
|
||||
Logger::error("No paths specified");
|
||||
return { 1, strings.GetString(IDS_ERROR_NO_PATHS), L"query" };
|
||||
return { 1, strings.GetString(IDS_ERROR_NO_PATHS) };
|
||||
}
|
||||
|
||||
Logger::info("Processing {} paths", paths.size());
|
||||
@@ -213,13 +213,13 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
{
|
||||
Logger::warn("Timeout waiting for files to be unlocked");
|
||||
ss << strings.GetString(IDS_TIMEOUT);
|
||||
return { 1, ss.str(), L"query-wait" };
|
||||
return { 1, ss.str() };
|
||||
}
|
||||
}
|
||||
|
||||
Sleep(200);
|
||||
}
|
||||
return { 0, ss.str(), L"query-wait" };
|
||||
return { 0, ss.str() };
|
||||
}
|
||||
|
||||
auto results = finder.find(paths);
|
||||
@@ -244,6 +244,5 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
|
||||
output_ss << get_text(results, strings);
|
||||
}
|
||||
|
||||
std::wstring cmd_name = kill ? L"kill" : (json_output ? L"query-json" : L"query");
|
||||
return { 0, output_ss.str(), cmd_name };
|
||||
return { 0, output_ss.str() };
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ struct CommandResult
|
||||
{
|
||||
int exit_code;
|
||||
std::wstring output;
|
||||
std::wstring command_name;
|
||||
};
|
||||
|
||||
struct IProcessFinder
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "CLILogic.h"
|
||||
#include "FileLocksmithLib/FileLocksmith.h"
|
||||
#include "FileLocksmithLib/Trace.h"
|
||||
#include <iostream>
|
||||
#include "resource.h"
|
||||
#include <common/logger/logger.h>
|
||||
@@ -48,7 +47,6 @@ struct RealStringProvider : IStringProvider
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
{
|
||||
winrt::init_apartment();
|
||||
Trace::RegisterProvider();
|
||||
LoggerHelpers::init_logger(L"FileLocksmithCLI", L"", LogSettings::fileLocksmithLoggerName);
|
||||
Logger::info("FileLocksmithCLI started");
|
||||
|
||||
@@ -67,10 +65,7 @@ int wmain(int argc, wchar_t* argv[])
|
||||
Logger::info("Command succeeded");
|
||||
}
|
||||
|
||||
Trace::CLICommand(result.command_name.c_str(), result.exit_code == 0);
|
||||
|
||||
std::wcout << result.output;
|
||||
Trace::UnregisterProvider();
|
||||
return result.exit_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(1, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(1, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"help"), result.command_name);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestHelp)
|
||||
@@ -65,7 +64,6 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(2, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"help"), result.command_name);
|
||||
}
|
||||
|
||||
TEST_METHOD(TestFindProcesses)
|
||||
@@ -79,7 +77,6 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(2, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"query"), result.command_name);
|
||||
Assert::IsTrue(result.output.find(L"123") != std::wstring::npos);
|
||||
Assert::IsTrue(result.output.find(L"process") != std::wstring::npos);
|
||||
}
|
||||
@@ -97,7 +94,6 @@ namespace FileLocksmithCLIUnitTests
|
||||
Microsoft::VisualStudio::CppUnitTestFramework::Logger::WriteMessage(result.output.c_str());
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"query-json"), result.command_name);
|
||||
Assert::IsTrue(result.output.find(L"\"pid\"") != std::wstring::npos);
|
||||
Assert::IsTrue(result.output.find(L"123") != std::wstring::npos);
|
||||
}
|
||||
@@ -113,7 +109,6 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(3, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(0, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"kill"), result.command_name);
|
||||
Assert::AreEqual((size_t)1, terminator.terminatedPids.size());
|
||||
Assert::AreEqual((DWORD)123, terminator.terminatedPids[0]);
|
||||
}
|
||||
@@ -130,7 +125,6 @@ namespace FileLocksmithCLIUnitTests
|
||||
auto result = run_command(5, argv, finder, terminator, strings);
|
||||
|
||||
Assert::AreEqual(1, result.exit_code);
|
||||
Assert::AreEqual(std::wstring(L"query-wait"), result.command_name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,14 +49,3 @@ void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CLICommand(_In_ PCWSTR commandName, _In_ bool successful) noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"FileLocksmith_CLICommand",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(commandName, "CommandName"),
|
||||
TraceLoggingBoolean(successful, "Successful"));
|
||||
}
|
||||
|
||||
@@ -11,5 +11,4 @@ public:
|
||||
static void Invoked() noexcept;
|
||||
static void InvokedRet(_In_ HRESULT hr) noexcept;
|
||||
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
|
||||
static void CLICommand(_In_ PCWSTR commandName, _In_ bool successful) noexcept;
|
||||
};
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 766 B |
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
|
||||
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
@@ -1,102 +0,0 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_APP_ICON ICON "GrabAndMove.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
@@ -1,197 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{568c4c30-2e3c-4c2c-a691-007362073765}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>GrabAndMove</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>GrabAndMove</ProjectName>
|
||||
<TargetName>PowerToys.GrabAndMove</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(RepoRoot)$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>
|
||||
$(RepoRoot)src\common;
|
||||
$(RepoRoot)src\common\SettingsAPI;
|
||||
$(RepoRoot)src\;
|
||||
%(AdditionalIncludeDirectories)
|
||||
</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
<AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GrabAndMove.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)src\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446D-23F7-4023-9BB3-8657F904AF99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="GrabAndMove.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="GrabAndMove.manifest" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{195243ad-53ca-40c9-8879-b9efef4fc26d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{9bdf974b-a58f-4af8-aed8-4882381f7172}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{0e19fd51-9939-4511-b8cb-d144c0e2a670}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="GrabAndMove.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GrabAndMove.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
#include "pch.h"
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <commctrl.h>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by GrabAndMove.rc
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#define IDI_APP_ICON 101
|
||||
#define IDR_TRAY_MENU 102
|
||||
#define IDM_EXIT 1001
|
||||
|
||||
#define WM_TRAY_ICON (WM_USER + 1)
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys GrabAndMove"
|
||||
#define INTERNAL_NAME "PowerToys.GrabAndMove"
|
||||
#define ORIGINAL_FILENAME "PowerToys.GrabAndMove.exe"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -1,36 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
@@ -1,208 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{2c3f7770-4e57-46b7-8dc1-7428a383d0db}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>GrabAndMoveModuleInterface</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>GrabAndMoveModuleInterface</ProjectName>
|
||||
<TargetName>PowerToys.GrabAndMoveModuleInterface</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>$(RepoRoot)$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;GRABANDMOVEMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(RepoRoot)src\common\inc;$(RepoRoot)src\common\Telemetry;..\..\;$(RepoRoot)src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GrabAndMoveModuleInterface.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)src\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(RepoRoot)src\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="$(RepoRoot)deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(RepoRoot)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,216 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
#include "trace.h"
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
Trace::RegisterProvider();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
Trace::UnregisterProvider();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// The PowerToy name that will be shown in the settings.
|
||||
const static wchar_t* MODULE_NAME = L"GrabAndMove";
|
||||
// Add a description that will be shown in the module settings page.
|
||||
const static wchar_t* MODULE_DESC = L"Move and resize windows with Alt+Drag (left button to move, right button to resize).";
|
||||
|
||||
class GrabAndMoveInterface : public PowertoyModuleIface
|
||||
{
|
||||
private:
|
||||
bool m_enabled = false;
|
||||
HANDLE m_process{ nullptr };
|
||||
HANDLE m_reload_settings_event_handle{ nullptr };
|
||||
HANDLE m_exit_event_handle{ nullptr };
|
||||
|
||||
public:
|
||||
GrabAndMoveInterface()
|
||||
{
|
||||
LoggerHelpers::init_logger(L"GrabAndMove", L"ModuleInterface", LogSettings::grabAndMoveLoggerName);
|
||||
m_reload_settings_event_handle = CreateDefaultEvent(CommonSharedConstants::GRABANDMOVE_REFRESH_SETTINGS_EVENT);
|
||||
m_exit_event_handle = CreateDefaultEvent(CommonSharedConstants::GRABANDMOVE_EXIT_EVENT);
|
||||
}
|
||||
|
||||
virtual const wchar_t* get_key() override
|
||||
{
|
||||
return L"GrabAndMove";
|
||||
}
|
||||
|
||||
virtual void destroy() override
|
||||
{
|
||||
disable();
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual const wchar_t* get_name() override
|
||||
{
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
|
||||
{
|
||||
return powertoys_gpo::getConfiguredGrabAndMoveEnabledValue();
|
||||
}
|
||||
|
||||
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
||||
{
|
||||
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
|
||||
PowerToysSettings::Settings settings(hinstance, get_name());
|
||||
settings.set_description(MODULE_DESC);
|
||||
settings.set_overview_link(L"https://aka.ms/powertoys");
|
||||
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
virtual void set_config(const wchar_t* config) override
|
||||
{
|
||||
try
|
||||
{
|
||||
auto values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
|
||||
values.save_to_settings_file();
|
||||
|
||||
// Signal the GrabAndMove process to reload settings
|
||||
if (m_reload_settings_event_handle)
|
||||
{
|
||||
SetEvent(m_reload_settings_event_handle);
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
Logger::error("[GrabAndMove] set_config: Failed to parse or apply config.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void enable()
|
||||
{
|
||||
Logger::info(L"Enabling GrabAndMove module...");
|
||||
|
||||
if (m_process && WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT)
|
||||
{
|
||||
m_enabled = true;
|
||||
Trace::Enable(true);
|
||||
Logger::debug(L"GrabAndMove process already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_process)
|
||||
{
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
m_enabled = false;
|
||||
|
||||
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||
std::wstring args = std::to_wstring(powertoys_pid);
|
||||
std::wstring exe_name = L"PowerToys.GrabAndMove.exe";
|
||||
|
||||
std::wstring resolved_path(MAX_PATH, L'\0');
|
||||
DWORD result = SearchPathW(
|
||||
nullptr,
|
||||
exe_name.c_str(),
|
||||
nullptr,
|
||||
static_cast<DWORD>(resolved_path.size()),
|
||||
resolved_path.data(),
|
||||
nullptr);
|
||||
|
||||
if (result == 0 || result >= resolved_path.size())
|
||||
{
|
||||
Logger::error(
|
||||
L"Failed to locate GrabAndMove executable named '{}' at location '{}'",
|
||||
exe_name,
|
||||
resolved_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
resolved_path.resize(result);
|
||||
Logger::debug(L"Resolved executable path: {}", resolved_path);
|
||||
|
||||
std::wstring command_line = L"\"" + resolved_path + L"\" " + args;
|
||||
|
||||
STARTUPINFO si = { sizeof(si) };
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
if (!CreateProcessW(
|
||||
resolved_path.c_str(),
|
||||
command_line.data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi))
|
||||
{
|
||||
Logger::error(L"Failed to launch GrabAndMove process. {}", get_last_error_or_default(GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info(L"GrabAndMove process launched successfully (PID: {}).", pi.dwProcessId);
|
||||
m_process = pi.hProcess;
|
||||
m_enabled = true;
|
||||
Trace::Enable(true);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
|
||||
virtual void disable()
|
||||
{
|
||||
Logger::info("GrabAndMove disabling");
|
||||
m_enabled = false;
|
||||
|
||||
if (m_exit_event_handle)
|
||||
{
|
||||
SetEvent(m_exit_event_handle);
|
||||
}
|
||||
|
||||
if (m_process)
|
||||
{
|
||||
constexpr DWORD timeout_ms = 1500;
|
||||
DWORD result = WaitForSingleObject(m_process, timeout_ms);
|
||||
|
||||
if (result == WAIT_TIMEOUT)
|
||||
{
|
||||
Logger::warn("GrabAndMove: Process didn't exit in time. Forcing termination.");
|
||||
TerminateProcess(m_process, 0);
|
||||
}
|
||||
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
Trace::Enable(false);
|
||||
}
|
||||
|
||||
virtual bool is_enabled() override
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new GrabAndMoveInterface();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include "pch.h"
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
@@ -1,12 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "GrabAndMove Module"
|
||||
#define INTERNAL_NAME "GrabAndMove"
|
||||
#define ORIGINAL_FILENAME "PowerToys.GrabAndMoveModuleInterface.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
#include <TraceLoggingProvider.h>
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToys",
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
void Trace::RegisterProvider()
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::UnregisterProvider()
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::Enable(bool enabled) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"GrabAndMove_EnableGrabAndMove",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(enabled, "Enabled"));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
#include <common/telemetry/ProjectTelemetry.h>
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hProvider);
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void Enable(bool enabled) noexcept;
|
||||
};
|
||||
@@ -1,180 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Linq;
|
||||
|
||||
using HostsUILib;
|
||||
using HostsUILib.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Hosts.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class ValidationHelperTest
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow("0.0.0.0")]
|
||||
[DataRow("127.0.0.1")]
|
||||
[DataRow("192.168.1.1")]
|
||||
[DataRow("255.255.255.255")]
|
||||
[DataRow("10.0.0.1")]
|
||||
[DataRow("172.16.0.1")]
|
||||
[DataRow("1.2.3.4")]
|
||||
[DataRow("01.01.01.01")]
|
||||
[DataRow("0.0.0.1")]
|
||||
public void ValidIPv4_ValidAddresses_ReturnsTrue(string address)
|
||||
{
|
||||
Assert.IsTrue(ValidationHelper.ValidIPv4(address));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("256.0.0.0")]
|
||||
[DataRow("0.256.0.0")]
|
||||
[DataRow("0.0.256.0")]
|
||||
[DataRow("0.0.0.256")]
|
||||
[DataRow("999.999.999.999")]
|
||||
[DataRow("1.2.3")]
|
||||
[DataRow("1.2.3.4.5")]
|
||||
[DataRow("1.2.3.")]
|
||||
[DataRow(".1.2.3")]
|
||||
[DataRow("1..2.3")]
|
||||
[DataRow("abc.def.ghi.jkl")]
|
||||
[DataRow("192.168.1.1/24")]
|
||||
[DataRow("192.168.1.1:80")]
|
||||
[DataRow("192.168.1")]
|
||||
[DataRow("-1.0.0.0")]
|
||||
public void ValidIPv4_InvalidAddresses_ReturnsFalse(string address)
|
||||
{
|
||||
Assert.IsFalse(ValidationHelper.ValidIPv4(address));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow("")]
|
||||
[DataRow(" ")]
|
||||
[DataRow("\t")]
|
||||
[DataRow("\n")]
|
||||
public void ValidIPv4_NullOrWhitespace_ReturnsFalse(string address)
|
||||
{
|
||||
Assert.IsFalse(ValidationHelper.ValidIPv4(address));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("::1")]
|
||||
[DataRow("::")]
|
||||
[DataRow("2001:0db8:85a3:0000:0000:8a2e:0370:7334")]
|
||||
[DataRow("2001:db8:85a3:0:0:8a2e:370:7334")]
|
||||
[DataRow("2001:db8:85a3::8a2e:370:7334")]
|
||||
[DataRow("fe80::1")]
|
||||
[DataRow("ff02::1")]
|
||||
[DataRow("2001:db8::1")]
|
||||
[DataRow("::ffff:192.168.1.1")]
|
||||
[DataRow("fe80::1%eth0")]
|
||||
public void ValidIPv6_ValidAddresses_ReturnsTrue(string address)
|
||||
{
|
||||
Assert.IsTrue(ValidationHelper.ValidIPv6(address));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("2001:db8:85a3:0:0:8a2e:370:7334:extra")]
|
||||
[DataRow("gggg::1")]
|
||||
[DataRow("12345::1")]
|
||||
[DataRow("192.168.1.1")]
|
||||
[DataRow("::ffff:999.999.999.999")]
|
||||
[DataRow("hello")]
|
||||
[DataRow("2001:db8:85a3::8a2e:370:7334:1234:5678")]
|
||||
public void ValidIPv6_InvalidAddresses_ReturnsFalse(string address)
|
||||
{
|
||||
Assert.IsFalse(ValidationHelper.ValidIPv6(address));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow("")]
|
||||
[DataRow(" ")]
|
||||
[DataRow("\t")]
|
||||
public void ValidIPv6_NullOrWhitespace_ReturnsFalse(string address)
|
||||
{
|
||||
Assert.IsFalse(ValidationHelper.ValidIPv6(address));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("localhost")]
|
||||
[DataRow("example.com")]
|
||||
[DataRow("sub.domain.example.com")]
|
||||
[DataRow("my-host")]
|
||||
[DataRow("host1 host2")]
|
||||
[DataRow("host1 host2 host3")]
|
||||
[DataRow("example.com www.example.com")]
|
||||
public void ValidHosts_ValidHostnames_ReturnsTrue(string hosts)
|
||||
{
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: false));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(null)]
|
||||
[DataRow("")]
|
||||
[DataRow(" ")]
|
||||
[DataRow("\t")]
|
||||
public void ValidHosts_NullOrWhitespace_ReturnsFalse(string hosts)
|
||||
{
|
||||
Assert.IsFalse(ValidationHelper.ValidHosts(hosts, validateHostsLength: false));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_WithLengthValidation_ExceedsMaxCount_ReturnsFalse()
|
||||
{
|
||||
// Create a host string with one more than MaxHostsCount hosts
|
||||
var hosts = string.Join(" ", Enumerable.Range(1, Consts.MaxHostsCount + 1).Select(i => $"h{i}"));
|
||||
Assert.IsFalse(ValidationHelper.ValidHosts(hosts, validateHostsLength: true));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_WithLengthValidation_AtMaxCount_ReturnsTrue()
|
||||
{
|
||||
// Create a host string with exactly MaxHostsCount hosts
|
||||
var hosts = string.Join(" ", Enumerable.Range(1, Consts.MaxHostsCount).Select(i => $"h{i}"));
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: true));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_WithLengthValidation_BelowMaxCount_ReturnsTrue()
|
||||
{
|
||||
string hosts = "host1 host2 host3";
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: true));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_WithoutLengthValidation_ExceedsMaxCount_ReturnsTrue()
|
||||
{
|
||||
// When validateHostsLength is false, exceeding max count should still return true
|
||||
var hosts = string.Join(" ", Enumerable.Range(1, Consts.MaxHostsCount + 1).Select(i => $"h{i}"));
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts(hosts, validateHostsLength: false));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_SingleHost_ReturnsTrue()
|
||||
{
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts("localhost", validateHostsLength: true));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_InvalidHostname_ReturnsFalse()
|
||||
{
|
||||
Assert.IsFalse(ValidationHelper.ValidHosts("host_with!invalid@chars", validateHostsLength: false));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_HostWithSubdomains_ReturnsTrue()
|
||||
{
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts("sub.domain.example.com", validateHostsLength: true));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidHosts_MultipleValidHosts_WithLengthValidation_ReturnsTrue()
|
||||
{
|
||||
Assert.IsTrue(ValidationHelper.ValidHosts("example.com www.example.com api.example.com", validateHostsLength: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.250325.1" targetFramework="native" />
|
||||
</packages>
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -3,101 +3,16 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:PowerOCR"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Exit="Application_Exit"
|
||||
ShutdownMode="OnExplicitShutdown"
|
||||
Startup="Application_Startup"
|
||||
ThemeMode="System">
|
||||
Startup="Application_Startup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<FontFamily x:Key="SymbolThemeFontFamily">Segoe Fluent Icons, Segoe MDL2 Assets</FontFamily>
|
||||
<Style
|
||||
x:Key="SubtleButtonStyle"
|
||||
BasedOn="{StaticResource {x:Type Button}}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorDisabledBrush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="SubtleToggleButtonStyle"
|
||||
BasedOn="{StaticResource {x:Type ToggleButton}}"
|
||||
TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SubtleFillColorTertiaryBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{DynamicResource AccentFillColorDefaultBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorDisabledBrush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ThemesDictionary Theme="Dark" />
|
||||
<ui:ControlsDictionary />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
@@ -30,12 +29,11 @@ namespace PowerOCR;
|
||||
|
||||
internal sealed class ImageMethods
|
||||
{
|
||||
internal static bool PadImage(Bitmap image, [NotNullWhen(true)] out Bitmap? paddedBitmap, int minW = 64, int minH = 64)
|
||||
internal static Bitmap PadImage(Bitmap image, int minW = 64, int minH = 64)
|
||||
{
|
||||
if (image.Height >= minH && image.Width >= minW)
|
||||
{
|
||||
paddedBitmap = null;
|
||||
return false;
|
||||
return image;
|
||||
}
|
||||
|
||||
int width = Math.Max(image.Width + 16, minW + 16);
|
||||
@@ -47,9 +45,8 @@ internal sealed class ImageMethods
|
||||
|
||||
gd.Clear(image.GetPixel(0, 0));
|
||||
gd.DrawImageUnscaled(image, 8, 8);
|
||||
paddedBitmap = destination;
|
||||
|
||||
return true;
|
||||
return destination;
|
||||
}
|
||||
|
||||
internal static ImageSource GetWindowBoundsImage(OCROverlay passedWindow)
|
||||
@@ -80,15 +77,8 @@ internal sealed class ImageMethods
|
||||
bmp.Size,
|
||||
CopyPixelOperation.SourceCopy);
|
||||
|
||||
if (PadImage(bmp, out var paddedBmp))
|
||||
{
|
||||
bmp.Dispose();
|
||||
return paddedBmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bmp;
|
||||
}
|
||||
bmp = PadImage(bmp);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
internal static async Task<string> GetRegionsText(OCROverlay? passedWindow, Rectangle selectedRegion, Language? preferredLanguage)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<Window
|
||||
<Window
|
||||
x:Class="PowerOCR.OCROverlay"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:p="clr-namespace:PowerOCR.Properties"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
x:Name="TextExtractorWindow"
|
||||
Title="TextExtractor"
|
||||
ui:Design.Background="Transparent"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
Loaded="Window_Loaded"
|
||||
@@ -20,6 +22,26 @@
|
||||
WindowStyle="None"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.Resources>
|
||||
<Style BasedOn="{StaticResource DefaultToggleButtonStyle}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Margin" Value="4,0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultButtonStyle}" TargetType="{x:Type Button}">
|
||||
<Setter Property="Margin" Value="4,0" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Width" Value="32" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Viewbox>
|
||||
<Image x:Name="BackgroundImage" Stretch="UniformToFill" />
|
||||
@@ -79,11 +101,17 @@
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
d:Visibility="Visible"
|
||||
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
|
||||
BorderBrush="{DynamicResource SurfaceStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
Background="{DynamicResource ApplicationBackgroundBrush}"
|
||||
CornerRadius="8"
|
||||
Visibility="Collapsed">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="32"
|
||||
Opacity="0.28"
|
||||
RenderingBias="Performance"
|
||||
ShadowDepth="1" />
|
||||
</Border.Effect>
|
||||
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
@@ -105,67 +133,35 @@
|
||||
</ComboBox>
|
||||
<ToggleButton
|
||||
x:Name="SingleLineToggleButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
d:IsChecked="True"
|
||||
AutomationProperties.Name="{x:Static p:Resources.ResultTextSingleLine}"
|
||||
Click="SingleLineMenuItem_Click"
|
||||
IsChecked="{Binding IsChecked, ElementName=SingleLineMenuItem, Mode=TwoWay}"
|
||||
Style="{DynamicResource SubtleToggleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.ResultTextSingleLineShortcut}">
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
<ui:SymbolIcon FontSize="18" Symbol="SubtractSquare24" />
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
x:Name="TableToggleButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
d:IsChecked="True"
|
||||
AutomationProperties.Name="{x:Static p:Resources.ResultTextTable}"
|
||||
Click="TableToggleButton_Click"
|
||||
IsChecked="{Binding IsChecked, ElementName=TableMenuItem, Mode=TwoWay}"
|
||||
Style="{DynamicResource SubtleToggleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.ResultTextTableShortcut}">
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
<ui:SymbolIcon FontSize="18" Symbol="Table24" />
|
||||
</ToggleButton>
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Settings}"
|
||||
Click="SettingsMenuItem_Click"
|
||||
Style="{DynamicResource SubtleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.Settings}">
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
<ui:SymbolIcon FontSize="18" Symbol="Settings24" />
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="CancelButton"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="4,0"
|
||||
Padding="0"
|
||||
AutomationProperties.Name="{x:Static p:Resources.Cancel}"
|
||||
Click="CancelMenuItem_Click"
|
||||
Style="{DynamicResource SubtleButtonStyle}"
|
||||
ToolTip="{x:Static p:Resources.CancelShortcut}">
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Text="" />
|
||||
<ui:SymbolIcon FontSize="18" Symbol="Dismiss24" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -64,6 +64,8 @@ public partial class OCROverlay : Window
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this, Wpf.Ui.Controls.WindowBackdropType.None);
|
||||
|
||||
PopulateLanguageMenu();
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.ComponentModel.Composition" />
|
||||
<PackageReference Include="System.Drawing.Common" />
|
||||
<PackageReference Include="WPF-UI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -252,13 +252,11 @@ void AlwaysOnTop::ProcessCommand(HWND window)
|
||||
}
|
||||
|
||||
Sound::Type soundType = Sound::Type::Off;
|
||||
bool stateChanged = false;
|
||||
bool topmost = IsTopmost(window);
|
||||
if (topmost)
|
||||
{
|
||||
if (UnpinTopmostWindow(window))
|
||||
{
|
||||
stateChanged = true;
|
||||
auto iter = m_topmostWindows.find(window);
|
||||
if (iter != m_topmostWindows.end())
|
||||
{
|
||||
@@ -276,7 +274,6 @@ void AlwaysOnTop::ProcessCommand(HWND window)
|
||||
{
|
||||
if (PinTopmostWindow(window))
|
||||
{
|
||||
stateChanged = true;
|
||||
soundType = Sound::Type::On;
|
||||
AssignBorder(window);
|
||||
|
||||
@@ -284,7 +281,7 @@ void AlwaysOnTop::ProcessCommand(HWND window)
|
||||
}
|
||||
}
|
||||
|
||||
if (stateChanged && AlwaysOnTopSettings::settings()->enableSound)
|
||||
if (AlwaysOnTopSettings::settings()->enableSound)
|
||||
{
|
||||
m_sound.Play(soundType);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ using Awake.Core;
|
||||
using Awake.Core.Models;
|
||||
using Awake.Core.Native;
|
||||
using Awake.Properties;
|
||||
using Awake.Telemetry;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
@@ -69,7 +68,6 @@ namespace Awake
|
||||
if (parseResult.Errors.Count > 0)
|
||||
{
|
||||
// Shows errors and returns non-zero.
|
||||
LogCLITelemetry(successful: false);
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
@@ -98,7 +96,6 @@ namespace Awake
|
||||
{
|
||||
// Awake is already running - there is no need for us to process
|
||||
// anything further
|
||||
LogCLITelemetry(successful: false);
|
||||
Exit(Core.Constants.AppName + " is already running! Exiting the application.", 1);
|
||||
return 1;
|
||||
}
|
||||
@@ -106,7 +103,6 @@ namespace Awake
|
||||
{
|
||||
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
LogCLITelemetry(successful: false);
|
||||
Exit("PowerToys.Awake tried to start with a group policy setting that disables the tool. Please contact your system administrator.", 1);
|
||||
return 1;
|
||||
}
|
||||
@@ -129,9 +125,7 @@ namespace Awake
|
||||
Bridge.GetPwrCapabilities(out _powerCapabilities);
|
||||
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities, _serializerOptions));
|
||||
|
||||
var result = await rootCommand.InvokeAsync(args);
|
||||
LogCLITelemetry(successful: result == 0);
|
||||
return result;
|
||||
return await rootCommand.InvokeAsync(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,22 +216,6 @@ namespace Awake
|
||||
return rootCommand;
|
||||
}
|
||||
|
||||
private static void LogCLITelemetry(bool successful)
|
||||
{
|
||||
try
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new AwakeCLICommandEvent
|
||||
{
|
||||
CommandName = "awake",
|
||||
Successful = successful,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to log CLI telemetry: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void AwakeUnhandledExceptionCatcher(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
if (e.ExceptionObject is Exception exception)
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Awake.Telemetry
|
||||
{
|
||||
/// <summary>
|
||||
/// Telemetry event for Awake CLI command execution.
|
||||
/// </summary>
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class AwakeCLICommandEvent : EventBase, IEvent
|
||||
{
|
||||
public AwakeCLICommandEvent()
|
||||
{
|
||||
EventName = "Awake_CLICommand";
|
||||
CommandName = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the CLI command that was executed.
|
||||
/// </summary>
|
||||
public string CommandName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the command executed successfully.
|
||||
/// </summary>
|
||||
public bool Successful { get; set; }
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"solution": {
|
||||
"path": "..\\..\\..\\PowerToys.slnx",
|
||||
"projects": [
|
||||
"src\\common\\CalculatorEngineCommon\\CalculatorEngineCommon.vcxproj",
|
||||
"src\\common\\Common.UI.Controls\\Common.UI.Controls.csproj",
|
||||
"src\\common\\ManagedCommon\\ManagedCommon.csproj",
|
||||
"src\\common\\ManagedCsWin32\\ManagedCsWin32.csproj",
|
||||
"src\\common\\ManagedTelemetry\\Telemetry\\ManagedTelemetry.csproj",
|
||||
"src\\common\\interop\\PowerToys.Interop.vcxproj",
|
||||
"src\\common\\version\\version.vcxproj",
|
||||
"src\\modules\\cmdpal\\CmdPalKeyboardService\\CmdPalKeyboardService.vcxproj",
|
||||
"src\\modules\\cmdpal\\CmdPalModuleInterface\\CmdPalModuleInterface.vcxproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.Common\\Microsoft.CmdPal.Common.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI.ViewModels\\Microsoft.CmdPal.UI.ViewModels.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI\\Microsoft.CmdPal.UI.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.Terminal.UI\\Microsoft.Terminal.UI.vcxproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Common.UnitTests\\Microsoft.CmdPal.Common.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Apps.UnitTests\\Microsoft.CmdPal.Ext.Apps.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Registry.UnitTests\\Microsoft.CmdPal.Ext.Registry.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Shell.UnitTests\\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.System.UnitTests\\Microsoft.CmdPal.Ext.System.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.TimeDate.UnitTests\\Microsoft.CmdPal.Ext.TimeDate.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.UnitTestsBase\\Microsoft.CmdPal.Ext.UnitTestBase.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.WebSearch.UnitTests\\Microsoft.CmdPal.Ext.WebSearch.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.WindowWalker.UnitTests\\Microsoft.CmdPal.Ext.WindowWalker.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.UI.ViewModels.UnitTests\\Microsoft.CmdPal.UI.ViewModels.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.UITests\\Microsoft.CmdPal.UITests.csproj",
|
||||
"src\\modules\\cmdpal\\Tests\\Microsoft.CommandPalette.Extensions.Toolkit.UnitTests\\Microsoft.CommandPalette.Extensions.Toolkit.UnitTests.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Apps\\Microsoft.CmdPal.Ext.Apps.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Bookmark\\Microsoft.CmdPal.Ext.Bookmarks.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Calc\\Microsoft.CmdPal.Ext.Calc.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.ClipboardHistory\\Microsoft.CmdPal.Ext.ClipboardHistory.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Indexer\\Microsoft.CmdPal.Ext.Indexer.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.PerformanceMonitor\\Microsoft.CmdPal.Ext.PerformanceMonitor.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Registry\\Microsoft.CmdPal.Ext.Registry.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.RemoteDesktop\\Microsoft.CmdPal.Ext.RemoteDesktop.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.Shell\\Microsoft.CmdPal.Ext.Shell.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.System\\Microsoft.CmdPal.Ext.System.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.TimeDate\\Microsoft.CmdPal.Ext.TimeDate.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WebSearch\\Microsoft.CmdPal.Ext.WebSearch.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WinGet\\Microsoft.CmdPal.Ext.WinGet.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowWalker\\Microsoft.CmdPal.Ext.WindowWalker.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowsServices\\Microsoft.CmdPal.Ext.WindowsServices.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowsSettings\\Microsoft.CmdPal.Ext.WindowsSettings.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\Microsoft.CmdPal.Ext.WindowsTerminal\\Microsoft.CmdPal.Ext.WindowsTerminal.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\ProcessMonitorExtension\\ProcessMonitorExtension.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\SamplePagesExtension\\SamplePagesExtension.csproj",
|
||||
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions.Toolkit\\Microsoft.CommandPalette.Extensions.Toolkit.csproj",
|
||||
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,8 @@
|
||||
"src\\common\\ManagedCommon\\ManagedCommon.csproj",
|
||||
"src\\common\\ManagedCsWin32\\ManagedCsWin32.csproj",
|
||||
"src\\common\\ManagedTelemetry\\Telemetry\\ManagedTelemetry.csproj",
|
||||
"src\\common\\SettingsAPI\\SettingsAPI.vcxproj",
|
||||
"src\\common\\UITestAutomation\\UITestAutomation.csproj",
|
||||
"src\\common\\interop\\PowerToys.Interop.vcxproj",
|
||||
"src\\common\\logger\\logger.vcxproj",
|
||||
"src\\common\\version\\version.vcxproj",
|
||||
"src\\logging\\logging.vcxproj",
|
||||
"src\\modules\\MouseUtils\\MouseJump.Common\\MouseJump.Common.csproj",
|
||||
"src\\modules\\ZoomIt\\ZoomItSettingsInterop\\ZoomItSettingsInterop.vcxproj",
|
||||
"src\\modules\\cmdpal\\CmdPalKeyboardService\\CmdPalKeyboardService.vcxproj",
|
||||
"src\\modules\\cmdpal\\CmdPalModuleInterface\\CmdPalModuleInterface.vcxproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.Common\\Microsoft.CmdPal.Common.csproj",
|
||||
@@ -57,9 +51,7 @@
|
||||
"src\\modules\\cmdpal\\ext\\ProcessMonitorExtension\\ProcessMonitorExtension.csproj",
|
||||
"src\\modules\\cmdpal\\ext\\SamplePagesExtension\\SamplePagesExtension.csproj",
|
||||
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions.Toolkit\\Microsoft.CommandPalette.Extensions.Toolkit.csproj",
|
||||
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj",
|
||||
"src\\modules\\powerdisplay\\PowerDisplay.Models\\PowerDisplay.Models.csproj",
|
||||
"src\\settings-ui\\Settings.UI.Library\\Settings.UI.Library.csproj"
|
||||
"src\\modules\\cmdpal\\extensionsdk\\Microsoft.CommandPalette.Extensions\\Microsoft.CommandPalette.Extensions.vcxproj"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
# Command Palette Extension – Copilot Instructions
|
||||
|
||||
Concise guidance for AI-assisted development of this Command Palette extension.
|
||||
|
||||
## Project Structure
|
||||
|
||||
| Folder | Purpose |
|
||||
|--------|---------|
|
||||
| `Pages/` | Extension pages (ListPage, ContentPage, DynamicListPage implementations) |
|
||||
| `Assets/` | Icons and images (StoreLogo.png, etc.) |
|
||||
| `Properties/` | Launch settings and publish profiles |
|
||||
| Root `.cs` files | Extension entry point, COM server (Program.cs), and CommandsProvider |
|
||||
|
||||
## Key Conventions
|
||||
|
||||
- Extensions run **out-of-process** via COM server registration
|
||||
- `Program.cs` hosts the COM server — do not modify the hosting pattern
|
||||
- The `CommandProvider` subclass is the entry point for all commands
|
||||
- Pages are **ICommand** implementations — they can be used anywhere commands are used
|
||||
- Always **Deploy** (not just Build) to register the MSIX package
|
||||
- After deploying, use the **Reload** command in Command Palette to refresh
|
||||
|
||||
## Build & Deploy
|
||||
|
||||
1. In Visual Studio, use **Build > Deploy** (not just Build)
|
||||
2. In Command Palette, run `Reload` → select "Reload Command Palette extensions"
|
||||
3. For debugging, run in Debug configuration (F5) and check Output window (Ctrl+Alt+O)
|
||||
|
||||
## Source Control
|
||||
|
||||
If using git, remove these lines from `.gitignore` (needed for deployment):
|
||||
- `**/Properties/launchSettings.json`
|
||||
- `*.pubxml`
|
||||
|
||||
## Available Skills
|
||||
|
||||
This project includes Copilot skills for common workflows:
|
||||
- **add-adaptive-card-form** — Create form-based UI with Adaptive Cards
|
||||
- **add-extension-settings** — Add a settings page to your extension
|
||||
- **add-dock-band** — Add persistent toolbar widgets
|
||||
- **add-fallback-commands** — Add catch-all search commands
|
||||
- **publish-extension** — Publish to Microsoft Store or WinGet
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Creating an extension](https://learn.microsoft.com/windows/powertoys/command-palette/creating-an-extension)
|
||||
- [Extension samples](https://learn.microsoft.com/windows/powertoys/command-palette/samples)
|
||||
- [Extensibility overview](https://learn.microsoft.com/windows/powertoys/command-palette/extensibility-overview)
|
||||
@@ -1,353 +0,0 @@
|
||||
---
|
||||
description: 'Comprehensive guide for developing Command Palette extensions — covers pages, content, commands, items, icons, settings, dock, and debugging'
|
||||
applyTo: '**/*.cs'
|
||||
---
|
||||
|
||||
# Command Palette Extension Development
|
||||
|
||||
Complete reference for building Command Palette (CmdPal) extensions. Extensions run out-of-process as MSIX-packaged COM servers.
|
||||
|
||||
## Extension Architecture
|
||||
|
||||
### IExtension Interface
|
||||
|
||||
The root class implements `IExtension` and `IDisposable`:
|
||||
|
||||
```csharp
|
||||
[Guid("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF")]
|
||||
public sealed partial class MyExtension : IExtension, IDisposable
|
||||
{
|
||||
private readonly ManualResetEvent _extensionDisposedEvent;
|
||||
private readonly MyCommandsProvider _provider = new();
|
||||
|
||||
public MyExtension(ManualResetEvent extensionDisposedEvent)
|
||||
{
|
||||
_extensionDisposedEvent = extensionDisposedEvent;
|
||||
}
|
||||
|
||||
public object? GetProvider(ProviderType providerType) => providerType switch
|
||||
{
|
||||
ProviderType.Commands => _provider,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
public void Dispose() => _extensionDisposedEvent.Set();
|
||||
}
|
||||
```
|
||||
|
||||
- Only `ProviderType.Commands` is currently supported
|
||||
- The `[Guid]` must match the CLSID in `Package.appxmanifest`
|
||||
|
||||
### CommandProvider
|
||||
|
||||
Override `TopLevelCommands()` to register main commands. Optionally override `FallbackCommands()` and `GetDockBands()`:
|
||||
|
||||
```csharp
|
||||
public partial class MyCommandsProvider : CommandProvider
|
||||
{
|
||||
public MyCommandsProvider()
|
||||
{
|
||||
DisplayName = "My Extension";
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() => [
|
||||
new CommandItem(new MyPage()) { Title = DisplayName },
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### COM Server (Program.cs)
|
||||
|
||||
`Program.cs` hosts the COM server. Do not change this pattern:
|
||||
|
||||
```csharp
|
||||
public class Program
|
||||
{
|
||||
[MTAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && args[0] == "-RegisterProcessAsComServer")
|
||||
{
|
||||
global::Shmuelie.WinRTServer.ComServer server = new();
|
||||
ManualResetEvent extensionDisposedEvent = new(false);
|
||||
var extensionInstance = new MyExtension(extensionDisposedEvent);
|
||||
server.RegisterClass<MyExtension, IExtension>(() => extensionInstance);
|
||||
server.Start();
|
||||
extensionDisposedEvent.WaitOne();
|
||||
server.Stop();
|
||||
server.UnsafeDispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Package.appxmanifest
|
||||
|
||||
Two critical extension registrations must be present:
|
||||
|
||||
1. **COM server** — `com:ComServer` with matching CLSID and `-RegisterProcessAsComServer` args
|
||||
2. **App extension** — `uap3:AppExtension` with `Name="com.microsoft.commandpalette"` and `CreateInstance ClassId` matching the GUID
|
||||
|
||||
The CLSID must be identical in three places: the `[Guid]` attribute, the `com:Class Id`, and the `CreateInstance ClassId`.
|
||||
|
||||
## Page Types
|
||||
|
||||
### ListPage (Most Common)
|
||||
|
||||
Displays a searchable list of items:
|
||||
|
||||
```csharp
|
||||
internal sealed partial class MyPage : ListPage
|
||||
{
|
||||
public MyPage()
|
||||
{
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
Title = "My page";
|
||||
Name = "Open";
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => [
|
||||
new ListItem(new OpenUrlCommand("https://example.com")) { Title = "Example" },
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### DynamicListPage (Search-Reactive)
|
||||
|
||||
Responds to search text changes for filtering or live queries:
|
||||
|
||||
```csharp
|
||||
internal sealed partial class MyDynamicPage : DynamicListPage
|
||||
{
|
||||
private IListItem[] _filteredItems = [];
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
_filteredItems = _allItems
|
||||
.Where(i => i.Title.Contains(newSearch, StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray();
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => _filteredItems;
|
||||
}
|
||||
```
|
||||
|
||||
- Supports `Filters` property for category filtering
|
||||
- Call `RaiseItemsChanged()` after updating items to notify the UI
|
||||
|
||||
### ContentPage (Rich Content)
|
||||
|
||||
Displays rich content like markdown, forms, or images:
|
||||
|
||||
```csharp
|
||||
internal sealed partial class MyContentPage : ContentPage
|
||||
{
|
||||
public override IContent[] GetContent() => [
|
||||
new MarkdownContent("# Hello\nThis is **markdown**."),
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
- Can return multiple `IContent` items (mix markdown, forms, images, etc.)
|
||||
- Supports `Commands` property for context menu items via `CommandContextItem`
|
||||
|
||||
## Content Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `MarkdownContent(string)` | Renders markdown with headers, links, code blocks, tables, images |
|
||||
| `FormContent` | Adaptive Cards forms with `TemplateJson`, optional `DataJson`, and `SubmitForm()` |
|
||||
| `PlainTextContent(string)` | Plain text; optional `FontFamily.Monospace` and `WrapWords` |
|
||||
| `ImageContent` | Images with `MaxWidth`/`MaxHeight` constraints |
|
||||
| `TreeContent` | Hierarchical nested content; override `GetChildren()` for child `IContent[]` |
|
||||
|
||||
### MarkdownContent Images
|
||||
|
||||
Supports `file:`, `data:` (base64), and `https:` URLs. Image hints control rendering:
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
### FormContent (Adaptive Cards)
|
||||
|
||||
```csharp
|
||||
internal sealed partial class MyForm : FormContent
|
||||
{
|
||||
public MyForm()
|
||||
{
|
||||
TemplateJson = """{ "type": "AdaptiveCard", ... }""";
|
||||
DataJson = """{ "name": "default" }""";
|
||||
}
|
||||
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var data = JsonSerializer.Deserialize<MyFormData>(payload);
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Design cards visually at [adaptivecards.io/designer](https://adaptivecards.io/designer)
|
||||
- Use `${...}` placeholders in `TemplateJson` bound to `DataJson` properties
|
||||
|
||||
## Commands
|
||||
|
||||
### InvokableCommand
|
||||
|
||||
Actions that do something when activated:
|
||||
|
||||
```csharp
|
||||
internal sealed partial class MyCommand : InvokableCommand
|
||||
{
|
||||
public override string Name => "Do it";
|
||||
public override IconInfo Icon => new("\uE945");
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
// Do work here
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Built-in Command Helpers
|
||||
|
||||
| Helper | Purpose |
|
||||
|--------|---------|
|
||||
| `OpenUrlCommand(string url)` | Open URL in default browser |
|
||||
| `CopyTextCommand(string text)` | Copy to clipboard with toast |
|
||||
| `NoOpCommand()` | Does nothing (placeholder) |
|
||||
| `AnonymousCommand(Action? action)` | Lambda command; set `Result` property for navigation |
|
||||
|
||||
### CommandResult Types
|
||||
|
||||
| Result | Behavior |
|
||||
|--------|----------|
|
||||
| `CommandResult.Dismiss()` | Hide palette, go home |
|
||||
| `CommandResult.KeepOpen()` | Stay on current page |
|
||||
| `CommandResult.Hide()` | Hide palette, keep page state |
|
||||
| `CommandResult.GoBack()` | Navigate back one page |
|
||||
| `CommandResult.GoHome()` | Navigate to home page |
|
||||
| `CommandResult.ShowToast("msg")` | Show toast notification, then dismiss |
|
||||
| `CommandResult.Confirm(args)` | Show confirmation dialog before proceeding |
|
||||
|
||||
## ListItem Properties
|
||||
|
||||
```csharp
|
||||
new ListItem(command)
|
||||
{
|
||||
Title = "Display name",
|
||||
Subtitle = "Secondary text",
|
||||
Icon = new IconInfo("\uE8A7"),
|
||||
Tags = [new Tag("label") { Foreground = ColorHelpers.FromRgb(255, 0, 0) }],
|
||||
Details = new Details
|
||||
{
|
||||
Title = "Detail panel",
|
||||
Body = "**Markdown** body",
|
||||
HeroImage = IconHelpers.FromRelativePath("Assets\\hero.png"),
|
||||
Size = ContentSize.Medium,
|
||||
Metadata = [
|
||||
new DetailsLink("URL", "https://example.com"),
|
||||
new DetailsSeparator(),
|
||||
],
|
||||
},
|
||||
MoreCommands = [
|
||||
new CommandContextItem(deleteCommand)
|
||||
{
|
||||
RequestedShortcut = KeyChordHelpers.FromModifiers(
|
||||
true, false, false, (int)VirtualKey.Delete),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Sections and Grid Layouts
|
||||
|
||||
### Sections
|
||||
|
||||
Group items under section headers:
|
||||
|
||||
```csharp
|
||||
public override ISection[] GetSections() => [
|
||||
new Section { Title = "Group A", Items = itemsA },
|
||||
new Section { Title = "Group B", Items = itemsB },
|
||||
];
|
||||
```
|
||||
|
||||
### Grid Layouts
|
||||
|
||||
Set `GridProperties` on a `ListPage`:
|
||||
|
||||
| Layout | Description |
|
||||
|--------|-------------|
|
||||
| `GalleryGridLayout()` | Large tiles with title + subtitle |
|
||||
| `SmallGridLayout()` | Compact grid |
|
||||
| `MediumGridLayout()` | Medium tiles with title |
|
||||
|
||||
## Icons
|
||||
|
||||
```csharp
|
||||
// Segoe Fluent UI icons (most common)
|
||||
new IconInfo("\uE8A5") // Document
|
||||
new IconInfo("\uE945") // Lightning bolt
|
||||
|
||||
// Emoji
|
||||
new IconInfo("📂")
|
||||
|
||||
// Image from package assets
|
||||
IconHelpers.FromRelativePath("Assets\\StoreLogo.png")
|
||||
|
||||
// Remote URL or SVG
|
||||
new IconInfo("https://example.com/icon.svg")
|
||||
|
||||
// From exe/dll resource
|
||||
new IconInfo("%systemroot%\\system32\\shell32.dll,3")
|
||||
```
|
||||
|
||||
## Dynamic Updates
|
||||
|
||||
- Call `RaiseItemsChanged()` on any page to trigger a UI refresh of its items
|
||||
- Call `RaisePropertyChanged(propertyName)` for individual property updates (e.g., title)
|
||||
- For top-level command changes, call `RaiseItemsChanged()` on the `CommandProvider`
|
||||
- Use `System.Timers.Timer` for periodic background updates
|
||||
|
||||
## Status Messages and Toasts
|
||||
|
||||
```csharp
|
||||
// Inline status message (e.g., loading indicator)
|
||||
var msg = new StatusMessage
|
||||
{
|
||||
Message = "Loading...",
|
||||
State = MessageState.Info,
|
||||
Progress = new ProgressState { IsIndeterminate = true },
|
||||
};
|
||||
ExtensionHost.ShowStatus(msg, StatusContext.Page);
|
||||
ExtensionHost.HideStatus(msg);
|
||||
|
||||
// Transient toast notification
|
||||
new ToastStatusMessage("Copied to clipboard").Show();
|
||||
```
|
||||
|
||||
## Build & Debug
|
||||
|
||||
1. Select **Debug** configuration
|
||||
2. **Deploy** via Build > Deploy (not just Build) — this registers the MSIX package
|
||||
3. Press **F5** to launch with debugger attached
|
||||
4. Use `Debug.Write()` / `Debug.WriteLine()` for diagnostic output
|
||||
5. Check Output window (**Ctrl+Alt+O**) set to "Debug"
|
||||
6. In Command Palette, run `Reload` → "Reload Command Palette extensions"
|
||||
|
||||
Use the `(Package)` launch profile, not `(Unpackaged)`.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
| Mistake | Fix |
|
||||
|---------|-----|
|
||||
| Building without deploying | Use Build > Deploy so the MSIX package is updated |
|
||||
| Running "(Unpackaged)" profile | Select the "(Package)" launch profile |
|
||||
| Forgetting to reload extensions | Run `Reload` in Command Palette after deploying |
|
||||
| CLSID mismatch | Ensure `[Guid]` in .cs matches `ClassId` in Package.appxmanifest (both places) |
|
||||
| Logging in hot paths | `GetItems()` is called frequently — avoid expensive work or logging here |
|
||||
@@ -1,145 +0,0 @@
|
||||
---
|
||||
name: add-adaptive-card-form
|
||||
description: >-
|
||||
Create form-based UI for your Command Palette extension using Adaptive Cards.
|
||||
Use when asked to add forms, user input fields, toggle switches, text inputs,
|
||||
dropdown menus, data entry, surveys, configuration dialogs, or interactive
|
||||
content pages. Supports the Adaptive Cards Designer for visual form building.
|
||||
---
|
||||
|
||||
# Add Forms with Adaptive Cards
|
||||
|
||||
Create interactive forms in your Command Palette extension using Adaptive Cards. Forms allow you to collect user input through text fields, toggles, dropdowns, and other controls.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Adding a form to collect user input (name, settings, feedback)
|
||||
- Creating interactive configuration dialogs
|
||||
- Building data entry interfaces
|
||||
- Adding toggle switches or dropdown menus
|
||||
- Displaying complex layouts beyond simple lists
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with [Adaptive Cards](https://adaptivecards.io/)
|
||||
- Optional: Use the [Adaptive Card Designer](https://adaptivecards.io/designer/) to visually build your form
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Step 1: Create a ContentPage with FormContent
|
||||
|
||||
Create a new file in your `Pages/` directory:
|
||||
|
||||
```csharp
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace YourExtension;
|
||||
|
||||
internal sealed partial class MyFormPage : ContentPage
|
||||
{
|
||||
private readonly MyForm _form = new();
|
||||
|
||||
public MyFormPage()
|
||||
{
|
||||
Name = "Open";
|
||||
Title = "My Form";
|
||||
Icon = new IconInfo("\uECA5");
|
||||
}
|
||||
|
||||
public override IContent[] GetContent() => [_form];
|
||||
}
|
||||
|
||||
internal sealed partial class MyForm : FormContent
|
||||
{
|
||||
public MyForm()
|
||||
{
|
||||
TemplateJson = """
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "Name",
|
||||
"id": "Name",
|
||||
"isRequired": true,
|
||||
"errorMessage": "Name is required",
|
||||
"placeholder": "Enter your name"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Submit"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var formInput = JsonNode.Parse(payload)?.AsObject();
|
||||
if (formInput == null)
|
||||
{
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
|
||||
var name = formInput["Name"]?.ToString() ?? "Unknown";
|
||||
return CommandResult.ShowToast($"Hello, {name}!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Register the Page
|
||||
|
||||
In your `CommandsProvider`, add the form page:
|
||||
|
||||
```csharp
|
||||
_commands = [
|
||||
new CommandItem(new MyFormPage()) { Title = "My Form" },
|
||||
];
|
||||
```
|
||||
|
||||
### Step 3: Deploy and Test
|
||||
|
||||
1. Deploy your extension
|
||||
2. In Command Palette, run `Reload`
|
||||
3. Navigate to your form and submit it
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### TemplateJson
|
||||
The JSON layout of your form (from Adaptive Cards schema). Design it at https://adaptivecards.io/designer/
|
||||
|
||||
### DataJson (Optional)
|
||||
Dynamic data binding using `${...}` placeholders in your TemplateJson:
|
||||
```csharp
|
||||
TemplateJson = """{ "body": [{ "type": "TextBlock", "text": "${title}" }] }""";
|
||||
DataJson = """{ "title": "Dynamic Title" }""";
|
||||
```
|
||||
|
||||
### SubmitForm
|
||||
Called when the user submits. Parse `payload` as JSON to read input values by their `id`.
|
||||
|
||||
### Mixing Content Types
|
||||
You can combine forms with markdown on the same page:
|
||||
```csharp
|
||||
public override IContent[] GetContent() => [
|
||||
new MarkdownContent("# Instructions\nFill out the form below."),
|
||||
_form,
|
||||
];
|
||||
```
|
||||
|
||||
## Common Form Patterns
|
||||
|
||||
See [form-patterns.md](references/form-patterns.md) for template JSON for common form types.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Get user input with forms](https://learn.microsoft.com/windows/powertoys/command-palette/using-form-pages)
|
||||
- [Adaptive Card Designer](https://adaptivecards.io/designer/)
|
||||
- [Adaptive Cards Schema](https://adaptivecards.io/explorer/)
|
||||
@@ -1,536 +0,0 @@
|
||||
# Common Adaptive Card Form Patterns
|
||||
|
||||
Reusable template JSON and handler code for the most common form types in Command Palette extensions.
|
||||
|
||||
---
|
||||
|
||||
## Simple Text Input Form
|
||||
|
||||
A basic form with one or two text fields and a submit button.
|
||||
|
||||
### TemplateJson
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "FirstName",
|
||||
"label": "First Name",
|
||||
"placeholder": "Enter your first name",
|
||||
"isRequired": true,
|
||||
"errorMessage": "First name is required"
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "Email",
|
||||
"label": "Email Address",
|
||||
"placeholder": "user@example.com",
|
||||
"style": "Email",
|
||||
"isRequired": true,
|
||||
"errorMessage": "A valid email is required"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Submit"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### SubmitForm Handler
|
||||
|
||||
```csharp
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var input = JsonNode.Parse(payload)?.AsObject();
|
||||
if (input == null) return CommandResult.GoHome();
|
||||
|
||||
var firstName = input["FirstName"]?.ToString() ?? "";
|
||||
var email = input["Email"]?.ToString() ?? "";
|
||||
|
||||
return CommandResult.ShowToast($"Registered {firstName} ({email})");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Toggle/Checkbox Form
|
||||
|
||||
Use `Input.Toggle` for boolean on/off settings. Combine with `DataJson` for dynamic defaults.
|
||||
|
||||
### TemplateJson
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Preferences",
|
||||
"weight": "Bolder",
|
||||
"size": "Medium"
|
||||
},
|
||||
{
|
||||
"type": "Input.Toggle",
|
||||
"id": "AcceptsTerms",
|
||||
"title": "I accept the terms and conditions",
|
||||
"valueOn": "true",
|
||||
"valueOff": "false",
|
||||
"value": "false"
|
||||
},
|
||||
{
|
||||
"type": "Input.Toggle",
|
||||
"id": "EnableNotifications",
|
||||
"title": "Enable notifications",
|
||||
"valueOn": "true",
|
||||
"valueOff": "false",
|
||||
"value": "${notificationsDefault}"
|
||||
},
|
||||
{
|
||||
"type": "Input.Toggle",
|
||||
"id": "DarkMode",
|
||||
"title": "Use dark mode",
|
||||
"valueOn": "true",
|
||||
"valueOff": "false",
|
||||
"value": "${darkModeDefault}"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save Preferences"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### DataJson (Dynamic Defaults)
|
||||
|
||||
```csharp
|
||||
DataJson = """
|
||||
{
|
||||
"notificationsDefault": "true",
|
||||
"darkModeDefault": "false"
|
||||
}
|
||||
""";
|
||||
```
|
||||
|
||||
### SubmitForm Handler
|
||||
|
||||
```csharp
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var input = JsonNode.Parse(payload)?.AsObject();
|
||||
if (input == null) return CommandResult.GoHome();
|
||||
|
||||
var accepted = input["AcceptsTerms"]?.ToString() == "true";
|
||||
var notifications = input["EnableNotifications"]?.ToString() == "true";
|
||||
var darkMode = input["DarkMode"]?.ToString() == "true";
|
||||
|
||||
if (!accepted)
|
||||
{
|
||||
return CommandResult.ShowToast("You must accept the terms to continue.");
|
||||
}
|
||||
|
||||
// Save preferences...
|
||||
return CommandResult.ShowToast("Preferences saved!");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Choice Set (Dropdown/Radio) Form
|
||||
|
||||
Use `Input.ChoiceSet` for single-select dropdowns or radio buttons.
|
||||
|
||||
### Compact Style (Dropdown)
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.ChoiceSet",
|
||||
"id": "Priority",
|
||||
"label": "Priority Level",
|
||||
"style": "compact",
|
||||
"value": "medium",
|
||||
"choices": [
|
||||
{ "title": "Low", "value": "low" },
|
||||
{ "title": "Medium", "value": "medium" },
|
||||
{ "title": "High", "value": "high" },
|
||||
{ "title": "Critical", "value": "critical" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Input.ChoiceSet",
|
||||
"id": "Category",
|
||||
"label": "Category",
|
||||
"style": "compact",
|
||||
"choices": [
|
||||
{ "title": "Bug Report", "value": "bug" },
|
||||
{ "title": "Feature Request", "value": "feature" },
|
||||
{ "title": "Documentation", "value": "docs" },
|
||||
{ "title": "Question", "value": "question" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Create Issue"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Expanded Style (Radio Buttons)
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.ChoiceSet",
|
||||
"id": "Theme",
|
||||
"label": "Select a theme",
|
||||
"style": "expanded",
|
||||
"value": "system",
|
||||
"choices": [
|
||||
{ "title": "Light", "value": "light" },
|
||||
{ "title": "Dark", "value": "dark" },
|
||||
{ "title": "System Default", "value": "system" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Apply"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multi-Section Form
|
||||
|
||||
Combine multiple input types with TextBlock headers to create organized, multi-section forms. Use `Action.ShowCard` for progressive disclosure of optional sections.
|
||||
|
||||
### TemplateJson
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Personal Information",
|
||||
"weight": "Bolder",
|
||||
"size": "Medium",
|
||||
"separator": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "FullName",
|
||||
"label": "Full Name",
|
||||
"placeholder": "Enter your full name",
|
||||
"isRequired": true,
|
||||
"errorMessage": "Name is required"
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "Email",
|
||||
"label": "Email",
|
||||
"placeholder": "user@example.com",
|
||||
"style": "Email"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Preferences",
|
||||
"weight": "Bolder",
|
||||
"size": "Medium",
|
||||
"separator": true,
|
||||
"spacing": "Large"
|
||||
},
|
||||
{
|
||||
"type": "Input.ChoiceSet",
|
||||
"id": "Language",
|
||||
"label": "Preferred Language",
|
||||
"style": "compact",
|
||||
"value": "en",
|
||||
"choices": [
|
||||
{ "title": "English", "value": "en" },
|
||||
{ "title": "Spanish", "value": "es" },
|
||||
{ "title": "French", "value": "fr" },
|
||||
{ "title": "German", "value": "de" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Input.Toggle",
|
||||
"id": "Newsletter",
|
||||
"title": "Subscribe to newsletter",
|
||||
"valueOn": "true",
|
||||
"valueOff": "false",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save Profile"
|
||||
},
|
||||
{
|
||||
"type": "Action.ShowCard",
|
||||
"title": "Advanced Options",
|
||||
"card": {
|
||||
"type": "AdaptiveCard",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "ApiKey",
|
||||
"label": "API Key (optional)",
|
||||
"placeholder": "Enter your API key"
|
||||
},
|
||||
{
|
||||
"type": "Input.Toggle",
|
||||
"id": "DebugMode",
|
||||
"title": "Enable debug mode",
|
||||
"valueOn": "true",
|
||||
"valueOff": "false",
|
||||
"value": "false"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save All"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Feedback Form
|
||||
|
||||
A common pattern for collecting user feedback with a multiline text area and a rating.
|
||||
|
||||
### TemplateJson
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "We'd love your feedback!",
|
||||
"weight": "Bolder",
|
||||
"size": "Medium"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Tell us what you think and how we can improve.",
|
||||
"wrap": true,
|
||||
"spacing": "Small"
|
||||
},
|
||||
{
|
||||
"type": "Input.ChoiceSet",
|
||||
"id": "Rating",
|
||||
"label": "How would you rate your experience?",
|
||||
"style": "expanded",
|
||||
"isRequired": true,
|
||||
"errorMessage": "Please select a rating",
|
||||
"choices": [
|
||||
{ "title": "⭐ Poor", "value": "1" },
|
||||
{ "title": "⭐⭐ Fair", "value": "2" },
|
||||
{ "title": "⭐⭐⭐ Good", "value": "3" },
|
||||
{ "title": "⭐⭐⭐⭐ Great", "value": "4" },
|
||||
{ "title": "⭐⭐⭐⭐⭐ Excellent", "value": "5" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "Comments",
|
||||
"label": "Comments",
|
||||
"placeholder": "Share your thoughts...",
|
||||
"isMultiline": true,
|
||||
"maxLength": 500
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Send Feedback"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### SubmitForm Handler with Confirmation Dialog
|
||||
|
||||
```csharp
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var input = JsonNode.Parse(payload)?.AsObject();
|
||||
if (input == null) return CommandResult.GoHome();
|
||||
|
||||
var rating = input["Rating"]?.ToString() ?? "0";
|
||||
var comments = input["Comments"]?.ToString() ?? "";
|
||||
|
||||
return CommandResult.Confirm(new ConfirmationArgs
|
||||
{
|
||||
Title = "Submit feedback?",
|
||||
Description = $"Rating: {rating}/5\n\n{(string.IsNullOrEmpty(comments) ? "No comments" : comments)}",
|
||||
PrimaryCommand = new AnonymousCommand(() =>
|
||||
{
|
||||
// Process and store feedback
|
||||
new ToastStatusMessage("Thank you for your feedback!").Show();
|
||||
})
|
||||
{
|
||||
Name = "Submit",
|
||||
Result = CommandResult.Dismiss(),
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tree Content with Forms (Comment/Reply Pattern)
|
||||
|
||||
Use `TreeContent` to create nested, threaded discussions where each node can contain a form for replies.
|
||||
|
||||
### Post Content (Tree Node)
|
||||
|
||||
```csharp
|
||||
internal sealed partial class PostContent : TreeContent
|
||||
{
|
||||
private readonly string _author;
|
||||
private readonly string _body;
|
||||
private readonly PostReplyForm _replyForm;
|
||||
private readonly List<PostContent> _childPosts = [];
|
||||
|
||||
public PostContent(string author, string body)
|
||||
{
|
||||
_author = author;
|
||||
_body = body;
|
||||
_replyForm = new PostReplyForm(this);
|
||||
|
||||
TemplateJson = """
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "${author}",
|
||||
"weight": "Bolder"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "${body}",
|
||||
"wrap": true
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
DataJson = $$"""{ "author": "{{_author}}", "body": "{{_body}}" }""";
|
||||
}
|
||||
|
||||
public override IContent[] GetChildren() => [_replyForm, .. _childPosts];
|
||||
|
||||
public void AddReply(PostContent reply) => _childPosts.Add(reply);
|
||||
}
|
||||
```
|
||||
|
||||
### Reply Form (Child of Tree Node)
|
||||
|
||||
```csharp
|
||||
internal sealed partial class PostReplyForm : FormContent
|
||||
{
|
||||
private readonly PostContent _parent;
|
||||
|
||||
public PostReplyForm(PostContent parent)
|
||||
{
|
||||
_parent = parent;
|
||||
TemplateJson = """
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"id": "ReplyText",
|
||||
"placeholder": "Write a reply...",
|
||||
"isMultiline": true
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Reply"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var input = JsonNode.Parse(payload)?.AsObject();
|
||||
if (input == null) return CommandResult.GoHome();
|
||||
|
||||
var replyText = input["ReplyText"]?.ToString();
|
||||
if (!string.IsNullOrWhiteSpace(replyText))
|
||||
{
|
||||
_parent.AddReply(new PostContent("You", replyText));
|
||||
}
|
||||
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hosting the Thread on a ContentPage
|
||||
|
||||
```csharp
|
||||
internal sealed partial class ThreadPage : ContentPage
|
||||
{
|
||||
private readonly PostContent _rootPost;
|
||||
|
||||
public ThreadPage()
|
||||
{
|
||||
Name = "Discussion";
|
||||
Title = "Discussion Thread";
|
||||
Icon = new IconInfo("\uE90A");
|
||||
|
||||
_rootPost = new PostContent("Alice", "Has anyone tried the new API?");
|
||||
_rootPost.AddReply(new PostContent("Bob", "Yes! It works great."));
|
||||
}
|
||||
|
||||
public override IContent[] GetContent() => [_rootPost];
|
||||
}
|
||||
```
|
||||
@@ -1,149 +0,0 @@
|
||||
---
|
||||
name: add-dock-band
|
||||
description: >-
|
||||
Add dock band support to your Command Palette extension for persistent toolbar widgets.
|
||||
Use when asked to add dock support, toolbar buttons, persistent UI widgets,
|
||||
taskbar integration, live-updating status displays, quick-access buttons,
|
||||
or always-visible controls. Supports single buttons, multi-button strips,
|
||||
and live-updating content.
|
||||
---
|
||||
|
||||
# Add Dock Band Support
|
||||
|
||||
The Command Palette Dock is a persistent toolbar at the edge of the user's screen. Your extension can provide **dock bands** — strips of items that appear in the Dock — giving users quick access to commands without opening the full Command Palette.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Adding a quick-access button to the persistent toolbar
|
||||
- Creating a multi-button toolbar strip
|
||||
- Displaying live-updating information (clock, CPU usage, etc.)
|
||||
- Providing frequently-used commands without opening the full palette
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Command Palette Extension SDK version 0.9 or later (`Microsoft.CommandPalette.Extensions` ≥ 0.9.260303001)
|
||||
|
||||
## Quick Start: Single Button Dock Band
|
||||
|
||||
Override `GetDockBands()` in your `CommandProvider`:
|
||||
|
||||
```csharp
|
||||
public partial class MyCommandsProvider : CommandProvider
|
||||
{
|
||||
private readonly ICommandItem[] _commands;
|
||||
private readonly ICommandItem _dockBand;
|
||||
|
||||
public MyCommandsProvider()
|
||||
{
|
||||
DisplayName = "My Extension";
|
||||
Id = "com.mycompany.myextension"; // Unique ID required for dock
|
||||
|
||||
var mainPage = new MyPage();
|
||||
_dockBand = new CommandItem(mainPage) { Title = DisplayName };
|
||||
_commands = [new CommandItem(mainPage) { Title = DisplayName }];
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() => _commands;
|
||||
|
||||
public override ICommandItem[]? GetDockBands() => [_dockBand];
|
||||
}
|
||||
```
|
||||
|
||||
## Multi-Button Dock Band
|
||||
|
||||
Use `WrappedDockItem` to create a band with multiple buttons:
|
||||
|
||||
```csharp
|
||||
public override ICommandItem[]? GetDockBands()
|
||||
{
|
||||
var button1 = new ListItem(new OpenUrlCommand("https://github.com"))
|
||||
{
|
||||
Title = "GitHub",
|
||||
Icon = new IconInfo("\uE774"),
|
||||
};
|
||||
var button2 = new ListItem(new OpenUrlCommand("https://learn.microsoft.com"))
|
||||
{
|
||||
Title = "Learn",
|
||||
Icon = new IconInfo("\uE82D"),
|
||||
};
|
||||
|
||||
var band = new WrappedDockItem(
|
||||
[button1, button2],
|
||||
"com.mycompany.myextension.quicklinks", // Unique band ID
|
||||
"Quick Links");
|
||||
|
||||
return [band];
|
||||
}
|
||||
```
|
||||
|
||||
## Live-Updating Dock Band
|
||||
|
||||
Create a dock band that updates its content periodically (like a clock):
|
||||
|
||||
```csharp
|
||||
internal sealed partial class LiveStatusBand : ListItem
|
||||
{
|
||||
private readonly System.Timers.Timer _timer;
|
||||
|
||||
public LiveStatusBand()
|
||||
: base(new NoOpCommand() { Result = CommandResult.KeepOpen() })
|
||||
{
|
||||
Title = DateTime.Now.ToString("HH:mm");
|
||||
Icon = new IconInfo("\uE823"); // Clock icon
|
||||
|
||||
_timer = new System.Timers.Timer(60_000); // Update every minute
|
||||
_timer.Elapsed += (s, e) =>
|
||||
{
|
||||
Title = DateTime.Now.ToString("HH:mm");
|
||||
Subtitle = DateTime.Now.ToString("dddd, MMMM d");
|
||||
};
|
||||
_timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
// In CommandProvider:
|
||||
public override ICommandItem[]? GetDockBands()
|
||||
{
|
||||
var band = new WrappedDockItem(
|
||||
[new LiveStatusBand()],
|
||||
"com.mycompany.myextension.status",
|
||||
"Live Status");
|
||||
return [band];
|
||||
}
|
||||
```
|
||||
|
||||
## How Dock Bands Render
|
||||
|
||||
| Command Type on ICommandItem | Dock Behavior |
|
||||
|------------------------------|---------------|
|
||||
| `IInvokableCommand` | Single button that executes the command |
|
||||
| `IListPage` | Each list item renders as a separate button in one band |
|
||||
| `IContentPage` | Single expandable button with a flyout |
|
||||
|
||||
## Support Pinning Nested Commands
|
||||
|
||||
By default, only top-level commands and dock bands can be pinned. To allow pinning nested commands:
|
||||
|
||||
```csharp
|
||||
public override ICommandItem? GetCommandItem(string id)
|
||||
{
|
||||
// Look up commands by their Id
|
||||
foreach (var item in GetAllCommands())
|
||||
{
|
||||
if (item?.Command is ICommand cmd && cmd.Id == id)
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- All dock band `ICommandItem` objects must have a `Command` with a **non-empty `Id`** — items without an ID are ignored
|
||||
- Set `Id` on your `CommandProvider` (e.g., `Id = "com.mycompany.myextension"`)
|
||||
- Use `WrappedDockItem` for multi-button bands backed by a `ListPage`
|
||||
- Keep dock band updates lightweight — they run frequently
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Adding Dock support](https://learn.microsoft.com/windows/powertoys/command-palette/adding-dock-support)
|
||||
@@ -1,202 +0,0 @@
|
||||
---
|
||||
name: add-extension-settings
|
||||
description: >-
|
||||
Add a settings page to your Command Palette extension.
|
||||
Use when asked to add settings, preferences, configuration options,
|
||||
toggles, text inputs, dropdowns, or user-customizable behavior.
|
||||
Covers ToggleSetting, TextSetting, ChoiceSetSetting, and persistence.
|
||||
---
|
||||
|
||||
# Add Extension Settings
|
||||
|
||||
Add a settings page to your Command Palette extension using the built-in settings helpers. Settings are automatically persisted and restored by the extension host.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Adding user-configurable options to your extension
|
||||
- Creating toggle switches for features
|
||||
- Adding text input fields for configuration
|
||||
- Creating dropdown menus for option selection
|
||||
- Persisting user preferences across sessions
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Step 1: Create a Settings Manager
|
||||
|
||||
Create a new file `SettingsManager.cs`:
|
||||
|
||||
```csharp
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace YourExtension;
|
||||
|
||||
internal sealed class SettingsManager
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
|
||||
public SettingsManager()
|
||||
{
|
||||
_settings = new Settings();
|
||||
|
||||
var maxResults = new TextSetting(
|
||||
"maxResults",
|
||||
"Maximum Results",
|
||||
"Maximum number of results to display",
|
||||
"10");
|
||||
|
||||
var showSubtitles = new ToggleSetting(
|
||||
"showSubtitles",
|
||||
"Show Subtitles",
|
||||
"Display subtitle text under each result",
|
||||
true);
|
||||
|
||||
var sortOrder = new ChoiceSetSetting(
|
||||
"sortOrder",
|
||||
"Sort Order",
|
||||
"How to sort results",
|
||||
[
|
||||
new ChoiceSetSetting.Choice("Alphabetical", "alpha"),
|
||||
new ChoiceSetSetting.Choice("Most Recent", "recent"),
|
||||
new ChoiceSetSetting.Choice("Most Used", "frequent"),
|
||||
],
|
||||
"alpha");
|
||||
|
||||
_settings.AddSetting(maxResults);
|
||||
_settings.AddSetting(showSubtitles);
|
||||
_settings.AddSetting(sortOrder);
|
||||
|
||||
// React to settings changes
|
||||
_settings.SettingsChanged += OnSettingsChanged;
|
||||
}
|
||||
|
||||
public ICommandSettings Settings => _settings;
|
||||
|
||||
public int MaxResults => int.TryParse(
|
||||
_settings.GetSetting<string>("maxResults"), out var val) ? val : 10;
|
||||
|
||||
public bool ShowSubtitles =>
|
||||
_settings.GetSetting<bool>("showSubtitles");
|
||||
|
||||
public string SortOrder =>
|
||||
_settings.GetSetting<string>("sortOrder") ?? "alpha";
|
||||
|
||||
private void OnSettingsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// React to settings changes (e.g., refresh data)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Wire into CommandProvider
|
||||
|
||||
In your `CommandsProvider`, expose the settings:
|
||||
|
||||
```csharp
|
||||
public partial class MyCommandsProvider : CommandProvider
|
||||
{
|
||||
private readonly SettingsManager _settingsManager = new();
|
||||
private readonly ICommandItem[] _commands;
|
||||
|
||||
public MyCommandsProvider()
|
||||
{
|
||||
DisplayName = "My Extension";
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
|
||||
Settings = _settingsManager.Settings; // This exposes settings to CmdPal
|
||||
_commands = [
|
||||
new CommandItem(new MyPage(_settingsManager)) { Title = DisplayName },
|
||||
];
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() => _commands;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Use Settings in Pages
|
||||
|
||||
```csharp
|
||||
internal sealed partial class MyPage : ListPage
|
||||
{
|
||||
private readonly SettingsManager _settings;
|
||||
|
||||
public MyPage(SettingsManager settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
var items = GetAllItems();
|
||||
return items.Take(_settings.MaxResults).ToArray();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setting Types
|
||||
|
||||
| Type | UI Control | Value Type | Constructor Parameters |
|
||||
|------|-----------|------------|----------------------|
|
||||
| `ToggleSetting` | Toggle switch | `bool` | `(id, label, description, defaultValue)` |
|
||||
| `TextSetting` | Text input | `string` | `(id, label, description, defaultValue)` |
|
||||
| `ChoiceSetSetting` | Dropdown | `string` | `(id, label, description, choices[], defaultValue)` |
|
||||
|
||||
## Key Points
|
||||
|
||||
- Settings are automatically persisted by the CmdPal host
|
||||
- Use `SettingsChanged` event to react to changes in real-time
|
||||
- Access values via `GetSetting<T>(id)` with the setting's string id
|
||||
- Pass the settings manager to pages/commands that need configuration
|
||||
- Settings page appears automatically when `Settings` is set on `CommandProvider`
|
||||
|
||||
## Grouping Settings
|
||||
|
||||
For extensions with many settings, organize them into logical groups:
|
||||
|
||||
```csharp
|
||||
public SettingsManager()
|
||||
{
|
||||
_settings = new Settings();
|
||||
|
||||
// Appearance group
|
||||
var theme = new ChoiceSetSetting("theme", "Theme", "UI theme",
|
||||
[
|
||||
new ChoiceSetSetting.Choice("Light", "light"),
|
||||
new ChoiceSetSetting.Choice("Dark", "dark"),
|
||||
new ChoiceSetSetting.Choice("System", "system"),
|
||||
],
|
||||
"system");
|
||||
|
||||
var fontSize = new TextSetting("fontSize", "Font Size", "Display font size", "14");
|
||||
|
||||
// Behavior group
|
||||
var autoRefresh = new ToggleSetting("autoRefresh", "Auto-Refresh",
|
||||
"Automatically refresh results", true);
|
||||
|
||||
var refreshInterval = new TextSetting("refreshInterval", "Refresh Interval",
|
||||
"Seconds between auto-refreshes", "30");
|
||||
|
||||
_settings.AddSetting(theme);
|
||||
_settings.AddSetting(fontSize);
|
||||
_settings.AddSetting(autoRefresh);
|
||||
_settings.AddSetting(refreshInterval);
|
||||
}
|
||||
```
|
||||
|
||||
## Reacting to Changes
|
||||
|
||||
Use the `SettingsChanged` event to update behavior when the user modifies settings:
|
||||
|
||||
```csharp
|
||||
private void OnSettingsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// Invalidate cached data
|
||||
_cachedItems = null;
|
||||
|
||||
// Notify pages to refresh
|
||||
OnItemsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [SampleSettingsPage.cs](https://github.com/microsoft/PowerToys/blob/main/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleSettingsPage.cs)
|
||||
@@ -1,164 +0,0 @@
|
||||
---
|
||||
name: add-fallback-commands
|
||||
description: >-
|
||||
Add fallback commands to your Command Palette extension for catch-all search behavior.
|
||||
Use when asked to add search functionality, query matching, direct input handling,
|
||||
calculator-style evaluation, URL opening, command execution, or results that appear
|
||||
when no other extension matches. Used by 14 of 20 built-in extensions.
|
||||
---
|
||||
|
||||
# Add Fallback Commands
|
||||
|
||||
Fallback commands are shown in Command Palette when no other results match the user's query. They enable your extension to act as a catch-all handler — perfect for calculators, web search, command execution, file path opening, and more.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Adding search functionality that responds to any user input
|
||||
- Creating a calculator that evaluates expressions as the user types
|
||||
- Building a web search that triggers on unmatched queries
|
||||
- Opening files or URLs typed directly into the palette
|
||||
- Executing shell commands from the search bar
|
||||
|
||||
## How Fallback Commands Work
|
||||
|
||||
1. User types a query in Command Palette
|
||||
2. If no top-level commands match, CmdPal asks extensions for fallback results
|
||||
3. Your extension's `FallbackCommands()` provides items that respond to the query
|
||||
4. The fallback items can be static (always shown) or dynamic (filtered by query)
|
||||
|
||||
## Quick Start: Static Fallback
|
||||
|
||||
Override `FallbackCommands()` in your `CommandProvider`:
|
||||
|
||||
```csharp
|
||||
public partial class MyCommandsProvider : CommandProvider
|
||||
{
|
||||
private readonly ICommandItem[] _commands;
|
||||
private readonly FallbackCommandItem[] _fallbacks;
|
||||
|
||||
public MyCommandsProvider()
|
||||
{
|
||||
DisplayName = "Web Search";
|
||||
Icon = new IconInfo("\uE721"); // Search icon
|
||||
|
||||
var searchPage = new WebSearchPage();
|
||||
_commands = [new CommandItem(searchPage) { Title = DisplayName }];
|
||||
_fallbacks = [new FallbackCommandItem(searchPage) { Title = "Search the web" }];
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() => _commands;
|
||||
public override IFallbackCommandItem[] FallbackCommands() => _fallbacks;
|
||||
}
|
||||
```
|
||||
|
||||
## Dynamic Fallback with DynamicListPage
|
||||
|
||||
For fallbacks that filter results based on the query, use `DynamicListPage`:
|
||||
|
||||
```csharp
|
||||
internal sealed partial class WebSearchPage : DynamicListPage
|
||||
{
|
||||
private string _query = string.Empty;
|
||||
|
||||
public WebSearchPage()
|
||||
{
|
||||
Icon = new IconInfo("\uE721");
|
||||
Title = "Web Search";
|
||||
Name = "Search";
|
||||
PlaceholderText = "Type to search...";
|
||||
}
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
_query = newSearch;
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_query))
|
||||
return [];
|
||||
|
||||
return [
|
||||
new ListItem(new OpenUrlCommand($"https://www.google.com/search?q={Uri.EscapeDataString(_query)}"))
|
||||
{
|
||||
Title = $"Search Google for \"{_query}\"",
|
||||
Icon = new IconInfo("\uE721"),
|
||||
},
|
||||
new ListItem(new OpenUrlCommand($"https://www.bing.com/search?q={Uri.EscapeDataString(_query)}"))
|
||||
{
|
||||
Title = $"Search Bing for \"{_query}\"",
|
||||
Icon = new IconInfo("\uE721"),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Responsive Fallback with Cancellation
|
||||
|
||||
For expensive operations (API calls, file searches), use cancellation to stay responsive:
|
||||
|
||||
```csharp
|
||||
internal sealed partial class SmartSearchPage : DynamicListPage
|
||||
{
|
||||
private CancellationTokenSource? _cts;
|
||||
private IListItem[] _results = [];
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
// Cancel any in-flight search
|
||||
_cts?.Cancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// Debounce: wait for user to stop typing
|
||||
await Task.Delay(300, token);
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
// Perform search
|
||||
_results = await SearchAsync(newSearch, token);
|
||||
RaiseItemsChanged();
|
||||
}, token);
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => _results;
|
||||
|
||||
private async Task<IListItem[]> SearchAsync(string query, CancellationToken token)
|
||||
{
|
||||
// Your search logic here
|
||||
// Check token.IsCancellationRequested periodically
|
||||
return [];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Real-World Examples (from built-in extensions)
|
||||
|
||||
| Extension | Fallback Behavior |
|
||||
|-----------|------------------|
|
||||
| **Apps** | Search installed applications by name |
|
||||
| **Calc** | Evaluate mathematical expressions directly |
|
||||
| **Shell** | Execute command-line commands |
|
||||
| **WebSearch** | Search the web with configured engine |
|
||||
| **Indexer** | Open files by path |
|
||||
| **TimeDate** | Parse time/date queries |
|
||||
| **WindowsSettings** | Jump to Windows Settings pages |
|
||||
| **WinGet** | Search WinGet packages |
|
||||
| **WindowWalker** | Find and switch to open windows |
|
||||
|
||||
## Key Points
|
||||
|
||||
- `FallbackCommands()` returns `IFallbackCommandItem[]` (not `ICommandItem[]`)
|
||||
- Use `FallbackCommandItem` wrapper (not `CommandItem`)
|
||||
- Wrap a `DynamicListPage` for query-reactive results
|
||||
- Cancel previous searches when new input arrives
|
||||
- Keep fallback responses fast — users expect instant results
|
||||
- Use `PlaceholderText` on your page to guide users
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Extension samples](https://learn.microsoft.com/windows/powertoys/command-palette/samples)
|
||||
- [Extensibility overview](https://learn.microsoft.com/windows/powertoys/command-palette/extensibility-overview)
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
name: publish-extension
|
||||
description: >-
|
||||
Publish your Command Palette extension to the Microsoft Store or WinGet.
|
||||
Use when asked to publish, distribute, release, deploy to store,
|
||||
create MSIX packages, submit to WinGet, set up CI/CD for releases,
|
||||
or automate builds with GitHub Actions.
|
||||
---
|
||||
|
||||
# Publish Your Command Palette Extension
|
||||
|
||||
Guide for distributing your Command Palette extension through the Microsoft Store, WinGet, or both.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Publishing your extension to the Microsoft Store
|
||||
- Submitting your extension to WinGet for `winget install` discovery
|
||||
- Setting up GitHub Actions to automate builds and releases
|
||||
- Creating MSIX packages for Store submission
|
||||
- Creating EXE installers for WinGet submission
|
||||
|
||||
## Publishing Options
|
||||
|
||||
| Channel | Package Format | Discovery | Auto-Updates |
|
||||
|---------|---------------|-----------|--------------|
|
||||
| Microsoft Store | MSIX bundle | Store app, `ms-windows-store://` link | Yes |
|
||||
| WinGet | EXE installer | `winget install`, CmdPal browse | Yes (via manifest) |
|
||||
|
||||
**Recommendation**: Publish to both for maximum reach. WinGet enables direct discovery from within Command Palette.
|
||||
|
||||
## Workflows
|
||||
|
||||
### Microsoft Store Publishing
|
||||
See [store-publishing.md](references/store-publishing.md) for the complete step-by-step guide.
|
||||
|
||||
**Summary:**
|
||||
1. Register for Partner Center
|
||||
2. Update `Package.appxmanifest` and `.csproj` with Partner Center identity
|
||||
3. Build MSIX for x64 and ARM64
|
||||
4. Create MSIX bundle
|
||||
5. Submit to Partner Center
|
||||
|
||||
### WinGet Publishing
|
||||
See [winget-publishing.md](references/winget-publishing.md) for the complete step-by-step guide.
|
||||
|
||||
**Summary:**
|
||||
1. Switch project to unpackaged mode
|
||||
2. Create Inno Setup installer script
|
||||
3. Build EXE installers
|
||||
4. Submit manifest via `wingetcreate new`
|
||||
5. Optionally automate with GitHub Actions
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Visual Studio](https://visualstudio.microsoft.com/) with C# and WinUI workloads
|
||||
- [Partner Center account](https://partner.microsoft.com/dashboard/home) (for Store publishing)
|
||||
- [GitHub CLI](https://cli.github.com/) (for WinGet publishing)
|
||||
- [WingetCreate](https://github.com/microsoft/winget-create) — `winget install Microsoft.WingetCreate`
|
||||
- [Inno Setup](https://jrsoftware.org/isdl.php) (for WinGet EXE packaging)
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Your extension's CLSID (the `[Guid("...")]` in your main .cs file) must be unique and consistent across all files
|
||||
- WinGet manifests must include the `windows-commandpalette-extension` tag for CmdPal discovery
|
||||
- MSIX packages require both x64 and ARM64 builds for Store submission
|
||||
- WindowsAppSdk must be listed as a dependency in WinGet manifests
|
||||
@@ -1,169 +0,0 @@
|
||||
# Microsoft Store Publishing Guide
|
||||
|
||||
Complete step-by-step guide for publishing your Command Palette extension to the Microsoft Store.
|
||||
|
||||
## Step 1: Set Up Microsoft Store
|
||||
|
||||
1. Go to [Partner Center](https://partner.microsoft.com/dashboard/home)
|
||||
2. Navigate to **Apps and Games** → **New product** → **MSIX or PWA app**
|
||||
3. Reserve your app name (e.g., `My Extension for Command Palette`)
|
||||
4. Once created, go to **Product Management** → **Product Identity**
|
||||
5. Copy these three values — you'll need them in the next step:
|
||||
|
||||
| Partner Center Field | Where It Goes |
|
||||
|---------------------|---------------|
|
||||
| **Package/Identity/Name** | `Package.appxmanifest` → `Identity Name` and `.csproj` → `AppxPackageIdentityName` |
|
||||
| **Package/Identity/Publisher** | `Package.appxmanifest` → `Identity Publisher` and `.csproj` → `AppxPackagePublisher` |
|
||||
| **Package/Properties/PublisherDisplayName** | `Package.appxmanifest` → `Properties PublisherDisplayName` |
|
||||
|
||||
## Step 2: Prepare the Extension
|
||||
|
||||
### Update `Package.appxmanifest`
|
||||
|
||||
Replace the placeholder identity values with your Partner Center values:
|
||||
|
||||
```xml
|
||||
<Identity
|
||||
Name="YOUR_PACKAGE_IDENTITY_NAME_HERE"
|
||||
Publisher="YOUR_PACKAGE_IDENTITY_PUBLISHER_HERE"
|
||||
Version="0.0.1.0" />
|
||||
```
|
||||
|
||||
And update the publisher display name:
|
||||
|
||||
```xml
|
||||
<Properties>
|
||||
<DisplayName>Your Extension Name</DisplayName>
|
||||
<PublisherDisplayName>YOUR_PUBLISHER_DISPLAY_NAME_HERE</PublisherDisplayName>
|
||||
<!-- ... -->
|
||||
</Properties>
|
||||
```
|
||||
|
||||
### Update `.csproj`
|
||||
|
||||
Add or update the following properties in your `.csproj` file:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<AppxPackageIdentityName>YOUR_PACKAGE_IDENTITY_NAME_HERE</AppxPackageIdentityName>
|
||||
<AppxPackagePublisher>YOUR_PACKAGE_IDENTITY_PUBLISHER_HERE</AppxPackagePublisher>
|
||||
<AppxPackageVersion>0.0.1.0</AppxPackageVersion>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Update Image Assets ItemGroup
|
||||
|
||||
Ensure all image assets are included in the package by updating the `ItemGroup`:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\**\*.png" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
> **Tip:** The `Assets` folder should contain your Store logos and extension icons at the required sizes (44x44, 150x150, etc.). You can generate these from a single high-resolution image.
|
||||
|
||||
## Step 3: Build MSIX Packages
|
||||
|
||||
Build for both x64 and ARM64 architectures:
|
||||
|
||||
```powershell
|
||||
# x64 build
|
||||
dotnet build --configuration Release -p:GenerateAppxPackageOnBuild=true -p:Platform=x64 -p:AppxPackageDir="AppPackages\x64\"
|
||||
|
||||
# ARM64 build
|
||||
dotnet build --configuration Release -p:GenerateAppxPackageOnBuild=true -p:Platform=ARM64 -p:AppxPackageDir="AppPackages\ARM64\"
|
||||
```
|
||||
|
||||
Verify the MSIX files were created:
|
||||
|
||||
```powershell
|
||||
dir AppPackages -Recurse -Filter "*.msix"
|
||||
```
|
||||
|
||||
You should see two `.msix` files, one for each architecture.
|
||||
|
||||
## Step 4: Create MSIX Bundle
|
||||
|
||||
### Create the bundle mapping file
|
||||
|
||||
Create a file named `bundle_mapping.txt` that maps each MSIX to its architecture:
|
||||
|
||||
```text
|
||||
[Files]
|
||||
"AppPackages\x64\YourExtension_0.0.1.0_x64\YourExtension_0.0.1.0_x64.msix" "YourExtension_0.0.1.0_x64.msix"
|
||||
"AppPackages\ARM64\YourExtension_0.0.1.0_ARM64\YourExtension_0.0.1.0_ARM64.msix" "YourExtension_0.0.1.0_ARM64.msix"
|
||||
```
|
||||
|
||||
> **Note:** Update the paths and filenames to match your actual build output. Check the `AppPackages` directory structure after building.
|
||||
|
||||
### Run makeappx
|
||||
|
||||
```powershell
|
||||
makeappx bundle /f bundle_mapping.txt /p YourExtension_0.0.1.0_Bundle.msixbundle
|
||||
```
|
||||
|
||||
> **Tip:** `makeappx.exe` is included with the Windows SDK. If it's not in your PATH, find it at:
|
||||
> `C:\Program Files (x86)\Windows Kits\10\bin\<version>\x64\makeappx.exe`
|
||||
|
||||
## Step 5: Submit to Partner Center
|
||||
|
||||
1. Go to [Partner Center](https://partner.microsoft.com/dashboard/home)
|
||||
2. Navigate to your app → **Start a new submission**
|
||||
3. In **Packages**, upload your `.msixbundle` file
|
||||
4. In **Store Listings** → **Description**, include a note like:
|
||||
|
||||
> `YourExtension` integrates with the Windows Command Palette to provide [describe your extension's functionality]. Requires PowerToys with Command Palette enabled.
|
||||
|
||||
5. In **Notes for certification**, add testing instructions:
|
||||
|
||||
> This extension requires Microsoft PowerToys (available from the Microsoft Store or https://github.com/microsoft/PowerToys) with the Command Palette feature enabled. To test:
|
||||
> 1. Install PowerToys and enable Command Palette
|
||||
> 2. Install this extension
|
||||
> 3. Open Command Palette (Win+Alt+Space by default)
|
||||
> 4. Search for [your extension's commands]
|
||||
|
||||
6. Set **Availability** and pricing as appropriate
|
||||
7. Click **Submit for certification**
|
||||
|
||||
Certification typically takes 1–3 business days.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before submitting, verify:
|
||||
|
||||
- [ ] Partner Center identity values match exactly in both `Package.appxmanifest` and `.csproj`
|
||||
- [ ] `AppxPackageVersion` is set correctly and incremented from any previous submission
|
||||
- [ ] Both x64 and ARM64 MSIX files are built successfully
|
||||
- [ ] MSIX bundle is created without errors
|
||||
- [ ] Extension installs and runs correctly from the MSIX package locally
|
||||
- [ ] Store listing includes clear description mentioning Command Palette integration
|
||||
- [ ] Testing instructions mention the PowerToys/Command Palette prerequisite
|
||||
- [ ] All required Store logos and screenshots are provided
|
||||
- [ ] Privacy policy URL is set (if your extension accesses network or user data)
|
||||
|
||||
## Store-Only Discovery Limitations
|
||||
|
||||
> **Important:** Command Palette cannot currently search for extensions published only to the Microsoft Store via its built-in browse experience. Users can find Store-published extensions through:
|
||||
>
|
||||
> - Direct Store link shared by the developer
|
||||
> - The Store's extension tag URL:
|
||||
> ```
|
||||
> ms-windows-store://assoc/?Tags=AppExtension-com.microsoft.commandpalette
|
||||
> ```
|
||||
> - Searching the Store app directly
|
||||
>
|
||||
> For discoverability within Command Palette's browse experience, also publish to WinGet.
|
||||
> See [winget-publishing.md](winget-publishing.md) for details.
|
||||
|
||||
## Updating Your Extension
|
||||
|
||||
To publish an update:
|
||||
|
||||
1. Increment the version in `.csproj` (`AppxPackageVersion`) and `Package.appxmanifest`
|
||||
2. Rebuild MSIX packages for both architectures
|
||||
3. Recreate the MSIX bundle with updated filenames
|
||||
4. Create a new submission in Partner Center and upload the new bundle
|
||||
5. Submit for certification
|
||||
|
||||
The Store will automatically update users who have installed your extension.
|
||||
@@ -1,413 +0,0 @@
|
||||
# WinGet Publishing Guide
|
||||
|
||||
Complete step-by-step guide for publishing your Command Palette extension to WinGet for `winget install` discovery and installation.
|
||||
|
||||
## Why WinGet?
|
||||
|
||||
Publishing to WinGet enables:
|
||||
|
||||
- Users to install via `winget install YourPublisher.YourExtension`
|
||||
- Discovery directly inside Command Palette's built-in browse experience
|
||||
- Automatic update detection via WinGet manifests
|
||||
|
||||
## Step 1: Prepare the Project for Unpackaged Distribution
|
||||
|
||||
WinGet distribution uses an unpackaged (EXE-based) build instead of MSIX.
|
||||
|
||||
### Update `.csproj`
|
||||
|
||||
Remove any existing `<PublishProfile>` property and add unpackaged mode:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<!-- Remove or comment out this line if present: -->
|
||||
<!-- <PublishProfile>win-$(Platform)</PublishProfile> -->
|
||||
|
||||
<!-- Add this for unpackaged distribution: -->
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Note Your CLSID
|
||||
|
||||
Find the `[Guid("...")]` attribute in your main `.cs` file (e.g., `SampleExtension.cs`):
|
||||
|
||||
```csharp
|
||||
[Guid("YOUR-GUID-HERE")]
|
||||
public sealed partial class SampleExtension : IExtension
|
||||
```
|
||||
|
||||
You'll need this exact GUID for the installer script. It must match across all files.
|
||||
|
||||
## Step 2: Create Installer Scripts
|
||||
|
||||
### Inno Setup Script: `setup-template.iss`
|
||||
|
||||
Create this file in your project root. Replace all `TODO` placeholders with your values:
|
||||
|
||||
```iss
|
||||
; Inno Setup script for Command Palette extension
|
||||
|
||||
#define MyAppName "TODO_YOUR_EXTENSION_NAME"
|
||||
#define MyAppVersion "TODO_YOUR_VERSION"
|
||||
#define MyAppPublisher "TODO_YOUR_PUBLISHER_NAME"
|
||||
#define MyAppURL "TODO_YOUR_PROJECT_URL"
|
||||
#define MyAppCLSID "TODO_YOUR_CLSID_WITH_BRACES"
|
||||
; Example CLSID: {12345678-1234-1234-1234-123456789012}
|
||||
|
||||
[Setup]
|
||||
AppId={#MyAppCLSID}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
OutputBaseFilename={#MyAppName}_{#MyAppVersion}_{#SetupSetting("ArchitecturesAllowed")}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
PrivilegesRequired=lowest
|
||||
OutputDir=Installer
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
Source: "publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
|
||||
[Registry]
|
||||
; Register the COM server for Command Palette discovery
|
||||
Root: HKCU; Subkey: "Software\Classes\CLSID\{#MyAppCLSID}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppName}"; Flags: uninsdeletekey
|
||||
Root: HKCU; Subkey: "Software\Classes\CLSID\{#MyAppCLSID}\InprocServer32"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppName}.dll"; Flags: uninsdeletekey
|
||||
Root: HKCU; Subkey: "Software\Classes\CLSID\{#MyAppCLSID}\InprocServer32"; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Both"; Flags: uninsdeletekey
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: "{app}"
|
||||
```
|
||||
|
||||
> **Important:** The `AppId` must use your CLSID wrapped in braces. The registry entries register your extension's COM server so Command Palette can discover it.
|
||||
|
||||
### Build Script: `build-exe.ps1`
|
||||
|
||||
Create this PowerShell script in your project root:
|
||||
|
||||
```powershell
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds EXE installers for x64 and ARM64 using dotnet publish and Inno Setup.
|
||||
.DESCRIPTION
|
||||
Publishes the project for both architectures, then runs Inno Setup to create
|
||||
EXE installers suitable for WinGet submission.
|
||||
#>
|
||||
|
||||
param(
|
||||
[string]$Configuration = "Release",
|
||||
[string]$Version = "0.0.1"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$projectName = (Get-ChildItem -Filter "*.csproj" | Select-Object -First 1).BaseName
|
||||
if (-not $projectName) {
|
||||
Write-Error "No .csproj file found in the current directory."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$architectures = @("x64", "arm64")
|
||||
|
||||
foreach ($arch in $architectures) {
|
||||
Write-Host "`n=== Building $arch ===" -ForegroundColor Cyan
|
||||
|
||||
# Publish
|
||||
Write-Host "Publishing for $arch..."
|
||||
dotnet publish -c $Configuration -r "win-$arch" -o "publish" --self-contained=false
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "dotnet publish failed for $arch"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create installer
|
||||
Write-Host "Creating installer for $arch..."
|
||||
$issFile = "setup-template.iss"
|
||||
if (-not (Test-Path $issFile)) {
|
||||
Write-Error "Inno Setup script not found: $issFile"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$archFlag = if ($arch -eq "arm64") { "arm64" } else { "x64" }
|
||||
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" `
|
||||
/DMyAppVersion="$Version" `
|
||||
/DArchitecturesAllowed="$archFlag" `
|
||||
$issFile
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Inno Setup failed for $arch"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Clean publish directory for next architecture
|
||||
Remove-Item -Recurse -Force "publish" -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "=== $arch complete ===" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host "`nInstallers created in the 'Installer' directory:" -ForegroundColor Cyan
|
||||
Get-ChildItem -Path "Installer" -Filter "*.exe" | ForEach-Object { Write-Host " $_" }
|
||||
```
|
||||
|
||||
## Step 3: Build EXE Installers
|
||||
|
||||
Run the build script from your project directory:
|
||||
|
||||
```powershell
|
||||
.\build-exe.ps1
|
||||
```
|
||||
|
||||
This produces two EXE files in the `Installer` directory:
|
||||
|
||||
```
|
||||
Installer\YourExtension_0.0.1_x64.exe
|
||||
Installer\YourExtension_0.0.1_arm64.exe
|
||||
```
|
||||
|
||||
Verify both installers work by running them locally and confirming your extension appears in Command Palette.
|
||||
|
||||
## Step 4: Create a GitHub Release
|
||||
|
||||
Tag your repository with the version and create a release with the EXE files:
|
||||
|
||||
```powershell
|
||||
# Tag the release
|
||||
git tag -a v0.0.1 -m "Release v0.0.1"
|
||||
git push origin v0.0.1
|
||||
|
||||
# Create release and upload assets (requires GitHub CLI)
|
||||
gh release create v0.0.1 `
|
||||
"Installer\YourExtension_0.0.1_x64.exe" `
|
||||
"Installer\YourExtension_0.0.1_arm64.exe" `
|
||||
--title "v0.0.1" `
|
||||
--notes "Initial release of YourExtension for Command Palette."
|
||||
```
|
||||
|
||||
After creating the release, copy the download URLs for both EXE files — you'll need them for the WinGet submission.
|
||||
|
||||
## Step 5: Submit to WinGet
|
||||
|
||||
Use `wingetcreate` to generate a WinGet manifest and submit a pull request:
|
||||
|
||||
```powershell
|
||||
wingetcreate new "<URL_TO_x64.exe>" "<URL_TO_arm64.exe>"
|
||||
```
|
||||
|
||||
`wingetcreate` will interactively prompt you for:
|
||||
|
||||
| Prompt | Example Value |
|
||||
|--------|---------------|
|
||||
| **PackageIdentifier** | `YourPublisher.YourExtension` |
|
||||
| **PackageVersion** | `0.0.1` |
|
||||
| **PackageLocale** | `en-US` |
|
||||
| **Publisher** | `Your Name` |
|
||||
| **PackageName** | `YourExtension for Command Palette` |
|
||||
| **License** | `MIT` |
|
||||
| **ShortDescription** | `A Command Palette extension that does X` |
|
||||
|
||||
After answering all prompts, `wingetcreate` will create a PR against the [winget-pkgs](https://github.com/microsoft/winget-pkgs) repository.
|
||||
|
||||
## Step 6: Add the Command Palette Tag (CRITICAL)
|
||||
|
||||
> **This step is required for your extension to appear in Command Palette's browse experience.**
|
||||
|
||||
After `wingetcreate` generates the manifest files, you **must** edit each `.locale.*.yaml` file to add the Command Palette tag.
|
||||
|
||||
In every locale YAML file (e.g., `YourPublisher.YourExtension.locale.en-US.yaml`), add:
|
||||
|
||||
```yaml
|
||||
Tags:
|
||||
- windows-commandpalette-extension
|
||||
```
|
||||
|
||||
Example of a complete locale file with the tag:
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.6.0.schema.json
|
||||
PackageIdentifier: YourPublisher.YourExtension
|
||||
PackageVersion: 0.0.1
|
||||
PackageLocale: en-US
|
||||
Publisher: Your Name
|
||||
PackageName: YourExtension for Command Palette
|
||||
License: MIT
|
||||
ShortDescription: A Command Palette extension that does X
|
||||
Tags:
|
||||
- windows-commandpalette-extension
|
||||
ManifestType: defaultLocale
|
||||
ManifestVersion: 1.6.0
|
||||
```
|
||||
|
||||
Without this tag, Command Palette will not discover your extension in its browse experience.
|
||||
|
||||
## Step 7: Ensure WindowsAppSdk Dependency
|
||||
|
||||
Your WinGet manifest must declare a dependency on the Windows App SDK so it gets installed automatically. In the `installer.yaml` manifest file, add:
|
||||
|
||||
```yaml
|
||||
Dependencies:
|
||||
PackageDependencies:
|
||||
- PackageIdentifier: Microsoft.WindowsAppRuntime.1.7
|
||||
MinimumVersion: 7001.632.252.0
|
||||
```
|
||||
|
||||
> **Note:** Update the version number to match the Windows App SDK version your project targets. Check your `.csproj` for the `WindowsAppSDK` package version.
|
||||
|
||||
## Step 8: GitHub Actions Automation (Optional)
|
||||
|
||||
Automate your build, release, and WinGet submission process with GitHub Actions.
|
||||
|
||||
### Release Workflow: `.github/workflows/release-extension.yml`
|
||||
|
||||
```yaml
|
||||
name: Release Extension
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
PROJECT_NAME: YourExtension
|
||||
DOTNET_VERSION: '9.0.x'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x64, arm64]
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Install Inno Setup
|
||||
run: choco install innosetup -y --no-progress
|
||||
|
||||
- name: Detect version
|
||||
id: version
|
||||
run: |
|
||||
$tag = "${{ github.ref_name }}" -replace '^v', ''
|
||||
echo "VERSION=$tag" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish -c Release -r win-${{ matrix.arch }} -o publish --self-contained=false
|
||||
|
||||
- name: Create installer
|
||||
run: |
|
||||
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" `
|
||||
/DMyAppVersion="${{ steps.version.outputs.VERSION }}" `
|
||||
/DArchitecturesAllowed="${{ matrix.arch }}" `
|
||||
setup-template.iss
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: installer-${{ matrix.arch }}
|
||||
path: Installer/*.exe
|
||||
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: artifacts/*.exe
|
||||
generate_release_notes: true
|
||||
|
||||
winget-update:
|
||||
needs: release
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Detect version
|
||||
id: version
|
||||
run: |
|
||||
$tag = "${{ github.ref_name }}" -replace '^v', ''
|
||||
echo "VERSION=$tag" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Update WinGet manifest
|
||||
run: |
|
||||
$baseUrl = "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}"
|
||||
wingetcreate update YourPublisher.YourExtension `
|
||||
--version ${{ steps.version.outputs.VERSION }} `
|
||||
--urls "$baseUrl/${{ env.PROJECT_NAME }}_${{ steps.version.outputs.VERSION }}_x64.exe" "$baseUrl/${{ env.PROJECT_NAME }}_${{ steps.version.outputs.VERSION }}_arm64.exe" `
|
||||
--submit `
|
||||
--token ${{ secrets.WINGET_PAT }}
|
||||
```
|
||||
|
||||
### Required Secrets
|
||||
|
||||
| Secret | Description |
|
||||
|--------|-------------|
|
||||
| `WINGET_PAT` | GitHub Personal Access Token with `public_repo` scope, used by `wingetcreate` to submit PRs to `microsoft/winget-pkgs` |
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Push a version tag** (e.g., `git tag v0.0.2 && git push origin v0.0.2`)
|
||||
2. **Build job** runs in parallel for x64 and ARM64, creating EXE installers
|
||||
3. **Release job** creates a GitHub Release and uploads the EXE files
|
||||
4. **WinGet update job** automatically submits an updated manifest to `winget-pkgs`
|
||||
|
||||
> **Note:** The `winget-update` job uses `wingetcreate update` (not `new`) because it assumes you've already submitted your initial manifest manually. For the first submission, follow Steps 5–7 above.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before submitting to WinGet, verify:
|
||||
|
||||
- [ ] `.csproj` has `<WindowsPackageType>None</WindowsPackageType>` set
|
||||
- [ ] CLSID in `setup-template.iss` matches the `[Guid("...")]` in your main `.cs` file
|
||||
- [ ] Both x64 and ARM64 EXE installers build successfully
|
||||
- [ ] Installer registers the COM server correctly (check `HKCU\Software\Classes\CLSID\{your-clsid}`)
|
||||
- [ ] Extension appears in Command Palette after installing via EXE
|
||||
- [ ] Extension is removed from Command Palette after uninstalling
|
||||
- [ ] GitHub Release contains both EXE files with correct download URLs
|
||||
- [ ] WinGet manifest includes `windows-commandpalette-extension` tag
|
||||
- [ ] WinGet manifest includes `WindowsAppRuntime` dependency
|
||||
- [ ] `winget validate` passes on all manifest files
|
||||
|
||||
## Updating Your Extension on WinGet
|
||||
|
||||
For subsequent releases:
|
||||
|
||||
```powershell
|
||||
wingetcreate update YourPublisher.YourExtension `
|
||||
--version "0.0.2" `
|
||||
--urls "<URL_TO_NEW_x64.exe>" "<URL_TO_NEW_arm64.exe>" `
|
||||
--submit
|
||||
```
|
||||
|
||||
Or simply push a new version tag if you've set up the GitHub Actions workflow above.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Extension not appearing in CmdPal browse | Verify the `windows-commandpalette-extension` tag is in your locale YAML |
|
||||
| COM registration fails | Check that the CLSID matches exactly and registry paths are correct |
|
||||
| `wingetcreate` validation errors | Run `winget validate --manifest <path>` and fix reported issues |
|
||||
| Installer doesn't run silently | Add `/VERYSILENT /SUPPRESSMSGBOXES` flags for silent install support |
|
||||
| Missing WindowsAppSdk at runtime | Ensure the `PackageDependencies` section is in your installer manifest |
|
||||
@@ -12,7 +12,7 @@ public struct InterlockedBoolean(bool initialValue = false)
|
||||
private int _value = initialValue ? 1 : 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the atomic boolean is true.
|
||||
/// Gets or sets the boolean value atomically
|
||||
/// </summary>
|
||||
public bool Value
|
||||
{
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MEL = Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.CmdPal.Common.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="ILogger"/> implementation that delegates to <see cref="ManagedCommon.Logger"/>.
|
||||
/// Instances are created by <see cref="CmdPalLoggerProvider"/>.
|
||||
/// </summary>
|
||||
public sealed class CmdPalLogger(string categoryName) : MEL.ILogger
|
||||
{
|
||||
public IDisposable? BeginScope<TState>(TState state)
|
||||
where TState : notnull => null;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
|
||||
|
||||
public void Log<TState>(
|
||||
LogLevel logLevel,
|
||||
EventId eventId,
|
||||
TState state,
|
||||
Exception? exception,
|
||||
Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(formatter);
|
||||
|
||||
var message = $"[{categoryName}] {formatter(state, exception)}";
|
||||
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Trace:
|
||||
ManagedCommon.Logger.LogTrace(message);
|
||||
break;
|
||||
case LogLevel.Debug:
|
||||
ManagedCommon.Logger.LogDebug(message);
|
||||
break;
|
||||
case LogLevel.Information:
|
||||
ManagedCommon.Logger.LogInfo(message);
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
ManagedCommon.Logger.LogWarning(message);
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
case LogLevel.Critical:
|
||||
if (exception is not null)
|
||||
{
|
||||
ManagedCommon.Logger.LogError(message, exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
ManagedCommon.Logger.LogError(message);
|
||||
}
|
||||
|
||||
break;
|
||||
case LogLevel.None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using MEL = Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.CmdPal.Common.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="MEL.ILoggerProvider"/> that creates <see cref="CmdPalLogger"/> instances
|
||||
/// backed by the <see cref="ManagedCommon.Logger"/> infrastructure.
|
||||
/// Register via <see cref="CmdPalLoggingExtensions.AddCmdPalLogging"/>.
|
||||
/// </summary>
|
||||
public sealed partial class CmdPalLoggerProvider : MEL.ILoggerProvider
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, CmdPalLogger> _loggers = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public MEL.ILogger CreateLogger(string categoryName) =>
|
||||
_loggers.GetOrAdd(categoryName, name => new CmdPalLogger(name));
|
||||
|
||||
public void Dispose() => _loggers.Clear();
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MEL = Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.CmdPal.Common.Logging;
|
||||
|
||||
public static class CmdPalLoggingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers the Microsoft.Extensions.Logging infrastructure and adds a
|
||||
/// <see cref="CmdPalLoggerProvider"/> that routes all <see cref="MEL.ILogger"/>
|
||||
/// output to <see cref="ManagedCommon.Logger"/>.
|
||||
/// </summary>
|
||||
public static IServiceCollection AddCmdPalLogging(this IServiceCollection services)
|
||||
{
|
||||
services.AddLogging(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<MEL.ILoggerProvider, CmdPalLoggerProvider>());
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -61,8 +60,4 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Logging\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,6 +8,6 @@ public sealed class PinyinFuzzyMatcherOptions
|
||||
{
|
||||
public PinyinMode Mode { get; init; } = PinyinMode.AutoSimplifiedChineseUi;
|
||||
|
||||
/// <summary>Gets a value indicating whether IME syllable separators (') are removed for query secondary variant.</summary>
|
||||
/// <summary>Remove IME syllable separators (') for query secondary variant.</summary>
|
||||
public bool RemoveApostrophesForQuery { get; init; } = true;
|
||||
}
|
||||
|
||||
@@ -341,25 +341,25 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the backdrop opacity slider should be visible.
|
||||
/// Gets whether the backdrop opacity slider should be visible.
|
||||
/// </summary>
|
||||
public bool IsBackdropOpacityVisible =>
|
||||
BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the backdrop description (for styles without options) should be visible.
|
||||
/// Gets whether the backdrop description (for styles without options) should be visible.
|
||||
/// </summary>
|
||||
public bool IsMicaBackdropDescriptionVisible =>
|
||||
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsOpacity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether background/colorization settings are available.
|
||||
/// Gets whether background/colorization settings are available.
|
||||
/// </summary>
|
||||
public bool IsBackgroundSettingsEnabled =>
|
||||
BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the "not available" message should be shown (inverse of IsBackgroundSettingsEnabled).
|
||||
/// Gets whether the "not available" message should be shown (inverse of IsBackgroundSettingsEnabled).
|
||||
/// </summary>
|
||||
public bool IsBackgroundNotAvailableVisible =>
|
||||
!BackdropStyles.Get(_settingsService.Settings.BackdropStyle).SupportsColorization;
|
||||
|
||||
Binary file not shown.
@@ -36,17 +36,17 @@ public sealed record BackdropStyleConfig
|
||||
public float FixedOpacity { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this backdrop style supports custom colorization (tint colors).
|
||||
/// Gets whether this backdrop style supports custom colorization (tint colors).
|
||||
/// </summary>
|
||||
public bool SupportsColorization { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this backdrop style supports custom background images.
|
||||
/// Gets whether this backdrop style supports custom background images.
|
||||
/// </summary>
|
||||
public bool SupportsBackgroundImage { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this backdrop style supports opacity adjustment.
|
||||
/// Gets whether this backdrop style supports opacity adjustment.
|
||||
/// </summary>
|
||||
public bool SupportsOpacity { get; init; } = true;
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ internal sealed class ExtensionTemplateService : IExtensionTemplateService
|
||||
|
||||
private static readonly HashSet<string> _copyAsIsTemplateExtensions = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
".md",
|
||||
".png",
|
||||
};
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public sealed class ThemeSnapshot
|
||||
public required float BackgroundBrightness { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether colorization is active (accent color, custom color, or image mode).
|
||||
/// Gets whether colorization is active (accent color, custom color, or image mode).
|
||||
/// </summary>
|
||||
public required bool HasColorization { get; init; }
|
||||
}
|
||||
|
||||
@@ -93,19 +93,19 @@ public record DockBandSettings
|
||||
public required string CommandId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether titles are shown for items in this band.
|
||||
/// Gets or sets whether titles are shown for items in this band.
|
||||
/// If null, falls back to dock-wide ShowLabels setting.
|
||||
/// </summary>
|
||||
public bool? ShowTitles { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether subtitles are shown for items in this band.
|
||||
/// Gets or sets whether subtitles are shown for items in this band.
|
||||
/// If null, falls back to dock-wide ShowLabels setting.
|
||||
/// </summary>
|
||||
public bool? ShowSubtitles { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value for backward compatibility. Maps to ShowTitles.
|
||||
/// Gets or sets a value for backward compatibility. Maps to ShowTitles.
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public bool? ShowLabels
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user