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
@@ -16,7 +16,6 @@ import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.mikepenz.markdown.compose.LocalMarkdownColors
|
||||
import com.mikepenz.markdown.compose.LocalMarkdownComponents
|
||||
import com.mikepenz.markdown.compose.LocalMarkdownPadding
|
||||
import com.mikepenz.markdown.compose.LocalMarkdownTypography
|
||||
@@ -98,9 +97,20 @@ val BLOCK_SPACING: Dp = 6.dp
|
||||
* or heading is a [LinkedText], a table is [LinkedTableRow]s, and a nested list comes back here
|
||||
* through [MarkdownList]. Only the list item is drawn directly, because a list item is the one
|
||||
* piece the renderer has no element for.
|
||||
*
|
||||
* [continuesList] and [listContinues] are for a list cut across the segments of a live reply (see
|
||||
* `LiveParse`): an item that is the first or last of its own parse but not of the list the reader
|
||||
* sees keeps an inner item's padding, so nothing moves when the seam between segments does.
|
||||
*/
|
||||
@Composable
|
||||
fun MarkdownPiece(parse: State, text: String, piece: Piece, modifier: Modifier = Modifier) {
|
||||
fun MarkdownPiece(
|
||||
parse: State,
|
||||
text: String,
|
||||
piece: Piece,
|
||||
modifier: Modifier = Modifier,
|
||||
continuesList: Boolean = false,
|
||||
listContinues: Boolean = false,
|
||||
) {
|
||||
if (parse !is State.Success) {
|
||||
// The parser threw. Nothing else in the app has seen this happen; if it does, the words
|
||||
// are still worth more than a blank.
|
||||
@@ -124,8 +134,8 @@ fun MarkdownPiece(parse: State, text: String, piece: Piece, modifier: Modifier =
|
||||
list = node,
|
||||
item = items[piece.item],
|
||||
index = piece.item,
|
||||
first = piece.item == 0,
|
||||
last = piece.item == items.lastIndex,
|
||||
first = piece.item == 0 && !continuesList,
|
||||
last = piece.item == items.lastIndex && !listContinues,
|
||||
depth = 0,
|
||||
modifier = modifier,
|
||||
)
|
||||
@@ -195,7 +205,7 @@ private fun MarkdownListItem(
|
||||
} else if (list.type == MarkdownElementTypes.ORDERED_LIST) {
|
||||
Marker("${list.startNumber(content) + index}. ", typography.ordered)
|
||||
} else {
|
||||
Marker("• ", typography.bullet)
|
||||
Marker(BULLETS[depth % BULLETS.size], typography.bullet)
|
||||
}
|
||||
Column {
|
||||
item.children.forEach { child ->
|
||||
@@ -212,16 +222,24 @@ private fun MarkdownListItem(
|
||||
}
|
||||
}
|
||||
|
||||
/** The renderer's text colour on the marker; its styles carry none of their own. */
|
||||
/** The marker in [listMarkerColor]; the renderer's styles carry no colour of their own. */
|
||||
@Composable
|
||||
private fun Marker(text: String, style: TextStyle) {
|
||||
BasicText(text, style = style.copy(color = LocalMarkdownColors.current.text))
|
||||
BasicText(text, style = style.copy(color = listMarkerColor))
|
||||
}
|
||||
|
||||
private val ASTNode.isList: Boolean
|
||||
/**
|
||||
* The bullet at each depth, cycling past the third: a disc, a ring, a square -- the ladder a
|
||||
* browser draws, so a nested list is told from its parent by the glyph as well as by the indent.
|
||||
* Checked on the emulator's system fonts, which is what makes them safe to rely on; a glyph the
|
||||
* platform lacks draws as a box, and that check is the price of adding one here.
|
||||
*/
|
||||
private val BULLETS = listOf("• ", "◦ ", "▪ ")
|
||||
|
||||
internal val ASTNode.isList: Boolean
|
||||
get() = type == MarkdownElementTypes.ORDERED_LIST || type == MarkdownElementTypes.UNORDERED_LIST
|
||||
|
||||
private fun ASTNode.listItems(): List<ASTNode> = children.filter {
|
||||
internal fun ASTNode.listItems(): List<ASTNode> = children.filter {
|
||||
it.type == MarkdownElementTypes.LIST_ITEM
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user