feat: full Markdown support in service card comments (mistune)
This commit is contained in:
+7
-24
@@ -16,6 +16,7 @@ from typing import Optional
|
||||
|
||||
import docker
|
||||
import requests
|
||||
import mistune
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException, Query, Request, UploadFile, status
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -299,34 +300,16 @@ def normalize_web_target(url: str) -> str:
|
||||
return f"http://{raw}"
|
||||
|
||||
|
||||
_md = mistune.create_markdown(
|
||||
escape=True,
|
||||
plugins=["strikethrough", "table", "task_lists"],
|
||||
)
|
||||
|
||||
def format_service_comment(raw_text: str) -> Markup:
|
||||
raw = (raw_text or "").replace("\r\n", "\n").replace("\r", "\n").strip()
|
||||
if not raw:
|
||||
return Markup("")
|
||||
escaped = str(escape(raw))
|
||||
# Support pasted/plain markdown-like bold fragments.
|
||||
escaped = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", escaped, flags=re.DOTALL)
|
||||
# Allow a small safe subset of pasted HTML tags.
|
||||
replacements = {
|
||||
"<b>": "<b>",
|
||||
"</b>": "</b>",
|
||||
"<strong>": "<strong>",
|
||||
"</strong>": "</strong>",
|
||||
"<i>": "<i>",
|
||||
"</i>": "</i>",
|
||||
"<em>": "<em>",
|
||||
"</em>": "</em>",
|
||||
"<u>": "<u>",
|
||||
"</u>": "</u>",
|
||||
"<br>": "<br>",
|
||||
"<br/>": "<br>",
|
||||
"<br />": "<br>",
|
||||
}
|
||||
for src, dst in replacements.items():
|
||||
escaped = escaped.replace(src, dst)
|
||||
escaped = escaped.replace("\n", "<br>")
|
||||
return Markup(escaped)
|
||||
|
||||
return Markup(_md(raw))
|
||||
|
||||
def parse_rdp_target(target: str) -> dict:
|
||||
raw = (target or "").strip()
|
||||
|
||||
@@ -7,3 +7,4 @@ jinja2==3.1.6
|
||||
passlib[argon2]==1.7.4
|
||||
docker==7.1.0
|
||||
itsdangerous==2.2.0
|
||||
mistune==3.1.3
|
||||
|
||||
@@ -781,3 +781,34 @@ button {
|
||||
color: rgba(240, 248, 255, 0.95);
|
||||
text-shadow: 0 2px 10px rgba(9, 44, 72, 0.45);
|
||||
}
|
||||
|
||||
/* Markdown inside service card comments */
|
||||
.tile-comment p { margin: 0 0 0.4em; }
|
||||
.tile-comment p:last-child { margin-bottom: 0; }
|
||||
.tile-comment ul, .tile-comment ol { margin: 0.3em 0 0.4em 1.2em; padding: 0; }
|
||||
.tile-comment li { margin-bottom: 0.15em; }
|
||||
.tile-comment h1,.tile-comment h2,.tile-comment h3,
|
||||
.tile-comment h4,.tile-comment h5,.tile-comment h6 {
|
||||
font-size: 0.85em; font-weight: 700; margin: 0.4em 0 0.2em;
|
||||
}
|
||||
.tile-comment code {
|
||||
font-family: monospace; font-size: 0.88em;
|
||||
background: rgba(0,0,0,.06); border-radius: 3px; padding: 0.1em 0.3em;
|
||||
}
|
||||
.tile-comment pre {
|
||||
background: rgba(0,0,0,.06); border-radius: 4px;
|
||||
padding: 0.4em 0.6em; overflow-x: auto; font-size: 0.82em;
|
||||
}
|
||||
.tile-comment pre code { background: none; padding: 0; }
|
||||
.tile-comment blockquote {
|
||||
border-left: 3px solid #c7d9e8; margin: 0.3em 0 0.3em 0;
|
||||
padding-left: 0.6em; color: #5a7a90;
|
||||
}
|
||||
.tile-comment a { color: #1668a6; text-decoration: underline; }
|
||||
.tile-comment table { border-collapse: collapse; font-size: 0.82em; margin: 0.3em 0; }
|
||||
.tile-comment th, .tile-comment td {
|
||||
border: 1px solid #c7d9e8; padding: 0.2em 0.5em;
|
||||
}
|
||||
.tile-comment th { background: #eaf2fb; font-weight: 700; }
|
||||
.tile-comment del { text-decoration: line-through; color: #7a9aaf; }
|
||||
.tile-comment input[type=checkbox] { margin-right: 0.3em; }
|
||||
|
||||
Reference in New Issue
Block a user