Skip to content

Conversation

@ryanwyler
Copy link
Contributor

Related Issues

Tables & Formatting:

Summary

Comprehensive markdown rendering overhaul for both CLI and TUI with proper table support, theme integration, syntax highlighting, and unified rendering architecture.

Key Features

📊 Table Rendering

  • Box-drawing borders - Unicode characters (┌─┬─┐) instead of ASCII pipes
  • Automatic column sizing - Proportional width distribution based on content
  • Word wrapping - Long content wraps within cells without breaking inline code
  • Backtick preservation - Never breaks `code` across lines
  • Inline markdown in cells - Bold, italic, code, links all work in table cells
  • Escaped pipes - Support for \| to show literal pipes (TypeScript union types: `number | undefined`)
  • Double-width character support - Proper alignment with Unicode/emoji

🎨 Theme Support

  • Unified theme system - CLI and TUI use identical colors from user's theme
  • 12+ built-in themes - opencode, dracula, nord, catppuccin, tokyo-night, etc.
  • Theme loader - Loads theme JSON files and converts to rendering format
  • Automatic detection - Reads from user config, no manual setup needed

💻 Syntax Highlighting

  • Tree-sitter integration - Uses OpenTUI's tree-sitter for code blocks
  • 10+ languages - JavaScript, TypeScript, Python, Go, Rust, Shell, etc.
  • Diff highlighting - Red/green colors for added/removed lines
  • Stable rendering - No flashing during streaming

🎯 Advanced Formatting

  • Inline markdown - **bold**, *italic*, `code`, links, strikethrough
  • Lists - Numbered and bulleted with colored markers
  • Blockquotes - Bar with proper formatting
  • Headers - Background color for h1/h2, bold for h3+
  • Horizontal rules - Clean separator lines

🏗️ Architecture

  • Unified rendering - Single source of truth (`renderMarkdownThemedStyled()`)
  • Code deduplication - CLI and TUI share rendering logic
  • TextChunk system - Universal format converted to ANSI (CLI) or StyledText (TUI)
  • Clean codebase - Removed 283 lines of dead legacy code (-18%)

🔧 Smart Behavior

  • TTY detection - Raw text when piped, formatted when interactive
  • Context-aware - GitHub bot doesn't load themes (no overhead for automation)
  • Streaming-friendly - No re-renders or flashing during message streaming

Technical Details

Files Changed

  • `markdown-renderer.ts` (new, 1297 lines) - Core rendering engine
  • `theme-loader.ts` (new, 98 lines) - Theme loading and conversion
  • `session/index.tsx` (+117 lines) - TUI integration with hybrid rendering
  • `run.ts` (+8 lines) - CLI theme integration
  • `ui.ts` (+5 lines) - Theme parameter support

Key Functions

  • `renderMarkdownThemedStyled()` - Main renderer, outputs TextChunks
  • `renderTableThemed()` - Table rendering with word wrap and inline markdown
  • `renderInlineThemed()` - Inline formatting (bold, italic, code, links)
  • `textChunksToAnsi()` - Converts TextChunks to ANSI for CLI
  • `loadTheme()` - Loads and converts theme JSON to rendering format

Rendering Flow

CLI:
```
User config → loadTheme() → MarkdownTheme
Markdown → renderMarkdownThemedStyled() → TextChunks
TextChunks → textChunksToAnsi() → ANSI string → stdout
```

TUI:
```
User config → Theme context → MarkdownTheme
Markdown → parseMarkdownSegments() → [text, code, text, code]
Text segments → renderMarkdownThemedStyled() → StyledText →
Code segments → tree-sitter →
```

Examples

Before/After: Tables

Before:
```
| Header | Value |
| A | B |
```

After:
```
┌────────┬───────┐
│ Header │ Value │
├────────┼───────┤
│ A │ B │
└────────┴───────┘
```

Theme Colors

All colors match user's selected theme:

  • Headers - Accent color with background
  • Code - Monospace with code color
  • Links - Link color + underline
  • Bold/Italic - Distinct from regular text
  • Borders - Border color from theme

Testing

✅ CLI output verified with multiple themes
✅ TUI rendering stable during streaming
✅ Tables with complex content (inline code, word wrap, unicode)
✅ Escaped pipes (\|) for union types
✅ Double-width characters (emoji, CJK)
✅ TTY detection (piped vs interactive)
✅ All 12+ built-in themes load correctly

Breaking Changes

None - backward compatible. Existing markdown rendering continues to work.

Performance

  • Minimal overhead - Only loads theme once per session
  • No flashing - Stable rendering during streaming
  • Smart caching - Theme colors resolved once
  • Efficient word wrap - Token-based wrapping preserves inline code

Migration Notes

No migration needed. The feature is automatically available in both CLI and TUI.

Commits

22 commits with iterative improvements:

  • Initial implementation
  • Stability fixes (flashing, streaming)
  • Feature additions (tables, themes, inline markdown)
  • Code cleanup (removed 283 lines of dead code)
  • Bug fixes (escaped pipes, unicode width)
  • Refactoring (unified architecture)

@github-actions
Copy link
Contributor

Hey! Your PR title Enhanced Markdown Renderer with Tables, Themes, and Syntax Highlighting doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

No duplicate PRs found

@ryanwyler ryanwyler force-pushed the feature/markdown-renderer branch from 21f2b08 to fd0fd68 Compare January 12, 2026 22:04
@ryanwyler ryanwyler changed the title Enhanced Markdown Renderer with Tables, Themes, and Syntax Highlighting feat(opencode): enhanced markdown renderer with tables, themes, and syntax highlighting Jan 12, 2026
@ryanwyler
Copy link
Contributor Author

Screenshot from 2026-01-12 15-00-36

@arsham
Copy link

arsham commented Jan 12, 2026

It is lovely. A small problem:

image

@ryanwyler ryanwyler force-pushed the feature/markdown-renderer branch from fd0fd68 to 143effe Compare January 12, 2026 22:20
…yntax highlighting

Comprehensive markdown rendering overhaul for both CLI and TUI.

Features:
- Table rendering with box-drawing borders, word wrap, inline markdown
- Theme integration: CLI and TUI use user's selected theme
- Syntax highlighting via tree-sitter for 10+ languages
- Unified architecture with shared rendering logic
- Smart TTY detection for clean piped output
- Escaped pipe support for TypeScript union types
- Double-width character support (Unicode/emoji)

Fixes (from peer review):
- Table parsing now preserves all columns (fixed last column drop)
- Conceal toggle works in markdown code blocks
- Link width calculation includes full URL for proper alignment
- Theme fetch wrapped in try/catch to prevent crashes

Architecture:
- New markdown-renderer.ts (1255 lines) - core rendering engine
- New theme-loader.ts (98 lines) - theme loading and conversion
- Unified TextChunk system for CLI (ANSI) and TUI (StyledText)
- Hybrid TUI rendering: text via custom renderer, code via tree-sitter

Changes:
- packages/opencode/src/cli/markdown-renderer.ts (new)
- packages/opencode/src/cli/theme-loader.ts (new)
- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx (+117)
- packages/opencode/src/cli/cmd/run.ts (+8)
- packages/opencode/src/cli/ui.ts (+5)

Closes anomalyco#3845, anomalyco#7671, anomalyco#4988, anomalyco#5675
@ryanwyler ryanwyler force-pushed the feature/markdown-renderer branch from 143effe to 297c82b Compare January 12, 2026 22:49
@ryanwyler
Copy link
Contributor Author

It is lovely. A small problem:

image

This was intentional. I guess you could say "artistic direction", thinking smaller headings maybe shouldn't be as "bold" as the main headings. Open to adjusting ... but I went back and fourth on that one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants