katex4arabic 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/cdn/katex-arabic.min.cjs +258 -0
  4. package/dist/cdn/katex-arabic.min.css +9 -0
  5. package/dist/cdn/katex-arabic.min.js +258 -0
  6. package/dist/cdn/katex-arabic.min.mjs +11878 -0
  7. package/dist/lib/arabicFunctions.d.ts +87 -0
  8. package/dist/lib/arabicFunctions.d.ts.map +1 -0
  9. package/dist/lib/arabicNumerals.d.ts +50 -0
  10. package/dist/lib/arabicNumerals.d.ts.map +1 -0
  11. package/dist/lib/arabicSymbols.d.ts +52 -0
  12. package/dist/lib/arabicSymbols.d.ts.map +1 -0
  13. package/dist/lib/cdn.d.ts +18 -0
  14. package/dist/lib/cdn.d.ts.map +1 -0
  15. package/dist/lib/hooks.cjs +41 -0
  16. package/dist/lib/hooks.cjs.map +1 -0
  17. package/dist/lib/hooks.d.cts +61 -0
  18. package/dist/lib/hooks.d.ts +61 -0
  19. package/dist/lib/hooks.d.ts.map +1 -0
  20. package/dist/lib/hooks.mjs +41 -0
  21. package/dist/lib/hooks.mjs.map +1 -0
  22. package/dist/lib/index.cjs +71 -0
  23. package/dist/lib/index.cjs.map +1 -0
  24. package/dist/lib/index.d.cts +53 -0
  25. package/dist/lib/index.d.ts +53 -0
  26. package/dist/lib/index.d.ts.map +1 -0
  27. package/dist/lib/index.mjs +72 -0
  28. package/dist/lib/index.mjs.map +1 -0
  29. package/dist/lib/katex-arabic.css +458 -0
  30. package/dist/lib/protectedRegions.d.ts +26 -0
  31. package/dist/lib/protectedRegions.d.ts.map +1 -0
  32. package/dist/lib/render.cjs +811 -0
  33. package/dist/lib/render.cjs.map +1 -0
  34. package/dist/lib/render.d.ts +65 -0
  35. package/dist/lib/render.d.ts.map +1 -0
  36. package/dist/lib/render.mjs +812 -0
  37. package/dist/lib/render.mjs.map +1 -0
  38. package/dist/lib/rtlRenderer.d.ts +100 -0
  39. package/dist/lib/rtlRenderer.d.ts.map +1 -0
  40. package/dist/lib/types.d.ts +98 -0
  41. package/dist/lib/types.d.ts.map +1 -0
  42. package/docs/INSTALL.md +314 -0
  43. package/package.json +119 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.cjs","sources":["../../src/lib/katex-arabic/arabicNumerals.ts","../../src/lib/katex-arabic/protectedRegions.ts","../../src/lib/katex-arabic/arabicFunctions.ts","../../src/lib/katex-arabic/arabicSymbols.ts","../../src/lib/katex-arabic/rtlRenderer.ts","../../src/lib/katex-arabic/render.ts"],"sourcesContent":["/**\n * ════════════════════════════════════════════════════════════════\n * arabicNumerals.ts\n * Convert Latin digits to Arabic-Indic / Persian digits, and\n * format numbers with proper Arabic separators.\n *\n * Digit ranges:\n * - Arabic-Indic (٠-٩) U+0660 – U+0669 (Standard Arabic)\n * - Eastern Arabic (۰-۹) U+06F0 – U+06F9 (Persian / Urdu)\n * ════════════════════════════════════════════════════════════════\n */\n\nimport type { NumeralStyle } from './types';\n\n// Lookup tables. Frozen at module load — never mutated.\nconst ARABIC_INDIC = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'] as const;\nconst EXTENDED_ARABIC = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const;\n\n// Build a single map per style for O(1) digit lookup during conversion.\nconst DIGIT_MAP: Record<Exclude<NumeralStyle, 'latin'>, readonly string[]> = {\n arabic: ARABIC_INDIC,\n extended: EXTENDED_ARABIC,\n};\n\n/**\n * Converts a string of Latin digits to the requested numeral style.\n * Returns the input unchanged when style is 'latin'.\n *\n * @param input The text to convert.\n * @param style Target numeral style. Defaults to 'arabic'.\n */\nexport function toArabicNumerals(input: string | number, style: NumeralStyle = 'arabic'): string {\n if (style === 'latin') return String(input);\n\n const digits = DIGIT_MAP[style];\n // String#replace with a single-char regex is significantly faster than\n // splitting + joining or array-mapping.\n return String(input).replace(/[0-9]/g, (d) => digits[Number(d)] as string);\n}\n\n/**\n * Converts Arabic-Indic and Eastern-Arabic digits back to Latin.\n * Useful for round-tripping or input normalization.\n */\nexport function fromArabicNumerals(input: string): string {\n return String(input)\n .replace(/[٠-٩]/g, (d) => String((ARABIC_INDIC as readonly string[]).indexOf(d)))\n .replace(/[۰-۹]/g, (d) => String((EXTENDED_ARABIC as readonly string[]).indexOf(d)));\n}\n\n/**\n * Formats a numeric string with Arabic-Indic digits and Arabic separators:\n * - Thousands separator: ٬ (U+066C)\n * - Decimal separator: ٫ (U+066B)\n *\n * Example: `1,000.50` → `١٬٠٠٠٫٥٠`\n *\n * Always uses the Arabic-Indic digit set, regardless of the requested style,\n * because the Arabic separators are paired with that set in standard usage.\n */\nexport function formatArabicNumber(numStr: string): string {\n return numStr\n .replace(/,/g, '٬')\n .replace(/\\./g, '٫')\n .replace(/[0-9]/g, (d) => ARABIC_INDIC[Number(d)] as string);\n}\n\n/**\n * Convert all numeric substrings inside a text to the requested style.\n * Optionally applies Arabic separators.\n */\nexport function convertNumbersInText(\n text: string,\n style: NumeralStyle = 'arabic',\n formatSeparators = true,\n): string {\n if (style === 'latin') return text;\n\n return text.replace(/\\d+(?:[.,]\\d+)*/g, (match) => {\n if (formatSeparators) {\n return formatArabicNumber(match);\n }\n return toArabicNumerals(match, style);\n });\n}\n\n/**\n * Internal: returns true if the given character is any Arabic digit\n * (Arabic-Indic or Eastern-Arabic).\n *\n * We rebuild a fresh regex per call to avoid the global-regex\n * `lastIndex` state that would otherwise cause flaky results.\n */\nexport function isArabicDigit(ch: string): boolean {\n return /[٠-٩۰-۹]/.test(ch);\n}\n","/**\n * ════════════════════════════════════════════════════════════════\n * protectedRegions.ts\n * Shared helpers for tracking spans of a LaTeX string that text\n * transforms must never touch (the bodies of \\text{…},\n * \\operatorname{…}, …). Used by the function translator and the\n * symbol-mirroring engine so user-written prose inside math stays\n * byte-for-byte identical.\n * ════════════════════════════════════════════════════════════════\n */\n\nexport interface TextRegion {\n start: number;\n end: number;\n}\n\n/**\n * Macros whose single-brace argument must be treated as verbatim text.\n * The list intentionally mirrors KaTeX's text / operator macros.\n */\nconst VERBATIM_MACROS = [\n 'text',\n 'textnormal',\n 'textbf',\n 'textit',\n 'textrm',\n 'mathrm',\n 'mathbf',\n 'mathit',\n 'mathtt',\n 'mathsf',\n 'mathscr',\n 'mathfrak',\n 'mathbb',\n 'mathcal',\n 'color',\n 'textcolor',\n 'operatorname',\n 'operatorname*',\n 'operatornamewithlimits',\n] as const;\n\n/** Pre-compiled once at module load. */\nconst VERBATIM_PATTERN = new RegExp(\n `\\\\\\\\(?:${VERBATIM_MACROS.join('|')})\\\\{[^}]*\\\\}`,\n 'g',\n);\n\n/**\n * Collect the indices of every verbatim span in `latex`.\n * Regions are sorted by start; overlapping spans are merged so a\n * single `isInsideRegions` walk stays correct.\n */\nexport function collectProtectedRegions(latex: string): TextRegion[] {\n const regions: TextRegion[] = [];\n let m: RegExpExecArray | null;\n // Fresh state per call — a shared global regex would leak lastIndex.\n VERBATIM_PATTERN.lastIndex = 0;\n while ((m = VERBATIM_PATTERN.exec(latex)) !== null) {\n regions.push({ start: m.index, end: m.index + m[0].length });\n }\n\n regions.sort((a, b) => a.start - b.start);\n\n const merged: TextRegion[] = [];\n for (const r of regions) {\n const last = merged[merged.length - 1];\n if (last && r.start <= last.end) {\n last.end = Math.max(last.end, r.end);\n } else {\n merged.push({ ...r });\n }\n }\n return merged;\n}\n\n/**\n * Returns true when `pos` (a match offset in the original string) lies\n * inside any protected region. Regions must be sorted (see above).\n */\nexport function isInsideRegions(pos: number, regions: TextRegion[]): boolean {\n for (const r of regions) {\n if (pos >= r.start && pos < r.end) return true;\n if (r.start > pos) break; // regions are sorted\n }\n return false;\n}\n","/**\r\n * ════════════════════════════════════════════════════════════════\r\n * arabicFunctions.ts\r\n * Comprehensive translation dictionaries and transforms that\r\n * convert LaTeX function names, variables, and Greek letters\r\n * into their Arabic equivalents.\r\n *\r\n * Design notes:\r\n * - All keys are LaTeX command names beginning with a backslash,\r\n * except for the VARIABLE_MAP which uses single letters.\r\n * - Translations never mutate the input. Each function returns\r\n * a new string.\r\n * - Regex safety: the `escapeRegex` helper protects against\r\n * special characters in keys when building dynamic patterns.\r\n * ════════════════════════════════════════════════════════════════\r\n */\r\n\r\nimport { collectProtectedRegions, isInsideRegions } from './protectedRegions';\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Function name translation table\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Map of LaTeX command → Arabic operator name.\r\n * \\operatorname* is used for operators with limits (e.g. lim, max, min).\r\n * \\operatorname is used for plain operator names.\r\n */\r\nexport const FUNCTION_MAP: Record<string, string> = {\r\n // ─── Basic trigonometric ─────────────────────────────────\r\n '\\\\sin': '\\\\operatorname{جا}',\r\n '\\\\cos': '\\\\operatorname{جتا}',\r\n '\\\\tan': '\\\\operatorname{ظا}',\r\n '\\\\cot': '\\\\operatorname{ظتا}',\r\n '\\\\sec': '\\\\operatorname{قا}',\r\n '\\\\csc': '\\\\operatorname{قتا}',\r\n\r\n // ─── Inverse trigonometric ───────────────────────────────\r\n '\\\\arcsin': '\\\\operatorname{جا}^{-1}',\r\n '\\\\arccos': '\\\\operatorname{جتا}^{-1}',\r\n '\\\\arctan': '\\\\operatorname{ظا}^{-1}',\r\n '\\\\arccot': '\\\\operatorname{ظتا}^{-1}',\r\n '\\\\arcsec': '\\\\operatorname{قا}^{-1}',\r\n '\\\\arccsc': '\\\\operatorname{قتا}^{-1}',\r\n\r\n // ─── Hyperbolic ──────────────────────────────────────────\r\n '\\\\sinh': '\\\\operatorname{جـا}', // جيب زائدي\r\n '\\\\cosh': '\\\\operatorname{جتـا}', // جيب تمام زائدي\r\n '\\\\tanh': '\\\\operatorname{ظـا}', // ظل زائدي\r\n '\\\\coth': '\\\\operatorname{ظتـا}', // ظل تمام زائدي\r\n '\\\\sech': '\\\\operatorname{قـا}', // قاطع زائدي\r\n '\\\\csch': '\\\\operatorname{قتـا}', // قاطع تمام زائدي\r\n\r\n // ─── Logarithms & exponentials ───────────────────────────\r\n '\\\\ln': '\\\\operatorname{لو}',\r\n '\\\\log': '\\\\operatorname{لغ}',\r\n '\\\\lg': '\\\\operatorname{لغ}',\r\n\r\n // ─── Limits (kept as \\arabiLim; translated separately) ───\r\n '\\\\liminf': '\\\\operatorname*{نها}',\r\n\r\n // ─── Maxima / minima ─────────────────────────────────────\r\n '\\\\max': '\\\\operatorname*{أقصى}',\r\n '\\\\min': '\\\\operatorname*{أدنى}',\r\n '\\\\sup': '\\\\operatorname*{حد\\\\,أعلى}',\r\n '\\\\inf': '\\\\operatorname*{حد\\\\,أدنى}',\r\n\r\n // ─── Linear algebra ──────────────────────────────────────\r\n '\\\\det': '\\\\operatorname{محدد}',\r\n '\\\\dim': '\\\\operatorname{بعد}',\r\n '\\\\ker': '\\\\operatorname{نواة}',\r\n '\\\\rank': '\\\\operatorname{رتبة}',\r\n '\\\\tr': '\\\\operatorname{أثر}',\r\n '\\\\diag': '\\\\operatorname{قطري}',\r\n '\\\\span': '\\\\operatorname{مدى}',\r\n '\\\\null': '\\\\operatorname{فضاء\\\\,صفري}',\r\n\r\n // ─── Number theory ───────────────────────────────────────\r\n '\\\\gcd': '\\\\operatorname{ق.م.أ}',\r\n '\\\\lcm': '\\\\operatorname{م.م.أ}',\r\n '\\\\mod': '\\\\operatorname{باقي}',\r\n '\\\\bmod': '\\\\operatorname{باقي}',\r\n\r\n // ─── Probability & statistics ────────────────────────────\r\n '\\\\Pr': '\\\\operatorname{حا}',\r\n '\\\\P': '\\\\operatorname{حا}',\r\n\r\n // ─── Complex & argument ──────────────────────────────────\r\n '\\\\arg': '\\\\operatorname{سعة}',\r\n '\\\\deg': '\\\\operatorname{درجة}',\r\n '\\\\sgn': '\\\\operatorname{إشا}',\r\n '\\\\Re': '\\\\operatorname{حق}',\r\n '\\\\Im': '\\\\operatorname{تخ}',\r\n '\\\\hom': '\\\\operatorname{تشا}',\r\n '\\\\Hom': '\\\\operatorname{تشا}',\r\n\r\n // ─── Special operator forms (preserved as-is) ────────────\r\n // \\sum, \\prod, \\int stay — they are mirrored visually via CSS.\r\n '\\\\sum': '\\\\sum',\r\n '\\\\prod': '\\\\prod',\r\n '\\\\coprod': '\\\\coprod',\r\n '\\\\int': '\\\\int',\r\n '\\\\iint': '\\\\iint',\r\n '\\\\iiint': '\\\\iiint',\r\n '\\\\oint': '\\\\oint',\r\n '\\\\partial': '\\\\partial',\r\n '\\\\nabla': '\\\\nabla',\r\n '\\\\infty': '\\\\infty',\r\n '\\\\lim': '\\\\lim', // processed via arabiLim pathway\r\n '\\\\limsup': '\\\\operatorname*{نها}',\r\n\r\n // ─── Special marker used internally for limit translation ─\r\n // \\arabiLim is a sentinel that the translator pipeline replaces\r\n // with the proper \\operatorname* form. Users normally do not write this.\r\n '\\\\arabiLim': '\\\\operatorname*{نها}',\r\n // '\\\\lim': '\\\\operatorname*{نها}',\r\n};\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Variable translation table (single Latin letters → Arabic)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Standard mapping of Latin letters to their conventional Arabic\r\n * equivalents in mathematical literature. The uppercase forms follow\r\n * the same scheme, with a few exceptions (e.g. E → هـ for Euler's\r\n * constant, I → ت for the imaginary unit).\r\n */\r\nexport const VARIABLE_MAP: Record<string, string> = {\r\n // ─── Lowercase ───────────────────────────────────────────\r\n x: 'س',\r\n y: 'ص',\r\n z: 'ع',\r\n a: 'أ',\r\n b: 'ب',\r\n c: 'جـ',\r\n d: 'د',\r\n e: 'هـ', // Euler's number\r\n f: 'ف',\r\n g: 'ق',\r\n h: 'ح',\r\n i: 'ت', // imaginary unit\r\n j: 'ج',\r\n k: 'ك',\r\n l: 'ل',\r\n m: 'م',\r\n n: 'ن',\r\n o: 'ع',\r\n p: 'ب',\r\n q: 'ق',\r\n r: 'ر',\r\n s: 'ز',\r\n t: 'ت',\r\n // u: 'ث',\r\n // v: 'ڤ',\r\n // w: 'و',\r\n\r\n // ─── Uppercase ───────────────────────────────────────────\r\n A: 'أ',\r\n B: 'ب',\r\n C: 'جـ',\r\n D: 'د',\r\n E: 'هـ',\r\n F: 'ف',\r\n G: 'ق',\r\n H: 'ح',\r\n I: 'ت',\r\n J: 'ج',\r\n K: 'ك',\r\n L: 'ل',\r\n M: 'م',\r\n N: 'ن',\r\n O: 'ع',\r\n P: 'ح',\r\n Q: 'ق',\r\n R: 'ر',\r\n S: 'ز',\r\n T: 'ت',\r\n U: 'ث',\r\n V: 'ڤ',\r\n W: 'و',\r\n X: 'س',\r\n Y: 'ص',\r\n Z: 'ز',\r\n};\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Greek letter table (already Unicode; listed for reference)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * KaTeX renders Greek letters as native Unicode, so no translation\r\n * is required. This table is provided for documentation and\r\n * for users who want to inspect the supported set.\r\n */\r\nexport const GREEK_MAP: Record<string, string> = {\r\n '\\\\alpha': 'α', '\\\\beta': 'β', '\\\\gamma': 'γ', '\\\\delta': 'δ',\r\n '\\\\epsilon': 'ε', '\\\\varepsilon': 'ε', '\\\\zeta': 'ζ', '\\\\eta': 'η',\r\n '\\\\theta': 'θ', '\\\\vartheta': 'ϑ', '\\\\iota': 'ι', '\\\\kappa': 'κ',\r\n '\\\\lambda': 'λ', '\\\\mu': 'μ', '\\\\nu': 'ν', '\\\\xi': 'ξ',\r\n '\\\\pi': 'π', '\\\\varpi': 'ϖ', '\\\\rho': 'ρ', '\\\\varrho': 'ϱ',\r\n '\\\\sigma': 'σ', '\\\\varsigma': 'ς', '\\\\tau': 'τ', '\\\\upsilon': 'υ',\r\n '\\\\phi': 'φ', '\\\\varphi': 'ϕ', '\\\\chi': 'χ', '\\\\psi': 'ψ', '\\\\omega': 'ω',\r\n '\\\\Gamma': 'Γ', '\\\\Delta': 'Δ', '\\\\Theta': 'Θ', '\\\\Lambda': 'Λ',\r\n '\\\\Xi': 'Ξ', '\\\\Pi': 'Π', '\\\\Sigma': 'Σ', '\\\\Upsilon': 'Υ',\r\n '\\\\Phi': 'Φ', '\\\\Psi': 'Ψ', '\\\\Omega': 'Ω',\r\n};\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Differential pattern map\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Convert a `dX` differential to Arabic form: `دX` where X is the\r\n * Arabic variable. e.g. dx → د س, dθ → د θ.\r\n */\r\nexport const DIFFERENTIAL_PATTERNS: Record<string, string> = {\r\n dx: '\\\\text{د}\\\\text{س}',\r\n dy: '\\\\text{د}\\\\text{ص}',\r\n dz: '\\\\text{د}\\\\text{ع}',\r\n dt: '\\\\text{د}\\\\text{ز}',\r\n dr: '\\\\text{د}\\\\text{ر}',\r\n du: '\\\\text{د}\\\\text{ث}',\r\n dv: '\\\\text{د}\\\\text{ڤ}',\r\n ds: '\\\\text{د}\\\\text{س}',\r\n dA: '\\\\text{د}\\\\text{أ}',\r\n dV: '\\\\text{د}\\\\text{ح}',\r\n dS: '\\\\text{د}\\\\text{س}',\r\n};\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Internal helpers\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Escape a string for safe use in a RegExp pattern.\r\n * Escapes: \\ ^ $ * + ? . ( ) | { } [ ]\r\n */\r\nfunction escapeRegex(input: string): string {\r\n return input.replace(/[\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Public translation functions\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Translate LaTeX function commands (sin, cos, lim, …) to Arabic.\r\n *\r\n * Uses a single left-to-right scan of the source so that:\r\n * • the longest command wins at every position (\\\\arcsin before \\\\sin),\r\n * • verbatim regions (\\\\text{…}, \\\\operatorname{…}) are never modified,\r\n * • offsets stay stable — there is no cascade of in-place edits that\r\n * could re-shift later matches.\r\n *\r\n * @param latex LaTeX source.\r\n * @param customMap User-supplied overrides merged on top of FUNCTION_MAP.\r\n */\r\nexport function translateFunctions(\r\n latex: string,\r\n customMap: Record<string, string> = {},\r\n): string {\r\n const merged = { ...FUNCTION_MAP, ...customMap };\r\n const protectedRegions = collectProtectedRegions(latex);\r\n\r\n // Match every LaTeX command token. The greedy match ensures \\\\sinh is\r\n // taken as a whole rather than as the prefix \\\\sin plus \"h\".\r\n const cmdPattern = /\\\\[a-zA-Z]+/g;\r\n let result = '';\r\n let cursor = 0;\r\n let m: RegExpExecArray | null;\r\n\r\n while ((m = cmdPattern.exec(latex)) !== null) {\r\n const command = m[0];\r\n const end = m.index + command.length;\r\n // A command is only a real token when it is not the prefix of a\r\n // longer letter sequence (e.g. `\\\\sin` inside `\\\\sinh`).\r\n const next = latex[end];\r\n const followedByLetter = !!next && /[A-Za-z]/.test(next);\r\n\r\n const replacement = followedByLetter ? undefined : merged[command];\r\n if (replacement !== undefined && !isInsideRegions(m.index, protectedRegions)) {\r\n result += latex.slice(cursor, m.index) + replacement;\r\n cursor = end;\r\n continue;\r\n }\r\n // No translation: copy this token (and any pending gaps) verbatim.\r\n result += latex.slice(cursor, end);\r\n cursor = end;\r\n }\r\n\r\n result += latex.slice(cursor);\r\n return result;\r\n}\r\n\r\n/**\r\n * Translate differential patterns like `dx`, `dy`, `d\\theta` to their\r\n * Arabic equivalents.\r\n */\r\nexport function translateDifferentials(latex: string): string {\r\n let result = latex;\r\n\r\n // Latin-letter differentials: dx, dy, dz, dt, dr, du, dv, ds.\r\n // Use a non-letter boundary on each side so we don't match `idx` or `dxyz`.\r\n result = result.replace(\r\n /\\bd([xyztruvs])\\b/g,\r\n (_match, letter: string) => {\r\n const arabic = VARIABLE_MAP[letter] ?? letter;\r\n return `\\\\text{د}\\\\text{${arabic}}`;\r\n },\r\n );\r\n\r\n // Greek-letter differentials: d\\theta, d\\phi, d\\rho, d\\alpha, d\\beta.\r\n result = result.replace(\r\n /d\\\\(theta|phi|rho|alpha|beta|gamma|delta)\\b/g,\r\n (_match, greek: string) => `\\\\text{د}\\\\${greek}`,\r\n );\r\n\r\n // Capital differentials: dA, dV, dS.\r\n result = result.replace(\r\n /\\bd([AVS])\\b/g,\r\n (_match, letter: string) => {\r\n const arabic = VARIABLE_MAP[letter] ?? letter;\r\n return `\\\\text{د}\\\\text{${arabic}}`;\r\n },\r\n );\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Translate standalone Latin variables to their Arabic equivalents.\r\n *\r\n * Critical: avoid translating inside LaTeX commands, math text blocks,\r\n * and \\\\operatorname names. We pre-compute the \"protected\" regions once\r\n * and run a single combined pass so that no offset ever shifts.\r\n *\r\n * Implementation note: the pattern has exactly one capture group, so the\r\n * `replace` callback is `(match, p1, offset, string)` — `offset` is the\r\n * **third** argument.\r\n */\r\nexport function translateVariables(\r\n latex: string,\r\n customMap: Record<string, string> = {},\r\n): string {\r\n const merged = { ...VARIABLE_MAP, ...customMap };\r\n const keys = Object.keys(merged);\r\n if (keys.length === 0) return latex;\r\n\r\n // Longest-first so a multi-character custom key beats single letters.\r\n const sorted = keys.sort((a, b) => b.length - a.length);\r\n const protectedRegions = collectProtectedRegions(latex);\r\n\r\n // A variable is a Latin letter (or custom key) that is not surrounded\r\n // by other Latin letters or a backslash, so command names are untouched.\r\n const pattern = new RegExp(\r\n `(?<![A-Za-z\\\\\\\\])(${sorted.map(escapeRegex).join('|')})(?![A-Za-z])`,\r\n 'g',\r\n );\r\n\r\n return latex.replace(pattern, (match, _p1, offset, _string) => {\r\n const pos = Number(offset);\r\n if (isInsideRegions(pos, protectedRegions)) return match;\r\n return `\\\\text{${merged[match as string] as string}}`;\r\n });\r\n}\r\n\r\n/**\r\n * Translate `d\\theta` and other named Greek differentials to Arabic.\r\n * Used by the differential pipeline; exposed for completeness.\r\n */\r\nexport function translateSpecialPatterns(latex: string): string {\r\n return translateDifferentials(latex);\r\n}\r\n\r\n/**\r\n * Translate everything: differentials, functions, and variables.\r\n * Each step is independent and may be toggled by the caller.\r\n */\r\nexport function translateAll(\r\n latex: string,\r\n options: {\r\n functions?: boolean;\r\n variables?: boolean;\r\n differentials?: boolean;\r\n customFunctionMap?: Record<string, string>;\r\n customVariableMap?: Record<string, string>;\r\n } = {},\r\n): string {\r\n const {\r\n functions = true,\r\n variables = true,\r\n differentials = true,\r\n customFunctionMap = {},\r\n customVariableMap = {},\r\n } = options;\r\n\r\n let result = latex;\r\n\r\n // Order matters:\r\n // 1. Differentials — must run before variables, otherwise `dx` becomes\r\n // `\\text{د}x` and the `x` inside won't be protected.\r\n // 2. Functions — convert command names to Arabic operators. Limit\r\n // commands are routed through a sentinel to preserve limits notation.\r\n // 3. Variables — single-letter translation, with protection of all\r\n // previously-inserted \\text{} and \\operatorname{} regions.\r\n if (differentials) {\r\n result = translateDifferentials(result);\r\n }\r\n if (functions) {\r\n result = translateFunctions(result, customFunctionMap);\r\n }\r\n if (variables) {\r\n result = translateVariables(result, customVariableMap);\r\n }\r\n\r\n return result;\r\n}\r\n","/**\r\n * ════════════════════════════════════════════════════════════════\r\n * arabicSymbols.ts\r\n * Symbol mirroring and Unicode math-symbol definitions.\r\n *\r\n * When an equation is rendered in an RTL context, certain symbols\r\n * (≤, ≥, ∫, Σ, …) should appear visually mirrored so that the\r\n * smaller / open end faces the start of the reading direction.\r\n *\r\n * This module is the *LaTeX-level* mirror. The CSS layer may\r\n * additionally apply `transform: scaleX(-1)` to the rendered\r\n * DOM nodes for finer-grained control over glyphs that KaTeX\r\n * renders directly (√, ∫, Σ, ∏).\r\n * ════════════════════════════════════════════════════════════════\r\n */\r\n\r\nimport { collectProtectedRegions, isInsideRegions } from './protectedRegions';\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Symbol mirroring tables\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Comparison / inequality operators.\r\n * In an RTL context, a < b is typically read \"a smaller than b\" so\r\n * the small end of the symbol should point right (the start side).\r\n * The mirror of `<` is `>`, of `≤` is `≥`, etc.\r\n */\r\nconst COMPARISON_MIRRORS: Record<string, string> = {\r\n '<': '>',\r\n '>': '<',\r\n '\\\\lt': '\\\\gt',\r\n '\\\\gt': '\\\\lt',\r\n '\\\\leq': '\\\\geq',\r\n '\\\\geq': '\\\\leq',\r\n '\\\\le': '\\\\ge',\r\n '\\\\ge': '\\\\le',\r\n '\\\\leqslant': '\\\\geqslant',\r\n '\\\\geqslant': '\\\\leqslant',\r\n '\\\\lesssim': '\\\\gtrsim',\r\n '\\\\gtrsim': '\\\\lesssim',\r\n '\\\\ll': '\\\\gg',\r\n '\\\\gg': '\\\\ll',\r\n '\\\\nless': '\\\\ngtr',\r\n '\\\\ngtr': '\\\\nless',\r\n '\\\\nleq': '\\\\ngeq',\r\n '\\\\ngeq': '\\\\nleq',\r\n};\r\n\r\n/**\r\n * Arrow mirroring. The arrow head should point to the start of the\r\n * reading direction (right in RTL), so a \\rightarrow becomes \\leftarrow.\r\n */\r\nconst ARROW_MIRRORS: Record<string, string> = {\r\n '\\\\to': '\\\\gets',\r\n '\\\\gets': '\\\\to',\r\n '\\\\rightarrow': '\\\\leftarrow',\r\n '\\\\leftarrow': '\\\\rightarrow',\r\n '\\\\longrightarrow': '\\\\longleftarrow',\r\n '\\\\longleftarrow': '\\\\longrightarrow',\r\n '\\\\Rightarrow': '\\\\Leftarrow',\r\n '\\\\Leftarrow': '\\\\Rightarrow',\r\n '\\\\Longrightarrow': '\\\\Longleftarrow',\r\n '\\\\Longleftarrow': '\\\\Longrightarrow',\r\n '\\\\hookrightarrow': '\\\\hookleftarrow',\r\n '\\\\hookleftarrow': '\\\\hookrightarrow',\r\n '\\\\mapsto': '\\\\mapsfrom',\r\n '\\\\mapsfrom': '\\\\mapsto',\r\n '\\\\rightleftharpoons': '\\\\leftrightharpoons',\r\n '\\\\leftrightharpoons': '\\\\rightleftharpoons',\r\n};\r\n\r\n/**\r\n * Bracket mirroring. A round opening bracket \"(\" points away from\r\n * the direction of writing, so in RTL we swap the LaTeX command\r\n * forms (\\\\langle ↔ \\\\rangle, \\\\lbrace ↔ \\\\rbrace, …).\r\n *\r\n * NOTE: We intentionally do NOT mirror raw `(`/`)`/`{`/`}`/`[`/`]`\r\n * characters. Those are part of LaTeX command syntax (e.g. the\r\n * braces in `\\frac{a}{b}`) and swapping them would break the\r\n * LaTeX structure.\r\n */\r\nconst BRACKET_MIRRORS: Record<string, string> = {\r\n '\\\\langle': '\\\\rangle',\r\n '\\\\rangle': '\\\\langle',\r\n '\\\\lceil': '\\\\rceil',\r\n '\\\\rceil': '\\\\lceil',\r\n '\\\\lfloor': '\\\\rfloor',\r\n '\\\\rfloor': '\\\\lfloor',\r\n '\\\\lbrace': '\\\\rbrace',\r\n '\\\\rbrace': '\\\\lbrace',\r\n '\\\\lbrack': '\\\\rbrack',\r\n '\\\\rbrack': '\\\\lbrack',\r\n};\r\n\r\n/**\r\n * Big-operator mirroring. KaTeX renders Σ, ∏, ∫ as native glyphs,\r\n * so this table is mainly a fallback for users who write the\r\n * commands as plain text outside LaTeX.\r\n */\r\nconst BIG_OP_MIRRORS: Record<string, string> = {\r\n '\\\\sum': '\\\\sum', // mirrored via CSS\r\n '\\\\prod': '\\\\prod', // mirrored via CSS\r\n '\\\\int': '\\\\int', // mirrored via CSS\r\n '\\\\iint': '\\\\iint',\r\n '\\\\iiint': '\\\\iiint',\r\n '\\\\oint': '\\\\oint',\r\n};\r\n\r\n/**\r\n * Combined mirror map. Each group can be toggled independently.\r\n */\r\nexport const MIRRORED_SYMBOLS: Record<string, string> = {\r\n ...COMPARISON_MIRRORS,\r\n ...ARROW_MIRRORS,\r\n ...BRACKET_MIRRORS,\r\n ...BIG_OP_MIRRORS,\r\n};\r\n\r\n/**\r\n * Sub-maps exposed for fine-grained control by callers.\r\n */\r\nexport const COMPARISON_SYMBOLS = COMPARISON_MIRRORS;\r\nexport const ARROW_SYMBOLS = ARROW_MIRRORS;\r\nexport const BRACKET_SYMBOLS = BRACKET_MIRRORS;\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Arabic mathematical alphabet (Unicode block U+1EE00 – U+1EEFF)\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * The Unicode \"Arabic Mathematical Alphabet\" provides isolated-form\r\n * Arabic letters optimized for math typesetting. These are *not*\r\n * connecting forms; they are intended to behave like individual\r\n * Latin letters inside an equation.\r\n */\r\nexport const ARABIC_MATH_UNICODE: Record<string, string> = {\r\n alef: '\\u{1EE00}',\r\n ba: '\\u{1EE01}',\r\n jeem: '\\u{1EE02}',\r\n dal: '\\u{1EE03}',\r\n waw: '\\u{1EE05}',\r\n zain: '\\u{1EE06}',\r\n ha: '\\u{1EE07}',\r\n tah: '\\u{1EE08}',\r\n ya: '\\u{1EE09}',\r\n kaf: '\\u{1EE0A}',\r\n lam: '\\u{1EE0B}',\r\n meem: '\\u{1EE0C}',\r\n noon: '\\u{1EE0D}',\r\n seen: '\\u{1EE0E}',\r\n ain: '\\u{1EE0F}',\r\n fa: '\\u{1EE10}',\r\n sad: '\\u{1EE11}',\r\n qaf: '\\u{1EE12}',\r\n ra: '\\u{1EE13}',\r\n sheen: '\\u{1EE14}',\r\n ta: '\\u{1EE15}',\r\n tha: '\\u{1EE16}',\r\n kha: '\\u{1EE17}',\r\n dhal: '\\u{1EE18}',\r\n dad: '\\u{1EE19}',\r\n ghayn: '\\u{1EE1A}',\r\n};\r\n\r\n/**\r\n * Curated special symbols used in Arabic math typography.\r\n */\r\nexport const SPECIAL_ARABIC_SYMBOLS = {\r\n arabicFractionSlash: '/',\r\n integralMaghribi: '\\\\int', // mirrored visually via CSS\r\n} as const;\r\n\r\n// ═══════════════════════════════════════════════════════════════\r\n// Mirroring engine\r\n// ═══════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Escape a string for safe use in a RegExp pattern.\r\n */\r\nfunction escapeRegex(input: string): string {\r\n return input.replace(/[\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\r\n}\r\n\r\n/**\r\n * Build a regex that matches a LaTeX command at a word boundary.\r\n * For single-character keys (like `<`), we require non-letter\r\n * neighbors instead of `\\b` (which doesn't fire between two\r\n * non-word characters).\r\n */\r\nfunction buildMirrorRegex(key: string): RegExp {\r\n const escaped = escapeRegex(key);\r\n if (key.length === 1) {\r\n return new RegExp(`(?<![A-Za-z\\\\\\\\])${escaped}(?![A-Za-z])`, 'g');\r\n }\r\n return new RegExp(escaped + '(?![A-Za-z])', 'g');\r\n}\r\n\r\n/**\r\n * Apply symbol mirroring to a LaTeX string.\r\n *\r\n * A single left-to-right pass over the *original* string is used. Each\r\n * symbol is mapped at most once (so `<` never re-mirrors back to `<`),\r\n * longer commands win over shorter prefixes, and verbatim regions are\r\n * skipped — all without the offset-shift problems of chained passes.\r\n *\r\n * @param latex LaTeX source.\r\n * @param enabled Whether mirroring is active.\r\n */\r\nexport function applyMirroredSymbols(latex: string, enabled = true): string {\r\n if (!enabled) return latex;\r\n\r\n // Sort by length descending so longer commands win at every position\r\n // (\\\\longrightarrow before \\\\rightarrow, \\\\leqslant before \\\\leq).\r\n const sorted = Object.entries(MIRRORED_SYMBOLS).sort(\r\n (a, b) => b[0].length - a[0].length,\r\n );\r\n\r\n // Build a single left-to-right pass: each key's pattern (with its own\r\n // word boundaries) becomes a non-capturing alternative. Scanning the\r\n // *original* string once means offsets never shift, so protected\r\n // regions stay valid and no symbol is ever mirrored twice (no ping-pong).\r\n const pattern = new RegExp(\r\n sorted.map(([key]) => `(?:${buildMirrorRegex(key).source})`).join('|'),\r\n 'g',\r\n );\r\n\r\n const mirrorByText = new Map<string, string>(\r\n sorted.map(([key, value]) => [key, value]),\r\n );\r\n\r\n // Verbatim spans (\\\\text{…}, \\\\operatorname{…}, …) hold user-authored\r\n // prose and must never be mirrored — they are not math operators.\r\n const protectedRegions = collectProtectedRegions(latex);\r\n\r\n return latex.replace(pattern, (...args) => {\r\n // The combined pattern has no capture groups, so the callback is\r\n // (match, offset, string) — offset is the second argument.\r\n const match = args[0] as string;\r\n const offset = args[1] as number;\r\n if (isInsideRegions(offset, protectedRegions)) return match;\r\n return mirrorByText.get(match) ?? match;\r\n });\r\n}\r\n","/**\n * ════════════════════════════════════════════════════════════════\n * rtlRenderer.ts\n * The main pipeline that transforms a LaTeX string for Arabic\n * rendering and builds a KaTeX options object.\n *\n * Pipeline (run in order — order is significant):\n * 1. Rewrite \\lim → \\arabiLim (sentinel) so that the function\n * translator can use \\operatorname* with limits.\n * 2. Translate differentials (dx, dy, d\\theta, …).\n * 3. Translate function commands (sin → جا, cos → جتا, …).\n * 4. Translate variables (x → س, y → ص, …).\n * 5. Mirror comparison and arrow symbols for RTL.\n * 6. Convert digits to the requested numeral style.\n *\n * Performance:\n * - An LRU render cache avoids re-processing identical LaTeX.\n * - Input length is capped to prevent catastrophic backtracking.\n * ════════════════════════════════════════════════════════════════\n */\n\nimport {\n toArabicNumerals,\n formatArabicNumber,\n} from './arabicNumerals';\nimport {\n translateFunctions,\n translateVariables,\n translateDifferentials,\n} from './arabicFunctions';\nimport { applyMirroredSymbols } from './arabicSymbols';\nimport type {\n ArabicKatexOptions,\n NumeralStyle,\n PartialArabicOptions,\n} from './types';\n\n// Re-export types for convenience.\nexport type { ArabicKatexOptions, NumeralStyle, PartialArabicOptions } from './types';\n\n// ═══════════════════════════════════════════════════════════════\n// Constants & guards\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Maximum allowed LaTeX input length (characters). Inputs beyond\n * this are truncated to prevent catastrophic regex backtracking and\n * excessive memory use. 20 000 chars is far above any realistic\n * equation (even a page of dense math is a few thousand chars) while\n * still bounding worst-case preprocessing and render cost.\n */\nexport const MAX_INPUT_LENGTH = 20_000;\n\n/**\n * LRU cache size for rendered HTML. Each entry is keyed by\n * (latex + serialized options). 256 entries is a good balance\n * between hit-rate and memory for interactive editors.\n */\nconst CACHE_CAPACITY = 256;\n\n// ═══════════════════════════════════════════════════════════════\n// LRU Render Cache\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * A minimal, dependency-free LRU cache backed by a Map.\n * Map iteration order in JS is insertion order, so the *first*\n * key is always the least-recently-used.\n */\nclass LruCache<K, V> {\n private readonly map = new Map<K, V>();\n constructor(private readonly capacity: number) {}\n\n get(key: K): V | undefined {\n const value = this.map.get(key);\n if (value !== undefined) {\n // Move to end (most-recently-used).\n this.map.delete(key);\n this.map.set(key, value);\n }\n return value;\n }\n\n set(key: K, value: V): void {\n if (this.map.has(key)) {\n this.map.delete(key);\n } else if (this.map.size >= this.capacity) {\n // Evict the least-recently-used (first) entry.\n const firstKey = this.map.keys().next().value;\n if (firstKey !== undefined) this.map.delete(firstKey);\n }\n this.map.set(key, value);\n }\n\n clear(): void {\n this.map.clear();\n }\n\n get size(): number {\n return this.map.size;\n }\n}\n\n/** Module-level render cache. Exported so the render entry point can reuse it. */\nexport const renderCache = new LruCache<string, string>(CACHE_CAPACITY);\n\n/**\n * Clear the internal render cache. Useful when options change\n * globally or in tests.\n */\nexport function clearRenderCache(): void {\n renderCache.clear();\n}\n\n/**\n * Build a stable cache key from latex + options.\n * Every field that can affect the rendered output is included so a\n * stale entry can never be served. Record fields are serialized with\n * JSON.stringify (key order is not guaranteed, but two identical\n * option objects built the same way produce identical JSON; callers\n * who mutate key order simply get a cache miss, never a wrong hit).\n */\nexport function buildCacheKey(latex: string, opts: ArabicKatexOptions): string {\n return [\n latex,\n opts.numerals,\n opts.formatNumbers,\n opts.translateFuncs,\n opts.translateVars,\n opts.translateDiffs,\n JSON.stringify(opts.customFunctionMap),\n JSON.stringify(opts.customVariableMap),\n opts.mirrorSymbols,\n opts.mirrorBigOperators,\n opts.mirrorSqrt,\n opts.mirrorBrackets,\n opts.direction,\n opts.fontFamily,\n opts.displayMode,\n opts.fullArabicMode,\n opts.operatorScale,\n JSON.stringify(opts.macros),\n opts.output,\n String(opts.strict),\n opts.trust,\n opts.minRuleThickness,\n ].join('|');\n}\n\n// ═══════════════════════════════════════════════════════════════\n// Default options\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * The default configuration. Tweak individual fields by passing\n * an overrides object to the render functions.\n */\nexport const DEFAULT_OPTIONS: ArabicKatexOptions = {\n // Numerals\n numerals: 'arabic',\n formatNumbers: true,\n\n // Translation\n translateFuncs: true,\n translateVars: true,\n translateDiffs: true,\n customFunctionMap: {},\n customVariableMap: {},\n\n // Mirroring\n mirrorSymbols: true,\n mirrorBigOperators: true,\n mirrorSqrt: true,\n mirrorBrackets: true,\n\n // Rendering\n direction: 'rtl',\n fontFamily: 'Amiri',\n displayMode: false,\n fullArabicMode: true,\n // 1.05 compensates for the x-height difference between Arabic fonts\n // (Amiri et al.) and KaTeX_Main, so Arabic operator names (جا، جتا…)\n // visually match the size of their Latin counterparts out of the box.\n operatorScale: 1.05,\n\n // KaTeX\n macros: {},\n throwOnError: false,\n output: 'html',\n strict: false,\n trust: false,\n minRuleThickness: 0.04,\n};\n\n/**\n * Merge partial user options on top of the defaults.\n * Pure function: safe to call repeatedly.\n */\nexport function resolveOptions(overrides: PartialArabicOptions = {}): ArabicKatexOptions {\n return { ...DEFAULT_OPTIONS, ...overrides };\n}\n\n// ═══════════════════════════════════════════════════════════════\n// LaTeX preprocessing pipeline\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * The single entry point for Arabic LaTeX transformation.\n *\n * Returns the transformed LaTeX that can be passed to KaTeX.renderToString.\n * Each pipeline step is gated by an option, so callers can disable\n * any stage they don't need.\n */\nexport function processArabicLatex(\n latex: string,\n options: PartialArabicOptions = {},\n): string {\n const opts = resolveOptions(options);\n\n // Guard: cap input length to bound worst-case regex cost.\n let processed = latex.length > MAX_INPUT_LENGTH\n ? latex.slice(0, MAX_INPUT_LENGTH)\n : latex;\n\n // Guard: empty / whitespace-only input short-circuits.\n if (!processed.trim()) return processed;\n\n // 1. Rewrite \\lim to a sentinel so the function translator can produce\n // \\operatorname* form (which preserves limits in display mode).\n if (opts.translateFuncs) {\n processed = processed.replace(/\\\\lim(?![A-Za-z])/g, '\\\\arabiLim');\n }\n\n // 2. Translate differentials before variables. This way \"dx\" becomes\n // \"\\text{د}\\text{س}\" and the inner \"x\" is already protected.\n if (opts.translateDiffs) {\n processed = translateDifferentials(processed);\n }\n\n // 3. Translate function names. This also resolves the \\arabiLim\n // sentinel into the final \\operatorname* form.\n if (opts.translateFuncs) {\n processed = translateFunctions(processed, opts.customFunctionMap);\n }\n\n // 4. Translate variables last so that everything inserted by the\n // previous steps is already protected.\n if (opts.translateVars) {\n processed = translateVariables(processed, opts.customVariableMap);\n }\n\n // 5. Mirror comparison/arrow/bracket symbols for RTL. This must run\n // after function translation so we don't accidentally mirror the\n // Arabic operator names (e.g. \\operatorname{جا} doesn't contain\n // arrow commands, but the protection is still good to have).\n if (opts.mirrorSymbols) {\n processed = applyMirroredSymbols(processed, true);\n }\n\n // 6. Convert digits to the requested numeral style. This is purely\n // a digit substitution, so it runs last.\n if (opts.numerals !== 'latin') {\n processed = convertNumbersInLatex(processed, opts.numerals, opts.formatNumbers);\n }\n\n return processed;\n}\n\n/**\n * Convert Latin digits in a LaTeX string to the requested numeral style.\n *\n * The regex is careful to skip digits that appear inside LaTeX command\n * names (e.g. `\\char3`) — though these are exceedingly rare in practice.\n * For better cross-browser support we use a manual scan instead of\n * relying on a lookbehind assertion.\n */\nfunction convertNumbersInLatex(\n latex: string,\n style: NumeralStyle,\n formatSeparators: boolean,\n): string {\n // Walk the string and only transform digit runs that are NOT preceded\n // by an alphabetic character (which would mean they are part of a\n // LaTeX command like \\char1).\n let result = '';\n let i = 0;\n while (i < latex.length) {\n const ch = latex[i];\n if (ch && /[0-9]/.test(ch)) {\n // Check the character immediately before: if it is a letter,\n // we are inside a command name, so leave the digit alone.\n const prev = i > 0 ? latex[i - 1] : '';\n if (prev && /[A-Za-z]/.test(prev)) {\n result += ch;\n } else {\n // Collect the full digit run (including decimal point / comma).\n let j = i;\n while (j < latex.length) {\n const cj = latex[j];\n if (cj && /[0-9.,]/.test(cj)) {\n j++;\n } else {\n break;\n }\n }\n const run = latex.slice(i, j);\n result += formatSeparators\n ? formatArabicNumber(run)\n : toArabicNumerals(run, style);\n i = j;\n continue;\n }\n } else {\n result += ch ?? '';\n }\n i++;\n }\n return result;\n}\n\n// ═══════════════════════════════════════════════════════════════\n// KaTeX macros & CSS classes\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Build the macro table for KaTeX. Custom user macros are merged on\n * top of the built-in ones.\n */\nexport function getArabicMacros(\n customMacros: Record<string, string> = {},\n): Record<string, string> {\n return {\n // Trigonometric\n '\\\\جا': '\\\\operatorname{جا}',\n '\\\\جتا': '\\\\operatorname{جتا}',\n '\\\\ظا': '\\\\operatorname{ظا}',\n '\\\\ظتا': '\\\\operatorname{ظتا}',\n '\\\\قا': '\\\\operatorname{قا}',\n '\\\\قتا': '\\\\operatorname{قتا}',\n\n // Logarithms\n '\\\\لو': '\\\\operatorname{لو}',\n '\\\\لغ': '\\\\operatorname{لغ}',\n\n // Limits\n '\\\\نها': '\\\\operatorname*{نها}',\n '\\\\نهى': '\\\\operatorname*{نها}',\n\n // Max/min\n '\\\\اقصى': '\\\\operatorname*{أقصى}',\n '\\\\ادنى': '\\\\operatorname*{أدنى}',\n\n // Imaginary / constants\n '\\\\ت': '\\\\text{ت}',\n '\\\\هـ': '\\\\text{هـ}',\n\n ...customMacros,\n };\n}\n\n/**\n * Build the list of CSS classes applied to the wrapper span.\n * The \"katex-arabic\" base class is always present; the rest are\n * toggled by options.\n */\nexport function buildCssClasses(options: PartialArabicOptions): string[] {\n const opts = resolveOptions(options);\n const classes = ['katex-arabic'];\n\n if (opts.mirrorBigOperators) classes.push('mirror-big-ops');\n if (opts.mirrorSqrt) classes.push('mirror-sqrt');\n if (opts.mirrorBrackets) classes.push('mirror-brackets');\n if (opts.mirrorSymbols) classes.push('mirror-symbols');\n if (opts.fullArabicMode) classes.push('full-rtl-mode');\n if (opts.displayMode) classes.push('is-display');\n else classes.push('is-inline');\n\n return classes;\n}\n\n/**\n * Get mirror classes without the base class. Useful when the wrapper\n * already has the base class.\n */\nexport function getMirrorClasses(options: PartialArabicOptions): string {\n return buildCssClasses(options).filter((c) => c !== 'katex-arabic').join(' ');\n}\n\n// ═══════════════════════════════════════════════════════════════\n// HTML wrapping\n// ═══════════════════════════════════════════════════════════════\n\n/**\n * Wrap the KaTeX-rendered HTML in a span with the right classes\n * and inline styles for Arabic rendering.\n */\nexport function wrapWithArabicStyles(\n html: string,\n options: PartialArabicOptions,\n): string {\n const opts = resolveOptions(options);\n const classes = buildCssClasses(opts);\n\n // Use CSS variables so all sizes/colors are themable.\n return (\n `<span class=\"${classes.join(' ')}\" dir=\"${opts.direction}\" ` +\n `style=\"font-family:'${opts.fontFamily}','KaTeX_Main',serif;` +\n `direction:${opts.direction};--ka-op-scale:${opts.operatorScale};\">${html}</span>`\n );\n}\n","/**\n * ════════════════════════════════════════════════════════════════\n * render.ts\n * Core render functions for KaTeX Arabic.\n *\n * This module is separated from index.ts so that hooks.ts can\n * import render functions without creating a circular dependency\n * through the barrel export.\n * ════════════════════════════════════════════════════════════════\n */\n\nimport katex from 'katex';\nimport {\n processArabicLatex,\n getArabicMacros,\n wrapWithArabicStyles,\n resolveOptions,\n renderCache,\n buildCacheKey,\n} from './rtlRenderer';\nimport type { PartialArabicOptions } from './types';\n\n/**\n * Escape a string so it can be safely embedded in HTML.\n * Used for error fallbacks where we display raw LaTeX source.\n */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n/**\n * Render LaTeX with Arabic transforms to an HTML string.\n *\n * Always returns a valid string — errors are reported via the\n * `throwOnError` option (which defaults to false, producing a\n * styled error message in the output).\n */\nexport function renderArabicToString(\n latex: string,\n options: PartialArabicOptions = {},\n): string {\n return renderArabicWithMeta(latex, options).html;\n}\n\n/**\n * Render LaTeX with Arabic transforms and return both the HTML and\n * any error message in a single pass. This avoids the double-render\n * cost of validate + render when callers need both.\n *\n * Returns `{ html, error }` where `error` is null on success.\n */\nexport function renderArabicWithMeta(\n latex: string,\n options: PartialArabicOptions = {},\n): { html: string; error: string | null } {\n const opts = resolveOptions(options);\n\n // Fast path: return from LRU cache when the same (latex, options)\n // pair has been rendered before.\n const cacheKey = buildCacheKey(latex, opts);\n const cached = renderCache.get(cacheKey);\n if (cached !== undefined) {\n return { html: cached, error: null };\n }\n\n const processedLatex = processArabicLatex(latex, opts);\n\n try {\n const html = katex.renderToString(processedLatex, {\n displayMode: opts.displayMode,\n throwOnError: true,\n output: opts.output,\n strict: opts.strict,\n trust: opts.trust,\n minRuleThickness: opts.minRuleThickness,\n macros: {\n ...getArabicMacros(opts.macros),\n ...(opts.macros ?? {}),\n },\n });\n const wrapped = wrapWithArabicStyles(html, opts);\n renderCache.set(cacheKey, wrapped);\n return { html: wrapped, error: null };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (opts.throwOnError) {\n throw error instanceof Error ? error : new Error(message);\n }\n // eslint-disable-next-line no-console\n console.error('[katex-arabic] render failed:', message, '\\nLaTeX:', latex);\n return {\n html:\n `<span class=\"katex-arabic katex-arabic--error\" dir=\"rtl\" role=\"alert\" ` +\n `style=\"color:#dc2626;font-family:monospace;\">` +\n `<span class=\"katex-arabic__error-icon\" aria-hidden=\"true\">⚠</span> ` +\n `<span class=\"katex-arabic__error-text\">${escapeHtml(latex)}</span>` +\n `</span>`,\n error: message,\n };\n }\n}\n\n/**\n * Render LaTeX with Arabic transforms into a DOM element.\n * Replaces the element's children with the rendered output.\n */\nexport function renderArabic(\n latex: string,\n element: HTMLElement,\n options: PartialArabicOptions = {},\n): void {\n const html = renderArabicToString(latex, options);\n element.innerHTML = html;\n applyDomStyles(element, options);\n}\n\n/**\n * Render a batch of LaTeX strings in one call.\n *\n * Returns an array of `{ html, error }` results in the same order as\n * the input. This is more efficient than calling `renderArabicToString`\n * in a loop because the options object is resolved once and the LRU\n * cache is shared across the batch.\n *\n * @param items Array of LaTeX strings (or `{ latex, options }` objects\n * for per-item overrides).\n * @param options Shared options applied to every item unless overridden.\n */\nexport function renderArabicBatch(\n items: ReadonlyArray<string | { latex: string; options?: PartialArabicOptions }>,\n options: PartialArabicOptions = {},\n): Array<{ html: string; error: string | null }> {\n return items.map((item) => {\n if (typeof item === 'string') {\n return renderArabicWithMeta(item, options);\n }\n return renderArabicWithMeta(item.latex, { ...options, ...item.options });\n });\n}\n\n/**\n * Process LaTeX without rendering. Useful for inspecting the\n * transformed source or for caching.\n */\nexport function processLatex(\n latex: string,\n options: PartialArabicOptions = {},\n): string {\n return processArabicLatex(latex, options);\n}\n\n/**\n * Validate that a LaTeX string can be rendered without errors.\n * Returns null on success, or an error message on failure.\n */\nexport function validateLatex(\n latex: string,\n options: PartialArabicOptions = {},\n): string | null {\n const opts = resolveOptions(options);\n const processed = processArabicLatex(latex, opts);\n try {\n katex.renderToString(processed, {\n displayMode: opts.displayMode,\n throwOnError: true,\n strict: opts.strict,\n trust: opts.trust,\n });\n return null;\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n}\n\n/**\n * Apply per-element styles after the HTML has been written.\n * This is the DOM-level equivalent of the inline styles we add\n * in `wrapWithArabicStyles`.\n */\nfunction applyDomStyles(element: HTMLElement, options: PartialArabicOptions): void {\n const opts = resolveOptions(options);\n const katexEl = element.querySelector('.katex') as HTMLElement | null;\n if (katexEl) {\n katexEl.setAttribute('dir', opts.direction);\n katexEl.style.fontFamily = `'${opts.fontFamily}', 'KaTeX_Main', serif`;\n katexEl.style.direction = opts.direction;\n }\n}"],"names":["escapeRegex"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * ════════════════════════════════════════════════════════════════
3
+ * render.ts
4
+ * Core render functions for KaTeX Arabic.
5
+ *
6
+ * This module is separated from index.ts so that hooks.ts can
7
+ * import render functions without creating a circular dependency
8
+ * through the barrel export.
9
+ * ════════════════════════════════════════════════════════════════
10
+ */
11
+ import type { PartialArabicOptions } from './types';
12
+ /**
13
+ * Render LaTeX with Arabic transforms to an HTML string.
14
+ *
15
+ * Always returns a valid string — errors are reported via the
16
+ * `throwOnError` option (which defaults to false, producing a
17
+ * styled error message in the output).
18
+ */
19
+ export declare function renderArabicToString(latex: string, options?: PartialArabicOptions): string;
20
+ /**
21
+ * Render LaTeX with Arabic transforms and return both the HTML and
22
+ * any error message in a single pass. This avoids the double-render
23
+ * cost of validate + render when callers need both.
24
+ *
25
+ * Returns `{ html, error }` where `error` is null on success.
26
+ */
27
+ export declare function renderArabicWithMeta(latex: string, options?: PartialArabicOptions): {
28
+ html: string;
29
+ error: string | null;
30
+ };
31
+ /**
32
+ * Render LaTeX with Arabic transforms into a DOM element.
33
+ * Replaces the element's children with the rendered output.
34
+ */
35
+ export declare function renderArabic(latex: string, element: HTMLElement, options?: PartialArabicOptions): void;
36
+ /**
37
+ * Render a batch of LaTeX strings in one call.
38
+ *
39
+ * Returns an array of `{ html, error }` results in the same order as
40
+ * the input. This is more efficient than calling `renderArabicToString`
41
+ * in a loop because the options object is resolved once and the LRU
42
+ * cache is shared across the batch.
43
+ *
44
+ * @param items Array of LaTeX strings (or `{ latex, options }` objects
45
+ * for per-item overrides).
46
+ * @param options Shared options applied to every item unless overridden.
47
+ */
48
+ export declare function renderArabicBatch(items: ReadonlyArray<string | {
49
+ latex: string;
50
+ options?: PartialArabicOptions;
51
+ }>, options?: PartialArabicOptions): Array<{
52
+ html: string;
53
+ error: string | null;
54
+ }>;
55
+ /**
56
+ * Process LaTeX without rendering. Useful for inspecting the
57
+ * transformed source or for caching.
58
+ */
59
+ export declare function processLatex(latex: string, options?: PartialArabicOptions): string;
60
+ /**
61
+ * Validate that a LaTeX string can be rendered without errors.
62
+ * Returns null on success, or an error message on failure.
63
+ */
64
+ export declare function validateLatex(latex: string, options?: PartialArabicOptions): string | null;
65
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/lib/katex-arabic/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAepD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA8CxC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,oBAAyB,GACjC,IAAI,CAIN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,oBAAoB,CAAA;CAAE,CAAC,EAChF,OAAO,GAAE,oBAAyB,GACjC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAO/C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,MAAM,GAAG,IAAI,CAcf"}