fix(ios): use shared TipTap mathematics extensions
Drop the duplicated iOS math tokenizer. Formula Markdown now round-trips against the web codec, including currency dollar pairs.
This commit is contained in:
1 parent
9d4d496823
commit
77d9f9c6aa
3 files changed
+32
-59
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
MERGE_DIVIDER_NODE_TYPE,
|
||||
MergeDivider as SharedMergeDivider,
|
||||
} from "@edgeever/shared";
|
||||
import { createEdgeEverMathematics } from "@edgeever/shared/mathematics";
|
||||
import { createIosImageGallery, ImageGallery, MergeDivider } from "./document-nodes.ts";
|
||||
|
||||
const galleryDoc = {
|
||||
@@ -30,6 +31,7 @@ const iosMarkdownManager = new MarkdownManager({
|
||||
StarterKit.configure({ codeBlock: false, link: false }),
|
||||
Image,
|
||||
MergeDivider,
|
||||
...createEdgeEverMathematics(),
|
||||
createIosImageGallery(() => "en-US"),
|
||||
Markdown.configure({
|
||||
markedOptions: { gfm: true },
|
||||
@@ -81,4 +83,33 @@ describe("iOS document nodes share the web schema", () => {
|
||||
});
|
||||
expect(persisted.content[0].content.map((node) => node.attrs.src)).toEqual(["/one.png", "/two.png"]);
|
||||
});
|
||||
|
||||
test("parses inline and block math the same as the web codec", () => {
|
||||
const markdown = "Euler: $e^{i\\pi}+1=0$.\n\n$$\n\\frac{a}{b}\n$$";
|
||||
const webDoc = markdownToDoc(markdown);
|
||||
const iosDoc = iosMarkdownManager.parse(markdown);
|
||||
|
||||
expect(webDoc.content[0]?.content?.[1]).toMatchObject({
|
||||
type: "inlineMath",
|
||||
attrs: { latex: "e^{i\\pi}+1=0" },
|
||||
});
|
||||
expect(webDoc.content[1]).toMatchObject({
|
||||
type: "blockMath",
|
||||
attrs: { latex: "\\frac{a}{b}" },
|
||||
});
|
||||
expect(iosDoc.content.map((node) => node.type)).toEqual(webDoc.content.map((node) => node.type));
|
||||
expect(iosDoc.content[0]?.content?.[1]).toMatchObject(webDoc.content[0]?.content?.[1]);
|
||||
expect(iosDoc.content[1]).toMatchObject(webDoc.content[1]);
|
||||
expect(docToMarkdown(iosDoc)).toBe(docToMarkdown(webDoc));
|
||||
});
|
||||
|
||||
test("keeps currency dollar pairs as literal text like the web codec", () => {
|
||||
const markdown = "Price: $100$";
|
||||
const webDoc = markdownToDoc(markdown);
|
||||
const iosDoc = iosMarkdownManager.parse(markdown);
|
||||
|
||||
expect(webDoc.content[0]?.content?.some((node) => node.type === "inlineMath")).toBe(false);
|
||||
expect(iosDoc.content[0]?.content?.some((node) => node.type === "inlineMath")).toBe(false);
|
||||
expect(docToMarkdown(iosDoc)).toBe(docToMarkdown(webDoc));
|
||||
});
|
||||
});
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
buildNoteImageCardMarkup,
|
||||
generateCardCss,
|
||||
} from "@edgeever/shared/note-image-card";
|
||||
import { createEdgeEverMathematics } from "./mathematics";
|
||||
import { createEdgeEverMathematics } from "@edgeever/shared/mathematics";
|
||||
import { createIosImageGallery, MergeDivider } from "./document-nodes";
|
||||
import { createImageInsertTransaction, groupUploadedImages, NATIVE_IMAGE_GALLERY_CSS } from "@edgeever/shared/native-image-gallery";
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { BlockMath, InlineMath } from "@tiptap/extension-mathematics";
|
||||
|
||||
const isEscaped = (source: string, index: number) => {
|
||||
let backslashCount = 0;
|
||||
for (let cursor = index - 1; cursor >= 0 && source[cursor] === "\\"; cursor -= 1) {
|
||||
backslashCount += 1;
|
||||
}
|
||||
return backslashCount % 2 === 1;
|
||||
};
|
||||
|
||||
const findClosingDelimiter = (source: string, delimiter: "$" | "$$", from: number) => {
|
||||
for (let index = from; index <= source.length - delimiter.length; index += 1) {
|
||||
if (source.startsWith(delimiter, index) && !isEscaped(source, index)) return index;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const EdgeEverInlineMath = InlineMath.extend({
|
||||
markdownTokenizer: {
|
||||
name: "inlineMath",
|
||||
level: "inline",
|
||||
start: (source: string) => source.indexOf("$"),
|
||||
tokenize: (source: string) => {
|
||||
if (!source.startsWith("$") || source.startsWith("$$")) return undefined;
|
||||
const closingIndex = findClosingDelimiter(source, "$", 1);
|
||||
if (closingIndex < 0 || source[closingIndex + 1] === "$") return undefined;
|
||||
const raw = source.slice(0, closingIndex + 1);
|
||||
const latex = source.slice(1, closingIndex).trim();
|
||||
if (!latex || latex.includes("\n")) return undefined;
|
||||
if (/^\d+(?:[.,]\d+)?$/u.test(latex)) return { type: "text", raw, text: raw };
|
||||
return { type: "inlineMath", raw, latex };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const EdgeEverBlockMath = BlockMath.extend({
|
||||
markdownTokenizer: {
|
||||
name: "blockMath",
|
||||
level: "block",
|
||||
start: (source: string) => source.indexOf("$$"),
|
||||
tokenize: (source: string) => {
|
||||
if (!source.startsWith("$$") || source.startsWith("$$$")) return undefined;
|
||||
const closingIndex = findClosingDelimiter(source, "$$", 2);
|
||||
if (closingIndex < 0) return undefined;
|
||||
const latex = source.slice(2, closingIndex).trim();
|
||||
if (!latex) return undefined;
|
||||
return { type: "blockMath", raw: source.slice(0, closingIndex + 2), latex };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const katexOptions = { throwOnError: false, strict: "warn" as const, trust: false };
|
||||
|
||||
/** Keep in sync with packages/shared/src/mathematics.ts. */
|
||||
export const createEdgeEverMathematics = () => [
|
||||
EdgeEverBlockMath.configure({ katexOptions }),
|
||||
EdgeEverInlineMath.configure({ katexOptions }),
|
||||
];
|
||||
Reference in new issue
Block a user