black-yt commited on
Commit
88e7786
·
1 Parent(s): 80d3a08

Improve frontend image and Mermaid rendering

Browse files
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.language-mermaid").forEach(function (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 protectedMath = protectMathSegments(unwrapFullMarkdownFence(text));
 
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 "![" + alt + "](<" + src + ">)";
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 } });