From ec5b08483ab5bfe0d87c6d975a0409820dbcc873 Mon Sep 17 00:00:00 2001 From: sagar-develop Date: Fri, 5 Jun 2026 13:51:03 +0530 Subject: [PATCH] fix(chart): render chart JSON from json/untagged fences, not only ```chart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Small on-device models (Gemma E2B, the default) reliably emit valid chart JSON but tag the fence ```json or leave it untagged — never ```chart — even when explicitly instructed. So the chart feature from #23 never rendered in practice; the block fell back to a code block. Now we also attempt ChartParser.parse on json/untagged fences and render a chart when it succeeds. ChartParser only returns non-null for JSON with a recognized chart type (donut/bar/progress/line + synonyms) AND valid data, so ordinary JSON code samples still render as code. The explicit ```chart path is unchanged. Verified on-device (Realme CPH2723, release): historical Gemma answers that emitted untagged chart JSON now re-render as a donut + bar charts. Co-Authored-By: Claude Opus 4.8 --- .../com/nativelm/app/ui/chat/MarkdownText.kt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sample-app/src/main/java/com/nativelm/app/ui/chat/MarkdownText.kt b/sample-app/src/main/java/com/nativelm/app/ui/chat/MarkdownText.kt index ca1d02d..65d12f0 100644 --- a/sample-app/src/main/java/com/nativelm/app/ui/chat/MarkdownText.kt +++ b/sample-app/src/main/java/com/nativelm/app/ui/chat/MarkdownText.kt @@ -293,14 +293,18 @@ private fun parseMarkdownBlocks(markdown: String): List { } i++ // skip closing fence val text = body.toString().trimEnd('\n') - if (lang == "chart") { - val spec = ChartParser.parse(text) - // Graceful fallback: a malformed chart shows its data as a code block, - // never an error and never lost content. - out += if (spec != null) MdBlock.Chart(spec) else MdBlock.Code(text) + // Prefer an explicit ```chart fence, but small on-device models routinely + // emit the chart JSON in a ```json (or untagged) block instead — so for those + // we also try to parse it. ChartParser only succeeds on JSON with a recognized + // chart "type" + valid data, so ordinary JSON code samples stay code blocks. + val spec = if (lang == "chart" || lang == "json" || lang.isEmpty()) { + ChartParser.parse(text) } else { - out += MdBlock.Code(text) + null } + // Graceful fallback: anything that isn't a valid chart shows as a code block, + // never an error and never lost content. + out += if (spec != null) MdBlock.Chart(spec) else MdBlock.Code(text) continue } line.isBlank() -> flushParagraph()