import html
import re
def _render_inline_markdown(text: str) -> str:
rendered = html.escape(text, quote=False)
rendered = re.sub(r"`([^`]+)`", lambda match: f"{match.group(1)}", rendered)
rendered = re.sub(r"\*\*([^*]+)\*\*", lambda match: f"{match.group(1)}", rendered)
return rendered
def _render_markdown(markdown: str) -> str:
lines = str(markdown or "").strip().splitlines()
parts, paragraph, list_items, code_lines = [], [], [], []
in_code = False
code_lang = ""
def flush_paragraph():
if paragraph:
parts.append(f"
{_render_inline_markdown(' '.join(paragraph))}
")
paragraph.clear()
def flush_list():
if list_items:
parts.append("" + "".join(f"- {item}
" for item in list_items) + "
")
list_items.clear()
def flush_code():
if code_lines:
lang_class = f" class='language-{html.escape(code_lang, quote=True)}'" if code_lang else ""
parts.append(f"{html.escape(chr(10).join(code_lines), quote=False)}
")
code_lines.clear()
for raw_line in lines:
stripped = raw_line.strip()
if stripped.startswith("```"):
if in_code:
flush_code()
in_code = False
code_lang = ""
else:
flush_paragraph()
flush_list()
in_code = True
code_lang = re.sub(r"[^A-Za-z0-9_-]", "", stripped[3:].strip())
continue
if in_code:
code_lines.append(raw_line.rstrip())
continue
if not stripped:
flush_paragraph()
flush_list()
continue
heading = re.match(r"^(#{1,3})\s+(.+)$", stripped)
if heading:
flush_paragraph()
flush_list()
level = len(heading.group(1)) + 1
parts.append(f"{_render_inline_markdown(heading.group(2).strip())}")
continue
if stripped.startswith("- "):
flush_paragraph()
list_items.append(_render_inline_markdown(stripped[2:].strip()))
continue
flush_list()
paragraph.append(stripped)
flush_code()
flush_paragraph()
flush_list()
return "\n".join(parts)
def _normalize_infos(infos, model_name: str) -> tuple[str, str]:
return str(model_name or "Model"), str(infos or "")
def _render_info_trigger_and_popup(popup_id: str, title: str, markdown: str) -> str:
title_attr = html.escape(title, quote=True)
title_html = html.escape(title)
return (
f""
f""
)
def render_model_description(description: str, infos=None, *, model_type: str = "", model_name: str = "Model", height: int = 40) -> str:
if not infos:
return f"{description}
"
title, markdown = _normalize_infos(infos, model_name)
if not markdown.strip():
return f"{description}
"
popup_id = "wangp-model-info-" + re.sub(r"[^A-Za-z0-9_-]", "-", str(model_type or model_name)).strip("-").lower()
return (
f""
f"
{description}
"
f"{_render_info_trigger_and_popup(popup_id, title, markdown)}"
"
"
)
def render_prompt_label(label: str, infos=None, *, model_type: str = "", prompt_id: str = "prompt", title: str = "Prompt Guidelines") -> str:
if not infos:
return ""
popup_title, markdown = _normalize_infos(infos, title)
if not markdown.strip():
return ""
popup_key = re.sub(r"[^A-Za-z0-9_-]", "-", f"{model_type or 'model'}-{prompt_id or 'prompt'}").strip("-").lower()
popup_id = f"wangp-prompt-info-{popup_key}"
return (
""
f"{_render_info_trigger_and_popup(popup_id, popup_title, markdown)}"
"
"
)
def get_css() -> str:
return """
.wangp-model-info-host {
position: relative;
padding-right: 0;
}
.header-markdown-group .html-container {
padding: 0 !important;
}
.wangp-model-info-description {
line-height: 1.35;
}
.wangp-prompt-info-host {
position: relative;
width: 100%;
height: 0;
margin: 0;
overflow: visible;
pointer-events: none;
z-index: 50;
}
.wangp-prompt-info-stack {
position: relative;
}
.wangp-prompt-info-stack .wangp-prompt-info-anchor {
position: absolute !important;
inset: 0 0 auto 0 !important;
width: 100% !important;
max-width: 100% !important;
z-index: 50;
}
.wangp-prompt-info-anchor,
.wangp-prompt-info-anchor > *,
.wangp-prompt-info-anchor .html-container {
min-height: 0 !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
background: transparent !important;
overflow: visible !important;
scrollbar-width: none !important;
}
.wangp-prompt-info-anchor::-webkit-scrollbar,
.wangp-prompt-info-anchor *::-webkit-scrollbar {
display: none !important;
width: 0 !important;
height: 0 !important;
}
.wangp-prompt-info-host .wangp-model-info-trigger {
pointer-events: auto;
z-index: 60;
top: 23px;
right: 8px;
width: 18px;
height: 18px;
min-width: 18px;
min-height: 18px;
border: 1px solid var(--button-secondary-border-color, rgba(17, 84, 118, 0.24));
background: var(--button-secondary-background-fill, rgba(255, 255, 255, 0.86));
box-shadow: none;
color: var(--button-secondary-text-color, #155574);
font-size: 11px;
}
.wangp-prompt-info-host .wangp-model-info-trigger:hover {
box-shadow: none;
}
.wangp-model-info-trigger {
position: absolute;
top: 1px;
right: 2px;
width: 26px;
height: 26px;
min-width: 26px;
min-height: 26px;
padding: 0;
border: 1px solid var(--button-secondary-border-color, rgba(17, 84, 118, 0.18));
border-radius: 999px;
background: var(--button-secondary-background-fill, linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(238, 247, 252, 0.98) 100%));
color: var(--button-secondary-text-color, #155574);
box-shadow: none;
cursor: pointer;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
}
.wangp-model-info-trigger:hover {
border-color: rgba(16, 86, 121, 0.36);
box-shadow: none;
}
.wangp-model-info-popup[hidden] {
display: none !important;
}
.wangp-model-info-popup {
position: fixed;
top: 96px;
right: 32px;
width: min(680px, calc(100vw - 34px));
max-height: min(78vh, 720px);
z-index: 1200;
pointer-events: none;
}
.wangp-model-info-card {
pointer-events: auto;
overflow: hidden;
border-radius: 18px;
border: 1px solid var(--border-color-primary, rgba(17, 84, 118, 0.16));
background: var(--background-fill-primary, rgba(255, 255, 255, 0.99));
box-shadow: 0 28px 62px rgba(7, 31, 48, 0.24);
color: var(--body-text-color, #174a67);
}
.wangp-model-info-titlebar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 10px 14px 9px 16px;
background: var(--button-primary-background-fill, linear-gradient(180deg, rgba(16, 86, 121, 0.98) 0%, rgba(10, 59, 84, 0.98) 100%));
color: var(--button-primary-text-color, #f3fbff);
cursor: grab;
user-select: none;
touch-action: none;
}
.wangp-model-info-titlebar:active {
cursor: grabbing;
}
.wangp-model-info-heading {
color: var(--button-primary-text-color, #f3fbff) !important;
font-size: 0.92rem;
font-weight: 800;
}
.wangp-model-info-close {
width: 26px;
height: 26px;
min-width: 26px;
min-height: 26px;
padding: 0;
border: 1px solid var(--button-primary-border-color, rgba(255, 255, 255, 0.24));
border-radius: 999px;
background: rgba(255, 255, 255, 0.12);
color: var(--button-primary-text-color, #f3fbff);
cursor: pointer;
font-size: 20px;
line-height: 1;
}
.wangp-model-info-content {
max-height: calc(min(78vh, 720px) - 46px);
overflow: auto;
padding: 16px 18px 18px;
color: var(--body-text-color, #174a67);
font-size: 0.92rem;
line-height: 1.5;
}
.wangp-model-info-content h2,
.wangp-model-info-content h3,
.wangp-model-info-content h4 {
margin: 12px 0 7px;
color: var(--body-text-color, #103f59);
font-weight: 800;
}
.wangp-model-info-content h2:first-child,
.wangp-model-info-content h3:first-child,
.wangp-model-info-content h4:first-child {
margin-top: 0;
}
.wangp-model-info-content p,
.wangp-model-info-content ul {
margin: 0 0 11px;
}
.wangp-model-info-content ul {
padding-left: 20px;
}
.wangp-model-info-content code {
padding: 1px 4px;
border-radius: 5px;
background: var(--background-fill-secondary, rgba(16, 86, 121, 0.08));
color: var(--body-text-color, #0f4967);
}
.wangp-model-info-content pre {
margin: 8px 0 13px;
padding: 12px;
border-radius: 12px;
border: 1px solid var(--border-color-primary, rgba(17, 84, 118, 0.12));
background: var(--background-fill-secondary, #f4f9fc);
overflow: auto;
}
.wangp-model-info-content pre code {
padding: 0;
border-radius: 0;
background: transparent;
color: var(--body-text-color, #123f58);
}
"""
def get_javascript() -> str:
return """
window.wangpModelInfo = window.wangpModelInfo || {};
window.wangpModelInfo.open = function(button) {
const popupId = button?.getAttribute("data-wangp-model-info-open");
const popup = popupId ? document.getElementById(popupId) : null;
if (!popup) return;
document.querySelectorAll("[data-wangp-model-info-popup]").forEach((other) => {
if (other !== popup) other.hidden = true;
});
popup.hidden = false;
popup.style.left = "auto";
popup.style.right = "32px";
popup.style.top = "96px";
};
window.wangpModelInfo.close = function(closeButton) {
const popup = closeButton?.closest("[data-wangp-model-info-popup]");
if (popup) popup.hidden = true;
};
window.wangpModelInfo.alignPromptInfoButtons = function() {
document.querySelectorAll(".wangp-prompt-info-anchor").forEach((anchor) => {
const trigger = anchor.querySelector(".wangp-prompt-info-host .wangp-model-info-trigger");
if (!trigger || !anchor.classList.contains("block")) return;
let target = null;
for (let sibling = anchor.nextElementSibling; sibling && !target; sibling = sibling.nextElementSibling) {
if (sibling.classList?.contains("wangp-prompt-info-anchor")) continue;
const label = sibling.querySelector?.("label");
if (!label) continue;
target = label.querySelector("span") || label.firstElementChild || label;
}
if (!target) return;
const host = trigger.closest(".wangp-prompt-info-host");
const hostRect = host.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
const triggerRect = trigger.getBoundingClientRect();
if (!hostRect.width || !targetRect.height) return;
trigger.style.top = Math.max(0, targetRect.top - hostRect.top + (targetRect.height - triggerRect.height) / 2) + "px";
});
};
window.wangpModelInfo.schedulePromptInfoAlign = function() {
if (window.wangpModelInfo.alignPending) return;
window.wangpModelInfo.alignPending = true;
requestAnimationFrame(() => {
window.wangpModelInfo.alignPending = false;
window.wangpModelInfo.alignPromptInfoButtons();
});
};
let wangpModelInfoDrag = null;
document.addEventListener("click", (event) => {
const openButton = event.target.closest("[data-wangp-model-info-open]");
if (openButton) {
event.preventDefault();
event.stopPropagation();
window.wangpModelInfo.open(openButton);
return;
}
const closeButton = event.target.closest("[data-wangp-model-info-close]");
if (closeButton) {
event.preventDefault();
event.stopPropagation();
window.wangpModelInfo.close(closeButton);
}
});
document.addEventListener("pointerdown", (event) => {
const handle = event.target.closest("[data-wangp-model-info-drag], .wangp-local-file-picker-titlebar");
if (!handle || event.target.closest("[data-wangp-model-info-close], .wangp-local-file-picker-close")) return;
const popup = handle.closest("[data-wangp-model-info-popup], .wangp-local-file-picker-popup");
if (!popup) return;
const rect = popup.getBoundingClientRect();
popup.style.setProperty("left", rect.left + "px", "important");
popup.style.setProperty("top", rect.top + "px", "important");
popup.style.setProperty("right", "auto", "important");
wangpModelInfoDrag = { popup, pointerId: event.pointerId, offsetX: event.clientX - rect.left, offsetY: event.clientY - rect.top };
handle.setPointerCapture?.(event.pointerId);
event.preventDefault();
});
document.addEventListener("pointermove", (event) => {
if (!wangpModelInfoDrag || wangpModelInfoDrag.pointerId !== event.pointerId) return;
const margin = 10;
const popup = wangpModelInfoDrag.popup;
const rect = popup.getBoundingClientRect();
const left = Math.min(Math.max(margin, event.clientX - wangpModelInfoDrag.offsetX), Math.max(margin, window.innerWidth - rect.width - margin));
const top = Math.min(Math.max(margin, event.clientY - wangpModelInfoDrag.offsetY), Math.max(margin, window.innerHeight - 48));
popup.style.setProperty("left", left + "px", "important");
popup.style.setProperty("top", top + "px", "important");
event.preventDefault();
});
document.addEventListener("pointerup", (event) => {
if (wangpModelInfoDrag && wangpModelInfoDrag.pointerId === event.pointerId) wangpModelInfoDrag = null;
});
document.addEventListener("pointercancel", (event) => {
if (wangpModelInfoDrag && wangpModelInfoDrag.pointerId === event.pointerId) wangpModelInfoDrag = null;
});
window.addEventListener("load", window.wangpModelInfo.schedulePromptInfoAlign);
window.addEventListener("resize", window.wangpModelInfo.schedulePromptInfoAlign);
new MutationObserver(window.wangpModelInfo.schedulePromptInfoAlign).observe(document.body, { childList: true, subtree: true, characterData: true });
setTimeout(window.wangpModelInfo.schedulePromptInfoAlign, 250);
setTimeout(window.wangpModelInfo.schedulePromptInfoAlign, 1200);
"""