RUST.md, IRIS.md, IRIS_TODO.md: record I5 -- transcript-ui built and
partial, the recommendation's numbers still missing I5's own box: the seven "hard to get back" behaviours each shown or given a sourced reason, the exact verification commands and results, and what remains (Android integration, touch-drag-vs-selection arbitration, row accessibility names, a tappable link, code-span chip, selection's anchor-row shortcut, code-fence syntax highlighting) -- each also a dated IRIS_TODO.md item so it is not silently dropped. Ticked [~] rather than [x]: the widget-tree half is built and tested, the emulator half is not. "Where things stand" and the Recommendation's item 3 updated in place to say plainly that neither Masonry (E2) nor iris (I5) has produced a render number yet, and why -- not a bad measurement, no measurement obtainable yet on either side -- with the structural findings that do exist (iris now does cross-row selection and per-span inline rich text, neither of which exists in masonry/masonry_core/xilem today) recorded as what currently favours iris absent a number. IRIS.md gets SpanStyle's own entry: what changed, why, and the one thing a future TextBuilderOutput impl must remember (both TextOutput and TextEditOutput apply .spans() -- this box shipped the bug of missing one of the pair once already). Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
This commit is contained in:
1 parent
3f25e7ebca
commit
b063fbd7f9
3 files changed
+347
-4
No files matched your search
@@ -8,6 +8,47 @@ capability that moved. Small and trivial changes do not go here.
|
|||||||
An entry gives the date, what changed, why, and a short before/after where
|
An entry gives the date, what changed, why, and a short before/after where
|
||||||
it helps judge the change without the session that made it. Newest first.
|
it helps judge the change without the session that made it. Newest first.
|
||||||
|
|
||||||
|
## 2026-09-05: `SpanStyle`, per-range text styling (RUST.md's I5)
|
||||||
|
|
||||||
|
A `TextBuffer` used to have exactly one style (`TextAttrs`: colour, size,
|
||||||
|
family, ...) for its whole string, applied via `push_default` into parley's
|
||||||
|
ranged builder. `SpanStyle` is a second, optional layer: a byte range plus
|
||||||
|
whichever of colour/family/font size/bold/italic/underline it overrides,
|
||||||
|
pushed with parley's own `push(property, range)` instead. Why: a transcript
|
||||||
|
row's markdown (a heading, **bold**, `inline code`, a link) all inside one
|
||||||
|
wrapped paragraph needs each to carry its own look while the paragraph
|
||||||
|
still wraps and selects as a single buffer — the thing `masonry`'s
|
||||||
|
`TextArea` cannot do (`StyleSet` is one style for the whole editor,
|
||||||
|
`text_area.rs:43-44`'s `// TODO: RichTextInput`), and the reason this
|
||||||
|
existed at all.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let (text, spans) = transcript_ui::markdown::render_markdown(src, 16.0);
|
||||||
|
wtext(text)
|
||||||
|
.spans(spans) // new: TextBuilder::spans, on both Text and TextEdit
|
||||||
|
.editable(EditMode::MultiLine)
|
||||||
|
.add(rsc);
|
||||||
|
```
|
||||||
|
|
||||||
|
Two things a widget author should know before reaching for it:
|
||||||
|
|
||||||
|
- **Call `.spans()` before or after `.editable()`, both work** — the field
|
||||||
|
lives on `TextBuilder` itself, not either output type, and both
|
||||||
|
`TextOutput::run` and `TextEditOutput::run` apply it to the buffer via
|
||||||
|
`TextBuffer::set_spans`. **These two call sites are a pair**: adding a
|
||||||
|
third `TextBuilderOutput` impl without also calling `set_spans` there
|
||||||
|
reproduces the exact bug this box shipped once already (spans silently
|
||||||
|
dropped for `TextEdit`, found only by screenshotting, not by any test —
|
||||||
|
`markdown.rs`'s own unit tests check string/range logic, which is
|
||||||
|
correct in isolation and proves nothing about whether the render path
|
||||||
|
ever sees it).
|
||||||
|
- **Colour is now per-glyph, not per-buffer.** `PlacedGlyph` gained a
|
||||||
|
`color: UiColor` field (from parley's own per-run `Style::brush`), and
|
||||||
|
`Painter::glyphs` draws each glyph in its own colour instead of
|
||||||
|
`RenderedText::color` uniformly. `RenderedText::color` still exists (the
|
||||||
|
buffer's *base* colour, for a caller that wants it as a whole, e.g. to
|
||||||
|
tint a cursor) but no longer drives what a glyph actually renders as.
|
||||||
|
|
||||||
## 2026-09-05: accessibility names via AccessKit (RUST.md's I4)
|
## 2026-09-05: accessibility names via AccessKit (RUST.md's I4)
|
||||||
|
|
||||||
`.label()` (already in `trait_fns.rs`, previously unused anywhere in-tree)
|
`.label()` (already in `trait_fns.rs`, previously unused anywhere in-tree)
|
||||||
|
|||||||
@@ -170,6 +170,54 @@ order and what "done" looks like. Tick and date them in place.
|
|||||||
behave as designed; its *append* half did not, until the fix above moved
|
behave as designed; its *append* half did not, until the fix above moved
|
||||||
masks/move_offsets out of the per-image bind group — now flat at O(1)
|
masks/move_offsets out of the per-image bind group — now flat at O(1)
|
||||||
the same way (b) and (c) are.
|
the same way (b) and (c) are.
|
||||||
|
|
||||||
|
- **I5's transcript screen (`iris/transcript-ui/`, 2026-09-05) — what it
|
||||||
|
left, each recorded at the point in the code it would go rather than
|
||||||
|
silently dropped. See RUST.md's I5 box for the full account of what
|
||||||
|
*was* built (the screen, `SpanStyle`, cross-row selection, the growing
|
||||||
|
composer).**
|
||||||
|
- [ ] **Android integration for this screen does not exist yet.** No
|
||||||
|
cdylib/Gradle shell the way `iris-android-app` wraps `tabs-ui` (I2),
|
||||||
|
so `transcript-bench.sh`'s render-number pass condition against the
|
||||||
|
Compose baseline cannot be run. Needs: real `client-core::ApiClient`/
|
||||||
|
`event_stream::follow_session_events` wiring against
|
||||||
|
`app/ui-sandbox.sh --delay` (this crate deliberately fetches nothing
|
||||||
|
itself, `transcript-ui/src/lib.rs`'s doc), a new cdylib + Gradle
|
||||||
|
module, then the bench script pointed at it.
|
||||||
|
- [ ] **Touch-drag panning over a row's own rendered text.** `row.rs`
|
||||||
|
registers `CursorSense::click_or_drag()` on each row's `TextEdit` for
|
||||||
|
cross-row selection; `TextEdit::draw`'s `painter.child_layer()`
|
||||||
|
(`iris/src/widget/text/edit.rs:87`) means that registration wins
|
||||||
|
`core/src/sense.rs::run_sensors`'s per-layer arbitration on every
|
||||||
|
frame it is pressed, not just the frame the press started, so a list
|
||||||
|
pan gesture registered on `List` itself never gets a turn while a
|
||||||
|
row is under the finger. Fix: a small press distance/time arbiter
|
||||||
|
deciding pan vs. select before either commits, or gate text-drag-
|
||||||
|
selection behind a long-press so an ordinary swipe always pans first.
|
||||||
|
`lib.rs`'s module doc has the full diagnosis.
|
||||||
|
- [ ] **Row-level accessibility names.** The composer carries
|
||||||
|
`.label("Message")`; transcript rows do not carry a `.label()` of
|
||||||
|
their own yet, so `Widgets::named()` (I4) does not include them —
|
||||||
|
`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`).
|
||||||
|
- [ ] **`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.
|
||||||
|
|
||||||
- [ ] **Masks defined relative to each other.** Wanted: mask A multiplies
|
- [ ] **Masks defined relative to each other.** Wanted: mask A multiplies
|
||||||
by something *and also* applies mask B — a mask can reference a parent
|
by something *and also* applies mask B — a mask can reference a parent
|
||||||
mask, the way the move chain references a parent offset. Today masks
|
mask, the way the move chain references a parent offset. Today masks
|
||||||
|
|||||||
@@ -42,7 +42,31 @@ session spending an afternoon on them again.
|
|||||||
below), **E3 (the Kotlin/Java shell over a JNI bridge into Rust, both
|
below), **E3 (the Kotlin/Java shell over a JNI bridge into Rust, both
|
||||||
pass conditions proved on the emulator — see its own box)**, I0a, I0b
|
pass conditions proved on the emulator — see its own box)**, I0a, I0b
|
||||||
(iris builds on a pinned nightly and runs), I1 (parley + glyph atlas),
|
(iris builds on a pinned nightly and runs), I1 (parley + glyph atlas),
|
||||||
I2 (iris on android-view), I3 (`iris::widget::List`).
|
I2 (iris on android-view), I3 (`iris::widget::List`), I4 (host half).
|
||||||
|
- **I5 — the transcript screen in iris: partial, 2026-09-05 (ticked `[~]`
|
||||||
|
in its own box, not `[x]`).** `iris/transcript-ui/` builds a real
|
||||||
|
transcript screen — markdown-folded rows in `iris::widget::List`,
|
||||||
|
cross-row selection, a growing composer, tool-row expand-hold — on top
|
||||||
|
of a new, genuinely useful iris capability this box added:
|
||||||
|
**`SpanStyle`**, per-range text styling (`core/src/primitive/text.rs`),
|
||||||
|
which is what lets one wrapped, selectable `TextEdit` carry a heading,
|
||||||
|
bold, italic, inline code and a link all inside the same paragraph —
|
||||||
|
exactly the inline-rich-text ceiling E2 found Masonry structurally
|
||||||
|
unable to cross. Screenshotted via `run-headless.sh` (real inline
|
||||||
|
styling visible, not just block-level). 9 new tests, all passing;
|
||||||
|
`cargo build/clippy/fmt/test --workspace` and `cargo ndk` (both `iris`
|
||||||
|
and `transcript-ui`) all clean. **What did not happen this pass**: any
|
||||||
|
Android integration for this specific screen (no cdylib/Gradle shell
|
||||||
|
exists for it yet, unlike `tabs-ui`'s `iris-android-app`), and therefore
|
||||||
|
the emulator-side pass condition (`transcript-bench.sh` against the
|
||||||
|
Compose baseline, `ui-trace` tap-by-name on a row) — `emu list` showed
|
||||||
|
the one emulator here held by another session, but the real blocker is
|
||||||
|
that the integration work itself is unbuilt, not the emulator being
|
||||||
|
busy. Touch-drag panning over a row's own rendered text is also not yet
|
||||||
|
reachable, for a specific, diagnosed reason (it competes with this box's
|
||||||
|
own row-level drag-select for the same gesture) rather than an absent
|
||||||
|
primitive. Full accounting, every citation, and the dated
|
||||||
|
IRIS_TODO.md items are in I5's own box below.
|
||||||
- **E3 done, 2026-09-05, and unlike E1/E2 it is committed to this repo**
|
- **E3 done, 2026-09-05, and unlike E1/E2 it is committed to this repo**
|
||||||
(`android-shell/` — a JNI-bridge crate on `client-core` — plus a new
|
(`android-shell/` — a JNI-bridge crate on `client-core` — plus a new
|
||||||
Gradle module `app/shellApp/`, left deliberately separate from
|
Gradle module `app/shellApp/`, left deliberately separate from
|
||||||
@@ -591,6 +615,25 @@ light" has a knob inside the same stack.
|
|||||||
carries the screen within the Compose baseline, it is the app's
|
carries the screen within the Compose baseline, it is the app's
|
||||||
framework and Masonry was the calibration. If it does not, the
|
framework and Masonry was the calibration. If it does not, the
|
||||||
measurement says which parts of Masonry to adopt underneath it.
|
measurement says which parts of Masonry to adopt underneath it.
|
||||||
|
|
||||||
|
**Not decidable yet, 2026-09-05 — what's missing, named rather than
|
||||||
|
guessed at.** Neither side of this comparison has a render number:
|
||||||
|
E2 found Masonry's own scroll gesture path absent on Android
|
||||||
|
entirely (its box, "measurable frames"), and I5 built the iris side of
|
||||||
|
the screen (`iris/transcript-ui/`) but not the Android integration
|
||||||
|
around it — no cdylib/Gradle shell exists for this screen yet (unlike
|
||||||
|
`tabs-ui`'s `iris-android-app`, I2), so there is nothing installed on a
|
||||||
|
device for `transcript-bench.sh` to measure against the Compose
|
||||||
|
baseline. What would close this: build that integration (real
|
||||||
|
`client-core` networking against `app/ui-sandbox.sh --delay`, a cdylib
|
||||||
|
+ Gradle module the way I2 did for `tabs-ui`), then run
|
||||||
|
`transcript-bench.sh`'s gesture on both. Until then, the decision rests
|
||||||
|
on the structural findings both sides *did* produce: Masonry cannot do
|
||||||
|
cross-row selection or per-span inline rich text at all today (E2's
|
||||||
|
`grep -rln`, zero hits, cited in its own box), and iris now does both
|
||||||
|
(I5's `SpanStyle` and `selection.rs`) as well as programmatic
|
||||||
|
touch-scroll (I3) — three structural points in iris's favour with no
|
||||||
|
opposing measurement yet on either side.
|
||||||
4. Then the shell (E3), the desktop window (E4) and the packaging (E5),
|
4. Then the shell (E3), the desktop window (E4) and the packaging (E5),
|
||||||
which do not depend on the choice.
|
which do not depend on the choice.
|
||||||
|
|
||||||
@@ -1924,9 +1967,220 @@ silently on real hardware.
|
|||||||
immediately after the first (attach, detach, attach again) and
|
immediately after the first (attach, detach, attach again) and
|
||||||
confirm the process is still alive afterward -- the detach-abort
|
confirm the process is still alive afterward -- the detach-abort
|
||||||
this box's mitigation exists for.
|
this box's mitigation exists for.
|
||||||
- [ ] **I5 — the transcript screen in iris.** E2's pass conditions, all
|
- [~] **I5 — the transcript screen in iris (2026-09-05). The widget-tree
|
||||||
seven behaviours, against the sandbox with `--delay`. This is the
|
half is built, tested and screenshotted; the emulator half (real
|
||||||
point the decision in the recommendation is made at.
|
device numbers against the Compose baseline) is not -- ticked
|
||||||
|
partial rather than done, see "What remains" at the end of this box.**
|
||||||
|
|
||||||
|
**Where it lives.** `iris/transcript-ui/` (new workspace member,
|
||||||
|
`[lib]`), the same shape as `iris/tabs-ui`: generic over `Rsc:
|
||||||
|
HasEvents` + `Rsc::State: FocusHost` so the same `build()` can run
|
||||||
|
under winit (`transcript-ui/examples/transcript.rs`) or an
|
||||||
|
android-view cdylib later. Depends on `client-core`/`event-model` by
|
||||||
|
path (real code, matching E2's precedent) and `pulldown-cmark`
|
||||||
|
(0.13.4, current stable). Four modules: `markdown.rs` (CommonMark ->
|
||||||
|
plain text + `Vec<SpanStyle>`), `row.rs` (one `iris::widget::List`
|
||||||
|
row per folded `TranscriptRow`), `selection.rs` (cross-row
|
||||||
|
selection), `composer.rs` (the growing input field). `lib.rs`'s own
|
||||||
|
module doc has the screen's shape and the one gap it documents up
|
||||||
|
front (below).
|
||||||
|
|
||||||
|
**New iris API, added in this box and recorded in `IRIS.md`:
|
||||||
|
`SpanStyle`, per-range text styling.** This is the actual answer to
|
||||||
|
RUST.md's E2 finding against Masonry ("rich inline text -- block-level
|
||||||
|
yes, inline no, and both for the same reason":
|
||||||
|
`masonry/src/widgets/text_area.rs:43-44`'s `TextArea::edit_styles()`
|
||||||
|
returns one `StyleSet` for the whole editor, with `// TODO:
|
||||||
|
RichTextInput` beside it). `core/src/primitive/text.rs`'s
|
||||||
|
`TextBuffer` gained `spans: Vec<SpanStyle>` and `set_spans`;
|
||||||
|
`SpanStyle{range, color, family, font_size, bold, italic,
|
||||||
|
underline}` pushes into parley's `RangedBuilder` via `.push(property,
|
||||||
|
range)` instead of only `.push_default(...)`, so one `TextEdit` can
|
||||||
|
carry a heading's bigger bold font, an inline-code span's monospace
|
||||||
|
colour, a link's colour+underline and an ordinary paragraph's base
|
||||||
|
style all in the *same* wrapped, selectable buffer.
|
||||||
|
`core/src/render/atlas.rs`'s `PlacedGlyph` gained a `color: UiColor`
|
||||||
|
field (read from parley's own per-run `Style::brush`,
|
||||||
|
`core/src/primitive/text.rs`'s `TextData::place`) and
|
||||||
|
`core/src/ui/painter.rs`'s `glyphs()` now colours each glyph from
|
||||||
|
that field instead of one colour for the whole `RenderedText` --
|
||||||
|
the change that actually makes a span's colour reach the screen.
|
||||||
|
**Real bug found and fixed while wiring this in**: `TextBuilder`'s
|
||||||
|
`.spans(...)` was only threaded through `TextOutput::run` (the
|
||||||
|
read-only `Text` widget), not the sibling `TextEditOutput::run` (the
|
||||||
|
`TextEdit` every transcript row actually uses) -- a "rule that
|
||||||
|
governs a set belongs to the set, not one member" miss, per
|
||||||
|
CODE_RULES.md; found because `run-headless.sh`'s screenshot showed
|
||||||
|
*no* styling at all despite `markdown.rs`'s own unit tests passing
|
||||||
|
(they only check the string/range logic, not the render path -- see
|
||||||
|
`iris/src/widget/text/build.rs`'s `TextEditOutput::run`, now fixed).
|
||||||
|
|
||||||
|
**The seven behaviours, each shown or given a sourced reason, same
|
||||||
|
structure as E2's own accounting:**
|
||||||
|
|
||||||
|
1. **Selection spanning rows -- shown, with a scoped shortcut
|
||||||
|
recorded rather than hidden.** `selection.rs`'s `Selection`
|
||||||
|
coordinates each visible row's own `TextEditCtx::select`/
|
||||||
|
`select_all`/`deselect` (already built for one field, I2) from a
|
||||||
|
single drag that crosses row boundaries: rows between the anchor
|
||||||
|
and the pointer get `select_all()`, the row under the pointer gets
|
||||||
|
a true partial selection from whichever edge faces the anchor,
|
||||||
|
and `selected_text()` concatenates the result in row order. The
|
||||||
|
one shortcut: the *anchor* row is selected in full once the drag
|
||||||
|
leaves it, rather than "from the click point to its far edge",
|
||||||
|
because that needs the row's own laid-out size and
|
||||||
|
`TextEditCtx`'s `layout()` helper is private
|
||||||
|
(`iris/src/widget/text/edit.rs`) -- see `selection.rs`'s module
|
||||||
|
doc. Pure range-membership logic (`in_range`, mirroring
|
||||||
|
`begin`/`extend`'s row-selection arithmetic) is unit-tested
|
||||||
|
without any render harness; the widget-level wiring is not
|
||||||
|
independently screenshotted this pass (would need a synthetic
|
||||||
|
drag injected into the winit example -- not attempted, time).
|
||||||
|
2. **Rich inline text -- shown, genuinely inline this time.**
|
||||||
|
`markdown::render_markdown` folds one row's whole markdown (not
|
||||||
|
one block at a time) into one string plus spans, so a heading, a
|
||||||
|
**bold** word, *italic* text, `inline code`, and a
|
||||||
|
[link](url) inside the same paragraph render in one `TextEdit`
|
||||||
|
that still wraps and selects as a single buffer --
|
||||||
|
screenshotted, see below. Deliberately not attempted, each
|
||||||
|
recorded at the point it would have gone in `markdown.rs`'s own
|
||||||
|
doc: a background chip behind inline code (needs glyph-run
|
||||||
|
geometry `TextEdit`-internal and not exposed, the same primitive
|
||||||
|
`TextEdit::draw`'s selection highlight uses,
|
||||||
|
`iris/src/widget/text/edit.rs:99`), a tappable link (same missing
|
||||||
|
primitive), a real table layout, and per-token syntax colour
|
||||||
|
inside a fence.
|
||||||
|
3. **Bottom-anchored virtualised list, hold-the-edge on expand --
|
||||||
|
shown**, reusing I3's `List` unmodified. A `TranscriptRow::Tools`
|
||||||
|
row collapses to "N tool calls" and expands to every call's own
|
||||||
|
tool/input/output on tap; `row.rs`'s click handler calls
|
||||||
|
`List::extent(key)` to convert the tap's row-local position into
|
||||||
|
the viewport-relative position `List::note_tap` wants, exactly
|
||||||
|
the two-step contract `list.rs`'s module doc describes for
|
||||||
|
`holdTopEdge`. Not independently screenshotted mid-expand this
|
||||||
|
pass (no input-injection into the desktop example was built) --
|
||||||
|
the mechanism is the same one I3 already benchmarked
|
||||||
|
(`expand-hold`, flat at 0.10-0.11ms across N), applied to real
|
||||||
|
content instead of a synthetic row.
|
||||||
|
4. **The soft keyboard -- inherited from I2, not re-investigated.**
|
||||||
|
The composer (`composer.rs`) is an ordinary `TextEdit` with the
|
||||||
|
same `InputConnection` bridge I2 built and measured (Gboard
|
||||||
|
suggestions over real buffer content); nothing new to add here,
|
||||||
|
and no Android shell exists yet for this screen specifically to
|
||||||
|
re-verify it against (see "What remains").
|
||||||
|
5. **Platform integration -- out of scope by design**, same as E2:
|
||||||
|
E3's list, not this box's.
|
||||||
|
6. **Accessibility names -- shown for the composer, not yet for
|
||||||
|
rows.** The composer field carries `.label("Message")` (I4). Rows
|
||||||
|
do not yet carry per-row labels (a row's own text *is* its
|
||||||
|
accessible content via `TextEdit`'s `access_role`, I4, but
|
||||||
|
nothing calls `.label()` on it, so `Widgets::named()` does not
|
||||||
|
include it) -- a small, real gap, recorded as an IRIS_TODO.md
|
||||||
|
item rather than silently left, since AGENTS.md's bench scripts
|
||||||
|
depend on exactly this for driving a screen by name.
|
||||||
|
7. **Measurable frames / the render-number pass condition -- not
|
||||||
|
attempted, and unlike E2 the reason is not an absent gesture
|
||||||
|
path.** `List` demonstrably scrolls (I3's flat draws/moves,
|
||||||
|
programmatic `scroll()`) and mouse-wheel scrolling is wired here
|
||||||
|
(`lib.rs`'s `CursorSense::Scroll` on `list`). What is *not*
|
||||||
|
reachable yet is a **touch-drag pan starting on a row's own
|
||||||
|
text**: `row.rs` registers `CursorSense::click_or_drag()` on each
|
||||||
|
row's `TextEdit` for selection, and `TextEdit::draw` calls
|
||||||
|
`painter.child_layer()` (`iris/src/widget/text/edit.rs:87`), so
|
||||||
|
`core/src/sense.rs`'s `run_sensors` (which stops at the first
|
||||||
|
layer, checked innermost-first, that consumed the gesture) gives
|
||||||
|
that row first refusal on *every* frame it is pressed, not just
|
||||||
|
the frame the press started -- a row's drag-select wins the same
|
||||||
|
gesture a list-level pan would want. `lib.rs`'s own module doc
|
||||||
|
states this precisely, with the fix named (a press distance/time
|
||||||
|
arbiter deciding pan vs. select before either commits, or gating
|
||||||
|
text-drag-selection behind a long-press). This is a genuine,
|
||||||
|
diagnosed architecture gap this box's *own* two features created
|
||||||
|
by both wanting the same gesture -- not a missing primitive the
|
||||||
|
way Masonry's absent `on_pointer_event` drag handling was.
|
||||||
|
|
||||||
|
**Verification, exact commands and results (2026-09-05, this VM):**
|
||||||
|
|
||||||
|
- `cargo fmt --all -- --check`: clean.
|
||||||
|
- `cargo build --workspace --all-targets`: clean, all six workspace
|
||||||
|
members (`iris`, `iris-core`, `iris-macro`, `tabs-ui`,
|
||||||
|
`transcript-ui`, plus the excluded `android-app`).
|
||||||
|
- `cargo clippy --all-targets` and `cargo clippy -p transcript-ui
|
||||||
|
--all-targets`: zero warnings.
|
||||||
|
- `cargo test --workspace`: 28 tests in `iris`/`iris-core` (all
|
||||||
|
pre-existing, unaffected) + **9 new in `transcript-ui`** -- 5 pure
|
||||||
|
markdown tests (`bold_and_italic_produce_spans_over_the_right_range`,
|
||||||
|
`heading_gets_a_bigger_font_size_span`,
|
||||||
|
`link_is_styled_and_keeps_its_visible_text`,
|
||||||
|
`fenced_code_block_is_monospaced`, a plain-text baseline) and 4
|
||||||
|
selection tests (forward/backward/single-row range arithmetic,
|
||||||
|
plus `unregister_forgets_the_row_and_clears_a_matching_anchor`
|
||||||
|
against a real minimal `TextEdit` in the arena, no window needed --
|
||||||
|
same harness style as `list.rs`'s own tests).
|
||||||
|
- `cargo ndk -t x86_64 -P 26 build -p transcript-ui` and `... clippy
|
||||||
|
-p transcript-ui --lib`: clean (`--lib` only -- the example uses
|
||||||
|
`iris::default`, winit-only by design, same as `iris/examples/
|
||||||
|
tabs`'s own example never having an Android build of itself; the
|
||||||
|
Android-facing entry point is a separate cdylib, not built this
|
||||||
|
pass, see below). `cargo ndk ... build -p iris` / `clippy -p iris`
|
||||||
|
also re-checked clean, since this box touched `iris-core`'s text
|
||||||
|
pipeline.
|
||||||
|
- `run-headless.sh transcript --shot ... -- -p transcript-ui`:
|
||||||
|
renders. Cropped for legibility (this VM has no image viewer --
|
||||||
|
see I3's own note on the same limitation and the throwaway crop
|
||||||
|
tool used here, not committed): a full conversation with a
|
||||||
|
**bold** word, *italic* text, `inline code` in its own colour, a
|
||||||
|
`# Sure` heading rendered visibly larger and bold, a coloured link,
|
||||||
|
a monospaced fenced code block, a collapsed "▸ 3 tool calls" row,
|
||||||
|
and the composer bar at the bottom -- every one of E2's markdown
|
||||||
|
screenshot's features, now inline within single paragraphs rather
|
||||||
|
than block-per-widget. Screenshots at `/tmp/iris_i5_transcript2.png`
|
||||||
|
(full) and crops there, not committed per the standing rule against
|
||||||
|
screenshots of real content leaving this repo -- these are
|
||||||
|
synthetic rows, but the rule is kept uniform regardless.
|
||||||
|
|
||||||
|
**What remains, named rather than silently dropped (also in
|
||||||
|
IRIS_TODO.md, dated 2026-09-05):**
|
||||||
|
|
||||||
|
- **The emulator half of the pass condition was not attempted.**
|
||||||
|
`emu list` shows `emulator-5554` (AVD `ai-app`, a different
|
||||||
|
checkout) held by another session during this pass, but even with
|
||||||
|
a free emulator this needs real Android integration that does not
|
||||||
|
exist yet for this screen: a cdylib + Gradle shell the way
|
||||||
|
`iris-android-app` wraps `tabs-ui` (I2), real
|
||||||
|
`client-core::ApiClient`/`event_stream::follow_session_events`
|
||||||
|
wiring against `app/ui-sandbox.sh` with `--delay` (this crate
|
||||||
|
deliberately does not fetch anything itself, see `lib.rs`'s doc),
|
||||||
|
and then `transcript-bench.sh`'s gesture compared against the
|
||||||
|
Compose baseline. That is real, multi-part follow-on work in its
|
||||||
|
own right -- closer in size to E2/E3 than to "run one more
|
||||||
|
script" -- not something this pass's remaining time could
|
||||||
|
responsibly rush and still report honestly.
|
||||||
|
- **Touch-drag panning over a row's own text** -- behaviour 7 above.
|
||||||
|
- **Row-level accessibility names** -- behaviour 6 above.
|
||||||
|
- **A tappable link and a code-span background chip** -- behaviour 2.
|
||||||
|
- **`Selection`'s anchor-row shortcut** -- behaviour 1.
|
||||||
|
- **No syntax highlighting inside a fenced code block** -- `markdown.rs`
|
||||||
|
notes `client_core::highlight` exists and could feed this.
|
||||||
|
- **`row.rs`'s tool-row expand and `selection.rs`'s cross-row drag
|
||||||
|
are not independently screenshotted/driven** -- covered by reading
|
||||||
|
and by the primitives they reuse (I3's `List` tests, this box's
|
||||||
|
own unit tests), not by a dedicated repro this pass.
|
||||||
|
|
||||||
|
**Net for the recommendation.** Item 3 ("decide when the transcript
|
||||||
|
screen exists in both, from the measurements") still cannot be
|
||||||
|
decided by a number -- E2 could not produce one for Masonry, and I5
|
||||||
|
has not yet produced one for iris either, for an unrelated reason
|
||||||
|
(no Android harness built yet, not an absent capability). What *can*
|
||||||
|
be said structurally, updating E2's own conclusion: iris now also
|
||||||
|
demonstrates the two things E2 found Masonry structurally unable to
|
||||||
|
do at all -- cross-row selection and true per-span inline rich text
|
||||||
|
inside one wrapped, selectable buffer -- neither of which exists
|
||||||
|
anywhere in `masonry`/`masonry_core`/`xilem` today (E2's own
|
||||||
|
`grep -rln` finding). That is a second structural point in iris's
|
||||||
|
favour, alongside I2's working touch-scroll-vs-Masonry's-absent one,
|
||||||
|
still short of the render-number comparison the recommendation
|
||||||
|
ultimately wants.
|
||||||
|
|
||||||
## For the next agent
|
## For the next agent
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user