@deot/docs-client 1.0.5 → 1.0.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/dist/index.cjs CHANGED
@@ -251,7 +251,7 @@ var _hoisted_4$9 = {
251
251
  };
252
252
  var _hoisted_5$7 = ["href"];
253
253
  var _hoisted_6$5 = { key: 2 };
254
- var _hoisted_7$3 = {
254
+ var _hoisted_7$4 = {
255
255
  key: 1,
256
256
  class: "docs-footer__powered-by"
257
257
  };
@@ -350,7 +350,7 @@ var default_footer_default = /* @__PURE__ */ (0, vue.defineComponent)({
350
350
  if (!value || typeof value !== "object" || Array.isArray(value)) return value;
351
351
  return findLanguageValue(value, lang.value) ?? findLanguageValue(value, getDefaultLanguage(config));
352
352
  }
353
- const groups = (0, vue.computed)(() => options.value ? resolveLocalized(options.value.groups) || [] : createDefaultGroups());
353
+ const groups = (0, vue.computed)(() => options.value ? resolveLocalized(options.value.nav) || [] : createDefaultGroups());
354
354
  const poweredBy = (0, vue.computed)(() => {
355
355
  const value = options.value ? resolveLocalized(options.value.poweredBy) : "default";
356
356
  if (value === "default") return t("client.common.poweredBy");
@@ -379,7 +379,7 @@ var default_footer_default = /* @__PURE__ */ (0, vue.defineComponent)({
379
379
  _: 2
380
380
  }, 1032, ["to"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_6$5, (0, vue.toDisplayString)(item.label), 1))]);
381
381
  }), 128))])) : (0, vue.createCommentVNode)("v-if", true)]);
382
- }), 128))])) : (0, vue.createCommentVNode)("v-if", true), poweredBy.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_7$3, (0, vue.toDisplayString)(poweredBy.value), 1)) : (0, vue.createCommentVNode)("v-if", true)])) : (0, vue.createCommentVNode)("v-if", true);
382
+ }), 128))])) : (0, vue.createCommentVNode)("v-if", true), poweredBy.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_7$4, (0, vue.toDisplayString)(poweredBy.value), 1)) : (0, vue.createCommentVNode)("v-if", true)])) : (0, vue.createCommentVNode)("v-if", true);
383
383
  };
384
384
  }
385
385
  });
@@ -1389,6 +1389,19 @@ var localizeRoutePath = (lang, pathname) => {
1389
1389
  return `/${lang}${normalized === "/" ? "" : normalized}`;
1390
1390
  };
1391
1391
  /**
1392
+ * 给站内地址补语言前缀;外链和已带已配置语言的地址保持不变。
1393
+ * @param config 文档配置,用于识别语言路径段。
1394
+ * @param lang 当前语言。
1395
+ * @param target 原始跳转地址。
1396
+ * @returns 可直接交给 RouterLink 的地址。
1397
+ */
1398
+ var localizePath = (config, lang, target) => {
1399
+ const language = target.split(/[?#]/u, 1)[0].split("/").filter(Boolean)[0];
1400
+ const configuredLanguages = Object.keys(config.locales);
1401
+ const hasLanguage = Boolean(language && (Object.prototype.hasOwnProperty.call(config.locales, language) || !configuredLanguages.length && language === lang));
1402
+ return isExternalLink(target) || hasLanguage ? target : localizeRoutePath(lang, target);
1403
+ };
1404
+ /**
1392
1405
  * 按显式 value、动态参数和路径末段的顺序确定当前文档值。
1393
1406
  * 该纯函数独立于 Router 注册模块,避免布局组件反向导入 Router 时形成循环依赖。
1394
1407
  * @param route 当前标准化路由,或资源计划合成的等价 route shape。
@@ -1448,6 +1461,45 @@ var toLogicalResourceSource = (config, lang, url) => {
1448
1461
  return `./${resolved.pathname.slice(root.pathname.length)}${resolved.search}${resolved.hash}`;
1449
1462
  };
1450
1463
  //#endregion
1464
+ //#region packages/client/src/utils/content.ts
1465
+ var isRendererDocument = (value) => Boolean(value && typeof value === "object" && "schemaVersion" in value);
1466
+ var isPageJsonSource = (value) => typeof value === "string" && /\.page\.json(?:$|[?#])/i.test(value);
1467
+ var isContentLocaleMap = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value) && !("schemaVersion" in value));
1468
+ var isHomeLikeContent = (content) => {
1469
+ if (isRendererDocument(content) || isPageJsonSource(content)) return true;
1470
+ if (!isContentLocaleMap(content)) return false;
1471
+ return Object.values(content).some((item) => isRendererDocument(item) || isPageJsonSource(item));
1472
+ };
1473
+ var isHomeLikeRoute = (routeConfig) => {
1474
+ if (!routeConfig || typeof routeConfig === "string" || typeof routeConfig === "function") return false;
1475
+ return isHomeLikeContent(routeConfig.content);
1476
+ };
1477
+ var resolveLocalizedContent = (content, lang, fallbackLang) => {
1478
+ if (content === void 0) return void 0;
1479
+ if (!isContentLocaleMap(content)) return content;
1480
+ return findLanguageValue(content, lang) ?? findLanguageValue(content, fallbackLang);
1481
+ };
1482
+ var resolveHomeContent = (config, lang) => {
1483
+ const root = config.routes["/"];
1484
+ if (!root || typeof root !== "object") return void 0;
1485
+ return resolveLocalizedContent(root.content, lang, "en-US");
1486
+ };
1487
+ var resolveRouteContent = (content, lang, config, pathname = "") => resolveLocalizedContent(content, lang, pathname === "/" ? "en-US" : getDefaultLanguage(config));
1488
+ var writeHomeContent = (config, lang, value) => {
1489
+ const current = config.routes["/"];
1490
+ const route = current && typeof current === "object" ? current : {};
1491
+ config.routes["/"] = route;
1492
+ const content = route.content;
1493
+ if (isContentLocaleMap(content) || content === void 0) {
1494
+ route.content = {
1495
+ ...isContentLocaleMap(content) ? content : {},
1496
+ [lang]: value
1497
+ };
1498
+ return;
1499
+ }
1500
+ route.content = value;
1501
+ };
1502
+ //#endregion
1451
1503
  //#region packages/client/src/components/renderer/markdown-props.ts
1452
1504
  var INDICATOR_POSITIONS = /* @__PURE__ */ new Set(["left", "right"]);
1453
1505
  var isPlainObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -2231,7 +2283,8 @@ var ResourcePlanner = class {
2231
2283
  async resolveRouteContentIdentity(config, lang, pathname, routeMatch) {
2232
2284
  if (!routeMatch) return null;
2233
2285
  const route = this.createRouteShape(lang, pathname, routeMatch.params);
2234
- const slot = typeof routeMatch.config.content === "undefined" ? "default" : routeMatch.config.content;
2286
+ const resolved = typeof routeMatch.config.content === "undefined" ? "default" : resolveRouteContent(routeMatch.config.content, lang, config, pathname);
2287
+ const slot = resolved === void 0 ? null : resolved;
2235
2288
  if (slot === null) return null;
2236
2289
  if (slot !== "default") {
2237
2290
  if (typeof slot !== "string") return null;
@@ -2273,14 +2326,6 @@ var ResourcePlanner = class {
2273
2326
  params: {}
2274
2327
  });
2275
2328
  }
2276
- this.collectBuiltInHomePages(config, collector);
2277
- }
2278
- collectBuiltInHomePages(config, collector) {
2279
- if (Object.prototype.hasOwnProperty.call(config.routes, "/")) return;
2280
- for (const lang of this.getLanguages(config)) {
2281
- const home = config.home?.locales?.[lang] || config.home?.locales?.["en-US"];
2282
- if (typeof home === "string") collector.add(lang, home, "page");
2283
- }
2284
2329
  }
2285
2330
  needsSidebarDiscovery(config) {
2286
2331
  return Object.entries(config.routes).some(([path, route]) => (path.includes(":") || path.includes("*")) && route && (typeof route === "function" || typeof route === "object" && (typeof route.content === "undefined" || route.content === "default") && typeof route.value !== "string"));
@@ -2395,17 +2440,6 @@ var ResourcePlanner = class {
2395
2440
  await this.collectConfiguredResources(config, configured);
2396
2441
  const sidebarKeys = new Set([...configured.identities.values()].filter((identity) => identity.type === "sidebar").map(resourceIdentityKey));
2397
2442
  for (const lang of this.getLanguages(config)) {
2398
- if (!Object.prototype.hasOwnProperty.call(config.routes, "/")) {
2399
- const home = config.home?.locales?.[lang] || config.home?.locales?.["en-US"];
2400
- if (typeof home === "string") {
2401
- const identity = createResourceIdentity(config, lang, "page", home);
2402
- const key = resourceIdentityKey(identity);
2403
- if (!resources.has(key)) resources.set(key, {
2404
- identity,
2405
- path: `/${lang}`
2406
- });
2407
- }
2408
- }
2409
2443
  await add(lang, "/");
2410
2444
  for (const [pathname, routeConfig] of Object.entries(config.routes)) {
2411
2445
  if (!routeConfig || pathname === "*") continue;
@@ -3506,29 +3540,27 @@ var SearchManager = class {
3506
3540
  this.parsed.set(key, document);
3507
3541
  documents.push(document);
3508
3542
  }
3509
- if (!Object.prototype.hasOwnProperty.call(config.routes, "/")) {
3510
- const home = config.home?.locales?.[lang] || config.home?.locales?.["en-US"];
3511
- if (home && typeof home !== "string") {
3512
- const key = `${namespace}|${lang}|inline-home`;
3513
- try {
3514
- const parsed = await parseRendererSearch(config, JSON.stringify(home), lang);
3515
- documents.push({
3516
- key,
3517
- hash: "inline-home",
3518
- namespace,
3519
- lang,
3520
- path: `/${lang}`,
3521
- source: "home",
3522
- title: parsed.title || t("client.search.untitled"),
3523
- text: parsed.text,
3524
- sections: parsed.sections.map((section) => ({
3525
- title: section.title,
3526
- anchor: section.anchor,
3527
- text: section.text
3528
- }))
3529
- });
3530
- } catch {}
3531
- }
3543
+ const home = resolveHomeContent(config, lang);
3544
+ if (isRendererDocument(home)) {
3545
+ const key = `${namespace}|${lang}|inline-home`;
3546
+ try {
3547
+ const parsed = await parseRendererSearch(config, JSON.stringify(home), lang);
3548
+ documents.push({
3549
+ key,
3550
+ hash: "inline-home",
3551
+ namespace,
3552
+ lang,
3553
+ path: `/${lang}`,
3554
+ source: "home",
3555
+ title: parsed.title || t("client.search.untitled"),
3556
+ text: parsed.text,
3557
+ sections: parsed.sections.map((section) => ({
3558
+ title: section.title,
3559
+ anchor: section.anchor,
3560
+ text: section.text
3561
+ }))
3562
+ });
3563
+ } catch {}
3532
3564
  }
3533
3565
  this.documents.set(`${namespace}|${lang}`, documents);
3534
3566
  return documents.length;
@@ -3619,36 +3651,36 @@ var _hoisted_3$12 = ["placeholder", "aria-label"];
3619
3651
  var _hoisted_4$7 = { class: "docs-search__actions" };
3620
3652
  var _hoisted_5$6 = ["aria-label"];
3621
3653
  var _hoisted_6$4 = { class: "docs-search__body" };
3622
- var _hoisted_7$2 = { class: "docs-search__content" };
3623
- var _hoisted_8$2 = {
3654
+ var _hoisted_7$3 = { class: "docs-search__content" };
3655
+ var _hoisted_8$3 = {
3624
3656
  key: 0,
3625
3657
  class: "docs-search__state"
3626
3658
  };
3627
- var _hoisted_9$2 = {
3659
+ var _hoisted_9$3 = {
3628
3660
  key: 1,
3629
3661
  class: "docs-search__state docs-search__state--error"
3630
3662
  };
3631
- var _hoisted_10$2 = { class: "docs-search__group-title" };
3632
- var _hoisted_11$2 = [
3663
+ var _hoisted_10$3 = { class: "docs-search__group-title" };
3664
+ var _hoisted_11$3 = [
3633
3665
  "data-search-index",
3634
3666
  "onMouseenter",
3635
3667
  "onClick"
3636
3668
  ];
3637
- var _hoisted_12$2 = {
3669
+ var _hoisted_12$3 = {
3638
3670
  class: "docs-search__result-icon",
3639
3671
  "aria-hidden": "true"
3640
3672
  };
3641
- var _hoisted_13$1 = { class: "docs-search__result-content" };
3642
- var _hoisted_14 = { class: "docs-search__result-title" };
3643
- var _hoisted_15 = {
3673
+ var _hoisted_13$2 = { class: "docs-search__result-content" };
3674
+ var _hoisted_14$1 = { class: "docs-search__result-title" };
3675
+ var _hoisted_15$1 = {
3644
3676
  key: 0,
3645
3677
  class: "docs-search__result-parent"
3646
3678
  };
3647
- var _hoisted_16 = {
3679
+ var _hoisted_16$1 = {
3648
3680
  key: 1,
3649
3681
  class: "docs-search__result-excerpt"
3650
3682
  };
3651
- var _hoisted_17 = {
3683
+ var _hoisted_17$1 = {
3652
3684
  key: 0,
3653
3685
  class: "docs-search__enter",
3654
3686
  "aria-hidden": "true"
@@ -3913,8 +3945,8 @@ var search_default = /* @__PURE__ */ (0, vue.defineComponent)({
3913
3945
  height: "100%",
3914
3946
  "wrapper-style": "overflow-x: hidden;"
3915
3947
  }, {
3916
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_7$2, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_8$2, (0, vue.toDisplayString)((0, vue.unref)(t)("client.search.searching")), 1)) : error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_9$2, (0, vue.toDisplayString)(error.value), 1)) : keyword.value.trim() ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [
3917
- (0, vue.createElementVNode)("div", _hoisted_10$2, (0, vue.toDisplayString)((0, vue.unref)(t)("client.search.documentation")), 1),
3948
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_7$3, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_8$3, (0, vue.toDisplayString)((0, vue.unref)(t)("client.search.searching")), 1)) : error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_9$3, (0, vue.toDisplayString)(error.value), 1)) : keyword.value.trim() ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [
3949
+ (0, vue.createElementVNode)("div", _hoisted_10$3, (0, vue.toDisplayString)((0, vue.unref)(t)("client.search.documentation")), 1),
3918
3950
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(results.value, (item, index) => {
3919
3951
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3920
3952
  key: `${item.id}:${item.kind}`,
@@ -3927,16 +3959,16 @@ var search_default = /* @__PURE__ */ (0, vue.defineComponent)({
3927
3959
  onMouseenter: ($event) => handleMouseenter(index),
3928
3960
  onClick: ($event) => handleSelect(item)
3929
3961
  }, [
3930
- (0, vue.createElementVNode)("span", _hoisted_12$2, (0, vue.toDisplayString)(item.kind === "section" ? "#" : "▤"), 1),
3931
- (0, vue.createElementVNode)("span", _hoisted_13$1, [(0, vue.createElementVNode)("span", _hoisted_14, [(0, vue.createVNode)(highlight_text_default, {
3962
+ (0, vue.createElementVNode)("span", _hoisted_12$3, (0, vue.toDisplayString)(item.kind === "section" ? "#" : "▤"), 1),
3963
+ (0, vue.createElementVNode)("span", _hoisted_13$2, [(0, vue.createElementVNode)("span", _hoisted_14$1, [(0, vue.createVNode)(highlight_text_default, {
3932
3964
  text: item.sectionTitle || item.title,
3933
3965
  keyword: keyword.value
3934
- }, null, 8, ["text", "keyword"])]), item.kind === "section" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_15, (0, vue.toDisplayString)(item.title), 1)) : item.excerpt ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_16, [(0, vue.createVNode)(highlight_text_default, {
3966
+ }, null, 8, ["text", "keyword"])]), item.kind === "section" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_15$1, (0, vue.toDisplayString)(item.title), 1)) : item.excerpt ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_16$1, [(0, vue.createVNode)(highlight_text_default, {
3935
3967
  text: item.excerpt,
3936
3968
  keyword: keyword.value
3937
3969
  }, null, 8, ["text", "keyword"])])) : (0, vue.createCommentVNode)("v-if", true)]),
3938
- index === selectedIndex.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_17, "↵")) : (0, vue.createCommentVNode)("v-if", true)
3939
- ], 42, _hoisted_11$2);
3970
+ index === selectedIndex.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_17$1, "↵")) : (0, vue.createCommentVNode)("v-if", true)
3971
+ ], 42, _hoisted_11$3);
3940
3972
  }), 128)),
3941
3973
  !results.value.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_18, (0, vue.toDisplayString)(preparedCount.value ? (0, vue.unref)(t)("client.search.noResults") : (0, vue.unref)(t)("client.search.noCachedDocuments")), 1)) : (0, vue.createCommentVNode)("v-if", true)
3942
3974
  ], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 3 }, [
@@ -4077,6 +4109,10 @@ var CLIENT_ICON_DEFINITIONS = {
4077
4109
  "a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"
4078
4110
  ].join("")
4079
4111
  },
4112
+ more: {
4113
+ filled: false,
4114
+ path: ["M4 4h6v6H4zm10 0h6v6h-6z", "M4 14h6v6H4zm10 0h6v6h-6z"].join("")
4115
+ },
4080
4116
  menu: {
4081
4117
  filled: false,
4082
4118
  path: "M4 7h16M4 12h16M4 17h16"
@@ -4092,6 +4128,26 @@ var CLIENT_ICON_DEFINITIONS = {
4092
4128
  home: {
4093
4129
  filled: false,
4094
4130
  path: "M3 11 12 3l9 8M5 10v10h14V10M9 20v-6h6v6"
4131
+ },
4132
+ github: {
4133
+ filled: true,
4134
+ path: [
4135
+ "M12.001 2c-5.525 0-10 4.475-10 10a9.99 9.99 0 0 0 6.837 9.488",
4136
+ "c.5.087.688-.213.688-.476c0-.237-.013-1.024-.013-1.862c-2.512.463-3.162-.612",
4137
+ "-3.362-1.175c-.113-.288-.6-1.175-1.025-1.413c-.35-.187-.85-.65-.013-.662",
4138
+ "c.788-.013 1.35.725 1.538 1.025c.9 1.512 2.337 1.087 2.912.825",
4139
+ "c.088-.65.35-1.087.638-1.337c-2.225-.25-4.55-1.113-4.55-4.938",
4140
+ "c0-1.088.387-1.987 1.025-2.687c-.1-.25-.45-1.275.1-2.65c0 0 .837-.263",
4141
+ " 2.75 1.024a9.3 9.3 0 0 1 2.5-.337c.85 0 1.7.112 2.5.337c1.913-1.3",
4142
+ " 2.75-1.024 2.75-1.024c.55 1.375.2 2.4.1 2.65c.637.7 1.025 1.587",
4143
+ " 1.025 2.687c0 3.838-2.337 4.688-4.562 4.938c.362.312.675.912.675 1.85",
4144
+ "c0 1.337-.013 2.412-.013 2.75c0 .262.188.574.688.474A10.02 10.02 0 0 0 22 12",
4145
+ "c0-5.525-4.475-10-10-10"
4146
+ ].join("")
4147
+ },
4148
+ repository: {
4149
+ filled: false,
4150
+ path: ["M4 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2Z", "M10 9.5 8 12l2 2.5M14 9.5 16 12l-2 2.5"].join("")
4095
4151
  }
4096
4152
  };
4097
4153
  //#endregion
@@ -4106,22 +4162,34 @@ var _hoisted_2$14 = ["d"];
4106
4162
  //#region packages/client/src/components/icon/icon.vue
4107
4163
  var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
4108
4164
  __name: "icon",
4109
- props: { name: {} },
4165
+ props: {
4166
+ name: {},
4167
+ size: {}
4168
+ },
4110
4169
  setup(__props) {
4111
4170
  const props = __props;
4112
4171
  const definition = (0, vue.computed)(() => CLIENT_ICON_DEFINITIONS[props.name]);
4172
+ const sizeStyle = (0, vue.computed)(() => {
4173
+ if (props.size == null) return;
4174
+ const value = typeof props.size === "number" ? `${props.size}px` : props.size;
4175
+ return {
4176
+ width: value,
4177
+ height: value
4178
+ };
4179
+ });
4113
4180
  return (_ctx, _cache) => {
4114
4181
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("svg", {
4115
4182
  class: "docs-client-icon",
4116
4183
  "data-icon": __props.name,
4117
4184
  viewBox: "0 0 24 24",
4185
+ style: (0, vue.normalizeStyle)(sizeStyle.value),
4118
4186
  fill: definition.value.filled ? "currentColor" : "none",
4119
4187
  stroke: definition.value.filled ? "none" : "currentColor",
4120
4188
  "stroke-width": "1.8",
4121
4189
  "stroke-linecap": "round",
4122
4190
  "stroke-linejoin": "round",
4123
4191
  "aria-hidden": "true"
4124
- }, [(0, vue.createElementVNode)("path", { d: definition.value.path }, null, 8, _hoisted_2$14)], 8, _hoisted_1$17);
4192
+ }, [(0, vue.createElementVNode)("path", { d: definition.value.path }, null, 8, _hoisted_2$14)], 12, _hoisted_1$17);
4125
4193
  };
4126
4194
  }
4127
4195
  });
@@ -4527,18 +4595,40 @@ var takeInlineRendererDocument = (from) => {
4527
4595
  //#endregion
4528
4596
  //#region packages/client/src/components/layout/default-header.vue?vue&type=script&setup=true&lang.ts
4529
4597
  var _hoisted_1$15 = { class: "docs-header" };
4530
- var _hoisted_2$13 = { class: "docs-header__search" };
4531
- var _hoisted_3$11 = { class: "docs-header__actions" };
4532
- var _hoisted_4$6 = {
4598
+ var _hoisted_2$13 = ["href"];
4599
+ var _hoisted_3$11 = ["src"];
4600
+ var _hoisted_4$6 = ["src"];
4601
+ var _hoisted_5$5 = { class: "docs-header__search" };
4602
+ var _hoisted_6$3 = ["aria-label"];
4603
+ var _hoisted_7$2 = ["aria-expanded"];
4604
+ var _hoisted_8$2 = ["href"];
4605
+ var _hoisted_9$2 = { key: 2 };
4606
+ var _hoisted_10$2 = ["href"];
4607
+ var _hoisted_11$2 = {
4608
+ key: 3,
4609
+ class: "docs-header__nav-item"
4610
+ };
4611
+ var _hoisted_12$2 = { class: "docs-header__actions" };
4612
+ var _hoisted_13$1 = {
4533
4613
  key: 0,
4534
4614
  class: "docs-header__locales"
4535
4615
  };
4536
- var _hoisted_5$5 = [
4616
+ var _hoisted_14 = [
4617
+ "title",
4618
+ "aria-label",
4619
+ "aria-expanded"
4620
+ ];
4621
+ var _hoisted_15 = [
4622
+ "href",
4623
+ "title",
4624
+ "aria-label"
4625
+ ];
4626
+ var _hoisted_16 = { class: "docs-header__tools" };
4627
+ var _hoisted_17 = [
4537
4628
  "title",
4538
4629
  "aria-label",
4539
4630
  "aria-expanded"
4540
4631
  ];
4541
- var _hoisted_6$3 = ["title", "aria-label"];
4542
4632
  //#endregion
4543
4633
  //#region packages/client/src/components/layout/default-header.vue
4544
4634
  var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -4549,13 +4639,95 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4549
4639
  const config = getDocsConfig();
4550
4640
  const { t } = (0, _deot_docs_locale.useLocale)();
4551
4641
  const localeMenuVisible = (0, vue.ref)(false);
4642
+ const toolsMenuVisible = (0, vue.ref)(false);
4552
4643
  const lang = (0, vue.computed)(() => String(route.params.lang));
4644
+ /**
4645
+ * 按当前语言和站点默认语言选择 Header 配置。
4646
+ * @param value 固定配置或语言映射。
4647
+ * @returns 当前语言、站点默认语言或空值。
4648
+ */
4649
+ function resolveLocalized(value) {
4650
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
4651
+ return findLanguageValue(value, lang.value) ?? findLanguageValue(value, getDefaultLanguage(config));
4652
+ }
4653
+ const brandOptions = config.layout?.header?.brand;
4654
+ const navItems = (0, vue.computed)(() => resolveLocalized(config.layout?.header?.nav) || []);
4655
+ const navMenuVisible = (0, vue.ref)({});
4656
+ /**
4657
+ * 判断导航地址是否对应当前路由(含前缀匹配)。
4658
+ * @param value 配置中的跳转地址。
4659
+ * @returns 是否为当前导航项。
4660
+ */
4661
+ function isNavPathActive(value) {
4662
+ if (!value || isExternalLink(value)) return false;
4663
+ const target = localizePath(config, lang.value, value);
4664
+ return route.path === target || route.path.startsWith(`${target}/`);
4665
+ }
4666
+ /**
4667
+ * 顶层项或其子项命中当前路由时视为激活。
4668
+ * @param item 一条 Header 导航。
4669
+ * @returns 是否显示激活下划线。
4670
+ */
4671
+ function isNavItemActive(item) {
4672
+ return isNavPathActive(item.value) || Boolean(item.children?.some((child) => isNavPathActive(child.value)));
4673
+ }
4674
+ const brandLogo = (0, vue.computed)(() => resolveLocalized(brandOptions?.logo) || "");
4675
+ const brandLabel = (0, vue.computed)(() => resolveLocalized(brandOptions?.label) || config.namespace || t("client.header.brand"));
4676
+ const brandValue = (0, vue.computed)(() => resolveLocalized(brandOptions?.value) || `/${lang.value}`);
4677
+ const brandExternal = (0, vue.computed)(() => isExternalLink(brandValue.value));
4678
+ const brandPath = (0, vue.computed)(() => localizePath(config, lang.value, brandValue.value));
4553
4679
  const databasePath = (0, vue.computed)(() => `/${lang.value}/__docs/database`);
4554
4680
  const playgroundResourcePath = (0, vue.computed)(() => `/${lang.value}/__docs/playground-resource`);
4555
4681
  const localeOptions = (0, vue.computed)(() => Object.entries(config.locales).map(([value, item]) => ({
4556
4682
  label: item.label,
4557
4683
  value
4558
4684
  })));
4685
+ /**
4686
+ * 校验站点仓库地址,仅允许在新窗口打开的 http(s) 链接。
4687
+ * @param value `$docs.repository` 原始值。
4688
+ * @returns 可打开的地址;非法值返回空字符串。
4689
+ */
4690
+ function resolveRepositoryHref(value) {
4691
+ const href = value?.trim();
4692
+ if (!href) return "";
4693
+ try {
4694
+ const url = new URL(href);
4695
+ return ["http:", "https:"].includes(url.protocol) ? href : "";
4696
+ } catch {
4697
+ return "";
4698
+ }
4699
+ }
4700
+ const repositoryHref = (0, vue.computed)(() => resolveRepositoryHref(config.repository));
4701
+ const repositoryIcon = (0, vue.computed)(() => {
4702
+ try {
4703
+ const host = new URL(repositoryHref.value).hostname.toLowerCase();
4704
+ return host === "github.com" || host.endsWith(".github.com") ? "github" : "repository";
4705
+ } catch {
4706
+ return "repository";
4707
+ }
4708
+ });
4709
+ const toolsOptions = (0, vue.computed)(() => {
4710
+ return [
4711
+ {
4712
+ value: "editor",
4713
+ icon: "editor",
4714
+ label: t("client.header.editor"),
4715
+ selected: Boolean(route.meta.docsEditor)
4716
+ },
4717
+ {
4718
+ value: "playgroundResource",
4719
+ icon: "playgroundResource",
4720
+ label: t("client.header.playgroundResource"),
4721
+ selected: Boolean(route.meta.docsPlaygroundResource)
4722
+ },
4723
+ {
4724
+ value: "database",
4725
+ icon: "database",
4726
+ label: t("client.header.database"),
4727
+ selected: Boolean(route.meta.docsDatabase)
4728
+ }
4729
+ ];
4730
+ });
4559
4731
  const localePath = (locale) => {
4560
4732
  const segments = route.path.split("/").filter(Boolean);
4561
4733
  segments[0] = locale;
@@ -4570,18 +4742,12 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4570
4742
  const target = String(locale);
4571
4743
  if (target !== lang.value) router.push(localePath(target));
4572
4744
  };
4573
- const isInlineRendererDocument = (value) => Boolean(value && typeof value === "object" && "schemaVersion" in value);
4745
+ const isInlineRendererDocument = (value) => isRendererDocument(value);
4574
4746
  const handleEditor = async () => {
4575
4747
  const routeConfig = route.meta.docsRoute;
4576
- let source = routeConfig?.content;
4748
+ let source = route.meta.docsHome ? resolveHomeContent(config, lang.value) : resolveRouteContent(routeConfig?.content, lang.value, config);
4577
4749
  let type = route.meta.docsHome ? "home" : "";
4578
- if (route.meta.docsHome) {
4579
- const configured = config.home?.locales?.[lang.value] || config.home?.locales?.["en-US"];
4580
- if (typeof configured === "string") {
4581
- source = configured;
4582
- type = "page";
4583
- }
4584
- }
4750
+ if (route.meta.docsHome && typeof source === "string") type = "page";
4585
4751
  if (!route.meta.docsHome && isInlineRendererDocument(source)) {
4586
4752
  stashInlineRendererDocument(route.fullPath, source);
4587
4753
  await router.push({
@@ -4608,19 +4774,113 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4608
4774
  }
4609
4775
  });
4610
4776
  };
4777
+ const handleTools = (action) => {
4778
+ toolsMenuVisible.value = false;
4779
+ if (action === "editor") {
4780
+ handleEditor();
4781
+ return;
4782
+ }
4783
+ if (action === "playgroundResource") {
4784
+ router.push(playgroundResourcePath.value);
4785
+ return;
4786
+ }
4787
+ if (action === "database") router.push(databasePath.value);
4788
+ };
4611
4789
  return (_ctx, _cache) => {
4612
4790
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("header", _hoisted_1$15, [
4613
- (0, vue.createVNode)((0, vue.unref)(vue_router.RouterLink), {
4791
+ brandExternal.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4792
+ key: 0,
4614
4793
  class: "docs-header__brand",
4615
- to: `/${lang.value}`
4794
+ href: brandValue.value,
4795
+ target: "_blank",
4796
+ rel: "noopener noreferrer"
4797
+ }, [brandLogo.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
4798
+ key: 0,
4799
+ class: "docs-header__brand-logo",
4800
+ src: brandLogo.value,
4801
+ alt: ""
4802
+ }, null, 8, _hoisted_3$11)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)(brandLabel.value), 1)], 8, _hoisted_2$13)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(vue_router.RouterLink), {
4803
+ key: 1,
4804
+ class: "docs-header__brand",
4805
+ to: brandPath.value
4616
4806
  }, {
4617
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(t)("client.header.brand")), 1)]),
4807
+ default: (0, vue.withCtx)(() => [brandLogo.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("img", {
4808
+ key: 0,
4809
+ class: "docs-header__brand-logo",
4810
+ src: brandLogo.value,
4811
+ alt: ""
4812
+ }, null, 8, _hoisted_4$6)) : (0, vue.createCommentVNode)("v-if", true), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)(brandLabel.value), 1)]),
4618
4813
  _: 1
4619
- }, 8, ["to"]),
4620
- (0, vue.createElementVNode)("div", _hoisted_2$13, [(0, vue.createVNode)((0, vue.unref)(trigger_default))]),
4621
- (0, vue.createElementVNode)("div", _hoisted_3$11, [
4814
+ }, 8, ["to"])),
4815
+ (0, vue.createElementVNode)("div", _hoisted_5$5, [(0, vue.createVNode)((0, vue.unref)(trigger_default))]),
4816
+ navItems.value.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("nav", {
4817
+ key: 2,
4818
+ class: "docs-header__nav",
4819
+ "aria-label": (0, vue.unref)(t)("client.header.nav")
4820
+ }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(navItems.value, (item, index) => {
4821
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: `${item.label}:${item.value || ""}:${index}` }, [item.children?.length ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.Dropdown), {
4822
+ key: 0,
4823
+ "model-value": Boolean(navMenuVisible.value[index]),
4824
+ class: "docs-header__nav-dropdown",
4825
+ "portal-class": "docs-header__nav-portal",
4826
+ trigger: "hover",
4827
+ placement: "bottom",
4828
+ "onUpdate:modelValue": (visible) => navMenuVisible.value[index] = Boolean(visible)
4829
+ }, {
4830
+ content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
4831
+ class: "docs-header__nav-options",
4832
+ role: "menu",
4833
+ "aria-label": item.label
4834
+ }, {
4835
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(item.children, (child) => {
4836
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
4837
+ key: `${child.label}:${child.value || ""}`,
4838
+ class: "docs-header__nav-option",
4839
+ value: child.value || child.label,
4840
+ role: "menuitem"
4841
+ }, {
4842
+ default: (0, vue.withCtx)(() => [child.value && (0, vue.unref)(isExternalLink)(child.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4843
+ key: 0,
4844
+ href: child.value,
4845
+ target: "_blank",
4846
+ rel: "noopener noreferrer"
4847
+ }, (0, vue.toDisplayString)(child.label), 9, _hoisted_8$2)) : child.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(vue_router.RouterLink), {
4848
+ key: 1,
4849
+ to: (0, vue.unref)(localizePath)((0, vue.unref)(config), lang.value, child.value)
4850
+ }, {
4851
+ default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(child.label), 1)]),
4852
+ _: 2
4853
+ }, 1032, ["to"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_9$2, (0, vue.toDisplayString)(child.label), 1))]),
4854
+ _: 2
4855
+ }, 1032, ["value"]);
4856
+ }), 128))]),
4857
+ _: 2
4858
+ }, 1032, ["aria-label"])]),
4859
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
4860
+ type: "button",
4861
+ class: (0, vue.normalizeClass)(["docs-header__nav-item docs-header__nav-trigger", { "is-active": isNavItemActive(item) }]),
4862
+ "aria-expanded": Boolean(navMenuVisible.value[index]),
4863
+ "aria-haspopup": "menu"
4864
+ }, (0, vue.toDisplayString)(item.label), 11, _hoisted_7$2)]),
4865
+ _: 2
4866
+ }, 1032, ["model-value", "onUpdate:modelValue"])) : item.value && (0, vue.unref)(isExternalLink)(item.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4867
+ key: 1,
4868
+ class: "docs-header__nav-item",
4869
+ href: item.value,
4870
+ target: "_blank",
4871
+ rel: "noopener noreferrer"
4872
+ }, (0, vue.toDisplayString)(item.label), 9, _hoisted_10$2)) : item.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(vue_router.RouterLink), {
4873
+ key: 2,
4874
+ class: (0, vue.normalizeClass)(["docs-header__nav-item", { "is-active": isNavItemActive(item) }]),
4875
+ to: (0, vue.unref)(localizePath)((0, vue.unref)(config), lang.value, item.value)
4876
+ }, {
4877
+ default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(item.label), 1)]),
4878
+ _: 2
4879
+ }, 1032, ["class", "to"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_11$2, (0, vue.toDisplayString)(item.label), 1))], 64);
4880
+ }), 128))], 8, _hoisted_6$3)) : (0, vue.createCommentVNode)("v-if", true),
4881
+ (0, vue.createElementVNode)("div", _hoisted_12$2, [
4622
4882
  (0, vue.createVNode)((0, vue.unref)(theme_toggler_default), { class: "docs-header__theme" }),
4623
- localeOptions.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("nav", _hoisted_4$6, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Dropdown), {
4883
+ localeOptions.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("nav", _hoisted_13$1, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Dropdown), {
4624
4884
  modelValue: localeMenuVisible.value,
4625
4885
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => localeMenuVisible.value = $event),
4626
4886
  class: "docs-header__locale-dropdown",
@@ -4660,42 +4920,68 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4660
4920
  "aria-label": (0, vue.unref)(t)("client.header.language"),
4661
4921
  "aria-expanded": localeMenuVisible.value,
4662
4922
  "aria-haspopup": "menu"
4663
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "language" })], 8, _hoisted_5$5)]),
4923
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), {
4924
+ name: "language",
4925
+ size: 22
4926
+ })], 8, _hoisted_14)]),
4664
4927
  _: 1
4665
4928
  }, 8, ["modelValue"])])) : (0, vue.createCommentVNode)("v-if", true),
4666
- (0, vue.createElementVNode)("button", {
4667
- type: "button",
4668
- class: "docs-header__action docs-header__editor",
4669
- title: (0, vue.unref)(t)("client.header.editor"),
4670
- "aria-label": (0, vue.unref)(t)("client.header.editor"),
4671
- onClick: handleEditor
4672
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_6$3),
4673
- (0, vue.createVNode)((0, vue.unref)(vue_router.RouterLink), {
4674
- class: "docs-header__action docs-header__playground-resource",
4675
- to: playgroundResourcePath.value,
4676
- title: (0, vue.unref)(t)("client.header.playgroundResource"),
4677
- "aria-label": (0, vue.unref)(t)("client.header.playgroundResource")
4678
- }, {
4679
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "playgroundResource" })]),
4680
- _: 1
4681
- }, 8, [
4682
- "to",
4683
- "title",
4684
- "aria-label"
4685
- ]),
4686
- (0, vue.createVNode)((0, vue.unref)(vue_router.RouterLink), {
4687
- class: "docs-header__action docs-header__database",
4688
- to: databasePath.value,
4689
- title: (0, vue.unref)(t)("client.header.database"),
4690
- "aria-label": (0, vue.unref)(t)("client.header.database")
4929
+ repositoryHref.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4930
+ key: 1,
4931
+ class: "docs-header__action docs-header__repository",
4932
+ href: repositoryHref.value,
4933
+ target: "_blank",
4934
+ rel: "noopener noreferrer",
4935
+ title: (0, vue.unref)(t)("client.header.repository"),
4936
+ "aria-label": (0, vue.unref)(t)("client.header.repository")
4937
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), {
4938
+ name: repositoryIcon.value,
4939
+ size: 24
4940
+ }, null, 8, ["name"])], 8, _hoisted_15)) : (0, vue.createCommentVNode)("v-if", true),
4941
+ (0, vue.createElementVNode)("nav", _hoisted_16, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Dropdown), {
4942
+ modelValue: toolsMenuVisible.value,
4943
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => toolsMenuVisible.value = $event),
4944
+ class: "docs-header__tools-dropdown",
4945
+ "portal-class": "docs-header__tools-portal",
4946
+ trigger: "hover",
4947
+ placement: "bottom-right"
4691
4948
  }, {
4692
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "database" })]),
4949
+ content: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_deot_vc.DropdownMenu), {
4950
+ class: "docs-header__tools-options",
4951
+ role: "menu",
4952
+ "aria-label": (0, vue.unref)(t)("client.header.tools")
4953
+ }, {
4954
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(toolsOptions.value, (item) => {
4955
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_deot_vc.DropdownItem), {
4956
+ key: item.value,
4957
+ class: "docs-header__tools-option",
4958
+ value: item.value,
4959
+ selected: item.selected,
4960
+ role: "menuitem",
4961
+ onClick: handleTools
4962
+ }, {
4963
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), {
4964
+ name: item.icon,
4965
+ size: 16
4966
+ }, null, 8, ["name"]), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)(item.label), 1)]),
4967
+ _: 2
4968
+ }, 1032, ["value", "selected"]);
4969
+ }), 128))]),
4970
+ _: 1
4971
+ }, 8, ["aria-label"])]),
4972
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("button", {
4973
+ type: "button",
4974
+ class: "docs-header__action docs-header__tools-trigger",
4975
+ title: (0, vue.unref)(t)("client.header.tools"),
4976
+ "aria-label": (0, vue.unref)(t)("client.header.tools"),
4977
+ "aria-expanded": toolsMenuVisible.value,
4978
+ "aria-haspopup": "menu"
4979
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), {
4980
+ name: "more",
4981
+ size: 22
4982
+ })], 8, _hoisted_17)]),
4693
4983
  _: 1
4694
- }, 8, [
4695
- "to",
4696
- "title",
4697
- "aria-label"
4698
- ])
4984
+ }, 8, ["modelValue"])])
4699
4985
  ])
4700
4986
  ]);
4701
4987
  };
@@ -4976,6 +5262,10 @@ var resource_slot_default = /* @__PURE__ */ (0, vue.defineComponent)({
4976
5262
  const config = routeConfig.value;
4977
5263
  let slot = config?.[props.name];
4978
5264
  if (typeof slot === "undefined") slot = props.name === "content" ? "default" : null;
5265
+ if (props.name === "content") {
5266
+ slot = resolveRouteContent(slot, lang.value, docs, route.meta.docsHome ? "/" : "");
5267
+ if (typeof slot === "undefined") slot = null;
5268
+ }
4979
5269
  if (slot === "default" && props.name === "sidebar") slot = "./sidebar.json";
4980
5270
  const isBuiltin = slot === "default" && props.name !== "content";
4981
5271
  const stableSlotKey = [
@@ -5307,13 +5597,13 @@ var createSortableDemo = (lang) => sortableDocument(copy(lang, "流式画布", "
5307
5597
  /**
5308
5598
  * 落地页模块链:Hero → Features → Steps → FAQ → CTA。
5309
5599
  * @param lang 当前文档语言。
5310
- * @returns 可交给 Combo 或 `$docs.home` 的 V2 文档。
5600
+ * @returns 可交给 Combo 或 `routes['/'].content` 的 V2 文档。
5311
5601
  */
5312
5602
  var createLandingDemo = (lang) => sortableDocument(copy(lang, "落地页组合", "Landing composition"), [
5313
5603
  sortableNode("hero", {
5314
5604
  eyebrow: copy(lang, "产品首页", "Product home"),
5315
5605
  title: copy(lang, "用可复用模块拼出完整落地页", "Compose a complete landing page from reusable modules"),
5316
- description: copy(lang, "Hero、特性、步骤、问答和行动条已经串好。把 landing 工厂赋给 `$docs.home` 或路由 `content`,再按品牌改文案。", "Hero, features, steps, FAQ and CTA are already wired. Assign the landing factory to `$docs.home` or route `content`."),
5606
+ description: copy(lang, "Hero、特性、步骤、问答和行动条已经串好。把 landing 工厂赋给 `routes['/'].content` 或其它路由 `content`,再按品牌改文案。", "Hero, features, steps, FAQ and CTA are already wired. Assign the landing factory to `routes['/'].content` or another route `content`."),
5317
5607
  align: "left",
5318
5608
  accent: "#873bf4",
5319
5609
  accentSecondary: "#2d8cf0",
@@ -5437,7 +5727,7 @@ var createLandingDemo = (lang) => sortableDocument(copy(lang, "落地页组合",
5437
5727
  items: [
5438
5728
  {
5439
5729
  question: copy(lang, "能直接当首页用吗?", "Can I use this as the home page?"),
5440
- answer: copy(lang, "可以。把 `createRendererEditorDemoDocument('landing', lang)` 赋给 `$docs.home.locales`,或把保存后的 `.page.json` 配到 `home.locales`。", "Yes. Assign `createRendererEditorDemoDocument('landing', lang)` to `$docs.home.locales`, or point `home.locales` at the saved `.page.json`.")
5730
+ answer: copy(lang, "可以。把 `createRendererEditorDemoDocument('landing', lang)` 赋给 `routes['/'].content`,或把保存后的 `.page.json` 配到对应语言。", "Yes. Assign `createRendererEditorDemoDocument('landing', lang)` to `routes['/'].content`, or point the matching language at the saved `.page.json`.")
5441
5731
  },
5442
5732
  {
5443
5733
  question: copy(lang, "切换到自由布局会丢块吗?", "Will switching to a free canvas drop blocks?"),
@@ -5655,7 +5945,7 @@ var createDocsDemo = (lang) => {
5655
5945
  "```",
5656
5946
  "",
5657
5947
  ":::tip",
5658
- "开发模式从 Header 进入编辑器后,保存会把内联文档写成 `.page.json`,并回写 `home.locales`。",
5948
+ "开发模式从 Header 进入编辑器后,保存会把内联文档写成 `.page.json`,并回写 `routes['/'].content`。",
5659
5949
  ":::",
5660
5950
  "",
5661
5951
  ":::warning",
@@ -5688,7 +5978,7 @@ var createDocsDemo = (lang) => {
5688
5978
  "```",
5689
5979
  "",
5690
5980
  ":::tip",
5691
- "In development, saving from the header editor writes an inline document to `.page.json` and updates `home.locales`.",
5981
+ "In development, saving from the header editor writes an inline document to `.page.json` and updates `routes['/'].content`.",
5692
5982
  ":::",
5693
5983
  "",
5694
5984
  ":::warning",
@@ -5868,7 +6158,7 @@ var createComboDemo = (lang) => sortableDocument(copy(lang, "混合组合", "Mix
5868
6158
  sortableNode("cta", {
5869
6159
  eyebrow: copy(lang, "收束", "Close"),
5870
6160
  title: copy(lang, "同一套模块表供给首页和内容页", "One module table for home and content pages"),
5871
- description: copy(lang, "把这份文档交给 `$docs.home` 或路由 `content`。需要纯落地页时打开 landing,需要纯文档块时打开 docs。", "Hand this document to `$docs.home` or a route `content`. Open landing for a pure marketing page, or docs for document blocks only."),
6161
+ description: copy(lang, "把这份文档交给 `routes['/'].content` 或其它路由 `content`。需要纯落地页时打开 landing,需要纯文档块时打开 docs。", "Hand this document to `routes['/'].content` or another route `content`. Open landing for a pure marketing page, or docs for document blocks only."),
5872
6162
  align: "center",
5873
6163
  accent: "#873bf4",
5874
6164
  accentSecondary: "#2d8cf0",
@@ -6109,7 +6399,7 @@ var createSelectionDemo = (lang) => {
6109
6399
  //#region packages/client/src/pages/renderer-editor-demos/catalog.ts
6110
6400
  /**
6111
6401
  * development 下 `/renderer-editor-demos?name=` 演示清单。
6112
- * 工厂返回值可直接赋给 `$docs.home` 或路由 `content`。
6402
+ * 工厂返回值可直接赋给 `routes['/'].content` 或其它路由 `content`。
6113
6403
  */
6114
6404
  var RENDERER_EDITOR_DEMOS = [
6115
6405
  "sortable",
@@ -7657,7 +7947,7 @@ var renderer_editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
7657
7947
  let generation = 0;
7658
7948
  const lang = (0, vue.computed)(() => String(route.params.lang || Object.keys(docs.locales)[0] || "en-US"));
7659
7949
  const source = (0, vue.computed)(() => String(route.query.source || ""));
7660
- const configuredHome = () => docs.home?.locales?.[lang.value] || docs.home?.locales?.["en-US"];
7950
+ const configuredHome = () => resolveHomeContent(docs, lang.value);
7661
7951
  const hashValue = (value) => {
7662
7952
  const text = JSON.stringify(value);
7663
7953
  let hash = 2166136261;
@@ -7822,10 +8112,7 @@ var renderer_editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
7822
8112
  if (getDocsRuntime().mode !== "development") return;
7823
8113
  try {
7824
8114
  const result = await saveDocument(value);
7825
- if (String(route.query.type || "") === "home") {
7826
- docs.home ||= { locales: {} };
7827
- docs.home.locales[lang.value] = result.source;
7828
- }
8115
+ if (String(route.query.type || "") === "home") writeHomeContent(docs, lang.value, result.source);
7829
8116
  handleSaved(result.source);
7830
8117
  _deot_vc.Message.success(t("renderer.common.saved"));
7831
8118
  } catch (reason) {
@@ -7922,8 +8209,8 @@ var home_default = /* @__PURE__ */ (0, vue.defineComponent)({
7922
8209
  loading.value = true;
7923
8210
  document.value = void 0;
7924
8211
  try {
7925
- const configured = config.home?.locales?.[lang.value] || config.home?.locales?.["en-US"];
7926
- if (!configured) return;
8212
+ const configured = resolveHomeContent(config, lang.value);
8213
+ if (!configured || configured === "default") return;
7927
8214
  if (typeof configured !== "string") {
7928
8215
  document.value = parseDocument(configured);
7929
8216
  return;
@@ -8519,11 +8806,6 @@ var slotProps = {
8519
8806
  extra: { name: "extra" }
8520
8807
  };
8521
8808
  var hasConfiguredLanguage = (config, language) => Object.prototype.hasOwnProperty.call(config.locales, language);
8522
- var hasLanguage = (config, path) => {
8523
- const language = path.split(/[?#]/u, 1)[0].split("/").filter(Boolean)[0];
8524
- const configuredLanguages = Object.keys(config.locales);
8525
- return Boolean(language && (hasConfiguredLanguage(config, language) || !configuredLanguages.length && language === getDefaultLanguage(config)));
8526
- };
8527
8809
  var matchesConfiguredPath = (config, pathname) => {
8528
8810
  const target = pathname.split("/").filter(Boolean);
8529
8811
  return Object.keys(config.routes).some((path) => {
@@ -8549,10 +8831,6 @@ var normalizeInvalidLanguage = (config, defaultLanguage, to, languageRoot = fals
8549
8831
  };
8550
8832
  return replaceLanguage(defaultLanguage, to);
8551
8833
  };
8552
- var localizePath = (config, lang, target) => {
8553
- if (isExternalLink(target) || hasLanguage(config, target)) return target;
8554
- return localizeRoutePath(lang, target);
8555
- };
8556
8834
  var resolveRedirect = (config, target, to) => localizePath(config, String(to.params.lang || getDefaultLanguage(config)), typeof target === "function" ? target(to) : target);
8557
8835
  var createRouteRecord = (config, path, routeConfig, options = {}) => {
8558
8836
  if (typeof routeConfig === "string" || typeof routeConfig === "function") return {
@@ -8577,7 +8855,6 @@ var createRouteRecord = (config, path, routeConfig, options = {}) => {
8577
8855
  var createDocsRouter = (config, options = {}) => {
8578
8856
  const defaultLanguage = getDefaultLanguage(config);
8579
8857
  let currentLanguage = options.initialLanguage && hasConfiguredLanguage(config, options.initialLanguage) ? options.initialLanguage : defaultLanguage;
8580
- const hasConfiguredRoot = Object.prototype.hasOwnProperty.call(config.routes, "/");
8581
8858
  const hasConfiguredEditorRoute = Object.keys(config.routes).some((path) => (path.startsWith("/") ? path : `/${path}`) === "/renderer-editor");
8582
8859
  const hasConfiguredDemosRoute = Object.keys(config.routes).some((path) => (path.startsWith("/") ? path : `/${path}`) === "/renderer-editor-demos");
8583
8860
  const isDevelopment = config.runtime?.mode === "development";
@@ -8649,26 +8926,35 @@ var createDocsRouter = (config, options = {}) => {
8649
8926
  path: "/",
8650
8927
  redirect: () => `/${currentLanguage}`
8651
8928
  });
8652
- if (hasConfiguredRoot) routes.push(createRouteRecord(config, "/:lang", config.routes["/"], {
8929
+ const rootConfig = config.routes["/"];
8930
+ if (rootConfig !== void 0 && !isHomeLikeRoute(rootConfig)) routes.push(createRouteRecord(config, "/:lang", rootConfig, {
8653
8931
  localized: true,
8654
8932
  languageRoot: true
8655
8933
  }));
8656
- else routes.push({
8657
- path: "/:lang",
8658
- component: home_default,
8659
- meta: {
8660
- docsHome: true,
8661
- docsLocalized: true,
8662
- docsLanguageRoot: true,
8663
- docsRoute: {
8664
- content: null,
8665
- sidebar: null,
8666
- header: "default",
8667
- footer: "default",
8668
- extra: null
8934
+ else {
8935
+ const routeObject = typeof rootConfig === "object" && rootConfig ? rootConfig : {};
8936
+ routes.push({
8937
+ path: "/:lang",
8938
+ components: {
8939
+ default: home_default,
8940
+ extra: resource_slot_default
8941
+ },
8942
+ props: { extra: { name: "extra" } },
8943
+ meta: {
8944
+ docsHome: true,
8945
+ docsLocalized: true,
8946
+ docsLanguageRoot: true,
8947
+ docsRoute: {
8948
+ content: null,
8949
+ sidebar: null,
8950
+ header: "default",
8951
+ footer: "default",
8952
+ extra: null,
8953
+ ...routeObject
8954
+ }
8669
8955
  }
8670
- }
8671
- });
8956
+ });
8957
+ }
8672
8958
  Object.entries(config.routes).forEach(([path, routeConfig]) => {
8673
8959
  if (path === "/" || path === "*") return;
8674
8960
  const normalized = path.startsWith("/") ? path : `/${path}`;
@@ -8711,6 +8997,23 @@ var createDocsRouter = (config, options = {}) => {
8711
8997
  var PlaygroundResource = Resource.playground;
8712
8998
  //#endregion
8713
8999
  //#region packages/client/src/index.ts
9000
+ var consumeStoredRedirect = async (router, config) => {
9001
+ const REDIRECT_KEY = "@deot/docs:redirect";
9002
+ let redirect;
9003
+ try {
9004
+ redirect = sessionStorage.getItem(REDIRECT_KEY);
9005
+ sessionStorage.removeItem(REDIRECT_KEY);
9006
+ } catch {
9007
+ return;
9008
+ }
9009
+ if (!redirect || !redirect.startsWith("/") || redirect.startsWith("//")) return;
9010
+ const base = new URL(getDocsDeploymentBase(config)).pathname;
9011
+ if (redirect === `${location.pathname}${location.search}${location.hash}`) return;
9012
+ if (base !== "/" && !redirect.startsWith(base)) return;
9013
+ try {
9014
+ await router.push(`/${redirect.slice(base.length)}`);
9015
+ } catch {}
9016
+ };
8714
9017
  var bootstrap = async (config) => {
8715
9018
  config ||= window.$docs || {
8716
9019
  locales: {},
@@ -8739,6 +9042,8 @@ var bootstrap = async (config) => {
8739
9042
  (async () => {
8740
9043
  try {
8741
9044
  await router.isReady();
9045
+ if (disconnected) return;
9046
+ await consumeStoredRedirect(router, config);
8742
9047
  if (!disconnected) stopPrefetch = Resource.prefetch.start(config);
8743
9048
  } catch {}
8744
9049
  })();