The repo carried two parallel UIs (WPF + WinUI) sharing Models/Services via
cross-directory Link includes. Now that WinUI is the only frontend, collapse
the structure so the WinUI project IS the repo root instead of a nested
sibling folder:
- Delete the WPF project entirely (App.xaml, MainWindow.xaml, csproj) and its
bin/obj output
- Move AmiReel.WinUI/* up to the repo root (App, MainWindow, MainPage,
DialogHelper, Assets, Package.appxmanifest, app.manifest, Properties,
.github/instructions, AGENTS.md) via git mv, preserving history
- Rename AmiReel.WinUI.csproj -> AmiReel.csproj; regenerate the solution as
AmiReel.slnx (the newer XML solution format) with a single project
- Rename namespace AmigaDB.VideoRenderer.{Models,Services} -> AmiReel.{...}
and AmiReel_WinUI -> AmiReel across all files, including the embedded
ffmpeg/ffprobe resource logical names in the csproj and ToolExtractor
- Models/ and Services/ no longer need the Link-based cross-directory
<Compile Include>; they're picked up by the SDK's default globbing now
that they live under the project directory
- Rename assets/ -> branding/ (source icon art) to avoid a case-insensitive
collision with Assets/ (packaged tile art) once both sit at repo root
- Merge the two .gitignore files into one; track the PublishProfiles pubxml
files instead of ignoring them (no secrets, and they keep publish
reproducible across machines) as branding, gitignore, etc.
- Simplify publish-win-x64.ps1 (drop the -Target Wpf/WinUI switch, there's
only one target now) and rewrite README.md to describe the single-project
layout, build/run/publish commands, and file structure
Verified: dotnet build succeeds for both AmiReel.csproj and AmiReel.slnx, and
the built exe launches and renders identically to before the move.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
18 KiB
Copilot Agent Instructions -- WinUI 3 / WinAppSDK
Project Overview
This is a WinUI 3 desktop application built on the Windows App SDK. It uses MSIX packaging and supports x86, x64, and ARM64 architectures.
Source of truth for versions & names: Always read the project
.csprojto determine the currentTargetFramework,RuntimeIdentifiers,Platforms,RootNamespace, andMicrosoft.WindowsAppSDKpackage version. Never hard-code project names or version numbers in instruction files.Throughout this document and the instruction files,
<ProjectName>is a placeholder -- replace it with the actual project folder/assembly name (derived from the.csprojfilename).
| Property | How to determine |
|---|---|
| UI Framework | WinUI 3 (Microsoft.UI.Xaml) -- always used |
| App SDK | Read Microsoft.WindowsAppSDK version from .csproj <PackageReference> |
| Runtime / TFM | Read <TargetFramework> from .csproj (e.g., net10.0-windows10.0.26100.0) |
| Target OS | Derived from <TargetFramework> and <TargetPlatformMinVersion> in .csproj |
| Platforms | Read <Platforms> from .csproj (e.g., x86;x64;ARM64) |
| Packaging | MSIX (<EnableMsixTooling>true</EnableMsixTooling>) |
| Namespace | Read <RootNamespace> from .csproj |
| Nullable | Read <Nullable> from .csproj |
Default TFM: Templates ship with
net10.0by default. Pass--dotnet-version <tfm>(for examplenet10.0) when runningdotnet new ...or edit<TargetFramework>inside the generated.csprojbefore the first build if you need a newer framework. Keep<RuntimeIdentifiers>synchronized with the framework you pick.
Instruction Files Index
All detailed agent instructions are organized under .github/instructions/:
| File | Scope |
|---|---|
| design-principles.instructions.md | DRY, KISS, SOLID, YAGNI |
| globalization.instructions.md | Globalization & Localization |
| accessibility.instructions.md | Accessibility |
| security.instructions.md | Security |
| performance.instructions.md | Performance |
| code-quality.instructions.md | Static Analysis, StyleCop, Code Cleanup |
| winui-best-practices.instructions.md | WinUI 3 / WinAppSDK patterns & references |
| windows-apis.instructions.md | WinAppSDK & Platform SDK API namespace catalog & lookup guidance |
| testing.instructions.md | Unit Testing, Build & Run |
Core Agent Workflow
Every time you work on this codebase, follow this checklist:
Before Writing Code
- Review the original goal -- Re-read the user's request and confirm you understand the intent.
- Check existing code -- Search for related implementations to avoid duplication (DRY).
- Find the right API -- If the task involves a platform capability (AI, UI controls, file access, notifications, windowing, widgets, sensors, etc.), first check the Windows APIs catalog and then look up the correct API in the WinUI 3 API Reference before writing code.
- Plan the approach -- Consider SOLID principles and identify which classes/interfaces are involved.
While Writing Code
Agent Rule -- MANDATORY: Steps 5-8 are not passive references. You must actually open and read the linked instruction file before writing code that falls within its scope. Do not skip this -- these files contain rules, anti-patterns, and checklists that must be applied.
- Apply Design Principles -- Read design-principles before adding/refactoring classes or logic. Apply DRY, KISS, SOLID, YAGNI.
- Follow Fundamentals -- Read the applicable instruction files based on what you're changing:
- Adding or changing UI controls / XAML? -> Read accessibility (AutomationProperties, keyboard nav, contrast) AND performance (x:Bind, x:Load, virtualization).
- Adding or changing user-facing strings (labels, messages, tooltips)? -> Read globalization (
.reswfiles,x:Uid,ResourceLoader). - Handling secrets, user input, HTTP, or permissions? -> Read security (no hard-coded secrets, input validation, least privilege).
- Working on data binding, collections, async/IO, or layout? -> Read performance (x:Bind, virtualization, async patterns).
- Respect Code Quality Rules -- Read code-quality before writing code. Follow all CA*/SA*/IDE* analyzer rules and naming conventions.
- Follow WinUI Patterns -- Read winui-best-practices for MVVM, x:Bind, community toolkit, and API verification.
After Writing Code
- Remove unused code -- Delete unused
usingstatements, dead code, commented-out blocks. - Write unit tests -- Every new public method/class needs tests. Read testing for framework setup, naming conventions (
MethodName_Scenario_ExpectedResult), AAA pattern, anddotnet testcommands. - Build the project -- Detect the platform first (
$Platform = $env:PROCESSOR_ARCHITECTURE), then rundotnet build -c Debug -p:Platform=$Platformfrom the project folder and fix all warnings/errors. If build errors occur, follow the Troubleshooting Build Errors workflow below. - Run tests -- Run tests related to the change using
--filter(see testing). Run the full suite only when the change is cross-cutting. - Run the app with package identity -- Use
dotnet run(preferred -- the project referencesMicrosoft.Windows.SDK.BuildTools.WinApp, which automatically invokeswinapp runto register a loose-layout package and launch via AUMID). See Build, Run & Deploy below for advanced scenarios. - Re-review against original goal -- Confirm the implementation matches the user's request.
Troubleshooting Build Errors
Agent Rule -- MANDATORY: When a build fails due to an unknown type, missing namespace, unresolved API, or similar definition error, follow this escalation order. Do NOT jump straight to reading
.winmdfiles or usingildasm/decompilers -- always try web search first.
Step 1 -- Web Search (ALWAYS try first):
- Open and read windows-apis.instructions.md -- it contains the API namespace catalog and lookup guidance.
- Translate the unknown type/namespace into search keywords (e.g.,
ImageDescription-> "WinAppSDK ImageDescription API"). - Use
web_searchorweb_fetchto search the WinAppSDK API Reference and the Platform SDK API Reference for the correct namespace, class name, and method signatures. - Check the release notes to verify the API is available in the project's SDK version (read from
.csproj).
Step 2 -- Sample Repos: If web search finds the API but usage is unclear, search the sample repositories listed in windows-apis.instructions.md for working examples.
Step 3 -- WinMD / Decompiler (last resort only):
Only if Steps 1-2 fail to resolve the issue, then inspect .winmd metadata files or use decompilation tools to discover the exact type definitions. This is a fallback, not the default approach.
Build, Run & Deploy
This is an MSIX-packaged WinUI 3 app. You must pass both -c (Configuration) and -p:Platform= to every dotnet build/dotnet test command.
This template references the Microsoft.Windows.SDK.BuildTools.WinApp NuGet package, which hooks dotnet run to invoke the winapp CLI. Use dotnet run for everyday inner-loop development -- you do not need to call Add-AppxPackage or MakeAppx.exe by hand.
Dotnet CLI Workflow
- Prefer
dotnet newfor scaffolding projects and items so namespaces, GUIDs, and resource wiring stay correct. - Common commands:
dotnet new winui -n MyAppdotnet new winui-page -n SettingsPage --project .\MyApp\MyApp.csprojdotnet new winui-usercontrol -n ProfileCard --project .\MyApp\MyApp.csproj
- Discover available scaffolds with
dotnet new winui --list(shows supported parameters such as--dotnet-version). - Need a newer TFM? Supply
--dotnet-version net10.0during scaffold or edit<TargetFramework>afterward before the first build.
Prerequisites
- Developer Mode must be enabled on Windows. Verify with:
# Check developer mode Get-WindowsDeveloperLicense # If not enabled: Settings -> System -> For developers -> Developer Mode -> On winappCLI -- installed transitively via theMicrosoft.Windows.SDK.BuildTools.WinAppNuGet reference (no separate install needed fordotnet run). To usewinappdirectly from the terminal for advanced scenarios (manifest editing, certificate management, packaging), install it standalone:winget install Microsoft.WinAppCli --source winget
Detect Platform
Always detect the machine's architecture first -- never hardcode a platform value. Run this once at the start of every build/test session:
# Detect the current machine's CPU architecture
# (returns AMD64 on x64 boxes, ARM64 on ARM64 boxes, x86 on 32-bit boxes)
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch } # MSBuild expects x64/x86/ARM64
Use $Platform in all subsequent dotnet commands.
Build
# Run from the project folder containing the .csproj
cd <ProjectName>
# Detect platform (see above)
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch }
# Debug build (matches current machine)
dotnet build -c Debug -p:Platform=$Platform
# Release build
dotnet build -c Release -p:Platform=$Platform
Run with Package Identity (preferred)
The template references Microsoft.Windows.SDK.BuildTools.WinApp, which makes dotnet run register a loose-layout package via winapp run and launch the app via AUMID activation -- giving it the same package identity it would have in production:
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch }
dotnet run -c Debug -p:Platform=$Platform
The CLI prints the registered package's AUMID and the launched process's PID -- attach a debugger to that PID for runtime debugging.
Useful MSBuild knobs (set in .csproj <PropertyGroup>)
| Property | When to set |
|---|---|
EnableWinAppRunSupport=false |
Disable the dotnet run integration entirely (e.g., to launch unpackaged) |
WinAppRunUseExecutionAlias=true |
For console apps -- launches via uap5:ExecutionAlias so stdin/stdout stay in the terminal. Add the alias first with winapp manifest add-alias. |
WinAppRunNoLaunch=true |
Register the package but don't launch (attach your IDE's debugger before launch) |
WinAppLaunchArgs="--flag value" |
Pass arguments to the app on launch |
Manual winapp run (when not using dotnet run)
# Read <TargetFramework> from .csproj first; example uses net10.0-windows10.0.26100.0
winapp run .\bin\$Platform\Debug\<TargetFramework>
# Pass args after -- to avoid escaping
winapp run .\bin\$Platform\Debug\<TargetFramework> -- --my-flag value
# Console app: keep stdin/stdout in the current terminal (requires uap5:ExecutionAlias)
winapp run .\bin\$Platform\Debug\<TargetFramework> --with-alias
# Wipe LocalState/settings between runs to test first-run behavior
winapp run .\bin\$Platform\Debug\<TargetFramework> --clean
Run Tests
# Run from the test project folder
cd <ProjectName>.Tests
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch }
dotnet test -c Debug -p:Platform=$Platform
winapp CLI command reference
The winapp CLI is the canonical entry point for app-identity, packaging, certificate, and asset operations. Reach for it instead of hand-rolling MakeAppx/SignTool/Add-AppxPackage invocations.
| Scenario | Command | Notes |
|---|---|---|
| Run/debug with identity (loose layout) | dotnet run (or winapp run <build-output>) |
Default for inner loop. Registers full loose-layout package. |
| Console app inner loop | Set WinAppRunUseExecutionAlias=true in .csproj, then dotnet run |
Requires uap5:ExecutionAlias -- add via winapp manifest add-alias. |
| Sparse identity on a single exe | winapp create-debug-identity .\bin\Debug\<TFM>\<ProjectName>.exe |
Use when the exe is outside the build folder, or for IDE F5 startup debugging. |
| Stop debugging / clean up | winapp unregister |
Removes dev packages registered for the current project. |
| Generate dev signing cert | winapp cert generate --manifest .\Package.appxmanifest --install |
Reads publisher from manifest. Stored as devcert.pfx in the project. |
| Inspect a cert | winapp cert info .\devcert.pfx |
Verify subject matches manifest publisher. |
| Sign a file | winapp sign .\MyApp.msix --cert .\devcert.pfx |
Wraps signtool. |
| Build distribution MSIX | winapp pack .\bin\$Platform\Release\<TFM>\win-<rid> --cert .\devcert.pfx |
Auto-resolves $targetnametoken$, registers third-party WinRT components. |
| Self-contained MSIX (bundles WinAppSDK) | winapp pack ... --self-contained |
No runtime dependency on the framework package. |
| Regenerate Square44/Square150/etc. icons | winapp manifest update-assets .\branding\logo.svg |
SVG preferred -- rendered at all 5 scale and 14 targetsize variants. |
| Add execution alias | winapp manifest add-alias |
Required for console-app inline I/O via WinAppRunUseExecutionAlias. |
| Underlying SDK tools | winapp tool signtool ..., winapp tool makeappx ... |
Falls back to the raw Microsoft.Windows.SDK.BuildTools tools when needed. |
For full reference, see the winapp CLI usage docs and the Debugging Guide.
Fallback: register a loose layout manually
Only use this if you've explicitly disabled the winapp integration (<EnableWinAppRunSupport>false</EnableWinAppRunSupport>) or are debugging the deployment itself. The supported path is dotnet run / winapp run.
$arch = $env:PROCESSOR_ARCHITECTURE
$Platform = if ($arch -eq 'AMD64') { 'x64' } else { $arch }
$Rid = $Platform.ToLower() # arm64, x64, x86
# Register the built MSIX package from the build output
# Read <TargetFramework> from .csproj to build the correct path.
Add-AppxPackage -Register ".\<ProjectName>\bin\$Platform\Debug\<TargetFramework>\win-$Rid\AppxManifest.xml"
Note: Replace
<TargetFramework>with the actual value from.csproj(e.g.,net10.0-windows10.0.26100.0).
If the launch fails because an old instance is still running, terminate it with taskkill /IM <ProjectName>.exe /F before re-running.
Key Rules (Always Enforced)
- Every change must build and pass tests -- Run
dotnet buildanddotnet test(see Build, Run & Deploy) before considering any task complete. - Follow all instruction files -- The detailed rules in
.github/instructions/are authoritative. You must actually open and read them (not just acknowledge they exist) when working within their scope. See the trigger conditions in steps 5-8 above. - Web search before decompilation -- When facing unknown types or build errors, always search the web / API docs first. Only use WinMD/ILDASM as a last resort (see Troubleshooting Build Errors).
- Use
winappfor app-identity / packaging / signing -- Don't hand-rollMakeAppx/SignTool/Add-AppxPackageinvocations. The CLI keeps the manifest, certificate, and registration steps in sync.
Windows AI Prerequisites
When integrating Windows AI APIs (Phi Silica, Windows Vision -- ImageDescription, TextRecognizer, ImageScaler, etc.) -- see windows-apis.instructions.md:
- Package identity is required. All Windows AI APIs require the app to
run with package identity. The
dotnet runflow described above already provides this. If you're testing outsidedotnet run, register identity first withwinapp runorwinapp create-debug-identity. - Manifest capabilities. Add the capabilities each API requires to
Package.appxmanifest(commonlyrunFullTrust; some scenarios additionally needinternetClient). Check the API's docs page for the exact list. - Hardware / OS gating. Some APIs require a Copilot+ PC (NPU) or a
minimum Windows build. Always probe availability with the API's
IsAvailable/EnsureReadyAsyncpattern (or equivalent) and provide a graceful fallback for unsupported devices. - Verify locally before checking in. After capability or manifest
changes, re-run
dotnet run(orwinapp run) so the registered identity reflects the updated manifest -- a stale registration will silently use the old capability set.