@clarify-labs/cli 0.10.3 → 0.11.0

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.
@@ -410,10 +410,10 @@ function frontmatterKeywords(frontmatter) {
410
410
  const keywords = Array.isArray(value) ? value.filter((item) => typeof item === "string") : typeof value === "string" ? value.split(",").map((keyword) => keyword.trim()).filter(Boolean) : [];
411
411
  return keywords.length > 0 ? keywords : void 0;
412
412
  }
413
- function withAlternates(route, routes, i18n) {
413
+ function withAlternates(route, routes, locales) {
414
414
  const basePath = route.basePath ?? route.path;
415
415
  const routeByLocaleAndBase = new Map(routes.filter((r) => !r.locale || r.path === `/${r.locale}` || r.path.startsWith(`/${r.locale}/`)).map((r) => [`${r.locale ?? ""}:${r.basePath ?? r.path}`, r]));
416
- const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
416
+ const alternates = Object.fromEntries(locales.locales.flatMap((locale) => {
417
417
  const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
418
418
  return alternate ? [[locale.code, alternate.path]] : [];
419
419
  }));
@@ -426,11 +426,11 @@ function withAlternates(route, routes, i18n) {
426
426
  * 为默认语言的 locale-prefixed 路由生成无前缀的"裸路径别名",方便不带语言
427
427
  * 前缀的 URL 也能访问。标记 `isBareAlias` 以便搜索索引生成时过滤重复索引。
428
428
  */
429
- function generateBareAliases(routes, i18n) {
429
+ function generateBareAliases(routes, locales) {
430
430
  const bareRoutes = [];
431
431
  const seenBare = new Set(routes.map((r) => r.path));
432
432
  for (const route of routes) {
433
- if (route.locale !== i18n.defaultLocale) continue;
433
+ if (route.locale !== locales.default) continue;
434
434
  const bp = route.basePath ?? route.path;
435
435
  if (bp === route.path || seenBare.has(bp)) continue;
436
436
  seenBare.add(bp);
@@ -442,42 +442,42 @@ function generateBareAliases(routes, i18n) {
442
442
  }
443
443
  return bareRoutes;
444
444
  }
445
- async function findLocalizedContentRoutes(contentRoot, i18n, options = {}) {
446
- if (!i18n) return findContentRoutes(contentRoot, contentRoot, options);
445
+ async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
446
+ if (!locales) return findContentRoutes(contentRoot, contentRoot, options);
447
447
  const localizedRoutes = [];
448
- for (const locale of i18n.locales) {
448
+ for (const locale of locales.locales) {
449
449
  const localeRoot = (0, node_path.join)(contentRoot, locale.code);
450
450
  const discovered = await findContentRoutes(localeRoot, localeRoot, options);
451
451
  for (const route of discovered) {
452
452
  const basePath = route.basePath ?? route.path;
453
453
  localizedRoutes.push({
454
454
  ...route,
455
- path: localizedRoutePath(basePath, locale.code, i18n),
455
+ path: localizedRoutePath(basePath, locale.code, locales),
456
456
  basePath,
457
457
  locale: locale.code,
458
458
  module: { virtualModuleId: virtualModuleIdForLocalizedRoute(contentRoot, route.source.filePath) }
459
459
  });
460
460
  }
461
461
  }
462
- if (i18n.missing === "fallback") {
462
+ if (locales.missing === "fallback") {
463
463
  const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
464
- const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
464
+ const defaultRoutes = localizedRoutes.filter((route) => route.locale === locales.default);
465
465
  for (const sourceRoute of defaultRoutes) {
466
466
  const basePath = sourceRoute.basePath ?? sourceRoute.path;
467
- for (const locale of i18n.locales) {
467
+ for (const locale of locales.locales) {
468
468
  const key = `${locale.code}:${basePath}`;
469
469
  if (routeByLocaleAndBase.has(key)) continue;
470
470
  localizedRoutes.push({
471
471
  ...sourceRoute,
472
- path: localizedRoutePath(basePath, locale.code, i18n),
472
+ path: localizedRoutePath(basePath, locale.code, locales),
473
473
  locale: locale.code,
474
474
  isFallback: true
475
475
  });
476
476
  }
477
477
  }
478
478
  }
479
- const routesWithAlternates = localizedRoutes.map((route) => withAlternates(route, localizedRoutes, i18n));
480
- return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates, i18n)];
479
+ const routesWithAlternates = localizedRoutes.map((route) => withAlternates(route, localizedRoutes, locales));
480
+ return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates, locales)];
481
481
  }
482
482
  function buildNavigation(routes) {
483
483
  const root = [];
@@ -554,7 +554,7 @@ function collectExplicitPagePathIntents(tabs) {
554
554
  }
555
555
  return intents;
556
556
  }
557
- function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
557
+ function applyConfiguredPageRoutePaths(routes, tabs, locales) {
558
558
  const pageIntents = collectExplicitPagePathIntents(tabs);
559
559
  if (!pageIntents.length) return routes;
560
560
  const additions = [];
@@ -570,15 +570,15 @@ function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
570
570
  existingKeys.add(key);
571
571
  additions.push({
572
572
  ...route,
573
- path: route.locale && i18n ? localizedRoutePath(targetBasePath, route.locale, i18n) : targetBasePath,
573
+ path: route.locale && locales ? localizedRoutePath(targetBasePath, route.locale, locales) : targetBasePath,
574
574
  basePath: targetBasePath
575
575
  });
576
576
  }
577
577
  }
578
578
  const nextRoutes = [...routes, ...additions];
579
- const routesWithAlternates = i18n ? nextRoutes.map((route) => withAlternates(route, nextRoutes, i18n)) : nextRoutes;
580
- if (!i18n) return routesWithAlternates;
581
- return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates, i18n)];
579
+ const routesWithAlternates = locales ? nextRoutes.map((route) => withAlternates(route, nextRoutes, locales)) : nextRoutes;
580
+ if (!locales) return routesWithAlternates;
581
+ return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates, locales)];
582
582
  }
583
583
  function buildNavigationFromPagesConfig(routes, config) {
584
584
  if (!config || config === "FileTree") return buildNavigation(routes);
@@ -641,25 +641,25 @@ function buildNavigationFromConfig(routes, config) {
641
641
  };
642
642
  });
643
643
  }
644
- function localizeNavigationPaths(nodes, locale, i18n) {
644
+ function localizeNavigationPaths(nodes, locale, locales) {
645
645
  return nodes.map((node) => ({
646
646
  ...node,
647
- path: localizedRoutePath(node.path, locale, i18n),
648
- children: node.children ? localizeNavigationPaths(node.children, locale, i18n) : void 0
647
+ path: localizedRoutePath(node.path, locale, locales),
648
+ children: node.children ? localizeNavigationPaths(node.children, locale, locales) : void 0
649
649
  }));
650
650
  }
651
- function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
651
+ function buildLocalizedNavigationForLocale(routes, config, locale, locales) {
652
652
  if (!config || config === "FileTree") return localizeNavigationPaths(buildNavigation(routes.map((route) => ({
653
653
  ...route,
654
654
  path: route.basePath ?? route.path
655
- }))), locale, i18n);
655
+ }))), locale, locales);
656
656
  const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
657
657
  const buildItem = (item) => {
658
658
  if (typeof item !== "string" && "group" in item) {
659
659
  const children = item.pages.map(buildItem);
660
660
  return {
661
661
  path: firstNavigationPath(children),
662
- title: resolveLocalizedText(item.group, locale, i18n.defaultLocale) ?? "",
662
+ title: resolveLocalizedText(item.group, locale, locales.default) ?? "",
663
663
  icon: item.icon,
664
664
  children
665
665
  };
@@ -667,10 +667,10 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
667
667
  const intent = routeIntentFromPagesItem(item);
668
668
  const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
669
669
  const route = routeMap.get(basePath);
670
- const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
670
+ const path = route?.path ?? localizedRoutePath(basePath, locale, locales);
671
671
  return {
672
- path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, i18n) : void 0) ?? path,
673
- title: resolveLocalizedText(intent.title, locale, i18n.defaultLocale) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
672
+ path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, locales) : void 0) ?? path,
673
+ title: resolveLocalizedText(intent.title, locale, locales.default) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
674
674
  icon: intent.icon,
675
675
  sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
676
676
  };
@@ -678,24 +678,24 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
678
678
  return config.map((group) => {
679
679
  const children = group.pages.map(buildItem);
680
680
  return {
681
- path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
682
- title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
681
+ path: children[0]?.path ?? localizedRoutePath("/", locale, locales),
682
+ title: resolveLocalizedText(group.group, locale, locales.default) ?? "",
683
683
  icon: group.icon,
684
684
  children
685
685
  };
686
686
  });
687
687
  }
688
- function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
689
- if (!i18n) return void 0;
688
+ function buildLocalizedNavigationFromTabsConfig(routes, tabs, localesConfig) {
689
+ if (!localesConfig) return void 0;
690
690
  const locales = {};
691
- for (const locale of i18n.locales) {
691
+ for (const locale of localesConfig.locales) {
692
692
  const localeRoutes = routes.filter((route) => route.locale === locale.code);
693
693
  locales[locale.code] = { tabs: tabs.map((tab) => {
694
- const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
694
+ const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, localesConfig);
695
695
  return {
696
696
  type: "tab",
697
697
  path: firstNavigationPath(children),
698
- title: resolveLocalizedText(tab.tab, locale.code, i18n.defaultLocale) ?? "",
698
+ title: resolveLocalizedText(tab.tab, locale.code, localesConfig.default) ?? "",
699
699
  icon: tab.icon,
700
700
  children
701
701
  };
@@ -762,7 +762,7 @@ function inferRouteForFile(filePath, contentRoot, defaultLocale) {
762
762
  const localeSegmentIndex = fileLocale ? segments.indexOf(fileLocale) : -1;
763
763
  const basePath = basePathFromRef(localeSegmentIndex >= 0 ? segments.slice(localeSegmentIndex + 1).join("/") : ref);
764
764
  const path = fileLocale && defaultLocale ? localizedRoutePath(basePath, fileLocale, {
765
- defaultLocale,
765
+ default: defaultLocale,
766
766
  missing: "fallback",
767
767
  locales: []
768
768
  }) : basePath;
@@ -815,12 +815,12 @@ async function handleDevRouteRequest(req, res, routes, context) {
815
815
  }
816
816
  const file = typeof body === "object" && body !== null ? body.file : void 0;
817
817
  if (typeof file === "string" && file.length > 0) {
818
- const route = resolveRouteForFile(file, routes, context.projectConfig.i18n?.defaultLocale);
818
+ const route = resolveRouteForFile(file, routes, context.projectConfig.locales?.default);
819
819
  if (route) {
820
820
  res.end(JSON.stringify(toDevRouteEntry(route)));
821
821
  return;
822
822
  }
823
- const inferred = inferRouteForFile(file, context.contentRoot, context.projectConfig.i18n?.defaultLocale);
823
+ const inferred = inferRouteForFile(file, context.contentRoot, context.projectConfig.locales?.default);
824
824
  res.end(JSON.stringify(inferred));
825
825
  return;
826
826
  }
@@ -846,8 +846,7 @@ function resolveThemeConfig(theme = {}) {
846
846
  layout: {
847
847
  ...presetTheme.layout,
848
848
  ...theme.layout
849
- },
850
- editor: theme.editor ?? presetTheme.editor
849
+ }
851
850
  };
852
851
  }
853
852
  //#endregion
@@ -855,26 +854,26 @@ function resolveThemeConfig(theme = {}) {
855
854
  var clarifyLogoConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
856
855
  light: zod.z.string().optional(),
857
856
  dark: zod.z.string().optional()
858
- })]);
857
+ }).strict()]);
859
858
  var clarifyFaviconConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
860
859
  light: zod.z.string().optional(),
861
860
  dark: zod.z.string().optional()
862
- })]);
861
+ }).strict()]);
863
862
  var clarifyLocalizedTextSchema = zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.string())]);
864
863
  var clarifyLocaleConfigSchema = zod.z.object({
865
864
  code: zod.z.string(),
866
865
  label: zod.z.string(),
867
866
  dir: zod.z.union([zod.z.literal("ltr"), zod.z.literal("rtl")]).optional()
868
- });
869
- var clarifyI18nConfigSchema = zod.z.object({
870
- defaultLocale: zod.z.string().optional(),
867
+ }).strict();
868
+ var clarifyLocalesConfigSchema = zod.z.object({
869
+ default: zod.z.string().optional(),
871
870
  missing: zod.z.union([
872
871
  zod.z.literal("fallback"),
873
872
  zod.z.literal("404"),
874
873
  zod.z.literal("hide")
875
874
  ]).optional(),
876
875
  locales: zod.z.array(clarifyLocaleConfigSchema).min(1)
877
- }).superRefine((config, ctx) => {
876
+ }).strict().superRefine((config, ctx) => {
878
877
  const localeCodes = /* @__PURE__ */ new Set();
879
878
  for (const [index, locale] of config.locales.entries()) {
880
879
  if (localeCodes.has(locale.code)) ctx.addIssue({
@@ -888,17 +887,17 @@ var clarifyI18nConfigSchema = zod.z.object({
888
887
  });
889
888
  localeCodes.add(locale.code);
890
889
  }
891
- if (config.defaultLocale && !localeCodes.has(config.defaultLocale)) ctx.addIssue({
890
+ if (config.default && !localeCodes.has(config.default)) ctx.addIssue({
892
891
  code: zod.z.ZodIssueCode.custom,
893
- message: "defaultLocale must be one of i18n.locales",
894
- path: ["defaultLocale"]
892
+ message: "default must be one of locales.locales",
893
+ path: ["default"]
895
894
  });
896
895
  });
897
896
  var clarifyNavbarLinkSchema = zod.z.object({
898
897
  label: clarifyLocalizedTextSchema,
899
898
  href: zod.z.string(),
900
899
  external: zod.z.boolean().optional()
901
- });
900
+ }).strict();
902
901
  var clarifyBannerConfigOptionsSchema = zod.z.object({
903
902
  content: clarifyLocalizedTextSchema,
904
903
  dismissible: zod.z.boolean().optional(),
@@ -916,11 +915,11 @@ var clarifyVariableValueSchema = zod.z.lazy(() => zod.z.union([
916
915
  zod.z.record(zod.z.string(), clarifyVariableValueSchema)
917
916
  ]));
918
917
  var clarifyVariablesConfigSchema = zod.z.record(zod.z.string(), clarifyVariableValueSchema);
919
- var clarifySourceConfigSchema = zod.z.object({
920
- repository: zod.z.string(),
918
+ var clarifyRepositoryConfigSchema = zod.z.object({
919
+ url: zod.z.string().optional(),
921
920
  branch: zod.z.string().optional(),
922
921
  directory: zod.z.string().optional()
923
- });
922
+ }).strict();
924
923
  var clarifyPagesItemSchema = zod.z.lazy(() => zod.z.union([
925
924
  zod.z.string(),
926
925
  clarifyPagesGroupSchema,
@@ -930,32 +929,32 @@ var clarifyPagesItemSchema = zod.z.lazy(() => zod.z.union([
930
929
  title: clarifyLocalizedTextSchema.optional(),
931
930
  icon: zod.z.string().optional(),
932
931
  redirect: zod.z.string().optional()
933
- }),
932
+ }).strict(),
934
933
  zod.z.object({
935
934
  openapi: zod.z.string(),
936
935
  path: zod.z.string().optional(),
937
936
  icon: zod.z.string().optional(),
938
937
  title: clarifyLocalizedTextSchema.optional(),
939
- filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).optional()
940
- })
938
+ filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).strict().optional()
939
+ }).strict()
941
940
  ]));
942
941
  var clarifyPagesGroupSchema = zod.z.lazy(() => zod.z.object({
943
942
  group: clarifyLocalizedTextSchema,
944
943
  icon: zod.z.string().optional(),
945
944
  pages: zod.z.array(clarifyPagesItemSchema)
946
- }));
945
+ }).strict());
947
946
  var clarifyPagesConfigSchema = zod.z.union([zod.z.array(clarifyPagesGroupSchema), zod.z.literal("FileTree")]);
948
947
  var clarifyTabItemSchema = zod.z.object({
949
948
  tab: clarifyLocalizedTextSchema,
950
949
  icon: zod.z.string().optional(),
951
950
  pages: clarifyPagesConfigSchema.optional()
952
- });
951
+ }).strict();
953
952
  var clarifyTabsConfigSchema = zod.z.array(clarifyTabItemSchema);
954
953
  var clarifyThemePresetSchema = zod.z.enum(["default", "base"]);
955
954
  var clarifyThemeModeColorValueSchema = zod.z.object({
956
955
  light: zod.z.string().optional(),
957
956
  dark: zod.z.string().optional()
958
- });
957
+ }).strict();
959
958
  var clarifyThemeColorValueSchema = zod.z.union([zod.z.string(), clarifyThemeModeColorValueSchema]);
960
959
  var clarifyThemeColorTokensConfigSchema = zod.z.object({
961
960
  primary: clarifyThemeColorValueSchema.optional(),
@@ -966,42 +965,65 @@ var clarifyThemeColorTokensConfigSchema = zod.z.object({
966
965
  muted: clarifyThemeColorValueSchema.optional(),
967
966
  border: clarifyThemeColorValueSchema.optional(),
968
967
  codeBackground: clarifyThemeColorValueSchema.optional()
969
- });
968
+ }).strict();
970
969
  var clarifyThemeRadiusTokensConfigSchema = zod.z.object({
971
970
  sm: zod.z.string().optional(),
972
971
  md: zod.z.string().optional(),
973
972
  lg: zod.z.string().optional(),
974
973
  xl: zod.z.string().optional()
975
- });
974
+ }).strict();
976
975
  var clarifyThemeTokensConfigSchema = zod.z.object({
977
976
  colors: clarifyThemeColorTokensConfigSchema.optional(),
978
977
  radius: clarifyThemeRadiusTokensConfigSchema.optional()
979
- });
980
- var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().optional() });
978
+ }).strict();
979
+ var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().optional() }).strict();
981
980
  var clarifyThemeConfigSchema = zod.z.object({
982
981
  preset: clarifyThemePresetSchema.optional(),
983
982
  tokens: clarifyThemeTokensConfigSchema.optional(),
984
- layout: clarifyThemeLayoutConfigSchema.optional(),
985
- editor: zod.z.boolean().optional()
986
- });
983
+ layout: clarifyThemeLayoutConfigSchema.optional()
984
+ }).strict();
985
+ function featureSchema(shape, defaults) {
986
+ const objectSchema = zod.z.object({
987
+ enabled: zod.z.boolean().default(true),
988
+ ...shape
989
+ }).strict().default({
990
+ enabled: true,
991
+ ...defaults
992
+ });
993
+ return zod.z.union([zod.z.boolean(), objectSchema]).default({
994
+ enabled: true,
995
+ ...defaults
996
+ }).transform((value) => typeof value === "boolean" ? {
997
+ enabled: value,
998
+ ...defaults
999
+ } : value);
1000
+ }
1001
+ var clarifyFeaturesConfigSchema = zod.z.object({
1002
+ search: featureSchema({ provider: zod.z.literal("pagefind").default("pagefind") }, { provider: "pagefind" }),
1003
+ repository: featureSchema(clarifyRepositoryConfigSchema.shape, {}),
1004
+ themeEditor: featureSchema({}, {}),
1005
+ openapi: featureSchema({ playground: zod.z.boolean().default(true) }, { playground: true })
1006
+ }).strict();
987
1007
  var clarifyProjectConfigSchema = zod.z.object({
988
1008
  title: zod.z.string().optional(),
989
1009
  description: zod.z.string().optional(),
990
1010
  siteUrl: zod.z.string().optional(),
991
- source: clarifySourceConfigSchema.optional(),
1011
+ routePrefix: zod.z.string().default("/"),
1012
+ assetPrefix: zod.z.string().optional(),
992
1013
  logo: clarifyLogoConfigSchema.optional(),
993
1014
  homeUrl: zod.z.string().optional(),
994
1015
  favicon: clarifyFaviconConfigSchema.optional(),
995
1016
  theme: clarifyThemeConfigSchema.optional(),
996
- routePrefix: zod.z.string().optional(),
997
- assetPrefix: zod.z.string().optional(),
998
- navbar: zod.z.object({ links: zod.z.array(clarifyNavbarLinkSchema).optional() }).optional(),
1017
+ navigation: zod.z.object({
1018
+ links: zod.z.array(clarifyNavbarLinkSchema).optional(),
1019
+ tabs: clarifyTabsConfigSchema.optional()
1020
+ }).strict().optional(),
999
1021
  banner: clarifyBannerConfigOptionsSchema.optional(),
1000
1022
  footer: clarifyFooterConfigSchema.optional(),
1023
+ locales: clarifyLocalesConfigSchema.optional(),
1001
1024
  variables: clarifyVariablesConfigSchema.optional(),
1002
- i18n: clarifyI18nConfigSchema.optional(),
1003
- tabs: clarifyTabsConfigSchema.optional()
1004
- });
1025
+ features: clarifyFeaturesConfigSchema.optional()
1026
+ }).strict();
1005
1027
  //#endregion
1006
1028
  //#region source/core/config/config.ts
1007
1029
  function formatIssuePath(path) {
@@ -1023,17 +1045,20 @@ function validateProjectConfig(value) {
1023
1045
  if (!result.success) throw new Error(formatProjectConfigError(result.error));
1024
1046
  return result.data;
1025
1047
  }
1026
- function resolveI18nConfig(i18n) {
1027
- if (!i18n) return void 0;
1028
- const firstLocale = i18n.locales[0]?.code;
1029
- const defaultLocale = i18n.defaultLocale ?? firstLocale;
1048
+ function resolveLocalesConfig(locales) {
1049
+ if (!locales) return void 0;
1050
+ const firstLocale = locales.locales[0]?.code;
1051
+ const defaultLocale = locales.default ?? firstLocale;
1030
1052
  if (!defaultLocale) return void 0;
1031
1053
  return {
1032
- defaultLocale,
1033
- missing: i18n.missing ?? "fallback",
1034
- locales: i18n.locales
1054
+ default: defaultLocale,
1055
+ missing: locales.missing ?? "fallback",
1056
+ locales: locales.locales
1035
1057
  };
1036
1058
  }
1059
+ function resolveFeaturesConfig(features = {}) {
1060
+ return clarifyFeaturesConfigSchema.parse(features);
1061
+ }
1037
1062
  function resolveRoutePrefix(routePrefix) {
1038
1063
  const trimmed = routePrefix?.trim();
1039
1064
  if (!trimmed || trimmed === "/") return "/";
@@ -1054,26 +1079,24 @@ function resolveProjectConfig(config = {}) {
1054
1079
  title: config.title ?? "Clarify Docs",
1055
1080
  description: config.description ?? "",
1056
1081
  siteUrl: config.siteUrl,
1057
- source: config.source,
1082
+ routePrefix,
1083
+ assetPrefix: resolveAssetPrefix(config.assetPrefix, routePrefix),
1058
1084
  logo: config.logo,
1059
1085
  homeUrl: config.homeUrl,
1060
1086
  favicon: config.favicon,
1061
- routePrefix,
1062
- assetPrefix: resolveAssetPrefix(config.assetPrefix, routePrefix),
1063
1087
  theme: resolveThemeConfig(config.theme),
1064
- navbar: config.navbar,
1088
+ navigation: config.navigation,
1065
1089
  banner: config.banner,
1066
1090
  footer: config.footer,
1091
+ locales: resolveLocalesConfig(config.locales),
1067
1092
  variables: config.variables ?? {},
1068
- i18n: resolveI18nConfig(config.i18n),
1069
- tabs: config.tabs
1093
+ features: resolveFeaturesConfig(config.features)
1070
1094
  };
1071
1095
  }
1072
1096
  //#endregion
1073
1097
  //#region source/core/config/user-config.ts
1074
1098
  function defineConfig(config) {
1075
- validateProjectConfig(config);
1076
- return config;
1099
+ return validateClarifyConfig(config);
1077
1100
  }
1078
1101
  /**
1079
1102
  * Config filenames Clarify CLI looks for at the project root, in resolution
@@ -1097,18 +1120,23 @@ function assertConfigObject(config, configFile) {
1097
1120
  }
1098
1121
  function loadJsonConfig(configFile) {
1099
1122
  const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
1100
- const projectConfig = validateProjectConfig(config);
1101
- const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
1102
- return {
1123
+ if ("plugins" in config) throw new Error("[clarify] clarify.json does not support plugins; use clarify.ts or clarify.js");
1124
+ return validateClarifyConfig(config);
1125
+ }
1126
+ function validateClarifyConfig(config) {
1127
+ const { plugins, ...projectConfigInput } = config;
1128
+ if (plugins !== void 0 && !Array.isArray(plugins)) throw new Error("[clarify] config field \"plugins\" is invalid: Expected an array");
1129
+ const projectConfig = validateProjectConfig(projectConfigInput);
1130
+ return plugins ? {
1103
1131
  ...projectConfig,
1104
- ...ssg ? { ssg } : {}
1105
- };
1132
+ plugins
1133
+ } : projectConfig;
1106
1134
  }
1107
1135
  async function loadClarifyConfig(root, env) {
1108
1136
  const configFile = findClarifyConfigFile(root);
1109
1137
  if (!configFile) return {};
1110
1138
  if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
1111
- return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
1139
+ return validateClarifyConfig(assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile));
1112
1140
  }
1113
1141
  /**
1114
1142
  * File extensions Clarify treats as content files. Kept in sync with the
@@ -1134,9 +1162,9 @@ function buildProjectInfo(context) {
1134
1162
  contentRoot: (contentRoot.startsWith(projectRoot) ? contentRoot.slice(projectRoot.length).replace(/^[/\\]+/, "") : contentRoot) || "source",
1135
1163
  projectRoot
1136
1164
  };
1137
- if (projectConfig.i18n) response.i18n = {
1138
- defaultLocale: projectConfig.i18n.defaultLocale,
1139
- locales: projectConfig.i18n.locales.map((l) => l.code)
1165
+ if (projectConfig.locales) response.locales = {
1166
+ default: projectConfig.locales.default,
1167
+ locales: projectConfig.locales.locales.map((l) => l.code)
1140
1168
  };
1141
1169
  return response;
1142
1170
  }
@@ -1196,10 +1224,23 @@ function resolveVirtualModuleId(id, modules, routes) {
1196
1224
  if (route) return resolveVirtualId(route.module.virtualModuleId);
1197
1225
  return null;
1198
1226
  }
1199
- function generateConfigModule(projectConfig, buildOptions, version) {
1227
+ function generateConfigModule(projectConfig, version) {
1200
1228
  const runtimeConfig = {
1201
- ...projectConfig,
1202
- ...buildOptions
1229
+ title: projectConfig.title,
1230
+ description: projectConfig.description,
1231
+ siteUrl: projectConfig.siteUrl,
1232
+ routePrefix: projectConfig.routePrefix,
1233
+ assetPrefix: projectConfig.assetPrefix,
1234
+ logo: projectConfig.logo,
1235
+ homeUrl: projectConfig.homeUrl,
1236
+ favicon: projectConfig.favicon,
1237
+ theme: projectConfig.theme,
1238
+ navigation: projectConfig.navigation,
1239
+ banner: projectConfig.banner,
1240
+ footer: projectConfig.footer,
1241
+ locales: projectConfig.locales,
1242
+ variables: projectConfig.variables,
1243
+ features: projectConfig.features
1203
1244
  };
1204
1245
  if (version) runtimeConfig.version = version;
1205
1246
  return `export const config = ${JSON.stringify(runtimeConfig)};`;
@@ -1303,7 +1344,7 @@ function buildVirtualModules(args) {
1303
1344
  const modules = /* @__PURE__ */ new Map();
1304
1345
  const clientEntryModule = createClientEntryModule({ themeEditor: args.themeEditor });
1305
1346
  const allPlugins = [...args.plugins ?? []];
1306
- modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.generateOptions, args.version));
1347
+ modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.version));
1307
1348
  modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, "client"));
1308
1349
  modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, "server"));
1309
1350
  modules.set(VIRTUAL_SLOTS, createRuntimeSlotsModule(allPlugins, args.generateOptions.projectRoot));
@@ -1528,7 +1569,7 @@ function createViteAdapter(engine) {
1528
1569
  const compilePolicyPlugin = {
1529
1570
  name: "clarify:content-compile-policy",
1530
1571
  configResolved(config) {
1531
- contentCompileErrorPolicy = config.command === "build" && engine.generateOptions.ssg.failOnError ? "throw" : "diagnostic";
1572
+ contentCompileErrorPolicy = config.command === "build" ? "throw" : "diagnostic";
1532
1573
  }
1533
1574
  };
1534
1575
  const tailwind = (0, _tailwindcss_vite.default)();
@@ -1544,7 +1585,7 @@ function createViteAdapter(engine) {
1544
1585
  }
1545
1586
  var cliPackageVersion = {
1546
1587
  name: "@clarify-labs/cli",
1547
- version: "0.10.3",
1588
+ version: "0.11.0",
1548
1589
  license: "AGPL-3.0-only",
1549
1590
  type: "module",
1550
1591
  repository: {
@@ -1624,8 +1665,7 @@ function resolveBuildOptions(options = {}) {
1624
1665
  return {
1625
1666
  projectRoot: options.projectRoot ?? process.cwd(),
1626
1667
  rootDirectory: options.rootDirectory ?? "source",
1627
- outputDirectory: options.outputDirectory,
1628
- ssg: { failOnError: options.ssg?.failOnError ?? true }
1668
+ outputDirectory: options.outputDirectory
1629
1669
  };
1630
1670
  }
1631
1671
  //#endregion
@@ -1683,7 +1723,7 @@ function llmsTxtListItem(route, basePath) {
1683
1723
  return description ? `- [${route.meta.title}](${basePath}${contentArtifactUrl}): ${description}` : `- [${route.meta.title}](${basePath}${contentArtifactUrl})`;
1684
1724
  }
1685
1725
  function llmsTxtLocaleLabel(locale, projectConfig) {
1686
- return projectConfig.i18n?.locales.find((item) => item.code === locale)?.label ?? locale;
1726
+ return projectConfig.locales?.locales.find((item) => item.code === locale)?.label ?? locale;
1687
1727
  }
1688
1728
  function groupRoutesByLocale(routes) {
1689
1729
  const groups = /* @__PURE__ */ new Map();
@@ -1695,7 +1735,7 @@ function groupRoutesByLocale(routes) {
1695
1735
  }
1696
1736
  function groupLlmsTxtRoutesByLocale(routes, projectConfig) {
1697
1737
  const routesBySource = /* @__PURE__ */ new Map();
1698
- const defaultLocale = projectConfig.i18n?.defaultLocale;
1738
+ const defaultLocale = projectConfig.locales?.default;
1699
1739
  for (const route of routes) {
1700
1740
  const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.artifacts?.contentArtifactUrl ?? route.path}`;
1701
1741
  const previousRoute = routesBySource.get(sourceKey);
@@ -2090,7 +2130,7 @@ function collectOpenAPIPageIntents(config) {
2090
2130
  if (!pages || pages === "FileTree") return;
2091
2131
  for (const group of pages) visitItems(group.pages);
2092
2132
  }
2093
- for (const tab of config.tabs ?? []) visitPages(tab.pages);
2133
+ for (const tab of config.navigation?.tabs ?? []) visitPages(tab.pages);
2094
2134
  return intents;
2095
2135
  }
2096
2136
  function virtualModuleIdFromPath(path) {
@@ -2113,14 +2153,14 @@ function cloneOpenAPIRoute(route, overrides = {}) {
2113
2153
  ...overrides
2114
2154
  };
2115
2155
  }
2116
- function findOpenAPIRoutesForContentRoot(contentRoot, i18n) {
2117
- if (!i18n) return findOpenAPIRoutes(contentRoot);
2156
+ function findOpenAPIRoutesForContentRoot(contentRoot, locales) {
2157
+ if (!locales) return findOpenAPIRoutes(contentRoot);
2118
2158
  const localizedRoutes = [];
2119
- for (const locale of i18n.locales) {
2159
+ for (const locale of locales.locales) {
2120
2160
  const localeRoot = (0, node_path.join)(contentRoot, locale.code);
2121
2161
  for (const route of findOpenAPIRoutes(localeRoot, localeRoot)) {
2122
2162
  const basePath = route.basePath ?? route.path;
2123
- const path = localizedRoutePath(basePath, locale.code, i18n);
2163
+ const path = localizedRoutePath(basePath, locale.code, locales);
2124
2164
  localizedRoutes.push({
2125
2165
  ...route,
2126
2166
  path,
@@ -2130,15 +2170,15 @@ function findOpenAPIRoutesForContentRoot(contentRoot, i18n) {
2130
2170
  });
2131
2171
  }
2132
2172
  }
2133
- if (i18n.missing === "fallback") {
2173
+ if (locales.missing === "fallback") {
2134
2174
  const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
2135
- const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
2175
+ const defaultRoutes = localizedRoutes.filter((route) => route.locale === locales.default);
2136
2176
  for (const sourceRoute of defaultRoutes) {
2137
2177
  const basePath = sourceRoute.basePath ?? sourceRoute.path;
2138
- for (const locale of i18n.locales) {
2178
+ for (const locale of locales.locales) {
2139
2179
  const key = `${locale.code}:${basePath}`;
2140
2180
  if (routeByLocaleAndBase.has(key)) continue;
2141
- const path = localizedRoutePath(basePath, locale.code, i18n);
2181
+ const path = localizedRoutePath(basePath, locale.code, locales);
2142
2182
  localizedRoutes.push(cloneOpenAPIRoute(sourceRoute, {
2143
2183
  path,
2144
2184
  locale: locale.code,
@@ -2165,7 +2205,7 @@ function createConfiguredOpenAPIRoutes(routes, config) {
2165
2205
  const key = `${route.locale ?? ""}:${targetBasePath}`;
2166
2206
  if (existingKeys.has(key)) continue;
2167
2207
  existingKeys.add(key);
2168
- const path = route.locale && config.i18n ? localizedRoutePath(targetBasePath, route.locale, config.i18n) : targetBasePath;
2208
+ const path = route.locale && config.locales ? localizedRoutePath(targetBasePath, route.locale, config.locales) : targetBasePath;
2169
2209
  additions.push(cloneOpenAPIRoute(route, {
2170
2210
  path,
2171
2211
  basePath: targetBasePath,
@@ -2178,12 +2218,12 @@ function createConfiguredOpenAPIRoutes(routes, config) {
2178
2218
  }
2179
2219
  }
2180
2220
  const nextRoutes = [...routes, ...additions];
2181
- if (!config.i18n) return nextRoutes;
2182
- const routesWithAlternates = nextRoutes.map((route) => withAlternates(route, nextRoutes, config.i18n));
2221
+ if (!config.locales) return nextRoutes;
2222
+ const routesWithAlternates = nextRoutes.map((route) => withAlternates(route, nextRoutes, config.locales));
2183
2223
  const bareAliases = [];
2184
2224
  const seenBare = new Set(routesWithAlternates.map((route) => route.path));
2185
2225
  for (const route of routesWithAlternates) {
2186
- if (route.locale !== config.i18n.defaultLocale) continue;
2226
+ if (route.locale !== config.locales.default) continue;
2187
2227
  const basePath = route.basePath ?? route.path;
2188
2228
  if (basePath === route.path || seenBare.has(basePath)) continue;
2189
2229
  seenBare.add(basePath);
@@ -2222,7 +2262,7 @@ function createOpenAPIPlugin() {
2222
2262
  hooks: {
2223
2263
  "routes:discover": (input, ctx) => ({
2224
2264
  ...input,
2225
- routes: [...input.routes, ...findOpenAPIRoutesForContentRoot(input.contentRoot, ctx.projectConfig.i18n)]
2265
+ routes: [...input.routes, ...findOpenAPIRoutesForContentRoot(input.contentRoot, ctx.projectConfig.locales)]
2226
2266
  }),
2227
2267
  "routes:discovered": async (routes, ctx) => {
2228
2268
  specs.clear();
@@ -2379,7 +2419,7 @@ async function generateDevSearchIndex(ctx, root, pagefind) {
2379
2419
  const result = await index.addCustomRecord({
2380
2420
  url: route.path,
2381
2421
  content,
2382
- language: toPagefindLanguage(route.locale ?? ctx.projectConfig.i18n?.defaultLocale),
2422
+ language: toPagefindLanguage(route.locale ?? ctx.projectConfig.locales?.default),
2383
2423
  meta: { title: route.meta.title }
2384
2424
  });
2385
2425
  assertNoPagefindErrors(`addCustomRecord ${route.path}`, result.errors);
@@ -2422,6 +2462,7 @@ function createSearchIndexPlugin() {
2422
2462
  name: "clarify:search-index",
2423
2463
  hooks: {
2424
2464
  async "dev:configureServer"(server, ctx) {
2465
+ if (!ctx.projectConfig.features.search.enabled) return;
2425
2466
  const pagefind = await import("pagefind");
2426
2467
  let currentRoot;
2427
2468
  const staleRoots = /* @__PURE__ */ new Set();
@@ -2461,6 +2502,7 @@ function createSearchIndexPlugin() {
2461
2502
  });
2462
2503
  },
2463
2504
  async "build:done"(ctx) {
2505
+ if (!ctx.projectConfig.features.search.enabled) return;
2464
2506
  const outputDirectory = ctx.generateOptions.outputDirectory;
2465
2507
  if (!outputDirectory) return;
2466
2508
  const pageCount = await generatePagefindIndex({ outputRoot: (0, node_path.resolve)(ctx.generateOptions.projectRoot, outputDirectory) });
@@ -2513,16 +2555,18 @@ function createSeoPlugin() {
2513
2555
  return {
2514
2556
  name: "clarify:seo",
2515
2557
  hooks: { "build:assets"(ctx) {
2558
+ const assets = [];
2516
2559
  const sitemap = createSitemapXml(ctx.routes, ctx.projectConfig);
2517
2560
  const robots = createRobotsTxt(ctx.projectConfig);
2518
- if (!sitemap || !robots) return [];
2519
- return [{
2561
+ if (sitemap) assets.push({
2520
2562
  fileName: "sitemap.xml",
2521
2563
  source: sitemap
2522
- }, {
2564
+ });
2565
+ if (robots) assets.push({
2523
2566
  fileName: "robots.txt",
2524
2567
  source: robots
2525
- }];
2568
+ });
2569
+ return assets;
2526
2570
  } }
2527
2571
  };
2528
2572
  }
@@ -2538,7 +2582,8 @@ function encodePath(path) {
2538
2582
  return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
2539
2583
  }
2540
2584
  function createSourceEditUrl(filePath, contentRoot, source) {
2541
- const repository = normalizeRepositoryUrl(source.repository);
2585
+ if (!source.url) return "";
2586
+ const repository = normalizeRepositoryUrl(source.url);
2542
2587
  const branch = source.branch ?? "main";
2543
2588
  const sourcePath = [source.directory ? trimSlashes(source.directory) : "", (0, node_path.relative)(contentRoot, filePath).replaceAll("\\", "/")].filter(Boolean).join("/");
2544
2589
  return `${repository}/edit/${encodeURIComponent(branch)}/${encodePath(sourcePath)}`;
@@ -2556,7 +2601,8 @@ function createSourceLinksPlugin() {
2556
2601
  return {
2557
2602
  name: "clarify:source-links",
2558
2603
  hooks: { "routes:discovered": (routes, ctx) => {
2559
- attachSourceEditUrls(routes, (0, node_path.join)(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.source);
2604
+ if (!ctx.projectConfig.features.repository.enabled) return routes;
2605
+ attachSourceEditUrls(routes, (0, node_path.join)(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.features.repository);
2560
2606
  return routes;
2561
2607
  } }
2562
2608
  };
@@ -2671,18 +2717,18 @@ async function resolveProjectContext(options = {}, env = {
2671
2717
  }) {
2672
2718
  const projectRoot = (0, node_path.resolve)(options.projectRoot ?? process.cwd());
2673
2719
  const initialBuildOptions = resolveBuildOptions(options);
2674
- const contentRoot = (0, node_path.join)(projectRoot, initialBuildOptions.rootDirectory);
2675
2720
  const configFilePath = findClarifyConfigFile(projectRoot);
2676
2721
  const config = configFilePath && (0, node_fs.existsSync)(configFilePath) ? await loadClarifyConfig(projectRoot, env) : {};
2677
2722
  const mergedOptions = {
2678
2723
  ...config,
2679
2724
  ...options,
2680
2725
  projectRoot,
2681
- rootDirectory: initialBuildOptions.rootDirectory,
2682
- outputDirectory: initialBuildOptions.outputDirectory
2726
+ rootDirectory: options.rootDirectory ?? initialBuildOptions.rootDirectory,
2727
+ outputDirectory: options.outputDirectory ?? initialBuildOptions.outputDirectory
2683
2728
  };
2684
2729
  const resolvedProjectConfig = resolveProjectConfig(mergedOptions);
2685
2730
  const resolvedBuildOptions = resolveBuildOptions(mergedOptions);
2731
+ const contentRoot = (0, node_path.join)(projectRoot, resolvedBuildOptions.rootDirectory);
2686
2732
  return {
2687
2733
  projectRoot,
2688
2734
  contentRoot,
@@ -2772,9 +2818,9 @@ function canonicalUrl(contextOrConfig, route) {
2772
2818
  /** Inject `lang` (and optional `dir`) onto the `<html>` tag for the route locale. */
2773
2819
  function injectHtmlLocaleAttributes(html, contextOrConfig, route) {
2774
2820
  const projectConfig = resolveProjectConfigFromContext(contextOrConfig);
2775
- const localeCode = route?.locale ?? projectConfig.i18n?.defaultLocale;
2821
+ const localeCode = route?.locale ?? projectConfig.locales?.default;
2776
2822
  if (!localeCode) return html;
2777
- const dir = (projectConfig.i18n?.locales.find((locale) => locale.code === localeCode))?.dir;
2823
+ const dir = (projectConfig.locales?.locales.find((locale) => locale.code === localeCode))?.dir;
2778
2824
  return html.replace(/<html\b([^>]*)>/i, (_match, attributes) => {
2779
2825
  let nextAttributes = attributes.replace(/\s+lang=("[^"]*"|'[^']*'|[^\s>]*)/i, "").replace(/\s+dir=("[^"]*"|'[^']*'|[^\s>]*)/i, "");
2780
2826
  nextAttributes = `${nextAttributes} lang="${escapeHtml(localeCode)}"`;
@@ -2952,7 +2998,7 @@ async function buildSSRBundle(root, ssrEntry, ssrOutDir, plugins) {
2952
2998
  }
2953
2999
  });
2954
3000
  }
2955
- async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBundlePath, failOnError = true) {
3001
+ async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBundlePath) {
2956
3002
  const { render } = await import((0, node_url.pathToFileURL)(ssrBundlePath).href);
2957
3003
  const template = readIndexHtml(outputDirectory);
2958
3004
  if (!template) throw new Error(`[clarify] index.html not found in outputDirectory "${outputDirectory}". Make sure Vite build produces it.`);
@@ -2964,7 +3010,7 @@ async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBund
2964
3010
  }
2965
3011
  } catch (err) {
2966
3012
  console.error(`[clarify] Failed to render route "${route.path}":`, err);
2967
- if (failOnError) throw err;
3013
+ throw err;
2968
3014
  }
2969
3015
  }
2970
3016
  //#endregion
@@ -3041,10 +3087,10 @@ var ClarifyContext = class {
3041
3087
  this.currentPlugins = plugins;
3042
3088
  }
3043
3089
  get isI18n() {
3044
- return Boolean(this.projectConfig.i18n);
3090
+ return Boolean(this.projectConfig.locales);
3045
3091
  }
3046
3092
  get defaultLocale() {
3047
- return this.projectConfig.i18n?.defaultLocale;
3093
+ return this.projectConfig.locales?.default;
3048
3094
  }
3049
3095
  get(key) {
3050
3096
  return this.values.get(key);
@@ -3238,10 +3284,10 @@ var ClarifyEngine = class {
3238
3284
  async discoverSite() {
3239
3285
  const plugins = this.ctx.plugins;
3240
3286
  const contentRoot = this.ctx.contentRoot;
3241
- const { i18n, tabs } = this.ctx.projectConfig;
3287
+ const { locales, navigation: navigationConfig } = this.ctx.projectConfig;
3242
3288
  let routes = await runPhase(plugins, "site:discover", this.ctx, async () => {
3243
3289
  const options = { contentProcessor: createProjectContentProcessor(plugins, this.ctx) };
3244
- const discovered = i18n ? await findLocalizedContentRoutes(contentRoot, i18n, options) : await findContentRoutes(contentRoot, contentRoot, options);
3290
+ const discovered = locales ? await findLocalizedContentRoutes(contentRoot, locales, options) : await findContentRoutes(contentRoot, contentRoot, options);
3245
3291
  const result = await runHooks(plugins, "routes:discover", {
3246
3292
  contentRoot,
3247
3293
  routes: discovered
@@ -3270,8 +3316,9 @@ var ClarifyEngine = class {
3270
3316
  };
3271
3317
  });
3272
3318
  });
3273
- routes = applyConfiguredPageRoutePaths(routes, tabs, i18n);
3274
- const navigation = tabs ? i18n ? buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) ?? {
3319
+ const tabs = navigationConfig?.tabs;
3320
+ routes = applyConfiguredPageRoutePaths(routes, tabs, locales);
3321
+ const navigation = tabs ? locales ? buildLocalizedNavigationFromTabsConfig(routes, tabs, locales) ?? {
3275
3322
  kind: "localized-tabbed",
3276
3323
  locales: {}
3277
3324
  } : buildNavigationFromTabsConfig(routes, tabs) : {
@@ -3301,7 +3348,7 @@ var ClarifyEngine = class {
3301
3348
  routes: this.routes,
3302
3349
  navigation: this.navigation,
3303
3350
  plugins: this.plugins,
3304
- themeEditor: this.runtime.command === "serve" || this.runtime.themeEditor === true || this.projectConfig.theme.editor,
3351
+ themeEditor: this.runtime.command === "serve" || this.runtime.themeEditor === true || this.projectConfig.features.themeEditor.enabled,
3305
3352
  version: this.ctx.version
3306
3353
  });
3307
3354
  this.virtualModules = await runHooks(this.plugins, "modules:before", this.virtualModules, this.ctx);
@@ -3344,7 +3391,7 @@ var ClarifyEngine = class {
3344
3391
  await runPhase(this.plugins, "ssg", this.ctx, async () => {
3345
3392
  if (process.env.SKIP_CLARIFY_SSG) return;
3346
3393
  if (!await runInterceptHooks(this.plugins, "ssg:shouldRun", this.ctx)) return;
3347
- if (this.generateOptions.ssg.failOnError) assertNoContentDiagnostics(this.routes);
3394
+ assertNoContentDiagnostics(this.routes);
3348
3395
  const outputDir = this.runtime.outputDirectory ?? this.generateOptions.outputDirectory;
3349
3396
  if (!outputDir) throw new Error("[clarify] outputDirectory is required before SSG runs");
3350
3397
  const ssrOutputDir = (0, node_path.join)(outputDir, ".ssr");
@@ -3358,10 +3405,10 @@ var ClarifyEngine = class {
3358
3405
  plugins: this.runtime.ssrPlugins ?? []
3359
3406
  });
3360
3407
  const ssrBundlePath = (0, node_path.join)(ssrOutputDir, "entry-server.js");
3361
- await renderSSGRoutes(this.routes, this.runtimeContext(), outputDir, ssrBundlePath, this.generateOptions.ssg.failOnError);
3408
+ await renderSSGRoutes(this.routes, this.runtimeContext(), outputDir, ssrBundlePath);
3362
3409
  } catch (err) {
3363
3410
  console.error("[clarify] SSG failed:", err);
3364
- if (this.generateOptions.ssg.failOnError) throw err;
3411
+ throw err;
3365
3412
  } finally {
3366
3413
  if (tempEntryPath) try {
3367
3414
  (0, node_fs.rmSync)(tempEntryPath, { force: true });
@@ -3452,4 +3499,4 @@ Object.defineProperty(exports, "defineConfig", {
3452
3499
  }
3453
3500
  });
3454
3501
 
3455
- //# sourceMappingURL=engine-BDdPszs2.cjs.map
3502
+ //# sourceMappingURL=engine-BfkoyVNo.cjs.map