r2mopi-markdownrender
v0.1.0
Published
Markdown report rendering for the Pi coding agent: heading levels, report icons, status-symbol colors, skill links, and a gold Working spinner.
Maintainers
Readme
r2mopi-markdownrender
Pi coding agent extension + theme that ports the report rendering style used by the
R2MO Codex fork TUI (codex-rs/tui/src/markdown_render.rs, status_indicator_widget.rs)
to Pi's interactive transcript.
pi install npm:r2mopi-markdownrenderThen run /reload, select the bundled theme once via /settings → Theme → mxt,
and use /mdstyle to switch render styles.
What it does
| Element | Effect |
| --- | --- |
| H1 | bold + underline (+ green in ANSI mode) |
| H2 | per-style color; codex style adds a ## ✨ marker |
| H3 | per-style color; codex style adds 📌 after the built-in ### prefix |
| H4–H6 | italic in ANSI mode (Codex fork parity) |
| Unordered list | - 🔸 in codex style |
| Task list | stays [x] / [ ]; the codex style colors that marker in ANSI mode |
| Status lines | ✓ green, ✗/× red, !/▲ yellow, ℹ cyan, ✦/★ purple — coloured in every mode, so a failure reads red even in the icons mode |
| Skill links | skill://… and …/SKILL.md labels in mauve + underline |
| Inline code | classified in ANSI mode: command teal, path peach, skill mauve, URL blue; anything else keeps the theme's mdCode |
| Bare URLs | underlined link blue — the fork styles bare URLs too (web_links.rs) |
| Working spinner | gold ⠋⠙⠹… frames (Working row only) |
Status symbols are written at the start of a report line, with or without a list bullet:
✓ build passed
! cache refresh needs a second look
i Windows coverage missing
ℹ informational note
✦ additional remark
- ✓ works inside a bullet too
[i] the letter i is only detected in bracketed formCommands
| Command | Meaning |
| --- | --- |
| /mdstyle or /mdstyle show | print the current configuration |
| /mdstyle off | fully disable (no icons, no colors, no spinner change) |
| /mdstyle icons | content-level icons plus the status-symbol colours; no wider ANSI injection (safe default) |
| /mdstyle ansi | icons + ANSI colors (experimental, see below) |
| /mdstyle a / b / c | the three palette options from the design sheet |
| /mdstyle codex | the landed R2MO Codex fork style (Catppuccin + ✨ 📌 🔸) |
| /mdstyle headings on \| off | toggle heading icons/colors |
| /mdstyle lists on \| off | toggle list icons and task checkboxes |
| /mdstyle status on \| off | toggle status-line coloring (applies in every mode) |
| /mdstyle working on \| off | toggle the gold Working spinner |
| /mdstyle code on \| off | toggle inline-code classification and bare-URL underlining |
| /mdstyle reset | restore defaults |
Configuration persists in <agent-dir>/r2mopi-markdownrender.json
($PI_CODING_AGENT_DIR or ~/.pi/agent).
Styles
| Style | H2 | H3 | Body of status lines | Icons |
| --- | --- | --- | --- | --- |
| a | green #7ee787 | blue #67e8f9 | colored | — |
| b | blue #67e8f9 | purple #d8b4fe | symbol only | — |
| c | purple #d8b4fe | plain #d7dde8 | colored | — |
| codex | green #a6e3a1 | blue #89b4fa | symbol only | ## ✨, 📌, 🔸, [x] |
a/b/c come from work/markdown-style-options.html; codex is what actually
landed in the fork.
Task markers: why [x]/[ ] and not ☑/☐
The fork hardcodes ☑/☐
(codex-rs/tui/src/markdown_render/task_lists.rs). Those two are a trap in a terminal:
- SF Mono and Andale Mono have no glyph for either character.
☑isExtended_Pictographic, so the missing glyph falls back to Apple Color Emoji and is drawn as a double-width colour emoji.- Pi and Codex both measure the marker as one cell, so the hanging indent of a task item lands one column short — the checkbox and its text no longer line up.
Task items therefore keep the ASCII markers [x] / [ ]. ASCII always measures and draws
as three cells, in every font and every terminal, and it is exactly what Pi's own Markdown
renderer emits (pi-tui dist/components/markdown.js). In ANSI mode the marker is
coloured — green when checked, grey when not — without changing its width. The rewrite
branch stays enabled so a task line never also picks up the 🔸 list icon.
Everything else in the codex style (✨ 📌 🔸) is emoji by design and measures two
cells on both sides, so it stays aligned.
List icon: the fork's 🔸
Unordered items render as - 🔸 (SMALL ORANGE DIAMOND, U+1F538), exactly like the fork.
The same diamond also exists one size up, 🔶 (LARGE ORANGE DIAMOND, U+1F536): both are
East Asian Wide, both measure and draw as two cells, and both come from the same emoji
font, so swapping them is purely cosmetic and never moves the indent. 🔹/🔷 are the
small/large blue pair. ◆ (U+25C6) is the narrow one-cell alternative, but it comes from
the monospace font instead of the emoji font, so it reads as a small diamond again.
Theme
themes/mxt.json is a full 56-token Pi theme (built from the built-in dark theme, so
every token is present) with an mxt* variable palette:
mdHeadinggreen,mdLinkblue,mdCodethe fork's inline-code blue#63a8f8(CHATGPT_BLUE_200),mdCodeBlockteal,mdQuotegreen- Catppuccin-style
syntax*colors accent= the fork's plan-heading green#87ff5f(rgb_color((135, 255, 95)),history_cell/plans.rs) sorpiv-todo's heading reads like Updated Plan;accentis shared, so see Plan and todo colours below for what else follows itsuccess/error/warningaligned with the✓ ! ✗symbol colorstoolSuccessBg/toolErrorBg(#1e2a22/#3a1c22) tint a settled tool call green or red without recolouring its text — Pi only paints the text of a failed call red in some renderers (edit/writepreviews,[invalid arg]), so the background is what makes every failure visibleexportbackgrounds tuned for HTML export
Because only one mdHeading token exists, the theme alone cannot color heading levels
differently — that needs the ANSI layer.
Plan and todo colours
The fork's Updated Plan cell hardcodes exactly one colour and takes the rest from its
palette: the heading • 🧬 Updated Plan · n/m complete is rgb_color((135, 255, 95)) —
bright green, bold — completed steps are dim plus struck through, in-progress steps use
accent_color() (the fork's UI_ACCENT = CHATGPT_BLUE_200, a blue), and pending steps are
dim.
Pi's todo list (rpiv-todo) draws itself, so its colours come from theme tokens:
| Element | Token | Value here |
| --- | --- | --- |
| Overlay heading ● Todos (n/m), task name in the call cell | accent | bright green #87ff5f — the fork's plan-heading colour |
| Completed row ✓ and the ✓ result cell | success | #4ade80 |
| In-progress row ◐ | warning | #facc15 |
| Pending row ○, connectors ├─/└─ | dim | #6c7086 |
The heading is the one element that gets the fork's bright green, because accent is the
token rpiv-todo reads for it (todo-overlay.ts:125). The catch is that Pi has a single
accent used as a foreground colour in roughly 176 sites — the read/grep/find path
colours, every selector, the loaders, the compaction spinner — so those follow the heading
to green, and the spinner no longer keeps the theme's sky blue. The fork separates the two:
its accent stays blue and only the plan heading is green. borderAccent and toolTitle
keep sky, so composer and tool borders stay blue.
Back to a sky accent (sky heading, green on completed rows only):
// themes/mxt.json
"accent": "mxtSky",Inline code, commands, paths and links
The fork paints every backticked span one colour: the inline-raw syntax scope, falling back
to accent_color() (CHATGPT_BLUE_200 = #63a8f8). It singles out only two things —
skill links (mauve, underlined) and bare URLs (web_links.rs, link style) — so a command, a
path and a skill name are never told apart. A third case, file citations, only exists for
the fork's own codex-file-citation{…} marker protocol.
This package sets the base colour to the fork's #63a8f8 through the mdCode token, so
backticked spans look like the fork out of the box, and then lets the ANSI layer read what
each span actually holds:
| Span | Detected by | Colour |
| --- | --- | --- |
| skill | mxt-…, /mxt-…, $mxt-…, SKILL.md, skill:// | mauve #cba6f7 |
| URL | http:// or https:// | link blue #89b4fa, underlined |
| command | first word is a shell command (mxt, git, npm, node, cargo, …) | teal #94e2d5 |
| path | /usr/local, ./src/x.js, ~/.pi/agent, docs/command/task.md, a/b.rs:12, or a bare name with a file extension | peach #fab387 |
| anything else | — | the theme's mdCode |
/mdstyle code off disables the layer. Command and path detection is a heuristic on the
span text, so a code-looking word can land in the neighbouring colour; the reset button is
the theme's mdCode.
The ANSI layer and its limits
Pi renders assistant Markdown itself, so content-level icons are the only thing a
theme can never provide. Everything else here is achieved by injecting ANSI escapes
into the Markdown source before Pi parses it (via registerMarkdownTransformer).
Pi's TUI keeps width math ANSI-aware, so wrapping and padding stay correct — verified
against @earendil-works/pi-tui's Markdown component.
Known gaps (honest list):
| Item | Status |
| --- | --- |
| Compaction spinner color | colored by the theme's accent, not by this extension; the Codex fork uses a separate light blue there. Pi's setWorkingIndicator() only affects the streaming Working row. |
| Compaction/retry message text color | hardcoded in Pi (theme.fg("muted", …)), cannot be overridden |
| Table border characters (━/─ vs Pi's └─┴─┘) | Pi renders tables itself; not reachable |
| List icons vs wrapped continuation lines | the icon lives in the item text, so Pi indents continuation lines to - width (2 columns narrower) |
| Task marker glyphs vs the fork | the fork uses ☑/☐; this package keeps ASCII [x]/[ ] because those two glyphs are missing from SF Mono and Andale Mono, which forces a double-width emoji fallback (see above) |
| Copy / HTML export | injected escapes may appear in copied text and /export output. /mdstyle status off removes the symbol colours; /mdstyle off removes everything |
| Future Pi releases | if Pi ever strips ANSI from transformed Markdown, the ANSI layer degrades — the icons layer keeps working |
| Inline-code classification | the span text is the only input, so and/or can read as a path: it becomes peach instead of the theme's mdCode |
Use /mdstyle icons if any of that matters; the theme still applies.
Development
# try it without installing
pi -e ./plugins/pi/r2mopi-markdownrender
# check what would be published
npm pack --dry-run
# publish — versions are owned by the family scripts (see ../README.md)
cd ../../plugins/pi && ./npm-login.sh && ./npm-publish.sh --planLicense
MIT
