@deot/docs-playground 1.1.0 → 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/README.md CHANGED
@@ -52,6 +52,7 @@ const files = ref({
52
52
  | `title` | `string` | `''` | 顶栏标题;空串或不传不渲染标题文本。runtime / 双视图始终保留顶栏;files-only 仅在有标题时显示顶栏。带标题时生成锚点(`#` 链接),规则对齐 markdown-it-anchor。 |
53
53
  | `id` | `string` | `''` | 标题锚点 id;未传时从 `title` 自动生成。仅挂在内联 runtime 标题上,弹窗不重复。 |
54
54
  | `expandable` | `true \| number` | `undefined` | 开启预览高度展开;未传不显示控件;`true` 展开到剩余视口;正数为目标高度(px)。 |
55
+ | `previewScroller` | `boolean` | `false` | 在 iframe 内用 `@deot/vc` Scroller 替换原生滚动条;开启后会动态探测 `Scroller`,不存在则回退原生滚动。 |
55
56
  | `options` | `PlaygroundOptions` | `{}` | 传给 Vue REPL store 的实例级选项;`cdnURL` 会同时作用于预览样式和默认 import map。 |
56
57
  | `previewOptions` | `SandboxProps['previewOptions']` | `undefined` | 传给当前 iframe 的 preview 选项。 |
57
58
  | `locale` | `Language` | `en-US` | 界面语言;未传入时使用上层 Locale Provider。 |
package/dist/index.cjs CHANGED
@@ -177,10 +177,10 @@ var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
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";
@@ -252,11 +252,19 @@ 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$6, [(0, vue.createElementVNode)("code", {
257
- class: "hljs",
258
- innerHTML: highlightedCode.value
259
- }, null, 8, _hoisted_4$6)])
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
  }
@@ -852,19 +860,112 @@ var resolvePlaygroundTitleId = (title, explicitId = "", isOccupied) => {
852
860
  while (isOccupied(`${base}-${index}`)) index += 1;
853
861
  return `${base}-${index}`;
854
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("");
855
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
+ };
856
922
  var useSandboxAutoHeight = (sandboxRef) => {
857
923
  const height = (0, vue.ref)(24);
858
924
  let container = null;
859
925
  let iframe = null;
860
926
  let containerObserver = null;
861
927
  let contentObserver = null;
928
+ let scrollMountObserver = null;
929
+ let observedScrollNode = null;
862
930
  let frameId = 0;
863
931
  const cancelMeasure = () => {
864
932
  if (!frameId) return;
865
933
  cancelAnimationFrame(frameId);
866
934
  frameId = 0;
867
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
+ };
868
969
  const measure = () => {
869
970
  frameId = 0;
870
971
  if (!iframe) return;
@@ -872,21 +973,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
872
973
  const iframeWindow = iframe.contentWindow;
873
974
  const iframeDocument = iframe.contentDocument;
874
975
  if (!iframeWindow || !iframeDocument?.body) return;
875
- const { body, documentElement } = iframeDocument;
876
- const bodyStyle = iframeWindow.getComputedStyle(body);
877
- const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
878
- const rootTop = documentElement.getBoundingClientRect().top;
879
- const bodyRect = body.getBoundingClientRect();
880
- const collapsedTop = Math.max(0, bodyRect.top - rootTop);
881
- let childrenBottom = 0;
882
- for (const child of body.children) {
883
- const rect = child.getBoundingClientRect();
884
- const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
885
- childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
886
- }
887
- const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
888
- const overflowingHeight = scrollHeight > iframe.clientHeight + 1 ? scrollHeight : 0;
889
- 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);
890
979
  const nextHeight = Math.max(contentHeight, 24);
891
980
  if (height.value !== nextHeight) height.value = nextHeight;
892
981
  } catch {
@@ -900,6 +989,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
900
989
  const disconnectContent = () => {
901
990
  contentObserver?.disconnect();
902
991
  contentObserver = null;
992
+ scrollMountObserver?.disconnect();
993
+ scrollMountObserver = null;
994
+ observedScrollNode = null;
903
995
  cancelMeasure();
904
996
  };
905
997
  const observeContent = () => {
@@ -917,7 +1009,28 @@ var useSandboxAutoHeight = (sandboxRef) => {
917
1009
  observer.observe(iframeDocument.documentElement);
918
1010
  observer.observe(iframeDocument.body);
919
1011
  contentObserver = observer;
920
- 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();
921
1034
  } catch {
922
1035
  if (height.value !== 24) height.value = 24;
923
1036
  }
@@ -961,6 +1074,12 @@ var useSandboxAutoHeight = (sandboxRef) => {
961
1074
  });
962
1075
  return height;
963
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
+ };
964
1083
  var CLIPPING_OVERFLOW = /* @__PURE__ */ new Set([
965
1084
  "auto",
966
1085
  "scroll",
@@ -1830,6 +1949,7 @@ var bindPlaygroundScss = (store) => {
1830
1949
  //#region packages/playground/src/core/store.ts
1831
1950
  var DOCS_LINK_IMPORT_CODE = "import { h as __docsH } from \"vue\"";
1832
1951
  var DOCS_LINK_USE_CODE = [
1952
+ PREVIEW_SCROLL_RESET_CODE,
1833
1953
  "app.component(\"DocsLink\",{",
1834
1954
  "props:{to:{type:String,default:\"\"}},",
1835
1955
  "setup(props,{slots}){return()=>__docsH(\"a\",{",
@@ -1846,21 +1966,27 @@ var escapeHtmlAttr = (value) => value.replace(/&/gu, "&").replace(/"/gu, "&q
1846
1966
  * `--vc-background-color-light` 不同,否则会出现色带割裂。
1847
1967
  */
1848
1968
  var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-light, var(--docs-background-color, #fff))";
1849
- 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
+ ];
1850
1981
  return {
1851
1982
  showRuntimeError: false,
1852
1983
  showRuntimeWarning: false,
1853
1984
  headHTML: [
1854
1985
  SANDBOX_RUNTIME_ERROR_CAPTURE_HTML,
1855
1986
  "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
1856
- ...Object.values({
1857
- ...createBuiltinStyles(cdnURL),
1858
- ...getPlaygroundSiteStyles(),
1859
- ...getPlaygroundStyleOverrides()
1860
- }).map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1987
+ ...hrefs.map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1861
1988
  "<style>",
1862
- `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1863
- "body{color:var(--vc-foreground-color,#080f20)}",
1989
+ ...styleBlocks,
1864
1990
  "</style>"
1865
1991
  ].join("\n"),
1866
1992
  customCode: {
@@ -2121,6 +2247,10 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2121
2247
  options: {},
2122
2248
  previewInset: { default: 10 },
2123
2249
  previewOptions: {},
2250
+ previewScroller: {
2251
+ type: Boolean,
2252
+ default: false
2253
+ },
2124
2254
  styleless: {
2125
2255
  type: Boolean,
2126
2256
  default: false
@@ -2154,7 +2284,7 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2154
2284
  const emit = __emit;
2155
2285
  const joinCode = (...values) => values.filter(Boolean).join("\n");
2156
2286
  const mergePreviewOptions = (extraHeadHTML) => {
2157
- const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL);
2287
+ const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL, { previewScroller: props.previewScroller });
2158
2288
  return {
2159
2289
  ...runtimePreviewOptions,
2160
2290
  ...props.previewOptions,
@@ -2164,8 +2294,8 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2164
2294
  extraHeadHTML
2165
2295
  ].filter(Boolean).join("\n"),
2166
2296
  customCode: {
2167
- importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode),
2168
- 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)
2169
2299
  }
2170
2300
  };
2171
2301
  };
@@ -2208,13 +2338,21 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2208
2338
  const error = root.querySelector(".docs-playground__runtime-error");
2209
2339
  return (header instanceof HTMLElement ? header.offsetHeight : 0) + (error instanceof HTMLElement ? error.offsetHeight : 0);
2210
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
+ });
2211
2347
  const measureExpandedPreviewHeight = () => {
2212
2348
  if (typeof window === "undefined") return runtimeHeight.value;
2213
2349
  const chromeHeight = measurePreviewChromeHeight();
2350
+ const viewport = getVisibleViewportRect(runtimeRoot.value);
2351
+ const [vertical] = normalizedPreviewInset.value;
2214
2352
  return resolveExpandedPreviewHeight(true, resolveRemainingPreviewHeight({
2215
- viewportHeight: getVisibleViewportRect(runtimeRoot.value).height,
2353
+ viewportHeight: viewport.height,
2216
2354
  chromeHeight
2217
- }));
2355
+ }) - vertical * 2 - 2);
2218
2356
  };
2219
2357
  const syncFrozenExpandedHeight = () => {
2220
2358
  if (!previewExpanded.value || props.expandable !== true) return;
@@ -2254,16 +2392,10 @@ var runtime_default = /* @__PURE__ */ (0, vue.defineComponent)({
2254
2392
  frozenExpandedHeight.value = 0;
2255
2393
  }
2256
2394
  });
2257
- const normalizedPreviewInset = (0, vue.computed)(() => {
2258
- const value = props.previewInset;
2259
- if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
2260
- if (Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "number" && Number.isFinite(item) && item >= 0)) return [value[0], value[1]];
2261
- return [10, 10];
2262
- });
2263
2395
  const previewStyle = (0, vue.computed)(() => {
2264
2396
  const [vertical, horizontal] = normalizedPreviewInset.value;
2265
2397
  return {
2266
- height: `${desiredViewportHeight.value + vertical * 2}px`,
2398
+ height: `${resolvePreviewBoxHeight(desiredViewportHeight.value, vertical)}px`,
2267
2399
  padding: vertical === horizontal ? `${vertical}px` : `${vertical}px ${horizontal}px`,
2268
2400
  background: PLAYGROUND_RUNTIME_CANVAS_BACKGROUND
2269
2401
  };
@@ -2476,6 +2608,10 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2476
2608
  expandable: { type: [Boolean, Number] },
2477
2609
  title: { default: "" },
2478
2610
  id: { default: "" },
2611
+ previewScroller: {
2612
+ type: Boolean,
2613
+ default: false
2614
+ },
2479
2615
  previewOptions: {},
2480
2616
  locale: {}
2481
2617
  },
@@ -2633,6 +2769,7 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2633
2769
  options: __props.options,
2634
2770
  "preview-options": __props.previewOptions,
2635
2771
  "preview-inset": __props.previewInset,
2772
+ "preview-scroller": __props.previewScroller,
2636
2773
  expandable: __props.expandable,
2637
2774
  styleless: true,
2638
2775
  title: __props.title,
@@ -2648,6 +2785,7 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2648
2785
  "options",
2649
2786
  "preview-options",
2650
2787
  "preview-inset",
2788
+ "preview-scroller",
2651
2789
  "expandable",
2652
2790
  "title",
2653
2791
  "id",
@@ -2700,6 +2838,7 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2700
2838
  options: __props.options,
2701
2839
  "preview-options": __props.previewOptions,
2702
2840
  "preview-inset": __props.previewInset,
2841
+ "preview-scroller": __props.previewScroller,
2703
2842
  expandable: __props.expandable,
2704
2843
  "hide-chrome": true,
2705
2844
  title: __props.title,
@@ -2717,6 +2856,7 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2717
2856
  "options",
2718
2857
  "preview-options",
2719
2858
  "preview-inset",
2859
+ "preview-scroller",
2720
2860
  "expandable",
2721
2861
  "title",
2722
2862
  "id",
@@ -2748,6 +2888,9 @@ var playground_default = /* @__PURE__ */ (0, vue.defineComponent)({
2748
2888
  exports.CodePreview = code_preview_default;
2749
2889
  exports.DEFAULT_CDN_URL = DEFAULT_CDN_URL;
2750
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;
2751
2894
  exports.Playground = playground_default;
2752
2895
  exports.applyPlaygroundImportMapOverride = applyPlaygroundImportMapOverride;
2753
2896
  exports.applyPlaygroundStyleOverride = applyPlaygroundStyleOverride;
package/dist/index.d.ts CHANGED
@@ -38,6 +38,7 @@ entry: string;
38
38
  modelValue: string;
39
39
  options: PlaygroundOptions;
40
40
  previewInset: PlaygroundPreviewInset;
41
+ previewScroller: PlaygroundPreviewScroller;
41
42
  styleless: boolean;
42
43
  id: string;
43
44
  }, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
@@ -63,6 +64,7 @@ declare type __VLS_Props_2 = {
63
64
  expandable?: PlaygroundExpandable;
64
65
  title?: string;
65
66
  id?: string;
67
+ previewScroller?: PlaygroundPreviewScroller;
66
68
  previewOptions?: PlaygroundPreviewOptions;
67
69
  locale?: Language;
68
70
  };
@@ -83,7 +85,11 @@ export declare const createBuiltinImports: (cdnURL?: string) => Record<string, s
83
85
 
84
86
  export declare const createBuiltinStyles: (cdnURL?: string) => Record<string, string>;
85
87
 
86
- export declare const createRuntimePreviewOptions: (cdnURL?: string) => NonNullable<SandboxProps["previewOptions"]>;
88
+ declare interface CreateRuntimePreviewOptions {
89
+ previewScroller?: boolean;
90
+ }
91
+
92
+ export declare const createRuntimePreviewOptions: (cdnURL?: string, options?: CreateRuntimePreviewOptions) => NonNullable<SandboxProps["previewOptions"]>;
87
93
 
88
94
  export declare const DEFAULT_CDN_URL = "https://cdn.jsdelivr.net/npm";
89
95
 
@@ -179,6 +185,8 @@ export declare type PlaygroundPreviewInset = number | [vertical: number, horizon
179
185
 
180
186
  export declare type PlaygroundPreviewOptions = SandboxProps['previewOptions'];
181
187
 
188
+ export declare type PlaygroundPreviewScroller = boolean;
189
+
182
190
  export declare type PlaygroundView = 'runtime' | 'files';
183
191
 
184
192
  export declare type PlaygroundViewport = 'auto' | number | [width: number, height: number];
@@ -188,6 +196,12 @@ export declare interface PlaygroundViewsProps {
188
196
  activeView: PlaygroundView;
189
197
  }
190
198
 
199
+ export declare const PREVIEW_SCROLL_CONTENT_CLASS = "docs-playground-preview-scroll__content";
200
+
201
+ export declare const PREVIEW_SCROLL_HTML_CLASS = "docs-playground-preview-scroll";
202
+
203
+ export declare const PREVIEW_SCROLLER_USE_CODE: string;
204
+
191
205
  export declare const registerVueHighlight: (api?: HLJSApi) => HLJSApi;
192
206
 
193
207
  export declare const removePlaygroundImportMapOverride: (alias: string) => {
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,112 @@ 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
+ "}",
873
+ "window.__app__=next;",
874
+ `document.documentElement.classList.add("${PREVIEW_SCROLL_HTML_CLASS}");`,
875
+ "return next.mount(container,...rest)",
876
+ "}",
877
+ "})()"
878
+ ].join("");
879
+ var PREVIEW_SCROLLER_STYLE = [
880
+ `html.${PREVIEW_SCROLL_HTML_CLASS},`,
881
+ `html.${PREVIEW_SCROLL_HTML_CLASS} body,`,
882
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app{`,
883
+ "height:100%;min-height:100%;margin:0;overflow:hidden",
884
+ "}",
885
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app>.vc-scroller,`,
886
+ `html.${PREVIEW_SCROLL_HTML_CLASS} #app>.vc-scroller>.vc-scroller__wrapper{`,
887
+ "height:100%;max-height:100%;box-sizing:border-box",
888
+ "}"
889
+ ].join("");
828
890
  var resolveSandboxContainer = (sandbox) => sandbox?.container ? unref(sandbox.container) : null;
891
+ var resolveScrollContent = (iframeDocument) => {
892
+ if (!iframeDocument.documentElement.classList.contains("docs-playground-preview-scroll")) return null;
893
+ return iframeDocument.querySelector(`.${PREVIEW_SCROLL_CONTENT_CLASS}`);
894
+ };
829
895
  var useSandboxAutoHeight = (sandboxRef) => {
830
896
  const height = ref(24);
831
897
  let container = null;
832
898
  let iframe = null;
833
899
  let containerObserver = null;
834
900
  let contentObserver = null;
901
+ let scrollMountObserver = null;
902
+ let observedScrollNode = null;
835
903
  let frameId = 0;
836
904
  const cancelMeasure = () => {
837
905
  if (!frameId) return;
838
906
  cancelAnimationFrame(frameId);
839
907
  frameId = 0;
840
908
  };
909
+ const measureDocumentContent = (iframeWindow, iframeDocument) => {
910
+ const { body, documentElement } = iframeDocument;
911
+ const bodyStyle = iframeWindow.getComputedStyle(body);
912
+ const bodyMargins = (Number.parseFloat(bodyStyle.marginTop) || 0) + (Number.parseFloat(bodyStyle.marginBottom) || 0);
913
+ const rootTop = documentElement.getBoundingClientRect().top;
914
+ const bodyRect = body.getBoundingClientRect();
915
+ const collapsedTop = Math.max(0, bodyRect.top - rootTop);
916
+ let childrenBottom = 0;
917
+ for (const child of body.children) {
918
+ const rect = child.getBoundingClientRect();
919
+ const marginBottom = Number.parseFloat(iframeWindow.getComputedStyle(child).marginBottom) || 0;
920
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
921
+ }
922
+ const scrollHeight = Math.max(body.scrollHeight, documentElement.scrollHeight);
923
+ const overflowingHeight = scrollHeight > (iframe?.clientHeight || 0) + 1 ? scrollHeight : 0;
924
+ return Math.ceil(Math.max(body.offsetHeight + bodyMargins + collapsedTop, bodyRect.height + bodyMargins + collapsedTop, childrenBottom, overflowingHeight));
925
+ };
926
+ const measureScrollContent = (scrollContent) => {
927
+ const contentStyle = scrollContent.ownerDocument.defaultView?.getComputedStyle(scrollContent);
928
+ const margins = contentStyle ? (Number.parseFloat(contentStyle.marginTop) || 0) + (Number.parseFloat(contentStyle.marginBottom) || 0) : 0;
929
+ let childrenBottom = 0;
930
+ const rootTop = scrollContent.getBoundingClientRect().top;
931
+ for (const child of scrollContent.children) {
932
+ const rect = child.getBoundingClientRect();
933
+ const marginBottom = Number.parseFloat(scrollContent.ownerDocument.defaultView?.getComputedStyle(child).marginBottom || "0") || 0;
934
+ childrenBottom = Math.max(childrenBottom, rect.bottom + marginBottom - rootTop);
935
+ }
936
+ return Math.ceil(Math.max(scrollContent.scrollHeight + margins, scrollContent.offsetHeight + margins, childrenBottom + margins));
937
+ };
938
+ const isEmptyPreviewApp = (iframeDocument) => {
939
+ const app = iframeDocument.getElementById("app");
940
+ return !!app && app.childElementCount === 0;
941
+ };
841
942
  const measure = () => {
842
943
  frameId = 0;
843
944
  if (!iframe) return;
@@ -845,21 +946,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
845
946
  const iframeWindow = iframe.contentWindow;
846
947
  const iframeDocument = iframe.contentDocument;
847
948
  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));
949
+ const scrollContent = resolveScrollContent(iframeDocument);
950
+ if (!scrollContent && isEmptyPreviewApp(iframeDocument)) return;
951
+ const contentHeight = scrollContent ? measureScrollContent(scrollContent) : measureDocumentContent(iframeWindow, iframeDocument);
863
952
  const nextHeight = Math.max(contentHeight, 24);
864
953
  if (height.value !== nextHeight) height.value = nextHeight;
865
954
  } catch {
@@ -873,6 +962,9 @@ var useSandboxAutoHeight = (sandboxRef) => {
873
962
  const disconnectContent = () => {
874
963
  contentObserver?.disconnect();
875
964
  contentObserver = null;
965
+ scrollMountObserver?.disconnect();
966
+ scrollMountObserver = null;
967
+ observedScrollNode = null;
876
968
  cancelMeasure();
877
969
  };
878
970
  const observeContent = () => {
@@ -890,7 +982,28 @@ var useSandboxAutoHeight = (sandboxRef) => {
890
982
  observer.observe(iframeDocument.documentElement);
891
983
  observer.observe(iframeDocument.body);
892
984
  contentObserver = observer;
893
- scheduleMeasure();
985
+ const attachScrollContent = (node) => {
986
+ if (!node || !contentObserver) return false;
987
+ if (observedScrollNode === node) return true;
988
+ if (observedScrollNode) contentObserver.unobserve(observedScrollNode);
989
+ observedScrollNode = node;
990
+ contentObserver.observe(node);
991
+ return true;
992
+ };
993
+ const syncScrollContent = () => {
994
+ attachScrollContent(resolveScrollContent(iframeDocument));
995
+ scheduleMeasure();
996
+ };
997
+ scrollMountObserver = new MutationObserver(syncScrollContent);
998
+ scrollMountObserver.observe(iframeDocument.documentElement, {
999
+ attributes: true,
1000
+ attributeFilter: ["class"]
1001
+ });
1002
+ scrollMountObserver.observe(iframeDocument.body, {
1003
+ childList: true,
1004
+ subtree: true
1005
+ });
1006
+ syncScrollContent();
894
1007
  } catch {
895
1008
  if (height.value !== 24) height.value = 24;
896
1009
  }
@@ -934,6 +1047,12 @@ var useSandboxAutoHeight = (sandboxRef) => {
934
1047
  });
935
1048
  return height;
936
1049
  };
1050
+ var resolvePreviewBoxHeight = (contentHeight, verticalInset = 0, borderWidth = 1) => {
1051
+ const content = Number.isFinite(contentHeight) ? Math.max(0, contentHeight) : 0;
1052
+ const inset = Number.isFinite(verticalInset) ? Math.max(0, verticalInset) : 0;
1053
+ const border = Number.isFinite(borderWidth) ? Math.max(0, borderWidth) : 1;
1054
+ return content + inset * 2 + border * 2;
1055
+ };
937
1056
  var CLIPPING_OVERFLOW = /* @__PURE__ */ new Set([
938
1057
  "auto",
939
1058
  "scroll",
@@ -1803,6 +1922,7 @@ var bindPlaygroundScss = (store) => {
1803
1922
  //#region packages/playground/src/core/store.ts
1804
1923
  var DOCS_LINK_IMPORT_CODE = "import { h as __docsH } from \"vue\"";
1805
1924
  var DOCS_LINK_USE_CODE = [
1925
+ PREVIEW_SCROLL_RESET_CODE,
1806
1926
  "app.component(\"DocsLink\",{",
1807
1927
  "props:{to:{type:String,default:\"\"}},",
1808
1928
  "setup(props,{slots}){return()=>__docsH(\"a\",{",
@@ -1819,21 +1939,27 @@ var escapeHtmlAttr = (value) => value.replace(/&/gu, "&amp;").replace(/"/gu, "&q
1819
1939
  * `--vc-background-color-light` 不同,否则会出现色带割裂。
1820
1940
  */
1821
1941
  var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-light, var(--docs-background-color, #fff))";
1822
- var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL) => {
1942
+ var createRuntimePreviewOptions = (cdnURL = DEFAULT_CDN_URL, options = {}) => {
1943
+ const previewScroller = options.previewScroller === true;
1944
+ const hrefs = Object.values({
1945
+ ...createBuiltinStyles(cdnURL),
1946
+ ...getPlaygroundSiteStyles(),
1947
+ ...getPlaygroundStyleOverrides()
1948
+ });
1949
+ const styleBlocks = [
1950
+ `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1951
+ "body{color:var(--vc-foreground-color,#080f20)}",
1952
+ ...previewScroller ? [PREVIEW_SCROLLER_STYLE] : []
1953
+ ];
1823
1954
  return {
1824
1955
  showRuntimeError: false,
1825
1956
  showRuntimeWarning: false,
1826
1957
  headHTML: [
1827
1958
  SANDBOX_RUNTIME_ERROR_CAPTURE_HTML,
1828
1959
  "<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)}">`),
1960
+ ...hrefs.map((href) => `<link rel="stylesheet" href="${escapeHtmlAttr(href)}">`),
1834
1961
  "<style>",
1835
- `html,body{height:auto;min-height:0;background:${PLAYGROUND_RUNTIME_CANVAS_BACKGROUND}}`,
1836
- "body{color:var(--vc-foreground-color,#080f20)}",
1962
+ ...styleBlocks,
1837
1963
  "</style>"
1838
1964
  ].join("\n"),
1839
1965
  customCode: {
@@ -2091,6 +2217,10 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2091
2217
  options: {},
2092
2218
  previewInset: { default: 10 },
2093
2219
  previewOptions: {},
2220
+ previewScroller: {
2221
+ type: Boolean,
2222
+ default: false
2223
+ },
2094
2224
  styleless: {
2095
2225
  type: Boolean,
2096
2226
  default: false
@@ -2124,7 +2254,7 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2124
2254
  const emit = __emit;
2125
2255
  const joinCode = (...values) => values.filter(Boolean).join("\n");
2126
2256
  const mergePreviewOptions = (extraHeadHTML) => {
2127
- const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL);
2257
+ const runtimePreviewOptions = createRuntimePreviewOptions(props.options.cdnURL, { previewScroller: props.previewScroller });
2128
2258
  return {
2129
2259
  ...runtimePreviewOptions,
2130
2260
  ...props.previewOptions,
@@ -2134,8 +2264,8 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2134
2264
  extraHeadHTML
2135
2265
  ].filter(Boolean).join("\n"),
2136
2266
  customCode: {
2137
- importCode: joinCode(runtimePreviewOptions.customCode?.importCode, props.previewOptions?.customCode?.importCode),
2138
- useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode)
2267
+ 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),
2268
+ useCode: joinCode(runtimePreviewOptions.customCode?.useCode, props.previewOptions?.customCode?.useCode, props.previewScroller ? PREVIEW_SCROLLER_USE_CODE : void 0)
2139
2269
  }
2140
2270
  };
2141
2271
  };
@@ -2178,13 +2308,21 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2178
2308
  const error = root.querySelector(".docs-playground__runtime-error");
2179
2309
  return (header instanceof HTMLElement ? header.offsetHeight : 0) + (error instanceof HTMLElement ? error.offsetHeight : 0);
2180
2310
  };
2311
+ const normalizedPreviewInset = computed(() => {
2312
+ const value = props.previewInset;
2313
+ if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [value, value] : [10, 10];
2314
+ if (Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "number" && Number.isFinite(item) && item >= 0)) return [value[0], value[1]];
2315
+ return [10, 10];
2316
+ });
2181
2317
  const measureExpandedPreviewHeight = () => {
2182
2318
  if (typeof window === "undefined") return runtimeHeight.value;
2183
2319
  const chromeHeight = measurePreviewChromeHeight();
2320
+ const viewport = getVisibleViewportRect(runtimeRoot.value);
2321
+ const [vertical] = normalizedPreviewInset.value;
2184
2322
  return resolveExpandedPreviewHeight(true, resolveRemainingPreviewHeight({
2185
- viewportHeight: getVisibleViewportRect(runtimeRoot.value).height,
2323
+ viewportHeight: viewport.height,
2186
2324
  chromeHeight
2187
- }));
2325
+ }) - vertical * 2 - 2);
2188
2326
  };
2189
2327
  const syncFrozenExpandedHeight = () => {
2190
2328
  if (!previewExpanded.value || props.expandable !== true) return;
@@ -2224,16 +2362,10 @@ var runtime_default = /* @__PURE__ */ defineComponent({
2224
2362
  frozenExpandedHeight.value = 0;
2225
2363
  }
2226
2364
  });
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
2365
  const previewStyle = computed(() => {
2234
2366
  const [vertical, horizontal] = normalizedPreviewInset.value;
2235
2367
  return {
2236
- height: `${desiredViewportHeight.value + vertical * 2}px`,
2368
+ height: `${resolvePreviewBoxHeight(desiredViewportHeight.value, vertical)}px`,
2237
2369
  padding: vertical === horizontal ? `${vertical}px` : `${vertical}px ${horizontal}px`,
2238
2370
  background: PLAYGROUND_RUNTIME_CANVAS_BACKGROUND
2239
2371
  };
@@ -2446,6 +2578,10 @@ var playground_default = /* @__PURE__ */ defineComponent({
2446
2578
  expandable: { type: [Boolean, Number] },
2447
2579
  title: { default: "" },
2448
2580
  id: { default: "" },
2581
+ previewScroller: {
2582
+ type: Boolean,
2583
+ default: false
2584
+ },
2449
2585
  previewOptions: {},
2450
2586
  locale: {}
2451
2587
  },
@@ -2603,6 +2739,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2603
2739
  options: __props.options,
2604
2740
  "preview-options": __props.previewOptions,
2605
2741
  "preview-inset": __props.previewInset,
2742
+ "preview-scroller": __props.previewScroller,
2606
2743
  expandable: __props.expandable,
2607
2744
  styleless: true,
2608
2745
  title: __props.title,
@@ -2618,6 +2755,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2618
2755
  "options",
2619
2756
  "preview-options",
2620
2757
  "preview-inset",
2758
+ "preview-scroller",
2621
2759
  "expandable",
2622
2760
  "title",
2623
2761
  "id",
@@ -2670,6 +2808,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2670
2808
  options: __props.options,
2671
2809
  "preview-options": __props.previewOptions,
2672
2810
  "preview-inset": __props.previewInset,
2811
+ "preview-scroller": __props.previewScroller,
2673
2812
  expandable: __props.expandable,
2674
2813
  "hide-chrome": true,
2675
2814
  title: __props.title,
@@ -2687,6 +2826,7 @@ var playground_default = /* @__PURE__ */ defineComponent({
2687
2826
  "options",
2688
2827
  "preview-options",
2689
2828
  "preview-inset",
2829
+ "preview-scroller",
2690
2830
  "expandable",
2691
2831
  "title",
2692
2832
  "id",
@@ -2715,4 +2855,4 @@ var playground_default = /* @__PURE__ */ defineComponent({
2715
2855
  }
2716
2856
  });
2717
2857
  //#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 };
2858
+ 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, 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deot/docs-playground",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {