|
| 1 | +# Copilot Instructions — Microsoft.UI.Reactor |
| 2 | + |
| 3 | +Reactor is a declarative, component-based C# framework for building WinUI 3 desktop apps. It renders real WinUI controls via a virtual element tree and reconciler — similar to React's programming model but targeting native Windows UI. |
| 4 | + |
| 5 | +## Build, Test, Lint |
| 6 | + |
| 7 | +```bash |
| 8 | +# Build (platform defaults to machine arch; force with -p:Platform=x64 or ARM64) |
| 9 | +dotnet build Reactor.sln |
| 10 | + |
| 11 | +# Unit tests — xUnit, headless, fast (~2200 tests incl. 590 Yoga fixtures) |
| 12 | +dotnet test tests/Reactor.Tests |
| 13 | + |
| 14 | +# Single test class |
| 15 | +dotnet test tests/Reactor.Tests --filter "FullyQualifiedName~ReconcilerMountUpdateTests" |
| 16 | + |
| 17 | +# Selftests — real WinUI window, in-process (~10s) |
| 18 | +dotnet test tests/Reactor.SelfTests |
| 19 | + |
| 20 | +# Raw TAP output (faster iteration, supports --filter prefix) |
| 21 | +dotnet run --project tests/Reactor.AppTests.Host -- --self-test --filter "Flex" |
| 22 | + |
| 23 | +# E2E — Appium/WinAppDriver (requires WinAppDriver installed) |
| 24 | +dotnet test tests/Reactor.AppTests |
| 25 | + |
| 26 | +# Single E2E class |
| 27 | +dotnet test tests/Reactor.AppTests --filter "ClassName=Reactor.AppTests.Tests.AccessibilityTests" |
| 28 | +``` |
| 29 | + |
| 30 | +CI runs unit tests + selftests + full solution build on every PR. .NET 9 SDK, `windows-latest` runner. |
| 31 | + |
| 32 | +## Architecture |
| 33 | + |
| 34 | +### Virtual DOM model |
| 35 | + |
| 36 | +UI is described as **immutable C# records** (`Element` subclasses), not WinUI controls. The reconciler diffs old vs. new element trees and patches only what changed on real controls. |
| 37 | + |
| 38 | +``` |
| 39 | +Component.Render() → Element tree (records) |
| 40 | + ↓ |
| 41 | + Reconciler |
| 42 | + ├── Mount → creates WinUI controls |
| 43 | + └── Update → diffs & patches controls |
| 44 | +``` |
| 45 | + |
| 46 | +### Reconciler is split across partial classes |
| 47 | + |
| 48 | +- `Reconciler.cs` — orchestration, child reconciliation, unmount, helpers |
| 49 | +- `Reconciler.Mount.cs` — `MountXxx()` handler per control type |
| 50 | +- `Reconciler.Update.cs` — `UpdateXxx()` handler per control type |
| 51 | + |
| 52 | +### Hooks follow React rules |
| 53 | + |
| 54 | +Hooks (`UseState`, `UseEffect`, `UseReducer`, `UseMemo`, etc.) are tracked by call order in `RenderContext`. They must be called unconditionally, in the same order every render — no conditional hooks. Pass `threadSafe: true` for cross-thread state updates. |
| 55 | + |
| 56 | +### Echo suppression for value controls |
| 57 | + |
| 58 | +Value-bearing controls (TextBox, Slider, ColorPicker) use `ChangeEchoSuppressor` + `SetElementTag`-first to prevent programmatic writes from re-firing user callbacks. Any new value control must follow this pattern. |
| 59 | + |
| 60 | +### Element pooling |
| 61 | + |
| 62 | +`ElementPool` recycles WinUI controls. Poolable types track one-time event wiring via `ConditionalWeakTable<FrameworkElement, PoolableWireFlags>` to avoid double-subscribing across rent/return cycles. |
| 63 | + |
| 64 | +### Per-element state via attached DP |
| 65 | + |
| 66 | +`ReactorAttached.StateProperty` stores `ReactorState` (Element pointer + EventHandlerState) on native elements — not `FrameworkElement.Tag` or a CWT. |
| 67 | + |
| 68 | +## Key Conventions |
| 69 | + |
| 70 | +### Elements are immutable records |
| 71 | + |
| 72 | +```csharp |
| 73 | +public record MyControlElement(string Label, Action? OnClick = null) : Element; |
| 74 | +``` |
| 75 | + |
| 76 | +Use `with` expressions for variations. Never mutate. |
| 77 | + |
| 78 | +### Factory methods over constructors |
| 79 | + |
| 80 | +The DSL entry point is `using static Microsoft.UI.Reactor.Factories;`. Factory methods return Element records, never WinUI controls: |
| 81 | + |
| 82 | +```csharp |
| 83 | +TextBlock("hello") // not new TextBlockElement("hello") |
| 84 | +Button("+", () => ...) // not new ButtonElement(...) |
| 85 | +VStack(child1, child2) // layout containers |
| 86 | +``` |
| 87 | + |
| 88 | +`Factories` is `public static partial class` — factory methods can be added from multiple files. |
| 89 | + |
| 90 | +### Fluent modifiers preserve concrete types |
| 91 | + |
| 92 | +Extension methods use `<T> where T : Element` to maintain the concrete type through chains: |
| 93 | + |
| 94 | +```csharp |
| 95 | +Text("Hello").Bold().Margin(16).Set(tb => tb.TextWrapping = TextWrapping.Wrap) |
| 96 | +// Still TextBlockElement throughout the chain |
| 97 | +``` |
| 98 | + |
| 99 | +### Adding a new WinUI control requires four touch points |
| 100 | + |
| 101 | +1. **Element record** in `src/Reactor/Core/Element.cs` |
| 102 | +2. **Factory method** in `src/Reactor/Elements/Dsl.cs` |
| 103 | +3. **Mount handler** in `src/Reactor/Core/Reconciler.Mount.cs` (+ register in dispatch switch) |
| 104 | +4. **Update handler** in `src/Reactor/Core/Reconciler.Update.cs` (+ register in dispatch switch) |
| 105 | + |
| 106 | +Optionally: fluent modifiers in `ElementExtensions.cs`, tests in `Reactor.Tests/` and/or selftest fixture in `Reactor.AppTests.Host/SelfTest/Fixtures/`. |
| 107 | + |
| 108 | +### Test tier selection |
| 109 | + |
| 110 | +| Testing… | Write a… | Location | |
| 111 | +|---|---|---| |
| 112 | +| Algorithm, pure function, hook bookkeeping, D3 math | Unit test (xUnit) | `tests/Reactor.Tests/` | |
| 113 | +| Element mount/update against real WinUI controls | Selftest fixture | `tests/Reactor.AppTests.Host/SelfTest/Fixtures/` | |
| 114 | +| Real user input, UIA properties, cross-process | E2E test (Appium) | `tests/Reactor.AppTests/Tests/` | |
| 115 | + |
| 116 | +Start with unit tests. Use selftests only when you need a live WinUI control. E2E is the slowest tier. |
| 117 | + |
| 118 | +### Console-mutating tests need collection isolation |
| 119 | + |
| 120 | +Tests that write to `Console.Out`/`Console.Error` must be grouped with `[Collection("ConsoleTests")]` to prevent cross-test interference. |
| 121 | + |
| 122 | +### AOT compatibility |
| 123 | + |
| 124 | +`IsAotCompatible=true` is set for all net8.0+ projects. The core Reactor library promotes IL trimming/AOT warnings to errors — new reflection usage must be annotated before merging. Non-Reactor projects (tests, samples) suppress these warnings. |
| 125 | + |
| 126 | +### WinUI library projects |
| 127 | + |
| 128 | +Class libraries must set `WindowsAppSDKSelfContained=false`. Only app executables own Windows App SDK self-contained packaging. |
| 129 | + |
| 130 | +### No XAML |
| 131 | + |
| 132 | +Everything is C#. No `.xaml` files for UI (except `ReactorApplication.xaml` which loads `XamlControlsResources` for AOT compatibility). |
| 133 | + |
| 134 | +### User guide docs are generated |
| 135 | + |
| 136 | +Docs under `docs/guide/` are compiled from `docs/_pipeline/templates/*.md.dt` via `mur docs compile`. Edit the templates, not the compiled output. |
| 137 | + |
| 138 | +## Project Layout |
| 139 | + |
| 140 | +``` |
| 141 | +src/Reactor/ Core framework |
| 142 | + Core/ Reconciler, Component, Element, Hooks, RenderContext |
| 143 | + Elements/ DSL factories (Dsl.cs) + fluent modifiers (ElementExtensions.cs) |
| 144 | + Flex/ FlexPanel — CSS Flexbox via Yoga |
| 145 | + Yoga/ Pure C# port of Meta's Yoga layout engine |
| 146 | + Hosting/ ReactorApp entry point, render loop, hot reload |
| 147 | +src/Reactor.Cli/ CLI tool (scaffolding, localization, preview) |
| 148 | +src/Reactor.Analyzers/ Roslyn analyzers (theming, accessibility) |
| 149 | +src/vscode-reactor/ VS Code live preview extension |
| 150 | +tests/ |
| 151 | + Reactor.Tests/ Unit tests (xUnit, headless) |
| 152 | + Reactor.SelfTests/ Selftest runner (MSTest, wraps TAP subprocess) |
| 153 | + Reactor.AppTests.Host/ Selftest host app + Appium fixture navigator |
| 154 | + Reactor.AppTests/ E2E tests (MSTest + Appium/WinAppDriver) |
| 155 | +samples/ Demo apps and samples |
| 156 | +docs/ |
| 157 | + guide/ User documentation (generated from templates) |
| 158 | + specs/ Numbered design specs |
| 159 | + reference/ API and subsystem reference |
| 160 | +``` |
0 commit comments