@deot/docs-client 1.0.6 → 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
@@ -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");
@@ -1461,6 +1461,45 @@ var toLogicalResourceSource = (config, lang, url) => {
1461
1461
  return `./${resolved.pathname.slice(root.pathname.length)}${resolved.search}${resolved.hash}`;
1462
1462
  };
1463
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
1464
1503
  //#region packages/client/src/components/renderer/markdown-props.ts
1465
1504
  var INDICATOR_POSITIONS = /* @__PURE__ */ new Set(["left", "right"]);
1466
1505
  var isPlainObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -2244,7 +2283,8 @@ var ResourcePlanner = class {
2244
2283
  async resolveRouteContentIdentity(config, lang, pathname, routeMatch) {
2245
2284
  if (!routeMatch) return null;
2246
2285
  const route = this.createRouteShape(lang, pathname, routeMatch.params);
2247
- 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;
2248
2288
  if (slot === null) return null;
2249
2289
  if (slot !== "default") {
2250
2290
  if (typeof slot !== "string") return null;
@@ -2286,14 +2326,6 @@ var ResourcePlanner = class {
2286
2326
  params: {}
2287
2327
  });
2288
2328
  }
2289
- this.collectBuiltInHomePages(config, collector);
2290
- }
2291
- collectBuiltInHomePages(config, collector) {
2292
- if (Object.prototype.hasOwnProperty.call(config.routes, "/")) return;
2293
- for (const lang of this.getLanguages(config)) {
2294
- const home = config.home?.locales?.[lang] || config.home?.locales?.["en-US"];
2295
- if (typeof home === "string") collector.add(lang, home, "page");
2296
- }
2297
2329
  }
2298
2330
  needsSidebarDiscovery(config) {
2299
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"));
@@ -2408,17 +2440,6 @@ var ResourcePlanner = class {
2408
2440
  await this.collectConfiguredResources(config, configured);
2409
2441
  const sidebarKeys = new Set([...configured.identities.values()].filter((identity) => identity.type === "sidebar").map(resourceIdentityKey));
2410
2442
  for (const lang of this.getLanguages(config)) {
2411
- if (!Object.prototype.hasOwnProperty.call(config.routes, "/")) {
2412
- const home = config.home?.locales?.[lang] || config.home?.locales?.["en-US"];
2413
- if (typeof home === "string") {
2414
- const identity = createResourceIdentity(config, lang, "page", home);
2415
- const key = resourceIdentityKey(identity);
2416
- if (!resources.has(key)) resources.set(key, {
2417
- identity,
2418
- path: `/${lang}`
2419
- });
2420
- }
2421
- }
2422
2443
  await add(lang, "/");
2423
2444
  for (const [pathname, routeConfig] of Object.entries(config.routes)) {
2424
2445
  if (!routeConfig || pathname === "*") continue;
@@ -3519,29 +3540,27 @@ var SearchManager = class {
3519
3540
  this.parsed.set(key, document);
3520
3541
  documents.push(document);
3521
3542
  }
3522
- if (!Object.prototype.hasOwnProperty.call(config.routes, "/")) {
3523
- const home = config.home?.locales?.[lang] || config.home?.locales?.["en-US"];
3524
- if (home && typeof home !== "string") {
3525
- const key = `${namespace}|${lang}|inline-home`;
3526
- try {
3527
- const parsed = await parseRendererSearch(config, JSON.stringify(home), lang);
3528
- documents.push({
3529
- key,
3530
- hash: "inline-home",
3531
- namespace,
3532
- lang,
3533
- path: `/${lang}`,
3534
- source: "home",
3535
- title: parsed.title || t("client.search.untitled"),
3536
- text: parsed.text,
3537
- sections: parsed.sections.map((section) => ({
3538
- title: section.title,
3539
- anchor: section.anchor,
3540
- text: section.text
3541
- }))
3542
- });
3543
- } catch {}
3544
- }
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 {}
3545
3564
  }
3546
3565
  this.documents.set(`${namespace}|${lang}`, documents);
3547
3566
  return documents.length;
@@ -3641,27 +3660,27 @@ var _hoisted_9$3 = {
3641
3660
  key: 1,
3642
3661
  class: "docs-search__state docs-search__state--error"
3643
3662
  };
3644
- var _hoisted_10$2 = { class: "docs-search__group-title" };
3645
- var _hoisted_11$2 = [
3663
+ var _hoisted_10$3 = { class: "docs-search__group-title" };
3664
+ var _hoisted_11$3 = [
3646
3665
  "data-search-index",
3647
3666
  "onMouseenter",
3648
3667
  "onClick"
3649
3668
  ];
3650
- var _hoisted_12$2 = {
3669
+ var _hoisted_12$3 = {
3651
3670
  class: "docs-search__result-icon",
3652
3671
  "aria-hidden": "true"
3653
3672
  };
3654
- var _hoisted_13$1 = { class: "docs-search__result-content" };
3655
- var _hoisted_14 = { class: "docs-search__result-title" };
3656
- 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 = {
3657
3676
  key: 0,
3658
3677
  class: "docs-search__result-parent"
3659
3678
  };
3660
- var _hoisted_16 = {
3679
+ var _hoisted_16$1 = {
3661
3680
  key: 1,
3662
3681
  class: "docs-search__result-excerpt"
3663
3682
  };
3664
- var _hoisted_17 = {
3683
+ var _hoisted_17$1 = {
3665
3684
  key: 0,
3666
3685
  class: "docs-search__enter",
3667
3686
  "aria-hidden": "true"
@@ -3927,7 +3946,7 @@ var search_default = /* @__PURE__ */ (0, vue.defineComponent)({
3927
3946
  "wrapper-style": "overflow-x: hidden;"
3928
3947
  }, {
3929
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 }, [
3930
- (0, vue.createElementVNode)("div", _hoisted_10$2, (0, vue.toDisplayString)((0, vue.unref)(t)("client.search.documentation")), 1),
3949
+ (0, vue.createElementVNode)("div", _hoisted_10$3, (0, vue.toDisplayString)((0, vue.unref)(t)("client.search.documentation")), 1),
3931
3950
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(results.value, (item, index) => {
3932
3951
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3933
3952
  key: `${item.id}:${item.kind}`,
@@ -3940,16 +3959,16 @@ var search_default = /* @__PURE__ */ (0, vue.defineComponent)({
3940
3959
  onMouseenter: ($event) => handleMouseenter(index),
3941
3960
  onClick: ($event) => handleSelect(item)
3942
3961
  }, [
3943
- (0, vue.createElementVNode)("span", _hoisted_12$2, (0, vue.toDisplayString)(item.kind === "section" ? "#" : "▤"), 1),
3944
- (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, {
3945
3964
  text: item.sectionTitle || item.title,
3946
3965
  keyword: keyword.value
3947
- }, 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, {
3948
3967
  text: item.excerpt,
3949
3968
  keyword: keyword.value
3950
3969
  }, null, 8, ["text", "keyword"])])) : (0, vue.createCommentVNode)("v-if", true)]),
3951
- index === selectedIndex.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_17, "↵")) : (0, vue.createCommentVNode)("v-if", true)
3952
- ], 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);
3953
3972
  }), 128)),
3954
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)
3955
3974
  ], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 3 }, [
@@ -4090,6 +4109,10 @@ var CLIENT_ICON_DEFINITIONS = {
4090
4109
  "a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"
4091
4110
  ].join("")
4092
4111
  },
4112
+ more: {
4113
+ filled: false,
4114
+ path: ["M4 4h6v6H4zm10 0h6v6h-6z", "M4 14h6v6H4zm10 0h6v6h-6z"].join("")
4115
+ },
4093
4116
  menu: {
4094
4117
  filled: false,
4095
4118
  path: "M4 7h16M4 12h16M4 17h16"
@@ -4105,6 +4128,26 @@ var CLIENT_ICON_DEFINITIONS = {
4105
4128
  home: {
4106
4129
  filled: false,
4107
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("")
4108
4151
  }
4109
4152
  };
4110
4153
  //#endregion
@@ -4119,22 +4162,34 @@ var _hoisted_2$14 = ["d"];
4119
4162
  //#region packages/client/src/components/icon/icon.vue
4120
4163
  var icon_default = /* @__PURE__ */ (0, vue.defineComponent)({
4121
4164
  __name: "icon",
4122
- props: { name: {} },
4165
+ props: {
4166
+ name: {},
4167
+ size: {}
4168
+ },
4123
4169
  setup(__props) {
4124
4170
  const props = __props;
4125
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
+ });
4126
4180
  return (_ctx, _cache) => {
4127
4181
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("svg", {
4128
4182
  class: "docs-client-icon",
4129
4183
  "data-icon": __props.name,
4130
4184
  viewBox: "0 0 24 24",
4185
+ style: (0, vue.normalizeStyle)(sizeStyle.value),
4131
4186
  fill: definition.value.filled ? "currentColor" : "none",
4132
4187
  stroke: definition.value.filled ? "none" : "currentColor",
4133
4188
  "stroke-width": "1.8",
4134
4189
  "stroke-linecap": "round",
4135
4190
  "stroke-linejoin": "round",
4136
4191
  "aria-hidden": "true"
4137
- }, [(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);
4138
4193
  };
4139
4194
  }
4140
4195
  });
@@ -4544,17 +4599,36 @@ var _hoisted_2$13 = ["href"];
4544
4599
  var _hoisted_3$11 = ["src"];
4545
4600
  var _hoisted_4$6 = ["src"];
4546
4601
  var _hoisted_5$5 = { class: "docs-header__search" };
4547
- var _hoisted_6$3 = { class: "docs-header__actions" };
4548
- var _hoisted_7$2 = {
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 = {
4549
4613
  key: 0,
4550
4614
  class: "docs-header__locales"
4551
4615
  };
4552
- var _hoisted_8$2 = [
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 = [
4553
4628
  "title",
4554
4629
  "aria-label",
4555
4630
  "aria-expanded"
4556
4631
  ];
4557
- var _hoisted_9$2 = ["title", "aria-label"];
4558
4632
  //#endregion
4559
4633
  //#region packages/client/src/components/layout/default-header.vue
4560
4634
  var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -4565,6 +4639,7 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4565
4639
  const config = getDocsConfig();
4566
4640
  const { t } = (0, _deot_docs_locale.useLocale)();
4567
4641
  const localeMenuVisible = (0, vue.ref)(false);
4642
+ const toolsMenuVisible = (0, vue.ref)(false);
4568
4643
  const lang = (0, vue.computed)(() => String(route.params.lang));
4569
4644
  /**
4570
4645
  * 按当前语言和站点默认语言选择 Header 配置。
@@ -4576,6 +4651,26 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4576
4651
  return findLanguageValue(value, lang.value) ?? findLanguageValue(value, getDefaultLanguage(config));
4577
4652
  }
4578
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
+ }
4579
4674
  const brandLogo = (0, vue.computed)(() => resolveLocalized(brandOptions?.logo) || "");
4580
4675
  const brandLabel = (0, vue.computed)(() => resolveLocalized(brandOptions?.label) || config.namespace || t("client.header.brand"));
4581
4676
  const brandValue = (0, vue.computed)(() => resolveLocalized(brandOptions?.value) || `/${lang.value}`);
@@ -4587,6 +4682,52 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4587
4682
  label: item.label,
4588
4683
  value
4589
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
+ });
4590
4731
  const localePath = (locale) => {
4591
4732
  const segments = route.path.split("/").filter(Boolean);
4592
4733
  segments[0] = locale;
@@ -4601,18 +4742,12 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4601
4742
  const target = String(locale);
4602
4743
  if (target !== lang.value) router.push(localePath(target));
4603
4744
  };
4604
- const isInlineRendererDocument = (value) => Boolean(value && typeof value === "object" && "schemaVersion" in value);
4745
+ const isInlineRendererDocument = (value) => isRendererDocument(value);
4605
4746
  const handleEditor = async () => {
4606
4747
  const routeConfig = route.meta.docsRoute;
4607
- let source = routeConfig?.content;
4748
+ let source = route.meta.docsHome ? resolveHomeContent(config, lang.value) : resolveRouteContent(routeConfig?.content, lang.value, config);
4608
4749
  let type = route.meta.docsHome ? "home" : "";
4609
- if (route.meta.docsHome) {
4610
- const configured = config.home?.locales?.[lang.value] || config.home?.locales?.["en-US"];
4611
- if (typeof configured === "string") {
4612
- source = configured;
4613
- type = "page";
4614
- }
4615
- }
4750
+ if (route.meta.docsHome && typeof source === "string") type = "page";
4616
4751
  if (!route.meta.docsHome && isInlineRendererDocument(source)) {
4617
4752
  stashInlineRendererDocument(route.fullPath, source);
4618
4753
  await router.push({
@@ -4639,6 +4774,18 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4639
4774
  }
4640
4775
  });
4641
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
+ };
4642
4789
  return (_ctx, _cache) => {
4643
4790
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("header", _hoisted_1$15, [
4644
4791
  brandExternal.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
@@ -4666,9 +4813,74 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4666
4813
  _: 1
4667
4814
  }, 8, ["to"])),
4668
4815
  (0, vue.createElementVNode)("div", _hoisted_5$5, [(0, vue.createVNode)((0, vue.unref)(trigger_default))]),
4669
- (0, vue.createElementVNode)("div", _hoisted_6$3, [
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, [
4670
4882
  (0, vue.createVNode)((0, vue.unref)(theme_toggler_default), { class: "docs-header__theme" }),
4671
- localeOptions.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("nav", _hoisted_7$2, [(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), {
4672
4884
  modelValue: localeMenuVisible.value,
4673
4885
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => localeMenuVisible.value = $event),
4674
4886
  class: "docs-header__locale-dropdown",
@@ -4708,42 +4920,68 @@ var default_header_default = /* @__PURE__ */ (0, vue.defineComponent)({
4708
4920
  "aria-label": (0, vue.unref)(t)("client.header.language"),
4709
4921
  "aria-expanded": localeMenuVisible.value,
4710
4922
  "aria-haspopup": "menu"
4711
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "language" })], 8, _hoisted_8$2)]),
4923
+ }, [(0, vue.createVNode)((0, vue.unref)(icon_default), {
4924
+ name: "language",
4925
+ size: 22
4926
+ })], 8, _hoisted_14)]),
4712
4927
  _: 1
4713
4928
  }, 8, ["modelValue"])])) : (0, vue.createCommentVNode)("v-if", true),
4714
- (0, vue.createElementVNode)("button", {
4715
- type: "button",
4716
- class: "docs-header__action docs-header__editor",
4717
- title: (0, vue.unref)(t)("client.header.editor"),
4718
- "aria-label": (0, vue.unref)(t)("client.header.editor"),
4719
- onClick: handleEditor
4720
- }, [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "editor" })], 8, _hoisted_9$2),
4721
- (0, vue.createVNode)((0, vue.unref)(vue_router.RouterLink), {
4722
- class: "docs-header__action docs-header__playground-resource",
4723
- to: playgroundResourcePath.value,
4724
- title: (0, vue.unref)(t)("client.header.playgroundResource"),
4725
- "aria-label": (0, vue.unref)(t)("client.header.playgroundResource")
4726
- }, {
4727
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(icon_default), { name: "playgroundResource" })]),
4728
- _: 1
4729
- }, 8, [
4730
- "to",
4731
- "title",
4732
- "aria-label"
4733
- ]),
4734
- (0, vue.createVNode)((0, vue.unref)(vue_router.RouterLink), {
4735
- class: "docs-header__action docs-header__database",
4736
- to: databasePath.value,
4737
- title: (0, vue.unref)(t)("client.header.database"),
4738
- "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"
4739
4948
  }, {
4740
- 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)]),
4741
4983
  _: 1
4742
- }, 8, [
4743
- "to",
4744
- "title",
4745
- "aria-label"
4746
- ])
4984
+ }, 8, ["modelValue"])])
4747
4985
  ])
4748
4986
  ]);
4749
4987
  };
@@ -5024,6 +5262,10 @@ var resource_slot_default = /* @__PURE__ */ (0, vue.defineComponent)({
5024
5262
  const config = routeConfig.value;
5025
5263
  let slot = config?.[props.name];
5026
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
+ }
5027
5269
  if (slot === "default" && props.name === "sidebar") slot = "./sidebar.json";
5028
5270
  const isBuiltin = slot === "default" && props.name !== "content";
5029
5271
  const stableSlotKey = [
@@ -5355,13 +5597,13 @@ var createSortableDemo = (lang) => sortableDocument(copy(lang, "流式画布", "
5355
5597
  /**
5356
5598
  * 落地页模块链:Hero → Features → Steps → FAQ → CTA。
5357
5599
  * @param lang 当前文档语言。
5358
- * @returns 可交给 Combo 或 `$docs.home` 的 V2 文档。
5600
+ * @returns 可交给 Combo 或 `routes['/'].content` 的 V2 文档。
5359
5601
  */
5360
5602
  var createLandingDemo = (lang) => sortableDocument(copy(lang, "落地页组合", "Landing composition"), [
5361
5603
  sortableNode("hero", {
5362
5604
  eyebrow: copy(lang, "产品首页", "Product home"),
5363
5605
  title: copy(lang, "用可复用模块拼出完整落地页", "Compose a complete landing page from reusable modules"),
5364
- 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`."),
5365
5607
  align: "left",
5366
5608
  accent: "#873bf4",
5367
5609
  accentSecondary: "#2d8cf0",
@@ -5485,7 +5727,7 @@ var createLandingDemo = (lang) => sortableDocument(copy(lang, "落地页组合",
5485
5727
  items: [
5486
5728
  {
5487
5729
  question: copy(lang, "能直接当首页用吗?", "Can I use this as the home page?"),
5488
- 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`.")
5489
5731
  },
5490
5732
  {
5491
5733
  question: copy(lang, "切换到自由布局会丢块吗?", "Will switching to a free canvas drop blocks?"),
@@ -5703,7 +5945,7 @@ var createDocsDemo = (lang) => {
5703
5945
  "```",
5704
5946
  "",
5705
5947
  ":::tip",
5706
- "开发模式从 Header 进入编辑器后,保存会把内联文档写成 `.page.json`,并回写 `home.locales`。",
5948
+ "开发模式从 Header 进入编辑器后,保存会把内联文档写成 `.page.json`,并回写 `routes['/'].content`。",
5707
5949
  ":::",
5708
5950
  "",
5709
5951
  ":::warning",
@@ -5736,7 +5978,7 @@ var createDocsDemo = (lang) => {
5736
5978
  "```",
5737
5979
  "",
5738
5980
  ":::tip",
5739
- "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`.",
5740
5982
  ":::",
5741
5983
  "",
5742
5984
  ":::warning",
@@ -5916,7 +6158,7 @@ var createComboDemo = (lang) => sortableDocument(copy(lang, "混合组合", "Mix
5916
6158
  sortableNode("cta", {
5917
6159
  eyebrow: copy(lang, "收束", "Close"),
5918
6160
  title: copy(lang, "同一套模块表供给首页和内容页", "One module table for home and content pages"),
5919
- 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."),
5920
6162
  align: "center",
5921
6163
  accent: "#873bf4",
5922
6164
  accentSecondary: "#2d8cf0",
@@ -6157,7 +6399,7 @@ var createSelectionDemo = (lang) => {
6157
6399
  //#region packages/client/src/pages/renderer-editor-demos/catalog.ts
6158
6400
  /**
6159
6401
  * development 下 `/renderer-editor-demos?name=` 演示清单。
6160
- * 工厂返回值可直接赋给 `$docs.home` 或路由 `content`。
6402
+ * 工厂返回值可直接赋给 `routes['/'].content` 或其它路由 `content`。
6161
6403
  */
6162
6404
  var RENDERER_EDITOR_DEMOS = [
6163
6405
  "sortable",
@@ -7705,7 +7947,7 @@ var renderer_editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
7705
7947
  let generation = 0;
7706
7948
  const lang = (0, vue.computed)(() => String(route.params.lang || Object.keys(docs.locales)[0] || "en-US"));
7707
7949
  const source = (0, vue.computed)(() => String(route.query.source || ""));
7708
- const configuredHome = () => docs.home?.locales?.[lang.value] || docs.home?.locales?.["en-US"];
7950
+ const configuredHome = () => resolveHomeContent(docs, lang.value);
7709
7951
  const hashValue = (value) => {
7710
7952
  const text = JSON.stringify(value);
7711
7953
  let hash = 2166136261;
@@ -7870,10 +8112,7 @@ var renderer_editor_default = /* @__PURE__ */ (0, vue.defineComponent)({
7870
8112
  if (getDocsRuntime().mode !== "development") return;
7871
8113
  try {
7872
8114
  const result = await saveDocument(value);
7873
- if (String(route.query.type || "") === "home") {
7874
- docs.home ||= { locales: {} };
7875
- docs.home.locales[lang.value] = result.source;
7876
- }
8115
+ if (String(route.query.type || "") === "home") writeHomeContent(docs, lang.value, result.source);
7877
8116
  handleSaved(result.source);
7878
8117
  _deot_vc.Message.success(t("renderer.common.saved"));
7879
8118
  } catch (reason) {
@@ -7970,8 +8209,8 @@ var home_default = /* @__PURE__ */ (0, vue.defineComponent)({
7970
8209
  loading.value = true;
7971
8210
  document.value = void 0;
7972
8211
  try {
7973
- const configured = config.home?.locales?.[lang.value] || config.home?.locales?.["en-US"];
7974
- if (!configured) return;
8212
+ const configured = resolveHomeContent(config, lang.value);
8213
+ if (!configured || configured === "default") return;
7975
8214
  if (typeof configured !== "string") {
7976
8215
  document.value = parseDocument(configured);
7977
8216
  return;
@@ -8616,7 +8855,6 @@ var createRouteRecord = (config, path, routeConfig, options = {}) => {
8616
8855
  var createDocsRouter = (config, options = {}) => {
8617
8856
  const defaultLanguage = getDefaultLanguage(config);
8618
8857
  let currentLanguage = options.initialLanguage && hasConfiguredLanguage(config, options.initialLanguage) ? options.initialLanguage : defaultLanguage;
8619
- const hasConfiguredRoot = Object.prototype.hasOwnProperty.call(config.routes, "/");
8620
8858
  const hasConfiguredEditorRoute = Object.keys(config.routes).some((path) => (path.startsWith("/") ? path : `/${path}`) === "/renderer-editor");
8621
8859
  const hasConfiguredDemosRoute = Object.keys(config.routes).some((path) => (path.startsWith("/") ? path : `/${path}`) === "/renderer-editor-demos");
8622
8860
  const isDevelopment = config.runtime?.mode === "development";
@@ -8688,26 +8926,35 @@ var createDocsRouter = (config, options = {}) => {
8688
8926
  path: "/",
8689
8927
  redirect: () => `/${currentLanguage}`
8690
8928
  });
8691
- 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, {
8692
8931
  localized: true,
8693
8932
  languageRoot: true
8694
8933
  }));
8695
- else routes.push({
8696
- path: "/:lang",
8697
- component: home_default,
8698
- meta: {
8699
- docsHome: true,
8700
- docsLocalized: true,
8701
- docsLanguageRoot: true,
8702
- docsRoute: {
8703
- content: null,
8704
- sidebar: null,
8705
- header: "default",
8706
- footer: "default",
8707
- 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
+ }
8708
8955
  }
8709
- }
8710
- });
8956
+ });
8957
+ }
8711
8958
  Object.entries(config.routes).forEach(([path, routeConfig]) => {
8712
8959
  if (path === "/" || path === "*") return;
8713
8960
  const normalized = path.startsWith("/") ? path : `/${path}`;
@@ -8750,6 +8997,23 @@ var createDocsRouter = (config, options = {}) => {
8750
8997
  var PlaygroundResource = Resource.playground;
8751
8998
  //#endregion
8752
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
+ };
8753
9017
  var bootstrap = async (config) => {
8754
9018
  config ||= window.$docs || {
8755
9019
  locales: {},
@@ -8778,6 +9042,8 @@ var bootstrap = async (config) => {
8778
9042
  (async () => {
8779
9043
  try {
8780
9044
  await router.isReady();
9045
+ if (disconnected) return;
9046
+ await consumeStoredRedirect(router, config);
8781
9047
  if (!disconnected) stopPrefetch = Resource.prefetch.start(config);
8782
9048
  } catch {}
8783
9049
  })();