chore: first version

This commit is contained in:
JNIH
2026-07-30 18:39:38 +02:00
commit c953ca8460
54 changed files with 4197 additions and 0 deletions
@@ -0,0 +1,169 @@
# 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 13 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.
@@ -0,0 +1,413 @@
# 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 57 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 |