` (inline) and
+// `` (display) holding the raw TeX, and
+// `rehype-katex` renders any element carrying that class. The default sanitize
+// schema already keeps `language-*` classes on ``, so the wrappers survive
+// sanitization untouched — and `rehype-katex` runs LAST, after sanitize, so KaTeX
+// (which uses `trust: false` and self-escapes its TeX input) emits its rendered
+// output without it being re-sanitized.
+//
+// Security-critical order: raw HTML must become nodes (rehypeRaw) before
+// sanitization can strip unsafe elements, attributes, and URLs.
const MARKDOWN_HTML_REHYPE_PLUGINS: MarkdownRehypePlugins = [
rehypeRaw,
rehypeSanitize,
+ rehypeKatex,
];
+// No raw HTML means nothing untrusted to sanitize, so KaTeX renders straight
+// from the `remark-math` wrappers.
+const MARKDOWN_MATH_REHYPE_PLUGINS: MarkdownRehypePlugins = [rehypeKatex];
+
function areMarkdownAbsoluteLocalFileLinkRoutingsEqual({
next,
previous,
@@ -1176,6 +1192,11 @@ function MarkdownPreviewComponent({
const remarkPlugins = useMemo(
() => [
remarkGfm,
+ // `remark-math` with single-dollar math left ON (the default), matching
+ // GitHub: `$x$` is inline and `$$x$$` is block. The known trade-off is that
+ // a line with two unescaped `$` (e.g. "$5 to $10", "$HOME and $PATH") parses
+ // the span between them as math; authors escape a literal dollar with `\$`.
+ remarkMath,
...(threadMentions !== undefined || promptMentions !== undefined
? [remarkBreaks]
: []),
@@ -1208,7 +1229,11 @@ function MarkdownPreviewComponent({
) : null}