diff --git a/QuickShell/build-exe.ps1 b/QuickShell/build-exe.ps1
index 661721f..34fd85b 100644
--- a/QuickShell/build-exe.ps1
+++ b/QuickShell/build-exe.ps1
@@ -46,11 +46,22 @@ foreach ($Platform in $Platforms) {
$fileCount = (Get-ChildItem -Path $publishDir -Recurse -File).Count
Write-Host "Published $fileCount files to $publishDir" -ForegroundColor Green
+ $runPlatform = if ($Platform -eq "arm64") { "ARM64" } else { "x64" }
+ $buildRunPlugin = Join-Path (Split-Path $ProjectDir -Parent) "scripts\build-run-plugin.ps1"
+ Write-Host "Building PowerToys Run plugin ($runPlatform)..." -ForegroundColor Yellow
+ & $buildRunPlugin -Configuration $Configuration -Platform $runPlatform
+ if ($LASTEXITCODE -ne 0) {
+ throw "build-run-plugin.ps1 failed for $runPlatform with exit code $LASTEXITCODE"
+ }
+
+ $repoRoot = Split-Path -Parent $ProjectDir
+ $runPluginSource = Join-Path $repoRoot "QuickShell.Run\bin\$runPlatform\$Configuration\package"
$setupTemplate = Get-Content (Join-Path $ProjectDir "setup-template.iss") -Raw
$setupScript = $setupTemplate -replace '#define AppVersion ".*"', "#define AppVersion `"$Version`""
+ $setupScript = $setupScript -replace '#define RunPluginSource ".*"', "#define RunPluginSource `"$($runPluginSource.Replace('\', '\\'))`""
+ $setupScript = $setupScript -replace 'OutputDir=bin\\[^\\]+\\installer', ("OutputDir=bin\{0}\installer" -f $Configuration)
$setupScript = $setupScript -replace 'OutputBaseFilename=(.*?)\{#AppVersion\}', "OutputBaseFilename=`$1{#AppVersion}-$Platform"
- $setupScript = $setupScript -replace 'Source: "bin\\Release\\win-x64\\publish', "Source: `"bin\Release\win-$Platform\publish"
-
+ $setupScript = $setupScript -replace 'Source: "bin\\Release\\win-x64\\publish', ("Source: `"bin\{0}\win-{1}\publish" -f $Configuration, $Platform)
if ($Platform -eq "arm64") {
$setupScript = $setupScript -replace '(\[Setup\][^\[]*)(MinVersion=)', "`$1ArchitecturesAllowed=arm64`r`nArchitecturesInstallIn64BitMode=arm64`r`n`$2"
}
diff --git a/QuickShell/setup-template.iss b/QuickShell/setup-template.iss
index 03fa692..f420f22 100644
--- a/QuickShell/setup-template.iss
+++ b/QuickShell/setup-template.iss
@@ -8,6 +8,8 @@
#define Clsid "528cc766-cbe8-4861-9933-722c7a3f3581"
#define InstallAppId "8C4E2F91-6B3D-4A5E-9F1C-2D7E8A0B4C6D"
+#define RunPluginSource "__MUST_BE_SET_BY_BUILD_SCRIPT__"
+#define RunPluginDest "{localappdata}\Microsoft\PowerToys\PowerToys Run\Plugins\QuickShell"
[Setup]
AppId={{{#InstallAppId}}
@@ -32,6 +34,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Files]
Source: "bin\Release\win-x64\publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
+Source: "{#RunPluginSource}\*"; DestDir: "{#RunPluginDest}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons]
Name: "{group}\{#DisplayName}"; Filename: "{app}\{#ExtensionName}.exe"
@@ -42,3 +45,4 @@ Root: HKCU; Subkey: "SOFTWARE\Classes\CLSID\{{{#Clsid}}}\LocalServer32"; ValueTy
[UninstallDelete]
Type: filesandordirs; Name: "{app}"
+Type: filesandordirs; Name: "{#RunPluginDest}"
diff --git a/docs/install.md b/docs/install.md
index e09f654..88d3b14 100644
--- a/docs/install.md
+++ b/docs/install.md
@@ -34,23 +34,32 @@ Install from PowerShell or Command Prompt:
winget install tonythethompson.QuickShell
```
+This installer registers the **Command Palette** extension and installs the **PowerToys Run** plugin (`qs` in Alt+Space). Restart PowerToys after install so Run picks up the plugin.
+
### GitHub Releases
Download the installer directly:
1. Go to [GitHub Releases](https://github.com/tonythethompson/QuickShell/releases){:target="_blank"}
-2. Download the latest **x64** or **ARM64** MSI installer
+2. Download the latest **x64** or **ARM64** **EXE** installer (`QuickShell-Setup-*-x64.exe` or `*-arm64.exe`)
3. Run the installer
+Same as WinGet: includes both Command Palette and PowerToys Run. Restart PowerToys after install.
+
Choose **x64** for most PCs, **ARM64** only if you're on an ARM-based Windows device.
+Standalone Run-only ZIPs (`QuickShell.Run-*.zip`) are also on Releases if you already use the Store build and only want the Run plugin.
+
## Complete setup
After installation, follow these steps:
-1. Open **PowerToys Command Palette** (press **Win + Alt + Space**)
-2. Search for **Reload Command Palette Extension** and run it
-3. Search for **Quick Shell** — you should see it in the results
+1. **Restart PowerToys** (WinGet / GitHub EXE installs the Run plugin; Store installs CmdPal only)
+2. Open **PowerToys Command Palette** (press **Win + Alt + Space**)
+3. Search for **Reload Command Palette Extension** and run it
+4. Search for **Quick Shell** — you should see it in the results
+
+Optional: open **PowerToys Run** (**Alt+Space**), type **`qs`**, to use the same shortcuts from Run.
Not showing up? Make sure Command Palette is enabled in PowerToys settings (Settings → Command Palette → enabled).
diff --git a/docs/powertoys-run-plugin.md b/docs/powertoys-run-plugin.md
index d28541b..bc538c6 100644
--- a/docs/powertoys-run-plugin.md
+++ b/docs/powertoys-run-plugin.md
@@ -4,7 +4,17 @@ Third-party plugin that reads the same shortcuts and settings as the [Quick Shel
## Install
-### From GitHub Releases
+### WinGet or GitHub EXE (recommended)
+
+The **WinGet** package and **GitHub EXE** installers include both the Command Palette extension and this Run plugin. Restart PowerToys after install.
+
+```powershell
+winget install tonythethompson.QuickShell
+```
+
+### Run plugin ZIP only
+
+Use this if you installed Quick Shell from the **Microsoft Store** (CmdPal only) and want Run without reinstalling:
1. Download `QuickShell.Run-x64.zip` or `QuickShell.Run-ARM64.zip` from [Releases](https://github.com/tonythethompson/QuickShell/releases) (added in releases after v0.1.7.0; older tags are CmdPal installer only).
2. Extract into:
@@ -25,9 +35,8 @@ Restart PowerToys after deploy.
| Action | How |
| --- | --- |
-| Browse shortcuts | **Alt+Space** → `qs` (optionally `qs measure`, etc.) |
-| Home keyword in Run | **Alt+Space** → `qs` + keyword (e.g. `qs measure`) — not bare `measure` |
-| Create / export / import | **Alt+Space** → `qs` → pick a utility row, or use **PowerToys Settings → PowerToys Run → Quick Shell** |
+| Browse shortcuts | **Alt+Space** → `qs` — shortcuts appear first; type more to filter (e.g. `qs measure`) |
+| Manage actions | **Alt+Space** → `qs create`, `qs export`, etc. (utilities rank high once you type a keyword) |
| Edit shortcut | Select shortcut → **→** context menu → **Edit shortcut** |
| Run elevated | **Ctrl+Shift+Enter**, or context menu |
diff --git a/scripts/build-run-plugin.ps1 b/scripts/build-run-plugin.ps1
index 7586a32..ca51287 100644
--- a/scripts/build-run-plugin.ps1
+++ b/scripts/build-run-plugin.ps1
@@ -16,6 +16,13 @@ $zipPath = Join-Path $repoRoot "QuickShell.Run\bin\$Platform\$Configuration\Quic
Write-Host "Building Quick Shell Run plugin ($Configuration | $Platform)..."
dotnet build $project -c $Configuration -p:Platform=$Platform
+if ($LASTEXITCODE -ne 0) {
+ exit $LASTEXITCODE
+}
+
+if (-not (Test-Path (Join-Path $outputRoot 'plugin.json'))) {
+ throw "Build output is missing plugin.json. qs will not register until it is deployed."
+}
if (Test-Path $stagingRoot) {
Remove-Item $stagingRoot -Recurse -Force
@@ -34,13 +41,72 @@ if (Test-Path $zipPath) {
Compress-Archive -Path (Join-Path $stagingRoot '*') -DestinationPath $zipPath
Write-Host "Created $zipPath"
+function Test-PowerToysRunning {
+ Get-Process -Name 'PowerToys', 'PowerToys.PowerLauncher' -ErrorAction SilentlyContinue |
+ Select-Object -First 1
+}
+
+function Copy-PluginPayload {
+ param(
+ [string]$SourceRoot,
+ [string]$DestinationRoot
+ )
+
+ New-Item -ItemType Directory -Force -Path $DestinationRoot | Out-Null
+
+ $lockedFiles = @()
+ Get-ChildItem $SourceRoot -Recurse -File | ForEach-Object {
+ $relative = $_.FullName.Substring($SourceRoot.Length).TrimStart('\')
+ $target = Join-Path $DestinationRoot $relative
+ $targetDir = Split-Path $target -Parent
+ if (-not (Test-Path $targetDir)) {
+ New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
+ }
+
+ try {
+ Copy-Item $_.FullName -Destination $target -Force -ErrorAction Stop
+ }
+ catch [System.IO.IOException] {
+ $lockedFiles += $relative
+ }
+ catch [System.UnauthorizedAccessException] {
+ $lockedFiles += $relative
+ }
+ }
+
+ return $lockedFiles
+}
+
if ($Deploy) {
- if (-not (Test-Path $pluginRoot)) {
- New-Item -ItemType Directory -Force -Path $pluginRoot | Out-Null
+ if (-not (Test-Path (Join-Path $stagingRoot 'plugin.json'))) {
+ throw "Staging directory is missing plugin.json."
+ }
+
+ $running = Test-PowerToysRunning
+ if ($running) {
+ Write-Host "PowerToys is running. DLLs may be locked; plugin.json and images will still be copied." -ForegroundColor Yellow
+ Write-Host "Restart PowerToys after deploy so qs and updated DLLs load." -ForegroundColor Yellow
+ }
+
+ # Do not wipe the plugin folder first. A failed delete while PowerToys holds DLLs
+ # removes plugin.json/Images and leaves only the locked DLLs — that breaks qs.
+ $lockedFiles = Copy-PluginPayload -SourceRoot $stagingRoot -DestinationRoot $pluginRoot
+
+ $pluginJson = Join-Path $pluginRoot 'plugin.json'
+ if (-not (Test-Path $pluginJson)) {
+ throw "Deploy failed: plugin.json is missing at $pluginRoot"
}
- Get-ChildItem $pluginRoot -Force | Remove-Item -Recurse -Force
- Copy-Item -Path (Join-Path $stagingRoot '*') -Destination $pluginRoot -Recurse -Force
- Write-Host "Deployed to $pluginRoot"
- Write-Host "Restart PowerToys to load the plugin."
+ $keyword = (Get-Content $pluginJson -Raw | ConvertFrom-Json).ActionKeyword
+ Write-Host "Deployed to $pluginRoot (action keyword: $keyword)"
+
+ if ($lockedFiles.Count -gt 0) {
+ Write-Host "These files were locked and may still be the previous build:" -ForegroundColor Yellow
+ $lockedFiles | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
+ Write-Host "Quit PowerToys, rerun with -Deploy, then start PowerToys again." -ForegroundColor Yellow
+ exit 1
+ }
+ else {
+ Write-Host "Restart PowerToys to load the plugin." -ForegroundColor Green
+ }
}