Files
markdown-html-renderer/README.md

2.4 KiB

Markdown HTML Renderer

Small, dependency-free Markdown-to-HTML converter originally built for the "Heimgeist" AI chat frontend. It focuses on predictable, chat-friendly rendering and ships with a matching CSS theme.

What it does

  • Removes <think>...</think> and <thinking>...</thinking> blocks.
  • Normalizes exotic spaces to regular spaces.
  • Supports headings (# to ####), horizontal rules, blockquotes, lists, GitHub-style tables, inline bold/italic/code, and fenced code blocks.
  • Escapes HTML outside fenced code blocks for safety.
  • Balances unfinished fenced code blocks for streaming output.
  • Converts newlines to <br> and normalizes spacing around block elements.
  • Adds a code-block header with language label and a copy button.
  • Sanitizes links to allow only http(s), mailto:, tel:, / and #.

Files

  • markdown.js — exports the renderer.
  • markdown-render.css — styles for the generated HTML.

Usage

import { markdownToHTML } from './markdown.js';

const markdown = `
# Title

Here is **bold**, *italic*, and \`inline code\`.

> Blockquote

1. One
2. Two

| Col A | Col B |
|:-----|------:|
| left | right |

\`\`\`js
console.log('hello');
\`\`\`
`;

const html = markdownToHTML(markdown);

// Example: render into the DOM
document.querySelector('#output').innerHTML = html;

Include the CSS and optionally wrap the output:

<link rel="stylesheet" href="markdown-render.css">
<div class="md-root" id="output"></div>

Copy button behavior

Each fenced code block includes a button with a URL-encoded payload:

<button data-copy-code="...">...</button>

You can wire it up like this:

document.addEventListener('click', (event) => {
  const button = event.target.closest('.md-codeblock__copy');
  if (!button) return;
  const raw = button.getAttribute('data-copy-code') || '';
  const code = decodeURIComponent(raw);
  navigator.clipboard.writeText(code);
});

Notes and limitations

  • This is a regex-based renderer, not a full Markdown parser.
  • Only fenced code blocks are supported (no indented code blocks).
  • Nested lists and advanced Markdown extensions are not parsed.
  • Link conversion is deliberately strict for safety.
  • The link-matching regex is tailored to the current implementation in markdown.js; if you need standard [label](url) parsing, adjust it there.

License

Unspecified. Add a license file if you plan to distribute this module.