create-zudo-doc 0.2.0-next.6 → 0.2.0-next.7
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/constants.d.ts +2 -0
- package/dist/constants.js +26 -0
- package/dist/scaffold.js +11 -5
- package/dist/settings-gen.js +9 -0
- package/dist/zfb-config-gen.js +15 -0
- package/package.json +1 -1
- package/templates/base/pages/_data.ts +28 -11
- package/templates/base/pages/docs/[[...slug]].tsx +52 -129
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +6 -6
- package/templates/base/pages/lib/_doc-page-shell.tsx +229 -0
- package/templates/base/pages/lib/_doc-route-paths.ts +101 -0
- package/templates/base/pages/lib/_extract-headings.ts +263 -33
- package/templates/base/pages/lib/_head-with-defaults.tsx +12 -14
- package/templates/base/pages/lib/_nav-source-cache.ts +3 -3
- package/templates/base/pages/lib/_nav-source-docs.ts +9 -9
- package/templates/base/pages/lib/locale-merge.ts +15 -8
- package/templates/base/src/config/color-scheme-utils.ts +5 -3
- package/templates/base/src/utils/base.ts +13 -1
- package/templates/base/src/utils/dedent.ts +1 -1
- package/templates/base/src/utils/docs.ts +21 -4
- package/templates/base/src/utils/smart-break.tsx +2 -2
- package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +2 -3
- package/templates/features/docTags/files/pages/docs/tags/index.tsx +2 -3
- package/templates/features/i18n/files/pages/[locale]/docs/[[...slug]].tsx +52 -126
- package/templates/features/tagGovernance/files/scripts/tags-audit.ts +4 -1
- package/templates/features/versioning/files/pages/v/[version]/[locale]/docs/[[...slug]].tsx +61 -125
- package/templates/features/versioning/files/pages/v/[version]/docs/[[...slug]].tsx +62 -131
- package/templates/base/src/components/html-preview/highlighted-code.tsx +0 -74
- package/templates/base/src/components/html-preview/html-preview.tsx +0 -108
- package/templates/base/src/components/html-preview/preflight.ts +0 -112
- package/templates/base/src/components/html-preview/preview-base.tsx +0 -159
- package/templates/base/src/components/mobile-toc.tsx +0 -94
- package/templates/base/src/components/toc.tsx +0 -63
package/dist/constants.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface LightDarkPairing {
|
|
|
5
5
|
}
|
|
6
6
|
export declare const LIGHT_DARK_PAIRINGS: LightDarkPairing[];
|
|
7
7
|
export declare const SINGLE_SCHEMES: string[];
|
|
8
|
+
export declare const LIGHT_SCHEMES: string[];
|
|
8
9
|
export interface SupportedLang {
|
|
9
10
|
value: string;
|
|
10
11
|
label: string;
|
|
@@ -18,3 +19,4 @@ export interface Feature {
|
|
|
18
19
|
cliFlag: string;
|
|
19
20
|
}
|
|
20
21
|
export declare const FEATURES: Feature[];
|
|
22
|
+
export declare const HEADER_RIGHT_LABELS: Record<string, string>;
|
package/dist/constants.js
CHANGED
|
@@ -55,6 +55,19 @@ export const SINGLE_SCHEMES = [
|
|
|
55
55
|
"Gruvbox Light",
|
|
56
56
|
"Ayu Light",
|
|
57
57
|
];
|
|
58
|
+
// Light-only subset of SINGLE_SCHEMES. Used by the preset generator to populate
|
|
59
|
+
// the "Light scheme" dropdown (dark schemes are derived as SINGLE_SCHEMES minus these).
|
|
60
|
+
export const LIGHT_SCHEMES = [
|
|
61
|
+
"Default Light",
|
|
62
|
+
"GitHub Light",
|
|
63
|
+
"Catppuccin Latte",
|
|
64
|
+
"Solarized Light",
|
|
65
|
+
"Rose Pine Dawn",
|
|
66
|
+
"Atom One Light",
|
|
67
|
+
"Everforest Light",
|
|
68
|
+
"Gruvbox Light",
|
|
69
|
+
"Ayu Light",
|
|
70
|
+
];
|
|
58
71
|
export const SUPPORTED_LANGS = [
|
|
59
72
|
{ value: "en", label: "English" },
|
|
60
73
|
{ value: "ja", label: "Japanese" },
|
|
@@ -222,3 +235,16 @@ export const FEATURES = [
|
|
|
222
235
|
cliFlag: "footer-taglist",
|
|
223
236
|
},
|
|
224
237
|
];
|
|
238
|
+
// Display labels for header-right items. Keys are canonical component/trigger
|
|
239
|
+
// names from HeaderRightComponentName / HeaderRightTriggerName
|
|
240
|
+
// (src/config/settings-types.ts in the host); they are deliberately not imported
|
|
241
|
+
// here so constants.ts stays pure data with no cross-package dependencies.
|
|
242
|
+
export const HEADER_RIGHT_LABELS = {
|
|
243
|
+
"version-switcher": "Version switcher",
|
|
244
|
+
"design-token-panel": "Design token panel (trigger)",
|
|
245
|
+
"ai-chat": "AI chat (trigger)",
|
|
246
|
+
"github-link": "GitHub link",
|
|
247
|
+
"theme-toggle": "Theme toggle",
|
|
248
|
+
search: "Search",
|
|
249
|
+
"language-switcher": "Language switcher",
|
|
250
|
+
};
|
package/dist/scaffold.js
CHANGED
|
@@ -258,16 +258,22 @@ function generatePackageJson(choices) {
|
|
|
258
258
|
// disabled, reproducible CSS-Modules scoped names (project-relative paths),
|
|
259
259
|
// dev-mode git-restore detection, Tailwind temp-file cleanup, and a
|
|
260
260
|
// near-miss `"use client"` directive scanner.
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
// next.33 added the opt-in hierarchical heading-ID strategy
|
|
262
|
+
// (Takazudo/zudo-front-builder#871): `markdown.features.headingIds.strategy`.
|
|
263
|
+
// The generated config + TOC builder use it via settings.headingIdStrategy.
|
|
264
|
+
// next.35 fixes resolve_links rewriting bare same-page `[text](#anchor)` /
|
|
265
|
+
// `[text](?query)` links to `/<parent-dir>/#anchor` (zudolab/zudo-doc#1948,
|
|
266
|
+
// upstream Takazudo/zudo-front-builder#875).
|
|
267
|
+
"@takazudo/zfb": "0.1.0-next.35",
|
|
268
|
+
"@takazudo/zfb-runtime": "0.1.0-next.35",
|
|
263
269
|
// zfb-adapter-cloudflare — required for any route with `prerender = false`.
|
|
264
270
|
// Pinned in lockstep with @takazudo/zfb.
|
|
265
|
-
"@takazudo/zfb-adapter-cloudflare": "0.1.0-next.
|
|
271
|
+
"@takazudo/zfb-adapter-cloudflare": "0.1.0-next.35",
|
|
266
272
|
// @takazudo/zudo-doc — published from this monorepo via
|
|
267
273
|
// .github/workflows/publish-zudo-doc.yml. The pin here is bumped in
|
|
268
274
|
// lockstep by scripts/release-create-zudo-doc.sh whenever zudo-doc's
|
|
269
275
|
// version moves, so a fresh scaffold pulls the version we just published.
|
|
270
|
-
"@takazudo/zudo-doc": "^0.2.0-next.
|
|
276
|
+
"@takazudo/zudo-doc": "^0.2.0-next.7",
|
|
271
277
|
// zod — used by the generated zfb.config.ts. zfb-config-gen emits
|
|
272
278
|
// `import { z } from "zod"` for the content-collection schema +
|
|
273
279
|
// `z.toJSONSchema(...)` conversion. Without this dep, the consumer
|
|
@@ -325,7 +331,7 @@ function generatePackageJson(choices) {
|
|
|
325
331
|
// @takazudo/zudo-doc/integrations/doc-history which in turn imports
|
|
326
332
|
// @takazudo/zudo-doc-history-server/git-history. Without this dep the
|
|
327
333
|
// plugin host fails at init with ERR_MODULE_NOT_FOUND — W8A (#1739).
|
|
328
|
-
deps["@takazudo/zudo-doc-history-server"] = "^0.2.0-next.
|
|
334
|
+
deps["@takazudo/zudo-doc-history-server"] = "^0.2.0-next.7";
|
|
329
335
|
// W7A (#1736): doc-history-plugin.mjs spawns `tsx -e <inline-script>` to
|
|
330
336
|
// run the v2 runtime in a TS-aware Node subprocess; without tsx the
|
|
331
337
|
// plugin's preBuild step exits with ENOENT before zfb finishes config
|
package/dist/settings-gen.js
CHANGED
|
@@ -130,6 +130,15 @@ export function generateSettingsFile(choices) {
|
|
|
130
130
|
else {
|
|
131
131
|
lines.push(` designTokenPanel: false as boolean,`);
|
|
132
132
|
}
|
|
133
|
+
lines.push(` tocMinDepth: 2 as number,`);
|
|
134
|
+
lines.push(` tocMaxDepth: 4 as number,`);
|
|
135
|
+
// Heading-ID (anchor) strategy — single source of truth shared by
|
|
136
|
+
// zfb.config.ts (markdown.features.headingIds) and the host TOC builder
|
|
137
|
+
// (pages/lib/_extract-headings.ts). "hierarchical" emits ancestor-prefixed
|
|
138
|
+
// anchors (foo / foo-moo / foo-moo-mew); "flat" is zfb's legacy scheme.
|
|
139
|
+
// Default to "hierarchical": safe for greenfield (no existing deep links to
|
|
140
|
+
// break) and the recommended scheme (upstream zfb#871).
|
|
141
|
+
lines.push(` headingIdStrategy: "hierarchical" as "flat" | "hierarchical",`);
|
|
133
142
|
if (choices.features.includes("sidebarResizer")) {
|
|
134
143
|
lines.push(` sidebarResizer: true as boolean,`);
|
|
135
144
|
}
|
package/dist/zfb-config-gen.js
CHANGED
|
@@ -152,6 +152,7 @@ export function generateZfbConfig(choices) {
|
|
|
152
152
|
lines.push(` options: {`);
|
|
153
153
|
lines.push(` docsDir: settings.docsDir,`);
|
|
154
154
|
lines.push(` locales: localeRecord,`);
|
|
155
|
+
lines.push(` base: settings.base,`);
|
|
155
156
|
lines.push(` },`);
|
|
156
157
|
lines.push(` },`);
|
|
157
158
|
lines.push(` ]`);
|
|
@@ -211,6 +212,16 @@ export function generateZfbConfig(choices) {
|
|
|
211
212
|
lines.push(` dir: locale.dir,`);
|
|
212
213
|
lines.push(` routePrefix: \`/\${code}/docs/\`,`);
|
|
213
214
|
lines.push(` })),`);
|
|
215
|
+
lines.push(` // Versioned collections: each version's EN dir + per-locale dirs.`);
|
|
216
|
+
lines.push(` ...(settings.versions`);
|
|
217
|
+
lines.push(` ? settings.versions.flatMap((version) => [`);
|
|
218
|
+
lines.push(` { dir: version.docsDir, routePrefix: \`/v/\${version.slug}/docs/\` },`);
|
|
219
|
+
lines.push(` ...Object.entries(version.locales ?? {}).map(([code, locale]) => ({`);
|
|
220
|
+
lines.push(` dir: locale.dir,`);
|
|
221
|
+
lines.push(` routePrefix: \`/v/\${version.slug}/\${code}/docs/\`,`);
|
|
222
|
+
lines.push(` })),`);
|
|
223
|
+
lines.push(` ])`);
|
|
224
|
+
lines.push(` : []),`);
|
|
214
225
|
lines.push(` ],`);
|
|
215
226
|
lines.push(` onBrokenLinks: "warn",`);
|
|
216
227
|
lines.push(` },`);
|
|
@@ -271,6 +282,10 @@ export function generateZfbConfig(choices) {
|
|
|
271
282
|
lines.push(` imageDimensions: {},`);
|
|
272
283
|
lines.push(` // warn-only link validation — failOnBroken: false never fails the build.`);
|
|
273
284
|
lines.push(` linkValidation: { failOnBroken: false },`);
|
|
285
|
+
lines.push(` // Heading-ID (anchor) strategy — single source of truth in`);
|
|
286
|
+
lines.push(` // settings.headingIdStrategy, also mirrored by the host TOC builder`);
|
|
287
|
+
lines.push(` // (pages/lib/_extract-headings.ts) so TOC anchors match rendered ids.`);
|
|
288
|
+
lines.push(` headingIds: { strategy: settings.headingIdStrategy },`);
|
|
274
289
|
lines.push(` },`);
|
|
275
290
|
lines.push(` },`);
|
|
276
291
|
lines.push(` plugins: integrationPlugins,`);
|
package/package.json
CHANGED
|
@@ -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 type { DocPageEntry } from "./lib/doc-page-props";
|
|
16
17
|
import { toRouteSlug } from "@/utils/slug";
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
@@ -79,7 +80,7 @@ export type ZfbDocsEntry = CollectionEntry<ZfbDocsData> & {
|
|
|
79
80
|
* - `collection` — the collection name, for DocsEntry compat
|
|
80
81
|
*/
|
|
81
82
|
export function getDocs(collectionName: string): ZfbDocsEntry[] {
|
|
82
|
-
const entries = getCollection(collectionName)
|
|
83
|
+
const entries = getCollection<ZfbDocsData>(collectionName);
|
|
83
84
|
return entries.map((e) => ({
|
|
84
85
|
...e,
|
|
85
86
|
// Astro-compat: strip a trailing `/index` from the entry id so
|
|
@@ -123,27 +124,43 @@ export function bridgeEntries<T = ZfbDocsData>(
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
/**
|
|
126
|
-
*
|
|
127
|
+
* Typed bridge from a raw zfb collection result to `DocPageEntry[]`.
|
|
127
128
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* `
|
|
129
|
+
* This is the **single, justified** cast at the zfb/DocsEntry boundary.
|
|
130
|
+
* `CollectionEntry<ZfbDocsData> & { id, collection }` structurally satisfies
|
|
131
|
+
* `DocPageEntry` because:
|
|
132
|
+
* - `id` and `collection` are added by `bridgeEntries`
|
|
133
|
+
* - `data` (ZfbDocsData) structurally satisfies `DocsEntry.data` (all
|
|
134
|
+
* required/optional fields are present; the index signature is wider)
|
|
135
|
+
* - `body`, `slug`, `module_specifier`, `Content` are provided by
|
|
136
|
+
* `CollectionEntry<ZfbDocsData>`
|
|
137
|
+
* The plain `as DocPageEntry[]` (not `as unknown as`) is intentional — it
|
|
138
|
+
* expresses that this is a well-understood structural subtype relationship,
|
|
139
|
+
* not an escape from the type system. The zfb type is the source of truth;
|
|
140
|
+
* DocsEntry/DocPageEntry are local compatibility shapes for @/utils/docs.
|
|
131
141
|
*/
|
|
132
|
-
export function
|
|
133
|
-
|
|
142
|
+
export function bridgeDocsEntries(
|
|
143
|
+
entries: ReadonlyArray<CollectionEntry<ZfbDocsData>>,
|
|
144
|
+
collectionName: string,
|
|
145
|
+
): DocPageEntry[] {
|
|
146
|
+
return bridgeEntries(entries, collectionName) as DocPageEntry[];
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
/**
|
|
137
150
|
* One-shot helper for paths()/render-time pages that just need a
|
|
138
|
-
* `DocsEntry[]` for `@/utils/docs` consumption — wraps `getDocs`
|
|
139
|
-
*
|
|
140
|
-
* any page that previously did
|
|
151
|
+
* `DocsEntry[]` for `@/utils/docs` consumption — wraps `getDocs` so
|
|
152
|
+
* call sites stay one-line. Use this from any page that previously did
|
|
141
153
|
* `getCollection("docs") as unknown as DocsEntry[]` — that idiom
|
|
142
154
|
* silently dropped the `id`/`collection` fields the utility helpers
|
|
143
155
|
* read, which threw `Cannot read properties of undefined` at runtime.
|
|
156
|
+
*
|
|
157
|
+
* `ZfbDocsEntry` structurally satisfies `DocsEntry`: it carries `id`,
|
|
158
|
+
* `collection`, `data` (ZfbDocsData satisfies DocsEntry.data field-for-
|
|
159
|
+
* field), `body`, plus the zfb-specific extras (`slug`, `Content`, etc.)
|
|
160
|
+
* that DocsEntry does not require.
|
|
144
161
|
*/
|
|
145
162
|
export function loadDocs(collectionName: string): DocsEntry[] {
|
|
146
|
-
return
|
|
163
|
+
return getDocs(collectionName);
|
|
147
164
|
}
|
|
148
165
|
|
|
149
166
|
/**
|
|
@@ -22,44 +22,32 @@
|
|
|
22
22
|
// Locale: defaultLocale (EN). Non-default locales are handled by
|
|
23
23
|
// pages/[locale]/docs/[[...slug]].tsx.
|
|
24
24
|
|
|
25
|
-
import type { DocsEntry } from "@/types/docs-entry";
|
|
26
25
|
import { settings } from "@/config/settings";
|
|
27
26
|
import { defaultLocale } from "@/config/i18n";
|
|
28
|
-
import { docsUrl } from "@/utils/base";
|
|
27
|
+
import { docsUrl, absoluteUrl } from "@/utils/base";
|
|
29
28
|
import {
|
|
30
29
|
buildNavTree,
|
|
31
30
|
buildBreadcrumbs,
|
|
32
|
-
flattenTree,
|
|
33
|
-
findNode,
|
|
34
31
|
collectAutoIndexNodes,
|
|
35
32
|
type NavNode,
|
|
36
33
|
} from "@/utils/docs";
|
|
37
34
|
import { getNavSectionForSlug, getNavSubtree } from "@/utils/nav-scope";
|
|
38
35
|
import { toRouteSlug, toSlugParams } from "@/utils/slug";
|
|
39
|
-
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
40
|
-
import { Breadcrumb } from "@takazudo/zudo-doc/breadcrumb";
|
|
41
|
-
import { NavCardGrid } from "@takazudo/zudo-doc/nav-indexing";
|
|
42
36
|
// Shared MDX-tag → Preact-component bag. Includes htmlOverrides
|
|
43
37
|
// (native typography), HtmlPreviewWrapper (Island), and stub bindings
|
|
44
38
|
// for every other custom tag the MDX corpus references — see
|
|
45
39
|
// `pages/_mdx-components.ts` for the full list and rationale.
|
|
46
40
|
import { createMdxComponents } from "../_mdx-components";
|
|
47
|
-
import { FooterWithDefaults } from "../lib/_footer-with-defaults";
|
|
48
41
|
import { DocHistoryArea } from "../lib/_doc-history-area";
|
|
49
42
|
import { DocMetainfoArea } from "../lib/_doc-metainfo-area";
|
|
50
|
-
import { SidebarWithDefaults } from "../lib/_sidebar-with-defaults";
|
|
51
|
-
import { HeaderWithDefaults } from "../lib/_header-with-defaults";
|
|
52
|
-
import { HeadWithDefaults } from "../lib/_head-with-defaults";
|
|
53
|
-
import { composeMetaTitle } from "../lib/_compose-meta-title";
|
|
54
43
|
import { buildInlineVersionSwitcher } from "../lib/_inline-version-switcher";
|
|
55
44
|
import type { JSX } from "preact";
|
|
56
45
|
import { resolveNavSource } from "../lib/_nav-source-docs";
|
|
57
46
|
import { extractHeadings } from "../lib/_extract-headings";
|
|
58
47
|
import type { DocPageEntry, AutoIndexNode, DocPageEntryProps, DocPageAutoIndexProps } from "../lib/doc-page-props";
|
|
59
|
-
import { DocPager } from "../lib/_doc-pager";
|
|
60
48
|
import { DocContentHeader } from "../lib/_doc-content-header";
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
49
|
+
import { DocPageShell } from "../lib/_doc-page-shell";
|
|
50
|
+
import { resolveDocPrevNext, flattenSubtree } from "../lib/_doc-route-paths";
|
|
63
51
|
|
|
64
52
|
export const frontmatter = { title: "Docs" };
|
|
65
53
|
|
|
@@ -94,9 +82,9 @@ export function paths(): Array<{
|
|
|
94
82
|
const { docs, navDocs, categoryMeta } = resolveNavSource(locale, undefined);
|
|
95
83
|
|
|
96
84
|
// Nav docs: exclude unlisted (for sidebar/prev-next) but keep for breadcrumbs
|
|
97
|
-
const tree = buildNavTree(navDocs
|
|
85
|
+
const tree = buildNavTree(navDocs, locale, categoryMeta);
|
|
98
86
|
// Full tree (including unlisted) for accurate breadcrumbs
|
|
99
|
-
const fullTree = buildNavTree(docs
|
|
87
|
+
const fullTree = buildNavTree(docs, locale, categoryMeta);
|
|
100
88
|
|
|
101
89
|
const result: Array<{ params: { slug: string[] }; props: DocPageProps }> = [];
|
|
102
90
|
|
|
@@ -105,29 +93,15 @@ export function paths(): Array<{
|
|
|
105
93
|
const slug = entry.data.slug ?? toRouteSlug(entry.slug);
|
|
106
94
|
const navSection = getNavSectionForSlug(slug);
|
|
107
95
|
const subtree = getNavSubtree(tree, navSection);
|
|
108
|
-
const flat = flattenTree(subtree);
|
|
109
|
-
const idx = flat.findIndex((n) => n.slug === slug);
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const found = findNode(tree, entry.data.pagination_prev);
|
|
120
|
-
prevNode = found ?? prevNode;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (entry.data.pagination_next !== undefined) {
|
|
124
|
-
if (entry.data.pagination_next === null) {
|
|
125
|
-
nextNode = null;
|
|
126
|
-
} else {
|
|
127
|
-
const found = findNode(tree, entry.data.pagination_next);
|
|
128
|
-
nextNode = found ?? nextNode;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
97
|
+
// Prev/next + frontmatter pagination overrides resolved against THIS
|
|
98
|
+
// route's own `tree`. Latest route — hrefs stay unversioned (no rewrite).
|
|
99
|
+
const { prev: prevNode, next: nextNode } = resolveDocPrevNext(
|
|
100
|
+
tree,
|
|
101
|
+
flattenSubtree(subtree),
|
|
102
|
+
slug,
|
|
103
|
+
entry.data,
|
|
104
|
+
);
|
|
131
105
|
|
|
132
106
|
result.push({
|
|
133
107
|
params: { slug: toSlugParams(slug) },
|
|
@@ -181,7 +155,8 @@ export default function DocsPage(props: PageArgs): JSX.Element {
|
|
|
181
155
|
// locale so CategoryNav/CategoryTreeNav/SiteTreeNav query the right collection.
|
|
182
156
|
const components = createMdxComponents(locale);
|
|
183
157
|
|
|
184
|
-
// Resolve child hrefs for auto-index pages
|
|
158
|
+
// Resolve child hrefs for auto-index pages — latest route keeps the nav
|
|
159
|
+
// node's own docsUrl href (fallback for a noPage parent without an href).
|
|
185
160
|
const autoIndexChildren = props.kind === "autoIndex"
|
|
186
161
|
? props.autoIndex.children
|
|
187
162
|
.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
@@ -191,12 +166,9 @@ export default function DocsPage(props: PageArgs): JSX.Element {
|
|
|
191
166
|
}))
|
|
192
167
|
: [];
|
|
193
168
|
|
|
194
|
-
// Canonical URL —
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
const canonical = settings.siteUrl
|
|
198
|
-
? settings.siteUrl.replace(/\/$/, "") + pageUrl
|
|
199
|
-
: undefined;
|
|
169
|
+
// Canonical URL — base-prefixed page path, absolutized against siteUrl.
|
|
170
|
+
const currentPath = docsUrl(slug, locale);
|
|
171
|
+
const canonical = absoluteUrl(currentPath);
|
|
200
172
|
|
|
201
173
|
// Persist key: locale + nav-section so the sidebar DOM node is reused
|
|
202
174
|
// across same-locale + same-section navigations only. No sanitizer needed —
|
|
@@ -209,95 +181,46 @@ export default function DocsPage(props: PageArgs): JSX.Element {
|
|
|
209
181
|
: `sidebar-${locale}-${navSection ?? "default"}`;
|
|
210
182
|
|
|
211
183
|
return (
|
|
212
|
-
<
|
|
213
|
-
|
|
184
|
+
<DocPageShell
|
|
185
|
+
kind={props.kind}
|
|
186
|
+
locale={locale}
|
|
187
|
+
slug={slug}
|
|
188
|
+
title={title}
|
|
214
189
|
description={description}
|
|
215
|
-
head={<HeadWithDefaults title={title} description={description} canonical={canonical} />}
|
|
216
|
-
lang={locale}
|
|
217
|
-
noindex={settings.noindex}
|
|
218
|
-
hideSidebar={hideSidebar}
|
|
219
|
-
hideToc={props.kind === "entry" ? props.entry.data.hide_toc : undefined}
|
|
220
|
-
headings={headings}
|
|
221
190
|
canonical={canonical}
|
|
191
|
+
breadcrumbs={breadcrumbs}
|
|
192
|
+
prev={prev}
|
|
193
|
+
next={next}
|
|
194
|
+
headings={headings}
|
|
195
|
+
navSection={navSection}
|
|
222
196
|
sidebarPersistKey={sidebarPersistKey}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
197
|
+
hideSidebar={hideSidebar}
|
|
198
|
+
hideToc={props.kind === "entry" ? props.entry.data.hide_toc : undefined}
|
|
199
|
+
currentPath={currentPath}
|
|
200
|
+
versionSwitcher={buildInlineVersionSwitcher(slug, locale)}
|
|
201
|
+
autoIndexLabel={props.kind === "autoIndex" ? props.autoIndex.label : undefined}
|
|
202
|
+
autoIndexChildren={autoIndexChildren}
|
|
203
|
+
metainfoSlot={
|
|
204
|
+
props.kind === "autoIndex" ? <DocMetainfoArea slug={slug} locale={locale} /> : null
|
|
230
205
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
<
|
|
234
|
-
items={breadcrumbs}
|
|
235
|
-
rightSlot={buildInlineVersionSwitcher(slug, locale)}
|
|
236
|
-
/>
|
|
206
|
+
contentHeaderSlot={
|
|
207
|
+
props.kind === "entry" ? (
|
|
208
|
+
<DocContentHeader entry={props.entry} slug={slug} locale={locale} />
|
|
237
209
|
) : undefined
|
|
238
210
|
}
|
|
239
|
-
|
|
240
|
-
<
|
|
241
|
-
currentSlug={slug}
|
|
242
|
-
lang={locale}
|
|
243
|
-
navSection={getNavSectionForSlug(slug)}
|
|
244
|
-
currentPath={docsUrl(slug, locale)}
|
|
245
|
-
/>
|
|
211
|
+
contentSlot={
|
|
212
|
+
props.kind === "entry" ? <props.entry.Content components={components} /> : undefined
|
|
246
213
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<>
|
|
259
|
-
<h1 class="text-heading font-bold mb-vsp-xs">{props.autoIndex.label}</h1>
|
|
260
|
-
|
|
261
|
-
{/* Build-time date block — chrome parity (#1461). Auto-index pages
|
|
262
|
-
previously rendered without doc-meta; reference site shows it on
|
|
263
|
-
every docs page. The component returns null when no manifest
|
|
264
|
-
entry exists for this slug. */}
|
|
265
|
-
<DocMetainfoArea slug={slug} locale={locale} />
|
|
266
|
-
|
|
267
|
-
{props.autoIndex.description && (
|
|
268
|
-
<p class="mb-vsp-lg text-title text-muted">
|
|
269
|
-
{props.autoIndex.description}
|
|
270
|
-
</p>
|
|
271
|
-
)}
|
|
272
|
-
<NavCardGrid children={autoIndexChildren} />
|
|
273
|
-
</>
|
|
274
|
-
) : (
|
|
275
|
-
/* Regular doc page. Fragment (not <div>) for the same reason as
|
|
276
|
-
the auto-index branch above — see #1460. */
|
|
277
|
-
<>
|
|
278
|
-
<DocContentHeader entry={props.entry} slug={slug} locale={locale} />
|
|
279
|
-
|
|
280
|
-
{/* MDX content rendered via zfb's Content bridge */}
|
|
281
|
-
<props.entry.Content components={components} />
|
|
282
|
-
|
|
283
|
-
{/* Prev / Next pagination — placed before the document utilities
|
|
284
|
-
section to match the Astro reference order: content → pager →
|
|
285
|
-
view-source / history. In the Astro layout, BodyFootUtilArea was
|
|
286
|
-
rendered by the doc-layout wrapper after the <slot /> content,
|
|
287
|
-
so the pager (inside the slot) came first. Fixes #1535. */}
|
|
288
|
-
<DocPager prev={prev} next={next} locale={locale} />
|
|
289
|
-
|
|
290
|
-
{/* Document utilities (revision history + view-source link) — skipped for unlisted pages */}
|
|
291
|
-
{!props.entry.data.unlisted && (
|
|
292
|
-
<DocHistoryArea
|
|
293
|
-
slug={slug}
|
|
294
|
-
locale={locale}
|
|
295
|
-
entrySlug={props.entry.slug}
|
|
296
|
-
contentDir={settings.docsDir}
|
|
297
|
-
/>
|
|
298
|
-
)}
|
|
299
|
-
</>
|
|
300
|
-
)}
|
|
301
|
-
</DocLayoutWithDefaults>
|
|
214
|
+
docHistorySlot={
|
|
215
|
+
props.kind === "entry" && !props.entry.data.unlisted ? (
|
|
216
|
+
<DocHistoryArea
|
|
217
|
+
slug={slug}
|
|
218
|
+
locale={locale}
|
|
219
|
+
entrySlug={props.entry.slug}
|
|
220
|
+
contentDir={settings.docsDir}
|
|
221
|
+
/>
|
|
222
|
+
) : null
|
|
223
|
+
}
|
|
224
|
+
/>
|
|
302
225
|
);
|
|
303
226
|
}
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
// (b11-2 pattern).
|
|
14
14
|
//
|
|
15
15
|
// Date formatting uses Intl.DateTimeFormat (browser-safe). We do NOT
|
|
16
|
-
// import `formatDate` from `src/utils/git-info.ts`
|
|
17
|
-
//
|
|
18
|
-
// would be dragged into the client bundle
|
|
16
|
+
// import the old `formatDate` from `src/utils/git-info.ts` — that module
|
|
17
|
+
// carried top-level Node.js imports (`execFileSync`, `existsSync`) that
|
|
18
|
+
// would be dragged into the client bundle (the B-11 lesson). That file
|
|
19
|
+
// was removed in S1 cleanup (#1928); the mirror below is the canonical copy.
|
|
19
20
|
//
|
|
20
21
|
// Labels are resolved from the project's i18n table so non-default
|
|
21
22
|
// locales (e.g. /ja/) get translated "作成" / "更新" strings.
|
|
@@ -36,9 +37,8 @@ import { toHistorySlug } from "@/utils/slug";
|
|
|
36
37
|
import docHistoryMeta from "#doc-history-meta";
|
|
37
38
|
|
|
38
39
|
// BCP-47 locale tag mapping used by Intl.DateTimeFormat.
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// (`execFileSync`, `existsSync`) — the B-11 lesson applies here too.
|
|
40
|
+
// Originally mirrored from `src/utils/git-info.ts` (removed in S1 #1928).
|
|
41
|
+
// The formatDate function below is the stable copy; kept in sync manually.
|
|
42
42
|
const LOCALE_TO_BCP47: Record<string, string> = {
|
|
43
43
|
en: "en-US",
|
|
44
44
|
ja: "ja-JP",
|