diff --git a/docs/IRIS_EXTRACTION_HANDOFF.md b/docs/IRIS_EXTRACTION_HANDOFF.md index 53d574f..220676f 100644 --- a/docs/IRIS_EXTRACTION_HANDOFF.md +++ b/docs/IRIS_EXTRACTION_HANDOFF.md @@ -5,54 +5,25 @@ Not a decisions log; delete it when the extraction is done. ## Where things stand -Canonical `main` is **`43ce8c7`** (#12, pointer routing). Fourteen slices are -in. +Canonical `main` is **`ca2b4b2`** (#17, the headless rig). Sixteen slices are +in, and #16's size work and #17's rig both merged on 2026-09-14. -- **#16** `split/16-draw-size`, worktree `/home/bob/repos/iris-pr16`, head - `4654756`. A widget sizes itself while drawing; `SizeCtx`, the `desired_*` - methods and the size cache are gone, and `on_resize(axis)` drives the - retained path. Two review rounds answered: `Widget::draw` returns the `Size` - (no `Painter::set_size`), both `place` methods are folded back into - `widget_within`/`draw_inner`, `Painter::size_hint` records the size - dependency `place` used to, `()` sizes itself rest so it is a gap, and a - resize now moves the drawing instead of wiping the tree. `tabs` (all five - tabs, and a replay that adds two images), `view` and `minimal` render - byte-identical to `upstream/main`. -- **#17** `split/17-headless-rig`, worktree `/home/bob/repos/iris-pr17`, head - `deb9c1b`. The compositor script, `rig-input`'s `replay-touch` and the - `.touch` parser, so a rendering claim no longer has to be checked from - ai-app's checkout. One review round answered: machine-specific prose out of - the comments, and `sway`/`swaymsg`/`grim` detected up front. Small, and - disjoint from #16. +- **#18** `split/18-position-chain`, worktree `/home/bob/repos/iris-pr18`, head + `8223a55`. LAYOUT.md §2's O(1) subtree movement: every active widget owns a + slot in `UiRenderState::moves`, `prelude.wgsl` walks the chain, and + `try_reuse`'s translation case writes one slot instead of remapping a + subtree's primitives. Measured at 100 primitive region writes to 0 on a span + of 20 rows five primitives deep. `Vec2` is `repr(align(8))` so a GPU struct + holding one matches WGSL without saying so itself. -**Waiting on the owner:** whether size dependencies should be recorded per -axis. They could be, but `mark_readers`' only trigger is `needs_redraw`, which -carries no axis, so the precision would be unusable until the trigger changes: -redraw a child first, compare its new size to the old, and propagate only to -readers of the axes that moved. Offered on #16 as its own slice, ahead of the -position chain. + Not in it, both wanting the above first: `Painter::set_child_offset` for a + container that moves its children as a group (the scrolling case), and + `LazySpan`. Built-in alignment is after those -- the owner reordered it + behind the chain on 2026-09-14 as the more important of the two. -**What the resize change taught, and why the chain moved up.** `mov` cannot -stretch a drawing out of a box with no relative extent. `UiScalar::within` maps -a part's `(rel, abs)` through a matrix whose determinant is the box's own rel -extent, so at zero extent it is singular: every fraction of that box collapses -onto one stored value, and nothing recovers which one. `lerp_inv` used to return -a plausible number there instead of saying so; it and `outside` now return -`Option`, and `Remap` is the checked region-level operation, so a drawing that -cannot be taken out of its box is redrawn rather than left at its old size. - -`Remap` is explicitly a holding pattern: the owner accepted it on 2026-09-14 -with *"I don't like remap much but I'll let it in for now until we get proper -region chaining"*. Delete it in the chaining slice rather than build on it -- -holding each part's own region against its box means there is no composition to -invert and nothing for `Remap` to check. - -Counted draws on a resize, against the old wipe-everything: 101 widgets of -padded rows went 101 -> 0, `tabs` 73 -> 0, and the `text` example 49 -> 48. The -last is the shape that does not benefit -- both paragraphs rewrap to a different -height and the relayout runs to the root. Holding each part's own region against -its box and resolving late is what removes those, so the position chain now sits -directly after built-in alignment. +The invariant the chain rests on: `resolved = region + resolve(slot)`, so +anything that rewrites a region owes that slot a zero. `mov` does it for the +subtree it rewrites and `draw_inner` for the widget it draws. Check for a review before starting anything, and read the newest `submitted_at` rather than the first result: @@ -127,8 +98,8 @@ Other standing instructions from the owner: ## The next slice -**Built-in alignment** — see the first entry below. #16 and #17 are both -waiting on review, and the alignment slice is disjoint from either. +**`set_child_offset` and `LazySpan`**, finishing LAYOUT.md §2 on top of #18, +then built-in alignment. The archive is not a patch here: it writes `Widget::draw` against `painter.set_size`, which #16 replaced with a returned `Size`, and it writes @@ -137,11 +108,14 @@ Recreate on today's `Len` and let the dp slice follow. Still in the target, roughly in dependency order: -- **Built-in alignment, and probably size**, directly after #16 and before the - position chain: the chain is an optimisation, this is behaviour. A child of a - span is handed the full extent on the ortho axis, so `.width(rel(0.5))` - inside a `Dir::DOWN` span changes what the child reports and not the box it - gets. **Do not "fix" that by reading the child's ortho `size_hint`**: a `Pad` +- **The rest of the position chain** (LAYOUT.md §2), on top of #18: + `set_child_offset` for a container that moves its children as a group, and + `LazySpan`. +- **Built-in alignment, and probably size**, after the chain rather than + before it: the owner reordered the two on 2026-09-14. Reproduced in the + harness -- a child of a span is handed the full extent on the ortho axis, so + `.width(rel(0.5))` inside a `Dir::DOWN` span changes what the child reports + and not the box it gets. **Do not "fix" that by reading the child's ortho `size_hint`**: a `Pad` between the `SetSize` and the span has no hint of its own, so the declared width silently goes back to filling. It works only when nothing is in the way. Alignment has to belong to the widget rather than be discovered through @@ -156,12 +130,7 @@ Still in the target, roughly in dependency order: Size is the harder half: a declared size beside the one `draw` returns is two sources of truth for one thing, so settle what each means before building it. -- **The position chain** (LAYOUT.md §2): `UiData::move_offsets`, per-primitive - slot ids and a bounded chain walk in WGSL, so moving a subtree writes one - slot instead of every descendant's primitives. - - It also has to make `OnResize::Translate` possible, which is the reason that - variant does nothing today. `ActiveData::region` is both the box a widget was +- **`OnResize::Translate`**, which still does nothing. `ActiveData::region` is both the box a widget was given and the box its primitives occupy, and `mov` remaps out of it; keeping a drawing at its old size while the box grows leaves the two disagreeing and the next move stretches it. Found by rendering `tabs` against `main`, not by