@deot/docs-playground 1.0.10 → 1.1.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 CHANGED
@@ -140,7 +140,7 @@ var PLAYGROUND_ICONS = {
140
140
  //#endregion
141
141
  //#region packages/playground/src/icon/icon.vue?vue&type=script&setup=true&lang.ts
142
142
  var _hoisted_1$7 = ["fill", "stroke"];
143
- var _hoisted_2$6 = ["d"];
143
+ var _hoisted_2$7 = ["d"];
144
144
  //#endregion
145
145
  //#region packages/playground/src/icon/icon.vue
146
146
  var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -171,16 +171,16 @@ var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
171
171
  "stroke-linecap": "round",
172
172
  "stroke-linejoin": "round",
173
173
  "aria-hidden": "true"
174
- }, [(0, vue.createElementVNode)("path", { d: icon.value.path }, null, 8, _hoisted_2$6)], 12, _hoisted_1$7);
174
+ }, [(0, vue.createElementVNode)("path", { d: icon.value.path }, null, 8, _hoisted_2$7)], 12, _hoisted_1$7);
175
175
  };
176
176
  }
177
177
  });
178
178
  //#endregion
179
179
  //#region packages/playground/src/core/code-preview/github.css?inline
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}";
180
+ var github_default = "pre code.hljs{display:block;padding:1em}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}";
181
181
  //#endregion
182
182
  //#region packages/playground/src/core/code-preview/code-preview.scss?inline
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}";
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__scroller{width:100%;height:100%;min-width:0;min-height:0;-webkit-box-flex:1;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto}.docs-code-preview__code{width:-webkit-max-content;width:-moz-max-content;width:max-content;margin:0}.docs-code-preview__code,.docs-code-preview__code code.hljs{min-width:100%;min-height:100%;overflow:visible;background:var(--docs-background-color-soft,var(--vc-background-color,#f7f8fa))}.docs-code-preview__code code.hljs{display:block;padding:12px 96px 12px 24px;-webkit-box-sizing:border-box;box-sizing:border-box}";
184
184
  //#endregion
185
185
  //#region packages/playground/src/core/code-preview/style.ts
186
186
  var CODE_PREVIEW_STYLE_ID = "docs-code-preview-style";
@@ -198,9 +198,9 @@ var ensureCodePreviewStyle = () => {
198
198
  //#endregion
199
199
  //#region packages/playground/src/core/code-preview/code-preview.vue?vue&type=script&setup=true&lang.ts
200
200
  var _hoisted_1$6 = { class: "docs-code-preview" };
201
- var _hoisted_2$5 = ["title"];
202
- var _hoisted_3$5 = { class: "docs-code-preview__code" };
203
- var _hoisted_4$5 = ["innerHTML"];
201
+ var _hoisted_2$6 = ["title"];
202
+ var _hoisted_3$6 = { class: "docs-code-preview__code" };
203
+ var _hoisted_4$6 = ["innerHTML"];
204
204
  //#endregion
205
205
  //#region packages/playground/src/core/code-preview/code-preview.vue
206
206
  var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -236,7 +236,7 @@ var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
236
236
  key: 0,
237
237
  class: "docs-code-preview__language",
238
238
  title: languageLabel.value
239
- }, (0, vue.toDisplayString)(languageLabel.value), 9, _hoisted_2$5)) : (0, vue.createCommentVNode)("v-if", true),
239
+ }, (0, vue.toDisplayString)(languageLabel.value), 9, _hoisted_2$6)) : (0, vue.createCommentVNode)("v-if", true),
240
240
  (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
241
241
  class: "docs-code-preview__copy",
242
242
  value: __props.code,
@@ -252,54 +252,32 @@ var code_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
252
252
  "title",
253
253
  "aria-label"
254
254
  ]),
255
- (0, vue.createCommentVNode)(" eslint-disable-next-line vue/no-v-html "),
256
- (0, vue.createElementVNode)("pre", _hoisted_3$5, [(0, vue.createElementVNode)("code", {
257
- class: "hljs",
258
- innerHTML: highlightedCode.value
259
- }, null, 8, _hoisted_4$5)])
255
+ (0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
256
+ class: "docs-code-preview__scroller",
257
+ "auto-resize": true,
258
+ native: false,
259
+ "show-bar": true,
260
+ height: "100%"
261
+ }, {
262
+ default: (0, vue.withCtx)(() => [(0, vue.createCommentVNode)(" eslint-disable-next-line vue/no-v-html "), (0, vue.createElementVNode)("pre", _hoisted_3$6, [(0, vue.createElementVNode)("code", {
263
+ class: "hljs",
264
+ innerHTML: highlightedCode.value
265
+ }, null, 8, _hoisted_4$6)])]),
266
+ _: 1
267
+ })
260
268
  ]);
261
269
  };
262
270
  }
263
271
  });
264
272
  //#endregion
265
- //#region packages/playground/src/utils.ts
266
- /**
267
- * 比较两组 playground 文件是否完全一致。
268
- * @param left 左侧文件表。
269
- * @param right 右侧文件表。
270
- * @returns 键集合与各文件内容是否都相同。
271
- */
272
- var filesEqual = (left, right) => {
273
- const leftKeys = Object.keys(left);
274
- const rightKeys = Object.keys(right);
275
- return leftKeys.length === rightKeys.length && leftKeys.every((key) => left[key] === right[key]);
276
- };
277
- /**
278
- * 视图切换条使用的文案键。
279
- * @param view 当前预览视图。
280
- * @returns locale 消息键。
281
- */
282
- var playgroundViewMessage = (view) => view === "runtime" ? "playground.runtime.preview" : "playground.runtime.files";
283
- //#endregion
284
273
  //#region packages/playground/src/core/files-preview/files-preview.vue?vue&type=script&setup=true&lang.ts
285
274
  var _hoisted_1$5 = { class: "docs-playground-files" };
286
- var _hoisted_2$4 = { class: "docs-playground-files__toolbar" };
287
- var _hoisted_3$4 = ["data-filename", "onClick"];
288
- var _hoisted_4$4 = {
275
+ var _hoisted_2$5 = { class: "docs-playground-files__tabs-row" };
276
+ var _hoisted_3$5 = ["data-filename", "onClick"];
277
+ var _hoisted_4$5 = {
289
278
  key: 0,
290
279
  class: "docs-playground-files__entry"
291
280
  };
292
- var _hoisted_5$4 = { class: "docs-playground-files__actions" };
293
- var _hoisted_6$3 = {
294
- key: 0,
295
- class: "docs-playground__views docs-playground__views--files"
296
- };
297
- var _hoisted_7$2 = [
298
- "title",
299
- "aria-label",
300
- "aria-pressed",
301
- "onClick"
302
- ];
303
281
  //#endregion
304
282
  //#region packages/playground/src/core/files-preview/files-preview.vue
305
283
  var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -311,28 +289,26 @@ var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
311
289
  activeView: {},
312
290
  activeFilename: {}
313
291
  },
314
- emits: ["active-change", "view-change"],
292
+ emits: ["active-change"],
315
293
  setup(__props, { emit: __emit }) {
316
294
  const props = __props;
317
295
  const { t } = (0, _deot_docs_locale.useLocale)();
318
- const getViewText = (view) => t(playgroundViewMessage(view));
319
296
  const emit = __emit;
320
297
  const filenames = (0, vue.computed)(() => Object.keys(props.files));
321
298
  const activeCode = (0, vue.computed)(() => props.files[props.activeFilename] || "");
322
299
  const handleActive = (filename) => {
323
300
  if (filename !== props.activeFilename) emit("active-change", filename);
324
301
  };
325
- const handleView = (view) => emit("view-change", view);
326
302
  return (_ctx, _cache) => {
327
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$5, [(0, vue.createElementVNode)("div", _hoisted_2$4, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
303
+ 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), {
328
304
  class: "docs-playground-files__scroller",
329
305
  "auto-resize": true,
330
306
  native: false,
331
307
  "show-bar": true,
332
- height: "44",
308
+ height: "40",
333
309
  "content-class": "docs-playground-files__tabs",
334
- "content-style": "height: 44px; overflow: hidden;",
335
- "wrapper-style": "height: 44px; overflow-x: auto; overflow-y: hidden;"
310
+ "content-style": "height: 40px; overflow: hidden;",
311
+ "wrapper-style": "height: 40px; overflow-x: auto; overflow-y: hidden;"
336
312
  }, {
337
313
  default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(filenames.value, (filename) => {
338
314
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
@@ -341,20 +317,10 @@ var files_preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
341
317
  class: (0, vue.normalizeClass)(["docs-playground-files__tab", { "is-active": filename === __props.activeFilename }]),
342
318
  "data-filename": filename,
343
319
  onClick: ($event) => handleActive(filename)
344
- }, [(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(filename), 1), filename === __props.entry ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("small", _hoisted_4$4, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.files.entry")), 1)) : (0, vue.createCommentVNode)("v-if", true)], 10, _hoisted_3$4);
320
+ }, [(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);
345
321
  }), 128))]),
346
322
  _: 1
347
- }), (0, vue.createElementVNode)("div", _hoisted_5$4, [__props.views.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$3, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.views, (item) => {
348
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
349
- key: item,
350
- type: "button",
351
- class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
352
- title: getViewText(item),
353
- "aria-label": getViewText(item),
354
- "aria-pressed": item === __props.activeView,
355
- onClick: ($event) => handleView(item)
356
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: item }, null, 8, ["name"])], 10, _hoisted_7$2);
357
- }), 128))])) : (0, vue.createCommentVNode)("v-if", true)])]), (0, vue.createVNode)((0, vue.unref)(code_preview_default), {
323
+ })]), (0, vue.createVNode)((0, vue.unref)(code_preview_default), {
358
324
  class: "docs-playground-files__body",
359
325
  code: activeCode.value,
360
326
  filename: __props.activeFilename,
@@ -486,21 +452,21 @@ var Drag = class {
486
452
  //#endregion
487
453
  //#region packages/playground/src/editor/editor.vue?vue&type=script&setup=true&lang.ts
488
454
  var _hoisted_1$4 = { class: "docs-playground-editor" };
489
- var _hoisted_2$3 = ["title"];
490
- var _hoisted_3$3 = { class: "docs-playground-editor__files" };
491
- var _hoisted_4$3 = [
455
+ var _hoisted_2$4 = ["title"];
456
+ var _hoisted_3$4 = { class: "docs-playground-editor__files" };
457
+ var _hoisted_4$4 = [
492
458
  "data-filename",
493
459
  "onClick",
494
460
  "onDblclick"
495
461
  ];
496
- var _hoisted_5$3 = ["onKeydown"];
497
- var _hoisted_6$2 = ["title"];
498
- var _hoisted_7$1 = ["title", "aria-label"];
499
- var _hoisted_8$1 = ["title", "aria-label"];
500
- var _hoisted_9 = { class: "docs-playground-editor__actions" };
501
- var _hoisted_10 = ["title"];
502
- var _hoisted_11 = ["title", "disabled"];
503
- var _hoisted_12 = {
462
+ var _hoisted_5$4 = ["onKeydown"];
463
+ var _hoisted_6$3 = ["title"];
464
+ var _hoisted_7$3 = ["title", "aria-label"];
465
+ var _hoisted_8$2 = ["title", "aria-label"];
466
+ var _hoisted_9$2 = { class: "docs-playground-editor__actions" };
467
+ var _hoisted_10$1 = ["title"];
468
+ var _hoisted_11$1 = ["title", "disabled"];
469
+ var _hoisted_12$1 = {
504
470
  key: 0,
505
471
  class: "docs-playground-editor__error"
506
472
  };
@@ -752,8 +718,8 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
752
718
  title: (0, vue.unref)(t)("playground.common.close"),
753
719
  style: { "cursor": "pointer" },
754
720
  onClick: handleHide
755
- }, "✕", 8, _hoisted_2$3)], 512),
756
- (0, vue.createElementVNode)("div", _hoisted_3$3, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
721
+ }, "✕", 8, _hoisted_2$4)], 512),
722
+ (0, vue.createElementVNode)("div", _hoisted_3$4, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
757
723
  ref_key: "scroller",
758
724
  ref: scroller,
759
725
  class: "docs-playground-editor__scroller",
@@ -781,11 +747,11 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
781
747
  onDblclick: _cache[2] || (_cache[2] = (0, vue.withModifiers)(() => {}, ["stop"])),
782
748
  onBlur: handleRenameSubmit,
783
749
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(handleRenameSubmit, ["prevent"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(handleRenameCancel, ["prevent"]), ["esc"])]
784
- }, null, 40, _hoisted_5$3)), [[vue.vModelText, filenameDraft.value]]) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
750
+ }, null, 40, _hoisted_5$4)), [[vue.vModelText, filenameDraft.value]]) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
785
751
  filename === currentEntry.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
786
752
  key: 0,
787
753
  title: (0, vue.unref)(t)("playground.editor.entry")
788
- }, "●", 8, _hoisted_6$2)) : (0, vue.createCommentVNode)("v-if", true),
754
+ }, "●", 8, _hoisted_6$3)) : (0, vue.createCommentVNode)("v-if", true),
789
755
  (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(filename), 1),
790
756
  filename === currentEntry.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
791
757
  key: 1,
@@ -794,7 +760,7 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
794
760
  title: (0, vue.unref)(t)("playground.editor.entryCannotDelete"),
795
761
  "aria-label": (0, vue.unref)(t)("playground.editor.entryCannotDelete"),
796
762
  disabled: ""
797
- }, null, 8, _hoisted_7$1)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Popconfirm), {
763
+ }, null, 8, _hoisted_7$3)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Popconfirm), {
798
764
  key: 2,
799
765
  class: "docs-playground-editor__delete",
800
766
  title: (0, vue.unref)(t)("playground.editor.confirmDelete", { filename }),
@@ -807,23 +773,23 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
807
773
  class: "docs-playground-editor__close",
808
774
  title: (0, vue.unref)(t)("playground.editor.deleteFile"),
809
775
  "aria-label": (0, vue.unref)(t)("playground.editor.confirmDelete", { filename })
810
- }, null, 8, _hoisted_8$1)]),
776
+ }, null, 8, _hoisted_8$2)]),
811
777
  _: 2
812
778
  }, 1032, ["title", "onOk"]))
813
- ], 64))], 42, _hoisted_4$3);
779
+ ], 64))], 42, _hoisted_4$4);
814
780
  }), 128))]),
815
781
  _: 1
816
- }, 512), (0, vue.createElementVNode)("div", _hoisted_9, [(0, vue.createElementVNode)("button", {
782
+ }, 512), (0, vue.createElementVNode)("div", _hoisted_9$2, [(0, vue.createElementVNode)("button", {
817
783
  type: "button",
818
784
  title: (0, vue.unref)(t)("playground.editor.createFile"),
819
785
  onClick: handleCreate
820
- }, "+", 8, _hoisted_10), (0, vue.createElementVNode)("button", {
786
+ }, "+", 8, _hoisted_10$1), (0, vue.createElementVNode)("button", {
821
787
  type: "button",
822
788
  title: (0, vue.unref)(t)("playground.editor.setEntry"),
823
789
  disabled: activeFilename.value === currentEntry.value,
824
790
  onClick: handleEntry
825
- }, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.editor.setEntry")), 9, _hoisted_11)])]),
826
- codeErrorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12, (0, vue.toDisplayString)(codeErrorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
791
+ }, (0, vue.toDisplayString)((0, vue.unref)(t)("playground.editor.setEntry")), 9, _hoisted_11$1)])]),
792
+ codeErrorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12$1, (0, vue.toDisplayString)(codeErrorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
827
793
  (0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
828
794
  ref_key: "editorScroller",
829
795
  ref: editorScroller,
@@ -850,19 +816,156 @@ var editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
850
816
  //#endregion
851
817
  //#region packages/playground/src/editor/index.ts
852
818
  var Editor = new _deot_vc.Portal(editor_default, { aliveRegExp: { className: /(PORTAL_TAG_DISABLE)/ } });
819
+ //#endregion
820
+ //#region packages/playground/src/utils.ts
821
+ /**
822
+ * 比较两组 playground 文件是否完全一致。
823
+ * @param left 左侧文件表。
824
+ * @param right 右侧文件表。
825
+ * @returns 键集合与各文件内容是否都相同。
826
+ */
827
+ var filesEqual = (left, right) => {
828
+ const leftKeys = Object.keys(left);
829
+ const rightKeys = Object.keys(right);
830
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => left[key] === right[key]);
831
+ };
832
+ /**
833
+ * 视图切换条使用的文案键。
834
+ * @param view 当前预览视图。
835
+ * @returns locale 消息键。
836
+ */
837
+ var playgroundViewMessage = (view) => view === "runtime" ? "playground.runtime.preview" : "playground.runtime.files";
838
+ /**
839
+ * 与 markdown-it-anchor 默认 `slugify` 对齐,用于标题锚点 id。
840
+ * @param title 标题文本。
841
+ * @returns 可用作元素 id / hash 的字符串;空标题返回空串。
842
+ */
843
+ var slugifyPlaygroundTitle = (title) => {
844
+ const normalized = String(title).trim().toLowerCase().replace(/\s+/g, "-");
845
+ return normalized ? encodeURIComponent(normalized) : "";
846
+ };
847
+ /**
848
+ * 解析标题锚点 id:显式 id 优先,否则从标题 slugify;必要时避开已占用 id。
849
+ * @param title 标题文本。
850
+ * @param explicitId 可选显式 id。
851
+ * @param isOccupied 返回该 id 是否已被其他元素占用。
852
+ * @returns 锚点 id;无标题时为空串。
853
+ */
854
+ var resolvePlaygroundTitleId = (title, explicitId = "", isOccupied) => {
855
+ if (!String(title).trim()) return "";
856
+ const base = String(explicitId).trim() || slugifyPlaygroundTitle(title);
857
+ if (!base) return "";
858
+ if (!isOccupied || !isOccupied(base)) return base;
859
+ let index = 1;
860
+ while (isOccupied(`${base}-${index}`)) index += 1;
861
+ return `${base}-${index}`;
862
+ };
863
+ //#endregion
864
+ //#region packages/playground/src/core/preview-scroll.ts
865
+ /** iframe 包装成功后挂在 `html` 上,供样式与 auto-height 识别。 */
866
+ var PREVIEW_SCROLL_HTML_CLASS = "docs-playground-preview-scroll";
867
+ /** Scroller 内容节点 class,auto-height 优先测量该节点。 */
868
+ var PREVIEW_SCROLL_CONTENT_CLASS = "docs-playground-preview-scroll__content";
869
+ /** 重评时先清掉 html class,避免 `await import` 期间仍套用 `height:100%`。 */
870
+ var PREVIEW_SCROLL_RESET_CODE = `document.documentElement.classList.remove("${PREVIEW_SCROLL_HTML_CLASS}");`;
871
+ /** 动态探测 `@deot/vc`;失败时为空对象,不阻断 DocsLink / 预览挂载。 */
872
+ var PREVIEW_SCROLLER_IMPORT_CODE = "const __docsVc=await import(\"@deot/vc\").catch(()=>({}))";
873
+ /**
874
+ * Vue 的 `mount` 闭包的是 `createApp(root)` 时的根组件,改 `app._component` 不会生效。
875
+ * 因此劫持 `app.mount`:等全部 useCode 注册完后,再 `createApp` 包一层 Scroller。
876
+ * 前导分号避免接在 `app.component(...)` 后被 ASI 解析成调用返回值。
877
+ */
878
+ var PREVIEW_SCROLLER_USE_CODE = [
879
+ ";(()=>{",
880
+ PREVIEW_SCROLL_RESET_CODE,
881
+ "const __DocsScroller=__docsVc&&__docsVc.Scroller;",
882
+ "if(!__DocsScroller)return;",
883
+ "const UserApp=app._component;",
884
+ "app.mount=(container,...rest)=>{",
885
+ "const next=_createApp({",
886
+ "name:\"DocsPreviewRoot\",",
887
+ "setup(){return()=>__docsH(__DocsScroller,{",
888
+ "height:\"100%\",",
889
+ "native:false,",
890
+ "autoResize:true,",
891
+ "showBar:true,",
892
+ `contentClass:"${PREVIEW_SCROLL_CONTENT_CLASS}"`,
893
+ "},{default:()=>__docsH(UserApp)})}",
894
+ "});",
895
+ "next.config.errorHandler=app.config.errorHandler;",
896
+ "if(app._context&&next._context){",
897
+ "if(app._context.components)Object.assign(next._context.components,app._context.components);",
898
+ "if(app._context.directives)Object.assign(next._context.directives,app._context.directives);",
899
+ "}",
900
+ "window.__app__=next;",
901
+ `document.documentElement.classList.add("${PREVIEW_SCROLL_HTML_CLASS}");`,
902
+ "return next.mount(container,...rest)",
903
+ "}",
904
+ "})()"
905
+ ].join("");
906
+ var PREVIEW_SCROLLER_STYLE = [
907
+ `html.${PREVIEW_SCROLL_HTML_CLASS},`,
908
+ `html.${PREVIEW_SCROLL_HTML_CLASS} body,`,
909
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app{`,
910
+ "height:100%;min-height:100%;margin:0;overflow:hidden",
911
+ "}",
912
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app>.vc-scroller,`,
913
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app>.vc-scroller>.vc-scroller__wrapper{`,
914
+ "height:100%;max-height:100%;box-sizing:border-box",
915
+ "}"
916
+ ].join("");
853
917
  var resolveSandboxContainer = (sandbox) => sandbox?.container ? (0, vue.unref)(sandbox.container) : null;
918
+ var resolveScrollContent = (iframeDocument) => {
919
+ if (!iframeDocument.documentElement.classList.contains("docs-playground-preview-scroll")) return null;
920
+ return iframeDocument.querySelector(`.${PREVIEW_SCROLL_CONTENT_CLASS}`);
921
+ };
854
922
  var useSandboxAutoHeight = (sandboxRef) => {
855
923
  const height = (0, vue.ref)(24);
856
924
  let container = null;
857
925
  let iframe = null;
858
926
  let containerObserver = null;
859
927
  let contentObserver = null;
928
+ let scrollMountObserver = null;
929
+ let observedScrollNode = null;
860
930
  let frameId = 0;
861
931
  const cancelMeasure = () => {
862
932
  if (!frameId) return;
863
933
  cancelAnimationFrame(frameId);
864
934
  frameId = 0;
865
935
  };
936
+ const measureDocumentContent = (iframeWindow, iframeDocument) => {
937
+ const { body, documentElement } = iframeDocument;
938
+ const bodyStyle = iframeWindow.getComputedStyle(body);
939
+ const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
940
+ const rootTop = documentElement.getBoundingClientRect().top;
941
+ const bodyRect = body.getBoundingClientRect();
942
+ const collapsedTop = Math.max(0, bodyRect.top - rootTop);
943
+ let childrenBottom = 0;
944
+ for (const child of body.children) {
945
+ const rect = child.getBoundingClientRect();
946
+ const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
947
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
948
+ }
949
+ const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
950
+ const overflowingHeight = scrollHeight > (iframe?.clientHeight || 0) + 1 ? scrollHeight : 0;
951
+ return Math.ceil(Math.max(body.offsetHeight + bodyMargins + collapsedTop, bodyRect.height + bodyMargins + collapsedTop, childrenBottom, overflowingHeight));
952
+ };
953
+ const measureScrollContent = (scrollContent) => {
954
+ const contentStyle = scrollContent.ownerDocument.defaultView?.getComputedStyle(scrollContent);
955
+ const margins = contentStyle ? (Number.parseFloat(contentStyle.marginTop) || 0) + (Number.parseFloat(contentStyle.marginBottom) || 0) : 0;
956
+ let childrenBottom = 0;
957
+ const rootTop = scrollContent.getBoundingClientRect().top;
958
+ for (const child of scrollContent.children) {
959
+ const rect = child.getBoundingClientRect();
960
+ const marginBottom = Number.parseFloat(scrollContent.ownerDocument.defaultView?.getComputedStyle(child).marginBottom || "0") || 0;
961
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
962
+ }
963
+ return Math.ceil(Math.max(scrollContent.scrollHeight + margins, scrollContent.offsetHeight + margins, childrenBottom + margins));
964
+ };
965
+ const isEmptyPreviewApp = (iframeDocument) => {
966
+ const app = iframeDocument.getElementById("app");
967
+ return !!app && app.childElementCount === 0;
968
+ };
866
969
  const measure = () => {
867
970
  frameId = 0;
868
971
  if (!iframe) return;
@@ -870,21 +973,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
870
973
  const iframeWindow = iframe.contentWindow;
871
974
  const iframeDocument = iframe.contentDocument;
872
975
  if (!iframeWindow || !iframeDocument?.body) return;
873
- const { body, documentElement } = iframeDocument;
874
- const bodyStyle = iframeWindow.getComputedStyle(body);
875
- const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
876
- const rootTop = documentElement.getBoundingClientRect().top;
877
- const bodyRect = body.getBoundingClientRect();
878
- const collapsedTop = Math.max(0, bodyRect.top - rootTop);
879
- let childrenBottom = 0;
880
- for (const child of body.children) {
881
- const rect = child.getBoundingClientRect();
882
- const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
883
- childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
884
- }
885
- const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
886
- const overflowingHeight = scrollHeight > iframe.clientHeight + 1 ? scrollHeight : 0;
887
- const contentHeight = Math.ceil(Math.max(body.offsetHeight + bodyMargins + collapsedTop, bodyRect.height + bodyMargins + collapsedTop, childrenBottom, overflowingHeight));
976
+ const scrollContent = resolveScrollContent(iframeDocument);
977
+ if (!scrollContent && isEmptyPreviewApp(iframeDocument)) return;
978
+ const contentHeight = scrollContent ? measureScrollContent(scrollContent) : measureDocumentContent(iframeWindow, iframeDocument);
888
979
  const nextHeight = Math.max(contentHeight, 24);
889
980
  if (height.value !== nextHeight) height.value = nextHeight;
890
981
  } catch {
@@ -898,6 +989,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
898
989
  const disconnectContent = () => {
899
990
  contentObserver?.disconnect();
900
991
  contentObserver = null;
992
+ scrollMountObserver?.disconnect();
993
+ scrollMountObserver = null;
994
+ observedScrollNode = null;
901
995
  cancelMeasure();
902
996
  };
903
997
  const observeContent = () => {
@@ -915,7 +1009,28 @@ var useSandboxAutoHeight = (sandboxRef) => {
915
1009
  observer.observe(iframeDocument.documentElement);
916
1010
  observer.observe(iframeDocument.body);
917
1011
  contentObserver = observer;
918
- scheduleMeasure();
1012
+ const attachScrollContent = (node) => {
1013
+ if (!node || !contentObserver) return false;
1014
+ if (observedScrollNode === node) return true;
1015
+ if (observedScrollNode) contentObserver.unobserve(observedScrollNode);
1016
+ observedScrollNode = node;
1017
+ contentObserver.observe(node);
1018
+ return true;
1019
+ };
1020
+ const syncScrollContent = () => {
1021
+ attachScrollContent(resolveScrollContent(iframeDocument));
1022
+ scheduleMeasure();
1023
+ };
1024
+ scrollMountObserver = new MutationObserver(syncScrollContent);
1025
+ scrollMountObserver.observe(iframeDocument.documentElement, {
1026
+ attributes: true,
1027
+ attributeFilter: ["class"]
1028
+ });
1029
+ scrollMountObserver.observe(iframeDocument.body, {
1030
+ childList: true,
1031
+ subtree: true
1032
+ });
1033
+ syncScrollContent();
919
1034
  } catch {
920
1035
  if (height.value !== 24) height.value = 24;
921
1036
  }
@@ -959,6 +1074,12 @@ var useSandboxAutoHeight = (sandboxRef) => {
959
1074
  });
960
1075
  return height;
961
1076
  };
1077
+ var resolvePreviewBoxHeight = (contentHeight, verticalInset = 0, borderWidth = 1) => {
1078
+ const content = Number.isFinite(contentHeight) ? Math.max(0, contentHeight) : 0;
1079
+ const inset = Number.isFinite(verticalInset) ? Math.max(0, verticalInset) : 0;
1080
+ const border = Number.isFinite(borderWidth) ? Math.max(0, borderWidth) : 1;
1081
+ return content + inset * 2 + border * 2;
1082
+ };
962
1083
  var CLIPPING_OVERFLOW = /* @__PURE__ */ new Set([
963
1084
  "auto",
964
1085
  "scroll",
@@ -972,14 +1093,14 @@ var SCROLLABLE_OVERFLOW = /* @__PURE__ */ new Set([
972
1093
  ]);
973
1094
  var getWindowInnerHeight = () => typeof window === "undefined" ? 0 : window.innerHeight;
974
1095
  var getPlaygroundHost = (el) => el?.closest(".docs-playground") ?? el;
975
- var isPlaygroundExpandEnabled = (expand) => {
976
- if (expand === true) return true;
977
- return typeof expand === "number" && Number.isFinite(expand) && expand > 0;
1096
+ var isPlaygroundExpandable = (value) => {
1097
+ if (value === true) return true;
1098
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
978
1099
  };
979
1100
  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)));
980
- var resolveExpandedPreviewHeight = (expand, availableHeight) => {
981
- if (expand === true) return Math.max(24, Math.round(Number.isFinite(availableHeight) ? availableHeight : 0));
982
- return Math.max(24, Math.round(expand));
1101
+ var resolveExpandedPreviewHeight = (expandable, availableHeight) => {
1102
+ if (expandable === true) return Math.max(24, Math.round(Number.isFinite(availableHeight) ? availableHeight : 0));
1103
+ return Math.max(24, Math.round(expandable));
983
1104
  };
984
1105
  var getVisibleViewportRect = (el) => {
985
1106
  const viewportHeight = getWindowInnerHeight();
@@ -1127,6 +1248,13 @@ var formatViewportLabel = (viewport, autoLabel = "Auto") => {
1127
1248
  if (viewport === "auto") return autoLabel;
1128
1249
  return Array.isArray(viewport) ? `${viewport[0]} × ${viewport[1]}px` : `${viewport}px`;
1129
1250
  };
1251
+ var resolvePopupScreenInset = (gap = 40) => {
1252
+ const safeGap = Number.isFinite(gap) ? Math.max(0, gap) : 40;
1253
+ return {
1254
+ x: safeGap * 2,
1255
+ y: safeGap
1256
+ };
1257
+ };
1130
1258
  var getWindowInnerSize = () => {
1131
1259
  if (typeof window === "undefined") return {
1132
1260
  width: 0,
@@ -1138,9 +1266,9 @@ var getWindowInnerSize = () => {
1138
1266
  };
1139
1267
  };
1140
1268
  var resolvePopupRequestedSize = (viewport, screen = getWindowInnerSize(), gap = 40) => {
1141
- const safeGap = Number.isFinite(gap) ? Math.max(0, gap) : 40;
1142
- const maxWidth = Math.max(0, Math.round((Number.isFinite(screen.width) ? screen.width : 0) - safeGap));
1143
- const maxHeight = Math.max(0, Math.round((Number.isFinite(screen.height) ? screen.height : 0) - safeGap));
1269
+ const inset = resolvePopupScreenInset(gap);
1270
+ const maxWidth = Math.max(0, Math.round((Number.isFinite(screen.width) ? screen.width : 0) - inset.x));
1271
+ const maxHeight = Math.max(0, Math.round((Number.isFinite(screen.height) ? screen.height : 0) - inset.y));
1144
1272
  const viewportWidth = getViewportWidth(viewport);
1145
1273
  const viewportHeight = getViewportHeight(viewport);
1146
1274
  return {
@@ -1151,11 +1279,11 @@ var resolvePopupRequestedSize = (viewport, screen = getWindowInnerSize(), gap =
1151
1279
  };
1152
1280
  };
1153
1281
  /**
1154
- * 壳层封顶到 `screen - gap`。未设 viewport 边时,画布铺满工具栏以下区域;
1155
- * 请求尺寸大于可用区域时由 Scroller 处理溢出。
1282
+ * 壳层封顶到 `screen.width - gap * 2`、`screen.height - gap`。
1283
+ * 未设 viewport 边时,画布铺满工具栏以下区域;请求尺寸大于可用区域时由 Scroller 处理溢出。
1156
1284
  * @param viewport 当前 playground 视口配置。
1157
- * @param screen 屏幕可用宽高,默认取 `window.inner*`.
1158
- * @param gap 相对屏幕边缘的留白。
1285
+ * @param screen 屏幕可用宽高,默认取 `window.inner*`。
1286
+ * @param gap 相对屏幕上下边缘的留白;左右为该值的 2 倍。
1159
1287
  * @returns 壳层、内容区与画布尺寸,以及是否需要滚动。
1160
1288
  */
1161
1289
  var resolvePopupLayout = (viewport, screen = getWindowInnerSize(), gap = 40) => {
@@ -1270,23 +1398,56 @@ var useSandboxTheme = (sandboxRef) => {
1270
1398
  //#endregion
1271
1399
  //#region packages/playground/src/core/runtime/toolbar.vue?vue&type=script&setup=true&lang.ts
1272
1400
  var _hoisted_1$3 = { class: "docs-playground__tools" };
1273
- var _hoisted_2$2 = ["title", "aria-label"];
1274
- var _hoisted_3$2 = [
1401
+ var _hoisted_2$3 = {
1402
+ key: 0,
1403
+ class: "docs-playground__tool-group"
1404
+ };
1405
+ var _hoisted_3$3 = [
1275
1406
  "title",
1276
1407
  "aria-label",
1277
1408
  "aria-expanded"
1278
1409
  ];
1279
- var _hoisted_4$2 = ["title", "aria-label"];
1280
- var _hoisted_5$2 = ["title", "aria-label"];
1281
- var _hoisted_6$1 = ["title", "aria-label"];
1410
+ var _hoisted_4$3 = {
1411
+ key: 1,
1412
+ class: "docs-playground__tool-divider",
1413
+ "aria-hidden": "true"
1414
+ };
1415
+ var _hoisted_5$3 = ["title", "aria-label"];
1416
+ var _hoisted_6$2 = ["title", "aria-label"];
1417
+ var _hoisted_7$2 = ["title", "aria-label"];
1418
+ var _hoisted_8$1 = {
1419
+ key: 3,
1420
+ class: "docs-playground__tool-divider",
1421
+ "aria-hidden": "true"
1422
+ };
1423
+ var _hoisted_9$1 = ["aria-label"];
1424
+ var _hoisted_10 = [
1425
+ "title",
1426
+ "aria-label",
1427
+ "aria-selected",
1428
+ "aria-pressed",
1429
+ "onClick"
1430
+ ];
1431
+ var _hoisted_11 = {
1432
+ key: 2,
1433
+ class: "docs-playground__tool-divider",
1434
+ "aria-hidden": "true"
1435
+ };
1436
+ var _hoisted_12 = ["title", "aria-label"];
1282
1437
  //#endregion
1283
1438
  //#region packages/playground/src/core/runtime/toolbar.vue
1284
1439
  var toolbar_default = /* @__PURE__ */ (0, vue.defineComponent)({
1285
1440
  __name: "toolbar",
1286
1441
  props: {
1287
- copyValue: {},
1442
+ copyValue: { default: "" },
1288
1443
  viewport: { default: "auto" },
1289
1444
  viewportOptions: { default: () => ["auto", 375] },
1445
+ views: { default: () => [] },
1446
+ activeView: { default: "runtime" },
1447
+ showRuntimeActions: {
1448
+ type: Boolean,
1449
+ default: true
1450
+ },
1290
1451
  showOpenPopup: {
1291
1452
  type: Boolean,
1292
1453
  default: false
@@ -1301,14 +1462,20 @@ var toolbar_default = /* @__PURE__ */ (0, vue.defineComponent)({
1301
1462
  "edit",
1302
1463
  "open-popup",
1303
1464
  "close-popup",
1304
- "viewport-change"
1465
+ "viewport-change",
1466
+ "view-change"
1305
1467
  ],
1306
1468
  setup(__props, { emit: __emit }) {
1307
1469
  const props = __props;
1308
1470
  const emit = __emit;
1309
1471
  const { t } = (0, _deot_docs_locale.useLocale)();
1472
+ const VIEW_ORDER = ["runtime", "files"];
1310
1473
  const viewportMenuVisible = (0, vue.ref)(false);
1474
+ const orderedViews = (0, vue.computed)(() => VIEW_ORDER.filter((view) => props.views.includes(view)));
1475
+ const showViews = (0, vue.computed)(() => orderedViews.value.length > 1);
1311
1476
  const viewportLabel = (0, vue.computed)(() => formatViewportLabel(props.viewport, t("playground.runtime.auto")));
1477
+ const getViewAriaLabel = (view) => t(playgroundViewMessage(view));
1478
+ const getViewLabel = (view) => view === "runtime" ? t("playground.runtime.previewLabel") : t("playground.runtime.codeLabel");
1312
1479
  const handleViewport = (index) => {
1313
1480
  const viewport = props.viewportOptions[index];
1314
1481
  if (viewport && !viewportEquals(viewport, props.viewport)) emit("viewport-change", viewport);
@@ -1316,95 +1483,120 @@ var toolbar_default = /* @__PURE__ */ (0, vue.defineComponent)({
1316
1483
  (0, vue.watch)(() => props.viewportOptions.length, (length) => {
1317
1484
  if (length <= 1) viewportMenuVisible.value = false;
1318
1485
  });
1486
+ (0, vue.watch)(() => props.showRuntimeActions, (enabled) => {
1487
+ if (!enabled) viewportMenuVisible.value = false;
1488
+ });
1319
1489
  return (_ctx, _cache) => {
1320
1490
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$3, [
1321
- (0, vue.createElementVNode)("button", {
1322
- type: "button",
1323
- class: "docs-playground__tool docs-playground__refresh",
1324
- "data-action": "refresh",
1325
- title: (0, vue.unref)(t)("playground.runtime.refresh"),
1326
- "aria-label": (0, vue.unref)(t)("playground.runtime.refresh"),
1327
- onClick: _cache[0] || (_cache[0] = ($event) => emit("refresh"))
1328
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "refresh" })], 8, _hoisted_2$2),
1329
- __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Dropdown), {
1330
- key: 0,
1331
- modelValue: viewportMenuVisible.value,
1332
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => viewportMenuVisible.value = $event),
1333
- class: "docs-playground__viewport-menu",
1334
- portal: true,
1335
- trigger: "click",
1336
- placement: "bottom-right"
1337
- }, {
1338
- content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
1339
- class: "docs-playground__viewport-options",
1340
- role: "menu",
1341
- "aria-label": (0, vue.unref)(t)("playground.runtime.viewportMenu")
1491
+ __props.showRuntimeActions ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [
1492
+ __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$3, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Dropdown), {
1493
+ modelValue: viewportMenuVisible.value,
1494
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => viewportMenuVisible.value = $event),
1495
+ class: "docs-playground__viewport-menu",
1496
+ portal: true,
1497
+ trigger: "click",
1498
+ placement: "bottom-right"
1342
1499
  }, {
1343
- default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.viewportOptions, (item, index) => {
1344
- return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
1345
- key: (0, vue.unref)(getViewportKey)(item),
1346
- class: "docs-playground__viewport-option",
1347
- value: index,
1348
- selected: (0, vue.unref)(viewportEquals)(item, __props.viewport),
1349
- role: "menuitemradio",
1350
- "aria-checked": (0, vue.unref)(viewportEquals)(item, __props.viewport),
1351
- onClick: ($event) => handleViewport(index)
1352
- }, {
1353
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(formatViewportLabel)(item, (0, vue.unref)(t)("playground.runtime.auto"))), 1)]),
1354
- _: 2
1355
- }, 1032, [
1356
- "value",
1357
- "selected",
1358
- "aria-checked",
1359
- "onClick"
1360
- ]);
1361
- }), 128))]),
1500
+ content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
1501
+ class: "docs-playground__viewport-options",
1502
+ role: "menu",
1503
+ "aria-label": (0, vue.unref)(t)("playground.runtime.viewportMenu")
1504
+ }, {
1505
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.viewportOptions, (item, index) => {
1506
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
1507
+ key: (0, vue.unref)(getViewportKey)(item),
1508
+ class: "docs-playground__viewport-option",
1509
+ value: index,
1510
+ selected: (0, vue.unref)(viewportEquals)(item, __props.viewport),
1511
+ role: "menuitemradio",
1512
+ "aria-checked": (0, vue.unref)(viewportEquals)(item, __props.viewport),
1513
+ onClick: ($event) => handleViewport(index)
1514
+ }, {
1515
+ default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(formatViewportLabel)(item, (0, vue.unref)(t)("playground.runtime.auto"))), 1)]),
1516
+ _: 2
1517
+ }, 1032, [
1518
+ "value",
1519
+ "selected",
1520
+ "aria-checked",
1521
+ "onClick"
1522
+ ]);
1523
+ }), 128))]),
1524
+ _: 1
1525
+ }, 8, ["aria-label"])]),
1526
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
1527
+ type: "button",
1528
+ class: (0, vue.normalizeClass)(["docs-playground__tool docs-playground__viewport-trigger", { "is-active": __props.viewport !== "auto" }]),
1529
+ title: (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1530
+ "aria-label": (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1531
+ "aria-expanded": viewportMenuVisible.value,
1532
+ "aria-haspopup": "menu"
1533
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "viewport" })], 10, _hoisted_3$3)]),
1362
1534
  _: 1
1363
- }, 8, ["aria-label"])]),
1364
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
1535
+ }, 8, ["modelValue"])])) : (0, vue.createCommentVNode)("v-if", true),
1536
+ __props.viewportOptions.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4$3)) : (0, vue.createCommentVNode)("v-if", true),
1537
+ (0, vue.createElementVNode)("button", {
1365
1538
  type: "button",
1366
- class: (0, vue.normalizeClass)(["docs-playground__tool docs-playground__viewport-trigger", { "is-active": __props.viewport !== "auto" }]),
1367
- title: (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1368
- "aria-label": (0, vue.unref)(t)("playground.runtime.viewport", { value: viewportLabel.value }),
1369
- "aria-expanded": viewportMenuVisible.value,
1370
- "aria-haspopup": "menu"
1371
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "viewport" })], 10, _hoisted_3$2)]),
1372
- _: 1
1373
- }, 8, ["modelValue"])) : (0, vue.createCommentVNode)("v-if", true),
1374
- (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
1375
- class: "docs-playground__tool",
1376
- value: __props.copyValue,
1377
- tag: "button",
1378
- type: "button",
1379
- title: (0, vue.unref)(t)("playground.common.copy"),
1380
- "aria-label": (0, vue.unref)(t)("playground.common.copy")
1381
- }, {
1382
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "copy" })]),
1383
- _: 1
1384
- }, 8, [
1385
- "value",
1386
- "title",
1387
- "aria-label"
1388
- ]),
1389
- (0, vue.createElementVNode)("button", {
1390
- type: "button",
1391
- class: "docs-playground__tool docs-playground__editor",
1392
- "data-action": "edit",
1393
- title: (0, vue.unref)(t)("playground.runtime.editFiles"),
1394
- "aria-label": (0, vue.unref)(t)("playground.runtime.editFiles"),
1395
- onClick: _cache[2] || (_cache[2] = ($event) => emit("edit"))
1396
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_4$2),
1397
- __props.showOpenPopup ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1539
+ class: "docs-playground__tool docs-playground__refresh",
1540
+ "data-action": "refresh",
1541
+ title: (0, vue.unref)(t)("playground.runtime.refresh"),
1542
+ "aria-label": (0, vue.unref)(t)("playground.runtime.refresh"),
1543
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("refresh"))
1544
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "refresh" })], 8, _hoisted_5$3),
1545
+ (0, vue.createElementVNode)("button", {
1546
+ type: "button",
1547
+ class: "docs-playground__tool docs-playground__editor",
1548
+ "data-action": "edit",
1549
+ title: (0, vue.unref)(t)("playground.runtime.editFiles"),
1550
+ "aria-label": (0, vue.unref)(t)("playground.runtime.editFiles"),
1551
+ onClick: _cache[2] || (_cache[2] = ($event) => emit("edit"))
1552
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_6$2),
1553
+ __props.showOpenPopup ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1554
+ key: 2,
1555
+ type: "button",
1556
+ class: "docs-playground__tool docs-playground__popup",
1557
+ "data-action": "open-popup",
1558
+ title: (0, vue.unref)(t)("playground.runtime.openPopup"),
1559
+ "aria-label": (0, vue.unref)(t)("playground.runtime.openPopup"),
1560
+ onClick: _cache[3] || (_cache[3] = ($event) => emit("open-popup"))
1561
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "popup" })], 8, _hoisted_7$2)) : (0, vue.createCommentVNode)("v-if", true),
1562
+ (0, vue.createVNode)((0, vue.unref)(_deot_vc.Clipboard), {
1563
+ class: "docs-playground__tool",
1564
+ value: __props.copyValue,
1565
+ tag: "button",
1566
+ type: "button",
1567
+ title: (0, vue.unref)(t)("playground.common.copy"),
1568
+ "aria-label": (0, vue.unref)(t)("playground.common.copy")
1569
+ }, {
1570
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "copy" })]),
1571
+ _: 1
1572
+ }, 8, [
1573
+ "value",
1574
+ "title",
1575
+ "aria-label"
1576
+ ]),
1577
+ showViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$1)) : (0, vue.createCommentVNode)("v-if", true)
1578
+ ], 64)) : (0, vue.createCommentVNode)("v-if", true),
1579
+ showViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1398
1580
  key: 1,
1399
- type: "button",
1400
- class: "docs-playground__tool docs-playground__popup",
1401
- "data-action": "open-popup",
1402
- title: (0, vue.unref)(t)("playground.runtime.openPopup"),
1403
- "aria-label": (0, vue.unref)(t)("playground.runtime.openPopup"),
1404
- onClick: _cache[3] || (_cache[3] = ($event) => emit("open-popup"))
1405
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "popup" })], 8, _hoisted_5$2)) : (0, vue.createCommentVNode)("v-if", true),
1581
+ class: "docs-playground__views",
1582
+ role: "tablist",
1583
+ "aria-label": (0, vue.unref)(t)("playground.runtime.views")
1584
+ }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(orderedViews.value, (item) => {
1585
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1586
+ key: item,
1587
+ type: "button",
1588
+ class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
1589
+ role: "tab",
1590
+ title: getViewAriaLabel(item),
1591
+ "aria-label": getViewAriaLabel(item),
1592
+ "aria-selected": item === __props.activeView,
1593
+ "aria-pressed": item === __props.activeView,
1594
+ onClick: ($event) => emit("view-change", item)
1595
+ }, (0, vue.toDisplayString)(getViewLabel(item)), 11, _hoisted_10);
1596
+ }), 128))], 8, _hoisted_9$1)) : (0, vue.createCommentVNode)("v-if", true),
1597
+ __props.showClose && (__props.showRuntimeActions || showViews.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_11)) : (0, vue.createCommentVNode)("v-if", true),
1406
1598
  __props.showClose ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1407
- key: 2,
1599
+ key: 3,
1408
1600
  type: "button",
1409
1601
  class: "docs-playground__tool docs-playground__popup-close",
1410
1602
  "data-action": "close-popup",
@@ -1414,7 +1606,7 @@ var toolbar_default = /* @__PURE__ */ (0, vue.defineComponent)({
1414
1606
  }, [..._cache[5] || (_cache[5] = [(0, vue.createElementVNode)("span", {
1415
1607
  class: "docs-playground__popup-close-mark",
1416
1608
  "aria-hidden": "true"
1417
- }, "✕", -1)])], 8, _hoisted_6$1)) : (0, vue.createCommentVNode)("v-if", true)
1609
+ }, "✕", -1)])], 8, _hoisted_12)) : (0, vue.createCommentVNode)("v-if", true)
1418
1610
  ]);
1419
1611
  };
1420
1612
  }
@@ -1757,6 +1949,7 @@ var bindPlaygroundScss = (store) => {
1757
1949
  //#region packages/playground/src/core/store.ts
1758
1950
  var DOCS_LINK_IMPORT_CODE = "import { h as __docsH } from \"vue\"";
1759
1951
  var DOCS_LINK_USE_CODE = [
1952
+ PREVIEW_SCROLL_RESET_CODE,
1760
1953
  "app.component(\"DocsLink\",{",
1761
1954
  "props:{to:{type:String,default:\"\"}},",
1762
1955
  "setup(props,{slots}){return()=>__docsH(\"a\",{",
@@ -1773,21 +1966,27 @@ var escapeHtmlAttr = (value) => value.replace(/&/gu, "&amp;").replace(/"/gu, "&q
1773
1966
  * `--vc-background-color-light` 不同,否则会出现色带割裂。
1774
1967
  */
1775
1968
  var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-light, var(--docs-background-color, #fff))";
1776
- var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL) => {
1969
+ var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL, options = {}) => {
1970
+ const previewScroller = options.previewScroller === true;
1971
+ const hrefs = Object.values({
1972
+ ...createBuiltinStyles(cdnURL),
1973
+ ...getPlaygroundSiteStyles(),
1974
+ ...getPlaygroundStyleOverrides()
1975
+ });
1976
+ const styleBlocks = [
1977
+ `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1978
+ "body{color:var(--vc-foreground-color,#080f20)}",
1979
+ ...previewScroller ? [PREVIEW_SCROLLER_STYLE] : []
1980
+ ];
1777
1981
  return {
1778
1982
  showRuntimeError: false,
1779
1983
  showRuntimeWarning: false,
1780
1984
  headHTML: [
1781
1985
  SANDBOX_RUNTIME_ERROR_CAPTURE_HTML,
1782
1986
  "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
1783
- ...Object.values({
1784
- ...createBuiltinStyles(cdnURL),
1785
- ...getPlaygroundSiteStyles(),
1786
- ...getPlaygroundStyleOverrides()
1787
- }).map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1987
+ ...hrefs.map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1788
1988
  "<style>",
1789
- `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1790
- "body{color:var(--vc-foreground-color,#080f20)}",
1989
+ ...styleBlocks,
1791
1990
  "</style>"
1792
1991
  ].join("\n"),
1793
1992
  customCode: {
@@ -1835,14 +2034,16 @@ var createRuntimeStore = (files, entry, options) => {
1835
2034
  //#endregion
1836
2035
  //#region packages/playground/src/core/runtime/alone/wrapper.vue?vue&type=script&setup=true&lang.ts
1837
2036
  var _hoisted_1$2 = ["aria-label"];
1838
- var _hoisted_2$1 = { class: "docs-playground-popup__frame" };
1839
- var _hoisted_3$1 = { class: "docs-playground-popup__header" };
1840
- var _hoisted_4$1 = {
2037
+ var _hoisted_2$2 = { class: "docs-playground-popup__frame" };
2038
+ var _hoisted_3$2 = { class: "docs-playground-popup__header" };
2039
+ var _hoisted_4$2 = ["title"];
2040
+ var _hoisted_5$2 = { class: "docs-playground__title-text" };
2041
+ var _hoisted_6$1 = {
1841
2042
  key: 0,
1842
2043
  class: "docs-playground__runtime-error",
1843
2044
  role: "alert"
1844
2045
  };
1845
- var _hoisted_5$1 = { class: "docs-playground-popup__body" };
2046
+ var _hoisted_7$1 = { class: "docs-playground-popup__body" };
1846
2047
  //#endregion
1847
2048
  //#region packages/playground/src/core/runtime/alone/wrapper.vue
1848
2049
  var wrapper_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -1850,6 +2051,7 @@ var wrapper_default = /* @__PURE__ */ (0, vue.defineComponent)({
1850
2051
  props: {
1851
2052
  store: {},
1852
2053
  copyValue: {},
2054
+ title: { default: "" },
1853
2055
  viewport: { default: "auto" },
1854
2056
  viewportOptions: { default: () => ["auto", 375] },
1855
2057
  previewOptions: {},
@@ -1950,8 +2152,12 @@ var wrapper_default = /* @__PURE__ */ (0, vue.defineComponent)({
1950
2152
  }), (0, vue.createElementVNode)("div", {
1951
2153
  class: "docs-playground-popup__shell",
1952
2154
  style: (0, vue.normalizeStyle)(shellStyle.value)
1953
- }, [(0, vue.createElementVNode)("div", _hoisted_2$1, [
1954
- (0, vue.createElementVNode)("div", _hoisted_3$1, [(0, vue.createVNode)(toolbar_default, {
2155
+ }, [(0, vue.createElementVNode)("div", _hoisted_2$2, [
2156
+ (0, vue.createElementVNode)("div", _hoisted_3$2, [__props.title.trim() ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2157
+ key: 0,
2158
+ class: "docs-playground__title",
2159
+ title: __props.title.trim()
2160
+ }, [(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, {
1955
2161
  "copy-value": __props.copyValue,
1956
2162
  viewport: currentViewport.value,
1957
2163
  "viewport-options": __props.viewportOptions,
@@ -1965,8 +2171,8 @@ var wrapper_default = /* @__PURE__ */ (0, vue.defineComponent)({
1965
2171
  "viewport",
1966
2172
  "viewport-options"
1967
2173
  ])]),
1968
- errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_4$1, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
1969
- (0, vue.createElementVNode)("div", _hoisted_5$1, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
2174
+ errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_6$1, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2175
+ (0, vue.createElementVNode)("div", _hoisted_7$1, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
1970
2176
  class: "docs-playground-popup__scroller",
1971
2177
  "content-class": "docs-playground-popup__scroller-content",
1972
2178
  "auto-resize": true,
@@ -2006,29 +2212,25 @@ var Alone = new _deot_vc.Portal(wrapper_default, {
2006
2212
  //#endregion
2007
2213
  //#region packages/playground/src/core/runtime/runtime.vue?vue&type=script&setup=true&lang.ts
2008
2214
  var _hoisted_1$1 = { class: "docs-playground-runtime-host" };
2009
- var _hoisted_2 = {
2215
+ var _hoisted_2$1 = {
2010
2216
  key: 0,
2011
2217
  class: "docs-playground__runtime-error",
2012
2218
  role: "alert"
2013
2219
  };
2014
- var _hoisted_3 = { class: "docs-playground__header" };
2015
- var _hoisted_4 = {
2220
+ var _hoisted_3$1 = {
2016
2221
  key: 0,
2017
- class: "docs-playground__views"
2222
+ class: "docs-playground__header"
2018
2223
  };
2019
- var _hoisted_5 = [
2020
- "title",
2021
- "aria-label",
2022
- "aria-pressed",
2023
- "onClick"
2024
- ];
2025
- var _hoisted_6 = {
2026
- key: 0,
2224
+ var _hoisted_4$1 = ["id", "title"];
2225
+ var _hoisted_5$1 = ["href"];
2226
+ var _hoisted_6 = { class: "docs-playground__title-text" };
2227
+ var _hoisted_7 = {
2228
+ key: 1,
2027
2229
  class: "docs-playground__runtime-error",
2028
2230
  role: "alert"
2029
2231
  };
2030
- var _hoisted_7 = { class: "docs-playground-runtime__viewport-stage" };
2031
- var _hoisted_8 = [
2232
+ var _hoisted_8 = { class: "docs-playground-runtime__viewport-stage" };
2233
+ var _hoisted_9 = [
2032
2234
  "aria-expanded",
2033
2235
  "title",
2034
2236
  "aria-label"
@@ -2045,11 +2247,21 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2045
2247
  options: {},
2046
2248
  previewInset: { default: 10 },
2047
2249
  previewOptions: {},
2250
+ previewScroller: {
2251
+ type: Boolean,
2252
+ default: false
2253
+ },
2048
2254
  styleless: {
2049
2255
  type: Boolean,
2050
2256
  default: false
2051
2257
  },
2052
- expand: { type: [Boolean, Number] },
2258
+ hideChrome: {
2259
+ type: Boolean,
2260
+ default: false
2261
+ },
2262
+ expandable: { type: [Boolean, Number] },
2263
+ title: { default: "" },
2264
+ id: { default: "" },
2053
2265
  viewport: { default: "auto" },
2054
2266
  viewportOptions: { default: () => ["auto", 375] }
2055
2267
  },
@@ -2059,14 +2271,20 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2059
2271
  "viewport-change",
2060
2272
  "navigate"
2061
2273
  ],
2062
- setup(__props, { emit: __emit }) {
2274
+ setup(__props, { expose: __expose, emit: __emit }) {
2063
2275
  const props = __props;
2064
2276
  const { locale, t } = (0, _deot_docs_locale.useLocale)();
2065
- const getViewText = (view) => t(playgroundViewMessage(view));
2277
+ const displayTitle = (0, vue.computed)(() => props.title.trim());
2278
+ const titleEl = (0, vue.ref)(null);
2279
+ const titleId = (0, vue.computed)(() => resolvePlaygroundTitleId(displayTitle.value, props.id, (candidate) => {
2280
+ if (typeof document === "undefined") return false;
2281
+ const existing = document.getElementById(candidate);
2282
+ return !!existing && existing !== titleEl.value;
2283
+ }));
2066
2284
  const emit = __emit;
2067
2285
  const joinCode = (...values) => values.filter(Boolean).join("\n");
2068
2286
  const mergePreviewOptions = (extraHeadHTML) => {
2069
- const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL);
2287
+ const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL, { previewScroller: props.previewScroller });
2070
2288
  return {
2071
2289
  ...runtimePreviewOptions,
2072
2290
  ...props.previewOptions,
@@ -2076,8 +2294,8 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2076
2294
  extraHeadHTML
2077
2295
  ].filter(Boolean).join("\n"),
2078
2296
  customCode: {
2079
- importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode),
2080
- useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode)
2297
+ importCode: joinCode(DOCS_LINK_IMPORT_CODE, props.previewOptions?.customCode?.importCode, props.previewScroller ? PREVIEW_SCROLL_RESET_CODE : void 0, props.previewScroller ? PREVIEW_SCROLLER_IMPORT_CODE : void 0),
2298
+ useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode, props.previewScroller ? PREVIEW_SCROLLER_USE_CODE : void 0)
2081
2299
  }
2082
2300
  };
2083
2301
  };
@@ -2111,36 +2329,44 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2111
2329
  };
2112
2330
  const runtimeRoot = (0, vue.ref)(null);
2113
2331
  const previewExpanded = (0, vue.ref)(false);
2114
- /** `expand: true` 时在展开瞬间冻结,避免滚动重算高度造成抖动。 */
2332
+ /** `expandable: true` 时在展开瞬间冻结,避免滚动重算高度造成抖动。 */
2115
2333
  const frozenExpandedHeight = (0, vue.ref)(0);
2116
2334
  const measurePreviewChromeHeight = () => {
2117
2335
  const root = runtimeRoot.value;
2118
2336
  if (!root) return 0;
2119
- const header = root.querySelector(".docs-playground__header");
2337
+ const header = (root.closest(".docs-playground") ?? root).querySelector(".docs-playground__header");
2120
2338
  const error = root.querySelector(".docs-playground__runtime-error");
2121
2339
  return (header instanceof HTMLElement ? header.offsetHeight : 0) + (error instanceof HTMLElement ? error.offsetHeight : 0);
2122
2340
  };
2341
+ const normalizedPreviewInset = (0, vue.computed)(() => {
2342
+ const value = props.previewInset;
2343
+ if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
2344
+ if (Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "number" && Number.isFinite(item) && item >= 0)) return [value[0], value[1]];
2345
+ return [10, 10];
2346
+ });
2123
2347
  const measureExpandedPreviewHeight = () => {
2124
2348
  if (typeof window === "undefined") return runtimeHeight.value;
2125
2349
  const chromeHeight = measurePreviewChromeHeight();
2350
+ const viewport = getVisibleViewportRect(runtimeRoot.value);
2351
+ const [vertical] = normalizedPreviewInset.value;
2126
2352
  return resolveExpandedPreviewHeight(true, resolveRemainingPreviewHeight({
2127
- viewportHeight: getVisibleViewportRect(runtimeRoot.value).height,
2353
+ viewportHeight: viewport.height,
2128
2354
  chromeHeight
2129
- }));
2355
+ }) - vertical * 2 - 2);
2130
2356
  };
2131
2357
  const syncFrozenExpandedHeight = () => {
2132
- if (!previewExpanded.value || props.expand !== true) return;
2358
+ if (!previewExpanded.value || props.expandable !== true) return;
2133
2359
  frozenExpandedHeight.value = measureExpandedPreviewHeight();
2134
2360
  };
2135
2361
  if (typeof window !== "undefined") {
2136
2362
  window.addEventListener("message", handleBridgeMessage);
2137
2363
  window.addEventListener("resize", syncFrozenExpandedHeight);
2138
2364
  }
2139
- const canExpandPreview = (0, vue.computed)(() => !props.styleless && isPlaygroundExpandEnabled(props.expand));
2365
+ const canExpandPreview = (0, vue.computed)(() => !props.styleless && isPlaygroundExpandable(props.expandable));
2140
2366
  const expandLabel = (0, vue.computed)(() => t(previewExpanded.value ? "playground.runtime.collapsePreview" : "playground.runtime.expandPreview"));
2141
2367
  const desiredViewportHeight = (0, vue.computed)(() => {
2142
- if (previewExpanded.value && props.expand === true) return frozenExpandedHeight.value;
2143
- if (previewExpanded.value && isPlaygroundExpandEnabled(props.expand)) return resolveExpandedPreviewHeight(props.expand, 0);
2368
+ if (previewExpanded.value && props.expandable === true) return frozenExpandedHeight.value;
2369
+ if (previewExpanded.value && isPlaygroundExpandable(props.expandable)) return resolveExpandedPreviewHeight(props.expandable, 0);
2144
2370
  const fixedHeight = getViewportHeight(props.viewport);
2145
2371
  if (fixedHeight) return fixedHeight;
2146
2372
  return runtimeHeight.value;
@@ -2151,7 +2377,7 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2151
2377
  const handleTogglePreviewExpand = () => {
2152
2378
  if (!canExpandPreview.value) return;
2153
2379
  if (!previewExpanded.value) {
2154
- if (props.expand === true) frozenExpandedHeight.value = measureExpandedPreviewHeight();
2380
+ if (props.expandable === true) frozenExpandedHeight.value = measureExpandedPreviewHeight();
2155
2381
  else frozenExpandedHeight.value = 0;
2156
2382
  previewExpanded.value = true;
2157
2383
  (0, vue.nextTick)(scrollExpandedPreviewIntoView);
@@ -2166,16 +2392,10 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2166
2392
  frozenExpandedHeight.value = 0;
2167
2393
  }
2168
2394
  });
2169
- const normalizedPreviewInset = (0, vue.computed)(() => {
2170
- const value = props.previewInset;
2171
- if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
2172
- if (Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "number" && Number.isFinite(item) && item >= 0)) return [value[0], value[1]];
2173
- return [10, 10];
2174
- });
2175
2395
  const previewStyle = (0, vue.computed)(() => {
2176
2396
  const [vertical, horizontal] = normalizedPreviewInset.value;
2177
2397
  return {
2178
- height: `${desiredViewportHeight.value + vertical * 2}px`,
2398
+ height: `${resolvePreviewBoxHeight(desiredViewportHeight.value, vertical)}px`,
2179
2399
  padding: vertical === horizontal ? `${vertical}px` : `${vertical}px ${horizontal}px`,
2180
2400
  background: PLAYGROUND_RUNTIME_CANVAS_BACKGROUND
2181
2401
  };
@@ -2240,6 +2460,7 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2240
2460
  Alone.popup({
2241
2461
  store,
2242
2462
  copyValue: copyValue.value,
2463
+ title: props.title,
2243
2464
  viewport: props.viewport,
2244
2465
  viewportOptions: props.viewportOptions,
2245
2466
  previewOptions: popupPreviewOptions.value,
@@ -2249,7 +2470,6 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2249
2470
  onNavigate: (to) => emit("navigate", to)
2250
2471
  });
2251
2472
  };
2252
- const handleView = (view) => emit("view-change", view);
2253
2473
  const handleViewportChange = (viewport) => {
2254
2474
  emit("viewport-change", viewport);
2255
2475
  };
@@ -2274,6 +2494,11 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2274
2494
  window.removeEventListener("resize", syncFrozenExpandedHeight);
2275
2495
  handleClosePopup();
2276
2496
  });
2497
+ __expose({
2498
+ refresh: handleInlineRefresh,
2499
+ edit: handleEditor,
2500
+ openPopup: handleOpenPopup
2501
+ });
2277
2502
  return (_ctx, _cache) => {
2278
2503
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [__props.styleless ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2279
2504
  key: 0,
@@ -2281,7 +2506,7 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2281
2506
  ref: runtimeRoot,
2282
2507
  class: "docs-playground-runtime--styleless",
2283
2508
  style: (0, vue.normalizeStyle)(stylelessStyle.value)
2284
- }, [errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_2, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createElementVNode)("div", {
2509
+ }, [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", {
2285
2510
  class: "docs-playground-runtime__viewport",
2286
2511
  style: (0, vue.normalizeStyle)(viewportStyle.value)
2287
2512
  }, [(0, vue.createVNode)((0, vue.unref)(_vue_repl.Sandbox), {
@@ -2296,7 +2521,18 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2296
2521
  ref: runtimeRoot,
2297
2522
  class: "docs-playground-runtime"
2298
2523
  }, [
2299
- (0, vue.createElementVNode)("div", _hoisted_3, [(0, vue.createVNode)(toolbar_default, {
2524
+ !__props.hideChrome ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3$1, [displayTitle.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2525
+ key: 0,
2526
+ ref_key: "titleEl",
2527
+ ref: titleEl,
2528
+ class: "docs-playground__title",
2529
+ id: titleId.value,
2530
+ tabindex: "-1",
2531
+ title: displayTitle.value
2532
+ }, [(0, vue.createElementVNode)("a", {
2533
+ class: "docs-playground__title-anchor",
2534
+ href: `#${titleId.value}`
2535
+ }, "#", 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, {
2300
2536
  "copy-value": copyValue.value,
2301
2537
  viewport: __props.viewport,
2302
2538
  "viewport-options": __props.viewportOptions,
@@ -2309,22 +2545,12 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2309
2545
  "copy-value",
2310
2546
  "viewport",
2311
2547
  "viewport-options"
2312
- ]), __props.views.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.views, (item) => {
2313
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2314
- key: item,
2315
- type: "button",
2316
- class: (0, vue.normalizeClass)(["docs-playground__view", { "is-active": item === __props.activeView }]),
2317
- title: getViewText(item),
2318
- "aria-label": getViewText(item),
2319
- "aria-pressed": item === __props.activeView,
2320
- onClick: ($event) => handleView(item)
2321
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: item }, null, 8, ["name"])], 10, _hoisted_5);
2322
- }), 128))])) : (0, vue.createCommentVNode)("v-if", true)]),
2323
- errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_6, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2548
+ ])])) : (0, vue.createCommentVNode)("v-if", true),
2549
+ errorText.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("pre", _hoisted_7, (0, vue.toDisplayString)(errorText.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2324
2550
  (0, vue.createElementVNode)("section", {
2325
2551
  class: (0, vue.normalizeClass)(["docs-playground__preview", { "is-expanded": previewExpanded.value }]),
2326
2552
  style: (0, vue.normalizeStyle)(previewStyle.value)
2327
- }, [(0, vue.createElementVNode)("div", _hoisted_7, [(0, vue.createElementVNode)("div", {
2553
+ }, [(0, vue.createElementVNode)("div", _hoisted_8, [(0, vue.createElementVNode)("div", {
2328
2554
  class: "docs-playground-runtime__viewport",
2329
2555
  style: (0, vue.normalizeStyle)(viewportStyle.value)
2330
2556
  }, [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_vue_repl.Sandbox), {
@@ -2344,7 +2570,7 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2344
2570
  title: expandLabel.value,
2345
2571
  "aria-label": expandLabel.value,
2346
2572
  onClick: handleTogglePreviewExpand
2347
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "expand" })], 10, _hoisted_8)) : (0, vue.createCommentVNode)("v-if", true)], 6)
2573
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "expand" })], 10, _hoisted_9)) : (0, vue.createCommentVNode)("v-if", true)], 6)
2348
2574
  ], 512))]);
2349
2575
  };
2350
2576
  }
@@ -2355,6 +2581,13 @@ var _hoisted_1 = {
2355
2581
  key: 0,
2356
2582
  class: "docs-playground__error"
2357
2583
  };
2584
+ var _hoisted_2 = {
2585
+ key: 0,
2586
+ class: "docs-playground__header"
2587
+ };
2588
+ var _hoisted_3 = ["id", "title"];
2589
+ var _hoisted_4 = ["href"];
2590
+ var _hoisted_5 = { class: "docs-playground__title-text" };
2358
2591
  //#endregion
2359
2592
  //#region packages/playground/src/playground.vue
2360
2593
  var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -2372,7 +2605,13 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2372
2605
  },
2373
2606
  options: { default: () => ({}) },
2374
2607
  previewInset: { default: 0 },
2375
- expand: { type: [Boolean, Number] },
2608
+ expandable: { type: [Boolean, Number] },
2609
+ title: { default: "" },
2610
+ id: { default: "" },
2611
+ previewScroller: {
2612
+ type: Boolean,
2613
+ default: false
2614
+ },
2376
2615
  previewOptions: {},
2377
2616
  locale: {}
2378
2617
  },
@@ -2396,6 +2635,8 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2396
2635
  const sourceFiles = (0, vue.ref)(initialFiles);
2397
2636
  const currentEntry = (0, vue.ref)(initialEntry);
2398
2637
  const fileActiveFilename = (0, vue.ref)(initialEntry);
2638
+ const runtimeRef = (0, vue.ref)(null);
2639
+ const titleEl = (0, vue.ref)(null);
2399
2640
  const normalizeViews = (views) => {
2400
2641
  const normalized = [];
2401
2642
  for (const view of views) {
@@ -2406,12 +2647,25 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2406
2647
  return normalized.length ? normalized : ["runtime"];
2407
2648
  };
2408
2649
  const normalizedViews = (0, vue.computed)(() => normalizeViews(props.views));
2650
+ const hasRuntimeView = (0, vue.computed)(() => normalizedViews.value.includes("runtime"));
2651
+ const hasFilesView = (0, vue.computed)(() => normalizedViews.value.includes("files"));
2409
2652
  const activeView = (0, vue.ref)(normalizedViews.value[0]);
2410
2653
  const runtimeActivated = (0, vue.ref)(props.styleless || activeView.value === "runtime");
2411
2654
  const normalizedViewportOptions = (0, vue.computed)(() => normalizeViewportOptions(props.viewportOptions));
2412
2655
  const activeViewport = (0, vue.ref)(resolveInitialViewport(props.viewport, normalizedViewportOptions.value));
2413
2656
  const selectableViewportOptions = (0, vue.computed)(() => includeActiveViewport(normalizedViewportOptions.value, activeViewport.value));
2414
2657
  const error = (0, vue.ref)(props.entry && initialFiles[props.entry] === void 0 ? t("playground.validation.entryMissing", { filename: props.entry }) : "");
2658
+ const displayTitle = (0, vue.computed)(() => props.title.trim());
2659
+ const titleId = (0, vue.computed)(() => resolvePlaygroundTitleId(displayTitle.value, props.id, (candidate) => {
2660
+ if (typeof document === "undefined") return false;
2661
+ const existing = document.getElementById(candidate);
2662
+ return !!existing && existing !== titleEl.value;
2663
+ }));
2664
+ const copyValue = (0, vue.computed)(() => sourceFiles.value[currentEntry.value] || "");
2665
+ /** runtime / 双视图始终有顶栏;files-only 仅有标题时渲染。 */
2666
+ const showHeader = (0, vue.computed)(() => hasRuntimeView.value || !!displayTitle.value);
2667
+ const showToolbar = (0, vue.computed)(() => hasRuntimeView.value);
2668
+ const showRuntimeActions = (0, vue.computed)(() => activeView.value === "runtime");
2415
2669
  (0, vue.watch)(locale, () => {
2416
2670
  if (props.entry && sourceFiles.value[props.entry] === void 0) error.value = t("playground.validation.entryMissing", { filename: props.entry });
2417
2671
  });
@@ -2425,6 +2679,15 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2425
2679
  activeViewport.value = cloneViewport(viewport);
2426
2680
  emit("update:viewport", cloneViewport(viewport));
2427
2681
  };
2682
+ const handleRefresh = () => {
2683
+ runtimeRef.value?.refresh();
2684
+ };
2685
+ const handleEdit = () => {
2686
+ runtimeRef.value?.edit();
2687
+ };
2688
+ const handleOpenPopup = () => {
2689
+ runtimeRef.value?.openPopup();
2690
+ };
2428
2691
  const handleFileActive = (filename) => {
2429
2692
  if (sourceFiles.value[filename] !== void 0) fileActiveFilename.value = filename;
2430
2693
  };
@@ -2506,8 +2769,11 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2506
2769
  options: __props.options,
2507
2770
  "preview-options": __props.previewOptions,
2508
2771
  "preview-inset": __props.previewInset,
2509
- expand: __props.expand,
2772
+ "preview-scroller": __props.previewScroller,
2773
+ expandable: __props.expandable,
2510
2774
  styleless: true,
2775
+ title: __props.title,
2776
+ id: __props.id,
2511
2777
  viewport: activeViewport.value,
2512
2778
  "viewport-options": selectableViewportOptions.value,
2513
2779
  onFilesChange: handleFilesChange,
@@ -2519,29 +2785,70 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2519
2785
  "options",
2520
2786
  "preview-options",
2521
2787
  "preview-inset",
2522
- "expand",
2788
+ "preview-scroller",
2789
+ "expandable",
2790
+ "title",
2791
+ "id",
2523
2792
  "viewport",
2524
2793
  "viewport-options"
2525
2794
  ])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2526
2795
  key: 1,
2527
2796
  class: (0, vue.normalizeClass)(["docs-playground", { "docs-playground--files": activeView.value === "files" }])
2528
- }, [
2529
- error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1, (0, vue.toDisplayString)(error.value), 1)) : (0, vue.createCommentVNode)("v-if", true),
2530
- runtimeActivated.value && normalizedViews.value.includes("runtime") ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(runtime_default), {
2797
+ }, [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 }, [
2798
+ showHeader.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2, [displayTitle.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2799
+ key: 0,
2800
+ ref_key: "titleEl",
2801
+ ref: titleEl,
2802
+ class: "docs-playground__title",
2803
+ id: titleId.value,
2804
+ tabindex: "-1",
2805
+ title: displayTitle.value
2806
+ }, [(0, vue.createElementVNode)("a", {
2807
+ class: "docs-playground__title-anchor",
2808
+ href: `#${titleId.value}`
2809
+ }, "#", 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, {
2810
+ key: 1,
2811
+ "copy-value": copyValue.value,
2812
+ viewport: activeViewport.value,
2813
+ "viewport-options": selectableViewportOptions.value,
2814
+ views: normalizedViews.value,
2815
+ "active-view": activeView.value,
2816
+ "show-runtime-actions": showRuntimeActions.value,
2817
+ "show-open-popup": hasRuntimeView.value,
2818
+ onRefresh: handleRefresh,
2819
+ onEdit: handleEdit,
2820
+ onOpenPopup: handleOpenPopup,
2821
+ onViewportChange: handleViewport,
2822
+ onViewChange: handleView
2823
+ }, null, 8, [
2824
+ "copy-value",
2825
+ "viewport",
2826
+ "viewport-options",
2827
+ "views",
2828
+ "active-view",
2829
+ "show-runtime-actions",
2830
+ "show-open-popup"
2831
+ ])) : (0, vue.createCommentVNode)("v-if", true)])) : (0, vue.createCommentVNode)("v-if", true),
2832
+ runtimeActivated.value && hasRuntimeView.value ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(runtime_default), {
2531
2833
  key: 1,
2834
+ ref_key: "runtimeRef",
2835
+ ref: runtimeRef,
2532
2836
  files: sourceFiles.value,
2533
2837
  entry: currentEntry.value,
2534
2838
  options: __props.options,
2535
2839
  "preview-options": __props.previewOptions,
2536
2840
  "preview-inset": __props.previewInset,
2537
- expand: __props.expand,
2841
+ "preview-scroller": __props.previewScroller,
2842
+ expandable: __props.expandable,
2843
+ "hide-chrome": true,
2844
+ title: __props.title,
2845
+ id: __props.id,
2538
2846
  "active-view": activeView.value,
2539
2847
  views: normalizedViews.value,
2540
2848
  viewport: activeViewport.value,
2541
2849
  "viewport-options": selectableViewportOptions.value,
2542
2850
  onFilesChange: handleFilesChange,
2543
2851
  onNavigate: _cache[1] || (_cache[1] = ($event) => emit("navigate", $event)),
2544
- onViewChange: handleView,
2545
2852
  onViewportChange: handleViewport
2546
2853
  }, null, 8, [
2547
2854
  "files",
@@ -2549,29 +2856,31 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2549
2856
  "options",
2550
2857
  "preview-options",
2551
2858
  "preview-inset",
2552
- "expand",
2859
+ "preview-scroller",
2860
+ "expandable",
2861
+ "title",
2862
+ "id",
2553
2863
  "active-view",
2554
2864
  "views",
2555
2865
  "viewport",
2556
2866
  "viewport-options"
2557
- ])), [[vue.vShow, !error.value && activeView.value === "runtime"]]) : (0, vue.createCommentVNode)("v-if", true),
2558
- normalizedViews.value.includes("files") ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(files_preview_default), {
2867
+ ])), [[vue.vShow, activeView.value === "runtime"]]) : (0, vue.createCommentVNode)("v-if", true),
2868
+ hasFilesView.value ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(files_preview_default), {
2559
2869
  key: 2,
2560
2870
  files: sourceFiles.value,
2561
2871
  entry: currentEntry.value,
2562
2872
  "active-filename": fileActiveFilename.value,
2563
2873
  "active-view": activeView.value,
2564
2874
  views: normalizedViews.value,
2565
- onActiveChange: handleFileActive,
2566
- onViewChange: handleView
2875
+ onActiveChange: handleFileActive
2567
2876
  }, null, 8, [
2568
2877
  "files",
2569
2878
  "entry",
2570
2879
  "active-filename",
2571
2880
  "active-view",
2572
2881
  "views"
2573
- ])), [[vue.vShow, !error.value && activeView.value === "files"]]) : (0, vue.createCommentVNode)("v-if", true)
2574
- ], 2));
2882
+ ])), [[vue.vShow, activeView.value === "files"]]) : (0, vue.createCommentVNode)("v-if", true)
2883
+ ], 64))], 2));
2575
2884
  };
2576
2885
  }
2577
2886
  });
@@ -2579,6 +2888,9 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2579
2888
  exports.CodePreview = code_preview_default;
2580
2889
  exports.DEFAULT_CDN_URL = DEFAULT_CDN_URL;
2581
2890
  exports.Editor = Editor;
2891
+ exports.PREVIEW_SCROLLER_USE_CODE = PREVIEW_SCROLLER_USE_CODE;
2892
+ exports.PREVIEW_SCROLL_CONTENT_CLASS = PREVIEW_SCROLL_CONTENT_CLASS;
2893
+ exports.PREVIEW_SCROLL_HTML_CLASS = PREVIEW_SCROLL_HTML_CLASS;
2582
2894
  exports.Playground = playground_default;
2583
2895
  exports.applyPlaygroundImportMapOverride = applyPlaygroundImportMapOverride;
2584
2896
  exports.applyPlaygroundStyleOverride = applyPlaygroundStyleOverride;
@@ -2598,8 +2910,10 @@ exports.registerVueHighlight = registerVueHighlight;
2598
2910
  exports.removePlaygroundImportMapOverride = removePlaygroundImportMapOverride;
2599
2911
  exports.removePlaygroundStyleOverride = removePlaygroundStyleOverride;
2600
2912
  exports.resolveHighlightLanguage = resolveHighlightLanguage;
2913
+ exports.resolvePlaygroundTitleId = resolvePlaygroundTitleId;
2601
2914
  exports.setPlaygroundImportMapOverrides = setPlaygroundImportMapOverrides;
2602
2915
  exports.setPlaygroundSiteModules = setPlaygroundSiteModules;
2603
2916
  exports.setPlaygroundSiteStyles = setPlaygroundSiteStyles;
2604
2917
  exports.setPlaygroundStyleOverrides = setPlaygroundStyleOverrides;
2918
+ exports.slugifyPlaygroundTitle = slugifyPlaygroundTitle;
2605
2919
  exports.vueHighlight = vueHighlight;