@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
|
@@ -9,6 +9,7 @@ import { smartBreakToHtml } from "../smart-break/index.js";
|
|
|
9
9
|
import { AFTER_NAVIGATE_EVENT, BEFORE_NAVIGATE_EVENT } from "../transitions/index.js";
|
|
10
10
|
import { filterTree } from "../sidebar-filter/index.js";
|
|
11
11
|
import { findActiveSlug, normalizePath } from "../sidebar-active-slug/index.js";
|
|
12
|
+
import { CURRENT_PATH_DATASET_KEY, readCurrentPath } from "../current-path/index.js";
|
|
12
13
|
import { ensureSidebarScrollPreserve } from "./sidebar-scroll-preserve.js";
|
|
13
14
|
ensureSidebarScrollPreserve();
|
|
14
15
|
function ToggleChevron({ isExpanded, className }) {
|
|
@@ -40,24 +41,25 @@ function saveOpenSet(set) {
|
|
|
40
41
|
} catch {
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return findActiveSlug(nodes,
|
|
44
|
+
function deriveActiveSlug(nodes, pathname) {
|
|
45
|
+
const resolved = readCurrentPath(CURRENT_PATH_DATASET_KEY, pathname);
|
|
46
|
+
if (!resolved) return void 0;
|
|
47
|
+
return findActiveSlug(nodes, normalizePath(resolved));
|
|
47
48
|
}
|
|
48
|
-
function useActiveSlug(nodes, initial) {
|
|
49
|
+
function useActiveSlug(nodes, initial, currentPath) {
|
|
49
50
|
const [slug, setSlug] = useState(
|
|
50
|
-
() => initial !== void 0 ? initial :
|
|
51
|
+
() => initial !== void 0 ? initial : deriveActiveSlug(nodes, currentPath)
|
|
51
52
|
);
|
|
52
53
|
useEffect(() => {
|
|
53
|
-
const update = () => {
|
|
54
|
-
const found =
|
|
54
|
+
const update = (pathname) => {
|
|
55
|
+
const found = deriveActiveSlug(nodes, pathname);
|
|
55
56
|
if (found !== void 0) setSlug(found);
|
|
56
57
|
};
|
|
57
|
-
update();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
const onNavigate = () => update();
|
|
59
|
+
update(currentPath);
|
|
60
|
+
document.addEventListener(AFTER_NAVIGATE_EVENT, onNavigate);
|
|
61
|
+
return () => document.removeEventListener(AFTER_NAVIGATE_EVENT, onNavigate);
|
|
62
|
+
}, [nodes, currentPath]);
|
|
61
63
|
return slug;
|
|
62
64
|
}
|
|
63
65
|
function RootMenuItemEntry({ item }) {
|
|
@@ -112,8 +114,8 @@ function SidebarFooter({ links, themeDefaultMode }) {
|
|
|
112
114
|
] })
|
|
113
115
|
);
|
|
114
116
|
}
|
|
115
|
-
function SidebarTree({ nodes, currentSlug, rootMenuItems, backToMenuLabel, localeLinks, themeDefaultMode }) {
|
|
116
|
-
const activeSlug = useActiveSlug(nodes, currentSlug);
|
|
117
|
+
function SidebarTree({ nodes, currentSlug, currentPath, rootMenuItems, backToMenuLabel, localeLinks, themeDefaultMode }) {
|
|
118
|
+
const activeSlug = useActiveSlug(nodes, currentSlug, currentPath);
|
|
117
119
|
const [query, setQuery] = useState("");
|
|
118
120
|
const [showingRootMenu, setShowingRootMenu] = useState(false);
|
|
119
121
|
const filterRef = useRef(null);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { AutoIndexNode, CategoryMeta, DocEntryLike, DocNavNode, DocPageBaseProps, HeadingItem, NavSourceDocs } from "./types.js";
|
|
2
|
+
/** One enumerated doc route: a content entry or an auto-generated category
|
|
3
|
+
* index, with all per-page derived data pre-computed. */
|
|
4
|
+
export interface DocRouteEntry<E extends DocEntryLike = DocEntryLike> {
|
|
5
|
+
/** Canonical route slug ("" for the docs root index — #1891). */
|
|
6
|
+
slug: string;
|
|
7
|
+
/** Optional-catchall params array — `toSlugParams(slug)` ([] for the root). */
|
|
8
|
+
slugParams: string[];
|
|
9
|
+
/**
|
|
10
|
+
* True when the entry came from the base collection rather than the locale
|
|
11
|
+
* collection (`!localeSlugSet.has(slug)`). Only meaningful on routes whose
|
|
12
|
+
* nav source performs a locale merge — routes without one (default-locale /
|
|
13
|
+
* versioned-EN, where `localeSlugSet` is empty) must ignore this field.
|
|
14
|
+
* Always false for autoIndex items.
|
|
15
|
+
*/
|
|
16
|
+
isFallback: boolean;
|
|
17
|
+
/** Shared page props (kind/entry/autoIndex/breadcrumbs/prev/next/headings). */
|
|
18
|
+
props: DocPageBaseProps<E>;
|
|
19
|
+
}
|
|
20
|
+
export interface BuildDocRouteEntriesArgs<E extends DocEntryLike = DocEntryLike> {
|
|
21
|
+
/** Identity-stable nav source for this route's (locale, version) context. */
|
|
22
|
+
source: NavSourceDocs<E>;
|
|
23
|
+
/** Active locale for nav-tree labels and breadcrumbs. */
|
|
24
|
+
locale: string;
|
|
25
|
+
/**
|
|
26
|
+
* Unique memo signature for this route context. Each route file passes its
|
|
27
|
+
* own prefix plus the loop variables (version slug / locale), e.g.
|
|
28
|
+
* "docs;en", "locale-docs;ja", "v-docs;1.0", "v-locale-docs;1.0;ja" —
|
|
29
|
+
* call sites that share a docs array identity must never collide on a key.
|
|
30
|
+
*/
|
|
31
|
+
routeSig: string;
|
|
32
|
+
/** Versioned URL closure bound to the route's version (+ locale). Presence
|
|
33
|
+
* switches the versioned behaviors (breadcrumbs, prev/next, child hrefs). */
|
|
34
|
+
urlFor?: (slug: string) => string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Context slice `createDocRouteEntries` derives its bag from (epic Collapse
|
|
38
|
+
* Wiring Shells #2420, FACTORIES #2424 — breaking signature). STRUCTURAL SUBSET
|
|
39
|
+
* of the unified `RouteContext`/`ChromeContext` — the nav-tree builder is
|
|
40
|
+
* reconstructed by binding the context's 4-arg `buildNavTree` to `docsUrl`, and
|
|
41
|
+
* the breadcrumb trail by binding the package `buildBreadcrumbs` to the
|
|
42
|
+
* locale-prefixed `withBase` base, exactly as the pre-collapse wiring did.
|
|
43
|
+
*/
|
|
44
|
+
export interface DocRouteEntriesContext<E extends DocEntryLike = DocEntryLike> {
|
|
45
|
+
/** Default locale code (drives the breadcrumb base-prefix selection). */
|
|
46
|
+
defaultLocale: string;
|
|
47
|
+
/** Build the nav tree for a locale (4-arg form with an explicit href builder). */
|
|
48
|
+
buildNavTree: (docs: E[], locale: string, categoryMeta: Map<string, CategoryMeta> | undefined, buildHref: (slug: string, locale: string) => string) => DocNavNode[];
|
|
49
|
+
/** Build a docs URL for the given slug and locale. */
|
|
50
|
+
docsUrl: (slug: string, locale?: string) => string;
|
|
51
|
+
/** Prefix a path with the configured base directory. */
|
|
52
|
+
withBase: (path: string) => string;
|
|
53
|
+
/** Collect all auto-generated index nodes (categories without index.mdx). */
|
|
54
|
+
collectAutoIndexNodes: (tree: DocNavNode[]) => AutoIndexNode[];
|
|
55
|
+
/** Determine the nav section (categoryMatch) for a slug. */
|
|
56
|
+
getNavSectionForSlug: (slug: string) => string | undefined;
|
|
57
|
+
/** Filter top-level nav nodes by a categoryMatch value. */
|
|
58
|
+
getNavSubtree: (tree: DocNavNode[], categoryMatch?: string) => DocNavNode[];
|
|
59
|
+
/** Convert a content entry slug to a canonical route slug. */
|
|
60
|
+
toRouteSlug: (entrySlug: string) => string;
|
|
61
|
+
/** Convert a canonical route slug to an optional-catchall params array. */
|
|
62
|
+
toSlugParams: (slug: string) => string[];
|
|
63
|
+
/** Extract headings from a raw MDX body (bound to settings depth/strategy). */
|
|
64
|
+
extractHeadings: (body: string) => HeadingItem[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Result of `createDocRouteEntries`. Contains the `buildDocRouteEntries`
|
|
68
|
+
* function bound to the injected context.
|
|
69
|
+
*/
|
|
70
|
+
export interface DocRouteEntriesAPI<E extends DocEntryLike = DocEntryLike> {
|
|
71
|
+
/**
|
|
72
|
+
* Enumerate all doc routes for one (locale, version) context, with per-entry
|
|
73
|
+
* derived data pre-computed. Memoized per build on the identity-stable
|
|
74
|
+
* `source.docs` array.
|
|
75
|
+
*/
|
|
76
|
+
buildDocRouteEntries: (args: BuildDocRouteEntriesArgs<E>) => DocRouteEntry<E>[];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create the `buildDocRouteEntries` function bound to the host's injected
|
|
80
|
+
* context.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* import { createDocRouteEntries } from "@takazudo/zudo-doc/site-schema";
|
|
85
|
+
*
|
|
86
|
+
* export const { buildDocRouteEntries } = createDocRouteEntries(routeContext);
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function createDocRouteEntries<E extends DocEntryLike = DocEntryLike>(ctx: DocRouteEntriesContext<E>): DocRouteEntriesAPI<E>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { memoizeDerived } from "../nav-source-cache/index.js";
|
|
2
|
+
import {
|
|
3
|
+
resolveDocPrevNext,
|
|
4
|
+
flattenSubtree,
|
|
5
|
+
rewriteNavHref,
|
|
6
|
+
remapNavChildHrefs
|
|
7
|
+
} from "../doc-route-paths/index.js";
|
|
8
|
+
import { buildBreadcrumbs as buildBreadcrumbsImpl } from "./nav-tree.js";
|
|
9
|
+
let nextFactoryId = 1;
|
|
10
|
+
function createDocRouteEntries(ctx) {
|
|
11
|
+
const factoryId = nextFactoryId++;
|
|
12
|
+
const defaultLocale = ctx.defaultLocale;
|
|
13
|
+
const buildNavTree = (docs, locale, categoryMeta) => ctx.buildNavTree(docs, locale, categoryMeta, (slug, loc) => ctx.docsUrl(slug, loc));
|
|
14
|
+
const buildBreadcrumbs = (tree, slug, locale, urlFor) => buildBreadcrumbsImpl(
|
|
15
|
+
tree,
|
|
16
|
+
slug,
|
|
17
|
+
locale === defaultLocale ? ctx.withBase("/") : ctx.withBase(`/${locale}/`),
|
|
18
|
+
urlFor
|
|
19
|
+
);
|
|
20
|
+
const collectAutoIndexNodes = ctx.collectAutoIndexNodes;
|
|
21
|
+
const getNavSectionForSlug = ctx.getNavSectionForSlug;
|
|
22
|
+
const getNavSubtree = ctx.getNavSubtree;
|
|
23
|
+
const toRouteSlug = ctx.toRouteSlug;
|
|
24
|
+
const toSlugParams = ctx.toSlugParams;
|
|
25
|
+
const extractHeadings = ctx.extractHeadings;
|
|
26
|
+
function buildDocRouteEntries(args) {
|
|
27
|
+
const { source, locale, routeSig, urlFor } = args;
|
|
28
|
+
return memoizeDerived([source.docs], `docRouteEntries;${factoryId};${routeSig}`, () => {
|
|
29
|
+
const { docs, navDocs, categoryMeta, localeSlugSet } = source;
|
|
30
|
+
const tree = buildNavTree(navDocs, locale, categoryMeta);
|
|
31
|
+
const breadcrumbTree = urlFor ? tree : buildNavTree(docs, locale, categoryMeta);
|
|
32
|
+
const result = [];
|
|
33
|
+
for (const entry of docs) {
|
|
34
|
+
if (entry.data.category_no_page === true) continue;
|
|
35
|
+
const slug = entry.data.slug ?? toRouteSlug(entry.slug);
|
|
36
|
+
const navSection = getNavSectionForSlug(slug);
|
|
37
|
+
const subtree = getNavSubtree(tree, navSection);
|
|
38
|
+
const { prev: prevNode, next: nextNode } = resolveDocPrevNext(
|
|
39
|
+
tree,
|
|
40
|
+
flattenSubtree(subtree),
|
|
41
|
+
slug,
|
|
42
|
+
entry.data
|
|
43
|
+
);
|
|
44
|
+
result.push({
|
|
45
|
+
slug,
|
|
46
|
+
slugParams: toSlugParams(slug),
|
|
47
|
+
isFallback: !localeSlugSet.has(slug),
|
|
48
|
+
props: {
|
|
49
|
+
kind: "entry",
|
|
50
|
+
entry,
|
|
51
|
+
breadcrumbs: buildBreadcrumbs(breadcrumbTree, slug, locale, urlFor),
|
|
52
|
+
prev: rewriteNavHref(prevNode, urlFor),
|
|
53
|
+
next: rewriteNavHref(nextNode, urlFor),
|
|
54
|
+
headings: extractHeadings(entry.body ?? "")
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const node of collectAutoIndexNodes(tree)) {
|
|
59
|
+
result.push({
|
|
60
|
+
slug: node.slug,
|
|
61
|
+
slugParams: toSlugParams(node.slug),
|
|
62
|
+
isFallback: false,
|
|
63
|
+
props: {
|
|
64
|
+
kind: "autoIndex",
|
|
65
|
+
autoIndex: urlFor ? {
|
|
66
|
+
...node,
|
|
67
|
+
children: remapNavChildHrefs(node.children, urlFor)
|
|
68
|
+
} : node,
|
|
69
|
+
breadcrumbs: buildBreadcrumbs(breadcrumbTree, node.slug, locale, urlFor),
|
|
70
|
+
prev: null,
|
|
71
|
+
next: null,
|
|
72
|
+
headings: []
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return { buildDocRouteEntries };
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
createDocRouteEntries
|
|
83
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract version of this module's shape. Bump on any breaking change to the
|
|
3
|
+
* exported types or functions so a consumer can fail closed rather than
|
|
4
|
+
* silently mis-read a newer schema (`@takazudo/zudo-doc/catalog` precedent).
|
|
5
|
+
*/
|
|
6
|
+
export declare const schemaVersion = 1;
|
|
7
|
+
export type { AutoIndexNode, BreadcrumbItem, CategoryMeta, CollectionEntryLike, DocEntryLike, DocNavNode, DocPageAutoIndexProps, DocPageBaseProps, DocPageEntryProps, DocPageFrontmatter, HeadingItem, NavSourceDocs, SidebarFrontmatter, SidebarNode, } from "./types.js";
|
|
8
|
+
export type { BuildDocRouteEntriesArgs, DocRouteEntriesAPI, DocRouteEntriesContext, DocRouteEntry, } from "./doc-route-entries.js";
|
|
9
|
+
export type { BuildHref } from "./nav-tree.js";
|
|
10
|
+
export type { PaginationOverrides } from "../doc-route-paths/index.js";
|
|
11
|
+
export { buildBreadcrumbs, buildNavTree, collectAutoIndexNodes, findNode, firstRoutedHref, groupSatelliteNodes, isNavVisible, } from "./nav-tree.js";
|
|
12
|
+
export { flattenSubtree, flattenTree, remapNavChildHrefs, resolveDocPrevNext, rewriteNavHref, } from "../doc-route-paths/index.js";
|
|
13
|
+
export { getCategoryOrder, getNavSectionForSlug, getNavSubtree, } from "../nav-scope/index.js";
|
|
14
|
+
export type { NavScopeHeaderNavItem, NavScopeNode } from "../nav-scope/index.js";
|
|
15
|
+
export { buildSidebarTree } from "../sidebar-tree/build-tree.js";
|
|
16
|
+
export { extractHeadings } from "../extract-headings/index.js";
|
|
17
|
+
export { createDocRouteEntries } from "./doc-route-entries.js";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const schemaVersion = 1;
|
|
2
|
+
import {
|
|
3
|
+
buildBreadcrumbs,
|
|
4
|
+
buildNavTree,
|
|
5
|
+
collectAutoIndexNodes,
|
|
6
|
+
findNode,
|
|
7
|
+
firstRoutedHref,
|
|
8
|
+
groupSatelliteNodes,
|
|
9
|
+
isNavVisible
|
|
10
|
+
} from "./nav-tree.js";
|
|
11
|
+
import {
|
|
12
|
+
flattenSubtree,
|
|
13
|
+
flattenTree,
|
|
14
|
+
remapNavChildHrefs,
|
|
15
|
+
resolveDocPrevNext,
|
|
16
|
+
rewriteNavHref
|
|
17
|
+
} from "../doc-route-paths/index.js";
|
|
18
|
+
import {
|
|
19
|
+
getCategoryOrder,
|
|
20
|
+
getNavSectionForSlug,
|
|
21
|
+
getNavSubtree
|
|
22
|
+
} from "../nav-scope/index.js";
|
|
23
|
+
import { buildSidebarTree } from "../sidebar-tree/build-tree.js";
|
|
24
|
+
import { extractHeadings } from "../extract-headings/index.js";
|
|
25
|
+
import { createDocRouteEntries } from "./doc-route-entries.js";
|
|
26
|
+
export {
|
|
27
|
+
buildBreadcrumbs,
|
|
28
|
+
buildNavTree,
|
|
29
|
+
buildSidebarTree,
|
|
30
|
+
collectAutoIndexNodes,
|
|
31
|
+
createDocRouteEntries,
|
|
32
|
+
extractHeadings,
|
|
33
|
+
findNode,
|
|
34
|
+
firstRoutedHref,
|
|
35
|
+
flattenSubtree,
|
|
36
|
+
flattenTree,
|
|
37
|
+
getCategoryOrder,
|
|
38
|
+
getNavSectionForSlug,
|
|
39
|
+
getNavSubtree,
|
|
40
|
+
groupSatelliteNodes,
|
|
41
|
+
isNavVisible,
|
|
42
|
+
remapNavChildHrefs,
|
|
43
|
+
resolveDocPrevNext,
|
|
44
|
+
rewriteNavHref,
|
|
45
|
+
schemaVersion
|
|
46
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CategoryMeta } from "../sidebar-tree/types.js";
|
|
2
|
+
import type { BreadcrumbItem, DocEntryLike, DocNavNode } from "./types.js";
|
|
3
|
+
/** Filter predicate: true when a doc should appear in navigation. */
|
|
4
|
+
export declare function isNavVisible(doc: DocEntryLike): boolean;
|
|
5
|
+
/** A docs-href builder: `(slug, locale) => url`. */
|
|
6
|
+
export type BuildHref = (slug: string, locale: string) => string;
|
|
7
|
+
/**
|
|
8
|
+
* Build a recursive navigation tree from a flat doc collection. Delegates tree
|
|
9
|
+
* construction to the shared `buildSidebarTree`; keeps the host-side concerns
|
|
10
|
+
* (DocNavNode shape, root-index node synthesis). `buildHref` parameterizes the
|
|
11
|
+
* href space (default: the injected `docsUrl`). Unlike the host copy this drops
|
|
12
|
+
* the LRU / identity caches — the snapshot-anchored `stableDocs` already makes
|
|
13
|
+
* the input arrays identity-stable so the factory-level `memoizeDerived` short-
|
|
14
|
+
* circuits per build.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildNavTree(docs: DocEntryLike[], locale: string, categoryMeta: Map<string, CategoryMeta> | undefined, buildHref: BuildHref): DocNavNode[];
|
|
17
|
+
/** Group "satellite" nodes (slug-prefix siblings) under their primary node. */
|
|
18
|
+
export declare function groupSatelliteNodes(tree: DocNavNode[], prefixes: string[]): DocNavNode[];
|
|
19
|
+
/** Find a node by slug anywhere in the tree. */
|
|
20
|
+
export declare function findNode(nodes: DocNavNode[], slug: string): DocNavNode | undefined;
|
|
21
|
+
/** Href of the first routed descendant, walking children depth-first. */
|
|
22
|
+
export declare function firstRoutedHref(node: DocNavNode): string | undefined;
|
|
23
|
+
/** Collect all category nodes that have children but no page (no index.mdx). */
|
|
24
|
+
export declare function collectAutoIndexNodes(nodes: DocNavNode[]): DocNavNode[];
|
|
25
|
+
/** Build breadcrumb trail by walking the nav tree. `homeHref` is the locale
|
|
26
|
+
* docs root (injected). `hrefFor` optionally remaps intermediate crumbs into
|
|
27
|
+
* a versioned URL space. */
|
|
28
|
+
export declare function buildBreadcrumbs(tree: DocNavNode[], slug: string, homeHref: string, hrefFor?: (slug: string) => string): BreadcrumbItem[];
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { buildSidebarTree } from "../sidebar-tree/build-tree.js";
|
|
2
|
+
import { toRouteSlug } from "../slug/index.js";
|
|
3
|
+
function isNavVisible(doc) {
|
|
4
|
+
return !doc.data.unlisted && !doc.data.standalone;
|
|
5
|
+
}
|
|
6
|
+
function toNavNode(node) {
|
|
7
|
+
return {
|
|
8
|
+
slug: node.id,
|
|
9
|
+
label: node.label,
|
|
10
|
+
description: node.description,
|
|
11
|
+
position: node.sidebar_position ?? 999,
|
|
12
|
+
href: node.href,
|
|
13
|
+
hasPage: node.hasPage,
|
|
14
|
+
children: node.children.map(toNavNode),
|
|
15
|
+
sortOrder: node.sortOrder ?? "asc"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function findRootIndexDoc(docs) {
|
|
19
|
+
let found;
|
|
20
|
+
for (const d of docs) {
|
|
21
|
+
const slug = d.data.slug ?? toRouteSlug(d.slug);
|
|
22
|
+
if (slug === "") found = d;
|
|
23
|
+
}
|
|
24
|
+
return found;
|
|
25
|
+
}
|
|
26
|
+
function toRootNavNode(doc, locale, buildHref, categoryMeta) {
|
|
27
|
+
const meta = categoryMeta?.get("");
|
|
28
|
+
const noPage = doc.data.category_no_page ?? meta?.noPage;
|
|
29
|
+
const sortOrder = doc.data.category_sort_order ?? meta?.sortOrder ?? "asc";
|
|
30
|
+
return {
|
|
31
|
+
slug: "",
|
|
32
|
+
label: doc.data.sidebar_label ?? doc.data.title ?? meta?.label ?? "",
|
|
33
|
+
description: doc.data.description ?? meta?.description,
|
|
34
|
+
position: doc.data.sidebar_position ?? meta?.position ?? 999,
|
|
35
|
+
href: noPage ? void 0 : buildHref("", locale),
|
|
36
|
+
hasPage: noPage !== true,
|
|
37
|
+
children: [],
|
|
38
|
+
sortOrder
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function buildNavTree(docs, locale, categoryMeta, buildHref) {
|
|
42
|
+
const href = buildHref;
|
|
43
|
+
const sidebarTree = buildSidebarTree(
|
|
44
|
+
docs,
|
|
45
|
+
locale,
|
|
46
|
+
{
|
|
47
|
+
categoryMeta,
|
|
48
|
+
buildHref: (slug, loc) => href(slug, loc),
|
|
49
|
+
// Host call sites own visibility; disable the builder's default filter so
|
|
50
|
+
// breadcrumb (unfiltered) and nav (pre-filtered) paths are unchanged.
|
|
51
|
+
isNavVisible: () => true
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
const result = sidebarTree.map(toNavNode);
|
|
55
|
+
const rootDoc = findRootIndexDoc(docs);
|
|
56
|
+
if (rootDoc) {
|
|
57
|
+
result.push(toRootNavNode(rootDoc, locale, href, categoryMeta));
|
|
58
|
+
result.sort((a, b) => {
|
|
59
|
+
const posCompare = a.position - b.position;
|
|
60
|
+
if (posCompare !== 0) return posCompare;
|
|
61
|
+
return a.slug.localeCompare(b.slug);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function groupSatelliteNodes(tree, prefixes) {
|
|
67
|
+
const result = [...tree];
|
|
68
|
+
for (const prefix of prefixes) {
|
|
69
|
+
const primaryIdx = result.findIndex((n) => n.slug === prefix);
|
|
70
|
+
if (primaryIdx < 0) continue;
|
|
71
|
+
const primary = result[primaryIdx];
|
|
72
|
+
if (!primary) continue;
|
|
73
|
+
const satelliteIdxs = [];
|
|
74
|
+
for (let i = 0; i < result.length; i++) {
|
|
75
|
+
const node = result[i];
|
|
76
|
+
if (node && i !== primaryIdx && node.slug.startsWith(`${prefix}-`)) {
|
|
77
|
+
satelliteIdxs.push(i);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (satelliteIdxs.length === 0) continue;
|
|
81
|
+
const extraChildren = [];
|
|
82
|
+
for (const idx of satelliteIdxs) {
|
|
83
|
+
const node = result[idx];
|
|
84
|
+
if (node) extraChildren.push(node);
|
|
85
|
+
}
|
|
86
|
+
result[primaryIdx] = { ...primary, children: [...primary.children, ...extraChildren] };
|
|
87
|
+
for (const idx of satelliteIdxs.reverse()) result.splice(idx, 1);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
function findNode(nodes, slug) {
|
|
92
|
+
for (const node of nodes) {
|
|
93
|
+
if (node.slug === slug) return node;
|
|
94
|
+
const found = findNode(node.children, slug);
|
|
95
|
+
if (found) return found;
|
|
96
|
+
}
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
99
|
+
function firstRoutedHref(node) {
|
|
100
|
+
for (const child of node.children) {
|
|
101
|
+
if (child.hasPage && child.href) return child.href;
|
|
102
|
+
const nested = firstRoutedHref(child);
|
|
103
|
+
if (nested) return nested;
|
|
104
|
+
}
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
function collectAutoIndexNodes(nodes) {
|
|
108
|
+
const result = [];
|
|
109
|
+
for (const node of nodes) {
|
|
110
|
+
if (!node.hasPage && node.children.length > 0 && node.href) result.push(node);
|
|
111
|
+
result.push(...collectAutoIndexNodes(node.children));
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
function buildBreadcrumbs(tree, slug, homeHref, hrefFor) {
|
|
116
|
+
const parts = slug.split("/");
|
|
117
|
+
const crumbs = [{ label: "", href: homeHref }];
|
|
118
|
+
let nodes = tree;
|
|
119
|
+
for (let i = 0; i < parts.length; i++) {
|
|
120
|
+
const partialSlug = parts.slice(0, i + 1).join("/");
|
|
121
|
+
const node = nodes.find((n) => n.slug === partialSlug);
|
|
122
|
+
if (!node) break;
|
|
123
|
+
const isLast = i === parts.length - 1;
|
|
124
|
+
const href = isLast ? void 0 : hrefFor && node.href !== void 0 ? hrefFor(node.slug) : node.href;
|
|
125
|
+
crumbs.push({ label: node.label, href });
|
|
126
|
+
nodes = node.children;
|
|
127
|
+
}
|
|
128
|
+
return crumbs;
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
buildBreadcrumbs,
|
|
132
|
+
buildNavTree,
|
|
133
|
+
collectAutoIndexNodes,
|
|
134
|
+
findNode,
|
|
135
|
+
firstRoutedHref,
|
|
136
|
+
groupSatelliteNodes,
|
|
137
|
+
isNavVisible
|
|
138
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { CategoryMeta, CollectionEntryLike, SidebarFrontmatter, SidebarNode } from "../sidebar-tree/types.js";
|
|
2
|
+
import type { BreadcrumbItem } from "../breadcrumb/types.js";
|
|
3
|
+
import type { HeadingItem } from "../extract-headings/index.js";
|
|
4
|
+
export type { BreadcrumbItem, CategoryMeta, CollectionEntryLike, HeadingItem, SidebarFrontmatter, SidebarNode, };
|
|
5
|
+
/**
|
|
6
|
+
* Nav tree node shape as consumed by doc-route pages.
|
|
7
|
+
*
|
|
8
|
+
* Structurally identical to a host project's `NavNode`. Defined here so the
|
|
9
|
+
* package types depend on no host alias and no engine package; a host `NavNode`
|
|
10
|
+
* is a structural subtype and assignable wherever `DocNavNode` is expected.
|
|
11
|
+
*/
|
|
12
|
+
export interface DocNavNode {
|
|
13
|
+
slug: string;
|
|
14
|
+
label: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
position: number;
|
|
17
|
+
href?: string;
|
|
18
|
+
hasPage: boolean;
|
|
19
|
+
children: DocNavNode[];
|
|
20
|
+
sortOrder?: "asc" | "desc";
|
|
21
|
+
collapsed?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** A category node with children but no page of its own — an auto-index. */
|
|
24
|
+
export interface AutoIndexNode extends DocNavNode {
|
|
25
|
+
children: DocNavNode[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Minimal docs frontmatter shape consumed by the doc-page types.
|
|
29
|
+
*
|
|
30
|
+
* A structural subset of a project's full docs schema — only the fields the
|
|
31
|
+
* nav/route domain actually reads. A richer `DocsData` is a structural
|
|
32
|
+
* supertype and is assignable here.
|
|
33
|
+
*/
|
|
34
|
+
export interface DocPageFrontmatter {
|
|
35
|
+
title: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
slug?: string;
|
|
38
|
+
draft?: boolean;
|
|
39
|
+
unlisted?: boolean;
|
|
40
|
+
standalone?: boolean;
|
|
41
|
+
sidebar_position?: number;
|
|
42
|
+
sidebar_label?: string;
|
|
43
|
+
category_no_page?: boolean;
|
|
44
|
+
category_sort_order?: "asc" | "desc";
|
|
45
|
+
pagination_prev?: string | null;
|
|
46
|
+
pagination_next?: string | null;
|
|
47
|
+
tags?: string[];
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The structural doc-collection entry this domain reads: a slug, docs
|
|
52
|
+
* frontmatter, and an optional raw body. zfb's `CollectionEntry<DocPageFrontmatter>`
|
|
53
|
+
* (exported as `DocPageEntry` from `@takazudo/zudo-doc/doc-page-props`) is
|
|
54
|
+
* assignable to it, as is any plain fixture object of the same shape.
|
|
55
|
+
*/
|
|
56
|
+
export type DocEntryLike = CollectionEntryLike<DocPageFrontmatter>;
|
|
57
|
+
/** Shared fields present in every doc-page route. */
|
|
58
|
+
interface DocPagePropsBase {
|
|
59
|
+
breadcrumbs: BreadcrumbItem[];
|
|
60
|
+
prev: DocNavNode | null;
|
|
61
|
+
next: DocNavNode | null;
|
|
62
|
+
/** Depth-2/3/4 headings extracted from the MDX body, for SSG TOC links. */
|
|
63
|
+
headings: HeadingItem[];
|
|
64
|
+
}
|
|
65
|
+
/** Branch: a real content entry. `autoIndex` is absent. */
|
|
66
|
+
export interface DocPageEntryProps<E extends DocEntryLike = DocEntryLike> extends DocPagePropsBase {
|
|
67
|
+
kind: "entry";
|
|
68
|
+
entry: E;
|
|
69
|
+
autoIndex?: undefined;
|
|
70
|
+
}
|
|
71
|
+
/** Branch: an auto-generated category index. `entry` is absent. */
|
|
72
|
+
export interface DocPageAutoIndexProps extends DocPagePropsBase {
|
|
73
|
+
kind: "autoIndex";
|
|
74
|
+
autoIndex: AutoIndexNode;
|
|
75
|
+
entry?: undefined;
|
|
76
|
+
}
|
|
77
|
+
/** Discriminated union for the `kind` prop. Narrow via `props.kind === "entry"`. */
|
|
78
|
+
export type DocPageBaseProps<E extends DocEntryLike = DocEntryLike> = DocPageEntryProps<E> | DocPageAutoIndexProps;
|
|
79
|
+
/**
|
|
80
|
+
* The resolved, identity-stable doc set for one (locale, version) context —
|
|
81
|
+
* the input every route-entry enumeration starts from.
|
|
82
|
+
*
|
|
83
|
+
* The concrete resolver that PRODUCES one (`createNavSourceDocs`) reads
|
|
84
|
+
* `_category_.json` sidecars off disk and therefore stays in
|
|
85
|
+
* `@takazudo/zudo-doc/nav-source-docs`; only the shape is browser-safe.
|
|
86
|
+
*/
|
|
87
|
+
export interface NavSourceDocs<E extends DocEntryLike = DocEntryLike> {
|
|
88
|
+
/** Full doc list (merged + draft-filtered; unlisted retained per options). */
|
|
89
|
+
docs: E[];
|
|
90
|
+
/** `docs.filter(isNavVisible)` — stable instance for buildNavTree. */
|
|
91
|
+
navDocs: E[];
|
|
92
|
+
/** Stable category-meta Map for the active (locale, version). */
|
|
93
|
+
categoryMeta: Map<string, CategoryMeta>;
|
|
94
|
+
/** Slugs that came from the locale collection (for isFallback). Empty for
|
|
95
|
+
* default-locale / single-collection cases. */
|
|
96
|
+
localeSlugSet: ReadonlySet<string>;
|
|
97
|
+
}
|
|
File without changes
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import type { JSX } from "preact";
|
|
2
2
|
import type { ThemePackRegistry } from "../theme-packs-registry/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Transient `<html>` attribute the bootstrap sets while the pack stylesheet
|
|
5
|
+
* request is in flight, but ONLY while `document.readyState === "loading"`
|
|
6
|
+
* (#3407/#3413) — a post-paint re-evaluation (SPA embedding, #3399) inserts a
|
|
7
|
+
* corrective link without ever arming the latch, since the body it would hide
|
|
8
|
+
* is already painted. Paired with {@link THEME_PACK_LATCH_CSS}; cleared on
|
|
9
|
+
* the link's load/error or by the watchdog. Deliberately NOT part of
|
|
10
|
+
* `preserveHtmlAttrs` — it must never survive a soft navigation.
|
|
11
|
+
*
|
|
12
|
+
* Not to be confused with `THEME_PACK_LINK_LOADING_ATTR`
|
|
13
|
+
* (`data-zd-theme-pack-css-loading`), which marks the in-flight `<link>`
|
|
14
|
+
* during a RUNTIME `applyThemePack` swap. Different element, different
|
|
15
|
+
* mechanism — a runtime swap awaits the sheet off-screen and never hides
|
|
16
|
+
* the body.
|
|
17
|
+
*/
|
|
18
|
+
export declare const THEME_PACK_LOADING_ATTR = "data-zd-theme-pack-loading";
|
|
19
|
+
/**
|
|
20
|
+
* Upper bound (ms) on how long the latch may hide the body. Past this the
|
|
21
|
+
* attribute is cleared regardless of the stylesheet's state: blank-screen
|
|
22
|
+
* avoidance wins over strict no-FOUC (see the latch contract in the file
|
|
23
|
+
* header).
|
|
24
|
+
*/
|
|
25
|
+
export declare const THEME_PACK_LOAD_WATCHDOG_MS = 2000;
|
|
26
|
+
/**
|
|
27
|
+
* The latch rule, emitted as a build-static `<style>` BEFORE the bootstrap
|
|
28
|
+
* script so it is live by the time the body parses. Inert until the script
|
|
29
|
+
* sets {@link THEME_PACK_LOADING_ATTR}.
|
|
30
|
+
*/
|
|
31
|
+
export declare const THEME_PACK_LATCH_CSS = "html[data-zd-theme-pack-loading] body{visibility:hidden}";
|
|
3
32
|
export interface ThemePackProviderProps {
|
|
4
33
|
/** The build-configured pack slug (`settings.themePack`, default `"default"`). */
|
|
5
34
|
configuredSlug: string;
|
|
@@ -35,8 +64,10 @@ export declare function resolveThemePackSsrSlug(registry: ThemePackRegistry | nu
|
|
|
35
64
|
*/
|
|
36
65
|
export declare function buildThemePackBootstrap(configuredSlug: string, enabled: Record<string, string>, base: string): string;
|
|
37
66
|
/**
|
|
38
|
-
* Server-rendered head fragment: the
|
|
39
|
-
* `<noscript>` configured-pack fallback. No
|
|
40
|
-
* `ColorSchemeProvider` it just emits markup the engine
|
|
67
|
+
* Server-rendered head fragment: the anti-FOUC latch `<style>`, the pre-paint
|
|
68
|
+
* bootstrap `<script>`, and the `<noscript>` configured-pack fallback. No
|
|
69
|
+
* hydration — like `ColorSchemeProvider` it just emits markup the engine
|
|
70
|
+
* streams into `<head>`. The latch style MUST stay first (see the contract in
|
|
71
|
+
* the file header).
|
|
41
72
|
*/
|
|
42
73
|
export default function ThemePackProvider({ configuredSlug, enabled, base, }: ThemePackProviderProps): JSX.Element;
|