Files
outline/shared/editor/nodes/MathBlock.ts
T
Tom Moor aadf47f2d7 fix: Escape backslashes in table-cell code & math fences
CodeQL flagged incomplete string escaping: escaping pipes as \| without
escaping pre-existing backslashes leaves the encoding ambiguous. Escape the
backslash (the escape character) and pipe together in a single pass so cell
content cannot break out of the column.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 07:44:49 -04:00

75 lines
1.9 KiB
TypeScript

import {
makeBlockMathInputRule,
mathSchemaSpec,
} from "@benrbray/prosemirror-math";
import type { PluginSimple } from "markdown-it";
import type {
NodeSpec,
NodeType,
Node as ProsemirrorNode,
} from "prosemirror-model";
import type { Command } from "prosemirror-state";
import { TextSelection } from "prosemirror-state";
import type { MarkdownSerializerState } from "../lib/markdown/serializer";
import mathRule, { REGEX_BLOCK_MATH_DOLLARS } from "../rules/math";
import Node from "./Node";
export default class MathBlock extends Node {
get name() {
return "math_block";
}
get schema(): NodeSpec {
return mathSchemaSpec.nodes.math_display;
}
get rulePlugins(): PluginSimple[] {
return [mathRule];
}
commands({ type }: { type: NodeType }) {
return (): Command => (state, dispatch) => {
const tr = state.tr.replaceSelectionWith(type.create());
dispatch?.(
tr
.setSelection(
TextSelection.near(tr.doc.resolve(state.selection.from - 1))
)
.scrollIntoView()
);
return true;
};
}
inputRules({ type }: { type: NodeType }) {
return [makeBlockMathInputRule(REGEX_BLOCK_MATH_DOLLARS, type)];
}
toMarkdown(state: MarkdownSerializerState, node: ProsemirrorNode) {
// Inside table cells literal newlines break the row structure, so encode
// the block on a single line using <br> for line breaks. Backslashes and
// pipes are escaped so the cell content cannot break out of the column.
if (state.inTable) {
const math = node.textContent
.replace(/[\\|]/g, "\\$&")
.replace(/\n/g, "<br>");
state.write("$$<br>" + math + "<br>$$");
return;
}
state.write("$$\n");
state.text(node.textContent, false);
state.ensureNewLine();
state.write("$$");
state.closeBlock(node);
}
parseMarkdown() {
return {
node: "math_block",
block: "math_block",
noCloseToken: true,
};
}
}