aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rwxr-xr-xdoc/docs/.vitepress/theme/components/YueCompiler.vue2
-rw-r--r--doc/docs/.vitepress/theme/components/prism-manual.js7
-rw-r--r--doc/docs/.vitepress/theme/index.ts174
-rwxr-xr-xdoc/package.json4
4 files changed, 11 insertions, 176 deletions
diff --git a/doc/docs/.vitepress/theme/components/YueCompiler.vue b/doc/docs/.vitepress/theme/components/YueCompiler.vue
index d9a3702..afbf018 100755
--- a/doc/docs/.vitepress/theme/components/YueCompiler.vue
+++ b/doc/docs/.vitepress/theme/components/YueCompiler.vue
@@ -24,9 +24,9 @@
24</template> 24</template>
25 25
26<script> 26<script>
27import "./prism-manual.js";
27import pkg from "prismjs/components/prism-core.js"; 28import pkg from "prismjs/components/prism-core.js";
28const { highlight, languages } = pkg; 29const { highlight, languages } = pkg;
29import "prismjs/components/prism-moonscript";
30import "prismjs/components/prism-lua"; 30import "prismjs/components/prism-lua";
31import { EditorState, Compartment } from "@codemirror/state"; 31import { EditorState, Compartment } from "@codemirror/state";
32import { EditorView, keymap, lineNumbers } from "@codemirror/view"; 32import { EditorView, keymap, lineNumbers } from "@codemirror/view";
diff --git a/doc/docs/.vitepress/theme/components/prism-manual.js b/doc/docs/.vitepress/theme/components/prism-manual.js
new file mode 100644
index 0000000..8f73abd
--- /dev/null
+++ b/doc/docs/.vitepress/theme/components/prism-manual.js
@@ -0,0 +1,7 @@
1if (typeof window !== "undefined") {
2 // Prevent Prism from auto-highlighting every code block on page load.
3 // We only want explicit highlight() calls inside YueCompiler.
4 window.Prism = window.Prism || {};
5 window.Prism.manual = true;
6}
7
diff --git a/doc/docs/.vitepress/theme/index.ts b/doc/docs/.vitepress/theme/index.ts
index 4dd7408..70145d5 100644
--- a/doc/docs/.vitepress/theme/index.ts
+++ b/doc/docs/.vitepress/theme/index.ts
@@ -1,11 +1,7 @@
1import DefaultTheme from "vitepress/theme"; 1import DefaultTheme from "vitepress/theme";
2import type { Theme } from "vitepress"; 2import type { Theme } from "vitepress";
3import { useRoute } from "vitepress"; 3import { h } from "vue";
4import { h, nextTick, watch } from "vue";
5import "./custom.css"; 4import "./custom.css";
6import darkPlus from "@shikijs/themes/dark-plus";
7import lightPlus from "@shikijs/themes/light-plus";
8import yuescriptGrammar from "../grammars/yuescript.tmLanguage.json";
9 5
10// @ts-ignore 6// @ts-ignore
11import CompilerModal from "./components/CompilerModal.vue"; 7import CompilerModal from "./components/CompilerModal.vue";
@@ -16,184 +12,16 @@ import YueCompiler from "./components/YueCompiler.vue";
16// @ts-ignore 12// @ts-ignore
17import YueDisplay from "./components/YueDisplay.vue"; 13import YueDisplay from "./components/YueDisplay.vue";
18 14
19type ShikiHighlighter = Awaited<
20 ReturnType<(typeof import("shiki/core"))["createHighlighterCore"]>
21>;
22
23let shikiHighlighterPromise: Promise<ShikiHighlighter> | null = null;
24let shikiRepairQueued = false;
25let shikiRepairRetry1 = 0;
26let shikiRepairRetry2 = 0;
27
28type SupportedLanguage = "yuescript" | "lua" | "shellscript";
29
30function toSupportedLanguage(className: string): SupportedLanguage | null {
31 const match = className.match(/(?:^|\s)language-([a-z0-9_-]+)/i);
32 if (!match) return null;
33 const lang = match[1].toLowerCase();
34 if (lang === "yuescript" || lang === "yue") return "yuescript";
35 if (lang === "lua") return "lua";
36 if (
37 lang === "shellscript" ||
38 lang === "shell" ||
39 lang === "bash" ||
40 lang === "sh" ||
41 lang === "zsh"
42 ) {
43 return "shellscript";
44 }
45 return null;
46}
47
48function getShikiHighlighter() {
49 if (!shikiHighlighterPromise) {
50 shikiHighlighterPromise = Promise.all([
51 import("shiki/core"),
52 import("shiki/engine/javascript"),
53 import("@shikijs/langs/lua"),
54 import("@shikijs/langs/shellscript"),
55 ]).then(
56 ([
57 { createHighlighterCore },
58 { createJavaScriptRegexEngine },
59 luaLang,
60 shellscriptLang,
61 ]) =>
62 createHighlighterCore({
63 themes: [lightPlus, darkPlus],
64 langs: [
65 {
66 ...yuescriptGrammar,
67 name: "yuescript",
68 scopeName: "source.yuescript",
69 aliases: ["yue"],
70 },
71 ...luaLang.default,
72 ...shellscriptLang.default,
73 ],
74 engine: createJavaScriptRegexEngine(),
75 }),
76 );
77 }
78 return shikiHighlighterPromise;
79}
80
81function collectPlainCodeTargets() {
82 if (typeof document === "undefined") return [];
83 const nodes = Array.from(
84 document.querySelectorAll<HTMLElement>(
85 "pre > code[class*='language-'], code[class*='language-']",
86 ),
87 );
88 return nodes.filter((code) => {
89 if (code.closest("pre.shiki")) return false;
90 const pre = code.parentElement;
91 if (pre && pre.tagName === "PRE" && pre.classList.contains("shiki")) {
92 return false;
93 }
94 return toSupportedLanguage(code.className) !== null;
95 });
96}
97
98async function repairPlainCodeBlocks() {
99 const targets = collectPlainCodeTargets();
100 if (!targets.length) return;
101
102 const highlighter = await getShikiHighlighter();
103
104 for (const code of targets) {
105 const lang = toSupportedLanguage(code.className);
106 if (!lang) continue;
107 const source = (code.textContent || "").replace(/\r\n?/g, "\n");
108 if (!source.trim()) continue;
109 const html = highlighter.codeToHtml(source, {
110 lang,
111 themes: {
112 light: "light-plus",
113 dark: "dark-plus",
114 },
115 });
116 const tpl = document.createElement("template");
117 tpl.innerHTML = html.trim();
118 const replacement = tpl.content.firstElementChild as HTMLElement | null;
119 if (!replacement) continue;
120 replacement.classList.add("vp-code");
121 const pre = code.parentElement?.tagName === "PRE" ? code.parentElement : null;
122 (pre || code).replaceWith(replacement);
123 }
124
125 applyShikiInlineColorFallback();
126}
127
128function scheduleShikiRepair() {
129 if (typeof window === "undefined") return;
130 if (shikiRepairQueued) return;
131 shikiRepairQueued = true;
132 window.requestAnimationFrame(() => {
133 shikiRepairQueued = false;
134 void repairPlainCodeBlocks();
135 });
136}
137
138function scheduleShikiRepairWithRetries() {
139 if (typeof window === "undefined") return;
140 scheduleShikiRepair();
141 window.clearTimeout(shikiRepairRetry1);
142 window.clearTimeout(shikiRepairRetry2);
143 shikiRepairRetry1 = window.setTimeout(scheduleShikiRepair, 120);
144 shikiRepairRetry2 = window.setTimeout(scheduleShikiRepair, 500);
145}
146
147function applyShikiInlineColorFallback() {
148 if (typeof document === "undefined") return;
149 const isDark = document.documentElement.classList.contains("dark");
150 const varName = isDark ? "--shiki-dark" : "--shiki-light";
151 const spans = document.querySelectorAll<HTMLElement>(".vp-code span");
152 spans.forEach((span) => {
153 const color = span.style.getPropertyValue(varName).trim();
154 if (color) {
155 span.style.color = color;
156 }
157 });
158}
159
160const theme: Theme = { 15const theme: Theme = {
161 extends: DefaultTheme, 16 extends: DefaultTheme,
162 Layout: () => 17 Layout: () =>
163 h(DefaultTheme.Layout, null, { 18 h(DefaultTheme.Layout, null, {
164 "layout-bottom": () => [h(HomeFooter), h(CompilerModal)], 19 "layout-bottom": () => [h(HomeFooter), h(CompilerModal)],
165 }), 20 }),
166 setup() {
167 if (typeof window === "undefined") return;
168 const route = useRoute();
169 watch(
170 () => route.path,
171 async () => {
172 await nextTick();
173 scheduleShikiRepairWithRetries();
174 },
175 { immediate: true },
176 );
177 },
178 enhanceApp({ app }) { 21 enhanceApp({ app }) {
179 app.component("CompilerModal", CompilerModal); 22 app.component("CompilerModal", CompilerModal);
180 app.component("YueCompiler", YueCompiler); 23 app.component("YueCompiler", YueCompiler);
181 app.component("YueDisplay", YueDisplay); 24 app.component("YueDisplay", YueDisplay);
182
183 if (typeof window !== "undefined") {
184 const run = () => {
185 requestAnimationFrame(applyShikiInlineColorFallback);
186 scheduleShikiRepairWithRetries();
187 };
188 window.addEventListener("DOMContentLoaded", run, { once: true });
189 window.addEventListener("load", run, { once: true });
190
191 const observer = new MutationObserver(run);
192 observer.observe(document.documentElement, {
193 attributes: true,
194 attributeFilter: ["class"],
195 });
196 }
197 }, 25 },
198}; 26};
199 27
diff --git a/doc/package.json b/doc/package.json
index 45fb213..a20002a 100755
--- a/doc/package.json
+++ b/doc/package.json
@@ -15,9 +15,9 @@
15 }, 15 },
16 "license": "MIT", 16 "license": "MIT",
17 "devDependencies": { 17 "devDependencies": {
18 "@types/node": "^25.2.0", 18 "@types/node": "^25.2.3",
19 "vitepress": "^1.6.4", 19 "vitepress": "^1.6.4",
20 "vue": "^3.4.38" 20 "vue": "^3.5.28"
21 }, 21 },
22 "dependencies": { 22 "dependencies": {
23 "@codemirror/commands": "^6.10.1", 23 "@codemirror/commands": "^6.10.1",