UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

1 lines 23.7 kB
{"version":3,"file":"latex.mjs","names":["codeBlocks: string[]","errors: Array<{\n formula: string;\n message: string;\n position: number;\n type: 'display' | 'inline';\n }>"],"sources":["../../../src/hooks/useMarkdown/latex.ts"],"sourcesContent":["import { renderToString } from 'katex';\n\n// ============================================================================\n// Utility Classes\n// ============================================================================\n\n/**\n * PlaceholderManager - Manages temporary replacement and restoration of protected content\n * Used to protect code blocks and LaTeX expressions during preprocessing\n */\nclass PlaceholderManager {\n private placeholders: string[] = [];\n private prefix: string;\n\n constructor(prefix = 'PROTECTED') {\n this.prefix = prefix;\n }\n\n add(content: string): string {\n const index = this.placeholders.length;\n this.placeholders.push(content);\n return `<<${this.prefix}_${index}>>`;\n }\n\n restore(text: string): string {\n return text.replaceAll(new RegExp(`<<${this.prefix}_(\\\\d+)>>`, 'g'), (_, index) => {\n return this.placeholders[Number.parseInt(index)] || '';\n });\n }\n\n clear(): void {\n this.placeholders = [];\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n// Helper: replace unescaped pipes with \\vert within a LaTeX math fragment\nconst replaceUnescapedPipes = (formula: string): string =>\n // Use \\vert{} so the control sequence terminates before the next token\n formula.replaceAll(/(?<!\\\\)\\|/g, '\\\\vert{}');\n\n/**\n * Converts LaTeX bracket delimiters to dollar sign delimiters.\n * Converts \\[...\\] to $$...$$ and \\(...\\) to $...$\n * Preserves code blocks during the conversion.\n *\n * @param text The input string containing LaTeX expressions\n * @returns The string with LaTeX bracket delimiters converted to dollar sign delimiters\n */\nexport function convertLatexDelimiters(text: string): string {\n const pattern = /(```[\\S\\s]*?```|`.*?`)|\\\\\\[([\\S\\s]*?[^\\\\])\\\\]|\\\\\\((.*?)\\\\\\)/g;\n return text.replaceAll(\n pattern,\n (\n match: string,\n codeBlock: string | undefined,\n squareBracket: string | undefined,\n roundBracket: string | undefined,\n ): string => {\n if (codeBlock !== undefined) {\n return codeBlock;\n } else if (squareBracket !== undefined) {\n return `$$${squareBracket}$$`;\n } else if (roundBracket !== undefined) {\n return `$${roundBracket}$`;\n }\n return match;\n },\n );\n}\n\n/**\n * Escapes mhchem commands in LaTeX expressions to ensure proper rendering.\n *\n * @param text The input string containing LaTeX expressions with mhchem commands\n * @returns The string with escaped mhchem commands\n */\nexport function escapeMhchemCommands(text: string) {\n return text.replaceAll('$\\\\ce{', '$\\\\\\\\ce{').replaceAll('$\\\\pu{', '$\\\\\\\\pu{');\n}\n\n/**\n * Escapes pipe characters within LaTeX expressions to prevent them from being interpreted\n * as table column separators in markdown tables.\n *\n * @param text The input string containing LaTeX expressions\n * @returns The string with pipe characters escaped in LaTeX expressions\n */\nexport function escapeLatexPipes(text: string): string {\n // Replace unescaped '|' inside LaTeX math spans with '\\vert' so that\n // remark-gfm table parsing won't treat them as column separators.\n // Leave code blocks/inline code untouched.\n // Also ignore escaped dollars (\\$) which are currency symbols\n\n // Process code blocks first to protect them\n const codeBlocks: string[] = [];\n let content = text.replaceAll(/(```[\\S\\s]*?```|`[^\\n`]*`)/g, (match) => {\n codeBlocks.push(match);\n return `<<CODE_${codeBlocks.length - 1}>>`;\n });\n\n // For display math, allow multiline\n content = content.replaceAll(/\\$\\$([\\S\\s]*?)\\$\\$/g, (match, display) => {\n return `$$${replaceUnescapedPipes(display)}$$`;\n });\n\n // For inline math, use non-greedy match that DOES NOT cross newlines\n // This prevents issues in tables where $ might appear in different cells\n content = content.replaceAll(/(?<!\\\\)\\$(?!\\$)([^\\n$]*?)(?<!\\\\)\\$(?!\\$)/g, (match, inline) => {\n return `$${replaceUnescapedPipes(inline)}$`;\n });\n\n // Restore code blocks\n content = content.replaceAll(/<<CODE_(\\d+)>>/g, (_, index) => {\n return codeBlocks[Number.parseInt(index)];\n });\n\n return content;\n}\n\n/**\n * Escapes underscores within \\text{...} commands in LaTeX expressions\n * that are not already escaped.\n * For example, \\text{node_domain} becomes \\text{node\\_domain},\n * but \\text{node\\_domain} remains \\text{node\\_domain}.\n *\n * @param text The input string potentially containing LaTeX expressions\n * @returns The string with unescaped underscores escaped within \\text{...} commands\n */\nexport function escapeTextUnderscores(text: string): string {\n return text.replaceAll(/\\\\text{([^}]*)}/g, (match, textContent: string) => {\n // textContent is the content within the braces, e.g., \"node_domain\" or \"already\\_escaped\"\n // Replace underscores '_' with '\\_' only if they are NOT preceded by a backslash '\\'.\n // The (?<!\\\\) is a negative lookbehind assertion that ensures the character before '_' is not a '\\'.\n const escapedTextContent = textContent.replaceAll(/(?<!\\\\)_/g, '\\\\_');\n return `\\\\text{${escapedTextContent}}`;\n });\n}\n\n/**\n * Escapes dollar signs that appear to be currency symbols to prevent them from being\n * interpreted as LaTeX math delimiters.\n *\n * This function identifies currency patterns such as:\n * - $20, $100, $1,000\n * - $20-50, $100+\n * - Patterns within markdown tables\n *\n * @param text The input string containing potential currency symbols\n * @returns The string with currency dollar signs escaped\n */\nexport function escapeCurrencyDollars(text: string): string {\n // Protect code blocks and existing LaTeX expressions from processing\n const manager = new PlaceholderManager('PROTECTED');\n\n let content = text.replaceAll(\n // Match patterns to protect (in order):\n // 1. Code blocks: ```...```\n // 2. Inline code: `...`\n // 3. Display math: $$...$$\n // 4. Inline math with LaTeX commands: $...\\...$ (must contain backslash to distinguish from currency)\n // 5. Simple number formulas: $1$, $10$, $100$ (pure digits in math mode)\n // 6. Number lists in math mode: $1,-1,0$ or $1,2,3$ (comma-separated numbers, possibly negative)\n // 7. LaTeX bracket notation: \\[...\\]\n // 8. LaTeX parenthesis notation: \\(...\\)\n /(```[\\S\\s]*?```|`[^\\n`]*`|\\$\\$[\\S\\s]*?\\$\\$|(?<!\\\\)\\$(?!\\$)(?=[\\S\\s]*?\\\\)[\\S\\s]*?(?<!\\\\)\\$(?!\\$)|\\$\\d+\\$|\\$-?\\d+(?:,-?\\d+)+\\$|\\\\\\[[\\S\\s]*?\\\\]|\\\\\\(.*?\\\\\\))/g,\n (match) => manager.add(match),\n );\n\n // Escape dollar signs that are clearly currency:\n // - $ followed by a digit\n // - Not preceded by another $ (to avoid breaking $$)\n // - Not followed immediately by another $ (to avoid breaking $1$ LaTeX)\n // - Followed by number patterns with optional commas, decimals, ranges, or plus signs\n // Match patterns like: $20, $1,000, $19.99, $20-50, $300+, $1,000-2,000+\n // But NOT: $1$, $2$ (these are LaTeX formulas)\n // In the replacement: \\\\ = backslash, $$ = literal $, $1 = capture group 1\n content = content.replaceAll(\n /(?<!\\$)\\$(\\d{1,3}(?:,\\d{3})*(?:\\.\\d+)?(?:-\\d{1,3}(?:,\\d{3})*(?:\\.\\d+)?)?\\+?)(?!\\$)/g,\n '\\\\$$$1',\n );\n\n // Restore protected content\n content = manager.restore(content);\n\n return content;\n}\n\n// Old simple preprocessLaTeX has been replaced by the comprehensive version below\n// The new preprocessLaTeX provides the same default behavior with optional advanced featuresgit\n\n/**\n * Extracts the LaTeX formula after the last $$ delimiter if there's an odd number of $$ delimiters.\n *\n * @param text The input string containing LaTeX formulas\n * @returns The content after the last $$ if there's an odd number of $$, otherwise an empty string\n */\nconst extractIncompleteFormula = (text: string) => {\n // Count the number of $$ delimiters\n const dollarsCount = (text.match(/\\$\\$/g) || []).length;\n\n // If odd number of $$ delimiters, extract content after the last $$\n if (dollarsCount % 2 === 1) {\n const match = text.match(/\\$\\$([^]*)$/);\n return match ? match[1] : '';\n }\n\n // If even number of $$ delimiters, return empty string\n return '';\n};\n\n/**\n * Checks if the last LaTeX formula in the text is renderable.\n * Only validates the formula after the last $$ if there's an odd number of $$.\n *\n * @param text The input string containing LaTeX formulas\n * @returns True if the last formula is renderable or if there's no incomplete formula\n */\nexport const isLastFormulaRenderable = (text: string) => {\n const formula = extractIncompleteFormula(text);\n\n // If no incomplete formula, return true\n if (!formula) return true;\n\n // Try to render the last formula\n try {\n renderToString(formula, {\n displayMode: true,\n throwOnError: true,\n });\n return true;\n } catch (error) {\n console.error(`LaTeX formula rendering error: ${error}`);\n return false;\n }\n};\n\n// ============================================================================\n// Advanced Preprocessing Functions\n// ============================================================================\n\n/**\n * Fixes common LaTeX syntax errors automatically\n * - Balances unmatched braces\n * - Balances \\left and \\right delimiters\n *\n * @param text The input string containing LaTeX expressions\n * @returns The string with fixed LaTeX expressions\n */\nexport function fixCommonLaTeXErrors(text: string): string {\n return text.replaceAll(/(\\$\\$[\\S\\s]*?\\$\\$|\\$[\\S\\s]*?\\$)/g, (match) => {\n let fixed = match;\n\n // Fix unbalanced braces\n const openBraces = (fixed.match(/(?<!\\\\){/g) || []).length;\n const closeBraces = (fixed.match(/(?<!\\\\)}/g) || []).length;\n if (openBraces > closeBraces) {\n const diff = openBraces - closeBraces;\n const closingBraces = '}'.repeat(diff);\n // Insert before the closing delimiter\n fixed = fixed.replace(/(\\$\\$?)$/, closingBraces + '$1');\n }\n\n // Fix unbalanced \\left and \\right\n const leftDelims = (fixed.match(/\\\\left[(.<[{|]/g) || []).length;\n const rightDelims = (fixed.match(/\\\\right[).>\\]|}]/g) || []).length;\n if (leftDelims > rightDelims) {\n const diff = leftDelims - rightDelims;\n const rightDots = '\\\\right.'.repeat(diff);\n fixed = fixed.replace(/(\\$\\$?)$/, rightDots + '$1');\n }\n\n return fixed;\n });\n}\n\n/**\n * Normalizes whitespace in LaTeX expressions\n * - Removes extra spaces around $ delimiters\n * - Normalizes multiple spaces to single space inside formulas\n *\n * @param text The input string containing LaTeX expressions\n * @returns The string with normalized whitespace\n */\nexport function normalizeLatexSpacing(text: string): string {\n let result = text;\n\n // Remove spaces inside $ delimiters (at the edges)\n result = result.replaceAll(/\\$\\s+/g, '$');\n result = result.replaceAll(/\\s+\\$/g, '$');\n result = result.replaceAll(/\\$\\$\\s+/g, '$$');\n result = result.replaceAll(/\\s+\\$\\$/g, '$$');\n\n // Normalize multiple spaces inside formulas to single space\n result = result.replaceAll(/(\\$\\$[\\S\\s]*?\\$\\$|\\$[\\S\\s]*?\\$)/g, (match) => {\n return match.replaceAll(/\\s{2,}/g, ' ');\n });\n\n return result;\n}\n\n/**\n * Validates all LaTeX expressions in the text\n * Returns detailed information about validation results\n *\n * @param text The input string containing LaTeX expressions\n * @returns Validation results with errors if any\n */\nexport function validateLatexExpressions(text: string): {\n errors: Array<{\n formula: string;\n message: string;\n position: number;\n type: 'display' | 'inline';\n }>;\n totalExpressions: number;\n valid: boolean;\n} {\n const errors: Array<{\n formula: string;\n message: string;\n position: number;\n type: 'display' | 'inline';\n }> = [];\n\n let totalExpressions = 0;\n const pattern = /\\$\\$([\\S\\s]*?)\\$\\$|(?<!\\\\)\\$(?!\\$)([\\S\\s]*?)(?<!\\\\)\\$(?!\\$)/g;\n let match;\n\n while ((match = pattern.exec(text)) !== null) {\n totalExpressions++;\n const formula = match[1] || match[2];\n const isDisplay = match[0].startsWith('$$');\n\n try {\n renderToString(formula, {\n displayMode: isDisplay,\n strict: 'warn',\n throwOnError: true,\n trust: false,\n });\n } catch (error) {\n errors.push({\n formula: formula.slice(0, 50) + (formula.length > 50 ? '...' : ''),\n message: error instanceof Error ? error.message : String(error),\n position: match.index,\n type: isDisplay ? 'display' : 'inline',\n });\n }\n }\n\n return {\n errors,\n totalExpressions,\n valid: errors.length === 0,\n };\n}\n\n/**\n * Handles CJK (Chinese, Japanese, Korean) characters mixed with LaTeX\n * Optionally adds spaces between CJK characters and LaTeX expressions for better rendering\n *\n * @param text The input string\n * @param addSpaces Whether to add spaces between CJK and LaTeX (default: false)\n * @returns The processed string\n */\nexport function handleCJKWithLatex(text: string, addSpaces = false): string {\n if (!addSpaces) return text;\n\n let result = text;\n\n // Add space between CJK character and opening $\n result = result.replaceAll(/([\\u3040-\\u30FF\\u4E00-\\u9FA5])(\\$)/g, '$1 $2');\n\n // Add space between closing $ and CJK character\n result = result.replaceAll(/(\\$)([\\u3040-\\u30FF\\u4E00-\\u9FA5])/g, '$1 $2');\n\n return result;\n}\n\n// ============================================================================\n// Advanced Preprocessing Options\n// ============================================================================\n\nexport interface AdvancedPreprocessOptions {\n /** Add spaces between CJK and LaTeX (default: false, requires handleCJK: true) */\n addCJKSpaces?: boolean;\n /** Convert bracket notation \\[...\\] to $$...$$ (default: true) */\n convertBrackets?: boolean;\n /** Enable currency escaping (default: true) */\n escapeCurrency?: boolean;\n /** Escape mhchem commands (default: true) */\n escapeMhchem?: boolean;\n /** Escape pipe symbols in LaTeX (default: true) */\n escapePipes?: boolean;\n /** Escape underscores in \\text{} (default: true) */\n escapeUnderscores?: boolean;\n /** Automatically fix common LaTeX errors (default: false) */\n fixErrors?: boolean;\n /** Handle CJK characters (default: false) */\n handleCJK?: boolean;\n /** Normalize whitespace (default: false) */\n normalizeSpacing?: boolean;\n /** Throw error on validation failure (default: false, requires validate: true) */\n throwOnValidationError?: boolean;\n /** Validate LaTeX syntax (default: false) */\n validate?: boolean;\n}\n\n/**\n * Comprehensive LaTeX preprocessing with configurable options\n *\n * This is the main preprocessing function that handles:\n * - Currency symbol escaping (e.g., $20 → \\$20)\n * - LaTeX delimiter conversion (\\[...\\] → $$...$$)\n * - Special character escaping (pipes, underscores, mhchem)\n * - Optional error fixing and validation\n * - Optional CJK character handling\n *\n * @param text The input string containing LaTeX and Markdown\n * @param options Configuration options for fine-grained control\n * @returns The preprocessed string\n *\n * @example\n * ```ts\n * // Default behavior (same as old preprocessLaTeX)\n * preprocessLaTeX('向量$90^\\\\circ$,非 $0^\\\\circ$ 和 $180^\\\\circ$')\n *\n * // With custom options\n * preprocessLaTeX(text, {\n * fixErrors: true,\n * validate: true,\n * handleCJK: true\n * })\n * ```\n */\nexport function preprocessLaTeX(text: string, options: AdvancedPreprocessOptions = {}): string {\n const {\n addCJKSpaces = false,\n convertBrackets = true,\n escapeCurrency = true,\n escapeMhchem = true,\n escapePipes = true,\n escapeUnderscores = true,\n fixErrors = false,\n handleCJK = false,\n normalizeSpacing = false,\n throwOnValidationError = false,\n validate = false,\n } = options;\n\n let content = text;\n\n // Phase 1: Currency escaping (if enabled)\n if (escapeCurrency) {\n content = escapeCurrencyDollars(content);\n }\n\n // Phase 2: Bracket conversion (if enabled)\n if (convertBrackets) {\n content = convertLatexDelimiters(content);\n }\n\n // Phase 3: LaTeX-specific escaping\n if (escapeMhchem) {\n content = escapeMhchemCommands(content);\n }\n\n if (escapePipes) {\n content = escapeLatexPipes(content);\n }\n\n if (escapeUnderscores) {\n content = escapeTextUnderscores(content);\n }\n\n // Phase 4: Error fixing (if enabled)\n if (fixErrors) {\n content = fixCommonLaTeXErrors(content);\n }\n\n // Phase 5: Whitespace normalization (if enabled)\n if (normalizeSpacing) {\n content = normalizeLatexSpacing(content);\n }\n\n // Phase 6: CJK handling (if enabled)\n if (handleCJK) {\n content = handleCJKWithLatex(content, addCJKSpaces);\n }\n\n // Phase 7: Validation (if enabled)\n if (validate) {\n const validation = validateLatexExpressions(content);\n if (!validation.valid) {\n const errorMessage = `LaTeX validation failed (${validation.errors.length}/${validation.totalExpressions} expressions have errors):\\n${validation.errors.map((e) => ` - [${e.type}] at position ${e.position}: ${e.message}\\n Formula: ${e.formula}`).join('\\n')}`;\n\n if (throwOnValidationError) {\n throw new Error(errorMessage);\n } else {\n console.warn(errorMessage);\n }\n }\n }\n\n return content;\n}\n\n/**\n * Strict preprocessing mode - enables all safety features and validations\n * Use this when you want maximum correctness and are willing to accept the performance cost\n *\n * @param text The input string\n * @returns The preprocessed string with all features enabled\n *\n * @example\n * ```ts\n * const processed = preprocessLaTeXStrict(userInput)\n * // Enables: error fixing, validation, CJK handling, space normalization\n * ```\n */\nexport function preprocessLaTeXStrict(text: string): string {\n return preprocessLaTeX(text, {\n addCJKSpaces: false, // Usually don't want extra spaces\n convertBrackets: true,\n escapeCurrency: true,\n escapeMhchem: true,\n escapePipes: true,\n escapeUnderscores: true,\n fixErrors: true,\n handleCJK: true,\n normalizeSpacing: true,\n throwOnValidationError: false, // Warn but don't throw\n validate: true,\n });\n}\n\n/**\n * Minimal preprocessing mode - only essential operations\n * Use this for better performance when you control the input\n *\n * @param text The input string\n * @returns The preprocessed string with minimal processing\n *\n * @example\n * ```ts\n * const processed = preprocessLaTeXMinimal(trustedInput)\n * // Only escapes currency and converts brackets\n * ```\n */\nexport function preprocessLaTeXMinimal(text: string): string {\n return preprocessLaTeX(text, {\n convertBrackets: true,\n escapeCurrency: true,\n escapeMhchem: false,\n escapePipes: false,\n escapeUnderscores: false,\n fixErrors: false,\n handleCJK: false,\n normalizeSpacing: false,\n validate: false,\n });\n}\n"],"mappings":"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"}