docs: Iris's second 2026-09-08 phone report, and the workaround list closed
RUST.md gets the report verbatim with what each of the four defects actually was, the tests that pin them, and two traps worth not re-finding (a fixed-coordinate tap that "failed" by 544px because it had toggled a tool group, and a layer-1 repro that only reproduces inside a `List`). IRIS.md and DECISIONS.md get the design half: one `Flinger` whose seam puts the sign convention and the content's end with the caller, a cancel as a first-class end to a gesture, and why a row is drawn twice on the frame its height changes. LAYOUT.md gains the two rules those turned on, since both govern the layout rather than this pass: padding works in any container and is an inset or an outset depending on how tight the parent's region is (Iris's own words), and a widget offered a box it does not fit is drawn again at its true box in the same frame rather than the next one. IRIS_TODO.md's "worked around in tool.rs rather than fixed here" is gone -- Iris, 2026-09-08: "There should never be workaround code." Two of the four entries are ticked; the two that remain are missing capabilities rather than defects being dodged, and each now carries a diagnosis of what building it costs instead of a workaround: an overflow ellipsis needs `TextBuffer` to have a displayed string distinct from its source (parley has none of its own, and every byte-offset consumer -- spans, `byte_at`, `Selection`, `apply_delta` -- moves if the buffer is truncated), and selectable tool-card text needs a register/unregister lifecycle across the three routes that rebuild a card, which is where a stale `Selection` handle panics. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
02b277e7ad
commit
fe7dc9c728
5 files changed
+353
-16
No files matched your search
+71
-15
@@ -785,22 +785,35 @@ Iris's report, verbatim, with a screenshot. Phone: Mali-G715 (Vulkan),
|
||||
## Found by P1b (2026-09-06), all with a headless repro
|
||||
|
||||
Each was found by looking at `iris/run-headless.sh transcript -- -p
|
||||
transcript-ui` rather than at a diff, and each is worked around in
|
||||
`transcript-ui/src/tool.rs` rather than fixed here. docs/RUST.md's P1b box
|
||||
has the fuller account.
|
||||
transcript-ui` rather than at a diff. docs/RUST.md's P1b box has the
|
||||
fuller account.
|
||||
|
||||
- [ ] **A `Span` of `Pad`ded children inside another `Span` places those
|
||||
**No entry here is worked around any more** (Iris, 2026-09-08: "All of
|
||||
those should be fixed. There should never be workaround code. Do the
|
||||
same for those; fix them if they're trivial, diagnose and report if
|
||||
not."). Two are fixed and ticked; the two that are left are missing
|
||||
*capabilities* rather than defects being dodged, and each carries its
|
||||
diagnosis and what building it actually costs.
|
||||
|
||||
- [x] **A `Span` of `Pad`ded children inside another `Span` places those
|
||||
children a slot out of step.** Each child drew its content one sibling's
|
||||
height below its own box. Repro: `IRIS_TOOLS_EXPANDED=1
|
||||
height below its own box. Repro was: `IRIS_TOOLS_EXPANDED=1
|
||||
iris/run-headless.sh transcript --shot /tmp/x.png -- -p transcript-ui`
|
||||
with `tool.rs`'s group built as `Span(DOWN)[header, Pad(Span(DOWN)
|
||||
[cards]), bar]` instead of the single `Span` it uses now. Bisected:
|
||||
removing the inner `Span` fixes it, and so does removing the children's
|
||||
own `Pad`; the background `Stack`, the `Sized` wrappers and the
|
||||
`WidgetPtr` per child make no difference. **Not** the `mov`-vs-
|
||||
`reposition` fault f5b8893 fixed -- it survives that commit. The
|
||||
workaround costs the group the 4dp inset its Compose counterpart holds
|
||||
its cards off the edge by, so this is worth fixing.
|
||||
[cards]), bar]` instead of the single `Span` it used. Bisected at the
|
||||
time: removing the inner `Span` fixed it, and so did removing the
|
||||
children's own `Pad`; the background `Stack`, the `Sized` wrappers and
|
||||
the `WidgetPtr` per child made no difference. **Not** the `mov`-vs-
|
||||
`reposition` fault f5b8893 fixed -- it survived that commit.
|
||||
**Not reproducible on 2026-09-08.** Both spans are nested again and the
|
||||
group has its 4dp inset back; that same headless render puts every
|
||||
card's content in its own box, and `iris`'s
|
||||
`a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is`
|
||||
(`layout_tests.rs`, the same shape inside a `List`, which is the
|
||||
context the real one is in) pins it at layer 1. Something between
|
||||
09-06 and 09-08 fixed it -- most likely the nested-mask pass or the
|
||||
`mov` work after f5b8893. Left ticked with the original symptom
|
||||
recorded rather than deleted, in case it comes back.
|
||||
- [x] **`scrollable_on(Axis::X)` on a non-editable `Text` draws nothing.**
|
||||
The panel is drawn and the text inside it is not. A markdown fence does
|
||||
the same to a `TextEdit` and is fine, so it is the widget kind rather
|
||||
@@ -819,6 +832,32 @@ has the fuller account.
|
||||
nothing on screen says it was cut. Whichever end is cut has to be a
|
||||
choice when this lands: a path is identified by its tail, a command by
|
||||
its head.
|
||||
|
||||
**Diagnosed 2026-09-08, and it is not trivial.** parley has no
|
||||
ellipsis of its own (checked: nothing in the vendored crates), so iris
|
||||
would build it, and the shape that looks easy is the one that breaks
|
||||
something. The easy half really is easy: shape at
|
||||
`max_advance = width - ellipsis_advance` with wrapping on, take line
|
||||
0's `text_range()`, and re-shape `text[..end].trim_end() + "…"` with
|
||||
wrapping off -- parley's own line breaker finds the cut, so nothing
|
||||
here counts glyph advances by hand. The hard half is that
|
||||
`TextBuffer` has exactly one string and everything addresses it by
|
||||
byte offset: the inline spans that carry a fence's colours and a
|
||||
link's range, `TextEditCtx::byte_at` (which turns a tap into a byte to
|
||||
match a link against), `Selection`'s `select`/`selected_text`, and
|
||||
`RowBlocks::apply_delta`. Truncating the buffer moves every one of
|
||||
those. So the real work is giving `TextBuffer` a **displayed** string
|
||||
distinct from its source, with one mapping from display byte to source
|
||||
byte that all of those go through -- worth doing, and not a
|
||||
by-the-way. Doing it only for text that is neither editable nor
|
||||
selectable would avoid all of that and is exactly the kind of
|
||||
exemption that comes back later.
|
||||
|
||||
It also wants an API change while it is open: `TextAttrs::wrap: bool`
|
||||
cannot say three states. Something like `Overflow::{Wrap, Clip,
|
||||
Ellipsis(End)}` replaces it, with `End::{Head, Tail}` making
|
||||
UI_RULES's "choose which end to truncate" a thing a caller must
|
||||
answer rather than a default nobody reads.
|
||||
- [x] **A chevron the platform cannot fail to have.** **Done
|
||||
2026-09-08**, twice. First as `iris::widget::mark(dir, dp, colour)`,
|
||||
which rasterised an antialiased triangle into the ordinary texture path
|
||||
@@ -841,9 +880,26 @@ has the fuller account.
|
||||
- [ ] **A tool card's text is not selectable.** `Selection` is keyed
|
||||
`(RowKey, block index)` and a card has no markdown blocks, so nothing in
|
||||
a card registers. Compose's `SelectionContainer` covers tool output,
|
||||
which is the text people most want to copy. Needs a key for "the nth
|
||||
text of this row" that a card can mint without colliding with a
|
||||
message's blocks.
|
||||
which is the text people most want to copy.
|
||||
|
||||
**Diagnosed 2026-09-08: mechanical, but more than a sitting.** There
|
||||
is no key collision to design around, which was the open question:
|
||||
a `TranscriptRow::Tools` has *only* cards and no markdown blocks at
|
||||
all, so a card is free to number its own texts from 0 in reading
|
||||
order. What it costs is the registration lifecycle rather than the
|
||||
key. Each card's `TextEdit`s have to `Selection::register` as they are
|
||||
built and `unregister` when they are not -- and a card is rebuilt from
|
||||
several directions (`redraw_card` when a result arrives,
|
||||
`Shared::set_content` when the group is toggled or a call joins the
|
||||
run, and the per-card `WidgetPtr` swap), each of which frees widgets
|
||||
the map would otherwise still point at. That is the exact shape of the
|
||||
crash `Selection::clear`'s doc records from
|
||||
docs/REVIEW-2026-09-06.md: a handle in that map outliving the widget
|
||||
panics on the *next* long press, somewhere else entirely. So the work
|
||||
is a per-card base index with a stride (and a `debug_assert` that a
|
||||
card stays inside it), one register/unregister path that every rebuild
|
||||
route goes through, and a test per route that a rebuilt card leaves no
|
||||
stale handle behind.
|
||||
|
||||
## Build (for the port)
|
||||
|
||||
|
||||
Reference in new issue
Block a user