feat(rex/comments): render combined.md → HTML for Zotero notes
Pure function over (str)→str. Reuses combine.parse_combined to peel off the YAML frontmatter, runs the body through markdown-it-py with table support enabled (CMS comments lean on tabular data). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
26
src/rex/comments/render.py
Normal file
26
src/rex/comments/render.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
"""Render combined.md → HTML for Zotero notes.
|
||||||
|
|
||||||
|
combined.md is the per-comment aggregate written by ``combine.extract_comment``:
|
||||||
|
a YAML frontmatter block followed by an inline-body section and one H2 section
|
||||||
|
per attachment. Zotero notes accept HTML, so we strip the frontmatter and run
|
||||||
|
the body through markdown-it-py.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from markdown_it import MarkdownIt
|
||||||
|
|
||||||
|
from rex.comments.combine import parse_combined
|
||||||
|
|
||||||
|
_MD = MarkdownIt("commonmark", {"html": False, "breaks": False, "linkify": True})
|
||||||
|
_MD.enable("table")
|
||||||
|
|
||||||
|
|
||||||
|
def render_combined_md(text: str) -> str:
|
||||||
|
"""Strip combined.md's YAML frontmatter, render body to HTML.
|
||||||
|
|
||||||
|
Raises ValueError if the input is missing frontmatter — combined.md
|
||||||
|
always has it, so a missing block means the input isn't combined.md.
|
||||||
|
"""
|
||||||
|
_, body = parse_combined(text)
|
||||||
|
return _MD.render(body)
|
||||||
51
tests/rex/comments/test_render.py
Normal file
51
tests/rex/comments/test_render.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
"""rex.comments.render — combined.md → Zotero-edible HTML."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from rex.comments.render import render_combined_md
|
||||||
|
|
||||||
|
|
||||||
|
def test_strips_frontmatter():
|
||||||
|
md = "---\ncomment_id: CMS-2024-0001-0001\nstatus: ok\n---\n\n# Heading\n\nbody\n"
|
||||||
|
html = render_combined_md(md)
|
||||||
|
assert "comment_id" not in html
|
||||||
|
assert "status: ok" not in html
|
||||||
|
assert "<h1>Heading</h1>" in html
|
||||||
|
assert "<p>body</p>" in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_renders_basic_markdown():
|
||||||
|
md = "---\nstatus: ok\n---\n\n## Section\n\nText with **bold** and *italic*.\n"
|
||||||
|
html = render_combined_md(md)
|
||||||
|
assert "<h2>Section</h2>" in html
|
||||||
|
assert "<strong>bold</strong>" in html
|
||||||
|
assert "<em>italic</em>" in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_renders_tables():
|
||||||
|
md = (
|
||||||
|
"---\nstatus: ok\n---\n\n"
|
||||||
|
"| Col A | Col B |\n"
|
||||||
|
"| --- | --- |\n"
|
||||||
|
"| a1 | b1 |\n"
|
||||||
|
"| a2 | b2 |\n"
|
||||||
|
)
|
||||||
|
html = render_combined_md(md)
|
||||||
|
assert "<table>" in html
|
||||||
|
assert "<th>Col A</th>" in html
|
||||||
|
assert "<td>a1</td>" in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_handles_empty_body():
|
||||||
|
md = "---\nstatus: ok\n---\n\n"
|
||||||
|
html = render_combined_md(md)
|
||||||
|
assert html == "" or html.strip() == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_raises_on_missing_frontmatter():
|
||||||
|
"""combined.md is always written with frontmatter — guard against
|
||||||
|
silently ingesting malformed input."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
with pytest.raises(ValueError, match="frontmatter"):
|
||||||
|
render_combined_md("# No frontmatter here\n")
|
||||||
Reference in New Issue
Block a user