Tool-call cards and grouping, with the state a result never arrived in
P1b (docs/RUST.md). `transcript-ui/src/tool.rs` draws a card per tool call and a group per run: collapsed, a card is its name and the one-line summary `parse_tool_input` derives; open, it is the description, the input (highlighted, on the verbatim surface) and the output, capped with a "Show all N lines". A run is one surface with a heading and a chevron bar at its foot, so it closes from either end. Three things worth knowing. **A collapsed card lays out its summary line and nothing else.** The fixture's tool outputs are tens of kilobytes and a collapsed card never builds a widget for one -- `collapsed_cards_shape_only_their_summary_ lines` opens a three-card group over 88 kB of output each and asserts the text-shape count equals the same group's over three bytes (17 either way; 17 against 20 when the discipline is deliberately broken, so the test is real). **A result arriving replaces one card.** `ToolRow::apply_calls` is the group's half of `RowBlocks::apply_delta`'s rule, and `build_row` now hands back one `TailRow` -- blocks for a message, cards for a run -- rather than two mechanisms chosen at each call site. **Every tap is a tap**: `GestureOutcome::Tapped` out of the `DragArbiter` `Selection` already owns, so a drag that started on a card scrolls the transcript instead of opening it. Three defects found by looking at the render, all recorded with their repro in docs/IRIS_TODO.md: a `Span` of padded children inside another `Span` places them a slot out of step (worked around by building the group as one span, which costs the 4dp inset); `scrollable_on(Axis::X)` on a non-editable text draws nothing, so a card's command is clipped rather than pannable; and `NotoSans-Regular` has no U+25B8/25BE/25B4 at all, so the expander mark is set in the monospace face. Screenshots: docs/bench/p1b-2026-09-06/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
b332873894
commit
7e7cbb5402
13 files changed
+356
-111
No files matched your search
+107
-6
@@ -5687,12 +5687,113 @@ device.
|
||||
correctly, and an emulator bench run with assertions live and no
|
||||
abort (`2438 frames over 147.7s, p50 27.2ms`).
|
||||
|
||||
- [ ] **P1b — tool-call cards and grouping.** `ToolRows.kt`/
|
||||
`ToolInput.kt`'s cards: a collapsed row per call with name
|
||||
and a one-line summary, expand to input and output, runs of
|
||||
calls grouped (`adopt_run` already groups in `client-core`),
|
||||
the busy/failed states, and the kilobyte outputs the fixture
|
||||
carries without laying them out while collapsed.
|
||||
- [x] **P1b — tool-call cards and grouping.** Done 2026-09-06.
|
||||
`ToolRows.kt`/`ToolInput.kt` ported to
|
||||
`iris/transcript-ui/src/tool.rs` plus two new pure modules in
|
||||
`client-core`. **Screenshots:
|
||||
`docs/bench/p1b-2026-09-06/iris-tools-collapsed.png` and
|
||||
`iris-tools-expanded.png`**, both from
|
||||
`iris/run-headless.sh transcript -- -p transcript-ui` on the
|
||||
desktop/winit backend (the emulator was not touched this pass
|
||||
-- another agent held this checkout's AVD). The expanded one
|
||||
is taken with `IRIS_TOOLS_EXPANDED=1`, which the example reads
|
||||
to call `TranscriptScreen::expand_tail_tools` -- the expanded
|
||||
appearance is otherwise unreachable on a machine with no
|
||||
display and no finger.
|
||||
**What the cards look like, against `ToolRows.kt`:**
|
||||
- *A collapsed card* -- a mark, the tool's name (14pt), the
|
||||
one-line summary `parse_tool_input` derives (12pt, Subtext
|
||||
0, one line, clipped), and the state word at the far right.
|
||||
Same as Compose, except that Compose ellipsises the summary
|
||||
and iris clips it: there is no overflow-ellipsis in
|
||||
`TextAttrs` yet (IRIS_TODO).
|
||||
- *An open card* -- the timeout at the top right, the tool's
|
||||
own description, the subject in a `Verbatim` panel with
|
||||
`client_core::highlight`'s spans, the leftover input fields
|
||||
under it, then the output. Same order as Compose.
|
||||
- *A group* -- "Called N tools" (Compose's exact wording, and
|
||||
so the name a `ui-trace` script taps), the cards on a Mantle
|
||||
surface, and a chevron bar at the foot that closes it from
|
||||
the end the reader is looking at.
|
||||
- *States* -- `client_core::transcript_fold::ToolState`, five
|
||||
of them, each with its own word and colour: nothing for
|
||||
`Succeeded`, "running" (Subtext 0), "your turn" (Peach, the
|
||||
Compose card's own wording and colour), "failed" (Red) and
|
||||
**"no result" (Yellow)**. The last two are new -- Compose
|
||||
can say neither.
|
||||
**Two things the port had to add to be able to say "it
|
||||
broke".** `event_model::Event::ToolEnd` gained `is_error`
|
||||
(`#[serde(default)]`), read from the CLI's own `tool_result`
|
||||
by one function used by both the live translator and the
|
||||
import replay (`import::tool_result_is_error`); without it a
|
||||
result was all a card had and a failed call drew exactly as
|
||||
confidently as one that worked. And `ToolState` separates
|
||||
`Succeeded`-with-empty-output from `NoResult`: both leave the
|
||||
same empty string, and only the session's own status tells
|
||||
them apart, which is why `TranscriptScreen::
|
||||
set_session_working` exists and why only the *newest* row can
|
||||
be "running" (every row behind it belongs to a turn that has
|
||||
ended).
|
||||
**Pass condition, met**:
|
||||
`collapsed_cards_shape_only_their_summary_lines`
|
||||
(`transcript-ui/src/lib.rs`) opens a group of three cards
|
||||
whose calls carry 88 kB of output each and asserts the
|
||||
text-shape count equals the same group's over three bytes.
|
||||
**17 either way.** Confirmed to be a real test, not a
|
||||
tautology, by pushing the output block into the collapsed
|
||||
branch: **17 against 20**.
|
||||
`a_result_arriving_redraws_one_card_whatever_the_run_holds` is
|
||||
the second: one `ToolEnd` costs the same number of
|
||||
`Widget::draw` calls in a twelve-call run as in a three-call
|
||||
one.
|
||||
**Three defects found on the way, all by looking at the
|
||||
render rather than at the diff:**
|
||||
1. **A `Span` of `Pad`ded children inside another `Span`
|
||||
places those children a slot out of step.** Every card drew
|
||||
its content one card's height below its own box, so the
|
||||
group read as empty bars with somebody else's summary in
|
||||
them. Bisected against
|
||||
`IRIS_TOOLS_EXPANDED=1 iris/run-headless.sh transcript`:
|
||||
removing the inner `Span` fixes it, and so does removing
|
||||
the cards' own `Pad`; the card background, the `Sized`
|
||||
wrappers and the per-card `WidgetPtr` all make no
|
||||
difference. Worked around by building the group as **one**
|
||||
`Span` (header, cards, collapse bar), which costs the 4dp
|
||||
inset Compose holds its cards off the group's edge by. The
|
||||
framework defect is still open -- IRIS_TODO has it, and it
|
||||
is not the `mov`/`reposition` one f5b8893 fixed (it
|
||||
survives that commit).
|
||||
2. **`scrollable_on(Axis::X)` on a non-editable `Text` draws
|
||||
nothing at all** -- an empty panel where the command should
|
||||
be. A markdown fence does the same thing to a `TextEdit`
|
||||
and is fine. So a card's verbatim block is `masked()` and
|
||||
clips rather than panning; when this is fixed the pan
|
||||
belongs there too, because the long command is the one
|
||||
being read closely.
|
||||
3. **`NotoSans-Regular.ttf` has no U+25B8/25BE/25B4** (read
|
||||
out of the bundled `cmap`s) while `NotoSansMono-Regular`
|
||||
does, so the expander mark is set in the monospace face at
|
||||
the one place the character is written. The old
|
||||
`build_tools` summary drew that codepoint in the sans face,
|
||||
which was a missing glyph nobody had looked closely enough
|
||||
to see.
|
||||
**Checks**: `cargo fmt --all --check` clean in both
|
||||
workspaces; `cargo clippy -p iris -p iris-core -p
|
||||
transcript-ui -p desktop-app -p tabs-ui --all-targets` and
|
||||
`cargo clippy --all-targets` in `client-core`/`server`/
|
||||
`event-model` warning-free; tests 86 (iris) + 13 (iris-core) +
|
||||
36 (transcript-ui, +5) + 137 (client-core, +11) + 160
|
||||
(server, +1).
|
||||
**Not done**: nothing on the emulator or the phone (the AVD
|
||||
was another agent's this pass, so no frame times were taken);
|
||||
a card's text is not selectable, unlike Compose's, since
|
||||
`Selection` is keyed per markdown block and a card has none
|
||||
(IRIS_TODO); no per-corner radius, so the "connected stack"
|
||||
shape `connectedShape` draws is a 2dp gap instead;
|
||||
`AskUserQuestionBody`/`PermissionAsk`'s answer buttons are not
|
||||
ported -- an unanswered ask forces its card open and says
|
||||
"your turn", but there is nothing to press yet, which is P1d's
|
||||
modal/controls work.
|
||||
- [ ] **P1c — history paging and jump-to-latest.** Wire
|
||||
`client-core::transcript_source` into `transcript-ui`:
|
||||
the opening page, paging back on scroll with the cushion
|
||||
|
||||
Reference in new issue
Block a user