Spaces:
Running
Running
Improve frontend image and Mermaid rendering
Browse files- frontend/local_server.py +1 -1
- frontend/static/app.css +10 -0
- frontend/static/app.js +16 -2
frontend/local_server.py
CHANGED
|
@@ -217,7 +217,7 @@ def _resolve_workspace_file_path(workspace_root: Path, raw_path: str) -> Path:
|
|
| 217 |
raise HTTPException(status_code=403, detail="workspace file path is outside the workspace") from exc
|
| 218 |
if not resolved.is_file():
|
| 219 |
raise HTTPException(status_code=404, detail="workspace file does not exist")
|
| 220 |
-
if resolved.suffix.lower() not in {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"}:
|
| 221 |
raise HTTPException(status_code=415, detail="only workspace image files can be displayed inline")
|
| 222 |
return resolved
|
| 223 |
|
|
|
|
| 217 |
raise HTTPException(status_code=403, detail="workspace file path is outside the workspace") from exc
|
| 218 |
if not resolved.is_file():
|
| 219 |
raise HTTPException(status_code=404, detail="workspace file does not exist")
|
| 220 |
+
if resolved.suffix.lower() not in {".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"}:
|
| 221 |
raise HTTPException(status_code=415, detail="only workspace image files can be displayed inline")
|
| 222 |
return resolved
|
| 223 |
|
frontend/static/app.css
CHANGED
|
@@ -467,6 +467,8 @@ button {
|
|
| 467 |
}
|
| 468 |
|
| 469 |
.message-body {
|
|
|
|
|
|
|
| 470 |
padding: 14px 16px;
|
| 471 |
}
|
| 472 |
|
|
@@ -477,6 +479,8 @@ button {
|
|
| 477 |
}
|
| 478 |
|
| 479 |
.event-body-inner {
|
|
|
|
|
|
|
| 480 |
padding: 14px 16px;
|
| 481 |
}
|
| 482 |
|
|
@@ -523,6 +527,9 @@ button {
|
|
| 523 |
}
|
| 524 |
|
| 525 |
.markdown-body {
|
|
|
|
|
|
|
|
|
|
| 526 |
line-height: 1.6;
|
| 527 |
word-break: break-word;
|
| 528 |
}
|
|
@@ -604,13 +611,16 @@ button {
|
|
| 604 |
|
| 605 |
.markdown-body img {
|
| 606 |
display: block;
|
|
|
|
| 607 |
max-width: 100%;
|
| 608 |
height: auto;
|
| 609 |
margin: 0.65rem 0;
|
| 610 |
border-radius: 14px;
|
|
|
|
| 611 |
}
|
| 612 |
|
| 613 |
.mermaid-chart {
|
|
|
|
| 614 |
margin: 0.85rem 0;
|
| 615 |
overflow-x: auto;
|
| 616 |
}
|
|
|
|
| 467 |
}
|
| 468 |
|
| 469 |
.message-body {
|
| 470 |
+
min-width: 0;
|
| 471 |
+
max-width: 100%;
|
| 472 |
padding: 14px 16px;
|
| 473 |
}
|
| 474 |
|
|
|
|
| 479 |
}
|
| 480 |
|
| 481 |
.event-body-inner {
|
| 482 |
+
min-width: 0;
|
| 483 |
+
max-width: 100%;
|
| 484 |
padding: 14px 16px;
|
| 485 |
}
|
| 486 |
|
|
|
|
| 527 |
}
|
| 528 |
|
| 529 |
.markdown-body {
|
| 530 |
+
min-width: 0;
|
| 531 |
+
max-width: 100%;
|
| 532 |
+
overflow-wrap: anywhere;
|
| 533 |
line-height: 1.6;
|
| 534 |
word-break: break-word;
|
| 535 |
}
|
|
|
|
| 611 |
|
| 612 |
.markdown-body img {
|
| 613 |
display: block;
|
| 614 |
+
width: auto;
|
| 615 |
max-width: 100%;
|
| 616 |
height: auto;
|
| 617 |
margin: 0.65rem 0;
|
| 618 |
border-radius: 14px;
|
| 619 |
+
object-fit: contain;
|
| 620 |
}
|
| 621 |
|
| 622 |
.mermaid-chart {
|
| 623 |
+
max-width: 100%;
|
| 624 |
margin: 0.85rem 0;
|
| 625 |
overflow-x: auto;
|
| 626 |
}
|
frontend/static/app.js
CHANGED
|
@@ -198,6 +198,15 @@
|
|
| 198 |
return template.innerHTML;
|
| 199 |
}
|
| 200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
function unwrapFullMarkdownFence(text) {
|
| 202 |
var source = String(text || "").trim();
|
| 203 |
var match = /^(```|~~~)[ \t]*(markdown|md|gfm)[^\n]*\n([\s\S]*?)\n\1[ \t]*$/i.exec(source);
|
|
@@ -231,7 +240,11 @@
|
|
| 231 |
console.warn("Mermaid initialization failed.", e);
|
| 232 |
return;
|
| 233 |
}
|
| 234 |
-
container.querySelectorAll(".markdown-body pre code
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
var pre = code.closest("pre");
|
| 236 |
if (!pre) return;
|
| 237 |
var source = code.textContent || "";
|
|
@@ -253,7 +266,8 @@
|
|
| 253 |
return "<pre>" + escapeHtml(text) + "</pre>";
|
| 254 |
}
|
| 255 |
try {
|
| 256 |
-
var
|
|
|
|
| 257 |
var rawHtml = window.marked.parse(protectedMath.text, { gfm: true, breaks: false, async: false });
|
| 258 |
rawHtml = rewriteWorkspaceImageSources(rawHtml);
|
| 259 |
var safeHtml = window.DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true } });
|
|
|
|
| 198 |
return template.innerHTML;
|
| 199 |
}
|
| 200 |
|
| 201 |
+
function normalizeMarkdownImageDestinations(text) {
|
| 202 |
+
return String(text || "").replace(/!\[([^\]\n]*)\]\(([^)\n]+)\)/g, function (match, alt, target) {
|
| 203 |
+
var src = String(target || "").trim();
|
| 204 |
+
if (!src || src[0] === "<" || !/\s/.test(src) || isRemoteOrInlineImageSrc(src)) return match;
|
| 205 |
+
if (/[<>]/.test(src)) return match;
|
| 206 |
+
return "";
|
| 207 |
+
});
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
function unwrapFullMarkdownFence(text) {
|
| 211 |
var source = String(text || "").trim();
|
| 212 |
var match = /^(```|~~~)[ \t]*(markdown|md|gfm)[^\n]*\n([\s\S]*?)\n\1[ \t]*$/i.exec(source);
|
|
|
|
| 240 |
console.warn("Mermaid initialization failed.", e);
|
| 241 |
return;
|
| 242 |
}
|
| 243 |
+
container.querySelectorAll(".markdown-body pre code").forEach(function (code) {
|
| 244 |
+
var className = String(code.className || "").toLowerCase();
|
| 245 |
+
if (!className.split(/\s+/).some(function (name) {
|
| 246 |
+
return name === "language-mermaid" || name === "lang-mermaid" || name === "language-mmd" || name === "lang-mmd";
|
| 247 |
+
})) return;
|
| 248 |
var pre = code.closest("pre");
|
| 249 |
if (!pre) return;
|
| 250 |
var source = code.textContent || "";
|
|
|
|
| 266 |
return "<pre>" + escapeHtml(text) + "</pre>";
|
| 267 |
}
|
| 268 |
try {
|
| 269 |
+
var normalizedText = normalizeMarkdownImageDestinations(unwrapFullMarkdownFence(text));
|
| 270 |
+
var protectedMath = protectMathSegments(normalizedText);
|
| 271 |
var rawHtml = window.marked.parse(protectedMath.text, { gfm: true, breaks: false, async: false });
|
| 272 |
rawHtml = rewriteWorkspaceImageSources(rawHtml);
|
| 273 |
var safeHtml = window.DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true } });
|