@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.
- package/CHANGELOG.md +36 -0
- package/README.md +39 -0
- package/dist/chrome/derive.d.ts +46 -0
- package/dist/chrome/derive.js +6 -2
- package/dist/config-assertions/index.d.ts +31 -0
- package/dist/config-assertions/index.js +24 -0
- package/dist/config.d.ts +23 -2
- package/dist/config.js +3 -0
- package/dist/current-path/index.d.ts +27 -0
- package/dist/current-path/index.js +11 -0
- package/dist/design-token-panel-bootstrap.d.ts +89 -23
- package/dist/design-token-panel-bootstrap.js +19 -6
- package/dist/doc-page-props/index.d.ts +5 -67
- package/dist/doc-route-entries/index.d.ts +10 -95
- package/dist/doc-route-entries/index.js +1 -78
- package/dist/doc-route-paths/index.d.ts +1 -1
- package/dist/head-with-defaults/index.d.ts +3 -1
- package/dist/head-with-defaults/index.js +76 -4
- package/dist/header/nav-active.d.ts +28 -0
- package/dist/header/nav-active.js +2 -1
- package/dist/header/nav-overflow-script.js +30 -17
- package/dist/header-with-defaults/index.js +11 -2
- package/dist/i18n-version/language-switcher.d.ts +6 -0
- package/dist/i18n-version/language-switcher.js +3 -1
- package/dist/i18n-version/version-switcher.d.ts +6 -0
- package/dist/i18n-version/version-switcher.js +3 -1
- package/dist/nav-source-docs/index.d.ts +7 -11
- package/dist/plugins/route-pages-candidates.d.ts +19 -0
- package/dist/plugins/route-pages-candidates.js +17 -0
- package/dist/plugins/routes.d.ts +46 -0
- package/dist/plugins/routes.js +72 -18
- package/dist/preset.d.ts +12 -1
- package/dist/preset.js +2 -0
- package/dist/route-context/index.js +2 -2
- package/dist/routes/_chrome.d.ts +1 -1
- package/dist/routes/_chrome.js +4 -0
- package/dist/routes/_context.d.ts +3 -3
- package/dist/routes/_design-token-panel-bootstrap.d.ts +18 -0
- package/dist/routes/_design-token-panel-bootstrap.js +11 -0
- package/dist/routes/_docs-helpers.d.ts +1 -36
- package/dist/routes/_docs-helpers.js +0 -138
- package/dist/safelist.css +1 -1
- package/dist/search-widget-script/generated-script.d.ts +8 -0
- package/dist/search-widget-script/generated-script.js +465 -0
- package/dist/search-widget-script/index.d.ts +1 -18
- package/dist/search-widget-script/index.js +1 -443
- package/dist/settings.d.ts +82 -1
- package/dist/sidebar-tree/category-meta.d.ts +9 -0
- package/dist/sidebar-tree/category-meta.js +21 -12
- package/dist/sidebar-tree-island/index.d.ts +8 -1
- package/dist/sidebar-tree-island/index.js +16 -14
- package/dist/site-schema/doc-route-entries.d.ts +89 -0
- package/dist/site-schema/doc-route-entries.js +83 -0
- package/dist/site-schema/index.d.ts +17 -0
- package/dist/site-schema/index.js +46 -0
- package/dist/site-schema/nav-tree.d.ts +28 -0
- package/dist/site-schema/nav-tree.js +138 -0
- package/dist/site-schema/types.d.ts +97 -0
- package/dist/site-schema/types.js +0 -0
- package/dist/theme/theme-pack-provider.d.ts +34 -3
- package/dist/theme/theme-pack-provider.js +30 -2
- package/eject/header/nav-active.ts +13 -1
- package/eject/header/nav-overflow-script.ts +30 -17
- package/eject/sidebar-tree-island/index.tsx +44 -20
- package/package.json +22 -12
- package/routes-src/_chrome.tsx +21 -9
- package/routes-src/_design-token-panel-bootstrap.tsx +63 -0
- package/routes-src/_docs-helpers.ts +18 -225
- package/routes-src/_virtual.d.ts +5 -2
- package/virtual-modules.d.ts +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to `@takazudo/zudo-doc` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
|
|
6
6
|
|
|
7
|
+
## [5.6.0] - 2026-08-19
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Added a configurable `favicon` setting. It accepts a plain path string or an object form for multiple slots, with slot types inferred from the file extension rather than hardcoded. (0a8376421, ad6ecb8d4, 5d389b887)
|
|
12
|
+
- Added a browser-safe `@takazudo/zudo-doc/site-schema` subpath so navigation-tree types and helpers can be imported from client code without pulling in Node built-ins. (303cffbc4, 2dde43bcd)
|
|
13
|
+
- Threaded an explicit current-route input through the four active-nav read sites instead of having each one rediscover the path. (9e6461aac, 85aee0625)
|
|
14
|
+
- Added a scaffold pin freshness gate that catches a generated project pinning an unpublished `@takazudo/*` version, and wired it into the publish and prepare flows. (2ba9b5d78, 87eafbddd)
|
|
15
|
+
- The design-token panel now warns when `designTokenPanelConfigModule`'s routes are fully shadowed by host routes. (f846f1d25)
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
- Theme-pack bootstrap no longer uses `document.write` — it appends to `<head>` and arms the anti-FOUC latch only while `document.readyState` is `loading`. (192976c7a, 0007c33b3)
|
|
20
|
+
- A stub page can no longer decide the whole session's design-token panel config; the DTP shadow diagnostic is now scoped to reader-facing routes, gated on `designTokenPanel`, and its file reads are guarded so it can never fail a build. (1f45c135e, 6c807e6bb, e649d957b, 506b1264d, 904f29f20)
|
|
21
|
+
- Empty-string `favicon` and `logo` values are now rejected at config resolution instead of emitting a broken tag. (fbff0f6be)
|
|
22
|
+
- `category-meta` resolves `node:fs` / `node:path` at call time, so importing it from a browser bundle no longer fails. (fd220bfd0)
|
|
23
|
+
- The search-widget inline script is now generated with esbuild `transformSync` and committed as a prebuilt static literal, guarded by a drift gate. (3f55098be, dbfe84092, f40faab3c)
|
|
24
|
+
- The scaffold pin freshness gate reports skipped-with-warning for differing same-core prerelease pins, and no longer has a blind spot when no `next` tag exists. (814a04229, 36dfce205, 86abf4d64)
|
|
25
|
+
|
|
26
|
+
### Other Changes
|
|
27
|
+
|
|
28
|
+
- Bumped the zfb family to 2.7.1, including `zfb-adapter-cloudflare`, and asserted a collision-free build. (918bffb75, d6be0800e, be7b507ee)
|
|
29
|
+
- Bumped `@takazudo/zdtp` to 0.4.11. (be76be2b8)
|
|
30
|
+
- Added a path-filtered A2 no-stub parity gate to PR checks, plus a chrome eval-graph guard and theme-pack write-time link guard. (c5583f116, 54f4e55ea, e77d992df)
|
|
31
|
+
- Documented the favicon setting, the site-schema subpath, and the theme-pack embedder link-ordering contract in both EN and JA. (417c3b058, 7b36984f1, d1a80cb32)
|
|
32
|
+
|
|
33
|
+
## [5.5.3] - 2026-08-17
|
|
34
|
+
|
|
35
|
+
### Bug Fixes
|
|
36
|
+
|
|
37
|
+
- Upgraded the zfb toolchain to 2.5.2, which fixes markdown directive nesting across blank lines, collapsed-run recognition on split shapes, fence-state carryover into the sibling scan, and JSX reconstruction swallowing nested line breaks. (f69f5c7b6)
|
|
38
|
+
|
|
39
|
+
### Other Changes
|
|
40
|
+
|
|
41
|
+
- Bumped the generated scaffold's zfb pins to 2.5.2 so a fresh project picks up the same fixes. (f69f5c7b6)
|
|
42
|
+
|
|
7
43
|
## [5.5.2] - 2026-08-16
|
|
8
44
|
|
|
9
45
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -17,6 +17,45 @@ This package provides the missing-by-design framework concerns:
|
|
|
17
17
|
- **View Transitions** (`./transitions`) — native View Transitions API shim (Chrome/Edge/Safari 18+); persistent regions via `view-transition-name`. No-op fallback in Firefox.
|
|
18
18
|
- **Head injection** (`./head`) — canonical, og:\*, twitter:\*, robots, preload hints, RSS link, sitemap link, and theme-color output.
|
|
19
19
|
- **SSR-skip wrappers** (`./ssr-skip`) — `<AiChatModalIsland>`, `<ImageEnlargeIsland>`, `<DesignTokenTweakPanelIsland>`, `<MockInitIsland>` — wrap zfb's `<Island ssrFallback>` with the right fallback markup so doc pages don't have to re-implement the SSR-skip pattern.
|
|
20
|
+
- **Site schema** (`./site-schema`) — browser-safe nav tree / breadcrumb / pager domain, with zero zfb engine or filesystem coupling. See below.
|
|
21
|
+
|
|
22
|
+
## `./site-schema` — the browser-safe site-shape domain
|
|
23
|
+
|
|
24
|
+
Everything needed to answer "what is the shape of this documentation site?" — which routes exist, how they nest, what the previous/next page is, and what breadcrumb trail leads to a slug — without any of the rendering, disk access, or zfb engine coupling the rest of the package carries. A browser bundle, a Cloudflare Worker, or any non-zfb tool can compute the same answers the SSG build computes.
|
|
25
|
+
|
|
26
|
+
**What it exports.** The route-existence builder `createDocRouteEntries`; the nav-tree builder `buildNavTree`; the blessed breadcrumb builder `buildBreadcrumbs`; the pager resolver `resolveDocPrevNext`; tree-walking helpers (`findNode`, `firstRoutedHref`, `flattenTree`, `flattenSubtree`, `collectAutoIndexNodes`, `groupSatelliteNodes`, `isNavVisible`, `rewriteNavHref`, `remapNavChildHrefs`); the `headerNav` scoping helpers `getCategoryOrder` / `getNavSectionForSlug` / `getNavSubtree`; the underlying `buildSidebarTree` primitive; the TOC helper `extractHeadings`; and the `schemaVersion` contract constant. Every props/route type is generic over the entry shape (defaulting to a structural `DocEntryLike`) so the subpath never needs to import zfb's `CollectionEntry` — see `API.md` for the full function and type reference.
|
|
27
|
+
|
|
28
|
+
**Derivation contracts** — the rules a consumer relies on, not just the function names:
|
|
29
|
+
|
|
30
|
+
- **Route emission.** An entry with `category_no_page: true` carries category metadata only and emits NO route. Every category with children but no `index.mdx` emits one synthesized auto-index route.
|
|
31
|
+
- **The blessed breadcrumb rule.** `buildBreadcrumbs` is THE route-time slug-split walk that produces `props.breadcrumbs` — the same contract the SSG build uses. The presentation-layer `findPath` / `buildBreadcrumbItems` pair stays a component-side detail and is deliberately NOT exported; consumers reconstruct breadcrumbs through this function, not by re-walking the tree themselves.
|
|
32
|
+
- **Category-scoped prev/next.** `resolveDocPrevNext` resolves prev/next against the route's OWN flattened subtree, not the whole site — so a category's last page has no `next` (and its first page has no `prev`). Frontmatter `pagination_prev` / `pagination_next` overrides resolve against that same caller-supplied tree, never a foreign one.
|
|
33
|
+
|
|
34
|
+
**The `schemaVersion` fail-closed contract.** `schemaVersion` (currently `1`) is a contract-version constant, not a feature flag — check it before trusting the shape of anything else the subpath exports, and fail closed (refuse to render, or warn loudly) rather than silently mis-reading a shape change after a package upgrade. The pattern is deliberately the same one `@takazudo/zudo-doc/catalog` already established for its `ThemePacksIndexManifest.schemaVersion`.
|
|
35
|
+
|
|
36
|
+
**Browser-safety guarantees.** Nothing reachable from `./site-schema` — through the bundled JS graph OR the transitive `.d.ts` graph — may be a `node:*` builtin, `preact`, a `.css` file, a `virtual:` module, or an `@takazudo/zfb*` package. Three guards hold that line:
|
|
37
|
+
|
|
38
|
+
1. `src/__tests__/site-schema.test.ts` bundles the barrel with esbuild `platform: "neutral"` and walks the emitted declaration graph for the same violations.
|
|
39
|
+
2. `scripts/check-site-schema.mjs` repeats the bundle check against the built `dist/site-schema/index.js` in the `prepack` chain, so a publish cannot ship a graph the source-level guard would have rejected.
|
|
40
|
+
3. The `package.json#exports` keyset snapshot in `src/__tests__/public-api-snapshot.test.ts` pins the subpath's presence and shape.
|
|
41
|
+
|
|
42
|
+
**Consumer story.** Import `@takazudo/zudo-doc/site-schema` anywhere you need zudo-doc's site semantics without a zfb build behind you — an SPA shell rendering its own nav chrome, a Worker computing breadcrumbs for an API response, or a script that needs to know what page comes next:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import {
|
|
46
|
+
schemaVersion,
|
|
47
|
+
buildNavTree,
|
|
48
|
+
buildBreadcrumbs,
|
|
49
|
+
resolveDocPrevNext,
|
|
50
|
+
} from "@takazudo/zudo-doc/site-schema";
|
|
51
|
+
|
|
52
|
+
if (schemaVersion !== 1) {
|
|
53
|
+
throw new Error(`Unsupported @takazudo/zudo-doc/site-schema version: ${schemaVersion}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const tree = buildNavTree(docs, "en", categoryMeta, buildHref);
|
|
57
|
+
const breadcrumbs = buildBreadcrumbs(tree, "guides/color", homeHref);
|
|
58
|
+
```
|
|
20
59
|
|
|
21
60
|
## Optional peer dependency: `@takazudo/zfb-md-wasm`
|
|
22
61
|
|
package/dist/chrome/derive.d.ts
CHANGED
|
@@ -45,6 +45,44 @@ export declare function deriveSearchWidgetSlot(ctx: ChromeContext): (props: {
|
|
|
45
45
|
loadingIndexText: string;
|
|
46
46
|
noResultsText: string;
|
|
47
47
|
}) => JSX.Element;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the package-default `DesignTokenPanelBootstrap` must NOT be used as
|
|
50
|
+
* the slot default (#3414, the fix for #3406's entry-page-dependent latch).
|
|
51
|
+
*
|
|
52
|
+
* `runDesignTokenPanelBootstrapOnce` configures zdtp at most once per browser
|
|
53
|
+
* session and both islands share that latch, so on a host that set
|
|
54
|
+
* `designTokenPanelConfigModule` the FIRST page loaded decides the config for
|
|
55
|
+
* the whole SPA session. A self-contained `pages/` stub (the locked-manifest
|
|
56
|
+
* shape, #2653) calls `createChrome` with no hostBindings and would otherwise
|
|
57
|
+
* mount the package default here — hard-load such a page first and the host's
|
|
58
|
+
* builder never applies to any page, silently.
|
|
59
|
+
*
|
|
60
|
+
* Skipping the slot default on exactly that combination makes the configured
|
|
61
|
+
* island the genuine first caller on every session, whatever the entry page.
|
|
62
|
+
* Two deliberate boundaries:
|
|
63
|
+
*
|
|
64
|
+
* - An explicit `ctx.hostBindings.DesignTokenPanelBootstrap` still wins —
|
|
65
|
+
* `deriveBodyEndIslands` consults this only once that slot came back empty.
|
|
66
|
+
* That covers the injected-route path too, where `routes/_chrome.tsx`
|
|
67
|
+
* supplies `ConfiguredDesignTokenPanelBootstrap` through the same slot.
|
|
68
|
+
* - `packageOwnedRoutes: false` is excluded: without the routes plugin there is
|
|
69
|
+
* no configured island anywhere, and `designTokenPanelConfigModule` is inert
|
|
70
|
+
* by its own contract — skipping there would drop the panel from every page
|
|
71
|
+
* rather than defer it to a better-configured one.
|
|
72
|
+
*
|
|
73
|
+
* The slot resolves to `undefined` rather than a null-rendering stub component:
|
|
74
|
+
* `createDesignTokenPanelIsland` already treats an absent component as "render
|
|
75
|
+
* nothing at all", so no `<Island>` marker and no pre-hydration toggle shim
|
|
76
|
+
* reach the HTML. A stub component would emit a marker under a name zfb's
|
|
77
|
+
* island registry does not carry, which zfb reports as an unmatched marker.
|
|
78
|
+
*
|
|
79
|
+
* Exported because `header-with-defaults` must apply the SAME predicate to the
|
|
80
|
+
* `trigger:design-token-panel` header item: a page whose slot resolved to
|
|
81
|
+
* `undefined` mounts no island AND no pre-hydration toggle shim, so a header
|
|
82
|
+
* toggle button rendered there would dispatch into a document with no listener
|
|
83
|
+
* and no click queue — a dead button. The two gates must stay in lockstep.
|
|
84
|
+
*/
|
|
85
|
+
export declare function skipsPackageDefaultDesignTokenPanel(settings: Settings): boolean;
|
|
48
86
|
/**
|
|
49
87
|
* Derive the body-end islands. The package owns the settings-gated
|
|
50
88
|
* DesignTokenPanelBootstrap + ThemePackSwitcher mounts even when a host
|
|
@@ -59,6 +97,14 @@ export declare function deriveSearchWidgetSlot(ctx: ChromeContext): (props: {
|
|
|
59
97
|
* still replace the DTP component through
|
|
60
98
|
* `hostBindings.DesignTokenPanelBootstrap`, but the package remains the sole
|
|
61
99
|
* owner of the mounts and settings gates.
|
|
100
|
+
*
|
|
101
|
+
* Since #3396 the injected package routes use that same slot: `routes/_chrome.tsx`
|
|
102
|
+
* supplies `ConfiguredDesignTokenPanelBootstrap`, which is identical to the
|
|
103
|
+
* default except that its builder comes from
|
|
104
|
+
* `virtual:zudo-doc-design-token-panel-config` (i.e. a host's
|
|
105
|
+
* `designTokenPanelConfigModule`). The default below is what every non-routes
|
|
106
|
+
* `createChrome` caller keeps getting — EXCEPT under the one condition
|
|
107
|
+
* {@link skipsPackageDefaultDesignTokenPanel} describes.
|
|
62
108
|
*/
|
|
63
109
|
export declare function deriveBodyEndIslands(ctx: ChromeContext): (props: import("../doc-body-end-islands/index.js").BodyEndIslandsProps) => JSX.Element;
|
|
64
110
|
/** Derive the DocHistory island: `ctx.hostBindings.DocHistory` when supplied,
|
package/dist/chrome/derive.js
CHANGED
|
@@ -168,9 +168,12 @@ function deriveThemePackSwitcherProps(ctx) {
|
|
|
168
168
|
base: ctx.withBase("/")
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
+
function skipsPackageDefaultDesignTokenPanel(settings) {
|
|
172
|
+
return Boolean(settings.designTokenPanelConfigModule) && settings.packageOwnedRoutes !== false;
|
|
173
|
+
}
|
|
171
174
|
function deriveBodyEndIslands(ctx) {
|
|
172
175
|
const designTokenPanelDeps = {
|
|
173
|
-
DesignTokenPanelBootstrap: ctx.hostBindings.DesignTokenPanelBootstrap ?? DesignTokenPanelBootstrap
|
|
176
|
+
DesignTokenPanelBootstrap: ctx.hostBindings.DesignTokenPanelBootstrap ?? (skipsPackageDefaultDesignTokenPanel(ctx.settings) ? void 0 : DesignTokenPanelBootstrap)
|
|
174
177
|
};
|
|
175
178
|
const themePackSwitcherDeps = {
|
|
176
179
|
themePackSwitcherProps: deriveThemePackSwitcherProps(ctx),
|
|
@@ -311,5 +314,6 @@ export {
|
|
|
311
314
|
deriveInlineVersionSwitcher,
|
|
312
315
|
deriveMdxComponents,
|
|
313
316
|
deriveNavDataPrep,
|
|
314
|
-
deriveSearchWidgetSlot
|
|
317
|
+
deriveSearchWidgetSlot,
|
|
318
|
+
skipsPackageDefaultDesignTokenPanel
|
|
315
319
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FaviconConfig } from "../settings.js";
|
|
2
|
+
/** Structural subset of `Settings` {@link assertNoEmptyStringFaviconOrLogo} reads. */
|
|
3
|
+
export interface EmptyStringFaviconOrLogoSubject {
|
|
4
|
+
logo?: string | false;
|
|
5
|
+
favicon?: string | FaviconConfig | false;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Reject the exact empty string `""` for `logo` and `favicon` — including
|
|
9
|
+
* `favicon`'s object-form slots (`svg`, `ico`, `png32`, `png16`).
|
|
10
|
+
*
|
|
11
|
+
* Per the HTML spec an empty `href` resolves to the CURRENT document, so
|
|
12
|
+
* `favicon: ""` silently emits `<link rel="icon" href="">` — every page
|
|
13
|
+
* tells the browser its own HTML is the favicon, a wasted request per page
|
|
14
|
+
* and a garbage tab icon — and `logo: ""` renders a theme-adaptive CSS mask
|
|
15
|
+
* of an empty path (#3471). `process.env.X ?? ""` is the ordinary way a
|
|
16
|
+
* config value gets wired from the environment, so this is not a contrived
|
|
17
|
+
* shape; failing loudly at config resolution turns a silent per-page waste
|
|
18
|
+
* into a build-time error naming the field, matching this repo's fail-loud
|
|
19
|
+
* habit (`resolveHostModuleOverride` in `plugins/routes.ts` fails at plugin
|
|
20
|
+
* setup rather than falling back silently).
|
|
21
|
+
*
|
|
22
|
+
* Only the EXACT empty string is rejected — whitespace-only (`" "`) does
|
|
23
|
+
* NOT throw. Keep this narrow: #3471's decision covers the one value the
|
|
24
|
+
* HTML spec treats specially, not "falsy-looking" strings in general. Do
|
|
25
|
+
* not tighten this to a `.trim() === ""` check later.
|
|
26
|
+
*
|
|
27
|
+
* No-ops for `false`, `undefined`, `"auto"`, any other non-empty string, and
|
|
28
|
+
* an empty `favicon` OBJECT (`{}`, which validly emits no links — an empty
|
|
29
|
+
* object is not an empty string).
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertNoEmptyStringFaviconOrLogo(settings: EmptyStringFaviconOrLogoSubject): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function assertNoEmptyStringFaviconOrLogo(settings) {
|
|
2
|
+
if (settings.logo === "") {
|
|
3
|
+
throw new TypeError(
|
|
4
|
+
'Invalid logo "": pass false to hide the logo, or omit the field for the default ("auto").'
|
|
5
|
+
);
|
|
6
|
+
}
|
|
7
|
+
if (settings.favicon === "") {
|
|
8
|
+
throw new TypeError(
|
|
9
|
+
'Invalid favicon "": pass false for no favicon links, or omit the field for the default four-link set.'
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
if (typeof settings.favicon === "object" && settings.favicon !== null) {
|
|
13
|
+
for (const slot of ["svg", "ico", "png32", "png16"]) {
|
|
14
|
+
if (settings.favicon[slot] === "") {
|
|
15
|
+
throw new TypeError(
|
|
16
|
+
`Invalid favicon.${slot} "": omit the slot to fall back to the default, or supply a non-empty path.`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
assertNoEmptyStringFaviconOrLogo
|
|
24
|
+
};
|
package/dist/config.d.ts
CHANGED
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* object (e.g. `colorMode`, `metaTags`) REPLACES the default wholesale; it is
|
|
21
21
|
* NOT deep-merged key-by-key. This is safe because every nested config type is
|
|
22
22
|
* all-required-fields, so a caller supplying one supplies all of its fields.
|
|
23
|
+
* ONE exception: `FaviconConfig` is all-optional — wholesale replacement IS its
|
|
24
|
+
* semantics (only the supplied slots emit a `<link rel="icon">`), and its
|
|
25
|
+
* default is `undefined`, so there is nothing for a partial object to clobber.
|
|
23
26
|
* `zudoDoc()` also supplies the Wave-3 package defaults
|
|
24
27
|
* (`buildDocsSchema`/`directiveVocabulary`/`translations`/`colorSchemes`/tag
|
|
25
28
|
* vocabulary) unless overridden, and returns a **complete `ZfbConfig`** — the
|
|
@@ -62,7 +65,7 @@ import type { ZfbConfig, BundleConfig } from "@takazudo/zfb/config";
|
|
|
62
65
|
import type { ZodType } from "zod";
|
|
63
66
|
import type { DirectiveVocabulary, PresetTranslations, PresetTagVocabularyEntry } from "./preset.js";
|
|
64
67
|
import type { ColorScheme } from "./color-scheme-utils.js";
|
|
65
|
-
import type { Settings, ColorModeConfig, LocaleConfig, MetaTagsConfig, SiteHeadConfig, TagPlacement, TagGovernanceMode, VersionConfig, FooterConfig, HeaderNavItem, HeaderRightItem, HomeConfig, FrontmatterPreviewConfig, BodyFootUtilAreaConfig, HtmlPreviewConfig } from "./settings.js";
|
|
68
|
+
import type { Settings, ColorModeConfig, LocaleConfig, MetaTagsConfig, SiteHeadConfig, TagPlacement, TagGovernanceMode, VersionConfig, FooterConfig, HeaderNavItem, HeaderRightItem, HomeConfig, FrontmatterPreviewConfig, BodyFootUtilAreaConfig, HtmlPreviewConfig, FaviconConfig } from "./settings.js";
|
|
66
69
|
/** The `settings.claudeResources` block (or `false` when disabled). */
|
|
67
70
|
type ClaudeResourcesConfig = {
|
|
68
71
|
claudeDir: string;
|
|
@@ -96,10 +99,28 @@ export interface ZudoDocConfig {
|
|
|
96
99
|
* Home-hero logo. `"auto"` renders a generated deterministic SVG seeded by
|
|
97
100
|
* `siteName` (no asset needed); a path string (e.g. `"/img/logo.svg"`)
|
|
98
101
|
* renders that asset as a theme-adaptive CSS mask; `false` hides the logo
|
|
99
|
-
* block entirely.
|
|
102
|
+
* block entirely. The empty string `""` throws a `TypeError` at config
|
|
103
|
+
* resolution instead of silently rendering a mask of an empty path — pass
|
|
104
|
+
* `false` or omit the field instead (#3471).
|
|
100
105
|
* @default "auto"
|
|
101
106
|
*/
|
|
102
107
|
logo?: string | false;
|
|
108
|
+
/**
|
|
109
|
+
* Favicon `<link rel="icon">` set. Omitted keeps the four-file convention
|
|
110
|
+
* (`/favicon.svg`, `/favicon.ico`, `/favicon-32x32.png`,
|
|
111
|
+
* `/favicon-16x16.png` — what `create-zudo-doc` ships in `public/`);
|
|
112
|
+
* `"auto"` emits one inline SVG data-URL icon generated from `siteName`,
|
|
113
|
+
* the same glyph `logo: "auto"` renders; any other string emits a single
|
|
114
|
+
* link with the `type` inferred from its extension; a `FaviconConfig`
|
|
115
|
+
* object emits only the slots it supplies (drop `ico` when your `public/`
|
|
116
|
+
* has no `favicon.ico` and the dead link is never emitted); `false` emits
|
|
117
|
+
* no favicon links at all. The empty string `""` (top-level or any
|
|
118
|
+
* `FaviconConfig` slot) throws a `TypeError` at config resolution instead
|
|
119
|
+
* of silently emitting `<link rel="icon" href="">`, which the HTML spec
|
|
120
|
+
* resolves to the current document — pass `false` or omit instead (#3471).
|
|
121
|
+
* @default undefined
|
|
122
|
+
*/
|
|
123
|
+
favicon?: string | FaviconConfig | false;
|
|
103
124
|
/**
|
|
104
125
|
* Public URL sub-path prefix mounted in front of every absolute asset URL
|
|
105
126
|
* (e.g. `"/pj/my-site/"`). `"/"` = root-mounted.
|
package/dist/config.js
CHANGED
|
@@ -4,6 +4,7 @@ import { defaultDirectiveVocabulary } from "./directive-vocabulary-defaults/inde
|
|
|
4
4
|
import { defaultTranslations } from "./i18n-defaults/index.js";
|
|
5
5
|
import { defaultColorSchemes } from "./color-schemes-defaults/index.js";
|
|
6
6
|
import { assertNoCommaInVersionSlugs } from "./version-availability/index.js";
|
|
7
|
+
import { assertNoEmptyStringFaviconOrLogo } from "./config-assertions/index.js";
|
|
7
8
|
const DEFAULT_SETTINGS = {
|
|
8
9
|
colorScheme: "Default Dark",
|
|
9
10
|
colorMode: {
|
|
@@ -15,6 +16,7 @@ const DEFAULT_SETTINGS = {
|
|
|
15
16
|
siteName: "Docs",
|
|
16
17
|
siteDescription: "",
|
|
17
18
|
logo: "auto",
|
|
19
|
+
favicon: void 0,
|
|
18
20
|
base: "/",
|
|
19
21
|
trailingSlash: false,
|
|
20
22
|
home: { wide: false },
|
|
@@ -102,6 +104,7 @@ function zudoDoc(user = {}) {
|
|
|
102
104
|
} = user;
|
|
103
105
|
const settings = { ...DEFAULT_SETTINGS, ...settingsOverrides };
|
|
104
106
|
assertNoCommaInVersionSlugs(settings.versions);
|
|
107
|
+
assertNoEmptyStringFaviconOrLogo(settings);
|
|
105
108
|
const fragment = zudoDocPreset({
|
|
106
109
|
settings,
|
|
107
110
|
buildDocsSchema: userBuildDocsSchema ?? (() => defaultBuildDocsSchema({
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `dataset` property name of the current-route override — i.e. the
|
|
3
|
+
* `data-zd-current-path` attribute on `<html>`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CURRENT_PATH_DATASET_KEY = "zdCurrentPath";
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the current route. Read order is explicit input → dataset override →
|
|
8
|
+
* `window.location.pathname`; `||` (not `??`) so an empty string at any step
|
|
9
|
+
* falls through to the next source rather than winning.
|
|
10
|
+
*
|
|
11
|
+
* `datasetKey` is a parameter rather than a closed-over reference to
|
|
12
|
+
* {@link CURRENT_PATH_DATASET_KEY} so the function body stays self-contained
|
|
13
|
+
* when {@link CURRENT_PATH_SCRIPT_PRELUDE} serializes it into a client script.
|
|
14
|
+
*
|
|
15
|
+
* Returns `undefined` only when no source resolves (SSR); callers that treat a
|
|
16
|
+
* route as optional must handle that, and a resolved value may still match no
|
|
17
|
+
* route (e.g. the literal "srcdoc").
|
|
18
|
+
*/
|
|
19
|
+
export declare function readCurrentPath(datasetKey: string, explicit?: string): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* JS source declaring `readCurrentPath` and `CURRENT_PATH_DATASET_KEY` inside
|
|
22
|
+
* an inline client script, so a string-embedded reader calls
|
|
23
|
+
* `readCurrentPath(CURRENT_PATH_DATASET_KEY)` with the exact same key the
|
|
24
|
+
* TypeScript call sites use. Splice this into the script body before the first
|
|
25
|
+
* call.
|
|
26
|
+
*/
|
|
27
|
+
export declare const CURRENT_PATH_SCRIPT_PRELUDE: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const CURRENT_PATH_DATASET_KEY = "zdCurrentPath";
|
|
2
|
+
function readCurrentPath(datasetKey, explicit) {
|
|
3
|
+
const override = typeof document === "undefined" ? void 0 : document.documentElement.dataset[datasetKey];
|
|
4
|
+
return explicit || override || (typeof window === "undefined" ? void 0 : window.location.pathname);
|
|
5
|
+
}
|
|
6
|
+
const CURRENT_PATH_SCRIPT_PRELUDE = `var CURRENT_PATH_DATASET_KEY=${JSON.stringify(CURRENT_PATH_DATASET_KEY)};var readCurrentPath=${readCurrentPath.toString()};`;
|
|
7
|
+
export {
|
|
8
|
+
CURRENT_PATH_DATASET_KEY,
|
|
9
|
+
CURRENT_PATH_SCRIPT_PRELUDE,
|
|
10
|
+
readCurrentPath
|
|
11
|
+
};
|
|
@@ -27,27 +27,56 @@
|
|
|
27
27
|
* bootstrapDesignTokenPanel(buildDesignTokenPanelConfig);
|
|
28
28
|
*
|
|
29
29
|
* `DesignTokenPanelBootstrap` (below) is the PACKAGE-OWNED island component
|
|
30
|
-
* that wires the mode-scoped builder for
|
|
31
|
-
* config file — the #2658 "Approach (a)" package default.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* `
|
|
35
|
-
* `@takazudo/zudo-doc/design-token-panel-config`'s `buildDesignTokenPanelConfig`.
|
|
30
|
+
* that wires the mode-scoped builder for every `createChrome` consumer with no
|
|
31
|
+
* host config file — the #2658 "Approach (a)" package default. Since #3396 it
|
|
32
|
+
* binds the package-default builder DIRECTLY
|
|
33
|
+
* (`./design-token-panel-config/index.js`), not the routes plugin's
|
|
34
|
+
* `virtual:zudo-doc-design-token-panel-config` module.
|
|
36
35
|
* `packages/zudo-doc/src/chrome/derive.tsx` statically imports it so zfb's
|
|
37
36
|
* island scanner walks route → chrome → derive → here (mirrors the DocHistory
|
|
38
37
|
* #2480 static-import contract). A host that ejects entirely can call
|
|
39
|
-
* `bootstrapDesignTokenPanel` with its own builder
|
|
40
|
-
* routes
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
38
|
+
* `bootstrapDesignTokenPanel` with its own builder. Package-owned INJECTED
|
|
39
|
+
* routes do not render this component — they render the configured wrapper
|
|
40
|
+
* described below — but every other `createChrome` caller does.
|
|
41
|
+
*
|
|
42
|
+
* COUPLING REMOVED (#3396, epic #3394): this module no longer imports any
|
|
43
|
+
* `virtual:` specifier, so `@takazudo/zudo-doc/chrome` (which reaches here via
|
|
44
|
+
* `chrome/derive.tsx`) bundles OUTSIDE a zfb build with zero shims, and a
|
|
45
|
+
* `packageOwnedRoutes: false` host needs no alias for the config specifier.
|
|
46
|
+
*
|
|
47
|
+
* WHERE THE HOST OVERRIDE ENTERS NOW: `settings.designTokenPanelConfigModule`
|
|
48
|
+
* still travels through `virtual:zudo-doc-design-token-panel-config`, but the
|
|
49
|
+
* ONLY module that imports that specifier is the routes-only wrapper
|
|
50
|
+
* `routes/_design-token-panel-bootstrap.tsx`. It calls
|
|
51
|
+
* {@link runDesignTokenPanelBootstrapOnce} with the resolved builder and is
|
|
52
|
+
* threaded into `hostBindings.DesignTokenPanelBootstrap` by `routes/_chrome.tsx`,
|
|
53
|
+
* so `deriveBodyEndIslands` renders it instead of the package default on
|
|
54
|
+
* injected routes. The wrapper is a component-level seam INSIDE the client
|
|
55
|
+
* island bundle — deliberately NOT a module-level setter: the route/SSR graph
|
|
56
|
+
* and the hydrated island bundle are separate module graphs, so a setter called
|
|
57
|
+
* during route evaluation would configure only the server instance while the
|
|
58
|
+
* client re-evaluated this module and kept the default.
|
|
59
|
+
*
|
|
60
|
+
* SELF-CONTAINED `pages/` STUBS (was a KNOWN GAP in #3396; closed by #3414).
|
|
61
|
+
* A host that renders docs through a self-contained `pages/` stub (the
|
|
62
|
+
* locked-manifest shape, #2653 — it calls `createChrome` directly rather than
|
|
63
|
+
* going through `routes/_chrome.tsx`) reaches the package default here, so
|
|
64
|
+
* `designTokenPanelConfigModule` does not apply to those pages. Because
|
|
65
|
+
* {@link runDesignTokenPanelBootstrapOnce}'s latch is per SESSION, not per
|
|
66
|
+
* page, that used to make the whole SPA session's panel config depend on which
|
|
67
|
+
* page was hard-loaded first (#3406). Two things now bound it:
|
|
68
|
+
*
|
|
69
|
+
* - `chrome/derive.tsx` SKIPS this package default as the slot default when
|
|
70
|
+
* `settings.designTokenPanelConfigModule` is set and the host supplied no
|
|
71
|
+
* explicit `chromeBindings.DesignTokenPanelBootstrap` (and
|
|
72
|
+
* `packageOwnedRoutes` is on). Stub-rendered pages then mount NO panel island
|
|
73
|
+
* at all, so they can no longer win the latch with the wrong builder — the
|
|
74
|
+
* configured island is the genuine first caller whatever the entry page. The
|
|
75
|
+
* absence on stub pages is deliberate; such a host threads its own builder
|
|
76
|
+
* via `chromeBindings.DesignTokenPanelBootstrap` to get a panel there too.
|
|
77
|
+
* - Any residual default-then-configured race that skip does not close warns at
|
|
78
|
+
* runtime, naming both the setting and that workaround — see
|
|
79
|
+
* {@link runDesignTokenPanelBootstrapOnce}.
|
|
51
80
|
*
|
|
52
81
|
* Package-first wiring landed in S9a zudolab/zudo-doc#2333; mode-scoped rebuild
|
|
53
82
|
* wiring was added in zudolab/zudo-doc#2610 and the package-default island in
|
|
@@ -103,20 +132,57 @@ export declare function withPackScopedStoragePrefix(config: PanelConfig, activeP
|
|
|
103
132
|
* mode-scoped rebuilds.
|
|
104
133
|
*/
|
|
105
134
|
export declare function bootstrapDesignTokenPanel(buildConfig: PanelConfigBuilder): void;
|
|
135
|
+
/**
|
|
136
|
+
* Which island won the once-latch below (#3414).
|
|
137
|
+
*
|
|
138
|
+
* - `"default"` — the package-default {@link DesignTokenPanelBootstrap}, bound
|
|
139
|
+
* to the package-default builder.
|
|
140
|
+
* - `"configured"` — the routes-only wrapper
|
|
141
|
+
* (`routes/_design-token-panel-bootstrap.tsx`), bound to whatever
|
|
142
|
+
* `virtual:zudo-doc-design-token-panel-config` resolved to (a host's
|
|
143
|
+
* `settings.designTokenPanelConfigModule` when set, the package default
|
|
144
|
+
* otherwise).
|
|
145
|
+
*/
|
|
146
|
+
export type DesignTokenPanelBootstrapOrigin = "default" | "configured";
|
|
147
|
+
/**
|
|
148
|
+
* Run {@link bootstrapDesignTokenPanel} at most once per module instance, with
|
|
149
|
+
* whichever mode-scoped builder the caller supplies.
|
|
150
|
+
*
|
|
151
|
+
* Exists so the routes-only configured wrapper
|
|
152
|
+
* (`routes/_design-token-panel-bootstrap.tsx`, #3396) gets the same one-shot
|
|
153
|
+
* semantics as {@link DesignTokenPanelBootstrap} while supplying the
|
|
154
|
+
* host-overridable builder from `virtual:zudo-doc-design-token-panel-config`.
|
|
155
|
+
* The guard is deliberately shared across both callers: `deriveBodyEndIslands`
|
|
156
|
+
* renders exactly ONE of them, and if a host ever mounted both, configuring the
|
|
157
|
+
* panel twice would re-mount it rather than layer the two configs.
|
|
158
|
+
*
|
|
159
|
+
* `origin` tags WHICH island is calling (#3414). It is optional, defaulting to
|
|
160
|
+
* `"default"`, because this function is reachable from the frozen
|
|
161
|
+
* `./design-token-panel-bootstrap` subpath (API.md) — an added required
|
|
162
|
+
* parameter would break an ejected host that calls it. Both in-package callers
|
|
163
|
+
* pass it explicitly.
|
|
164
|
+
*
|
|
165
|
+
* This is a CALL, not a module-level setter — the builder is passed in at
|
|
166
|
+
* render time inside whichever bundle the island actually hydrates in, so it
|
|
167
|
+
* never has to cross the route-graph → island-bundle boundary.
|
|
168
|
+
*/
|
|
169
|
+
export declare function runDesignTokenPanelBootstrapOnce(build: PanelConfigBuilder, origin?: DesignTokenPanelBootstrapOrigin): void;
|
|
106
170
|
/**
|
|
107
171
|
* The package-default `<DesignTokenPanelBootstrap/>` island: mounted (via
|
|
108
172
|
* `Island({ when: "load" })`, no `ssrFallback` — it renders nothing on either
|
|
109
173
|
* side) by `../doc-body-end-islands/index.tsx` when `settings.designTokenPanel`
|
|
110
174
|
* is on, gated the same way `AiChatModal`/`ImageEnlarge`/`MermaidEnlarge` are.
|
|
111
|
-
* On first render, calls `bootstrapDesignTokenPanel` with the
|
|
112
|
-
* `buildDesignTokenPanelConfig` builder
|
|
113
|
-
* `
|
|
114
|
-
*
|
|
175
|
+
* On first render, calls `bootstrapDesignTokenPanel` with the PACKAGE-DEFAULT
|
|
176
|
+
* mode-scoped `buildDesignTokenPanelConfig` builder. A host's
|
|
177
|
+
* `designTokenPanelConfigModule` override does not reach this component — it
|
|
178
|
+
* arrives through the routes-only wrapper described in the module docblock.
|
|
115
179
|
*
|
|
116
180
|
* `displayName` pinned explicitly (belt-and-braces, matching
|
|
117
181
|
* `AiChatModal`/`ImageEnlarge`/`MermaidEnlarge`/`DocHistory`) so zfb's
|
|
118
182
|
* `captureComponentName` emits a stable `data-zfb-island="DesignTokenPanelBootstrap"`
|
|
119
|
-
* marker independent of minification.
|
|
183
|
+
* marker independent of minification. It MUST keep matching the exported
|
|
184
|
+
* binding identifier — zfb's island scanner registers by the scanner-visible
|
|
185
|
+
* export name and warns when a `displayName` diverges from it.
|
|
120
186
|
*/
|
|
121
187
|
export declare function DesignTokenPanelBootstrap(): JSX.Element | null;
|
|
122
188
|
export declare namespace DesignTokenPanelBootstrap {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
THEME_PACK_CHANGED_EVENT,
|
|
9
9
|
readThemePackFromDom
|
|
10
10
|
} from "./theme-pack-switcher/theme-pack-sync.js";
|
|
11
|
-
import { buildDesignTokenPanelConfig } from "
|
|
11
|
+
import { buildDesignTokenPanelConfig } from "./design-token-panel-config/index.js";
|
|
12
12
|
const COLOR_SCHEME_CHANGED_EVENT = "color-scheme-changed";
|
|
13
13
|
const TOGGLE_PANEL_EVENT = "toggle-design-token-panel";
|
|
14
14
|
const ZDTP_DEFAULT_STORAGE_PREFIX = "zudo-design-token-panel";
|
|
@@ -262,17 +262,30 @@ function bootstrapDesignTokenPanel(buildConfig) {
|
|
|
262
262
|
refreshInstanceToggleChannel(readActiveConfig());
|
|
263
263
|
});
|
|
264
264
|
}
|
|
265
|
-
let
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
265
|
+
let bootstrapWinner = null;
|
|
266
|
+
let warnedLostRace = false;
|
|
267
|
+
function warnLostBootstrapRace(winnerOrigin, loserOrigin) {
|
|
268
|
+
if (typeof window === "undefined" || warnedLostRace) return;
|
|
269
|
+
warnedLostRace = true;
|
|
270
|
+
const detail = loserOrigin === "configured" && winnerOrigin === "default" ? "The package-default island configured the panel earlier in this browser session, so `designTokenPanelConfigModule` did not apply on this page. This happens when the session first hard-loaded a page rendered by a self-contained `pages/` stub, which reaches the package default instead of the package-injected route. Thread your builder through `chromeBindings.DesignTokenPanelBootstrap` to make it apply on every page regardless of entry point." : `The ${winnerOrigin} island configured the panel first; this ${loserOrigin} island's builder was ignored. zdtp is configured once per session \u2014 mount exactly one design-token-panel bootstrap, e.g. via \`chromeBindings.DesignTokenPanelBootstrap\`.`;
|
|
271
|
+
console.warn(`[zudo-doc] design-token panel already configured. ${detail}`);
|
|
272
|
+
}
|
|
273
|
+
function runDesignTokenPanelBootstrapOnce(build, origin = "default") {
|
|
274
|
+
if (bootstrapWinner) {
|
|
275
|
+
if (bootstrapWinner.build !== build) warnLostBootstrapRace(bootstrapWinner.origin, origin);
|
|
276
|
+
return;
|
|
270
277
|
}
|
|
278
|
+
bootstrapWinner = { origin, build };
|
|
279
|
+
bootstrapDesignTokenPanel(build);
|
|
280
|
+
}
|
|
281
|
+
function DesignTokenPanelBootstrap() {
|
|
282
|
+
runDesignTokenPanelBootstrapOnce(buildDesignTokenPanelConfig, "default");
|
|
271
283
|
return null;
|
|
272
284
|
}
|
|
273
285
|
DesignTokenPanelBootstrap.displayName = "DesignTokenPanelBootstrap";
|
|
274
286
|
export {
|
|
275
287
|
DesignTokenPanelBootstrap,
|
|
276
288
|
bootstrapDesignTokenPanel,
|
|
289
|
+
runDesignTokenPanelBootstrapOnce,
|
|
277
290
|
withPackScopedStoragePrefix
|
|
278
291
|
};
|
|
@@ -1,77 +1,15 @@
|
|
|
1
1
|
export type { HeadingItem } from "../extract-headings/index.js";
|
|
2
|
-
import type { HeadingItem } from "../extract-headings/index.js";
|
|
3
|
-
import type { BreadcrumbItem } from "../breadcrumb/types.js";
|
|
4
2
|
import type { CollectionEntry } from "@takazudo/zfb/content";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* Structurally identical to the host project's `NavNode` from
|
|
9
|
-
* `src/utils/docs.ts`. Defined here so the package types do not import
|
|
10
|
-
* the host `@/` alias. The host's `NavNode` is a structural subtype and
|
|
11
|
-
* assignable wherever `DocNavNode` is expected.
|
|
12
|
-
*/
|
|
13
|
-
export interface DocNavNode {
|
|
14
|
-
slug: string;
|
|
15
|
-
label: string;
|
|
16
|
-
description?: string;
|
|
17
|
-
position: number;
|
|
18
|
-
href?: string;
|
|
19
|
-
hasPage: boolean;
|
|
20
|
-
children: DocNavNode[];
|
|
21
|
-
sortOrder?: "asc" | "desc";
|
|
22
|
-
collapsed?: boolean;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Minimal docs frontmatter shape consumed by the doc-page types.
|
|
26
|
-
*
|
|
27
|
-
* A structural subset of the host's `DocsData` — only the fields that
|
|
28
|
-
* `DocPageEntry` and `DocPageBaseProps` actually read. The full `DocsData`
|
|
29
|
-
* (from `src/config/docs-schema.ts`) is a structural supertype and is
|
|
30
|
-
* assignable here.
|
|
31
|
-
*/
|
|
32
|
-
export interface DocPageFrontmatter {
|
|
33
|
-
title: string;
|
|
34
|
-
description?: string;
|
|
35
|
-
slug?: string;
|
|
36
|
-
draft?: boolean;
|
|
37
|
-
unlisted?: boolean;
|
|
38
|
-
standalone?: boolean;
|
|
39
|
-
sidebar_position?: number;
|
|
40
|
-
sidebar_label?: string;
|
|
41
|
-
category_no_page?: boolean;
|
|
42
|
-
category_sort_order?: "asc" | "desc";
|
|
43
|
-
pagination_prev?: string | null;
|
|
44
|
-
pagination_next?: string | null;
|
|
45
|
-
tags?: string[];
|
|
46
|
-
[key: string]: unknown;
|
|
47
|
-
}
|
|
3
|
+
import type { AutoIndexNode, DocNavNode, DocPageAutoIndexProps as DocPageAutoIndexPropsBase, DocPageBaseProps as DocPageBasePropsGeneric, DocPageEntryProps as DocPageEntryPropsGeneric, DocPageFrontmatter } from "../site-schema/types.js";
|
|
4
|
+
export type { AutoIndexNode, DocNavNode, DocPageFrontmatter };
|
|
48
5
|
/**
|
|
49
6
|
* The native zfb collection entry with docs frontmatter. Route consumers
|
|
50
7
|
* derive canonical route slugs from `entry.data.slug ?? toRouteSlug(entry.slug)`.
|
|
51
8
|
*/
|
|
52
9
|
export type DocPageEntry = CollectionEntry<DocPageFrontmatter>;
|
|
53
|
-
export interface AutoIndexNode extends DocNavNode {
|
|
54
|
-
children: DocNavNode[];
|
|
55
|
-
}
|
|
56
|
-
/** Shared fields present in every doc-page route. */
|
|
57
|
-
interface DocPagePropsBase {
|
|
58
|
-
breadcrumbs: BreadcrumbItem[];
|
|
59
|
-
prev: DocNavNode | null;
|
|
60
|
-
next: DocNavNode | null;
|
|
61
|
-
/** Depth-2/3/4 headings extracted from the MDX body, for SSG TOC links. */
|
|
62
|
-
headings: HeadingItem[];
|
|
63
|
-
}
|
|
64
10
|
/** Branch: a real content entry. `autoIndex` is absent. */
|
|
65
|
-
export
|
|
66
|
-
kind: "entry";
|
|
67
|
-
entry: DocPageEntry;
|
|
68
|
-
autoIndex?: undefined;
|
|
69
|
-
}
|
|
11
|
+
export type DocPageEntryProps = DocPageEntryPropsGeneric<DocPageEntry>;
|
|
70
12
|
/** Branch: an auto-generated category index. `entry` is absent. */
|
|
71
|
-
export
|
|
72
|
-
kind: "autoIndex";
|
|
73
|
-
autoIndex: AutoIndexNode;
|
|
74
|
-
entry?: undefined;
|
|
75
|
-
}
|
|
13
|
+
export type DocPageAutoIndexProps = DocPageAutoIndexPropsBase;
|
|
76
14
|
/** Discriminated union for the `kind` prop. Narrow via `props.kind === "entry"`. */
|
|
77
|
-
export type DocPageBaseProps =
|
|
15
|
+
export type DocPageBaseProps = DocPageBasePropsGeneric<DocPageEntry>;
|