返回 DeepSeek-Reasonix
mathClassify.ts
根目录 / desktop / frontend / src / components / mathClassify.ts
1 export type InlineMathDecision = "math" | "literal";
2
3 const PURE_NUMBER = /^\d+(?:,\d{3})*(?:\.\d+)?$/;
4
5 /**
6 * Decide how an `inlineMath` node produced by remark-math should render.
7 *
8 * Pure-number spans with glued delimiters are mathematical by default
9 * (assistant-ui `escapeCurrencyDollars` parity): prose currency pairs such
10 * as `$5 and $10` never reach this classifier with a numeric body, because
11 * their word-containing spans match no math pattern below and restore
12 * verbatim. Other non-math content is also restored verbatim as text.
13 */
14 export function classifyInlineMath(math: string): InlineMathDecision {
15 if (!math || math !== math.trim() || math.includes("\n")) return "literal";
16 if (math.includes("://") || math.includes("](")) return "literal";
17
18 if (PURE_NUMBER.test(math)) return "math";
19
20 // A percentage enclosed in explicit delimiters is mathematical notation.
21 // latexNormalizeForKatex escapes its `%` for KaTeX.
22 if (/^\d+(?:,\d{3})*(?:\.\d+)?%$/.test(math)) return "math";
23
24 // Number followed by variable: implicit multiplication (2.5x, 3y^2).
25 if (/^\d+(?:\.\d+)?[A-Za-z](?:[A-Za-z0-9^_{}]*)?$/.test(math)) return "math";
26 // Number with LaTeX escape: 10\%, 5\cdot3.
27 if (/^\d+(?:\.\d+)?\\(?:%|[A-Za-z]+)(?:\{[^{}]*\})?(?:[A-Za-z0-9\\{}^_+\-*/=<>.()]*)$/.test(math)) return "math";
28
29 // Unary plus/minus: +2, -x, +\alpha, - 3.14.
30 if (/^[+\-]\s*(?:\d+(?:\.\d+)?|[A-Za-z\\])/.test(math)) return "math";
31
32 // LaTeX command (\alpha, \frac{x}{y}, \tfrac12, ...).
33 if (/\\[A-Za-z]+/.test(math)) return "math";
34 if (/[\^_{}|]/.test(math)) return "math";
35 if (/^[+\-=<>±∓]$/.test(math)) return "math";
36 if (/\b(?:alpha|beta|gamma|sum|int|prod|lim|infty|sqrt|frac|sin|cos|tan|log|ln|max|min|partial|nabla|left|right)\b/.test(math)) return "math";
37 if (/^[A-Za-z]{1,6}\s*\([^)]{1,80}\)$/.test(math)) return "math";
38 if (/^[A-Za-z]'{1,}\s*\([^)]{1,80}\)$/.test(math)) return "math";
39 if (/^(?:\(\d+\))+$/.test(math)) return "math";
40 if (/[A-Za-z0-9)\]}]\s*[+\-*/=<>]\s*[+\-]?\s*[A-Za-z0-9([{\\]/.test(math)) return "math";
41
42 // One-sided relation/equality against an implicit operand. The complete
43 // anchors are important: `=1 dollar` must not be accepted by a prefix match.
44 const operand = String.raw`(?:[+\-]?\s*(?:\d+(?:\.\d+)?|[A-Za-z]|\\[A-Za-z]+))`;
45 const relation = String.raw`(?:<=?|>=?|=|≠|≤|≥)`;
46 if (new RegExp(`^(?:${relation}\\s*${operand}|${operand}\\s*${relation})$`).test(math)) return "math";
47
48 if (/^\(?(?:[A-Za-z0-9]|\\[A-Za-z]+)(?:\s*,\s*(?:[A-Za-z0-9]|\\[A-Za-z]+)){1,10}\)?$/.test(math)) return "math";
49 if (/^\[[A-Za-z0-9^_+\-,.\\\s{}]+\]$/.test(math)) return "math";
50
51 if (/[A-Za-z]\s+[A-Za-z]/.test(math)) return "literal";
52 if (/^[A-Z][A-Z0-9_]{1,}$/.test(math)) return "literal";
53 if (/^v\d+(?:\.\d+)*$/i.test(math)) return "literal";
54 if (/^[A-Za-z]{2,}$/.test(math)) return "literal";
55
56 if (/^(?:[A-Za-z]|\\[A-Za-z]+)'{1,}$/.test(math)) return "math";
57 return /^[A-Za-z]$/.test(math) ? "math" : "literal";
58 }
59
60 export function isLikelyInlineMath(math: string): boolean {
61 return classifyInlineMath(math) === "math";
62 }
63
63 lines TYPESCRIPT