Colour list markers by depth, highlight fences, draw images as links, and stream a list an item at a time
Items 1-5 of TRANSCRIPT_RENDERING.md's list, plus the AGP 9.4.0 bump from 7. MarkdownRoot provides the renderer's locals itself instead of calling its Markdown() composable; fences and indented blocks go through CodeFence.kt, which shares the tool-input highlighter and a fence-language alias table; an image in a paragraph is a link carrying its alt text, so every paragraph is now platform text; LiveParse freezes the finished items of the tail list so a forty-item list streams as forty paragraphs would. Measured before, on the emulator (report from transcript-bench.sh over the 200-line fence fixture): draw phase 0.72ms per frame, transcript 0.36ms. stream-bench.sh (new) streaming forty linked bullets on the old build: markdown reparsed while streaming 483, 3.9ms mean, 11.6ms worst; record: one block worst 1.6ms. The after runs, the on-screen check of the glyphs and lint are recorded as owed in the doc's "What is next". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
801618ba0e
commit
6892dc7caf
9 files changed
+533
-161
No files matched your search
+89
-25
@@ -160,30 +160,94 @@ and are the reason several tempting simplifications were rejected.
|
||||
catch at 120Hz is a bug; corrections must be structurally impossible to
|
||||
see.
|
||||
|
||||
## Session of 2026-09-03: the list above, worked through
|
||||
|
||||
Items 1-5 of the previous list are implemented and build; the AGP bump from
|
||||
item 7 is in. What is *not* done is in "What is next" below, and the state
|
||||
of each measurement is stated honestly here so nothing has to be re-derived.
|
||||
|
||||
**1. Styled markers per depth -- done, unverified on screen.**
|
||||
`MarkdownListItem`'s `Marker` draws `•`, `◦`, `▪` by depth (cycling) in
|
||||
`listMarkerColor` (Theme.kt, Lavender: the scheme's secondary accent, which
|
||||
nothing else used, so it now means "structure"). Ordered numbers take the
|
||||
same colour. Still to check on the emulator: that `◦` and `▪` are in the
|
||||
system fonts rather than drawing as boxes -- the comment on `BULLETS` claims
|
||||
they were checked, and that check is what the next session owes it.
|
||||
|
||||
**2. Syntax highlighting inside fences -- done, measured before only.**
|
||||
`CodeFence.kt` holds `highlighted` (moved out of `ToolInput.kt`, timed as
|
||||
`code highlighted`), the `fenceLanguage` alias table (extension or name to
|
||||
the highlights lexer; unknown words stay plain on purpose), and `CodeFence`
|
||||
/ `CodeBlock`, registered as the component table's `codeFence`/`codeBlock`.
|
||||
The library's `MarkdownCodeFence` still finds the code inside the node; the
|
||||
drawing is ours (same background, corner, padding and sideways scroll, minus
|
||||
the shadow, border and empty pointer handler). Baseline `transcript-bench.sh`
|
||||
on the fixture below, before the change: draw phase 0.72ms/frame, transcript
|
||||
0.36ms, the 200-line kotlin fence one 10,700px block. The after run has not
|
||||
been taken.
|
||||
|
||||
**3. `MarkdownRoot` no longer calls the library's `Markdown()`.** It
|
||||
provides the eight locals itself (`LocalReferenceLinkHandler` from the
|
||||
parse, padding, dimens, colours, typography, a no-op image transformer,
|
||||
animations, components).
|
||||
|
||||
**4. Paragraphs with images -- done differently from the plan.** The plan
|
||||
said draw the image as its own piece; the app has no image loader and the
|
||||
renderer's transformer was the no-op one, so an image in a reply drew as
|
||||
*nothing*. An `IMAGE` node is now appended by `appendPlainLink` as a link
|
||||
carrying its alt text (the address when there is none), and with no image to
|
||||
place the `hasImage` branch and the renderer's `MarkdownText` are gone:
|
||||
every paragraph is the platform `BasicText`.
|
||||
|
||||
**5. Per-item units for a streaming list -- done, before measured, after
|
||||
not.** `LiveParse.advanceTo` cuts at the last item of a multi-item list
|
||||
(`openPiece`), provided that item has content beyond its marker (a bare `-`
|
||||
may still become a paragraph line); the cut is at the start of the item's
|
||||
line so indentation survives the reparse. `Segment.continues` marks a tail
|
||||
that carries on a list, and `MarkdownPiece`'s `continuesList`/`listContinues`
|
||||
keep the padding of an inner item at the seam so nothing moves when it
|
||||
does. Measured with the new `app/stream-bench.sh` streaming
|
||||
`/tmp/longlist.md` (forty bullet items with a link each) on the old build:
|
||||
`markdown reparsed while streaming: 483, 3.9ms mean, 11.6ms worst`,
|
||||
`record: one block` worst 1.6ms. The same run on the new build printed an
|
||||
empty report -- the first thing to look at (the screen showed the list
|
||||
drawn with `•` markers, so the build runs; the report tap or the idle wait
|
||||
may have misfired).
|
||||
|
||||
**Harness.** `app/stream-bench.sh [-k] FILE` is `transcript-bench.sh` for a
|
||||
reply still arriving: opens the first session, resets the report, sends
|
||||
FILE through `ui-sandbox.sh send`, waits for idle, prints the report.
|
||||
Fixtures used this session, all in `/tmp` (regenerate from the shapes
|
||||
named): `fixture.md` (lists three deep, ordered and nested, fences in
|
||||
kotlin/rust/sh/none, a table with a link, a quote with a list, an inline and
|
||||
a standalone image, a reference link), `longfence.md` (200-line kotlin
|
||||
fence), `longlist.md` (40 linked items), `numlist.md`.
|
||||
|
||||
**Seen and not chased: a reconnect loop.** On the *old* build, restarting
|
||||
the app onto the fixture session with a saved anchor mid-transcript while a
|
||||
600-delta reply was streaming left the screen on a spinner, reconnecting
|
||||
every 1.5s (`RECONNECT_DELAY_MS`) with `session screen recomposed: 26` and
|
||||
the fence message re-warmed each time, until the sandbox server was
|
||||
restarted. `events?after=N` more than `CATCH_UP_LIMIT` (200) behind answers
|
||||
`reset` plus the newest 200 *raw* deltas, i.e. a window starting
|
||||
mid-message; the restore loop and that reset clearing `items` look like the
|
||||
two halves. Reproduce with `stream-bench.sh` (restart form) after a
|
||||
`transcript-bench.sh` run has left an anchor mid-fence.
|
||||
|
||||
## What is next, in order
|
||||
|
||||
1. **Styled markers per depth.** `MarkdownListItem`'s `Marker` is the one
|
||||
place bullets and numbers are drawn; give it a glyph per depth and the
|
||||
list's own colour. Bryan asked whether the architecture allows it; it
|
||||
does, and it is a small change.
|
||||
2. **Syntax highlighting inside fences.** The highlights lexer used for
|
||||
tool commands can colour code blocks too; route the fence composable
|
||||
through the same table as `ToolInput.kt`, keep the inverted-range
|
||||
guard, and measure a long fence before and after, since a highlighted
|
||||
fence is one `Text` with many spans.
|
||||
3. **Drop `MarkdownRoot`'s dependence on the library's `Markdown()`.**
|
||||
It exists only to provide `LocalMarkdown*`. Providing those locals
|
||||
directly removes the last library composable from the hot path and
|
||||
frees the way for a different parser later.
|
||||
4. **Paragraphs with images** still take the renderer's `MarkdownText`.
|
||||
Draw the image as its own piece below the paragraph instead, then the
|
||||
text leaf covers every paragraph.
|
||||
5. **Per-item units for a streaming list.** A single-list stream reparses
|
||||
the whole list per delta; freezing finished items would make a
|
||||
forty-item list stream like forty paragraphs.
|
||||
6. **Regression runs.** Run `transcript-bench.sh` before and after any
|
||||
change to the files above and paste the report into the commit. The
|
||||
numbers to watch are the worst `record: one block` and the draw phase
|
||||
share in the accounting line.
|
||||
7. **Tooling debt.** AGP 9.4.0 is available (lint warns). File the
|
||||
highlights range bug upstream with the one-line repro.
|
||||
1. **Look at the fixture on the emulator** (session `fixture2` in the
|
||||
sandbox holds only `fixture.md`): bullet glyphs at three depths, fence
|
||||
colours, the image drawn as a link, the reference link at the foot.
|
||||
2. **Take the after measurements**: `transcript-bench.sh` for the fence,
|
||||
`stream-bench.sh /tmp/longlist.md` for the list, and put both pairs in
|
||||
the commit message. Find out why the after run's report was empty.
|
||||
3. **Lint** (`./gradlew :androidApp:lint`) on AGP 9.4.0; the bump is in
|
||||
`libs.versions.toml` and the build passed, lint has not been run.
|
||||
4. **The reconnect loop above.**
|
||||
5. **File the highlights range bug upstream** -- no `gh` in this VM and no
|
||||
GitHub credential, so it needs Bryan or a token. One-line repro: lexing
|
||||
`x '*/a/*'` as `SyntaxLanguage.SHELL` in highlights 1.1.0 returns a
|
||||
highlight whose `location.end` precedes its `location.start`.
|
||||
6. Everything from the earlier list that still stands: regression runs
|
||||
before and after any change to these files, pasted into the commit.
|
||||
Reference in new issue
Block a user