From fff01530bbbfed7e7b76566d15ade9836eeaf14f Mon Sep 17 00:00:00 2001 From: Li Jin Date: Thu, 12 Feb 2026 12:04:03 +0800 Subject: Again. [skip CI] --- doc/docs/.vitepress/theme/index.ts | 151 +++++++++++++++++++++++++++- doc/docs/de/doc/getting-started/usage.md | 2 +- doc/docs/doc/getting-started/usage.md | 2 +- doc/docs/id-id/doc/getting-started/usage.md | 2 +- doc/docs/pt-br/doc/getting-started/usage.md | 2 +- doc/docs/zh/doc/getting-started/usage.md | 2 +- 6 files changed, 154 insertions(+), 7 deletions(-) (limited to 'doc/docs') diff --git a/doc/docs/.vitepress/theme/index.ts b/doc/docs/.vitepress/theme/index.ts index 7e9882e..4dd7408 100644 --- a/doc/docs/.vitepress/theme/index.ts +++ b/doc/docs/.vitepress/theme/index.ts @@ -1,7 +1,11 @@ import DefaultTheme from "vitepress/theme"; import type { Theme } from "vitepress"; -import { h } from "vue"; +import { useRoute } from "vitepress"; +import { h, nextTick, watch } from "vue"; import "./custom.css"; +import darkPlus from "@shikijs/themes/dark-plus"; +import lightPlus from "@shikijs/themes/light-plus"; +import yuescriptGrammar from "../grammars/yuescript.tmLanguage.json"; // @ts-ignore import CompilerModal from "./components/CompilerModal.vue"; @@ -12,6 +16,134 @@ import YueCompiler from "./components/YueCompiler.vue"; // @ts-ignore import YueDisplay from "./components/YueDisplay.vue"; +type ShikiHighlighter = Awaited< + ReturnType<(typeof import("shiki/core"))["createHighlighterCore"]> +>; + +let shikiHighlighterPromise: Promise | null = null; +let shikiRepairQueued = false; +let shikiRepairRetry1 = 0; +let shikiRepairRetry2 = 0; + +type SupportedLanguage = "yuescript" | "lua" | "shellscript"; + +function toSupportedLanguage(className: string): SupportedLanguage | null { + const match = className.match(/(?:^|\s)language-([a-z0-9_-]+)/i); + if (!match) return null; + const lang = match[1].toLowerCase(); + if (lang === "yuescript" || lang === "yue") return "yuescript"; + if (lang === "lua") return "lua"; + if ( + lang === "shellscript" || + lang === "shell" || + lang === "bash" || + lang === "sh" || + lang === "zsh" + ) { + return "shellscript"; + } + return null; +} + +function getShikiHighlighter() { + if (!shikiHighlighterPromise) { + shikiHighlighterPromise = Promise.all([ + import("shiki/core"), + import("shiki/engine/javascript"), + import("@shikijs/langs/lua"), + import("@shikijs/langs/shellscript"), + ]).then( + ([ + { createHighlighterCore }, + { createJavaScriptRegexEngine }, + luaLang, + shellscriptLang, + ]) => + createHighlighterCore({ + themes: [lightPlus, darkPlus], + langs: [ + { + ...yuescriptGrammar, + name: "yuescript", + scopeName: "source.yuescript", + aliases: ["yue"], + }, + ...luaLang.default, + ...shellscriptLang.default, + ], + engine: createJavaScriptRegexEngine(), + }), + ); + } + return shikiHighlighterPromise; +} + +function collectPlainCodeTargets() { + if (typeof document === "undefined") return []; + const nodes = Array.from( + document.querySelectorAll( + "pre > code[class*='language-'], code[class*='language-']", + ), + ); + return nodes.filter((code) => { + if (code.closest("pre.shiki")) return false; + const pre = code.parentElement; + if (pre && pre.tagName === "PRE" && pre.classList.contains("shiki")) { + return false; + } + return toSupportedLanguage(code.className) !== null; + }); +} + +async function repairPlainCodeBlocks() { + const targets = collectPlainCodeTargets(); + if (!targets.length) return; + + const highlighter = await getShikiHighlighter(); + + for (const code of targets) { + const lang = toSupportedLanguage(code.className); + if (!lang) continue; + const source = (code.textContent || "").replace(/\r\n?/g, "\n"); + if (!source.trim()) continue; + const html = highlighter.codeToHtml(source, { + lang, + themes: { + light: "light-plus", + dark: "dark-plus", + }, + }); + const tpl = document.createElement("template"); + tpl.innerHTML = html.trim(); + const replacement = tpl.content.firstElementChild as HTMLElement | null; + if (!replacement) continue; + replacement.classList.add("vp-code"); + const pre = code.parentElement?.tagName === "PRE" ? code.parentElement : null; + (pre || code).replaceWith(replacement); + } + + applyShikiInlineColorFallback(); +} + +function scheduleShikiRepair() { + if (typeof window === "undefined") return; + if (shikiRepairQueued) return; + shikiRepairQueued = true; + window.requestAnimationFrame(() => { + shikiRepairQueued = false; + void repairPlainCodeBlocks(); + }); +} + +function scheduleShikiRepairWithRetries() { + if (typeof window === "undefined") return; + scheduleShikiRepair(); + window.clearTimeout(shikiRepairRetry1); + window.clearTimeout(shikiRepairRetry2); + shikiRepairRetry1 = window.setTimeout(scheduleShikiRepair, 120); + shikiRepairRetry2 = window.setTimeout(scheduleShikiRepair, 500); +} + function applyShikiInlineColorFallback() { if (typeof document === "undefined") return; const isDark = document.documentElement.classList.contains("dark"); @@ -31,13 +163,28 @@ const theme: Theme = { h(DefaultTheme.Layout, null, { "layout-bottom": () => [h(HomeFooter), h(CompilerModal)], }), + setup() { + if (typeof window === "undefined") return; + const route = useRoute(); + watch( + () => route.path, + async () => { + await nextTick(); + scheduleShikiRepairWithRetries(); + }, + { immediate: true }, + ); + }, enhanceApp({ app }) { app.component("CompilerModal", CompilerModal); app.component("YueCompiler", YueCompiler); app.component("YueDisplay", YueDisplay); if (typeof window !== "undefined") { - const run = () => requestAnimationFrame(applyShikiInlineColorFallback); + const run = () => { + requestAnimationFrame(applyShikiInlineColorFallback); + scheduleShikiRepairWithRetries(); + }; window.addEventListener("DOMContentLoaded", run, { once: true }); window.addEventListener("load", run, { once: true }); diff --git a/doc/docs/de/doc/getting-started/usage.md b/doc/docs/de/doc/getting-started/usage.md index 2c10406..2a1864a 100644 --- a/doc/docs/de/doc/getting-started/usage.md +++ b/doc/docs/de/doc/getting-started/usage.md @@ -8,7 +8,7 @@ YueScript-Modul in Lua verwenden: "your_yuescript_entry.yue" in Lua require'n. - ```Lua + ```lua require("yue")("your_yuescript_entry") ``` diff --git a/doc/docs/doc/getting-started/usage.md b/doc/docs/doc/getting-started/usage.md index 8818dd0..c02e8c0 100644 --- a/doc/docs/doc/getting-started/usage.md +++ b/doc/docs/doc/getting-started/usage.md @@ -8,7 +8,7 @@ Use YueScript module in Lua: Require "your_yuescript_entry.yue" in Lua. - ```Lua + ```lua require("yue")("your_yuescript_entry") ``` diff --git a/doc/docs/id-id/doc/getting-started/usage.md b/doc/docs/id-id/doc/getting-started/usage.md index a9523f7..a092163 100644 --- a/doc/docs/id-id/doc/getting-started/usage.md +++ b/doc/docs/id-id/doc/getting-started/usage.md @@ -8,7 +8,7 @@ Gunakan modul YueScript di Lua: Require "your_yuescript_entry.yue" di Lua. - ```Lua + ```lua require("yue")("your_yuescript_entry") ``` diff --git a/doc/docs/pt-br/doc/getting-started/usage.md b/doc/docs/pt-br/doc/getting-started/usage.md index 055c6c2..c3b5dfe 100644 --- a/doc/docs/pt-br/doc/getting-started/usage.md +++ b/doc/docs/pt-br/doc/getting-started/usage.md @@ -8,7 +8,7 @@ Use o módulo YueScript em Lua: Use require em "your_yuescript_entry.yue" no Lua. - ```Lua + ```lua require("yue")("your_yuescript_entry") ``` diff --git a/doc/docs/zh/doc/getting-started/usage.md b/doc/docs/zh/doc/getting-started/usage.md index c9bad2e..b605e7c 100644 --- a/doc/docs/zh/doc/getting-started/usage.md +++ b/doc/docs/zh/doc/getting-started/usage.md @@ -8,7 +8,7 @@   在 Lua 中引入 "你的脚本入口文件.yue"。 - ```Lua + ```lua require("yue")("你的脚本入口文件") ``` -- cgit v1.2.3-55-g6feb