@ai-matrx/print 0.12.33 → 0.12.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/markdown.ts","../src/core.ts","../src/katex-css.generated.ts","../src/math-text.ts","../src/document/math.ts","../src/safe-html.ts","../src/markdown-css.ts","../src/directives.ts"],"sourcesContent":["/**\n * @ai-matrx/print/markdown — the SHIPPED DEFAULT markdown→HTML converter and\n * print stylesheet.\n *\n * Why this entry exists: `markdownToPdfBlob` used to declare its converter and\n * its stylesheet as REQUIRED injected ports (\"bring your own converter +\n * stylesheet\"). That is the exact failure THE ALL-INCLUSIVE LAW names — every\n * host had to build the hard half itself, and both hosts that tried ended up\n * with a different converter, a different stylesheet, and a different set of\n * bugs. The package now ships a complete, working default for both ports; the\n * injection seam survives as the OVERRIDE path, never as a prerequisite.\n *\n * Two skins, one vocabulary:\n * • \"document\" — `markdownToHtml` emits `.matrx-*`-classed HTML (tables,\n * nested lists, images, links, code, FAQ blocks, flashcards) and\n * `MARKDOWN_DOCUMENT_CSS` styles it. This is the PDF/publish/WordPress\n * lane.\n * • \"article\" — `markdownToArticleHtml` emits bare semantic tags and\n * `MARKDOWN_ARTICLE_CSS` gives them the serif AI-response look. This is\n * the print-window lane.\n *\n * Nothing here touches `window`/`document` at import time, and nothing here is\n * heavy — but it IS ~20KB of CSS, so `markdownToPdfBlob` reaches it through a\n * lazy `import(\"./markdown\")` and a consumer that never prints markdown pays\n * nothing.\n *\n * NOTHING FAILS SILENTLY: every case where the converter drops input it cannot\n * represent (a malformed flashcard, an empty document) is reported through the\n * optional `onNotice` sink with a concrete remedy, instead of vanishing.\n */\n\nimport {\n openPendingPrintWindow,\n openPrintWindow,\n printBlockOutputHtml,\n type PrintBlockOutput,\n type PrintOutcome,\n} from \"./core\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport {\n codeSpanText,\n collectLinkDefinitions,\n fenceLineKinds,\n findLinkDefinitionLines,\n findTableEnd,\n isHtmlBlockTagName,\n lineIndent,\n mapInlineCodeSpans,\n resolveReferenceLinks,\n rowCells,\n stripThinking,\n tableStartsAt,\n unescapeCellPipes,\n} from \"@ai-matrx/content-ir/source\";\nimport { KATEX_CSS } from \"./katex-css.generated\";\nimport { typesetTex } from \"./math-text\";\nimport { extractMath, MATH_TOKEN_RE, type MathSpan } from \"./document/math\";\nimport { PRINT_HTML_SCHEMA, sanitizeEngineHtml } from \"./safe-html\";\nimport {\n MARKDOWN_ARTICLE_CSS,\n MARKDOWN_ARTICLE_TOKENS_CSS,\n MARKDOWN_DOCUMENT_CSS,\n MARKDOWN_DOCUMENT_TOKENS_CSS,\n MARKDOWN_PAGE_CSS,\n MARKDOWN_PAGE_PLAIN_CSS,\n MARKDOWN_SHARED_CSS,\n} from \"./markdown-css\";\nimport {\n PAGE_BREAK_CLASS,\n PAGE_BREAK_LABEL,\n PAGE_BREAK_MARKDOWN,\n isBibliographyLine,\n isBreakLine,\n isPageBreakLine,\n isTocLine,\n splitAtPageBreaks,\n} from \"./directives\";\n\nexport {\n MARKDOWN_ARTICLE_CSS,\n MARKDOWN_ARTICLE_TOKENS_CSS,\n MARKDOWN_DOCUMENT_CSS,\n MARKDOWN_DOCUMENT_TOKENS_CSS,\n MARKDOWN_PAGE_CSS,\n MARKDOWN_SHARED_CSS,\n PAGE_BREAK_CLASS,\n PAGE_BREAK_MARKDOWN,\n isPageBreakLine,\n splitAtPageBreaks,\n};\n\n/** Which shipped look to render markdown in. */\nexport type MarkdownSkin = \"document\" | \"article\";\n\n/**\n * A thing the converter could not represent, with the remedy. Announced, never\n * swallowed — a dropped card that says nothing is indistinguishable from a card\n * the author never wrote.\n */\nexport interface MarkdownNotice {\n code: \"flashcard-malformed\" | \"empty-output\" | \"html-stripped\";\n message: string;\n /** What the caller (or the author of the markdown) should do about it. */\n remedy: string;\n}\n\n/** A picture the host drew for a fenced block (a diagram), printed in its place. */\nexport interface FencePicture {\n /** A complete `<svg …>…</svg>` document. */\n svg: string;\n /** What the picture shows, for the printed alt text. */\n alt?: string;\n}\n\nexport interface MarkdownToHtmlOptions {\n /** Keep `<thinking>` / `<think>` / `<reasoning>` blocks. Default: false. */\n includeThinking?: boolean;\n /** Where dropped/degraded content is announced. */\n onNotice?: (notice: MarkdownNotice) => void;\n /**\n * The print form of one embedded block (a fenced block or an `<artifact>`),\n * looked up synchronously while converting — how a host prints a diagram as\n * a picture, a quiz through its printer, an HTML page as its image. Return\n * null to print the block's source. Hosts with async work (drawing, a page\n * capture) pass an async `renderBlock` to `printMarkdownWhenReady`, which\n * resolves every block first and hands this the answers.\n */\n renderBlock?: (block: PrintBlock) => PrintBlockRender | null | undefined;\n}\n\n/** One embedded block of a message, as the print composer finds it. */\nexport interface PrintBlock {\n /** \"fence\": a fenced block (type = its language); \"artifact\": an `<artifact type>` wrapper. */\n readonly source: \"fence\" | \"artifact\";\n /** Lower-cased language / artifact type — the block-printer registry key. */\n readonly type: string;\n /** The block's source text. */\n readonly body: string;\n readonly title?: string | undefined;\n /** An `<artifact>`'s attributes (`id`, `version`, …) — how a host finds the block's record. */\n readonly attributes?: Readonly<Record<string, string>> | undefined;\n}\n\n/** What prints in a block's place: a drawing, or any `PrintBlockOutput`. */\nexport type PrintBlockRender = FencePicture | PrintBlockOutput;\n\n/** The identity of a block within one markdown string (stable across conversions). */\nexport function printBlockKey(block: PrintBlock): string {\n return `${block.source}\\u0000${block.type}\\u0000${block.body}`;\n}\n\nfunction printBlockRenderHtml(render: PrintBlockRender): string {\n return \"svg\" in render ? fencePictureHtml(render) : printBlockOutputHtml(render);\n}\n\n/**\n * Every embedded block of `markdown`, in order, exactly as the converter sees\n * it (it runs the converter once and records what it is asked about).\n */\nexport function listPrintBlocks(markdown: string, skin: MarkdownSkin = \"article\"): PrintBlock[] {\n const seen = new Map<string, PrintBlock>();\n markdownToHtmlForSkin(markdown, skin, {\n renderBlock: (block) => {\n seen.set(printBlockKey(block), block);\n return null;\n },\n });\n return [...seen.values()];\n}\n\nfunction fencePictureHtml(picture: FencePicture): string {\n const bytes = new TextEncoder().encode(picture.svg);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i] as number);\n const src = `data:image/svg+xml;base64,${btoa(binary)}`;\n return `<figure class=\"matrx-fence-picture\"><img src=\"${src}\" alt=\"${escapeHtml(picture.alt ?? \"Diagram\")}\"></figure>`;\n}\n\n// ─── thinking-block stripping ────────────────────────────────────────────────\n\n/**\n * Strip `<thinking>` / `<think>` / `<reasoning>` blocks from model output.\n *\n * All three tags are treated as equivalent — a host that handled only\n * `<thinking>` leaked raw reasoning into printed documents the moment a model\n * switched tag names, which is exactly the bug this package exists to stop\n * being re-solved per app.\n */\nexport function removeThinkingContent(content: string): string {\n // One home for the rule (content-ir/source): paragraph structure around a hidden block survives\n // (verify-RC-B5 round 3 F6) and a tag inside code is text, never a block.\n return stripThinking(content);\n}\n\n/** Does this content carry any reasoning block? */\nexport function hasThinkingContent(content: string): boolean {\n if (!content) return false;\n return /<thinking>|<think>|<reasoning>/i.test(content);\n}\n\n// ─── shared machinery: ONE pre-pass, ONE table/list/rule grammar ────────────\n//\n// Both skins used to carry their own table parser, their own list builder,\n// their own rule regex, and their own (or no) code-fence handling — and they\n// had drifted: the article skin had no nested lists, the document skin ran the\n// emphasis pass over code, neither understood a page break, and both pasted an\n// `<artifact>`'s raw HTML (its <html>, <body> and <script>) straight into the\n// host page. Everything below is shared; a skin supplies only class names and\n// the HTML for a code block / flashcard deck.\n\n// Private-use delimiters: they cannot collide with author text (the input is\n// scrubbed of them first) and no markdown pass treats them as syntax.\nconst TOKEN_OPEN = \"\\uE000\";\nconst TOKEN_CLOSE = \"\\uE001\";\nconst BLOCK_TOKEN = (i: number): string => `${TOKEN_OPEN}B${i}${TOKEN_CLOSE}`;\nconst INLINE_TOKEN = (i: number): string => `${TOKEN_OPEN}I${i}${TOKEN_CLOSE}`;\nconst TOKEN_RE = /\\uE000([BI])(\\d+)\\uE001/g;\nconst PARAGRAPH_WRAPPED_BLOCK_RE = /<p[^>]*>\\s*(\\uE000B\\d+\\uE001)\\s*<\\/p>/g;\n\n/** Converted HTML parked behind a placeholder so later passes cannot touch it. */\nclass Stash {\n private readonly items: string[] = [];\n\n block(html: string): string {\n return BLOCK_TOKEN(this.items.push(html) - 1);\n }\n\n inline(html: string): string {\n return INLINE_TOKEN(this.items.push(html) - 1);\n }\n\n restore(html: string): string {\n // Items can hold tokens of their own (inline code inside a link), so\n // restore until nothing is left — bounded, since items never self-nest.\n let out = html;\n for (let pass = 0; pass < 8 && out.includes(TOKEN_OPEN); pass++) {\n out = out\n .replace(PARAGRAPH_WRAPPED_BLOCK_RE, \"$1\")\n .replace(TOKEN_RE, (_m: string, _k: string, i: string) => this.items[Number(i)] ?? \"\");\n }\n // Raw-HTML footnote masking is normally restored before inline rendering,\n // but indented code is stashed earlier in that pipeline. Restore once more\n // after every nested stash expansion so code samples keep their exact bytes.\n return restoreMaskedFootnotes(out);\n }\n}\n\ninterface SkinGrammar {\n skin: MarkdownSkin;\n table: string;\n thead: string;\n tbody: string;\n tr: string;\n th: string;\n td: string;\n ul: string;\n ol: string;\n ulNested: string;\n olNested: string;\n li: string;\n hr: string;\n del: string;\n strong: string;\n em: string;\n inlineCode: string;\n blockquote: string;\n}\n\nconst DOCUMENT_GRAMMAR: SkinGrammar = {\n skin: \"document\",\n table: \"matrx-table\",\n thead: \"matrx-table-head\",\n tbody: \"matrx-table-body\",\n tr: \"matrx-table-row\",\n th: \"matrx-table-header\",\n td: \"matrx-table-cell\",\n ul: \"matrx-list matrx-bullet-list\",\n ol: \"matrx-list matrx-numbered-list\",\n ulNested: \"matrx-list matrx-bullet-list matrx-nested-list\",\n olNested: \"matrx-list matrx-numbered-list matrx-nested-list\",\n li: \"matrx-list-item\",\n hr: \"matrx-hr\",\n del: \"matrx-del\",\n strong: \"matrx-strong\",\n em: \"matrx-em\",\n inlineCode: \"matrx-inline-code\",\n blockquote: \"matrx-blockquote\",\n};\n\nconst ARTICLE_GRAMMAR: SkinGrammar = {\n skin: \"article\",\n table: \"print-table\",\n thead: \"\",\n tbody: \"\",\n tr: \"\",\n th: \"\",\n td: \"\",\n ul: \"\",\n ol: \"\",\n ulNested: \"\",\n olNested: \"\",\n li: \"\",\n hr: \"\",\n del: \"\",\n strong: \"\",\n em: \"\",\n inlineCode: \"inline-code\",\n blockquote: \"\",\n};\n\nconst cls = (...names: string[]): string => {\n const joined = names.filter(Boolean).join(\" \");\n return joined ? ` class=\"${joined}\"` : \"\";\n};\n\n/** The rendered page break — identical markup in both skins. */\nconst PAGE_BREAK_HTML = `<div class=\"${PAGE_BREAK_CLASS}\" role=\"separator\" aria-label=\"${PAGE_BREAK_LABEL}\" data-label=\"${PAGE_BREAK_LABEL}\"></div>`;\n\nfunction codeBlockHtml(g: SkinGrammar, code: string, language: string): string {\n const body = escapeHtml(code.replace(/\\n$/, \"\"));\n const lang = language.trim();\n if (g.skin === \"article\") {\n const label = lang ? `<span class=\"code-lang\">${escapeHtml(lang)}</span>` : \"\";\n return `<div class=\"code-block-wrapper\">${label}<pre class=\"code-block\"><code>${body}</code></pre></div>`;\n }\n const dataLang = lang ? ` data-language=\"${escapeHtml(lang)}\"` : \"\";\n return `<pre class=\"matrx-code-block\"${dataLang}><code class=\"matrx-code\">${body}</code></pre>`;\n}\n\n// ── artifacts ───────────────────────────────────────────────────────────────\n\n/** Artifact types whose body is source code, printed as its source. */\nconst CODE_ARTIFACT_LANGUAGE: Readonly<Record<string, string>> = {\n html: \"html\",\n react: \"tsx\",\n svg: \"svg\",\n mermaid: \"mermaid\",\n diff: \"diff\",\n iframe: \"html\",\n code: \"\",\n};\n\nconst ARTIFACT_TYPE_LABEL: Readonly<Record<string, string>> = {\n html: \"HTML\",\n svg: \"SVG\",\n react: \"React\",\n iframe: \"Embed\",\n};\n\nfunction parseTagAttributes(tag: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const m of tag.matchAll(/([a-zA-Z_][\\w-]*)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g)) {\n attrs[(m[1] as string).toLowerCase()] = m[2] ?? m[3] ?? \"\";\n }\n return attrs;\n}\n\nfunction artifactLabel(type: string): string {\n if (!type) return \"Artifact\";\n return (\n ARTIFACT_TYPE_LABEL[type] ??\n type.replace(/[-_]+/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase())\n );\n}\n\n/**\n * `<artifact …>body</artifact>` envelopes as plain markdown — the SAME rules the\n * skins use for their titled panels, for pipelines that parse markdown (the\n * document writer): source-code types and JSON bodies become fenced code;\n * every other body (tables, checklists, notes) is markdown and is kept as is.\n * Fenced code is never touched.\n */\nexport function unwrapArtifactsToMarkdown(markdown: string): string {\n if (!/<artifact\\b/i.test(markdown)) return markdown;\n return markdown\n .split(/(^[ \\t]*(?:`{3,}|~{3,})[\\s\\S]*?^[ \\t]*(?:`{3,}|~{3,})[ \\t]*$)/m)\n .map((part, i) =>\n i % 2 === 1\n ? part\n : part.replace(/<artifact\\b((?:[^>\"']|\"[^\"]*\"|'[^']*')*?)>([\\s\\S]*?)<\\/artifact>/gi, (_m, tag: string, body: string) => {\n const attrs = parseTagAttributes(tag);\n const type = (attrs.type ?? \"\").toLowerCase();\n const trimmed = body.replace(/^\\n+|\\n+$/g, \"\");\n if (!trimmed.trim()) return \"\";\n if (/^\\s*(`{3,}|~{3,})/.test(trimmed)) return `\\n\\n${trimmed}\\n\\n`;\n if (type in CODE_ARTIFACT_LANGUAGE) {\n const lang = CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || \"\";\n return `\\n\\n\\`\\`\\`\\`${lang}\\n${trimmed}\\n\\`\\`\\`\\`\\n\\n`;\n }\n if (looksLikeJson(trimmed)) return `\\n\\n\\`\\`\\`\\`json\\n${JSON.stringify(JSON.parse(trimmed), null, 2)}\\n\\`\\`\\`\\`\\n\\n`;\n return `\\n\\n${trimmed}\\n\\n`;\n }),\n )\n .join(\"\");\n}\n\nfunction looksLikeJson(body: string): boolean {\n const t = body.trim();\n if (!/^[[{]/.test(t)) return false;\n try {\n JSON.parse(t);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * An `<artifact type id version title>body</artifact>` block, rendered as a\n * titled panel. Source-code artifacts (html, react, svg, mermaid, diff, code)\n * and JSON bodies print as escaped source — never as live markup, which is\n * what used to inject a whole `<html>…<script>` document into the host page.\n * Everything else (checklists, flashcards, research, notes) is markdown and is\n * converted in the same skin.\n */\nfunction artifactHtml(\n g: SkinGrammar,\n attrs: Record<string, string>,\n body: string | null,\n ctx: ConvertContext,\n): string {\n const type = (attrs.type ?? \"\").toLowerCase();\n const title = attrs.title?.trim() ?? \"\";\n const header = `<div class=\"matrx-artifact-header\"><span class=\"matrx-artifact-kind\">${escapeHtml(\n artifactLabel(type),\n )}</span>${title ? `<span class=\"matrx-artifact-title\">${escapeHtml(title)}</span>` : \"\"}</div>`;\n\n let inner: string;\n const trimmed = (body ?? \"\").replace(/^\\n+|\\n+$/g, \"\");\n const rendered =\n body !== null && trimmed.trim()\n ? ctx.options.renderBlock?.({ source: \"artifact\", type, body: trimmed, attributes: attrs, ...(title ? { title } : {}) })\n : null;\n if (rendered) {\n inner = printBlockRenderHtml(rendered);\n } else if (body === null || !trimmed.trim()) {\n inner = `<p class=\"matrx-artifact-empty\">Saved artifact — open it in AI Matrx to view its content.</p>`;\n } else if (/^\\s*(`{3,}|~{3,})/.test(trimmed)) {\n inner = convertBody(g.skin, trimmed, ctx);\n } else if (type in CODE_ARTIFACT_LANGUAGE) {\n inner = codeBlockHtml(g, trimmed, CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || \"\");\n } else if (looksLikeJson(trimmed)) {\n inner = codeBlockHtml(g, JSON.stringify(JSON.parse(trimmed), null, 2), \"json\");\n } else {\n inner = convertBody(g.skin, trimmed, ctx);\n }\n\n return `<section class=\"matrx-artifact\" data-artifact-type=\"${escapeHtml(type || \"artifact\")}\">${header}<div class=\"matrx-artifact-body\">${inner}</div></section>`;\n}\n\n// ── flashcards ──────────────────────────────────────────────────────────────\n\ninterface ParsedCard {\n front: string;\n back: string;\n}\n\nfunction parseFlashcards(content: string, notice: MarkdownToHtmlOptions[\"onNotice\"]): ParsedCard[] {\n const cards: ParsedCard[] = [];\n for (const card of content.split(/\\n*---\\n*/g).map((c) => c.trim()).filter(Boolean)) {\n const front = card.match(/Front:\\s*(.+?)(?=\\n|$)/i)?.[1]?.trim();\n const back = card.match(/Back:\\s*([\\s\\S]+?)$/i)?.[1]?.trim();\n if (!front || !back) {\n // A card that silently disappears from a printed deck is a defect the\n // printer can see and the author cannot.\n notice?.({\n code: \"flashcard-malformed\",\n message: `A flashcard was skipped: it is missing a ${front ? \"Back:\" : \"Front:\"} line.`,\n remedy:\n 'Every card inside <flashcards> needs both \"Front: …\" and \"Back: …\", separated from the next card by ---.',\n });\n continue;\n }\n cards.push({ front, back });\n }\n return cards;\n}\n\nfunction flashcardsHtml(g: SkinGrammar, cards: ParsedCard[], ctx: ConvertContext): string {\n if (g.skin === \"article\") {\n // Paper cannot flip: print the deck as question / answer pairs.\n return `<div class=\"matrx-flashcards-print\">${cards\n .map(\n (c, i) =>\n `<div class=\"matrx-fc\"><div class=\"matrx-fc-front\"><span class=\"matrx-fc-num\">${\n i + 1\n }</span>${c.front}</div><div class=\"matrx-fc-back\">${c.back}</div></div>`,\n )\n .join(\"\")}</div>`;\n }\n let out = '<div class=\"matrx-flashcards-container\">\\n';\n for (const { front, back } of cards) {\n ctx.cardIndex++;\n const cardId = `card${ctx.cardIndex}`;\n out += ` <div class=\"matrx-flashcard\">\n <input type=\"checkbox\" class=\"matrx-flashcard-input\" id=\"${cardId}\">\n <label for=\"${cardId}\" class=\"matrx-flashcard-label\"></label>\n <div class=\"matrx-flashcard-inner\">\n <div class=\"matrx-flashcard-front\">\n <div class=\"matrx-flashcard-content\">\n <strong>${front}</strong>\n </div>\n <span class=\"matrx-flashcard-indicator\">CLICK TO FLIP</span>\n </div>\n <div class=\"matrx-flashcard-back\">\n <div class=\"matrx-flashcard-content\">\n ${back}\n </div>\n <span class=\"matrx-flashcard-indicator\">CLICK TO FLIP</span>\n </div>\n </div>\n </div>\n\n`;\n }\n return `${out}</div>\\n`;\n}\n\n// ── the pre-pass ────────────────────────────────────────────────────────────\n\ninterface ConvertContext {\n options: MarkdownToHtmlOptions;\n depth: number;\n cardIndex: number;\n strippedAnnounced: boolean;\n}\n\nconst FENCE_OPEN = /^(\\s*)(`{3,}|~{3,})\\s*([^\\s`]*)[^`]*$/;\n\n/**\n * Pull every region the markdown passes must not touch out of the text:\n * fenced code, `<artifact>` blocks, page-break directives, `<flashcards>`,\n * inline code — and remove what must never reach a printed page (scripts,\n * styles, a document shell, HTML comments). Returns the text with\n * placeholders; `stash.restore` puts the finished HTML back.\n */\nfunction protectRegions(markdown: string, g: SkinGrammar, stash: Stash, ctx: ConvertContext): string {\n // Math FIRST — what is math is decided by the shared tokenizer\n // (@ai-matrx/content-ir/source, via /document's extractMath): code is never\n // math, a `$5` price is never math, and a formula's `_`, `*`, `\\,` and `|`\n // never reach the markdown passes below. Each span prints typeset\n // (typesetMathHtml), in prose and in table cells alike.\n const { text: withoutMath, spans } = extractMath(markdown);\n const prepared = withoutMath.replace(MATH_TOKEN_RE, (_m: string, index: string) => {\n const span = spans[Number(index)];\n if (!span) return \"\";\n return span.display ? stash.block(typesetMathHtml(span)) : stash.inline(typesetMathHtml(span));\n });\n // Reference-style links resolve against the document's definitions, in prose\n // and in table cells alike, and a definition line prints nothing — as GFM\n // reads them (THE link-reference rule, @ai-matrx/content-ir/source;\n // verify-RC-B4 round 9, R9-4). Fenced code is left alone.\n // The footnote parser must not read raw HTML, but it must keep prose before\n // or after that HTML on the same line. Mask only the footnote introducer (\"[^\") in\n // opaque regions and restore it before the normal HTML pass below.\n const kinds = fenceLineKinds(prepared);\n const footnoteSafe = maskFootnotesInRawHtml(prepared, kinds);\n const defs = collectLinkDefinitions(footnoteSafe);\n const definitionLines = new Set(findLinkDefinitionLines(footnoteSafe));\n const lines = footnoteSafe.split(\"\\n\").map((raw, k) => {\n const line = raw.replace(/\\r$/, \"\");\n if (definitionLines.has(k)) return \"\";\n if (kinds[k] !== \"prose\" || defs.size === 0) return line;\n return resolveReferenceLinks(line, defs);\n });\n // Footnotes, as GFM and the screen show them: numbered references (order of\n // first reference, prose and cells alike) and one footnote section at the end\n // (verify-RC-B4 round 9).\n const footnoteSection = extractFootnotes(lines, kinds, g, stash);\n const out: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] as string;\n\n // Fenced code (``` or ~~~, any indent, unclosed = to the end).\n const fence = FENCE_OPEN.exec(line);\n if (fence) {\n const marker = fence[2] as string;\n const code: string[] = [];\n let j = i + 1;\n for (; j < lines.length; j++) {\n const close = /^\\s*(`{3,}|~{3,})\\s*$/.exec(lines[j] as string);\n if (close && (close[1] as string)[0] === marker[0] && (close[1] as string).length >= marker.length) break;\n code.push(lines[j] as string);\n }\n const indent = (fence[1] as string).length;\n const dedented = code.map((l) => l.slice(Math.min(indent, l.length - l.trimStart().length)));\n const rendered = ctx.options.renderBlock?.({\n source: \"fence\",\n type: (fence[3] ?? \"\").trim().toLowerCase(),\n body: dedented.join(\"\\n\"),\n });\n out.push(\n stash.block(\n rendered ? printBlockRenderHtml(rendered) : codeBlockHtml(g, dedented.join(\"\\n\"), fence[3] ?? \"\"),\n ),\n );\n i = j;\n continue;\n }\n\n if (isBreakLine(line)) {\n out.push(stash.block(PAGE_BREAK_HTML));\n continue;\n }\n // Placement directives only mean something to the document pipeline\n // (@ai-matrx/print/document); a skin never prints them as literal text.\n if (isTocLine(line) || isBibliographyLine(line)) continue;\n\n // <artifact …>body</artifact> — the canonical materialized wire form.\n const open = /^\\s*<artifact\\b((?:[^>\"']|\"[^\"]*\"|'[^']*')*?)(\\/?)>(.*)$/i.exec(line);\n if (open) {\n const attrs = parseTagAttributes(open[1] ?? \"\");\n if (open[2] === \"/\") {\n out.push(stash.block(artifactHtml(g, attrs, null, ctx)));\n if (open[3]?.trim()) out.push(open[3]);\n continue;\n }\n const bodyLines: string[] = [];\n let rest = open[3] ?? \"\";\n let after = \"\";\n let j = i;\n let inFence: string | null = null;\n for (;;) {\n const closeAt = inFence ? -1 : rest.search(/<\\/artifact\\s*>/i);\n if (closeAt >= 0) {\n bodyLines.push(rest.slice(0, closeAt));\n after = rest.slice(closeAt).replace(/^<\\/artifact\\s*>/i, \"\");\n break;\n }\n bodyLines.push(rest);\n const f = /^\\s*(`{3,}|~{3,})/.exec(rest);\n if (f) {\n const m = f[1] as string;\n if (inFence === null) inFence = m;\n else if (m[0] === inFence[0] && m.length >= inFence.length) inFence = null;\n }\n j++;\n if (j >= lines.length) break;\n rest = lines[j] as string;\n }\n const html =\n ctx.depth >= 3\n ? codeBlockHtml(g, bodyLines.join(\"\\n\"), \"\")\n : artifactHtml(g, attrs, bodyLines.join(\"\\n\"), { ...ctx, depth: ctx.depth + 1 });\n out.push(stash.block(html));\n if (after.trim()) out.push(after);\n i = j;\n continue;\n }\n\n // An indented code block (CommonMark 4.4): a run of lines indented 4+ columns\n // after a blank line (or at the start), outside any list — ` | a | b |`\n // there is code, never a table (verify-RC-B4 round 9, R9-6).\n if (lineIndent(line) >= 4 && line.trim() && startsIndentedCode(lines, i)) {\n let j = i;\n while (j < lines.length && (!(lines[j] ?? \"\").trim() || lineIndent(lines[j] ?? \"\") >= 4)) j += 1;\n while (j > i && !(lines[j - 1] ?? \"\").trim()) j -= 1;\n const code = lines.slice(i, j).map((l) => l.replace(/^(?: {4}|\\t| {1,3}\\t)/, \"\"));\n out.push(stash.block(codeBlockHtml(g, code.join(\"\\n\"), \"\")));\n i = j - 1;\n continue;\n }\n\n // A GFM table inside a block quote (`> | a | b |`): the quote's markers are\n // stripped and THE rule reads the lines inside, so it prints as GFM draws it —\n // a table inside the quote, as many levels deep as it sits (verify-RC-B4\n // round 9). The quote's other lines keep their own pass.\n const quoted = quotedTableAt(lines, i);\n if (quoted) {\n const open = `<blockquote${cls(g.blockquote)}>`.repeat(quoted.depth);\n const close = \"</blockquote>\".repeat(quoted.depth);\n out.push(stash.block(`${open}${tableHtml(quoted.rows, g, stash)}${close}`));\n i = quoted.end - 1;\n continue;\n }\n\n // A GFM table — found and split BEFORE any inline pass (THE rule,\n // @ai-matrx/content-ir/source), so code spans, escapes and emphasis are read\n // per cell exactly as GFM reads a cell, and a line that starts another block\n // (`> 8C | …`, `- x`) ends the table and is left for its own pass.\n if (tableStartsAt(lines, i)) {\n const end = findTableEnd(lines, i);\n out.push(stash.block(tableHtml(lines.slice(i, end), g, stash)));\n i = end - 1;\n continue;\n }\n\n out.push(line);\n }\n if (footnoteSection) out.push(\"\", stash.block(footnoteSection));\n\n let text = restoreMaskedFootnotes(out.join(\"\\n\"));\n\n // Inline code FIRST: prose about HTML (\"put it in a `<script>` tag\") must\n // reach the page as code, not be read as markup by the passes below.\n text = text.replace(/`([^`\\n]+)`/g, (_m: string, code: string) =>\n stash.inline(`<code${cls(g.inlineCode)}>${escapeHtml(code)}</code>`),\n );\n // A block tag written INSIDE a line of prose, a quote or a list item\n // (`Handles native <table> with thead…`) is inline text — dropped as the\n // screen drops it, never a raw empty table or a stray div on the page\n // (verify-RC-B4 round 9, R9-3; a line that STARTS with the tag is an HTML block).\n text = text\n .split(\"\\n\")\n .map((line) =>\n // A line that opens with a tag is an HTML block: every tag on it stays.\n line.replace(LINE_LEAD_RE, \"\").startsWith(\"<\")\n ? line\n : line.replace(INLINE_TAG_RE, (whole: string, name: string) => (isInlineBlockTag(name) ? \"\" : whole)),\n )\n .join(\"\\n\");\n // Backslash escapes (CommonMark): `\\*` is a literal `*`, `\\\\` a literal `\\` —\n // never syntax for the passes below, never a printed backslash.\n text = resolveEscapes(text, stash);\n\n // Comments are invisible by definition — page-break comments were already\n // taken above, so what remains is safe to drop without a notice.\n text = text.replace(/<!--[\\s\\S]*?-->/g, \"\");\n\n // A printed page and a published HTML page are not a place to run someone\n // else's script or restyle the host. Paired regions go whole; a stray\n // opening/closing tag goes alone (never \"everything to the end\"). Repeat\n // until stable so a split `<scr<script></script>ipt>` cannot reassemble.\n const before = text;\n for (let pass = 0; pass < 8; pass++) {\n const next = text\n .replace(/<(script|style|head)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi, \"\")\n .replace(/<\\/?(?:script|style|head|html|body)\\b[^>]*>/gi, \"\")\n .replace(/<!DOCTYPE[^>]*>/gi, \"\");\n if (next === text) break;\n text = next;\n }\n text = sanitizeRawTags(text);\n // The allow-list (`./safe-html`) is what actually guards the output; this\n // only decides whether the author is told something of theirs was dropped.\n if ((text !== before || hasDisallowedTag(text)) && !ctx.strippedAnnounced) {\n ctx.strippedAnnounced = true;\n ctx.options.onNotice?.({\n code: \"html-stripped\",\n message:\n \"Markup that is not on the safe list (a <script>, <style>, <iframe>, <form>, <object>/<embed>, <meta>, <link>, an on… event handler, inline style, or a javascript: URL) was removed from the rendered copy.\",\n remedy:\n 'To show a full HTML page, put it in an ```html fence or an <artifact type=\"html\"> block — it is printed as its source.',\n });\n }\n\n // Flashcard decks — behind placeholders so no markdown pass re-processes them.\n text = text.replace(/<flashcards>([\\s\\S]*?)<\\/flashcards>/gi, (_m: string, content: string) => {\n const cards = parseFlashcards(content, ctx.options.onNotice);\n return cards.length ? `\\n${stash.block(flashcardsHtml(g, cards, ctx))}\\n` : \"\";\n });\n\n return text;\n}\n\nconst ALLOWED_TAGS = new Set(PRINT_HTML_SCHEMA.tagNames ?? []);\n\n/** A raw tag the allow-list will drop (our own `<flashcards>` island excepted). */\nfunction hasDisallowedTag(text: string): boolean {\n for (const m of text.matchAll(/<\\/?([a-zA-Z][\\w:-]*)/g)) {\n const tag = (m[1] as string).toLowerCase();\n if (tag !== \"flashcards\" && !ALLOWED_TAGS.has(tag)) return true;\n }\n return false;\n}\n\nconst URL_ATTRS = new Set([\"href\", \"src\", \"action\", \"formaction\", \"xlink:href\", \"poster\", \"background\"]);\n\nfunction decodeForSchemeCheck(value: string): string {\n return value\n .replace(/&#x([0-9a-f]+);?/gi, (_m: string, h: string) => String.fromCharCode(parseInt(h, 16)))\n .replace(/&#(\\d+);?/g, (_m: string, d: string) => String.fromCharCode(Number(d)))\n .replace(/&colon;/gi, \":\")\n .replace(/[\\u0000-\\u0020]+/g, \"\")\n .toLowerCase();\n}\n\n/**\n * Author raw HTML survives (the document skin feeds WordPress), but never with\n * an `on…` handler, a `srcdoc`, or a script-scheme URL — entity- and\n * whitespace-obfuscated forms included.\n */\nfunction sanitizeRawTags(text: string): string {\n return text.replace(\n /<([a-zA-Z][\\w:-]*)((?:[^>\"']|\"[^\"]*\"|'[^']*')*)>/g,\n (whole: string, tag: string, rawAttrs: string) => {\n if (!/[=\\s]/.test(rawAttrs)) return whole;\n let changed = false;\n const attrs = rawAttrs.replace(\n /(\\s+)([^\\s\"'=<>/]+)(\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/g,\n (m: string, _sp: string, name: string, _eq: string, dq?: string, sq?: string, bare?: string) => {\n const lower = name.toLowerCase();\n if (lower.startsWith(\"on\") || lower === \"srcdoc\") {\n changed = true;\n return \"\";\n }\n if (URL_ATTRS.has(lower)) {\n const value = decodeForSchemeCheck(dq ?? sq ?? bare ?? \"\");\n if (/^(?:javascript|vbscript):|^data:text\\/html/.test(value)) {\n changed = true;\n return ` ${name}=\"#\"`;\n }\n }\n return m;\n },\n );\n return changed ? `<${tag}${attrs}>` : whole;\n },\n );\n}\n\n/** A URL that can go in an attribute: no script schemes, no attribute breakout. */\nfunction safeUrl(url: string): string {\n const u = url.trim();\n if (/^(?:javascript|vbscript):|^data:text\\/html/.test(decodeForSchemeCheck(u))) return \"#\";\n return u.replace(/\"/g, \"%22\");\n}\n\n/**\n * Bold / italic / strikethrough, shared by both skins. Underscores follow the\n * CommonMark intraword rule — `snake_case_name` stays literal.\n */\nfunction convertEmphasis(html: string, g: SkinGrammar): string {\n const strong = `<strong${cls(g.strong)}>`;\n const em = `<em${cls(g.em)}>`;\n return html\n .replace(/\\*\\*\\*(?!\\s)(.+?)(?<!\\s)\\*\\*\\*/g, `${strong}${em}$1</em></strong>`)\n .replace(/\\*\\*(?!\\s)(.+?)(?<!\\s)\\*\\*/g, `${strong}$1</strong>`)\n .replace(/(?<![\\w*])\\*(?![\\s*])(.+?)(?<![\\s*])\\*(?![\\w*])/g, `${em}$1</em>`)\n .replace(/(?<![\\w_])___(?!\\s)(.+?)(?<!\\s)___(?![\\w_])/g, `${strong}${em}$1</em></strong>`)\n .replace(/(?<![\\w_])__(?!\\s)(.+?)(?<!\\s)__(?![\\w_])/g, `${strong}$1</strong>`)\n .replace(/(?<![\\w_])_(?![\\s_])(.+?)(?<![\\s_])_(?![\\w_])/g, `${em}$1</em>`)\n .replace(/~~(?!\\s)(.+?)(?<!\\s)~~/g, `<del${cls(g.del)}>$1</del>`);\n}\n\n/** `[text](url)` / `![alt](url \"title\")` with one level of parentheses in the URL. */\nconst LINK_URL = String.raw`((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?`;\nconst IMAGE_RE = new RegExp(String.raw`!\\[([^\\]]*)\\]\\(` + LINK_URL + String.raw`\\)`, \"g\");\nconst LINK_RE = new RegExp(String.raw`\\[([^\\]]+)\\]\\(` + LINK_URL + String.raw`\\)`, \"g\");\n\n/** `***` accent rule, `#===` heavy rule, `---` / `___` plain rule. */\nfunction convertRules(html: string, g: SkinGrammar): string {\n return html\n .replace(/^\\s{0,3}#\\s*={3,}\\s*$/gm, `<hr${cls(g.hr, \"matrx-hr-heavy\")}>`)\n .replace(/^\\s{0,3}\\*(?:\\s*\\*){2,}\\s*$/gm, `<hr${cls(g.hr, \"matrx-hr-accent\")}>`)\n .replace(/^\\s{0,3}(?:-\\s*){3,}$/gm, `<hr${cls(g.hr)}>`)\n .replace(/^\\s{0,3}(?:_\\s*){3,}$/gm, `<hr${cls(g.hr)}>`);\n}\n\nfunction cellAlign(separator: string): string {\n const s = separator.trim();\n const left = s.startsWith(\":\");\n const right = s.endsWith(\":\");\n if (left && right) return ' style=\"text-align:center\"';\n if (right) return ' style=\"text-align:right\"';\n return \"\";\n}\n\n/**\n * One math span typeset the way the screen typesets it — KaTeX, the renderer\n * every chat and Studio surface uses — as KaTeX's laid-out HTML with MathML\n * for accessibility, mhchem loaded for chemistry. The KaTeX stylesheet (fonts\n * pinned to the installed version) ships in every skin's stylesheet. TeX KaTeX cannot parse\n * prints as its source in the error colour, exactly as on screen.\n */\n/**\n * The print allow-list for authored HTML; typeset math is restored after it.\n */\nfunction sanitizePrintHtmlWithMath(html: string): string {\n // Typeset math rides through the sanitizer as an opaque token and is put\n // back after it: the allow-list strips every class and inline style, and\n // KaTeX's layout IS classes plus computed heights. KaTeX runs with\n // `trust: false`, which refuses every command that could inject HTML\n // (\\href, \\url, \\htmlClass, \\htmlId, \\htmlStyle, \\htmlData,\n // \\includegraphics), so its output is safe by construction.\n const math = typesetMath;\n typesetMath = [];\n return sanitizeEngineHtml(html).replace(MATH_TOKEN_OUT_RE, (_m: string, i: string) => math[Number(i)] ?? \"\");\n}\n\n/** KaTeX output held out of the sanitizer for the conversion in flight. */\nlet typesetMath: string[] = [];\nconst MATH_TOKEN_OUT_RE = /\\uE017(\\d+)\\uE018/g;\n\nfunction typesetMathHtml(span: MathSpan): string {\n // The ONE KaTeX configuration (math-text.ts), held out of the sanitizer.\n return `\\uE017${typesetMath.push(typesetTex(span.tex, span.display)) - 1}\\uE018`;\n}\n\n/** A CommonMark backslash escape: `\\` before ASCII punctuation. */\nconst ESCAPE_RE = /\\\\([!-\\/:-@[-`{-~])/g;\n\nfunction resolveEscapes(text: string, stash: Stash): string {\n return text.replace(ESCAPE_RE, (_m: string, ch: string) => stash.inline(escapeHtml(ch)));\n}\n\n/**\n * One cell's inline content, read the way GFM reads a cell: the table-level\n * `\\|` → `|` first (inside code spans too, spec example 200), then code spans,\n * backslash escapes, images, links and emphasis — never block syntax.\n */\nconst INLINE_TAG_RE = /<\\/?([A-Za-z][\\w-]*)\\b(?:[^<>\"']|\"[^\"]*\"|'[^']*')*>/g;\n/** A line's lead-in before its text: indentation, quote markers, a list marker. */\nconst LINE_LEAD_RE = /^\\s*(?:>\\s*)*(?:(?:[-+*]|\\d{1,9}[.)])\\s+)?/;\n/** A tag that opens a block (or a table part) — never meaningful inside a line of text. */\nconst isInlineBlockTag = (name: string): boolean =>\n isHtmlBlockTagName(name) || /^(?:thead|tbody|tfoot|tr|td|th|caption|colgroup|col|li|dt|dd)$/i.test(name);\n\nfunction cellInlineHtml(cell: string, g: SkinGrammar, stash: Stash): string {\n // Code spans by THE one code-range rule (@ai-matrx/content-ir/source), read as\n // INLINE content: a cell opening with ```` is a code span, never a fence.\n let t = mapInlineCodeSpans(unescapeCellPipes(cell), (_range, raw) =>\n stash.inline(`<code${cls(g.inlineCode)}>${escapeHtml(codeSpanText(raw))}</code>`),\n );\n // A cell is INLINE content: a block tag written in it (`native <table> exports`)\n // is dropped, as the screen drops it — it never opens a nested table that\n // swallows the next row (verify-RC-B4 round 9, R9-3).\n t = t.replace(INLINE_TAG_RE, (whole: string, name: string) => (isInlineBlockTag(name) ? \"\" : whole));\n t = resolveEscapes(t, stash);\n t = t.replace(IMAGE_RE, (_m: string, alt: string, url: string) =>\n stash.inline(`<img class=\"${g.skin === \"document\" ? \"matrx-image\" : \"print-image\"}\" src=\"${safeUrl(url)}\" alt=\"${escapeHtml(alt)}\" />`),\n );\n t = t.replace(LINK_RE, (_m: string, text: string, url: string) =>\n stash.inline(`<a${g.skin === \"document\" ? ' class=\"matrx-link\"' : \"\"} href=\"${safeUrl(url)}\">${convertEmphasis(text, g)}</a>`),\n );\n return convertEmphasis(t, g);\n}\n\n/**\n * A GFM table by THE rule (@ai-matrx/content-ir/source — the rule chat, the\n * editor and the server read tables by): edge pipes optional, `\\|` stays in its\n * cell, every row printed at the header's width (empty cells kept — columns\n * never shift), `:---:` alignment honoured, each cell read as inline content.\n */\n/**\n * Line `i` (indented 4+) opens an indented code block: it follows a blank line or\n * starts the text, and the nearest line above the blank is no list line (a list\n * item's indented content belongs to the item).\n */\nfunction startsIndentedCode(lines: readonly string[], i: number): boolean {\n if (i > 0 && (lines[i - 1] ?? \"\").trim()) return false;\n let k = i - 1;\n while (k >= 0 && !(lines[k] ?? \"\").trim()) k -= 1;\n if (k < 0) return true;\n const above = lines[k] ?? \"\";\n return lineIndent(above) === 0 && !/^\\s*(?:[-+*]|\\d{1,9}[.)])\\s/.test(above);\n}\n\nconst FOOTNOTE_DEF = /^ {0,3}\\[\\^([^\\]\\s]+)\\]:[ \\t]?(.*)$/;\nconst FOOTNOTE_REF = /\\[\\^([^\\]\\s]+)\\](?!:)/g;\nconst BLOCK_START_LINE = /^ {0,3}(?:[-+*]\\s|\\d{1,9}[.)]\\s|>|#{1,6}\\s|\\||```|~~~|\\[\\^[^\\]\\s]+\\]:)/;\nconst THEMATIC_BREAK_LINE = /^ {0,3}(?:(?:-[ \\t]*){3,}|(?:\\*[ \\t]*){3,}|(?:_[ \\t]*){3,})$/;\n/**\n * A line that ends a note's paragraph instead of continuing it lazily: a block\n * start, a thematic break (`-----` under a note is a rule, not its text), or an\n * HTML block that may interrupt a paragraph (CommonMark 4.6 conditions 1–6; a\n * lone complete tag, condition 7, may not).\n */\nfunction interruptsNoteParagraph(line: string): boolean {\n if (BLOCK_START_LINE.test(line) || THEMATIC_BREAK_LINE.test(line)) return true;\n const t = line.trim();\n if (/^<(?:!--|\\?|![A-Za-z]|!\\[CDATA\\[)/.test(t)) return true;\n const tag = /^<(\\/?)([A-Za-z][A-Za-z0-9-]*)(?:[\\s>]|\\/>|$)/.exec(t);\n if (!tag || !isHtmlBlockTagName(tag[2] as string)) return false;\n return !(tag[1] && /^(?:script|pre|style|textarea)$/i.test(tag[2] as string));\n}\nconst FOOTNOTE_PROTECTED_TOKEN = /\\uE013(\\d+)\\uE013/g;\nconst FOOTNOTE_STASH_TOKEN = /\\uE000I\\d+\\uE001/g;\n\n/** The matching `]` or `)`, respecting nested delimiters and CommonMark escapes. */\nfunction closingInlineDelimiter(text: string, start: number, open: string, close: string): number {\n let depth = 1;\n for (let i = start + 1; i < text.length; i += 1) {\n if (text[i] === \"\\\\\") {\n i += 1;\n continue;\n }\n if (text[i] === open) depth += 1;\n else if (text[i] === close && --depth === 0) return i;\n }\n return -1;\n}\n\n/**\n * Footnotes are valid in a link's visible label, but never in its destination\n * or an image's alt text. Unlike IMAGE_RE/LINK_RE this follows balanced\n * brackets, so `[label[^n]](url[^n])` does not leak a footnote into `href`.\n */\nfunction protectFootnoteLinkContexts(text: string, protect: (raw: string) => string): string {\n let out = \"\";\n for (let i = 0; i < text.length; ) {\n const image = text.startsWith(\"![\", i);\n const link = image || text[i] === \"[\";\n if (!link) {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const labelStart = i + (image ? 1 : 0);\n const labelEnd = closingInlineDelimiter(text, labelStart, \"[\", \"]\");\n if (labelEnd < 0 || text[labelEnd + 1] !== \"(\") {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const destinationEnd = closingInlineDelimiter(text, labelEnd + 1, \"(\", \")\");\n if (destinationEnd < 0) {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n if (image) out += protect(text.slice(i, destinationEnd + 1));\n else out += `${text.slice(i, labelEnd + 2)}${protect(text.slice(labelEnd + 2, destinationEnd))})`;\n i = destinationEnd + 1;\n }\n return out;\n}\n\n/**\n * A footnote reference is itself an anchor, so it cannot remain inside the\n * anchor made for a Markdown link label. Split that label around footnote\n * tokens: its ordinary text stays linked while the reference remains a valid,\n * clickable sibling. This mirrors GFM's non-nested-anchor HTML shape.\n */\nfunction detachFootnotesFromLinkLabels(text: string, g: SkinGrammar, stash: Stash): string {\n let out = \"\";\n for (let i = 0; i < text.length; ) {\n if (text[i] !== \"[\" || text[i - 1] === \"!\") {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const labelEnd = closingInlineDelimiter(text, i, \"[\", \"]\");\n if (labelEnd < 0 || text[labelEnd + 1] !== \"(\") {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const destinationEnd = closingInlineDelimiter(text, labelEnd + 1, \"(\", \")\");\n if (destinationEnd < 0) {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const label = text.slice(i + 1, labelEnd);\n if (!FOOTNOTE_STASH_TOKEN.test(label)) {\n FOOTNOTE_STASH_TOKEN.lastIndex = 0;\n out += text.slice(i, destinationEnd + 1);\n i = destinationEnd + 1;\n continue;\n }\n FOOTNOTE_STASH_TOKEN.lastIndex = 0;\n // Match the regular inline-link grammar: a quoted title belongs to the\n // destination syntax, not to the URL placed in href.\n const destination = text.slice(labelEnd + 2, destinationEnd);\n const href = safeUrl(destination.match(new RegExp(`^${LINK_URL}$`))?.[1] ?? destination);\n for (const part of label.split(/(\\uE000I\\d+\\uE001)/g)) {\n if (!part) continue;\n out += FOOTNOTE_STASH_TOKEN.test(part)\n ? part\n : stash.inline(`<a${g.skin === \"document\" ? ' class=\"matrx-link\"' : \"\"} href=\"${href}\">${convertEmphasis(escapeHtml(part), g)}</a>`);\n FOOTNOTE_STASH_TOKEN.lastIndex = 0;\n }\n i = destinationEnd + 1;\n }\n return out;\n}\n\n/**\n * The footnote pass only reads Markdown prose. Fenced code is already\n * classified by content-ir; raw comments, stripped raw-HTML blocks, and a\n * multi-line raw tag are opaque too. In particular, an attribute split over\n * lines must not turn `[^note]` into a visible reference before raw HTML is\n * sanitized later in the rendering pipeline.\n */\nconst FOOTNOTE_RAW_MASK = \"\\uE014\";\nconst RAW_TAG_REGION = /<\\/?[A-Za-z][\\w-]*\\b(?:[^>\"']|\"[^\"]*\"|'[^']*')*>/g;\n\n/**\n * Raw HTML itself is opaque to footnote syntax, including multi-line tag\n * attributes. This masks only the footnote introducer in those regions, rather\n * than their whole lines, so surrounding Markdown prose is still parsed.\n */\nfunction maskFootnotesInRawHtml(markdown: string, kinds: readonly string[]): string {\n const mask = (raw: string): string => raw.replaceAll(\"[^\", FOOTNOTE_RAW_MASK);\n const maskProseRun = (run: string): string => {\n // Raw-tag-like text inside an inline code span is code, never HTML. Park\n // it while finding raw regions so an unclosed `<script>` code sample does\n // not make the following prose opaque.\n const codeSpans: string[] = [];\n const withoutCode = mapInlineCodeSpans(run, (_range, raw) => `\\uE015${codeSpans.push(raw) - 1}\\uE016`);\n return withoutCode\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, mask)\n .replace(/<(script|style|head)\\b[^>]*>[\\s\\S]*?(?:<\\/\\1\\s*>|$)/gi, mask)\n .replace(RAW_TAG_REGION, mask)\n .replace(/\\uE015(\\d+)\\uE016/g, (_m: string, index: string) => codeSpans[Number(index)] ?? \"\");\n };\n const lines = markdown.split(\"\\n\");\n for (let start = 0; start < lines.length; ) {\n if (kinds[start] !== \"prose\") {\n start += 1;\n continue;\n }\n let end = start + 1;\n while (end < lines.length && kinds[end] === \"prose\") end += 1;\n const masked = maskProseRun(lines.slice(start, end).join(\"\\n\")).split(\"\\n\");\n lines.splice(start, end - start, ...masked);\n start = end;\n }\n return lines.join(\"\\n\");\n}\n\nfunction restoreMaskedFootnotes(text: string): string {\n return text.replaceAll(FOOTNOTE_RAW_MASK, \"[^\");\n}\n\n/**\n * GFM footnotes for the print skins. Every definition (`[^label]: text`, with\n * its indented or lazy continuation lines) is taken out of `lines`; every\n * reference to a defined label becomes a superscript number, numbered in order\n * of first reference; a reference to an undefined label stays literal text (as\n * GFM leaves it). Returns the footnote section (referenced definitions in\n * number order; an unreferenced definition is dropped, as GFM drops it), or \"\".\n * Fenced code is never touched; inline code spans keep their text.\n */\nfunction extractFootnotes(lines: string[], kinds: readonly string[], g: SkinGrammar, stash: Stash): string {\n if (!lines.some((line) => line.includes(\"[^\"))) return \"\";\n const bodies = new Map<string, string[][]>(); // label → paragraphs → lines\n for (let i = 0; i < lines.length; i += 1) {\n if (kinds[i] !== \"prose\") continue;\n const m = FOOTNOTE_DEF.exec(lines[i] ?? \"\");\n if (!m) continue;\n const label = (m[1] as string).toLowerCase();\n const paragraphs: string[][] = [[m[2] ?? \"\"]];\n // The previous line as WRITTEN: lines consumed into the note are blanked as\n // we go, so reading lines[j - 1] would see \"\" and refuse every lazy line.\n let previous = lines[i] ?? \"\";\n lines[i] = \"\";\n let j = i + 1;\n for (; j < lines.length && kinds[j] === \"prose\"; j += 1) {\n const line = lines[j] ?? \"\";\n if (/^(?: {4}|\\t)/.test(line)) {\n paragraphs[paragraphs.length - 1]!.push(line.replace(/^(?: {4}|\\t)/, \"\"));\n } else if (line.trim() && previous.trim() && !interruptsNoteParagraph(line)) {\n paragraphs[paragraphs.length - 1]!.push(line); // lazy continuation\n } else if (!line.trim() && /^(?: {4}|\\t)\\S/.test(lines[j + 1] ?? \"\")) {\n paragraphs.push([]); // a blank line, then an indented paragraph of the same note\n } else {\n break;\n }\n previous = line;\n lines[j] = \"\";\n }\n if (!bodies.has(label)) bodies.set(label, paragraphs.filter((p) => p.length));\n i = j - 1;\n }\n if (bodies.size === 0) return \"\";\n const numbers = new Map<string, number>();\n const seen = new Map<string, number>();\n // A reference to a defined label becomes its number, numbered on FIRST reference;\n // inline code spans keep their text.\n const numberRefs = (line: string): string => {\n if (!line.includes(\"[^\")) return line;\n const spans: string[] = [];\n const masked = mapInlineCodeSpans(line, (_range, raw) => `\\uE012${spans.push(raw) - 1}\\uE012`);\n // Footnote syntax is prose syntax. Keep code spans, image alt text, link\n // destinations, and raw-tag attributes opaque until their own passes read\n // them; only a link's visible label remains prose. This also means an\n // escaped `\\\\[^note]` remains literal through resolveEscapes below.\n const protectedRegions: string[] = [];\n const protect = (raw: string): string => `\\uE013${protectedRegions.push(raw) - 1}\\uE013`;\n const contextual = masked\n .replace(INLINE_TAG_RE, (whole: string) => protect(whole));\n const linkSafe = protectFootnoteLinkContexts(contextual, protect);\n const replaced = linkSafe.replace(FOOTNOTE_REF, (whole: string, raw: string, offset: number, source: string) => {\n let slashes = 0;\n for (let i = offset - 1; i >= 0 && source[i] === \"\\\\\"; i -= 1) slashes += 1;\n if (slashes % 2 === 1) return whole;\n const label = raw.toLowerCase();\n if (!bodies.has(label)) return whole;\n if (!numbers.has(label)) numbers.set(label, numbers.size + 1);\n const n = numbers.get(label) as number;\n const k = (seen.get(label) ?? 0) + 1; // GFM's ids: fnref-1, then fnref-1-2, …\n seen.set(label, k);\n const id = k === 1 ? `fnref-${n}` : `fnref-${n}-${k}`;\n return stash.inline(`<sup class=\"print-footnote-ref\"><a href=\"#fn-${n}\" id=\"${id}\">${n}</a></sup>`);\n });\n const restored = replaced\n .replace(FOOTNOTE_PROTECTED_TOKEN, (_m: string, k: string) => protectedRegions[Number(k)] ?? \"\")\n .replace(/\\uE012(\\d+)\\uE012/g, (_m: string, k: string) => spans[Number(k)] ?? \"\");\n return detachFootnotesFromLinkLabels(restored, g, stash);\n };\n for (let i = 0; i < lines.length; i += 1) if (kinds[i] === \"prose\") lines[i] = numberRefs(lines[i] as string);\n const items: string[] = [];\n const note = (n: number, label: string) => {\n const paragraphs = (bodies.get(label) ?? []).map((p) => `<p>${cellInlineHtml(numberRefs(p.join(\" \")), g, stash)}</p>`).join(\"\");\n items.push(`<li id=\"fn-${n}\" value=\"${n}\">${paragraphs}</li>`);\n };\n // Only referenced notes print, in number order: GFM (and so the screen) drops\n // a definition nothing references, and it never prints as text either. A\n // reference inside a note numbers a new note on first sight, and the loop\n // reaches it (a Map iterates entries added while iterating), as GFM does.\n for (const [label, n] of numbers) note(n, label);\n if (items.length === 0) return \"\";\n return `<section class=\"print-footnotes\"><hr /><ol>${items.join(\"\")}</ol></section>`;\n}\n\nconst QUOTE_MARKERS = /^(?: {0,3}> ?)+/;\n/** How many `>` quote markers open the line (0 for none). */\nconst quoteDepth = (line: string): number => (QUOTE_MARKERS.exec(line)?.[0].match(/>/g) ?? []).length;\n\n/**\n * A GFM table opening at line `i` INSIDE a block quote: the run of lines at the\n * same quote depth, markers stripped, read by THE table rule. Returns the table's\n * rows (quote markers gone), its depth and the index just past it; null when line\n * `i` opens no quoted table.\n */\nfunction quotedTableAt(lines: readonly string[], i: number): { rows: string[]; depth: number; end: number } | null {\n const depth = quoteDepth(lines[i] ?? \"\");\n if (depth === 0) return null;\n let j = i;\n const inner: string[] = [];\n while (j < lines.length && quoteDepth(lines[j] ?? \"\") === depth) {\n inner.push((lines[j] ?? \"\").replace(QUOTE_MARKERS, \"\"));\n j += 1;\n }\n // The quote's own lines above the header stay in the run: a table interrupts a\n // quoted paragraph as it does a top-level one, and a lazy line opens none.\n let start = i;\n while (start > 0 && quoteDepth(lines[start - 1] ?? \"\") === depth) start -= 1;\n const context = lines.slice(start, i).map((line) => line.replace(QUOTE_MARKERS, \"\"));\n const all = [...context, ...inner];\n const at = context.length;\n if (!tableStartsAt(all, at)) return null;\n const end = findTableEnd(all, at);\n return { rows: all.slice(at, end), depth, end: i + (end - at) };\n}\n\nfunction tableHtml(lines: readonly string[], g: SkinGrammar, stash: Stash): string {\n const cellsOf = (line: string) => rowCells(line).map((cell) => cellInlineHtml(cell, g, stash));\n const headers = cellsOf(lines[0] ?? \"\");\n const aligns = rowCells(lines[1] ?? \"\").map(cellAlign);\n const rows = lines.slice(2).map((line) => {\n const cells = cellsOf(line);\n return headers.map((_h, k) => cells[k] ?? \"\");\n });\n let t = `<table${cls(g.table)}>\\n<thead${cls(g.thead)}>\\n<tr${cls(g.tr)}>\\n`;\n headers.forEach((h, k) => {\n t += `<th${cls(g.th)}${aligns[k] ?? \"\"}>${h}</th>\\n`;\n });\n t += \"</tr>\\n</thead>\\n\";\n if (rows.length > 0) {\n t += `<tbody${cls(g.tbody)}>\\n`;\n for (const row of rows) {\n t += `<tr${cls(g.tr)}>\\n`;\n row.forEach((c, k) => {\n t += `<td${cls(g.td)}${aligns[k] ?? \"\"}>${c}</td>\\n`;\n });\n t += \"</tr>\\n\";\n }\n t += \"</tbody>\\n\";\n }\n return `${t}</table>`;\n}\n\ninterface OpenList {\n type: \"ul\" | \"ol\";\n indent: number;\n}\n\n/** Nested ordered/bullet lists — indentation-driven open/close bookkeeping. */\n/** A line that is already a block of its own at the list pass — never an item's continuation text. */\nconst LIST_BLOCK_LINE_RE = /^(?:<\\/?(?:h[1-6]|ul|ol|li|blockquote|pre|hr|table|thead|tbody|tr|th|td|div|figure|section)\\b|\\uE000B\\d+\\uE001$)/;\n\nfunction convertLists(html: string, g: SkinGrammar): string {\n const lines = html.split(\"\\n\");\n let processed = \"\";\n const listStack: OpenList[] = [];\n let lastListItem: \"\" | \"ul\" | \"ol\" = \"\";\n const li = `<li${cls(g.li)}>`;\n\n const top = (): OpenList | undefined => listStack[listStack.length - 1];\n const openTag = (type: \"ul\" | \"ol\", nested: boolean): string =>\n type === \"ol\"\n ? `<ol${cls(nested ? g.olNested : g.ol)}>\\n`\n : `<ul${cls(nested ? g.ulNested : g.ul)}>\\n`;\n\n const popOne = (): void => {\n if (lastListItem) {\n processed += \"</li>\\n\";\n lastListItem = \"\";\n }\n const closing = listStack.pop();\n if (!closing) return;\n processed += closing.type === \"ol\" ? \"</ol>\\n\" : \"</ul>\\n\";\n if (listStack.length > 0) processed += \"</li>\\n\";\n };\n\n const closeDeeperThan = (indent: number): void => {\n while (listStack.length > 0 && (top() as OpenList).indent > indent) popOne();\n };\n\n // The open item's content column and whether a blank line has passed since\n // its text — what decides where a following line belongs (CommonMark 5.2).\n let contentCol = 0;\n let sawBlank = false;\n const indentOf = (line: string): number => line.length - line.trimStart().length;\n\n for (const line of lines) {\n const numbered = line.match(/^(\\s*)(\\d+)[.)]\\s+(.+)$/);\n const bullet = numbered ? null : line.match(/^(\\s*)[*\\-+]\\s+(.+)$/);\n if (numbered || bullet) {\n const type: \"ul\" | \"ol\" = numbered ? \"ol\" : \"ul\";\n const indent = ((numbered ?? bullet) as RegExpMatchArray)[1]?.length ?? 0;\n const content = (numbered ? numbered[3] : (bullet as RegExpMatchArray)[2]) ?? \"\";\n const current = top();\n\n if (current && indent > current.indent) {\n processed += `\\n${openTag(type, true)}`;\n listStack.push({ type, indent });\n } else {\n closeDeeperThan(indent);\n if (lastListItem && top()?.indent === indent) processed += \"</li>\\n\";\n const now = top();\n if (now && now.indent === indent && now.type !== type) {\n // Same depth, other list type: close the old list, open the new one.\n const closing = listStack.pop() as OpenList;\n processed += closing.type === \"ol\" ? \"</ol>\\n\" : \"</ul>\\n\";\n }\n const after = top();\n if (!after || after.indent !== indent || after.type !== type) {\n processed += openTag(type, listStack.length > 0);\n listStack.push({ type, indent });\n }\n }\n processed += `${li}${content}`;\n lastListItem = type;\n contentCol = line.length - content.length;\n sawBlank = false;\n continue;\n }\n\n const text = line.trim();\n if (text === \"\") {\n sawBlank = true;\n processed += `${line}\\n`;\n continue;\n }\n if (lastListItem && !LIST_BLOCK_LINE_RE.test(text)) {\n // A line right under the item's text — lazy or indented — continues that\n // item's paragraph (even a `| a | b |` line, which GFM never reads as a\n // table there); after a blank line, a line indented to the item's content\n // column is a new paragraph INSIDE the item.\n if (!sawBlank) {\n processed += ` ${text}`;\n continue;\n }\n if (indentOf(line) >= contentCol) {\n processed += `${text}\\n`;\n continue;\n }\n }\n while (listStack.length > 0) popOne();\n processed += `${line}\\n`;\n }\n while (listStack.length > 0) popOne();\n\n // GFM task items: `- [ ] todo` / `- [x] done` print as real boxes.\n return processed.replace(\n /<li( class=\"([^\"]*)\")?>\\s*\\[([ xX])\\]\\s+/g,\n (_m: string, _c: string | undefined, classes: string | undefined, mark: string) =>\n `<li${cls(classes ?? \"\", \"matrx-task-item\")}><span class=\"matrx-task-box${\n mark === \" \" ? \"\" : \" is-checked\"\n }\" aria-hidden=\"true\"></span>`,\n );\n}\n\nfunction convertBody(skin: MarkdownSkin, markdown: string, ctx: ConvertContext): string {\n return skin === \"article\" ? articleBody(markdown, ctx) : documentBody(markdown, ctx);\n}\n\n/** Author text can never forge a placeholder. */\nfunction scrubTokens(markdown: string): string {\n // Every private-use marker in this range is internal parser state. Remove\n // authored lookalikes before any pass can mistake one for a real placeholder.\n return markdown.replace(/[\\uE000-\\uE018]/g, \"\");\n}\n\nfunction newContext(options: MarkdownToHtmlOptions): ConvertContext {\n return { options, depth: 0, cardIndex: 0, strippedAnnounced: false };\n}\n\n// ─── document skin: markdown → `.matrx-*` HTML ───────────────────────────────\n\n/**\n * markdown → `.matrx-*`-classed HTML wrapped in `.matrx-content-container`.\n *\n * The class vocabulary is deliberate and non-configurable: it is what\n * `MARKDOWN_DOCUMENT_CSS` styles, what the PDF lane renders, and what a\n * WordPress theme targets. Handles (in this order, because the order is the\n * whole trick): the shared pre-pass (code, artifacts, page breaks, flashcards,\n * inline code, unsafe page markup) → rules → images → links → headings →\n * emphasis → nested/task lists → blockquotes → tables → block elements →\n * paragraphs → FAQ promotion → placeholder restoration.\n */\nexport function markdownToHtml(\n markdown: string,\n options: MarkdownToHtmlOptions = {},\n): string {\n if (!markdown) {\n options.onNotice?.({\n code: \"empty-output\",\n message: \"markdownToHtml received an empty string.\",\n remedy: \"Check the content is loaded before converting; nothing was rendered.\",\n });\n return \"\";\n }\n const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));\n typesetMath = [];\n return sanitizePrintHtmlWithMath(\n `<div class=\"matrx-content-container\">${documentBody(source, newContext(options))}</div>`,\n );\n}\n\nfunction documentBody(markdown: string, ctx: ConvertContext): string {\n const g = DOCUMENT_GRAMMAR;\n const stash = new Stash();\n let html = protectRegions(markdown, g, stash, ctx);\n\n // Rules before headings/lists so `---` / `#===` / `***` are never eaten.\n html = convertRules(html, g);\n\n // Images BEFORE links — `![a](u)` is a superset of `[a](u)`.\n html = html.replace(IMAGE_RE, (_m: string, alt: string, url: string) =>\n stash.block(`<img class=\"matrx-image\" src=\"${safeUrl(url)}\" alt=\"${escapeHtml(alt)}\" />`),\n );\n html = html.replace(LINK_RE, (_m: string, text: string, url: string) =>\n stash.inline(`<a class=\"matrx-link\" href=\"${safeUrl(url)}\">${convertEmphasis(text, g)}</a>`),\n );\n\n html = html\n .replace(/^# (.+)$/gm, '<h1 class=\"matrx-h1\">$1</h1>')\n .replace(/^## (.+)$/gm, '<h2 class=\"matrx-h2\">$1</h2>')\n .replace(/^### (.+)$/gm, '<h3 class=\"matrx-h3\">$1</h3>')\n .replace(/^#### (.+)$/gm, '<h4 class=\"matrx-h4\">$1</h4>')\n .replace(/^##### (.+)$/gm, '<h5 class=\"matrx-h5\">$1</h5>')\n .replace(/^###### (.+)$/gm, '<h6 class=\"matrx-h6\">$1</h6>');\n\n // Lists BEFORE emphasis, so a bullet opening with bold (`* **A** and *b*`)\n // is still a bullet.\n html = convertLists(html, g);\n html = convertEmphasis(html, g);\n\n html = html.replace(/^> ?(.+)$/gm, '<blockquote class=\"matrx-blockquote\">$1</blockquote>');\n\n\n html = html\n .replace(/<table>/g, '<table class=\"matrx-table\">')\n .replace(/<thead>/g, '<thead class=\"matrx-table-head\">')\n .replace(/<tbody>/g, '<tbody class=\"matrx-table-body\">')\n .replace(/<tr>/g, '<tr class=\"matrx-table-row\">')\n .replace(/<th>/g, '<th class=\"matrx-table-header\">')\n .replace(/<td>/g, '<td class=\"matrx-table-cell\">');\n\n html = html.replace(/<img(?![^>]*class=)([^>]*)>/g, '<img class=\"matrx-image\"$1>');\n // Author divs join the vocabulary without losing their own class\n // (the old rewrite emitted two class attributes and the browser kept one).\n html = html.replace(/<div\\b([^>]*)>/g, (m: string, attrs: string) => {\n if (/\\bclass=\"matrx-/.test(attrs)) return m;\n if (/\\bclass=\"/.test(attrs)) return `<div${attrs.replace(/\\bclass=\"/, 'class=\"matrx-div ')}>`;\n return `<div class=\"matrx-div\"${attrs}>`;\n });\n html = html.replace(/<span(?![^>]*class=)([^>]*)>/g, '<span class=\"matrx-span\"$1>');\n\n html = html.replace(/^([^<\\n].+)$/gm, '<p class=\"matrx-paragraph\">$1</p>');\n html = html.replace(/<\\/p>\\s*<p class=\"matrx-paragraph\">/g, '</p><p class=\"matrx-paragraph\">');\n\n // Intro paragraph (first <p> after the h1) — before FAQ promotion.\n html = html.replace(\n /(<h1 class=\"matrx-h1\">[\\s\\S]*?<\\/h1>\\s*)<p class=\"matrx-paragraph\">((?:(?!\\uE000B)[\\s\\S])*?)<\\/p>/g,\n '$1<p class=\"matrx-intro\">$2</p>',\n );\n\n // FAQ promotion: bold questions and question headings become FAQ items.\n html = html.replace(\n /^<strong class=\"matrx-strong\">([^<]*\\?[^<]*)<\\/strong>$/gm,\n '<h3 class=\"matrx-h3\">$1</h3>',\n );\n html = html.replace(\n /<p class=\"matrx-paragraph\"><strong class=\"matrx-strong\">([^<]*\\?[^<]*)<\\/strong><\\/p>/g,\n '<h3 class=\"matrx-h3\">$1</h3>',\n );\n html = html.replace(\n /<h3 class=\"matrx-h3\">([^<]*\\?[^<]*)<\\/h3>/g,\n '<div class=\"matrx-faq-question\">$1</div>',\n );\n html = html.replace(\n /(<div class=\"matrx-faq-question\">[^<]*<\\/div>)\\s*(<p class=\"matrx-paragraph\">[\\s\\S]*?<\\/p>)/g,\n '<div class=\"matrx-faq-item\">$1<div class=\"matrx-faq-answer\">$2</div></div>',\n );\n html = html.replace(\n /(<div class=\"matrx-faq-question\">[^<]*<\\/div>)(?!\\s*<div class=\"matrx-faq-answer\">)/g,\n '<div class=\"matrx-faq-item\">$1<div class=\"matrx-faq-answer\"></div></div>',\n );\n html = html.replace(\n /(<div class=\"matrx-faq-answer\">)<p class=\"matrx-paragraph\">([\\s\\S]*?)<\\/p>(<\\/div>)/g,\n \"$1$2$3\",\n );\n\n return stash.restore(html);\n}\n\n// ─── article skin: markdown → bare semantic HTML ─────────────────────────────\n\n/**\n * markdown → bare semantic HTML (`h1`…`h6`, `p`, `ul`/`ol`, `blockquote`,\n * `.code-block-wrapper`, `.print-table`) for the article skin.\n *\n * Code is HTML-escaped in both skins (the shared pre-pass); a print window\n * never re-escapes, so unescaped code would render as markup.\n */\nexport function markdownToArticleHtml(\n markdown: string,\n options: MarkdownToHtmlOptions = {},\n): string {\n if (!markdown) {\n options.onNotice?.({\n code: \"empty-output\",\n message: \"markdownToArticleHtml received an empty string.\",\n remedy: \"Check the content is loaded before printing; nothing was rendered.\",\n });\n return \"\";\n }\n const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));\n typesetMath = [];\n return sanitizePrintHtmlWithMath(articleBody(source, newContext(options)));\n}\n\nfunction articleBody(markdown: string, ctx: ConvertContext): string {\n const g = ARTICLE_GRAMMAR;\n const stash = new Stash();\n let html = protectRegions(markdown, g, stash, ctx);\n html = convertRules(html, g);\n\n // Images BEFORE links — `![a](u)` is a superset of `[a](u)`. Without this\n // pass an image fell through to the link pass and printed as a literal\n // \"!\" followed by a link (chat \"Print\", 2026-09-21).\n html = html.replace(IMAGE_RE, (_m: string, alt: string, url: string) =>\n stash.block(\n `<figure class=\"print-figure\"><img class=\"print-image\" src=\"${safeUrl(url)}\" alt=\"${escapeHtml(alt)}\" />${\n alt ? `<figcaption>${escapeHtml(alt)}</figcaption>` : \"\"\n }</figure>`,\n ),\n );\n html = html.replace(LINK_RE, (_m: string, text: string, url: string) =>\n stash.inline(`<a href=\"${safeUrl(url)}\">${convertEmphasis(text, g)}</a>`),\n );\n\n html = html\n .replace(/^#{6}\\s+(.+)$/gm, \"<h6>$1</h6>\")\n .replace(/^#{5}\\s+(.+)$/gm, \"<h5>$1</h5>\")\n .replace(/^#{4}\\s+(.+)$/gm, \"<h4>$1</h4>\")\n .replace(/^#{3}\\s+(.+)$/gm, \"<h3>$1</h3>\")\n .replace(/^#{2}\\s+(.+)$/gm, \"<h2>$1</h2>\")\n .replace(/^#{1}\\s+(.+)$/gm, \"<h1>$1</h1>\");\n\n html = html.replace(/^> ?(.+)$/gm, \"<blockquote>$1</blockquote>\");\n html = convertLists(html, g);\n html = convertEmphasis(html, g);\n\n html = html\n .split(\"\\n\")\n .map((line) => {\n const t = line.trim();\n if (!t) return \"\";\n if (\n /^<\\/?(h[1-6]|ul|ol|li|blockquote|pre|hr|table|thead|tbody|tr|th|td|div|figure|section)/.test(t) ||\n /^\\uE000B\\d+\\uE001$/.test(t)\n ) {\n return line;\n }\n return `<p>${t}</p>`;\n })\n .join(\"\\n\");\n\n return stash.restore(html).replace(/\\n{3,}/g, \"\\n\\n\").trim();\n}\n\n// ─── stylesheet + document composition ───────────────────────────────────────\n\n/**\n * The Print / Close action bar. The article sheet styles it already; the\n * document sheet is a content stylesheet with no chrome, so this ships beside\n * it — otherwise every host printing document-skin markdown gets two unstyled\n * browser buttons at the top of the page.\n */\nexport const PRINT_ACTIONS_CSS = `\n.print-actions { display: flex; gap: 10px; margin: 0 auto 28px; max-width: 800px; padding: 0 2rem 20px; border-bottom: 2px solid var(--matrx-print-border, #e1e1e1); }\n.print-btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 20px; border-radius: 8px; font-size: 13px; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; cursor: pointer; border: none; font-weight: 600; }\n.print-btn-primary { background: var(--matrx-print-accent, #667eea); color: var(--matrx-print-bg, #ffffff); }\n.print-btn-secondary { background: var(--matrx-print-surface, #f8f9fa); color: var(--matrx-print-fg-strong, #374151); border: 1px solid var(--matrx-print-border-muted, #d1d5db); }\n@media print { .print-actions { display: none !important; } }\n`;\n\nexport interface MarkdownStylesheetOptions {\n /** Which shipped look. Default: `\"document\"`. */\n skin?: MarkdownSkin;\n /**\n * Host brand overrides — a CSS string appended AFTER the package's default\n * token values, so `:root { --matrx-print-accent: #0f766e; }` wins. Token\n * VALUES are host-owned; the token CONTRACT and the structural CSS are not.\n */\n tokens?: string;\n /** Extra rules appended after everything else. */\n extraCss?: string;\n /**\n * Number printed pages (\"3 / 12\", bottom centre) via `@page` margin boxes.\n * Default: true. Page margins are set either way.\n */\n pageNumbers?: boolean;\n}\n\n/**\n * The complete default stylesheet for a skin: default token values, the\n * structural sheet, the rules both skins share (page breaks, artifacts, task\n * boxes, print hard parts), page geometry, then the host's overrides. Never returns an empty string.\n */\nexport function getMarkdownStylesheet(\n options: MarkdownStylesheetOptions = {},\n): string {\n const skin: MarkdownSkin = options.skin ?? \"document\";\n const base =\n skin === \"article\"\n ? `${MARKDOWN_ARTICLE_TOKENS_CSS}\\n${MARKDOWN_ARTICLE_CSS}`\n : `${MARKDOWN_DOCUMENT_TOKENS_CSS}\\n${MARKDOWN_DOCUMENT_CSS}`;\n const page = options.pageNumbers === false ? MARKDOWN_PAGE_PLAIN_CSS : MARKDOWN_PAGE_CSS;\n return [base, MARKDOWN_SHARED_CSS, KATEX_CSS, page, options.tokens ?? \"\", options.extraCss ?? \"\"]\n .filter((part) => part.trim().length > 0)\n .join(\"\\n\");\n}\n\n/** Convert markdown with the converter that matches a skin. */\nexport function markdownToHtmlForSkin(\n markdown: string,\n skin: MarkdownSkin,\n options: MarkdownToHtmlOptions = {},\n): string {\n return skin === \"article\"\n ? markdownToArticleHtml(markdown, options)\n : markdownToHtml(markdown, options);\n}\n\nexport interface RenderMarkdownDocumentOptions extends MarkdownStylesheetOptions {\n title?: string;\n /** Override the shipped converter. The seam survives; it is no longer required. */\n convertToHtml?: (markdown: string) => string;\n /** Replace the whole stylesheet (not merely the tokens). */\n css?: string;\n onNotice?: (notice: MarkdownNotice) => void;\n /** See `MarkdownToHtmlOptions.renderBlock`. */\n renderBlock?: MarkdownToHtmlOptions[\"renderBlock\"];\n /** Include the Print / Close action bar (hidden by `@media print`). */\n withPrintActions?: boolean;\n}\n\n/**\n * markdown → a complete, standalone, styled HTML document string.\n *\n * This is the one composition every lane uses — the print window, the PDF\n * capture host, and \"copy a full HTML page\". A host that builds its own\n * `<!DOCTYPE html>` wrapper around package CSS is re-implementing this.\n */\nexport function renderMarkdownDocument(\n markdown: string,\n options: RenderMarkdownDocumentOptions = {},\n): string {\n const skin: MarkdownSkin = options.skin ?? \"document\";\n const title = options.title ?? \"AI Response\";\n const convert =\n options.convertToHtml ??\n ((md: string) =>\n markdownToHtmlForSkin(md, skin, {\n ...(options.onNotice ? { onNotice: options.onNotice } : {}),\n ...(options.renderBlock ? { renderBlock: options.renderBlock } : {}),\n }));\n const bodyHtml = convert(markdown);\n const css =\n options.css ??\n getMarkdownStylesheet({\n skin,\n ...(options.tokens === undefined ? {} : { tokens: options.tokens }),\n ...(options.extraCss === undefined ? {} : { extraCss: options.extraCss }),\n });\n\n // The article sheet already styles the action bar; the document sheet is a\n // pure content stylesheet, so it gets the shared chrome appended.\n const chrome =\n options.withPrintActions && skin !== \"article\" ? PRINT_ACTIONS_CSS : \"\";\n\n const actions = options.withPrintActions\n ? '<div class=\"print-actions\"><button class=\"print-btn print-btn-primary\" onclick=\"window.print()\">Print / Save as PDF</button><button class=\"print-btn print-btn-secondary\" onclick=\"window.close()\">Close</button></div>'\n : \"\";\n\n return `<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>${escapeHtml(\n title,\n )}</title>\n<style>${css}${chrome}</style></head>\n<body>${actions}<div class=\"content\">${bodyHtml}</div></body></html>`;\n}\n\nexport interface PrintMarkdownOptions extends RenderMarkdownDocumentOptions {\n /** Window features for the print window. */\n windowFeatures?: string;\n}\n\n/**\n * Open a print window for a markdown string, in one call.\n *\n * Returns the `PrintOutcome` so the host can tell the user when the popup was\n * blocked and the `.html` download fallback fired instead — a silent fallback\n * is indistinguishable from \"print did nothing\".\n */\nexport function printMarkdown(\n markdown: string,\n options: PrintMarkdownOptions = {},\n): PrintOutcome {\n const title = options.title ?? \"AI Response\";\n const doc = renderMarkdownDocument(markdown, {\n ...options,\n title,\n withPrintActions: options.withPrintActions ?? true,\n });\n return options.windowFeatures === undefined\n ? openPrintWindow(doc, title)\n : openPrintWindow(doc, title, options.windowFeatures);\n}\n\n/**\n * Print markdown that needs async preparation first (drawing its diagrams).\n * The window opens NOW, inside the click, so no popup blocker stops it; it\n * shows \"Preparing…\" until `prepare` resolves, then gets the document. When\n * the popup is blocked the prepared document downloads instead, as\n * `printMarkdown` does.\n */\nexport async function printMarkdownWhenReady(\n markdown: string,\n options: Omit<PrintMarkdownOptions, \"renderBlock\"> & {\n /**\n * The print form of each embedded block, asked once per block BEFORE the\n * document is composed (a diagram drawn, a page captured, a quiz through\n * its registry printer). Null prints the block's source. A block whose\n * answer throws prints an honest notice, never blank.\n */\n renderBlock: (block: PrintBlock) => Promise<PrintBlockRender | null | undefined>;\n },\n): Promise<PrintOutcome> {\n const title = options.title ?? \"AI Response\";\n const pending = openPendingPrintWindow(title, options.windowFeatures);\n const { renderBlock, ...rest } = options;\n const answers = await resolvePrintBlocks(markdown, rest.skin ?? \"document\", renderBlock);\n // Each block printer's own stylesheet, once (block HTML itself is sanitized: no <style>).\n const blockCss = [...new Set([...answers.values()].map((a) => (\"css\" in a && typeof a.css === \"string\" ? a.css : \"\")).filter(Boolean))].join(\"\\n\");\n const doc = renderMarkdownDocument(markdown, {\n ...rest,\n ...(blockCss\n ? rest.css !== undefined\n ? { css: `${rest.css}\\n${blockCss}` }\n : { extraCss: `${rest.extraCss ?? \"\"}\\n${blockCss}` }\n : {}),\n renderBlock: (block) => answers.get(printBlockKey(block)) ?? null,\n title,\n withPrintActions: rest.withPrintActions ?? true,\n });\n return pending.write(doc);\n}\n\n/** Asks `renderBlock` for every block of `markdown` (in parallel); keyed by `printBlockKey`. */\nexport async function resolvePrintBlocks(\n markdown: string,\n skin: MarkdownSkin,\n renderBlock: (block: PrintBlock) => Promise<PrintBlockRender | null | undefined>,\n): Promise<Map<string, PrintBlockRender>> {\n const answers = new Map<string, PrintBlockRender>();\n await Promise.all(\n listPrintBlocks(markdown, skin).map(async (block) => {\n try {\n const render = await renderBlock(block);\n if (render) answers.set(printBlockKey(block), render);\n } catch (error) {\n console.error(\"[@ai-matrx/print] a block's print form failed\", error);\n answers.set(printBlockKey(block), {\n notice: `This ${block.type || \"block\"} could not be prepared for print.`,\n });\n }\n }),\n );\n return answers;\n}\n","/**\n * @ai-matrx/print/core — the Block Print contract & print-window composition.\n *\n * Ported VERBATIM from matrx-frontend `lib/block-print/block-print-utils.ts`\n * (2026-08-29 state, including the `number` and `select` PrintSetting types the\n * commerce label lane added). Zero dependencies; touches `window` only at call\n * time, so the module stays import-inert and RSC-safe.\n *\n * BlockPrinter is the only interface a host system needs to know about.\n * Each printable unit exports its own BlockPrinter alongside its render\n * component; host code only ever calls print(data, variantId, settings).\n *\n * `buildFitTextScript` is the ONE auto-shrink script (the flashcards\n * FIT_TEXT pattern, parameterized by selector so the label lane shares it).\n */\n\n// THE ONE HTML escaper (duplication census row 14). Nine copies of\n// `escapeHtml` were live across the fleet and THEY DID NOT ESCAPE THE SAME\n// CHARACTERS — a string safe through one was an attribute break-out through\n// another. It moved to `@ai-matrx/kit/html-escape` with a decided character\n// set: an HTML escaper must not require this package's jspdf + html2canvas\n// graph. Not re-exported from here; import `@ai-matrx/kit/html-escape`.\nimport { downloadFile } from \"@ai-matrx/kit/download\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { KATEX_CSS, KATEX_CSS_MARKER } from \"./katex-css.generated\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\nexport interface PrintVariant {\n id: string;\n label: string;\n description?: string;\n}\n\n/**\n * A single user-configurable setting shown in the print dialog.\n * Extend the discriminated union for new control types\n * without breaking existing printers.\n */\nexport type PrintSetting =\n | {\n type: \"boolean\";\n id: string;\n label: string;\n description?: string;\n /** Default value shown when the dialog first opens */\n defaultValue: boolean;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /**\n * A numeric \"from → to\" range control, rendered as two small number inputs.\n * Writes two entries into the resolved PrintSettings map — one under fromId,\n * one under toId. Printers interpret 0 (or missing) as \"no bound\" by convention.\n */\n type: \"range\";\n /** Stable id used as React key — not written to the settings map */\n id: string;\n label: string;\n description?: string;\n /** Settings map key for the lower bound value */\n fromId: string;\n /** Settings map key for the upper bound value */\n toId: string;\n defaultFrom: number;\n defaultTo: number;\n /** Minimum accepted value for each input (inclusive). Default 0. */\n min?: number;\n /** Placeholder shown when the input is empty (0). Default \"start\" / \"end\". */\n fromPlaceholder?: string;\n toPlaceholder?: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A single numeric input (e.g. \"start at label #\", \"copies\"). */\n type: \"number\";\n id: string;\n label: string;\n description?: string;\n defaultValue: number;\n /** Minimum accepted value (inclusive). Default 0. */\n min?: number;\n /** Maximum accepted value (inclusive). Omit = unbounded. */\n max?: number;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A one-of-N choice rendered as a compact segmented row. */\n type: \"select\";\n id: string;\n label: string;\n description?: string;\n options: { value: string; label: string }[];\n defaultValue: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n };\n\n/** A resolved map of setting id → current value, passed to print(). */\nexport type PrintSettings = Record<string, boolean | string | number>;\n\n/**\n * The self-contained print capability that each block can export.\n * Host code only ever calls print(data, variantId, settings) — it never inspects internals.\n */\nexport interface BlockPrinter {\n /** Tooltip / aria-label for the print button in the block header */\n label: string;\n\n /**\n * Variants offered to the user before printing.\n * Empty array = no options dialog shown; prints immediately with defaults.\n */\n variants: PrintVariant[];\n\n /**\n * Optional user-configurable settings shown below the variant picker.\n * Omit entirely if the block has no configurable settings.\n */\n settings?: PrintSetting[];\n\n /**\n * Execute the print.\n * data — the block's structured data (serverData or parsed content)\n * variantId — which variant was chosen (undefined = default / no variants)\n * settings — resolved setting values chosen by the user\n *\n * MAY resolve to a PrintOutcome (\"opened\" | \"downloaded\") so the host can\n * tell the user when the popup-blocked download fallback fired; `void`\n * stays legal for printers that don't go through a print window.\n */\n print: (\n data: unknown,\n variantId?: string,\n settings?: PrintSettings,\n ) => void | PrintOutcome | Promise<void | PrintOutcome>;\n\n /**\n * The block as it appears INSIDE a composed print (a message's Print / Full\n * Print, a document export): the same adapter the block's own print uses,\n * returning body HTML or a picture. A frame block (an HTML page) answers\n * with an image — a composed print window runs no author scripts — or an\n * honest `{ notice }` when no picture can be made. Absent ⇒ the composer\n * prints the block's source.\n */\n toPrintHtml?: (data: unknown, context: PrintBlockContext) => PrintBlockOutput | null | Promise<PrintBlockOutput | null>;\n}\n\n/** Where a block sits when a composer asks for its print form. */\nexport interface PrintBlockContext {\n /** The registry key the block was found under (artifact type or `__kind`). */\n readonly type: string;\n /** The block's raw source text, exactly as it sits in the message. */\n readonly raw: string;\n /** The block's title, when the wire form carries one. */\n readonly title?: string | undefined;\n /** The wire form's attributes (an `<artifact>`'s `id`, `version`) — how a printer finds the block's record. */\n readonly attributes?: Readonly<Record<string, string>> | undefined;\n}\n\n/**\n * A block's print form inside a composed print. `html` is trusted body HTML\n * from a printer (escape what you interpolate); `image` is a picture (a\n * captured page, a drawn diagram); `notice` is an honest line printed in the\n * block's place when neither exists.\n */\nexport type PrintBlockOutput =\n | {\n readonly html: string;\n /**\n * The block's stylesheet. A composed print sanitizes block HTML (no\n * `<style>`, no inline styles, only `matrx-*` / `print-*` classes), so a\n * printer that styles its layout passes its CSS here: the composer puts\n * each distinct sheet once in the document head. Scope it under your own\n * `matrx-…` class.\n */\n readonly css?: string | undefined;\n }\n | { readonly image: { readonly src: string; readonly alt?: string | undefined; readonly caption?: string | undefined } }\n | { readonly notice: string };\n\n// ─── The ONE block-printer registry ───────────────────────────────────────────\n//\n// Keyed by artifact type / `__kind`. A block's own print button, the canvas\n// tab's Print and the message composer all read the same entry, so a block\n// has one print adapter with every caller. Lives on globalThis under a\n// Symbol.for key so duplicated bundles (ESM + CJS) share one registry.\n\nconst PRINTER_REGISTRY = Symbol.for(\"ai-matrx.print.block-printers\");\n\nfunction printerRegistry(): Map<string, BlockPrinter> {\n const g = globalThis as unknown as { [PRINTER_REGISTRY]?: Map<string, BlockPrinter> };\n g[PRINTER_REGISTRY] ??= new Map();\n return g[PRINTER_REGISTRY];\n}\n\n/** Registers `printer` under each key (artifact type and/or `__kind`). Returns the unregister. */\nexport function registerBlockPrinter(keys: string | readonly string[], printer: BlockPrinter): () => void {\n const map = printerRegistry();\n const list = (typeof keys === \"string\" ? [keys] : keys).map((key) => key.toLowerCase());\n for (const key of list) map.set(key, printer);\n return () => {\n for (const key of list) if (map.get(key) === printer) map.delete(key);\n };\n}\n\n/** The printer registered for an artifact type or `__kind` (case-insensitive). */\nexport function getBlockPrinter(key: string | null | undefined): BlockPrinter | undefined {\n return key ? printerRegistry().get(key.toLowerCase()) : undefined;\n}\n\n/** Every registered key — for guards and diagnostics. */\nexport function listBlockPrinterKeys(): string[] {\n return [...printerRegistry().keys()];\n}\n\n/** The HTML a composer puts in a block's place for a `PrintBlockOutput`. */\nexport function printBlockOutputHtml(output: PrintBlockOutput): string {\n if (\"html\" in output) return `<div class=\"matrx-print-block\">${output.html}</div>`;\n if (\"image\" in output) {\n const { src, alt, caption } = output.image;\n return `<figure class=\"matrx-print-block matrx-print-image\"><img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(\n alt ?? \"Picture\",\n )}\" style=\"max-width:100%;height:auto\">${caption ? `<figcaption>${escapeHtml(caption)}</figcaption>` : \"\"}</figure>`;\n }\n return `<p class=\"matrx-print-block matrx-print-notice\" style=\"border:1px dashed #94a3b8;border-radius:6px;padding:10px 12px;color:#475569;font-style:italic\">${escapeHtml(\n output.notice,\n )}</p>`;\n}\n\n// ─── Shared Utilities ─────────────────────────────────────────────────────────\n\nconst PRINT_STYLES = `\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;\n font-size: 11pt;\n line-height: 1.6;\n color: #1a1a1a;\n background: #fff;\n max-width: 860px;\n margin: 0 auto;\n padding: 28px 36px;\n }\n\n .print-actions {\n display: flex;\n gap: 10px;\n margin-bottom: 24px;\n padding-bottom: 18px;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .print-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n cursor: pointer;\n border: none;\n font-weight: 600;\n }\n\n .print-btn-primary { background: #2563eb; color: #fff; }\n .print-btn-secondary { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }\n\n h1 { font-size: 22pt; font-weight: 700; margin-bottom: 0.5em; }\n h2 { font-size: 16pt; font-weight: 700; margin: 1.2em 0 0.4em; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.2em; }\n h3 { font-size: 13pt; font-weight: 600; margin: 1em 0 0.3em; }\n p { margin-bottom: 0.7em; }\n\n table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n font-size: 10pt;\n }\n th {\n background: #1e293b;\n color: #f8fafc;\n font-weight: 600;\n text-align: left;\n padding: 7px 12px;\n border: 1px solid #334155;\n }\n td {\n padding: 6px 12px;\n border: 1px solid #e2e8f0;\n vertical-align: top;\n }\n tr:nth-child(even) td { background: #f8fafc; }\n\n @media print {\n body { max-width: 100%; padding: 0; }\n .print-actions { display: none !important; }\n th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n }\n`;\n\n/**\n * Wrap body HTML in a complete, styled print document.\n */\nexport function buildPrintDocument(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(title)}</title>\n <style>\n${PRINT_STYLES}\n${extraStyles}\n </style>\n</head>\n<body>\n <div class=\"print-actions\">\n <button class=\"print-btn print-btn-primary\" onclick=\"window.print()\">Print / Save as PDF</button>\n <button class=\"print-btn print-btn-secondary\" onclick=\"window.close()\">Close</button>\n </div>\n <div class=\"content\">\n${bodyHtml}\n </div>\n</body>\n</html>`;\n}\n\n/**\n * How a print request actually reached the user.\n *\n * \"opened\" — a print window/tab was opened and the document written into it.\n * \"downloaded\" — the popup was blocked, so the document was downloaded as an\n * .html file instead (the contract's fallback). Hosts SHOULD\n * surface this to the user (e.g. a toast saying the print file\n * was downloaded) — a blocked popup plus a silent download is\n * indistinguishable from \"nothing happened\", which is exactly\n * how QA filed it (2026-08-30, F5/F6/F8).\n */\nexport type PrintOutcome = \"opened\" | \"downloaded\";\n\n/**\n * Open a new browser window and write a complete HTML document into it.\n * Falls back to downloading an .html file if the popup is blocked.\n *\n * Returns which path was taken so callers can tell the user when the\n * fallback fired. `windowFeatures` customizes the popup geometry (the\n * flashcards fixed-geometry variants pass a wider window).\n */\n/**\n * Every printed document leaves through here, so this is where typeset math\n * gets its stylesheet: when the page carries KaTeX output and does not already\n * carry the sheet, it is added to <head>. A page with no math is untouched,\n * byte for byte (the flashcard golden master holds every printer to that).\n */\nexport function withMathStyles(htmlDocument: string): string {\n if (!htmlDocument.includes('class=\"katex\"') || htmlDocument.includes(KATEX_CSS_MARKER)) {\n return htmlDocument;\n }\n const style = `<style data-matrx-katex>${KATEX_CSS}</style>`;\n const head = htmlDocument.search(/<\\/head>/i);\n return head >= 0\n ? htmlDocument.slice(0, head) + style + htmlDocument.slice(head)\n : style + htmlDocument;\n}\n\nexport function openPrintWindow(\n rawDocument: string,\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n const win = window.open(\"\", \"_blank\", windowFeatures);\n // Popup blocked — fall back to file download.\n if (!win) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n}\n\n/** A print window opened before its document is ready (see `openPendingPrintWindow`). */\nexport interface PendingPrintWindow {\n /** Write the finished document; downloads it when the popup was blocked or closed. */\n write(htmlDocument: string): PrintOutcome;\n}\n\n/**\n * Open the print window NOW — inside the click, where popup blockers allow it —\n * and write the document later, once async preparation (drawing diagrams) is\n * done. The window says \"Preparing…\" meanwhile.\n */\nexport function openPendingPrintWindow(\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PendingPrintWindow {\n const win = window.open(\"\", \"_blank\", windowFeatures);\n if (win) {\n win.document.open();\n win.document.write(\n '<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Preparing…</title></head><body style=\"font:14px -apple-system,BlinkMacSystemFont,sans-serif;color:#555;padding:2rem\">Preparing the document for print…</body></html>',\n );\n win.document.close();\n }\n return {\n write(rawDocument: string): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n if (!win || win.closed) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n },\n };\n}\n\nfunction openPrintWindowFallback(htmlDocument: string, filename: string): PrintOutcome {\n downloadFile(`${filename.replace(/\\s+/g, \"-\").toLowerCase()}.html`, htmlDocument, \"text/html;charset=utf-8\");\n return \"downloaded\";\n}\n\n/**\n * Shorthand: build the document and open it in one call.\n * Returns the same PrintOutcome as openPrintWindow.\n */\nexport function printHtmlContent(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): PrintOutcome {\n const doc = buildPrintDocument(bodyHtml, title, extraStyles);\n return openPrintWindow(doc, title);\n}\n\n/**\n * Build the auto-shrink script that runs INSIDE a printed window: for every\n * element matching each selector, its font-size is reduced (0.5pt steps, 6pt\n * floor) until it fits its parent element's box. This is the flashcards\n * FIT_TEXT behavior verbatim, parameterized by selector so every lane\n * (flashcards card text, Avery text, label captions) shares the ONE loop.\n *\n * Pass the same selectors the original used to get byte-identical behavior:\n * flashcards passes `[\".card-text .card-text-inner\", \".avery-text-wrap .avery-text\"]`.\n */\nexport function buildFitTextScript(selectors: readonly string[]): string {\n const selectorsJson = JSON.stringify([...selectors]);\n return `\n<script>\n(function() {\n function fitText(el, container) {\n // container is the bounding box we must fit inside\n var maxW = container.clientWidth;\n var maxH = container.clientHeight;\n var size = parseFloat(window.getComputedStyle(el).fontSize);\n var minSize = 6;\n // Give the element a chance to reflow at current size before measuring\n el.style.fontSize = size + \"pt\";\n while (size > minSize && (el.scrollWidth > maxW + 1 || el.scrollHeight > maxH + 1)) {\n size -= 0.5;\n el.style.fontSize = size + \"pt\";\n }\n }\n function runAll() {\n var selectors = ${selectorsJson};\n for (var s = 0; s < selectors.length; s++) {\n var els = document.querySelectorAll(selectors[s]);\n for (var i = 0; i < els.length; i++) {\n fitText(els[i], els[i].parentElement);\n }\n }\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", runAll);\n } else {\n // document.write pages may already be loaded\n setTimeout(runAll, 0);\n }\n})();\n</script>`;\n}\n\n\n// ─── DOM print: a rendered body, as it looks ─────────────────────────────────\n\nexport interface PrintElementOptions {\n /** The print window's title (and the download fallback's file name). */\n title?: string;\n /** Open the system print dialog once the window has loaded. Default true. */\n autoPrint?: boolean;\n /** Keep buttons, toolbars and menus in the print (default false: they are chrome, not content). */\n keepInteractive?: boolean;\n}\n\n/**\n * Interactive chrome that is not content: anything marked `data-print-hide`,\n * toolbars and menus, and buttons (\"Chart this\", \"Save to\", icon actions).\n * A button stays when it IS content — marked `data-print-keep`, or a disclosure\n * trigger (`aria-expanded`) whose label is a heading — and so does anything\n * inside a `data-print-keep` region. Removal runs on the print clone only.\n */\nconst PRINT_CHROME_SELECTOR = [\n \"[data-print-hide]\",\n '[role=\"toolbar\"]',\n '[role=\"menu\"]',\n '[role=\"menubar\"]',\n \"button\",\n '[role=\"button\"]',\n 'input[type=\"button\"]',\n 'input[type=\"submit\"]',\n 'input[type=\"reset\"]',\n].join(\",\");\n\nexport function stripInteractiveChrome(root: ParentNode): number {\n let removed = 0;\n root.querySelectorAll(PRINT_CHROME_SELECTOR).forEach((node) => {\n if (!root.contains(node)) return; // already gone with a removed ancestor\n const explicit = node.hasAttribute(\"data-print-hide\");\n if (!explicit) {\n if (node.closest(\"[data-print-keep]\")) return;\n if (node.matches(\"button, [role=\\\"button\\\"]\") && node.hasAttribute(\"aria-expanded\")) return;\n }\n node.remove();\n removed += 1;\n });\n return removed;\n}\n\nconst ELEMENT_PRINT_CSS = `\n html, body { height: auto !important; min-height: 0 !important; overflow: visible !important; background: #fff !important; }\n body { margin: 0; padding: 16px; }\n *, *::before, *::after { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }\n .matrx-print-root, .matrx-print-root * { max-height: none !important; }\n .matrx-print-root [class*=\"overflow-\"], .matrx-print-root [class*=\"h-full\"], .matrx-print-root [class*=\"min-h-0\"] { overflow: visible !important; height: auto !important; }\n .matrx-print-frame-notice { border: 1px dashed #94a3b8; border-radius: 6px; padding: 10px 12px; color: #475569; font: italic 12px sans-serif; }\n @media print { .matrx-print-root * { break-inside: auto; } img, svg, figure, tr { break-inside: avoid; } }\n`;\n\n/**\n * Prints a same-origin DOM body exactly as it is drawn: the element's markup\n * with the page's own stylesheets, light theme, backgrounds kept, scroll\n * containers opened to full height. A `<canvas>` prints as its picture; an\n * `<iframe>` cannot be copied and prints an honest notice in its place (a\n * frame kind declares its own print instead). Opens inside the click.\n */\nexport function printElement(element: HTMLElement, options: PrintElementOptions = {}): PrintOutcome {\n const title = options.title ?? (document.title || \"Print\");\n const head = [...document.querySelectorAll<HTMLLinkElement | HTMLStyleElement>('link[rel=\"stylesheet\"], style')]\n .map((node) => {\n if (node instanceof HTMLLinkElement) return `<link rel=\"stylesheet\" href=\"${escapeHtml(node.href)}\">`;\n return `<style>${node.textContent ?? \"\"}</style>`;\n })\n .join(\"\\n\");\n const clone = element.cloneNode(true) as HTMLElement;\n const liveCanvases = element.querySelectorAll(\"canvas\");\n clone.querySelectorAll(\"canvas\").forEach((canvas, i) => {\n const live = liveCanvases[i];\n const img = document.createElement(\"img\");\n try {\n img.src = live ? live.toDataURL(\"image/png\") : \"\";\n } catch {\n img.alt = \"This drawing cannot be copied for print.\";\n }\n img.style.maxWidth = \"100%\";\n canvas.replaceWith(img);\n });\n clone.querySelectorAll(\"iframe\").forEach((frame) => {\n const note = document.createElement(\"p\");\n note.className = \"matrx-print-frame-notice\";\n note.textContent = \"An embedded page is here — print it from its own Print.\";\n frame.replaceWith(note);\n });\n clone.querySelectorAll(\"script\").forEach((node) => node.remove());\n if (options.keepInteractive !== true) stripInteractiveChrome(clone);\n const htmlClass = document.documentElement.className.replace(/\\bdark\\b/g, \"light\");\n const autoPrint = options.autoPrint ?? true;\n const doc = `<!DOCTYPE html><html lang=\"en\" class=\"${escapeHtml(htmlClass)}\"><head><meta charset=\"UTF-8\"><base href=\"${escapeHtml(\n document.baseURI,\n )}\"><title>${escapeHtml(title)}</title>${head}<style>${ELEMENT_PRINT_CSS}</style></head><body><div class=\"matrx-print-root\">${\n clone.outerHTML\n }</div>${\n autoPrint\n ? '<script>window.addEventListener(\"load\",function(){setTimeout(function(){window.focus();window.print();},250);});</script>'\n : \"\"\n }</body></html>`;\n return openPrintWindow(doc, title);\n}\n","// GENERATED by scripts/gen-katex-css.mjs from katex@0.18.9 — do not edit.\nexport const KATEX_VERSION = \"0.18.9\";\n/** Leads KATEX_CSS; lets openPrintWindow see the sheet is already present. */\nexport const KATEX_CSS_MARKER = \"/* matrx-katex@0.18.9 */\";\nexport const KATEX_CSS = \"/* matrx-katex@0.18.9 */@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.ttf) format(\\\"truetype\\\")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:\\\"0.18.9\\\"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:\\\"\\\"}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:\\\"(\\\" counter(katexEqnNo) \\\")\\\";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:\\\"(\\\" counter(mmlEqnNo) \\\")\\\";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}\\n\";\n","/**\n * Plain text with math → safe HTML: THE one way a printer turns authored text\n * (a flashcard face, an exam prompt, a glossary definition) into markup.\n *\n * Every non-markdown printer used to `escapeHtml()` its text, so `\\(\\text{IO}_3^-\\)`\n * on a flashcard printed as that literal source (Arman, 2026-09-27). What is math\n * is decided by the shared tokenizer (`extractMath` — the same rule the screen,\n * the markdown lane and the /document pipeline use: code and `$5` prices are\n * never math); everything else is escaped exactly as before.\n *\n * Typeset output is KaTeX's laid-out HTML plus MathML, chemistry (mhchem)\n * loaded. KaTeX runs with `trust: false`, which refuses every command that could\n * inject HTML (\\href, \\url, \\htmlClass, \\htmlId, \\htmlStyle, \\htmlData,\n * \\includegraphics). The stylesheet is added by `openPrintWindow` whenever a\n * document contains math, so no printer can forget it.\n */\nimport katex from \"katex\";\n// Registers \\ce{…} and \\pu{…} on the same katex instance.\nimport \"katex/contrib/mhchem\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { extractMath, MATH_TOKEN_RE } from \"./document/math\";\n\n/** Typeset one TeX span — the ONE KaTeX configuration every print lane uses. */\nexport function typesetTex(tex: string, display: boolean): string {\n return katex.renderToString(tex, {\n displayMode: display,\n output: \"htmlAndMathml\",\n throwOnError: false,\n trust: false,\n strict: \"ignore\",\n });\n}\n\n/**\n * Escape `text` for HTML, with every math span typeset. Newlines are kept as\n * `\\n` in the prose so callers that turn them into `<br>` keep doing so; a\n * newline inside a formula is folded to a space (TeX treats it as one).\n */\nexport function mathTextHtml(text: string | null | undefined): string {\n // The tokenizer's own markers can never come from an author.\n const source = (text ?? \"\").replace(/[\\uE100\\uE101]/g, \"\");\n if (!/[$\\\\]/.test(source)) return escapeHtml(source);\n const { text: prose, spans } = extractMath(source);\n if (spans.length === 0) return escapeHtml(source);\n return escapeHtml(prose).replace(MATH_TOKEN_RE, (_m: string, i: string) => {\n const span = spans[Number(i)];\n return span ? typesetTex(span.tex.replace(/\\s*\\n\\s*/g, \" \"), span.display) : \"\";\n });\n}\n\n/**\n * A field that IS a formula (a cheat-sheet \"formula\" slot): delimited math is\n * typeset as usual; undelimited TeX (it has a command, `^` or `_`) is typeset\n * whole as inline math; anything else is escaped text.\n */\nexport function formulaHtml(formula: string | null | undefined): string {\n const source = (formula ?? \"\").trim();\n const withMath = mathTextHtml(source);\n if (withMath.includes('class=\"katex\"')) return withMath;\n return /\\\\[a-zA-Z]|[\\^_]/.test(source) ? typesetTex(source, false) : withMath;\n}\n","/**\n * Math in documents — ONE math dialect, typeset in every format.\n *\n * What is math is decided ONLY by the shared tokenizer (`@ai-matrx/content-ir/\n * source`): its fence-nesting rule finds code, its pairing finds `$$…$$`,\n * `$…$` (math, not currency), `\\[…\\]` and `\\(…\\)`. Math is lifted out\n * BEFORE markdown parsing, so a TeX `\\,` is never eaten as a markdown escape\n * and `_`/`*` inside a formula never become emphasis.\n *\n * Typesetting: MathJax (TeX → its internal tree) produces\n * - SVG for PDF display math (drawn as vector paths; allow-list sanitized),\n * - MathML for HTML and EPUB (rendered natively by browsers and readers),\n * - MathML that the Word writer turns into native OMML equations and the\n * PDF writer turns into inline text with real super/subscripts.\n */\n\nimport { listIslands, tokenizeSource } from \"@ai-matrx/content-ir/source\";\nimport { sanitizeMathml, sanitizeMathSvg } from \"../safe-html\";\n\nexport const MATH_OPEN = \"\\uE100\";\nexport const MATH_CLOSE = \"\\uE101\";\nexport const MATH_TOKEN_RE = /\\uE100(\\d+)\\uE101/g;\n\nexport interface MathSpan {\n tex: string;\n display: boolean;\n}\n\nconst DELIMITERS: Record<string, { open: number; close: number; display: boolean }> = {\n $$: { open: 2, close: 2, display: true },\n \"\\\\[\": { open: 2, close: 2, display: true },\n $: { open: 1, close: 1, display: false },\n \"\\\\(\": { open: 2, close: 2, display: false },\n};\n\n/**\n * Replace every math span with a private-use token; returns the spans.\n *\n * Print keeps NO fence, code-span or math detection of its own: the spans are\n * the shared tokenizer's complete `math_block` / `math_inline` islands\n * (`tokenizeSource` + `listIslands` from `@ai-matrx/content-ir/source` — the\n * one fence-nesting rule and the one math pairing), so print, the renderer and\n * the editor can never disagree about what is math. The delimiter decides the\n * form: `$$`/`\\[` display, `$`/`\\(` inline.\n */\nexport function extractMath(body: string): { text: string; spans: MathSpan[] } {\n const spans: MathSpan[] = [];\n if (!/[$\\\\]/.test(body)) return { text: body, spans };\n const islands = listIslands(tokenizeSource(body))\n .filter((isl) => isl.complete && (isl.islandType === \"math_block\" || isl.islandType === \"math_inline\"))\n .sort((a, b) => a.start - b.start);\n let out = \"\";\n let at = 0;\n for (const isl of islands) {\n const d = DELIMITERS[String(isl.meta.delimiter ?? \"\")];\n if (!d || isl.start < at) continue;\n out += body.slice(at, isl.start);\n spans.push({ tex: body.slice(isl.start + d.open, isl.end - d.close).trim(), display: d.display });\n out += `${MATH_OPEN}${spans.length - 1}${MATH_CLOSE}`;\n at = isl.end;\n }\n return { text: out + body.slice(at), spans };\n}\n\n// ── typesetting ────────────────────────────────────────────────────────────\n\nexport interface TypesetMath {\n /** MathML (<math …>…</math>) for HTML/EPUB and the Word/PDF-inline writers. */\n mathml: string;\n /** SVG for PDF display math; widths in ex (1ex ≈ 0.442em in MathJax). */\n svg?: string | undefined;\n widthEx?: number | undefined;\n heightEx?: number | undefined;\n /** Error message when the TeX could not be typeset. */\n error?: string | undefined;\n}\n\nexport const mathKey = (tex: string, display: boolean): string => `${display ? \"D\" : \"I\"}:${tex}`;\n\ntype MathJaxKit = import(\"../mathjax\").MathJaxKit;\n\nconst MATHJAX = Symbol.for(\"ai-matrx.print.document.mathjax\");\n\nasync function loadMathJax(): Promise<MathJaxKit> {\n const g = globalThis as unknown as Record<symbol, Promise<MathJaxKit> | undefined>;\n if (g[MATHJAX]) return g[MATHJAX] as Promise<MathJaxKit>;\n // The self-referencing specifier keeps this a real lazy edge in dist (see src/mathjax.ts).\n const pending = import(\"@ai-matrx/print/mathjax\").then((m) => m.createMathJaxKit());\n pending.catch(() => {\n if (g[MATHJAX] === pending) g[MATHJAX] = undefined;\n });\n g[MATHJAX] = pending;\n return pending;\n}\n\n/** Typeset every math span once; keyed by `mathKey`. */\nexport async function typesetMathSpans(spans: MathSpan[]): Promise<Map<string, TypesetMath>> {\n const out = new Map<string, TypesetMath>();\n if (spans.length === 0) return out;\n const mj = await loadMathJax();\n for (const s of spans) {\n const key = mathKey(s.tex, s.display);\n if (out.has(key)) continue;\n try {\n const mathml = mj.mml(s.tex, s.display);\n const err = /<merror[\\s\\S]*?<mtext[^>]*>([\\s\\S]*?)<\\/mtext>/.exec(mathml);\n if (err) {\n out.set(key, { mathml: sanitizeMathml(mathml), error: decodeXml(err[1] as string) });\n continue;\n }\n // TeX is author input: `\\href`, `\\style`, `\\bbox[…]`, `\\mmlToken` reach\n // MathML attributes, so the typeset MathML goes through the allow-list.\n const entry: TypesetMath = { mathml: sanitizeMathml(mathml.replace(/\\n\\s*/g, \"\")) };\n if (s.display) {\n // The same author TeX reaches the SVG as <a href>, style, class and id\n // (LIST T26): only the drawing survives.\n const svg = sanitizeMathSvg(mj.svg(s.tex, true));\n entry.svg = svg;\n entry.widthEx = Number(/width=\"([\\d.]+)ex\"/.exec(svg)?.[1] ?? 0);\n entry.heightEx = Number(/height=\"([\\d.]+)ex\"/.exec(svg)?.[1] ?? 0);\n }\n out.set(key, entry);\n } catch (e) {\n out.set(key, { mathml: \"\", error: (e as Error).message });\n }\n }\n return out;\n}\n\n// ── a small MathML reader (MathJax's serializer output is well-formed XML) ──\n\nexport interface MNode {\n tag: string;\n attrs: Record<string, string>;\n children: MNode[];\n text: string;\n}\n\nexport function decodeXml(s: string): string {\n return s.replace(/&(#x[0-9a-f]+|#\\d+|amp|lt|gt|quot|apos);/gi, (m, e: string) => {\n if (e[0] === \"#\") return String.fromCodePoint(e[1] === \"x\" || e[1] === \"X\" ? parseInt(e.slice(2), 16) : Number(e.slice(1)));\n return ({ amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\" } as Record<string, string>)[e.toLowerCase()] ?? m;\n });\n}\n\nexport function parseMathML(xml: string): MNode {\n const root: MNode = { tag: \"#root\", attrs: {}, children: [], text: \"\" };\n const stack: MNode[] = [root];\n for (const m of xml.matchAll(/<(\\/?)([\\w:-]+)((?:\\s+[\\w:-]+=\"[^\"]*\")*)\\s*(\\/?)>|([^<]+)/g)) {\n const top = stack[stack.length - 1] as MNode;\n if (m[5] !== undefined) {\n const t = decodeXml(m[5]);\n if (t.trim() || top.tag === \"mtext\" || top.tag === \"mo\") top.text += t;\n continue;\n }\n if (m[1] === \"/\") {\n if (stack.length > 1) stack.pop();\n continue;\n }\n const attrs: Record<string, string> = {};\n for (const a of (m[3] ?? \"\").matchAll(/([\\w:-]+)=\"([^\"]*)\"/g)) attrs[a[1] as string] = decodeXml(a[2] as string);\n const node: MNode = { tag: (m[2] as string).replace(/^m:/, \"\"), attrs, children: [], text: \"\" };\n top.children.push(node);\n if (m[4] !== \"/\") stack.push(node);\n }\n return root.children[0] ?? root;\n}\n\n/** Flatten to plain text (the linear reading of a formula). */\nexport function mathText(n: MNode): string {\n if (n.children.length === 0) return n.text;\n return n.children.map(mathText).join(\"\");\n}\n","/**\n * @ai-matrx/print/safe-html — THE allow-list every Markdown→HTML string this\n * package hands out goes through.\n *\n * Why an allow-list: the regex converter (`markdownToHtml` /\n * `markdownToArticleHtml`) lets author raw HTML through so a document can feed\n * WordPress. Its old guard was a block-list (script, style, on… handlers,\n * javascript: URLs) and everything nobody listed — `<iframe>`, `<form>`,\n * `<meta http-equiv=refresh>`, `<object>`/`<embed>`, `<base>`, `<link>`,\n * inline `style` — reached the clipboard, the downloaded page and the preview.\n * Now a tag or attribute survives only when it is listed here.\n *\n * Sync and DOM-free (parse5 via hast), so it runs in Node, workers, the\n * extension service worker and Tauri alike.\n *\n * Two schemas:\n * • `SAFE_HTML_SCHEMA` — GitHub's rendering rules (hast-util-sanitize's\n * default schema) with no id clobber-prefixing and the \"drop the element\n * AND its text\" list for elements whose text is not prose. For any host\n * that renders plain semantic HTML (email bodies, CMS drafts).\n * • `PRINT_HTML_SCHEMA` — the same, plus exactly the vocabulary print's own\n * converters emit: `matrx-*` / `print-*` / `code-*` / `doc-*` classes,\n * table-cell alignment, the flashcard checkbox + label, figures, and the\n * page-break separator's data/aria attributes.\n */\n\nimport { fromHtml } from \"hast-util-from-html\";\nimport { defaultSchema, sanitize, type Schema } from \"hast-util-sanitize\";\nimport { toHtml } from \"hast-util-to-html\";\nimport type { Root } from \"hast\";\n\ntype Attributes = NonNullable<Schema[\"attributes\"]>;\ntype AttributeList = Attributes[string];\n\n/** Elements removed together with their content (their text is not prose). */\nconst STRIP_WITH_CONTENT = [\n \"script\",\n \"style\",\n \"noscript\",\n \"template\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"textarea\",\n \"select\",\n \"head\",\n \"title\",\n];\n\n/** Never allowed, whatever a schema extension adds. */\nexport const FORBIDDEN_TAGS: readonly string[] = [\n ...STRIP_WITH_CONTENT,\n \"form\",\n \"meta\",\n \"base\",\n \"link\",\n \"frame\",\n \"frameset\",\n \"applet\",\n \"button\",\n \"svg\",\n \"math\",\n];\n\nconst baseAttributes = defaultSchema.attributes ?? {};\n\nexport const SAFE_HTML_SCHEMA: Schema = {\n ...defaultSchema,\n tagNames: (defaultSchema.tagNames ?? []).filter((t) => !FORBIDDEN_TAGS.includes(t)),\n strip: STRIP_WITH_CONTENT,\n // Authored anchors keep their names; the GitHub `user-content-` rewrite\n // would also break `<label for>` → `<input id>` pairs.\n clobberPrefix: \"\",\n clobber: [],\n};\n\n/** Class tokens print's converters emit. Author classes outside these are dropped. */\nconst PRINT_CLASS = /^(?:(?:matrx|print|code|doc|language)-[\\w-]*|inline-code|is-checked)$/;\n\n/**\n * A tag's own `className` definition wins over `'*'` (and returns an empty\n * list rather than falling through), so the print class pattern is merged into\n * every definition that names `className`, and added where none does.\n */\nfunction withPrintClasses(defs: AttributeList = []): AttributeList {\n let found = false;\n const out = defs.map((d) => {\n if (Array.isArray(d) && d[0] === \"className\") {\n found = true;\n return [...d, PRINT_CLASS] as unknown as typeof d;\n }\n return d;\n });\n return found ? out : [...out, [\"className\", PRINT_CLASS]];\n}\n\nconst printAttributes: Attributes = Object.fromEntries(\n Object.entries(baseAttributes).map(([tag, defs]) => [tag, withPrintClasses(defs)]),\n);\n\nconst cellAlign = [\"style\", /^text-align:\\s*(?:left|center|right);?$/] as const;\n\nexport const PRINT_HTML_SCHEMA: Schema = {\n ...SAFE_HTML_SCHEMA,\n tagNames: [\n ...new Set([...(SAFE_HTML_SCHEMA.tagNames ?? []), \"section\", \"figure\", \"figcaption\", \"label\"]),\n ],\n attributes: {\n ...printAttributes,\n \"*\": [\n ...(baseAttributes[\"*\"] ?? []),\n [\"className\", PRINT_CLASS],\n \"role\",\n \"ariaLabel\",\n \"ariaHidden\",\n \"dataLabel\",\n \"dataLanguage\",\n \"dataArtifactType\",\n ],\n // Table-cell alignment is the one inline style the converter writes.\n td: withPrintClasses([...(baseAttributes[\"td\"] ?? []), [...cellAlign]]),\n th: withPrintClasses([...(baseAttributes[\"th\"] ?? []), [...cellAlign]]),\n // The flashcard flip is a checkbox + label pair (GitHub forces `disabled`).\n input: withPrintClasses([[\"type\", \"checkbox\"], \"checked\"]),\n label: withPrintClasses([\"htmlFor\"]),\n },\n required: { input: { type: \"checkbox\" } },\n protocols: {\n ...defaultSchema.protocols,\n href: [...(defaultSchema.protocols?.href ?? []), \"tel\"],\n // Embedded images (`data:image/png;base64,…`) — an <img> never runs script.\n src: [...(defaultSchema.protocols?.src ?? []), \"data\"],\n },\n};\n\n/** Sanitize an HTML fragment against an allow-list (default: `SAFE_HTML_SCHEMA`). */\nexport function sanitizeHtmlFragment(html: string, schema: Schema = SAFE_HTML_SCHEMA): string {\n if (!html) return \"\";\n const tree = sanitize(fromHtml(html, { fragment: true }), schema) as Root;\n return toHtml(tree, { characterReferences: { useNamedReferences: true } });\n}\n\n/** Sanitize the output of print's own converters (keeps the `.matrx-*` vocabulary). */\nexport function sanitizePrintHtml(html: string): string {\n return sanitizeHtmlFragment(html, PRINT_HTML_SCHEMA);\n}\n\nexport type { Schema as SafeHtmlSchema };\n\n/**\n * MathML from the TeX typesetter. TeX is author input, and MathJax's `html`,\n * `bbox` and `\\mmlToken` commands turn it into attributes (`\\href{javascript:…}`\n * → `href`, `\\style{position:fixed…}` / `\\bbox[position:fixed]` → `style`).\n * Only presentation MathML elements and their layout attributes survive.\n */\nconst MATHML_TAGS = [\n \"math\", \"maction\", \"annotation\", \"menclose\", \"merror\", \"mfrac\", \"mglyph\", \"mi\",\n \"mlabeledtr\", \"mmultiscripts\", \"mn\", \"mo\", \"mover\", \"mpadded\", \"mphantom\", \"mprescripts\", \"none\",\n \"mroot\", \"mrow\", \"ms\", \"mspace\", \"msqrt\", \"mstyle\", \"msub\", \"msubsup\", \"msup\", \"mtable\", \"mtd\",\n \"mtext\", \"mtr\", \"munder\", \"munderover\", \"semantics\",\n];\n\nconst MATHML_ATTRIBUTES = [\n \"xmlns\", \"display\", \"displaystyle\", \"mathvariant\", \"mathsize\", \"mathcolor\", \"mathbackground\",\n \"scriptlevel\", \"dir\", \"fence\", \"form\", \"largeop\", \"lspace\", \"rspace\", \"movablelimits\",\n \"separator\", \"stretchy\", \"symmetric\", \"maxsize\", \"minsize\", \"accent\", \"accentunder\", \"align\",\n \"columnalign\", \"rowalign\", \"columnspacing\", \"rowspacing\", \"columnlines\", \"rowlines\", \"frame\",\n \"framespacing\", \"equalrows\", \"equalcolumns\", \"linethickness\", \"numalign\", \"denomalign\",\n \"bevelled\", \"notation\", \"width\", \"height\", \"depth\", \"voffset\", \"lquote\", \"rquote\", \"columnspan\",\n \"rowspan\", \"encoding\", \"open\", \"close\", \"separators\", \"actiontype\", \"selection\", \"mslinebreak\",\n \"linebreak\", \"indentalign\", \"data*\",\n];\n\nexport const MATHML_SCHEMA: Schema = {\n tagNames: MATHML_TAGS,\n attributes: { \"*\": MATHML_ATTRIBUTES },\n strip: [\"script\", \"style\"],\n clobberPrefix: \"\",\n clobber: [],\n protocols: {},\n};\n\n/** Sanitize a typeset MathML string (keeps layout, drops links and styles). */\nexport function sanitizeMathml(mathml: string): string {\n return sanitizeHtmlFragment(mathml, MATHML_SCHEMA);\n}\n\n/**\n * SVG from the TeX typesetter (PDF display math, LIST T26). MathJax's `\\href`,\n * `\\style`, `\\class`, `\\cssId`, `\\bbox[…]` and `\\mmlToken[…]` reach the SVG as\n * `<a href=\"javascript:…\">`, `style=\"position:fixed…\"`, `class`, `id`. The PDF\n * writer draws this SVG, and any host that shows it would too, so only the\n * drawing vocabulary survives: shapes, paths, text, transforms and paint. A\n * link wrapper is unwrapped (its glyphs stay), everything else is dropped.\n */\nconst MATH_SVG_TAGS = [\"svg\", \"g\", \"path\", \"rect\", \"line\", \"polyline\", \"polygon\", \"circle\", \"ellipse\", \"text\", \"title\", \"desc\"];\n\nconst MATH_SVG_ATTRIBUTES = [\n \"xmlns\", \"width\", \"height\", \"viewBox\", \"role\", \"focusable\", \"ariaHidden\", \"ariaLabel\",\n \"stroke\", \"fill\", \"strokeWidth\", \"strokeLinecap\", \"strokeLinejoin\", \"transform\", \"d\",\n \"x\", \"y\", \"x1\", \"y1\", \"x2\", \"y2\", \"points\", \"cx\", \"cy\", \"r\", \"rx\", \"ry\",\n \"dataMmlNode\", \"dataC\",\n];\n\nexport const MATH_SVG_SCHEMA: Schema = {\n tagNames: MATH_SVG_TAGS,\n attributes: { \"*\": MATH_SVG_ATTRIBUTES },\n strip: [\"script\", \"style\", \"foreignObject\"],\n clobberPrefix: \"\",\n clobber: [],\n protocols: {},\n};\n\n/** Sanitize a typeset math SVG (keeps the drawing, drops links, styles, classes and ids). */\nexport function sanitizeMathSvg(svg: string): string {\n return sanitizeHtmlFragment(svg, MATH_SVG_SCHEMA);\n}\n\n/**\n * THE final allow-list of the one Markdown→HTML engine (`@ai-matrx/print/\n * markdown-html`, CONTRACT §1.9): print's own vocabulary plus typeset MathML\n * (MathML elements carry only the MathML layout attributes), and — for the\n * inline clipboard skin only — `style` attributes whose value is EXACTLY one\n * the engine itself wrote (`inlineStyles`). An author can never reach a style.\n */\nexport function engineHtmlSchema(inlineStyles: readonly string[] = []): Schema {\n const exact = inlineStyles.length\n ? [new RegExp(`^(?:${[...new Set(inlineStyles)].map((s) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"|\")})$`)]\n : [];\n const mathAttributes = Object.fromEntries(MATHML_TAGS.map((tag) => [tag, MATHML_ATTRIBUTES]));\n const printAttrs = PRINT_HTML_SCHEMA.attributes ?? {};\n const styled = (defs: AttributeList = []): AttributeList =>\n exact.length ? [...defs.filter((d) => !(Array.isArray(d) && d[0] === \"style\")), [\"style\", ...exact]] : defs;\n const attributes: Attributes = { ...printAttrs, ...mathAttributes };\n if (exact.length) {\n for (const tag of new Set([...(PRINT_HTML_SCHEMA.tagNames ?? []), \"div\", \"span\"])) {\n if (MATHML_TAGS.includes(tag)) continue;\n attributes[tag] = styled(printAttrs[tag] ?? []);\n }\n // Table cells keep their alignment AND the skin's cell style (the skin writes both into one value).\n }\n return {\n ...PRINT_HTML_SCHEMA,\n tagNames: [...new Set([...(PRINT_HTML_SCHEMA.tagNames ?? []), ...MATHML_TAGS])],\n attributes,\n };\n}\n\n/** Sanitize the engine's output (see `engineHtmlSchema`). */\nexport function sanitizeEngineHtml(html: string, inlineStyles: readonly string[] = []): string {\n return sanitizeHtmlFragment(html, engineHtmlSchema(inlineStyles));\n}\n","/**\n * Default print stylesheets for `@ai-matrx/print/markdown`.\n *\n * Two skins, ONE semantic-token vocabulary (C26 — structural CSS ships in the\n * package, the token CONTRACT is enforced, token VALUES are host-overridable):\n *\n * \"document\" — the `.matrx-*` content sheet. What `markdownToHtml` emits,\n * what the markdown→PDF lane renders, and what pastes straight\n * into a WordPress theme.\n * \"article\" — the serif AI-response sheet used by the print-window lane.\n *\n * Every colour is `var(--matrx-print-<role>, <default>)`. The inline fallback\n * is what makes the drop-in test pass in a fresh, unthemed app; the `:root`\n * block is the shipped default token-values sheet a host overrides by\n * appending its own `:root` rule (`getMarkdownStylesheet({ tokens })`).\n *\n * Both sheets carry the print hard parts — `@media print`, page-break\n * avoidance, orphans/widows, `print-color-adjust` — so no host re-derives them.\n *\n * THIS FILE IS THE SOURCE OF TRUTH. It was seeded once from the two host\n * stylesheets it replaced (matrx-frontend's WordPress sheet and its serif\n * print sheet, both deleted in the same session); there is no generator to\n * re-run and no upstream to diff against. Edit it here.\n *\n * DO NOT hand-edit colour literals back in: a hardcoded colour in package CSS\n * is a C26 violation. Add a role to the vocabulary instead.\n */\n\n/** Default token VALUES for the document skin. */\nexport const MARKDOWN_DOCUMENT_TOKENS_CSS = `:root {\n --matrx-print-accent: #667eea;\n --matrx-print-accent-2: #764ba2;\n --matrx-print-bg: #ffffff;\n --matrx-print-border: #e1e1e1;\n --matrx-print-border-muted: #d1d5db;\n --matrx-print-border-strong: #e5e5e5;\n --matrx-print-code-bg: #f5f5f5;\n --matrx-print-code-block-bg: #f8f8f8;\n --matrx-print-code-block-fg: #333;\n --matrx-print-code-fg: #d73a49;\n --matrx-print-fg: #1a1a1a;\n --matrx-print-fg-muted: #4a4a4a;\n --matrx-print-fg-subtle: #6b7280;\n --matrx-print-heading: #2a2a2a;\n --matrx-print-heading-sub: #3a3a3a;\n --matrx-print-link-fg: #374151;\n --matrx-print-link-hover-fg: #1f2937;\n --matrx-print-link-underline: #9ca3af;\n --matrx-print-quote-fg: #666;\n --matrx-print-shadow: rgba(0, 0, 0, 0.1);\n --matrx-print-shadow-strong: rgba(0, 0, 0, 0.2);\n --matrx-print-success: #2d5016;\n --matrx-print-success-2: #1e3a0f;\n --matrx-print-surface: #f8f9fa;\n --matrx-print-table-fg: #333333;\n}`;\n\n/** Default token VALUES for the article skin. */\nexport const MARKDOWN_ARTICLE_TOKENS_CSS = `:root {\n --matrx-print-accent: #2563eb;\n --matrx-print-bg: #fff;\n --matrx-print-border: #e5e7eb;\n --matrx-print-border-muted: #d1d5db;\n --matrx-print-code-bg: #f1f5f9;\n --matrx-print-fg: #1a1a1a;\n --matrx-print-fg-muted: #374151;\n --matrx-print-fg-subtle: #64748b;\n --matrx-print-heading: #111827;\n --matrx-print-ink: #0f172a;\n --matrx-print-line: #e2e8f0;\n --matrx-print-quote-bg: #f5f3ff;\n --matrx-print-quote-border: #6366f1;\n --matrx-print-surface: #f8fafc;\n --matrx-print-surface-sunken: #f3f4f6;\n --matrx-print-table-head-bg: #1e293b;\n --matrx-print-table-head-border: #334155;\n}`;\n\n/** Structural CSS for the document skin (`.matrx-*` classes). */\nexport const MARKDOWN_DOCUMENT_CSS = `/* \n * MATRX WordPress CSS - Production Ready\n * Use this CSS in your WordPress theme to style MATRX content\n * All styles are scoped to .matrx-content-container to avoid conflicts\n */\n\n/* Content Container */\n.matrx-content-container {\n max-width: 800px;\n margin: 0 auto;\n padding: 2rem;\n line-height: 1.6;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n}\n\n/* Typography */\n.matrx-h1 {\n font-size: 2.5rem;\n font-weight: 700;\n margin: 0 0 1.5rem 0;\n color: var(--matrx-print-fg, #1a1a1a);\n line-height: 1.2;\n}\n\n.matrx-h2 {\n font-size: 1.8rem;\n font-weight: 600;\n margin: 3rem 0 1rem 0 !important;\n color: var(--matrx-print-heading, #2a2a2a);\n line-height: 1.3 !important;\n border-bottom: 2px solid var(--matrx-print-border-strong, #e5e5e5);\n padding-bottom: 0.75rem !important;\n padding-top: 0 !important;\n}\n\n.matrx-h3 {\n font-size: 1.3rem;\n font-weight: 600;\n margin: 2rem 0 1rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n.matrx-h4 {\n font-size: 1.1rem;\n font-weight: 600;\n margin: 1.5rem 0 0.75rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n.matrx-h5 {\n font-size: 1rem;\n font-weight: 600;\n margin: 1.25rem 0 0.5rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n.matrx-h6 {\n font-size: 0.9rem;\n font-weight: 600;\n margin: 1rem 0 0.5rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n/* Paragraphs */\n.matrx-intro {\n font-size: 1.1rem;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n margin-bottom: 2rem;\n padding: 1.5rem;\n background: var(--matrx-print-surface, #f8f9fa);\n border-left: 4px solid var(--matrx-print-border-muted, #d1d5db);\n border-radius: 0 8px 8px 0;\n}\n\n.matrx-paragraph {\n font-size: 1rem;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n margin-bottom: 1.5rem;\n text-align: justify;\n}\n\n/* Text Formatting */\n.matrx-em {\n font-style: italic;\n color: var(--matrx-print-heading, #2a2a2a);\n}\n\n.matrx-strong {\n font-weight: 600;\n color: var(--matrx-print-heading, #2a2a2a);\n}\n\n/* Links */\n.matrx-link {\n color: var(--matrx-print-link-fg, #374151);\n text-decoration: underline;\n text-decoration-color: var(--matrx-print-border-muted, #d1d5db);\n text-underline-offset: 3px;\n transition: all 0.2s ease;\n}\n\n.matrx-link:hover {\n color: var(--matrx-print-link-hover-fg, #1f2937);\n text-decoration-color: var(--matrx-print-link-underline, #9ca3af);\n}\n\n/* Images */\n.matrx-image {\n max-width: 100%;\n height: auto;\n display: block;\n margin: 1.5rem auto;\n border-radius: 8px;\n box-shadow: 0 2px 8px var(--matrx-print-shadow, rgba(0, 0, 0, 0.1));\n}\n\n/* Lists */\n.matrx-list {\n margin: 1.5rem 0;\n padding-left: 0;\n}\n\n.matrx-bullet-list {\n list-style: none;\n}\n\n.matrx-list-item {\n margin-bottom: 1rem;\n padding-left: 1.5rem;\n position: relative;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n}\n\n.matrx-list-item::before {\n content: \"•\";\n color: var(--matrx-print-fg-subtle, #6b7280);\n font-weight: bold;\n position: absolute;\n left: 0;\n top: 0;\n font-size: 1.2rem;\n}\n\n/* Numbered Lists */\n.matrx-numbered-list {\n list-style: decimal;\n padding-left: 2rem;\n}\n\n.matrx-numbered-list > .matrx-list-item {\n padding-left: 0.5rem;\n}\n\n.matrx-numbered-list > .matrx-list-item::before {\n content: none;\n /* Remove the bullet */\n}\n\n/* Nested list indentation */\n.matrx-nested-list {\n margin-top: 0.5rem;\n margin-bottom: 0.5rem;\n padding-left: 1.5rem !important;\n}\n\n/* Ensure nested lists within list items are properly indented */\n.matrx-list-item > .matrx-list {\n margin-top: 0.5rem;\n margin-bottom: 0.5rem;\n padding-left: 1.5rem;\n}\n\n/* For multiple levels of nesting, increase indentation */\n.matrx-nested-list .matrx-nested-list {\n padding-left: 1.5rem !important;\n}\n\n/* Code */\n.matrx-content-container .matrx-inline-code {\n background-color: var(--matrx-print-code-bg, #f5f5f5);\n padding: 0.125rem 0.25rem;\n border-radius: 0.25rem;\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n color: var(--matrx-print-code-fg, #d73a49);\n}\n\n.matrx-content-container .matrx-code-block {\n background-color: var(--matrx-print-code-block-bg, #f8f8f8);\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 1.5rem 0;\n overflow-x: auto;\n}\n\n.matrx-content-container .matrx-code {\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--matrx-print-code-block-fg, #333);\n}\n\n/* Blockquotes */\n.matrx-content-container .matrx-blockquote {\n border-left: 4px solid var(--matrx-print-border, #e1e1e1);\n padding-left: 1rem;\n margin: 1.5rem 0;\n font-style: italic;\n color: var(--matrx-print-quote-fg, #666);\n}\n\n/* Tables */\n.matrx-content-container .matrx-table {\n width: 100%;\n border-collapse: collapse;\n margin: 1.5rem 0;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n}\n\n.matrx-content-container .matrx-table-header {\n background-color: var(--matrx-print-surface, #f8f9fa);\n font-weight: 600;\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n text-align: left;\n}\n\n.matrx-content-container .matrx-table-cell {\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n}\n\n.matrx-content-container .matrx-table-row:nth-child(even) {\n background-color: var(--matrx-print-surface, #f8f9fa);\n}\n\n/* Images */\n.matrx-content-container .matrx-image {\n max-width: 100%;\n height: auto;\n border-radius: 0.375rem;\n margin: 1rem 0;\n}\n\n/* Horizontal Rules */\n.matrx-content-container .matrx-hr {\n border: none;\n border-top: 1px solid var(--matrx-print-border, #e1e1e1);\n margin: 2rem 0;\n}\n\n/* FAQ Styles - Clean Design */\n.matrx-faq-item {\n margin-bottom: 2rem;\n padding: 1.5rem;\n background: var(--matrx-print-surface, #f8f9fa);\n border-radius: 8px;\n border-left: 4px solid var(--matrx-print-border-muted, #d1d5db);\n}\n\n.matrx-faq-question {\n font-size: 1.2rem;\n font-weight: 600;\n margin: 0 0 0.75rem 0;\n color: var(--matrx-print-heading, #2a2a2a);\n}\n\n.matrx-faq-answer {\n margin: 0;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n line-height: 1.6;\n}\n\n/* Code Blocks */\n.matrx-inline-code {\n background-color: var(--matrx-print-code-bg, #f5f5f5);\n padding: 0.125rem 0.25rem;\n border-radius: 0.25rem;\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n color: var(--matrx-print-code-fg, #d73a49);\n}\n\n.matrx-code-block {\n background-color: var(--matrx-print-code-block-bg, #f8f8f8);\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 1.5rem 0;\n overflow-x: auto;\n}\n\n.matrx-code {\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--matrx-print-code-block-fg, #333);\n}\n\n/* Blockquotes */\n.matrx-blockquote {\n border-left: 4px solid var(--matrx-print-border, #e1e1e1);\n padding-left: 1rem;\n margin: 1.5rem 0;\n font-style: italic;\n color: var(--matrx-print-quote-fg, #666);\n}\n\n/* Tables */\n.matrx-table {\n width: 100%;\n border-collapse: collapse;\n margin: 1.5rem 0;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-head {\n background-color: var(--matrx-print-surface, #f8f9fa);\n}\n\n.matrx-table-body {\n background-color: var(--matrx-print-bg, #ffffff);\n}\n\n.matrx-table-header {\n background-color: var(--matrx-print-surface, #f8f9fa);\n font-weight: 600;\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n text-align: left;\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-cell {\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-row {\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-row:nth-child(even) {\n background-color: var(--matrx-print-surface, #f8f9fa);\n}\n\n/* Ensure table content doesn't inherit external colors */\n.matrx-table * {\n color: inherit;\n}\n\n/* Images */\n.matrx-image {\n max-width: 100%;\n height: auto;\n border-radius: 0.375rem;\n margin: 1rem 0;\n}\n\n/* Horizontal Rules */\n.matrx-hr {\n border: none;\n border-top: 1px solid var(--matrx-print-border, #e1e1e1);\n margin: 2rem 0;\n}\n\n/* Utility Classes */\n.matrx-div {\n margin: 0.5rem 0;\n}\n\n.matrx-span {\n /* Inherit parent styling by default */\n}\n\n/* Flashcard Styles */\n.matrx-flashcards-container {\n margin: 2rem 0;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.matrx-flashcard {\n perspective: 1000px;\n perspective-origin: center center;\n height: 200px;\n cursor: pointer;\n position: relative;\n margin-bottom: 0.5rem;\n}\n\n.matrx-flashcard-input {\n display: none;\n}\n\n.matrx-flashcard-inner {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n transition: transform 0.6s;\n transform-style: preserve-3d;\n transform-origin: center center;\n}\n\n.matrx-flashcard-input:checked ~ .matrx-flashcard-inner {\n transform: rotateY(180deg);\n}\n\n.matrx-flashcard-front,\n.matrx-flashcard-back {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n backface-visibility: hidden;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1.5rem;\n box-shadow: 0 4px 6px var(--matrx-print-shadow, rgba(0, 0, 0, 0.1));\n transition: box-shadow 0.3s ease;\n box-sizing: border-box;\n}\n\n.matrx-flashcard-front {\n background: linear-gradient(135deg, var(--matrx-print-accent, #667eea) 0%, var(--matrx-print-accent-2, #764ba2) 100%);\n color: white;\n}\n\n.matrx-flashcard-back {\n background: linear-gradient(135deg, var(--matrx-print-success, #2d5016) 0%, var(--matrx-print-success-2, #1e3a0f) 100%);\n color: white;\n transform: rotateY(180deg);\n}\n\n.matrx-flashcard:hover .matrx-flashcard-front,\n.matrx-flashcard:hover .matrx-flashcard-back {\n box-shadow: 0 8px 16px var(--matrx-print-shadow-strong, rgba(0, 0, 0, 0.2));\n}\n\n.matrx-flashcard-content {\n text-align: center;\n font-size: 1.3rem;\n line-height: 1.8;\n max-height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.matrx-flashcard-label {\n cursor: pointer;\n user-select: none;\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 10;\n}\n\n.matrx-flashcard-indicator {\n position: absolute;\n bottom: 12px;\n right: 16px;\n font-size: 0.75rem;\n opacity: 0.8;\n font-weight: 500;\n letter-spacing: 0.5px;\n}\n\n/* Responsive adjustments */\n@media (max-width: 768px) {\n .matrx-flashcard {\n height: 180px;\n }\n\n .matrx-flashcard-content {\n font-size: 1rem;\n }\n\n .matrx-flashcard-front,\n .matrx-flashcard-back {\n padding: 1.25rem;\n }\n}\n\n@media (max-width: 480px) {\n .matrx-flashcard {\n height: 160px;\n }\n\n .matrx-flashcard-content {\n font-size: 0.95rem;\n }\n\n .matrx-flashcard-front,\n .matrx-flashcard-back {\n padding: 1rem;\n }\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .matrx-content-container {\n padding: 1rem;\n }\n\n .matrx-h1 {\n font-size: 2rem;\n }\n\n .matrx-h2 {\n font-size: 1.5rem;\n margin: 2rem 0 1rem 0 !important;\n }\n\n .matrx-h3 {\n font-size: 1.2rem;\n }\n\n .matrx-h4 {\n font-size: 1.05rem;\n }\n\n .matrx-h5 {\n font-size: 0.95rem;\n }\n\n .matrx-h6 {\n font-size: 0.9rem;\n }\n\n .matrx-intro {\n padding: 1rem;\n font-size: 1rem;\n }\n\n .matrx-faq-question {\n font-size: 1.1rem;\n margin: 1.5rem 0 0.75rem 0;\n }\n\n .matrx-paragraph {\n text-align: left;\n }\n\n .matrx-table {\n font-size: 0.875rem;\n }\n\n .matrx-table-header,\n .matrx-table-cell {\n padding: 0.5rem;\n }\n\n .matrx-code-block {\n padding: 0.75rem;\n margin: 1rem 0;\n }\n}\n\n@media (max-width: 480px) {\n .matrx-content-container {\n padding: 0.75rem;\n }\n\n .matrx-h1 {\n font-size: 1.75rem;\n margin-bottom: 1rem;\n }\n\n .matrx-h2 {\n font-size: 1.3rem;\n margin: 1.5rem 0 0.75rem 0 !important;\n }\n\n .matrx-h3 {\n font-size: 1.1rem;\n margin: 1.5rem 0 0.75rem 0;\n }\n\n .matrx-h4 {\n font-size: 1rem;\n margin: 1.25rem 0 0.5rem 0;\n }\n\n .matrx-h5 {\n font-size: 0.9rem;\n margin: 1rem 0 0.5rem 0;\n }\n\n .matrx-h6 {\n font-size: 0.85rem;\n margin: 1rem 0 0.5rem 0;\n }\n\n .matrx-intro {\n padding: 0.75rem;\n margin-bottom: 1.5rem;\n }\n\n .matrx-paragraph {\n margin-bottom: 1.25rem;\n }\n\n .matrx-list-item {\n margin-bottom: 0.75rem;\n }\n\n .matrx-table-header,\n .matrx-table-cell {\n padding: 0.375rem;\n }\n\n .matrx-code-block {\n padding: 0.5rem;\n font-size: 0.8rem;\n }\n\n .matrx-inline-code {\n font-size: 0.8rem;\n }\n}\n/* Print / PDF hard parts — added by @ai-matrx/print 0.3.0. */\n@media print {\n .matrx-content-container {\n max-width: 100%;\n padding: 0;\n }\n\n .matrx-h1,\n .matrx-h2,\n .matrx-h3,\n .matrx-h4,\n .matrx-h5,\n .matrx-h6 {\n page-break-after: avoid;\n break-after: avoid;\n }\n\n .matrx-paragraph,\n .matrx-intro {\n orphans: 3;\n widows: 3;\n }\n\n .matrx-table,\n .matrx-code-block,\n .matrx-blockquote,\n .matrx-list-item,\n .matrx-image,\n .matrx-faq-item,\n .matrx-flashcard {\n page-break-inside: avoid;\n break-inside: avoid;\n }\n\n .matrx-table-header,\n .matrx-code-block,\n .matrx-blockquote,\n .matrx-flashcard {\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n }\n\n .matrx-link {\n text-decoration: underline;\n }\n}\n`;\n\n/** Structural CSS for the article skin (bare tags + `.print-*` classes). */\nexport const MARKDOWN_ARTICLE_CSS = `*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}body{font-family:Georgia,'Times New Roman',serif;font-size:11pt;line-height:1.7;color:var(--matrx-print-fg, #1a1a1a);background:var(--matrx-print-bg, #fff);max-width:780px;margin:0 auto;padding:32px 40px}.print-actions{display:flex;gap:10px;margin-bottom:28px;padding-bottom:20px;border-bottom:2px solid var(--matrx-print-border, #e5e7eb)}.print-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 20px;border-radius:8px;font-size:13px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;cursor:pointer;border:none;font-weight:600;transition:opacity .15s}.print-btn:hover{opacity:.85}.print-btn-primary{background:var(--matrx-print-accent, #2563eb);color:var(--matrx-print-bg, #fff)}.print-btn-secondary{background:var(--matrx-print-surface-sunken, #f3f4f6);color:var(--matrx-print-fg-muted, #374151);border:1px solid var(--matrx-print-border-muted, #d1d5db)}h1,h2,h3,h4,h5,h6{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Helvetica Neue',sans-serif;font-weight:700;line-height:1.3;color:var(--matrx-print-heading, #111827);margin-top:1.6em;margin-bottom:.5em;page-break-after:avoid}h1{font-size:24pt;border-bottom:2px solid var(--matrx-print-border, #e5e7eb);padding-bottom:.3em;margin-top:0}h2{font-size:18pt;border-bottom:1px solid var(--matrx-print-border, #e5e7eb);padding-bottom:.2em}h3{font-size:14pt}p{margin-bottom:.9em;orphans:3;widows:3}a{color:var(--matrx-print-accent, #2563eb);text-decoration:underline;word-break:break-word}ul,ol{margin:.6em 0 .9em 0;padding-left:1.8em}li{margin-bottom:.3em;page-break-inside:avoid}blockquote{margin:1em 0;padding:.7em 1em .7em 1.2em;border-left:4px solid var(--matrx-print-quote-border, #6366f1);background:var(--matrx-print-quote-bg, #f5f3ff);border-radius:0 6px 6px 0;color:var(--matrx-print-fg-muted, #374151);font-style:italic;page-break-inside:avoid}.inline-code{font-family:'JetBrains Mono','Fira Code',monospace;font-size:9.5pt;background:var(--matrx-print-code-bg, #f1f5f9);color:var(--matrx-print-ink, #0f172a);padding:1px 5px;border-radius:4px;border:1px solid var(--matrx-print-line, #e2e8f0)}.code-block-wrapper{position:relative;margin:1em 0 1.2em;border-radius:8px;overflow:hidden;border:1px solid var(--matrx-print-line, #e2e8f0);page-break-inside:avoid}.code-lang{display:block;font-family:-apple-system,sans-serif;font-size:9pt;font-weight:600;color:var(--matrx-print-fg-subtle, #64748b);background:var(--matrx-print-surface, #f8fafc);padding:4px 12px;border-bottom:1px solid var(--matrx-print-line, #e2e8f0);text-transform:uppercase;letter-spacing:.05em}.code-block{margin:0;padding:14px 16px;background:var(--matrx-print-ink, #0f172a);color:var(--matrx-print-line, #e2e8f0);font-family:'JetBrains Mono',monospace;font-size:9pt;line-height:1.6;overflow-x:auto;white-space:pre;tab-size:2}.print-table{width:100%;border-collapse:collapse;margin:1em 0 1.2em;font-size:10pt;page-break-inside:avoid}.print-table th{background:var(--matrx-print-table-head-bg, #1e293b);color:var(--matrx-print-surface, #f8fafc);font-family:-apple-system,sans-serif;font-weight:600;font-size:9.5pt;text-align:left;padding:8px 12px;border:1px solid var(--matrx-print-table-head-border, #334155)}.print-table td{padding:7px 12px;border:1px solid var(--matrx-print-line, #e2e8f0);vertical-align:top}.print-table tr:nth-child(even) td{background:var(--matrx-print-surface, #f8fafc)}hr{border:none;border-top:2px solid var(--matrx-print-border, #e5e7eb);margin:1.5em 0}.print-figure{margin:1em 0 1.2em;page-break-inside:avoid}.print-image{display:block;max-width:100%;height:auto;margin:0 auto;border-radius:6px}.print-figure figcaption{margin-top:.4em;text-align:center;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:9.5pt;color:var(--matrx-print-fg-subtle, #64748b)}@media print{body{max-width:100%;padding:0;font-size:10.5pt}.print-actions{display:none!important}.code-block,.print-table th,blockquote{color-adjust:exact;-webkit-print-color-adjust:exact;print-color-adjust:exact}a[href]::after{content:\" (\"attr(href)\")\";font-size:9pt;color:var(--matrx-print-fg-subtle, #64748b)}a[href^=\"#\"]::after,a[href^=\"javascript:\"]::after{content:\"\"}h1,h2,h3{page-break-after:avoid}pre,blockquote,table{page-break-inside:avoid}}`;\n\n/**\n * Rules BOTH skins share — appended after the skin sheet by\n * `getMarkdownStylesheet`. Everything the two converters emit identically\n * (page breaks, artifact panels, task boxes, accent/heavy rules, strikethrough,\n * printed flashcard pairs) is styled once here so the skins cannot drift, plus\n * the print hard parts every printed page needs regardless of skin.\n *\n * Page break: on screen a dashed rule labelled \"Page break\" with generous\n * padding — the same idea the chat preview draws; on paper a zero-height\n * element that forces the next content onto a new page.\n */\nexport const MARKDOWN_SHARED_CSS = `\n.matrx-page-break {\n position: relative;\n clear: both;\n height: 0;\n margin: 2.75rem 0;\n border: 0;\n border-top: 2px dashed var(--matrx-print-border-muted, #d1d5db);\n}\n.matrx-page-break::after {\n content: attr(data-label);\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n padding: 0 0.75rem;\n background: var(--matrx-print-bg, #ffffff);\n color: var(--matrx-print-fg-subtle, #6b7280);\n font: 600 10px/1.6 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n white-space: nowrap;\n}\n/* GFM footnotes: numbered superscript references, notes gathered at the end. */\n.print-footnote-ref {\n font-size: 0.75em;\n line-height: 0;\n vertical-align: super;\n}\n.print-footnote-ref a {\n text-decoration: none;\n}\n.print-footnotes {\n margin-top: 2em;\n font-size: 0.9em;\n color: var(--matrx-print-fg-subtle, #6b7280);\n page-break-inside: avoid;\n}\n.print-footnotes hr {\n width: 33%;\n margin: 0 0 0.75em;\n border: 0;\n border-top: 1px solid var(--matrx-print-border-muted, #d1d5db);\n}\n.print-footnotes ol {\n margin: 0;\n padding-left: 1.6em;\n}\n.print-footnotes li p {\n margin: 0 0 0.35em;\n}\n\n.matrx-artifact {\n margin: 1.25em 0;\n border: 1px solid var(--matrx-print-border, #e5e7eb);\n border-radius: 8px;\n overflow: hidden;\n}\n.matrx-artifact-header {\n display: flex;\n align-items: baseline;\n gap: 0.6em;\n padding: 0.45em 0.9em;\n background: var(--matrx-print-surface, #f8fafc);\n border-bottom: 1px solid var(--matrx-print-border, #e5e7eb);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n.matrx-artifact-kind {\n font-size: 0.72em;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--matrx-print-accent, #2563eb);\n}\n.matrx-artifact-title {\n font-weight: 600;\n color: var(--matrx-print-fg, #1a1a1a);\n}\n.matrx-artifact-body {\n padding: 0.8em 0.9em;\n}\n.matrx-artifact-body > :first-child {\n margin-top: 0;\n}\n.matrx-artifact-body > :last-child {\n margin-bottom: 0;\n}\n/* The panel's kind chip already names these languages. */\n.matrx-artifact:is([data-artifact-type=\"html\"], [data-artifact-type=\"react\"], [data-artifact-type=\"svg\"], [data-artifact-type=\"mermaid\"], [data-artifact-type=\"diff\"], [data-artifact-type=\"iframe\"]) .code-lang {\n display: none;\n}\n.matrx-artifact-empty {\n margin: 0;\n font-style: italic;\n color: var(--matrx-print-fg-subtle, #6b7280);\n}\n\nli.matrx-task-item {\n list-style: none;\n}\n/* The box IS the marker — the document skin's custom bullet must not sit on it. */\n:is(.matrx-content-container, .content) li.matrx-task-item::before {\n content: none;\n}\n.matrx-task-box {\n position: relative;\n display: inline-block;\n width: 0.9em;\n height: 0.9em;\n margin: 0 0.5em 0 -1.35em;\n border: 1.5px solid var(--matrx-print-fg-muted, #4a4a4a);\n border-radius: 3px;\n vertical-align: -0.1em;\n}\n.matrx-task-box.is-checked::after {\n content: \"\\\\2713\";\n position: absolute;\n inset: 0;\n font-size: 0.8em;\n font-weight: 700;\n line-height: 1.1em;\n text-align: center;\n color: var(--matrx-print-fg, #1a1a1a);\n}\n\n:is(.matrx-content-container, .content) del {\n color: var(--matrx-print-fg-muted, #4a4a4a);\n text-decoration: line-through;\n}\n:is(.matrx-content-container, .content) hr.matrx-hr-accent {\n height: 2px;\n margin: 1.75em 0;\n border: 0;\n border-radius: 2px;\n background: var(--matrx-print-accent, #2563eb);\n opacity: 0.6;\n}\n:is(.matrx-content-container, .content) hr.matrx-hr-heavy {\n height: 4px;\n margin: 2.25em 0;\n border: 0;\n border-radius: 4px;\n background: var(--matrx-print-accent, #2563eb);\n}\n\n.matrx-flashcards-print {\n margin: 1em 0 1.2em;\n}\n.matrx-fc {\n display: grid;\n grid-template-columns: 1fr 1fr;\n margin-bottom: 0.5em;\n border: 1px solid var(--matrx-print-border, #e5e7eb);\n border-radius: 6px;\n}\n.matrx-fc-front,\n.matrx-fc-back {\n padding: 0.6em 0.8em;\n}\n.matrx-fc-front {\n font-weight: 600;\n border-right: 1px dashed var(--matrx-print-border-muted, #d1d5db);\n}\n.matrx-fc-num {\n display: inline-block;\n min-width: 1.6em;\n font-size: 0.85em;\n color: var(--matrx-print-fg-subtle, #6b7280);\n}\n\n@media print {\n .matrx-page-break {\n margin: 0;\n border: 0;\n break-after: page;\n page-break-after: always;\n }\n .matrx-page-break::after {\n content: none;\n }\n .matrx-page-break + * {\n margin-top: 0;\n }\n\n /* Scoped to the rendered markdown (.matrx-content-container = document\n skin, .content = print window) so a host page that carries this sheet\n — a WordPress theme — keeps its own print rules everywhere else. */\n\n /* Long tables flow across pages: the header row repeats, rows never split. */\n :is(.matrx-content-container, .content) thead {\n display: table-header-group;\n }\n :is(.matrx-content-container, .content) :is(tr, img, figure),\n .matrx-fc,\n .matrx-task-item {\n break-inside: avoid;\n page-break-inside: avoid;\n }\n :is(.matrx-content-container, .content) :is(h1, h2, h3, h4, h5, h6),\n .matrx-artifact-header {\n break-after: avoid;\n page-break-after: avoid;\n break-inside: avoid;\n }\n\n /* Paper cannot scroll: long code lines wrap instead of being cut off. */\n :is(.matrx-content-container, .content) :is(pre, pre code) {\n white-space: pre-wrap !important;\n overflow-wrap: anywhere;\n overflow: visible !important;\n }\n .code-block-wrapper,\n .matrx-artifact {\n overflow: visible;\n }\n :is(.matrx-content-container, .content) img {\n max-width: 100% !important;\n }\n\n /* A PICTURE (a drawn diagram, a captured page) never outgrows one printed\n page: taller than the page, it was pushed whole to the next page and\n left its panel header alone at the foot of the last one, then printed a\n page with nothing but the page number (2026-10-08). Capped to fit beside\n its header on a portrait page, scaled, never cropped; a panel that holds\n a picture moves as one, header with it. */\n :is(.matrx-fence-picture, .matrx-print-image) img {\n display: block;\n width: auto !important;\n max-width: 100% !important;\n height: auto !important;\n max-height: 8.25in;\n object-fit: contain;\n margin: 0 auto;\n }\n :is(.matrx-fence-picture, .matrx-print-image),\n .matrx-artifact:has(.matrx-fence-picture, .matrx-print-image) {\n break-inside: avoid;\n page-break-inside: avoid;\n }\n /* A forced break with nothing after it (or right after another) would\n print an empty page. */\n .matrx-page-break:last-child,\n .matrx-page-break:has(+ .matrx-page-break) {\n break-after: auto;\n page-break-after: auto;\n }\n\n .matrx-artifact-header,\n .matrx-task-box {\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n }\n\n a.matrx-link[href^=\"http\"]::after {\n content: \" (\" attr(href) \")\";\n font-size: 0.85em;\n color: var(--matrx-print-fg-subtle, #6b7280);\n overflow-wrap: anywhere;\n }\n}\n`;\n\n/**\n * Page geometry + page numbers (\"3 / 12\", bottom centre). Part of the default\n * sheet; `getMarkdownStylesheet({ pageNumbers: false })` leaves it out.\n * Browsers without `@page` margin boxes ignore the numbering and keep the\n * margins.\n */\nexport const MARKDOWN_PAGE_CSS = `\n@page {\n margin: 0.6in 0.65in 0.7in;\n @bottom-center {\n content: counter(page) \" / \" counter(pages);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 8pt;\n color: var(--matrx-print-fg-subtle, #6b7280);\n }\n}\n`;\n\n/** `@page` geometry without numbering. */\nexport const MARKDOWN_PAGE_PLAIN_CSS = `\n@page {\n margin: 0.6in 0.65in 0.7in;\n}\n`;\n","/**\n * @ai-matrx/print/directives — the ONE definition of the print directives\n * markdown can carry, shared by every surface that reads markdown.\n *\n * Why a separate, zero-dependency entry: the directive syntax has three\n * readers — this package's two converters (print window / PDF / HTML page),\n * the chat renderer's remark plugin (the on-screen preview), and any editor\n * that inserts one. If each re-derived the regex they would drift the first\n * time someone added an alias. `/markdown` carries ~20KB of stylesheet, so the\n * chat bundle imports the syntax from here instead and pays nothing.\n *\n * ── Page break ────────────────────────────────────────────────────────────\n * A line that is exactly one of these (case-insensitive, surrounding\n * whitespace ignored) forces the next content onto a new printed page:\n *\n * <!-- pagebreak --> ← canonical; invisible in any other renderer\n * <!-- page-break --> <!-- page break --> <!-- newpage -->\n * \\pagebreak \\newpage ← LaTeX / pandoc habit\n * <div style=\"page-break-after: always\"></div> ← Typora / VS Code habit\n * (also `page-break-before`, `break-after: page`, `break-before: page`,\n * and `<div class=\"page-break\"></div>` / `class=\"pagebreak\"`)\n *\n * On screen every surface draws the same idea: a dashed rule labelled\n * \"Page break\" with generous padding. In print it is a zero-height element\n * with `break-after: page`.\n */\n\n/** The form editors insert and docs recommend. */\nexport const PAGE_BREAK_MARKDOWN = \"<!-- pagebreak -->\";\n\n/** The class every surface puts on a rendered page break. */\nexport const PAGE_BREAK_CLASS = \"matrx-page-break\";\n\n/** Screen label for a rendered page break. */\nexport const PAGE_BREAK_LABEL = \"Page break\";\n\nconst COMMENT_FORM = /^<!--\\s*(?:page[\\s_-]?break|new[\\s_-]?page)\\s*-->$/i;\nconst LATEX_FORM = /^\\\\(?:pagebreak|newpage|clearpage)\\s*(?:\\{\\s*\\})?$/i;\nconst DIV_FORM = /^<div\\b([^>]*)>\\s*<\\/div>$|^<div\\b([^>]*)\\/>$/i;\nconst DIV_BREAK_ATTR =\n /(?:page-break-(?:after|before)\\s*:\\s*always|break-(?:after|before)\\s*:\\s*page|class\\s*=\\s*[\"'](?:[^\"']*\\s)?page-?break(?:\\s[^\"']*)?[\"'])/i;\n\n/** Columns of leading indentation (tab = 4, NBSP counts as a space). */\nfunction indentColumns(line: string): number {\n let cols = 0;\n for (const ch of line) {\n if (ch === \" \" || ch === \"\\u00A0\") cols += 1;\n else if (ch === \"\\t\") cols += 4;\n else break;\n }\n return cols;\n}\n\n/**\n * Is this single line a page-break directive? Indented four or more columns\n * it is not — in markdown that is a code block, and every surface must agree.\n */\nexport function isPageBreakLine(line: string): boolean {\n if (indentColumns(line) >= 4) return false;\n const t = line.trim();\n if (t.length < 8 || t.length > 200) return false;\n if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;\n const div = DIV_FORM.exec(t);\n if (!div) return false;\n return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? \"\");\n}\n\n// ── Section break ─────────────────────────────────────────────────────────\n//\n// A section break is a page break that can also change the page layout for\n// everything after it (Word's \"Section break (next page)\"):\n//\n// <!-- section --> ← new page, same layout\n// <!-- section: landscape --> ← new page, landscape from here on\n// <!-- section landscape columns=2 --> ← landscape AND two columns\n// <!-- section portrait columns=1 --> ← back to the default layout\n//\n// Recognised words: `landscape` / `portrait`, `columns=N` (or `cols=N`,\n// `N columns`, 1–3). Unknown words are ignored, never an error — the break\n// still happens. Every page-break reader treats a section break as a break.\n\n/** The form editors insert for a plain section break. */\nexport const SECTION_BREAK_MARKDOWN = \"<!-- section -->\";\n\nexport interface SectionBreakDirective {\n /** Absent = inherit the document's orientation. */\n orientation?: \"portrait\" | \"landscape\";\n /** Absent = inherit the document's column count. 1–3. */\n columns?: 1 | 2 | 3;\n}\n\nconst SECTION_FORM = /^<!--\\s*section(?:[\\s_-]?break)?\\b\\s*:?\\s*([^>]*?)\\s*-->$/i;\n\n/** Parse a section-break line; `null` when the line is not one. */\nexport function parseSectionBreakLine(line: string): SectionBreakDirective | null {\n if (indentColumns(line) >= 4) return null;\n const m = SECTION_FORM.exec(line.trim());\n if (!m) return null;\n const words = (m[1] ?? \"\").toLowerCase();\n const out: SectionBreakDirective = {};\n if (/\\blandscape\\b/.test(words)) out.orientation = \"landscape\";\n else if (/\\bportrait\\b/.test(words)) out.orientation = \"portrait\";\n const cols = /\\b(?:columns?|cols?)\\s*[=:]\\s*(\\d)\\b/.exec(words) ?? /\\b(\\d)\\s*[-\\s]?columns?\\b/.exec(words);\n if (cols) {\n const n = Math.min(3, Math.max(1, Number(cols[1])));\n out.columns = n as 1 | 2 | 3;\n }\n return out;\n}\n\n// ── Table of contents / bibliography placement ───────────────────────────\n//\n// `[[toc]]`, `[toc]`, `[TOC]`, `${toc}`, `<!-- toc -->`, `\\tableofcontents`\n// put the table of contents at that spot (otherwise frontmatter `toc: true`\n// puts it at the start). `<!-- bibliography -->`, `[[bibliography]]`,\n// `\\printbibliography` put the reference list there (otherwise it closes the\n// document whenever something is cited).\n\nexport const TOC_MARKDOWN = \"[[toc]]\";\nexport const BIBLIOGRAPHY_MARKDOWN = \"<!-- bibliography -->\";\n\nconst TOC_FORM = /^(?:\\[\\[\\s*toc\\s*\\]\\]|\\[toc\\]|\\$\\{toc\\}|<!--\\s*toc\\s*-->|\\\\tableofcontents)$/i;\nconst BIBLIOGRAPHY_FORM =\n /^(?:\\[\\[\\s*(?:bibliography|references)\\s*\\]\\]|<!--\\s*(?:bibliography|references)\\s*-->|\\\\printbibliography|\\\\bibliography\\{[^}]*\\})$/i;\n\nexport function isTocLine(line: string): boolean {\n return indentColumns(line) < 4 && TOC_FORM.test(line.trim());\n}\n\nexport function isBibliographyLine(line: string): boolean {\n return indentColumns(line) < 4 && BIBLIOGRAPHY_FORM.test(line.trim());\n}\n\n/** Any directive that ends the current printed page (page or section break). */\nexport function isBreakLine(line: string): boolean {\n return isPageBreakLine(line) || parseSectionBreakLine(line) !== null;\n}\n\n/**\n * Split markdown into the pieces that belong on separate pages. Fence-aware:\n * a directive inside a ``` / ~~~ code block is code, not a break. Always\n * returns at least one piece; empty pieces (two breaks in a row, a break at\n * the very start or end) are kept out so no blank page is produced.\n */\nexport function splitAtPageBreaks(markdown: string): string[] {\n const lines = markdown.split(/\\r?\\n/);\n const pieces: string[] = [];\n let current: string[] = [];\n let fence: string | null = null;\n\n for (const line of lines) {\n // Any indent — the converters accept list-nested fences, so must this.\n const fenceMatch = /^\\s*(`{3,}|~{3,})/.exec(line);\n if (fenceMatch) {\n const marker = fenceMatch[1] as string;\n if (fence === null) fence = marker;\n else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;\n } else if (fence === null && isBreakLine(line)) {\n if (current.join(\"\\n\").trim()) pieces.push(current.join(\"\\n\"));\n current = [];\n continue;\n }\n current.push(line);\n }\n if (current.join(\"\\n\").trim() || pieces.length === 0) pieces.push(current.join(\"\\n\"));\n return pieces;\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4qB9B,IAAM,uBAAuB;AAa7B,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["import_html_escape","import_source","import_html_escape","katex","open","cellAlign"]}
1
+ {"version":3,"sources":["../src/markdown.ts","../src/core.ts","../src/katex-css.generated.ts","../src/math-text.ts","../src/document/math.ts","../src/safe-html.ts","../src/markdown-css.ts","../src/directives.ts"],"sourcesContent":["/**\n * @ai-matrx/print/markdown — the SHIPPED DEFAULT markdown→HTML converter and\n * print stylesheet.\n *\n * Why this entry exists: `markdownToPdfBlob` used to declare its converter and\n * its stylesheet as REQUIRED injected ports (\"bring your own converter +\n * stylesheet\"). That is the exact failure THE ALL-INCLUSIVE LAW names — every\n * host had to build the hard half itself, and both hosts that tried ended up\n * with a different converter, a different stylesheet, and a different set of\n * bugs. The package now ships a complete, working default for both ports; the\n * injection seam survives as the OVERRIDE path, never as a prerequisite.\n *\n * Two skins, one vocabulary:\n * • \"document\" — `markdownToHtml` emits `.matrx-*`-classed HTML (tables,\n * nested lists, images, links, code, FAQ blocks, flashcards) and\n * `MARKDOWN_DOCUMENT_CSS` styles it. This is the PDF/publish/WordPress\n * lane.\n * • \"article\" — `markdownToArticleHtml` emits bare semantic tags and\n * `MARKDOWN_ARTICLE_CSS` gives them the serif AI-response look. This is\n * the print-window lane.\n *\n * Nothing here touches `window`/`document` at import time, and nothing here is\n * heavy — but it IS ~20KB of CSS, so `markdownToPdfBlob` reaches it through a\n * lazy `import(\"./markdown\")` and a consumer that never prints markdown pays\n * nothing.\n *\n * NOTHING FAILS SILENTLY: every case where the converter drops input it cannot\n * represent (a malformed flashcard, an empty document) is reported through the\n * optional `onNotice` sink with a concrete remedy, instead of vanishing.\n */\n\nimport {\n openPendingPrintWindow,\n openPrintWindow,\n printBlockOutputHtml,\n type PrintBlockOutput,\n type PrintOutcome,\n} from \"./core\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport {\n codeSpanText,\n collectLinkDefinitions,\n fenceLineKinds,\n findLinkDefinitionLines,\n findTableEnd,\n isHtmlBlockTagName,\n lineIndent,\n mapInlineCodeSpans,\n resolveReferenceLinks,\n rowCells,\n stripThinking,\n tableStartsAt,\n unescapeCellPipes,\n} from \"@ai-matrx/content-ir/source\";\nimport { KATEX_CSS } from \"./katex-css.generated\";\nimport { typesetTex } from \"./math-text\";\nimport { extractMath, MATH_TOKEN_RE, type MathSpan } from \"./document/math\";\nimport { PRINT_HTML_SCHEMA, sanitizeEngineHtml } from \"./safe-html\";\nimport {\n MARKDOWN_ARTICLE_CSS,\n MARKDOWN_ARTICLE_TOKENS_CSS,\n MARKDOWN_DOCUMENT_CSS,\n MARKDOWN_DOCUMENT_TOKENS_CSS,\n MARKDOWN_PAGE_CSS,\n MARKDOWN_PAGE_PLAIN_CSS,\n MARKDOWN_SHARED_CSS,\n} from \"./markdown-css\";\nimport {\n PAGE_BREAK_CLASS,\n PAGE_BREAK_LABEL,\n PAGE_BREAK_MARKDOWN,\n isBibliographyLine,\n isBreakLine,\n isPageBreakLine,\n isTocLine,\n splitAtPageBreaks,\n} from \"./directives\";\n\nexport {\n MARKDOWN_ARTICLE_CSS,\n MARKDOWN_ARTICLE_TOKENS_CSS,\n MARKDOWN_DOCUMENT_CSS,\n MARKDOWN_DOCUMENT_TOKENS_CSS,\n MARKDOWN_PAGE_CSS,\n MARKDOWN_SHARED_CSS,\n PAGE_BREAK_CLASS,\n PAGE_BREAK_MARKDOWN,\n isPageBreakLine,\n splitAtPageBreaks,\n};\n\n/** Which shipped look to render markdown in. */\nexport type MarkdownSkin = \"document\" | \"article\";\n\n/**\n * A thing the converter could not represent, with the remedy. Announced, never\n * swallowed — a dropped card that says nothing is indistinguishable from a card\n * the author never wrote.\n */\nexport interface MarkdownNotice {\n code: \"flashcard-malformed\" | \"empty-output\" | \"html-stripped\";\n message: string;\n /** What the caller (or the author of the markdown) should do about it. */\n remedy: string;\n}\n\n/** A picture the host drew for a fenced block (a diagram), printed in its place. */\nexport interface FencePicture {\n /** A complete `<svg …>…</svg>` document. */\n svg: string;\n /** What the picture shows, for the printed alt text. */\n alt?: string;\n}\n\nexport interface MarkdownToHtmlOptions {\n /** Keep `<thinking>` / `<think>` / `<reasoning>` blocks. Default: false. */\n includeThinking?: boolean;\n /** Where dropped/degraded content is announced. */\n onNotice?: (notice: MarkdownNotice) => void;\n /**\n * The print form of one embedded block (a fenced block or an `<artifact>`),\n * looked up synchronously while converting — how a host prints a diagram as\n * a picture, a quiz through its printer, an HTML page as its image. Return\n * null to print the block's source. Hosts with async work (drawing, a page\n * capture) pass an async `renderBlock` to `printMarkdownWhenReady`, which\n * resolves every block first and hands this the answers.\n */\n renderBlock?: (block: PrintBlock) => PrintBlockRender | null | undefined;\n}\n\n/** One embedded block of a message, as the print composer finds it. */\nexport interface PrintBlock {\n /** \"fence\": a fenced block (type = its language); \"artifact\": an `<artifact type>` wrapper. */\n readonly source: \"fence\" | \"artifact\";\n /** Lower-cased language / artifact type — the block-printer registry key. */\n readonly type: string;\n /** The block's source text. */\n readonly body: string;\n readonly title?: string | undefined;\n /** An `<artifact>`'s attributes (`id`, `version`, …) — how a host finds the block's record. */\n readonly attributes?: Readonly<Record<string, string>> | undefined;\n}\n\n/** What prints in a block's place: a drawing, or any `PrintBlockOutput`. */\nexport type PrintBlockRender = FencePicture | PrintBlockOutput;\n\n/** The identity of a block within one markdown string (stable across conversions). */\nexport function printBlockKey(block: PrintBlock): string {\n return `${block.source}\\u0000${block.type}\\u0000${block.body}`;\n}\n\nfunction printBlockRenderHtml(render: PrintBlockRender): string {\n return \"svg\" in render ? fencePictureHtml(render) : printBlockOutputHtml(render);\n}\n\n/**\n * Every embedded block of `markdown`, in order, exactly as the converter sees\n * it (it runs the converter once and records what it is asked about).\n */\nexport function listPrintBlocks(markdown: string, skin: MarkdownSkin = \"article\"): PrintBlock[] {\n const seen = new Map<string, PrintBlock>();\n markdownToHtmlForSkin(markdown, skin, {\n renderBlock: (block) => {\n seen.set(printBlockKey(block), block);\n return null;\n },\n });\n return [...seen.values()];\n}\n\nfunction fencePictureHtml(picture: FencePicture): string {\n const bytes = new TextEncoder().encode(picture.svg);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i] as number);\n const src = `data:image/svg+xml;base64,${btoa(binary)}`;\n return `<figure class=\"matrx-fence-picture\"><img src=\"${src}\" alt=\"${escapeHtml(picture.alt ?? \"Diagram\")}\"></figure>`;\n}\n\n// ─── thinking-block stripping ────────────────────────────────────────────────\n\n/**\n * Strip `<thinking>` / `<think>` / `<reasoning>` blocks from model output.\n *\n * All three tags are treated as equivalent — a host that handled only\n * `<thinking>` leaked raw reasoning into printed documents the moment a model\n * switched tag names, which is exactly the bug this package exists to stop\n * being re-solved per app.\n */\nexport function removeThinkingContent(content: string): string {\n // One home for the rule (content-ir/source): paragraph structure around a hidden block survives\n // (verify-RC-B5 round 3 F6) and a tag inside code is text, never a block.\n return stripThinking(content);\n}\n\n/** Does this content carry any reasoning block? */\nexport function hasThinkingContent(content: string): boolean {\n if (!content) return false;\n return /<thinking>|<think>|<reasoning>/i.test(content);\n}\n\n// ─── shared machinery: ONE pre-pass, ONE table/list/rule grammar ────────────\n//\n// Both skins used to carry their own table parser, their own list builder,\n// their own rule regex, and their own (or no) code-fence handling — and they\n// had drifted: the article skin had no nested lists, the document skin ran the\n// emphasis pass over code, neither understood a page break, and both pasted an\n// `<artifact>`'s raw HTML (its <html>, <body> and <script>) straight into the\n// host page. Everything below is shared; a skin supplies only class names and\n// the HTML for a code block / flashcard deck.\n\n// Private-use delimiters: they cannot collide with author text (the input is\n// scrubbed of them first) and no markdown pass treats them as syntax.\nconst TOKEN_OPEN = \"\\uE000\";\nconst TOKEN_CLOSE = \"\\uE001\";\nconst BLOCK_TOKEN = (i: number): string => `${TOKEN_OPEN}B${i}${TOKEN_CLOSE}`;\nconst INLINE_TOKEN = (i: number): string => `${TOKEN_OPEN}I${i}${TOKEN_CLOSE}`;\nconst TOKEN_RE = /\\uE000([BI])(\\d+)\\uE001/g;\nconst PARAGRAPH_WRAPPED_BLOCK_RE = /<p[^>]*>\\s*(\\uE000B\\d+\\uE001)\\s*<\\/p>/g;\n\n/** Converted HTML parked behind a placeholder so later passes cannot touch it. */\nclass Stash {\n private readonly items: string[] = [];\n\n block(html: string): string {\n return BLOCK_TOKEN(this.items.push(html) - 1);\n }\n\n inline(html: string): string {\n return INLINE_TOKEN(this.items.push(html) - 1);\n }\n\n restore(html: string): string {\n // Items can hold tokens of their own (inline code inside a link), so\n // restore until nothing is left — bounded, since items never self-nest.\n let out = html;\n for (let pass = 0; pass < 8 && out.includes(TOKEN_OPEN); pass++) {\n out = out\n .replace(PARAGRAPH_WRAPPED_BLOCK_RE, \"$1\")\n .replace(TOKEN_RE, (_m: string, _k: string, i: string) => this.items[Number(i)] ?? \"\");\n }\n // Raw-HTML footnote masking is normally restored before inline rendering,\n // but indented code is stashed earlier in that pipeline. Restore once more\n // after every nested stash expansion so code samples keep their exact bytes.\n return restoreMaskedFootnotes(out);\n }\n}\n\ninterface SkinGrammar {\n skin: MarkdownSkin;\n table: string;\n thead: string;\n tbody: string;\n tr: string;\n th: string;\n td: string;\n ul: string;\n ol: string;\n ulNested: string;\n olNested: string;\n li: string;\n hr: string;\n del: string;\n strong: string;\n em: string;\n inlineCode: string;\n blockquote: string;\n}\n\nconst DOCUMENT_GRAMMAR: SkinGrammar = {\n skin: \"document\",\n table: \"matrx-table\",\n thead: \"matrx-table-head\",\n tbody: \"matrx-table-body\",\n tr: \"matrx-table-row\",\n th: \"matrx-table-header\",\n td: \"matrx-table-cell\",\n ul: \"matrx-list matrx-bullet-list\",\n ol: \"matrx-list matrx-numbered-list\",\n ulNested: \"matrx-list matrx-bullet-list matrx-nested-list\",\n olNested: \"matrx-list matrx-numbered-list matrx-nested-list\",\n li: \"matrx-list-item\",\n hr: \"matrx-hr\",\n del: \"matrx-del\",\n strong: \"matrx-strong\",\n em: \"matrx-em\",\n inlineCode: \"matrx-inline-code\",\n blockquote: \"matrx-blockquote\",\n};\n\nconst ARTICLE_GRAMMAR: SkinGrammar = {\n skin: \"article\",\n table: \"print-table\",\n thead: \"\",\n tbody: \"\",\n tr: \"\",\n th: \"\",\n td: \"\",\n ul: \"\",\n ol: \"\",\n ulNested: \"\",\n olNested: \"\",\n li: \"\",\n hr: \"\",\n del: \"\",\n strong: \"\",\n em: \"\",\n inlineCode: \"inline-code\",\n blockquote: \"\",\n};\n\nconst cls = (...names: string[]): string => {\n const joined = names.filter(Boolean).join(\" \");\n return joined ? ` class=\"${joined}\"` : \"\";\n};\n\n/** The rendered page break — identical markup in both skins. */\nconst PAGE_BREAK_HTML = `<div class=\"${PAGE_BREAK_CLASS}\" role=\"separator\" aria-label=\"${PAGE_BREAK_LABEL}\" data-label=\"${PAGE_BREAK_LABEL}\"></div>`;\n\nfunction codeBlockHtml(g: SkinGrammar, code: string, language: string): string {\n const body = escapeHtml(code.replace(/\\n$/, \"\"));\n const lang = language.trim();\n if (g.skin === \"article\") {\n const label = lang ? `<span class=\"code-lang\">${escapeHtml(lang)}</span>` : \"\";\n return `<div class=\"code-block-wrapper\">${label}<pre class=\"code-block\"><code>${body}</code></pre></div>`;\n }\n const dataLang = lang ? ` data-language=\"${escapeHtml(lang)}\"` : \"\";\n return `<pre class=\"matrx-code-block\"${dataLang}><code class=\"matrx-code\">${body}</code></pre>`;\n}\n\n// ── artifacts ───────────────────────────────────────────────────────────────\n\n/** Artifact types whose body is source code, printed as its source. */\nconst CODE_ARTIFACT_LANGUAGE: Readonly<Record<string, string>> = {\n html: \"html\",\n react: \"tsx\",\n svg: \"svg\",\n mermaid: \"mermaid\",\n diff: \"diff\",\n iframe: \"html\",\n code: \"\",\n};\n\nconst ARTIFACT_TYPE_LABEL: Readonly<Record<string, string>> = {\n html: \"HTML\",\n svg: \"SVG\",\n react: \"React\",\n iframe: \"Embed\",\n};\n\nfunction parseTagAttributes(tag: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n for (const m of tag.matchAll(/([a-zA-Z_][\\w-]*)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g)) {\n attrs[(m[1] as string).toLowerCase()] = m[2] ?? m[3] ?? \"\";\n }\n return attrs;\n}\n\nfunction artifactLabel(type: string): string {\n if (!type) return \"Artifact\";\n return (\n ARTIFACT_TYPE_LABEL[type] ??\n type.replace(/[-_]+/g, \" \").replace(/\\b\\w/g, (c) => c.toUpperCase())\n );\n}\n\n/**\n * `<artifact …>body</artifact>` envelopes as plain markdown — the SAME rules the\n * skins use for their titled panels, for pipelines that parse markdown (the\n * document writer): source-code types and JSON bodies become fenced code;\n * every other body (tables, checklists, notes) is markdown and is kept as is.\n * Fenced code is never touched.\n */\nexport function unwrapArtifactsToMarkdown(markdown: string): string {\n if (!/<artifact\\b/i.test(markdown)) return markdown;\n return markdown\n .split(/(^[ \\t]*(?:`{3,}|~{3,})[\\s\\S]*?^[ \\t]*(?:`{3,}|~{3,})[ \\t]*$)/m)\n .map((part, i) =>\n i % 2 === 1\n ? part\n : part.replace(/<artifact\\b((?:[^>\"']|\"[^\"]*\"|'[^']*')*?)>([\\s\\S]*?)<\\/artifact>/gi, (_m, tag: string, body: string) => {\n const attrs = parseTagAttributes(tag);\n const type = (attrs.type ?? \"\").toLowerCase();\n const trimmed = body.replace(/^\\n+|\\n+$/g, \"\");\n if (!trimmed.trim()) return \"\";\n if (/^\\s*(`{3,}|~{3,})/.test(trimmed)) return `\\n\\n${trimmed}\\n\\n`;\n if (type in CODE_ARTIFACT_LANGUAGE) {\n const lang = CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || \"\";\n return `\\n\\n\\`\\`\\`\\`${lang}\\n${trimmed}\\n\\`\\`\\`\\`\\n\\n`;\n }\n if (looksLikeJson(trimmed)) return `\\n\\n\\`\\`\\`\\`json\\n${JSON.stringify(JSON.parse(trimmed), null, 2)}\\n\\`\\`\\`\\`\\n\\n`;\n return `\\n\\n${trimmed}\\n\\n`;\n }),\n )\n .join(\"\");\n}\n\nfunction looksLikeJson(body: string): boolean {\n const t = body.trim();\n if (!/^[[{]/.test(t)) return false;\n try {\n JSON.parse(t);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * An `<artifact type id version title>body</artifact>` block, rendered as a\n * titled panel. Source-code artifacts (html, react, svg, mermaid, diff, code)\n * and JSON bodies print as escaped source — never as live markup, which is\n * what used to inject a whole `<html>…<script>` document into the host page.\n * Everything else (checklists, flashcards, research, notes) is markdown and is\n * converted in the same skin.\n */\nfunction artifactHtml(\n g: SkinGrammar,\n attrs: Record<string, string>,\n body: string | null,\n ctx: ConvertContext,\n): string {\n const type = (attrs.type ?? \"\").toLowerCase();\n const title = attrs.title?.trim() ?? \"\";\n const header = `<div class=\"matrx-artifact-header\"><span class=\"matrx-artifact-kind\">${escapeHtml(\n artifactLabel(type),\n )}</span>${title ? `<span class=\"matrx-artifact-title\">${escapeHtml(title)}</span>` : \"\"}</div>`;\n\n let inner: string;\n const trimmed = (body ?? \"\").replace(/^\\n+|\\n+$/g, \"\");\n const rendered =\n body !== null && trimmed.trim()\n ? ctx.options.renderBlock?.({ source: \"artifact\", type, body: trimmed, attributes: attrs, ...(title ? { title } : {}) })\n : null;\n if (rendered) {\n inner = printBlockRenderHtml(rendered);\n } else if (body === null || !trimmed.trim()) {\n inner = `<p class=\"matrx-artifact-empty\">Saved artifact — open it in AI Matrx to view its content.</p>`;\n } else if (/^\\s*(`{3,}|~{3,})/.test(trimmed)) {\n inner = convertBody(g.skin, trimmed, ctx);\n } else if (type in CODE_ARTIFACT_LANGUAGE) {\n inner = codeBlockHtml(g, trimmed, CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || \"\");\n } else if (looksLikeJson(trimmed)) {\n inner = codeBlockHtml(g, JSON.stringify(JSON.parse(trimmed), null, 2), \"json\");\n } else {\n inner = convertBody(g.skin, trimmed, ctx);\n }\n\n return `<section class=\"matrx-artifact\" data-artifact-type=\"${escapeHtml(type || \"artifact\")}\">${header}<div class=\"matrx-artifact-body\">${inner}</div></section>`;\n}\n\n// ── flashcards ──────────────────────────────────────────────────────────────\n\ninterface ParsedCard {\n front: string;\n back: string;\n}\n\nfunction parseFlashcards(content: string, notice: MarkdownToHtmlOptions[\"onNotice\"]): ParsedCard[] {\n const cards: ParsedCard[] = [];\n for (const card of content.split(/\\n*---\\n*/g).map((c) => c.trim()).filter(Boolean)) {\n const front = card.match(/Front:\\s*(.+?)(?=\\n|$)/i)?.[1]?.trim();\n const back = card.match(/Back:\\s*([\\s\\S]+?)$/i)?.[1]?.trim();\n if (!front || !back) {\n // A card that silently disappears from a printed deck is a defect the\n // printer can see and the author cannot.\n notice?.({\n code: \"flashcard-malformed\",\n message: `A flashcard was skipped: it is missing a ${front ? \"Back:\" : \"Front:\"} line.`,\n remedy:\n 'Every card inside <flashcards> needs both \"Front: …\" and \"Back: …\", separated from the next card by ---.',\n });\n continue;\n }\n cards.push({ front, back });\n }\n return cards;\n}\n\nfunction flashcardsHtml(g: SkinGrammar, cards: ParsedCard[], ctx: ConvertContext): string {\n if (g.skin === \"article\") {\n // Paper cannot flip: print the deck as question / answer pairs.\n return `<div class=\"matrx-flashcards-print\">${cards\n .map(\n (c, i) =>\n `<div class=\"matrx-fc\"><div class=\"matrx-fc-front\"><span class=\"matrx-fc-num\">${\n i + 1\n }</span>${c.front}</div><div class=\"matrx-fc-back\">${c.back}</div></div>`,\n )\n .join(\"\")}</div>`;\n }\n let out = '<div class=\"matrx-flashcards-container\">\\n';\n for (const { front, back } of cards) {\n ctx.cardIndex++;\n const cardId = `card${ctx.cardIndex}`;\n out += ` <div class=\"matrx-flashcard\">\n <input type=\"checkbox\" class=\"matrx-flashcard-input\" id=\"${cardId}\">\n <label for=\"${cardId}\" class=\"matrx-flashcard-label\"></label>\n <div class=\"matrx-flashcard-inner\">\n <div class=\"matrx-flashcard-front\">\n <div class=\"matrx-flashcard-content\">\n <strong>${front}</strong>\n </div>\n <span class=\"matrx-flashcard-indicator\">CLICK TO FLIP</span>\n </div>\n <div class=\"matrx-flashcard-back\">\n <div class=\"matrx-flashcard-content\">\n ${back}\n </div>\n <span class=\"matrx-flashcard-indicator\">CLICK TO FLIP</span>\n </div>\n </div>\n </div>\n\n`;\n }\n return `${out}</div>\\n`;\n}\n\n// ── the pre-pass ────────────────────────────────────────────────────────────\n\ninterface ConvertContext {\n options: MarkdownToHtmlOptions;\n depth: number;\n cardIndex: number;\n strippedAnnounced: boolean;\n}\n\nconst FENCE_OPEN = /^(\\s*)(`{3,}|~{3,})\\s*([^\\s`]*)[^`]*$/;\n\n/**\n * Pull every region the markdown passes must not touch out of the text:\n * fenced code, `<artifact>` blocks, page-break directives, `<flashcards>`,\n * inline code — and remove what must never reach a printed page (scripts,\n * styles, a document shell, HTML comments). Returns the text with\n * placeholders; `stash.restore` puts the finished HTML back.\n */\nfunction protectRegions(markdown: string, g: SkinGrammar, stash: Stash, ctx: ConvertContext): string {\n // Math FIRST — what is math is decided by the shared tokenizer\n // (@ai-matrx/content-ir/source, via /document's extractMath): code is never\n // math, a `$5` price is never math, and a formula's `_`, `*`, `\\,` and `|`\n // never reach the markdown passes below. Each span prints typeset\n // (typesetMathHtml), in prose and in table cells alike.\n const { text: withoutMath, spans } = extractMath(markdown);\n const prepared = withoutMath.replace(MATH_TOKEN_RE, (_m: string, index: string) => {\n const span = spans[Number(index)];\n if (!span) return \"\";\n return span.display ? stash.block(typesetMathHtml(span)) : stash.inline(typesetMathHtml(span));\n });\n // Reference-style links resolve against the document's definitions, in prose\n // and in table cells alike, and a definition line prints nothing — as GFM\n // reads them (THE link-reference rule, @ai-matrx/content-ir/source;\n // verify-RC-B4 round 9, R9-4). Fenced code is left alone.\n // The footnote parser must not read raw HTML, but it must keep prose before\n // or after that HTML on the same line. Mask only the footnote introducer (\"[^\") in\n // opaque regions and restore it before the normal HTML pass below.\n const kinds = fenceLineKinds(prepared);\n const footnoteSafe = maskFootnotesInRawHtml(prepared, kinds);\n const defs = collectLinkDefinitions(footnoteSafe);\n const definitionLines = new Set(findLinkDefinitionLines(footnoteSafe));\n const lines = footnoteSafe.split(\"\\n\").map((raw, k) => {\n const line = raw.replace(/\\r$/, \"\");\n if (definitionLines.has(k)) return \"\";\n if (kinds[k] !== \"prose\" || defs.size === 0) return line;\n return resolveReferenceLinks(line, defs);\n });\n // Footnotes, as GFM and the screen show them: numbered references (order of\n // first reference, prose and cells alike) and one footnote section at the end\n // (verify-RC-B4 round 9).\n const footnoteSection = extractFootnotes(lines, kinds, g, stash);\n const out: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] as string;\n\n // Fenced code (``` or ~~~, any indent, unclosed = to the end).\n const fence = FENCE_OPEN.exec(line);\n if (fence) {\n const marker = fence[2] as string;\n const code: string[] = [];\n let j = i + 1;\n for (; j < lines.length; j++) {\n const close = /^\\s*(`{3,}|~{3,})\\s*$/.exec(lines[j] as string);\n if (close && (close[1] as string)[0] === marker[0] && (close[1] as string).length >= marker.length) break;\n code.push(lines[j] as string);\n }\n const indent = (fence[1] as string).length;\n const dedented = code.map((l) => l.slice(Math.min(indent, l.length - l.trimStart().length)));\n const rendered = ctx.options.renderBlock?.({\n source: \"fence\",\n type: (fence[3] ?? \"\").trim().toLowerCase(),\n body: dedented.join(\"\\n\"),\n });\n out.push(\n stash.block(\n rendered ? printBlockRenderHtml(rendered) : codeBlockHtml(g, dedented.join(\"\\n\"), fence[3] ?? \"\"),\n ),\n );\n i = j;\n continue;\n }\n\n if (isBreakLine(line)) {\n out.push(stash.block(PAGE_BREAK_HTML));\n continue;\n }\n // Placement directives only mean something to the document pipeline\n // (@ai-matrx/print/document); a skin never prints them as literal text.\n if (isTocLine(line) || isBibliographyLine(line)) continue;\n\n // <artifact …>body</artifact> — the canonical materialized wire form.\n const open = /^\\s*<artifact\\b((?:[^>\"']|\"[^\"]*\"|'[^']*')*?)(\\/?)>(.*)$/i.exec(line);\n if (open) {\n const attrs = parseTagAttributes(open[1] ?? \"\");\n if (open[2] === \"/\") {\n out.push(stash.block(artifactHtml(g, attrs, null, ctx)));\n if (open[3]?.trim()) out.push(open[3]);\n continue;\n }\n const bodyLines: string[] = [];\n let rest = open[3] ?? \"\";\n let after = \"\";\n let j = i;\n let inFence: string | null = null;\n for (;;) {\n const closeAt = inFence ? -1 : rest.search(/<\\/artifact\\s*>/i);\n if (closeAt >= 0) {\n bodyLines.push(rest.slice(0, closeAt));\n after = rest.slice(closeAt).replace(/^<\\/artifact\\s*>/i, \"\");\n break;\n }\n bodyLines.push(rest);\n const f = /^\\s*(`{3,}|~{3,})/.exec(rest);\n if (f) {\n const m = f[1] as string;\n if (inFence === null) inFence = m;\n else if (m[0] === inFence[0] && m.length >= inFence.length) inFence = null;\n }\n j++;\n if (j >= lines.length) break;\n rest = lines[j] as string;\n }\n const html =\n ctx.depth >= 3\n ? codeBlockHtml(g, bodyLines.join(\"\\n\"), \"\")\n : artifactHtml(g, attrs, bodyLines.join(\"\\n\"), { ...ctx, depth: ctx.depth + 1 });\n out.push(stash.block(html));\n if (after.trim()) out.push(after);\n i = j;\n continue;\n }\n\n // An indented code block (CommonMark 4.4): a run of lines indented 4+ columns\n // after a blank line (or at the start), outside any list — ` | a | b |`\n // there is code, never a table (verify-RC-B4 round 9, R9-6).\n if (lineIndent(line) >= 4 && line.trim() && startsIndentedCode(lines, i)) {\n let j = i;\n while (j < lines.length && (!(lines[j] ?? \"\").trim() || lineIndent(lines[j] ?? \"\") >= 4)) j += 1;\n while (j > i && !(lines[j - 1] ?? \"\").trim()) j -= 1;\n const code = lines.slice(i, j).map((l) => l.replace(/^(?: {4}|\\t| {1,3}\\t)/, \"\"));\n out.push(stash.block(codeBlockHtml(g, code.join(\"\\n\"), \"\")));\n i = j - 1;\n continue;\n }\n\n // A GFM table inside a block quote (`> | a | b |`): the quote's markers are\n // stripped and THE rule reads the lines inside, so it prints as GFM draws it —\n // a table inside the quote, as many levels deep as it sits (verify-RC-B4\n // round 9). The quote's other lines keep their own pass.\n const quoted = quotedTableAt(lines, i);\n if (quoted) {\n const open = `<blockquote${cls(g.blockquote)}>`.repeat(quoted.depth);\n const close = \"</blockquote>\".repeat(quoted.depth);\n out.push(stash.block(`${open}${tableHtml(quoted.rows, g, stash)}${close}`));\n i = quoted.end - 1;\n continue;\n }\n\n // A GFM table — found and split BEFORE any inline pass (THE rule,\n // @ai-matrx/content-ir/source), so code spans, escapes and emphasis are read\n // per cell exactly as GFM reads a cell, and a line that starts another block\n // (`> 8C | …`, `- x`) ends the table and is left for its own pass.\n if (tableStartsAt(lines, i)) {\n const end = findTableEnd(lines, i);\n out.push(stash.block(tableHtml(lines.slice(i, end), g, stash)));\n i = end - 1;\n continue;\n }\n\n out.push(line);\n }\n if (footnoteSection) out.push(\"\", stash.block(footnoteSection));\n\n let text = restoreMaskedFootnotes(out.join(\"\\n\"));\n\n // Inline code FIRST: prose about HTML (\"put it in a `<script>` tag\") must\n // reach the page as code, not be read as markup by the passes below.\n text = text.replace(/`([^`\\n]+)`/g, (_m: string, code: string) =>\n stash.inline(`<code${cls(g.inlineCode)}>${escapeHtml(code)}</code>`),\n );\n // A block tag written INSIDE a line of prose, a quote or a list item\n // (`Handles native <table> with thead…`) is inline text — dropped as the\n // screen drops it, never a raw empty table or a stray div on the page\n // (verify-RC-B4 round 9, R9-3; a line that STARTS with the tag is an HTML block).\n text = text\n .split(\"\\n\")\n .map((line) =>\n // A line that opens with a tag is an HTML block: every tag on it stays.\n line.replace(LINE_LEAD_RE, \"\").startsWith(\"<\")\n ? line\n : line.replace(INLINE_TAG_RE, (whole: string, name: string) => (isInlineBlockTag(name) ? \"\" : whole)),\n )\n .join(\"\\n\");\n // Backslash escapes (CommonMark): `\\*` is a literal `*`, `\\\\` a literal `\\` —\n // never syntax for the passes below, never a printed backslash.\n text = resolveEscapes(text, stash);\n\n // Comments are invisible by definition — page-break comments were already\n // taken above, so what remains is safe to drop without a notice.\n text = text.replace(/<!--[\\s\\S]*?-->/g, \"\");\n\n // A printed page and a published HTML page are not a place to run someone\n // else's script or restyle the host. Paired regions go whole; a stray\n // opening/closing tag goes alone (never \"everything to the end\"). Repeat\n // until stable so a split `<scr<script></script>ipt>` cannot reassemble.\n const before = text;\n for (let pass = 0; pass < 8; pass++) {\n const next = text\n .replace(/<(script|style|head)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi, \"\")\n .replace(/<\\/?(?:script|style|head|html|body)\\b[^>]*>/gi, \"\")\n .replace(/<!DOCTYPE[^>]*>/gi, \"\");\n if (next === text) break;\n text = next;\n }\n text = sanitizeRawTags(text);\n // The allow-list (`./safe-html`) is what actually guards the output; this\n // only decides whether the author is told something of theirs was dropped.\n if ((text !== before || hasDisallowedTag(text)) && !ctx.strippedAnnounced) {\n ctx.strippedAnnounced = true;\n ctx.options.onNotice?.({\n code: \"html-stripped\",\n message:\n \"Markup that is not on the safe list (a <script>, <style>, <iframe>, <form>, <object>/<embed>, <meta>, <link>, an on… event handler, inline style, or a javascript: URL) was removed from the rendered copy.\",\n remedy:\n 'To show a full HTML page, put it in an ```html fence or an <artifact type=\"html\"> block — it is printed as its source.',\n });\n }\n\n // Flashcard decks — behind placeholders so no markdown pass re-processes them.\n text = text.replace(/<flashcards>([\\s\\S]*?)<\\/flashcards>/gi, (_m: string, content: string) => {\n const cards = parseFlashcards(content, ctx.options.onNotice);\n return cards.length ? `\\n${stash.block(flashcardsHtml(g, cards, ctx))}\\n` : \"\";\n });\n\n return text;\n}\n\nconst ALLOWED_TAGS = new Set(PRINT_HTML_SCHEMA.tagNames ?? []);\n\n/** A raw tag the allow-list will drop (our own `<flashcards>` island excepted). */\nfunction hasDisallowedTag(text: string): boolean {\n for (const m of text.matchAll(/<\\/?([a-zA-Z][\\w:-]*)/g)) {\n const tag = (m[1] as string).toLowerCase();\n if (tag !== \"flashcards\" && !ALLOWED_TAGS.has(tag)) return true;\n }\n return false;\n}\n\nconst URL_ATTRS = new Set([\"href\", \"src\", \"action\", \"formaction\", \"xlink:href\", \"poster\", \"background\"]);\n\nfunction decodeForSchemeCheck(value: string): string {\n return value\n .replace(/&#x([0-9a-f]+);?/gi, (_m: string, h: string) => String.fromCharCode(parseInt(h, 16)))\n .replace(/&#(\\d+);?/g, (_m: string, d: string) => String.fromCharCode(Number(d)))\n .replace(/&colon;/gi, \":\")\n .replace(/[\\u0000-\\u0020]+/g, \"\")\n .toLowerCase();\n}\n\n/**\n * Author raw HTML survives (the document skin feeds WordPress), but never with\n * an `on…` handler, a `srcdoc`, or a script-scheme URL — entity- and\n * whitespace-obfuscated forms included.\n */\nfunction sanitizeRawTags(text: string): string {\n return text.replace(\n /<([a-zA-Z][\\w:-]*)((?:[^>\"']|\"[^\"]*\"|'[^']*')*)>/g,\n (whole: string, tag: string, rawAttrs: string) => {\n if (!/[=\\s]/.test(rawAttrs)) return whole;\n let changed = false;\n const attrs = rawAttrs.replace(\n /(\\s+)([^\\s\"'=<>/]+)(\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/g,\n (m: string, _sp: string, name: string, _eq: string, dq?: string, sq?: string, bare?: string) => {\n const lower = name.toLowerCase();\n if (lower.startsWith(\"on\") || lower === \"srcdoc\") {\n changed = true;\n return \"\";\n }\n if (URL_ATTRS.has(lower)) {\n const value = decodeForSchemeCheck(dq ?? sq ?? bare ?? \"\");\n if (/^(?:javascript|vbscript):|^data:text\\/html/.test(value)) {\n changed = true;\n return ` ${name}=\"#\"`;\n }\n }\n return m;\n },\n );\n return changed ? `<${tag}${attrs}>` : whole;\n },\n );\n}\n\n/** A URL that can go in an attribute: no script schemes, no attribute breakout. */\nfunction safeUrl(url: string): string {\n const u = url.trim();\n if (/^(?:javascript|vbscript):|^data:text\\/html/.test(decodeForSchemeCheck(u))) return \"#\";\n return u.replace(/\"/g, \"%22\");\n}\n\n/**\n * Bold / italic / strikethrough, shared by both skins. Underscores follow the\n * CommonMark intraword rule — `snake_case_name` stays literal.\n */\nfunction convertEmphasis(html: string, g: SkinGrammar): string {\n const strong = `<strong${cls(g.strong)}>`;\n const em = `<em${cls(g.em)}>`;\n return html\n .replace(/\\*\\*\\*(?!\\s)(.+?)(?<!\\s)\\*\\*\\*/g, `${strong}${em}$1</em></strong>`)\n .replace(/\\*\\*(?!\\s)(.+?)(?<!\\s)\\*\\*/g, `${strong}$1</strong>`)\n .replace(/(?<![\\w*])\\*(?![\\s*])(.+?)(?<![\\s*])\\*(?![\\w*])/g, `${em}$1</em>`)\n .replace(/(?<![\\w_])___(?!\\s)(.+?)(?<!\\s)___(?![\\w_])/g, `${strong}${em}$1</em></strong>`)\n .replace(/(?<![\\w_])__(?!\\s)(.+?)(?<!\\s)__(?![\\w_])/g, `${strong}$1</strong>`)\n .replace(/(?<![\\w_])_(?![\\s_])(.+?)(?<![\\s_])_(?![\\w_])/g, `${em}$1</em>`)\n .replace(/~~(?!\\s)(.+?)(?<!\\s)~~/g, `<del${cls(g.del)}>$1</del>`);\n}\n\n/** `[text](url)` / `![alt](url \"title\")` with one level of parentheses in the URL. */\nconst LINK_URL = String.raw`((?:[^()\\s]|\\([^()\\s]*\\))+)(?:\\s+\"[^\"]*\")?`;\nconst IMAGE_RE = new RegExp(String.raw`!\\[([^\\]]*)\\]\\(` + LINK_URL + String.raw`\\)`, \"g\");\nconst LINK_RE = new RegExp(String.raw`\\[([^\\]]+)\\]\\(` + LINK_URL + String.raw`\\)`, \"g\");\n\n/** `***` accent rule, `#===` heavy rule, `---` / `___` plain rule. */\nfunction convertRules(html: string, g: SkinGrammar): string {\n return html\n .replace(/^\\s{0,3}#\\s*={3,}\\s*$/gm, `<hr${cls(g.hr, \"matrx-hr-heavy\")}>`)\n .replace(/^\\s{0,3}\\*(?:\\s*\\*){2,}\\s*$/gm, `<hr${cls(g.hr, \"matrx-hr-accent\")}>`)\n .replace(/^\\s{0,3}(?:-\\s*){3,}$/gm, `<hr${cls(g.hr)}>`)\n .replace(/^\\s{0,3}(?:_\\s*){3,}$/gm, `<hr${cls(g.hr)}>`);\n}\n\nfunction cellAlign(separator: string): string {\n const s = separator.trim();\n const left = s.startsWith(\":\");\n const right = s.endsWith(\":\");\n if (left && right) return ' style=\"text-align:center\"';\n if (right) return ' style=\"text-align:right\"';\n return \"\";\n}\n\n/**\n * One math span typeset the way the screen typesets it — KaTeX, the renderer\n * every chat and Studio surface uses — as KaTeX's laid-out HTML with MathML\n * for accessibility, mhchem loaded for chemistry. The KaTeX stylesheet (fonts\n * pinned to the installed version) ships in every skin's stylesheet. TeX KaTeX cannot parse\n * prints as its source in the error colour, exactly as on screen.\n */\n/**\n * The print allow-list for authored HTML; typeset math is restored after it.\n */\nfunction sanitizePrintHtmlWithMath(html: string): string {\n // Typeset math rides through the sanitizer as an opaque token and is put\n // back after it: the allow-list strips every class and inline style, and\n // KaTeX's layout IS classes plus computed heights. KaTeX runs with\n // `trust: false`, which refuses every command that could inject HTML\n // (\\href, \\url, \\htmlClass, \\htmlId, \\htmlStyle, \\htmlData,\n // \\includegraphics), so its output is safe by construction.\n const math = typesetMath;\n typesetMath = [];\n return sanitizeEngineHtml(html).replace(MATH_TOKEN_OUT_RE, (_m: string, i: string) => math[Number(i)] ?? \"\");\n}\n\n/** KaTeX output held out of the sanitizer for the conversion in flight. */\nlet typesetMath: string[] = [];\nconst MATH_TOKEN_OUT_RE = /\\uE017(\\d+)\\uE018/g;\n\nfunction typesetMathHtml(span: MathSpan): string {\n // The ONE KaTeX configuration (math-text.ts), held out of the sanitizer.\n return `\\uE017${typesetMath.push(typesetTex(span.tex, span.display)) - 1}\\uE018`;\n}\n\n/** A CommonMark backslash escape: `\\` before ASCII punctuation. */\nconst ESCAPE_RE = /\\\\([!-\\/:-@[-`{-~])/g;\n\nfunction resolveEscapes(text: string, stash: Stash): string {\n return text.replace(ESCAPE_RE, (_m: string, ch: string) => stash.inline(escapeHtml(ch)));\n}\n\n/**\n * One cell's inline content, read the way GFM reads a cell: the table-level\n * `\\|` → `|` first (inside code spans too, spec example 200), then code spans,\n * backslash escapes, images, links and emphasis — never block syntax.\n */\nconst INLINE_TAG_RE = /<\\/?([A-Za-z][\\w-]*)\\b(?:[^<>\"']|\"[^\"]*\"|'[^']*')*>/g;\n/** A line's lead-in before its text: indentation, quote markers, a list marker. */\nconst LINE_LEAD_RE = /^\\s*(?:>\\s*)*(?:(?:[-+*]|\\d{1,9}[.)])\\s+)?/;\n/** A tag that opens a block (or a table part) — never meaningful inside a line of text. */\nconst isInlineBlockTag = (name: string): boolean =>\n isHtmlBlockTagName(name) || /^(?:thead|tbody|tfoot|tr|td|th|caption|colgroup|col|li|dt|dd)$/i.test(name);\n\nfunction cellInlineHtml(cell: string, g: SkinGrammar, stash: Stash): string {\n // Code spans by THE one code-range rule (@ai-matrx/content-ir/source), read as\n // INLINE content: a cell opening with ```` is a code span, never a fence.\n let t = mapInlineCodeSpans(unescapeCellPipes(cell), (_range, raw) =>\n stash.inline(`<code${cls(g.inlineCode)}>${escapeHtml(codeSpanText(raw))}</code>`),\n );\n // A cell is INLINE content: a block tag written in it (`native <table> exports`)\n // is dropped, as the screen drops it — it never opens a nested table that\n // swallows the next row (verify-RC-B4 round 9, R9-3).\n t = t.replace(INLINE_TAG_RE, (whole: string, name: string) => (isInlineBlockTag(name) ? \"\" : whole));\n t = resolveEscapes(t, stash);\n t = t.replace(IMAGE_RE, (_m: string, alt: string, url: string) =>\n stash.inline(`<img class=\"${g.skin === \"document\" ? \"matrx-image\" : \"print-image\"}\" src=\"${safeUrl(url)}\" alt=\"${escapeHtml(alt)}\" />`),\n );\n t = t.replace(LINK_RE, (_m: string, text: string, url: string) =>\n stash.inline(`<a${g.skin === \"document\" ? ' class=\"matrx-link\"' : \"\"} href=\"${safeUrl(url)}\">${convertEmphasis(text, g)}</a>`),\n );\n return convertEmphasis(t, g);\n}\n\n/**\n * A GFM table by THE rule (@ai-matrx/content-ir/source — the rule chat, the\n * editor and the server read tables by): edge pipes optional, `\\|` stays in its\n * cell, every row printed at the header's width (empty cells kept — columns\n * never shift), `:---:` alignment honoured, each cell read as inline content.\n */\n/**\n * Line `i` (indented 4+) opens an indented code block: it follows a blank line or\n * starts the text, and the nearest line above the blank is no list line (a list\n * item's indented content belongs to the item).\n */\nfunction startsIndentedCode(lines: readonly string[], i: number): boolean {\n if (i > 0 && (lines[i - 1] ?? \"\").trim()) return false;\n let k = i - 1;\n while (k >= 0 && !(lines[k] ?? \"\").trim()) k -= 1;\n if (k < 0) return true;\n const above = lines[k] ?? \"\";\n return lineIndent(above) === 0 && !/^\\s*(?:[-+*]|\\d{1,9}[.)])\\s/.test(above);\n}\n\nconst FOOTNOTE_DEF = /^ {0,3}\\[\\^([^\\]\\s]+)\\]:[ \\t]?(.*)$/;\nconst FOOTNOTE_REF = /\\[\\^([^\\]\\s]+)\\](?!:)/g;\nconst BLOCK_START_LINE = /^ {0,3}(?:[-+*]\\s|\\d{1,9}[.)]\\s|>|#{1,6}\\s|\\||```|~~~|\\[\\^[^\\]\\s]+\\]:)/;\nconst THEMATIC_BREAK_LINE = /^ {0,3}(?:(?:-[ \\t]*){3,}|(?:\\*[ \\t]*){3,}|(?:_[ \\t]*){3,})$/;\n/**\n * A line that ends a note's paragraph instead of continuing it lazily: a block\n * start, a thematic break (`-----` under a note is a rule, not its text), or an\n * HTML block that may interrupt a paragraph (CommonMark 4.6 conditions 1–6; a\n * lone complete tag, condition 7, may not).\n */\nfunction interruptsNoteParagraph(line: string): boolean {\n if (BLOCK_START_LINE.test(line) || THEMATIC_BREAK_LINE.test(line)) return true;\n const t = line.trim();\n if (/^<(?:!--|\\?|![A-Za-z]|!\\[CDATA\\[)/.test(t)) return true;\n const tag = /^<(\\/?)([A-Za-z][A-Za-z0-9-]*)(?:[\\s>]|\\/>|$)/.exec(t);\n if (!tag || !isHtmlBlockTagName(tag[2] as string)) return false;\n return !(tag[1] && /^(?:script|pre|style|textarea)$/i.test(tag[2] as string));\n}\nconst FOOTNOTE_PROTECTED_TOKEN = /\\uE013(\\d+)\\uE013/g;\nconst FOOTNOTE_STASH_TOKEN = /\\uE000I\\d+\\uE001/g;\n\n/** The matching `]` or `)`, respecting nested delimiters and CommonMark escapes. */\nfunction closingInlineDelimiter(text: string, start: number, open: string, close: string): number {\n let depth = 1;\n for (let i = start + 1; i < text.length; i += 1) {\n if (text[i] === \"\\\\\") {\n i += 1;\n continue;\n }\n if (text[i] === open) depth += 1;\n else if (text[i] === close && --depth === 0) return i;\n }\n return -1;\n}\n\n/**\n * Footnotes are valid in a link's visible label, but never in its destination\n * or an image's alt text. Unlike IMAGE_RE/LINK_RE this follows balanced\n * brackets, so `[label[^n]](url[^n])` does not leak a footnote into `href`.\n */\nfunction protectFootnoteLinkContexts(text: string, protect: (raw: string) => string): string {\n let out = \"\";\n for (let i = 0; i < text.length; ) {\n const image = text.startsWith(\"![\", i);\n const link = image || text[i] === \"[\";\n if (!link) {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const labelStart = i + (image ? 1 : 0);\n const labelEnd = closingInlineDelimiter(text, labelStart, \"[\", \"]\");\n if (labelEnd < 0 || text[labelEnd + 1] !== \"(\") {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const destinationEnd = closingInlineDelimiter(text, labelEnd + 1, \"(\", \")\");\n if (destinationEnd < 0) {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n if (image) out += protect(text.slice(i, destinationEnd + 1));\n else out += `${text.slice(i, labelEnd + 2)}${protect(text.slice(labelEnd + 2, destinationEnd))})`;\n i = destinationEnd + 1;\n }\n return out;\n}\n\n/**\n * A footnote reference is itself an anchor, so it cannot remain inside the\n * anchor made for a Markdown link label. Split that label around footnote\n * tokens: its ordinary text stays linked while the reference remains a valid,\n * clickable sibling. This mirrors GFM's non-nested-anchor HTML shape.\n */\nfunction detachFootnotesFromLinkLabels(text: string, g: SkinGrammar, stash: Stash): string {\n let out = \"\";\n for (let i = 0; i < text.length; ) {\n if (text[i] !== \"[\" || text[i - 1] === \"!\") {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const labelEnd = closingInlineDelimiter(text, i, \"[\", \"]\");\n if (labelEnd < 0 || text[labelEnd + 1] !== \"(\") {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const destinationEnd = closingInlineDelimiter(text, labelEnd + 1, \"(\", \")\");\n if (destinationEnd < 0) {\n out += text[i] ?? \"\";\n i += 1;\n continue;\n }\n const label = text.slice(i + 1, labelEnd);\n if (!FOOTNOTE_STASH_TOKEN.test(label)) {\n FOOTNOTE_STASH_TOKEN.lastIndex = 0;\n out += text.slice(i, destinationEnd + 1);\n i = destinationEnd + 1;\n continue;\n }\n FOOTNOTE_STASH_TOKEN.lastIndex = 0;\n // Match the regular inline-link grammar: a quoted title belongs to the\n // destination syntax, not to the URL placed in href.\n const destination = text.slice(labelEnd + 2, destinationEnd);\n const href = safeUrl(destination.match(new RegExp(`^${LINK_URL}$`))?.[1] ?? destination);\n for (const part of label.split(/(\\uE000I\\d+\\uE001)/g)) {\n if (!part) continue;\n out += FOOTNOTE_STASH_TOKEN.test(part)\n ? part\n : stash.inline(`<a${g.skin === \"document\" ? ' class=\"matrx-link\"' : \"\"} href=\"${href}\">${convertEmphasis(escapeHtml(part), g)}</a>`);\n FOOTNOTE_STASH_TOKEN.lastIndex = 0;\n }\n i = destinationEnd + 1;\n }\n return out;\n}\n\n/**\n * The footnote pass only reads Markdown prose. Fenced code is already\n * classified by content-ir; raw comments, stripped raw-HTML blocks, and a\n * multi-line raw tag are opaque too. In particular, an attribute split over\n * lines must not turn `[^note]` into a visible reference before raw HTML is\n * sanitized later in the rendering pipeline.\n */\nconst FOOTNOTE_RAW_MASK = \"\\uE014\";\nconst RAW_TAG_REGION = /<\\/?[A-Za-z][\\w-]*\\b(?:[^>\"']|\"[^\"]*\"|'[^']*')*>/g;\n\n/**\n * Raw HTML itself is opaque to footnote syntax, including multi-line tag\n * attributes. This masks only the footnote introducer in those regions, rather\n * than their whole lines, so surrounding Markdown prose is still parsed.\n */\nfunction maskFootnotesInRawHtml(markdown: string, kinds: readonly string[]): string {\n const mask = (raw: string): string => raw.replaceAll(\"[^\", FOOTNOTE_RAW_MASK);\n const maskProseRun = (run: string): string => {\n // Raw-tag-like text inside an inline code span is code, never HTML. Park\n // it while finding raw regions so an unclosed `<script>` code sample does\n // not make the following prose opaque.\n const codeSpans: string[] = [];\n const withoutCode = mapInlineCodeSpans(run, (_range, raw) => `\\uE015${codeSpans.push(raw) - 1}\\uE016`);\n return withoutCode\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, mask)\n .replace(/<(script|style|head)\\b[^>]*>[\\s\\S]*?(?:<\\/\\1\\s*>|$)/gi, mask)\n .replace(RAW_TAG_REGION, mask)\n .replace(/\\uE015(\\d+)\\uE016/g, (_m: string, index: string) => codeSpans[Number(index)] ?? \"\");\n };\n const lines = markdown.split(\"\\n\");\n for (let start = 0; start < lines.length; ) {\n if (kinds[start] !== \"prose\") {\n start += 1;\n continue;\n }\n let end = start + 1;\n while (end < lines.length && kinds[end] === \"prose\") end += 1;\n const masked = maskProseRun(lines.slice(start, end).join(\"\\n\")).split(\"\\n\");\n lines.splice(start, end - start, ...masked);\n start = end;\n }\n return lines.join(\"\\n\");\n}\n\nfunction restoreMaskedFootnotes(text: string): string {\n return text.replaceAll(FOOTNOTE_RAW_MASK, \"[^\");\n}\n\n/**\n * GFM footnotes for the print skins. Every definition (`[^label]: text`, with\n * its indented or lazy continuation lines) is taken out of `lines`; every\n * reference to a defined label becomes a superscript number, numbered in order\n * of first reference; a reference to an undefined label stays literal text (as\n * GFM leaves it). Returns the footnote section (referenced definitions in\n * number order; an unreferenced definition is dropped, as GFM drops it), or \"\".\n * Fenced code is never touched; inline code spans keep their text.\n */\nfunction extractFootnotes(lines: string[], kinds: readonly string[], g: SkinGrammar, stash: Stash): string {\n if (!lines.some((line) => line.includes(\"[^\"))) return \"\";\n const bodies = new Map<string, string[][]>(); // label → paragraphs → lines\n for (let i = 0; i < lines.length; i += 1) {\n if (kinds[i] !== \"prose\") continue;\n const m = FOOTNOTE_DEF.exec(lines[i] ?? \"\");\n if (!m) continue;\n const label = (m[1] as string).toLowerCase();\n const paragraphs: string[][] = [[m[2] ?? \"\"]];\n // The previous line as WRITTEN: lines consumed into the note are blanked as\n // we go, so reading lines[j - 1] would see \"\" and refuse every lazy line.\n let previous = lines[i] ?? \"\";\n lines[i] = \"\";\n let j = i + 1;\n for (; j < lines.length && kinds[j] === \"prose\"; j += 1) {\n const line = lines[j] ?? \"\";\n if (/^(?: {4}|\\t)/.test(line)) {\n paragraphs[paragraphs.length - 1]!.push(line.replace(/^(?: {4}|\\t)/, \"\"));\n } else if (line.trim() && previous.trim() && !interruptsNoteParagraph(line)) {\n paragraphs[paragraphs.length - 1]!.push(line); // lazy continuation\n } else if (!line.trim() && /^(?: {4}|\\t)\\S/.test(lines[j + 1] ?? \"\")) {\n paragraphs.push([]); // a blank line, then an indented paragraph of the same note\n } else {\n break;\n }\n previous = line;\n lines[j] = \"\";\n }\n if (!bodies.has(label)) bodies.set(label, paragraphs.filter((p) => p.length));\n i = j - 1;\n }\n if (bodies.size === 0) return \"\";\n const numbers = new Map<string, number>();\n const seen = new Map<string, number>();\n // A reference to a defined label becomes its number, numbered on FIRST reference;\n // inline code spans keep their text.\n const numberRefs = (line: string): string => {\n if (!line.includes(\"[^\")) return line;\n const spans: string[] = [];\n const masked = mapInlineCodeSpans(line, (_range, raw) => `\\uE012${spans.push(raw) - 1}\\uE012`);\n // Footnote syntax is prose syntax. Keep code spans, image alt text, link\n // destinations, and raw-tag attributes opaque until their own passes read\n // them; only a link's visible label remains prose. This also means an\n // escaped `\\\\[^note]` remains literal through resolveEscapes below.\n const protectedRegions: string[] = [];\n const protect = (raw: string): string => `\\uE013${protectedRegions.push(raw) - 1}\\uE013`;\n const contextual = masked\n .replace(INLINE_TAG_RE, (whole: string) => protect(whole));\n const linkSafe = protectFootnoteLinkContexts(contextual, protect);\n const replaced = linkSafe.replace(FOOTNOTE_REF, (whole: string, raw: string, offset: number, source: string) => {\n let slashes = 0;\n for (let i = offset - 1; i >= 0 && source[i] === \"\\\\\"; i -= 1) slashes += 1;\n if (slashes % 2 === 1) return whole;\n const label = raw.toLowerCase();\n if (!bodies.has(label)) return whole;\n if (!numbers.has(label)) numbers.set(label, numbers.size + 1);\n const n = numbers.get(label) as number;\n const k = (seen.get(label) ?? 0) + 1; // GFM's ids: fnref-1, then fnref-1-2, …\n seen.set(label, k);\n const id = k === 1 ? `fnref-${n}` : `fnref-${n}-${k}`;\n return stash.inline(`<sup class=\"print-footnote-ref\"><a href=\"#fn-${n}\" id=\"${id}\">${n}</a></sup>`);\n });\n const restored = replaced\n .replace(FOOTNOTE_PROTECTED_TOKEN, (_m: string, k: string) => protectedRegions[Number(k)] ?? \"\")\n .replace(/\\uE012(\\d+)\\uE012/g, (_m: string, k: string) => spans[Number(k)] ?? \"\");\n return detachFootnotesFromLinkLabels(restored, g, stash);\n };\n for (let i = 0; i < lines.length; i += 1) if (kinds[i] === \"prose\") lines[i] = numberRefs(lines[i] as string);\n const items: string[] = [];\n const note = (n: number, label: string) => {\n const paragraphs = (bodies.get(label) ?? []).map((p) => `<p>${cellInlineHtml(numberRefs(p.join(\" \")), g, stash)}</p>`).join(\"\");\n items.push(`<li id=\"fn-${n}\" value=\"${n}\">${paragraphs}</li>`);\n };\n // Only referenced notes print, in number order: GFM (and so the screen) drops\n // a definition nothing references, and it never prints as text either. A\n // reference inside a note numbers a new note on first sight, and the loop\n // reaches it (a Map iterates entries added while iterating), as GFM does.\n for (const [label, n] of numbers) note(n, label);\n if (items.length === 0) return \"\";\n return `<section class=\"print-footnotes\"><hr /><ol>${items.join(\"\")}</ol></section>`;\n}\n\nconst QUOTE_MARKERS = /^(?: {0,3}> ?)+/;\n/** How many `>` quote markers open the line (0 for none). */\nconst quoteDepth = (line: string): number => (QUOTE_MARKERS.exec(line)?.[0].match(/>/g) ?? []).length;\n\n/**\n * A GFM table opening at line `i` INSIDE a block quote: the run of lines at the\n * same quote depth, markers stripped, read by THE table rule. Returns the table's\n * rows (quote markers gone), its depth and the index just past it; null when line\n * `i` opens no quoted table.\n */\nfunction quotedTableAt(lines: readonly string[], i: number): { rows: string[]; depth: number; end: number } | null {\n const depth = quoteDepth(lines[i] ?? \"\");\n if (depth === 0) return null;\n let j = i;\n const inner: string[] = [];\n while (j < lines.length && quoteDepth(lines[j] ?? \"\") === depth) {\n inner.push((lines[j] ?? \"\").replace(QUOTE_MARKERS, \"\"));\n j += 1;\n }\n // The quote's own lines above the header stay in the run: a table interrupts a\n // quoted paragraph as it does a top-level one, and a lazy line opens none.\n let start = i;\n while (start > 0 && quoteDepth(lines[start - 1] ?? \"\") === depth) start -= 1;\n const context = lines.slice(start, i).map((line) => line.replace(QUOTE_MARKERS, \"\"));\n const all = [...context, ...inner];\n const at = context.length;\n if (!tableStartsAt(all, at)) return null;\n const end = findTableEnd(all, at);\n return { rows: all.slice(at, end), depth, end: i + (end - at) };\n}\n\nfunction tableHtml(lines: readonly string[], g: SkinGrammar, stash: Stash): string {\n const cellsOf = (line: string) => rowCells(line).map((cell) => cellInlineHtml(cell, g, stash));\n const headers = cellsOf(lines[0] ?? \"\");\n const aligns = rowCells(lines[1] ?? \"\").map(cellAlign);\n const rows = lines.slice(2).map((line) => {\n const cells = cellsOf(line);\n return headers.map((_h, k) => cells[k] ?? \"\");\n });\n let t = `<table${cls(g.table)}>\\n<thead${cls(g.thead)}>\\n<tr${cls(g.tr)}>\\n`;\n headers.forEach((h, k) => {\n t += `<th${cls(g.th)}${aligns[k] ?? \"\"}>${h}</th>\\n`;\n });\n t += \"</tr>\\n</thead>\\n\";\n if (rows.length > 0) {\n t += `<tbody${cls(g.tbody)}>\\n`;\n for (const row of rows) {\n t += `<tr${cls(g.tr)}>\\n`;\n row.forEach((c, k) => {\n t += `<td${cls(g.td)}${aligns[k] ?? \"\"}>${c}</td>\\n`;\n });\n t += \"</tr>\\n\";\n }\n t += \"</tbody>\\n\";\n }\n return `${t}</table>`;\n}\n\ninterface OpenList {\n type: \"ul\" | \"ol\";\n indent: number;\n}\n\n/** Nested ordered/bullet lists — indentation-driven open/close bookkeeping. */\n/** A line that is already a block of its own at the list pass — never an item's continuation text. */\nconst LIST_BLOCK_LINE_RE = /^(?:<\\/?(?:h[1-6]|ul|ol|li|blockquote|pre|hr|table|thead|tbody|tr|th|td|div|figure|section)\\b|\\uE000B\\d+\\uE001$)/;\n\nfunction convertLists(html: string, g: SkinGrammar): string {\n const lines = html.split(\"\\n\");\n let processed = \"\";\n const listStack: OpenList[] = [];\n let lastListItem: \"\" | \"ul\" | \"ol\" = \"\";\n const li = `<li${cls(g.li)}>`;\n\n const top = (): OpenList | undefined => listStack[listStack.length - 1];\n const openTag = (type: \"ul\" | \"ol\", nested: boolean): string =>\n type === \"ol\"\n ? `<ol${cls(nested ? g.olNested : g.ol)}>\\n`\n : `<ul${cls(nested ? g.ulNested : g.ul)}>\\n`;\n\n const popOne = (): void => {\n if (lastListItem) {\n processed += \"</li>\\n\";\n lastListItem = \"\";\n }\n const closing = listStack.pop();\n if (!closing) return;\n processed += closing.type === \"ol\" ? \"</ol>\\n\" : \"</ul>\\n\";\n if (listStack.length > 0) processed += \"</li>\\n\";\n };\n\n const closeDeeperThan = (indent: number): void => {\n while (listStack.length > 0 && (top() as OpenList).indent > indent) popOne();\n };\n\n // The open item's content column and whether a blank line has passed since\n // its text — what decides where a following line belongs (CommonMark 5.2).\n let contentCol = 0;\n let sawBlank = false;\n const indentOf = (line: string): number => line.length - line.trimStart().length;\n\n for (const line of lines) {\n const numbered = line.match(/^(\\s*)(\\d+)[.)]\\s+(.+)$/);\n const bullet = numbered ? null : line.match(/^(\\s*)[*\\-+]\\s+(.+)$/);\n if (numbered || bullet) {\n const type: \"ul\" | \"ol\" = numbered ? \"ol\" : \"ul\";\n const indent = ((numbered ?? bullet) as RegExpMatchArray)[1]?.length ?? 0;\n const content = (numbered ? numbered[3] : (bullet as RegExpMatchArray)[2]) ?? \"\";\n const current = top();\n\n if (current && indent > current.indent) {\n processed += `\\n${openTag(type, true)}`;\n listStack.push({ type, indent });\n } else {\n closeDeeperThan(indent);\n if (lastListItem && top()?.indent === indent) processed += \"</li>\\n\";\n const now = top();\n if (now && now.indent === indent && now.type !== type) {\n // Same depth, other list type: close the old list, open the new one.\n const closing = listStack.pop() as OpenList;\n processed += closing.type === \"ol\" ? \"</ol>\\n\" : \"</ul>\\n\";\n }\n const after = top();\n if (!after || after.indent !== indent || after.type !== type) {\n processed += openTag(type, listStack.length > 0);\n listStack.push({ type, indent });\n }\n }\n processed += `${li}${content}`;\n lastListItem = type;\n contentCol = line.length - content.length;\n sawBlank = false;\n continue;\n }\n\n const text = line.trim();\n if (text === \"\") {\n sawBlank = true;\n processed += `${line}\\n`;\n continue;\n }\n if (lastListItem && !LIST_BLOCK_LINE_RE.test(text)) {\n // A line right under the item's text — lazy or indented — continues that\n // item's paragraph (even a `| a | b |` line, which GFM never reads as a\n // table there); after a blank line, a line indented to the item's content\n // column is a new paragraph INSIDE the item.\n if (!sawBlank) {\n processed += ` ${text}`;\n continue;\n }\n if (indentOf(line) >= contentCol) {\n processed += `${text}\\n`;\n continue;\n }\n }\n while (listStack.length > 0) popOne();\n processed += `${line}\\n`;\n }\n while (listStack.length > 0) popOne();\n\n // GFM task items: `- [ ] todo` / `- [x] done` print as real boxes.\n return processed.replace(\n /<li( class=\"([^\"]*)\")?>\\s*\\[([ xX])\\]\\s+/g,\n (_m: string, _c: string | undefined, classes: string | undefined, mark: string) =>\n `<li${cls(classes ?? \"\", \"matrx-task-item\")}><span class=\"matrx-task-box${\n mark === \" \" ? \"\" : \" is-checked\"\n }\" aria-hidden=\"true\"></span>`,\n );\n}\n\nfunction convertBody(skin: MarkdownSkin, markdown: string, ctx: ConvertContext): string {\n return skin === \"article\" ? articleBody(markdown, ctx) : documentBody(markdown, ctx);\n}\n\n/** Author text can never forge a placeholder. */\nfunction scrubTokens(markdown: string): string {\n // Every private-use marker in this range is internal parser state. Remove\n // authored lookalikes before any pass can mistake one for a real placeholder.\n return markdown.replace(/[\\uE000-\\uE018]/g, \"\");\n}\n\nfunction newContext(options: MarkdownToHtmlOptions): ConvertContext {\n return { options, depth: 0, cardIndex: 0, strippedAnnounced: false };\n}\n\n// ─── document skin: markdown → `.matrx-*` HTML ───────────────────────────────\n\n/**\n * markdown → `.matrx-*`-classed HTML wrapped in `.matrx-content-container`.\n *\n * The class vocabulary is deliberate and non-configurable: it is what\n * `MARKDOWN_DOCUMENT_CSS` styles, what the PDF lane renders, and what a\n * WordPress theme targets. Handles (in this order, because the order is the\n * whole trick): the shared pre-pass (code, artifacts, page breaks, flashcards,\n * inline code, unsafe page markup) → rules → images → links → headings →\n * emphasis → nested/task lists → blockquotes → tables → block elements →\n * paragraphs → FAQ promotion → placeholder restoration.\n */\nexport function markdownToHtml(\n markdown: string,\n options: MarkdownToHtmlOptions = {},\n): string {\n if (!markdown) {\n options.onNotice?.({\n code: \"empty-output\",\n message: \"markdownToHtml received an empty string.\",\n remedy: \"Check the content is loaded before converting; nothing was rendered.\",\n });\n return \"\";\n }\n const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));\n typesetMath = [];\n return sanitizePrintHtmlWithMath(\n `<div class=\"matrx-content-container\">${documentBody(source, newContext(options))}</div>`,\n );\n}\n\nfunction documentBody(markdown: string, ctx: ConvertContext): string {\n const g = DOCUMENT_GRAMMAR;\n const stash = new Stash();\n let html = protectRegions(markdown, g, stash, ctx);\n\n // Rules before headings/lists so `---` / `#===` / `***` are never eaten.\n html = convertRules(html, g);\n\n // Images BEFORE links — `![a](u)` is a superset of `[a](u)`.\n html = html.replace(IMAGE_RE, (_m: string, alt: string, url: string) =>\n stash.block(`<img class=\"matrx-image\" src=\"${safeUrl(url)}\" alt=\"${escapeHtml(alt)}\" />`),\n );\n html = html.replace(LINK_RE, (_m: string, text: string, url: string) =>\n stash.inline(`<a class=\"matrx-link\" href=\"${safeUrl(url)}\">${convertEmphasis(text, g)}</a>`),\n );\n\n html = html\n .replace(/^# (.+)$/gm, '<h1 class=\"matrx-h1\">$1</h1>')\n .replace(/^## (.+)$/gm, '<h2 class=\"matrx-h2\">$1</h2>')\n .replace(/^### (.+)$/gm, '<h3 class=\"matrx-h3\">$1</h3>')\n .replace(/^#### (.+)$/gm, '<h4 class=\"matrx-h4\">$1</h4>')\n .replace(/^##### (.+)$/gm, '<h5 class=\"matrx-h5\">$1</h5>')\n .replace(/^###### (.+)$/gm, '<h6 class=\"matrx-h6\">$1</h6>');\n\n // Lists BEFORE emphasis, so a bullet opening with bold (`* **A** and *b*`)\n // is still a bullet.\n html = convertLists(html, g);\n html = convertEmphasis(html, g);\n\n html = html.replace(/^> ?(.+)$/gm, '<blockquote class=\"matrx-blockquote\">$1</blockquote>');\n\n\n html = html\n .replace(/<table>/g, '<table class=\"matrx-table\">')\n .replace(/<thead>/g, '<thead class=\"matrx-table-head\">')\n .replace(/<tbody>/g, '<tbody class=\"matrx-table-body\">')\n .replace(/<tr>/g, '<tr class=\"matrx-table-row\">')\n .replace(/<th>/g, '<th class=\"matrx-table-header\">')\n .replace(/<td>/g, '<td class=\"matrx-table-cell\">');\n\n html = html.replace(/<img(?![^>]*class=)([^>]*)>/g, '<img class=\"matrx-image\"$1>');\n // Author divs join the vocabulary without losing their own class\n // (the old rewrite emitted two class attributes and the browser kept one).\n html = html.replace(/<div\\b([^>]*)>/g, (m: string, attrs: string) => {\n if (/\\bclass=\"matrx-/.test(attrs)) return m;\n if (/\\bclass=\"/.test(attrs)) return `<div${attrs.replace(/\\bclass=\"/, 'class=\"matrx-div ')}>`;\n return `<div class=\"matrx-div\"${attrs}>`;\n });\n html = html.replace(/<span(?![^>]*class=)([^>]*)>/g, '<span class=\"matrx-span\"$1>');\n\n html = html.replace(/^([^<\\n].+)$/gm, '<p class=\"matrx-paragraph\">$1</p>');\n html = html.replace(/<\\/p>\\s*<p class=\"matrx-paragraph\">/g, '</p><p class=\"matrx-paragraph\">');\n\n // Intro paragraph (first <p> after the h1) — before FAQ promotion.\n html = html.replace(\n /(<h1 class=\"matrx-h1\">[\\s\\S]*?<\\/h1>\\s*)<p class=\"matrx-paragraph\">((?:(?!\\uE000B)[\\s\\S])*?)<\\/p>/g,\n '$1<p class=\"matrx-intro\">$2</p>',\n );\n\n // FAQ promotion: bold questions and question headings become FAQ items.\n html = html.replace(\n /^<strong class=\"matrx-strong\">([^<]*\\?[^<]*)<\\/strong>$/gm,\n '<h3 class=\"matrx-h3\">$1</h3>',\n );\n html = html.replace(\n /<p class=\"matrx-paragraph\"><strong class=\"matrx-strong\">([^<]*\\?[^<]*)<\\/strong><\\/p>/g,\n '<h3 class=\"matrx-h3\">$1</h3>',\n );\n html = html.replace(\n /<h3 class=\"matrx-h3\">([^<]*\\?[^<]*)<\\/h3>/g,\n '<div class=\"matrx-faq-question\">$1</div>',\n );\n html = html.replace(\n /(<div class=\"matrx-faq-question\">[^<]*<\\/div>)\\s*(<p class=\"matrx-paragraph\">[\\s\\S]*?<\\/p>)/g,\n '<div class=\"matrx-faq-item\">$1<div class=\"matrx-faq-answer\">$2</div></div>',\n );\n html = html.replace(\n /(<div class=\"matrx-faq-question\">[^<]*<\\/div>)(?!\\s*<div class=\"matrx-faq-answer\">)/g,\n '<div class=\"matrx-faq-item\">$1<div class=\"matrx-faq-answer\"></div></div>',\n );\n html = html.replace(\n /(<div class=\"matrx-faq-answer\">)<p class=\"matrx-paragraph\">([\\s\\S]*?)<\\/p>(<\\/div>)/g,\n \"$1$2$3\",\n );\n\n return stash.restore(html);\n}\n\n// ─── article skin: markdown → bare semantic HTML ─────────────────────────────\n\n/**\n * markdown → bare semantic HTML (`h1`…`h6`, `p`, `ul`/`ol`, `blockquote`,\n * `.code-block-wrapper`, `.print-table`) for the article skin.\n *\n * Code is HTML-escaped in both skins (the shared pre-pass); a print window\n * never re-escapes, so unescaped code would render as markup.\n */\nexport function markdownToArticleHtml(\n markdown: string,\n options: MarkdownToHtmlOptions = {},\n): string {\n if (!markdown) {\n options.onNotice?.({\n code: \"empty-output\",\n message: \"markdownToArticleHtml received an empty string.\",\n remedy: \"Check the content is loaded before printing; nothing was rendered.\",\n });\n return \"\";\n }\n const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));\n typesetMath = [];\n return sanitizePrintHtmlWithMath(articleBody(source, newContext(options)));\n}\n\nfunction articleBody(markdown: string, ctx: ConvertContext): string {\n const g = ARTICLE_GRAMMAR;\n const stash = new Stash();\n let html = protectRegions(markdown, g, stash, ctx);\n html = convertRules(html, g);\n\n // Images BEFORE links — `![a](u)` is a superset of `[a](u)`. Without this\n // pass an image fell through to the link pass and printed as a literal\n // \"!\" followed by a link (chat \"Print\", 2026-09-21).\n html = html.replace(IMAGE_RE, (_m: string, alt: string, url: string) =>\n stash.block(\n `<figure class=\"print-figure\"><img class=\"print-image\" src=\"${safeUrl(url)}\" alt=\"${escapeHtml(alt)}\" />${\n alt ? `<figcaption>${escapeHtml(alt)}</figcaption>` : \"\"\n }</figure>`,\n ),\n );\n html = html.replace(LINK_RE, (_m: string, text: string, url: string) =>\n stash.inline(`<a href=\"${safeUrl(url)}\">${convertEmphasis(text, g)}</a>`),\n );\n\n html = html\n .replace(/^#{6}\\s+(.+)$/gm, \"<h6>$1</h6>\")\n .replace(/^#{5}\\s+(.+)$/gm, \"<h5>$1</h5>\")\n .replace(/^#{4}\\s+(.+)$/gm, \"<h4>$1</h4>\")\n .replace(/^#{3}\\s+(.+)$/gm, \"<h3>$1</h3>\")\n .replace(/^#{2}\\s+(.+)$/gm, \"<h2>$1</h2>\")\n .replace(/^#{1}\\s+(.+)$/gm, \"<h1>$1</h1>\");\n\n html = html.replace(/^> ?(.+)$/gm, \"<blockquote>$1</blockquote>\");\n html = convertLists(html, g);\n html = convertEmphasis(html, g);\n\n html = html\n .split(\"\\n\")\n .map((line) => {\n const t = line.trim();\n if (!t) return \"\";\n if (\n /^<\\/?(h[1-6]|ul|ol|li|blockquote|pre|hr|table|thead|tbody|tr|th|td|div|figure|section)/.test(t) ||\n /^\\uE000B\\d+\\uE001$/.test(t)\n ) {\n return line;\n }\n return `<p>${t}</p>`;\n })\n .join(\"\\n\");\n\n return stash.restore(html).replace(/\\n{3,}/g, \"\\n\\n\").trim();\n}\n\n// ─── stylesheet + document composition ───────────────────────────────────────\n\n/**\n * The Print / Close action bar. The article sheet styles it already; the\n * document sheet is a content stylesheet with no chrome, so this ships beside\n * it — otherwise every host printing document-skin markdown gets two unstyled\n * browser buttons at the top of the page.\n */\nexport const PRINT_ACTIONS_CSS = `\n.print-actions { display: flex; gap: 10px; margin: 0 auto 28px; max-width: 800px; padding: 0 2rem 20px; border-bottom: 2px solid var(--matrx-print-border, #e1e1e1); }\n.print-btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 20px; border-radius: 8px; font-size: 13px; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; cursor: pointer; border: none; font-weight: 600; }\n.print-btn-primary { background: var(--matrx-print-accent, #667eea); color: var(--matrx-print-bg, #ffffff); }\n.print-btn-secondary { background: var(--matrx-print-surface, #f8f9fa); color: var(--matrx-print-fg-strong, #374151); border: 1px solid var(--matrx-print-border-muted, #d1d5db); }\n@media print { .print-actions { display: none !important; } }\n`;\n\nexport interface MarkdownStylesheetOptions {\n /** Which shipped look. Default: `\"document\"`. */\n skin?: MarkdownSkin;\n /**\n * Host brand overrides — a CSS string appended AFTER the package's default\n * token values, so `:root { --matrx-print-accent: #0f766e; }` wins. Token\n * VALUES are host-owned; the token CONTRACT and the structural CSS are not.\n */\n tokens?: string;\n /** Extra rules appended after everything else. */\n extraCss?: string;\n /**\n * Number printed pages (\"3 / 12\", bottom centre) via `@page` margin boxes.\n * Default: true. Page margins are set either way.\n */\n pageNumbers?: boolean;\n}\n\n/**\n * The complete default stylesheet for a skin: default token values, the\n * structural sheet, the rules both skins share (page breaks, artifacts, task\n * boxes, print hard parts), page geometry, then the host's overrides. Never returns an empty string.\n */\nexport function getMarkdownStylesheet(\n options: MarkdownStylesheetOptions = {},\n): string {\n const skin: MarkdownSkin = options.skin ?? \"document\";\n const base =\n skin === \"article\"\n ? `${MARKDOWN_ARTICLE_TOKENS_CSS}\\n${MARKDOWN_ARTICLE_CSS}`\n : `${MARKDOWN_DOCUMENT_TOKENS_CSS}\\n${MARKDOWN_DOCUMENT_CSS}`;\n const page = options.pageNumbers === false ? MARKDOWN_PAGE_PLAIN_CSS : MARKDOWN_PAGE_CSS;\n return [base, MARKDOWN_SHARED_CSS, KATEX_CSS, page, options.tokens ?? \"\", options.extraCss ?? \"\"]\n .filter((part) => part.trim().length > 0)\n .join(\"\\n\");\n}\n\n/** Convert markdown with the converter that matches a skin. */\nexport function markdownToHtmlForSkin(\n markdown: string,\n skin: MarkdownSkin,\n options: MarkdownToHtmlOptions = {},\n): string {\n return skin === \"article\"\n ? markdownToArticleHtml(markdown, options)\n : markdownToHtml(markdown, options);\n}\n\nexport interface RenderMarkdownDocumentOptions extends MarkdownStylesheetOptions {\n title?: string;\n /** Override the shipped converter. The seam survives; it is no longer required. */\n convertToHtml?: (markdown: string) => string;\n /** Replace the whole stylesheet (not merely the tokens). */\n css?: string;\n onNotice?: (notice: MarkdownNotice) => void;\n /** See `MarkdownToHtmlOptions.renderBlock`. */\n renderBlock?: MarkdownToHtmlOptions[\"renderBlock\"];\n /** Include the Print / Close action bar (hidden by `@media print`). */\n withPrintActions?: boolean;\n}\n\n/**\n * markdown → a complete, standalone, styled HTML document string.\n *\n * This is the one composition every lane uses — the print window, the PDF\n * capture host, and \"copy a full HTML page\". A host that builds its own\n * `<!DOCTYPE html>` wrapper around package CSS is re-implementing this.\n */\nexport function renderMarkdownDocument(\n markdown: string,\n options: RenderMarkdownDocumentOptions = {},\n): string {\n const skin: MarkdownSkin = options.skin ?? \"document\";\n const title = options.title ?? \"AI Response\";\n const convert =\n options.convertToHtml ??\n ((md: string) =>\n markdownToHtmlForSkin(md, skin, {\n ...(options.onNotice ? { onNotice: options.onNotice } : {}),\n ...(options.renderBlock ? { renderBlock: options.renderBlock } : {}),\n }));\n const bodyHtml = convert(markdown);\n const css =\n options.css ??\n getMarkdownStylesheet({\n skin,\n ...(options.tokens === undefined ? {} : { tokens: options.tokens }),\n ...(options.extraCss === undefined ? {} : { extraCss: options.extraCss }),\n });\n\n // The article sheet already styles the action bar; the document sheet is a\n // pure content stylesheet, so it gets the shared chrome appended.\n const chrome =\n options.withPrintActions && skin !== \"article\" ? PRINT_ACTIONS_CSS : \"\";\n\n const actions = options.withPrintActions\n ? '<div class=\"print-actions\"><button class=\"print-btn print-btn-primary\" onclick=\"window.print()\">Print / Save as PDF</button><button class=\"print-btn print-btn-secondary\" onclick=\"window.close()\">Close</button></div>'\n : \"\";\n\n return `<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>${escapeHtml(\n title,\n )}</title>\n<style>${css}${chrome}</style></head>\n<body>${actions}<div class=\"content\">${bodyHtml}</div></body></html>`;\n}\n\nexport interface PrintMarkdownOptions extends RenderMarkdownDocumentOptions {\n /** Window features for the print window. */\n windowFeatures?: string;\n}\n\n/**\n * Open a print window for a markdown string, in one call.\n *\n * Returns the `PrintOutcome` so the host can tell the user when the popup was\n * blocked and the `.html` download fallback fired instead — a silent fallback\n * is indistinguishable from \"print did nothing\".\n */\nexport function printMarkdown(\n markdown: string,\n options: PrintMarkdownOptions = {},\n): PrintOutcome {\n const title = options.title ?? \"AI Response\";\n const doc = renderMarkdownDocument(markdown, {\n ...options,\n title,\n withPrintActions: options.withPrintActions ?? true,\n });\n return options.windowFeatures === undefined\n ? openPrintWindow(doc, title)\n : openPrintWindow(doc, title, options.windowFeatures);\n}\n\n/**\n * Print markdown that needs async preparation first (drawing its diagrams).\n * The window opens NOW, inside the click, so no popup blocker stops it; it\n * shows \"Preparing…\" until `prepare` resolves, then gets the document. When\n * the popup is blocked the prepared document downloads instead, as\n * `printMarkdown` does.\n */\nexport async function printMarkdownWhenReady(\n markdown: string,\n options: Omit<PrintMarkdownOptions, \"renderBlock\"> & {\n /**\n * The print form of each embedded block, asked once per block BEFORE the\n * document is composed (a diagram drawn, a page captured, a quiz through\n * its registry printer). Null prints the block's source. A block whose\n * answer throws prints an honest notice, never blank.\n */\n renderBlock: (block: PrintBlock) => Promise<PrintBlockRender | null | undefined>;\n },\n): Promise<PrintOutcome> {\n const title = options.title ?? \"AI Response\";\n const pending = openPendingPrintWindow(title, options.windowFeatures);\n const { renderBlock, ...rest } = options;\n const answers = await resolvePrintBlocks(markdown, rest.skin ?? \"document\", renderBlock);\n // Each block printer's own stylesheet, once (block HTML itself is sanitized: no <style>).\n const blockCss = [...new Set([...answers.values()].map((a) => (\"css\" in a && typeof a.css === \"string\" ? a.css : \"\")).filter(Boolean))].join(\"\\n\");\n const doc = renderMarkdownDocument(markdown, {\n ...rest,\n ...(blockCss\n ? rest.css !== undefined\n ? { css: `${rest.css}\\n${blockCss}` }\n : { extraCss: `${rest.extraCss ?? \"\"}\\n${blockCss}` }\n : {}),\n renderBlock: (block) => answers.get(printBlockKey(block)) ?? null,\n title,\n withPrintActions: rest.withPrintActions ?? true,\n });\n return pending.write(doc);\n}\n\n/** Asks `renderBlock` for every block of `markdown` (in parallel); keyed by `printBlockKey`. */\nexport async function resolvePrintBlocks(\n markdown: string,\n skin: MarkdownSkin,\n renderBlock: (block: PrintBlock) => Promise<PrintBlockRender | null | undefined>,\n): Promise<Map<string, PrintBlockRender>> {\n const answers = new Map<string, PrintBlockRender>();\n await Promise.all(\n listPrintBlocks(markdown, skin).map(async (block) => {\n try {\n const render = await renderBlock(block);\n if (render) answers.set(printBlockKey(block), render);\n } catch (error) {\n console.error(\"[@ai-matrx/print] a block's print form failed\", error);\n answers.set(printBlockKey(block), {\n notice: `This ${block.type || \"block\"} could not be prepared for print.`,\n });\n }\n }),\n );\n return answers;\n}\n","/**\n * @ai-matrx/print/core — the Block Print contract & print-window composition.\n *\n * Ported VERBATIM from matrx-frontend `lib/block-print/block-print-utils.ts`\n * (2026-08-29 state, including the `number` and `select` PrintSetting types the\n * commerce label lane added). Zero dependencies; touches `window` only at call\n * time, so the module stays import-inert and RSC-safe.\n *\n * BlockPrinter is the only interface a host system needs to know about.\n * Each printable unit exports its own BlockPrinter alongside its render\n * component; host code only ever calls print(data, variantId, settings).\n *\n * `buildFitTextScript` is the ONE auto-shrink script (the flashcards\n * FIT_TEXT pattern, parameterized by selector so the label lane shares it).\n */\n\n// THE ONE HTML escaper (duplication census row 14). Nine copies of\n// `escapeHtml` were live across the fleet and THEY DID NOT ESCAPE THE SAME\n// CHARACTERS — a string safe through one was an attribute break-out through\n// another. It moved to `@ai-matrx/kit/html-escape` with a decided character\n// set: an HTML escaper must not require this package's jspdf + html2canvas\n// graph. Not re-exported from here; import `@ai-matrx/kit/html-escape`.\nimport { downloadFile } from \"@ai-matrx/kit/download\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { KATEX_CSS, KATEX_CSS_MARKER } from \"./katex-css.generated\";\nimport { openOutbound } from \"@ai-matrx/kit/outbound\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\nexport interface PrintVariant {\n id: string;\n label: string;\n description?: string;\n}\n\n/**\n * A single user-configurable setting shown in the print dialog.\n * Extend the discriminated union for new control types\n * without breaking existing printers.\n */\nexport type PrintSetting =\n | {\n type: \"boolean\";\n id: string;\n label: string;\n description?: string;\n /** Default value shown when the dialog first opens */\n defaultValue: boolean;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /**\n * A numeric \"from → to\" range control, rendered as two small number inputs.\n * Writes two entries into the resolved PrintSettings map — one under fromId,\n * one under toId. Printers interpret 0 (or missing) as \"no bound\" by convention.\n */\n type: \"range\";\n /** Stable id used as React key — not written to the settings map */\n id: string;\n label: string;\n description?: string;\n /** Settings map key for the lower bound value */\n fromId: string;\n /** Settings map key for the upper bound value */\n toId: string;\n defaultFrom: number;\n defaultTo: number;\n /** Minimum accepted value for each input (inclusive). Default 0. */\n min?: number;\n /** Placeholder shown when the input is empty (0). Default \"start\" / \"end\". */\n fromPlaceholder?: string;\n toPlaceholder?: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A single numeric input (e.g. \"start at label #\", \"copies\"). */\n type: \"number\";\n id: string;\n label: string;\n description?: string;\n defaultValue: number;\n /** Minimum accepted value (inclusive). Default 0. */\n min?: number;\n /** Maximum accepted value (inclusive). Omit = unbounded. */\n max?: number;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A one-of-N choice rendered as a compact segmented row. */\n type: \"select\";\n id: string;\n label: string;\n description?: string;\n options: { value: string; label: string }[];\n defaultValue: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n };\n\n/** A resolved map of setting id → current value, passed to print(). */\nexport type PrintSettings = Record<string, boolean | string | number>;\n\n/**\n * The self-contained print capability that each block can export.\n * Host code only ever calls print(data, variantId, settings) — it never inspects internals.\n */\nexport interface BlockPrinter {\n /** Tooltip / aria-label for the print button in the block header */\n label: string;\n\n /**\n * Variants offered to the user before printing.\n * Empty array = no options dialog shown; prints immediately with defaults.\n */\n variants: PrintVariant[];\n\n /**\n * Optional user-configurable settings shown below the variant picker.\n * Omit entirely if the block has no configurable settings.\n */\n settings?: PrintSetting[];\n\n /**\n * Execute the print.\n * data — the block's structured data (serverData or parsed content)\n * variantId — which variant was chosen (undefined = default / no variants)\n * settings — resolved setting values chosen by the user\n *\n * MAY resolve to a PrintOutcome (\"opened\" | \"downloaded\") so the host can\n * tell the user when the popup-blocked download fallback fired; `void`\n * stays legal for printers that don't go through a print window.\n */\n print: (\n data: unknown,\n variantId?: string,\n settings?: PrintSettings,\n ) => void | PrintOutcome | Promise<void | PrintOutcome>;\n\n /**\n * The block as it appears INSIDE a composed print (a message's Print / Full\n * Print, a document export): the same adapter the block's own print uses,\n * returning body HTML or a picture. A frame block (an HTML page) answers\n * with an image — a composed print window runs no author scripts — or an\n * honest `{ notice }` when no picture can be made. Absent ⇒ the composer\n * prints the block's source.\n */\n toPrintHtml?: (data: unknown, context: PrintBlockContext) => PrintBlockOutput | null | Promise<PrintBlockOutput | null>;\n}\n\n/** Where a block sits when a composer asks for its print form. */\nexport interface PrintBlockContext {\n /** The registry key the block was found under (artifact type or `__kind`). */\n readonly type: string;\n /** The block's raw source text, exactly as it sits in the message. */\n readonly raw: string;\n /** The block's title, when the wire form carries one. */\n readonly title?: string | undefined;\n /** The wire form's attributes (an `<artifact>`'s `id`, `version`) — how a printer finds the block's record. */\n readonly attributes?: Readonly<Record<string, string>> | undefined;\n}\n\n/**\n * A block's print form inside a composed print. `html` is trusted body HTML\n * from a printer (escape what you interpolate); `image` is a picture (a\n * captured page, a drawn diagram); `notice` is an honest line printed in the\n * block's place when neither exists.\n */\nexport type PrintBlockOutput =\n | {\n readonly html: string;\n /**\n * The block's stylesheet. A composed print sanitizes block HTML (no\n * `<style>`, no inline styles, only `matrx-*` / `print-*` classes), so a\n * printer that styles its layout passes its CSS here: the composer puts\n * each distinct sheet once in the document head. Scope it under your own\n * `matrx-…` class.\n */\n readonly css?: string | undefined;\n }\n | { readonly image: { readonly src: string; readonly alt?: string | undefined; readonly caption?: string | undefined } }\n | { readonly notice: string };\n\n// ─── The ONE block-printer registry ───────────────────────────────────────────\n//\n// Keyed by artifact type / `__kind`. A block's own print button, the canvas\n// tab's Print and the message composer all read the same entry, so a block\n// has one print adapter with every caller. Lives on globalThis under a\n// Symbol.for key so duplicated bundles (ESM + CJS) share one registry.\n\nconst PRINTER_REGISTRY = Symbol.for(\"ai-matrx.print.block-printers\");\n\nfunction printerRegistry(): Map<string, BlockPrinter> {\n const g = globalThis as unknown as { [PRINTER_REGISTRY]?: Map<string, BlockPrinter> };\n g[PRINTER_REGISTRY] ??= new Map();\n return g[PRINTER_REGISTRY];\n}\n\n/** Registers `printer` under each key (artifact type and/or `__kind`). Returns the unregister. */\nexport function registerBlockPrinter(keys: string | readonly string[], printer: BlockPrinter): () => void {\n const map = printerRegistry();\n const list = (typeof keys === \"string\" ? [keys] : keys).map((key) => key.toLowerCase());\n for (const key of list) map.set(key, printer);\n return () => {\n for (const key of list) if (map.get(key) === printer) map.delete(key);\n };\n}\n\n/** The printer registered for an artifact type or `__kind` (case-insensitive). */\nexport function getBlockPrinter(key: string | null | undefined): BlockPrinter | undefined {\n return key ? printerRegistry().get(key.toLowerCase()) : undefined;\n}\n\n/** Every registered key — for guards and diagnostics. */\nexport function listBlockPrinterKeys(): string[] {\n return [...printerRegistry().keys()];\n}\n\n/** The HTML a composer puts in a block's place for a `PrintBlockOutput`. */\nexport function printBlockOutputHtml(output: PrintBlockOutput): string {\n if (\"html\" in output) return `<div class=\"matrx-print-block\">${output.html}</div>`;\n if (\"image\" in output) {\n const { src, alt, caption } = output.image;\n return `<figure class=\"matrx-print-block matrx-print-image\"><img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(\n alt ?? \"Picture\",\n )}\" style=\"max-width:100%;height:auto\">${caption ? `<figcaption>${escapeHtml(caption)}</figcaption>` : \"\"}</figure>`;\n }\n return `<p class=\"matrx-print-block matrx-print-notice\" style=\"border:1px dashed #94a3b8;border-radius:6px;padding:10px 12px;color:#475569;font-style:italic\">${escapeHtml(\n output.notice,\n )}</p>`;\n}\n\n// ─── Shared Utilities ─────────────────────────────────────────────────────────\n\nconst PRINT_STYLES = `\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;\n font-size: 11pt;\n line-height: 1.6;\n color: #1a1a1a;\n background: #fff;\n max-width: 860px;\n margin: 0 auto;\n padding: 28px 36px;\n }\n\n .print-actions {\n display: flex;\n gap: 10px;\n margin-bottom: 24px;\n padding-bottom: 18px;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .print-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n cursor: pointer;\n border: none;\n font-weight: 600;\n }\n\n .print-btn-primary { background: #2563eb; color: #fff; }\n .print-btn-secondary { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }\n\n h1 { font-size: 22pt; font-weight: 700; margin-bottom: 0.5em; }\n h2 { font-size: 16pt; font-weight: 700; margin: 1.2em 0 0.4em; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.2em; }\n h3 { font-size: 13pt; font-weight: 600; margin: 1em 0 0.3em; }\n p { margin-bottom: 0.7em; }\n\n table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n font-size: 10pt;\n }\n th {\n background: #1e293b;\n color: #f8fafc;\n font-weight: 600;\n text-align: left;\n padding: 7px 12px;\n border: 1px solid #334155;\n }\n td {\n padding: 6px 12px;\n border: 1px solid #e2e8f0;\n vertical-align: top;\n }\n tr:nth-child(even) td { background: #f8fafc; }\n\n @media print {\n body { max-width: 100%; padding: 0; }\n .print-actions { display: none !important; }\n th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n }\n`;\n\n/**\n * Wrap body HTML in a complete, styled print document.\n */\nexport function buildPrintDocument(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(title)}</title>\n <style>\n${PRINT_STYLES}\n${extraStyles}\n </style>\n</head>\n<body>\n <div class=\"print-actions\">\n <button class=\"print-btn print-btn-primary\" onclick=\"window.print()\">Print / Save as PDF</button>\n <button class=\"print-btn print-btn-secondary\" onclick=\"window.close()\">Close</button>\n </div>\n <div class=\"content\">\n${bodyHtml}\n </div>\n</body>\n</html>`;\n}\n\n/**\n * How a print request actually reached the user.\n *\n * \"opened\" — a print window/tab was opened and the document written into it.\n * \"downloaded\" — the popup was blocked, so the document was downloaded as an\n * .html file instead (the contract's fallback). Hosts SHOULD\n * surface this to the user (e.g. a toast saying the print file\n * was downloaded) — a blocked popup plus a silent download is\n * indistinguishable from \"nothing happened\", which is exactly\n * how QA filed it (2026-08-30, F5/F6/F8).\n */\nexport type PrintOutcome = \"opened\" | \"downloaded\";\n\n/**\n * Open a new browser window and write a complete HTML document into it.\n * Falls back to downloading an .html file if the popup is blocked.\n *\n * Returns which path was taken so callers can tell the user when the\n * fallback fired. `windowFeatures` customizes the popup geometry (the\n * flashcards fixed-geometry variants pass a wider window).\n */\n/**\n * Every printed document leaves through here, so this is where typeset math\n * gets its stylesheet: when the page carries KaTeX output and does not already\n * carry the sheet, it is added to <head>. A page with no math is untouched,\n * byte for byte (the flashcard golden master holds every printer to that).\n */\nexport function withMathStyles(htmlDocument: string): string {\n if (!htmlDocument.includes('class=\"katex\"') || htmlDocument.includes(KATEX_CSS_MARKER)) {\n return htmlDocument;\n }\n const style = `<style data-matrx-katex>${KATEX_CSS}</style>`;\n const head = htmlDocument.search(/<\\/head>/i);\n return head >= 0\n ? htmlDocument.slice(0, head) + style + htmlDocument.slice(head)\n : style + htmlDocument;\n}\n\nexport function openPrintWindow(\n rawDocument: string,\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n const win = openOutbound(\"\", \"_blank\", windowFeatures);\n // Popup blocked — fall back to file download.\n if (!win) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n}\n\n/** A print window opened before its document is ready (see `openPendingPrintWindow`). */\nexport interface PendingPrintWindow {\n /** Write the finished document; downloads it when the popup was blocked or closed. */\n write(htmlDocument: string): PrintOutcome;\n}\n\n/**\n * Open the print window NOW — inside the click, where popup blockers allow it —\n * and write the document later, once async preparation (drawing diagrams) is\n * done. The window says \"Preparing…\" meanwhile.\n */\nexport function openPendingPrintWindow(\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PendingPrintWindow {\n const win = openOutbound(\"\", \"_blank\", windowFeatures);\n if (win) {\n win.document.open();\n win.document.write(\n '<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Preparing…</title></head><body style=\"font:14px -apple-system,BlinkMacSystemFont,sans-serif;color:#555;padding:2rem\">Preparing the document for print…</body></html>',\n );\n win.document.close();\n }\n return {\n write(rawDocument: string): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n if (!win || win.closed) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n },\n };\n}\n\nfunction openPrintWindowFallback(htmlDocument: string, filename: string): PrintOutcome {\n downloadFile(`${filename.replace(/\\s+/g, \"-\").toLowerCase()}.html`, htmlDocument, \"text/html;charset=utf-8\");\n return \"downloaded\";\n}\n\n/**\n * Shorthand: build the document and open it in one call.\n * Returns the same PrintOutcome as openPrintWindow.\n */\nexport function printHtmlContent(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): PrintOutcome {\n const doc = buildPrintDocument(bodyHtml, title, extraStyles);\n return openPrintWindow(doc, title);\n}\n\n/**\n * Build the auto-shrink script that runs INSIDE a printed window: for every\n * element matching each selector, its font-size is reduced (0.5pt steps, 6pt\n * floor) until it fits its parent element's box. This is the flashcards\n * FIT_TEXT behavior verbatim, parameterized by selector so every lane\n * (flashcards card text, Avery text, label captions) shares the ONE loop.\n *\n * Pass the same selectors the original used to get byte-identical behavior:\n * flashcards passes `[\".card-text .card-text-inner\", \".avery-text-wrap .avery-text\"]`.\n */\nexport function buildFitTextScript(selectors: readonly string[]): string {\n const selectorsJson = JSON.stringify([...selectors]);\n return `\n<script>\n(function() {\n function fitText(el, container) {\n // container is the bounding box we must fit inside\n var maxW = container.clientWidth;\n var maxH = container.clientHeight;\n var size = parseFloat(window.getComputedStyle(el).fontSize);\n var minSize = 6;\n // Give the element a chance to reflow at current size before measuring\n el.style.fontSize = size + \"pt\";\n while (size > minSize && (el.scrollWidth > maxW + 1 || el.scrollHeight > maxH + 1)) {\n size -= 0.5;\n el.style.fontSize = size + \"pt\";\n }\n }\n function runAll() {\n var selectors = ${selectorsJson};\n for (var s = 0; s < selectors.length; s++) {\n var els = document.querySelectorAll(selectors[s]);\n for (var i = 0; i < els.length; i++) {\n fitText(els[i], els[i].parentElement);\n }\n }\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", runAll);\n } else {\n // document.write pages may already be loaded\n setTimeout(runAll, 0);\n }\n})();\n</script>`;\n}\n\n\n// ─── DOM print: a rendered body, as it looks ─────────────────────────────────\n\nexport interface PrintElementOptions {\n /** The print window's title (and the download fallback's file name). */\n title?: string;\n /** Open the system print dialog once the window has loaded. Default true. */\n autoPrint?: boolean;\n /** Keep buttons, toolbars and menus in the print (default false: they are chrome, not content). */\n keepInteractive?: boolean;\n}\n\n/**\n * Interactive chrome that is not content: anything marked `data-print-hide`,\n * toolbars and menus, and buttons (\"Chart this\", \"Save to\", icon actions).\n * A button stays when it IS content — marked `data-print-keep`, or a disclosure\n * trigger (`aria-expanded`) whose label is a heading — and so does anything\n * inside a `data-print-keep` region. Removal runs on the print clone only.\n */\nconst PRINT_CHROME_SELECTOR = [\n \"[data-print-hide]\",\n '[role=\"toolbar\"]',\n '[role=\"menu\"]',\n '[role=\"menubar\"]',\n \"button\",\n '[role=\"button\"]',\n 'input[type=\"button\"]',\n 'input[type=\"submit\"]',\n 'input[type=\"reset\"]',\n].join(\",\");\n\nexport function stripInteractiveChrome(root: ParentNode): number {\n let removed = 0;\n root.querySelectorAll(PRINT_CHROME_SELECTOR).forEach((node) => {\n if (!root.contains(node)) return; // already gone with a removed ancestor\n const explicit = node.hasAttribute(\"data-print-hide\");\n if (!explicit) {\n if (node.closest(\"[data-print-keep]\")) return;\n if (node.matches(\"button, [role=\\\"button\\\"]\") && node.hasAttribute(\"aria-expanded\")) return;\n }\n node.remove();\n removed += 1;\n });\n return removed;\n}\n\nconst ELEMENT_PRINT_CSS = `\n html, body { height: auto !important; min-height: 0 !important; overflow: visible !important; background: #fff !important; }\n body { margin: 0; padding: 16px; }\n *, *::before, *::after { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }\n .matrx-print-root, .matrx-print-root * { max-height: none !important; }\n .matrx-print-root [class*=\"overflow-\"], .matrx-print-root [class*=\"h-full\"], .matrx-print-root [class*=\"min-h-0\"] { overflow: visible !important; height: auto !important; }\n .matrx-print-frame-notice { border: 1px dashed #94a3b8; border-radius: 6px; padding: 10px 12px; color: #475569; font: italic 12px sans-serif; }\n @media print { .matrx-print-root * { break-inside: auto; } img, svg, figure, tr { break-inside: avoid; } }\n`;\n\n/**\n * Prints a same-origin DOM body exactly as it is drawn: the element's markup\n * with the page's own stylesheets, light theme, backgrounds kept, scroll\n * containers opened to full height. A `<canvas>` prints as its picture; an\n * `<iframe>` cannot be copied and prints an honest notice in its place (a\n * frame kind declares its own print instead). Opens inside the click.\n */\nexport function printElement(element: HTMLElement, options: PrintElementOptions = {}): PrintOutcome {\n const title = options.title ?? (document.title || \"Print\");\n const head = [...document.querySelectorAll<HTMLLinkElement | HTMLStyleElement>('link[rel=\"stylesheet\"], style')]\n .map((node) => {\n if (node instanceof HTMLLinkElement) return `<link rel=\"stylesheet\" href=\"${escapeHtml(node.href)}\">`;\n return `<style>${node.textContent ?? \"\"}</style>`;\n })\n .join(\"\\n\");\n const clone = element.cloneNode(true) as HTMLElement;\n const liveCanvases = element.querySelectorAll(\"canvas\");\n clone.querySelectorAll(\"canvas\").forEach((canvas, i) => {\n const live = liveCanvases[i];\n const img = document.createElement(\"img\");\n try {\n img.src = live ? live.toDataURL(\"image/png\") : \"\";\n } catch {\n img.alt = \"This drawing cannot be copied for print.\";\n }\n img.style.maxWidth = \"100%\";\n canvas.replaceWith(img);\n });\n clone.querySelectorAll(\"iframe\").forEach((frame) => {\n const note = document.createElement(\"p\");\n note.className = \"matrx-print-frame-notice\";\n note.textContent = \"An embedded page is here — print it from its own Print.\";\n frame.replaceWith(note);\n });\n clone.querySelectorAll(\"script\").forEach((node) => node.remove());\n if (options.keepInteractive !== true) stripInteractiveChrome(clone);\n const htmlClass = document.documentElement.className.replace(/\\bdark\\b/g, \"light\");\n const autoPrint = options.autoPrint ?? true;\n const doc = `<!DOCTYPE html><html lang=\"en\" class=\"${escapeHtml(htmlClass)}\"><head><meta charset=\"UTF-8\"><base href=\"${escapeHtml(\n document.baseURI,\n )}\"><title>${escapeHtml(title)}</title>${head}<style>${ELEMENT_PRINT_CSS}</style></head><body><div class=\"matrx-print-root\">${\n clone.outerHTML\n }</div>${\n autoPrint\n ? '<script>window.addEventListener(\"load\",function(){setTimeout(function(){window.focus();window.print();},250);});</script>'\n : \"\"\n }</body></html>`;\n return openPrintWindow(doc, title);\n}\n","// GENERATED by scripts/gen-katex-css.mjs from katex@0.18.9 — do not edit.\nexport const KATEX_VERSION = \"0.18.9\";\n/** Leads KATEX_CSS; lets openPrintWindow see the sheet is already present. */\nexport const KATEX_CSS_MARKER = \"/* matrx-katex@0.18.9 */\";\nexport const KATEX_CSS = \"/* matrx-katex@0.18.9 */@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.ttf) format(\\\"truetype\\\")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:\\\"0.18.9\\\"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:\\\"\\\"}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:\\\"(\\\" counter(katexEqnNo) \\\")\\\";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:\\\"(\\\" counter(mmlEqnNo) \\\")\\\";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}\\n\";\n","/**\n * Plain text with math → safe HTML: THE one way a printer turns authored text\n * (a flashcard face, an exam prompt, a glossary definition) into markup.\n *\n * Every non-markdown printer used to `escapeHtml()` its text, so `\\(\\text{IO}_3^-\\)`\n * on a flashcard printed as that literal source (Arman, 2026-09-27). What is math\n * is decided by the shared tokenizer (`extractMath` — the same rule the screen,\n * the markdown lane and the /document pipeline use: code and `$5` prices are\n * never math); everything else is escaped exactly as before.\n *\n * Typeset output is KaTeX's laid-out HTML plus MathML, chemistry (mhchem)\n * loaded. KaTeX runs with `trust: false`, which refuses every command that could\n * inject HTML (\\href, \\url, \\htmlClass, \\htmlId, \\htmlStyle, \\htmlData,\n * \\includegraphics). The stylesheet is added by `openPrintWindow` whenever a\n * document contains math, so no printer can forget it.\n */\nimport katex from \"katex\";\n// Registers \\ce{…} and \\pu{…} on the same katex instance.\nimport \"katex/contrib/mhchem\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { extractMath, MATH_TOKEN_RE } from \"./document/math\";\n\n/** Typeset one TeX span — the ONE KaTeX configuration every print lane uses. */\nexport function typesetTex(tex: string, display: boolean): string {\n return katex.renderToString(tex, {\n displayMode: display,\n output: \"htmlAndMathml\",\n throwOnError: false,\n trust: false,\n strict: \"ignore\",\n });\n}\n\n/**\n * Escape `text` for HTML, with every math span typeset. Newlines are kept as\n * `\\n` in the prose so callers that turn them into `<br>` keep doing so; a\n * newline inside a formula is folded to a space (TeX treats it as one).\n */\nexport function mathTextHtml(text: string | null | undefined): string {\n // The tokenizer's own markers can never come from an author.\n const source = (text ?? \"\").replace(/[\\uE100\\uE101]/g, \"\");\n if (!/[$\\\\]/.test(source)) return escapeHtml(source);\n const { text: prose, spans } = extractMath(source);\n if (spans.length === 0) return escapeHtml(source);\n return escapeHtml(prose).replace(MATH_TOKEN_RE, (_m: string, i: string) => {\n const span = spans[Number(i)];\n return span ? typesetTex(span.tex.replace(/\\s*\\n\\s*/g, \" \"), span.display) : \"\";\n });\n}\n\n/**\n * A field that IS a formula (a cheat-sheet \"formula\" slot): delimited math is\n * typeset as usual; undelimited TeX (it has a command, `^` or `_`) is typeset\n * whole as inline math; anything else is escaped text.\n */\nexport function formulaHtml(formula: string | null | undefined): string {\n const source = (formula ?? \"\").trim();\n const withMath = mathTextHtml(source);\n if (withMath.includes('class=\"katex\"')) return withMath;\n return /\\\\[a-zA-Z]|[\\^_]/.test(source) ? typesetTex(source, false) : withMath;\n}\n","/**\n * Math in documents — ONE math dialect, typeset in every format.\n *\n * What is math is decided ONLY by the shared tokenizer (`@ai-matrx/content-ir/\n * source`): its fence-nesting rule finds code, its pairing finds `$$…$$`,\n * `$…$` (math, not currency), `\\[…\\]` and `\\(…\\)`. Math is lifted out\n * BEFORE markdown parsing, so a TeX `\\,` is never eaten as a markdown escape\n * and `_`/`*` inside a formula never become emphasis.\n *\n * Typesetting: MathJax (TeX → its internal tree) produces\n * - SVG for PDF display math (drawn as vector paths; allow-list sanitized),\n * - MathML for HTML and EPUB (rendered natively by browsers and readers),\n * - MathML that the Word writer turns into native OMML equations and the\n * PDF writer turns into inline text with real super/subscripts.\n */\n\nimport { listIslands, tokenizeSource } from \"@ai-matrx/content-ir/source\";\nimport { sanitizeMathml, sanitizeMathSvg } from \"../safe-html\";\n\nexport const MATH_OPEN = \"\\uE100\";\nexport const MATH_CLOSE = \"\\uE101\";\nexport const MATH_TOKEN_RE = /\\uE100(\\d+)\\uE101/g;\n\nexport interface MathSpan {\n tex: string;\n display: boolean;\n}\n\nconst DELIMITERS: Record<string, { open: number; close: number; display: boolean }> = {\n $$: { open: 2, close: 2, display: true },\n \"\\\\[\": { open: 2, close: 2, display: true },\n $: { open: 1, close: 1, display: false },\n \"\\\\(\": { open: 2, close: 2, display: false },\n};\n\n/**\n * Replace every math span with a private-use token; returns the spans.\n *\n * Print keeps NO fence, code-span or math detection of its own: the spans are\n * the shared tokenizer's complete `math_block` / `math_inline` islands\n * (`tokenizeSource` + `listIslands` from `@ai-matrx/content-ir/source` — the\n * one fence-nesting rule and the one math pairing), so print, the renderer and\n * the editor can never disagree about what is math. The delimiter decides the\n * form: `$$`/`\\[` display, `$`/`\\(` inline.\n */\nexport function extractMath(body: string): { text: string; spans: MathSpan[] } {\n const spans: MathSpan[] = [];\n if (!/[$\\\\]/.test(body)) return { text: body, spans };\n const islands = listIslands(tokenizeSource(body))\n .filter((isl) => isl.complete && (isl.islandType === \"math_block\" || isl.islandType === \"math_inline\"))\n .sort((a, b) => a.start - b.start);\n let out = \"\";\n let at = 0;\n for (const isl of islands) {\n const d = DELIMITERS[String(isl.meta.delimiter ?? \"\")];\n if (!d || isl.start < at) continue;\n out += body.slice(at, isl.start);\n spans.push({ tex: body.slice(isl.start + d.open, isl.end - d.close).trim(), display: d.display });\n out += `${MATH_OPEN}${spans.length - 1}${MATH_CLOSE}`;\n at = isl.end;\n }\n return { text: out + body.slice(at), spans };\n}\n\n// ── typesetting ────────────────────────────────────────────────────────────\n\nexport interface TypesetMath {\n /** MathML (<math …>…</math>) for HTML/EPUB and the Word/PDF-inline writers. */\n mathml: string;\n /** SVG for PDF display math; widths in ex (1ex ≈ 0.442em in MathJax). */\n svg?: string | undefined;\n widthEx?: number | undefined;\n heightEx?: number | undefined;\n /** Error message when the TeX could not be typeset. */\n error?: string | undefined;\n}\n\nexport const mathKey = (tex: string, display: boolean): string => `${display ? \"D\" : \"I\"}:${tex}`;\n\ntype MathJaxKit = import(\"../mathjax\").MathJaxKit;\n\nconst MATHJAX = Symbol.for(\"ai-matrx.print.document.mathjax\");\n\nasync function loadMathJax(): Promise<MathJaxKit> {\n const g = globalThis as unknown as Record<symbol, Promise<MathJaxKit> | undefined>;\n if (g[MATHJAX]) return g[MATHJAX] as Promise<MathJaxKit>;\n // The self-referencing specifier keeps this a real lazy edge in dist (see src/mathjax.ts).\n const pending = import(\"@ai-matrx/print/mathjax\").then((m) => m.createMathJaxKit());\n pending.catch(() => {\n if (g[MATHJAX] === pending) g[MATHJAX] = undefined;\n });\n g[MATHJAX] = pending;\n return pending;\n}\n\n/** Typeset every math span once; keyed by `mathKey`. */\nexport async function typesetMathSpans(spans: MathSpan[]): Promise<Map<string, TypesetMath>> {\n const out = new Map<string, TypesetMath>();\n if (spans.length === 0) return out;\n const mj = await loadMathJax();\n for (const s of spans) {\n const key = mathKey(s.tex, s.display);\n if (out.has(key)) continue;\n try {\n const mathml = mj.mml(s.tex, s.display);\n const err = /<merror[\\s\\S]*?<mtext[^>]*>([\\s\\S]*?)<\\/mtext>/.exec(mathml);\n if (err) {\n out.set(key, { mathml: sanitizeMathml(mathml), error: decodeXml(err[1] as string) });\n continue;\n }\n // TeX is author input: `\\href`, `\\style`, `\\bbox[…]`, `\\mmlToken` reach\n // MathML attributes, so the typeset MathML goes through the allow-list.\n const entry: TypesetMath = { mathml: sanitizeMathml(mathml.replace(/\\n\\s*/g, \"\")) };\n if (s.display) {\n // The same author TeX reaches the SVG as <a href>, style, class and id\n // (LIST T26): only the drawing survives.\n const svg = sanitizeMathSvg(mj.svg(s.tex, true));\n entry.svg = svg;\n entry.widthEx = Number(/width=\"([\\d.]+)ex\"/.exec(svg)?.[1] ?? 0);\n entry.heightEx = Number(/height=\"([\\d.]+)ex\"/.exec(svg)?.[1] ?? 0);\n }\n out.set(key, entry);\n } catch (e) {\n out.set(key, { mathml: \"\", error: (e as Error).message });\n }\n }\n return out;\n}\n\n// ── a small MathML reader (MathJax's serializer output is well-formed XML) ──\n\nexport interface MNode {\n tag: string;\n attrs: Record<string, string>;\n children: MNode[];\n text: string;\n}\n\nexport function decodeXml(s: string): string {\n return s.replace(/&(#x[0-9a-f]+|#\\d+|amp|lt|gt|quot|apos);/gi, (m, e: string) => {\n if (e[0] === \"#\") return String.fromCodePoint(e[1] === \"x\" || e[1] === \"X\" ? parseInt(e.slice(2), 16) : Number(e.slice(1)));\n return ({ amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\" } as Record<string, string>)[e.toLowerCase()] ?? m;\n });\n}\n\nexport function parseMathML(xml: string): MNode {\n const root: MNode = { tag: \"#root\", attrs: {}, children: [], text: \"\" };\n const stack: MNode[] = [root];\n for (const m of xml.matchAll(/<(\\/?)([\\w:-]+)((?:\\s+[\\w:-]+=\"[^\"]*\")*)\\s*(\\/?)>|([^<]+)/g)) {\n const top = stack[stack.length - 1] as MNode;\n if (m[5] !== undefined) {\n const t = decodeXml(m[5]);\n if (t.trim() || top.tag === \"mtext\" || top.tag === \"mo\") top.text += t;\n continue;\n }\n if (m[1] === \"/\") {\n if (stack.length > 1) stack.pop();\n continue;\n }\n const attrs: Record<string, string> = {};\n for (const a of (m[3] ?? \"\").matchAll(/([\\w:-]+)=\"([^\"]*)\"/g)) attrs[a[1] as string] = decodeXml(a[2] as string);\n const node: MNode = { tag: (m[2] as string).replace(/^m:/, \"\"), attrs, children: [], text: \"\" };\n top.children.push(node);\n if (m[4] !== \"/\") stack.push(node);\n }\n return root.children[0] ?? root;\n}\n\n/** Flatten to plain text (the linear reading of a formula). */\nexport function mathText(n: MNode): string {\n if (n.children.length === 0) return n.text;\n return n.children.map(mathText).join(\"\");\n}\n","/**\n * @ai-matrx/print/safe-html — THE allow-list every Markdown→HTML string this\n * package hands out goes through.\n *\n * Why an allow-list: the regex converter (`markdownToHtml` /\n * `markdownToArticleHtml`) lets author raw HTML through so a document can feed\n * WordPress. Its old guard was a block-list (script, style, on… handlers,\n * javascript: URLs) and everything nobody listed — `<iframe>`, `<form>`,\n * `<meta http-equiv=refresh>`, `<object>`/`<embed>`, `<base>`, `<link>`,\n * inline `style` — reached the clipboard, the downloaded page and the preview.\n * Now a tag or attribute survives only when it is listed here.\n *\n * Sync and DOM-free (parse5 via hast), so it runs in Node, workers, the\n * extension service worker and Tauri alike.\n *\n * Two schemas:\n * • `SAFE_HTML_SCHEMA` — GitHub's rendering rules (hast-util-sanitize's\n * default schema) with no id clobber-prefixing and the \"drop the element\n * AND its text\" list for elements whose text is not prose. For any host\n * that renders plain semantic HTML (email bodies, CMS drafts).\n * • `PRINT_HTML_SCHEMA` — the same, plus exactly the vocabulary print's own\n * converters emit: `matrx-*` / `print-*` / `code-*` / `doc-*` classes,\n * table-cell alignment, the flashcard checkbox + label, figures, and the\n * page-break separator's data/aria attributes.\n */\n\nimport { fromHtml } from \"hast-util-from-html\";\nimport { defaultSchema, sanitize, type Schema } from \"hast-util-sanitize\";\nimport { toHtml } from \"hast-util-to-html\";\nimport type { Root } from \"hast\";\n\ntype Attributes = NonNullable<Schema[\"attributes\"]>;\ntype AttributeList = Attributes[string];\n\n/** Elements removed together with their content (their text is not prose). */\nconst STRIP_WITH_CONTENT = [\n \"script\",\n \"style\",\n \"noscript\",\n \"template\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"textarea\",\n \"select\",\n \"head\",\n \"title\",\n];\n\n/** Never allowed, whatever a schema extension adds. */\nexport const FORBIDDEN_TAGS: readonly string[] = [\n ...STRIP_WITH_CONTENT,\n \"form\",\n \"meta\",\n \"base\",\n \"link\",\n \"frame\",\n \"frameset\",\n \"applet\",\n \"button\",\n \"svg\",\n \"math\",\n];\n\nconst baseAttributes = defaultSchema.attributes ?? {};\n\nexport const SAFE_HTML_SCHEMA: Schema = {\n ...defaultSchema,\n tagNames: (defaultSchema.tagNames ?? []).filter((t) => !FORBIDDEN_TAGS.includes(t)),\n strip: STRIP_WITH_CONTENT,\n // Authored anchors keep their names; the GitHub `user-content-` rewrite\n // would also break `<label for>` → `<input id>` pairs.\n clobberPrefix: \"\",\n clobber: [],\n};\n\n/** Class tokens print's converters emit. Author classes outside these are dropped. */\nconst PRINT_CLASS = /^(?:(?:matrx|print|code|doc|language)-[\\w-]*|inline-code|is-checked)$/;\n\n/**\n * A tag's own `className` definition wins over `'*'` (and returns an empty\n * list rather than falling through), so the print class pattern is merged into\n * every definition that names `className`, and added where none does.\n */\nfunction withPrintClasses(defs: AttributeList = []): AttributeList {\n let found = false;\n const out = defs.map((d) => {\n if (Array.isArray(d) && d[0] === \"className\") {\n found = true;\n return [...d, PRINT_CLASS] as unknown as typeof d;\n }\n return d;\n });\n return found ? out : [...out, [\"className\", PRINT_CLASS]];\n}\n\nconst printAttributes: Attributes = Object.fromEntries(\n Object.entries(baseAttributes).map(([tag, defs]) => [tag, withPrintClasses(defs)]),\n);\n\nconst cellAlign = [\"style\", /^text-align:\\s*(?:left|center|right);?$/] as const;\n\nexport const PRINT_HTML_SCHEMA: Schema = {\n ...SAFE_HTML_SCHEMA,\n tagNames: [\n ...new Set([...(SAFE_HTML_SCHEMA.tagNames ?? []), \"section\", \"figure\", \"figcaption\", \"label\"]),\n ],\n attributes: {\n ...printAttributes,\n \"*\": [\n ...(baseAttributes[\"*\"] ?? []),\n [\"className\", PRINT_CLASS],\n \"role\",\n \"ariaLabel\",\n \"ariaHidden\",\n \"dataLabel\",\n \"dataLanguage\",\n \"dataArtifactType\",\n ],\n // Table-cell alignment is the one inline style the converter writes.\n td: withPrintClasses([...(baseAttributes[\"td\"] ?? []), [...cellAlign]]),\n th: withPrintClasses([...(baseAttributes[\"th\"] ?? []), [...cellAlign]]),\n // The flashcard flip is a checkbox + label pair (GitHub forces `disabled`).\n input: withPrintClasses([[\"type\", \"checkbox\"], \"checked\"]),\n label: withPrintClasses([\"htmlFor\"]),\n },\n required: { input: { type: \"checkbox\" } },\n protocols: {\n ...defaultSchema.protocols,\n href: [...(defaultSchema.protocols?.href ?? []), \"tel\"],\n // Embedded images (`data:image/png;base64,…`) — an <img> never runs script.\n src: [...(defaultSchema.protocols?.src ?? []), \"data\"],\n },\n};\n\n/** Sanitize an HTML fragment against an allow-list (default: `SAFE_HTML_SCHEMA`). */\nexport function sanitizeHtmlFragment(html: string, schema: Schema = SAFE_HTML_SCHEMA): string {\n if (!html) return \"\";\n const tree = sanitize(fromHtml(html, { fragment: true }), schema) as Root;\n return toHtml(tree, { characterReferences: { useNamedReferences: true } });\n}\n\n/** Sanitize the output of print's own converters (keeps the `.matrx-*` vocabulary). */\nexport function sanitizePrintHtml(html: string): string {\n return sanitizeHtmlFragment(html, PRINT_HTML_SCHEMA);\n}\n\nexport type { Schema as SafeHtmlSchema };\n\n/**\n * MathML from the TeX typesetter. TeX is author input, and MathJax's `html`,\n * `bbox` and `\\mmlToken` commands turn it into attributes (`\\href{javascript:…}`\n * → `href`, `\\style{position:fixed…}` / `\\bbox[position:fixed]` → `style`).\n * Only presentation MathML elements and their layout attributes survive.\n */\nconst MATHML_TAGS = [\n \"math\", \"maction\", \"annotation\", \"menclose\", \"merror\", \"mfrac\", \"mglyph\", \"mi\",\n \"mlabeledtr\", \"mmultiscripts\", \"mn\", \"mo\", \"mover\", \"mpadded\", \"mphantom\", \"mprescripts\", \"none\",\n \"mroot\", \"mrow\", \"ms\", \"mspace\", \"msqrt\", \"mstyle\", \"msub\", \"msubsup\", \"msup\", \"mtable\", \"mtd\",\n \"mtext\", \"mtr\", \"munder\", \"munderover\", \"semantics\",\n];\n\nconst MATHML_ATTRIBUTES = [\n \"xmlns\", \"display\", \"displaystyle\", \"mathvariant\", \"mathsize\", \"mathcolor\", \"mathbackground\",\n \"scriptlevel\", \"dir\", \"fence\", \"form\", \"largeop\", \"lspace\", \"rspace\", \"movablelimits\",\n \"separator\", \"stretchy\", \"symmetric\", \"maxsize\", \"minsize\", \"accent\", \"accentunder\", \"align\",\n \"columnalign\", \"rowalign\", \"columnspacing\", \"rowspacing\", \"columnlines\", \"rowlines\", \"frame\",\n \"framespacing\", \"equalrows\", \"equalcolumns\", \"linethickness\", \"numalign\", \"denomalign\",\n \"bevelled\", \"notation\", \"width\", \"height\", \"depth\", \"voffset\", \"lquote\", \"rquote\", \"columnspan\",\n \"rowspan\", \"encoding\", \"open\", \"close\", \"separators\", \"actiontype\", \"selection\", \"mslinebreak\",\n \"linebreak\", \"indentalign\", \"data*\",\n];\n\nexport const MATHML_SCHEMA: Schema = {\n tagNames: MATHML_TAGS,\n attributes: { \"*\": MATHML_ATTRIBUTES },\n strip: [\"script\", \"style\"],\n clobberPrefix: \"\",\n clobber: [],\n protocols: {},\n};\n\n/** Sanitize a typeset MathML string (keeps layout, drops links and styles). */\nexport function sanitizeMathml(mathml: string): string {\n return sanitizeHtmlFragment(mathml, MATHML_SCHEMA);\n}\n\n/**\n * SVG from the TeX typesetter (PDF display math, LIST T26). MathJax's `\\href`,\n * `\\style`, `\\class`, `\\cssId`, `\\bbox[…]` and `\\mmlToken[…]` reach the SVG as\n * `<a href=\"javascript:…\">`, `style=\"position:fixed…\"`, `class`, `id`. The PDF\n * writer draws this SVG, and any host that shows it would too, so only the\n * drawing vocabulary survives: shapes, paths, text, transforms and paint. A\n * link wrapper is unwrapped (its glyphs stay), everything else is dropped.\n */\nconst MATH_SVG_TAGS = [\"svg\", \"g\", \"path\", \"rect\", \"line\", \"polyline\", \"polygon\", \"circle\", \"ellipse\", \"text\", \"title\", \"desc\"];\n\nconst MATH_SVG_ATTRIBUTES = [\n \"xmlns\", \"width\", \"height\", \"viewBox\", \"role\", \"focusable\", \"ariaHidden\", \"ariaLabel\",\n \"stroke\", \"fill\", \"strokeWidth\", \"strokeLinecap\", \"strokeLinejoin\", \"transform\", \"d\",\n \"x\", \"y\", \"x1\", \"y1\", \"x2\", \"y2\", \"points\", \"cx\", \"cy\", \"r\", \"rx\", \"ry\",\n \"dataMmlNode\", \"dataC\",\n];\n\nexport const MATH_SVG_SCHEMA: Schema = {\n tagNames: MATH_SVG_TAGS,\n attributes: { \"*\": MATH_SVG_ATTRIBUTES },\n strip: [\"script\", \"style\", \"foreignObject\"],\n clobberPrefix: \"\",\n clobber: [],\n protocols: {},\n};\n\n/** Sanitize a typeset math SVG (keeps the drawing, drops links, styles, classes and ids). */\nexport function sanitizeMathSvg(svg: string): string {\n return sanitizeHtmlFragment(svg, MATH_SVG_SCHEMA);\n}\n\n/**\n * THE final allow-list of the one Markdown→HTML engine (`@ai-matrx/print/\n * markdown-html`, CONTRACT §1.9): print's own vocabulary plus typeset MathML\n * (MathML elements carry only the MathML layout attributes), and — for the\n * inline clipboard skin only — `style` attributes whose value is EXACTLY one\n * the engine itself wrote (`inlineStyles`). An author can never reach a style.\n */\nexport function engineHtmlSchema(inlineStyles: readonly string[] = []): Schema {\n const exact = inlineStyles.length\n ? [new RegExp(`^(?:${[...new Set(inlineStyles)].map((s) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"|\")})$`)]\n : [];\n const mathAttributes = Object.fromEntries(MATHML_TAGS.map((tag) => [tag, MATHML_ATTRIBUTES]));\n const printAttrs = PRINT_HTML_SCHEMA.attributes ?? {};\n const styled = (defs: AttributeList = []): AttributeList =>\n exact.length ? [...defs.filter((d) => !(Array.isArray(d) && d[0] === \"style\")), [\"style\", ...exact]] : defs;\n const attributes: Attributes = { ...printAttrs, ...mathAttributes };\n if (exact.length) {\n for (const tag of new Set([...(PRINT_HTML_SCHEMA.tagNames ?? []), \"div\", \"span\"])) {\n if (MATHML_TAGS.includes(tag)) continue;\n attributes[tag] = styled(printAttrs[tag] ?? []);\n }\n // Table cells keep their alignment AND the skin's cell style (the skin writes both into one value).\n }\n return {\n ...PRINT_HTML_SCHEMA,\n tagNames: [...new Set([...(PRINT_HTML_SCHEMA.tagNames ?? []), ...MATHML_TAGS])],\n attributes,\n };\n}\n\n/** Sanitize the engine's output (see `engineHtmlSchema`). */\nexport function sanitizeEngineHtml(html: string, inlineStyles: readonly string[] = []): string {\n return sanitizeHtmlFragment(html, engineHtmlSchema(inlineStyles));\n}\n","/**\n * Default print stylesheets for `@ai-matrx/print/markdown`.\n *\n * Two skins, ONE semantic-token vocabulary (C26 — structural CSS ships in the\n * package, the token CONTRACT is enforced, token VALUES are host-overridable):\n *\n * \"document\" — the `.matrx-*` content sheet. What `markdownToHtml` emits,\n * what the markdown→PDF lane renders, and what pastes straight\n * into a WordPress theme.\n * \"article\" — the serif AI-response sheet used by the print-window lane.\n *\n * Every colour is `var(--matrx-print-<role>, <default>)`. The inline fallback\n * is what makes the drop-in test pass in a fresh, unthemed app; the `:root`\n * block is the shipped default token-values sheet a host overrides by\n * appending its own `:root` rule (`getMarkdownStylesheet({ tokens })`).\n *\n * Both sheets carry the print hard parts — `@media print`, page-break\n * avoidance, orphans/widows, `print-color-adjust` — so no host re-derives them.\n *\n * THIS FILE IS THE SOURCE OF TRUTH. It was seeded once from the two host\n * stylesheets it replaced (matrx-frontend's WordPress sheet and its serif\n * print sheet, both deleted in the same session); there is no generator to\n * re-run and no upstream to diff against. Edit it here.\n *\n * DO NOT hand-edit colour literals back in: a hardcoded colour in package CSS\n * is a C26 violation. Add a role to the vocabulary instead.\n */\n\n/** Default token VALUES for the document skin. */\nexport const MARKDOWN_DOCUMENT_TOKENS_CSS = `:root {\n --matrx-print-accent: #667eea;\n --matrx-print-accent-2: #764ba2;\n --matrx-print-bg: #ffffff;\n --matrx-print-border: #e1e1e1;\n --matrx-print-border-muted: #d1d5db;\n --matrx-print-border-strong: #e5e5e5;\n --matrx-print-code-bg: #f5f5f5;\n --matrx-print-code-block-bg: #f8f8f8;\n --matrx-print-code-block-fg: #333;\n --matrx-print-code-fg: #d73a49;\n --matrx-print-fg: #1a1a1a;\n --matrx-print-fg-muted: #4a4a4a;\n --matrx-print-fg-subtle: #6b7280;\n --matrx-print-heading: #2a2a2a;\n --matrx-print-heading-sub: #3a3a3a;\n --matrx-print-link-fg: #374151;\n --matrx-print-link-hover-fg: #1f2937;\n --matrx-print-link-underline: #9ca3af;\n --matrx-print-quote-fg: #666;\n --matrx-print-shadow: rgba(0, 0, 0, 0.1);\n --matrx-print-shadow-strong: rgba(0, 0, 0, 0.2);\n --matrx-print-success: #2d5016;\n --matrx-print-success-2: #1e3a0f;\n --matrx-print-surface: #f8f9fa;\n --matrx-print-table-fg: #333333;\n}`;\n\n/** Default token VALUES for the article skin. */\nexport const MARKDOWN_ARTICLE_TOKENS_CSS = `:root {\n --matrx-print-accent: #2563eb;\n --matrx-print-bg: #fff;\n --matrx-print-border: #e5e7eb;\n --matrx-print-border-muted: #d1d5db;\n --matrx-print-code-bg: #f1f5f9;\n --matrx-print-fg: #1a1a1a;\n --matrx-print-fg-muted: #374151;\n --matrx-print-fg-subtle: #64748b;\n --matrx-print-heading: #111827;\n --matrx-print-ink: #0f172a;\n --matrx-print-line: #e2e8f0;\n --matrx-print-quote-bg: #f5f3ff;\n --matrx-print-quote-border: #6366f1;\n --matrx-print-surface: #f8fafc;\n --matrx-print-surface-sunken: #f3f4f6;\n --matrx-print-table-head-bg: #1e293b;\n --matrx-print-table-head-border: #334155;\n}`;\n\n/** Structural CSS for the document skin (`.matrx-*` classes). */\nexport const MARKDOWN_DOCUMENT_CSS = `/* \n * MATRX WordPress CSS - Production Ready\n * Use this CSS in your WordPress theme to style MATRX content\n * All styles are scoped to .matrx-content-container to avoid conflicts\n */\n\n/* Content Container */\n.matrx-content-container {\n max-width: 800px;\n margin: 0 auto;\n padding: 2rem;\n line-height: 1.6;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n}\n\n/* Typography */\n.matrx-h1 {\n font-size: 2.5rem;\n font-weight: 700;\n margin: 0 0 1.5rem 0;\n color: var(--matrx-print-fg, #1a1a1a);\n line-height: 1.2;\n}\n\n.matrx-h2 {\n font-size: 1.8rem;\n font-weight: 600;\n margin: 3rem 0 1rem 0 !important;\n color: var(--matrx-print-heading, #2a2a2a);\n line-height: 1.3 !important;\n border-bottom: 2px solid var(--matrx-print-border-strong, #e5e5e5);\n padding-bottom: 0.75rem !important;\n padding-top: 0 !important;\n}\n\n.matrx-h3 {\n font-size: 1.3rem;\n font-weight: 600;\n margin: 2rem 0 1rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n.matrx-h4 {\n font-size: 1.1rem;\n font-weight: 600;\n margin: 1.5rem 0 0.75rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n.matrx-h5 {\n font-size: 1rem;\n font-weight: 600;\n margin: 1.25rem 0 0.5rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n.matrx-h6 {\n font-size: 0.9rem;\n font-weight: 600;\n margin: 1rem 0 0.5rem 0;\n color: var(--matrx-print-heading-sub, #3a3a3a);\n line-height: 1.4;\n}\n\n/* Paragraphs */\n.matrx-intro {\n font-size: 1.1rem;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n margin-bottom: 2rem;\n padding: 1.5rem;\n background: var(--matrx-print-surface, #f8f9fa);\n border-left: 4px solid var(--matrx-print-border-muted, #d1d5db);\n border-radius: 0 8px 8px 0;\n}\n\n.matrx-paragraph {\n font-size: 1rem;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n margin-bottom: 1.5rem;\n text-align: justify;\n}\n\n/* Text Formatting */\n.matrx-em {\n font-style: italic;\n color: var(--matrx-print-heading, #2a2a2a);\n}\n\n.matrx-strong {\n font-weight: 600;\n color: var(--matrx-print-heading, #2a2a2a);\n}\n\n/* Links */\n.matrx-link {\n color: var(--matrx-print-link-fg, #374151);\n text-decoration: underline;\n text-decoration-color: var(--matrx-print-border-muted, #d1d5db);\n text-underline-offset: 3px;\n transition: all 0.2s ease;\n}\n\n.matrx-link:hover {\n color: var(--matrx-print-link-hover-fg, #1f2937);\n text-decoration-color: var(--matrx-print-link-underline, #9ca3af);\n}\n\n/* Images */\n.matrx-image {\n max-width: 100%;\n height: auto;\n display: block;\n margin: 1.5rem auto;\n border-radius: 8px;\n box-shadow: 0 2px 8px var(--matrx-print-shadow, rgba(0, 0, 0, 0.1));\n}\n\n/* Lists */\n.matrx-list {\n margin: 1.5rem 0;\n padding-left: 0;\n}\n\n.matrx-bullet-list {\n list-style: none;\n}\n\n.matrx-list-item {\n margin-bottom: 1rem;\n padding-left: 1.5rem;\n position: relative;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n}\n\n.matrx-list-item::before {\n content: \"•\";\n color: var(--matrx-print-fg-subtle, #6b7280);\n font-weight: bold;\n position: absolute;\n left: 0;\n top: 0;\n font-size: 1.2rem;\n}\n\n/* Numbered Lists */\n.matrx-numbered-list {\n list-style: decimal;\n padding-left: 2rem;\n}\n\n.matrx-numbered-list > .matrx-list-item {\n padding-left: 0.5rem;\n}\n\n.matrx-numbered-list > .matrx-list-item::before {\n content: none;\n /* Remove the bullet */\n}\n\n/* Nested list indentation */\n.matrx-nested-list {\n margin-top: 0.5rem;\n margin-bottom: 0.5rem;\n padding-left: 1.5rem !important;\n}\n\n/* Ensure nested lists within list items are properly indented */\n.matrx-list-item > .matrx-list {\n margin-top: 0.5rem;\n margin-bottom: 0.5rem;\n padding-left: 1.5rem;\n}\n\n/* For multiple levels of nesting, increase indentation */\n.matrx-nested-list .matrx-nested-list {\n padding-left: 1.5rem !important;\n}\n\n/* Code */\n.matrx-content-container .matrx-inline-code {\n background-color: var(--matrx-print-code-bg, #f5f5f5);\n padding: 0.125rem 0.25rem;\n border-radius: 0.25rem;\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n color: var(--matrx-print-code-fg, #d73a49);\n}\n\n.matrx-content-container .matrx-code-block {\n background-color: var(--matrx-print-code-block-bg, #f8f8f8);\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 1.5rem 0;\n overflow-x: auto;\n}\n\n.matrx-content-container .matrx-code {\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--matrx-print-code-block-fg, #333);\n}\n\n/* Blockquotes */\n.matrx-content-container .matrx-blockquote {\n border-left: 4px solid var(--matrx-print-border, #e1e1e1);\n padding-left: 1rem;\n margin: 1.5rem 0;\n font-style: italic;\n color: var(--matrx-print-quote-fg, #666);\n}\n\n/* Tables */\n.matrx-content-container .matrx-table {\n width: 100%;\n border-collapse: collapse;\n margin: 1.5rem 0;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n}\n\n.matrx-content-container .matrx-table-header {\n background-color: var(--matrx-print-surface, #f8f9fa);\n font-weight: 600;\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n text-align: left;\n}\n\n.matrx-content-container .matrx-table-cell {\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n}\n\n.matrx-content-container .matrx-table-row:nth-child(even) {\n background-color: var(--matrx-print-surface, #f8f9fa);\n}\n\n/* Images */\n.matrx-content-container .matrx-image {\n max-width: 100%;\n height: auto;\n border-radius: 0.375rem;\n margin: 1rem 0;\n}\n\n/* Horizontal Rules */\n.matrx-content-container .matrx-hr {\n border: none;\n border-top: 1px solid var(--matrx-print-border, #e1e1e1);\n margin: 2rem 0;\n}\n\n/* FAQ Styles - Clean Design */\n.matrx-faq-item {\n margin-bottom: 2rem;\n padding: 1.5rem;\n background: var(--matrx-print-surface, #f8f9fa);\n border-radius: 8px;\n border-left: 4px solid var(--matrx-print-border-muted, #d1d5db);\n}\n\n.matrx-faq-question {\n font-size: 1.2rem;\n font-weight: 600;\n margin: 0 0 0.75rem 0;\n color: var(--matrx-print-heading, #2a2a2a);\n}\n\n.matrx-faq-answer {\n margin: 0;\n color: var(--matrx-print-fg-muted, #4a4a4a);\n line-height: 1.6;\n}\n\n/* Code Blocks */\n.matrx-inline-code {\n background-color: var(--matrx-print-code-bg, #f5f5f5);\n padding: 0.125rem 0.25rem;\n border-radius: 0.25rem;\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n color: var(--matrx-print-code-fg, #d73a49);\n}\n\n.matrx-code-block {\n background-color: var(--matrx-print-code-block-bg, #f8f8f8);\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n border-radius: 0.375rem;\n padding: 1rem;\n margin: 1.5rem 0;\n overflow-x: auto;\n}\n\n.matrx-code {\n font-family: \"Monaco\", \"Menlo\", \"Ubuntu Mono\", monospace;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--matrx-print-code-block-fg, #333);\n}\n\n/* Blockquotes */\n.matrx-blockquote {\n border-left: 4px solid var(--matrx-print-border, #e1e1e1);\n padding-left: 1rem;\n margin: 1.5rem 0;\n font-style: italic;\n color: var(--matrx-print-quote-fg, #666);\n}\n\n/* Tables */\n.matrx-table {\n width: 100%;\n border-collapse: collapse;\n margin: 1.5rem 0;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-head {\n background-color: var(--matrx-print-surface, #f8f9fa);\n}\n\n.matrx-table-body {\n background-color: var(--matrx-print-bg, #ffffff);\n}\n\n.matrx-table-header {\n background-color: var(--matrx-print-surface, #f8f9fa);\n font-weight: 600;\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n text-align: left;\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-cell {\n padding: 0.75rem;\n border: 1px solid var(--matrx-print-border, #e1e1e1);\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-row {\n color: var(--matrx-print-table-fg, #333333);\n}\n\n.matrx-table-row:nth-child(even) {\n background-color: var(--matrx-print-surface, #f8f9fa);\n}\n\n/* Ensure table content doesn't inherit external colors */\n.matrx-table * {\n color: inherit;\n}\n\n/* Images */\n.matrx-image {\n max-width: 100%;\n height: auto;\n border-radius: 0.375rem;\n margin: 1rem 0;\n}\n\n/* Horizontal Rules */\n.matrx-hr {\n border: none;\n border-top: 1px solid var(--matrx-print-border, #e1e1e1);\n margin: 2rem 0;\n}\n\n/* Utility Classes */\n.matrx-div {\n margin: 0.5rem 0;\n}\n\n.matrx-span {\n /* Inherit parent styling by default */\n}\n\n/* Flashcard Styles */\n.matrx-flashcards-container {\n margin: 2rem 0;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.matrx-flashcard {\n perspective: 1000px;\n perspective-origin: center center;\n height: 200px;\n cursor: pointer;\n position: relative;\n margin-bottom: 0.5rem;\n}\n\n.matrx-flashcard-input {\n display: none;\n}\n\n.matrx-flashcard-inner {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n transition: transform 0.6s;\n transform-style: preserve-3d;\n transform-origin: center center;\n}\n\n.matrx-flashcard-input:checked ~ .matrx-flashcard-inner {\n transform: rotateY(180deg);\n}\n\n.matrx-flashcard-front,\n.matrx-flashcard-back {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n backface-visibility: hidden;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1.5rem;\n box-shadow: 0 4px 6px var(--matrx-print-shadow, rgba(0, 0, 0, 0.1));\n transition: box-shadow 0.3s ease;\n box-sizing: border-box;\n}\n\n.matrx-flashcard-front {\n background: linear-gradient(135deg, var(--matrx-print-accent, #667eea) 0%, var(--matrx-print-accent-2, #764ba2) 100%);\n color: white;\n}\n\n.matrx-flashcard-back {\n background: linear-gradient(135deg, var(--matrx-print-success, #2d5016) 0%, var(--matrx-print-success-2, #1e3a0f) 100%);\n color: white;\n transform: rotateY(180deg);\n}\n\n.matrx-flashcard:hover .matrx-flashcard-front,\n.matrx-flashcard:hover .matrx-flashcard-back {\n box-shadow: 0 8px 16px var(--matrx-print-shadow-strong, rgba(0, 0, 0, 0.2));\n}\n\n.matrx-flashcard-content {\n text-align: center;\n font-size: 1.3rem;\n line-height: 1.8;\n max-height: 100%;\n width: 100%;\n overflow: hidden;\n}\n\n.matrx-flashcard-label {\n cursor: pointer;\n user-select: none;\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 10;\n}\n\n.matrx-flashcard-indicator {\n position: absolute;\n bottom: 12px;\n right: 16px;\n font-size: 0.75rem;\n opacity: 0.8;\n font-weight: 500;\n letter-spacing: 0.5px;\n}\n\n/* Responsive adjustments */\n@media (max-width: 768px) {\n .matrx-flashcard {\n height: 180px;\n }\n\n .matrx-flashcard-content {\n font-size: 1rem;\n }\n\n .matrx-flashcard-front,\n .matrx-flashcard-back {\n padding: 1.25rem;\n }\n}\n\n@media (max-width: 480px) {\n .matrx-flashcard {\n height: 160px;\n }\n\n .matrx-flashcard-content {\n font-size: 0.95rem;\n }\n\n .matrx-flashcard-front,\n .matrx-flashcard-back {\n padding: 1rem;\n }\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .matrx-content-container {\n padding: 1rem;\n }\n\n .matrx-h1 {\n font-size: 2rem;\n }\n\n .matrx-h2 {\n font-size: 1.5rem;\n margin: 2rem 0 1rem 0 !important;\n }\n\n .matrx-h3 {\n font-size: 1.2rem;\n }\n\n .matrx-h4 {\n font-size: 1.05rem;\n }\n\n .matrx-h5 {\n font-size: 0.95rem;\n }\n\n .matrx-h6 {\n font-size: 0.9rem;\n }\n\n .matrx-intro {\n padding: 1rem;\n font-size: 1rem;\n }\n\n .matrx-faq-question {\n font-size: 1.1rem;\n margin: 1.5rem 0 0.75rem 0;\n }\n\n .matrx-paragraph {\n text-align: left;\n }\n\n .matrx-table {\n font-size: 0.875rem;\n }\n\n .matrx-table-header,\n .matrx-table-cell {\n padding: 0.5rem;\n }\n\n .matrx-code-block {\n padding: 0.75rem;\n margin: 1rem 0;\n }\n}\n\n@media (max-width: 480px) {\n .matrx-content-container {\n padding: 0.75rem;\n }\n\n .matrx-h1 {\n font-size: 1.75rem;\n margin-bottom: 1rem;\n }\n\n .matrx-h2 {\n font-size: 1.3rem;\n margin: 1.5rem 0 0.75rem 0 !important;\n }\n\n .matrx-h3 {\n font-size: 1.1rem;\n margin: 1.5rem 0 0.75rem 0;\n }\n\n .matrx-h4 {\n font-size: 1rem;\n margin: 1.25rem 0 0.5rem 0;\n }\n\n .matrx-h5 {\n font-size: 0.9rem;\n margin: 1rem 0 0.5rem 0;\n }\n\n .matrx-h6 {\n font-size: 0.85rem;\n margin: 1rem 0 0.5rem 0;\n }\n\n .matrx-intro {\n padding: 0.75rem;\n margin-bottom: 1.5rem;\n }\n\n .matrx-paragraph {\n margin-bottom: 1.25rem;\n }\n\n .matrx-list-item {\n margin-bottom: 0.75rem;\n }\n\n .matrx-table-header,\n .matrx-table-cell {\n padding: 0.375rem;\n }\n\n .matrx-code-block {\n padding: 0.5rem;\n font-size: 0.8rem;\n }\n\n .matrx-inline-code {\n font-size: 0.8rem;\n }\n}\n/* Print / PDF hard parts — added by @ai-matrx/print 0.3.0. */\n@media print {\n .matrx-content-container {\n max-width: 100%;\n padding: 0;\n }\n\n .matrx-h1,\n .matrx-h2,\n .matrx-h3,\n .matrx-h4,\n .matrx-h5,\n .matrx-h6 {\n page-break-after: avoid;\n break-after: avoid;\n }\n\n .matrx-paragraph,\n .matrx-intro {\n orphans: 3;\n widows: 3;\n }\n\n .matrx-table,\n .matrx-code-block,\n .matrx-blockquote,\n .matrx-list-item,\n .matrx-image,\n .matrx-faq-item,\n .matrx-flashcard {\n page-break-inside: avoid;\n break-inside: avoid;\n }\n\n .matrx-table-header,\n .matrx-code-block,\n .matrx-blockquote,\n .matrx-flashcard {\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n }\n\n .matrx-link {\n text-decoration: underline;\n }\n}\n`;\n\n/** Structural CSS for the article skin (bare tags + `.print-*` classes). */\nexport const MARKDOWN_ARTICLE_CSS = `*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}body{font-family:Georgia,'Times New Roman',serif;font-size:11pt;line-height:1.7;color:var(--matrx-print-fg, #1a1a1a);background:var(--matrx-print-bg, #fff);max-width:780px;margin:0 auto;padding:32px 40px}.print-actions{display:flex;gap:10px;margin-bottom:28px;padding-bottom:20px;border-bottom:2px solid var(--matrx-print-border, #e5e7eb)}.print-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 20px;border-radius:8px;font-size:13px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;cursor:pointer;border:none;font-weight:600;transition:opacity .15s}.print-btn:hover{opacity:.85}.print-btn-primary{background:var(--matrx-print-accent, #2563eb);color:var(--matrx-print-bg, #fff)}.print-btn-secondary{background:var(--matrx-print-surface-sunken, #f3f4f6);color:var(--matrx-print-fg-muted, #374151);border:1px solid var(--matrx-print-border-muted, #d1d5db)}h1,h2,h3,h4,h5,h6{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Helvetica Neue',sans-serif;font-weight:700;line-height:1.3;color:var(--matrx-print-heading, #111827);margin-top:1.6em;margin-bottom:.5em;page-break-after:avoid}h1{font-size:24pt;border-bottom:2px solid var(--matrx-print-border, #e5e7eb);padding-bottom:.3em;margin-top:0}h2{font-size:18pt;border-bottom:1px solid var(--matrx-print-border, #e5e7eb);padding-bottom:.2em}h3{font-size:14pt}p{margin-bottom:.9em;orphans:3;widows:3}a{color:var(--matrx-print-accent, #2563eb);text-decoration:underline;word-break:break-word}ul,ol{margin:.6em 0 .9em 0;padding-left:1.8em}li{margin-bottom:.3em;page-break-inside:avoid}blockquote{margin:1em 0;padding:.7em 1em .7em 1.2em;border-left:4px solid var(--matrx-print-quote-border, #6366f1);background:var(--matrx-print-quote-bg, #f5f3ff);border-radius:0 6px 6px 0;color:var(--matrx-print-fg-muted, #374151);font-style:italic;page-break-inside:avoid}.inline-code{font-family:'JetBrains Mono','Fira Code',monospace;font-size:9.5pt;background:var(--matrx-print-code-bg, #f1f5f9);color:var(--matrx-print-ink, #0f172a);padding:1px 5px;border-radius:4px;border:1px solid var(--matrx-print-line, #e2e8f0)}.code-block-wrapper{position:relative;margin:1em 0 1.2em;border-radius:8px;overflow:hidden;border:1px solid var(--matrx-print-line, #e2e8f0);page-break-inside:avoid}.code-lang{display:block;font-family:-apple-system,sans-serif;font-size:9pt;font-weight:600;color:var(--matrx-print-fg-subtle, #64748b);background:var(--matrx-print-surface, #f8fafc);padding:4px 12px;border-bottom:1px solid var(--matrx-print-line, #e2e8f0);text-transform:uppercase;letter-spacing:.05em}.code-block{margin:0;padding:14px 16px;background:var(--matrx-print-ink, #0f172a);color:var(--matrx-print-line, #e2e8f0);font-family:'JetBrains Mono',monospace;font-size:9pt;line-height:1.6;overflow-x:auto;white-space:pre;tab-size:2}.print-table{width:100%;border-collapse:collapse;margin:1em 0 1.2em;font-size:10pt;page-break-inside:avoid}.print-table th{background:var(--matrx-print-table-head-bg, #1e293b);color:var(--matrx-print-surface, #f8fafc);font-family:-apple-system,sans-serif;font-weight:600;font-size:9.5pt;text-align:left;padding:8px 12px;border:1px solid var(--matrx-print-table-head-border, #334155)}.print-table td{padding:7px 12px;border:1px solid var(--matrx-print-line, #e2e8f0);vertical-align:top}.print-table tr:nth-child(even) td{background:var(--matrx-print-surface, #f8fafc)}hr{border:none;border-top:2px solid var(--matrx-print-border, #e5e7eb);margin:1.5em 0}.print-figure{margin:1em 0 1.2em;page-break-inside:avoid}.print-image{display:block;max-width:100%;height:auto;margin:0 auto;border-radius:6px}.print-figure figcaption{margin-top:.4em;text-align:center;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:9.5pt;color:var(--matrx-print-fg-subtle, #64748b)}@media print{body{max-width:100%;padding:0;font-size:10.5pt}.print-actions{display:none!important}.code-block,.print-table th,blockquote{color-adjust:exact;-webkit-print-color-adjust:exact;print-color-adjust:exact}a[href]::after{content:\" (\"attr(href)\")\";font-size:9pt;color:var(--matrx-print-fg-subtle, #64748b)}a[href^=\"#\"]::after,a[href^=\"javascript:\"]::after{content:\"\"}h1,h2,h3{page-break-after:avoid}pre,blockquote,table{page-break-inside:avoid}}`;\n\n/**\n * Rules BOTH skins share — appended after the skin sheet by\n * `getMarkdownStylesheet`. Everything the two converters emit identically\n * (page breaks, artifact panels, task boxes, accent/heavy rules, strikethrough,\n * printed flashcard pairs) is styled once here so the skins cannot drift, plus\n * the print hard parts every printed page needs regardless of skin.\n *\n * Page break: on screen a dashed rule labelled \"Page break\" with generous\n * padding — the same idea the chat preview draws; on paper a zero-height\n * element that forces the next content onto a new page.\n */\nexport const MARKDOWN_SHARED_CSS = `\n.matrx-page-break {\n position: relative;\n clear: both;\n height: 0;\n margin: 2.75rem 0;\n border: 0;\n border-top: 2px dashed var(--matrx-print-border-muted, #d1d5db);\n}\n.matrx-page-break::after {\n content: attr(data-label);\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n padding: 0 0.75rem;\n background: var(--matrx-print-bg, #ffffff);\n color: var(--matrx-print-fg-subtle, #6b7280);\n font: 600 10px/1.6 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n white-space: nowrap;\n}\n/* GFM footnotes: numbered superscript references, notes gathered at the end. */\n.print-footnote-ref {\n font-size: 0.75em;\n line-height: 0;\n vertical-align: super;\n}\n.print-footnote-ref a {\n text-decoration: none;\n}\n.print-footnotes {\n margin-top: 2em;\n font-size: 0.9em;\n color: var(--matrx-print-fg-subtle, #6b7280);\n page-break-inside: avoid;\n}\n.print-footnotes hr {\n width: 33%;\n margin: 0 0 0.75em;\n border: 0;\n border-top: 1px solid var(--matrx-print-border-muted, #d1d5db);\n}\n.print-footnotes ol {\n margin: 0;\n padding-left: 1.6em;\n}\n.print-footnotes li p {\n margin: 0 0 0.35em;\n}\n\n.matrx-artifact {\n margin: 1.25em 0;\n border: 1px solid var(--matrx-print-border, #e5e7eb);\n border-radius: 8px;\n overflow: hidden;\n}\n.matrx-artifact-header {\n display: flex;\n align-items: baseline;\n gap: 0.6em;\n padding: 0.45em 0.9em;\n background: var(--matrx-print-surface, #f8fafc);\n border-bottom: 1px solid var(--matrx-print-border, #e5e7eb);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n.matrx-artifact-kind {\n font-size: 0.72em;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--matrx-print-accent, #2563eb);\n}\n.matrx-artifact-title {\n font-weight: 600;\n color: var(--matrx-print-fg, #1a1a1a);\n}\n.matrx-artifact-body {\n padding: 0.8em 0.9em;\n}\n.matrx-artifact-body > :first-child {\n margin-top: 0;\n}\n.matrx-artifact-body > :last-child {\n margin-bottom: 0;\n}\n/* The panel's kind chip already names these languages. */\n.matrx-artifact:is([data-artifact-type=\"html\"], [data-artifact-type=\"react\"], [data-artifact-type=\"svg\"], [data-artifact-type=\"mermaid\"], [data-artifact-type=\"diff\"], [data-artifact-type=\"iframe\"]) .code-lang {\n display: none;\n}\n.matrx-artifact-empty {\n margin: 0;\n font-style: italic;\n color: var(--matrx-print-fg-subtle, #6b7280);\n}\n\nli.matrx-task-item {\n list-style: none;\n}\n/* The box IS the marker — the document skin's custom bullet must not sit on it. */\n:is(.matrx-content-container, .content) li.matrx-task-item::before {\n content: none;\n}\n.matrx-task-box {\n position: relative;\n display: inline-block;\n width: 0.9em;\n height: 0.9em;\n margin: 0 0.5em 0 -1.35em;\n border: 1.5px solid var(--matrx-print-fg-muted, #4a4a4a);\n border-radius: 3px;\n vertical-align: -0.1em;\n}\n.matrx-task-box.is-checked::after {\n content: \"\\\\2713\";\n position: absolute;\n inset: 0;\n font-size: 0.8em;\n font-weight: 700;\n line-height: 1.1em;\n text-align: center;\n color: var(--matrx-print-fg, #1a1a1a);\n}\n\n:is(.matrx-content-container, .content) del {\n color: var(--matrx-print-fg-muted, #4a4a4a);\n text-decoration: line-through;\n}\n:is(.matrx-content-container, .content) hr.matrx-hr-accent {\n height: 2px;\n margin: 1.75em 0;\n border: 0;\n border-radius: 2px;\n background: var(--matrx-print-accent, #2563eb);\n opacity: 0.6;\n}\n:is(.matrx-content-container, .content) hr.matrx-hr-heavy {\n height: 4px;\n margin: 2.25em 0;\n border: 0;\n border-radius: 4px;\n background: var(--matrx-print-accent, #2563eb);\n}\n\n.matrx-flashcards-print {\n margin: 1em 0 1.2em;\n}\n.matrx-fc {\n display: grid;\n grid-template-columns: 1fr 1fr;\n margin-bottom: 0.5em;\n border: 1px solid var(--matrx-print-border, #e5e7eb);\n border-radius: 6px;\n}\n.matrx-fc-front,\n.matrx-fc-back {\n padding: 0.6em 0.8em;\n}\n.matrx-fc-front {\n font-weight: 600;\n border-right: 1px dashed var(--matrx-print-border-muted, #d1d5db);\n}\n.matrx-fc-num {\n display: inline-block;\n min-width: 1.6em;\n font-size: 0.85em;\n color: var(--matrx-print-fg-subtle, #6b7280);\n}\n\n@media print {\n .matrx-page-break {\n margin: 0;\n border: 0;\n break-after: page;\n page-break-after: always;\n }\n .matrx-page-break::after {\n content: none;\n }\n .matrx-page-break + * {\n margin-top: 0;\n }\n\n /* Scoped to the rendered markdown (.matrx-content-container = document\n skin, .content = print window) so a host page that carries this sheet\n — a WordPress theme — keeps its own print rules everywhere else. */\n\n /* Long tables flow across pages: the header row repeats, rows never split. */\n :is(.matrx-content-container, .content) thead {\n display: table-header-group;\n }\n :is(.matrx-content-container, .content) :is(tr, img, figure),\n .matrx-fc,\n .matrx-task-item {\n break-inside: avoid;\n page-break-inside: avoid;\n }\n :is(.matrx-content-container, .content) :is(h1, h2, h3, h4, h5, h6),\n .matrx-artifact-header {\n break-after: avoid;\n page-break-after: avoid;\n break-inside: avoid;\n }\n\n /* Paper cannot scroll: long code lines wrap instead of being cut off. */\n :is(.matrx-content-container, .content) :is(pre, pre code) {\n white-space: pre-wrap !important;\n overflow-wrap: anywhere;\n overflow: visible !important;\n }\n .code-block-wrapper,\n .matrx-artifact {\n overflow: visible;\n }\n :is(.matrx-content-container, .content) img {\n max-width: 100% !important;\n }\n\n /* A PICTURE (a drawn diagram, a captured page) never outgrows one printed\n page: taller than the page, it was pushed whole to the next page and\n left its panel header alone at the foot of the last one, then printed a\n page with nothing but the page number (2026-10-08). Capped to fit beside\n its header on a portrait page, scaled, never cropped; a panel that holds\n a picture moves as one, header with it. */\n :is(.matrx-fence-picture, .matrx-print-image) img {\n display: block;\n width: auto !important;\n max-width: 100% !important;\n height: auto !important;\n max-height: 8.25in;\n object-fit: contain;\n margin: 0 auto;\n }\n :is(.matrx-fence-picture, .matrx-print-image),\n .matrx-artifact:has(.matrx-fence-picture, .matrx-print-image) {\n break-inside: avoid;\n page-break-inside: avoid;\n }\n /* A forced break with nothing after it (or right after another) would\n print an empty page. */\n .matrx-page-break:last-child,\n .matrx-page-break:has(+ .matrx-page-break) {\n break-after: auto;\n page-break-after: auto;\n }\n\n .matrx-artifact-header,\n .matrx-task-box {\n -webkit-print-color-adjust: exact;\n print-color-adjust: exact;\n }\n\n a.matrx-link[href^=\"http\"]::after {\n content: \" (\" attr(href) \")\";\n font-size: 0.85em;\n color: var(--matrx-print-fg-subtle, #6b7280);\n overflow-wrap: anywhere;\n }\n}\n`;\n\n/**\n * Page geometry + page numbers (\"3 / 12\", bottom centre). Part of the default\n * sheet; `getMarkdownStylesheet({ pageNumbers: false })` leaves it out.\n * Browsers without `@page` margin boxes ignore the numbering and keep the\n * margins.\n */\nexport const MARKDOWN_PAGE_CSS = `\n@page {\n margin: 0.6in 0.65in 0.7in;\n @bottom-center {\n content: counter(page) \" / \" counter(pages);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n font-size: 8pt;\n color: var(--matrx-print-fg-subtle, #6b7280);\n }\n}\n`;\n\n/** `@page` geometry without numbering. */\nexport const MARKDOWN_PAGE_PLAIN_CSS = `\n@page {\n margin: 0.6in 0.65in 0.7in;\n}\n`;\n","/**\n * @ai-matrx/print/directives — the ONE definition of the print directives\n * markdown can carry, shared by every surface that reads markdown.\n *\n * Why a separate, zero-dependency entry: the directive syntax has three\n * readers — this package's two converters (print window / PDF / HTML page),\n * the chat renderer's remark plugin (the on-screen preview), and any editor\n * that inserts one. If each re-derived the regex they would drift the first\n * time someone added an alias. `/markdown` carries ~20KB of stylesheet, so the\n * chat bundle imports the syntax from here instead and pays nothing.\n *\n * ── Page break ────────────────────────────────────────────────────────────\n * A line that is exactly one of these (case-insensitive, surrounding\n * whitespace ignored) forces the next content onto a new printed page:\n *\n * <!-- pagebreak --> ← canonical; invisible in any other renderer\n * <!-- page-break --> <!-- page break --> <!-- newpage -->\n * \\pagebreak \\newpage ← LaTeX / pandoc habit\n * <div style=\"page-break-after: always\"></div> ← Typora / VS Code habit\n * (also `page-break-before`, `break-after: page`, `break-before: page`,\n * and `<div class=\"page-break\"></div>` / `class=\"pagebreak\"`)\n *\n * On screen every surface draws the same idea: a dashed rule labelled\n * \"Page break\" with generous padding. In print it is a zero-height element\n * with `break-after: page`.\n */\n\n/** The form editors insert and docs recommend. */\nexport const PAGE_BREAK_MARKDOWN = \"<!-- pagebreak -->\";\n\n/** The class every surface puts on a rendered page break. */\nexport const PAGE_BREAK_CLASS = \"matrx-page-break\";\n\n/** Screen label for a rendered page break. */\nexport const PAGE_BREAK_LABEL = \"Page break\";\n\nconst COMMENT_FORM = /^<!--\\s*(?:page[\\s_-]?break|new[\\s_-]?page)\\s*-->$/i;\nconst LATEX_FORM = /^\\\\(?:pagebreak|newpage|clearpage)\\s*(?:\\{\\s*\\})?$/i;\nconst DIV_FORM = /^<div\\b([^>]*)>\\s*<\\/div>$|^<div\\b([^>]*)\\/>$/i;\nconst DIV_BREAK_ATTR =\n /(?:page-break-(?:after|before)\\s*:\\s*always|break-(?:after|before)\\s*:\\s*page|class\\s*=\\s*[\"'](?:[^\"']*\\s)?page-?break(?:\\s[^\"']*)?[\"'])/i;\n\n/** Columns of leading indentation (tab = 4, NBSP counts as a space). */\nfunction indentColumns(line: string): number {\n let cols = 0;\n for (const ch of line) {\n if (ch === \" \" || ch === \"\\u00A0\") cols += 1;\n else if (ch === \"\\t\") cols += 4;\n else break;\n }\n return cols;\n}\n\n/**\n * Is this single line a page-break directive? Indented four or more columns\n * it is not — in markdown that is a code block, and every surface must agree.\n */\nexport function isPageBreakLine(line: string): boolean {\n if (indentColumns(line) >= 4) return false;\n const t = line.trim();\n if (t.length < 8 || t.length > 200) return false;\n if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;\n const div = DIV_FORM.exec(t);\n if (!div) return false;\n return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? \"\");\n}\n\n// ── Section break ─────────────────────────────────────────────────────────\n//\n// A section break is a page break that can also change the page layout for\n// everything after it (Word's \"Section break (next page)\"):\n//\n// <!-- section --> ← new page, same layout\n// <!-- section: landscape --> ← new page, landscape from here on\n// <!-- section landscape columns=2 --> ← landscape AND two columns\n// <!-- section portrait columns=1 --> ← back to the default layout\n//\n// Recognised words: `landscape` / `portrait`, `columns=N` (or `cols=N`,\n// `N columns`, 1–3). Unknown words are ignored, never an error — the break\n// still happens. Every page-break reader treats a section break as a break.\n\n/** The form editors insert for a plain section break. */\nexport const SECTION_BREAK_MARKDOWN = \"<!-- section -->\";\n\nexport interface SectionBreakDirective {\n /** Absent = inherit the document's orientation. */\n orientation?: \"portrait\" | \"landscape\";\n /** Absent = inherit the document's column count. 1–3. */\n columns?: 1 | 2 | 3;\n}\n\nconst SECTION_FORM = /^<!--\\s*section(?:[\\s_-]?break)?\\b\\s*:?\\s*([^>]*?)\\s*-->$/i;\n\n/** Parse a section-break line; `null` when the line is not one. */\nexport function parseSectionBreakLine(line: string): SectionBreakDirective | null {\n if (indentColumns(line) >= 4) return null;\n const m = SECTION_FORM.exec(line.trim());\n if (!m) return null;\n const words = (m[1] ?? \"\").toLowerCase();\n const out: SectionBreakDirective = {};\n if (/\\blandscape\\b/.test(words)) out.orientation = \"landscape\";\n else if (/\\bportrait\\b/.test(words)) out.orientation = \"portrait\";\n const cols = /\\b(?:columns?|cols?)\\s*[=:]\\s*(\\d)\\b/.exec(words) ?? /\\b(\\d)\\s*[-\\s]?columns?\\b/.exec(words);\n if (cols) {\n const n = Math.min(3, Math.max(1, Number(cols[1])));\n out.columns = n as 1 | 2 | 3;\n }\n return out;\n}\n\n// ── Table of contents / bibliography placement ───────────────────────────\n//\n// `[[toc]]`, `[toc]`, `[TOC]`, `${toc}`, `<!-- toc -->`, `\\tableofcontents`\n// put the table of contents at that spot (otherwise frontmatter `toc: true`\n// puts it at the start). `<!-- bibliography -->`, `[[bibliography]]`,\n// `\\printbibliography` put the reference list there (otherwise it closes the\n// document whenever something is cited).\n\nexport const TOC_MARKDOWN = \"[[toc]]\";\nexport const BIBLIOGRAPHY_MARKDOWN = \"<!-- bibliography -->\";\n\nconst TOC_FORM = /^(?:\\[\\[\\s*toc\\s*\\]\\]|\\[toc\\]|\\$\\{toc\\}|<!--\\s*toc\\s*-->|\\\\tableofcontents)$/i;\nconst BIBLIOGRAPHY_FORM =\n /^(?:\\[\\[\\s*(?:bibliography|references)\\s*\\]\\]|<!--\\s*(?:bibliography|references)\\s*-->|\\\\printbibliography|\\\\bibliography\\{[^}]*\\})$/i;\n\nexport function isTocLine(line: string): boolean {\n return indentColumns(line) < 4 && TOC_FORM.test(line.trim());\n}\n\nexport function isBibliographyLine(line: string): boolean {\n return indentColumns(line) < 4 && BIBLIOGRAPHY_FORM.test(line.trim());\n}\n\n/** Any directive that ends the current printed page (page or section break). */\nexport function isBreakLine(line: string): boolean {\n return isPageBreakLine(line) || parseSectionBreakLine(line) !== null;\n}\n\n/**\n * Split markdown into the pieces that belong on separate pages. Fence-aware:\n * a directive inside a ``` / ~~~ code block is code, not a break. Always\n * returns at least one piece; empty pieces (two breaks in a row, a break at\n * the very start or end) are kept out so no blank page is produced.\n */\nexport function splitAtPageBreaks(markdown: string): string[] {\n const lines = markdown.split(/\\r?\\n/);\n const pieces: string[] = [];\n let current: string[] = [];\n let fence: string | null = null;\n\n for (const line of lines) {\n // Any indent — the converters accept list-nested fences, so must this.\n const fenceMatch = /^\\s*(`{3,}|~{3,})/.exec(line);\n if (fenceMatch) {\n const marker = fenceMatch[1] as string;\n if (fence === null) fence = marker;\n else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;\n } else if (fence === null && isBreakLine(line)) {\n if (current.join(\"\\n\").trim()) pieces.push(current.join(\"\\n\"));\n current = [];\n continue;\n }\n current.push(line);\n }\n if (current.join(\"\\n\").trim() || pieces.length === 0) pieces.push(current.join(\"\\n\"));\n return pieces;\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4qB9B,IAAM,uBAAuB;AAa7B,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["import_html_escape","import_source","import_html_escape","katex","open","cellAlign"]}