Display Mists as Rendered Markdown
Display Mists as Rendered Markdown
Current Behavior
When a mist has language == "markdown" (or artifact_type == "prose" with markdown content), the detail page wraps the raw content in a <pre><code class="language-markdown"> block. The user sees the raw markdown source — # prefixes, **asterisks**, backtick fences, etc. — instead of formatted output.
Relevant template: musehub/templates/musehub/pages/mist_detail.html line 122:
<pre class="ms-content-pre">
<code class="language-{{ prism_lang }}" id="artifact-content">
{{ mist.content | e }}
</code>
</pre>
This single branch handles all non-MIDI artifact types, including markdown.
Expected Behavior
When prism_lang == "markdown" (i.e. mist.language == "markdown"), the detail page renders the mist content as HTML:
# Heading→<h1>Heading</h1>**bold**→<strong>bold</strong>- fenced code blocks → syntax-highlighted
<pre><code>blocks (Prism still applies inside rendered markdown) - links, tables, blockquotes — fully rendered
All other artifact types continue to display as syntax-highlighted source.
Implementation
Template — add a branch before the existing <pre> block:
{% if prism_lang == "markdown" %}
<div class="ms-markdown-body" id="artifact-content"></div>
<script>
const raw = {{ mist.content | tojson }};
document.getElementById("artifact-content").innerHTML =
DOMPurify.sanitize(marked.parse(raw));
</script>
{% else %}
<pre class="ms-content-pre">
<code class="language-{{ prism_lang }}" id="artifact-content">
{{ mist.content | e }}
</code>
</pre>
{% endif %}
Dependencies — add to the base template or mist_detail block:
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
DOMPurify is non-negotiable — mist.content is user-supplied; rendering it without sanitization is an XSS vector.
Styling — add .ms-markdown-body styles (prose spacing, heading hierarchy, code block contrast) to the mist detail stylesheet.
Acceptance Criteria
- A mist with
language == "markdown"renders as formatted HTML on the detail page - A mist with any other language continues to render as a syntax-highlighted code block
- Raw markdown is never injected directly into the DOM — DOMPurify sanitizes all output
- Fenced code blocks inside markdown are syntax-highlighted by Prism
- The raw content is still accessible via the
/rawURL
Edge Cases
- Mists with
artifact_type == "prose"and nolanguageset — treat as plain text, not markdown - Very large markdown files —
marked.parseis synchronous; consider a size threshold above which we fall back to the raw code view - Images in markdown —
DOMPurifystrips<img>by default unlessADD_TAGSis configured; decide policy before shipping
Implemented. Mists with
language == "markdown"(or any mist where the Prism map resolves to"markdown") now render as HTML via the existing_markdown()SSR filter rather than syntax-highlighted source.Changes:
ui_mists.py: computemd_htmlvia_markdown(mist.content)whenprism_lang == "markdown"; pass to template contextmist_detail.html:{% elif md_html %}branch renders<div class="blob2-markdown">instead of<pre><code>mist-detail.ts: addedinitMarkdownAnchors()(same asblob.ts) — injects#anchor links on headings withidattributesUses the same
.blob2-markdownCSS class and_MuseRenderer(heading slugs, h1→h2 level shift, link sanitization) as the repo README blob page.