mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-01 01:10:21 +08:00
feat: enable shiki highlighting for t2i templates and add a template (#7501)
* fix: enable shiki highlighting for t2i templates * fix: t2i templates cr * feat: add new t2i template astrbot_vitepress.html
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import logging
|
||||
import random
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
import aiohttp
|
||||
|
||||
@@ -16,6 +19,31 @@ ASTRBOT_T2I_DEFAULT_ENDPOINT = "https://t2i.soulter.top/text2img"
|
||||
logger = logging.getLogger("astrbot")
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_shiki_runtime() -> str:
|
||||
runtime_path = (
|
||||
Path(__file__).resolve().parent / "template" / "shiki_runtime.iife.js"
|
||||
)
|
||||
if not runtime_path.exists():
|
||||
logger.error(
|
||||
"T2I Shiki runtime not found at %s. Run `cd dashboard && pnpm run build:t2i-shiki-runtime` to regenerate it. Continuing without code highlighting.",
|
||||
runtime_path,
|
||||
)
|
||||
return ""
|
||||
|
||||
try:
|
||||
runtime = runtime_path.read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeDecodeError) as err:
|
||||
logger.warning(
|
||||
"Failed to load T2I Shiki runtime from %s: %s. Continuing without code highlighting.",
|
||||
runtime_path,
|
||||
err,
|
||||
)
|
||||
return ""
|
||||
|
||||
return runtime.replace("</script", "<\\/script")
|
||||
|
||||
|
||||
class NetworkRenderStrategy(RenderStrategy):
|
||||
def __init__(self, base_url: str | None = None) -> None:
|
||||
super().__init__()
|
||||
@@ -77,6 +105,7 @@ class NetworkRenderStrategy(RenderStrategy):
|
||||
if options:
|
||||
default_options |= options
|
||||
|
||||
tmpl_data = {"shiki_runtime": get_shiki_runtime()} | tmpl_data
|
||||
post_data = {
|
||||
"tmpl": tmpl_str,
|
||||
"json": return_url,
|
||||
@@ -129,9 +158,9 @@ class NetworkRenderStrategy(RenderStrategy):
|
||||
if not template_name:
|
||||
template_name = "base"
|
||||
tmpl_str = await self.get_template(name=template_name)
|
||||
text = text.replace("`", "\\`")
|
||||
text_base64 = base64.b64encode(text.encode("utf-8")).decode("ascii")
|
||||
return await self.render_custom_template(
|
||||
tmpl_str,
|
||||
{"text": text, "version": f"v{VERSION}"},
|
||||
{"text_base64": text_base64, "version": f"v{VERSION}"},
|
||||
return_url,
|
||||
)
|
||||
|
||||
@@ -2,20 +2,15 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Astrbot PowerShell {{ version }} </title>
|
||||
<title>Astrbot PowerShell {{ version }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/lib/common.min.js"></script>
|
||||
<script>hljs.highlightAll();</script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"
|
||||
onload="renderMathInElement(document.getElementById('content'),{delimiters: [{left: '$$', right: '$$', display: true},{left: '$', right: '$', display: false}]});"></script>
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #010409;
|
||||
--text-color: #e6edf3;
|
||||
--title-bar-color: #161b22;
|
||||
--title-text-color: #e6edf3;
|
||||
--font-family: 'Consolas', 'Microsoft YaHei Mono', 'Dengxian Mono', 'Courier New', monospace;
|
||||
--font-family: "Consolas", "Microsoft YaHei Mono", "Dengxian Mono", "Courier New", monospace;
|
||||
--glow-color: rgba(200, 220, 255, 0.7);
|
||||
}
|
||||
|
||||
@@ -36,7 +31,6 @@
|
||||
padding: 0;
|
||||
line-height: 1.6;
|
||||
font-size: 18px;
|
||||
/* The CRT glow effect from the image */
|
||||
text-shadow: 0 0 15px var(--glow-color), 0 0 7px rgba(255, 255, 255, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -63,9 +57,9 @@
|
||||
color: var(--title-text-color);
|
||||
font-size: 16px;
|
||||
border-bottom: 1px solid #30363d;
|
||||
text-shadow: none; /* No glow for title bar */
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
.header .title {
|
||||
font-weight: bold;
|
||||
font-size: 28px;
|
||||
@@ -78,13 +72,10 @@
|
||||
|
||||
main {
|
||||
padding: 1rem 1.5rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#content {
|
||||
/* min-width and max-width removed as per request */
|
||||
}
|
||||
|
||||
/* --- Markdown Styles adjusted for terminal look --- */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1.4;
|
||||
margin-top: 20px;
|
||||
@@ -144,7 +135,16 @@
|
||||
font-size: 100%;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
text-shadow: none; /* Disable glow inside code blocks for clarity */
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
pre.shiki {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
pre.shiki > code,
|
||||
pre.shiki span {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -165,9 +165,8 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<span class="title">> Astrbot PowerShell</span>
|
||||
<span class="title">> Astrbot PowerShell</span>
|
||||
<span class="version">{{ version }}</span>
|
||||
</div>
|
||||
|
||||
@@ -175,10 +174,45 @@
|
||||
<div id="content"></div>
|
||||
</main>
|
||||
|
||||
<script>{{ shiki_runtime | safe }}</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.getElementById('content').innerHTML = marked.parse(`{{ text | safe }}`);
|
||||
</script>
|
||||
(function () {
|
||||
const contentElement = document.getElementById("content");
|
||||
const source = decodeBase64Utf8("{{ text_base64 }}");
|
||||
|
||||
contentElement.innerHTML = marked.parse(source);
|
||||
|
||||
if (window.AstrBotT2IShiki) {
|
||||
window.AstrBotT2IShiki.highlightAllCodeBlocks(contentElement, "github-dark");
|
||||
}
|
||||
|
||||
if (window.renderMathInElement) {
|
||||
window.renderMathInElement(contentElement, {
|
||||
delimiters: [
|
||||
{ left: "$$", right: "$$", display: true },
|
||||
{ left: "$", right: "$", display: false }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function decodeBase64Utf8(base64Text) {
|
||||
const binary = window.atob(base64Text || "");
|
||||
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
||||
|
||||
if (window.TextDecoder) {
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
let fallback = "";
|
||||
bytes.forEach((byte) => {
|
||||
fallback += String.fromCharCode(byte);
|
||||
});
|
||||
return decodeURIComponent(escape(fallback));
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
552
astrbot/core/utils/t2i/template/astrbot_vitepress.html
Normal file
552
astrbot/core/utils/t2i/template/astrbot_vitepress.html
Normal file
@@ -0,0 +1,552 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN" class="dark">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>AstrBot Docs {{ version }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">
|
||||
<style>
|
||||
:root {
|
||||
--vp-c-bg: #1b1b1f;
|
||||
--vp-c-bg-soft: #202127;
|
||||
--vp-c-bg-alt: #161618;
|
||||
--vp-c-bg-elv: #202127;
|
||||
--vp-c-border: #3c3f44;
|
||||
--vp-c-divider: #2e2e32;
|
||||
--vp-c-gutter: #000000;
|
||||
--vp-c-text-1: #dfdfd6;
|
||||
--vp-c-text-2: #98989f;
|
||||
--vp-c-text-3: #6a6a71;
|
||||
--vp-c-brand-1: #a8b1ff;
|
||||
--vp-c-brand-2: #5c73e7;
|
||||
--vp-c-brand-3: #3e63dd;
|
||||
--vp-c-brand-soft: rgba(100, 108, 255, 0.16);
|
||||
--vp-c-default-soft: rgba(101, 117, 133, 0.16);
|
||||
--vp-code-bg: var(--vp-c-default-soft);
|
||||
--vp-code-block-bg: var(--vp-c-bg-alt);
|
||||
--vp-code-line-height: 1.7;
|
||||
--vp-code-font-size: 0.875em;
|
||||
--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07);
|
||||
--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
--vp-layout-max-width: 1440px;
|
||||
--vp-nav-height: 64px;
|
||||
--vp-radius: 12px;
|
||||
--vp-font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--vp-font-family-cjk: "Inter4CJK", -apple-system, BlinkMacSystemFont, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--vp-code-font-family: ui-monospace, "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
color: var(--vp-c-text-1);
|
||||
font-family: var(--vp-font-family);
|
||||
background: linear-gradient(180deg, rgba(27, 27, 31, 0.96), rgba(27, 27, 31, 1));
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
body:lang(zh),
|
||||
body:lang(ja) {
|
||||
font-family: var(--vp-font-family-cjk);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
transition: color 0.25s, opacity 0.25s;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--vp-c-brand-2);
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: var(--vp-layout-max-width);
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.vp-nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: var(--vp-nav-height);
|
||||
padding: 0 32px;
|
||||
backdrop-filter: blur(18px);
|
||||
background: rgba(27, 27, 31, 0.9);
|
||||
border-bottom: 1px solid var(--vp-c-gutter);
|
||||
}
|
||||
|
||||
.vp-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.vp-brand-logo {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
object-fit: contain;
|
||||
filter: drop-shadow(0 6px 16px rgba(62, 99, 221, 0.24));
|
||||
}
|
||||
|
||||
.vp-brand-name {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.vp-nav-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.vp-search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vp-search kbd {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vp-shell {
|
||||
padding: 42px 32px 56px;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.vp-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.vp-content-frame {
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.vp-hero {
|
||||
display: block;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.vp-hero.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vp-hero-copy h1 {
|
||||
margin: 0;
|
||||
font-size: 48px;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.04em;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.vp-hero-copy p {
|
||||
max-width: 720px;
|
||||
margin: 16px 0 0;
|
||||
font-size: 18px;
|
||||
line-height: 1.78;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.vp-doc {
|
||||
color: var(--vp-c-text-1);
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.vp-doc > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.vp-doc h1,
|
||||
.vp-doc h2,
|
||||
.vp-doc h3,
|
||||
.vp-doc h4 {
|
||||
position: relative;
|
||||
scroll-margin-top: 100px;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.vp-doc h1 {
|
||||
margin: 0 0 20px;
|
||||
font-size: 32px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.vp-doc h2 {
|
||||
margin: 48px 0 16px;
|
||||
padding-top: 24px;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.vp-doc h3 {
|
||||
margin: 32px 0 0;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.vp-doc p,
|
||||
.vp-doc ul,
|
||||
.vp-doc ol,
|
||||
.vp-doc table,
|
||||
.vp-doc blockquote,
|
||||
.vp-doc [class*="language-"],
|
||||
.vp-doc .math {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.vp-doc strong {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.vp-doc code {
|
||||
font-family: var(--vp-code-font-family);
|
||||
padding: 3px 6px;
|
||||
border-radius: 4px;
|
||||
color: var(--vp-c-brand-1);
|
||||
background: var(--vp-code-bg);
|
||||
}
|
||||
|
||||
.vp-doc pre code {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"],
|
||||
.vp-block {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: var(--vp-code-block-bg);
|
||||
transition: background-color 0.5s;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"] > span.lang,
|
||||
.vp-block > span.lang {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 8px;
|
||||
z-index: 2;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
color: var(--vp-c-text-2);
|
||||
background: transparent;
|
||||
transition: color 0.4s, opacity 0.4s;
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"] pre.shiki,
|
||||
.vp-doc [class*="language-"] pre,
|
||||
.vp-block pre.shiki,
|
||||
.vp-block pre {
|
||||
margin: 0;
|
||||
padding: 20px 0;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
background: var(--vp-code-block-bg) !important;
|
||||
line-height: var(--vp-code-line-height);
|
||||
font-size: var(--vp-code-font-size);
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"] code,
|
||||
.vp-block code {
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"] pre.shiki code,
|
||||
.vp-doc [class*="language-"] pre code,
|
||||
.vp-block pre.shiki code,
|
||||
.vp-block pre code {
|
||||
font-family: var(--vp-code-font-family);
|
||||
display: block;
|
||||
width: fit-content;
|
||||
min-width: 100%;
|
||||
padding: 0 24px;
|
||||
font-size: 14px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"] pre.shiki,
|
||||
.vp-block pre.shiki {
|
||||
color: var(--shiki-dark, #e1e4e8) !important;
|
||||
background-color: var(--shiki-dark-bg, var(--vp-code-block-bg)) !important;
|
||||
}
|
||||
|
||||
.vp-doc [class*="language-"] pre.shiki > code,
|
||||
.vp-doc [class*="language-"] pre.shiki span,
|
||||
.vp-block pre.shiki > code,
|
||||
.vp-block pre.shiki span {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.vp-doc blockquote {
|
||||
margin: 16px 0;
|
||||
padding-left: 16px;
|
||||
border-left: 2px solid var(--vp-c-divider);
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.vp-doc blockquote p {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.vp-doc hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
margin: 38px 0;
|
||||
background: var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.vp-doc img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
margin: 22px auto;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: var(--vp-shadow-3);
|
||||
}
|
||||
|
||||
.vp-doc ul,
|
||||
.vp-doc ol {
|
||||
padding-left: 1.35rem;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.vp-doc li {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.vp-doc table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--vp-c-border);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.vp-doc thead {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.vp-doc th,
|
||||
.vp-doc td {
|
||||
padding: 8px 16px;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.vp-doc tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.vp-footer {
|
||||
height: 72px;
|
||||
padding: 0 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 72px;
|
||||
color: var(--vp-c-text-3);
|
||||
font-size: 14px;
|
||||
border-top: 1px solid var(--vp-c-gutter);
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.vp-shell {
|
||||
padding-inline: 28px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<header class="vp-nav">
|
||||
<div class="vp-brand">
|
||||
<img class="vp-brand-logo" src="https://cf.s3.soulter.top/astrbot-logo.svg" alt="AstrBot logo" />
|
||||
<div class="vp-brand-name">AstrBot</div>
|
||||
</div>
|
||||
<div class="vp-nav-actions">
|
||||
<span>{{ version }}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="vp-shell">
|
||||
<main class="vp-main">
|
||||
<div class="vp-content-frame">
|
||||
<div class="vp-hero is-hidden" id="heroBlock">
|
||||
<div class="vp-hero-copy">
|
||||
<h1 id="heroTitle">AstrBot Docs</h1>
|
||||
<p id="heroLead">将长文本内容整理为单页文档,参考 VitePress 默认主题的深色配色、正文排版与代码块节奏,适合技术说明与发布页。</p>
|
||||
</div>
|
||||
</div>
|
||||
<article class="vp-doc" id="content"></article>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="vp-footer">Rendered by AstrBot {{ version }}</footer>
|
||||
</div>
|
||||
|
||||
<script>{{ shiki_runtime | safe }}</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const contentElement = document.getElementById("content");
|
||||
const source = decodeBase64Utf8("{{ text_base64 }}");
|
||||
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
});
|
||||
|
||||
contentElement.innerHTML = marked.parse(source);
|
||||
|
||||
assignHeadingIds(contentElement);
|
||||
enhanceCodeBlocks(contentElement);
|
||||
|
||||
if (window.renderMathInElement) {
|
||||
window.renderMathInElement(contentElement, {
|
||||
delimiters: [
|
||||
{ left: "$$", right: "$$", display: true },
|
||||
{ left: "$", right: "$", display: false }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
const headings = collectHeadings(contentElement);
|
||||
populateHero(contentElement, headings);
|
||||
|
||||
function decodeBase64Utf8(base64Text) {
|
||||
const binary = window.atob(base64Text || "");
|
||||
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
||||
|
||||
if (window.TextDecoder) {
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
let fallback = "";
|
||||
bytes.forEach((byte) => {
|
||||
fallback += String.fromCharCode(byte);
|
||||
});
|
||||
return decodeURIComponent(escape(fallback));
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value || "")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function assignHeadingIds(root) {
|
||||
Array.from(root.querySelectorAll("h1, h2, h3")).forEach((heading, index) => {
|
||||
heading.id = `section-${index + 1}`;
|
||||
});
|
||||
}
|
||||
|
||||
function collectHeadings(root) {
|
||||
return Array.from(root.querySelectorAll("h1, h2, h3")).map((heading) => ({
|
||||
element: heading,
|
||||
id: heading.id,
|
||||
level: Number(heading.tagName.slice(1)),
|
||||
text: heading.textContent.trim(),
|
||||
}));
|
||||
}
|
||||
|
||||
function populateHero(root, headings) {
|
||||
const heroBlock = document.getElementById("heroBlock");
|
||||
const heroTitle = document.getElementById("heroTitle");
|
||||
const heroLead = document.getElementById("heroLead");
|
||||
const firstHeading = headings.find((heading) => heading.level === 1) || headings[0];
|
||||
|
||||
if (!firstHeading) {
|
||||
return;
|
||||
}
|
||||
|
||||
heroBlock.classList.remove("is-hidden");
|
||||
|
||||
const leadParagraph = firstHeading.element.nextElementSibling;
|
||||
const title = firstHeading.text;
|
||||
|
||||
heroTitle.textContent = title;
|
||||
|
||||
if (leadParagraph && leadParagraph.tagName === "P") {
|
||||
heroLead.textContent = leadParagraph.textContent.trim();
|
||||
leadParagraph.remove();
|
||||
}
|
||||
|
||||
if (firstHeading.element.parentElement === root) {
|
||||
firstHeading.element.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function extractLanguage(codeElement) {
|
||||
const className = codeElement.className || "";
|
||||
const match = className.match(/language-([\\w+#.-]+)/i);
|
||||
return match ? match[1] : "";
|
||||
}
|
||||
|
||||
function enhanceCodeBlocks(root) {
|
||||
const blocks = Array.from(root.querySelectorAll("pre > code")).map((codeElement) => ({
|
||||
rawLanguage: extractLanguage(codeElement),
|
||||
displayLanguage: extractLanguage(codeElement).trim().split(/\\s+/, 1)[0].toLowerCase(),
|
||||
}));
|
||||
|
||||
if (window.AstrBotT2IShiki) {
|
||||
window.AstrBotT2IShiki.highlightAllCodeBlocks(root, "github-dark");
|
||||
}
|
||||
|
||||
Array.from(root.querySelectorAll("pre")).forEach((preElement, index) => {
|
||||
if (preElement.parentElement && preElement.parentElement.classList.contains("vp-code-block")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const block = blocks[index] || { displayLanguage: "" };
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = `language-${block.displayLanguage || "text"}`;
|
||||
|
||||
if (block.displayLanguage) {
|
||||
wrapper.innerHTML = `<span class="lang">${escapeHtml(block.displayLanguage)}</span>`;
|
||||
}
|
||||
|
||||
preElement.replaceWith(wrapper);
|
||||
wrapper.appendChild(preElement);
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,246 +2,285 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>AstrBot {{ version }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="/path/to/styles/default.min.css">
|
||||
<script src="/path/to/highlight.min.js"></script>
|
||||
<script>hljs.highlightAll();</script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"
|
||||
onload="renderMathInElement(document.getElementById('content'),{delimiters: [{left: '$$', right: '$$', display: true},{left: '$', right: '$', display: false}]});"></script>
|
||||
<style>
|
||||
#content {
|
||||
min-width: 200px;
|
||||
max-width: 85%;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1em 1em;
|
||||
}
|
||||
|
||||
body {
|
||||
word-break: break-word;
|
||||
line-height: 1.75;
|
||||
font-weight: 400;
|
||||
font-size: 32px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
color: #333;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1.5;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
h1:first-child, h2:first-child, h3:first-child, h4:first-child, h5:first-child, h6:first-child {
|
||||
margin-top: -1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
h1::before, h2::before, h3::before, h4::before, h5::before, h6::before {
|
||||
content: "#";
|
||||
display: inline-block;
|
||||
color: #3eaf7c;
|
||||
padding-right: 0.23em;
|
||||
}
|
||||
h1 {
|
||||
position: relative;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
h1::before {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
h2 {
|
||||
padding-bottom: 0.5rem;
|
||||
font-size: 2.2rem;
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
h6 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
p {
|
||||
line-height: inherit;
|
||||
margin-top: 22px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
strong {
|
||||
color: #3eaf7c;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
border: 3px solid rgba(62, 175, 124, 0.2);
|
||||
}
|
||||
hr {
|
||||
border-top: 1px solid #3eaf7c;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
code {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
word-break: break-word;
|
||||
overflow-x: auto;
|
||||
padding: 0.2rem 0.5rem;
|
||||
margin: 0;
|
||||
color: #3eaf7c;
|
||||
font-size: 0.85em;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
line-height: 1.75;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #3eaf7c;
|
||||
}
|
||||
pre > code {
|
||||
font-size: 12px;
|
||||
padding: 15px 12px;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
pre.shiki {
|
||||
padding: 15px 12px;
|
||||
}
|
||||
pre.shiki > code {
|
||||
padding: 0;
|
||||
background: transparent !important;
|
||||
color: inherit;
|
||||
font-size: 12px;
|
||||
}
|
||||
a {
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: #3eaf7c;
|
||||
}
|
||||
a:hover, a:active {
|
||||
border-bottom: 1.5px solid #3eaf7c;
|
||||
}
|
||||
a:before {
|
||||
content: "⇲";
|
||||
}
|
||||
table {
|
||||
display: inline-block !important;
|
||||
font-size: 12px;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
border: solid 1px #3eaf7c;
|
||||
}
|
||||
thead {
|
||||
background: #3eaf7c;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: rgba(62, 175, 124, 0.2);
|
||||
}
|
||||
th, td {
|
||||
padding: 12px 7px;
|
||||
line-height: 24px;
|
||||
}
|
||||
td {
|
||||
min-width: 120px;
|
||||
}
|
||||
blockquote {
|
||||
color: #666;
|
||||
padding: 1px 23px;
|
||||
margin: 22px 0;
|
||||
border-left: 0.5rem solid rgba(62, 175, 124, 0.6);
|
||||
border-color: #42b983;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
blockquote::after {
|
||||
display: block;
|
||||
content: "";
|
||||
}
|
||||
blockquote > p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
details {
|
||||
border: none;
|
||||
outline: none;
|
||||
border-left: 4px solid #3eaf7c;
|
||||
padding-left: 10px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
details summary {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: white;
|
||||
margin: 0 -17px;
|
||||
}
|
||||
details summary::-webkit-details-marker {
|
||||
color: #3eaf7c;
|
||||
}
|
||||
ol, ul {
|
||||
padding-left: 28px;
|
||||
}
|
||||
ol li, ul li {
|
||||
margin-bottom: 0;
|
||||
list-style: inherit;
|
||||
}
|
||||
ol li .task-list-item, ul li .task-list-item {
|
||||
list-style: none;
|
||||
}
|
||||
ol li .task-list-item ul, ul li .task-list-item ul, ol li .task-list-item ol, ul li .task-list-item ol {
|
||||
margin-top: 0;
|
||||
}
|
||||
ol ul, ul ul, ol ol, ul ol {
|
||||
margin-top: 3px;
|
||||
}
|
||||
ol li {
|
||||
padding-left: 6px;
|
||||
}
|
||||
ol li::marker {
|
||||
color: #3eaf7c;
|
||||
}
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
||||
ul li:before {
|
||||
content: "•";
|
||||
margin-right: 4px;
|
||||
color: #3eaf7c;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="background-color: #3276dc; color: #fff; font-size: 64px; ">
|
||||
<div style="background-color: #3276dc; color: #fff; font-size: 64px;">
|
||||
<span style="font-weight: bold; margin-left: 16px"># AstrBot</span>
|
||||
<span>{{ version }}</span>
|
||||
</div>
|
||||
<article style="margin-top: 32px" id="content"></article>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script>
|
||||
document.getElementById('content').innerHTML = marked.parse(`{{ text | safe}}`);
|
||||
</script>
|
||||
|
||||
<script>{{ shiki_runtime | safe }}</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const contentElement = document.getElementById("content");
|
||||
const source = decodeBase64Utf8("{{ text_base64 }}");
|
||||
|
||||
contentElement.innerHTML = marked.parse(source);
|
||||
|
||||
if (window.AstrBotT2IShiki) {
|
||||
window.AstrBotT2IShiki.highlightAllCodeBlocks(contentElement, "github-light");
|
||||
}
|
||||
|
||||
if (window.renderMathInElement) {
|
||||
window.renderMathInElement(contentElement, {
|
||||
delimiters: [
|
||||
{ left: "$$", right: "$$", display: true },
|
||||
{ left: "$", right: "$", display: false }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function decodeBase64Utf8(base64Text) {
|
||||
const binary = window.atob(base64Text || "");
|
||||
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
||||
|
||||
if (window.TextDecoder) {
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
let fallback = "";
|
||||
bytes.forEach((byte) => {
|
||||
fallback += String.fromCharCode(byte);
|
||||
});
|
||||
return decodeURIComponent(escape(fallback));
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<style>
|
||||
#content {
|
||||
min-width: 200px;
|
||||
max-width: 85%;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1em 1em;
|
||||
}
|
||||
|
||||
body {
|
||||
word-break: break-word;
|
||||
line-height: 1.75;
|
||||
font-weight: 400;
|
||||
font-size: 32px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
color: #333;
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1.5;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
h1:first-child, h2:first-child, h3:first-child, h4:first-child, h5:first-child, h6:first-child {
|
||||
margin-top: -1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
h1::before, h2::before, h3::before, h4::before, h5::before, h6::before {
|
||||
content: "#";
|
||||
display: inline-block;
|
||||
color: #3eaf7c;
|
||||
padding-right: 0.23em;
|
||||
}
|
||||
h1 {
|
||||
position: relative;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
h1::before {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
h2 {
|
||||
padding-bottom: 0.5rem;
|
||||
font-size: 2.2rem;
|
||||
border-bottom: 1px solid #ececec;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
h6 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
p {
|
||||
line-height: inherit;
|
||||
margin-top: 22px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
strong {
|
||||
color: #3eaf7c;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
border: 3px solid rgba(62, 175, 124, 0.2);
|
||||
}
|
||||
hr {
|
||||
border-top: 1px solid #3eaf7c;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
code {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
word-break: break-word;
|
||||
overflow-x: auto;
|
||||
padding: 0.2rem 0.5rem;
|
||||
margin: 0;
|
||||
color: #3eaf7c;
|
||||
font-size: 0.85em;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
line-height: 1.75;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #3eaf7c;
|
||||
}
|
||||
pre > code {
|
||||
font-size: 12px;
|
||||
padding: 15px 12px;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
a {
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: #3eaf7c;
|
||||
}
|
||||
a:hover, a:active {
|
||||
border-bottom: 1.5px solid #3eaf7c;
|
||||
}
|
||||
a:before {
|
||||
content: "⇲";
|
||||
}
|
||||
table {
|
||||
display: inline-block !important;
|
||||
font-size: 12px;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
border: solid 1px #3eaf7c;
|
||||
}
|
||||
thead {
|
||||
background: #3eaf7c;
|
||||
color: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: rgba(62, 175, 124, 0.2);
|
||||
}
|
||||
th, td {
|
||||
padding: 12px 7px;
|
||||
line-height: 24px;
|
||||
}
|
||||
td {
|
||||
min-width: 120px;
|
||||
}
|
||||
blockquote {
|
||||
color: #666;
|
||||
padding: 1px 23px;
|
||||
margin: 22px 0;
|
||||
border-left: 0.5rem solid rgba(62, 175, 124, 0.6);
|
||||
border-color: #42b983;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
blockquote::after {
|
||||
display: block;
|
||||
content: "";
|
||||
}
|
||||
blockquote > p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
details {
|
||||
border: none;
|
||||
outline: none;
|
||||
border-left: 4px solid #3eaf7c;
|
||||
padding-left: 10px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
details summary {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: white;
|
||||
margin: 0px -17px;
|
||||
}
|
||||
details summary::-webkit-details-marker {
|
||||
color: #3eaf7c;
|
||||
}
|
||||
ol, ul {
|
||||
padding-left: 28px;
|
||||
}
|
||||
ol li, ul li {
|
||||
margin-bottom: 0;
|
||||
list-style: inherit;
|
||||
}
|
||||
ol li .task-list-item, ul li .task-list-item {
|
||||
list-style: none;
|
||||
}
|
||||
ol li .task-list-item ul, ul li .task-list-item ul, ol li .task-list-item ol, ul li .task-list-item ol {
|
||||
margin-top: 0;
|
||||
}
|
||||
ol ul, ul ul, ol ol, ul ol {
|
||||
margin-top: 3px;
|
||||
}
|
||||
ol li {
|
||||
padding-left: 6px;
|
||||
}
|
||||
ol li::marker {
|
||||
color: #3eaf7c;
|
||||
}
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
||||
ul li:before {
|
||||
content: "•";
|
||||
margin-right: 4px;
|
||||
color: #3eaf7c;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
152
astrbot/core/utils/t2i/template/shiki_runtime.iife.js
Normal file
152
astrbot/core/utils/t2i/template/shiki_runtime.iife.js
Normal file
File diff suppressed because one or more lines are too long
@@ -12,7 +12,11 @@ class TemplateManager:
|
||||
所有创建、更新、删除操作仅影响用户目录,以确保更新框架时用户数据安全。
|
||||
"""
|
||||
|
||||
CORE_TEMPLATES = ["base.html", "astrbot_powershell.html"]
|
||||
CORE_TEMPLATES = [
|
||||
"base.html",
|
||||
"astrbot_powershell.html",
|
||||
"astrbot_vitepress.html",
|
||||
]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.builtin_template_dir = os.path.join(
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"subset-icons": "node scripts/subset-mdi-font.mjs",
|
||||
"build:t2i-shiki-runtime": "node scripts/build-t2i-shiki-runtime.mjs",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"build-stage": "vue-tsc --noEmit && vite build --base=/vue/free/stage/",
|
||||
"build-prod": "vue-tsc --noEmit && vite build --base=/vue/free/",
|
||||
|
||||
232
dashboard/scripts/build-t2i-shiki-runtime.mjs
Normal file
232
dashboard/scripts/build-t2i-shiki-runtime.mjs
Normal file
@@ -0,0 +1,232 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
import { build } from "vite";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dashboardRoot = path.resolve(__dirname, "..");
|
||||
const runtimeOutputFile = path.resolve(
|
||||
dashboardRoot,
|
||||
"..",
|
||||
"astrbot",
|
||||
"core",
|
||||
"utils",
|
||||
"t2i",
|
||||
"template",
|
||||
"shiki_runtime.iife.js",
|
||||
);
|
||||
const shikiRequire = createRequire(require.resolve("shiki/package.json"));
|
||||
|
||||
const languageSpecs = [
|
||||
["bash", "bash"],
|
||||
["css", "css"],
|
||||
["html", "html"],
|
||||
["javascript", "javascript"],
|
||||
["json", "json"],
|
||||
["jsx", "jsx"],
|
||||
["markdown", "markdown"],
|
||||
["powershell", "powershell"],
|
||||
["python", "python"],
|
||||
["sql", "sql"],
|
||||
["tsx", "tsx"],
|
||||
["typescript", "typescript"],
|
||||
["xml", "xml"],
|
||||
["yaml", "yaml"],
|
||||
];
|
||||
|
||||
const themeSpecs = [
|
||||
["github-light", "github-light"],
|
||||
["github-dark", "github-dark"],
|
||||
];
|
||||
|
||||
// Shiki exposes plain text as a built-in special language, so we keep it
|
||||
// in the supported language list without importing a package for it.
|
||||
const builtInLanguageSpecs = ["text"];
|
||||
|
||||
const languageAliases = {
|
||||
bat: "powershell",
|
||||
cjs: "javascript",
|
||||
console: "bash",
|
||||
cts: "typescript",
|
||||
dockerfile: "bash",
|
||||
env: "bash",
|
||||
htm: "html",
|
||||
js: "javascript",
|
||||
md: "markdown",
|
||||
mjs: "javascript",
|
||||
mts: "typescript",
|
||||
plain: "text",
|
||||
plaintext: "text",
|
||||
ps1: "powershell",
|
||||
pwsh: "powershell",
|
||||
py: "python",
|
||||
shell: "bash",
|
||||
shellscript: "bash",
|
||||
sh: "bash",
|
||||
svg: "xml",
|
||||
text: "text",
|
||||
ts: "typescript",
|
||||
txt: "text",
|
||||
vue: "html",
|
||||
xhtml: "html",
|
||||
xml: "xml",
|
||||
yml: "yaml",
|
||||
zsh: "bash",
|
||||
};
|
||||
|
||||
function resolveShikiModule(specifier) {
|
||||
return pathToFileURL(shikiRequire.resolve(specifier)).href;
|
||||
}
|
||||
|
||||
function buildVirtualSource() {
|
||||
const shikiImport = JSON.stringify(
|
||||
pathToFileURL(require.resolve("shiki")).href,
|
||||
);
|
||||
const languageImports = languageSpecs
|
||||
.map(
|
||||
([, packageName], index) =>
|
||||
`import lang${index} from ${JSON.stringify(resolveShikiModule(`@shikijs/langs/${packageName}`))};`,
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
const themeImports = themeSpecs
|
||||
.map(
|
||||
([, packageName], index) =>
|
||||
`import theme${index} from ${JSON.stringify(resolveShikiModule(`@shikijs/themes/${packageName}`))};`,
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
const supportedLanguages = [
|
||||
...builtInLanguageSpecs,
|
||||
...languageSpecs.map(([runtimeName]) => runtimeName),
|
||||
];
|
||||
|
||||
return `import { createHighlighterCoreSync, createJavaScriptRegexEngine } from ${shikiImport};
|
||||
${languageImports}
|
||||
${themeImports}
|
||||
|
||||
const highlighter = createHighlighterCoreSync({
|
||||
engine: createJavaScriptRegexEngine(),
|
||||
langs: [${languageSpecs.map((_, index) => `...lang${index}`).join(", ")}],
|
||||
themes: [${themeSpecs.map((_, index) => `theme${index}`).join(", ")}],
|
||||
});
|
||||
|
||||
const supportedLanguages = new Set(${JSON.stringify(supportedLanguages)});
|
||||
const languageAliases = ${JSON.stringify(languageAliases)};
|
||||
const supportedThemes = new Set(${JSON.stringify(themeSpecs.map(([theme]) => theme))});
|
||||
|
||||
function normalizeLanguage(language) {
|
||||
const normalized = String(language || "").trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
return "text";
|
||||
}
|
||||
|
||||
if (normalized in languageAliases) {
|
||||
return languageAliases[normalized];
|
||||
}
|
||||
|
||||
return supportedLanguages.has(normalized) ? normalized : "text";
|
||||
}
|
||||
|
||||
function normalizeTheme(theme) {
|
||||
const normalized = String(theme || "").trim();
|
||||
return supportedThemes.has(normalized) ? normalized : "github-light";
|
||||
}
|
||||
|
||||
function extractLanguage(codeElement) {
|
||||
const className = codeElement.className || "";
|
||||
const match = className.match(/language-([\\w+#.-]+)/i);
|
||||
return match ? match[1] : "";
|
||||
}
|
||||
|
||||
function renderCodeToHtml(code, language, theme) {
|
||||
const normalizedTheme = normalizeTheme(theme);
|
||||
|
||||
try {
|
||||
return highlighter.codeToHtml(String(code || ""), {
|
||||
lang: normalizeLanguage(language),
|
||||
theme: normalizedTheme,
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn("Failed to render T2I code block with Shiki.", error);
|
||||
return highlighter.codeToHtml(String(code || ""), {
|
||||
lang: "text",
|
||||
theme: normalizedTheme,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function highlightAllCodeBlocks(root, theme) {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
root.querySelectorAll("pre > code").forEach((codeElement) => {
|
||||
const preElement = codeElement.parentElement;
|
||||
if (!preElement || preElement.classList.contains("shiki")) {
|
||||
return;
|
||||
}
|
||||
|
||||
preElement.outerHTML = renderCodeToHtml(
|
||||
codeElement.textContent || "",
|
||||
extractLanguage(codeElement),
|
||||
theme,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
window.AstrBotT2IShiki = Object.freeze({
|
||||
highlightAllCodeBlocks,
|
||||
normalizeLanguage,
|
||||
renderCodeToHtml,
|
||||
});
|
||||
`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const tempDir = mkdtempSync(path.join(tmpdir(), "astrbot-t2i-shiki-runtime-"));
|
||||
const entryPath = path.join(tempDir, "entry.mjs");
|
||||
|
||||
writeFileSync(entryPath, buildVirtualSource(), "utf-8");
|
||||
|
||||
try {
|
||||
await build({
|
||||
configFile: false,
|
||||
logLevel: "info",
|
||||
publicDir: false,
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1500,
|
||||
cssCodeSplit: false,
|
||||
emptyOutDir: false,
|
||||
lib: {
|
||||
entry: entryPath,
|
||||
fileName: () => path.basename(runtimeOutputFile),
|
||||
formats: ["iife"],
|
||||
name: "AstrBotT2IShikiRuntime",
|
||||
},
|
||||
minify: "esbuild",
|
||||
outDir: path.dirname(runtimeOutputFile),
|
||||
rollupOptions: {
|
||||
output: {
|
||||
inlineDynamicImports: true,
|
||||
},
|
||||
},
|
||||
sourcemap: false,
|
||||
target: "es2018",
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
rmSync(tempDir, { force: true, recursive: true });
|
||||
}
|
||||
|
||||
console.log(`Built ${runtimeOutputFile}`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user