Skip to content

Add Reactor single-file app template (reactor-singlefile) - #6844

Merged
Alexandre Zollinger Chohfi (azchohfi) merged 3 commits into
mainfrom
azchohfi-reactor-singlefile-template
Oct 15, 2026
Merged

Alexandre Zollinger Chohfi (azchohfi) merged 3 commits into
mainfrom
azchohfi-reactor-singlefile-template

Conversation

@azchohfi

@azchohfi Alexandre Zollinger Chohfi (azchohfi) commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

This replaces #6824, which is now closed. The change is the same, recreated as one fresh commit on current main; its tree is identical to #6824's last head, 40ce46e8. That commit kept a stale failing check from the legacy ProjectReunion WindowsAppSDK-Templates-PR pipeline (since disabled), and closing and reopening #6824 didn't clear it. On that same tree, WinAppSDK-Foundation-PR passed 48/48 jobs and the current WindowsAppSDK-Templates-PR passed every stage.

Summary

Adds a Reactor Single-File App template to the Microsoft.WindowsAppSDK.WinUI.CSharp.Templates dotnet new pack, alongside the four Reactor templates from #6620. It scaffolds the blank Reactor (Microsoft.UI.Reactor) app as a single-file .NET 10 file-based app: one .cs file whose #:package / #:property directives replace the .csproj. This is the shape Reactor's getting-started guide documents under One file, no project (microsoft/microsoft-ui-reactor#1283).

Template Short names What it scaffolds
Reactor Single-File App (Experimental) reactor-singlefile, winui-reactor-singlefile The blank Reactor app as a single <name>.cs, with no .csproj, assets or manifest
dotnet new reactor-singlefile -n MyApp   # creates MyApp\MyApp.cs and nothing else
cd MyApp
dotnet run MyApp.cs                      # launches with package identity

The generated MyApp.cs with default parameters (the body is the same as the reactor blank app):

#:package Microsoft.UI.Reactor@0.1.0-preview.16
#:package Microsoft.WindowsAppSDK@*
#:package Microsoft.Windows.SDK.BuildTools.WinApp@*
#:property OutputType=WinExe
#:property TargetFramework=net10.0-windows10.0.26100.0
#:property UseWinUI=true

// A .NET file-based app: the #: directives above take the place of a .csproj.
// `dotnet run MyApp.cs` launches it with package identity (needs Developer Mode).
// To run it unpackaged instead, add `#:property WindowsPackageType=None` above.

using Microsoft.UI.Reactor;
// ...same usings and App component as ReactorBlankApp/App.cs...
ReactorApp.Run<App>("MyApp");

What changed

  • dev/Templates/Source/ProjectTemplates/Desktop/CSharp/ReactorSingleFileApp/ProjectTemplate.cs is the whole template: the directive header above plus the ReactorBlankApp/App.cs body. sourceName renames it to <name>.cs, so the assembly name and the synthesized package identity follow the project name.
  • dev/Templates/Dotnet/templates/reactor-singlefile-app/.template.config (template.json, dotnetcli.host.json, ide.host.json, icon.png):
    • Identity is Microsoft.WindowsAppSDK.Reactor.CSharp.SingleFileApp; classifications are Windows/WinUI/Desktop/Reactor/SingleFile/Experimental.
    • Parameters that carry over from the Reactor siblings keep the same names and defaults: --dotnet-version (net10.0), --reactor-version (0.1.0-preview.16), --wasdk-version (*, 2.2.0 or later), --windowsSdkBuildToolsWinAppVersion (*, 0.7.0 or later) and --no-restore.
    • The standard restore post action runs dotnet restore <name>.cs, which works on a .cs primary output.
  • dev/Templates/templates.props:
    • A new reactor.singlefile row in the DotnetNewOnlyProjectTemplate slot.
    • A new, documented IncludeGitignore metadata value (default true). The new template sets it to false so its output stays a single file.
    • Also fixes the stale "no instances today" note on that slot.
  • dev/Templates/Dotnet/WinAppSdk.CSharp.DotnetNewTemplates.csproj: the pack task skips the shared .gitignore when IncludeGitignore=false. Every other template's packed file list is unchanged.
  • dev/Templates/Dotnet/README.md: adds a table row, a "Quickstart a single-file Reactor app" section, and a note on setting the WinAppRun* properties with #:property in a single-file app.
  • dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1: a minimal FileBasedApp kind, named after the .NET mechanism. It asserts that the scaffold is exactly <name>.cs, then runs dotnet build <name>.cs in the default (packaged) configuration.

Design notes

  • Naming. The template is named single-file to match the only first-party dotnet new template for a .NET file-based app, Aspire's aspire-apphost-singlefile ("Aspire Single-File App Host", classification SingleFile). The descriptions and README keep the official .NET term file-based app, which also separates this from PublishSingleFile.
  • Why it references Microsoft.WindowsAppSDK. With the published Microsoft.UI.Reactor 0.1.0-preview.16 alone, the Windows App SDK defaults to self-contained and the build fails with WindowsAppSDKSelfContained requires a supported Windows architecture. The Runtime dependency restored by Restore the Windows App Runtime dependency and document single-file apps microsoft-ui-reactor#1283 hasn't shipped yet. Referencing the metapackage, as the sibling templates do, makes the app framework-dependent and keeps --wasdk-version meaningful.
  • Why WinApp 0.7.0 or later. Packaged dotnet run <file>.cs (Run a .NET file-based app with package identity winappCli#794, Support packaged dotnet run for .NET file-based apps winappCli#874) is new in Microsoft.Windows.SDK.BuildTools.WinApp 0.7.0. With 0.6.1, _WinAppRunSupportActive stays false, so dotnet run starts the unpackaged exe and the app fails at startup.
  • What was left out of the siblings' setup.
    • The manifest is synthesized from the directives. Its display name is the file stem, which is the project name, just like the siblings' manifest.
    • So publisherDn, publisherDisplay, guid9 and the Windows SDK BuildTools pin were dropped. Microsoft.Windows.SDK.BuildTools comes in transitively.
    • Devtools are left out, because a #:package can't be Debug-only and file-based apps default to PublishAot=true.
    • Unlike the siblings' .csproj, there's no app.manifest, since Reactor sets PerMonitorV2 DPI awareness itself.
  • Hidden from Visual Studio's New Project dialog. ide.host.json sets "unsupportedHosts": [{ "id": "vs" }], because a file-based app has no project for VS to open. The Aspire single-file template linked above does the same. The .NET CLI ignores ide.host.json, so dotnet new and winapp new are unaffected.
  • Why the winui-reactor-singlefile alias. winapp new finds templates with dotnet new list winui, which matches names and short names only. Like the other Reactor templates, this one carries a winui-reactor* alias so winapp new --list and winapp new -t reactor-singlefile pick it up.

Validation

Local runs used .NET SDK 10.0.401, winapp 0.7.0 and Developer Mode, against the template pack packed from this change.

  • Pack and install
    • dotnet pack: content/reactor.singlefile/ holds ProjectTemplate.cs plus the four .template.config files and no gitignore.txt. Compared with the published 0.0.7-alpha, the only content difference is those 5 new entries; the 10 sibling project templates still get their gitignore.txt.
    • dotnet new list winui and dotnet new list reactor both list Reactor Single-File App (Experimental) reactor-singlefile,winui-reactor-singlefile.
  • Scaffold and build
    • dotnet new reactor-singlefile -n MyApp creates exactly MyApp\MyApp.cs and prints Restoring ...\MyApp\MyApp.cs: ... Restore succeeded..
    • dotnet build MyApp.cs in Debug and Release: 0 warnings, 0 errors.
    • Builds and runs write nothing next to the .cs file; the output goes to %TEMP%\dotnet\runfile\.
  • Run
    • dotnet run MyApp.cs prints ✅ Manifest generated for MyApp-53b1794c 1.0.0.0 and then launched. The window titled "MyApp" opens, and GetPackageFullName on the process returns MyApp-53b1794c_1.0.0.0_x64__gspb8g6x97k2t (AUMID …!App). The package is a dev-mode registration with a dependency on Microsoft.WindowsAppRuntime.2 2.5.1.0.
    • Unpackaged: with #:property WindowsPackageType=None added, dotnet run opens the window, the process has no package identity (APPMODEL_ERROR_NO_PACKAGE), and nothing is registered.
    • winapp run MyApp.cs --detach launches the same identity.
    • #:property WinAppRunNoLaunch=true registers the app without launching it (checks the README note).
  • dotnet publish MyApp.cs (Native AOT, the default for file-based apps)
    • Publishes to .\artifacts\MyApp\MyApp.exe with 0 warnings.
    • On my machine it only succeeds once %ProgramFiles(x86)%\Microsoft Visual Studio\Installer is on PATH. Without that, VS 18's vcvarsall fails with 'vswhere.exe' is not recognized. This is a toolchain environment issue, not a template one.
  • winapp and pinned versions
    • winapp new --list --template-version installed lists Reactor Single-File App (Experimental) │ reactor-singlefile.
    • winapp new -t reactor-singlefile -n WinappApp scaffolds WinappApp\WinappApp.cs.
    • Pinning --wasdk-version 2.2.0 (the documented floor) builds with 0 warnings.
  • Test script
    • Test-DotnetNewTemplates.ps1 -PackagePath <local nupkg> -SkipAppLaunch: All templates validated successfully. 41 summary rows are Succeeded: the 6 WinUI and 4 existing Reactor project templates, the new reactor-singlefile (create and build, where the build step first asserts the scaffold is a single file), the 7 item templates and the version scenarios. The one Skipped row is the default-wildcard build, which the script skips whenever * resolves to a concrete version.
    • Feed caveat: from my machine the repo's internal feed timed out (100 s) downloading the .NET 10.0.12 runtime packs, so a direct run failed on the first winui build before any Reactor template ran. That is a network problem on my end, not this change. The passing run used a byte-identical copy of the script whose generated NuGet.config points at a nuget.org proxy. The WindowsAppSDK-Templates-PR pipeline runs the script against the internal feed.

Follow-up (outside this repo)

  • winapp new ends with Next: cd "<name>" then winapp run. For a single-file template that fails with Manifest file not found, because winapp run only enters single-file mode for an explicit .cs path. The correct command is winapp run <name>.cs (or dotnet run <name>.cs). That hint needs a fix in winappCli; this template's comments and the README already give the right command.
  • When a single-file app runs packaged through winapp, two icons differ from the .csproj templates (Single-file (file-based) apps: taskbar icon drawn on accent plate and title bar icon missing winappCli#1077). The taskbar icon is drawn on an accent-colored plate, because the generated resources.pri doesn't index the unplated AppList* assets. The title bar has no icon, because the exe has no embedded .ico. Both need a fix in winapp; the template can't fix them without adding files next to the .cs.

A Microsoft employee must use /azp run to validate using the PR pipelines.

Adds a "Reactor Single-File App (Experimental)" template to the
Microsoft.WindowsAppSDK.WinUI.CSharp.Templates dotnet new pack. It scaffolds
the blank Microsoft.UI.Reactor app as a single-file .NET 10 file-based app:
one <name>.cs whose #:package / #:property directives replace the .csproj.
It runs packaged by default: for `dotnet run <name>.cs`,
Microsoft.Windows.SDK.BuildTools.WinApp 0.7.0+ synthesizes an appxmanifest
from the directives and launches the app with package identity. The name
follows Aspire's aspire-apphost-singlefile, the only first-party dotnet new
template for a .NET file-based app.

- templates.props: new DotnetNewOnlyProjectTemplate row (reactor.singlefile)
  and an IncludeGitignore metadata opt-out (default true) so the scaffolded
  output stays that single file.
- WinAppSdk.CSharp.DotnetNewTemplates.csproj: honor IncludeGitignore=false.
- reactor-singlefile-app/.template.config: short names reactor-singlefile
  and winui-reactor-singlefile (winapp new enumerates with `dotnet new list
  winui`), and hidden from the Visual Studio New Project dialog through
  ide.host.json unsupportedHosts, since VS cannot open a file-based app.
- The file references Microsoft.WindowsAppSDK: with Microsoft.UI.Reactor
  0.1.0-preview.16 alone the Windows App SDK defaults to self-contained and
  the build fails.
- README and Test-DotnetNewTemplates.ps1 (new FileBasedApp kind) updated.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@niels9001

Copy link
Copy Markdown
Contributor

Tried this out locally next to the four Reactor .csproj templates (packed from this branch, WinApp 0.7.1, WASDK 2.5.1). reactor-singlefile scaffolds, builds and launches packaged as described. 🎉

Two visual differences compared to the .csproj templates, both on the winapp side rather than in this template:

  1. Taskbar icon is drawn on an accent-colored plate. winapp writes Assets\AppList.targetsize-24_altform-unplated.png, but the generated resources.pri doesn't index any of the AppList* assets (no TARGETSIZE/ALTFORM qualifiers). So the shell falls back to the plated logo on the accent color.
  2. No title bar icon. The .csproj templates embed Assets\AppIcon.ico through <ApplicationIcon>. The file-based app has no .ico, so the exe has no icon resource.

Tracking this in microsoft/winappCli#1077. We'll fix it there, so the template can stay a single file. I don't think this blocks the PR.

@azchohfi

Copy link
Copy Markdown
Contributor Author

Tracking this in microsoft/winappCli#1077. We'll fix it there, so the template can stay a single file.

Thanks for trying it next to the .csproj templates, and for filing the issue. Agreed, nothing to change in the template. I've added it to the follow-ups in the PR description.

@azchohfi

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@azchohfi
Alexandre Zollinger Chohfi (azchohfi) merged commit f5d43fa into main Oct 15, 2026
56 checks passed
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) deleted the azchohfi-reactor-singlefile-template branch October 15, 2026 20:27
Nikola Metulev (nmetulev) added a commit to microsoft/winappCli that referenced this pull request Oct 16, 2026
…1109)

## What and why

After `winapp new -t reactor-singlefile -n MyApp`, winapp told you to
run `winapp run` from the new folder. That fails with `Manifest file not
found`, because `winapp run` treats an app as a single-file (.NET
file-based) app only when you pass the `.cs` file. Now the next step
names the file, so you can copy and run it.

winapp knows the template made a single-file app if either of these is
true:
- the template has the `SingleFile` tag, or
- the new folder has `MyApp.cs` and no `.csproj`.

The file check is a fallback: `dotnet new list` sometimes cuts off long
Tags columns, so the `SingleFile` tag can be missing.

Fixes #1107

## Before / after

`reactor-singlefile` isn't in a published template pack yet, so I
couldn't run it end to end or take screenshots. The "after" text below
is the real output from the new unit test, which fakes the `dotnet new`
call.

**Before** (from the issue):
```
👉 Next: cd "MyApp" then winapp run.
```

**After**:
```
✅ Created WinUI app MyApp at ...\MyApp.
⚠  reactor-singlefile is an experimental template. It depends on prerelease packages whose APIs can change or be removed in a future release.
👉 Next: cd "MyApp" then winapp run "MyApp.cs".
```

Other templates still print the same next steps as before.

## Not tested / risks

- I haven't run this against a real `reactor-singlefile` pack, because
none is published yet. The tests use the tags from the template's
`template.json` in microsoft/WindowsAppSDK#6844.

## Checklist

- [x] Tests added or updated
- [x] Tested locally on Windows

<details><summary>Notes for reviewers</summary>

The file name is quoted (`winapp run "MyApp.cs"`) so names with spaces
work. `winapp new` accepts a name like `My App`, and without quotes
`winapp run My App.cs` would take `App.cs` as an app argument.

</details>

---------

Co-authored-by: Nikola Metulev <nmetulev@users.noreply.github.com>
Copilot-Session: a2691a0e-67c7-434a-9a69-33559c878e0c
Alexandre Zollinger Chohfi (azchohfi) added a commit that referenced this pull request Oct 21, 2026
Resolve the README conflict with the new reactor-singlefile template (#6844),
keeping its quickstart pointer and dropping the --wasdk-version mention.

Apply the same change to reactor-singlefile that this PR makes to the other
Reactor templates: remove the `#:package Microsoft.WindowsAppSDK` directive
and the windowsAppSdkVersion / --wasdk-version parameter, and default
reactorVersion to 0.1.0-preview.17, which carries Microsoft.WindowsAppSDK.Runtime.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants