@takazudo/zudo-doc 5.5.2 → 5.6.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +39 -0
  3. package/dist/chrome/derive.d.ts +46 -0
  4. package/dist/chrome/derive.js +6 -2
  5. package/dist/config-assertions/index.d.ts +31 -0
  6. package/dist/config-assertions/index.js +24 -0
  7. package/dist/config.d.ts +23 -2
  8. package/dist/config.js +3 -0
  9. package/dist/current-path/index.d.ts +27 -0
  10. package/dist/current-path/index.js +11 -0
  11. package/dist/design-token-panel-bootstrap.d.ts +89 -23
  12. package/dist/design-token-panel-bootstrap.js +19 -6
  13. package/dist/doc-page-props/index.d.ts +5 -67
  14. package/dist/doc-route-entries/index.d.ts +10 -95
  15. package/dist/doc-route-entries/index.js +1 -78
  16. package/dist/doc-route-paths/index.d.ts +1 -1
  17. package/dist/head-with-defaults/index.d.ts +3 -1
  18. package/dist/head-with-defaults/index.js +76 -4
  19. package/dist/header/nav-active.d.ts +28 -0
  20. package/dist/header/nav-active.js +2 -1
  21. package/dist/header/nav-overflow-script.js +30 -17
  22. package/dist/header-with-defaults/index.js +11 -2
  23. package/dist/i18n-version/language-switcher.d.ts +6 -0
  24. package/dist/i18n-version/language-switcher.js +3 -1
  25. package/dist/i18n-version/version-switcher.d.ts +6 -0
  26. package/dist/i18n-version/version-switcher.js +3 -1
  27. package/dist/nav-source-docs/index.d.ts +7 -11
  28. package/dist/plugins/route-pages-candidates.d.ts +19 -0
  29. package/dist/plugins/route-pages-candidates.js +17 -0
  30. package/dist/plugins/routes.d.ts +46 -0
  31. package/dist/plugins/routes.js +72 -18
  32. package/dist/preset.d.ts +12 -1
  33. package/dist/preset.js +2 -0
  34. package/dist/route-context/index.js +2 -2
  35. package/dist/routes/_chrome.d.ts +1 -1
  36. package/dist/routes/_chrome.js +4 -0
  37. package/dist/routes/_context.d.ts +3 -3
  38. package/dist/routes/_design-token-panel-bootstrap.d.ts +18 -0
  39. package/dist/routes/_design-token-panel-bootstrap.js +11 -0
  40. package/dist/routes/_docs-helpers.d.ts +1 -36
  41. package/dist/routes/_docs-helpers.js +0 -138
  42. package/dist/safelist.css +1 -1
  43. package/dist/search-widget-script/generated-script.d.ts +8 -0
  44. package/dist/search-widget-script/generated-script.js +465 -0
  45. package/dist/search-widget-script/index.d.ts +1 -18
  46. package/dist/search-widget-script/index.js +1 -443
  47. package/dist/settings.d.ts +82 -1
  48. package/dist/sidebar-tree/category-meta.d.ts +9 -0
  49. package/dist/sidebar-tree/category-meta.js +21 -12
  50. package/dist/sidebar-tree-island/index.d.ts +8 -1
  51. package/dist/sidebar-tree-island/index.js +16 -14
  52. package/dist/site-schema/doc-route-entries.d.ts +89 -0
  53. package/dist/site-schema/doc-route-entries.js +83 -0
  54. package/dist/site-schema/index.d.ts +17 -0
  55. package/dist/site-schema/index.js +46 -0
  56. package/dist/site-schema/nav-tree.d.ts +28 -0
  57. package/dist/site-schema/nav-tree.js +138 -0
  58. package/dist/site-schema/types.d.ts +97 -0
  59. package/dist/site-schema/types.js +0 -0
  60. package/dist/theme/theme-pack-provider.d.ts +34 -3
  61. package/dist/theme/theme-pack-provider.js +30 -2
  62. package/eject/header/nav-active.ts +13 -1
  63. package/eject/header/nav-overflow-script.ts +30 -17
  64. package/eject/sidebar-tree-island/index.tsx +44 -20
  65. package/package.json +22 -12
  66. package/routes-src/_chrome.tsx +21 -9
  67. package/routes-src/_design-token-panel-bootstrap.tsx +63 -0
  68. package/routes-src/_docs-helpers.ts +18 -225
  69. package/routes-src/_virtual.d.ts +5 -2
  70. package/virtual-modules.d.ts +5 -2
@@ -1,2 +1,48 @@
1
+ /** One injected route: a zfb route pattern + the package entrypoint specifier
2
+ * that renders it, with optional zfb `injectRoute` opts. */
3
+ interface RouteSpec {
4
+ pattern: string;
5
+ entrypoint: string;
6
+ opts?: {
7
+ prerender?: boolean;
8
+ };
9
+ /**
10
+ * Whether this route counts toward the DTP shadow diagnostic's denominator
11
+ * (#3434). REQUIRED, deliberately — a newly added route must state its
12
+ * membership, so forgetting one is a type error instead of the silent
13
+ * miss #3434 *is*.
14
+ *
15
+ * The name is scoped to the DIAGNOSTIC, not to the route's own nature,
16
+ * because that is all the flag actually asserts. In particular it is NOT
17
+ * `rendersPanel`: `/404` genuinely DOES render the configured DTP bootstrap
18
+ * (`routes/404.tsx` passes `bodyEndComponents={<BodyEndIslands …/>}`, and
19
+ * `routes/_chrome.tsx` feeds that chrome the configured wrapper), so a
20
+ * `rendersPanel: false` tag on it would be a false claim in the source. It
21
+ * is likewise not `kind: "doc-content"` — the counted set includes the
22
+ * locale home, the tag pages and the version pages.
23
+ *
24
+ * `false` for the four routes a reader never browses as documentation:
25
+ * `/sitemap.xml` and `/robots.txt` cannot render an HTML panel at all (they
26
+ * would hold the check at "not fully shadowed" forever), `/api/ai-chat` is a
27
+ * JSON endpoint, and `/404` is an error page — losing the panel there says
28
+ * nothing about whether the site's docs still have one. The never-injected
29
+ * `/` (see the note in `deriveRoutes`) is absent from the catalog entirely
30
+ * and so cannot be counted either.
31
+ */
32
+ includedInDtpShadowDiagnostic: boolean;
33
+ }
34
+ /**
35
+ * The DTP shadow diagnostic's warn/silent decision, extracted from `setup()`
36
+ * so the vacuity guard below is directly unit-testable (#3434).
37
+ *
38
+ * Returns `true` only when there is at least one reader-facing route AND every
39
+ * one of them is shadowed. The emptiness check is the whole reason this is a
40
+ * named function: `[].every(…)` is vacuously `true`, so an empty denominator
41
+ * would otherwise fire the warning for every host. That state is unreachable
42
+ * through `deriveRoutes` today (`/docs/[[...slug]]` is always emitted and
43
+ * always counted), which is exactly why it needs a test that calls this
44
+ * directly — a build-driven test cannot construct it.
45
+ */
46
+ export declare function shouldWarnDtpFullyShadowed(routes: ReadonlyArray<Pick<RouteSpec, "pattern" | "includedInDtpShadowDiagnostic">>, isShadowed: (pattern: string) => boolean): boolean;
1
47
  declare const plugin: import("@takazudo/zfb").ZfbPlugin;
2
48
  export default plugin;
@@ -1,46 +1,61 @@
1
1
  import { createRequire } from "node:module";
2
- import { existsSync, statSync, cpSync, rmSync, mkdirSync } from "node:fs";
2
+ import { existsSync, statSync, readFileSync, cpSync, rmSync, mkdirSync } from "node:fs";
3
3
  import { dirname, basename, join } from "node:path";
4
4
  import { definePlugin } from "@takazudo/zfb/plugins";
5
5
  import {
6
6
  loadThemePackRegistry,
7
7
  resolveEnabledPacks
8
8
  } from "../theme-packs-registry/index.js";
9
+ import { derivePagesCandidates } from "./route-pages-candidates.js";
10
+ function shouldWarnDtpFullyShadowed(routes, isShadowed) {
11
+ const counted = routes.filter((route) => route.includedInDtpShadowDiagnostic);
12
+ if (counted.length === 0) return false;
13
+ return counted.every((route) => isShadowed(route.pattern));
14
+ }
15
+ function isExactDefaultReExport(source, entrypoint) {
16
+ const withoutComments = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
17
+ const specifier = entrypoint.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18
+ const reExportPattern = new RegExp(
19
+ `export\\s*\\{[^}]*\\bdefault\\b(?!\\s+as\\s)[^}]*\\}\\s*from\\s*(["'])${specifier}\\1`
20
+ );
21
+ return reExportPattern.test(withoutComments);
22
+ }
9
23
  function deriveRoutes(settings) {
10
24
  const routes = [];
11
25
  const localeCodes = Object.keys(settings.locales ?? {});
12
26
  const hasVersions = Array.isArray(settings.versions) && settings.versions.length > 0;
13
27
  const docTags = settings.docTags === true;
14
28
  const aiAssistant = settings.aiAssistant === true;
15
- routes.push({ pattern: "/404", entrypoint: "@takazudo/zudo-doc/routes/404" });
16
- routes.push({ pattern: "/sitemap.xml", entrypoint: "@takazudo/zudo-doc/routes/sitemap.xml" });
17
- routes.push({ pattern: "/robots.txt", entrypoint: "@takazudo/zudo-doc/routes/robots.txt" });
18
- routes.push({ pattern: "/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/docs-slug" });
29
+ routes.push({ pattern: "/404", entrypoint: "@takazudo/zudo-doc/routes/404", includedInDtpShadowDiagnostic: false });
30
+ routes.push({ pattern: "/sitemap.xml", entrypoint: "@takazudo/zudo-doc/routes/sitemap.xml", includedInDtpShadowDiagnostic: false });
31
+ routes.push({ pattern: "/robots.txt", entrypoint: "@takazudo/zudo-doc/routes/robots.txt", includedInDtpShadowDiagnostic: false });
32
+ routes.push({ pattern: "/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/docs-slug", includedInDtpShadowDiagnostic: true });
19
33
  if (docTags) {
20
- routes.push({ pattern: "/docs/tags", entrypoint: "@takazudo/zudo-doc/routes/docs-tags-index" });
21
- routes.push({ pattern: "/docs/tags/[tag]", entrypoint: "@takazudo/zudo-doc/routes/docs-tags-tag" });
34
+ routes.push({ pattern: "/docs/tags", entrypoint: "@takazudo/zudo-doc/routes/docs-tags-index", includedInDtpShadowDiagnostic: true });
35
+ routes.push({ pattern: "/docs/tags/[tag]", entrypoint: "@takazudo/zudo-doc/routes/docs-tags-tag", includedInDtpShadowDiagnostic: true });
22
36
  }
23
37
  if (aiAssistant) {
24
38
  routes.push({
25
39
  pattern: "/api/ai-chat",
26
40
  entrypoint: "@takazudo/zudo-doc/routes/api-ai-chat",
27
- opts: { prerender: false }
41
+ opts: { prerender: false },
42
+ includedInDtpShadowDiagnostic: false
28
43
  });
29
44
  }
30
45
  if (hasVersions) {
31
- routes.push({ pattern: "/docs/versions", entrypoint: "@takazudo/zudo-doc/routes/docs-versions" });
32
- routes.push({ pattern: "/v/[version]/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/v-docs-slug" });
33
- routes.push({ pattern: "/v/[version]/[locale]/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/v-locale-docs-slug" });
46
+ routes.push({ pattern: "/docs/versions", entrypoint: "@takazudo/zudo-doc/routes/docs-versions", includedInDtpShadowDiagnostic: true });
47
+ routes.push({ pattern: "/v/[version]/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/v-docs-slug", includedInDtpShadowDiagnostic: true });
48
+ routes.push({ pattern: "/v/[version]/[locale]/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/v-locale-docs-slug", includedInDtpShadowDiagnostic: true });
34
49
  }
35
50
  if (localeCodes.length > 0) {
36
- routes.push({ pattern: "/[locale]", entrypoint: "@takazudo/zudo-doc/routes/locale-index" });
37
- routes.push({ pattern: "/[locale]/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-slug" });
51
+ routes.push({ pattern: "/[locale]", entrypoint: "@takazudo/zudo-doc/routes/locale-index", includedInDtpShadowDiagnostic: true });
52
+ routes.push({ pattern: "/[locale]/docs/[[...slug]]", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-slug", includedInDtpShadowDiagnostic: true });
38
53
  if (docTags) {
39
- routes.push({ pattern: "/[locale]/docs/tags", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-tags-index" });
40
- routes.push({ pattern: "/[locale]/docs/tags/[tag]", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-tags-tag" });
54
+ routes.push({ pattern: "/[locale]/docs/tags", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-tags-index", includedInDtpShadowDiagnostic: true });
55
+ routes.push({ pattern: "/[locale]/docs/tags/[tag]", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-tags-tag", includedInDtpShadowDiagnostic: true });
41
56
  }
42
57
  if (hasVersions) {
43
- routes.push({ pattern: "/[locale]/docs/versions", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-versions" });
58
+ routes.push({ pattern: "/[locale]/docs/versions", entrypoint: "@takazudo/zudo-doc/routes/locale-docs-versions", includedInDtpShadowDiagnostic: true });
44
59
  }
45
60
  }
46
61
  return routes;
@@ -74,6 +89,7 @@ const plugin = definePlugin({
74
89
  const translations = options.translations ?? {};
75
90
  const tagVocabulary = options.tagVocabulary ?? [];
76
91
  const colorSchemes = options.colorSchemes ?? null;
92
+ const derivedRoutes = deriveRoutes(settings);
77
93
  const themePacksDir = new URL("../theme-packs/", import.meta.url);
78
94
  const themePackRegistry = resolveEnabledPacks(
79
95
  loadThemePackRegistry(themePacksDir),
@@ -120,6 +136,43 @@ const plugin = definePlugin({
120
136
  ` : `export { buildDesignTokenPanelConfig } from "@takazudo/zudo-doc/design-token-panel-config";
121
137
  `
122
138
  );
139
+ if (settings.designTokenPanel === true && designTokenPanelConfigAbsPath) {
140
+ const pagesDir = join(ctx.projectRoot, "pages");
141
+ const routesByPattern = new Map(derivedRoutes.map((route) => [route.pattern, route]));
142
+ const readerRoutesAllShadowed = shouldWarnDtpFullyShadowed(derivedRoutes, (pattern) => {
143
+ const route = routesByPattern.get(pattern);
144
+ return derivePagesCandidates(pattern).some((rel) => {
145
+ const abs = join(pagesDir, rel);
146
+ if (!existsSync(abs)) return false;
147
+ let source;
148
+ try {
149
+ source = readFileSync(abs, "utf8");
150
+ } catch {
151
+ return true;
152
+ }
153
+ if (route && isExactDefaultReExport(source, route.entrypoint)) {
154
+ return false;
155
+ }
156
+ return true;
157
+ });
158
+ });
159
+ if (readerRoutesAllShadowed) {
160
+ let chromeBindingsSource;
161
+ if (chromeBindingsAbsPath !== void 0) {
162
+ try {
163
+ chromeBindingsSource = readFileSync(chromeBindingsAbsPath, "utf8");
164
+ } catch {
165
+ chromeBindingsSource = void 0;
166
+ }
167
+ }
168
+ const workaroundLikelyApplied = chromeBindingsSource !== void 0 && chromeBindingsSource.includes("DesignTokenPanelBootstrap");
169
+ if (!workaroundLikelyApplied) {
170
+ ctx.logger.warn(
171
+ 'zudo-doc: settings.designTokenPanelConfigModule is set, but every reader-facing injected route\'s URL is shadowed by a kept user pages/ file \u2014 the configured Design Token Panel builder can never apply on any documentation page a reader browses on this site (zudolab/zudo-doc#3420). Thread your builder through chromeBindings.DesignTokenPanelBootstrap instead \u2014 that binding wins everywhere, including stub-rendered pages (see the designTokenPanelConfigModule docblock in settings.ts). If you already did this via a chromeBindingsModule that composes the bootstrap indirectly (never spelling "DesignTokenPanelBootstrap" literally in the resolved file), this warning is safe to ignore. A pages/ file that cleanly re-exports a shadowed route\'s own entrypoint (`export { default, paths, frontmatter } from "@takazudo/zudo-doc/routes/\u2026"`) does not count as a real shadow either \u2014 but that check is also a best-effort text scan, not module evaluation, so a file that reaches the same entrypoint some other way may still trigger this warning even though it is safe.'
172
+ );
173
+ }
174
+ }
175
+ }
123
176
  const require2 = createRequire(import.meta.url);
124
177
  let stagedRoutesDir;
125
178
  const ensureStaged = (routesSrcDir) => {
@@ -131,7 +184,7 @@ const plugin = definePlugin({
131
184
  stagedRoutesDir = dest;
132
185
  return dest;
133
186
  };
134
- for (const route of deriveRoutes(settings)) {
187
+ for (const route of derivedRoutes) {
135
188
  let resolvedEntrypoint;
136
189
  try {
137
190
  const compiledPath = require2.resolve(route.entrypoint);
@@ -159,5 +212,6 @@ const plugin = definePlugin({
159
212
  });
160
213
  var routes_default = plugin;
161
214
  export {
162
- routes_default as default
215
+ routes_default as default,
216
+ shouldWarnDtpFullyShadowed
163
217
  };
package/dist/preset.d.ts CHANGED
@@ -34,7 +34,7 @@
34
34
  */
35
35
  import { z } from "zod";
36
36
  import type { ColorScheme } from "./color-scheme-utils.js";
37
- import type { TagVocabularyEntry } from "./settings.js";
37
+ import type { TagVocabularyEntry, FaviconConfig } from "./settings.js";
38
38
  import type { DirectiveSpec } from "@takazudo/zfb/config";
39
39
  /** A single locale's content directory (`settings.locales[code]`). */
40
40
  export interface PresetLocaleConfig {
@@ -76,6 +76,17 @@ export interface PresetSettings {
76
76
  base: string;
77
77
  siteName: string;
78
78
  siteDescription: string;
79
+ /**
80
+ * Home-hero logo. `zudoDocPreset()` doesn't otherwise consume this field —
81
+ * rendering happens in `home-page/index.tsx` against the full `Settings`
82
+ * object — it is carried here only so `assertNoEmptyStringFaviconOrLogo`
83
+ * (`config-assertions/index.ts`) can validate a direct `zudoDocPreset()`
84
+ * call the same way `zudoDoc()` does (#3474).
85
+ */
86
+ logo?: string | false;
87
+ /** Favicon links. Same rationale as {@link PresetSettings.logo} — carried
88
+ * only for `assertNoEmptyStringFaviconOrLogo` (#3474). */
89
+ favicon?: string | FaviconConfig | false;
79
90
  siteUrl: string;
80
91
  trailingSlash: boolean;
81
92
  minifyHtml?: boolean;
package/dist/preset.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { assertNoCommaInVersionSlugs } from "./version-availability/index.js";
3
+ import { assertNoEmptyStringFaviconOrLogo } from "./config-assertions/index.js";
3
4
  function zudoDocPreset({
4
5
  settings,
5
6
  buildDocsSchema,
@@ -9,6 +10,7 @@ function zudoDocPreset({
9
10
  colorSchemes
10
11
  }) {
11
12
  assertNoCommaInVersionSlugs(settings.versions);
13
+ assertNoEmptyStringFaviconOrLogo(settings);
12
14
  const docsSchemaJson = z.toJSONSchema(buildDocsSchema());
13
15
  return {
14
16
  collections: buildCollections(settings, docsSchemaJson),
@@ -16,8 +16,8 @@ import {
16
16
  import {
17
17
  createRouteEnumerators
18
18
  } from "../route-enumerators/index.js";
19
+ import { stableDocs as defaultStableDocs } from "../routes/_docs-helpers.js";
19
20
  import {
20
- stableDocs as defaultStableDocs,
21
21
  isNavVisible,
22
22
  buildNavTree,
23
23
  buildBreadcrumbs,
@@ -25,7 +25,7 @@ import {
25
25
  groupSatelliteNodes,
26
26
  findNode,
27
27
  firstRoutedHref
28
- } from "../routes/_docs-helpers.js";
28
+ } from "../site-schema/nav-tree.js";
29
29
  function createRouteContext(payload, options = {}) {
30
30
  const settings = payload.settings;
31
31
  const translations = payload.translations;
@@ -1,4 +1,4 @@
1
- import type { DocNavNode } from "./_docs-helpers.js";
1
+ import type { DocNavNode } from "../site-schema/types.js";
2
2
  export declare const composeMetaTitle: (title: string) => string, HeadWithDefaults: (props: import("../head-with-defaults/index.js").HeadWithDefaultsProps) => import("preact").JSX.Element, HeaderWithDefaults: (props: import("../header-with-defaults/index.js").HeaderWithDefaultsProps) => import("preact").JSX.Element, FooterWithDefaults: (props: {
3
3
  lang?: string;
4
4
  }) => import("preact").VNode, SidebarWithDefaults: (props: import("../sidebar-with-defaults/index.js").SidebarWithDefaultsProps) => import("preact").JSX.Element, renderDocPage: (props: import("../doc-page-props/index.js").DocPageBaseProps, opts: import("../doc-page-renderer/index.js").RenderDocPageOptions) => import("preact").JSX.Element, VersionsPageView: (props: import("../versions-page/index.js").VersionsPageViewProps) => import("preact").JSX.Element, collectTagMapForLocale: (locale: string) => Map<string, import("../tag-pages/index.js").TagInfo>, TagDetailPageView: (props: {
@@ -3,7 +3,11 @@ import { createChrome } from "../chrome/index.js";
3
3
  import { DocHistory } from "../doc-history/index.js";
4
4
  import { defineChromeBindings } from "@takazudo/zudo-doc/chrome-bindings";
5
5
  import { chromeBindings } from "virtual:zudo-doc-chrome-bindings";
6
+ import { ConfiguredDesignTokenPanelBootstrap } from "./_design-token-panel-bootstrap.js";
6
7
  const chrome = createChrome(routeCtx, {
8
+ ...defineChromeBindings({
9
+ DesignTokenPanelBootstrap: ConfiguredDesignTokenPanelBootstrap
10
+ }),
7
11
  ...chromeBindings,
8
12
  ...defineChromeBindings({ DocHistory })
9
13
  });
@@ -17,7 +17,7 @@ export declare const themePackRegistry: ThemePackRegistry | null;
17
17
  export declare const i18n: import("../factory-context/index.js").FactoryI18n, defaultLocale: string, locales: readonly string[], getLocaleConfig: (locale: string) => {
18
18
  label: string;
19
19
  dir: string;
20
- } | undefined, getLocaleLabel: (locale: string) => string, t: (key: string, locale?: string) => string, urlHelpers: import("../url-helpers/index.js").UrlHelpers, withBase: (path: string) => string, stripBase: (path: string) => string, docsUrl: (slug: string, lang?: string) => string, versionedDocsUrl: (slug: string, versionSlug: string, lang?: string) => string, navHref: (path: string, lang: string | undefined, currentVersion: string | undefined, versioned?: boolean) => string, isDefaultLocaleOnlyPath: (path: string) => boolean, absoluteUrl: (pageUrl: string) => string | undefined, isExternal: (href: string) => boolean, resolveHref: (href: string) => string, buildLocaleLinks: (currentPath: string, currentLang: string) => import("../url-helpers/index.js").LocaleLink[], getCategoryOrder: () => string[], getNavSectionForSlug: (slug: string) => string | undefined, getNavSubtree: (tree: import("./_docs-helpers.js").DocNavNode[], categoryMatch?: string) => import("./_docs-helpers.js").DocNavNode[], extractHeadings: (body: string) => import("../extract-headings/index.js").HeadingItem[], resolveTagBound: (raw: string) => import("../tag-helpers/index.js").ResolvedTag, collectTags: (entries: ReadonlyArray<{
20
+ } | undefined, getLocaleLabel: (locale: string) => string, t: (key: string, locale?: string) => string, urlHelpers: import("../url-helpers/index.js").UrlHelpers, withBase: (path: string) => string, stripBase: (path: string) => string, docsUrl: (slug: string, lang?: string) => string, versionedDocsUrl: (slug: string, versionSlug: string, lang?: string) => string, navHref: (path: string, lang: string | undefined, currentVersion: string | undefined, versioned?: boolean) => string, isDefaultLocaleOnlyPath: (path: string) => boolean, absoluteUrl: (pageUrl: string) => string | undefined, isExternal: (href: string) => boolean, resolveHref: (href: string) => string, buildLocaleLinks: (currentPath: string, currentLang: string) => import("../url-helpers/index.js").LocaleLink[], getCategoryOrder: () => string[], getNavSectionForSlug: (slug: string) => string | undefined, getNavSubtree: (tree: import("./_chrome.js").DocNavNode[], categoryMatch?: string) => import("./_chrome.js").DocNavNode[], extractHeadings: (body: string) => import("../extract-headings/index.js").HeadingItem[], resolveTagBound: (raw: string) => import("../tag-helpers/index.js").ResolvedTag, collectTags: (entries: ReadonlyArray<{
21
21
  slug: string;
22
22
  data: {
23
23
  slug?: string;
@@ -27,6 +27,6 @@ export declare const i18n: import("../factory-context/index.js").FactoryI18n, de
27
27
  };
28
28
  }>, slugFn: (entrySlug: string, data: {
29
29
  slug?: string;
30
- }) => string) => Map<string, import("./_context.js").TagInfo>, resolveNavSource: (lang: string, currentVersion: string | undefined, options?: import("../nav-source-docs/index.js").NavSourceOptions) => import("../nav-source-docs/index.js").NavSourceDocs, resolveVersionedLocaleSource: (versionSlug: string, versionDocsDir: string | undefined, lang: string, localeDir: string | undefined, options?: import("../nav-source-docs/index.js").NavSourceOptions) => import("../nav-source-docs/index.js").NavSourceDocs, loadNavSourceDocs: (lang: string, currentVersion: string | undefined) => import("../nav-source-docs/index.js").NavSourceDocs, buildDocRouteEntries: (args: import("../doc-route-entries/index.js").BuildDocRouteEntriesArgs) => import("../doc-route-entries/index.js").DocRouteEntry[], enumerateDocsRoutes: (locale: string) => string[], enumerateTagsRoutes: (locale: string) => string[], enumerateVersionedRoutes: (version: import("../route-enumerators/index.js").VersionConfigForEnum, locale: string) => string[], enumerateAllRoutes: () => Map<string, string>, buildNavTree: (docs: import("./_docs-helpers.js").DocPageEntry[], locale: string, categoryMeta: Map<string, import("./_docs-helpers.js").CategoryMeta> | undefined, buildHref: import("../factory-context/index.js").RouteHrefBuilder, options?: {
30
+ }) => string) => Map<string, import("./_context.js").TagInfo>, resolveNavSource: (lang: string, currentVersion: string | undefined, options?: import("../nav-source-docs/index.js").NavSourceOptions) => import("../nav-source-docs/index.js").NavSourceDocs, resolveVersionedLocaleSource: (versionSlug: string, versionDocsDir: string | undefined, lang: string, localeDir: string | undefined, options?: import("../nav-source-docs/index.js").NavSourceOptions) => import("../nav-source-docs/index.js").NavSourceDocs, loadNavSourceDocs: (lang: string, currentVersion: string | undefined) => import("../nav-source-docs/index.js").NavSourceDocs, buildDocRouteEntries: (args: import("../site-schema/doc-route-entries.js").BuildDocRouteEntriesArgs<import("../doc-page-props/index.js").DocPageEntry>) => import("../site-schema/doc-route-entries.js").DocRouteEntry<import("../doc-page-props/index.js").DocPageEntry>[], enumerateDocsRoutes: (locale: string) => string[], enumerateTagsRoutes: (locale: string) => string[], enumerateVersionedRoutes: (version: import("../route-enumerators/index.js").VersionConfigForEnum, locale: string) => string[], enumerateAllRoutes: () => Map<string, string>, buildNavTree: (docs: import("../doc-page-props/index.js").DocPageEntry[], locale: string, categoryMeta: Map<string, import("../sidebar-tree/types.js").CategoryMeta> | undefined, buildHref: import("../factory-context/index.js").RouteHrefBuilder, options?: {
31
31
  buildHref?: import("../factory-context/index.js").RouteHrefBuilder;
32
- }) => import("./_docs-helpers.js").DocNavNode[], groupSatelliteNodes: (tree: import("./_docs-helpers.js").DocNavNode[], prefixes: string[]) => import("./_docs-helpers.js").DocNavNode[], findNode: (nodes: import("./_docs-helpers.js").DocNavNode[], slug: string) => import("./_docs-helpers.js").DocNavNode | undefined, firstRoutedHref: (node: import("./_docs-helpers.js").DocNavNode) => string | undefined, collectAutoIndexNodes: (nodes: import("./_docs-helpers.js").DocNavNode[]) => import("./_docs-helpers.js").DocNavNode[], isNavVisible: (doc: import("./_docs-helpers.js").DocPageEntry) => boolean, stableDocs: (collectionName: string) => import("./_docs-helpers.js").DocPageEntry[], toRouteSlug: (entrySlug: string) => string, toSlugParams: (routeSlug: string) => string[];
32
+ }) => import("./_chrome.js").DocNavNode[], groupSatelliteNodes: (tree: import("./_chrome.js").DocNavNode[], prefixes: string[]) => import("./_chrome.js").DocNavNode[], findNode: (nodes: import("./_chrome.js").DocNavNode[], slug: string) => import("./_chrome.js").DocNavNode | undefined, firstRoutedHref: (node: import("./_chrome.js").DocNavNode) => string | undefined, collectAutoIndexNodes: (nodes: import("./_chrome.js").DocNavNode[]) => import("./_chrome.js").DocNavNode[], isNavVisible: (doc: import("../doc-page-props/index.js").DocPageEntry) => boolean, stableDocs: (collectionName: string) => import("../doc-page-props/index.js").DocPageEntry[], toRouteSlug: (entrySlug: string) => string, toSlugParams: (routeSlug: string) => string[];
@@ -0,0 +1,18 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ import type { JSX } from "preact";
4
+ /**
5
+ * The design-token-panel island injected routes mount instead of the package
6
+ * default: identical behavior, except the mode-scoped `PanelConfig` builder is
7
+ * the one the routes plugin resolved — the host's
8
+ * `settings.designTokenPanelConfigModule` when set, the package default
9
+ * otherwise.
10
+ *
11
+ * Renders `null` on both SSR and client (the panel self-mounts as a side
12
+ * effect of `bootstrapDesignTokenPanel`), matching the package default's
13
+ * non-`ssrFallback` `Island({ when: "load" })` shape.
14
+ */
15
+ export declare function ConfiguredDesignTokenPanelBootstrap(): JSX.Element | null;
16
+ export declare namespace ConfiguredDesignTokenPanelBootstrap {
17
+ var displayName: string;
18
+ }
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { runDesignTokenPanelBootstrapOnce } from "../design-token-panel-bootstrap.js";
3
+ import { buildDesignTokenPanelConfig } from "virtual:zudo-doc-design-token-panel-config";
4
+ function ConfiguredDesignTokenPanelBootstrap() {
5
+ runDesignTokenPanelBootstrapOnce(buildDesignTokenPanelConfig, "configured");
6
+ return null;
7
+ }
8
+ ConfiguredDesignTokenPanelBootstrap.displayName = "ConfiguredDesignTokenPanelBootstrap";
9
+ export {
10
+ ConfiguredDesignTokenPanelBootstrap
11
+ };
@@ -1,6 +1,4 @@
1
- import { type CategoryMeta } from "../sidebar-tree/index.js";
2
- import type { DocNavNode, DocPageEntry } from "../doc-page-props/index.js";
3
- export type { CategoryMeta, DocNavNode, DocPageEntry };
1
+ import type { DocPageEntry } from "../doc-page-props/index.js";
4
2
  /**
5
3
  * Identity-stable, draft-filtered `DocPageEntry[]` for a collection. Returns the
6
4
  * SAME array instance on every call within one build (anchored on the snapshot
@@ -8,36 +6,3 @@ export type { CategoryMeta, DocNavNode, DocPageEntry };
8
6
  * deliberately unmemoized. Passed as `stableDocs` to `createNavSourceDocs`.
9
7
  */
10
8
  export declare function stableDocs(collectionName: string): DocPageEntry[];
11
- /** Filter predicate: true when a doc should appear in navigation. */
12
- export declare function isNavVisible(doc: DocPageEntry): boolean;
13
- /** A docs-href builder: `(slug, locale) => url`. */
14
- export type BuildHref = (slug: string, locale: string) => string;
15
- export interface BuildNavTreeOptions {
16
- buildHref?: BuildHref;
17
- }
18
- /**
19
- * Build a recursive navigation tree from a flat doc collection. Delegates tree
20
- * construction to the shared `buildSidebarTree`; keeps the host-side concerns
21
- * (DocNavNode shape, root-index node synthesis). `buildHref` parameterizes the
22
- * href space (default: the injected `docsUrl`). Unlike the host copy this drops
23
- * the LRU / identity caches — the snapshot-anchored `stableDocs` already makes
24
- * the input arrays identity-stable so the factory-level `memoizeDerived` short-
25
- * circuits per build.
26
- */
27
- export declare function buildNavTree(docs: DocPageEntry[], locale: string, categoryMeta: Map<string, CategoryMeta> | undefined, buildHref: BuildHref, options?: BuildNavTreeOptions): DocNavNode[];
28
- /** Group "satellite" nodes (slug-prefix siblings) under their primary node. */
29
- export declare function groupSatelliteNodes(tree: DocNavNode[], prefixes: string[]): DocNavNode[];
30
- /** Find a node by slug anywhere in the tree. */
31
- export declare function findNode(nodes: DocNavNode[], slug: string): DocNavNode | undefined;
32
- /** Href of the first routed descendant, walking children depth-first. */
33
- export declare function firstRoutedHref(node: DocNavNode): string | undefined;
34
- /** Collect all category nodes that have children but no page (no index.mdx). */
35
- export declare function collectAutoIndexNodes(nodes: DocNavNode[]): DocNavNode[];
36
- export interface BreadcrumbItem {
37
- label: string;
38
- href?: string;
39
- }
40
- /** Build breadcrumb trail by walking the nav tree. `homeHref` is the locale
41
- * docs root (injected). `hrefFor` optionally remaps intermediate crumbs into
42
- * a versioned URL space. */
43
- export declare function buildBreadcrumbs(tree: DocNavNode[], slug: string, homeHref: string, hrefFor?: (slug: string) => string): BreadcrumbItem[];
@@ -2,10 +2,6 @@ import {
2
2
  getCollection,
3
3
  getContentSnapshot
4
4
  } from "@takazudo/zfb/content";
5
- import {
6
- buildSidebarTree
7
- } from "../sidebar-tree/index.js";
8
- import { toRouteSlug } from "../slug/index.js";
9
5
  function snapshotAnchor(name) {
10
6
  return getContentSnapshot()?.collections[name];
11
7
  }
@@ -22,140 +18,6 @@ function stableDocs(collectionName) {
22
18
  docsByAnchor.set(anchor, built);
23
19
  return built;
24
20
  }
25
- function isNavVisible(doc) {
26
- return !doc.data.unlisted && !doc.data.standalone;
27
- }
28
- function toNavNode(node) {
29
- return {
30
- slug: node.id,
31
- label: node.label,
32
- description: node.description,
33
- position: node.sidebar_position ?? 999,
34
- href: node.href,
35
- hasPage: node.hasPage,
36
- children: node.children.map(toNavNode),
37
- sortOrder: node.sortOrder ?? "asc"
38
- };
39
- }
40
- function findRootIndexDoc(docs) {
41
- let found;
42
- for (const d of docs) {
43
- const slug = d.data.slug ?? toRouteSlug(d.slug);
44
- if (slug === "") found = d;
45
- }
46
- return found;
47
- }
48
- function toRootNavNode(doc, locale, buildHref, categoryMeta) {
49
- const meta = categoryMeta?.get("");
50
- const noPage = doc.data.category_no_page ?? meta?.noPage;
51
- const sortOrder = doc.data.category_sort_order ?? meta?.sortOrder ?? "asc";
52
- return {
53
- slug: "",
54
- label: doc.data.sidebar_label ?? doc.data.title ?? meta?.label ?? "",
55
- description: doc.data.description ?? meta?.description,
56
- position: doc.data.sidebar_position ?? meta?.position ?? 999,
57
- href: noPage ? void 0 : buildHref("", locale),
58
- hasPage: noPage !== true,
59
- children: [],
60
- sortOrder
61
- };
62
- }
63
- function buildNavTree(docs, locale, categoryMeta, buildHref, options) {
64
- const href = options?.buildHref ?? buildHref;
65
- const sidebarTree = buildSidebarTree(
66
- docs,
67
- locale,
68
- {
69
- categoryMeta,
70
- buildHref: (slug, loc) => href(slug, loc),
71
- // Host call sites own visibility; disable the builder's default filter so
72
- // breadcrumb (unfiltered) and nav (pre-filtered) paths are unchanged.
73
- isNavVisible: () => true
74
- }
75
- );
76
- const result = sidebarTree.map(toNavNode);
77
- const rootDoc = findRootIndexDoc(docs);
78
- if (rootDoc) {
79
- result.push(toRootNavNode(rootDoc, locale, href, categoryMeta));
80
- result.sort((a, b) => {
81
- const posCompare = a.position - b.position;
82
- if (posCompare !== 0) return posCompare;
83
- return a.slug.localeCompare(b.slug);
84
- });
85
- }
86
- return result;
87
- }
88
- function groupSatelliteNodes(tree, prefixes) {
89
- const result = [...tree];
90
- for (const prefix of prefixes) {
91
- const primaryIdx = result.findIndex((n) => n.slug === prefix);
92
- if (primaryIdx < 0) continue;
93
- const primary = result[primaryIdx];
94
- if (!primary) continue;
95
- const satelliteIdxs = [];
96
- for (let i = 0; i < result.length; i++) {
97
- const node = result[i];
98
- if (node && i !== primaryIdx && node.slug.startsWith(`${prefix}-`)) {
99
- satelliteIdxs.push(i);
100
- }
101
- }
102
- if (satelliteIdxs.length === 0) continue;
103
- const extraChildren = [];
104
- for (const idx of satelliteIdxs) {
105
- const node = result[idx];
106
- if (node) extraChildren.push(node);
107
- }
108
- result[primaryIdx] = { ...primary, children: [...primary.children, ...extraChildren] };
109
- for (const idx of satelliteIdxs.reverse()) result.splice(idx, 1);
110
- }
111
- return result;
112
- }
113
- function findNode(nodes, slug) {
114
- for (const node of nodes) {
115
- if (node.slug === slug) return node;
116
- const found = findNode(node.children, slug);
117
- if (found) return found;
118
- }
119
- return void 0;
120
- }
121
- function firstRoutedHref(node) {
122
- for (const child of node.children) {
123
- if (child.hasPage && child.href) return child.href;
124
- const nested = firstRoutedHref(child);
125
- if (nested) return nested;
126
- }
127
- return void 0;
128
- }
129
- function collectAutoIndexNodes(nodes) {
130
- const result = [];
131
- for (const node of nodes) {
132
- if (!node.hasPage && node.children.length > 0 && node.href) result.push(node);
133
- result.push(...collectAutoIndexNodes(node.children));
134
- }
135
- return result;
136
- }
137
- function buildBreadcrumbs(tree, slug, homeHref, hrefFor) {
138
- const parts = slug.split("/");
139
- const crumbs = [{ label: "", href: homeHref }];
140
- let nodes = tree;
141
- for (let i = 0; i < parts.length; i++) {
142
- const partialSlug = parts.slice(0, i + 1).join("/");
143
- const node = nodes.find((n) => n.slug === partialSlug);
144
- if (!node) break;
145
- const isLast = i === parts.length - 1;
146
- const href = isLast ? void 0 : hrefFor && node.href !== void 0 ? hrefFor(node.slug) : node.href;
147
- crumbs.push({ label: node.label, href });
148
- nodes = node.children;
149
- }
150
- return crumbs;
151
- }
152
21
  export {
153
- buildBreadcrumbs,
154
- buildNavTree,
155
- collectAutoIndexNodes,
156
- findNode,
157
- firstRoutedHref,
158
- groupSatelliteNodes,
159
- isNavVisible,
160
22
  stableDocs
161
23
  };