Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147import type { Action } from "svelte/action";
const BLOCK = /<pre><code class="language-([^"]+)">([\s\S]*?)<\/code><\/pre>/g;
const MAX_SSR_BLOCK_CHARS = 50_000;
const MAX_CSR_BLOCK_CHARS = 200_000;
const ENTITIES: Record<string, string> = { amp: "&", lt: "<", gt: ">", quot: '"', "#39": "'", "#x27": "'", "#x2F": "/"};
const decodeEntities = (value: string): string => value.replace(/&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z#0-9]+);/g, (match, body: string) => { if (body.startsWith("#x") || body.startsWith("#X")) try { return String.fromCodePoint(parseInt(body.slice(2), 16)); } catch { return match; } if (body.startsWith("#")) { const point = parseInt(body.slice(1), 10); return Number.isNaN(point) ? match : String.fromCodePoint(point); } return ENTITIES[body] ?? match; });
const INNER = /<code[^>]*>([\s\S]*)<\/code>\s*<\/pre>/;
type Highlighter = Awaited<ReturnType<typeof import("@pierre/diffs").getSharedHighlighter>>;
type Loaded = { highlighter: Highlighter; theme: string };
// importing this lazily keeps shiki out of markdown pages with no codeconst loadHighlighter = async (langs: string[]): Promise<Loaded | null> => { try { const { SYNTAX_THEME_NAME, getSharedHighlighter } = await import("$lib/syntax"); const highlighter = await getSharedHighlighter({ themes: [SYNTAX_THEME_NAME], langs }); return { highlighter, theme: SYNTAX_THEME_NAME }; } catch { return null; }};
const highlightInner = (loaded: Loaded, code: string, lang: string): string | null => { let full: string; try { full = loaded.highlighter.codeToHtml(code, { lang, theme: loaded.theme, defaultColor: false, cssVariablePrefix: "--diffs-token-" }); } catch { return null; } return INNER.exec(full)?.[1] ?? null;};
export const highlightCodeblocks = async (html: string): Promise<string> => { const found = [...html.matchAll(BLOCK)]; if (found.length === 0) return html;
const blocks = found.flatMap((match) => { const [raw, lang, escaped] = match as unknown as [string, string, string]; const code = decodeEntities(escaped); return code.length > 0 && code.length <= MAX_SSR_BLOCK_CHARS ? [{ raw, lang, code }] : []; }); if (blocks.length === 0) return html;
const loaded = await loadHighlighter([...new Set(blocks.map((block) => block.lang))]); if (!loaded) return html;
const swapped = new Map<string, string>(); for (const block of blocks) { if (swapped.has(block.raw)) continue; const inner = highlightInner(loaded, block.code, block.lang); if (inner !== null) swapped.set( block.raw, `<pre><code class="language-${block.lang}">${inner}</code></pre>` ); } if (swapped.size === 0) return html; return html.replace(BLOCK, (raw) => swapped.get(raw) ?? raw);};
const LANG = /(?:^|\s)language-([^\s]+)/;
const idle = (): Promise<void> => new Promise((resolve) => { const ric = ( globalThis as { requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => void; } ).requestIdleCallback; if (typeof ric === "function") ric(() => resolve(), { timeout: 1500 }); else setTimeout(resolve, 0); });
export const enhanceCodeblocks: Action<HTMLElement, unknown> = (node) => { let cancelled = false;
const run = async (): Promise<void> => { const plain = (el: Element): { code: HTMLElement; text: string } | null => { const code = el.firstElementChild; if (!(code instanceof HTMLElement) || code.querySelector("span") !== null) return null; const text = code.textContent ?? ""; return text.length > 0 && text.length <= MAX_CSR_BLOCK_CHARS ? { code, text } : null; }; const targets = [...node.querySelectorAll("pre")].flatMap((pre) => { const found = plain(pre); if (!found) return []; const lang = LANG.exec(found.code.className)?.[1]; return lang ? [{ ...found, lang }] : []; }); if (targets.length === 0) return;
await idle(); if (cancelled || !node.isConnected) return; const loaded = await loadHighlighter([...new Set(targets.map((target) => target.lang))]); if (!loaded || cancelled) return;
for (const target of targets) { if (cancelled || !target.code.isConnected) continue; // a re-render may have highlighted it since if (target.code.querySelector("span") !== null) continue; const inner = highlightInner(loaded, target.text, target.lang); if (inner !== null) target.code.innerHTML = inner; } };
void run(); return { update: () => void run(), destroy: () => { cancelled = true; } };};