Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/claude_code_transcripts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,14 @@ def format_json(obj):
def render_markdown_text(text):
if not text:
return ""
return markdown.markdown(text, extensions=["fenced_code", "tables"])
# Render literal HTML in message text as visible text rather than live markup: an unclosed
# <style>/<script> in a prompt would otherwise swallow the rest of the page. Deregistering the
# HTML handlers (rather than escaping the input first) keeps fenced code blocks intact, since
# fenced_code stashes their content in a preprocessor that runs before html_block.
md = markdown.Markdown(extensions=["fenced_code", "tables"])
md.preprocessors.deregister("html_block")
md.inlinePatterns.deregister("html")
return md.convert(text)


def is_json_like(text):
Expand Down
14 changes: 14 additions & 0 deletions tests/test_generate_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ def test_render_markdown_text_empty(self):
assert render_markdown_text("") == ""
assert render_markdown_text(None) == ""

def test_render_markdown_text_escapes_literal_html(self):
"""Literal HTML in message text renders as visible text, not live markup."""
result = render_markdown_text("Leave the <head> and the <style> block alone.")
assert "&lt;head&gt;" in result
assert "&lt;style&gt;" in result
assert "<head>" not in result
assert "<style>" not in result

def test_render_markdown_text_keeps_fenced_html(self):
"""A fenced code block containing HTML is preserved as escaped code, not dropped."""
result = render_markdown_text("text\n\n```html\n<div>x</div>\n```")
assert "<pre>" in result
assert "&lt;div&gt;x&lt;/div&gt;" in result

def test_format_json(self, snapshot_html):
"""Test JSON formatting."""
result = format_json({"key": "value", "number": 42, "nested": {"a": 1}})
Expand Down