Spaces:
Running
Running
Render math in frontend Markdown
Browse files- frontend/static/app.css +11 -0
- frontend/static/app.js +41 -1
- frontend/static/index.html +3 -0
frontend/static/app.css
CHANGED
|
@@ -390,6 +390,17 @@ button {
|
|
| 390 |
word-break: break-word;
|
| 391 |
}
|
| 392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
.markdown-body > *:first-child {
|
| 394 |
margin-top: 0;
|
| 395 |
}
|
|
|
|
| 390 |
word-break: break-word;
|
| 391 |
}
|
| 392 |
|
| 393 |
+
.markdown-body .katex {
|
| 394 |
+
font-size: 1.02em;
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
.markdown-body .katex-display {
|
| 398 |
+
margin: 0.85rem 0;
|
| 399 |
+
overflow-x: auto;
|
| 400 |
+
overflow-y: hidden;
|
| 401 |
+
padding-bottom: 0.1rem;
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
.markdown-body > *:first-child {
|
| 405 |
margin-top: 0;
|
| 406 |
}
|
frontend/static/app.js
CHANGED
|
@@ -164,14 +164,53 @@
|
|
| 164 |
.replaceAll("'", "'");
|
| 165 |
}
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
function renderMarkdown(text) {
|
| 168 |
if (!window.marked || !window.DOMPurify) {
|
| 169 |
console.warn("Markdown renderer unavailable; falling back to plain text.");
|
| 170 |
return "<pre>" + escapeHtml(text) + "</pre>";
|
| 171 |
}
|
| 172 |
try {
|
| 173 |
-
var
|
|
|
|
| 174 |
var safeHtml = window.DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true } });
|
|
|
|
| 175 |
return '<div class="markdown-body">' + safeHtml + "</div>";
|
| 176 |
} catch (e) {
|
| 177 |
console.warn("Markdown rendering failed; falling back to plain text.", e);
|
|
@@ -338,6 +377,7 @@
|
|
| 338 |
toggleEvent(node);
|
| 339 |
});
|
| 340 |
timeline.appendChild(node);
|
|
|
|
| 341 |
setEventExpanded(node, true, false);
|
| 342 |
scrollTimeline(shouldFollow);
|
| 343 |
}
|
|
|
|
| 164 |
.replaceAll("'", "'");
|
| 165 |
}
|
| 166 |
|
| 167 |
+
function protectMathSegments(text) {
|
| 168 |
+
var segments = [];
|
| 169 |
+
var protectedText = String(text || "").replace(/(\$\$[\s\S]+?\$\$|\\\[[\s\S]+?\\\]|\\\([\s\S]+?\\\))/g, function (match) {
|
| 170 |
+
var token = "@@RH_MATH_" + segments.length + "@@";
|
| 171 |
+
segments.push({ token: token, text: match });
|
| 172 |
+
return token;
|
| 173 |
+
});
|
| 174 |
+
return { text: protectedText, segments: segments };
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
function restoreMathSegments(html, segments) {
|
| 178 |
+
var restored = String(html || "");
|
| 179 |
+
(segments || []).forEach(function (segment) {
|
| 180 |
+
restored = restored.split(segment.token).join(escapeHtml(segment.text));
|
| 181 |
+
});
|
| 182 |
+
return restored;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
function renderMathInMarkdown(container) {
|
| 186 |
+
if (!window.renderMathInElement) return;
|
| 187 |
+
container.querySelectorAll(".markdown-body").forEach(function (body) {
|
| 188 |
+
try {
|
| 189 |
+
window.renderMathInElement(body, {
|
| 190 |
+
delimiters: [
|
| 191 |
+
{ left: "$$", right: "$$", display: true },
|
| 192 |
+
{ left: "\\[", right: "\\]", display: true },
|
| 193 |
+
{ left: "\\(", right: "\\)", display: false }
|
| 194 |
+
],
|
| 195 |
+
ignoredTags: ["script", "noscript", "style", "textarea", "pre", "code"],
|
| 196 |
+
throwOnError: false
|
| 197 |
+
});
|
| 198 |
+
} catch (e) {
|
| 199 |
+
console.warn("Math rendering failed.", e);
|
| 200 |
+
}
|
| 201 |
+
});
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
function renderMarkdown(text) {
|
| 205 |
if (!window.marked || !window.DOMPurify) {
|
| 206 |
console.warn("Markdown renderer unavailable; falling back to plain text.");
|
| 207 |
return "<pre>" + escapeHtml(text) + "</pre>";
|
| 208 |
}
|
| 209 |
try {
|
| 210 |
+
var protectedMath = protectMathSegments(text);
|
| 211 |
+
var rawHtml = window.marked.parse(protectedMath.text, { gfm: true, breaks: false, async: false });
|
| 212 |
var safeHtml = window.DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true } });
|
| 213 |
+
safeHtml = restoreMathSegments(safeHtml, protectedMath.segments);
|
| 214 |
return '<div class="markdown-body">' + safeHtml + "</div>";
|
| 215 |
} catch (e) {
|
| 216 |
console.warn("Markdown rendering failed; falling back to plain text.", e);
|
|
|
|
| 377 |
toggleEvent(node);
|
| 378 |
});
|
| 379 |
timeline.appendChild(node);
|
| 380 |
+
renderMathInMarkdown(node);
|
| 381 |
setEventExpanded(node, true, false);
|
| 382 |
scrollTimeline(shouldFollow);
|
| 383 |
}
|
frontend/static/index.html
CHANGED
|
@@ -5,6 +5,7 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
<title>ResearchHarness Chat</title>
|
| 7 |
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=rocket-1" />
|
|
|
|
| 8 |
<link rel="stylesheet" href="/static/app.css" />
|
| 9 |
</head>
|
| 10 |
<body>
|
|
@@ -80,6 +81,8 @@
|
|
| 80 |
</nav>
|
| 81 |
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.6/dist/purify.min.js"></script>
|
| 82 |
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js"></script>
|
|
|
|
|
|
|
| 83 |
<script src="/static/app.js"></script>
|
| 84 |
</body>
|
| 85 |
</html>
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
<title>ResearchHarness Chat</title>
|
| 7 |
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=rocket-1" />
|
| 8 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" />
|
| 9 |
<link rel="stylesheet" href="/static/app.css" />
|
| 10 |
</head>
|
| 11 |
<body>
|
|
|
|
| 81 |
</nav>
|
| 82 |
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.6/dist/purify.min.js"></script>
|
| 83 |
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js"></script>
|
| 84 |
+
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
|
| 85 |
+
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"></script>
|
| 86 |
<script src="/static/app.js"></script>
|
| 87 |
</body>
|
| 88 |
</html>
|