iris: a Rect is not size-independent, and P1a's block appearance verified

The defect P1a's screenshots found, and the one that mattered:
`Rect::is_size_independent()` answered `true`. A `Rect` fills whatever
region it is handed, so its content *is* the region -- and
`draw_inner`'s fast path, which rewrites a widget's primitives with
`r.outside(&from).within(&region)` instead of redrawing it, cannot
reproduce that once a region carries both `rel` and `abs`. What it
looked like: a fenced code block's background kept the height of the
provisional full-region draw `Span` does in its first phase, so one
fence's panel covered every block below it and every row below that,
with the text underneath laid out correctly. Likely the same cause as
RUST.md's older "the composer bar's grey background is not drawn".

Also here: a quote's bar is a `Stack` background behind padded text
rather than a two-child `Span(Dir::RIGHT)` (one widget fewer and no
provisional pass), and `transcript-ui`'s `transcript` example gains a
row holding one of every block kind -- the fixture's own heading,
paragraph, fence and table source, plus a list and a quote, which the
fixture has neither of.

docs/bench/p1a-2026-09-06/ has the pairs and docs/RUST.md's P1a box
names what still differs. The iris half is from the desktop backend
because this emulator cannot draw iris's glyphs at all (solid boxes,
reproduced on the previous commit, with Compose drawing text correctly
on the same AVD); both routes to Vulkan on this AVD were tried and both
fail. Bench stream phase, assertions live, no abort: p50 53.0ms p90
108.6ms p99 132.0ms against 52.8/108.1/137.3 before -- unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-06 19:30:39 -04:00
1 parent 64f64b54e5
commit 69525bd131
11 files changed
+355 -34

No files matched your search

+62 -11
View File
@@ -535,22 +535,31 @@ agent ticks it here with the evidence.
`row.rs`'s `build_text_row` is where one would go, keyed to something
stable per row (its sender + a short excerpt, matching what a screen
reader announcing a chat message would say).
- [ ] **A tappable link and a background chip behind inline code.**
Both need per-range glyph geometry that `TextEditCtx` does not expose
outside `iris::widget::text` (`edit.rs`'s `layout()` helper is
private) — see `markdown.rs`'s module doc for the exact shape the fix
would take (the same primitive `TextEdit::draw`'s own selection
highlight already uses internally,
`iris/src/widget/text/edit.rs:99`).
- [x] **A tappable link** — done 2026-09-06 (P1a). `TextEditCtx::
byte_at(pos, size)` answers which byte a tap landed on without
handing out the parley layout, `GestureOutcome::Tapped` says the
press committed to neither a pan nor a selection, and
`iris::platform::OpenUrl` is the capability each backend implements
(`xdg-open`/`open`/`start`; an `ACTION_VIEW` intent on Android,
deferred to `after_input` the way `pending_show_keyboard` is).
- [ ] **A background chip behind inline code.** Still needs per-range
glyph *geometry* — a run's boxes, not one offset — which
`TextEditCtx` does not expose outside `iris::widget::text`
(`edit.rs`'s `layout()` helper is private). The same primitive
`TextEdit::draw`'s own selection highlight uses internally,
`iris/src/widget/text/edit.rs:99`. `byte_at` above deliberately did
not open that up: a tap needs one offset and a chip needs the run.
- [ ] **`Selection`'s anchor-row shortcut.** The row a drag started in
is selected in full (`select_all`) the moment the drag leaves it,
rather than "from the click point to whichever edge points away from
the drag" — needs the same private `layout()` access as the item
above. `selection.rs`'s module doc has the exact reasoning.
- [ ] **No syntax highlighting inside a fenced code block.**
`client_core::highlight` exists (built for the file explorer) and
could feed per-token `SpanStyle`s into a code block's span; wiring it
in was not attempted this pass.
- [x] **Syntax highlighting inside a fenced code block** — done
2026-09-06 (P1a). `client_core::highlight::spans_of` by language,
converted from its char indices to `SpanStyle`'s byte offsets, in
the same Catppuccin palette `Theme.kt` uses. A language the scanner
has no rules for stays plain rather than being coloured by the
nearest one's.
- [ ] **Masks defined relative to each other.** Wanted: mask A multiplies
by something *and also* applies mask B — a mask can reference a parent
@@ -570,6 +579,48 @@ agent ticks it here with the evidence.
everything, the same way input is**. Whatever the mechanism, a widget
that does not animate must pay nothing and import nothing for it.
## Found by P1a (2026-09-06)
- [x] **`Rect` claimed to be size-independent, and it is not.** A `Rect`
fills whatever region it is handed, so `draw_inner`'s size-independent
fast path -- which rewrites primitives with
`r.outside(&from).within(&region)` rather than redrawing -- could not
reproduce its `draw`, and a `.background(rect(..))` kept the size of
the *provisional* full-region pass `Span` does in phase 1. One fenced
code block's panel covered every block below it and every row below
that. Fixed in `iris/src/widget/rect.rs`; the reason is written at the
definition. Suspect the same cause for anything else tinted with a
background rect.
- [ ] **A wrapped transcript row trips `reposition`'s debug assert.**
*"widget ... is both moved by its parent's own layout (`mov`) and
repositioned within it"*, raised from `List::place`. Repro: change
`.wrap(!verbatim)` to `.wrap(true)` in `transcript-ui/src/row.rs`'s
`build_block` and run `iris/run-headless.sh transcript --shot
/tmp/x.png -- -p transcript-ui`. Survives the `Rect` fix above and is
not specific to any block kind -- it appears once the row is tall
enough. The shipping configuration does not reach it (verbatim blocks
do not wrap) and the Android bench runs clean with assertions live,
but it is a real disagreement about who owns a widget's move slot and
should be settled before more of P1 leans on `List`.
- [ ] **Desktop colours are washed out: the winit surface is sRGB and
the shader writes the palette's bytes as linear.** Mocha Crust
(17,17,27) is drawn as (73,73,91), measured off
`run-headless.sh --shot`. Android is correct, so this is the surface
format rather than the palette -- but it makes the desktop build
useless as a colour reference, which is exactly what P1a needed it for
when the emulator could not draw glyphs.
- [ ] **The emulator cannot draw iris's glyphs.** Under `-gpu host` with
Vulkan disabled (Mesa 26.2.2 / virgl -- what `emu` does on this
machine) every character renders as a solid filled box: the atlas
sample's alpha reads 1, which is what an incomplete GL texture returns
(0,0,0,1). Not new (`20303e0` does it too) and not the platform's
(Compose draws text perfectly on the same AVD in the same minute).
Enabling host Vulkan still dies at boot in gfxstream, and
`EMU_GPU=software` gives SwiftShader Vulkan on which iris **SIGSEGVs
in `surface_changed`**. Either of the last two would restore
appearance testing on Android; today it has to be done on the desktop
backend or on Iris's phone.
## Build (for the port)
Widgets `RUST.md`'s "The port, in order (decided 2026-09-05)" needs and