Skip to content

Commit 1300c9c

Browse files
docs: add AGENTS.md for AI coding assistants
Provides build/test commands, architecture overview, and key conventions for Copilot, Claude, Codex, and other AI assistants working on the framework itself. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7983633 commit 1300c9c

1 file changed

Lines changed: 160 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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

Comments
 (0)