@deot/docs-playground 1.1.0 → 1.1.2

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.js CHANGED
@@ -153,10 +153,10 @@ var icon_default = /* @__PURE__ */ defineComponent({
153
153
  });
154
154
  //#endregion
155
155
  //#region packages/playground/src/core/code-preview/github.css?inline
156
- 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}";
156
+ 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}";
157
157
  //#endregion
158
158
  //#region packages/playground/src/core/code-preview/code-preview.scss?inline
159
- 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}";
159
+ 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}";
160
160
  //#endregion
161
161
  //#region packages/playground/src/core/code-preview/style.ts
162
162
  var CODE_PREVIEW_STYLE_ID = "docs-code-preview-style";
@@ -228,11 +228,19 @@ var code_preview_default = /* @__PURE__ */ defineComponent({
228
228
  "title",
229
229
  "aria-label"
230
230
  ]),
231
- createCommentVNode(" eslint-disable-next-line vue/no-v-html "),
232
- createElementVNode("pre", _hoisted_3$6, [createElementVNode("code", {
233
- class: "hljs",
234
- innerHTML: highlightedCode.value
235
- }, null, 8, _hoisted_4$6)])
231
+ createVNode(unref(Scroller), {
232
+ class: "docs-code-preview__scroller",
233
+ "auto-resize": true,
234
+ native: false,
235
+ "show-bar": true,
236
+ height: "100%"
237
+ }, {
238
+ default: withCtx(() => [createCommentVNode(" eslint-disable-next-line vue/no-v-html "), createElementVNode("pre", _hoisted_3$6, [createElementVNode("code", {
239
+ class: "hljs",
240
+ innerHTML: highlightedCode.value
241
+ }, null, 8, _hoisted_4$6)])]),
242
+ _: 1
243
+ })
236
244
  ]);
237
245
  };
238
246
  }
@@ -825,19 +833,120 @@ var resolvePlaygroundTitleId = (title, explicitId = "", isOccupied) => {
825
833
  while (isOccupied(`${base}-${index}`)) index += 1;
826
834
  return `${base}-${index}`;
827
835
  };
836
+ //#endregion
837
+ //#region packages/playground/src/core/preview-scroll.ts
838
+ /** iframe 包装成功后挂在 `html` 上,供样式与 auto-height 识别。 */
839
+ var PREVIEW_SCROLL_HTML_CLASS = "docs-playground-preview-scroll";
840
+ /** Scroller 内容节点 class,auto-height 优先测量该节点。 */
841
+ var PREVIEW_SCROLL_CONTENT_CLASS = "docs-playground-preview-scroll__content";
842
+ /** 重评时先清掉 html class,避免 `await import` 期间仍套用 `height:100%`。 */
843
+ var PREVIEW_SCROLL_RESET_CODE = `document.documentElement.classList.remove("${PREVIEW_SCROLL_HTML_CLASS}");`;
844
+ /** 动态探测 `@deot/vc`;失败时为空对象,不阻断 DocsLink / 预览挂载。 */
845
+ var PREVIEW_SCROLLER_IMPORT_CODE = "const __docsVc=await import(\"@deot/vc\").catch(()=>({}))";
846
+ /**
847
+ * Vue 的 `mount` 闭包的是 `createApp(root)` 时的根组件,改 `app._component` 不会生效。
848
+ * 因此劫持 `app.mount`:等全部 useCode 注册完后,再 `createApp` 包一层 Scroller。
849
+ * 前导分号避免接在 `app.component(...)` 后被 ASI 解析成调用返回值。
850
+ */
851
+ var PREVIEW_SCROLLER_USE_CODE = [
852
+ ";(()=>{",
853
+ PREVIEW_SCROLL_RESET_CODE,
854
+ "const __DocsScroller=__docsVc&&__docsVc.Scroller;",
855
+ "if(!__DocsScroller)return;",
856
+ "const UserApp=app._component;",
857
+ "app.mount=(container,...rest)=>{",
858
+ "const next=_createApp({",
859
+ "name:\"DocsPreviewRoot\",",
860
+ "setup(){return()=>__docsH(__DocsScroller,{",
861
+ "height:\"100%\",",
862
+ "native:false,",
863
+ "autoResize:true,",
864
+ "showBar:true,",
865
+ `contentClass:"${PREVIEW_SCROLL_CONTENT_CLASS}"`,
866
+ "},{default:()=>__docsH(UserApp)})}",
867
+ "});",
868
+ "next.config.errorHandler=app.config.errorHandler;",
869
+ "if(app._context&&next._context){",
870
+ "if(app._context.components)Object.assign(next._context.components,app._context.components);",
871
+ "if(app._context.directives)Object.assign(next._context.directives,app._context.directives);",
872
+ "Object.assign(next._context.provides,app._context.provides);",
873
+ "next._context.mixins.push(...app._context.mixins);",
874
+ "}",
875
+ "window.__app__=next;",
876
+ `document.documentElement.classList.add("${PREVIEW_SCROLL_HTML_CLASS}");`,
877
+ "return next.mount(container,...rest)",
878
+ "}",
879
+ "})()"
880
+ ].join("");
881
+ var PREVIEW_SCROLLER_STYLE = [
882
+ `html.${PREVIEW_SCROLL_HTML_CLASS},`,
883
+ `html.${PREVIEW_SCROLL_HTML_CLASS} body,`,
884
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app{`,
885
+ "height:100%;min-height:100%;margin:0;overflow:hidden",
886
+ "}",
887
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app>.vc-scroller,`,
888
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app>.vc-scroller>.vc-scroller__wrapper{`,
889
+ "height:100%;max-height:100%;box-sizing:border-box",
890
+ "}"
891
+ ].join("");
828
892
  var resolveSandboxContainer = (sandbox) => sandbox?.container ? unref(sandbox.container) : null;
893
+ var resolveScrollContent = (iframeDocument) => {
894
+ if (!iframeDocument.documentElement.classList.contains("docs-playground-preview-scroll")) return null;
895
+ return iframeDocument.querySelector(`.${PREVIEW_SCROLL_CONTENT_CLASS}`);
896
+ };
829
897
  var useSandboxAutoHeight = (sandboxRef) => {
830
898
  const height = ref(24);
831
899
  let container = null;
832
900
  let iframe = null;
833
901
  let containerObserver = null;
834
902
  let contentObserver = null;
903
+ let scrollMountObserver = null;
904
+ let observedScrollNode = null;
835
905
  let frameId = 0;
836
906
  const cancelMeasure = () => {
837
907
  if (!frameId) return;
838
908
  cancelAnimationFrame(frameId);
839
909
  frameId = 0;
840
910
  };
911
+ const measureDocumentContent = (iframeWindow, iframeDocument) => {
912
+ const { body, documentElement } = iframeDocument;
913
+ const bodyStyle = iframeWindow.getComputedStyle(body);
914
+ const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
915
+ const rootTop = documentElement.getBoundingClientRect().top;
916
+ const bodyRect = body.getBoundingClientRect();
917
+ const collapsedTop = Math.max(0, bodyRect.top - rootTop);
918
+ let childrenBottom = 0;
919
+ for (const child of body.children) {
920
+ if (iframeWindow.getComputedStyle(child).position === "fixed") continue;
921
+ const rect = child.getBoundingClientRect();
922
+ const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
923
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
924
+ }
925
+ const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
926
+ const overflowingHeight = scrollHeight > (iframe?.clientHeight || 0) + 1 ? scrollHeight : 0;
927
+ return Math.ceil(Math.max(body.offsetHeight + bodyMargins + collapsedTop, bodyRect.height + bodyMargins + collapsedTop, childrenBottom, overflowingHeight));
928
+ };
929
+ const measureScrollContent = (scrollContent) => {
930
+ const contentStyle = scrollContent.ownerDocument.defaultView?.getComputedStyle(scrollContent);
931
+ const margins = contentStyle ? (Number.parseFloat(contentStyle.marginTop) || 0) + (Number.parseFloat(contentStyle.marginBottom) || 0) : 0;
932
+ let childrenBottom = 0;
933
+ const rootTop = scrollContent.getBoundingClientRect().top;
934
+ for (const child of scrollContent.children) {
935
+ const rect = child.getBoundingClientRect();
936
+ const marginBottom = Number.parseFloat(scrollContent.ownerDocument.defaultView?.getComputedStyle(child).marginBottom || "0") || 0;
937
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
938
+ }
939
+ return Math.ceil(Math.max(scrollContent.scrollHeight + margins, scrollContent.offsetHeight + margins, childrenBottom + margins));
940
+ };
941
+ const isPendingPreviewApp = (iframeDocument) => {
942
+ const app = iframeDocument.getElementById("app");
943
+ return app ? app.childElementCount === 0 : !!iframe?.srcdoc;
944
+ };
945
+ const releaseBootstrapHeight = (target) => {
946
+ nextTick(() => {
947
+ if (iframe === target) target.style.height = "100%";
948
+ });
949
+ };
841
950
  const measure = () => {
842
951
  frameId = 0;
843
952
  if (!iframe) return;
@@ -845,23 +954,12 @@ var useSandboxAutoHeight = (sandboxRef) => {
845
954
  const iframeWindow = iframe.contentWindow;
846
955
  const iframeDocument = iframe.contentDocument;
847
956
  if (!iframeWindow || !iframeDocument?.body) return;
848
- const { body, documentElement } = iframeDocument;
849
- const bodyStyle = iframeWindow.getComputedStyle(body);
850
- const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
851
- const rootTop = documentElement.getBoundingClientRect().top;
852
- const bodyRect = body.getBoundingClientRect();
853
- const collapsedTop = Math.max(0, bodyRect.top - rootTop);
854
- let childrenBottom = 0;
855
- for (const child of body.children) {
856
- const rect = child.getBoundingClientRect();
857
- const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
858
- childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
859
- }
860
- const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
861
- const overflowingHeight = scrollHeight > iframe.clientHeight + 1 ? scrollHeight : 0;
862
- const contentHeight = Math.ceil(Math.max(body.offsetHeight + bodyMargins + collapsedTop, bodyRect.height + bodyMargins + collapsedTop, childrenBottom, overflowingHeight));
957
+ const scrollContent = resolveScrollContent(iframeDocument);
958
+ if (!scrollContent && isPendingPreviewApp(iframeDocument)) return;
959
+ const contentHeight = scrollContent ? measureScrollContent(scrollContent) : measureDocumentContent(iframeWindow, iframeDocument);
863
960
  const nextHeight = Math.max(contentHeight, 24);
864
961
  if (height.value !== nextHeight) height.value = nextHeight;
962
+ releaseBootstrapHeight(iframe);
865
963
  } catch {
866
964
  if (height.value !== 24) height.value = 24;
867
965
  }
@@ -873,6 +971,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
873
971
  const disconnectContent = () => {
874
972
  contentObserver?.disconnect();
875
973
  contentObserver = null;
974
+ scrollMountObserver?.disconnect();
975
+ scrollMountObserver = null;
976
+ observedScrollNode = null;
876
977
  cancelMeasure();
877
978
  };
878
979
  const observeContent = () => {
@@ -890,7 +991,28 @@ var useSandboxAutoHeight = (sandboxRef) => {
890
991
  observer.observe(iframeDocument.documentElement);
891
992
  observer.observe(iframeDocument.body);
892
993
  contentObserver = observer;
893
- scheduleMeasure();
994
+ const attachScrollContent = (node) => {
995
+ if (!node || !contentObserver) return false;
996
+ if (observedScrollNode === node) return true;
997
+ if (observedScrollNode) contentObserver.unobserve(observedScrollNode);
998
+ observedScrollNode = node;
999
+ contentObserver.observe(node);
1000
+ return true;
1001
+ };
1002
+ const syncScrollContent = () => {
1003
+ attachScrollContent(resolveScrollContent(iframeDocument));
1004
+ scheduleMeasure();
1005
+ };
1006
+ scrollMountObserver = new MutationObserver(syncScrollContent);
1007
+ scrollMountObserver.observe(iframeDocument.documentElement, {
1008
+ attributes: true,
1009
+ attributeFilter: ["class"]
1010
+ });
1011
+ scrollMountObserver.observe(iframeDocument.body, {
1012
+ childList: true,
1013
+ subtree: true
1014
+ });
1015
+ syncScrollContent();
894
1016
  } catch {
895
1017
  if (height.value !== 24) height.value = 24;
896
1018
  }
@@ -906,7 +1028,7 @@ var useSandboxAutoHeight = (sandboxRef) => {
906
1028
  return;
907
1029
  }
908
1030
  iframe.addEventListener("load", handleIframeLoad);
909
- iframe.style.height = "100%";
1031
+ iframe.style.height = `${Math.max(window.innerHeight, 24)}px`;
910
1032
  observeContent();
911
1033
  };
912
1034
  const syncIframe = () => setIframe(container?.querySelector("iframe") || null);
@@ -934,6 +1056,12 @@ var useSandboxAutoHeight = (sandboxRef) => {
934
1056
  });
935
1057
  return height;
936
1058
  };
1059
+ var resolvePreviewBoxHeight = (contentHeight, verticalInset = 0, borderWidth = 1) => {
1060
+ const content = Number.isFinite(contentHeight) ? Math.max(0, contentHeight) : 0;
1061
+ const inset = Number.isFinite(verticalInset) ? Math.max(0, verticalInset) : 0;
1062
+ const border = Number.isFinite(borderWidth) ? Math.max(0, borderWidth) : 1;
1063
+ return content + inset * 2 + border * 2;
1064
+ };
937
1065
  var CLIPPING_OVERFLOW = /* @__PURE__ */ new Set([
938
1066
  "auto",
939
1067
  "scroll",
@@ -1250,6 +1378,73 @@ var useSandboxTheme = (sandboxRef) => {
1250
1378
  });
1251
1379
  };
1252
1380
  //#endregion
1381
+ //#region packages/playground/src/core/runtime/height-bridge.ts
1382
+ /**
1383
+ * 只接收本宿主当前 iframe 的高度请求,应用重评时切换会话。
1384
+ * @param sandbox 预览容器。
1385
+ * @param maxHeight 当前可用高度,普通预览默认不限制。
1386
+ * @returns 所有有效请求的最大高度。
1387
+ */
1388
+ function usePreviewHeightBridge(sandbox, maxHeight = () => Infinity) {
1389
+ const height = ref(0);
1390
+ const requests = /* @__PURE__ */ new Map();
1391
+ let source = null;
1392
+ let session = "";
1393
+ let disposed = false;
1394
+ const sync = () => {
1395
+ height.value = Math.max(0, ...requests.values());
1396
+ };
1397
+ const reset = () => {
1398
+ requests.clear();
1399
+ session = "";
1400
+ source = null;
1401
+ sync();
1402
+ };
1403
+ const handleMessage = async (event) => {
1404
+ const iframe = resolveSandboxContainer(sandbox.value)?.querySelector("iframe");
1405
+ const data = event.data;
1406
+ if (!iframe?.contentWindow || event.source !== iframe.contentWindow || data?.action !== "docs:height" || typeof data.session !== "string") return;
1407
+ if (data.operation === "start") {
1408
+ reset();
1409
+ source = iframe.contentWindow;
1410
+ session = data.session;
1411
+ return;
1412
+ }
1413
+ if (source !== iframe.contentWindow || session !== data.session) return;
1414
+ if (data.operation === "stop") {
1415
+ reset();
1416
+ return;
1417
+ }
1418
+ if (!Number.isSafeInteger(data.id) || data.id <= 0) return;
1419
+ if (data.operation === "release") {
1420
+ requests.delete(data.id);
1421
+ sync();
1422
+ return;
1423
+ }
1424
+ if (data.operation !== "enable" || !Number.isFinite(data.height) || data.height <= 0) return;
1425
+ const accepted = data.height <= maxHeight();
1426
+ if (accepted) requests.set(data.id, data.height);
1427
+ sync();
1428
+ const receiver = source;
1429
+ await nextTick();
1430
+ if (disposed || source !== receiver || session !== data.session) return;
1431
+ receiver?.postMessage({
1432
+ action: "docs:height:applied",
1433
+ session,
1434
+ id: data.id,
1435
+ accepted
1436
+ }, "*");
1437
+ };
1438
+ watch(() => resolveSandboxContainer(sandbox.value), reset, { flush: "sync" });
1439
+ if (typeof window !== "undefined") window.addEventListener("message", handleMessage);
1440
+ onBeforeUnmount(() => {
1441
+ disposed = true;
1442
+ if (typeof window !== "undefined") window.removeEventListener("message", handleMessage);
1443
+ reset();
1444
+ });
1445
+ return height;
1446
+ }
1447
+ //#endregion
1253
1448
  //#region packages/playground/src/core/runtime/toolbar.vue?vue&type=script&setup=true&lang.ts
1254
1449
  var _hoisted_1$3 = { class: "docs-playground__tools" };
1255
1450
  var _hoisted_2$3 = {
@@ -1494,7 +1689,9 @@ var BUILTIN_IMPORT_ASSETS = {
1494
1689
  "@deot/helper": "@deot/helper/dist/index.js",
1495
1690
  "normalize-wheel": "normalize-wheel-es/dist/index.mjs",
1496
1691
  "photoswipe": "photoswipe/dist/photoswipe.esm.js",
1497
- "photoswipe/lightbox": "photoswipe/dist/photoswipe-lightbox.esm.js"
1692
+ "photoswipe/lightbox": "photoswipe/dist/photoswipe-lightbox.esm.js",
1693
+ "vue": "vue/dist/vue.runtime.esm-browser.js",
1694
+ "vue/server-renderer": "vue/dist/server-renderer.esm-browser.js"
1498
1695
  };
1499
1696
  var normalizeCdnURL = (cdnURL = DEFAULT_CDN_URL) => {
1500
1697
  return cdnURL.trim().replace(/\/+$/u, "") || "https://cdn.jsdelivr.net/npm";
@@ -1505,9 +1702,7 @@ var SASS_CDN_URL = jsdelivrEsmURL("sass/+esm");
1505
1702
  var cdnAsset = (cdnURL, asset) => cdnJoin(normalizeCdnURL(cdnURL), asset);
1506
1703
  var createBuiltinImports = (cdnURL = DEFAULT_CDN_URL) => ({
1507
1704
  ...Object.fromEntries(Object.entries(BUILTIN_IMPORT_ASSETS).map(([specifier, asset]) => [specifier, cdnAsset(cdnURL, asset)])),
1508
- "lodash-es": jsdelivrEsmURL("lodash-es/+esm"),
1509
- "vue": "https://play.vuejs.org/vue.runtime.esm-browser.js",
1510
- "vue/server-renderer": "https://play.vuejs.org/server-renderer.esm-browser.js"
1705
+ "lodash-es": jsdelivrEsmURL("lodash-es/+esm")
1511
1706
  });
1512
1707
  var createBuiltinStyles = (cdnURL = DEFAULT_CDN_URL) => Object.fromEntries(PREVIEW_STYLE_ASSETS.map((asset) => [asset, cdnAsset(cdnURL, asset)]));
1513
1708
  //#endregion
@@ -1587,6 +1782,227 @@ var clearPlaygroundMaps = () => {
1587
1782
  ].forEach((store) => store.set());
1588
1783
  };
1589
1784
  //#endregion
1785
+ //#region packages/playground/src/preview-service.ts
1786
+ /** Preview iframe 内的高度服务;class 会被序列化到 iframe 执行。 */
1787
+ var PlaygroundService = class {
1788
+ target;
1789
+ vue;
1790
+ session;
1791
+ /* istanbul ignore next -- class 会被序列化到没有覆盖率运行时的 iframe。 */
1792
+ sequence = 0;
1793
+ /* istanbul ignore next -- class 会被序列化到没有覆盖率运行时的 iframe。 */
1794
+ disposed = false;
1795
+ manual;
1796
+ /* istanbul ignore next -- class 会被序列化到没有覆盖率运行时的 iframe。 */
1797
+ requests = /* @__PURE__ */ new Set();
1798
+ /* istanbul ignore next -- class 会被序列化到没有覆盖率运行时的 iframe。 */
1799
+ owners = /* @__PURE__ */ new Set();
1800
+ /**
1801
+ * @param target 当前预览窗口。
1802
+ * @param vue 与预览应用共用的 Vue 实例。
1803
+ */
1804
+ /* istanbul ignore next -- class 会被序列化到没有覆盖率运行时的 iframe。 */
1805
+ constructor(target, vue) {
1806
+ this.target = target;
1807
+ this.vue = vue;
1808
+ this.session = `${Date.now()}-${Math.random()}`;
1809
+ this.dispose = this.dispose.bind(this);
1810
+ this.post("start");
1811
+ this.target.addEventListener("pagehide", this.dispose);
1812
+ }
1813
+ /**
1814
+ * 申请手动临时高度;新请求会替换上一个手动请求。
1815
+ * @param height iframe 最小高度。
1816
+ * @returns iframe 是否在超时前应用目标高度。
1817
+ */
1818
+ enable(height) {
1819
+ this.validateHeight(height);
1820
+ this.manual?.release();
1821
+ this.manual = this.createRequest(height);
1822
+ return this.manual.ready;
1823
+ }
1824
+ /** 释放手动临时高度,不影响 `run` 创建的请求。 */
1825
+ disable() {
1826
+ this.manual?.release();
1827
+ this.manual = void 0;
1828
+ }
1829
+ /**
1830
+ * 创建等待高度后执行的事件处理函数。
1831
+ * @param height iframe 最小高度。
1832
+ * @param optionsOrHandler 可见状态配置,或最后一个 handler。
1833
+ * @param lastHandler 使用配置时的最后一个 handler。
1834
+ * @returns 保留 handler 参数和结果的异步事件处理函数。
1835
+ */
1836
+ run(height, optionsOrHandler, lastHandler) {
1837
+ this.validateHeight(height);
1838
+ if (optionsOrHandler === void 0) return (visible) => {
1839
+ if (visible) return this.enable(height);
1840
+ this.disable();
1841
+ return Promise.resolve(void 0);
1842
+ };
1843
+ const visible = (typeof optionsOrHandler === "function" ? {} : optionsOrHandler).visible;
1844
+ if (visible !== void 0 && !this.vue.isRef(visible) && typeof visible !== "function") throw new TypeError("Playground visible must be a ref or getter");
1845
+ const handler = typeof optionsOrHandler === "function" ? optionsOrHandler : lastHandler ?? (this.vue.isRef(visible) ? () => {
1846
+ visible.value = true;
1847
+ } : void 0);
1848
+ if (typeof handler !== "function") throw new TypeError("Playground run requires a handler");
1849
+ let ownerDisposed = false;
1850
+ let current;
1851
+ let pending;
1852
+ const disposeOwner = () => {
1853
+ ownerDisposed = true;
1854
+ current?.release();
1855
+ this.owners.delete(disposeOwner);
1856
+ };
1857
+ this.owners.add(disposeOwner);
1858
+ if (this.vue.getCurrentScope()) this.vue.onScopeDispose(disposeOwner);
1859
+ return (...args) => {
1860
+ if (this.disposed || ownerDisposed) return Promise.resolve(void 0);
1861
+ if (pending) return pending;
1862
+ const request = current = this.createRequest(height);
1863
+ let executionFinished = false;
1864
+ const unlock = () => {
1865
+ if (executionFinished && request.released && current === request) {
1866
+ pending = void 0;
1867
+ current = void 0;
1868
+ }
1869
+ };
1870
+ request.closed.then(unlock);
1871
+ const execution = (async () => {
1872
+ try {
1873
+ if (!await request.ready || ownerDisposed || this.disposed) return void 0;
1874
+ if (visible) request.watch(visible);
1875
+ const result = await handler(...args);
1876
+ if (!visible || !this.vue.toValue(visible)) request.release();
1877
+ return result;
1878
+ } catch (error) {
1879
+ request.release();
1880
+ throw error;
1881
+ } finally {
1882
+ executionFinished = true;
1883
+ unlock();
1884
+ }
1885
+ })();
1886
+ pending = execution;
1887
+ return execution;
1888
+ };
1889
+ }
1890
+ /** 释放当前预览应用持有的全部高度和监听。 */
1891
+ /* istanbul ignore next -- 注入测试会从序列化后的 iframe class 调用该方法。 */
1892
+ dispose() {
1893
+ if (this.disposed) return;
1894
+ this.disposed = true;
1895
+ for (const disposeOwner of this.owners) disposeOwner();
1896
+ for (const request of this.requests) request.release();
1897
+ this.post("stop");
1898
+ this.target.removeEventListener("pagehide", this.dispose);
1899
+ if (this.target.$docsPlayground === this) delete this.target.$docsPlayground;
1900
+ }
1901
+ /** @param height iframe 最小高度。 */
1902
+ validateHeight(height) {
1903
+ if (!Number.isFinite(height) || height <= 0) throw new TypeError("Playground height must be a positive finite number");
1904
+ }
1905
+ /**
1906
+ * @param operation 请求操作。
1907
+ * @param id 请求标识。
1908
+ * @param height iframe 最小高度。
1909
+ */
1910
+ /* istanbul ignore next -- 构造和销毁时会从序列化后的 iframe class 调用该方法。 */
1911
+ post(operation, id = 0, height = 0) {
1912
+ this.target.parent.postMessage({
1913
+ action: "docs:height",
1914
+ session: this.session,
1915
+ operation,
1916
+ id,
1917
+ height
1918
+ }, "*");
1919
+ }
1920
+ /**
1921
+ * @param height iframe 最小高度。
1922
+ * @returns 内部等待与释放句柄。
1923
+ */
1924
+ createRequest(height) {
1925
+ this.validateHeight(height);
1926
+ const id = ++this.sequence;
1927
+ let released = false;
1928
+ let settled = false;
1929
+ let frame = 0;
1930
+ let timer = 0;
1931
+ let stopWatch;
1932
+ let resolveReady;
1933
+ let resolveClosed;
1934
+ const ready = new Promise((resolve) => {
1935
+ resolveReady = resolve;
1936
+ });
1937
+ const closed = new Promise((resolve) => {
1938
+ resolveClosed = resolve;
1939
+ });
1940
+ const stopWaiting = () => {
1941
+ this.target.cancelAnimationFrame(frame);
1942
+ if (timer) {
1943
+ this.target.clearTimeout(timer);
1944
+ timer = 0;
1945
+ }
1946
+ this.target.removeEventListener("message", onMessage);
1947
+ };
1948
+ const release = () => {
1949
+ if (released) return;
1950
+ released = true;
1951
+ stopWaiting();
1952
+ stopWatch?.();
1953
+ resolveReady(false);
1954
+ resolveClosed();
1955
+ this.requests.delete(request);
1956
+ this.post("release", id);
1957
+ };
1958
+ const check = () => {
1959
+ if (released) return;
1960
+ if (this.target.innerHeight >= height) {
1961
+ settled = true;
1962
+ stopWaiting();
1963
+ resolveReady(true);
1964
+ } else frame = this.target.requestAnimationFrame(check);
1965
+ };
1966
+ const onMessage = (event) => {
1967
+ const data = event.data;
1968
+ if (event.source !== this.target.parent || data?.action !== "docs:height:applied" || data.session !== this.session || data.id !== id || settled) return;
1969
+ if (!data.accepted) release();
1970
+ else check();
1971
+ };
1972
+ const request = {
1973
+ ready,
1974
+ closed,
1975
+ get released() {
1976
+ return released;
1977
+ },
1978
+ release,
1979
+ watch: (visible) => {
1980
+ stopWatch = this.vue.watch(() => this.vue.toValue(visible), (value) => {
1981
+ if (!value) release();
1982
+ }, { flush: "sync" });
1983
+ }
1984
+ };
1985
+ this.requests.add(request);
1986
+ if (this.disposed) release();
1987
+ else {
1988
+ this.target.addEventListener("message", onMessage);
1989
+ timer = this.target.setTimeout(() => {
1990
+ timer = 0;
1991
+ release();
1992
+ }, 2e3);
1993
+ this.post("enable", id, height);
1994
+ }
1995
+ return request;
1996
+ }
1997
+ };
1998
+ var PREVIEW_SERVICE_IMPORT_CODE = "import * as __docsPreviewVue from \"vue\"";
1999
+ var PREVIEW_SERVICE_USE_CODE = [
2000
+ `;const __docsPlayground=new (${PlaygroundService.toString()})(window,__docsPreviewVue);`,
2001
+ "app.provide(\"docs:playground\",__docsPlayground);",
2002
+ "window.$docsPlayground=__docsPlayground;",
2003
+ "app.mixin({beforeUnmount(){if(this===this.$root)__docsPlayground.dispose()}});"
2004
+ ].join("");
2005
+ //#endregion
1590
2006
  //#region packages/playground/src/core/scss.ts
1591
2007
  var SCHEME = "docs-playground";
1592
2008
  var STYLE_EXTS = [".scss", ".sass"];
@@ -1803,6 +2219,7 @@ var bindPlaygroundScss = (store) => {
1803
2219
  //#region packages/playground/src/core/store.ts
1804
2220
  var DOCS_LINK_IMPORT_CODE = "import { h as __docsH } from \"vue\"";
1805
2221
  var DOCS_LINK_USE_CODE = [
2222
+ PREVIEW_SCROLL_RESET_CODE,
1806
2223
  "app.component(\"DocsLink\",{",
1807
2224
  "props:{to:{type:String,default:\"\"}},",
1808
2225
  "setup(props,{slots}){return()=>__docsH(\"a\",{",
@@ -1819,26 +2236,32 @@ var escapeHtmlAttr = (value) => value.replace(/&/gu, "&amp;").replace(/"/gu, "&q
1819
2236
  * `--vc-background-color-light` 不同,否则会出现色带割裂。
1820
2237
  */
1821
2238
  var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-light, var(--docs-background-color, #fff))";
1822
- var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL) => {
2239
+ var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL, options = {}) => {
2240
+ const previewScroller = options.previewScroller === true;
2241
+ const hrefs = Object.values({
2242
+ ...createBuiltinStyles(cdnURL),
2243
+ ...getPlaygroundSiteStyles(),
2244
+ ...getPlaygroundStyleOverrides()
2245
+ });
2246
+ const styleBlocks = [
2247
+ `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
2248
+ "body{color:var(--vc-foreground-color,#080f20)}",
2249
+ ...previewScroller ? [PREVIEW_SCROLLER_STYLE] : []
2250
+ ];
1823
2251
  return {
1824
2252
  showRuntimeError: false,
1825
2253
  showRuntimeWarning: false,
1826
2254
  headHTML: [
1827
2255
  SANDBOX_RUNTIME_ERROR_CAPTURE_HTML,
1828
2256
  "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
1829
- ...Object.values({
1830
- ...createBuiltinStyles(cdnURL),
1831
- ...getPlaygroundSiteStyles(),
1832
- ...getPlaygroundStyleOverrides()
1833
- }).map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
2257
+ ...hrefs.map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1834
2258
  "<style>",
1835
- `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1836
- "body{color:var(--vc-foreground-color,#080f20)}",
2259
+ ...styleBlocks,
1837
2260
  "</style>"
1838
2261
  ].join("\n"),
1839
2262
  customCode: {
1840
- importCode: DOCS_LINK_IMPORT_CODE,
1841
- useCode: DOCS_LINK_USE_CODE
2263
+ importCode: `${DOCS_LINK_IMPORT_CODE}\n${PREVIEW_SERVICE_IMPORT_CODE}`,
2264
+ useCode: `${DOCS_LINK_USE_CODE}\n${PREVIEW_SERVICE_USE_CODE}`
1842
2265
  }
1843
2266
  };
1844
2267
  };
@@ -1919,6 +2342,8 @@ var Alone = new Portal(/* @__PURE__ */ defineComponent({
1919
2342
  const { t } = useLocale();
1920
2343
  const currentViewport = ref(props.viewport);
1921
2344
  const sandboxRef = ref(null);
2345
+ const availableHeight = () => Math.max(0, getWindowInnerSize().height - 40 - 44);
2346
+ const temporaryHeight = usePreviewHeightBridge(sandboxRef, availableHeight);
1922
2347
  const sandboxKey = ref(0);
1923
2348
  const layoutTick = ref(0);
1924
2349
  const runtimeError = useSandboxRuntimeErrorGuard(sandboxRef);
@@ -1930,7 +2355,16 @@ var Alone = new Portal(/* @__PURE__ */ defineComponent({
1930
2355
  });
1931
2356
  const layout = computed(() => {
1932
2357
  layoutTick.value;
1933
- return resolvePopupLayout(currentViewport.value);
2358
+ const base = resolvePopupLayout(currentViewport.value);
2359
+ if (!temporaryHeight.value) return base;
2360
+ const canvasHeight = Math.min(availableHeight(), Math.max(base.canvasHeight, temporaryHeight.value));
2361
+ const shellHeight = Math.max(base.shellHeight, canvasHeight + 44);
2362
+ return {
2363
+ ...base,
2364
+ canvasHeight,
2365
+ shellHeight,
2366
+ bodyHeight: shellHeight - 44
2367
+ };
1934
2368
  });
1935
2369
  const shellStyle = computed(() => ({
1936
2370
  width: `${layout.value.shellWidth}px`,
@@ -2091,6 +2525,10 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2091
2525
  options: {},
2092
2526
  previewInset: { default: 10 },
2093
2527
  previewOptions: {},
2528
+ previewScroller: {
2529
+ type: Boolean,
2530
+ default: false
2531
+ },
2094
2532
  styleless: {
2095
2533
  type: Boolean,
2096
2534
  default: false
@@ -2124,7 +2562,7 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2124
2562
  const emit = __emit;
2125
2563
  const joinCode = (...values) => values.filter(Boolean).join("\n");
2126
2564
  const mergePreviewOptions = (extraHeadHTML) => {
2127
- const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL);
2565
+ const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL, { previewScroller: props.previewScroller });
2128
2566
  return {
2129
2567
  ...runtimePreviewOptions,
2130
2568
  ...props.previewOptions,
@@ -2134,8 +2572,8 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2134
2572
  extraHeadHTML
2135
2573
  ].filter(Boolean).join("\n"),
2136
2574
  customCode: {
2137
- importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode),
2138
- useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode)
2575
+ importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode, props.previewScroller ? PREVIEW_SCROLL_RESET_CODE : void 0, props.previewScroller ? PREVIEW_SCROLLER_IMPORT_CODE : void 0),
2576
+ useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode, props.previewScroller ? PREVIEW_SCROLLER_USE_CODE : void 0)
2139
2577
  }
2140
2578
  };
2141
2579
  };
@@ -2151,6 +2589,7 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2151
2589
  const copyValue = computed(() => props.files[props.entry] || "");
2152
2590
  const store = createRuntimeStore(props.files, props.entry, props.options);
2153
2591
  const sandboxRef = ref(null);
2592
+ const temporaryHeight = usePreviewHeightBridge(sandboxRef);
2154
2593
  const sandboxKey = ref(0);
2155
2594
  const runtimeHeight = useSandboxAutoHeight(sandboxRef);
2156
2595
  const runtimeError = useSandboxRuntimeErrorGuard(sandboxRef);
@@ -2178,13 +2617,21 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2178
2617
  const error = root.querySelector(".docs-playground__runtime-error");
2179
2618
  return (header instanceof HTMLElement ? header.offsetHeight : 0) + (error instanceof HTMLElement ? error.offsetHeight : 0);
2180
2619
  };
2620
+ const normalizedPreviewInset = computed(() => {
2621
+ const value = props.previewInset;
2622
+ if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
2623
+ if (Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "number" && Number.isFinite(item) && item >= 0)) return [value[0], value[1]];
2624
+ return [10, 10];
2625
+ });
2181
2626
  const measureExpandedPreviewHeight = () => {
2182
2627
  if (typeof window === "undefined") return runtimeHeight.value;
2183
2628
  const chromeHeight = measurePreviewChromeHeight();
2629
+ const viewport = getVisibleViewportRect(runtimeRoot.value);
2630
+ const [vertical] = normalizedPreviewInset.value;
2184
2631
  return resolveExpandedPreviewHeight(true, resolveRemainingPreviewHeight({
2185
- viewportHeight: getVisibleViewportRect(runtimeRoot.value).height,
2632
+ viewportHeight: viewport.height,
2186
2633
  chromeHeight
2187
- }));
2634
+ }) - vertical * 2 - 2);
2188
2635
  };
2189
2636
  const syncFrozenExpandedHeight = () => {
2190
2637
  if (!previewExpanded.value || props.expandable !== true) return;
@@ -2196,13 +2643,14 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2196
2643
  }
2197
2644
  const canExpandPreview = computed(() => !props.styleless && isPlaygroundExpandable(props.expandable));
2198
2645
  const expandLabel = computed(() => t(previewExpanded.value ? "playground.runtime.collapsePreview" : "playground.runtime.expandPreview"));
2199
- const desiredViewportHeight = computed(() => {
2646
+ const baseViewportHeight = computed(() => {
2200
2647
  if (previewExpanded.value && props.expandable === true) return frozenExpandedHeight.value;
2201
2648
  if (previewExpanded.value && isPlaygroundExpandable(props.expandable)) return resolveExpandedPreviewHeight(props.expandable, 0);
2202
2649
  const fixedHeight = getViewportHeight(props.viewport);
2203
2650
  if (fixedHeight) return fixedHeight;
2204
2651
  return runtimeHeight.value;
2205
2652
  });
2653
+ const desiredViewportHeight = computed(() => Math.max(baseViewportHeight.value, temporaryHeight.value));
2206
2654
  const scrollExpandedPreviewIntoView = () => {
2207
2655
  scrollPlaygroundToViewportStart(runtimeRoot.value);
2208
2656
  };
@@ -2224,16 +2672,10 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2224
2672
  frozenExpandedHeight.value = 0;
2225
2673
  }
2226
2674
  });
2227
- const normalizedPreviewInset = computed(() => {
2228
- const value = props.previewInset;
2229
- if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
2230
- if (Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "number" && Number.isFinite(item) && item >= 0)) return [value[0], value[1]];
2231
- return [10, 10];
2232
- });
2233
2675
  const previewStyle = computed(() => {
2234
2676
  const [vertical, horizontal] = normalizedPreviewInset.value;
2235
2677
  return {
2236
- height: `${desiredViewportHeight.value + vertical * 2}px`,
2678
+ height: `${resolvePreviewBoxHeight(desiredViewportHeight.value, vertical)}px`,
2237
2679
  padding: vertical === horizontal ? `${vertical}px` : `${vertical}px ${horizontal}px`,
2238
2680
  background: PLAYGROUND_RUNTIME_CANVAS_BACKGROUND
2239
2681
  };
@@ -2446,6 +2888,10 @@ var playground_default = /* @__PURE__ */ defineComponent({
2446
2888
  expandable: { type: [Boolean, Number] },
2447
2889
  title: { default: "" },
2448
2890
  id: { default: "" },
2891
+ previewScroller: {
2892
+ type: Boolean,
2893
+ default: false
2894
+ },
2449
2895
  previewOptions: {},
2450
2896
  locale: {}
2451
2897
  },
@@ -2603,6 +3049,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2603
3049
  options: __props.options,
2604
3050
  "preview-options": __props.previewOptions,
2605
3051
  "preview-inset": __props.previewInset,
3052
+ "preview-scroller": __props.previewScroller,
2606
3053
  expandable: __props.expandable,
2607
3054
  styleless: true,
2608
3055
  title: __props.title,
@@ -2618,6 +3065,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2618
3065
  "options",
2619
3066
  "preview-options",
2620
3067
  "preview-inset",
3068
+ "preview-scroller",
2621
3069
  "expandable",
2622
3070
  "title",
2623
3071
  "id",
@@ -2670,6 +3118,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2670
3118
  options: __props.options,
2671
3119
  "preview-options": __props.previewOptions,
2672
3120
  "preview-inset": __props.previewInset,
3121
+ "preview-scroller": __props.previewScroller,
2673
3122
  expandable: __props.expandable,
2674
3123
  "hide-chrome": true,
2675
3124
  title: __props.title,
@@ -2687,6 +3136,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2687
3136
  "options",
2688
3137
  "preview-options",
2689
3138
  "preview-inset",
3139
+ "preview-scroller",
2690
3140
  "expandable",
2691
3141
  "title",
2692
3142
  "id",
@@ -2715,4 +3165,4 @@ var playground_default = /* @__PURE__ */ defineComponent({
2715
3165
  }
2716
3166
  });
2717
3167
  //#endregion
2718
- export { code_preview_default as CodePreview, DEFAULT_CDN_URL, Editor, playground_default as Playground, applyPlaygroundImportMapOverride, applyPlaygroundStyleOverride, clearPlaygroundMaps, createBuiltinImports, createBuiltinStyles, createRuntimePreviewOptions, getPlaygroundImportMapOverrides, getPlaygroundSiteModules, getPlaygroundSiteStyles, getPlaygroundStyleOverrides, highlightCode, highlightCodeByLanguage, isSafePlaygroundHref, normalizeCdnURL, registerVueHighlight, removePlaygroundImportMapOverride, removePlaygroundStyleOverride, resolveHighlightLanguage, resolvePlaygroundTitleId, setPlaygroundImportMapOverrides, setPlaygroundSiteModules, setPlaygroundSiteStyles, setPlaygroundStyleOverrides, slugifyPlaygroundTitle, vueHighlight };
3168
+ export { code_preview_default as CodePreview, DEFAULT_CDN_URL, Editor, PREVIEW_SCROLLER_USE_CODE, PREVIEW_SCROLL_CONTENT_CLASS, PREVIEW_SCROLL_HTML_CLASS, playground_default as Playground, PlaygroundService, applyPlaygroundImportMapOverride, applyPlaygroundStyleOverride, clearPlaygroundMaps, createBuiltinImports, createBuiltinStyles, createRuntimePreviewOptions, getPlaygroundImportMapOverrides, getPlaygroundSiteModules, getPlaygroundSiteStyles, getPlaygroundStyleOverrides, highlightCode, highlightCodeByLanguage, isSafePlaygroundHref, normalizeCdnURL, registerVueHighlight, removePlaygroundImportMapOverride, removePlaygroundStyleOverride, resolveHighlightLanguage, resolvePlaygroundTitleId, setPlaygroundImportMapOverrides, setPlaygroundSiteModules, setPlaygroundSiteStyles, setPlaygroundStyleOverrides, slugifyPlaygroundTitle, vueHighlight };