Let the markdown renderer draw tables, and stop headings shouting

Two things a reply could not render, both from the same cause: the
renderer was pinned nineteen releases back.

Tables arrived in the library at 0.30.0. On 0.26.0 a GFM table was not a
table at all -- the rows fell through as text and ran together, pipes and
all. They now draw as a table, and scroll sideways when they are wider
than the phone rather than losing the last column.

Headings took the renderer's defaults, which are the Material *display*
styles: `#` came out at 57sp and `##` at 45sp, both larger than this
app's own screen titles, so any reply with a heading in it read as
shouting. They now descend from headlineSmall to labelSmall -- six steps,
every one a different size, so two levels of nesting never draw the same.

The pin was not carelessness, which is the part worth recording: the
version comment says Maven Central was checked on 2026-08-29 and 0.26.0
was the newest stable. It still answers that, because
`search.maven.org/solrsearch` is stale for this artifact -- it knows
nothing past 0.27.0-rc02. `maven-metadata.xml` in the repository itself
lists up to 0.45.0, updated 2026-08-28. The comment now says to read the
metadata rather than the search API, since the same check will otherwise
be made the same way next time.

The colour mapping moved with the API: `markdownColor` no longer carries
`codeText`, `inlineCodeText` or `linkText`, which now ride on the
typography as the style's own colour and a `TextLinkStyles`. Same
Catppuccin values as before. `tableBackground` is set to the tint code
blocks use rather than the library's 2%-alpha default, which on this
surface was invisible.

Checked on the emulator against a reply carrying all six heading levels,
inline code, a link, and a three-column table -- including scrolling the
table to confirm the clipped last column is reachable rather than lost.
This commit is contained in:
iris committed 2026-08-29 21:54:16 -04:00
1 parent 6b4911c67b
commit 2bf90daada
2 files changed
+65 -11

No files matched your search

@@ -3,7 +3,10 @@ package com.example.aiapp
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.TextUnit
import com.mikepenz.markdown.m3.Markdown
import com.mikepenz.markdown.m3.markdownColor
import com.mikepenz.markdown.m3.markdownTypography
@@ -13,31 +16,75 @@ import com.mikepenz.markdown.m3.markdownTypography
*
* The parsing is the library's. Markdown is somebody else's specification, and a hand-written
* subset of one disagrees with it at the edges -- which is where the bug reports come from, one
* case at a time. This file's whole job is the mapping onto the app's palette.
* case at a time. This file's whole job is the mapping onto the app's palette and type scale.
*
* Colours come from the theme rather than from the renderer's defaults, so code, links and rules
* are the same Catppuccin values the rest of the app uses. Nothing here picks a colour of its own.
*/
@Composable
fun MarkdownText(text: String, modifier: Modifier = Modifier) {
val body = MaterialTheme.typography.bodyLarge
Markdown(
content = text,
colors =
markdownColor(
text = MaterialTheme.colorScheme.onSurface,
codeText = codeColor,
inlineCodeText = codeColor,
linkText = linkColor,
dividerColor = MaterialTheme.colorScheme.outlineVariant,
codeBackground = MaterialTheme.colorScheme.surfaceVariant,
inlineCodeBackground = MaterialTheme.colorScheme.surfaceVariant,
// The same tint a code block gets, rather than the renderer's 2%-alpha default:
// two adjacent tints that differ by a fiftieth read as one flat block on a phone,
// so the table would have had a border-less grid and nothing saying where it began.
tableBackground = MaterialTheme.colorScheme.surfaceVariant,
),
// Body text at the size everything else in the transcript uses, and code in a monospace
// face: a code block set in the body font stops looking like code at all.
typography =
markdownTypography(
text = MaterialTheme.typography.bodyLarge,
code = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
// A ladder that starts near the body text and descends, because these are headings
// inside a chat message rather than the top of a document. The renderer's defaults
// are the Material *display* styles -- `#` came out at 57sp and `##` at 45sp, which
// is bigger than this app's own screen titles and reads as the reply shouting.
//
// Every step is a different size, so two levels of nesting never draw the same:
// one clear step per level is the whole job of a heading.
h1 = MaterialTheme.typography.headlineSmall,
h2 = MaterialTheme.typography.titleLarge,
h3 = MaterialTheme.typography.titleMedium,
h4 = MaterialTheme.typography.titleSmall,
h5 = MaterialTheme.typography.labelMedium,
h6 = MaterialTheme.typography.labelSmall,
// Body text at the size everything else in the transcript uses.
text = body,
paragraph = body,
ordered = body,
bullet = body,
list = body,
table = body,
// Code in a monospace face: a code block set in the body font stops looking like
// code at all. The colour rides on the style here rather than in `markdownColor`,
// which stopped carrying `codeText`/`inlineCodeText`/`linkText` when the renderer
// moved them onto the typography.
code =
MaterialTheme.typography.bodyMedium.copy(
fontFamily = FontFamily.Monospace,
color = codeColor,
),
inlineCode =
body.copy(
fontFamily = FontFamily.Monospace,
// Unspecified so an inline span keeps the size of the line it sits in.
fontSize = TextUnit.Unspecified,
color = codeColor,
),
textLink =
TextLinkStyles(
style =
body
.copy(
color = linkColor,
textDecoration = TextDecoration.Underline,
)
.toSpanStyle()
),
),
modifier = modifier,
)
+10 -3
View File
@@ -16,9 +16,16 @@ zxing-embedded = "4.3.0"
# Markdown rendering for assistant replies. The widely-used Compose
# Multiplatform renderer; markdown is somebody else's specification and a
# hand-written subset disagrees with it at the edges, one bug report at a
# time. Latest stable, checked 2026-08-29 against Maven Central -- 0.27.0
# exists only as release candidates.
markdown-renderer = "0.26.0"
# time.
#
# Read the version from `maven-metadata.xml`, not from the search API:
# `search.maven.org/solrsearch` still answers 0.26.0 with 0.27.0 only as
# release candidates, which is what pinned this nineteen releases behind
# and cost us table support -- tables arrived in 0.30.0 and simply
# rendered as run-together text until then.
# https://repo1.maven.org/maven2/com/mikepenz/multiplatform-markdown-renderer-m3/maven-metadata.xml
# Latest stable there, checked 2026-08-29.
markdown-renderer = "0.45.0"
# Syntax highlighting for a tool call's input. Same reasoning as the markdown
# renderer: a language's lexical rules are somebody else's specification.
# Latest stable, checked 2026-08-29 against Maven Central.