@takazudo/zudo-doc 5.5.3 → 5.7.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 (83) hide show
  1. package/CHANGELOG.md +51 -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 +31 -0
  20. package/dist/header/nav-active.js +2 -1
  21. package/dist/header/nav-overflow-generated-script.d.ts +11 -0
  22. package/dist/header/nav-overflow-generated-script.js +302 -0
  23. package/dist/header/nav-overflow-script.d.ts +1 -1
  24. package/dist/header/nav-overflow-script.js +1 -286
  25. package/dist/header-with-defaults/index.js +11 -2
  26. package/dist/i18n-version/language-switcher.d.ts +6 -0
  27. package/dist/i18n-version/language-switcher.js +3 -1
  28. package/dist/i18n-version/version-switcher.d.ts +6 -0
  29. package/dist/i18n-version/version-switcher.js +3 -1
  30. package/dist/md-utils/index.js +33 -1
  31. package/dist/nav-source-docs/index.d.ts +7 -11
  32. package/dist/plugins/route-pages-candidates.d.ts +19 -0
  33. package/dist/plugins/route-pages-candidates.js +17 -0
  34. package/dist/plugins/routes.d.ts +46 -0
  35. package/dist/plugins/routes.js +72 -18
  36. package/dist/preset.d.ts +12 -1
  37. package/dist/preset.js +2 -0
  38. package/dist/route-context/index.js +2 -2
  39. package/dist/routes/_chrome.d.ts +1 -1
  40. package/dist/routes/_chrome.js +4 -0
  41. package/dist/routes/_context.d.ts +3 -3
  42. package/dist/routes/_design-token-panel-bootstrap.d.ts +18 -0
  43. package/dist/routes/_design-token-panel-bootstrap.js +11 -0
  44. package/dist/routes/_docs-helpers.d.ts +1 -36
  45. package/dist/routes/_docs-helpers.js +0 -138
  46. package/dist/safelist.css +1 -1
  47. package/dist/search-widget-script/generated-script.d.ts +8 -0
  48. package/dist/search-widget-script/generated-script.js +465 -0
  49. package/dist/search-widget-script/index.d.ts +1 -18
  50. package/dist/search-widget-script/index.js +1 -443
  51. package/dist/settings.d.ts +82 -1
  52. package/dist/sidebar-toggle-island/index.js +2 -1
  53. package/dist/sidebar-tree/category-meta.d.ts +9 -0
  54. package/dist/sidebar-tree/category-meta.js +21 -12
  55. package/dist/sidebar-tree-island/index.d.ts +8 -1
  56. package/dist/sidebar-tree-island/index.js +16 -14
  57. package/dist/site-schema/doc-route-entries.d.ts +89 -0
  58. package/dist/site-schema/doc-route-entries.js +83 -0
  59. package/dist/site-schema/index.d.ts +17 -0
  60. package/dist/site-schema/index.js +46 -0
  61. package/dist/site-schema/nav-tree.d.ts +28 -0
  62. package/dist/site-schema/nav-tree.js +138 -0
  63. package/dist/site-schema/types.d.ts +97 -0
  64. package/dist/site-schema/types.js +0 -0
  65. package/dist/theme/theme-pack-provider.d.ts +34 -3
  66. package/dist/theme/theme-pack-provider.js +30 -2
  67. package/dist/transitions/index.d.ts +1 -0
  68. package/dist/transitions/index.js +2 -0
  69. package/dist/transitions/nested-island-props-refresh.d.ts +19 -0
  70. package/dist/transitions/nested-island-props-refresh.js +104 -0
  71. package/eject/header/header.tsx +13 -5
  72. package/eject/header/nav-active.ts +16 -1
  73. package/eject/header/nav-class-tokens.ts +9 -5
  74. package/eject/header/nav-overflow-generated-script.ts +29 -0
  75. package/eject/header/nav-overflow-script.ts +26 -304
  76. package/eject/sidebar-toggle-island/index.tsx +13 -1
  77. package/eject/sidebar-tree-island/index.tsx +44 -20
  78. package/package.json +25 -12
  79. package/routes-src/_chrome.tsx +21 -9
  80. package/routes-src/_design-token-panel-bootstrap.tsx +63 -0
  81. package/routes-src/_docs-helpers.ts +18 -225
  82. package/routes-src/_virtual.d.ts +5 -2
  83. package/virtual-modules.d.ts +5 -2
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ /** @jsxRuntime automatic */
4
+ /** @jsxImportSource preact */
5
+
6
+ // routes/_design-token-panel-bootstrap — the ROUTES-ONLY configured
7
+ // design-token-panel island (#3396, epic #3394).
8
+ //
9
+ // WHY THIS FILE EXISTS: `virtual:zudo-doc-design-token-panel-config` is
10
+ // registered by the routes plugin, so any module that imports it statically
11
+ // drags the plugin along as a hard requirement. Until #3396 that importer was
12
+ // `../design-token-panel-bootstrap.tsx`, which `chrome/derive.tsx` imports —
13
+ // meaning `@takazudo/zudo-doc/chrome` could not be bundled outside a zfb build
14
+ // (or by a `packageOwnedRoutes: false` host) without aliasing the specifier.
15
+ // Moving the import HERE confines it to the routes graph, which zfb always
16
+ // bundles with the plugin active.
17
+ //
18
+ // WHY A COMPONENT AND NOT A SETTER: the route/SSR module graph and the
19
+ // hydrated island bundle are SEPARATE graphs. A `setPanelConfigBuilder(...)`
20
+ // called while `_chrome.tsx` evaluates would configure only the server-side
21
+ // copy of the bootstrap module; the client bundle re-evaluates that module
22
+ // independently and would keep the package default. Passing the builder as an
23
+ // argument from a component that lives INSIDE the island bundle is the only
24
+ // shape that survives the boundary.
25
+ //
26
+ // ISLAND-SCANNER CONTRACT (#2480 lesson): `_chrome.tsx` imports this module
27
+ // STATICALLY and threads the component into `hostBindings`, so the scanner
28
+ // walks route → _chrome → here. The exported binding identifier and the
29
+ // `displayName` must stay IDENTICAL — zfb registers islands by the
30
+ // scanner-visible export name, and a marker whose name has no registry entry
31
+ // silently never hydrates. The name must also stay DISTINCT from
32
+ // `DesignTokenPanelBootstrap`: `chrome/derive.tsx` still statically imports
33
+ // that package default (it is the slot default for bare `createChrome`
34
+ // callers), so both components are in the scanned graph and a shared name
35
+ // would trip zfb's "island marker name collision" warning and drop one of them.
36
+
37
+ import type { JSX } from "preact";
38
+ import { runDesignTokenPanelBootstrapOnce } from "@takazudo/zudo-doc/design-token-panel-bootstrap";
39
+ // Host-callables channel, third virtual module (#2658, mirrors #2501's
40
+ // chromeBindingsModule): absent `settings.designTokenPanelConfigModule` →
41
+ // re-exports the package default (`@takazudo/zudo-doc/design-token-panel-config`);
42
+ // present → re-exports the host's module. Registered unconditionally by the
43
+ // routes plugin (`../plugins/routes.ts`) whenever `packageOwnedRoutes` is on.
44
+ // Not present on disk; the package ships ambient typings for it
45
+ // (`./_virtual.d.ts`).
46
+ import { buildDesignTokenPanelConfig } from "virtual:zudo-doc-design-token-panel-config";
47
+
48
+ /**
49
+ * The design-token-panel island injected routes mount instead of the package
50
+ * default: identical behavior, except the mode-scoped `PanelConfig` builder is
51
+ * the one the routes plugin resolved — the host's
52
+ * `settings.designTokenPanelConfigModule` when set, the package default
53
+ * otherwise.
54
+ *
55
+ * Renders `null` on both SSR and client (the panel self-mounts as a side
56
+ * effect of `bootstrapDesignTokenPanel`), matching the package default's
57
+ * non-`ssrFallback` `Island({ when: "load" })` shape.
58
+ */
59
+ export function ConfiguredDesignTokenPanelBootstrap(): JSX.Element | null {
60
+ runDesignTokenPanelBootstrapOnce(buildDesignTokenPanelConfig, "configured");
61
+ return null;
62
+ }
63
+ ConfiguredDesignTokenPanelBootstrap.displayName = "ConfiguredDesignTokenPanelBootstrap";
@@ -1,37 +1,28 @@
1
- // routes/_docs-helpers — package-side nav-tree helpers for the package-owned
2
- // route entrypoints (epic Package-First Finale #2356, A1 #2361).
1
+ // routes/_docs-helpers — the content bridge for the package-owned route
2
+ // entrypoints (epic Package-First Finale #2356, A1 #2361).
3
3
  //
4
- // The host's `src/utils/docs.ts` `buildNavTree` / `buildBreadcrumbs` /
5
- // `collectAutoIndexNodes` / `groupSatelliteNodes` / `findNode` /
6
- // `firstRoutedHref` are host-only wrappers around the package primitive
7
- // `buildSidebarTree` (`@takazudo/zudo-doc/sidebar-tree`). The package route
8
- // entrypoints inject these same helpers into the doc-route / nav-source /
9
- // nav-wrapper factories, so this module reproduces them WITHOUT the host `@/`
10
- // alias — the "+ package `docs` helpers" note in the ADR's host-dep table.
4
+ // WHAT IS LEFT HERE: `stableDocs`, and only `stableDocs`. It is the one piece
5
+ // of this module that cannot be browser-safe — it imports `@takazudo/zfb/content`
6
+ // directly (NOT the host `zfb/content` tsconfig alias — Decision 2) to anchor
7
+ // the bridged + draft-filtered array on the build snapshot, so repeat callers
8
+ // within a build get the SAME instance and the nav-tree / doc-route memos
9
+ // short-circuit (the content bridge — Decision 5).
11
10
  //
12
- // Faithful to the host derivation (root-index node synthesis, NavNode shape,
13
- // satellite grouping, breadcrumb walk) but without the host-only LRU /
14
- // identity caches: build-time array-identity stability already comes from the
15
- // snapshot-anchored `stableDocs` (below) + `memoizeDerived` in the factories.
16
- //
17
- // `stableDocs` (the content bridge — Decision 5) imports `@takazudo/zfb/content`
18
- // directly (NOT the host `zfb/content` tsconfig alias — Decision 2), anchoring
19
- // the bridged + draft-filtered array on the snapshot so repeat callers within a
20
- // build get the SAME instance and the nav-tree / doc-route memos short-circuit.
11
+ // WHAT MOVED: every pure nav helper this file used to define — `buildNavTree`,
12
+ // `groupSatelliteNodes`, `findNode`, `firstRoutedHref`, `collectAutoIndexNodes`,
13
+ // `buildBreadcrumbs`, `isNavVisible` — now lives in `../site-schema/nav-tree.js`
14
+ // (#3395); import them from there, not from here. Cutting them loose is what
15
+ // lets `createDocRouteEntries` reach `buildBreadcrumbs` without dragging
16
+ // `@takazudo/zfb/content` into an otherwise browser-safe graph. This module
17
+ // deliberately re-exports NOTHING: a second import path for the nav helpers
18
+ // through this file would put them one hop from the `@takazudo/zfb/content`
19
+ // edge this file exists to carry.
21
20
 
22
21
  import {
23
22
  getCollection,
24
23
  getContentSnapshot,
25
24
  } from "@takazudo/zfb/content";
26
- import {
27
- buildSidebarTree,
28
- type CategoryMeta,
29
- type SidebarNode,
30
- } from "@takazudo/zudo-doc/sidebar-tree";
31
- import { toRouteSlug } from "@takazudo/zudo-doc/slug";
32
- import type { DocNavNode, DocPageEntry, DocPageFrontmatter } from "@takazudo/zudo-doc/doc-page-props";
33
-
34
- export type { CategoryMeta, DocNavNode, DocPageEntry };
25
+ import type { DocPageEntry, DocPageFrontmatter } from "@takazudo/zudo-doc/doc-page-props";
35
26
 
36
27
  // ---------------------------------------------------------------------------
37
28
  // Content bridge — snapshot-anchored stable docs (Decision 5)
@@ -64,201 +55,3 @@ export function stableDocs(collectionName: string): DocPageEntry[] {
64
55
  docsByAnchor.set(anchor, built);
65
56
  return built;
66
57
  }
67
-
68
- // ---------------------------------------------------------------------------
69
- // Nav-tree helpers — ported from src/utils/docs.ts onto buildSidebarTree
70
- // ---------------------------------------------------------------------------
71
-
72
- /** Filter predicate: true when a doc should appear in navigation. */
73
- export function isNavVisible(doc: DocPageEntry): boolean {
74
- return !doc.data.unlisted && !doc.data.standalone;
75
- }
76
-
77
- /** A docs-href builder: `(slug, locale) => url`. */
78
- export type BuildHref = (slug: string, locale: string) => string;
79
-
80
- export interface BuildNavTreeOptions {
81
- buildHref?: BuildHref;
82
- }
83
-
84
- function toNavNode(node: SidebarNode): DocNavNode {
85
- return {
86
- slug: node.id,
87
- label: node.label,
88
- description: node.description,
89
- position: node.sidebar_position ?? 999,
90
- href: node.href,
91
- hasPage: node.hasPage,
92
- children: node.children.map(toNavNode),
93
- sortOrder: node.sortOrder ?? "asc",
94
- };
95
- }
96
-
97
- /** Last entry whose package-derived slug is empty ("") — the entry the shared
98
- * builder skips. Last one wins (mirrors the host builder's overwrite). */
99
- function findRootIndexDoc(docs: DocPageEntry[]): DocPageEntry | undefined {
100
- let found: DocPageEntry | undefined;
101
- for (const d of docs) {
102
- const slug = d.data.slug ?? toRouteSlug(d.slug);
103
- if (slug === "") found = d;
104
- }
105
- return found;
106
- }
107
-
108
- function toRootNavNode(
109
- doc: DocPageEntry,
110
- locale: string,
111
- buildHref: BuildHref,
112
- categoryMeta?: Map<string, CategoryMeta>,
113
- ): DocNavNode {
114
- const meta = categoryMeta?.get("");
115
- const noPage = doc.data.category_no_page ?? meta?.noPage;
116
- const sortOrder =
117
- (doc.data.category_sort_order as "asc" | "desc" | undefined) ?? meta?.sortOrder ?? "asc";
118
- return {
119
- slug: "",
120
- label:
121
- (doc.data.sidebar_label as string | undefined) ??
122
- doc.data.title ??
123
- meta?.label ??
124
- "",
125
- description: doc.data.description ?? meta?.description,
126
- position: (doc.data.sidebar_position as number | undefined) ?? meta?.position ?? 999,
127
- href: noPage ? undefined : buildHref("", locale),
128
- hasPage: noPage !== true,
129
- children: [],
130
- sortOrder,
131
- };
132
- }
133
-
134
- /**
135
- * Build a recursive navigation tree from a flat doc collection. Delegates tree
136
- * construction to the shared `buildSidebarTree`; keeps the host-side concerns
137
- * (DocNavNode shape, root-index node synthesis). `buildHref` parameterizes the
138
- * href space (default: the injected `docsUrl`). Unlike the host copy this drops
139
- * the LRU / identity caches — the snapshot-anchored `stableDocs` already makes
140
- * the input arrays identity-stable so the factory-level `memoizeDerived` short-
141
- * circuits per build.
142
- */
143
- export function buildNavTree(
144
- docs: DocPageEntry[],
145
- locale: string,
146
- categoryMeta: Map<string, CategoryMeta> | undefined,
147
- buildHref: BuildHref,
148
- options?: BuildNavTreeOptions,
149
- ): DocNavNode[] {
150
- const href: BuildHref = options?.buildHref ?? buildHref;
151
- const sidebarTree = buildSidebarTree(
152
- docs,
153
- locale,
154
- {
155
- categoryMeta,
156
- buildHref: (slug, loc) => href(slug, loc),
157
- // Host call sites own visibility; disable the builder's default filter so
158
- // breadcrumb (unfiltered) and nav (pre-filtered) paths are unchanged.
159
- isNavVisible: () => true,
160
- },
161
- );
162
- const result = sidebarTree.map(toNavNode);
163
-
164
- const rootDoc = findRootIndexDoc(docs);
165
- if (rootDoc) {
166
- result.push(toRootNavNode(rootDoc, locale, href, categoryMeta));
167
- result.sort((a, b) => {
168
- const posCompare = a.position - b.position;
169
- if (posCompare !== 0) return posCompare;
170
- return a.slug.localeCompare(b.slug);
171
- });
172
- }
173
- return result;
174
- }
175
-
176
- /** Group "satellite" nodes (slug-prefix siblings) under their primary node. */
177
- export function groupSatelliteNodes(tree: DocNavNode[], prefixes: string[]): DocNavNode[] {
178
- const result = [...tree];
179
- for (const prefix of prefixes) {
180
- const primaryIdx = result.findIndex((n) => n.slug === prefix);
181
- if (primaryIdx < 0) continue;
182
- const primary = result[primaryIdx];
183
- if (!primary) continue;
184
- const satelliteIdxs: number[] = [];
185
- for (let i = 0; i < result.length; i++) {
186
- const node = result[i];
187
- if (node && i !== primaryIdx && node.slug.startsWith(`${prefix}-`)) {
188
- satelliteIdxs.push(i);
189
- }
190
- }
191
- if (satelliteIdxs.length === 0) continue;
192
- const extraChildren: DocNavNode[] = [];
193
- for (const idx of satelliteIdxs) {
194
- const node = result[idx];
195
- if (node) extraChildren.push(node);
196
- }
197
- result[primaryIdx] = { ...primary, children: [...primary.children, ...extraChildren] };
198
- for (const idx of satelliteIdxs.reverse()) result.splice(idx, 1);
199
- }
200
- return result;
201
- }
202
-
203
- /** Find a node by slug anywhere in the tree. */
204
- export function findNode(nodes: DocNavNode[], slug: string): DocNavNode | undefined {
205
- for (const node of nodes) {
206
- if (node.slug === slug) return node;
207
- const found = findNode(node.children, slug);
208
- if (found) return found;
209
- }
210
- return undefined;
211
- }
212
-
213
- /** Href of the first routed descendant, walking children depth-first. */
214
- export function firstRoutedHref(node: DocNavNode): string | undefined {
215
- for (const child of node.children) {
216
- if (child.hasPage && child.href) return child.href;
217
- const nested = firstRoutedHref(child);
218
- if (nested) return nested;
219
- }
220
- return undefined;
221
- }
222
-
223
- /** Collect all category nodes that have children but no page (no index.mdx). */
224
- export function collectAutoIndexNodes(nodes: DocNavNode[]): DocNavNode[] {
225
- const result: DocNavNode[] = [];
226
- for (const node of nodes) {
227
- if (!node.hasPage && node.children.length > 0 && node.href) result.push(node);
228
- result.push(...collectAutoIndexNodes(node.children));
229
- }
230
- return result;
231
- }
232
-
233
- export interface BreadcrumbItem {
234
- label: string;
235
- href?: string;
236
- }
237
-
238
- /** Build breadcrumb trail by walking the nav tree. `homeHref` is the locale
239
- * docs root (injected). `hrefFor` optionally remaps intermediate crumbs into
240
- * a versioned URL space. */
241
- export function buildBreadcrumbs(
242
- tree: DocNavNode[],
243
- slug: string,
244
- homeHref: string,
245
- hrefFor?: (slug: string) => string,
246
- ): BreadcrumbItem[] {
247
- const parts = slug.split("/");
248
- const crumbs: BreadcrumbItem[] = [{ label: "", href: homeHref }];
249
- let nodes = tree;
250
- for (let i = 0; i < parts.length; i++) {
251
- const partialSlug = parts.slice(0, i + 1).join("/");
252
- const node = nodes.find((n) => n.slug === partialSlug);
253
- if (!node) break;
254
- const isLast = i === parts.length - 1;
255
- const href = isLast
256
- ? undefined
257
- : hrefFor && node.href !== undefined
258
- ? hrefFor(node.slug)
259
- : node.href;
260
- crumbs.push({ label: node.label, href });
261
- nodes = node.children;
262
- }
263
- return crumbs;
264
- }
@@ -57,8 +57,11 @@ declare module "virtual:zudo-doc-chrome-bindings" {
57
57
  // …)`, #2658). No on-disk source — materialised at build, either as a
58
58
  // re-export of the host's `settings.designTokenPanelConfigModule` file or as
59
59
  // a re-export of the package default (`@takazudo/zudo-doc/design-token-panel-config`)
60
- // when the setting is absent. See `plugins/routes.ts` and this package's
61
- // `design-token-panel-bootstrap.tsx` for the full contract.
60
+ // when the setting is absent. Since #3396 its SOLE importer is the
61
+ // routes-only wrapper `./_design-token-panel-bootstrap.tsx` — the generic
62
+ // `design-token-panel-bootstrap.tsx` binds the package default directly, so
63
+ // `@takazudo/zudo-doc/chrome` bundles without the routes plugin. See
64
+ // `plugins/routes.ts` and that bootstrap module for the full contract.
62
65
  //
63
66
  // Typed via an inline `import(...)` type (not a top-level import, for the
64
67
  // same ambient-module-declaration reason as `chromeBindings` above) — see
@@ -70,8 +70,11 @@ declare module "virtual:zudo-doc-chrome-bindings" {
70
70
  // …)`, #2658). No on-disk source — materialised at build, either as a
71
71
  // re-export of the host's `settings.designTokenPanelConfigModule` file or as
72
72
  // a re-export of the package default (`@takazudo/zudo-doc/design-token-panel-config`)
73
- // when the setting is absent. See `plugins/routes.ts` and this package's
74
- // `design-token-panel-bootstrap.tsx` for the full contract.
73
+ // when the setting is absent. Since #3396 its SOLE importer is the
74
+ // routes-only wrapper `./_design-token-panel-bootstrap.tsx` — the generic
75
+ // `design-token-panel-bootstrap.tsx` binds the package default directly, so
76
+ // `@takazudo/zudo-doc/chrome` bundles without the routes plugin. See
77
+ // `plugins/routes.ts` and that bootstrap module for the full contract.
75
78
  //
76
79
  // Typed via an inline `import(...)` type (not a top-level import, for the
77
80
  // same ambient-module-declaration reason as `chromeBindings` above) — see