@deot/docs-playground 1.0.9 → 1.1.0

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 CHANGED
@@ -133,12 +133,14 @@ var PLAYGROUND_ICONS = {
133
133
  "c.43-.63.66-1.08.92-1.81h2.37c.25.8.51 1.26.97 1.77l.03.05zM8.12 8.4H2.9",
134
134
  "c-.64 0-1.17.53-1.17 1.19v10.75c0 .66.53 1.19 1.17 1.19h5.21c.65 0 1.18-.53 1.18-1.19V9.6",
135
135
  "a1.16 1.16 0 0 0-1.17-1.19M8.1 20.36H2.94V9.64h5.15z"
136
- ].join(""))
136
+ ].join("")),
137
+ expand: strokeIcon("M6 9l6 6 6-6"),
138
+ popup: strokeIcon(["M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3", "M3 16v3a2 2 0 0 0 2 2h3"].join(""))
137
139
  };
138
140
  //#endregion
139
141
  //#region packages/playground/src/icon/icon.vue?vue&type=script&setup=true&lang.ts
140
- var _hoisted_1$5 = ["fill", "stroke"];
141
- var _hoisted_2$4 = ["d"];
142
+ var _hoisted_1$7 = ["fill", "stroke"];
143
+ var _hoisted_2$7 = ["d"];
142
144
  //#endregion
143
145
  //#region packages/playground/src/icon/icon.vue
144
146
  var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -169,7 +171,7 @@ var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
169
171
  "stroke-linecap": "round",
170
172
  "stroke-linejoin": "round",
171
173
  "aria-hidden": "true"
172
- }, [(0, vue.createElementVNode)("path", { d: icon.value.path }, null, 8, _hoisted_2$4)], 12, _hoisted_1$5);
174
+ }, [(0, vue.createElementVNode)("path", { d: icon.value.path }, null, 8, _hoisted_2$7)], 12, _hoisted_1$7);
173
175
  };
174
176
  }
175
177
  });
@@ -178,7 +180,7 @@ var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
178
180
  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}";
179
181
  //#endregion
180
182
  //#region packages/playground/src/core/code-preview/code-preview.scss?inline
181
- 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}";
183
+ 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;min-width:28px;padding:0 8px;font-size:12px;line-height:18px;white-space:nowrap;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:opacity .15s ease;transition:opacity .15s ease}.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;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;width:28px;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:has(.docs-code-preview__copy:focus-visible) .docs-code-preview__language,.docs-code-preview:hover .docs-code-preview__language{opacity:0}.docs-code-preview:has(.docs-code-preview__copy:focus-visible) .docs-code-preview__copy,.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 96px 12px 24px;-webkit-box-sizing:border-box;box-sizing:border-box}";
182
184
  //#endregion
183
185
  //#region packages/playground/src/core/code-preview/style.ts
184
186
  var CODE_PREVIEW_STYLE_ID = "docs-code-preview-style";
@@ -195,10 +197,10 @@ var ensureCodePreviewStyle = () => {
195
197
  };
196
198
  //#endregion
197
199
  //#region packages/playground/src/core/code-preview/code-preview.vue?vue&type=script&setup=true&lang.ts
198
- var _hoisted_1$4 = { class: "docs-code-preview" };
199
- var _hoisted_2$3 = ["title"];
200
- var _hoisted_3$3 = { class: "docs-code-preview__code" };
201
- var _hoisted_4$3 = ["innerHTML"];
200
+ var _hoisted_1$6 = { class: "docs-code-preview" };
201
+ var _hoisted_2$6 = ["title"];
202
+ var _hoisted_3$6 = { class: "docs-code-preview__code" };
203
+ var _hoisted_4$6 = ["innerHTML"];
202
204
  //#endregion
203
205
  //#region packages/playground/src/core/code-preview/code-preview.vue
204
206
  var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -229,12 +231,12 @@ var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
229
231
  const highlightLanguage = (0, vue.computed)(() => props.language || resolveHighlightLanguage(props.filename));
230
232
  const highlightedCode = (0, vue.computed)(() => highlightCodeByLanguage(props.code, highlightLanguage.value));
231
233
  return (_ctx, _cache) => {
232
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$4, [
234
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$6, [
233
235
  languageLabel.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
234
236
  key: 0,
235
237
  class: "docs-code-preview__language",
236
238
  title: languageLabel.value
237
- }, (0, vue.toDisplayString)(languageLabel.value), 9, _hoisted_2$3)) : (0, vue.createCommentVNode)("v-if", true),
239
+ }, (0, vue.toDisplayString)(languageLabel.value), 9, _hoisted_2$6)) : (0, vue.createCommentVNode)("v-if", true),
238
240
  (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
239
241
  class: "docs-code-preview__copy",
240
242
  value: __props.code,
@@ -251,53 +253,23 @@ var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
251
253
  "aria-label"
252
254
  ]),
253
255
  (0, vue.createCommentVNode)(" eslint-disable-next-line vue/no-v-html "),
254
- (0, vue.createElementVNode)("pre", _hoisted_3$3, [(0, vue.createElementVNode)("code", {
256
+ (0, vue.createElementVNode)("pre", _hoisted_3$6, [(0, vue.createElementVNode)("code", {
255
257
  class: "hljs",
256
258
  innerHTML: highlightedCode.value
257
- }, null, 8, _hoisted_4$3)])
259
+ }, null, 8, _hoisted_4$6)])
258
260
  ]);
259
261
  };
260
262
  }
261
263
  });
262
264
  //#endregion
263
- //#region packages/playground/src/utils.ts
264
- /**
265
- * 比较两组 playground 文件是否完全一致。
266
- * @param left 左侧文件表。
267
- * @param right 右侧文件表。
268
- * @returns 键集合与各文件内容是否都相同。
269
- */
270
- var filesEqual = (left, right) => {
271
- const leftKeys = Object.keys(left);
272
- const rightKeys = Object.keys(right);
273
- return leftKeys.length === rightKeys.length && leftKeys.every((key) => left[key] === right[key]);
274
- };
275
- /**
276
- * 视图切换条使用的文案键。
277
- * @param view 当前预览视图。
278
- * @returns locale 消息键。
279
- */
280
- var playgroundViewMessage = (view) => view === "runtime" ? "playground.runtime.preview" : "playground.runtime.files";
281
- //#endregion
282
265
  //#region packages/playground/src/core/files-preview/files-preview.vue?vue&type=script&setup=true&lang.ts
283
- var _hoisted_1$3 = { class: "docs-playground-files" };
284
- var _hoisted_2$2 = { class: "docs-playground-files__toolbar" };
285
- var _hoisted_3$2 = ["data-filename", "onClick"];
286
- var _hoisted_4$2 = {
266
+ var _hoisted_1$5 = { class: "docs-playground-files" };
267
+ var _hoisted_2$5 = { class: "docs-playground-files__tabs-row" };
268
+ var _hoisted_3$5 = ["data-filename", "onClick"];
269
+ var _hoisted_4$5 = {
287
270
  key: 0,
288
271
  class: "docs-playground-files__entry"
289
272
  };
290
- var _hoisted_5$2 = { class: "docs-playground-files__actions" };
291
- var _hoisted_6$2 = {
292
- key: 0,
293
- class: "docs-playground__views docs-playground__views--files"
294
- };
295
- var _hoisted_7$2 = [
296
- "title",
297
- "aria-label",
298
- "aria-pressed",
299
- "onClick"
300
- ];
301
273
  //#endregion
302
274
  //#region packages/playground/src/core/files-preview/files-preview.vue
303
275
  var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -309,28 +281,26 @@ var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
309
281
  activeView: {},
310
282
  activeFilename: {}
311
283
  },
312
- emits: ["active-change", "view-change"],
284
+ emits: ["active-change"],
313
285
  setup(__props, { emit: __emit }) {
314
286
  const props = __props;
315
287
  const { t } = (0, _deot_docs_locale.useLocale)();
316
- const getViewText = (view) => t(playgroundViewMessage(view));
317
288
  const emit = __emit;
318
289
  const filenames = (0, vue.computed)(() => Object.keys(props.files));
319
290
  const activeCode = (0, vue.computed)(() => props.files[props.activeFilename] || "");
320
291
  const handleActive = (filename) => {
321
292
  if (filename !== props.activeFilename) emit("active-change", filename);
322
293
  };
323
- const handleView = (view) => emit("view-change", view);
324
294
  return (_ctx, _cache) => {
325
- 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), {
295
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$5, [(0, vue.createElementVNode)("div", _hoisted_2$5, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
326
296
  class: "docs-playground-files__scroller",
327
297
  "auto-resize": true,
328
298
  native: false,
329
299
  "show-bar": true,
330
- height: "44",
300
+ height: "40",
331
301
  "content-class": "docs-playground-files__tabs",
332
- "content-style": "height: 44px; overflow: hidden;",
333
- "wrapper-style": "height: 44px; overflow-x: auto; overflow-y: hidden;"
302
+ "content-style": "height: 40px; overflow: hidden;",
303
+ "wrapper-style": "height: 40px; overflow-x: auto; overflow-y: hidden;"
334
304
  }, {
335
305
  default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(filenames.value, (filename) => {
336
306
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
@@ -339,20 +309,10 @@ var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
339
309
  class: (0, vue.normalizeClass)(["docs-playground-files__tab", { "is-active": filename === __props.activeFilename }]),
340
310
  "data-filename": filename,
341
311
  onClick: ($event) => handleActive(filename)
342
- }, [(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);
312
+ }, [(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(filename), 1), filename === __props.entry ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("small", _hoisted_4$5, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.files.entry")), 1)) : (0, vue.createCommentVNode)("v-if", true)], 10, _hoisted_3$5);
343
313
  }), 128))]),
344
314
  _: 1
345
- }), (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) => {
346
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
347
- key: item,
348
- type: "button",
349
- class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
350
- title: getViewText(item),
351
- "aria-label": getViewText(item),
352
- "aria-pressed": item === __props.activeView,
353
- onClick: ($event) => handleView(item)
354
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: item }, null, 8, ["name"])], 10, _hoisted_7$2);
355
- }), 128))])) : (0, vue.createCommentVNode)("v-if", true)])]), (0, vue.createVNode)((0, vue.unref)(code_preview_default), {
315
+ })]), (0, vue.createVNode)((0, vue.unref)(code_preview_default), {
356
316
  class: "docs-playground-files__body",
357
317
  code: activeCode.value,
358
318
  filename: __props.activeFilename,
@@ -483,25 +443,26 @@ var Drag = class {
483
443
  };
484
444
  //#endregion
485
445
  //#region packages/playground/src/editor/editor.vue?vue&type=script&setup=true&lang.ts
486
- var _hoisted_1$2 = { class: "docs-playground-editor" };
487
- var _hoisted_2$1 = ["title"];
488
- var _hoisted_3$1 = { class: "docs-playground-editor__files" };
489
- var _hoisted_4$1 = [
446
+ var _hoisted_1$4 = { class: "docs-playground-editor" };
447
+ var _hoisted_2$4 = ["title"];
448
+ var _hoisted_3$4 = { class: "docs-playground-editor__files" };
449
+ var _hoisted_4$4 = [
490
450
  "data-filename",
491
451
  "onClick",
492
452
  "onDblclick"
493
453
  ];
494
- var _hoisted_5$1 = ["onKeydown"];
495
- var _hoisted_6$1 = ["title"];
496
- var _hoisted_7$1 = ["title", "aria-label"];
497
- var _hoisted_8$1 = ["title", "aria-label"];
498
- var _hoisted_9$1 = { class: "docs-playground-editor__actions" };
454
+ var _hoisted_5$4 = ["onKeydown"];
455
+ var _hoisted_6$3 = ["title"];
456
+ var _hoisted_7$3 = ["title", "aria-label"];
457
+ var _hoisted_8$2 = ["title", "aria-label"];
458
+ var _hoisted_9$2 = { class: "docs-playground-editor__actions" };
499
459
  var _hoisted_10$1 = ["title"];
500
460
  var _hoisted_11$1 = ["title", "disabled"];
501
- var _hoisted_12 = {
461
+ var _hoisted_12$1 = {
502
462
  key: 0,
503
463
  class: "docs-playground-editor__error"
504
464
  };
465
+ var EDITOR_SCROLLER_MAX_HEIGHT = "calc(100vh - 110px)";
505
466
  //#endregion
506
467
  //#region packages/playground/src/editor/editor.vue
507
468
  var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -530,6 +491,7 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
530
491
  },
531
492
  setup(__props) {
532
493
  const SUPPORTED_FILE_RE = /\.(vue|js|ts|jsx|tsx|css|s[ac]ss|json|html)$/i;
494
+ /** 浮层留白 20px + 标题栏 44px + 文件栏 46px,避免大文件把标签栏挤扁。 */
533
495
  const props = __props;
534
496
  const { t } = (0, _deot_docs_locale.useLocale)((0, vue.computed)(() => props.locale));
535
497
  const initialFiles = Object.keys(props.files).length ? props.files : { "App.vue": props.value };
@@ -541,6 +503,7 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
541
503
  const textarea = (0, vue.ref)(null);
542
504
  const filenameInput = (0, vue.ref)(null);
543
505
  const scroller = (0, vue.ref)(null);
506
+ const editorScroller = (0, vue.ref)(null);
544
507
  const editor = (0, vue.ref)(null);
545
508
  const bar = (0, vue.ref)(null);
546
509
  const wrapper = (0, vue.ref)(null);
@@ -548,6 +511,10 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
548
511
  const renamingFilename = (0, vue.ref)("");
549
512
  const filenameDraft = (0, vue.ref)("");
550
513
  const codeErrorText = (0, vue.computed)(() => props.getCodeErrors().map((item) => item instanceof Error ? item.message : item).join("\n"));
514
+ let editorResize;
515
+ const refreshEditorScroller = () => {
516
+ editorScroller.value?.refresh();
517
+ };
551
518
  const setFilenameInput = (el) => {
552
519
  filenameInput.value = el instanceof HTMLInputElement ? el : null;
553
520
  };
@@ -585,11 +552,13 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
585
552
  filename
586
553
  });
587
554
  if (filename === currentEntry.value) props.onChange(code);
555
+ (0, vue.nextTick)(refreshEditorScroller);
588
556
  })
589
557
  ],
590
558
  parent: textarea.value
591
559
  });
592
560
  editor.value.focus();
561
+ (0, vue.nextTick)(refreshEditorScroller);
593
562
  };
594
563
  const handleActive = (filename) => {
595
564
  if (filename === activeFilename.value || editorFiles.value[filename] === void 0) return;
@@ -713,13 +682,18 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
713
682
  wrapper: wrapper.value,
714
683
  container: window
715
684
  });
685
+ if (typeof ResizeObserver !== "undefined") {
686
+ editorResize = new ResizeObserver(refreshEditorScroller);
687
+ editorResize.observe(textarea.value);
688
+ }
716
689
  });
717
690
  (0, vue.onBeforeUnmount)(() => {
691
+ editorResize?.disconnect();
718
692
  drag.value?.off();
719
693
  editor.value?.destroy();
720
694
  });
721
695
  return (_ctx, _cache) => {
722
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$2, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.TransitionFade), { onAfterLeave: handleHide }, {
696
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$4, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.TransitionFade), { onAfterLeave: handleHide }, {
723
697
  default: (0, vue.withCtx)(() => [(0, vue.withDirectives)((0, vue.createElementVNode)("div", {
724
698
  ref_key: "wrapper",
725
699
  ref: wrapper,
@@ -736,8 +710,8 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
736
710
  title: (0, vue.unref)(t)("playground.common.close"),
737
711
  style: { "cursor": "pointer" },
738
712
  onClick: handleHide
739
- }, "✕", 8, _hoisted_2$1)], 512),
740
- (0, vue.createElementVNode)("div", _hoisted_3$1, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
713
+ }, "✕", 8, _hoisted_2$4)], 512),
714
+ (0, vue.createElementVNode)("div", _hoisted_3$4, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
741
715
  ref_key: "scroller",
742
716
  ref: scroller,
743
717
  class: "docs-playground-editor__scroller",
@@ -765,11 +739,11 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
765
739
  onDblclick: _cache[2] || (_cache[2] = (0, vue.withModifiers)(() => {}, ["stop"])),
766
740
  onBlur: handleRenameSubmit,
767
741
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(handleRenameSubmit, ["prevent"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(handleRenameCancel, ["prevent"]), ["esc"])]
768
- }, null, 40, _hoisted_5$1)), [[vue.vModelText, filenameDraft.value]]) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
742
+ }, null, 40, _hoisted_5$4)), [[vue.vModelText, filenameDraft.value]]) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
769
743
  filename === currentEntry.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
770
744
  key: 0,
771
745
  title: (0, vue.unref)(t)("playground.editor.entry")
772
- }, "●", 8, _hoisted_6$1)) : (0, vue.createCommentVNode)("v-if", true),
746
+ }, "●", 8, _hoisted_6$3)) : (0, vue.createCommentVNode)("v-if", true),
773
747
  (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(filename), 1),
774
748
  filename === currentEntry.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
775
749
  key: 1,
@@ -778,7 +752,7 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
778
752
  title: (0, vue.unref)(t)("playground.editor.entryCannotDelete"),
779
753
  "aria-label": (0, vue.unref)(t)("playground.editor.entryCannotDelete"),
780
754
  disabled: ""
781
- }, null, 8, _hoisted_7$1)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Popconfirm), {
755
+ }, null, 8, _hoisted_7$3)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Popconfirm), {
782
756
  key: 2,
783
757
  class: "docs-playground-editor__delete",
784
758
  title: (0, vue.unref)(t)("playground.editor.confirmDelete", { filename }),
@@ -791,13 +765,13 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
791
765
  class: "docs-playground-editor__close",
792
766
  title: (0, vue.unref)(t)("playground.editor.deleteFile"),
793
767
  "aria-label": (0, vue.unref)(t)("playground.editor.confirmDelete", { filename })
794
- }, null, 8, _hoisted_8$1)]),
768
+ }, null, 8, _hoisted_8$2)]),
795
769
  _: 2
796
770
  }, 1032, ["title", "onOk"]))
797
- ], 64))], 42, _hoisted_4$1);
771
+ ], 64))], 42, _hoisted_4$4);
798
772
  }), 128))]),
799
773
  _: 1
800
- }, 512), (0, vue.createElementVNode)("div", _hoisted_9$1, [(0, vue.createElementVNode)("button", {
774
+ }, 512), (0, vue.createElementVNode)("div", _hoisted_9$2, [(0, vue.createElementVNode)("button", {
801
775
  type: "button",
802
776
  title: (0, vue.unref)(t)("playground.editor.createFile"),
803
777
  onClick: handleCreate
@@ -807,12 +781,24 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
807
781
  disabled: activeFilename.value === currentEntry.value,
808
782
  onClick: handleEntry
809
783
  }, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.editor.setEntry")), 9, _hoisted_11$1)])]),
810
- codeErrorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12, (0, vue.toDisplayString)(codeErrorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
811
- (0, vue.createElementVNode)("div", {
812
- ref_key: "textarea",
813
- ref: textarea,
814
- class: "docs-playground-editor__editor"
815
- }, null, 512)
784
+ codeErrorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12$1, (0, vue.toDisplayString)(codeErrorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
785
+ (0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
786
+ ref_key: "editorScroller",
787
+ ref: editorScroller,
788
+ class: "docs-playground-editor__body",
789
+ "auto-resize": true,
790
+ native: false,
791
+ "show-bar": true,
792
+ always: true,
793
+ "max-height": EDITOR_SCROLLER_MAX_HEIGHT
794
+ }, {
795
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", {
796
+ ref_key: "textarea",
797
+ ref: textarea,
798
+ class: "docs-playground-editor__editor"
799
+ }, null, 512)]),
800
+ _: 1
801
+ }, 512)
816
802
  ], 512), [[vue.vShow, isActive.value]])]),
817
803
  _: 1
818
804
  })]);
@@ -822,6 +808,50 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
822
808
  //#endregion
823
809
  //#region packages/playground/src/editor/index.ts
824
810
  var Editor = new _deot_vc.Portal(editor_default, { aliveRegExp: { className: /(PORTAL_TAG_DISABLE)/ } });
811
+ //#endregion
812
+ //#region packages/playground/src/utils.ts
813
+ /**
814
+ * 比较两组 playground 文件是否完全一致。
815
+ * @param left 左侧文件表。
816
+ * @param right 右侧文件表。
817
+ * @returns 键集合与各文件内容是否都相同。
818
+ */
819
+ var filesEqual = (left, right) => {
820
+ const leftKeys = Object.keys(left);
821
+ const rightKeys = Object.keys(right);
822
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => left[key] === right[key]);
823
+ };
824
+ /**
825
+ * 视图切换条使用的文案键。
826
+ * @param view 当前预览视图。
827
+ * @returns locale 消息键。
828
+ */
829
+ var playgroundViewMessage = (view) => view === "runtime" ? "playground.runtime.preview" : "playground.runtime.files";
830
+ /**
831
+ * 与 markdown-it-anchor 默认 `slugify` 对齐,用于标题锚点 id。
832
+ * @param title 标题文本。
833
+ * @returns 可用作元素 id / hash 的字符串;空标题返回空串。
834
+ */
835
+ var slugifyPlaygroundTitle = (title) => {
836
+ const normalized = String(title).trim().toLowerCase().replace(/\s+/g, "-");
837
+ return normalized ? encodeURIComponent(normalized) : "";
838
+ };
839
+ /**
840
+ * 解析标题锚点 id:显式 id 优先,否则从标题 slugify;必要时避开已占用 id。
841
+ * @param title 标题文本。
842
+ * @param explicitId 可选显式 id。
843
+ * @param isOccupied 返回该 id 是否已被其他元素占用。
844
+ * @returns 锚点 id;无标题时为空串。
845
+ */
846
+ var resolvePlaygroundTitleId = (title, explicitId = "", isOccupied) => {
847
+ if (!String(title).trim()) return "";
848
+ const base = String(explicitId).trim() || slugifyPlaygroundTitle(title);
849
+ if (!base) return "";
850
+ if (!isOccupied || !isOccupied(base)) return base;
851
+ let index = 1;
852
+ while (isOccupied(`${base}-${index}`)) index += 1;
853
+ return `${base}-${index}`;
854
+ };
825
855
  var resolveSandboxContainer = (sandbox) => sandbox?.container ? (0, vue.unref)(sandbox.container) : null;
826
856
  var useSandboxAutoHeight = (sandboxRef) => {
827
857
  const height = (0, vue.ref)(24);
@@ -931,6 +961,77 @@ var useSandboxAutoHeight = (sandboxRef) => {
931
961
  });
932
962
  return height;
933
963
  };
964
+ var CLIPPING_OVERFLOW = /* @__PURE__ */ new Set([
965
+ "auto",
966
+ "scroll",
967
+ "hidden",
968
+ "overlay"
969
+ ]);
970
+ var SCROLLABLE_OVERFLOW = /* @__PURE__ */ new Set([
971
+ "auto",
972
+ "scroll",
973
+ "overlay"
974
+ ]);
975
+ var getWindowInnerHeight = () => typeof window === "undefined" ? 0 : window.innerHeight;
976
+ var getPlaygroundHost = (el) => el?.closest(".docs-playground") ?? el;
977
+ var isPlaygroundExpandable = (value) => {
978
+ if (value === true) return true;
979
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
980
+ };
981
+ var resolveRemainingPreviewHeight = ({ viewportHeight, chromeHeight, gap = 16 }) => Math.max(0, Math.round((Number.isFinite(viewportHeight) ? viewportHeight : 0) - (Number.isFinite(chromeHeight) ? Math.max(0, chromeHeight) : 0) - (Number.isFinite(gap) ? gap : 16)));
982
+ var resolveExpandedPreviewHeight = (expandable, availableHeight) => {
983
+ if (expandable === true) return Math.max(24, Math.round(Number.isFinite(availableHeight) ? availableHeight : 0));
984
+ return Math.max(24, Math.round(expandable));
985
+ };
986
+ var getVisibleViewportRect = (el) => {
987
+ const viewportHeight = getWindowInnerHeight();
988
+ let top = 0;
989
+ let bottom = viewportHeight;
990
+ if (typeof window === "undefined" || !el) return {
991
+ top,
992
+ bottom,
993
+ height: Math.max(0, Math.round(bottom - top))
994
+ };
995
+ let node = getPlaygroundHost(el)?.parentElement ?? null;
996
+ while (node && node !== document.documentElement) {
997
+ const overflowY = getComputedStyle(node).overflowY;
998
+ if (CLIPPING_OVERFLOW.has(overflowY)) {
999
+ const rect = node.getBoundingClientRect();
1000
+ if (Number.isFinite(rect.top)) top = Math.max(top, rect.top);
1001
+ if (Number.isFinite(rect.bottom)) bottom = Math.min(bottom, rect.bottom);
1002
+ }
1003
+ node = node.parentElement;
1004
+ }
1005
+ return {
1006
+ top,
1007
+ bottom,
1008
+ height: Math.max(0, Math.round(bottom - top))
1009
+ };
1010
+ };
1011
+ var findScrollableAncestor = (el) => {
1012
+ if (!el || typeof window === "undefined") return null;
1013
+ let node = getPlaygroundHost(el)?.parentElement ?? null;
1014
+ while (node && node !== document.documentElement) {
1015
+ const overflowY = getComputedStyle(node).overflowY;
1016
+ if (SCROLLABLE_OVERFLOW.has(overflowY) && node.scrollHeight > node.clientHeight + 1) return node;
1017
+ node = node.parentElement;
1018
+ }
1019
+ return null;
1020
+ };
1021
+ var scrollPlaygroundToViewportStart = (el) => {
1022
+ if (!el) return;
1023
+ const host = getPlaygroundHost(el) ?? el;
1024
+ const scroller = findScrollableAncestor(host);
1025
+ if (scroller) {
1026
+ const delta = host.getBoundingClientRect().top - scroller.getBoundingClientRect().top;
1027
+ scroller.scrollTop = Math.max(0, scroller.scrollTop + delta);
1028
+ return;
1029
+ }
1030
+ if (typeof host.scrollIntoView === "function") host.scrollIntoView({
1031
+ block: "start",
1032
+ behavior: "auto"
1033
+ });
1034
+ };
934
1035
  //#endregion
935
1036
  //#region packages/playground/src/core/runtime/error-guard.ts
936
1037
  var UNKNOWN_RUNTIME_ERROR = "运行时发生未知错误";
@@ -994,6 +1095,96 @@ var useSandboxRuntimeErrorGuard = (sandboxRef) => {
994
1095
  return error;
995
1096
  };
996
1097
  //#endregion
1098
+ //#region packages/playground/src/core/runtime/viewport.ts
1099
+ var DEFAULT_VIEWPORT_OPTIONS = ["auto", 375];
1100
+ var isValidViewport = (viewport) => {
1101
+ if (viewport === "auto") return true;
1102
+ if (typeof viewport === "number") return Number.isFinite(viewport) && viewport > 0;
1103
+ return Array.isArray(viewport) && viewport.length === 2 && viewport.every((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
1104
+ };
1105
+ var getViewportKey = (viewport) => Array.isArray(viewport) ? `${viewport[0]}x${viewport[1]}` : String(viewport);
1106
+ var viewportEquals = (left, right) => getViewportKey(left) === getViewportKey(right);
1107
+ var cloneViewport = (viewport) => Array.isArray(viewport) ? [viewport[0], viewport[1]] : viewport;
1108
+ var normalizeViewportOptions = (options) => {
1109
+ const source = options === void 0 ? DEFAULT_VIEWPORT_OPTIONS : options;
1110
+ const normalized = [];
1111
+ for (const viewport of source) {
1112
+ if (!isValidViewport(viewport)) continue;
1113
+ if (normalized.some((item) => viewportEquals(item, viewport))) continue;
1114
+ normalized.push(cloneViewport(viewport));
1115
+ }
1116
+ return normalized;
1117
+ };
1118
+ var resolveInitialViewport = (viewport, options) => isValidViewport(viewport) ? cloneViewport(viewport) : cloneViewport(options[0] || "auto");
1119
+ var includeActiveViewport = (options, viewport) => {
1120
+ if (!options.length || options.some((item) => viewportEquals(item, viewport))) return options;
1121
+ return [...options, cloneViewport(viewport)];
1122
+ };
1123
+ var getViewportWidth = (viewport) => {
1124
+ if (viewport === "auto") return null;
1125
+ return Array.isArray(viewport) ? viewport[0] : viewport;
1126
+ };
1127
+ var getViewportHeight = (viewport) => Array.isArray(viewport) ? viewport[1] : null;
1128
+ var formatViewportLabel = (viewport, autoLabel = "Auto") => {
1129
+ if (viewport === "auto") return autoLabel;
1130
+ return Array.isArray(viewport) ? `${viewport[0]} × ${viewport[1]}px` : `${viewport}px`;
1131
+ };
1132
+ var resolvePopupScreenInset = (gap = 40) => {
1133
+ const safeGap = Number.isFinite(gap) ? Math.max(0, gap) : 40;
1134
+ return {
1135
+ x: safeGap * 2,
1136
+ y: safeGap
1137
+ };
1138
+ };
1139
+ var getWindowInnerSize = () => {
1140
+ if (typeof window === "undefined") return {
1141
+ width: 0,
1142
+ height: 0
1143
+ };
1144
+ return {
1145
+ width: window.innerWidth,
1146
+ height: window.innerHeight
1147
+ };
1148
+ };
1149
+ var resolvePopupRequestedSize = (viewport, screen = getWindowInnerSize(), gap = 40) => {
1150
+ const inset = resolvePopupScreenInset(gap);
1151
+ const maxWidth = Math.max(0, Math.round((Number.isFinite(screen.width) ? screen.width : 0) - inset.x));
1152
+ const maxHeight = Math.max(0, Math.round((Number.isFinite(screen.height) ? screen.height : 0) - inset.y));
1153
+ const viewportWidth = getViewportWidth(viewport);
1154
+ const viewportHeight = getViewportHeight(viewport);
1155
+ return {
1156
+ width: viewportWidth ?? maxWidth,
1157
+ height: viewportHeight ?? maxHeight,
1158
+ maxWidth,
1159
+ maxHeight
1160
+ };
1161
+ };
1162
+ /**
1163
+ * 壳层封顶到 `screen.width - gap * 2`、`screen.height - gap`。
1164
+ * 未设 viewport 边时,画布铺满工具栏以下区域;请求尺寸大于可用区域时由 Scroller 处理溢出。
1165
+ * @param viewport 当前 playground 视口配置。
1166
+ * @param screen 屏幕可用宽高,默认取 `window.inner*`。
1167
+ * @param gap 相对屏幕上下边缘的留白;左右为该值的 2 倍。
1168
+ * @returns 壳层、内容区与画布尺寸,以及是否需要滚动。
1169
+ */
1170
+ var resolvePopupLayout = (viewport, screen = getWindowInnerSize(), gap = 40) => {
1171
+ const requested = resolvePopupRequestedSize(viewport, screen, gap);
1172
+ const shellWidth = Math.min(requested.width, requested.maxWidth);
1173
+ const shellHeight = Math.min(requested.height, requested.maxHeight);
1174
+ const bodyHeight = Math.max(0, shellHeight - 44);
1175
+ const canvasWidth = getViewportWidth(viewport) ?? shellWidth;
1176
+ const canvasHeight = getViewportHeight(viewport) ?? bodyHeight;
1177
+ return {
1178
+ shellWidth,
1179
+ shellHeight,
1180
+ bodyHeight,
1181
+ canvasWidth,
1182
+ canvasHeight,
1183
+ needsScrollX: canvasWidth > shellWidth,
1184
+ needsScrollY: canvasHeight > bodyHeight
1185
+ };
1186
+ };
1187
+ //#endregion
997
1188
  //#region packages/playground/src/core/runtime/theme.ts
998
1189
  var resolveElementTheme = (element) => {
999
1190
  const vcTheme = element?.getAttribute("data-vc-theme");
@@ -1086,40 +1277,221 @@ var useSandboxTheme = (sandboxRef) => {
1086
1277
  });
1087
1278
  };
1088
1279
  //#endregion
1089
- //#region packages/playground/src/core/runtime/viewport.ts
1090
- var DEFAULT_VIEWPORT_OPTIONS = ["auto", 375];
1091
- var isValidViewport = (viewport) => {
1092
- if (viewport === "auto") return true;
1093
- if (typeof viewport === "number") return Number.isFinite(viewport) && viewport > 0;
1094
- return Array.isArray(viewport) && viewport.length === 2 && viewport.every((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
1280
+ //#region packages/playground/src/core/runtime/toolbar.vue?vue&type=script&setup=true&lang.ts
1281
+ var _hoisted_1$3 = { class: "docs-playground__tools" };
1282
+ var _hoisted_2$3 = {
1283
+ key: 0,
1284
+ class: "docs-playground__tool-group"
1095
1285
  };
1096
- var getViewportKey = (viewport) => Array.isArray(viewport) ? `${viewport[0]}x${viewport[1]}` : String(viewport);
1097
- var viewportEquals = (left, right) => getViewportKey(left) === getViewportKey(right);
1098
- var cloneViewport = (viewport) => Array.isArray(viewport) ? [viewport[0], viewport[1]] : viewport;
1099
- var normalizeViewportOptions = (options) => {
1100
- const source = options === void 0 ? DEFAULT_VIEWPORT_OPTIONS : options;
1101
- const normalized = [];
1102
- for (const viewport of source) {
1103
- if (!isValidViewport(viewport)) continue;
1104
- if (normalized.some((item) => viewportEquals(item, viewport))) continue;
1105
- normalized.push(cloneViewport(viewport));
1106
- }
1107
- return normalized;
1286
+ var _hoisted_3$3 = [
1287
+ "title",
1288
+ "aria-label",
1289
+ "aria-expanded"
1290
+ ];
1291
+ var _hoisted_4$3 = {
1292
+ key: 1,
1293
+ class: "docs-playground__tool-divider",
1294
+ "aria-hidden": "true"
1108
1295
  };
1109
- var resolveInitialViewport = (viewport, options) => isValidViewport(viewport) ? cloneViewport(viewport) : cloneViewport(options[0] || "auto");
1110
- var includeActiveViewport = (options, viewport) => {
1111
- if (!options.length || options.some((item) => viewportEquals(item, viewport))) return options;
1112
- return [...options, cloneViewport(viewport)];
1296
+ var _hoisted_5$3 = ["title", "aria-label"];
1297
+ var _hoisted_6$2 = ["title", "aria-label"];
1298
+ var _hoisted_7$2 = ["title", "aria-label"];
1299
+ var _hoisted_8$1 = {
1300
+ key: 3,
1301
+ class: "docs-playground__tool-divider",
1302
+ "aria-hidden": "true"
1113
1303
  };
1114
- var getViewportWidth = (viewport) => {
1115
- if (viewport === "auto") return null;
1116
- return Array.isArray(viewport) ? viewport[0] : viewport;
1117
- };
1118
- var getViewportHeight = (viewport) => Array.isArray(viewport) ? viewport[1] : null;
1119
- var formatViewportLabel = (viewport, autoLabel = "Auto") => {
1120
- if (viewport === "auto") return autoLabel;
1121
- return Array.isArray(viewport) ? `${viewport[0]} × ${viewport[1]}px` : `${viewport}px`;
1304
+ var _hoisted_9$1 = ["aria-label"];
1305
+ var _hoisted_10 = [
1306
+ "title",
1307
+ "aria-label",
1308
+ "aria-selected",
1309
+ "aria-pressed",
1310
+ "onClick"
1311
+ ];
1312
+ var _hoisted_11 = {
1313
+ key: 2,
1314
+ class: "docs-playground__tool-divider",
1315
+ "aria-hidden": "true"
1122
1316
  };
1317
+ var _hoisted_12 = ["title", "aria-label"];
1318
+ //#endregion
1319
+ //#region packages/playground/src/core/runtime/toolbar.vue
1320
+ var toolbar_default = /* @__PURE__ */ (0, vue.defineComponent)({
1321
+ __name: "toolbar",
1322
+ props: {
1323
+ copyValue: { default: "" },
1324
+ viewport: { default: "auto" },
1325
+ viewportOptions: { default: () => ["auto", 375] },
1326
+ views: { default: () => [] },
1327
+ activeView: { default: "runtime" },
1328
+ showRuntimeActions: {
1329
+ type: Boolean,
1330
+ default: true
1331
+ },
1332
+ showOpenPopup: {
1333
+ type: Boolean,
1334
+ default: false
1335
+ },
1336
+ showClose: {
1337
+ type: Boolean,
1338
+ default: false
1339
+ }
1340
+ },
1341
+ emits: [
1342
+ "refresh",
1343
+ "edit",
1344
+ "open-popup",
1345
+ "close-popup",
1346
+ "viewport-change",
1347
+ "view-change"
1348
+ ],
1349
+ setup(__props, { emit: __emit }) {
1350
+ const props = __props;
1351
+ const emit = __emit;
1352
+ const { t } = (0, _deot_docs_locale.useLocale)();
1353
+ const VIEW_ORDER = ["runtime", "files"];
1354
+ const viewportMenuVisible = (0, vue.ref)(false);
1355
+ const orderedViews = (0, vue.computed)(() => VIEW_ORDER.filter((view) => props.views.includes(view)));
1356
+ const showViews = (0, vue.computed)(() => orderedViews.value.length > 1);
1357
+ const viewportLabel = (0, vue.computed)(() => formatViewportLabel(props.viewport, t("playground.runtime.auto")));
1358
+ const getViewAriaLabel = (view) => t(playgroundViewMessage(view));
1359
+ const getViewLabel = (view) => view === "runtime" ? t("playground.runtime.previewLabel") : t("playground.runtime.codeLabel");
1360
+ const handleViewport = (index) => {
1361
+ const viewport = props.viewportOptions[index];
1362
+ if (viewport && !viewportEquals(viewport, props.viewport)) emit("viewport-change", viewport);
1363
+ };
1364
+ (0, vue.watch)(() => props.viewportOptions.length, (length) => {
1365
+ if (length <= 1) viewportMenuVisible.value = false;
1366
+ });
1367
+ (0, vue.watch)(() => props.showRuntimeActions, (enabled) => {
1368
+ if (!enabled) viewportMenuVisible.value = false;
1369
+ });
1370
+ return (_ctx, _cache) => {
1371
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$3, [
1372
+ __props.showRuntimeActions ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [
1373
+ __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$3, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Dropdown), {
1374
+ modelValue: viewportMenuVisible.value,
1375
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewportMenuVisible.value = $event),
1376
+ class: "docs-playground__viewport-menu",
1377
+ portal: true,
1378
+ trigger: "click",
1379
+ placement: "bottom-right"
1380
+ }, {
1381
+ content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
1382
+ class: "docs-playground__viewport-options",
1383
+ role: "menu",
1384
+ "aria-label": (0, vue.unref)(t)("playground.runtime.viewportMenu")
1385
+ }, {
1386
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.viewportOptions, (item, index) => {
1387
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
1388
+ key: (0, vue.unref)(getViewportKey)(item),
1389
+ class: "docs-playground__viewport-option",
1390
+ value: index,
1391
+ selected: (0, vue.unref)(viewportEquals)(item, __props.viewport),
1392
+ role: "menuitemradio",
1393
+ "aria-checked": (0, vue.unref)(viewportEquals)(item, __props.viewport),
1394
+ onClick: ($event) => handleViewport(index)
1395
+ }, {
1396
+ default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(formatViewportLabel)(item, (0, vue.unref)(t)("playground.runtime.auto"))), 1)]),
1397
+ _: 2
1398
+ }, 1032, [
1399
+ "value",
1400
+ "selected",
1401
+ "aria-checked",
1402
+ "onClick"
1403
+ ]);
1404
+ }), 128))]),
1405
+ _: 1
1406
+ }, 8, ["aria-label"])]),
1407
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
1408
+ type: "button",
1409
+ class: (0, vue.normalizeClass)(["docs-playground__tool docs-playground__viewport-trigger", { "is-active": __props.viewport !== "auto" }]),
1410
+ title: (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1411
+ "aria-label": (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1412
+ "aria-expanded": viewportMenuVisible.value,
1413
+ "aria-haspopup": "menu"
1414
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "viewport" })], 10, _hoisted_3$3)]),
1415
+ _: 1
1416
+ }, 8, ["modelValue"])])) : (0, vue.createCommentVNode)("v-if", true),
1417
+ __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4$3)) : (0, vue.createCommentVNode)("v-if", true),
1418
+ (0, vue.createElementVNode)("button", {
1419
+ type: "button",
1420
+ class: "docs-playground__tool docs-playground__refresh",
1421
+ "data-action": "refresh",
1422
+ title: (0, vue.unref)(t)("playground.runtime.refresh"),
1423
+ "aria-label": (0, vue.unref)(t)("playground.runtime.refresh"),
1424
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("refresh"))
1425
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "refresh" })], 8, _hoisted_5$3),
1426
+ (0, vue.createElementVNode)("button", {
1427
+ type: "button",
1428
+ class: "docs-playground__tool docs-playground__editor",
1429
+ "data-action": "edit",
1430
+ title: (0, vue.unref)(t)("playground.runtime.editFiles"),
1431
+ "aria-label": (0, vue.unref)(t)("playground.runtime.editFiles"),
1432
+ onClick: _cache[2] || (_cache[2] = ($event) => emit("edit"))
1433
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_6$2),
1434
+ __props.showOpenPopup ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1435
+ key: 2,
1436
+ type: "button",
1437
+ class: "docs-playground__tool docs-playground__popup",
1438
+ "data-action": "open-popup",
1439
+ title: (0, vue.unref)(t)("playground.runtime.openPopup"),
1440
+ "aria-label": (0, vue.unref)(t)("playground.runtime.openPopup"),
1441
+ onClick: _cache[3] || (_cache[3] = ($event) => emit("open-popup"))
1442
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "popup" })], 8, _hoisted_7$2)) : (0, vue.createCommentVNode)("v-if", true),
1443
+ (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
1444
+ class: "docs-playground__tool",
1445
+ value: __props.copyValue,
1446
+ tag: "button",
1447
+ type: "button",
1448
+ title: (0, vue.unref)(t)("playground.common.copy"),
1449
+ "aria-label": (0, vue.unref)(t)("playground.common.copy")
1450
+ }, {
1451
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "copy" })]),
1452
+ _: 1
1453
+ }, 8, [
1454
+ "value",
1455
+ "title",
1456
+ "aria-label"
1457
+ ]),
1458
+ showViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$1)) : (0, vue.createCommentVNode)("v-if", true)
1459
+ ], 64)) : (0, vue.createCommentVNode)("v-if", true),
1460
+ showViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1461
+ key: 1,
1462
+ class: "docs-playground__views",
1463
+ role: "tablist",
1464
+ "aria-label": (0, vue.unref)(t)("playground.runtime.views")
1465
+ }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(orderedViews.value, (item) => {
1466
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1467
+ key: item,
1468
+ type: "button",
1469
+ class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
1470
+ role: "tab",
1471
+ title: getViewAriaLabel(item),
1472
+ "aria-label": getViewAriaLabel(item),
1473
+ "aria-selected": item === __props.activeView,
1474
+ "aria-pressed": item === __props.activeView,
1475
+ onClick: ($event) => emit("view-change", item)
1476
+ }, (0, vue.toDisplayString)(getViewLabel(item)), 11, _hoisted_10);
1477
+ }), 128))], 8, _hoisted_9$1)) : (0, vue.createCommentVNode)("v-if", true),
1478
+ __props.showClose && (__props.showRuntimeActions || showViews.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_11)) : (0, vue.createCommentVNode)("v-if", true),
1479
+ __props.showClose ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1480
+ key: 3,
1481
+ type: "button",
1482
+ class: "docs-playground__tool docs-playground__popup-close",
1483
+ "data-action": "close-popup",
1484
+ title: (0, vue.unref)(t)("playground.runtime.closePopup"),
1485
+ "aria-label": (0, vue.unref)(t)("playground.runtime.closePopup"),
1486
+ onClick: _cache[4] || (_cache[4] = ($event) => emit("close-popup"))
1487
+ }, [..._cache[5] || (_cache[5] = [(0, vue.createElementVNode)("span", {
1488
+ class: "docs-playground__popup-close-mark",
1489
+ "aria-hidden": "true"
1490
+ }, "✕", -1)])], 8, _hoisted_12)) : (0, vue.createCommentVNode)("v-if", true)
1491
+ ]);
1492
+ };
1493
+ }
1494
+ });
1123
1495
  //#endregion
1124
1496
  //#region packages/playground/src/cdn.ts
1125
1497
  var PREVIEW_STYLE_ASSETS = [
@@ -1468,6 +1840,12 @@ var DOCS_LINK_USE_CODE = [
1468
1840
  "})"
1469
1841
  ].join("");
1470
1842
  var escapeHtmlAttr = (value) => value.replace(/&/gu, "&amp;").replace(/"/gu, "&quot;").replace(/'/gu, "&#39;").replace(/</gu, "&lt;").replace(/>/gu, "&gt;");
1843
+ /**
1844
+ * runtime 画布色。`previewInset` 露出的 padding、iframe chrome 和 sandbox
1845
+ * `html`/`body` 必须共用这一变量,暗色下 `--docs-background-color` 与
1846
+ * `--vc-background-color-light` 不同,否则会出现色带割裂。
1847
+ */
1848
+ var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-light, var(--docs-background-color, #fff))";
1471
1849
  var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL) => {
1472
1850
  return {
1473
1851
  showRuntimeError: false,
@@ -1481,8 +1859,8 @@ var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL) => {
1481
1859
  ...getPlaygroundStyleOverrides()
1482
1860
  }).map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1483
1861
  "<style>",
1484
- "html,body{height:auto;min-height:0}",
1485
- "body{color:var(--vc-foreground-color,#080f20);background:var(--vc-background-color-light,#fff)}",
1862
+ `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1863
+ "body{color:var(--vc-foreground-color,#080f20)}",
1486
1864
  "</style>"
1487
1865
  ].join("\n"),
1488
1866
  customCode: {
@@ -1528,41 +1906,209 @@ var createRuntimeStore = (files, entry, options) => {
1528
1906
  return store;
1529
1907
  };
1530
1908
  //#endregion
1531
- //#region packages/playground/src/core/runtime/runtime.vue?vue&type=script&setup=true&lang.ts
1532
- var _hoisted_1$1 = {
1909
+ //#region packages/playground/src/core/runtime/alone/wrapper.vue?vue&type=script&setup=true&lang.ts
1910
+ var _hoisted_1$2 = ["aria-label"];
1911
+ var _hoisted_2$2 = { class: "docs-playground-popup__frame" };
1912
+ var _hoisted_3$2 = { class: "docs-playground-popup__header" };
1913
+ var _hoisted_4$2 = ["title"];
1914
+ var _hoisted_5$2 = { class: "docs-playground__title-text" };
1915
+ var _hoisted_6$1 = {
1533
1916
  key: 0,
1534
1917
  class: "docs-playground__runtime-error",
1535
1918
  role: "alert"
1536
1919
  };
1537
- var _hoisted_2 = {
1538
- key: 1,
1539
- class: "docs-playground-runtime"
1540
- };
1541
- var _hoisted_3 = { class: "docs-playground__header" };
1542
- var _hoisted_4 = { class: "docs-playground__tools" };
1543
- var _hoisted_5 = ["title", "aria-label"];
1544
- var _hoisted_6 = [
1545
- "title",
1546
- "aria-label",
1547
- "aria-expanded"
1548
- ];
1549
- var _hoisted_7 = ["title", "aria-label"];
1550
- var _hoisted_8 = {
1920
+ var _hoisted_7$1 = { class: "docs-playground-popup__body" };
1921
+ //#endregion
1922
+ //#region packages/playground/src/core/runtime/alone/wrapper.vue
1923
+ var wrapper_default = /* @__PURE__ */ (0, vue.defineComponent)({
1924
+ __name: "wrapper",
1925
+ props: {
1926
+ store: {},
1927
+ copyValue: {},
1928
+ title: { default: "" },
1929
+ viewport: { default: "auto" },
1930
+ viewportOptions: { default: () => ["auto", 375] },
1931
+ previewOptions: {},
1932
+ clearConsole: {
1933
+ type: Boolean,
1934
+ default: true
1935
+ }
1936
+ },
1937
+ emits: [
1938
+ "edit",
1939
+ "navigate",
1940
+ "viewport-change",
1941
+ "portal-fulfilled"
1942
+ ],
1943
+ setup(__props, { emit: __emit }) {
1944
+ const props = __props;
1945
+ const emit = __emit;
1946
+ const { t } = (0, _deot_docs_locale.useLocale)();
1947
+ const currentViewport = (0, vue.ref)(props.viewport);
1948
+ const sandboxRef = (0, vue.ref)(null);
1949
+ const sandboxKey = (0, vue.ref)(0);
1950
+ const layoutTick = (0, vue.ref)(0);
1951
+ const runtimeError = useSandboxRuntimeErrorGuard(sandboxRef);
1952
+ useSandboxTheme(sandboxRef);
1953
+ const storeErrors = (0, vue.computed)(() => (props.store.errors || []).map(toErrorText).filter(Boolean).join("\n"));
1954
+ const errorText = (0, vue.computed)(() => {
1955
+ const runtime = runtimeError.value ? formatSandboxRuntimeError(runtimeError.value, t("playground.runtime.importMapTip")) : "";
1956
+ return [storeErrors.value, runtime].filter(Boolean).join("\n\n");
1957
+ });
1958
+ const layout = (0, vue.computed)(() => {
1959
+ layoutTick.value;
1960
+ return resolvePopupLayout(currentViewport.value);
1961
+ });
1962
+ const shellStyle = (0, vue.computed)(() => ({
1963
+ width: `${layout.value.shellWidth}px`,
1964
+ height: `${layout.value.shellHeight}px`
1965
+ }));
1966
+ const canvasStyle = (0, vue.computed)(() => ({
1967
+ width: `${layout.value.canvasWidth}px`,
1968
+ height: `${layout.value.canvasHeight}px`,
1969
+ background: PLAYGROUND_RUNTIME_CANVAS_BACKGROUND
1970
+ }));
1971
+ const handleRefresh = () => {
1972
+ runtimeError.value = "";
1973
+ sandboxKey.value++;
1974
+ };
1975
+ const handleViewportChange = (viewport) => {
1976
+ currentViewport.value = viewport;
1977
+ emit("viewport-change", viewport);
1978
+ };
1979
+ const handleClose = () => emit("portal-fulfilled");
1980
+ const isPlaygroundEditorOpen = () => {
1981
+ if (typeof document === "undefined") return false;
1982
+ const editor = document.querySelector(".docs-playground-editor__wrapper");
1983
+ if (!(editor instanceof HTMLElement)) return false;
1984
+ const style = getComputedStyle(editor);
1985
+ return style.display !== "none" && style.visibility !== "hidden";
1986
+ };
1987
+ const handleKeydown = (event) => {
1988
+ if (event.key !== "Escape") return;
1989
+ if (isPlaygroundEditorOpen()) return;
1990
+ event.preventDefault();
1991
+ handleClose();
1992
+ };
1993
+ const handleResize = () => {
1994
+ layoutTick.value++;
1995
+ };
1996
+ const handleBridgeMessage = (event) => {
1997
+ const iframe = resolveSandboxContainer(sandboxRef.value)?.querySelector("iframe");
1998
+ if (!iframe || event.source !== iframe.contentWindow) return;
1999
+ const data = event.data;
2000
+ if (!data || data.action !== "docs:navigate" || typeof data.to !== "string") return;
2001
+ emit("navigate", data.to);
2002
+ };
2003
+ (0, vue.watch)(() => props.viewport, (viewport) => {
2004
+ currentViewport.value = viewport;
2005
+ });
2006
+ if (typeof window !== "undefined") {
2007
+ window.addEventListener("keydown", handleKeydown);
2008
+ window.addEventListener("resize", handleResize);
2009
+ window.addEventListener("message", handleBridgeMessage);
2010
+ }
2011
+ (0, vue.onBeforeUnmount)(() => {
2012
+ window.removeEventListener("keydown", handleKeydown);
2013
+ window.removeEventListener("resize", handleResize);
2014
+ window.removeEventListener("message", handleBridgeMessage);
2015
+ });
2016
+ return (_ctx, _cache) => {
2017
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2018
+ class: "docs-playground-popup",
2019
+ role: "dialog",
2020
+ "aria-modal": "true",
2021
+ "aria-label": (0, vue.unref)(t)("playground.runtime.openPopup")
2022
+ }, [(0, vue.createElementVNode)("div", {
2023
+ class: "docs-playground-popup__mask",
2024
+ "data-action": "popup-mask",
2025
+ onClick: handleClose
2026
+ }), (0, vue.createElementVNode)("div", {
2027
+ class: "docs-playground-popup__shell",
2028
+ style: (0, vue.normalizeStyle)(shellStyle.value)
2029
+ }, [(0, vue.createElementVNode)("div", _hoisted_2$2, [
2030
+ (0, vue.createElementVNode)("div", _hoisted_3$2, [__props.title.trim() ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2031
+ key: 0,
2032
+ class: "docs-playground__title",
2033
+ title: __props.title.trim()
2034
+ }, [(0, vue.createElementVNode)("span", _hoisted_5$2, (0, vue.toDisplayString)(__props.title.trim()), 1)], 8, _hoisted_4$2)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createVNode)(toolbar_default, {
2035
+ "copy-value": __props.copyValue,
2036
+ viewport: currentViewport.value,
2037
+ "viewport-options": __props.viewportOptions,
2038
+ "show-close": true,
2039
+ onRefresh: handleRefresh,
2040
+ onEdit: _cache[0] || (_cache[0] = ($event) => emit("edit")),
2041
+ onClosePopup: handleClose,
2042
+ onViewportChange: handleViewportChange
2043
+ }, null, 8, [
2044
+ "copy-value",
2045
+ "viewport",
2046
+ "viewport-options"
2047
+ ])]),
2048
+ errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_6$1, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2049
+ (0, vue.createElementVNode)("div", _hoisted_7$1, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
2050
+ class: "docs-playground-popup__scroller",
2051
+ "content-class": "docs-playground-popup__scroller-content",
2052
+ "auto-resize": true,
2053
+ native: false,
2054
+ "show-bar": true,
2055
+ height: "100%"
2056
+ }, {
2057
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", {
2058
+ class: "docs-playground-popup__canvas",
2059
+ style: (0, vue.normalizeStyle)(canvasStyle.value)
2060
+ }, [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_vue_repl.Sandbox), {
2061
+ key: sandboxKey.value,
2062
+ ref_key: "sandboxRef",
2063
+ ref: sandboxRef,
2064
+ store: __props.store,
2065
+ "auto-store-init": false,
2066
+ "clear-console": __props.clearConsole,
2067
+ "preview-options": __props.previewOptions
2068
+ }, null, 8, [
2069
+ "store",
2070
+ "clear-console",
2071
+ "preview-options"
2072
+ ]))], 4)]),
2073
+ _: 1
2074
+ })])
2075
+ ])], 4)], 8, _hoisted_1$2);
2076
+ };
2077
+ }
2078
+ });
2079
+ //#endregion
2080
+ //#region packages/playground/src/core/runtime/alone/index.ts
2081
+ var Alone = new _deot_vc.Portal(wrapper_default, {
2082
+ name: "docs-playground-alone",
2083
+ leaveDelay: 0,
2084
+ multiple: false
2085
+ });
2086
+ //#endregion
2087
+ //#region packages/playground/src/core/runtime/runtime.vue?vue&type=script&setup=true&lang.ts
2088
+ var _hoisted_1$1 = { class: "docs-playground-runtime-host" };
2089
+ var _hoisted_2$1 = {
1551
2090
  key: 0,
1552
- class: "docs-playground__views"
2091
+ class: "docs-playground__runtime-error",
2092
+ role: "alert"
1553
2093
  };
1554
- var _hoisted_9 = [
1555
- "title",
1556
- "aria-label",
1557
- "aria-pressed",
1558
- "onClick"
1559
- ];
1560
- var _hoisted_10 = {
2094
+ var _hoisted_3$1 = {
1561
2095
  key: 0,
2096
+ class: "docs-playground__header"
2097
+ };
2098
+ var _hoisted_4$1 = ["id", "title"];
2099
+ var _hoisted_5$1 = ["href"];
2100
+ var _hoisted_6 = { class: "docs-playground__title-text" };
2101
+ var _hoisted_7 = {
2102
+ key: 1,
1562
2103
  class: "docs-playground__runtime-error",
1563
2104
  role: "alert"
1564
2105
  };
1565
- var _hoisted_11 = { class: "docs-playground-runtime__viewport-stage" };
2106
+ var _hoisted_8 = { class: "docs-playground-runtime__viewport-stage" };
2107
+ var _hoisted_9 = [
2108
+ "aria-expanded",
2109
+ "title",
2110
+ "aria-label"
2111
+ ];
1566
2112
  //#endregion
1567
2113
  //#region packages/playground/src/core/runtime/runtime.vue
1568
2114
  var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -1579,6 +2125,13 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1579
2125
  type: Boolean,
1580
2126
  default: false
1581
2127
  },
2128
+ hideChrome: {
2129
+ type: Boolean,
2130
+ default: false
2131
+ },
2132
+ expandable: { type: [Boolean, Number] },
2133
+ title: { default: "" },
2134
+ id: { default: "" },
1582
2135
  viewport: { default: "auto" },
1583
2136
  viewportOptions: { default: () => ["auto", 375] }
1584
2137
  },
@@ -1588,24 +2141,36 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1588
2141
  "viewport-change",
1589
2142
  "navigate"
1590
2143
  ],
1591
- setup(__props, { emit: __emit }) {
2144
+ setup(__props, { expose: __expose, emit: __emit }) {
1592
2145
  const props = __props;
1593
2146
  const { locale, t } = (0, _deot_docs_locale.useLocale)();
1594
- const getViewText = (view) => t(playgroundViewMessage(view));
2147
+ const displayTitle = (0, vue.computed)(() => props.title.trim());
2148
+ const titleEl = (0, vue.ref)(null);
2149
+ const titleId = (0, vue.computed)(() => resolvePlaygroundTitleId(displayTitle.value, props.id, (candidate) => {
2150
+ if (typeof document === "undefined") return false;
2151
+ const existing = document.getElementById(candidate);
2152
+ return !!existing && existing !== titleEl.value;
2153
+ }));
1595
2154
  const emit = __emit;
1596
2155
  const joinCode = (...values) => values.filter(Boolean).join("\n");
1597
- const mergedPreviewOptions = (0, vue.computed)(() => {
2156
+ const mergePreviewOptions = (extraHeadHTML) => {
1598
2157
  const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL);
1599
2158
  return {
1600
2159
  ...runtimePreviewOptions,
1601
2160
  ...props.previewOptions,
1602
- headHTML: [runtimePreviewOptions.headHTML, props.previewOptions?.headHTML].filter(Boolean).join("\n"),
2161
+ headHTML: [
2162
+ runtimePreviewOptions.headHTML,
2163
+ props.previewOptions?.headHTML,
2164
+ extraHeadHTML
2165
+ ].filter(Boolean).join("\n"),
1603
2166
  customCode: {
1604
2167
  importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode),
1605
2168
  useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode)
1606
2169
  }
1607
2170
  };
1608
- });
2171
+ };
2172
+ const mergedPreviewOptions = (0, vue.computed)(() => mergePreviewOptions());
2173
+ const popupPreviewOptions = (0, vue.computed)(() => mergePreviewOptions("<style>html,body{height:100%;min-height:100%}</style>"));
1609
2174
  const clearConsole = {
1610
2175
  "BASE_URL": "/",
1611
2176
  "DEV": false,
@@ -1632,11 +2197,63 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1632
2197
  if (!data || data.action !== "docs:navigate" || typeof data.to !== "string") return;
1633
2198
  emit("navigate", data.to);
1634
2199
  };
1635
- if (typeof window !== "undefined") window.addEventListener("message", handleBridgeMessage);
1636
- (0, vue.onBeforeUnmount)(() => window.removeEventListener("message", handleBridgeMessage));
1637
- const viewportMenuVisible = (0, vue.ref)(false);
1638
- const viewportLabel = (0, vue.computed)(() => formatViewportLabel(props.viewport, t("playground.runtime.auto")));
1639
- const desiredViewportHeight = (0, vue.computed)(() => getViewportHeight(props.viewport) || runtimeHeight.value);
2200
+ const runtimeRoot = (0, vue.ref)(null);
2201
+ const previewExpanded = (0, vue.ref)(false);
2202
+ /** `expandable: true` 时在展开瞬间冻结,避免滚动重算高度造成抖动。 */
2203
+ const frozenExpandedHeight = (0, vue.ref)(0);
2204
+ const measurePreviewChromeHeight = () => {
2205
+ const root = runtimeRoot.value;
2206
+ if (!root) return 0;
2207
+ const header = (root.closest(".docs-playground") ?? root).querySelector(".docs-playground__header");
2208
+ const error = root.querySelector(".docs-playground__runtime-error");
2209
+ return (header instanceof HTMLElement ? header.offsetHeight : 0) + (error instanceof HTMLElement ? error.offsetHeight : 0);
2210
+ };
2211
+ const measureExpandedPreviewHeight = () => {
2212
+ if (typeof window === "undefined") return runtimeHeight.value;
2213
+ const chromeHeight = measurePreviewChromeHeight();
2214
+ return resolveExpandedPreviewHeight(true, resolveRemainingPreviewHeight({
2215
+ viewportHeight: getVisibleViewportRect(runtimeRoot.value).height,
2216
+ chromeHeight
2217
+ }));
2218
+ };
2219
+ const syncFrozenExpandedHeight = () => {
2220
+ if (!previewExpanded.value || props.expandable !== true) return;
2221
+ frozenExpandedHeight.value = measureExpandedPreviewHeight();
2222
+ };
2223
+ if (typeof window !== "undefined") {
2224
+ window.addEventListener("message", handleBridgeMessage);
2225
+ window.addEventListener("resize", syncFrozenExpandedHeight);
2226
+ }
2227
+ const canExpandPreview = (0, vue.computed)(() => !props.styleless && isPlaygroundExpandable(props.expandable));
2228
+ const expandLabel = (0, vue.computed)(() => t(previewExpanded.value ? "playground.runtime.collapsePreview" : "playground.runtime.expandPreview"));
2229
+ const desiredViewportHeight = (0, vue.computed)(() => {
2230
+ if (previewExpanded.value && props.expandable === true) return frozenExpandedHeight.value;
2231
+ if (previewExpanded.value && isPlaygroundExpandable(props.expandable)) return resolveExpandedPreviewHeight(props.expandable, 0);
2232
+ const fixedHeight = getViewportHeight(props.viewport);
2233
+ if (fixedHeight) return fixedHeight;
2234
+ return runtimeHeight.value;
2235
+ });
2236
+ const scrollExpandedPreviewIntoView = () => {
2237
+ scrollPlaygroundToViewportStart(runtimeRoot.value);
2238
+ };
2239
+ const handleTogglePreviewExpand = () => {
2240
+ if (!canExpandPreview.value) return;
2241
+ if (!previewExpanded.value) {
2242
+ if (props.expandable === true) frozenExpandedHeight.value = measureExpandedPreviewHeight();
2243
+ else frozenExpandedHeight.value = 0;
2244
+ previewExpanded.value = true;
2245
+ (0, vue.nextTick)(scrollExpandedPreviewIntoView);
2246
+ return;
2247
+ }
2248
+ previewExpanded.value = false;
2249
+ frozenExpandedHeight.value = 0;
2250
+ };
2251
+ (0, vue.watch)(canExpandPreview, (enabled) => {
2252
+ if (!enabled) {
2253
+ previewExpanded.value = false;
2254
+ frozenExpandedHeight.value = 0;
2255
+ }
2256
+ });
1640
2257
  const normalizedPreviewInset = (0, vue.computed)(() => {
1641
2258
  const value = props.previewInset;
1642
2259
  if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
@@ -1647,7 +2264,8 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1647
2264
  const [vertical, horizontal] = normalizedPreviewInset.value;
1648
2265
  return {
1649
2266
  height: `${desiredViewportHeight.value + vertical * 2}px`,
1650
- padding: vertical === horizontal ? `${vertical}px` : `${vertical}px ${horizontal}px`
2267
+ padding: vertical === horizontal ? `${vertical}px` : `${vertical}px ${horizontal}px`,
2268
+ background: PLAYGROUND_RUNTIME_CANVAS_BACKGROUND
1651
2269
  };
1652
2270
  });
1653
2271
  const stylelessStyle = (0, vue.computed)(() => errorText.value ? void 0 : { height: `${desiredViewportHeight.value}px` });
@@ -1699,18 +2317,30 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1699
2317
  onActiveChange: (filename) => store.setActive(toReplFilename(filename))
1700
2318
  });
1701
2319
  };
1702
- const handleRefresh = () => {
2320
+ const handleInlineRefresh = () => {
1703
2321
  runtimeError.value = "";
1704
2322
  sandboxKey.value++;
1705
2323
  };
1706
- const handleView = (view) => emit("view-change", view);
1707
- const handleViewport = (index) => {
1708
- const viewport = props.viewportOptions[index];
1709
- if (viewport && !viewportEquals(viewport, props.viewport)) emit("viewport-change", viewport);
2324
+ const handleClosePopup = () => {
2325
+ Alone.destroy();
2326
+ };
2327
+ const handleOpenPopup = () => {
2328
+ Alone.popup({
2329
+ store,
2330
+ copyValue: copyValue.value,
2331
+ title: props.title,
2332
+ viewport: props.viewport,
2333
+ viewportOptions: props.viewportOptions,
2334
+ previewOptions: popupPreviewOptions.value,
2335
+ clearConsole,
2336
+ onEdit: handleEditor,
2337
+ onViewportChange: handleViewportChange,
2338
+ onNavigate: (to) => emit("navigate", to)
2339
+ });
2340
+ };
2341
+ const handleViewportChange = (viewport) => {
2342
+ emit("viewport-change", viewport);
1710
2343
  };
1711
- (0, vue.watch)(() => props.viewportOptions.length, (length) => {
1712
- if (length <= 1) viewportMenuVisible.value = false;
1713
- });
1714
2344
  (0, vue.watch)(() => props.files, (files) => {
1715
2345
  if (filesEqual(files, syncedFiles)) return;
1716
2346
  syncedFiles = { ...files };
@@ -1724,12 +2354,27 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1724
2354
  store.mainFile = toReplFilename(entry);
1725
2355
  store.setActive(toReplFilename(entry));
1726
2356
  });
2357
+ (0, vue.watch)(() => props.activeView, (view) => {
2358
+ if (view !== "runtime") handleClosePopup();
2359
+ });
2360
+ (0, vue.onBeforeUnmount)(() => {
2361
+ window.removeEventListener("message", handleBridgeMessage);
2362
+ window.removeEventListener("resize", syncFrozenExpandedHeight);
2363
+ handleClosePopup();
2364
+ });
2365
+ __expose({
2366
+ refresh: handleInlineRefresh,
2367
+ edit: handleEditor,
2368
+ openPopup: handleOpenPopup
2369
+ });
1727
2370
  return (_ctx, _cache) => {
1728
- return __props.styleless ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2371
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [__props.styleless ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1729
2372
  key: 0,
2373
+ ref_key: "runtimeRoot",
2374
+ ref: runtimeRoot,
1730
2375
  class: "docs-playground-runtime--styleless",
1731
2376
  style: (0, vue.normalizeStyle)(stylelessStyle.value)
1732
- }, [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", {
2377
+ }, [errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_2$1, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", {
1733
2378
  class: "docs-playground-runtime__viewport",
1734
2379
  style: (0, vue.normalizeStyle)(viewportStyle.value)
1735
2380
  }, [(0, vue.createVNode)((0, vue.unref)(_vue_repl.Sandbox), {
@@ -1738,100 +2383,42 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1738
2383
  store: (0, vue.unref)(store),
1739
2384
  "auto-store-init": false,
1740
2385
  "preview-options": mergedPreviewOptions.value
1741
- }, null, 8, ["store", "preview-options"])], 4)], 4)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2, [
1742
- (0, vue.createElementVNode)("div", _hoisted_3, [(0, vue.createElementVNode)("div", _hoisted_4, [
1743
- (0, vue.createElementVNode)("button", {
1744
- type: "button",
1745
- class: "docs-playground__tool docs-playground__refresh",
1746
- "data-action": "refresh",
1747
- title: (0, vue.unref)(t)("playground.runtime.refresh"),
1748
- "aria-label": (0, vue.unref)(t)("playground.runtime.refresh"),
1749
- onClick: handleRefresh
1750
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "refresh" })], 8, _hoisted_5),
1751
- __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Dropdown), {
1752
- key: 0,
1753
- modelValue: viewportMenuVisible.value,
1754
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewportMenuVisible.value = $event),
1755
- class: "docs-playground__viewport-menu",
1756
- portal: true,
1757
- trigger: "click",
1758
- placement: "bottom-right"
1759
- }, {
1760
- content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
1761
- class: "docs-playground__viewport-options",
1762
- role: "menu",
1763
- "aria-label": (0, vue.unref)(t)("playground.runtime.viewportMenu")
1764
- }, {
1765
- default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.viewportOptions, (item, index) => {
1766
- return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
1767
- key: (0, vue.unref)(getViewportKey)(item),
1768
- class: "docs-playground__viewport-option",
1769
- value: index,
1770
- selected: (0, vue.unref)(viewportEquals)(item, __props.viewport),
1771
- role: "menuitemradio",
1772
- "aria-checked": (0, vue.unref)(viewportEquals)(item, __props.viewport),
1773
- onClick: ($event) => handleViewport(index)
1774
- }, {
1775
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(formatViewportLabel)(item, (0, vue.unref)(t)("playground.runtime.auto"))), 1)]),
1776
- _: 2
1777
- }, 1032, [
1778
- "value",
1779
- "selected",
1780
- "aria-checked",
1781
- "onClick"
1782
- ]);
1783
- }), 128))]),
1784
- _: 1
1785
- }, 8, ["aria-label"])]),
1786
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
1787
- type: "button",
1788
- class: (0, vue.normalizeClass)(["docs-playground__tool docs-playground__viewport-trigger", { "is-active": __props.viewport !== "auto" }]),
1789
- title: (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1790
- "aria-label": (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1791
- "aria-expanded": viewportMenuVisible.value,
1792
- "aria-haspopup": "menu"
1793
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "viewport" })], 10, _hoisted_6)]),
1794
- _: 1
1795
- }, 8, ["modelValue"])) : (0, vue.createCommentVNode)("v-if", true),
1796
- (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
1797
- class: "docs-playground__tool",
1798
- value: copyValue.value,
1799
- tag: "button",
1800
- type: "button",
1801
- title: (0, vue.unref)(t)("playground.common.copy"),
1802
- "aria-label": (0, vue.unref)(t)("playground.common.copy")
1803
- }, {
1804
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "copy" })]),
1805
- _: 1
1806
- }, 8, [
1807
- "value",
1808
- "title",
1809
- "aria-label"
1810
- ]),
1811
- (0, vue.createElementVNode)("button", {
1812
- type: "button",
1813
- class: "docs-playground__tool docs-playground__editor",
1814
- "data-action": "edit",
1815
- title: (0, vue.unref)(t)("playground.runtime.editFiles"),
1816
- "aria-label": (0, vue.unref)(t)("playground.runtime.editFiles"),
1817
- onClick: handleEditor
1818
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_7)
1819
- ]), __props.views.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_8, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.views, (item) => {
1820
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1821
- key: item,
1822
- type: "button",
1823
- class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
1824
- title: getViewText(item),
1825
- "aria-label": getViewText(item),
1826
- "aria-pressed": item === __props.activeView,
1827
- onClick: ($event) => handleView(item)
1828
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: item }, null, 8, ["name"])], 10, _hoisted_9);
1829
- }), 128))])) : (0, vue.createCommentVNode)("v-if", true)]),
1830
- errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_10, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2386
+ }, null, 8, ["store", "preview-options"])], 4)], 4)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2387
+ key: 1,
2388
+ ref_key: "runtimeRoot",
2389
+ ref: runtimeRoot,
2390
+ class: "docs-playground-runtime"
2391
+ }, [
2392
+ !__props.hideChrome ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3$1, [displayTitle.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2393
+ key: 0,
2394
+ ref_key: "titleEl",
2395
+ ref: titleEl,
2396
+ class: "docs-playground__title",
2397
+ id: titleId.value,
2398
+ tabindex: "-1",
2399
+ title: displayTitle.value
2400
+ }, [(0, vue.createElementVNode)("a", {
2401
+ class: "docs-playground__title-anchor",
2402
+ href: `#${titleId.value}`
2403
+ }, "#", 8, _hoisted_5$1), (0, vue.createElementVNode)("span", _hoisted_6, (0, vue.toDisplayString)(displayTitle.value), 1)], 8, _hoisted_4$1)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createVNode)(toolbar_default, {
2404
+ "copy-value": copyValue.value,
2405
+ viewport: __props.viewport,
2406
+ "viewport-options": __props.viewportOptions,
2407
+ "show-open-popup": true,
2408
+ onRefresh: handleInlineRefresh,
2409
+ onEdit: handleEditor,
2410
+ onOpenPopup: handleOpenPopup,
2411
+ onViewportChange: handleViewportChange
2412
+ }, null, 8, [
2413
+ "copy-value",
2414
+ "viewport",
2415
+ "viewport-options"
2416
+ ])])) : (0, vue.createCommentVNode)("v-if", true),
2417
+ errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_7, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
1831
2418
  (0, vue.createElementVNode)("section", {
1832
- class: "docs-playground__preview",
2419
+ class: (0, vue.normalizeClass)(["docs-playground__preview", { "is-expanded": previewExpanded.value }]),
1833
2420
  style: (0, vue.normalizeStyle)(previewStyle.value)
1834
- }, [(0, vue.createElementVNode)("div", _hoisted_11, [(0, vue.createElementVNode)("div", {
2421
+ }, [(0, vue.createElementVNode)("div", _hoisted_8, [(0, vue.createElementVNode)("div", {
1835
2422
  class: "docs-playground-runtime__viewport",
1836
2423
  style: (0, vue.normalizeStyle)(viewportStyle.value)
1837
2424
  }, [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_vue_repl.Sandbox), {
@@ -1842,8 +2429,17 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
1842
2429
  "auto-store-init": false,
1843
2430
  "clear-console": clearConsole,
1844
2431
  "preview-options": mergedPreviewOptions.value
1845
- }, null, 8, ["store", "preview-options"]))], 4)])], 4)
1846
- ]));
2432
+ }, null, 8, ["store", "preview-options"]))], 4)]), canExpandPreview.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2433
+ key: 0,
2434
+ type: "button",
2435
+ class: (0, vue.normalizeClass)(["docs-playground__expand", { "is-expanded": previewExpanded.value }]),
2436
+ "data-action": "expand-preview",
2437
+ "aria-expanded": previewExpanded.value,
2438
+ title: expandLabel.value,
2439
+ "aria-label": expandLabel.value,
2440
+ onClick: handleTogglePreviewExpand
2441
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "expand" })], 10, _hoisted_9)) : (0, vue.createCommentVNode)("v-if", true)], 6)
2442
+ ], 512))]);
1847
2443
  };
1848
2444
  }
1849
2445
  });
@@ -1853,6 +2449,13 @@ var _hoisted_1 = {
1853
2449
  key: 0,
1854
2450
  class: "docs-playground__error"
1855
2451
  };
2452
+ var _hoisted_2 = {
2453
+ key: 0,
2454
+ class: "docs-playground__header"
2455
+ };
2456
+ var _hoisted_3 = ["id", "title"];
2457
+ var _hoisted_4 = ["href"];
2458
+ var _hoisted_5 = { class: "docs-playground__title-text" };
1856
2459
  //#endregion
1857
2460
  //#region packages/playground/src/playground.vue
1858
2461
  var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -1870,6 +2473,9 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
1870
2473
  },
1871
2474
  options: { default: () => ({}) },
1872
2475
  previewInset: { default: 0 },
2476
+ expandable: { type: [Boolean, Number] },
2477
+ title: { default: "" },
2478
+ id: { default: "" },
1873
2479
  previewOptions: {},
1874
2480
  locale: {}
1875
2481
  },
@@ -1893,6 +2499,8 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
1893
2499
  const sourceFiles = (0, vue.ref)(initialFiles);
1894
2500
  const currentEntry = (0, vue.ref)(initialEntry);
1895
2501
  const fileActiveFilename = (0, vue.ref)(initialEntry);
2502
+ const runtimeRef = (0, vue.ref)(null);
2503
+ const titleEl = (0, vue.ref)(null);
1896
2504
  const normalizeViews = (views) => {
1897
2505
  const normalized = [];
1898
2506
  for (const view of views) {
@@ -1903,12 +2511,25 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
1903
2511
  return normalized.length ? normalized : ["runtime"];
1904
2512
  };
1905
2513
  const normalizedViews = (0, vue.computed)(() => normalizeViews(props.views));
2514
+ const hasRuntimeView = (0, vue.computed)(() => normalizedViews.value.includes("runtime"));
2515
+ const hasFilesView = (0, vue.computed)(() => normalizedViews.value.includes("files"));
1906
2516
  const activeView = (0, vue.ref)(normalizedViews.value[0]);
1907
2517
  const runtimeActivated = (0, vue.ref)(props.styleless || activeView.value === "runtime");
1908
2518
  const normalizedViewportOptions = (0, vue.computed)(() => normalizeViewportOptions(props.viewportOptions));
1909
2519
  const activeViewport = (0, vue.ref)(resolveInitialViewport(props.viewport, normalizedViewportOptions.value));
1910
2520
  const selectableViewportOptions = (0, vue.computed)(() => includeActiveViewport(normalizedViewportOptions.value, activeViewport.value));
1911
2521
  const error = (0, vue.ref)(props.entry && initialFiles[props.entry] === void 0 ? t("playground.validation.entryMissing", { filename: props.entry }) : "");
2522
+ const displayTitle = (0, vue.computed)(() => props.title.trim());
2523
+ const titleId = (0, vue.computed)(() => resolvePlaygroundTitleId(displayTitle.value, props.id, (candidate) => {
2524
+ if (typeof document === "undefined") return false;
2525
+ const existing = document.getElementById(candidate);
2526
+ return !!existing && existing !== titleEl.value;
2527
+ }));
2528
+ const copyValue = (0, vue.computed)(() => sourceFiles.value[currentEntry.value] || "");
2529
+ /** runtime / 双视图始终有顶栏;files-only 仅有标题时渲染。 */
2530
+ const showHeader = (0, vue.computed)(() => hasRuntimeView.value || !!displayTitle.value);
2531
+ const showToolbar = (0, vue.computed)(() => hasRuntimeView.value);
2532
+ const showRuntimeActions = (0, vue.computed)(() => activeView.value === "runtime");
1912
2533
  (0, vue.watch)(locale, () => {
1913
2534
  if (props.entry && sourceFiles.value[props.entry] === void 0) error.value = t("playground.validation.entryMissing", { filename: props.entry });
1914
2535
  });
@@ -1922,6 +2543,15 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
1922
2543
  activeViewport.value = cloneViewport(viewport);
1923
2544
  emit("update:viewport", cloneViewport(viewport));
1924
2545
  };
2546
+ const handleRefresh = () => {
2547
+ runtimeRef.value?.refresh();
2548
+ };
2549
+ const handleEdit = () => {
2550
+ runtimeRef.value?.edit();
2551
+ };
2552
+ const handleOpenPopup = () => {
2553
+ runtimeRef.value?.openPopup();
2554
+ };
1925
2555
  const handleFileActive = (filename) => {
1926
2556
  if (sourceFiles.value[filename] !== void 0) fileActiveFilename.value = filename;
1927
2557
  };
@@ -2003,7 +2633,10 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2003
2633
  options: __props.options,
2004
2634
  "preview-options": __props.previewOptions,
2005
2635
  "preview-inset": __props.previewInset,
2636
+ expandable: __props.expandable,
2006
2637
  styleless: true,
2638
+ title: __props.title,
2639
+ id: __props.id,
2007
2640
  viewport: activeViewport.value,
2008
2641
  "viewport-options": selectableViewportOptions.value,
2009
2642
  onFilesChange: handleFilesChange,
@@ -2015,27 +2648,68 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2015
2648
  "options",
2016
2649
  "preview-options",
2017
2650
  "preview-inset",
2651
+ "expandable",
2652
+ "title",
2653
+ "id",
2018
2654
  "viewport",
2019
2655
  "viewport-options"
2020
2656
  ])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2021
2657
  key: 1,
2022
2658
  class: (0, vue.normalizeClass)(["docs-playground", { "docs-playground--files": activeView.value === "files" }])
2023
- }, [
2024
- error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1, (0, vue.toDisplayString)(error.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2025
- runtimeActivated.value && normalizedViews.value.includes("runtime") ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(runtime_default), {
2659
+ }, [error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1, (0, vue.toDisplayString)(error.value), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
2660
+ showHeader.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2, [displayTitle.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2661
+ key: 0,
2662
+ ref_key: "titleEl",
2663
+ ref: titleEl,
2664
+ class: "docs-playground__title",
2665
+ id: titleId.value,
2666
+ tabindex: "-1",
2667
+ title: displayTitle.value
2668
+ }, [(0, vue.createElementVNode)("a", {
2669
+ class: "docs-playground__title-anchor",
2670
+ href: `#${titleId.value}`
2671
+ }, "#", 8, _hoisted_4), (0, vue.createElementVNode)("span", _hoisted_5, (0, vue.toDisplayString)(displayTitle.value), 1)], 8, _hoisted_3)) : (0, vue.createCommentVNode)("v-if", true), showToolbar.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(toolbar_default, {
2026
2672
  key: 1,
2673
+ "copy-value": copyValue.value,
2674
+ viewport: activeViewport.value,
2675
+ "viewport-options": selectableViewportOptions.value,
2676
+ views: normalizedViews.value,
2677
+ "active-view": activeView.value,
2678
+ "show-runtime-actions": showRuntimeActions.value,
2679
+ "show-open-popup": hasRuntimeView.value,
2680
+ onRefresh: handleRefresh,
2681
+ onEdit: handleEdit,
2682
+ onOpenPopup: handleOpenPopup,
2683
+ onViewportChange: handleViewport,
2684
+ onViewChange: handleView
2685
+ }, null, 8, [
2686
+ "copy-value",
2687
+ "viewport",
2688
+ "viewport-options",
2689
+ "views",
2690
+ "active-view",
2691
+ "show-runtime-actions",
2692
+ "show-open-popup"
2693
+ ])) : (0, vue.createCommentVNode)("v-if", true)])) : (0, vue.createCommentVNode)("v-if", true),
2694
+ runtimeActivated.value && hasRuntimeView.value ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(runtime_default), {
2695
+ key: 1,
2696
+ ref_key: "runtimeRef",
2697
+ ref: runtimeRef,
2027
2698
  files: sourceFiles.value,
2028
2699
  entry: currentEntry.value,
2029
2700
  options: __props.options,
2030
2701
  "preview-options": __props.previewOptions,
2031
2702
  "preview-inset": __props.previewInset,
2703
+ expandable: __props.expandable,
2704
+ "hide-chrome": true,
2705
+ title: __props.title,
2706
+ id: __props.id,
2032
2707
  "active-view": activeView.value,
2033
2708
  views: normalizedViews.value,
2034
2709
  viewport: activeViewport.value,
2035
2710
  "viewport-options": selectableViewportOptions.value,
2036
2711
  onFilesChange: handleFilesChange,
2037
2712
  onNavigate: _cache[1] || (_cache[1] = ($event) => emit("navigate", $event)),
2038
- onViewChange: handleView,
2039
2713
  onViewportChange: handleViewport
2040
2714
  }, null, 8, [
2041
2715
  "files",
@@ -2043,28 +2717,30 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2043
2717
  "options",
2044
2718
  "preview-options",
2045
2719
  "preview-inset",
2720
+ "expandable",
2721
+ "title",
2722
+ "id",
2046
2723
  "active-view",
2047
2724
  "views",
2048
2725
  "viewport",
2049
2726
  "viewport-options"
2050
- ])), [[vue.vShow, !error.value && activeView.value === "runtime"]]) : (0, vue.createCommentVNode)("v-if", true),
2051
- normalizedViews.value.includes("files") ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(files_preview_default), {
2727
+ ])), [[vue.vShow, activeView.value === "runtime"]]) : (0, vue.createCommentVNode)("v-if", true),
2728
+ hasFilesView.value ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(files_preview_default), {
2052
2729
  key: 2,
2053
2730
  files: sourceFiles.value,
2054
2731
  entry: currentEntry.value,
2055
2732
  "active-filename": fileActiveFilename.value,
2056
2733
  "active-view": activeView.value,
2057
2734
  views: normalizedViews.value,
2058
- onActiveChange: handleFileActive,
2059
- onViewChange: handleView
2735
+ onActiveChange: handleFileActive
2060
2736
  }, null, 8, [
2061
2737
  "files",
2062
2738
  "entry",
2063
2739
  "active-filename",
2064
2740
  "active-view",
2065
2741
  "views"
2066
- ])), [[vue.vShow, !error.value && activeView.value === "files"]]) : (0, vue.createCommentVNode)("v-if", true)
2067
- ], 2));
2742
+ ])), [[vue.vShow, activeView.value === "files"]]) : (0, vue.createCommentVNode)("v-if", true)
2743
+ ], 64))], 2));
2068
2744
  };
2069
2745
  }
2070
2746
  });
@@ -2091,8 +2767,10 @@ exports.registerVueHighlight = registerVueHighlight;
2091
2767
  exports.removePlaygroundImportMapOverride = removePlaygroundImportMapOverride;
2092
2768
  exports.removePlaygroundStyleOverride = removePlaygroundStyleOverride;
2093
2769
  exports.resolveHighlightLanguage = resolveHighlightLanguage;
2770
+ exports.resolvePlaygroundTitleId = resolvePlaygroundTitleId;
2094
2771
  exports.setPlaygroundImportMapOverrides = setPlaygroundImportMapOverrides;
2095
2772
  exports.setPlaygroundSiteModules = setPlaygroundSiteModules;
2096
2773
  exports.setPlaygroundSiteStyles = setPlaygroundSiteStyles;
2097
2774
  exports.setPlaygroundStyleOverrides = setPlaygroundStyleOverrides;
2775
+ exports.slugifyPlaygroundTitle = slugifyPlaygroundTitle;
2098
2776
  exports.vueHighlight = vueHighlight;