Skip to content

feat(ui): render markdown in message bubbles#1

Merged
Killea merged 2 commits intoKillea:mainfrom
bertheto:feat/markdown-rendering
Feb 28, 2026
Merged

feat(ui): render markdown in message bubbles#1
Killea merged 2 commits intoKillea:mainfrom
bertheto:feat/markdown-rendering

Conversation

@bertheto
Copy link
Contributor

Problem

Agent messages are commonly written in markdown — headers, bold/italic text, tables, code blocks, numbered and bulleted lists. The current console renders raw markdown syntax, which makes longer exchanges difficult to read.

Example of what users see today vs. what they should see:

# Final ADR — Confidence & Trade-off         →  rendered H1
**Decision:** Accept the larger refactor.     →  bold text
| Item | Decision |                           →  HTML table
|------|----------|

Solution

Add a lightweight inline markdown parser written in vanilla JavaScript (zero external dependencies, no CDN required):

  • Block elements: fenced code blocks (```), tables, ATX headings (#######), horizontal rules, unordered lists, ordered lists, blockquotes
  • Inline elements: backtick code, **bold**, *italic*, ~~strikethrough~~
  • XSS-safe: all input is HTML-escaped before markdown transformations are applied
  • CSS: new rules scoped to .bubble-v2 use the existing CSS variables (--accent, --border, --text-1, etc.) and respect the dark/light theme toggle

The call site replaces the previous one-liner:

- ${esc(m.content).replace(/\n/g, '<br/>')}
+ ${renderMarkdown(m.content)}

The parser is intentionally minimal — it covers what LLM agents realistically produce. A full CommonMark implementation can replace it later without touching the call site.

Testing

Paste a message with mixed markdown into any thread via the compose box or the MCP msg_post tool:

# Heading

**Bold** and *italic* and `inline code`.

| Col A | Col B |
|-------|-------|
| foo   | bar   |

- item one
- item two

> blockquote

---

Expected: all elements render correctly in both dark and light themes.

Agent messages are written in markdown (headers, bold, tables, code
blocks, lists, blockquotes). The console was displaying raw syntax
instead of formatted output, making long exchanges hard to read.

Add a lightweight inline markdown parser (no external dependencies):
- Block elements: fenced code, tables, headings, HR, ul, ol, blockquote
- Inline elements: backtick code, bold, italic, strikethrough
- Input is HTML-escaped before transformation to prevent XSS
- CSS added for all rendered elements, respecting existing CSS variables
  and the dark/light theme toggle

The parser is intentionally minimal. It handles the subset of markdown
that LLM agents realistically produce. A full CommonMark parser can
replace it later without changing the call site (renderMarkdown).
@bertheto bertheto force-pushed the feat/markdown-rendering branch from f716c18 to 23879e4 Compare February 27, 2026 10:23
@Killea Killea merged commit 136e484 into Killea:main Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants