|
5 | 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
6 | 6 | <title>OpenCluely</title> |
7 | 7 | <script src="lib/markdown.js"></script> |
| 8 | + <script src="lib/mathrender.js"></script> |
8 | 9 | <style> |
9 | 10 | .text-white { color: white; } |
10 | 11 | .text-gray-300 { color: #d1d5db; } |
|
834 | 835 | // Render text content |
835 | 836 | const textHtml = renderMarkdown(textContent); |
836 | 837 | document.getElementById('text-content').innerHTML = textHtml; |
837 | | - renderMathInElement(document.getElementById('text-content')); |
| 838 | + if (window.renderMathInElement) window.renderMathInElement(document.getElementById('text-content')); |
838 | 839 |
|
839 | 840 | // Render code blocks |
840 | 841 | const codeContainer = document.getElementById('code-content'); |
|
879 | 880 | const html = renderMarkdown(response); |
880 | 881 | const full = document.getElementById('full-markdown'); |
881 | 882 | full.innerHTML = html; |
882 | | - renderMathInElement(full); |
| 883 | + if (window.renderMathInElement) window.renderMathInElement(full); |
883 | 884 |
|
884 | 885 | // Highlight any code if Prism is available |
885 | 886 | if (typeof Prism !== 'undefined') { |
|
907 | 908 | return div.innerHTML; |
908 | 909 | } |
909 | 910 |
|
910 | | - function escapeHtmlStr(text) { |
911 | | - return String(text) |
912 | | - .replace(/&/g, '&') |
913 | | - .replace(/</g, '<') |
914 | | - .replace(/>/g, '>'); |
915 | | - } |
916 | | - |
917 | | - function latexToHtml(tex) { |
918 | | - let s = escapeHtmlStr(tex); |
919 | | - s = s.replace(/\\frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}/g, '($1)/($2)'); |
920 | | - s = s.replace(/\\(?:text|mathrm|mathbf|mathit|mathcal|operatorname)\s*\{([^{}]*)\}/g, '$1'); |
921 | | - const sym = { |
922 | | - '\\cdot':'·','\\times':'×','\\div':'÷','\\pm':'±','\\mp':'∓', |
923 | | - '\\le':'≤','\\leq':'≤','\\ge':'≥','\\geq':'≥','\\neq':'≠','\\ne':'≠', |
924 | | - '\\to':'→','\\rightarrow':'→','\\leftarrow':'←','\\Rightarrow':'⇒','\\iff':'⇔', |
925 | | - '\\infty':'∞','\\in':'∈','\\notin':'∉','\\subset':'⊂','\\subseteq':'⊆', |
926 | | - '\\cup':'∪','\\cap':'∩','\\forall':'∀','\\exists':'∃','\\emptyset':'∅', |
927 | | - '\\ldots':'…','\\dots':'…','\\cdots':'⋯','\\equiv':'≡','\\approx':'≈','\\sim':'∼', |
928 | | - '\\sum':'∑','\\prod':'∏','\\int':'∫','\\sqrt':'√','\\partial':'∂','\\nabla':'∇', |
929 | | - '\\alpha':'α','\\beta':'β','\\gamma':'γ','\\delta':'δ','\\epsilon':'ε','\\varepsilon':'ε', |
930 | | - '\\zeta':'ζ','\\eta':'η','\\theta':'θ','\\kappa':'κ','\\lambda':'λ','\\mu':'μ', |
931 | | - '\\nu':'ν','\\xi':'ξ','\\pi':'π','\\rho':'ρ','\\sigma':'σ','\\tau':'τ', |
932 | | - '\\phi':'φ','\\varphi':'φ','\\chi':'χ','\\psi':'ψ','\\omega':'ω', |
933 | | - '\\Gamma':'Γ','\\Delta':'Δ','\\Theta':'Θ','\\Lambda':'Λ','\\Pi':'Π','\\Sigma':'Σ','\\Phi':'Φ','\\Omega':'Ω', |
934 | | - '\\bmod':'mod','\\mod':'mod','\\gcd':'gcd','\\log':'log','\\ln':'ln','\\lg':'lg', |
935 | | - '\\min':'min','\\max':'max','\\deg':'deg','\\dim':'dim', |
936 | | - '\\lfloor':'⌊','\\rfloor':'⌋','\\lceil':'⌈','\\rceil':'⌉','\\langle':'⟨','\\rangle':'⟩', |
937 | | - '\\,':' ','\\;':' ','\\:':' ','\\!':'','\\quad':' ','\\qquad':' ' |
938 | | - }; |
939 | | - s = s.replace(/\\[a-zA-Z]+|\\[,;:!]/g, m => (sym[m] !== undefined ? sym[m] : '')); |
940 | | - s = s.replace(/\\%/g, '%').replace(/\\_/g, '_').replace(/\\&/g, '&').replace(/\\#/g, '#'); |
941 | | - s = s.replace(/\^\{([^{}]*)\}/g, '<sup>$1</sup>'); |
942 | | - s = s.replace(/\^([A-Za-z0-9])/g, '<sup>$1</sup>'); |
943 | | - s = s.replace(/_\{([^{}]*)\}/g, '<sub>$1</sub>'); |
944 | | - s = s.replace(/_([A-Za-z0-9])/g, '<sub>$1</sub>'); |
945 | | - s = s.replace(/[{}]/g, ''); |
946 | | - return s; |
947 | | - } |
948 | | - |
949 | | - function convertMathText(text) { |
950 | | - if (text.indexOf('$') === -1) return null; |
951 | | - const re = /\$\$([\s\S]+?)\$\$|\$([^$\n]+?)\$/g; |
952 | | - let out = '', last = 0, m, found = false; |
953 | | - while ((m = re.exec(text)) !== null) { |
954 | | - found = true; |
955 | | - out += escapeHtmlStr(text.slice(last, m.index)); |
956 | | - const tex = m[1] != null ? m[1] : m[2]; |
957 | | - out += '<span class="math">' + latexToHtml(tex) + '</span>'; |
958 | | - last = re.lastIndex; |
959 | | - } |
960 | | - if (!found) return null; |
961 | | - out += escapeHtmlStr(text.slice(last)); |
962 | | - return out; |
963 | | - } |
964 | | - |
965 | | - function renderMathInElement(root) { |
966 | | - if (!root) return; |
967 | | - const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { |
968 | | - acceptNode(node) { |
969 | | - if (!node.nodeValue || node.nodeValue.indexOf('$') === -1) return NodeFilter.FILTER_REJECT; |
970 | | - let p = node.parentNode; |
971 | | - while (p && p !== root) { |
972 | | - const tag = p.nodeName; |
973 | | - if (tag === 'CODE' || tag === 'PRE' || tag === 'SCRIPT' || tag === 'STYLE') return NodeFilter.FILTER_REJECT; |
974 | | - p = p.parentNode; |
975 | | - } |
976 | | - return NodeFilter.FILTER_ACCEPT; |
977 | | - } |
978 | | - }); |
979 | | - const targets = []; |
980 | | - let n; |
981 | | - while ((n = walker.nextNode())) targets.push(n); |
982 | | - targets.forEach(node => { |
983 | | - const html = convertMathText(node.nodeValue); |
984 | | - if (html == null) return; |
985 | | - const span = document.createElement('span'); |
986 | | - span.innerHTML = html; |
987 | | - node.parentNode.replaceChild(span, node); |
988 | | - }); |
989 | | - } |
990 | 911 |
|
991 | 912 | // Handle window focus |
992 | 913 | window.addEventListener('focus', () => { |
|
0 commit comments