Skip to content

Commit 9182ae1

Browse files
docs: wire up nav + rename "V1 Protocol" page to "Control Reconciler Protocol"
The two pages added in #444 (the protocol reference + the extension cookbook) were never added to the mkdocs `nav:` tree, so they published but were unreachable from the site navigation. Wire both into the "Under the Hood" section. Rename the reference page from the internal "V1 Protocol" label to the clearer "Control Reconciler Protocol" (reader-visible only): - template v1-protocol.md.dt -> control-reconciler-protocol.md.dt, which drives the generated output filename - page title/H1, nav label, and the 8 cross-links + link text from the cookbook page - internal slugs left as-is (doc-app folder, image paths, the src/Reactor/Core/V1Protocol namespace) since they are not reader-visible Recompiled the docset (no screenshots), which refreshes generated output that had drifted from source: - threading-and-dispatch.md: echo-suppression snippet picks up the EchoSuppressScopeDepth path - skills/reactor.api.txt (both copies) regenerated: adds the ItemsRepeater<T> factory overloads and the V1UnmountDisposition enum Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c14d172 commit 9182ae1

8 files changed

Lines changed: 36 additions & 23 deletions

File tree

docs/_pipeline/templates/v1-protocol.md.dt renamed to docs/_pipeline/templates/control-reconciler-protocol.md.dt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "The V1 Handler Protocol"
2+
title: "The Control Reconciler Protocol"
33
app: v1-protocol
44
order: 33.5
55
audience: advanced
@@ -27,7 +27,7 @@ every control you add yourself dispatches through the same tight loop.
2727
Read this page before adding a new native control or before reaching for
2828
a debugger when an existing one mis-updates.
2929

30-
# The V1 Handler Protocol
30+
# The Control Reconciler Protocol
3131

3232
This page documents the data path between
3333
[`Reconciler.Reconcile`](reconciliation.md#reconcile--the-entry-point)

docs/_pipeline/templates/extending-reactor-controls.md.dt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ that flow. By the end you will have added a `StarMeter` element to a
2525
Reactor app, wired it to WinUI's `RatingControl`, and matched the
2626
built-in performance bar without writing a line of imperative
2727
interpreter code. Read the reference companion
28-
[The V1 Handler Protocol](v1-protocol.md) for the model the steps here
28+
[The Control Reconciler Protocol](control-reconciler-protocol.md) for the model the steps here
2929
plug into.
3030

3131
# Extending Reactor with Native Controls
@@ -102,9 +102,9 @@ shape; keeping the demo focused, the cookbook below omits it.
102102
## Step 2 — Choose a shape (descriptor vs hand-coded)
103103

104104
Reactor's handler protocol has two surfaces — declarative
105-
([`ControlDescriptor`](v1-protocol.md#descriptors--declarative-handlers))
105+
([`ControlDescriptor`](control-reconciler-protocol.md#descriptors--declarative-handlers))
106106
and imperative
107-
([`IElementHandler`](v1-protocol.md#the-handler-contract)) — that
107+
([`IElementHandler`](control-reconciler-protocol.md#the-handler-contract)) — that
108108
land on the same dispatch table. New controls should default to a
109109
descriptor; reach for a hand-coded handler only when the descriptor
110110
escape hatches (`.Imperative`, `.HandCoded…`) still cannot express
@@ -129,7 +129,7 @@ the bar for "complicated enough to write by hand."
129129
```
130130

131131
Each fluent builder call adds one
132-
[`PropEntry`](v1-protocol.md#propentry-shapes--the-prop-binding-vocabulary)
132+
[`PropEntry`](control-reconciler-protocol.md#propentry-shapes--the-prop-binding-vocabulary)
133133
to the descriptor. The interpreter iterates them in declaration order
134134
during Mount and Update. The descriptor above uses three of the
135135
common shapes:
@@ -199,7 +199,7 @@ short-circuit for `IsDisabledFocusable` and the
199199

200200
`StarMeterElement` is a leaf, so the descriptor declares
201201
`Children = new None<…>()`. The dispatch surface is the same as the
202-
[ChildrenStrategy survey](v1-protocol.md#children-strategies); the
202+
[ChildrenStrategy survey](control-reconciler-protocol.md#children-strategies); the
203203
right strategy is whichever matches your WinUI control's structural
204204
contract:
205205

@@ -265,7 +265,7 @@ opt-ins.
265265
exposes `(Color, IsOn) → Background` is one entry, not two — the
266266
`get` returns a tuple and the `set` reads both fields. The diff fires
267267
exactly when either input changes. This is how the LED indicator in
268-
[the V1 Protocol reference](v1-protocol.md#descriptors--declarative-handlers)
268+
[the Control Reconciler Protocol reference](control-reconciler-protocol.md#descriptors--declarative-handlers)
269269
collapses two element fields into one WinUI write.
270270

271271
**Layer in a Reactor-friendly default for an awkward control.** Many
@@ -358,14 +358,14 @@ floating-point tolerance, deliberately imperative entry — capture
358358
the reason in a comment on the descriptor field, not on the calling
359359
component. The descriptor is where future maintainers will look.
360360

361-
**Read the V1 Protocol reference once end-to-end.** Steps 1 through 6
362-
above are the recipe; [the protocol page](v1-protocol.md) is the
361+
**Read the Control Reconciler Protocol reference once end-to-end.** Steps 1 through 6
362+
above are the recipe; [the protocol page](control-reconciler-protocol.md) is the
363363
specification it implements. The two pages together are the complete
364364
contract — bookmark both.
365365

366366
## Next Steps
367367

368-
- [The V1 Handler Protocol](v1-protocol.md) — the model the steps on this page plug into.
368+
- [The Control Reconciler Protocol](control-reconciler-protocol.md) — the model the steps on this page plug into.
369369
- [Reconciliation](reconciliation.md) — what happens before the registered handler is invoked, and how Mount vs Update vs Unmount get chosen.
370370
- [Element Pool](element-pool.md) — what `RentControl` actually does, and how the per-type pool keeps `Mount` allocation-free.
371371
- [Modifier System](modifier-system.md) — how the `Setters` chain a descriptor applies after its own prop loop works.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ every control you add yourself dispatches through the same tight loop.
1212
Read this page before adding a new native control or before reaching for
1313
a debugger when an existing one mis-updates.
1414

15-
# The V1 Handler Protocol
15+
# The Control Reconciler Protocol
1616

1717
This page documents the data path between
1818
[`Reconciler.Reconcile`](reconciliation.md#reconcile--the-entry-point)

docs/guide/extending-reactor-controls.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ that flow. By the end you will have added a `StarMeter` element to a
1111
Reactor app, wired it to WinUI's `RatingControl`, and matched the
1212
built-in performance bar without writing a line of imperative
1313
interpreter code. Read the reference companion
14-
[The V1 Handler Protocol](v1-protocol.md) for the model the steps here
14+
[The Control Reconciler Protocol](control-reconciler-protocol.md) for the model the steps here
1515
plug into.
1616

1717
# Extending Reactor with Native Controls
@@ -100,9 +100,9 @@ shape; keeping the demo focused, the cookbook below omits it.
100100
## Step 2 — Choose a shape (descriptor vs hand-coded)
101101

102102
Reactor's handler protocol has two surfaces — declarative
103-
([`ControlDescriptor`](v1-protocol.md#descriptors--declarative-handlers))
103+
([`ControlDescriptor`](control-reconciler-protocol.md#descriptors--declarative-handlers))
104104
and imperative
105-
([`IElementHandler`](v1-protocol.md#the-handler-contract)) — that
105+
([`IElementHandler`](control-reconciler-protocol.md#the-handler-contract)) — that
106106
land on the same dispatch table. New controls should default to a
107107
descriptor; reach for a hand-coded handler only when the descriptor
108108
escape hatches (`.Imperative`, `.HandCoded…`) still cannot express
@@ -164,7 +164,7 @@ public static class StarMeterDescriptor
164164
```
165165

166166
Each fluent builder call adds one
167-
[`PropEntry`](v1-protocol.md#propentry-shapes--the-prop-binding-vocabulary)
167+
[`PropEntry`](control-reconciler-protocol.md#propentry-shapes--the-prop-binding-vocabulary)
168168
to the descriptor. The interpreter iterates them in declaration order
169169
during Mount and Update. The descriptor above uses three of the
170170
common shapes:
@@ -232,7 +232,7 @@ short-circuit for `IsDisabledFocusable` and the
232232

233233
`StarMeterElement` is a leaf, so the descriptor declares
234234
`Children = new None<…>()`. The dispatch surface is the same as the
235-
[ChildrenStrategy survey](v1-protocol.md#children-strategies); the
235+
[ChildrenStrategy survey](control-reconciler-protocol.md#children-strategies); the
236236
right strategy is whichever matches your WinUI control's structural
237237
contract:
238238

@@ -335,7 +335,7 @@ opt-ins.
335335
exposes `(Color, IsOn) → Background` is one entry, not two — the
336336
`get` returns a tuple and the `set` reads both fields. The diff fires
337337
exactly when either input changes. This is how the LED indicator in
338-
[the V1 Protocol reference](v1-protocol.md#descriptors--declarative-handlers)
338+
[the Control Reconciler Protocol reference](control-reconciler-protocol.md#descriptors--declarative-handlers)
339339
collapses two element fields into one WinUI write.
340340

341341
**Layer in a Reactor-friendly default for an awkward control.** Many
@@ -428,14 +428,14 @@ floating-point tolerance, deliberately imperative entry — capture
428428
the reason in a comment on the descriptor field, not on the calling
429429
component. The descriptor is where future maintainers will look.
430430

431-
**Read the V1 Protocol reference once end-to-end.** Steps 1 through 6
432-
above are the recipe; [the protocol page](v1-protocol.md) is the
431+
**Read the Control Reconciler Protocol reference once end-to-end.** Steps 1 through 6
432+
above are the recipe; [the protocol page](control-reconciler-protocol.md) is the
433433
specification it implements. The two pages together are the complete
434434
contract — bookmark both.
435435

436436
## Next Steps
437437

438-
- [The V1 Handler Protocol](v1-protocol.md) — the model the steps on this page plug into.
438+
- [The Control Reconciler Protocol](control-reconciler-protocol.md) — the model the steps on this page plug into.
439439
- [Reconciliation](reconciliation.md) — what happens before the registered handler is invoked, and how Mount vs Update vs Unmount get chosen.
440440
- [Element Pool](element-pool.md) — what `RentControl` actually does, and how the per-type pool keeps `Mount` allocation-free.
441441
- [Modifier System](modifier-system.md) — how the `Setters` chain a descriptor applies after its own prop loop works.

docs/guide/threading-and-dispatch.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,13 @@ internal static void BeginSuppress(UIElement control)
165165
internal static bool ShouldSuppress(UIElement control)
166166
{
167167
if (control is not FrameworkElement fe) return false;
168-
if (fe.GetValue(Reconciler.ReactorAttached.StateProperty) is Reconciler.ReactorState state
169-
&& state.EchoSuppressCount > 0)
168+
if (fe.GetValue(Reconciler.ReactorAttached.StateProperty) is not Reconciler.ReactorState state)
169+
return false;
170+
// §8.2 — setter-suppression scope: drop the echo without consuming a
171+
// counter token. The scope wraps ApplySetters, where the engine can't
172+
// predict which value-bearing DPs the user's `.Set(...)` will write.
173+
if (state.EchoSuppressScopeDepth > 0) return true;
174+
if (state.EchoSuppressCount > 0)
170175
{
171176
state.EchoSuppressCount--;
172177
return true;

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ nav:
136136
- WPF Interop: wpf-interop.md
137137
- Under the Hood:
138138
- Architecture Overview: architecture-overview.md
139+
- Control Reconciler Protocol: control-reconciler-protocol.md
140+
- Extending Reactor Controls: extending-reactor-controls.md
139141
- Reactivity Model: reactivity-model.md
140142
- Hooks Internals: hooks-internals.md
141143
- Reconciliation: reconciliation.md

plugins/reactor/skills/reactor-dsl/references/reactor.api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ InfoBadge(int value) → InfoBadgeElement
9090
InfoBar(string title = null, string message = null) → InfoBarElement
9191
InterspersedGrid(Orientation orientation, Element[] items, double[] proportions, double separatorSize, Func<int, Element> separatorFactory) → GridElement
9292
ItemContainer(Element child) → ItemContainerElement
93+
ItemsRepeater<T>(IReadOnlyList<T> items, Func<T, int, Element> viewBuilder) → ItemsRepeaterElement<T>
94+
ItemsRepeater<T>(IReadOnlyList<T> items, Func<T, string> keySelector, Func<T, int, Element> viewBuilder) → ItemsRepeaterElement<T>
9395
ItemsView<T>(IReadOnlyList<T> items, Func<T, string> keySelector, Func<T, int, Element> viewBuilder) → ItemsViewElement<T>
9496
LazyHStack<T>(IReadOnlyList<T> items, Func<T, int, Element> viewBuilder) → LazyHStackElement<T>
9597
LazyHStack<T>(IReadOnlyList<T> items, Func<T, string> keySelector, Func<T, int, Element> viewBuilder) → LazyHStackElement<T>
@@ -918,6 +920,7 @@ KeyedListDiagnosticKind { NullKey, DuplicateKey }
918920
ItemsViewLayoutKind { StackLayout, LinedFlowLayout, UniformGridLayout }
919921
PersistedScope { Window, Application }
920922
TemplatedControlKind { ListView, GridView, FlipView }
923+
V1UnmountDisposition { CollectSelf, ContinueDefaultTraversal, SkipPool }
921924
BlockStatus { Loading, Loaded, Failed }
922925
DataSourceCapabilities { None, ServerSort, ServerFilter, ServerSearch, ServerCount, ServerSelect, Mutate, Refresh }
923926
FilterOperator { Equals, NotEquals, Contains, StartsWith, EndsWith, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, Between, In, IsNull, IsNotNull }

skills/reactor.api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ InfoBadge(int value) → InfoBadgeElement
9090
InfoBar(string title = null, string message = null) → InfoBarElement
9191
InterspersedGrid(Orientation orientation, Element[] items, double[] proportions, double separatorSize, Func<int, Element> separatorFactory) → GridElement
9292
ItemContainer(Element child) → ItemContainerElement
93+
ItemsRepeater<T>(IReadOnlyList<T> items, Func<T, int, Element> viewBuilder) → ItemsRepeaterElement<T>
94+
ItemsRepeater<T>(IReadOnlyList<T> items, Func<T, string> keySelector, Func<T, int, Element> viewBuilder) → ItemsRepeaterElement<T>
9395
ItemsView<T>(IReadOnlyList<T> items, Func<T, string> keySelector, Func<T, int, Element> viewBuilder) → ItemsViewElement<T>
9496
LazyHStack<T>(IReadOnlyList<T> items, Func<T, int, Element> viewBuilder) → LazyHStackElement<T>
9597
LazyHStack<T>(IReadOnlyList<T> items, Func<T, string> keySelector, Func<T, int, Element> viewBuilder) → LazyHStackElement<T>
@@ -918,6 +920,7 @@ KeyedListDiagnosticKind { NullKey, DuplicateKey }
918920
ItemsViewLayoutKind { StackLayout, LinedFlowLayout, UniformGridLayout }
919921
PersistedScope { Window, Application }
920922
TemplatedControlKind { ListView, GridView, FlipView }
923+
V1UnmountDisposition { CollectSelf, ContinueDefaultTraversal, SkipPool }
921924
BlockStatus { Loading, Loaded, Failed }
922925
DataSourceCapabilities { None, ServerSort, ServerFilter, ServerSearch, ServerCount, ServerSelect, Mutate, Refresh }
923926
FilterOperator { Equals, NotEquals, Contains, StartsWith, EndsWith, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, Between, In, IsNull, IsNotNull }

0 commit comments

Comments
 (0)