@deot/docs-playground 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2025 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let vue = require("vue");
25
+ let _deot_vc = require("@deot/vc");
26
+ let _deot_docs_locale = require("@deot/docs-locale");
27
+ let highlight_js = require("highlight.js");
28
+ highlight_js = __toESM(highlight_js);
29
+ let _vue_repl = require("@vue/repl");
30
+ let codemirror = require("codemirror");
31
+ let _codemirror_lang_css = require("@codemirror/lang-css");
32
+ let _codemirror_lang_javascript = require("@codemirror/lang-javascript");
33
+ let _codemirror_lang_vue = require("@codemirror/lang-vue");
34
+ let _codemirror_view = require("@codemirror/view");
35
+ //#region packages/playground/src/highlight.ts
36
+ var vueHighlight = (api) => ({
37
+ subLanguage: "xml",
38
+ contains: [
39
+ api.COMMENT("<!--", "-->", { relevance: 10 }),
40
+ {
41
+ begin: /^(\s*)(<script>)/gm,
42
+ end: /^(\s*)(<\/script>)/gm,
43
+ subLanguage: "javascript",
44
+ excludeBegin: true,
45
+ excludeEnd: true
46
+ },
47
+ {
48
+ begin: /^(?:\s*)(?:<script\s+lang=(["'])ts\1>)/gm,
49
+ end: /^(\s*)(<\/script>)/gm,
50
+ subLanguage: "typescript",
51
+ excludeBegin: true,
52
+ excludeEnd: true
53
+ },
54
+ {
55
+ begin: /^(\s*)(<style(\s+scoped)?>)/gm,
56
+ end: /^(\s*)(<\/style>)/gm,
57
+ subLanguage: "css",
58
+ excludeBegin: true,
59
+ excludeEnd: true
60
+ },
61
+ {
62
+ begin: /^(?:\s*)(?:<style(?:\s+scoped)?\s+lang=(["'])(?:s[ca]ss)\1(?:\s+scoped)?>)/gm,
63
+ end: /^(\s*)(<\/style>)/gm,
64
+ subLanguage: "scss",
65
+ excludeBegin: true,
66
+ excludeEnd: true
67
+ },
68
+ {
69
+ begin: /^(?:\s*)(?:<style(?:\s+scoped)?\s+lang=(["'])stylus\1(?:\s+scoped)?>)/gm,
70
+ end: /^(\s*)(<\/style>)/gm,
71
+ subLanguage: "stylus",
72
+ excludeBegin: true,
73
+ excludeEnd: true
74
+ }
75
+ ]
76
+ });
77
+ var registerVueHighlight = (api = highlight_js.default) => {
78
+ if (!api.getLanguage("vue")) api.registerLanguage("vue", vueHighlight);
79
+ return api;
80
+ };
81
+ var resolveHighlightLanguage = (filename) => {
82
+ if (/\.vue$/i.test(filename)) return "vue";
83
+ if (/\.tsx?$/i.test(filename)) return "typescript";
84
+ if (/\.jsx?$/i.test(filename)) return "javascript";
85
+ if (/\.html$/i.test(filename)) return "xml";
86
+ if (/\.css$/i.test(filename)) return "css";
87
+ if (/\.s[ac]ss$/i.test(filename)) return "scss";
88
+ if (/\.json$/i.test(filename)) return "json";
89
+ return "plaintext";
90
+ };
91
+ var escapeHTML = (code) => code.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
92
+ var TAB_REPLACE_RE = /^(<[^>]+>|\t)+/gm;
93
+ var normalizeHighlightTabs = (code) => code.replace(TAB_REPLACE_RE, (match) => match.replace(/\t/g, " "));
94
+ var highlightCodeByLanguage = (code, language) => {
95
+ try {
96
+ const highlighted = registerVueHighlight().highlight(code, {
97
+ language: language || "plaintext",
98
+ ignoreIllegals: true
99
+ }).value;
100
+ return normalizeHighlightTabs(highlighted);
101
+ } catch {
102
+ return escapeHTML(code);
103
+ }
104
+ };
105
+ var highlightCode = (code, filename) => highlightCodeByLanguage(code, resolveHighlightLanguage(filename));
106
+ //#endregion
107
+ //#region packages/playground/src/icon/icons.ts
108
+ var PLAYGROUND_ICON_PATHS = {
109
+ runtime: "M5 3h14a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Zm3 18h8m-4-3v3m-2-13 5 3-5 3V8Z",
110
+ files: "M6 2h8l5 5v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2Zm8 0v6h5m-9 5-2 2 2 2m5-4 2 2-2 2",
111
+ copy: "M9 8h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2Zm-2 8H5a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v4",
112
+ editor: "M8 8 4 12l4 4m8-8 4 4-4 4m-3-10-2 12",
113
+ viewport: "M3 4h12v10H3V4Zm3 14h6m-3-4v4m9-9h3v11h-6V9h3m0 9h0"
114
+ };
115
+ //#endregion
116
+ //#region packages/playground/src/icon/icon.vue?vue&type=script&setup=true&lang.ts
117
+ var _hoisted_1$5 = {
118
+ class: "docs-playground-icon",
119
+ viewBox: "0 0 24 24",
120
+ fill: "none",
121
+ stroke: "currentColor",
122
+ "stroke-width": "1.8",
123
+ "stroke-linecap": "round",
124
+ "stroke-linejoin": "round",
125
+ "aria-hidden": "true"
126
+ };
127
+ var _hoisted_2$4 = ["d"];
128
+ //#endregion
129
+ //#region packages/playground/src/icon/icon.vue
130
+ var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
131
+ __name: "icon",
132
+ props: { name: {} },
133
+ setup(__props) {
134
+ return (_ctx, _cache) => {
135
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("svg", _hoisted_1$5, [(0, vue.createElementVNode)("path", { d: (0, vue.unref)(PLAYGROUND_ICON_PATHS)[__props.name] }, null, 8, _hoisted_2$4)]);
136
+ };
137
+ }
138
+ });
139
+ //#endregion
140
+ //#region packages/playground/src/core/code-preview/github.css?inline
141
+ var github_default = "pre code.hljs{display:block;padding:1em;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{font-weight:700;color:#005cc5}.hljs-bullet{color:#735c0f}.hljs-emphasis{font-style:italic;color:#24292e}.hljs-strong{font-weight:700;color:#24292e}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}[data-doc-theme=dark] .hljs,[data-vc-theme=dark] .hljs{color:#c9d1d9;background:#0d1117}[data-doc-theme=dark] .hljs-doctag,[data-doc-theme=dark] .hljs-keyword,[data-doc-theme=dark] .hljs-meta .hljs-keyword,[data-doc-theme=dark] .hljs-template-tag,[data-doc-theme=dark] .hljs-template-variable,[data-doc-theme=dark] .hljs-type,[data-doc-theme=dark] .hljs-variable.language_,[data-vc-theme=dark] .hljs-doctag,[data-vc-theme=dark] .hljs-keyword,[data-vc-theme=dark] .hljs-meta .hljs-keyword,[data-vc-theme=dark] .hljs-template-tag,[data-vc-theme=dark] .hljs-template-variable,[data-vc-theme=dark] .hljs-type,[data-vc-theme=dark] .hljs-variable.language_{color:#ff7b72}[data-doc-theme=dark] .hljs-title,[data-doc-theme=dark] .hljs-title.class_,[data-doc-theme=dark] .hljs-title.function_,[data-vc-theme=dark] .hljs-title,[data-vc-theme=dark] .hljs-title.class_,[data-vc-theme=dark] .hljs-title.function_{color:#d2a8ff}[data-doc-theme=dark] .hljs-attr,[data-doc-theme=dark] .hljs-literal,[data-doc-theme=dark] .hljs-number,[data-doc-theme=dark] .hljs-variable,[data-vc-theme=dark] .hljs-attr,[data-vc-theme=dark] .hljs-literal,[data-vc-theme=dark] .hljs-number,[data-vc-theme=dark] .hljs-variable{color:#79c0ff}[data-doc-theme=dark] .hljs-regexp,[data-doc-theme=dark] .hljs-string,[data-vc-theme=dark] .hljs-regexp,[data-vc-theme=dark] .hljs-string{color:#a5d6ff}[data-doc-theme=dark] .hljs-code,[data-doc-theme=dark] .hljs-comment,[data-vc-theme=dark] .hljs-code,[data-vc-theme=dark] .hljs-comment{color:#8b949e}[data-doc-theme=dark] .hljs-name,[data-doc-theme=dark] .hljs-selector-tag,[data-vc-theme=dark] .hljs-name,[data-vc-theme=dark] .hljs-selector-tag{color:#7ee787}";
142
+ //#endregion
143
+ //#region packages/playground/src/core/code-preview/code-preview.scss?inline
144
+ var code_preview_default$1 = ".docs-code-preview{position:relative;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:0;overflow:hidden;background:var(--docs-background-color-soft,var(--vc-background-color,#f7f8fa));border-radius:8px}.docs-code-preview__language{z-index:1;overflow:hidden;font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.docs-code-preview__copy,.docs-code-preview__language{position:absolute;top:6px;right:12px;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;width:28px;height:28px;color:var(--docs-foreground-color-mute,var(--vc-color-dark-extralight,#71717a));-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.docs-code-preview__copy{z-index:2;padding:0;pointer-events:none;cursor:pointer;background:var(--docs-background-color-soft,var(--vc-background-color,#f7f8fa));border:1px solid var(--docs-border-color,var(--vc-color-light-deeper,#d4d4d8));border-radius:8px;opacity:0;-webkit-transition:opacity .15s ease,background-color .15s ease;transition:opacity .15s ease,background-color .15s ease}.docs-code-preview__copy:hover{background:var(--docs-background-color,var(--vc-background-color-light,#fff))}.docs-code-preview__copy:focus-visible{pointer-events:auto;opacity:1}.docs-code-preview:hover .docs-code-preview__copy{pointer-events:auto;opacity:1}.docs-code-preview__code{width:100%;min-height:0;margin:0;overflow:auto;-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto}.docs-code-preview__code,.docs-code-preview__code code.hljs{background:var(--docs-background-color-soft,var(--vc-background-color,#f7f8fa))}.docs-code-preview__code code.hljs{min-height:100%;padding:12px 56px 12px 24px;-webkit-box-sizing:border-box;box-sizing:border-box}";
145
+ //#endregion
146
+ //#region packages/playground/src/core/code-preview/style.ts
147
+ var CODE_PREVIEW_STYLE_ID = "docs-code-preview-style";
148
+ var styleText = `${github_default}\n${code_preview_default$1}`;
149
+ var ensureCodePreviewStyle = () => {
150
+ if (typeof document === "undefined") return;
151
+ let style = document.getElementById(CODE_PREVIEW_STYLE_ID);
152
+ if (!style) {
153
+ style = document.createElement("style");
154
+ style.id = CODE_PREVIEW_STYLE_ID;
155
+ document.head.appendChild(style);
156
+ }
157
+ if (style.textContent !== styleText) style.textContent = styleText;
158
+ };
159
+ //#endregion
160
+ //#region packages/playground/src/core/code-preview/code-preview.vue?vue&type=script&setup=true&lang.ts
161
+ var _hoisted_1$4 = { class: "docs-code-preview" };
162
+ var _hoisted_2$3 = ["title"];
163
+ var _hoisted_3$3 = { class: "docs-code-preview__code" };
164
+ var _hoisted_4$3 = ["innerHTML"];
165
+ //#endregion
166
+ //#region packages/playground/src/core/code-preview/code-preview.vue
167
+ var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
168
+ __name: "code-preview",
169
+ props: {
170
+ code: {},
171
+ filename: { default: "" },
172
+ language: { default: "" },
173
+ copyLabel: { default: void 0 },
174
+ locale: {}
175
+ },
176
+ setup(__props) {
177
+ ensureCodePreviewStyle();
178
+ const props = __props;
179
+ const { t } = (0, _deot_docs_locale.useLocale)((0, vue.computed)(() => props.locale));
180
+ const resolvedCopyLabel = (0, vue.computed)(() => props.copyLabel || t("playground.common.copyCode"));
181
+ const languageLabels = {
182
+ javascript: "js",
183
+ typescript: "ts",
184
+ plaintext: "txt",
185
+ xml: "html"
186
+ };
187
+ const filenameExtension = (0, vue.computed)(() => props.filename.split(".").at(-1)?.toLowerCase() || "");
188
+ const languageLabel = (0, vue.computed)(() => {
189
+ const language = props.language.toLowerCase();
190
+ return languageLabels[language] || language || filenameExtension.value;
191
+ });
192
+ const highlightLanguage = (0, vue.computed)(() => props.language || resolveHighlightLanguage(props.filename));
193
+ const highlightedCode = (0, vue.computed)(() => highlightCodeByLanguage(props.code, highlightLanguage.value));
194
+ return (_ctx, _cache) => {
195
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$4, [
196
+ languageLabel.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
197
+ key: 0,
198
+ class: "docs-code-preview__language",
199
+ title: languageLabel.value
200
+ }, (0, vue.toDisplayString)(languageLabel.value), 9, _hoisted_2$3)) : (0, vue.createCommentVNode)("v-if", true),
201
+ (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
202
+ class: "docs-code-preview__copy",
203
+ value: __props.code,
204
+ tag: "button",
205
+ type: "button",
206
+ title: resolvedCopyLabel.value,
207
+ "aria-label": resolvedCopyLabel.value
208
+ }, {
209
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "copy" })]),
210
+ _: 1
211
+ }, 8, [
212
+ "value",
213
+ "title",
214
+ "aria-label"
215
+ ]),
216
+ (0, vue.createCommentVNode)(" eslint-disable-next-line vue/no-v-html "),
217
+ (0, vue.createElementVNode)("pre", _hoisted_3$3, [(0, vue.createElementVNode)("code", {
218
+ class: "hljs",
219
+ innerHTML: highlightedCode.value
220
+ }, null, 8, _hoisted_4$3)])
221
+ ]);
222
+ };
223
+ }
224
+ });
225
+ //#endregion
226
+ //#region packages/playground/src/utils.ts
227
+ /**
228
+ * 比较两组 playground 文件是否完全一致。
229
+ * @param left 左侧文件表。
230
+ * @param right 右侧文件表。
231
+ * @returns 键集合与各文件内容是否都相同。
232
+ */
233
+ var filesEqual = (left, right) => {
234
+ const leftKeys = Object.keys(left);
235
+ const rightKeys = Object.keys(right);
236
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => left[key] === right[key]);
237
+ };
238
+ /**
239
+ * 视图切换条使用的文案键。
240
+ * @param view 当前预览视图。
241
+ * @returns locale 消息键。
242
+ */
243
+ var playgroundViewMessage = (view) => view === "runtime" ? "playground.runtime.preview" : "playground.runtime.files";
244
+ //#endregion
245
+ //#region packages/playground/src/core/files-preview/files-preview.vue?vue&type=script&setup=true&lang.ts
246
+ var _hoisted_1$3 = { class: "docs-playground-files" };
247
+ var _hoisted_2$2 = { class: "docs-playground-files__toolbar" };
248
+ var _hoisted_3$2 = ["data-filename", "onClick"];
249
+ var _hoisted_4$2 = {
250
+ key: 0,
251
+ class: "docs-playground-files__entry"
252
+ };
253
+ var _hoisted_5$2 = { class: "docs-playground-files__actions" };
254
+ var _hoisted_6$2 = {
255
+ key: 0,
256
+ class: "docs-playground__views docs-playground__views--files"
257
+ };
258
+ var _hoisted_7$2 = [
259
+ "title",
260
+ "aria-label",
261
+ "aria-pressed",
262
+ "onClick"
263
+ ];
264
+ //#endregion
265
+ //#region packages/playground/src/core/files-preview/files-preview.vue
266
+ var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
267
+ __name: "files-preview",
268
+ props: {
269
+ files: {},
270
+ entry: {},
271
+ views: {},
272
+ activeView: {},
273
+ activeFilename: {}
274
+ },
275
+ emits: ["active-change", "view-change"],
276
+ setup(__props, { emit: __emit }) {
277
+ const props = __props;
278
+ const { t } = (0, _deot_docs_locale.useLocale)();
279
+ const getViewText = (view) => t(playgroundViewMessage(view));
280
+ const emit = __emit;
281
+ const filenames = (0, vue.computed)(() => Object.keys(props.files));
282
+ const activeCode = (0, vue.computed)(() => props.files[props.activeFilename] || "");
283
+ const handleActive = (filename) => {
284
+ if (filename !== props.activeFilename) emit("active-change", filename);
285
+ };
286
+ const handleView = (view) => emit("view-change", view);
287
+ return (_ctx, _cache) => {
288
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$3, [(0, vue.createElementVNode)("div", _hoisted_2$2, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
289
+ class: "docs-playground-files__scroller",
290
+ "auto-resize": true,
291
+ native: false,
292
+ "show-bar": true,
293
+ height: "44",
294
+ "content-class": "docs-playground-files__tabs",
295
+ "content-style": "height: 44px; overflow: hidden;",
296
+ "wrapper-style": "height: 44px; overflow-x: auto; overflow-y: hidden;"
297
+ }, {
298
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(filenames.value, (filename) => {
299
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
300
+ key: filename,
301
+ type: "button",
302
+ class: (0, vue.normalizeClass)(["docs-playground-files__tab", { "is-active": filename === __props.activeFilename }]),
303
+ "data-filename": filename,
304
+ onClick: ($event) => handleActive(filename)
305
+ }, [(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(filename), 1), filename === __props.entry ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("small", _hoisted_4$2, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.files.entry")), 1)) : (0, vue.createCommentVNode)("v-if", true)], 10, _hoisted_3$2);
306
+ }), 128))]),
307
+ _: 1
308
+ }), (0, vue.createElementVNode)("div", _hoisted_5$2, [__props.views.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$2, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.views, (item) => {
309
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
310
+ key: item,
311
+ type: "button",
312
+ class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
313
+ title: getViewText(item),
314
+ "aria-label": getViewText(item),
315
+ "aria-pressed": item === __props.activeView,
316
+ onClick: ($event) => handleView(item)
317
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: item }, null, 8, ["name"])], 10, _hoisted_7$2);
318
+ }), 128))])) : (0, vue.createCommentVNode)("v-if", true)])]), (0, vue.createVNode)((0, vue.unref)(code_preview_default), {
319
+ class: "docs-playground-files__body",
320
+ code: activeCode.value,
321
+ filename: __props.activeFilename,
322
+ "copy-label": (0, vue.unref)(t)("playground.common.copyCurrentFile")
323
+ }, null, 8, [
324
+ "code",
325
+ "filename",
326
+ "copy-label"
327
+ ])]);
328
+ };
329
+ }
330
+ });
331
+ //#endregion
332
+ //#region packages/playground/src/constants.ts
333
+ var DEFAULT_ENTRY = "App.vue";
334
+ /** 预览样式和默认 import map 共用的 npm CDN 前缀。 */
335
+ var DEFAULT_CDN_URL = "https://cdn.jsdelivr.net/npm";
336
+ var NEW_SFC_CODE = `<script setup lang="ts"><\/script>\n\n<template>\n <div>\n <slot />\n <\/div>\n<\/template>\n`;
337
+ //#endregion
338
+ //#region packages/playground/src/editor/drag.ts
339
+ var events = typeof document !== "undefined" && "ontouchstart" in document ? {
340
+ start: "touchstart",
341
+ move: "touchmove",
342
+ end: "touchend"
343
+ } : {
344
+ start: "mousedown",
345
+ move: "mousemove",
346
+ end: "mouseup"
347
+ };
348
+ var DEFAULT_OFFSET = 10;
349
+ var END_OFFSET = 0;
350
+ var readPoint = (e) => {
351
+ const touch = e.touches?.[0];
352
+ if (touch) return touch;
353
+ return {
354
+ clientX: Number(e.clientX),
355
+ clientY: Number(e.clientY)
356
+ };
357
+ };
358
+ /**
359
+ * 读取样式上已有的偏移量。
360
+ * @param value 样式声明的 right / bottom 值。
361
+ * @param fallback 样式缺省时使用的偏移量。
362
+ * @returns 像素偏移量;auto 表示该方向由样式表接管,返回 null 以保留当前偏移。
363
+ */
364
+ var readOffset = (value, fallback) => {
365
+ if (value === "auto") return null;
366
+ const parsed = parseInt(value, 10);
367
+ return Number.isNaN(parsed) ? fallback : parsed;
368
+ };
369
+ /**
370
+ * 目前只针对right / bottom 做处理
371
+ */
372
+ var Drag = class {
373
+ el;
374
+ wrapper;
375
+ container;
376
+ right = 0;
377
+ bottom = 0;
378
+ maxRight;
379
+ maxBottom;
380
+ currentX = 0;
381
+ currentY = 0;
382
+ flag = false;
383
+ constructor(options = {}) {
384
+ const { el, wrapper, container } = options;
385
+ if (!el) throw new Error("必传");
386
+ this.el = el;
387
+ this.wrapper = wrapper || el;
388
+ this.container = container || window;
389
+ const width = "innerWidth" in this.container ? this.container.innerWidth : this.container.clientWidth;
390
+ const height = "innerHeight" in this.container ? this.container.innerHeight : this.container.clientHeight;
391
+ this.maxRight = width - this.wrapper.offsetWidth;
392
+ this.maxBottom = height - this.wrapper.offsetHeight;
393
+ this.readOffsets(DEFAULT_OFFSET);
394
+ this.handleStart = this.handleStart.bind(this);
395
+ this.handleMove = this.handleMove.bind(this);
396
+ this.handleEnd = this.handleEnd.bind(this);
397
+ el.addEventListener(events.start, this.handleStart, false);
398
+ }
399
+ off() {
400
+ const { el } = this;
401
+ el.removeEventListener(events.start, this.handleStart, false);
402
+ }
403
+ readOffsets(fallback) {
404
+ const right = readOffset(this.wrapper.style.right, fallback);
405
+ const bottom = readOffset(this.wrapper.style.bottom, fallback);
406
+ if (right !== null) this.right = right;
407
+ if (bottom !== null) this.bottom = bottom;
408
+ }
409
+ operateDOMEvents(type) {
410
+ if (type === "remove") {
411
+ document.removeEventListener(events.move, this.handleMove, false);
412
+ document.removeEventListener(events.end, this.handleEnd, false);
413
+ return;
414
+ }
415
+ document.addEventListener(events.move, this.handleMove, false);
416
+ document.addEventListener(events.end, this.handleEnd, false);
417
+ }
418
+ handleStart(e) {
419
+ this.flag = true;
420
+ e.preventDefault();
421
+ const point = readPoint(e);
422
+ this.currentX = point.clientX;
423
+ this.currentY = point.clientY;
424
+ this.operateDOMEvents("add");
425
+ }
426
+ handleMove(e) {
427
+ e.preventDefault();
428
+ const point = readPoint(e);
429
+ if (!this.flag) return;
430
+ const disX = point.clientX - this.currentX;
431
+ const disY = point.clientY - this.currentY;
432
+ let nowRight = this.right - disX;
433
+ let nowBottom = this.bottom - disY;
434
+ nowRight < 0 && (nowRight = 0);
435
+ nowBottom < 0 && (nowBottom = 0);
436
+ nowRight > this.maxRight && (nowRight = this.maxRight);
437
+ nowBottom > this.maxBottom && (nowBottom = this.maxBottom);
438
+ this.wrapper.style.right = nowRight + "px";
439
+ this.wrapper.style.bottom = nowBottom + "px";
440
+ }
441
+ handleEnd() {
442
+ this.flag = false;
443
+ this.readOffsets(END_OFFSET);
444
+ this.operateDOMEvents("remove");
445
+ }
446
+ };
447
+ //#endregion
448
+ //#region packages/playground/src/editor/editor.vue?vue&type=script&setup=true&lang.ts
449
+ var _hoisted_1$2 = { class: "docs-playground-editor" };
450
+ var _hoisted_2$1 = ["title"];
451
+ var _hoisted_3$1 = { class: "docs-playground-editor__files" };
452
+ var _hoisted_4$1 = [
453
+ "data-filename",
454
+ "onClick",
455
+ "onDblclick"
456
+ ];
457
+ var _hoisted_5$1 = ["onKeydown"];
458
+ var _hoisted_6$1 = ["title"];
459
+ var _hoisted_7$1 = ["title", "aria-label"];
460
+ var _hoisted_8$1 = ["title", "aria-label"];
461
+ var _hoisted_9$1 = { class: "docs-playground-editor__actions" };
462
+ var _hoisted_10$1 = ["title"];
463
+ var _hoisted_11 = ["title", "disabled"];
464
+ var _hoisted_12 = {
465
+ key: 0,
466
+ class: "docs-playground-editor__error"
467
+ };
468
+ //#endregion
469
+ //#region packages/playground/src/editor/editor.vue
470
+ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
471
+ __name: "editor",
472
+ props: {
473
+ value: { default: "" },
474
+ files: { default: () => ({}) },
475
+ entry: { default: "" },
476
+ onChange: {
477
+ type: Function,
478
+ default: () => {}
479
+ },
480
+ onFilesChange: {
481
+ type: Function,
482
+ default: () => {}
483
+ },
484
+ onActiveChange: {
485
+ type: Function,
486
+ default: () => {}
487
+ },
488
+ getCodeErrors: {
489
+ type: Function,
490
+ default: () => []
491
+ },
492
+ locale: {}
493
+ },
494
+ setup(__props) {
495
+ const SUPPORTED_FILE_RE = /\.(vue|js|ts|jsx|tsx|css|s[ac]ss|json|html)$/i;
496
+ const props = __props;
497
+ const { t } = (0, _deot_docs_locale.useLocale)((0, vue.computed)(() => props.locale));
498
+ const initialFiles = Object.keys(props.files).length ? props.files : { "App.vue": props.value };
499
+ const editorFiles = (0, vue.ref)({ ...initialFiles });
500
+ const filenames = (0, vue.computed)(() => Object.keys(editorFiles.value));
501
+ const currentEntry = (0, vue.ref)(props.entry && editorFiles.value[props.entry] !== void 0 ? props.entry : filenames.value[0]);
502
+ const activeFilename = (0, vue.ref)(currentEntry.value);
503
+ const isActive = (0, vue.ref)(false);
504
+ const textarea = (0, vue.ref)(null);
505
+ const filenameInput = (0, vue.ref)(null);
506
+ const scroller = (0, vue.ref)(null);
507
+ const editor = (0, vue.ref)(null);
508
+ const bar = (0, vue.ref)(null);
509
+ const wrapper = (0, vue.ref)(null);
510
+ const drag = (0, vue.ref)(null);
511
+ const renamingFilename = (0, vue.ref)("");
512
+ const filenameDraft = (0, vue.ref)("");
513
+ const codeErrorText = (0, vue.computed)(() => props.getCodeErrors().map((item) => item instanceof Error ? item.message : item).join("\n"));
514
+ const setFilenameInput = (el) => {
515
+ filenameInput.value = el instanceof HTMLInputElement ? el : null;
516
+ };
517
+ const handleHide = () => {
518
+ isActive.value = false;
519
+ };
520
+ const languageExtension = (filename) => {
521
+ if (/\.vue$/i.test(filename)) return (0, _codemirror_lang_vue.vue)();
522
+ if (/\.[jt]sx?$/i.test(filename)) return (0, _codemirror_lang_javascript.javascript)({
523
+ jsx: /x$/i.test(filename),
524
+ typescript: /\.tsx?$/i.test(filename)
525
+ });
526
+ if (/\.(css|s[ac]ss)$/i.test(filename)) return (0, _codemirror_lang_css.css)();
527
+ };
528
+ const createEditor = () => {
529
+ if (!textarea.value || !activeFilename.value) return;
530
+ editor.value?.destroy();
531
+ const filename = activeFilename.value;
532
+ const language = languageExtension(filename);
533
+ editor.value = new codemirror.EditorView({
534
+ doc: editorFiles.value[filename] || "",
535
+ extensions: [
536
+ codemirror.basicSetup,
537
+ ...language ? [language] : [],
538
+ (0, _codemirror_view.highlightActiveLine)(),
539
+ codemirror.EditorView.updateListener.of((e) => {
540
+ if (!e.docChanged) return;
541
+ const code = e.state.doc.toString();
542
+ editorFiles.value = {
543
+ ...editorFiles.value,
544
+ [filename]: code
545
+ };
546
+ props.onFilesChange({ ...editorFiles.value }, currentEntry.value, {
547
+ type: "update",
548
+ filename
549
+ });
550
+ if (filename === currentEntry.value) props.onChange(code);
551
+ })
552
+ ],
553
+ parent: textarea.value
554
+ });
555
+ editor.value.focus();
556
+ };
557
+ const handleActive = (filename) => {
558
+ if (filename === activeFilename.value || editorFiles.value[filename] === void 0) return;
559
+ activeFilename.value = filename;
560
+ props.onActiveChange(filename);
561
+ createEditor();
562
+ };
563
+ const handleTabClick = (e, filename) => {
564
+ if (e.target.closest(".docs-playground-editor__close")) return;
565
+ handleActive(filename);
566
+ };
567
+ const nextFilename = () => {
568
+ let index = 1;
569
+ let filename = "comp1.vue";
570
+ while (editorFiles.value[filename] !== void 0) {
571
+ index++;
572
+ filename = `comp${index}.vue`;
573
+ }
574
+ return filename;
575
+ };
576
+ const focusFilename = () => (0, vue.nextTick)(() => {
577
+ filenameInput.value?.focus();
578
+ filenameInput.value?.select();
579
+ });
580
+ const handleCreate = () => {
581
+ const filename = nextFilename();
582
+ editorFiles.value = {
583
+ ...editorFiles.value,
584
+ [filename]: NEW_SFC_CODE
585
+ };
586
+ activeFilename.value = filename;
587
+ notifyFilesChange({
588
+ type: "create",
589
+ filename
590
+ });
591
+ props.onActiveChange(filename);
592
+ createEditor();
593
+ (0, vue.nextTick)(async () => {
594
+ await scroller.value?.refresh();
595
+ scroller.value?.setScrollLeft(Number.MAX_SAFE_INTEGER);
596
+ });
597
+ };
598
+ const handleRename = (filename) => {
599
+ handleActive(filename);
600
+ renamingFilename.value = filename;
601
+ filenameDraft.value = filename;
602
+ focusFilename();
603
+ };
604
+ const handleRenameCancel = () => {
605
+ renamingFilename.value = "";
606
+ filenameDraft.value = "";
607
+ };
608
+ const validateFilename = (filename, previousFilename = "") => {
609
+ if (!filename) return t("playground.validation.filenameRequired");
610
+ if (filename.startsWith("/") || filename.includes("\\")) return t("playground.validation.filenameRelative");
611
+ if (filename.split("/").some((part) => !part || part === "." || part === "..")) return t("playground.validation.filenameSegments");
612
+ if (!SUPPORTED_FILE_RE.test(filename)) return t("playground.validation.fileTypeUnsupported");
613
+ if (filename !== previousFilename && editorFiles.value[filename] !== void 0) return t("playground.validation.filenameExists");
614
+ return "";
615
+ };
616
+ const notifyFilesChange = (action) => {
617
+ props.onFilesChange({ ...editorFiles.value }, currentEntry.value, action);
618
+ };
619
+ const handleRenameSubmit = () => {
620
+ const filename = filenameDraft.value.trim();
621
+ const previousFilename = renamingFilename.value;
622
+ if (!previousFilename) return;
623
+ const operationError = validateFilename(filename, previousFilename);
624
+ if (operationError) {
625
+ _deot_vc.Message.error(operationError);
626
+ renamingFilename.value = "";
627
+ filenameDraft.value = "";
628
+ return;
629
+ }
630
+ if (filename !== previousFilename) {
631
+ const renamedFiles = {};
632
+ for (const [name, code] of Object.entries(editorFiles.value)) renamedFiles[name === previousFilename ? filename : name] = code;
633
+ editorFiles.value = renamedFiles;
634
+ activeFilename.value = filename;
635
+ if (currentEntry.value === previousFilename) currentEntry.value = filename;
636
+ notifyFilesChange({
637
+ type: "rename",
638
+ filename,
639
+ previousFilename
640
+ });
641
+ }
642
+ renamingFilename.value = "";
643
+ filenameDraft.value = "";
644
+ createEditor();
645
+ };
646
+ const handleDelete = (filename) => {
647
+ if (filename === currentEntry.value) return;
648
+ const nextFiles = { ...editorFiles.value };
649
+ delete nextFiles[filename];
650
+ editorFiles.value = nextFiles;
651
+ notifyFilesChange({
652
+ type: "delete",
653
+ filename
654
+ });
655
+ if (activeFilename.value === filename) {
656
+ activeFilename.value = currentEntry.value;
657
+ props.onActiveChange(currentEntry.value);
658
+ createEditor();
659
+ }
660
+ };
661
+ const handleEntry = () => {
662
+ if (activeFilename.value === currentEntry.value) return;
663
+ currentEntry.value = activeFilename.value;
664
+ notifyFilesChange({
665
+ type: "entry",
666
+ filename: currentEntry.value
667
+ });
668
+ };
669
+ (0, vue.onMounted)(async () => {
670
+ isActive.value = true;
671
+ await (0, vue.nextTick)();
672
+ if (!textarea.value || !bar.value || !wrapper.value) return;
673
+ createEditor();
674
+ drag.value = new Drag({
675
+ el: bar.value,
676
+ wrapper: wrapper.value,
677
+ container: window
678
+ });
679
+ });
680
+ (0, vue.onBeforeUnmount)(() => {
681
+ drag.value?.off();
682
+ editor.value?.destroy();
683
+ });
684
+ return (_ctx, _cache) => {
685
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$2, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.TransitionFade), { onAfterLeave: handleHide }, {
686
+ default: (0, vue.withCtx)(() => [(0, vue.withDirectives)((0, vue.createElementVNode)("div", {
687
+ ref_key: "wrapper",
688
+ ref: wrapper,
689
+ class: "docs-playground-editor__wrapper"
690
+ }, [
691
+ (0, vue.createElementVNode)("div", {
692
+ ref_key: "bar",
693
+ ref: bar,
694
+ class: "docs-playground-editor__header"
695
+ }, [_cache[3] || (_cache[3] = (0, vue.createElementVNode)("span", null, "</>", -1)), (0, vue.createElementVNode)("span", {
696
+ title: (0, vue.unref)(t)("playground.common.close"),
697
+ style: { "cursor": "pointer" },
698
+ onClick: handleHide
699
+ }, "✕", 8, _hoisted_2$1)], 512),
700
+ (0, vue.createElementVNode)("div", _hoisted_3$1, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
701
+ ref_key: "scroller",
702
+ ref: scroller,
703
+ class: "docs-playground-editor__scroller",
704
+ "auto-resize": true,
705
+ native: false,
706
+ "show-bar": true,
707
+ height: "40",
708
+ "content-class": "docs-playground-editor__tabs",
709
+ "wrapper-style": "overflow-y: hidden;"
710
+ }, {
711
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(filenames.value, (filename) => {
712
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
713
+ key: filename,
714
+ class: (0, vue.normalizeClass)(["docs-playground-editor__tab", { "is-active": filename === activeFilename.value }]),
715
+ "data-filename": filename,
716
+ onClick: ($event) => handleTabClick($event, filename),
717
+ onDblclick: ($event) => handleRename(filename)
718
+ }, [renamingFilename.value === filename ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
719
+ key: 0,
720
+ ref_for: true,
721
+ ref: setFilenameInput,
722
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => filenameDraft.value = $event),
723
+ class: "docs-playground-editor__filename",
724
+ onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(() => {}, ["stop"])),
725
+ onDblclick: _cache[2] || (_cache[2] = (0, vue.withModifiers)(() => {}, ["stop"])),
726
+ onBlur: handleRenameSubmit,
727
+ onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(handleRenameSubmit, ["prevent"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(handleRenameCancel, ["prevent"]), ["esc"])]
728
+ }, null, 40, _hoisted_5$1)), [[vue.vModelText, filenameDraft.value]]) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
729
+ filename === currentEntry.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
730
+ key: 0,
731
+ title: (0, vue.unref)(t)("playground.editor.entry")
732
+ }, "●", 8, _hoisted_6$1)) : (0, vue.createCommentVNode)("v-if", true),
733
+ (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(filename), 1),
734
+ filename === currentEntry.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
735
+ key: 1,
736
+ type: "button",
737
+ class: "docs-playground-editor__close",
738
+ title: (0, vue.unref)(t)("playground.editor.entryCannotDelete"),
739
+ "aria-label": (0, vue.unref)(t)("playground.editor.entryCannotDelete"),
740
+ disabled: ""
741
+ }, null, 8, _hoisted_7$1)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Popconfirm), {
742
+ key: 2,
743
+ class: "docs-playground-editor__delete",
744
+ title: (0, vue.unref)(t)("playground.editor.confirmDelete", { filename }),
745
+ portal: true,
746
+ placement: "bottom",
747
+ onOk: ($event) => handleDelete(filename)
748
+ }, {
749
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
750
+ type: "button",
751
+ class: "docs-playground-editor__close",
752
+ title: (0, vue.unref)(t)("playground.editor.deleteFile"),
753
+ "aria-label": (0, vue.unref)(t)("playground.editor.confirmDelete", { filename })
754
+ }, null, 8, _hoisted_8$1)]),
755
+ _: 2
756
+ }, 1032, ["title", "onOk"]))
757
+ ], 64))], 42, _hoisted_4$1);
758
+ }), 128))]),
759
+ _: 1
760
+ }, 512), (0, vue.createElementVNode)("div", _hoisted_9$1, [(0, vue.createElementVNode)("button", {
761
+ type: "button",
762
+ title: (0, vue.unref)(t)("playground.editor.createFile"),
763
+ onClick: handleCreate
764
+ }, "+", 8, _hoisted_10$1), (0, vue.createElementVNode)("button", {
765
+ type: "button",
766
+ title: (0, vue.unref)(t)("playground.editor.setEntry"),
767
+ disabled: activeFilename.value === currentEntry.value,
768
+ onClick: handleEntry
769
+ }, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.editor.setEntry")), 9, _hoisted_11)])]),
770
+ codeErrorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12, (0, vue.toDisplayString)(codeErrorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
771
+ (0, vue.createElementVNode)("div", {
772
+ ref_key: "textarea",
773
+ ref: textarea,
774
+ class: "docs-playground-editor__editor"
775
+ }, null, 512)
776
+ ], 512), [[vue.vShow, isActive.value]])]),
777
+ _: 1
778
+ })]);
779
+ };
780
+ }
781
+ });
782
+ //#endregion
783
+ //#region packages/playground/src/editor/index.ts
784
+ var Editor = new _deot_vc.Portal(editor_default, { aliveRegExp: { className: /(PORTAL_TAG_DISABLE)/ } });
785
+ var resolveSandboxContainer = (sandbox) => sandbox?.container ? (0, vue.unref)(sandbox.container) : null;
786
+ var useSandboxAutoHeight = (sandboxRef) => {
787
+ const height = (0, vue.ref)(24);
788
+ let container = null;
789
+ let iframe = null;
790
+ let containerObserver = null;
791
+ let contentObserver = null;
792
+ let frameId = 0;
793
+ const cancelMeasure = () => {
794
+ if (!frameId) return;
795
+ cancelAnimationFrame(frameId);
796
+ frameId = 0;
797
+ };
798
+ const measure = () => {
799
+ frameId = 0;
800
+ if (!iframe) return;
801
+ try {
802
+ const iframeWindow = iframe.contentWindow;
803
+ const iframeDocument = iframe.contentDocument;
804
+ if (!iframeWindow || !iframeDocument?.body) return;
805
+ const { body, documentElement } = iframeDocument;
806
+ const bodyStyle = iframeWindow.getComputedStyle(body);
807
+ const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
808
+ const rootTop = documentElement.getBoundingClientRect().top;
809
+ const bodyRect = body.getBoundingClientRect();
810
+ const collapsedTop = Math.max(0, bodyRect.top - rootTop);
811
+ let childrenBottom = 0;
812
+ for (const child of body.children) {
813
+ const rect = child.getBoundingClientRect();
814
+ const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
815
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
816
+ }
817
+ const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
818
+ const overflowingHeight = scrollHeight > iframe.clientHeight + 1 ? scrollHeight : 0;
819
+ const contentHeight = Math.ceil(Math.max(body.offsetHeight + bodyMargins + collapsedTop, bodyRect.height + bodyMargins + collapsedTop, childrenBottom, overflowingHeight));
820
+ const nextHeight = Math.max(contentHeight, 24);
821
+ if (height.value !== nextHeight) height.value = nextHeight;
822
+ } catch {
823
+ if (height.value !== 24) height.value = 24;
824
+ }
825
+ };
826
+ const scheduleMeasure = () => {
827
+ if (frameId) return;
828
+ frameId = requestAnimationFrame(measure);
829
+ };
830
+ const disconnectContent = () => {
831
+ contentObserver?.disconnect();
832
+ contentObserver = null;
833
+ cancelMeasure();
834
+ };
835
+ const observeContent = () => {
836
+ disconnectContent();
837
+ if (!iframe) return;
838
+ try {
839
+ const iframeWindow = iframe.contentWindow;
840
+ const iframeDocument = iframe.contentDocument;
841
+ const Observer = iframeWindow?.ResizeObserver;
842
+ if (!iframeWindow || !iframeDocument?.body || !Observer) {
843
+ scheduleMeasure();
844
+ return;
845
+ }
846
+ const observer = new Observer(scheduleMeasure);
847
+ observer.observe(iframeDocument.documentElement);
848
+ observer.observe(iframeDocument.body);
849
+ contentObserver = observer;
850
+ scheduleMeasure();
851
+ } catch {
852
+ if (height.value !== 24) height.value = 24;
853
+ }
854
+ };
855
+ const handleIframeLoad = () => observeContent();
856
+ const setIframe = (nextIframe) => {
857
+ if (iframe === nextIframe) return;
858
+ iframe?.removeEventListener("load", handleIframeLoad);
859
+ disconnectContent();
860
+ iframe = nextIframe;
861
+ if (!iframe) {
862
+ height.value = 24;
863
+ return;
864
+ }
865
+ iframe.addEventListener("load", handleIframeLoad);
866
+ iframe.style.height = "100%";
867
+ observeContent();
868
+ };
869
+ const syncIframe = () => setIframe(container?.querySelector("iframe") || null);
870
+ const setContainer = (nextContainer) => {
871
+ if (container === nextContainer) return;
872
+ containerObserver?.disconnect();
873
+ containerObserver = null;
874
+ container = nextContainer;
875
+ setIframe(null);
876
+ if (!container) return;
877
+ containerObserver = new MutationObserver(syncIframe);
878
+ containerObserver.observe(container, {
879
+ childList: true,
880
+ subtree: true
881
+ });
882
+ syncIframe();
883
+ };
884
+ (0, vue.watch)(() => resolveSandboxContainer(sandboxRef.value), setContainer, {
885
+ flush: "post",
886
+ immediate: true
887
+ });
888
+ (0, vue.onBeforeUnmount)(() => {
889
+ setContainer(null);
890
+ cancelMeasure();
891
+ });
892
+ return height;
893
+ };
894
+ //#endregion
895
+ //#region packages/playground/src/core/runtime/error-guard.ts
896
+ var UNKNOWN_RUNTIME_ERROR = "运行时发生未知错误";
897
+ var MODULE_SPECIFIER_RE = /Failed to resolve module specifier|Error resolving module specifier/u;
898
+ var forwardSandboxRuntimeError = (event) => {
899
+ if (!(event instanceof ErrorEvent) || event.error != null || !event.message) return;
900
+ event.stopImmediatePropagation();
901
+ if (event.message === "ResizeObserver loop limit exceeded" || event.message === "ResizeObserver loop completed with undelivered notifications.") return;
902
+ window.parent.postMessage({
903
+ action: "error",
904
+ value: event.message
905
+ }, "*");
906
+ };
907
+ var SANDBOX_RUNTIME_ERROR_CAPTURE_HTML = [
908
+ "<script>",
909
+ `window.addEventListener('error', ${forwardSandboxRuntimeError.toString()}, true);`,
910
+ "<\/script>"
911
+ ].join("\n");
912
+ var toRuntimeErrorMessage = (value, message) => {
913
+ if (typeof value === "string" && value) return value;
914
+ if (value instanceof Error && value.message) return value.message;
915
+ if (value && typeof value === "object" && "message" in value) {
916
+ const valueMessage = value.message;
917
+ if (typeof valueMessage === "string" && valueMessage) return valueMessage;
918
+ }
919
+ if (typeof message === "string" && message) return message;
920
+ if (value !== null && value !== void 0) try {
921
+ return String(value);
922
+ } catch {
923
+ return UNKNOWN_RUNTIME_ERROR;
924
+ }
925
+ return UNKNOWN_RUNTIME_ERROR;
926
+ };
927
+ var normalizeRuntimeErrorMessage = (data) => {
928
+ if (!data || typeof data !== "object") return false;
929
+ const message = data;
930
+ if (message.action !== "error") return false;
931
+ message.value = toRuntimeErrorMessage(message.value, message.message);
932
+ return true;
933
+ };
934
+ var toErrorText = (value) => {
935
+ if (typeof value === "string" && value) return value;
936
+ if (value instanceof Error && value.message) return value.message;
937
+ return "";
938
+ };
939
+ var formatSandboxRuntimeError = (message, importMapTip) => {
940
+ if (!MODULE_SPECIFIER_RE.test(message)) return message;
941
+ return `${message.replace(/\. Relative references must.*$/u, "").replace(/\.$/u, "")}.\n${importMapTip}`;
942
+ };
943
+ var useSandboxRuntimeErrorGuard = (sandboxRef) => {
944
+ const error = (0, vue.ref)("");
945
+ if (typeof window === "undefined") return error;
946
+ const handleMessage = (event) => {
947
+ const iframe = resolveSandboxContainer(sandboxRef.value)?.querySelector("iframe");
948
+ if (!iframe || event.source !== iframe.contentWindow) return;
949
+ if (!normalizeRuntimeErrorMessage(event.data)) return;
950
+ error.value = event.data.value;
951
+ };
952
+ window.addEventListener("message", handleMessage, true);
953
+ (0, vue.onBeforeUnmount)(() => window.removeEventListener("message", handleMessage, true));
954
+ return error;
955
+ };
956
+ //#endregion
957
+ //#region packages/playground/src/core/runtime/theme.ts
958
+ var resolveElementTheme = (element) => {
959
+ const vcTheme = element?.getAttribute("data-vc-theme");
960
+ if (vcTheme === "light" || vcTheme === "dark") return vcTheme;
961
+ const docsTheme = element?.getAttribute("data-doc-theme");
962
+ return docsTheme === "light" || docsTheme === "dark" ? docsTheme : null;
963
+ };
964
+ var resolveHostTheme = (container) => {
965
+ let current = container;
966
+ while (current) {
967
+ const theme = resolveElementTheme(current);
968
+ if (theme) return theme;
969
+ current = current.parentElement;
970
+ }
971
+ return typeof document === "undefined" ? null : resolveElementTheme(document.body) || resolveElementTheme(document.documentElement);
972
+ };
973
+ var syncSandboxTheme = (iframe, theme) => {
974
+ if (!iframe) return;
975
+ try {
976
+ const iframeDocument = iframe.contentDocument;
977
+ if (!iframeDocument?.body) return;
978
+ if (!theme) {
979
+ iframeDocument.body.removeAttribute("data-doc-theme");
980
+ iframeDocument.body.removeAttribute("data-vc-theme");
981
+ iframeDocument.documentElement.removeAttribute("data-doc-theme");
982
+ iframeDocument.documentElement.removeAttribute("data-vc-theme");
983
+ iframeDocument.documentElement.style.removeProperty("color-scheme");
984
+ return;
985
+ }
986
+ iframeDocument.body.setAttribute("data-doc-theme", theme);
987
+ iframeDocument.body.setAttribute("data-vc-theme", theme);
988
+ iframeDocument.documentElement.setAttribute("data-doc-theme", theme);
989
+ iframeDocument.documentElement.setAttribute("data-vc-theme", theme);
990
+ iframeDocument.documentElement.style.colorScheme = theme;
991
+ } catch {}
992
+ };
993
+ var useSandboxTheme = (sandboxRef) => {
994
+ let container = null;
995
+ let iframe = null;
996
+ let containerObserver = null;
997
+ let themeObserver = null;
998
+ const syncTheme = () => syncSandboxTheme(iframe, resolveHostTheme(container));
999
+ const handleIframeLoad = () => syncTheme();
1000
+ const setIframe = (nextIframe) => {
1001
+ if (iframe === nextIframe) {
1002
+ syncTheme();
1003
+ return;
1004
+ }
1005
+ iframe?.removeEventListener("load", handleIframeLoad);
1006
+ iframe = nextIframe;
1007
+ iframe?.addEventListener("load", handleIframeLoad);
1008
+ syncTheme();
1009
+ };
1010
+ const syncIframe = () => setIframe(container?.querySelector("iframe") || null);
1011
+ const setContainer = (nextContainer) => {
1012
+ if (container === nextContainer) {
1013
+ syncIframe();
1014
+ return;
1015
+ }
1016
+ containerObserver?.disconnect();
1017
+ containerObserver = null;
1018
+ container = nextContainer;
1019
+ setIframe(null);
1020
+ if (!container) return;
1021
+ if (typeof MutationObserver !== "undefined") {
1022
+ containerObserver = new MutationObserver(syncIframe);
1023
+ containerObserver.observe(container, {
1024
+ childList: true,
1025
+ subtree: true
1026
+ });
1027
+ }
1028
+ syncIframe();
1029
+ };
1030
+ (0, vue.watch)(() => resolveSandboxContainer(sandboxRef.value), setContainer, {
1031
+ flush: "post",
1032
+ immediate: true
1033
+ });
1034
+ if (typeof document !== "undefined" && typeof MutationObserver !== "undefined") {
1035
+ themeObserver = new MutationObserver(syncTheme);
1036
+ themeObserver.observe(document.documentElement, {
1037
+ attributes: true,
1038
+ attributeFilter: ["data-doc-theme", "data-vc-theme"],
1039
+ subtree: true
1040
+ });
1041
+ }
1042
+ (0, vue.onBeforeUnmount)(() => {
1043
+ themeObserver?.disconnect();
1044
+ themeObserver = null;
1045
+ setContainer(null);
1046
+ });
1047
+ };
1048
+ //#endregion
1049
+ //#region packages/playground/src/core/runtime/viewport.ts
1050
+ var DEFAULT_VIEWPORT_OPTIONS = ["auto", 375];
1051
+ var isValidViewport = (viewport) => {
1052
+ if (viewport === "auto") return true;
1053
+ if (typeof viewport === "number") return Number.isFinite(viewport) && viewport > 0;
1054
+ return Array.isArray(viewport) && viewport.length === 2 && viewport.every((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
1055
+ };
1056
+ var getViewportKey = (viewport) => Array.isArray(viewport) ? `${viewport[0]}x${viewport[1]}` : String(viewport);
1057
+ var viewportEquals = (left, right) => getViewportKey(left) === getViewportKey(right);
1058
+ var cloneViewport = (viewport) => Array.isArray(viewport) ? [viewport[0], viewport[1]] : viewport;
1059
+ var normalizeViewportOptions = (options) => {
1060
+ const source = options === void 0 ? DEFAULT_VIEWPORT_OPTIONS : options;
1061
+ const normalized = [];
1062
+ for (const viewport of source) {
1063
+ if (!isValidViewport(viewport)) continue;
1064
+ if (normalized.some((item) => viewportEquals(item, viewport))) continue;
1065
+ normalized.push(cloneViewport(viewport));
1066
+ }
1067
+ return normalized;
1068
+ };
1069
+ var resolveInitialViewport = (viewport, options) => isValidViewport(viewport) ? cloneViewport(viewport) : cloneViewport(options[0] || "auto");
1070
+ var includeActiveViewport = (options, viewport) => {
1071
+ if (!options.length || options.some((item) => viewportEquals(item, viewport))) return options;
1072
+ return [...options, cloneViewport(viewport)];
1073
+ };
1074
+ var getViewportWidth = (viewport) => {
1075
+ if (viewport === "auto") return null;
1076
+ return Array.isArray(viewport) ? viewport[0] : viewport;
1077
+ };
1078
+ var getViewportHeight = (viewport) => Array.isArray(viewport) ? viewport[1] : null;
1079
+ var formatViewportLabel = (viewport, autoLabel = "Auto") => {
1080
+ if (viewport === "auto") return autoLabel;
1081
+ return Array.isArray(viewport) ? `${viewport[0]} × ${viewport[1]}px` : `${viewport}px`;
1082
+ };
1083
+ //#endregion
1084
+ //#region packages/playground/src/cdn.ts
1085
+ var PREVIEW_STYLE_ASSETS = [
1086
+ "@deot/style/dist/index.normalize-only.css",
1087
+ "@deot/vc-components/dist/index.style.css",
1088
+ "@deot/style/dist/index.css"
1089
+ ];
1090
+ var BUILTIN_IMPORT_ASSETS = {
1091
+ "@deot/vc": "@deot/vc/dist/index.js",
1092
+ "@deot/vc-shared": "@deot/vc-shared/dist/index.js",
1093
+ "@deot/vc-hooks": "@deot/vc-hooks/dist/index.js",
1094
+ "@deot/vc-components": "@deot/vc-components/dist/index.js",
1095
+ "@deot/helper-resize": "@deot/helper-resize/dist/index.js",
1096
+ "@deot/helper-utils": "@deot/helper-utils/dist/index.js",
1097
+ "@deot/helper-cache": "@deot/helper-cache/dist/index.js",
1098
+ "@deot/helper-fp": "@deot/helper-fp/dist/index.js",
1099
+ "@deot/helper-dom": "@deot/helper-dom/dist/index.js",
1100
+ "@deot/helper-wheel": "@deot/helper-wheel/dist/index.js",
1101
+ "@deot/helper-validator": "@deot/helper-validator/dist/index.js",
1102
+ "@deot/helper-load": "@deot/helper-load/dist/index.js",
1103
+ "@deot/helper-scheduler": "@deot/helper-scheduler/dist/index.js",
1104
+ "@deot/helper-emitter": "@deot/helper-emitter/dist/index.js",
1105
+ "@deot/helper-is": "@deot/helper-is/dist/index.js",
1106
+ "@deot/helper-device": "@deot/helper-device/dist/index.js",
1107
+ "@deot/helper-route": "@deot/helper-route/dist/index.js",
1108
+ "@deot/helper-unicode": "@deot/helper-unicode/dist/index.js",
1109
+ "@deot/helper": "@deot/helper/dist/index.js",
1110
+ "normalize-wheel": "normalize-wheel-es/dist/index.mjs",
1111
+ "photoswipe": "photoswipe/dist/photoswipe.esm.js",
1112
+ "photoswipe/lightbox": "photoswipe/dist/photoswipe-lightbox.esm.js"
1113
+ };
1114
+ var normalizeCdnURL = (cdnURL = DEFAULT_CDN_URL) => {
1115
+ return cdnURL.trim().replace(/\/+$/u, "") || "https://cdn.jsdelivr.net/npm";
1116
+ };
1117
+ var cdnJoin = (origin, asset) => `${origin}/${asset.replace(/^\/+/u, "")}`;
1118
+ var jsdelivrEsmURL = (asset) => cdnJoin(DEFAULT_CDN_URL, asset);
1119
+ var SASS_CDN_URL = jsdelivrEsmURL("sass/+esm");
1120
+ var cdnAsset = (cdnURL, asset) => cdnJoin(normalizeCdnURL(cdnURL), asset);
1121
+ var createBuiltinImports = (cdnURL = DEFAULT_CDN_URL) => ({
1122
+ ...Object.fromEntries(Object.entries(BUILTIN_IMPORT_ASSETS).map(([specifier, asset]) => [specifier, cdnAsset(cdnURL, asset)])),
1123
+ "lodash-es": jsdelivrEsmURL("lodash-es/+esm"),
1124
+ "vue": "https://play.vuejs.org/vue.runtime.esm-browser.js",
1125
+ "vue/server-renderer": "https://play.vuejs.org/server-renderer.esm-browser.js"
1126
+ });
1127
+ var createBuiltinStyles = (cdnURL = DEFAULT_CDN_URL) => Object.fromEntries(PREVIEW_STYLE_ASSETS.map((asset) => [asset, cdnAsset(cdnURL, asset)]));
1128
+ //#endregion
1129
+ //#region packages/playground/src/import-map.ts
1130
+ /**
1131
+ * 站点级同步内存:`$docs.modules` / `$docs.styles` 与管理页 import、CSS 覆盖。
1132
+ */
1133
+ var normalize = (value) => value.trim();
1134
+ var isSafePlaygroundHref = (href) => {
1135
+ const next = href.trim();
1136
+ if (!next) return false;
1137
+ if (/^https?:\/\//iu.test(next)) return true;
1138
+ return next.startsWith("/") && !next.startsWith("//");
1139
+ };
1140
+ var filterSafeHrefs = (value = {}) => Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string" && isSafePlaygroundHref(entry[1])));
1141
+ var sanitizeMap = (value = {}) => filterSafeHrefs(Object.fromEntries(Object.entries(value).map(([alias, url]) => [normalize(alias), normalize(url)]).filter(([alias]) => Boolean(alias))));
1142
+ var createMapStore = () => {
1143
+ let data = {};
1144
+ const snapshot = () => ({ ...data });
1145
+ const drop = (alias) => {
1146
+ if (data[alias] === void 0) return snapshot();
1147
+ const next = { ...data };
1148
+ delete next[alias];
1149
+ data = next;
1150
+ return snapshot();
1151
+ };
1152
+ return {
1153
+ get: snapshot,
1154
+ set: (value = {}) => {
1155
+ data = sanitizeMap(value);
1156
+ },
1157
+ apply: (alias, url) => {
1158
+ const nextAlias = normalize(alias);
1159
+ const nextUrl = normalize(url);
1160
+ if (!nextAlias) return snapshot();
1161
+ if (!isSafePlaygroundHref(nextUrl)) return drop(nextAlias);
1162
+ data = {
1163
+ ...data,
1164
+ [nextAlias]: nextUrl
1165
+ };
1166
+ return snapshot();
1167
+ },
1168
+ remove: (alias) => {
1169
+ const nextAlias = normalize(alias);
1170
+ return nextAlias ? drop(nextAlias) : snapshot();
1171
+ }
1172
+ };
1173
+ };
1174
+ var siteModules = createMapStore();
1175
+ var siteStyles = createMapStore();
1176
+ var importOverrides = createMapStore();
1177
+ var styleOverrides = createMapStore();
1178
+ /**
1179
+ * 站点默认 import。合并时夹在内置 CDN 与实例 `builtinImportMap` 之间。
1180
+ */
1181
+ var getPlaygroundSiteModules = siteModules.get;
1182
+ var setPlaygroundSiteModules = siteModules.set;
1183
+ /**
1184
+ * 站点默认预览 CSS。合并时夹在内置样式表与管理页覆盖之间。
1185
+ */
1186
+ var getPlaygroundSiteStyles = siteStyles.get;
1187
+ var setPlaygroundSiteStyles = siteStyles.set;
1188
+ var getPlaygroundImportMapOverrides = importOverrides.get;
1189
+ var setPlaygroundImportMapOverrides = importOverrides.set;
1190
+ var applyPlaygroundImportMapOverride = importOverrides.apply;
1191
+ var removePlaygroundImportMapOverride = importOverrides.remove;
1192
+ var getPlaygroundStyleOverrides = styleOverrides.get;
1193
+ var setPlaygroundStyleOverrides = styleOverrides.set;
1194
+ var applyPlaygroundStyleOverride = styleOverrides.apply;
1195
+ var removePlaygroundStyleOverride = styleOverrides.remove;
1196
+ var clearPlaygroundMaps = () => {
1197
+ [
1198
+ siteModules,
1199
+ siteStyles,
1200
+ importOverrides,
1201
+ styleOverrides
1202
+ ].forEach((store) => store.set());
1203
+ };
1204
+ //#endregion
1205
+ //#region packages/playground/src/core/scss.ts
1206
+ var SCHEME = "docs-playground";
1207
+ var STYLE_EXTS = [".scss", ".sass"];
1208
+ var VUE_SCSS_STYLE_RE = /<style\b[^>]*\blang\s*=\s*(['"]?)s[ac]ss\1/i;
1209
+ var LANG_ATTR_RE = /\s*lang\s*=\s*(['"]?)s[ac]ss\1/i;
1210
+ var SCSS_ERROR_MARK = "[playground-sass]";
1211
+ var INJECT_START = "/* docs-playground-scss-start */";
1212
+ var INJECT_END = "/* docs-playground-scss-end */";
1213
+ var EMPTY_JS = "export default {}\n";
1214
+ var boundStores = /* @__PURE__ */ new WeakSet();
1215
+ var wrappedCompilers = /* @__PURE__ */ new WeakSet();
1216
+ var testLoader;
1217
+ var loadedSass;
1218
+ var loadPromise;
1219
+ var fileCode = (value) => typeof value === "string" ? value : value.code;
1220
+ var playgroundPath = (url) => decodeURIComponent(url.pathname.replace(/^\/+/u, ""));
1221
+ var toPlaygroundUrl = (filename) => new URL(`${SCHEME}:///${filename.replace(/^\/+/u, "")}`);
1222
+ var basename = (path) => path.split("/").pop() || path;
1223
+ var scssSyntax = (filename, lang) => lang === "sass" || /\.sass$/i.test(filename) ? "indented" : "scss";
1224
+ var errorText = (item) => item instanceof Error ? item.message : String(item);
1225
+ var isScssFile = (filename) => /\.s[ac]ss$/i.test(filename);
1226
+ var vueSourceNeedsScss = (source) => VUE_SCSS_STYLE_RE.test(source);
1227
+ var resolveSassModule = async (url) => {
1228
+ const mod = await import(
1229
+ /* @vite-ignore */
1230
+ url
1231
+ );
1232
+ const sass = typeof mod.compileString === "function" ? mod : mod.default;
1233
+ if (typeof sass?.compileString !== "function") throw new Error(`Sass module at ${url} does not export compileString`);
1234
+ return sass;
1235
+ };
1236
+ var loadSass = () => {
1237
+ loadPromise ??= (testLoader ?? (() => resolveSassModule(SASS_CDN_URL)))().then((sass) => {
1238
+ loadedSass = sass;
1239
+ return sass;
1240
+ });
1241
+ return loadPromise;
1242
+ };
1243
+ var isScssPartial = (filename) => isScssFile(filename) && basename(filename).startsWith("_");
1244
+ var filesNeedScss = (files) => Object.entries(files).some(([filename, value]) => isScssFile(filename) || /\.vue$/i.test(filename) && vueSourceNeedsScss(fileCode(value)));
1245
+ var whenSassReady = (files, run) => {
1246
+ if (!filesNeedScss(files) || loadedSass) run();
1247
+ else loadSass().then(run);
1248
+ };
1249
+ var toPlaygroundFileMap = (files) => Object.fromEntries(Object.entries(files).map(([filename, value]) => [filename, fileCode(value)]));
1250
+ var formatScssError = (filename, message) => `${SCSS_ERROR_MARK} ${filename}: ${message}`;
1251
+ var scssFail = (filename, error) => formatScssError(filename, errorText(error));
1252
+ var withUnderscore = (path) => {
1253
+ const base = basename(path);
1254
+ if (base.startsWith("_")) return path;
1255
+ return `${path.slice(0, -base.length)}_${base}`;
1256
+ };
1257
+ var matchFileKey = (files, path) => [
1258
+ path,
1259
+ path.startsWith("src/") ? path : `src/${path}`,
1260
+ path.replace(/^src\//u, "")
1261
+ ].find((key) => files[key] !== void 0);
1262
+ var stylesheetCandidates = (requestPath) => {
1263
+ if (isScssFile(requestPath)) return [requestPath, withUnderscore(requestPath)];
1264
+ return STYLE_EXTS.flatMap((ext) => [
1265
+ `${requestPath}${ext}`,
1266
+ withUnderscore(`${requestPath}${ext}`),
1267
+ `${requestPath}/index${ext}`,
1268
+ withUnderscore(`${requestPath}/index${ext}`)
1269
+ ]);
1270
+ };
1271
+ var resolvePlaygroundStylesheet = (files, requestPath) => {
1272
+ const found = [...new Set(stylesheetCandidates(requestPath).map((path) => matchFileKey(files, path)).filter((key) => Boolean(key)))];
1273
+ if (found.length > 1) throw new Error(`Ambiguous Sass import "${requestPath}": ${found.join(", ")}`);
1274
+ return found[0] ?? null;
1275
+ };
1276
+ var createPlaygroundImporter = (getFiles) => ({
1277
+ canonicalize(url, context) {
1278
+ if (/^[a-zA-Z][\w+.-]*:/u.test(url) && !url.startsWith(`${SCHEME}:`)) return null;
1279
+ try {
1280
+ const resolved = resolvePlaygroundStylesheet(getFiles(), playgroundPath(new URL(url, context.containingUrl ?? toPlaygroundUrl(""))));
1281
+ return resolved ? toPlaygroundUrl(resolved) : null;
1282
+ } catch {
1283
+ return null;
1284
+ }
1285
+ },
1286
+ load(canonicalUrl) {
1287
+ const files = getFiles();
1288
+ const key = playgroundPath(canonicalUrl);
1289
+ return files[key] === void 0 ? null : {
1290
+ contents: files[key],
1291
+ syntax: scssSyntax(key)
1292
+ };
1293
+ }
1294
+ });
1295
+ var compileScssSource = (source, filename, sass, files, lang) => {
1296
+ const importer = createPlaygroundImporter(() => files);
1297
+ return sass.compileString(source, {
1298
+ syntax: scssSyntax(filename, lang),
1299
+ url: toPlaygroundUrl(filename),
1300
+ importer,
1301
+ importers: [importer]
1302
+ }).css;
1303
+ };
1304
+ var rewriteVueSfcStyles = (source, filename, parse, sass, files) => {
1305
+ const scssStyles = parse(source, { filename }).descriptor.styles.filter((style) => style.lang === "scss" || style.lang === "sass");
1306
+ if (!scssStyles.length) return {
1307
+ code: source,
1308
+ errors: []
1309
+ };
1310
+ const errors = [];
1311
+ let code = source;
1312
+ for (const style of [...scssStyles].sort((left, right) => right.loc.start.offset - left.loc.start.offset)) {
1313
+ const before = code.slice(0, style.loc.start.offset);
1314
+ const after = code.slice(style.loc.end.offset);
1315
+ const openStart = before.lastIndexOf("<style");
1316
+ if (openStart < 0) {
1317
+ errors.push(formatScssError(filename, "unable to locate <style> opening tag"));
1318
+ continue;
1319
+ }
1320
+ try {
1321
+ const css = compileScssSource(style.content, filename, sass, files, style.lang);
1322
+ code = `${before.slice(0, openStart)}${before.slice(openStart).replace(LANG_ATTR_RE, "")}${css}${after}`;
1323
+ } catch (error) {
1324
+ errors.push(scssFail(filename, error));
1325
+ }
1326
+ }
1327
+ return {
1328
+ code,
1329
+ errors
1330
+ };
1331
+ };
1332
+ var appendParseErrors = (result, errors) => errors.length ? {
1333
+ ...result,
1334
+ errors: [...result.errors, ...errors.map((item) => new Error(item))]
1335
+ } : result;
1336
+ var wrapCompiler = (compiler, getFiles) => {
1337
+ if (wrappedCompilers.has(compiler)) return compiler;
1338
+ const originalParse = compiler.parse.bind(compiler);
1339
+ const wrapped = Object.create(compiler);
1340
+ wrapped.parse = ((source, options) => {
1341
+ const filename = options?.filename || "anonymous.vue";
1342
+ if (!vueSourceNeedsScss(source)) return originalParse(source, options);
1343
+ const rewritten = loadedSass ? rewriteVueSfcStyles(source, filename, originalParse, loadedSass, getFiles()) : {
1344
+ code: source,
1345
+ errors: [formatScssError(filename, "compiler is not loaded")]
1346
+ };
1347
+ return appendParseErrors(originalParse(rewritten.errors.length ? source : rewritten.code, options), rewritten.errors);
1348
+ });
1349
+ wrappedCompilers.add(wrapped);
1350
+ return wrapped;
1351
+ };
1352
+ var withInjectedCss = (css, extra) => {
1353
+ const start = css.indexOf(INJECT_START);
1354
+ const end = start < 0 ? -1 : css.indexOf(INJECT_END, start);
1355
+ const base = (start < 0 ? css : `${css.slice(0, start)}${end >= 0 ? css.slice(end + 30) : ""}`).trimEnd();
1356
+ if (!extra.trim()) return base;
1357
+ const block = `${INJECT_START}\n${extra.trim()}\n${INJECT_END}`;
1358
+ return base ? `${base}\n${block}` : block;
1359
+ };
1360
+ var isStandaloneScssError = (item) => {
1361
+ const text = errorText(item);
1362
+ return text.startsWith(SCSS_ERROR_MARK) && /\.s[ac]ss:/i.test(text);
1363
+ };
1364
+ var syncScssErrors = (store, scssErrors) => {
1365
+ const next = [...scssErrors, ...store.errors.filter((item) => !isStandaloneScssError(item))];
1366
+ if (next.map(String).join("\0") === store.errors.map(String).join("\0")) return;
1367
+ store.errors = next;
1368
+ };
1369
+ var applyStandaloneScss = (store, sass = loadedSass) => {
1370
+ if (!sass) return [];
1371
+ const fileMap = toPlaygroundFileMap(store.files);
1372
+ const errors = [];
1373
+ const injected = [];
1374
+ for (const [filename, file] of Object.entries(store.files)) {
1375
+ if (!isScssFile(filename) || !file.compiled) continue;
1376
+ try {
1377
+ const css = compileScssSource(file.code, filename, sass, fileMap);
1378
+ file.compiled.css = css;
1379
+ file.compiled.js = EMPTY_JS;
1380
+ file.compiled.ssr = EMPTY_JS;
1381
+ if (!isScssPartial(filename) && css.trim()) injected.push(css);
1382
+ } catch (error) {
1383
+ errors.push(scssFail(filename, error));
1384
+ }
1385
+ }
1386
+ const main = store.files[store.mainFile];
1387
+ if (main?.compiled) {
1388
+ const next = withInjectedCss(main.compiled.css || "", injected.join("\n"));
1389
+ if (next !== (main.compiled.css || "")) main.compiled.css = next;
1390
+ }
1391
+ syncScssErrors(store, errors);
1392
+ return errors;
1393
+ };
1394
+ var bindPlaygroundScss = (store) => {
1395
+ if (boundStores.has(store)) return;
1396
+ boundStores.add(store);
1397
+ const getFiles = () => toPlaygroundFileMap(store.files);
1398
+ const installCompiler = () => {
1399
+ if (typeof store.compiler?.parse === "function") store.compiler = wrapCompiler(store.compiler, getFiles);
1400
+ };
1401
+ installCompiler();
1402
+ (0, vue.watch)(() => store.compiler, installCompiler);
1403
+ (0, vue.watch)(() => [
1404
+ store.mainFile,
1405
+ store.files[store.mainFile]?.compiled?.css,
1406
+ store.errors.map(String).join("\0"),
1407
+ Object.entries(store.files).filter(([filename]) => isScssFile(filename)).map(([filename, file]) => `${filename}:${file.code}`).join("\n")
1408
+ ].join("\0"), () => {
1409
+ if (loadedSass) applyStandaloneScss(store);
1410
+ });
1411
+ const originalSetFiles = store.setFiles.bind(store);
1412
+ store.setFiles = async (newFiles, mainFile) => {
1413
+ if (filesNeedScss(newFiles) && !loadedSass) await loadSass();
1414
+ return originalSetFiles(newFiles, mainFile);
1415
+ };
1416
+ };
1417
+ //#endregion
1418
+ //#region packages/playground/src/core/store.ts
1419
+ var DOCS_LINK_IMPORT_CODE = "import { h as __docsH } from \"vue\"";
1420
+ var DOCS_LINK_USE_CODE = [
1421
+ "app.component(\"DocsLink\",{",
1422
+ "props:{to:{type:String,default:\"\"}},",
1423
+ "setup(props,{slots}){return()=>__docsH(\"a\",{",
1424
+ "href:props.to,",
1425
+ "style:{color:\"inherit\",textDecoration:\"none\"},",
1426
+ "onClick(event){event.preventDefault();window.parent.postMessage({action:\"docs:navigate\",to:props.to},\"*\")}",
1427
+ "},slots.default?.())}",
1428
+ "})"
1429
+ ].join("");
1430
+ var escapeHtmlAttr = (value) => value.replace(/&/gu, "&amp;").replace(/"/gu, "&quot;").replace(/'/gu, "&#39;").replace(/</gu, "&lt;").replace(/>/gu, "&gt;");
1431
+ var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL) => {
1432
+ return {
1433
+ showRuntimeError: false,
1434
+ showRuntimeWarning: false,
1435
+ headHTML: [
1436
+ SANDBOX_RUNTIME_ERROR_CAPTURE_HTML,
1437
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
1438
+ ...Object.values({
1439
+ ...createBuiltinStyles(cdnURL),
1440
+ ...getPlaygroundSiteStyles(),
1441
+ ...getPlaygroundStyleOverrides()
1442
+ }).map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1443
+ "<style>",
1444
+ "html,body{height:auto;min-height:0}",
1445
+ "body{color:var(--vc-foreground-color,#080f20);background:var(--vc-background-color-light,#fff)}",
1446
+ "</style>"
1447
+ ].join("\n"),
1448
+ customCode: {
1449
+ importCode: DOCS_LINK_IMPORT_CODE,
1450
+ useCode: DOCS_LINK_USE_CODE
1451
+ }
1452
+ };
1453
+ };
1454
+ var toReplFilename = (filename) => filename.startsWith("src/") ? filename : `src/${filename}`;
1455
+ var createReplFile = (filename, code) => {
1456
+ const replFilename = toReplFilename(filename);
1457
+ return new _vue_repl.File(replFilename, code);
1458
+ };
1459
+ var createRuntimeStore = (files, entry, options) => {
1460
+ const { cdnURL, builtinImportMap, ...storeOptions } = options;
1461
+ const replFiles = (0, vue.ref)(Object.fromEntries(Object.entries(files).map(([filename, code]) => {
1462
+ const file = createReplFile(filename, code);
1463
+ return [file.filename, file];
1464
+ })));
1465
+ const mainFile = (0, vue.ref)(toReplFilename(entry));
1466
+ const activeFilename = (0, vue.ref)(toReplFilename(entry));
1467
+ const store = (0, _vue_repl.useStore)({
1468
+ ...storeOptions,
1469
+ files: replFiles,
1470
+ mainFile,
1471
+ activeFilename,
1472
+ builtinImportMap: (0, vue.ref)({
1473
+ ...builtinImportMap,
1474
+ imports: {
1475
+ ...createBuiltinImports(cdnURL),
1476
+ ...getPlaygroundSiteModules(),
1477
+ ...filterSafeHrefs(builtinImportMap?.imports),
1478
+ ...getPlaygroundImportMapOverrides()
1479
+ }
1480
+ }),
1481
+ template: (0, vue.ref)({
1482
+ welcomeSFC: files[entry],
1483
+ newSFC: NEW_SFC_CODE
1484
+ })
1485
+ });
1486
+ bindPlaygroundScss(store);
1487
+ whenSassReady(files, () => store.init());
1488
+ return store;
1489
+ };
1490
+ //#endregion
1491
+ //#region packages/playground/src/core/runtime/runtime.vue?vue&type=script&setup=true&lang.ts
1492
+ var _hoisted_1$1 = {
1493
+ key: 0,
1494
+ class: "docs-playground__runtime-error",
1495
+ role: "alert"
1496
+ };
1497
+ var _hoisted_2 = {
1498
+ key: 1,
1499
+ class: "docs-playground-runtime"
1500
+ };
1501
+ var _hoisted_3 = { class: "docs-playground__header" };
1502
+ var _hoisted_4 = { class: "docs-playground__tools" };
1503
+ var _hoisted_5 = [
1504
+ "title",
1505
+ "aria-label",
1506
+ "aria-expanded"
1507
+ ];
1508
+ var _hoisted_6 = ["title", "aria-label"];
1509
+ var _hoisted_7 = {
1510
+ key: 0,
1511
+ class: "docs-playground__views"
1512
+ };
1513
+ var _hoisted_8 = [
1514
+ "title",
1515
+ "aria-label",
1516
+ "aria-pressed",
1517
+ "onClick"
1518
+ ];
1519
+ var _hoisted_9 = {
1520
+ key: 0,
1521
+ class: "docs-playground__runtime-error",
1522
+ role: "alert"
1523
+ };
1524
+ var _hoisted_10 = { class: "docs-playground-runtime__viewport-stage" };
1525
+ //#endregion
1526
+ //#region packages/playground/src/core/runtime/runtime.vue
1527
+ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1528
+ __name: "runtime",
1529
+ props: {
1530
+ files: {},
1531
+ entry: {},
1532
+ views: { default: () => ["runtime"] },
1533
+ activeView: { default: "runtime" },
1534
+ options: {},
1535
+ previewOptions: {},
1536
+ styleless: {
1537
+ type: Boolean,
1538
+ default: false
1539
+ },
1540
+ viewport: { default: "auto" },
1541
+ viewportOptions: { default: () => ["auto", 375] }
1542
+ },
1543
+ emits: [
1544
+ "files-change",
1545
+ "view-change",
1546
+ "viewport-change",
1547
+ "navigate"
1548
+ ],
1549
+ setup(__props, { emit: __emit }) {
1550
+ const props = __props;
1551
+ const { locale, t } = (0, _deot_docs_locale.useLocale)();
1552
+ const getViewText = (view) => t(playgroundViewMessage(view));
1553
+ const emit = __emit;
1554
+ const joinCode = (...values) => values.filter(Boolean).join("\n");
1555
+ const mergedPreviewOptions = (0, vue.computed)(() => {
1556
+ const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL);
1557
+ return {
1558
+ ...runtimePreviewOptions,
1559
+ ...props.previewOptions,
1560
+ headHTML: [runtimePreviewOptions.headHTML, props.previewOptions?.headHTML].filter(Boolean).join("\n"),
1561
+ customCode: {
1562
+ importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode),
1563
+ useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode)
1564
+ }
1565
+ };
1566
+ });
1567
+ const clearConsole = {
1568
+ "BASE_URL": "/",
1569
+ "DEV": false,
1570
+ "MODE": "production",
1571
+ "PROD": true,
1572
+ "SSR": false
1573
+ }.MODE !== "development";
1574
+ const copyValue = (0, vue.computed)(() => props.files[props.entry] || "");
1575
+ const store = createRuntimeStore(props.files, props.entry, props.options);
1576
+ const sandboxRef = (0, vue.ref)(null);
1577
+ const runtimeHeight = useSandboxAutoHeight(sandboxRef);
1578
+ const runtimeError = useSandboxRuntimeErrorGuard(sandboxRef);
1579
+ useSandboxTheme(sandboxRef);
1580
+ const compileErrorText = (0, vue.computed)(() => (store.errors || []).map(toErrorText).filter(Boolean).join("\n"));
1581
+ const errorText = (0, vue.computed)(() => {
1582
+ const runtime = runtimeError.value ? formatSandboxRuntimeError(runtimeError.value, t("playground.runtime.importMapTip")) : "";
1583
+ return [compileErrorText.value, runtime].filter(Boolean).join("\n\n");
1584
+ });
1585
+ const handleBridgeMessage = (event) => {
1586
+ const iframe = resolveSandboxContainer(sandboxRef.value)?.querySelector("iframe");
1587
+ if (!iframe || event.source !== iframe.contentWindow) return;
1588
+ const data = event.data;
1589
+ if (!data || data.action !== "docs:navigate" || typeof data.to !== "string") return;
1590
+ emit("navigate", data.to);
1591
+ };
1592
+ if (typeof window !== "undefined") window.addEventListener("message", handleBridgeMessage);
1593
+ (0, vue.onBeforeUnmount)(() => window.removeEventListener("message", handleBridgeMessage));
1594
+ const viewportMenuVisible = (0, vue.ref)(false);
1595
+ const viewportLabel = (0, vue.computed)(() => formatViewportLabel(props.viewport, t("playground.runtime.auto")));
1596
+ const desiredViewportHeight = (0, vue.computed)(() => getViewportHeight(props.viewport) || runtimeHeight.value);
1597
+ const previewStyle = (0, vue.computed)(() => ({ height: `${desiredViewportHeight.value + 20}px` }));
1598
+ const stylelessStyle = (0, vue.computed)(() => errorText.value ? void 0 : { height: `${desiredViewportHeight.value}px` });
1599
+ const viewportStyle = (0, vue.computed)(() => {
1600
+ const width = getViewportWidth(props.viewport);
1601
+ return {
1602
+ width: width ? `${width}px` : "100%",
1603
+ maxWidth: "100%",
1604
+ height: "100%"
1605
+ };
1606
+ });
1607
+ let syncedFiles = { ...props.files };
1608
+ let syncedEntry = props.entry;
1609
+ const handleFilesChange = (files, entry, action) => {
1610
+ syncedFiles = { ...files };
1611
+ syncedEntry = entry;
1612
+ const apply = () => {
1613
+ switch (action.type) {
1614
+ case "update": {
1615
+ const file = store.files[toReplFilename(action.filename)];
1616
+ if (file) file.code = files[action.filename];
1617
+ break;
1618
+ }
1619
+ case "create":
1620
+ store.addFile(createReplFile(action.filename, files[action.filename]));
1621
+ break;
1622
+ case "rename":
1623
+ store.renameFile(toReplFilename(action.previousFilename), toReplFilename(action.filename));
1624
+ break;
1625
+ case "delete":
1626
+ store.setActive(toReplFilename(entry));
1627
+ delete store.files[toReplFilename(action.filename)];
1628
+ break;
1629
+ case "entry":
1630
+ store.mainFile = toReplFilename(entry);
1631
+ store.setActive(toReplFilename(entry));
1632
+ }
1633
+ emit("files-change", files, entry, action);
1634
+ };
1635
+ whenSassReady(files, apply);
1636
+ };
1637
+ const handleEditor = () => {
1638
+ Editor.popup({
1639
+ files: { ...props.files },
1640
+ entry: props.entry,
1641
+ locale: locale.value,
1642
+ getCodeErrors: () => store.errors,
1643
+ onFilesChange: handleFilesChange,
1644
+ onActiveChange: (filename) => store.setActive(toReplFilename(filename))
1645
+ });
1646
+ };
1647
+ const handleView = (view) => emit("view-change", view);
1648
+ const handleViewport = (index) => {
1649
+ const viewport = props.viewportOptions[index];
1650
+ if (viewport && !viewportEquals(viewport, props.viewport)) emit("viewport-change", viewport);
1651
+ };
1652
+ (0, vue.watch)(() => props.viewportOptions.length, (length) => {
1653
+ if (length <= 1) viewportMenuVisible.value = false;
1654
+ });
1655
+ (0, vue.watch)(() => props.files, (files) => {
1656
+ if (filesEqual(files, syncedFiles)) return;
1657
+ syncedFiles = { ...files };
1658
+ syncedEntry = props.entry;
1659
+ runtimeError.value = "";
1660
+ store.setFiles(files, props.entry);
1661
+ }, { deep: true });
1662
+ (0, vue.watch)(() => props.entry, (entry) => {
1663
+ if (!entry || entry === syncedEntry) return;
1664
+ syncedEntry = entry;
1665
+ store.mainFile = toReplFilename(entry);
1666
+ store.setActive(toReplFilename(entry));
1667
+ });
1668
+ return (_ctx, _cache) => {
1669
+ return __props.styleless ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1670
+ key: 0,
1671
+ class: "docs-playground-runtime--styleless",
1672
+ style: (0, vue.normalizeStyle)(stylelessStyle.value)
1673
+ }, [errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_1$1, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", {
1674
+ class: "docs-playground-runtime__viewport",
1675
+ style: (0, vue.normalizeStyle)(viewportStyle.value)
1676
+ }, [(0, vue.createVNode)((0, vue.unref)(_vue_repl.Sandbox), {
1677
+ ref_key: "sandboxRef",
1678
+ ref: sandboxRef,
1679
+ store: (0, vue.unref)(store),
1680
+ "auto-store-init": false,
1681
+ "preview-options": mergedPreviewOptions.value
1682
+ }, null, 8, ["store", "preview-options"])], 4)], 4)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2, [
1683
+ (0, vue.createElementVNode)("div", _hoisted_3, [(0, vue.createElementVNode)("div", _hoisted_4, [
1684
+ __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Dropdown), {
1685
+ key: 0,
1686
+ modelValue: viewportMenuVisible.value,
1687
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewportMenuVisible.value = $event),
1688
+ class: "docs-playground__viewport-menu",
1689
+ portal: true,
1690
+ trigger: "click",
1691
+ placement: "bottom-right"
1692
+ }, {
1693
+ content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
1694
+ class: "docs-playground__viewport-options",
1695
+ role: "menu",
1696
+ "aria-label": (0, vue.unref)(t)("playground.runtime.viewportMenu")
1697
+ }, {
1698
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.viewportOptions, (item, index) => {
1699
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
1700
+ key: (0, vue.unref)(getViewportKey)(item),
1701
+ class: "docs-playground__viewport-option",
1702
+ value: index,
1703
+ selected: (0, vue.unref)(viewportEquals)(item, __props.viewport),
1704
+ role: "menuitemradio",
1705
+ "aria-checked": (0, vue.unref)(viewportEquals)(item, __props.viewport),
1706
+ onClick: ($event) => handleViewport(index)
1707
+ }, {
1708
+ default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(formatViewportLabel)(item, (0, vue.unref)(t)("playground.runtime.auto"))), 1)]),
1709
+ _: 2
1710
+ }, 1032, [
1711
+ "value",
1712
+ "selected",
1713
+ "aria-checked",
1714
+ "onClick"
1715
+ ]);
1716
+ }), 128))]),
1717
+ _: 1
1718
+ }, 8, ["aria-label"])]),
1719
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
1720
+ type: "button",
1721
+ class: (0, vue.normalizeClass)(["docs-playground__tool docs-playground__viewport-trigger", { "is-active": __props.viewport !== "auto" }]),
1722
+ title: (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1723
+ "aria-label": (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1724
+ "aria-expanded": viewportMenuVisible.value,
1725
+ "aria-haspopup": "menu"
1726
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "viewport" })], 10, _hoisted_5)]),
1727
+ _: 1
1728
+ }, 8, ["modelValue"])) : (0, vue.createCommentVNode)("v-if", true),
1729
+ (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
1730
+ class: "docs-playground__tool",
1731
+ value: copyValue.value,
1732
+ tag: "button",
1733
+ type: "button",
1734
+ title: (0, vue.unref)(t)("playground.common.copy"),
1735
+ "aria-label": (0, vue.unref)(t)("playground.common.copy")
1736
+ }, {
1737
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "copy" })]),
1738
+ _: 1
1739
+ }, 8, [
1740
+ "value",
1741
+ "title",
1742
+ "aria-label"
1743
+ ]),
1744
+ (0, vue.createElementVNode)("button", {
1745
+ type: "button",
1746
+ class: "docs-playground__tool docs-playground__editor",
1747
+ "data-action": "edit",
1748
+ title: (0, vue.unref)(t)("playground.runtime.editFiles"),
1749
+ "aria-label": (0, vue.unref)(t)("playground.runtime.editFiles"),
1750
+ onClick: handleEditor
1751
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_6)
1752
+ ]), __props.views.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_7, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.views, (item) => {
1753
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1754
+ key: item,
1755
+ type: "button",
1756
+ class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
1757
+ title: getViewText(item),
1758
+ "aria-label": getViewText(item),
1759
+ "aria-pressed": item === __props.activeView,
1760
+ onClick: ($event) => handleView(item)
1761
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: item }, null, 8, ["name"])], 10, _hoisted_8);
1762
+ }), 128))])) : (0, vue.createCommentVNode)("v-if", true)]),
1763
+ errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_9, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
1764
+ (0, vue.createElementVNode)("section", {
1765
+ class: "docs-playground__preview",
1766
+ style: (0, vue.normalizeStyle)(previewStyle.value)
1767
+ }, [(0, vue.createElementVNode)("div", _hoisted_10, [(0, vue.createElementVNode)("div", {
1768
+ class: "docs-playground-runtime__viewport",
1769
+ style: (0, vue.normalizeStyle)(viewportStyle.value)
1770
+ }, [(0, vue.createVNode)((0, vue.unref)(_vue_repl.Sandbox), {
1771
+ ref_key: "sandboxRef",
1772
+ ref: sandboxRef,
1773
+ store: (0, vue.unref)(store),
1774
+ "auto-store-init": false,
1775
+ "clear-console": clearConsole,
1776
+ "preview-options": mergedPreviewOptions.value
1777
+ }, null, 8, ["store", "preview-options"])], 4)])], 4)
1778
+ ]));
1779
+ };
1780
+ }
1781
+ });
1782
+ //#endregion
1783
+ //#region packages/playground/src/playground.vue?vue&type=script&setup=true&lang.ts
1784
+ var _hoisted_1 = {
1785
+ key: 0,
1786
+ class: "docs-playground__error"
1787
+ };
1788
+ //#endregion
1789
+ //#region packages/playground/src/playground.vue
1790
+ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
1791
+ __name: "playground",
1792
+ props: {
1793
+ modelValue: { default: "" },
1794
+ files: { default: () => ({}) },
1795
+ entry: { default: "" },
1796
+ views: { default: () => ["runtime"] },
1797
+ viewport: {},
1798
+ viewportOptions: {},
1799
+ styleless: {
1800
+ type: Boolean,
1801
+ default: false
1802
+ },
1803
+ options: { default: () => ({}) },
1804
+ previewOptions: {},
1805
+ locale: {}
1806
+ },
1807
+ emits: [
1808
+ "update:modelValue",
1809
+ "update:files",
1810
+ "update:entry",
1811
+ "update:viewport",
1812
+ "change",
1813
+ "navigate"
1814
+ ],
1815
+ setup(__props, { emit: __emit }) {
1816
+ const emit = __emit;
1817
+ const props = __props;
1818
+ const inheritedLocale = (0, _deot_docs_locale.useLocale)();
1819
+ const locale = (0, vue.computed)(() => props.locale || inheritedLocale.locale.value);
1820
+ (0, _deot_docs_locale.provideLocale)(locale);
1821
+ const { t } = (0, _deot_docs_locale.useLocale)(locale);
1822
+ const initialFiles = Object.keys(props.files).length ? { ...props.files } : { [DEFAULT_ENTRY]: props.modelValue || NEW_SFC_CODE };
1823
+ const initialEntry = props.entry && initialFiles[props.entry] !== void 0 ? props.entry : Object.keys(initialFiles)[0];
1824
+ const sourceFiles = (0, vue.ref)(initialFiles);
1825
+ const currentEntry = (0, vue.ref)(initialEntry);
1826
+ const fileActiveFilename = (0, vue.ref)(initialEntry);
1827
+ const normalizeViews = (views) => {
1828
+ const normalized = [];
1829
+ for (const view of views) {
1830
+ if (view !== "runtime" && view !== "files") continue;
1831
+ if (normalized.includes(view)) continue;
1832
+ normalized.push(view);
1833
+ }
1834
+ return normalized.length ? normalized : ["runtime"];
1835
+ };
1836
+ const normalizedViews = (0, vue.computed)(() => normalizeViews(props.views));
1837
+ const activeView = (0, vue.ref)(normalizedViews.value[0]);
1838
+ const runtimeActivated = (0, vue.ref)(props.styleless || activeView.value === "runtime");
1839
+ const normalizedViewportOptions = (0, vue.computed)(() => normalizeViewportOptions(props.viewportOptions));
1840
+ const activeViewport = (0, vue.ref)(resolveInitialViewport(props.viewport, normalizedViewportOptions.value));
1841
+ const selectableViewportOptions = (0, vue.computed)(() => includeActiveViewport(normalizedViewportOptions.value, activeViewport.value));
1842
+ const error = (0, vue.ref)(props.entry && initialFiles[props.entry] === void 0 ? t("playground.validation.entryMissing", { filename: props.entry }) : "");
1843
+ (0, vue.watch)(locale, () => {
1844
+ if (props.entry && sourceFiles.value[props.entry] === void 0) error.value = t("playground.validation.entryMissing", { filename: props.entry });
1845
+ });
1846
+ const emitFiles = () => emit("update:files", { ...sourceFiles.value });
1847
+ const handleView = (view) => {
1848
+ activeView.value = view;
1849
+ if (view === "runtime") runtimeActivated.value = true;
1850
+ };
1851
+ const handleViewport = (viewport) => {
1852
+ if (!isValidViewport(viewport) || viewportEquals(activeViewport.value, viewport)) return;
1853
+ activeViewport.value = cloneViewport(viewport);
1854
+ emit("update:viewport", cloneViewport(viewport));
1855
+ };
1856
+ const handleFileActive = (filename) => {
1857
+ if (sourceFiles.value[filename] !== void 0) fileActiveFilename.value = filename;
1858
+ };
1859
+ const handleFilesChange = (files, entry, action) => {
1860
+ const previousEntry = currentEntry.value;
1861
+ sourceFiles.value = { ...files };
1862
+ currentEntry.value = entry;
1863
+ error.value = "";
1864
+ switch (action.type) {
1865
+ case "create":
1866
+ fileActiveFilename.value = action.filename;
1867
+ break;
1868
+ case "rename":
1869
+ if (fileActiveFilename.value === action.previousFilename) fileActiveFilename.value = action.filename;
1870
+ break;
1871
+ case "delete":
1872
+ if (fileActiveFilename.value === action.filename) fileActiveFilename.value = entry;
1873
+ break;
1874
+ case "entry": fileActiveFilename.value = entry;
1875
+ }
1876
+ if (action.type !== "entry") emitFiles();
1877
+ if (entry !== previousEntry) emit("update:entry", entry);
1878
+ if (action.type === "update" && action.filename === entry) {
1879
+ emit("update:modelValue", files[entry]);
1880
+ emit("change", files[entry]);
1881
+ }
1882
+ };
1883
+ (0, vue.watch)(() => props.modelValue, (value) => {
1884
+ if (Object.keys(props.files).length || sourceFiles.value[currentEntry.value] === value) return;
1885
+ sourceFiles.value = {
1886
+ ...sourceFiles.value,
1887
+ [currentEntry.value]: value
1888
+ };
1889
+ });
1890
+ (0, vue.watch)(() => props.files, (files) => {
1891
+ if (!Object.keys(files).length || filesEqual(files, sourceFiles.value)) return;
1892
+ const previousEntry = currentEntry.value;
1893
+ const entry = props.entry && files[props.entry] !== void 0 ? props.entry : Object.keys(files)[0];
1894
+ sourceFiles.value = { ...files };
1895
+ currentEntry.value = entry;
1896
+ error.value = props.entry && files[props.entry] === void 0 ? t("playground.validation.entryMissing", { filename: props.entry }) : "";
1897
+ if (files[fileActiveFilename.value] === void 0 || fileActiveFilename.value === previousEntry) fileActiveFilename.value = entry;
1898
+ }, { deep: true });
1899
+ (0, vue.watch)(() => props.entry, (entry) => {
1900
+ if (!entry || entry === currentEntry.value || !Object.keys(props.files).length) return;
1901
+ if (sourceFiles.value[entry] === void 0) {
1902
+ error.value = t("playground.validation.entryMissing", { filename: entry });
1903
+ return;
1904
+ }
1905
+ const previousEntry = currentEntry.value;
1906
+ currentEntry.value = entry;
1907
+ if (fileActiveFilename.value === previousEntry) fileActiveFilename.value = entry;
1908
+ error.value = "";
1909
+ });
1910
+ (0, vue.watch)(normalizedViews, (views) => {
1911
+ if (!views.includes(activeView.value)) activeView.value = views[0];
1912
+ if (!views.includes("runtime")) runtimeActivated.value = false;
1913
+ else if (activeView.value === "runtime") runtimeActivated.value = true;
1914
+ });
1915
+ (0, vue.watch)(() => props.viewport, (viewport) => {
1916
+ const nextViewport = resolveInitialViewport(viewport, normalizedViewportOptions.value);
1917
+ if (viewportEquals(activeViewport.value, nextViewport)) return;
1918
+ activeViewport.value = nextViewport;
1919
+ });
1920
+ (0, vue.watch)(normalizedViewportOptions, (options) => {
1921
+ if (!options.length) {
1922
+ const nextViewport = resolveInitialViewport(props.viewport, options);
1923
+ if (!viewportEquals(activeViewport.value, nextViewport)) activeViewport.value = nextViewport;
1924
+ return;
1925
+ }
1926
+ if (options.some((viewport) => viewportEquals(viewport, activeViewport.value))) return;
1927
+ activeViewport.value = isValidViewport(props.viewport) ? cloneViewport(props.viewport) : cloneViewport(options[0]);
1928
+ });
1929
+ return (_ctx, _cache) => {
1930
+ return __props.styleless ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(runtime_default), {
1931
+ key: 0,
1932
+ files: sourceFiles.value,
1933
+ entry: currentEntry.value,
1934
+ options: __props.options,
1935
+ "preview-options": __props.previewOptions,
1936
+ styleless: true,
1937
+ viewport: activeViewport.value,
1938
+ "viewport-options": selectableViewportOptions.value,
1939
+ onFilesChange: handleFilesChange,
1940
+ onNavigate: _cache[0] || (_cache[0] = ($event) => emit("navigate", $event)),
1941
+ onViewportChange: handleViewport
1942
+ }, null, 8, [
1943
+ "files",
1944
+ "entry",
1945
+ "options",
1946
+ "preview-options",
1947
+ "viewport",
1948
+ "viewport-options"
1949
+ ])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1950
+ key: 1,
1951
+ class: (0, vue.normalizeClass)(["docs-playground", { "docs-playground--files": activeView.value === "files" }])
1952
+ }, [
1953
+ error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1, (0, vue.toDisplayString)(error.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
1954
+ runtimeActivated.value && normalizedViews.value.includes("runtime") ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(runtime_default), {
1955
+ key: 1,
1956
+ files: sourceFiles.value,
1957
+ entry: currentEntry.value,
1958
+ options: __props.options,
1959
+ "preview-options": __props.previewOptions,
1960
+ "active-view": activeView.value,
1961
+ views: normalizedViews.value,
1962
+ viewport: activeViewport.value,
1963
+ "viewport-options": selectableViewportOptions.value,
1964
+ onFilesChange: handleFilesChange,
1965
+ onNavigate: _cache[1] || (_cache[1] = ($event) => emit("navigate", $event)),
1966
+ onViewChange: handleView,
1967
+ onViewportChange: handleViewport
1968
+ }, null, 8, [
1969
+ "files",
1970
+ "entry",
1971
+ "options",
1972
+ "preview-options",
1973
+ "active-view",
1974
+ "views",
1975
+ "viewport",
1976
+ "viewport-options"
1977
+ ])), [[vue.vShow, !error.value && activeView.value === "runtime"]]) : (0, vue.createCommentVNode)("v-if", true),
1978
+ normalizedViews.value.includes("files") ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(files_preview_default), {
1979
+ key: 2,
1980
+ files: sourceFiles.value,
1981
+ entry: currentEntry.value,
1982
+ "active-filename": fileActiveFilename.value,
1983
+ "active-view": activeView.value,
1984
+ views: normalizedViews.value,
1985
+ onActiveChange: handleFileActive,
1986
+ onViewChange: handleView
1987
+ }, null, 8, [
1988
+ "files",
1989
+ "entry",
1990
+ "active-filename",
1991
+ "active-view",
1992
+ "views"
1993
+ ])), [[vue.vShow, !error.value && activeView.value === "files"]]) : (0, vue.createCommentVNode)("v-if", true)
1994
+ ], 2));
1995
+ };
1996
+ }
1997
+ });
1998
+ //#endregion
1999
+ exports.CodePreview = code_preview_default;
2000
+ exports.DEFAULT_CDN_URL = DEFAULT_CDN_URL;
2001
+ exports.Editor = Editor;
2002
+ exports.Playground = playground_default;
2003
+ exports.applyPlaygroundImportMapOverride = applyPlaygroundImportMapOverride;
2004
+ exports.applyPlaygroundStyleOverride = applyPlaygroundStyleOverride;
2005
+ exports.clearPlaygroundMaps = clearPlaygroundMaps;
2006
+ exports.createBuiltinImports = createBuiltinImports;
2007
+ exports.createBuiltinStyles = createBuiltinStyles;
2008
+ exports.createRuntimePreviewOptions = createRuntimePreviewOptions;
2009
+ exports.getPlaygroundImportMapOverrides = getPlaygroundImportMapOverrides;
2010
+ exports.getPlaygroundSiteModules = getPlaygroundSiteModules;
2011
+ exports.getPlaygroundSiteStyles = getPlaygroundSiteStyles;
2012
+ exports.getPlaygroundStyleOverrides = getPlaygroundStyleOverrides;
2013
+ exports.highlightCode = highlightCode;
2014
+ exports.highlightCodeByLanguage = highlightCodeByLanguage;
2015
+ exports.isSafePlaygroundHref = isSafePlaygroundHref;
2016
+ exports.normalizeCdnURL = normalizeCdnURL;
2017
+ exports.registerVueHighlight = registerVueHighlight;
2018
+ exports.removePlaygroundImportMapOverride = removePlaygroundImportMapOverride;
2019
+ exports.removePlaygroundStyleOverride = removePlaygroundStyleOverride;
2020
+ exports.resolveHighlightLanguage = resolveHighlightLanguage;
2021
+ exports.setPlaygroundImportMapOverrides = setPlaygroundImportMapOverrides;
2022
+ exports.setPlaygroundSiteModules = setPlaygroundSiteModules;
2023
+ exports.setPlaygroundSiteStyles = setPlaygroundSiteStyles;
2024
+ exports.setPlaygroundStyleOverrides = setPlaygroundStyleOverrides;
2025
+ exports.vueHighlight = vueHighlight;