create-zudo-doc 0.2.0-next.3 → 0.2.0-next.6
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/dist/features/versioning.d.ts +1 -5
- package/dist/features/versioning.js +4 -8
- package/dist/scaffold.js +15 -5
- package/dist/settings-gen.js +2 -0
- package/package.json +2 -1
- package/templates/base/pages/_data.ts +8 -31
- package/templates/base/pages/docs/{[...slug].tsx → [[...slug]].tsx} +48 -193
- package/templates/base/pages/index.tsx +5 -12
- package/templates/base/pages/lib/_category-nav.tsx +7 -40
- package/templates/base/pages/lib/_category-tree-nav.tsx +6 -38
- package/templates/base/pages/lib/_doc-body-end.tsx +34 -0
- package/templates/base/pages/lib/_doc-content-header.tsx +98 -0
- package/templates/base/pages/lib/_doc-history-area.tsx +14 -2
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +12 -2
- package/templates/base/pages/lib/_doc-pager.tsx +79 -0
- package/templates/base/pages/lib/_footer-with-defaults.tsx +13 -15
- package/templates/base/pages/lib/_header-with-defaults.tsx +1 -3
- package/templates/base/pages/lib/_nav-source-cache.ts +145 -0
- package/templates/base/pages/lib/_nav-source-docs.ts +207 -42
- package/templates/base/pages/lib/_sidebar-prepaint.tsx +52 -0
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +1 -3
- package/templates/base/pages/lib/_site-tree-nav.tsx +7 -42
- package/templates/base/pages/lib/doc-page-props.ts +48 -0
- package/templates/base/pages/lib/locale-merge.ts +149 -41
- package/templates/base/pages/lib/route-enumerators.ts +47 -78
- package/templates/base/src/components/content/heading-h3.tsx +1 -7
- package/templates/base/src/components/site-tree-nav.tsx +2 -14
- package/templates/base/src/config/i18n.ts +1 -3
- package/templates/base/src/utils/base.ts +18 -7
- package/templates/base/src/utils/docs.ts +51 -1
- package/templates/base/src/utils/slug.ts +43 -0
- package/templates/base/src/utils/smart-break.tsx +1 -7
- package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +26 -16
- package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -2
- package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +6 -1
- package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +6 -1
- package/templates/features/i18n/files/pages/[locale]/docs/{[...slug].tsx → [[...slug]].tsx} +72 -198
- package/templates/features/i18n/files/pages/[locale]/index.tsx +11 -32
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +18 -10
- package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +3 -1
- package/templates/features/versioning/files/pages/v/[version]/[locale]/docs/[[...slug]].tsx +356 -0
- package/templates/features/versioning/files/pages/v/[version]/docs/{[...slug].tsx → [[...slug]].tsx} +50 -171
- package/templates/base/src/components/content/component-map.ts +0 -25
- package/templates/base/src/components/content/content-blockquote.tsx +0 -16
- package/templates/base/src/components/content/content-code.tsx +0 -117
- package/templates/base/src/components/content/content-link.tsx +0 -83
- package/templates/base/src/components/content/content-ol.tsx +0 -19
- package/templates/base/src/components/content/content-paragraph.tsx +0 -10
- package/templates/base/src/components/content/content-strong.tsx +0 -16
- package/templates/base/src/components/content/content-table.tsx +0 -18
- package/templates/base/src/components/content/content-ul.tsx +0 -18
- package/templates/base/src/components/content/heading-h2.tsx +0 -26
- package/templates/base/src/components/content/heading-h4.tsx +0 -26
- package/templates/base/src/plugins/rehype-strip-md-extension.ts +0 -58
- package/templates/base/src/plugins/remark-admonitions.ts +0 -99
- package/templates/base/src/plugins/remark-resolve-markdown-links.ts +0 -127
- package/templates/features/versioning/files/pages/v/[version]/ja/docs/[...slug].tsx +0 -490
|
@@ -13,15 +13,11 @@ import type { FeatureModule } from "../compose.js";
|
|
|
13
13
|
*
|
|
14
14
|
* docs/versions.tsx (always — versioning gate only)
|
|
15
15
|
* v/[version]/docs/[...slug].tsx (always — versioning gate only)
|
|
16
|
-
* v/[version]/
|
|
16
|
+
* v/[version]/[locale]/docs/[...slug].tsx (i18n + versioning — see below)
|
|
17
17
|
* [locale]/docs/versions.tsx (i18n + versioning)
|
|
18
18
|
*
|
|
19
19
|
* `copyFeatureFiles` (compose.ts) auto-copies everything under `files/`.
|
|
20
20
|
* postProcess removes the i18n-gated subset when i18n is OFF so single-
|
|
21
21
|
* locale projects don't ship orphan routes.
|
|
22
|
-
*
|
|
23
|
-
* Note: `v/[version]/ja/docs/[...slug].tsx` hardcodes `ja` per W2 spec-lock
|
|
24
|
-
* Decision 9 / §6.4 — matches main verbatim. Future generalization to
|
|
25
|
-
* `[locale]` is deferred (maintainer-question follow-up).
|
|
26
22
|
*/
|
|
27
23
|
export declare const versioningFeature: FeatureModule;
|
|
@@ -14,16 +14,12 @@ import path from "path";
|
|
|
14
14
|
*
|
|
15
15
|
* docs/versions.tsx (always — versioning gate only)
|
|
16
16
|
* v/[version]/docs/[...slug].tsx (always — versioning gate only)
|
|
17
|
-
* v/[version]/
|
|
17
|
+
* v/[version]/[locale]/docs/[...slug].tsx (i18n + versioning — see below)
|
|
18
18
|
* [locale]/docs/versions.tsx (i18n + versioning)
|
|
19
19
|
*
|
|
20
20
|
* `copyFeatureFiles` (compose.ts) auto-copies everything under `files/`.
|
|
21
21
|
* postProcess removes the i18n-gated subset when i18n is OFF so single-
|
|
22
22
|
* locale projects don't ship orphan routes.
|
|
23
|
-
*
|
|
24
|
-
* Note: `v/[version]/ja/docs/[...slug].tsx` hardcodes `ja` per W2 spec-lock
|
|
25
|
-
* Decision 9 / §6.4 — matches main verbatim. Future generalization to
|
|
26
|
-
* `[locale]` is deferred (maintainer-question follow-up).
|
|
27
23
|
*/
|
|
28
24
|
export const versioningFeature = (choices) => ({
|
|
29
25
|
name: "versioning",
|
|
@@ -33,9 +29,9 @@ export const versioningFeature = (choices) => ({
|
|
|
33
29
|
if (await fs.pathExists(localeVersions)) {
|
|
34
30
|
await fs.remove(localeVersions);
|
|
35
31
|
}
|
|
36
|
-
const
|
|
37
|
-
if (await fs.pathExists(
|
|
38
|
-
await fs.remove(
|
|
32
|
+
const localeVersionedDocs = path.join(targetDir, "pages", "v", "[version]", "[locale]");
|
|
33
|
+
if (await fs.pathExists(localeVersionedDocs)) {
|
|
34
|
+
await fs.remove(localeVersionedDocs);
|
|
39
35
|
}
|
|
40
36
|
}
|
|
41
37
|
},
|
package/dist/scaffold.js
CHANGED
|
@@ -248,16 +248,26 @@ function generatePackageJson(choices) {
|
|
|
248
248
|
// so render failures surface `console.*` output. next.27 is unusable
|
|
249
249
|
// for adapter consumers — its tarball omitted emit-worker.mjs
|
|
250
250
|
// (Takazudo/zudo-front-builder#794, fixed in next.28) — so never pin 27.
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
// Bumped to next.30 — adds Next.js-style `[[...slug]]` optional-catchall
|
|
252
|
+
// route syntax (Takazudo/zudo-front-builder#812) and raises the zfb-runtime
|
|
253
|
+
// hono floor to ^4.12.23, clearing 9 advisories (#813); also two router
|
|
254
|
+
// hardening fixes (overlapping-sibling rejection #816, per-segment rank
|
|
255
|
+
// sort for dev/prod parity). No consumer-facing breaking change.
|
|
256
|
+
// Bumped to next.31 — CSS-pipeline and islands-scanner fixes (no
|
|
257
|
+
// consumer-facing breaking change): authored-CSS path when Tailwind is
|
|
258
|
+
// disabled, reproducible CSS-Modules scoped names (project-relative paths),
|
|
259
|
+
// dev-mode git-restore detection, Tailwind temp-file cleanup, and a
|
|
260
|
+
// near-miss `"use client"` directive scanner.
|
|
261
|
+
"@takazudo/zfb": "0.1.0-next.31",
|
|
262
|
+
"@takazudo/zfb-runtime": "0.1.0-next.31",
|
|
253
263
|
// zfb-adapter-cloudflare — required for any route with `prerender = false`.
|
|
254
264
|
// Pinned in lockstep with @takazudo/zfb.
|
|
255
|
-
"@takazudo/zfb-adapter-cloudflare": "0.1.0-next.
|
|
265
|
+
"@takazudo/zfb-adapter-cloudflare": "0.1.0-next.31",
|
|
256
266
|
// @takazudo/zudo-doc — published from this monorepo via
|
|
257
267
|
// .github/workflows/publish-zudo-doc.yml. The pin here is bumped in
|
|
258
268
|
// lockstep by scripts/release-create-zudo-doc.sh whenever zudo-doc's
|
|
259
269
|
// version moves, so a fresh scaffold pulls the version we just published.
|
|
260
|
-
"@takazudo/zudo-doc": "^0.2.0-next.
|
|
270
|
+
"@takazudo/zudo-doc": "^0.2.0-next.6",
|
|
261
271
|
// zod — used by the generated zfb.config.ts. zfb-config-gen emits
|
|
262
272
|
// `import { z } from "zod"` for the content-collection schema +
|
|
263
273
|
// `z.toJSONSchema(...)` conversion. Without this dep, the consumer
|
|
@@ -315,7 +325,7 @@ function generatePackageJson(choices) {
|
|
|
315
325
|
// @takazudo/zudo-doc/integrations/doc-history which in turn imports
|
|
316
326
|
// @takazudo/zudo-doc-history-server/git-history. Without this dep the
|
|
317
327
|
// plugin host fails at init with ERR_MODULE_NOT_FOUND — W8A (#1739).
|
|
318
|
-
deps["@takazudo/zudo-doc-history-server"] = "^0.2.0-next.
|
|
328
|
+
deps["@takazudo/zudo-doc-history-server"] = "^0.2.0-next.6";
|
|
319
329
|
// W7A (#1736): doc-history-plugin.mjs spawns `tsx -e <inline-script>` to
|
|
320
330
|
// run the v2 runtime in a TS-aware Node subprocess; without tsx the
|
|
321
331
|
// plugin's preBuild step exits with ENOENT before zfb finishes config
|
package/dist/settings-gen.js
CHANGED
|
@@ -107,6 +107,8 @@ export function generateSettingsFile(choices) {
|
|
|
107
107
|
// the chat once they're wiring up a real `ANTHROPIC_API_KEY`. Defaulting
|
|
108
108
|
// demo mode off avoids silently disabling chat for them.
|
|
109
109
|
lines.push(` aiChatDemoMode: false as boolean,`);
|
|
110
|
+
lines.push(` aiChatAllowedOrigins: [] as string[],`);
|
|
111
|
+
lines.push(` aiChatGlobalDailyLimit: false as number | false,`);
|
|
110
112
|
if (choices.features.includes("docHistory")) {
|
|
111
113
|
lines.push(` docHistory: true,`);
|
|
112
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-zudo-doc",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.6",
|
|
4
4
|
"description": "Create a new zudo-doc documentation site",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Takeshi Takatsudo",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
47
47
|
"build": "pnpm clean && tsc",
|
|
48
48
|
"dev": "tsc --watch",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
49
50
|
"test": "vitest run",
|
|
50
51
|
"test:slow": "vitest run --config vitest.slow.config.ts",
|
|
51
52
|
"prepublishOnly": "pnpm build && pnpm test"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { getCollection } from "zfb/content";
|
|
14
14
|
import type { CollectionEntry } from "zfb/content";
|
|
15
15
|
import type { DocsEntry } from "@/types/docs-entry";
|
|
16
|
+
import { toRouteSlug } from "@/utils/slug";
|
|
16
17
|
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
18
19
|
// Types
|
|
@@ -92,9 +93,14 @@ export function getDocs(collectionName: string): ZfbDocsEntry[] {
|
|
|
92
93
|
}));
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
// The `id` field bridged onto every entry is the canonical route slug, so it
|
|
97
|
+
// routes through the one shared rule (`toRouteSlug` in @/utils/slug) — bare
|
|
98
|
+
// root `index` → "" (URL /docs/), nested `x/index` → "x". Previously this was
|
|
99
|
+
// a standalone copy of the strip logic (the lone "" dissenter of the five
|
|
100
|
+
// index-stripping sites); consolidating it here means there is one source of
|
|
101
|
+
// truth. See @/utils/slug for the canonical-root rationale (#1891 / #1873).
|
|
95
102
|
function stripIndexSuffix(slug: string): string {
|
|
96
|
-
|
|
97
|
-
return slug.endsWith("/index") ? slug.slice(0, -"/index".length) : slug;
|
|
103
|
+
return toRouteSlug(slug);
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
/**
|
|
@@ -148,32 +154,3 @@ export function filterDrafts(entries: ZfbDocsEntry[]): ZfbDocsEntry[] {
|
|
|
148
154
|
return entries.filter((e) => !e.data.draft);
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
/**
|
|
152
|
-
* Merge locale docs with base (EN) fallbacks.
|
|
153
|
-
*
|
|
154
|
-
* Strategy (mirrors src/utils/locale-docs.ts):
|
|
155
|
-
* 1. Load locale docs (e.g. "docs-ja")
|
|
156
|
-
* 2. Load base docs ("docs")
|
|
157
|
-
* 3. Locale docs take priority; base docs fill in missing slugs.
|
|
158
|
-
* 4. Track which slugs came from base (fallbackSlugs).
|
|
159
|
-
*
|
|
160
|
-
* Returns { allDocs, fallbackSlugs }.
|
|
161
|
-
* categoryMeta is not merged here — callers use loadCategoryMeta() directly.
|
|
162
|
-
*/
|
|
163
|
-
export function mergeLocaleDocs(
|
|
164
|
-
locale: string,
|
|
165
|
-
): { allDocs: ZfbDocsEntry[]; fallbackSlugs: Set<string> } {
|
|
166
|
-
const localeDocs = filterDrafts(getDocs(`docs-${locale}`));
|
|
167
|
-
const baseDocs = filterDrafts(getDocs("docs"));
|
|
168
|
-
|
|
169
|
-
const localeSlugSet = new Set(localeDocs.map((d) => d.data.slug ?? d.id));
|
|
170
|
-
|
|
171
|
-
const fallbackDocs = baseDocs.filter(
|
|
172
|
-
(d) => !localeSlugSet.has(d.data.slug ?? d.id),
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
allDocs: [...localeDocs, ...fallbackDocs],
|
|
177
|
-
fallbackSlugs: new Set(fallbackDocs.map((d) => d.data.slug ?? d.id)),
|
|
178
|
-
};
|
|
179
|
-
}
|
|
@@ -11,36 +11,34 @@
|
|
|
11
11
|
// params: { slug: string[] } — e.g. ["getting-started", "intro"]
|
|
12
12
|
// props: { entry, autoIndex, breadcrumbs, prev, next }
|
|
13
13
|
//
|
|
14
|
+
// Route is the OPTIONAL catchall `[[...slug]]` so a bare root index.mdx can
|
|
15
|
+
// build at `/docs/` (canonical root URL — #1891). The root entry emits
|
|
16
|
+
// `params.slug = []` (zero segments) via `toSlugParams`; a required `[...slug]`
|
|
17
|
+
// catchall rejects an empty array and would drop the whole route.
|
|
18
|
+
//
|
|
14
19
|
// The catchall slug is an array per zfb spec — the component joins it when
|
|
15
20
|
// deriving the string form (e.g. for Content lookups, breadcrumbs, etc.).
|
|
16
21
|
//
|
|
17
22
|
// Locale: defaultLocale (EN). Non-default locales are handled by
|
|
18
|
-
// pages/[locale]/docs/[...slug].tsx.
|
|
23
|
+
// pages/[locale]/docs/[[...slug]].tsx.
|
|
19
24
|
|
|
20
|
-
import { getCollection } from "zfb/content";
|
|
21
|
-
import type { CollectionEntry } from "zfb/content";
|
|
22
25
|
import type { DocsEntry } from "@/types/docs-entry";
|
|
23
26
|
import { settings } from "@/config/settings";
|
|
24
|
-
import { defaultLocale
|
|
27
|
+
import { defaultLocale } from "@/config/i18n";
|
|
25
28
|
import { docsUrl } from "@/utils/base";
|
|
26
29
|
import {
|
|
27
30
|
buildNavTree,
|
|
28
31
|
buildBreadcrumbs,
|
|
29
32
|
flattenTree,
|
|
30
33
|
findNode,
|
|
31
|
-
loadCategoryMeta,
|
|
32
34
|
collectAutoIndexNodes,
|
|
33
|
-
isNavVisible,
|
|
34
35
|
type NavNode,
|
|
35
|
-
type BreadcrumbItem,
|
|
36
36
|
} from "@/utils/docs";
|
|
37
37
|
import { getNavSectionForSlug, getNavSubtree } from "@/utils/nav-scope";
|
|
38
|
-
import { toRouteSlug } from "@/utils/slug";
|
|
38
|
+
import { toRouteSlug, toSlugParams } from "@/utils/slug";
|
|
39
39
|
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
40
40
|
import { Breadcrumb } from "@takazudo/zudo-doc/breadcrumb";
|
|
41
41
|
import { NavCardGrid } from "@takazudo/zudo-doc/nav-indexing";
|
|
42
|
-
import { FrontmatterPreview } from "@takazudo/zudo-doc/metainfo";
|
|
43
|
-
import { frontmatterRenderers } from "@/config/frontmatter-preview-renderers";
|
|
44
42
|
// Shared MDX-tag → Preact-component bag. Includes htmlOverrides
|
|
45
43
|
// (native typography), HtmlPreviewWrapper (Island), and stub bindings
|
|
46
44
|
// for every other custom tag the MDX corpus references — see
|
|
@@ -49,21 +47,19 @@ import { createMdxComponents } from "../_mdx-components";
|
|
|
49
47
|
import { FooterWithDefaults } from "../lib/_footer-with-defaults";
|
|
50
48
|
import { DocHistoryArea } from "../lib/_doc-history-area";
|
|
51
49
|
import { DocMetainfoArea } from "../lib/_doc-metainfo-area";
|
|
52
|
-
import { DocTagsArea } from "../lib/_doc-tags-area";
|
|
53
|
-
import { BodyEndIslands } from "../lib/_body-end-islands";
|
|
54
50
|
import { SidebarWithDefaults } from "../lib/_sidebar-with-defaults";
|
|
55
51
|
import { HeaderWithDefaults } from "../lib/_header-with-defaults";
|
|
56
52
|
import { HeadWithDefaults } from "../lib/_head-with-defaults";
|
|
57
|
-
import { buildFrontmatterPreviewEntries } from "../lib/_frontmatter-preview-data";
|
|
58
53
|
import { composeMetaTitle } from "../lib/_compose-meta-title";
|
|
59
54
|
import { buildInlineVersionSwitcher } from "../lib/_inline-version-switcher";
|
|
60
55
|
import type { JSX } from "preact";
|
|
61
|
-
import {
|
|
56
|
+
import { resolveNavSource } from "../lib/_nav-source-docs";
|
|
62
57
|
import { extractHeadings } from "../lib/_extract-headings";
|
|
63
|
-
import
|
|
64
|
-
import {
|
|
65
|
-
import
|
|
66
|
-
import {
|
|
58
|
+
import type { DocPageEntry, AutoIndexNode, DocPageEntryProps, DocPageAutoIndexProps } from "../lib/doc-page-props";
|
|
59
|
+
import { DocPager } from "../lib/_doc-pager";
|
|
60
|
+
import { DocContentHeader } from "../lib/_doc-content-header";
|
|
61
|
+
import { SidebarPrepaint } from "../lib/_sidebar-prepaint";
|
|
62
|
+
import { DocBodyEnd } from "../lib/_doc-body-end";
|
|
67
63
|
|
|
68
64
|
export const frontmatter = { title: "Docs" };
|
|
69
65
|
|
|
@@ -71,31 +67,9 @@ export const frontmatter = { title: "Docs" };
|
|
|
71
67
|
// Props contract
|
|
72
68
|
// ---------------------------------------------------------------------------
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
/** zfb content renderer. */
|
|
76
|
-
Content: CollectionEntry<unknown>["Content"];
|
|
77
|
-
/** zfb module specifier (for Content bridge). */
|
|
78
|
-
module_specifier: string;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
interface AutoIndexNode extends NavNode {
|
|
82
|
-
children: NavNode[];
|
|
83
|
-
}
|
|
70
|
+
// DocPageEntry, AutoIndexNode imported from pages/lib/doc-page-props.ts
|
|
84
71
|
|
|
85
|
-
|
|
86
|
-
/** The docs entry to render, or null for auto-index pages. */
|
|
87
|
-
entry: DocPageEntry | null;
|
|
88
|
-
/** Pre-built auto-index node (categories without index.mdx). */
|
|
89
|
-
autoIndex?: AutoIndexNode;
|
|
90
|
-
/** Breadcrumb trail, first item is home. */
|
|
91
|
-
breadcrumbs: BreadcrumbItem[];
|
|
92
|
-
/** Preceding page in the nav tree. */
|
|
93
|
-
prev: NavNode | null;
|
|
94
|
-
/** Following page in the nav tree. */
|
|
95
|
-
next: NavNode | null;
|
|
96
|
-
/** Depth-2/3/4 headings extracted from the MDX body, for SSG TOC links. */
|
|
97
|
-
headings: ReturnType<typeof extractHeadings>;
|
|
98
|
-
}
|
|
72
|
+
type DocPageProps = DocPageEntryProps | DocPageAutoIndexProps;
|
|
99
73
|
|
|
100
74
|
// ---------------------------------------------------------------------------
|
|
101
75
|
// paths() — synchronous route enumeration (ADR-004)
|
|
@@ -113,13 +87,13 @@ export function paths(): Array<{
|
|
|
113
87
|
props: DocPageProps;
|
|
114
88
|
}> {
|
|
115
89
|
const locale = defaultLocale;
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
90
|
+
// Identity-stable nav source (draft-filtered, unlisted retained). The same
|
|
91
|
+
// instances are returned across this route's many per-page paths()
|
|
92
|
+
// invocations, so buildNavTree's identity fast-path skips the key
|
|
93
|
+
// recomputation — see pages/lib/_nav-source-docs.ts (#1902).
|
|
94
|
+
const { docs, navDocs, categoryMeta } = resolveNavSource(locale, undefined);
|
|
120
95
|
|
|
121
96
|
// Nav docs: exclude unlisted (for sidebar/prev-next) but keep for breadcrumbs
|
|
122
|
-
const navDocs = docs.filter(isNavVisible);
|
|
123
97
|
const tree = buildNavTree(navDocs as unknown as DocsEntry[], locale, categoryMeta);
|
|
124
98
|
// Full tree (including unlisted) for accurate breadcrumbs
|
|
125
99
|
const fullTree = buildNavTree(docs as unknown as DocsEntry[], locale, categoryMeta);
|
|
@@ -156,8 +130,9 @@ export function paths(): Array<{
|
|
|
156
130
|
}
|
|
157
131
|
|
|
158
132
|
result.push({
|
|
159
|
-
params: { slug: slug
|
|
133
|
+
params: { slug: toSlugParams(slug) },
|
|
160
134
|
props: {
|
|
135
|
+
kind: "entry",
|
|
161
136
|
entry,
|
|
162
137
|
breadcrumbs: buildBreadcrumbs(fullTree, slug, locale),
|
|
163
138
|
prev: prevNode,
|
|
@@ -170,9 +145,9 @@ export function paths(): Array<{
|
|
|
170
145
|
// Auto-generated index pages for categories without index.mdx
|
|
171
146
|
for (const node of collectAutoIndexNodes(tree)) {
|
|
172
147
|
result.push({
|
|
173
|
-
params: { slug: node.slug
|
|
148
|
+
params: { slug: toSlugParams(node.slug) },
|
|
174
149
|
props: {
|
|
175
|
-
|
|
150
|
+
kind: "autoIndex",
|
|
176
151
|
autoIndex: node as AutoIndexNode,
|
|
177
152
|
breadcrumbs: buildBreadcrumbs(fullTree, node.slug, locale),
|
|
178
153
|
prev: null,
|
|
@@ -189,33 +164,26 @@ export function paths(): Array<{
|
|
|
189
164
|
// Page component
|
|
190
165
|
// ---------------------------------------------------------------------------
|
|
191
166
|
|
|
192
|
-
|
|
193
|
-
params: { slug: string[] };
|
|
194
|
-
entry: DocPageProps["entry"];
|
|
195
|
-
autoIndex?: DocPageProps["autoIndex"];
|
|
196
|
-
breadcrumbs: DocPageProps["breadcrumbs"];
|
|
197
|
-
prev: DocPageProps["prev"];
|
|
198
|
-
next: DocPageProps["next"];
|
|
199
|
-
headings: DocPageProps["headings"];
|
|
200
|
-
}
|
|
167
|
+
type PageArgs = DocPageProps & { params: { slug: string[] } };
|
|
201
168
|
|
|
202
|
-
export default function DocsPage(
|
|
169
|
+
export default function DocsPage(props: PageArgs): JSX.Element {
|
|
170
|
+
const { breadcrumbs, prev, next, headings } = props;
|
|
203
171
|
const locale = defaultLocale;
|
|
204
172
|
|
|
205
|
-
const slug = autoIndex
|
|
206
|
-
? autoIndex.slug
|
|
207
|
-
: (entry
|
|
173
|
+
const slug = props.kind === "autoIndex"
|
|
174
|
+
? props.autoIndex.slug
|
|
175
|
+
: (props.entry.data.slug ?? toRouteSlug(props.entry.slug));
|
|
208
176
|
|
|
209
|
-
const title = autoIndex ? autoIndex.label : entry
|
|
210
|
-
const description = autoIndex ? autoIndex.description : entry
|
|
177
|
+
const title = props.kind === "autoIndex" ? props.autoIndex.label : props.entry.data.title;
|
|
178
|
+
const description = props.kind === "autoIndex" ? props.autoIndex.description : props.entry.data.description;
|
|
211
179
|
|
|
212
180
|
// Locale-aware components bag — creates nav wrappers bound to the active
|
|
213
181
|
// locale so CategoryNav/CategoryTreeNav/SiteTreeNav query the right collection.
|
|
214
182
|
const components = createMdxComponents(locale);
|
|
215
183
|
|
|
216
184
|
// Resolve child hrefs for auto-index pages
|
|
217
|
-
const autoIndexChildren = autoIndex
|
|
218
|
-
? autoIndex.children
|
|
185
|
+
const autoIndexChildren = props.kind === "autoIndex"
|
|
186
|
+
? props.autoIndex.children
|
|
219
187
|
.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
220
188
|
.map((c: NavNode) => ({
|
|
221
189
|
...c,
|
|
@@ -235,7 +203,7 @@ export default function DocsPage({ entry, autoIndex, breadcrumbs, prev, next, he
|
|
|
235
203
|
// both lang (BCP-47 locale string) and navSection (filesystem-derived
|
|
236
204
|
// kebab-case slug) come from controlled, trusted sources.
|
|
237
205
|
const navSection = getNavSectionForSlug(slug);
|
|
238
|
-
const hideSidebar = entry
|
|
206
|
+
const hideSidebar = props.kind === "entry" ? props.entry.data.hide_sidebar : undefined;
|
|
239
207
|
const sidebarPersistKey = hideSidebar
|
|
240
208
|
? undefined
|
|
241
209
|
: `sidebar-${locale}-${navSection ?? "default"}`;
|
|
@@ -248,7 +216,7 @@ export default function DocsPage({ entry, autoIndex, breadcrumbs, prev, next, he
|
|
|
248
216
|
lang={locale}
|
|
249
217
|
noindex={settings.noindex}
|
|
250
218
|
hideSidebar={hideSidebar}
|
|
251
|
-
hideToc={entry
|
|
219
|
+
hideToc={props.kind === "entry" ? props.entry.data.hide_toc : undefined}
|
|
252
220
|
headings={headings}
|
|
253
221
|
canonical={canonical}
|
|
254
222
|
sidebarPersistKey={sidebarPersistKey}
|
|
@@ -276,36 +244,11 @@ export default function DocsPage({ entry, autoIndex, breadcrumbs, prev, next, he
|
|
|
276
244
|
currentPath={docsUrl(slug, locale)}
|
|
277
245
|
/>
|
|
278
246
|
}
|
|
279
|
-
afterSidebar={
|
|
280
|
-
// Pre-paint inline script: restore persisted sidebar visibility to
|
|
281
|
-
// <html data-sidebar-hidden> before first paint to avoid flash.
|
|
282
|
-
// Runs unconditionally when sidebarToggle is enabled; the attribute
|
|
283
|
-
// is only set when localStorage says "false" so the default (visible)
|
|
284
|
-
// needs no attribute and causes no layout shift.
|
|
285
|
-
settings.sidebarToggle ? (
|
|
286
|
-
<>
|
|
287
|
-
<script dangerouslySetInnerHTML={{
|
|
288
|
-
__html: `(function(){try{if(localStorage.getItem('zudo-doc-sidebar-visible')==='false'){document.documentElement.setAttribute('data-sidebar-hidden','');}}catch(e){}})();`,
|
|
289
|
-
}} />
|
|
290
|
-
{Island({
|
|
291
|
-
when: "load",
|
|
292
|
-
children: <DesktopSidebarToggle />,
|
|
293
|
-
}) as unknown as VNode}
|
|
294
|
-
</>
|
|
295
|
-
) : undefined
|
|
296
|
-
}
|
|
247
|
+
afterSidebar={<SidebarPrepaint />}
|
|
297
248
|
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
298
|
-
bodyEndComponents={
|
|
299
|
-
<>
|
|
300
|
-
<BodyEndIslands basePath={settings.base ?? "/"} />
|
|
301
|
-
{/* SidebarResizerInit: attach drag handle to #desktop-sidebar on load
|
|
302
|
-
and on AFTER_NAVIGATE_EVENT (zfb:after-swap under the Strategy B
|
|
303
|
-
SPA navigation model). Idempotent — safe on every page. */}
|
|
304
|
-
{settings.sidebarResizer && <SidebarResizerInit />}
|
|
305
|
-
</>
|
|
306
|
-
}
|
|
249
|
+
bodyEndComponents={<DocBodyEnd />}
|
|
307
250
|
>
|
|
308
|
-
{autoIndex ? (
|
|
251
|
+
{props.kind === "autoIndex" ? (
|
|
309
252
|
/* Auto-index page: category without an index.mdx.
|
|
310
253
|
Fragment (not <div>) so children become direct children of
|
|
311
254
|
<article class="zd-content">, picking up the flow-space rule
|
|
@@ -313,7 +256,7 @@ export default function DocsPage({ entry, autoIndex, breadcrumbs, prev, next, he
|
|
|
313
256
|
Wrapping in <div> would make h1/description p children-of-children
|
|
314
257
|
and the flow gap (~24px) would never apply — see #1460. */
|
|
315
258
|
<>
|
|
316
|
-
<h1 class="text-heading font-bold mb-vsp-xs">{autoIndex.label}</h1>
|
|
259
|
+
<h1 class="text-heading font-bold mb-vsp-xs">{props.autoIndex.label}</h1>
|
|
317
260
|
|
|
318
261
|
{/* Build-time date block — chrome parity (#1461). Auto-index pages
|
|
319
262
|
previously rendered without doc-meta; reference site shows it on
|
|
@@ -321,9 +264,9 @@ export default function DocsPage({ entry, autoIndex, breadcrumbs, prev, next, he
|
|
|
321
264
|
entry exists for this slug. */}
|
|
322
265
|
<DocMetainfoArea slug={slug} locale={locale} />
|
|
323
266
|
|
|
324
|
-
{autoIndex.description && (
|
|
267
|
+
{props.autoIndex.description && (
|
|
325
268
|
<p class="mb-vsp-lg text-title text-muted">
|
|
326
|
-
{autoIndex.description}
|
|
269
|
+
{props.autoIndex.description}
|
|
327
270
|
</p>
|
|
328
271
|
)}
|
|
329
272
|
<NavCardGrid children={autoIndexChildren} />
|
|
@@ -332,112 +275,24 @@ export default function DocsPage({ entry, autoIndex, breadcrumbs, prev, next, he
|
|
|
332
275
|
/* Regular doc page. Fragment (not <div>) for the same reason as
|
|
333
276
|
the auto-index branch above — see #1460. */
|
|
334
277
|
<>
|
|
335
|
-
<
|
|
336
|
-
|
|
337
|
-
{/* Build-time date block (Created / Updated / Author).
|
|
338
|
-
doc-metainfo placement — between <h1> and description.
|
|
339
|
-
Data from `.zfb/doc-history-meta.json` (esbuild-inlined, no fs). */}
|
|
340
|
-
<DocMetainfoArea slug={slug} locale={locale} />
|
|
341
|
-
|
|
342
|
-
{/* Page-level tag chips — matching doc-tags placement (#1658). */}
|
|
343
|
-
<DocTagsArea slug={slug} locale={locale} tags={entry!.data.tags} />
|
|
344
|
-
|
|
345
|
-
{entry!.data.description && (
|
|
346
|
-
<p class="mb-vsp-lg text-title text-muted">
|
|
347
|
-
{entry!.data.description}
|
|
348
|
-
</p>
|
|
349
|
-
)}
|
|
350
|
-
|
|
351
|
-
{/* Frontmatter preview — non-system, custom keys only. Returns
|
|
352
|
-
null when the entries array is empty, so pages without
|
|
353
|
-
custom frontmatter emit nothing. Custom per-key renderers
|
|
354
|
-
from frontmatter-preview-renderers.tsx produce styled cells
|
|
355
|
-
(pills, badges, etc.) instead of plain text. */}
|
|
356
|
-
<FrontmatterPreview
|
|
357
|
-
entries={buildFrontmatterPreviewEntries(entry!.data)}
|
|
358
|
-
title={t("frontmatter.preview.title", locale)}
|
|
359
|
-
keyColLabel={t("frontmatter.preview.keyCol", locale)}
|
|
360
|
-
valueColLabel={t("frontmatter.preview.valueCol", locale)}
|
|
361
|
-
renderers={frontmatterRenderers}
|
|
362
|
-
data={entry!.data as Record<string, unknown>}
|
|
363
|
-
locale={locale}
|
|
364
|
-
/>
|
|
278
|
+
<DocContentHeader entry={props.entry} slug={slug} locale={locale} />
|
|
365
279
|
|
|
366
280
|
{/* MDX content rendered via zfb's Content bridge */}
|
|
367
|
-
|
|
281
|
+
<props.entry.Content components={components} />
|
|
368
282
|
|
|
369
283
|
{/* Prev / Next pagination — placed before the document utilities
|
|
370
284
|
section to match the Astro reference order: content → pager →
|
|
371
285
|
view-source / history. In the Astro layout, BodyFootUtilArea was
|
|
372
286
|
rendered by the doc-layout wrapper after the <slot /> content,
|
|
373
287
|
so the pager (inside the slot) came first. Fixes #1535. */}
|
|
374
|
-
<
|
|
375
|
-
{prev ? (
|
|
376
|
-
<a
|
|
377
|
-
href={prev.href}
|
|
378
|
-
class="group border border-muted rounded-lg p-hsp-lg hover:border-accent"
|
|
379
|
-
>
|
|
380
|
-
<div class="flex items-center gap-hsp-xs text-caption text-muted mb-vsp-2xs">
|
|
381
|
-
<svg
|
|
382
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
383
|
-
class="h-[1.125rem] w-[1.125rem]"
|
|
384
|
-
fill="none"
|
|
385
|
-
viewBox="0 0 24 24"
|
|
386
|
-
stroke="currentColor"
|
|
387
|
-
stroke-width="2"
|
|
388
|
-
>
|
|
389
|
-
<path
|
|
390
|
-
stroke-linecap="round"
|
|
391
|
-
stroke-linejoin="round"
|
|
392
|
-
d="M15 19l-7-7 7-7"
|
|
393
|
-
/>
|
|
394
|
-
</svg>
|
|
395
|
-
<span class="no-underline">{t("nav.previous", locale)}</span>
|
|
396
|
-
</div>
|
|
397
|
-
<p class="text-small font-semibold underline group-hover:text-accent">
|
|
398
|
-
{prev.label}
|
|
399
|
-
</p>
|
|
400
|
-
</a>
|
|
401
|
-
) : (
|
|
402
|
-
<div />
|
|
403
|
-
)}
|
|
404
|
-
{next ? (
|
|
405
|
-
<a
|
|
406
|
-
href={next.href}
|
|
407
|
-
class="group border border-muted rounded-lg p-hsp-lg hover:border-accent text-right"
|
|
408
|
-
>
|
|
409
|
-
<div class="flex items-center justify-end gap-hsp-xs text-caption text-muted mb-vsp-2xs">
|
|
410
|
-
<span class="no-underline">{t("nav.next", locale)}</span>
|
|
411
|
-
<svg
|
|
412
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
413
|
-
class="h-[1.125rem] w-[1.125rem]"
|
|
414
|
-
fill="none"
|
|
415
|
-
viewBox="0 0 24 24"
|
|
416
|
-
stroke="currentColor"
|
|
417
|
-
stroke-width="2"
|
|
418
|
-
>
|
|
419
|
-
<path
|
|
420
|
-
stroke-linecap="round"
|
|
421
|
-
stroke-linejoin="round"
|
|
422
|
-
d="M9 5l7 7-7 7"
|
|
423
|
-
/>
|
|
424
|
-
</svg>
|
|
425
|
-
</div>
|
|
426
|
-
<p class="text-small font-semibold underline group-hover:text-accent">
|
|
427
|
-
{next.label}
|
|
428
|
-
</p>
|
|
429
|
-
</a>
|
|
430
|
-
) : (
|
|
431
|
-
<div />
|
|
432
|
-
)}
|
|
433
|
-
</nav>
|
|
288
|
+
<DocPager prev={prev} next={next} locale={locale} />
|
|
434
289
|
|
|
435
290
|
{/* Document utilities (revision history + view-source link) — skipped for unlisted pages */}
|
|
436
|
-
{!entry
|
|
291
|
+
{!props.entry.data.unlisted && (
|
|
437
292
|
<DocHistoryArea
|
|
438
293
|
slug={slug}
|
|
439
294
|
locale={locale}
|
|
440
|
-
entrySlug={entry
|
|
295
|
+
entrySlug={props.entry.slug}
|
|
441
296
|
contentDir={settings.docsDir}
|
|
442
297
|
/>
|
|
443
298
|
)}
|
|
@@ -12,16 +12,14 @@
|
|
|
12
12
|
// → collectTags() counts unique tags for the tag section header
|
|
13
13
|
// → DocLayoutWithDefaults renders the page with no sidebar/TOC
|
|
14
14
|
|
|
15
|
-
import { loadDocs } from "./_data";
|
|
16
15
|
import { settings } from "@/config/settings";
|
|
17
16
|
import { defaultLocale, t } from "@/config/i18n";
|
|
18
17
|
import { withBase } from "@/utils/base";
|
|
19
18
|
import {
|
|
20
19
|
buildNavTree,
|
|
21
20
|
groupSatelliteNodes,
|
|
22
|
-
isNavVisible,
|
|
23
21
|
} from "@/utils/docs";
|
|
24
|
-
import {
|
|
22
|
+
import { resolveNavSource } from "./lib/_nav-source-docs";
|
|
25
23
|
import { getCategoryOrder } from "@/utils/nav-scope";
|
|
26
24
|
import { collectTags } from "@/utils/tags";
|
|
27
25
|
import { toRouteSlug } from "@/utils/slug";
|
|
@@ -41,20 +39,15 @@ export const frontmatter = { title: "Home" };
|
|
|
41
39
|
export default function IndexPage(): JSX.Element {
|
|
42
40
|
const locale = defaultLocale;
|
|
43
41
|
|
|
44
|
-
//
|
|
45
|
-
// (
|
|
46
|
-
|
|
47
|
-
const allDocs = loadDocs("docs");
|
|
48
|
-
const docs = allDocs.filter((doc) => !doc.data.draft);
|
|
49
|
-
const categoryMeta = loadCategoryMeta(settings.docsDir);
|
|
50
|
-
const navDocs = docs.filter(isNavVisible);
|
|
42
|
+
// Identity-stable nav source (draft-filtered, unlisted retained). navDocs is
|
|
43
|
+
// pre-filtered (isNavVisible) and shared with the nav-tree fast-path.
|
|
44
|
+
const { navDocs, categoryMeta } = resolveNavSource(locale, undefined);
|
|
51
45
|
const tree = buildNavTree(navDocs, locale, categoryMeta);
|
|
52
46
|
const categoryOrder = getCategoryOrder();
|
|
53
47
|
const groupedTree = groupSatelliteNodes(tree, categoryOrder);
|
|
54
48
|
|
|
55
|
-
const tagDocs = docs.filter(isNavVisible);
|
|
56
49
|
const tagCount = collectTags(
|
|
57
|
-
|
|
50
|
+
navDocs,
|
|
58
51
|
(id, data) => data.slug ?? toRouteSlug(id),
|
|
59
52
|
).size;
|
|
60
53
|
|