gabriel / musehub public
Closed #78
filed by gabriel human · 4 days ago

Display Mists as Rendered Markdown

0 Anchors
Blast radius
Churn 30d
0 Proposals

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 /raw URL

Edge Cases

  • Mists with artifact_type == "prose" and no language set — treat as plain text, not markdown
  • Very large markdown files — marked.parse is synchronous; consider a size threshold above which we fall back to the raw code view
  • Images in markdown — DOMPurify strips <img> by default unless ADD_TAGS is configured; decide policy before shipping
Activity1
gabriel opened this issue 4 days ago
gabriel 3 days ago

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: compute md_html via _markdown(mist.content) when prism_lang == "markdown"; pass to template context
  • mist_detail.html: {% elif md_html %} branch renders <div class="blob2-markdown"> instead of <pre><code>
  • mist-detail.ts: added initMarkdownAnchors() (same as blob.ts) — injects # anchor links on headings with id attributes

Uses the same .blob2-markdown CSS class and _MuseRenderer (heading slugs, h1→h2 level shift, link sanitization) as the repo README blob page.

closed this issue 3 days ago