@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.
68 lines (67 loc) • 3.05 kB
TypeScript
/**
* SPIKE — streaming-compatible Shiki syntax highlighting.
*
* This module wraps Shiki's **synchronous** core highlighter
* ({@link createHighlighterCoreSync}) with the pure-JavaScript regex engine so
* that highlighting happens at render time with no WASM load and no top-level
* `await`. That is the property that keeps the streaming diff path intact: the
* `code` renderer stays synchronous, so `SvelteMarkdown`'s `hasAsyncExtension`
* guard never trips and `streaming` is never silently disabled.
*
* The consumer supplies explicitly-imported languages and themes, e.g.
*
* ```ts
* import js from 'shiki/langs/javascript.mjs'
* import ts from 'shiki/langs/typescript.mjs'
* import githubDark from 'shiki/themes/github-dark.mjs'
* import { createShikiHighlighter } from '.../extensions/shiki'
*
* const highlighter = createShikiHighlighter({ langs: [js, ts], themes: [githubDark] })
* ```
*
* Only the languages/themes you import are bundled — nothing is pulled in
* dynamically — which is what keeps the core package's "lightweight" bundle
* positioning honest (see `scripts/tree-shaking.mjs`).
*
* @module
*/
import { type LanguageInput, type ThemeInput } from 'shiki/core';
/**
* Escape the five HTML-significant characters so untrusted text can never break
* out of its element or attribute context. Used by the unregistered-language
* fallback, which must **escape, never interpolate** its inputs — the fenced
* code `lang` is untrusted (agent/LLM-streamed) input in this package's
* headline use case.
*/
export declare const escapeHtml: (value: string) => string;
/** A minimal, synchronous highlighter facade consumed by `ShikiCode.svelte`. */
export interface ShikiHighlighter {
/**
* Highlight `code` for `lang`, returning an HTML string. For an
* unregistered (or empty) `lang`, returns an escaped `<pre><code>` fallback
* instead of throwing — critical mid-stream, where an exception would tear
* down the render.
*/
highlight(_code: string, _lang: string): string;
/** Whether `lang` (name or alias) is registered on the highlighter. */
hasLang(_lang: string): boolean;
}
export interface CreateShikiHighlighterOptions {
/** Explicitly-imported Shiki languages (e.g. `import js from 'shiki/langs/javascript.mjs'`). */
langs: LanguageInput[];
/** Explicitly-imported Shiki themes (e.g. `import theme from 'shiki/themes/github-dark.mjs'`). */
themes: ThemeInput[];
/**
* Theme name to render with. Defaults to the first loaded theme. Must match
* one of the loaded `themes`.
*/
theme?: string;
}
/**
* Build a synchronous {@link ShikiHighlighter} from explicit languages/themes.
*
* @throws If `createHighlighterCoreSync` itself fails (e.g. a malformed
* language registration) — construction is a one-time setup concern, not a
* per-block/mid-stream one, so it is allowed to surface.
*/
export declare const createShikiHighlighter: (options: CreateShikiHighlighterOptions) => ShikiHighlighter;