| import html |
| import re |
|
|
|
|
| def _render_inline_markdown(text: str) -> str: |
| rendered = html.escape(text, quote=False) |
| rendered = re.sub(r"`([^`]+)`", lambda match: f"<code>{match.group(1)}</code>", rendered) |
| rendered = re.sub(r"\*\*([^*]+)\*\*", lambda match: f"<strong>{match.group(1)}</strong>", 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"<p>{_render_inline_markdown(' '.join(paragraph))}</p>") |
| paragraph.clear() |
|
|
| def flush_list(): |
| if list_items: |
| parts.append("<ul>" + "".join(f"<li>{item}</li>" for item in list_items) + "</ul>") |
| 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"<pre><code{lang_class}>{html.escape(chr(10).join(code_lines), quote=False)}</code></pre>") |
| 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"<h{level}>{_render_inline_markdown(heading.group(2).strip())}</h{level}>") |
| 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"<button type='button' class='wangp-model-info-trigger' title='{title_attr}' aria-label='{title_attr}' data-wangp-model-info-open='{popup_id}'>ⓘ</button>" |
| f"<div id='{popup_id}' class='wangp-model-info-popup' role='dialog' aria-label='{title_attr}' data-wangp-model-info-popup hidden>" |
| "<div class='wangp-model-info-card'>" |
| "<div class='wangp-model-info-titlebar' data-wangp-model-info-drag>" |
| f"<div class='wangp-model-info-heading'>{title_html}</div>" |
| "<button type='button' class='wangp-model-info-close' aria-label='Close information' data-wangp-model-info-close>×</button>" |
| "</div>" |
| f"<div class='wangp-model-info-content'>{_render_markdown(markdown)}</div>" |
| "</div>" |
| "</div>" |
| ) |
|
|
|
|
| def render_model_description(description: str, infos=None, *, model_type: str = "", model_name: str = "Model", height: int = 40) -> str: |
| if not infos: |
| return f"<div style='height:{int(height)}px'>{description}</div>" |
| title, markdown = _normalize_infos(infos, model_name) |
| if not markdown.strip(): |
| return f"<div style='height:{int(height)}px'>{description}</div>" |
| popup_id = "wangp-model-info-" + re.sub(r"[^A-Za-z0-9_-]", "-", str(model_type or model_name)).strip("-").lower() |
| return ( |
| f"<div class='wangp-model-info-host' style='min-height:{int(height)}px'>" |
| f"<div class='wangp-model-info-description'>{description}</div>" |
| f"{_render_info_trigger_and_popup(popup_id, title, markdown)}" |
| "</div>" |
| ) |
|
|
|
|
| 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 ( |
| "<div class='wangp-prompt-info-host'>" |
| f"{_render_info_trigger_and_popup(popup_id, popup_title, markdown)}" |
| "</div>" |
| ) |
|
|
|
|
| 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); |
| """ |
|
|