mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-16 19:29:26 +02:00
MUL-3267: fix(markdown): disable single-dollar inline math in web renderer
remark-math defaults to singleDollarTextMath: true, so any paragraph containing two dollar amounts (e.g. "costs $120/mo (~$85 net)") has the text between them parsed as inline TeX and rendered by KaTeX in an italic math font, with ~ treated as a non-breaking space. Disable single-dollar parsing in both web render paths, matching GitHub's behavior; explicit $$...$$ math still renders. Co-authored-by: Matt Voska <voska@users.noreply.github.com>
This commit is contained in:
@@ -445,7 +445,11 @@ export function Markdown({
|
||||
return (
|
||||
<div className={cn('markdown-content break-words', className)}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkMath, remarkBreaks, [remarkGfm, { singleTilde: false }]]}
|
||||
remarkPlugins={[
|
||||
[remarkMath, { singleDollarTextMath: false }],
|
||||
remarkBreaks,
|
||||
[remarkGfm, { singleTilde: false }],
|
||||
]}
|
||||
rehypePlugins={[rehypeRaw, [rehypeSanitize, sanitizeSchema], rehypeKatex]}
|
||||
urlTransform={urlTransform}
|
||||
components={components}
|
||||
|
||||
34
packages/views/editor/readonly-content-math.test.tsx
Normal file
34
packages/views/editor/readonly-content-math.test.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { Markdown } from "@multica/ui/markdown";
|
||||
import { ReadonlyContent } from "./readonly-content";
|
||||
|
||||
// Prose with two dollar amounts and `~` (approximately) markers. With
|
||||
// remark-math's default single-dollar parsing, everything between the two
|
||||
// `$` signs is swallowed into a KaTeX inline-math span and rendered in an
|
||||
// italic math font, with `~` treated as a TeX non-breaking space.
|
||||
const FINANCE_TEXT =
|
||||
"Revenue ≈ $120/mo gross (~$85 net of fees), 12 active subscriptions";
|
||||
|
||||
describe("dollar amounts in markdown", () => {
|
||||
it("Markdown renders $ amounts as plain text, not inline math", () => {
|
||||
const { container } = render(<Markdown>{FINANCE_TEXT}</Markdown>);
|
||||
expect(container.querySelector(".katex")).toBeNull();
|
||||
expect(container.textContent).toContain(
|
||||
"$120/mo gross (~$85 net of fees)",
|
||||
);
|
||||
});
|
||||
|
||||
it("ReadonlyContent renders $ amounts as plain text, not inline math", () => {
|
||||
const { container } = render(<ReadonlyContent content={FINANCE_TEXT} />);
|
||||
expect(container.querySelector(".katex")).toBeNull();
|
||||
expect(container.textContent).toContain(
|
||||
"$120/mo gross (~$85 net of fees)",
|
||||
);
|
||||
});
|
||||
|
||||
it("still renders explicit $$ display math", () => {
|
||||
const { container } = render(<Markdown>{"$$\nE = mc^2\n$$"}</Markdown>);
|
||||
expect(container.querySelector(".katex")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -92,7 +92,7 @@ describe("ReadonlyContent math rendering", () => {
|
||||
const { container } = render(
|
||||
<ReadonlyContent
|
||||
content={[
|
||||
"Inline math: $E = mc^2$",
|
||||
"Inline math: $$E = mc^2$$",
|
||||
"",
|
||||
"$$",
|
||||
"\\int_0^1 x^2 \\, dx",
|
||||
|
||||
@@ -375,7 +375,11 @@ export const ReadonlyContent = memo(function ReadonlyContent({
|
||||
<AttachmentDownloadProvider attachments={attachments}>
|
||||
<div ref={wrapperRef} className={cn("rich-text-editor readonly text-sm", className)}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkMath, remarkBreaks, [remarkGfm, { singleTilde: false }]]}
|
||||
remarkPlugins={[
|
||||
[remarkMath, { singleDollarTextMath: false }],
|
||||
remarkBreaks,
|
||||
[remarkGfm, { singleTilde: false }],
|
||||
]}
|
||||
rehypePlugins={[rehypeRaw, [rehypeSanitize, sanitizeSchema], rehypeKatex]}
|
||||
urlTransform={urlTransform}
|
||||
components={components}
|
||||
|
||||
Reference in New Issue
Block a user