@deot/docs-playground 1.1.5 → 1.1.7

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
@@ -53,7 +53,7 @@ const files = ref({
53
53
  | `id` | `string` | `''` | 标题锚点 id;未传时从 `title` 自动生成。仅挂在内联 runtime 标题上,弹窗不重复。 |
54
54
  | `expandable` | `true \| number` | `undefined` | 开启预览高度展开;未传不显示控件;`true` 展开到剩余视口;正数为目标高度(px)。 |
55
55
  | `previewScroller` | `boolean` | `false` | 在 iframe 内用 `@deot/vc` Scroller 替换原生滚动条;开启后会动态探测 `Scroller`,不存在则回退原生滚动。 |
56
- | `local` | `boolean` | `false` | 在当前文档编译并挂载,不使用 iframe。`vue` 使用宿主同一份实例;其余 import map 仍是 URL,加载后作为模块变量。 |
56
+ | `local` | `boolean` | `true` | 在当前文档编译并挂载。内置模块默认继承宿主,可通过 import map 逐个覆盖;预览使用所选 Vue 运行时挂载。 |
57
57
  | `options` | `PlaygroundOptions` | `{}` | 传给 Vue REPL store 的实例级选项;`cdnURL` 会同时作用于预览样式和默认 import map。 |
58
58
  | `previewOptions` | `SandboxProps['previewOptions']` | `undefined` | 传给当前 iframe 的 preview 选项。 |
59
59
  | `locale` | `Language` | `en-US` | 界面语言;未传入时使用上层 Locale Provider。 |
@@ -149,18 +149,19 @@ const handleMessage = async () => {
149
149
 
150
150
  同时导出 `PlaygroundFiles`、`PlaygroundView`、`PlaygroundViewport`、`PlaygroundOptions`、`PlaygroundPreviewOptions`、`EditorFilesChange` 和 `EditorFilesChangeAction` 类型。
151
151
 
152
- 站点级 Playground 模块与预览 CSS 默认由 Client 的 `$docs.modules` 与 `$docs.styles` 管理(见 [`@deot/docs-client`](../client/README.md))。独立嵌入 Playground 时,可通过 `options.builtinImportMap` 与 `options.cdnURL` 覆盖实例级配置。
152
+ 站点级 Playground 模块与预览 CSS 默认由 Client 的 `$docs.modules` 与 `$docs.styles` 管理(见 [`@deot/docs-client`](../client/README.md))。模块地址优先级由低到高为:内置默认 → `$docs.modules` → 管理页覆盖 → 实例 `options.builtinImportMap.imports`。实例只覆盖声明的模块,其余继承默认配置;也可通过 `options.cdnURL` 调整默认 CDN。
153
153
 
154
154
  ## 运行环境
155
155
 
156
- - Playground 运行时通过公共 CDN 加载 Vue 和默认 import map 中的依赖,手工预览需要网络访问。
156
+ - iframe 运行时通过公共 CDN 加载 Vue 和默认 import map 中的依赖;local 运行时加载 URL 覆盖或宿主未提供的模块时也需要网络访问。
157
157
  - 默认 CDN 为 `https://cdn.jsdelivr.net/npm`,可通过 `options.cdnURL` 换成 unpkg 等兼容 `/{package}/{file}` 路径的镜像;该地址用于预览样式和内置 import map。
158
158
  - `lodash-es` 和在线 Sass 编译器使用 jsDelivr 的 `/+esm` 入口;unpkg 等 CDN 不支持该路径。
159
- - 直接渲染时,未配置的 npm 模块(含作用域包和子路径)自动从 jsDelivr 的 `/+esm` 入口加载;组件包的传递依赖也适用,`import()` 在调用时加载。`@deot/vc` 的组件、hooks 和 locale 走远程模块加载器,Vue 保持与宿主共用。可用 `options.builtinImportMap.imports` 指定依赖的版本和 URL。
159
+ - 直接渲染时,宿主未提供且未配置的 npm 模块(含作用域包和子路径)自动从 jsDelivr 的 `/+esm` 入口加载;组件包的传递依赖也适用,`import()` 在调用时加载。可用 `options.builtinImportMap.imports` 指定依赖的版本和 URL。
160
160
  - Vue SFC 的 `<style lang="scss">` / `lang="sass"` 以及独立 `.scss` / `.sass` 文件会在浏览器里编译;`_partial.scss` 只作为 `@use` 依赖。
161
- - `options.builtinImportMap.imports` 可以覆盖默认模块 URL。Vue 运行时仍从 `play.vuejs.org` 加载。
161
+ - local 模式按模块识别显式 URL 覆盖,即使地址与内置默认相同也会加载该 URL。覆盖 `vue` 时,预览的编译产物、挂载、组件引用和注入使用这份 Vue;内置组件、hooks、locale、Pinia 和 vue-router 随之重新加载以匹配 Vue,未覆盖的普通工具模块继续复用宿主。不同预览的覆盖互不影响,显式覆盖的模块在各自预览内执行。
162
+ - local 模式在当前页面按 URL 缓存外部模块的下载和转换成果,并合并并发请求;切换文档时可复用,最多保留最近使用的 128 个模块。各预览仍独立执行模块并解析依赖覆盖;失败的加载不缓存,同源模块实时读取,刷新页面会清空缓存。
162
163
  - 每个 Playground 实例都使用自己的 preview 配置和 iframe 消息来源校验。
163
- - `local` 默认开启:同一份 `files` 在当前文档里编译。内置依赖默认用宿主副本;`modules` 里与默认 CDN 不同的地址会覆盖,包括 `vue` 和 `vue/server-renderer`。直接渲染不撑开预览高度,`expandable` 固定为关闭,`docs:playground.run` 不会申请临时高度;`<DocsLink>` 仍通过 `docs:navigate` 通知。`local: false` 改回 iframe Sandbox。
164
+ - `local` 默认开启:同一份 `files` 在当前文档里编译。直接渲染不撑开预览高度,`expandable` 固定为关闭,`docs:playground.run` 不会申请临时高度;`<DocsLink>` 仍通过 `docs:navigate` 通知。`local: false` 改回 iframe Sandbox。
164
165
 
165
166
  ## 仓库内 examples
166
167
 
package/dist/index.cjs CHANGED
@@ -170,10 +170,10 @@ var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
170
170
  });
171
171
  //#endregion
172
172
  //#region packages/playground/src/core/code-preview/github.css?inline
173
- 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}";
173
+ 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}@media screen{[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}}";
174
174
  //#endregion
175
175
  //#region packages/playground/src/core/code-preview/code-preview.scss?inline
176
- 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;transition:opacity .15s}.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,background-color .15s;transition:opacity .15s,background-color .15s}.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}";
176
+ 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;transition:opacity .15s}.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-border-color,#d4d4d8));border-radius:8px;opacity:0;-webkit-transition:opacity .15s,background-color .15s;transition:opacity .15s,background-color .15s}.docs-code-preview__copy:hover{background:var(--docs-background-color,var(--vc-background-color-lightest,#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}";
177
177
  //#endregion
178
178
  //#region packages/playground/src/core/code-preview/style.ts
179
179
  var CODE_PREVIEW_STYLE_ID = "docs-code-preview-style";
@@ -2201,9 +2201,9 @@ var escapeHtmlAttr = (value) => value.replace(/&/gu, "&amp;").replace(/"/gu, "&q
2201
2201
  /**
2202
2202
  * runtime 画布色。`previewInset` 露出的 padding、iframe chrome 和 sandbox
2203
2203
  * `html`/`body` 必须共用这一变量,暗色下 `--docs-background-color` 与
2204
- * `--vc-background-color-light` 不同,否则会出现色带割裂。
2204
+ * `--vc-background-color-lightest` 不同,否则会出现色带割裂。
2205
2205
  */
2206
- var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-light, var(--docs-background-color, #fff))";
2206
+ var PLAYGROUND_RUNTIME_CANVAS_BACKGROUND = "var(--vc-background-color-lightest, var(--docs-background-color, #fff))";
2207
2207
  var createRuntimePreviewOptions = (cdnURL = require_css.DEFAULT_CDN_URL, options = {}) => {
2208
2208
  const previewScroller = options.previewScroller === true;
2209
2209
  const hrefs = Object.values({
@@ -2233,22 +2233,30 @@ var createRuntimePreviewOptions = (cdnURL = require_css.DEFAULT_CDN_URL, options
2233
2233
  }
2234
2234
  };
2235
2235
  };
2236
- var toReplFilename = (filename) => filename.startsWith("src/") ? filename : `src/${filename}`;
2236
+ var toReplFilename = (filename) => filename === "import-map.json" || filename.startsWith("src/") ? filename : `src/${filename}`;
2237
2237
  var createReplFile = (filename, code) => {
2238
2238
  const replFilename = toReplFilename(filename);
2239
2239
  return new _vue_repl.File(replFilename, code);
2240
2240
  };
2241
2241
  /**
2242
- * 默认 CDN → 站点 modules → 实例 builtinImportMap → 管理页覆盖。
2242
+ * 保留显式配置的模块,供 local 区分宿主默认与 URL 覆盖。
2243
+ * @param builtinImportMap 实例层 import map。
2244
+ * @returns 按优先级合并的显式模块地址。
2245
+ */
2246
+ var createRuntimeImportOverrides = (builtinImportMap) => ({
2247
+ ...getPlaygroundSiteModules(),
2248
+ ...getPlaygroundImportMapOverrides(),
2249
+ ...filterSafeHrefs(builtinImportMap?.imports)
2250
+ });
2251
+ /**
2252
+ * 默认 CDN → 站点 modules → 管理页覆盖 → 实例 builtinImportMap。
2243
2253
  * iframe import map 与直接渲染加载的是同一份地址表。
2244
2254
  * @param cdnURL
2245
2255
  * @param builtinImportMap
2246
2256
  */
2247
2257
  var createRuntimeImports = (cdnURL = require_css.DEFAULT_CDN_URL, builtinImportMap) => ({
2248
2258
  ...require_css.createBuiltinImports(cdnURL),
2249
- ...getPlaygroundSiteModules(),
2250
- ...filterSafeHrefs(builtinImportMap?.imports),
2251
- ...getPlaygroundImportMapOverrides()
2259
+ ...createRuntimeImportOverrides(builtinImportMap)
2252
2260
  });
2253
2261
  var createRuntimeStore = (files, entry, options) => {
2254
2262
  const { cdnURL, builtinImportMap, ...storeOptions } = options;
@@ -2256,6 +2264,7 @@ var createRuntimeStore = (files, entry, options) => {
2256
2264
  const file = createReplFile(filename, code);
2257
2265
  return [file.filename, file];
2258
2266
  })));
2267
+ replFiles.value["import-map.json"] ??= new _vue_repl.File("import-map.json", "{}");
2259
2268
  const mainFile = (0, vue.ref)(toReplFilename(entry));
2260
2269
  const activeFilename = (0, vue.ref)(toReplFilename(entry));
2261
2270
  const imports = createRuntimeImports(cdnURL, builtinImportMap);
@@ -2274,6 +2283,11 @@ var createRuntimeStore = (files, entry, options) => {
2274
2283
  })
2275
2284
  });
2276
2285
  require_css.bindPlaygroundScss(store);
2286
+ const setFiles = store.setFiles.bind(store);
2287
+ store.setFiles = (nextFiles, nextEntry) => setFiles({
2288
+ "import-map.json": store.files["import-map.json"].code,
2289
+ ...nextFiles
2290
+ }, nextEntry);
2277
2291
  bindReplModules(store);
2278
2292
  require_css.whenSassReady(files, () => {
2279
2293
  const specifiers = require_css.collectBareSpecifiers(files, Object.keys(files));
@@ -2525,7 +2539,7 @@ var Alone = new _deot_vc.Portal(wrapper_default, {
2525
2539
  /**
2526
2540
  * 直接渲染与页面同文档,弹层不需要临时撑高。
2527
2541
  * `run` 仍返回可调用函数,但不会申请高度,也不会改动预览尺寸。
2528
- * @param vue 宿主 Vue,只用其中的 `isRef`。
2542
+ * @param vue 当前预览的 Vue,只用其中的 `isRef`。
2529
2543
  * @returns 注入给预览的 `docs:playground`。
2530
2544
  */
2531
2545
  var createLocalPlayground = (vue) => {
@@ -2776,6 +2790,15 @@ var resolveHostModule = (id, modules) => {
2776
2790
  return null;
2777
2791
  };
2778
2792
  var HOST_VUE_SPECIFIERS = /* @__PURE__ */ new Set(["vue", "vue/server-renderer"]);
2793
+ var VUE_DEPENDENT_BUILTINS = /* @__PURE__ */ new Set([
2794
+ "vue/server-renderer",
2795
+ "vue-router",
2796
+ "pinia",
2797
+ "@deot/vc",
2798
+ "@deot/vc-components",
2799
+ "@deot/vc-hooks",
2800
+ "@deot/vc-locale"
2801
+ ]);
2779
2802
  var messageOf = (error) => error instanceof Error ? error.message : String(error);
2780
2803
  /**
2781
2804
  * 找到 import map 里能覆盖该说明符的键。子路径没有独立 URL 时回退到父包。
@@ -2794,24 +2817,23 @@ var resolveImportTarget = (id, imports) => {
2794
2817
  cursor = parent;
2795
2818
  }
2796
2819
  };
2797
- var prefersLocalBuiltin = (id, imports, cdnURL) => {
2820
+ var prefersLocalBuiltin = (id, imports, overrides = new Set(Object.keys(imports))) => {
2798
2821
  if (!localBuiltinIds.has(id) && !HOST_VUE_SPECIFIERS.has(id)) return false;
2799
- const url = imports[id];
2800
- if (!url) return true;
2801
- return url === require_css.createBuiltinImports(cdnURL)[id];
2822
+ if (overrides.has(id)) return false;
2823
+ return !VUE_DEPENDENT_BUILTINS.has(id) || !overrides.has("vue");
2802
2824
  };
2803
2825
  /**
2804
- * 内置依赖先注入宿主副本。import map 里与默认 CDN 不同的地址会覆盖,包括 `vue`。
2826
+ * 内置依赖默认注入宿主副本,显式 URL 按模块覆盖,包括与默认 CDN 相同的地址。
2805
2827
  * @param specifiers 本次预览用到的裸模块说明符。
2806
2828
  * @param imports 合并后的 import map。
2807
2829
  * @param builtins 宿主 Vue 与 vue-router。
2808
2830
  * @param builtins.vue 当前页面的 Vue 模块。
2809
2831
  * @param builtins.vueRouter 打包进来的 vue-router,可被 import map 覆盖。
2810
2832
  * @param loader 按 URL 加载远程模块。
2811
- * @param cdnURL 当前 CDN 根,用来识别默认地址。
2833
+ * @param overrides 显式配置的模块名,与自动补齐的默认地址区分。
2812
2834
  * @returns 模块表和加载错误。
2813
2835
  */
2814
- var loadLocalModules = async (specifiers, imports, builtins, loader, cdnURL) => {
2836
+ var loadLocalModules = async (specifiers, imports, builtins, loader, overrides = new Set(Object.keys(imports))) => {
2815
2837
  const modules = {
2816
2838
  "vue": builtins.vue,
2817
2839
  "vue-router": builtins.vueRouter
@@ -2821,14 +2843,14 @@ var loadLocalModules = async (specifiers, imports, builtins, loader, cdnURL) =>
2821
2843
  const localIds = /* @__PURE__ */ new Set();
2822
2844
  specifiers.forEach((specifier) => {
2823
2845
  let local = false;
2824
- if (prefersLocalBuiltin(specifier, imports, cdnURL)) localIds.add(specifier);
2846
+ if (prefersLocalBuiltin(specifier, imports, overrides)) localIds.add(specifier);
2825
2847
  else {
2826
2848
  let cursor = specifier;
2827
2849
  while (true) {
2828
2850
  const slash = cursor.lastIndexOf("/");
2829
2851
  if (slash <= 0) break;
2830
2852
  const parent = cursor.slice(0, slash);
2831
- if (prefersLocalBuiltin(parent, imports, cdnURL)) {
2853
+ if (prefersLocalBuiltin(parent, imports, overrides)) {
2832
2854
  localIds.add(parent);
2833
2855
  local = true;
2834
2856
  break;
@@ -2836,12 +2858,8 @@ var loadLocalModules = async (specifiers, imports, builtins, loader, cdnURL) =>
2836
2858
  cursor = parent;
2837
2859
  }
2838
2860
  }
2839
- if (specifier === "vue-router") {
2840
- if (imports["vue-router"]) targets.add("vue-router");
2841
- return;
2842
- }
2843
2861
  const target = resolveImportTarget(specifier, imports);
2844
- if (target && prefersLocalBuiltin(target, imports, cdnURL)) localIds.add(target);
2862
+ if (target && prefersLocalBuiltin(target, imports, overrides)) localIds.add(target);
2845
2863
  else if (target) targets.add(target);
2846
2864
  else if (!local && !localIds.has(specifier)) {
2847
2865
  imports[specifier] = require_css.resolveNpmImport(specifier);
@@ -2858,7 +2876,7 @@ var loadLocalModules = async (specifiers, imports, builtins, loader, cdnURL) =>
2858
2876
  }));
2859
2877
  for (const id of targets) {
2860
2878
  const url = imports[id];
2861
- if (!url || prefersLocalBuiltin(id, imports, cdnURL)) continue;
2879
+ if (!url || prefersLocalBuiltin(id, imports, overrides)) continue;
2862
2880
  try {
2863
2881
  modules[id] = await loader(url);
2864
2882
  } catch (error) {
@@ -2873,12 +2891,13 @@ var loadLocalModules = async (specifiers, imports, builtins, loader, cdnURL) =>
2873
2891
  //#endregion
2874
2892
  //#region packages/playground/src/core/runtime/local/vue-guard.ts
2875
2893
  /**
2876
- * 给 files 注入的 vue:同一份宿主 Vue,但 `createApp().mount` 不会卸掉页面。
2894
+ * 给 files 注入选定的 Vue,但 `createApp().mount` 不会卸掉页面。
2877
2895
  * 防护按本次 link 创建,多个 Playground 互不覆盖。
2878
2896
  * @param capture 本次链接收集到的根组件、局部组件和 provide。
2897
+ * @param vue 当前预览选定的 Vue。
2879
2898
  * @returns 带防护的 Vue 模块。
2880
2899
  */
2881
- var createGuardedVue = (capture) => {
2900
+ var createGuardedVue = (capture, vue$4 = vue) => {
2882
2901
  const createApp = (rootComponent, rootProps) => {
2883
2902
  const fakeApp = {
2884
2903
  _component: rootComponent,
@@ -2918,14 +2937,14 @@ var createGuardedVue = (capture) => {
2918
2937
  mount() {
2919
2938
  capture.components = { ...fakeApp._context.components };
2920
2939
  capture.provides = { ...fakeApp._context.provides };
2921
- capture.mountedComponent = (0, vue.defineComponent)({
2940
+ capture.mountedComponent = vue$4.defineComponent({
2922
2941
  name: "PlaygroundCapturedApp",
2923
2942
  components: fakeApp._context.components,
2924
2943
  setup() {
2925
2944
  Reflect.ownKeys(fakeApp._context.provides).forEach((key) => {
2926
- (0, vue.provide)(key, fakeApp._context.provides[key]);
2945
+ vue$4.provide(key, fakeApp._context.provides[key]);
2927
2946
  });
2928
- return () => (0, vue.h)(rootComponent, rootProps || null);
2947
+ return () => vue$4.h(rootComponent, rootProps || null);
2929
2948
  }
2930
2949
  });
2931
2950
  return capture.mountedComponent;
@@ -2937,7 +2956,7 @@ var createGuardedVue = (capture) => {
2937
2956
  };
2938
2957
  return fakeApp;
2939
2958
  };
2940
- return new Proxy(vue, { get(target, prop, receiver) {
2959
+ return new Proxy(vue$4, { get(target, prop, receiver) {
2941
2960
  if (prop === "createApp" || prop === "createSSRApp") return createApp;
2942
2961
  if (prop === "default") return receiver;
2943
2962
  return Reflect.get(target, prop, receiver);
@@ -2947,7 +2966,7 @@ var createGuardedVue = (capture) => {
2947
2966
  //#region packages/playground/src/core/runtime/local/linker.ts
2948
2967
  /**
2949
2968
  * 在当前页面链接编译结果,返回入口组件。
2950
- * 未覆盖 `vue` 时换成带防护的宿主实例,避免 `mount` 卸掉页面。
2969
+ * 给选定的 Vue 加上 mount 防护,避免字符串入口卸掉页面。
2951
2970
  * @param compiled 浏览器编译结果。
2952
2971
  * @param files 当前 Playground 文件表。
2953
2972
  * @param options 可选的宿主模块表。
@@ -2966,7 +2985,7 @@ var linkPlayground = (compiled, files, options = {}) => {
2966
2985
  provides: {}
2967
2986
  };
2968
2987
  const modules = { ...options.modules || {} };
2969
- if (modules.vue == null || modules.vue === vue) modules.vue = createGuardedVue(capture);
2988
+ modules.vue = createGuardedVue(capture, modules.vue || vue);
2970
2989
  const fileIndex = require_css.createFileIndex(files);
2971
2990
  const root = require_css.getCommonRoot([...fileIndex.keys()]);
2972
2991
  const cache = /* @__PURE__ */ new Map();
@@ -3034,16 +3053,54 @@ var linkPlayground = (compiled, files, options = {}) => {
3034
3053
  //#endregion
3035
3054
  //#region packages/playground/src/core/runtime/local/remote.ts
3036
3055
  var HOST_VUE = /* @__PURE__ */ new Set(["vue", "vue/server-renderer"]);
3056
+ var moduleUrl = (url) => new URL(url, globalThis.location?.href).href;
3057
+ var REMOTE_SOURCE_CACHE_LIMIT = 128;
3058
+ var sourceCache = /* @__PURE__ */ new Map();
3059
+ var prepareRemoteSource = async (url) => {
3060
+ const response = await fetch(url);
3061
+ if (!response.ok) throw new Error(`${response.status} ${url}`);
3062
+ const text = await response.text();
3063
+ await (0, es_module_lexer.init)();
3064
+ const [specifiers] = (0, es_module_lexer.parse)(text);
3065
+ let source = text;
3066
+ for (const item of [...specifiers].reverse()) if (item.type === "dynamic") source = source.slice(0, item.importStart) + "__import" + source.slice(item.importStart + 6);
3067
+ const cjs = (0, sucrase.transform)(require_css.rewriteImportMeta(source, url), {
3068
+ transforms: ["imports"],
3069
+ disableESTransforms: true,
3070
+ production: true,
3071
+ filePath: url
3072
+ }).code;
3073
+ return {
3074
+ dependencies: specifiers.filter((item) => item.type === "static" || item.type === "reexport-star").map((item) => item.specifier),
3075
+ run: new Function("require", "module", "exports", "__import", cjs)
3076
+ };
3077
+ };
3078
+ var readRemoteSource = (url) => {
3079
+ if (new URL(url).origin === globalThis.location?.origin) return prepareRemoteSource(url);
3080
+ const cached = sourceCache.get(url);
3081
+ if (cached) {
3082
+ sourceCache.delete(url);
3083
+ sourceCache.set(url, cached);
3084
+ return cached;
3085
+ }
3086
+ const task = prepareRemoteSource(url).catch((error) => {
3087
+ if (sourceCache.get(url) === task) sourceCache.delete(url);
3088
+ throw error;
3089
+ });
3090
+ sourceCache.set(url, task);
3091
+ if (sourceCache.size > REMOTE_SOURCE_CACHE_LIMIT) sourceCache.delete(sourceCache.keys().next().value);
3092
+ return task;
3093
+ };
3037
3094
  /**
3038
3095
  * 把 CDN 上的 ESM 先拉完整张依赖图,再在当前页面里执行。
3039
3096
  * 裸导入优先走 import map,缺失的 npm 依赖回退到 CDN ESM;import() 按需加载。
3040
3097
  * @param imports 合并后的 import map。
3041
3098
  * @param builtins 宿主 Vue 与 vue-router。
3042
- * @param cdnURL 当前 CDN 根,用来识别默认地址。
3099
+ * @param overrides 显式配置的模块名,与自动补齐的默认地址区分。
3043
3100
  * @param importModule 浏览器原生 ESM 加载器。
3044
3101
  * @returns 按 URL 加载并执行远程模块的加载器。
3045
3102
  */
3046
- var createRemoteLoader = (imports, builtins, cdnURL, importModule = (url) => import(
3103
+ var createRemoteLoader = (imports, builtins, overrides = new Set(Object.keys(imports)), importModule = (url) => import(
3047
3104
  /* @vite-ignore */
3048
3105
  url
3049
3106
  )) => {
@@ -3052,9 +3109,9 @@ var createRemoteLoader = (imports, builtins, cdnURL, importModule = (url) => imp
3052
3109
  "vue-router": builtins.vueRouter
3053
3110
  };
3054
3111
  const pendingBuiltins = /* @__PURE__ */ new Set();
3055
- const texts = /* @__PURE__ */ new Map();
3112
+ const sources = /* @__PURE__ */ new Map();
3113
+ const pendingSources = /* @__PURE__ */ new Map();
3056
3114
  const evaluated = /* @__PURE__ */ new Map();
3057
- const crawled = /* @__PURE__ */ new Set();
3058
3115
  const nativeModules = /* @__PURE__ */ new Map();
3059
3116
  const loadNative = (url) => {
3060
3117
  let pending = nativeModules.get(url);
@@ -3069,24 +3126,34 @@ var createRemoteLoader = (imports, builtins, cdnURL, importModule = (url) => imp
3069
3126
  return pending;
3070
3127
  };
3071
3128
  const localBuiltinId = (id) => {
3072
- if (imports[id] && !prefersLocalBuiltin(id, imports, cdnURL)) return null;
3073
- if (prefersLocalBuiltin(id, imports, cdnURL)) return id;
3129
+ if (imports[id] && !prefersLocalBuiltin(id, imports, overrides)) return null;
3130
+ if (prefersLocalBuiltin(id, imports, overrides)) return id;
3074
3131
  let cursor = id;
3075
3132
  while (true) {
3076
3133
  const slash = cursor.lastIndexOf("/");
3077
3134
  if (slash <= 0) return null;
3078
3135
  const parent = cursor.slice(0, slash);
3079
- if (prefersLocalBuiltin(parent, imports, cdnURL)) return parent;
3136
+ if (prefersLocalBuiltin(parent, imports, overrides)) return parent;
3080
3137
  cursor = parent;
3081
3138
  }
3082
3139
  };
3083
- const dependencyUrl = (from, specifier) => {
3084
- if (require_css.isRelativeSpecifier(specifier) || /^https?:\/\//.test(specifier)) return new URL(specifier, from).href;
3085
- if (imports[specifier] && !prefersLocalBuiltin(specifier, imports, cdnURL)) return imports[specifier];
3086
- if (HOST_VUE.has(specifier) && prefersLocalBuiltin(specifier, imports, cdnURL)) return null;
3140
+ const dependencySpecifier = (from, specifier) => {
3141
+ if (require_css.isRelativeSpecifier(specifier) || /^https?:\/\//.test(specifier)) {
3142
+ const url = new URL(specifier, from);
3143
+ const match = url.origin === "https://cdn.jsdelivr.net" ? url.pathname.match(/^\/npm\/((?:@[^/]+\/)?[^/@]+)(?:@[^/]+)?\/\+esm$/) : null;
3144
+ if (match && (imports[match[1]] || localBuiltinIds.has(match[1]))) return match[1];
3145
+ return url.href;
3146
+ }
3147
+ return specifier;
3148
+ };
3149
+ const dependencyUrl = (from, id) => {
3150
+ const specifier = dependencySpecifier(from, id);
3151
+ if (/^https?:\/\//.test(specifier)) return specifier;
3152
+ if (imports[specifier] && !prefersLocalBuiltin(specifier, imports, overrides)) return moduleUrl(imports[specifier]);
3153
+ if (HOST_VUE.has(specifier) && prefersLocalBuiltin(specifier, imports, overrides)) return null;
3087
3154
  let cursor = specifier;
3088
3155
  while (cursor) {
3089
- if (localBuiltinIds.has(cursor) && prefersLocalBuiltin(cursor, imports, cdnURL)) {
3156
+ if (localBuiltinIds.has(cursor) && prefersLocalBuiltin(cursor, imports, overrides)) {
3090
3157
  pendingBuiltins.add(cursor);
3091
3158
  return null;
3092
3159
  }
@@ -3099,37 +3166,36 @@ var createRemoteLoader = (imports, builtins, cdnURL, importModule = (url) => imp
3099
3166
  imports[specifier] = require_css.resolveNpmImport(specifier);
3100
3167
  return imports[specifier];
3101
3168
  }
3102
- return imports[target];
3169
+ return moduleUrl(imports[target]);
3103
3170
  };
3104
- const crawl = async (url) => {
3105
- if (/\/\+esm(?:[?#]|$)/.test(url)) {
3171
+ const crawl = async (url, visited = /* @__PURE__ */ new Set()) => {
3172
+ if (!overrides.size && /\/\+esm(?:[?#]|$)/.test(url)) {
3106
3173
  await loadNative(url);
3107
3174
  return;
3108
3175
  }
3109
- if (crawled.has(url)) return;
3110
- crawled.add(url);
3111
- const response = await fetch(url);
3112
- if (!response.ok) throw new Error(`${response.status} ${url}`);
3113
- const text = await response.text();
3114
- await (0, es_module_lexer.init)();
3115
- const [specifiers] = (0, es_module_lexer.parse)(text);
3116
- let source = text;
3117
- for (const item of [...specifiers].reverse()) if (item.type === "dynamic") source = source.slice(0, item.importStart) + "__import" + source.slice(item.importStart + 6);
3118
- texts.set(url, source);
3119
- await Promise.all(specifiers.map(async (item) => {
3120
- if (item.type !== "static" && item.type !== "reexport-star") return;
3121
- const next = dependencyUrl(url, item.specifier);
3122
- if (next) await crawl(next);
3176
+ if (visited.has(url)) return;
3177
+ visited.add(url);
3178
+ let pending = pendingSources.get(url);
3179
+ if (!pending) {
3180
+ pending = readRemoteSource(url);
3181
+ pendingSources.set(url, pending);
3182
+ }
3183
+ const source = await pending;
3184
+ sources.set(url, source);
3185
+ await Promise.all(source.dependencies.map(async (specifier) => {
3186
+ const next = dependencyUrl(url, specifier);
3187
+ if (next) await crawl(next, visited);
3123
3188
  }));
3124
3189
  };
3125
- const requireFrom = (from) => (id) => {
3126
- if (HOST_VUE.has(id) && prefersLocalBuiltin(id, imports, cdnURL)) return toHostModule(builtins.vue);
3190
+ const requireFrom = (from) => (specifier) => {
3191
+ const id = dependencySpecifier(from, specifier);
3192
+ if (HOST_VUE.has(id) && prefersLocalBuiltin(id, imports, overrides)) return toHostModule(builtins.vue);
3127
3193
  const builtinId = localBuiltinId(id);
3128
3194
  if (builtinId) return toHostModule(hostModules[builtinId]);
3129
- if (require_css.isRelativeSpecifier(id) || /^https?:\/\//.test(id)) return evaluate(new URL(id, from).href);
3195
+ if (require_css.isRelativeSpecifier(id) || /^https?:\/\//.test(id)) return evaluate(dependencyUrl(from, id));
3130
3196
  const target = imports[id] ? id : resolveImportTarget(id, imports);
3131
3197
  if (!target || !imports[target]) throw new Error(`[Playground] 未注册的模块 "${id}"`);
3132
- const loaded = evaluate(imports[target]);
3198
+ const loaded = evaluate(moduleUrl(imports[target]));
3133
3199
  if (target === id) return loaded;
3134
3200
  if (!resolveHostModule(id, { [target]: loaded }))
3135
3201
  /* istanbul ignore next -- 父包没有对应导出时才拒绝 */
@@ -3138,21 +3204,16 @@ var createRemoteLoader = (imports, builtins, cdnURL, importModule = (url) => imp
3138
3204
  };
3139
3205
  const evaluate = (url) => {
3140
3206
  if (evaluated.has(url)) return evaluated.get(url);
3141
- const text = texts.get(url);
3142
- if (text == null) throw new Error(`[Playground] 远程模块尚未下载: ${url}`);
3143
- const cjs = (0, sucrase.transform)(require_css.rewriteImportMeta(text, url), {
3144
- transforms: ["imports"],
3145
- disableESTransforms: true,
3146
- production: true,
3147
- filePath: url
3148
- }).code;
3207
+ const source = sources.get(url);
3208
+ if (!source) throw new Error(`[Playground] 远程模块尚未下载: ${url}`);
3149
3209
  const module = { exports: {} };
3150
3210
  evaluated.set(url, module.exports);
3151
- new Function("require", "module", "exports", "__import", cjs)(requireFrom(url), module, module.exports, async (id) => {
3211
+ const runner = source.run;
3212
+ runner(requireFrom(url), module, module.exports, async (id) => {
3152
3213
  const next = dependencyUrl(url, id);
3153
3214
  if (next) await load(next);
3154
3215
  else {
3155
- const builtinId = localBuiltinId(id);
3216
+ const builtinId = localBuiltinId(dependencySpecifier(url, id));
3156
3217
  if (builtinId && !(builtinId in hostModules)) hostModules[builtinId] = await ensureLocalBuiltin(builtinId);
3157
3218
  }
3158
3219
  return requireFrom(url)(id);
@@ -3161,7 +3222,8 @@ var createRemoteLoader = (imports, builtins, cdnURL, importModule = (url) => imp
3161
3222
  return module.exports;
3162
3223
  };
3163
3224
  const loading = /* @__PURE__ */ new Map();
3164
- const load = (url) => {
3225
+ const load = (sourceUrl) => {
3226
+ const url = moduleUrl(sourceUrl);
3165
3227
  const pending = loading.get(url);
3166
3228
  if (pending) return pending;
3167
3229
  const task = (async () => {
@@ -3222,14 +3284,14 @@ var local_sandbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0
3222
3284
  });
3223
3285
  };
3224
3286
  (0, vue.watch)([compileError, runtimeError], publishError);
3225
- const createDocsLink = (preview) => (0, vue.defineComponent)({
3287
+ const createDocsLink = (preview, vue$2) => vue$2.defineComponent({
3226
3288
  name: "DocsLink",
3227
3289
  props: { to: {
3228
3290
  type: String,
3229
3291
  default: ""
3230
3292
  } },
3231
3293
  setup(linkProps, { slots }) {
3232
- return () => (0, vue.h)("a", {
3294
+ return () => vue$2.h("a", {
3233
3295
  href: linkProps.to,
3234
3296
  style: {
3235
3297
  color: "inherit",
@@ -3250,27 +3312,27 @@ var local_sandbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0
3250
3312
  previewApp = null;
3251
3313
  require_css.removeStyle(styleId);
3252
3314
  };
3253
- const mountPreview = (component, preview, css) => {
3315
+ const mountPreview = (job, preview) => {
3254
3316
  if (!mountEl.value) return;
3317
+ const { component, vue: vue$3, scroller, css } = job;
3255
3318
  destroyPreview();
3256
3319
  require_css.injectStyle(styleId, css);
3257
3320
  if (props.clearConsole) console.clear();
3258
- const useScroller = props.previewScroller === true;
3259
- const app = vue.createApp({
3321
+ const app = vue$3.createApp({
3260
3322
  name: "LocalSandboxRoot",
3261
3323
  setup() {
3262
- const service = createLocalPlayground(vue);
3263
- (0, vue.provide)("docs:playground", service);
3264
- (0, vue.onErrorCaptured)((error) => {
3324
+ const service = createLocalPlayground(vue$3);
3325
+ vue$3.provide("docs:playground", service);
3326
+ vue$3.onErrorCaptured((error) => {
3265
3327
  const message = formatPlaygroundRuntimeError(error);
3266
3328
  if (message) runtimeError.value = message;
3267
3329
  return false;
3268
3330
  });
3269
- (0, vue.onBeforeUnmount)(() => service.dispose());
3331
+ vue$3.onBeforeUnmount(() => service.dispose());
3270
3332
  return () => {
3271
- const content = (0, vue.h)(component);
3272
- if (!useScroller) return content;
3273
- return (0, vue.h)(_deot_vc.Scroller, {
3333
+ const content = vue$3.h(component);
3334
+ if (!scroller) return content;
3335
+ return vue$3.h(scroller, {
3274
3336
  height: "100%",
3275
3337
  native: false,
3276
3338
  autoResize: true,
@@ -3280,7 +3342,7 @@ var local_sandbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0
3280
3342
  };
3281
3343
  }
3282
3344
  });
3283
- app.component("DocsLink", createDocsLink(preview));
3345
+ app.component("DocsLink", createDocsLink(preview, vue$3));
3284
3346
  app.config.errorHandler = (error) => {
3285
3347
  const message = formatPlaygroundRuntimeError(error);
3286
3348
  if (message) runtimeError.value = message;
@@ -3311,7 +3373,7 @@ var local_sandbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0
3311
3373
  /* istanbul ignore if -- 桥接 iframe 尚未就绪或编译已被取代 */
3312
3374
  if (pendingMount !== job || !mountEl.value) return;
3313
3375
  pendingMount = null;
3314
- mountPreview(job.component, preview, job.css);
3376
+ mountPreview(job, preview);
3315
3377
  };
3316
3378
  const run = async () => {
3317
3379
  const current = ++version;
@@ -3333,15 +3395,18 @@ var local_sandbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0
3333
3395
  return;
3334
3396
  }
3335
3397
  const imports = createRuntimeImports(props.options.cdnURL, props.options.builtinImportMap);
3398
+ const overrides = new Set(Object.keys(createRuntimeImportOverrides(props.options.builtinImportMap)));
3336
3399
  const specifiers = require_css.collectBareSpecifiers(props.files, require_css.collectReachableFilenames(props.files, compiled.entry));
3400
+ specifiers.push("vue");
3401
+ if (props.previewScroller === true) specifiers.push("@deot/vc");
3337
3402
  const remote = createRemoteLoader(imports, {
3338
3403
  vue,
3339
3404
  vueRouter: vue_router
3340
- }, props.options.cdnURL);
3405
+ }, overrides);
3341
3406
  const loaded = await loadLocalModules(specifiers, imports, {
3342
3407
  vue,
3343
3408
  vueRouter: vue_router
3344
- }, (url) => remote.load(url), props.options.cdnURL);
3409
+ }, (url) => remote.load(url), overrides);
3345
3410
  /* istanbul ignore if -- 并发编译时只保留最后一次 */
3346
3411
  if (current !== version) return;
3347
3412
  if (loaded.errors.length) {
@@ -3359,6 +3424,8 @@ var local_sandbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ (0
3359
3424
  }
3360
3425
  pendingMount = {
3361
3426
  component: linked.component,
3427
+ vue: loaded.modules.vue,
3428
+ scroller: props.previewScroller === true ? loaded.modules["@deot/vc"].Scroller : void 0,
3362
3429
  css: linked.css,
3363
3430
  version: current
3364
3431
  };