Draw an inline code chip behind the text instead of under it

The chip was the renderer's span background, and a span's background is
part of the text's own drawing: the text node paints the selection first
and the glyphs over it, so an opaque chip covered the selection and
selecting a sentence highlighted every word of it except the ones in
backticks. The previous fix let the selection show through by taking the
chip to 60% alpha, which is a compromise on both sides -- the chip is a
weaker step down from the page, and selected it reached #3C344F where the
words around it reached #776394.

There is a place that is under both, and a fenced block was already in it:
a modifier on the text rather than a style inside it. So `appendCodeChip`
takes the code span from the renderer's inline builder, keeps its style and
its space of padding either side but drops the background, and marks the
range; `LinkedText` draws those ranges in a `drawBehind`. The chip is back
to the full `rawSurface` fill (measured #11111B against a #1E1E2E page) and
a selection over it now lands at #776394, the same as the rest of the
sentence -- the fenced block's numbers exactly.

The geometry is one box per line, from the bounding boxes of the run's
first and last characters, taken as far as the line's `visibleEnd`. Not
`getPathForRange`: that is the shape of a *selection*, which runs to the
right edge of every line but the last, and a code span that wrapped left a
full-width empty chip behind on the line above -- twice in one fixture.
`visibleEnd` is the same rule the selection rectangle obeys, so the chip
stops where the selection stops instead of sticking its padding space out
past the end of a selected line.

Checked on the emulator against a fixture with chips in a heading, three
kinds of list item, a quote, a table cell and a link label, unselected and
under Select All, and a link with a chip in its label still opens. Cost,
against the same build without the change, streaming sixty paragraphs of
three chips each: measure 755ms against 776ms, record 327ms against 321ms,
transcript draw 0.22ms in both.
This commit is contained in:
iris committed 2026-09-03 23:32:17 -04:00
1 parent 9c4df43951
commit 3bb178363d
4 files changed
+119 -24

No files matched your search

+20
View File
@@ -72,6 +72,26 @@ the inline builder draws nothing for a node type it does not know (a week
of blank headings). Tables go through `LinkedTable`/`LinkedTableRow` so
cells get the same treatment.
**An inline code chip is drawn behind the text, not as a span
background.** A `SpanStyle` background is part of the text's own drawing
and the text node draws the selection *under* the glyphs, so an opaque
chip hid the selection: selecting a sentence highlighted every word of it
except the ones in backticks, and there is no way to reorder that -- the
order is the node's. `appendCodeChip` therefore takes the code span from
the renderer's builder, keeps its style and its space of padding either
side but drops the background, and marks the range; `LinkedText` draws
those ranges in a `drawBehind`, which is under both the selection and the
glyphs -- the same place a fenced block's box already was, which is why
one of those always looked right. Geometry is one box per line, from the
bounding boxes of the run's first and last characters, taken as far as the
line's `visibleEnd`: `getPathForRange` is a *selection* shape and runs to
the right edge of every line but the last, which left a full-width empty
chip behind whenever the code wrapped, and `visibleEnd` is what makes the
chip and the selection rectangle stop in the same place. Measured against
the same build without it, streaming 60 paragraphs of three chips each:
measure 755ms against 776ms, record 327ms against 321ms, transcript draw
0.22ms in both -- noise.
**Text draws on the platform directly.** A paragraph without an image
skips the renderer's `MarkdownText`, which charges every paragraph for the
possibility of inline images (placement callback, derived inline-content