Implementing your own reader

Skip the @rho/md library and implement a Rho reader directly from the Rho format spec. Suitable for: non-JS ecosystems (Python / Go / Rust / Swift) / wanting fully license-free (avoiding PolyForm) / deep integration with an existing reader (Obsidian plugin / Vim plugin / etc.).


Why implement your own

You want to… Self-implement?
Plug into Obsidian / Bear / Typora and similar existing readers ✅ Self-implement (as plugin)
Use it in Python / Go / Rust ecosystems ✅ Self-implement (@rho/md is a JS package)
Fully avoid PolyForm Noncommercial license ✅ Self-implement (spec is CC BY 4.0; your impl chooses license)
Integrate into a web project ❌ Just use @rho/md (unless special requirements)
Hand-coded markdown tool for learning ✅ Self-implement (learning purpose)

  • The spec is CC BY 4.0 — you can implement freely; no need to contact scos-lab
  • Your implementation's license is yours to choose — MIT / Apache / commercial / closed-source freely
  • Only requirement: preserve attribution in your reader's docs / about page: "Implements Rho format spec by scos-lab"

You cannot use "Rho" / "Rho format" as a trademark in marketing — but you can say "compatible with Rho" / "supports Rho format documents."


Core components needed

In rough order of importance:

1. Markdown parser (any standard CommonMark / GFM implementation)

No need to write from scratch — use an existing markdown parser:

  • Python: markdown / mistune / markdown-it-py
  • Go: blackfriday / goldmark
  • Rust: pulldown-cmark / comrak
  • Swift: swift-markdown / Down
  • C: cmark / md4c

Needs to support:

  • Standard CommonMark
  • GFM tables / strikethrough / task lists
  • Fenced code blocks (with lang label)
  • Blockquotes
  • Ideally directive containers (:::xxx); if not, parse them yourself

2. Rho DSL parser (you write this)

Per spec:

  • > [!TYPE] blockquote → callout
  • ```layout grid cols=N + :::card → layout
  • ```tabs + :::tab → tabs
  • ```stepper + :::step → stepper
  • ```modal + content → modal
  • ```timeline + :::event → timeline
  • ```annotate + body + --- + annotation table → annotate
  • ```stl → STL syntax highlighting
  • ```interact + sliders/templates/computed/timer → interact

Each DSL outputs marker class HTML (see Remark plugins).

3. Inner processor (nesting support)

Container DSLs (layout / tabs / stepper / modal / timeline / callout) need their content run through the full pipeline again — see Inner processor.

Simplest implementation: recursively call the same parser function on inner content.

4. CSS (write your own or borrow Rho's)

You can:

  • Write CSS from scratch (refer to CSS class hooks)
  • Borrow @rho/md's default CSS (CC BY 4.0 spec doesn't include CSS impl license — CSS is PolyForm Noncommercial. Verify license independently before borrowing)
  • Or design your own visual entirely

5. Hydration (if web/client reader)

Turn static HTML into interactive components:

  • Tabs / Stepper / Modal — DOM event listeners
  • Interact controls — form elements + reactive update
  • Vega-Lite chart — integrate vega-embed library (callable from any language)
  • SVG scenes — built-in mini DSL evaluator (or borrow an existing expression parser)
  • Timer — setInterval / equivalent scheduling

Non-web readers (e.g., Obsidian plugin) — implement equivalent functionality in Obsidian's view system.

6. Mini DSL evaluator (interact expressions)

Rho's mini DSL: + - * / % ** == != < > <= >= && || ! ?: pow sqrt abs min max sin cos tan log exp pi e ...

Implementation options:

  • Hand-write parser + tree-walking interpreter (~300 lines)
  • Use existing expression libraries:
    • JS: custom (don't use eval!)
    • Python: simpleeval
    • Go: expr
    • Rust: evalexpr

⚠️ Critical: the mini DSL must be pure-function + safe — no IO / network / state mutation / eval. This is Rho's safety stance.

7. SVG sanitizer (when using svg template)

Strip dangerous elements / attributes:

  • <script>, <foreignObject>
  • on* event attributes
  • href containing javascript:
  • External URL <image href="external://...">

Implementation:

  • JS: DOMParser + allowlist filter
  • Python: nh3 / bleach
  • Rust: ammonia

Step 1: standard markdown working (0.5 day)
   - Pick a markdown parser
   - Get it producing normal HTML

Step 2: add Callout support (0.5 day)
   - parse > [!TYPE] blockquote
   - emit marker class HTML

Step 3: add Layout grid + cards (1 day)
   - parse ```layout fenced + :::card directive
   - CSS Grid layout

Step 4: add other static DSLs (1 day)
   - Annotate / Timeline / STL syntax highlight

Step 5: add interactive structural DSLs (2 days)
   - Tabs / Stepper / Modal
   - Their hydration

Step 6: add interact basics (2 days)
   - sliders / inputs / template stl
   - mini DSL evaluator

Step 7: add computed + namespace sharing (1 day)
   - Derived values + topological sort
   - Cross-block state sharing

Step 8: add advanced interact (3 days)
   - Vega-Lite chart integration
   - Timer auto-play
   - SVG scenes + sanitizer

Step 9: add inner processor (nesting support) (1 day)
   - Recursive parse

Step 10: plain-text fallback testing (0.5 day)
   - Verify source is readable in cat / GitHub

Total: ~13 days person-effort.


@rho/md compatibility testing

If you want to claim "compatible with @rho/md" — run this conformance test pack:

  1. Take a standard document rendered with @rho/md (refer to SPEC_v0.0_RENDER_TEST.md)
  2. Render the same document with your reader
  3. Compare output HTML (class names, data attributes should match)
  4. Compare interactive behavior (slider drag, output text matches)

A complete conformance test pack is planned for spec v1.0 release.


Known / planned implementations

Reader Status License
@rho/md (official JS reference impl) ✅ v0.1 released PolyForm Noncommercial
markview desktop app (Tauri 2 + React) 🔨 v1.0 binary in preparation PolyForm Noncommercial
cloud.rho.md (SaaS) 🔨 MVP in development Closed-source SaaS
Obsidian plugin 🔮 community contribution welcome Choose your own
Bear plugin 🔮 community contribution welcome Choose your own
Python python-rho 🔮 (community wanted) Choose your own
Rust rho-rs 🔮 (community wanted) Choose your own
Vim plugin 🔮 (community wanted) Choose your own

Community implementations: PRs / issues welcome at scos-lab/markview (repo to be renamed) for compatibility issues.


See also