diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 5163512..b8eb47d 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -114,7 +114,12 @@ export default defineConfig({ }, markdown: { config: (md) => { - md.use(vitepressDemoPlugin) + md.use(vitepressDemoPlugin, { + playground: { show: true }, + codeTransformer: (code) => { + return code.replace(/import\.meta\.env\.BASE_URL/g, `'${process.env.VITEPRESS_BASE || '/'}'`) + }, + }) md.use(tabsMarkdownPlugin) } }, diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index 28328ec..367a031 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -1,3 +1,4 @@ +import { generateStore, getDefaultFiles } from '@opentiny/tiny-robot-playground/utils' import DefaultTheme from 'vitepress/theme' import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client' import { setupDarkModeListener } from './color-mode' @@ -15,6 +16,7 @@ import './style.css' declare global { interface Window { __SW_REGISTERED__?: boolean + __CODE_PLAYGROUND_LISTENED__?: boolean } } @@ -27,6 +29,7 @@ export default { app.mixin({ mounted() { registerServiceWorker() + listenCodePlaygroundEvent() }, }) app.component('HomePage', HomePage) @@ -71,3 +74,62 @@ function registerServiceWorker() { console.log('ServiceWorker registration failed: ', err) }) } + +function listenCodePlaygroundEvent() { + if (typeof window === 'undefined' || window.__CODE_PLAYGROUND_LISTENED__) { + return + } + + window.__CODE_PLAYGROUND_LISTENED__ = true + document.addEventListener('code-playground', (event) => { + const detail = (event as CustomEvent).detail + if (!detail) return + const { props, currentFiles, activeFile } = detail + + const files: { filename: string; code: string }[] = [] + + if (Object.keys(currentFiles).length === 0) { + files.push({ + filename: 'src/App.vue', + code: props.vueCode, + }) + } else { + files.push({ + filename: 'src/App.vue', + code: currentFiles[activeFile].code, + }) + + Object.entries(currentFiles).forEach(([filename, file]) => { + if (filename === activeFile) return + files.push({ filename: `src/${filename}`, code: (file as { code: string }).code }) + }) + } + + const tinyRobotVersion = 'latest' + const defaultFiles = getDefaultFiles({ tinyRobotVersion }) + const cssFile = defaultFiles.find((file) => file.filename === 'src/index.css') + if (cssFile) { + files.push(cssFile) + } + + const extraPackages: string[] = JSON.parse(decodeURIComponent(props.playground))?.packages || [] + const extraImports = extraPackages + .map((pkgAndVersion) => { + const index = pkgAndVersion.lastIndexOf('@') + const pkg = pkgAndVersion.slice(0, index) + const version = pkgAndVersion.slice(index + 1) + return { [pkg]: version } + }) + .reduce((acc, curr) => { + return { ...acc, ...curr } + }, {}) + + const { store } = generateStore({ + tinyRobotVersion, + files, + extraImports, + }) + + window.open(`https://playground.opentiny.design/tiny-robot.html` + store.serialize(), '_blank') + }) +} diff --git a/package.json b/package.json index eeacbed..c76e8ee 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,22 @@ }, "devDependencies": { "@opentiny/tiny-robot": "workspace:^", + "@opentiny/tiny-robot-playground": "workspace:^", "@vitejs/plugin-vue-jsx": "^4.1.2", "cross-env": "^7.0.3", "gh-pages": "^2.1.1", "less": "^4.4.1", "medium-zoom": "^1.1.0", "vitepress": "^1.6.3", - "vitepress-demo-plugin": "^1.4.2", + "vitepress-demo-plugin": "1.5.0", "vitepress-plugin-tabs": "^0.7.3" }, "dependencies": { "vue": "^3.5.13" + }, + "pnpm": { + "patchedDependencies": { + "vitepress-demo-plugin@1.5.0": "patches/vitepress-demo-plugin@1.5.0.patch" + } } } diff --git a/patches/vitepress-demo-plugin@1.5.0.patch b/patches/vitepress-demo-plugin@1.5.0.patch new file mode 100644 index 0000000..0fe96ec --- /dev/null +++ b/patches/vitepress-demo-plugin@1.5.0.patch @@ -0,0 +1,219 @@ +diff --git a/dist/index.d.ts b/dist/index.d.ts +index 2cbca8520f72560e73c2b6eba13eff51484ab3dd..605b59e5c08f46cd6852171d92d7177cde7f7119 100644 +--- a/dist/index.d.ts ++++ b/dist/index.d.ts +@@ -53,6 +53,14 @@ declare interface VitepressDemoBoxConfig { + * @en The configuration of the codeplayer platform + */ + codeplayer?: Platform; ++ /** ++ * @cn playground 配置 ++ * @en The configuration of the playground ++ */ ++ playground?: { ++ show: boolean; ++ packages?: string[]; ++ }; + /** + * @cn vue 展示的代码文件 + * @en The code files of the vue +@@ -88,6 +96,11 @@ declare interface VitepressDemoBoxConfig { + * @en The locale configuration 'zh-CN' | 'en-US' + */ + locale?: Locale; ++ /** ++ * @cn 代码转换函数 ++ * @en The code transformer function ++ */ ++ codeTransformer?: (code: string, type: string) => string; + } + + export declare const vitepressDemoPlugin: (md: default_2 & any, params?: VitepressDemoBoxConfig) => void; +diff --git a/dist/index.js b/dist/index.js +index 00b16443191a4aed8cca2a24518ab6320676d5f4..4190083068399d9a025ece1a747f39fd3b4fd5fa 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -2075,7 +2075,7 @@ var ri, vn = function(i) { + return typeof i == "string" && bn().test(i); + }; + function bn() { +- return ri || (ri = new RegExp(`^[\\s ++ return ri || (ri = new RegExp(`^[\\s + \v\f\r \xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF"]+$`)); + } + /*! +@@ -2968,6 +2968,24 @@ function Is(n, i) { + L("path", { d: "m22 13.29-3.33-10a.42.42 0 0 0-.14-.18.38.38 0 0 0-.22-.11.39.39 0 0 0-.23.07.42.42 0 0 0-.14.18l-2.26 6.67H8.32L6.1 3.26a.42.42 0 0 0-.1-.18.38.38 0 0 0-.26-.08.39.39 0 0 0-.23.07.42.42 0 0 0-.14.18L2 13.29a.74.74 0 0 0 .27.83L12 21l9.69-6.88a.71.71 0 0 0 .31-.83Z" }, null, -1) + ])); + } ++const playgroundIconProps = { ++ xmlns: "http://www.w3.org/2000/svg", ++ width: "24", ++ height: "24", ++ viewBox: "0 0 24 24", ++ fill: "none", ++ stroke: "currentColor", ++ "stroke-width": "2", ++ "stroke-linecap": "round", ++ "stroke-linejoin": "round", ++ class: "lucide lucide-play-icon lucide-play" ++}; ++function playgroundIconVnode(n, i) { ++ return I(), B("svg", playgroundIconProps, i[0] || (i[0] = [ ++ L("path", { d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" }, null, -1) ++ ])); ++} ++const playgroundIcon = st({}, [["render", playgroundIconVnode]]); + const Ps = /* @__PURE__ */ st(Ds, [["render", Is]]), Bs = {}, Ms = { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:xlink": "http://www.w3.org/1999/xlink", +@@ -3079,6 +3097,7 @@ const ue = [], qs = { + const lt = /* @__PURE__ */ st(Vs, [["__scopeId", "data-v-588bc5b3"]]), Kt = { + openInStackblitz: "\u5728 Stackblitz \u4E2D\u6253\u5F00", + openInCodeSandbox: "\u5728 Codesandbox \u4E2D\u6253\u5F00", ++ openInPlayground: "\u5728 Playground \u4E2D\u6253\u5F00", + openInGithub: "\u5728 GitHub \u4E2D\u6253\u5F00", + openInGitlab: "\u5728 GitLub \u4E2D\u6253\u5F00", + collapseCode: "\u6536\u8D77\u4EE3\u7801", +@@ -3088,6 +3107,7 @@ const lt = /* @__PURE__ */ st(Vs, [["__scopeId", "data-v-588bc5b3"]]), Kt = { + }, ji = { + openInStackblitz: "Open In Stackblitz", + openInCodeSandbox: "Open In Codesandbox", ++ openInPlayground: "Open In Playground", + openInGithub: "Open in GitHub", + openInGitlab: "Open in GitLab", + collapseCode: "Collapse Code", +@@ -3139,6 +3159,7 @@ const Qs = { style: { "flex-shrink": "0" } }, Js = ["innerHTML"], Zs = ["onClick + stackblitz: {}, + codesandbox: {}, + codeplayer: {}, ++ playground: {}, + scope: {}, + files: {}, + lightTheme: {}, +@@ -3166,6 +3187,7 @@ const Qs = { style: { "flex-shrink": "0" } }, Js = ["innerHTML"], Zs = ["onClick + } + const u = et(() => JSON.parse(decodeURIComponent(e.stackblitz || "{}"))), d = et(() => JSON.parse(decodeURIComponent(e.codesandbox || "{}"))); + et(() => JSON.parse(decodeURIComponent(e.codeplayer || "{}"))); ++ const playgroundProp = et(() => JSON.parse(decodeURIComponent(e.playground || "{}"))); + const o = q(""), _ = et(() => { + var W; + const M = JSON.parse(decodeURIComponent(e.files || "{}"))[c.value]; +@@ -3209,6 +3231,15 @@ const Qs = { style: { "flex-shrink": "0" } }, Js = ["innerHTML"], Zs = ["onClick + window.open(e.github, "_blank"); + }, O = () => { + window.open(e.gitlab, "_blank"); ++ }, openInPlayground = () => { ++ const R = new CustomEvent("code-playground", { ++ detail: { ++ props: e, ++ currentFiles: _.value, ++ activeFile: o.value ++ } ++ }); ++ document.dispatchEvent(R); + }; + at( + () => c.value, +@@ -3427,7 +3458,16 @@ const Qs = { style: { "flex-shrink": "0" } }, Js = ["innerHTML"], Zs = ["onClick + tt(Zi, { onClick: s }) + ]), + _: 1 +- }, 8, ["content"]) ++ }, 8, ["content"]), ++ playgroundProp.value.show ? (I(), pt(lt, { ++ key: 6, ++ content: $(J).openInPlayground ++ }, { ++ default: ot(() => [ ++ tt(playgroundIcon, { onClick: openInPlayground }) ++ ]), ++ _: 1 ++ }, 8, ["content"])) : V("", !0) + ], 2) + ], 2), + L("section", { +@@ -3544,14 +3584,15 @@ const Lr = /* @__PURE__ */ st(ir, [["__scopeId", "data-v-5eb3f8d4"]]), pi = [ + e.join("-").split(".").slice(0, -1).join(".") + ) + ).replace(/=/g, "Equal"); +-}, or = /title="(.*?)"/, _r = /vue="(.*?)"/, ur = /html="(.*?)"/, lr = /react="(.*?)"/, pr = /description="(.*?)"/, hr = /order="(.*?)"/, cr = /select="(.*?)"/, fr = /github="(.*?)"/, dr = /gitlab="(.*?)"/, mr = /stackblitz="(.*?)"/, gr = /codesandbox="(.*?)"/, vr = /codeplayer="(.*?)"/, br = /scope="(.*?)"/, yr = /vueFiles=("\{((.|\n)*?)\}"|"\[((.|\n)*?)\]")/, wr = /reactFiles=("\{((.|\n)*?)\}"|"\[((.|\n)*?)\]")/, xr = /htmlFiles=("\{((.|\n)*?)\}"|"\[((.|\n)*?)\]")/, Er = /ssg="(.*?)"/, Or = /htmlWriteWay="(.*?)"/, Tr = /background="(.*?)"/, hi = (n, i, e, t) => { ++}, or = /title="(.*?)"/, _r = /vue="(.*?)"/, ur = /html="(.*?)"/, lr = /react="(.*?)"/, pr = /description="(.*?)"/, hr = /order="(.*?)"/, cr = /select="(.*?)"/, fr = /github="(.*?)"/, dr = /gitlab="(.*?)"/, mr = /stackblitz="(.*?)"/, gr = /codesandbox="(.*?)"/, vr = /codeplayer="(.*?)"/, pgdRegex = /playground="(.*?)"/, pgdPkgRegex = /playgroundPackages="(.*?)"/, br = /scope="(.*?)"/, yr = /vueFiles=("\{((.|\n)*?)\}"|"\[((.|\n)*?)\]")/, wr = /reactFiles=("\{((.|\n)*?)\}"|"\[((.|\n)*?)\]")/, xr = /htmlFiles=("\{((.|\n)*?)\}"|"\[((.|\n)*?)\]")/, Er = /ssg="(.*?)"/, Or = /htmlWriteWay="(.*?)"/, Tr = /background="(.*?)"/, hi = (n, i, e, t) => { + var Ee, Oe, Te, ke, Re; + const { + demoDir: a, + tab: u = {}, + stackblitz: d = { show: !1 }, + codesandbox: o = { show: !1 }, +- codeplayer: _ = { show: !1 } ++ codeplayer: _ = { show: !1 }, ++ codeTransformer = (c) => c, + } = t || {}; + let { + order: g = "vue,react,html", +@@ -3564,10 +3605,20 @@ const Lr = /* @__PURE__ */ st(ir, [["__scopeId", "data-v-5eb3f8d4"]]), pi = [ + description: "", + html: "", + react: "" +- }, k = i.content.match(or), C = i.content.match(_r), R = i.content.match(ur), h = i.content.match(lr), r = i.content.match(pr), f = i.content.match(hr), T = i.content.match(cr), x = i.content.match(fr), E = i.content.match(dr), l = i.content.match(mr), m = i.content.match(gr), p = i.content.match(vr), O = ((Ee = i.content.match(br)) == null ? void 0 : Ee[1]) || "", s = i.content.match(yr), y = i.content.match(wr), w = i.content.match(xr), A = !!((Oe = i.content.match(Er)) != null && Oe[1]), N = ((Te = i.content.match(Or)) == null ? void 0 : Te[1]) || "write", j = (ke = i.content.match(Tr)) == null ? void 0 : ke[1], P = (Re = e.realPath) != null ? Re : e.path, F = a || K.dirname(P); ++ }, k = i.content.match(or), C = i.content.match(_r), R = i.content.match(ur), h = i.content.match(lr), r = i.content.match(pr), f = i.content.match(hr), T = i.content.match(cr), x = i.content.match(fr), E = i.content.match(dr), l = i.content.match(mr), m = i.content.match(gr), p = i.content.match(vr), pgdV = i.content.match(pgdRegex), pgdPkgV = i.content.match(pgdPkgRegex), O = ((Ee = i.content.match(br)) == null ? void 0 : Ee[1]) || "", s = i.content.match(yr), y = i.content.match(wr), w = i.content.match(xr), A = !!((Oe = i.content.match(Er)) != null && Oe[1]), N = ((Te = i.content.match(Or)) == null ? void 0 : Te[1]) || "write", j = (ke = i.content.match(Tr)) == null ? void 0 : ke[1], P = (Re = e.realPath) != null ? Re : e.path, F = a || K.dirname(P); + f != null && f[1] && (g = f[1]), T != null && T[1] && (b = T[1]); + let z = "", it = ""; + x != null && x[1] && (z = x[1]), E != null && E[1] && (it = E[1]), l != null && l[1] && (d.show = l[1] === "true"), m != null && m[1] && (o.show = m[1] === "true"), p != null && p[1] && (_.show = p[1] === "true"), C != null && C[1] && (c.vue = K.join(F, C[1]).replace(/\\/g, "/")), R != null && R[1] && (c.html = K.join(F, R[1]).replace(/\\/g, "/")), h != null && h[1] && (c.react = K.join(F, h[1]).replace(/\\/g, "/")), c.title = k ? k[1] : "", c.description = r ? r[1] : ""; ++ const playground = { show: !1, ...t == null ? void 0 : t.playground }; ++ pgdV != null && pgdV[1] && (playground.show = pgdV[1] === "true"); ++ if (pgdPkgV != null && pgdPkgV[1]) { ++ const packages = pgdPkgV[1].split(","); ++ if(Array.isArray(playground.packages)) { ++ playground.packages = playground.packages.concat(packages); ++ } else { ++ playground.packages = packages; ++ } ++ } + const S = c.vue ? K.resolve( + a || K.dirname(P), + (C == null ? void 0 : C[1]) || "." +@@ -3663,7 +3714,7 @@ const Lr = /* @__PURE__ */ st(ir, [["__scopeId", "data-v-5eb3f8d4"]]), pi = [ + const Ce = K.resolve(a || K.dirname(P), Et || ".").replace(/\\/g, "/"); + if (De.existsSync(Ce)) { + const Di = De.readFileSync(Ce, "utf-8"); +- rt[Z][Y].code = Di; ++ rt[Z][Y].code = codeTransformer(Di, Z); + } else + delete rt[Z][Y]; + } else +@@ -3676,7 +3727,7 @@ const Lr = /* @__PURE__ */ st(ir, [["__scopeId", "data-v-5eb3f8d4"]]), pi = [ + return (t == null ? void 0 : t.locale) && typeof t.locale == "object" && (xe = encodeURIComponent(JSON.stringify(t.locale))), ` + ${A ? "" : ``} + ${A ? "" : ""} +-