diff --git a/api/config.py b/api/config.py index f14e8c43b2..c071007e1f 100644 --- a/api/config.py +++ b/api/config.py @@ -1016,6 +1016,9 @@ def verify_hermes_imports() -> tuple: ".json": "application/json", ".html": "text/html", ".htm": "text/html", + ".md": "text/markdown", + ".mkd": "text/markdown", + ".mkdn": "text/markdown", ".xls": "application/vnd.ms-excel", ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".doc": "application/msword", diff --git a/api/routes.py b/api/routes.py index 91d0b73a12..ba2cb5dd1e 100644 --- a/api/routes.py +++ b/api/routes.py @@ -18767,7 +18767,7 @@ def _handle_media(handler, parsed): "video/mp4", "video/quicktime", "video/webm", "video/ogg", "application/pdf", } - _SESSION_MEDIA_TOKEN_TYPES = _INLINE_IMAGE_TYPES | _AUDIO_VIDEO_PDF_TYPES | {"text/html"} + _SESSION_MEDIA_TOKEN_TYPES = _INLINE_IMAGE_TYPES | _AUDIO_VIDEO_PDF_TYPES | {"text/html", "text/markdown"} session_media_allowed = _session_media_token_allows_path( qs.get("session_id", [""])[0], target, diff --git a/static/style.css b/static/style.css index d6d8f505fa..d1d35eecd7 100644 --- a/static/style.css +++ b/static/style.css @@ -6582,6 +6582,17 @@ main.main > .main-view:not([id="mainChat"]):not([id="mainSettings"]) .main-view- .html-preview-fallback{padding:8px;font-size:13px;} .html-preview-spinner{animation:pulse 1.5s ease-in-out infinite;} +/* ── Markdown inline preview ──────────────────────────────────────────────── */ +.md-inline-load{color:var(--muted);font-size:13px;padding:8px 12px;border:1px dashed var(--border);border-radius:8px;margin:6px 0;} +.md-preview-spinner{animation:pulse 1.5s ease-in-out infinite;} +.md-inline-wrap{border:1px solid var(--border);border-radius:6px;overflow:hidden;margin:4px 0;background:var(--surface);} +.md-inline-header{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;background:var(--surface-2);font-size:12px;font-weight:600;border-bottom:1px solid var(--border);} +.md-preview-title{color:var(--text);} +.md-inline-fallback{padding:8px;font-size:13px;} +.md-inline-content{padding:12px 16px;overflow-x:auto;} +.md-inline-content > :first-child{margin-top:0;} +.md-inline-content > :last-child{margin-bottom:0;} + /* ── Insights panel (#464) ────────────────────────────────────────────────── */ main.main.showing-insights > #mainInsights{display:flex;overflow-y:auto;} .insights-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-bottom:16px;} diff --git a/static/ui.js b/static/ui.js index 00952e4a1d..81a2b1ac71 100644 --- a/static/ui.js +++ b/static/ui.js @@ -2499,6 +2499,7 @@ const _SVG_EXTS=/\.svg$/i; const _AUDIO_EXTS=/\.(mp3|ogg|wav|m4a|aac|flac|wma|opus|webm|oga)$/i; const _VIDEO_EXTS=/\.(mp4|webm|mkv|mov|avi|ogv|m4v)$/i; const _CSV_EXTS=/\.csv$/i; +const _MD_EXTS=/\.(md|mkd|mkdn)$/i; const _EXCALIDRAW_EXTS=/\.excalidraw$/i; // ── Media playback speed controls ───────────────────────────────────────── const MEDIA_PLAYBACK_RATES=[0.5,0.75,1,1.25,1.5,2]; @@ -2661,6 +2662,9 @@ function _inlineMediaHtmlForRef(ref, sessionId, altText){ if(_HTML_EXTS.test(ref)){ return `