Skip to content

Comments

feat(opencode): enhanced markdown renderer with tables, themes, and syntax highlighting#1

Open
ryanwyler wants to merge 4 commits intodevfrom
feature/markdown-renderer
Open

feat(opencode): enhanced markdown renderer with tables, themes, and syntax highlighting#1
ryanwyler wants to merge 4 commits intodevfrom
feature/markdown-renderer

Conversation

@ryanwyler
Copy link

@ryanwyler ryanwyler commented Jan 20, 2026

Summary

Custom markdown renderer that works alongside upstream's experimental markdown flag. When OPENCODE_EXPERIMENTAL_MARKDOWN is set, upstream's <markdown> element is used unchanged. When unset (default), a hybrid renderer provides enhanced markdown formatting with proper table rendering, tree-sitter syntax highlighting, and theme support.

Single commit, additive only - no upstream code removed or modified beyond minimal integration points.

Features

  • Box-drawing table rendering with cell word wrap and unicode-aware widths
  • Tree-sitter syntax highlighting for code blocks via native <code> element
  • Task list checkbox rendering (- [x] / - [ ])
  • Strikethrough support (~~text~~)
  • Header background panels for h1/h2
  • Diff code block coloring (+/- lines)
  • CLI markdown rendering via UI.markdown() with theme support
  • Theme loader supporting all 33 upstream themes

Files Changed

File Change Description
cli/markdown-renderer.ts New (1310 lines) Core rendering engine
cli/theme-loader.ts New (127 lines) Loads all 33 upstream themes, converts to renderer format
cli/cmd/tui/routes/session/index.tsx +113 lines TUI integration: TextPart, Prose, CodeBlock, MarkdownDiff components
cli/cmd/run.ts +12 lines CLI theme loading for non-interactive mode
cli/ui.ts +5 lines UI.markdown() now calls custom renderer with theme support

Total: 5 files, 1552 additions, 15 deletions

Architecture

The renderer integrates with upstream's existing OPENCODE_EXPERIMENTAL_MARKDOWN flag:

TextPart component
  |
  +-- OPENCODE_EXPERIMENTAL_MARKDOWN=1
  |     -> upstream's <markdown> element (untouched)
  |
  +-- OPENCODE_EXPERIMENTAL_MARKDOWN unset (default)
        -> parseMarkdownSegments() splits markdown into text + code segments
             |
             +-- Text segments -> renderMarkdownThemedStyled() -> StyledText -> <text>
             +-- Code segments -> tree-sitter via <code filetype={lang}> (native OpenTUI)

CLI mode (opencode run):

User config -> loadTheme() -> MarkdownTheme
Markdown -> renderMarkdownThemedStyled() -> TextChunks -> ANSI -> stdout

Table Rendering

Before (plain text):          After (box-drawing):
| Header | Value |            +---------+-------+
| A      | B     |            | Header  | Value |
                              +---------+-------+
                              | A       | B     |
                              +---------+-------+
image

Related Issues

@ryanwyler ryanwyler force-pushed the feature/markdown-renderer branch from 580b630 to 9cfe157 Compare January 20, 2026 03:28
@ryanwyler ryanwyler force-pushed the feature/markdown-renderer branch from 9cfe157 to 969dc48 Compare February 7, 2026 06:33
@github-actions
Copy link

github-actions bot commented Feb 7, 2026

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@ryanwyler ryanwyler changed the base branch from main to dev February 7, 2026 07:09
@ryanwyler ryanwyler force-pushed the feature/markdown-renderer branch from 927acf8 to f419b65 Compare February 7, 2026 08:08
…yntax highlighting

Custom markdown renderer that works alongside upstream's experimental markdown flag:
- When OPENCODE_EXPERIMENTAL_MARKDOWN is ON: uses upstream's <markdown> element
- When OFF (default): uses custom hybrid renderer with Prose/CodeBlock components

Features:
- Box-drawing table rendering with cell word wrap and unicode-aware widths
- Tree-sitter syntax highlighting for code blocks via native <code> element
- Task list checkbox rendering
- Strikethrough support
- Header background panels for h1/h2
- Diff code block coloring (+/- lines)
- CLI markdown rendering via UI.markdown() with theme support
- Theme loader supporting all 33 upstream themes
Adds control panel option to render markdown in user messages:
- Default: agent messages only (existing behavior)
- Toggle: Ctrl+P -> Render markdown: all messages
- Persisted via KV store (markdown_all_messages)
- Respects OPENCODE_EXPERIMENTAL_MARKDOWN flag
…bles

Inline markdown (backticks, links, strikethrough) nested inside bold or
italic markers was rendered as raw text. For example, table cells like
**HTTP `/tools/list`** would show literal backticks instead of
styled code spans.

Replace hardcoded single-nesting checks (bold-inside-italic,
italic-inside-bold) with recursive renderInlineThemedWithDefault calls
that process all inline markdown within bold/italic/bold-italic content.
Recursive calls inherit parent attrs via bitwise OR to preserve combined
styling (e.g. italic inside bold retains bold+italic).

Also update table header rendering to use renderCell with inline
markdown processing instead of dumping raw text as a single bold chunk.

Additional fixes identified via code review:
- Add truncation in renderCell when rendered content exceeds column
  width, preventing table border misalignment on narrow terminals
- Propagate defaultAttrs to inline code, links, and strikethrough so
  they inherit outer bold/italic styling when nested
The 'Render markdown: all messages' toggle was under the Session
category in the command palette. Move it to the System category
alongside other app-wide toggles (animations, diff wrapping) since
markdown rendering preference is a global setting, not session-specific.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant