@humanspeak/svelte-markdown
Version:
Markdown and HTML renderer for Svelte 5 — built for rendering streaming AI agent output from Claude Code, ChatGPT, and agentic workflows. XSS-safe defaults, streaming-aware sanitization, token caching, TypeScript types, and Svelte 5 runes.
63 lines (62 loc) • 2.09 kB
JavaScript
const ALERT_TYPES = new Set(['note', 'tip', 'important', 'warning', 'caution']);
/**
* Creates a marked extension that tokenizes GitHub-style alert blockquotes
* into custom `alert` tokens.
*
* The extension produces block-level tokens with
* `{ type: 'alert', raw, text, alertType }` where `alertType` is one of
* `note`, `tip`, `important`, `warning`, or `caution`, and `text` is the
* alert body with leading `> ` stripped.
*
* Pair it with `AlertRenderer` (or your own component) to render the alerts.
*
* @example
* ```svelte
* <script lang="ts">
* import SvelteMarkdown from '@humanspeak/svelte-markdown'
* import { markedAlert, AlertRenderer } from '@humanspeak/svelte-markdown/extensions'
*
* const renderers = { alert: AlertRenderer }
* </script>
*
* <SvelteMarkdown
* source={markdown}
* extensions={[markedAlert()]}
* {renderers}
* />
* ```
*
* @returns A `MarkedExtension` with a single block-level `alert` tokenizer
*/
export function markedAlert() {
return {
extensions: [
{
name: 'alert',
level: 'block',
start(src) {
return src.match(/>\s*\[!/)?.index;
},
tokenizer(src) {
const match = src.match(/^(?:>\s*\[!(\w+)\]\n)((?:[^\n]*(?:\n(?:>\s?)[^\n]*)*)?)(?:\n|$)/);
if (match) {
const alertType = match[1].toLowerCase();
if (!ALERT_TYPES.has(alertType))
return;
const text = match[2]
.split('\n')
.map((line) => line.replace(/^>\s?/, ''))
.join('\n')
.trim();
return {
type: 'alert',
raw: match[0],
text,
alertType: alertType
};
}
}
}
]
};
}