create-zudo-doc 0.2.0 → 0.2.2
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/api.js +4 -1
- package/dist/cli.js +4 -6
- package/dist/compose.d.ts +2 -3
- package/dist/compose.js +7 -4
- package/dist/features/tauri.d.ts +10 -5
- package/dist/features/tauri.js +49 -6
- package/dist/preset.js +11 -0
- package/dist/prompts.js +2 -6
- package/dist/scaffold.js +15 -9
- package/dist/settings-gen.js +9 -6
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +25 -0
- package/dist/zfb-config-gen.js +11 -50
- package/package.json +1 -1
- package/templates/base/pages/_data.ts +10 -23
- package/templates/base/pages/docs/[[...slug]].tsx +27 -168
- package/templates/base/pages/lib/_body-end-islands.tsx +3 -0
- package/templates/base/pages/lib/_doc-content-header.tsx +24 -4
- package/templates/base/pages/lib/_doc-history-area.tsx +21 -5
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +22 -2
- package/templates/base/pages/lib/_doc-page-renderer.tsx +192 -0
- package/templates/base/pages/lib/_doc-page-shell.tsx +3 -2
- package/templates/base/pages/lib/_doc-route-entries.ts +188 -0
- package/templates/base/pages/lib/_doc-tags-area.tsx +7 -2
- package/templates/base/pages/lib/_footer-with-defaults.tsx +38 -27
- package/templates/base/pages/lib/_head-with-defaults.tsx +7 -10
- package/templates/base/pages/lib/_header-with-defaults.tsx +54 -89
- package/templates/base/pages/lib/_inline-version-switcher.tsx +5 -4
- package/templates/base/pages/lib/_nav-data-prep.ts +137 -0
- package/templates/base/pages/lib/_nav-source-docs.ts +10 -6
- package/templates/base/pages/lib/_search-widget-script.ts +32 -9
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +15 -60
- package/templates/base/pages/lib/locale-merge.ts +1 -1
- package/templates/base/pages/lib/route-enumerators.ts +11 -7
- package/templates/base/plugins/connect-adapter.mjs +30 -1
- package/templates/base/plugins/copy-public-plugin.mjs +10 -2
- package/templates/base/plugins/search-index-plugin.mjs +20 -8
- package/templates/base/src/components/ai-chat-modal.tsx +2 -0
- package/templates/base/src/components/doc-history.tsx +2 -0
- package/templates/base/src/components/image-enlarge.tsx +2 -0
- package/templates/base/src/components/sidebar-toggle.tsx +1 -1
- package/templates/base/src/components/sidebar-tree.tsx +11 -5
- package/templates/base/src/components/theme-toggle.tsx +18 -102
- package/templates/base/src/config/color-schemes.ts +4 -0
- package/templates/base/src/config/docs-schema.ts +94 -0
- package/templates/base/src/config/i18n.ts +10 -3
- package/templates/base/src/styles/global.css +14 -0
- package/templates/base/src/types/docs-entry.ts +8 -26
- package/templates/base/src/utils/base.ts +5 -3
- package/templates/base/src/utils/docs.ts +144 -169
- package/templates/base/zfb-shim.d.ts +167 -0
- package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +20 -110
- package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +62 -38
- package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +34 -8
- package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +27 -45
- package/templates/features/docHistory/files/src/components/doc-history.tsx +30 -8
- package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +6 -74
- package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +6 -77
- package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +7 -69
- package/templates/features/docTags/files/pages/docs/tags/index.tsx +6 -76
- package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +201 -0
- package/templates/features/i18n/files/pages/[locale]/docs/[[...slug]].tsx +41 -179
- package/templates/features/i18n/files/pages/[locale]/index.tsx +5 -5
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +2 -0
- package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +33 -21
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +1 -1
- package/templates/features/tauri/files/src/components/find-in-page-init.tsx +9 -3
- package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +5 -59
- package/templates/features/versioning/files/pages/docs/versions.tsx +8 -66
- package/templates/features/versioning/files/pages/lib/_versions-page.tsx +79 -0
- package/templates/features/versioning/files/pages/v/[version]/[locale]/docs/[[...slug]].tsx +46 -191
- package/templates/features/versioning/files/pages/v/[version]/docs/[[...slug]].tsx +31 -173
- package/templates/base/src/components/content/heading-h3.tsx +0 -20
- package/templates/base/src/hooks/use-active-heading.ts +0 -133
- package/templates/base/src/plugins/docs-source-map.ts +0 -103
- package/templates/base/src/plugins/hast-utils.ts +0 -10
- package/templates/base/src/plugins/rehype-code-title.ts +0 -50
- package/templates/base/src/plugins/rehype-heading-links.ts +0 -53
- package/templates/base/src/plugins/rehype-mermaid.ts +0 -41
- package/templates/base/src/plugins/url-utils.ts +0 -4
- package/templates/base/src/utils/dedent.ts +0 -24
- package/templates/features/docHistory/files/src/utils/doc-history.ts +0 -180
- package/templates/features/sidebarResizer/files/src/scripts/sidebar-resizer.ts +0 -198
|
@@ -21,33 +21,19 @@
|
|
|
21
21
|
//
|
|
22
22
|
// Locale: defaultLocale (EN). Non-default locales are handled by
|
|
23
23
|
// pages/[locale]/docs/[[...slug]].tsx.
|
|
24
|
+
//
|
|
25
|
+
// Enumeration + per-entry derived data (breadcrumbs, prev/next, headings) are
|
|
26
|
+
// built by the shared, memoized buildDocRouteEntries (#2010); rendering by the
|
|
27
|
+
// shared renderDocPage. This file owns only the route's nav source and the
|
|
28
|
+
// param/prop shapes.
|
|
24
29
|
|
|
25
30
|
import { settings } from "@/config/settings";
|
|
26
31
|
import { defaultLocale } from "@/config/i18n";
|
|
27
|
-
import { docsUrl, absoluteUrl } from "@/utils/base";
|
|
28
|
-
import {
|
|
29
|
-
buildNavTree,
|
|
30
|
-
buildBreadcrumbs,
|
|
31
|
-
collectAutoIndexNodes,
|
|
32
|
-
type NavNode,
|
|
33
|
-
} from "@/utils/docs";
|
|
34
|
-
import { getNavSectionForSlug, getNavSubtree } from "@/utils/nav-scope";
|
|
35
|
-
import { toRouteSlug, toSlugParams } from "@/utils/slug";
|
|
36
|
-
// Shared MDX-tag → Preact-component bag. Includes htmlOverrides
|
|
37
|
-
// (native typography), HtmlPreviewWrapper (Island), and stub bindings
|
|
38
|
-
// for every other custom tag the MDX corpus references — see
|
|
39
|
-
// `pages/_mdx-components.ts` for the full list and rationale.
|
|
40
|
-
import { createMdxComponents } from "../_mdx-components";
|
|
41
|
-
import { DocHistoryArea } from "../lib/_doc-history-area";
|
|
42
|
-
import { DocMetainfoArea } from "../lib/_doc-metainfo-area";
|
|
43
|
-
import { buildInlineVersionSwitcher } from "../lib/_inline-version-switcher";
|
|
44
32
|
import type { JSX } from "preact";
|
|
45
33
|
import { resolveNavSource } from "../lib/_nav-source-docs";
|
|
46
|
-
import {
|
|
47
|
-
import
|
|
48
|
-
import {
|
|
49
|
-
import { DocPageShell } from "../lib/_doc-page-shell";
|
|
50
|
-
import { resolveDocPrevNext, flattenSubtree } from "../lib/_doc-route-paths";
|
|
34
|
+
import type { DocPageEntryProps, DocPageAutoIndexProps } from "../lib/doc-page-props";
|
|
35
|
+
import { buildDocRouteEntries } from "../lib/_doc-route-entries";
|
|
36
|
+
import { renderDocPage } from "../lib/_doc-page-renderer";
|
|
51
37
|
|
|
52
38
|
export const frontmatter = { title: "Docs" };
|
|
53
39
|
|
|
@@ -55,8 +41,6 @@ export const frontmatter = { title: "Docs" };
|
|
|
55
41
|
// Props contract
|
|
56
42
|
// ---------------------------------------------------------------------------
|
|
57
43
|
|
|
58
|
-
// DocPageEntry, AutoIndexNode imported from pages/lib/doc-page-props.ts
|
|
59
|
-
|
|
60
44
|
type DocPageProps = DocPageEntryProps | DocPageAutoIndexProps;
|
|
61
45
|
|
|
62
46
|
// ---------------------------------------------------------------------------
|
|
@@ -67,8 +51,8 @@ type DocPageProps = DocPageEntryProps | DocPageAutoIndexProps;
|
|
|
67
51
|
* Enumerate all doc routes for the default locale (EN).
|
|
68
52
|
*
|
|
69
53
|
* Synchronous per ADR-004: getCollection() resolves from the pre-loaded
|
|
70
|
-
* ContentSnapshot. All nav-tree and breadcrumb computation is done
|
|
71
|
-
* so the component is a pure renderer.
|
|
54
|
+
* ContentSnapshot. All nav-tree and breadcrumb computation is done in the
|
|
55
|
+
* shared builder so the component is a pure renderer.
|
|
72
56
|
*/
|
|
73
57
|
export function paths(): Array<{
|
|
74
58
|
params: { slug: string[] };
|
|
@@ -77,66 +61,19 @@ export function paths(): Array<{
|
|
|
77
61
|
const locale = defaultLocale;
|
|
78
62
|
// Identity-stable nav source (draft-filtered, unlisted retained). The same
|
|
79
63
|
// instances are returned across this route's many per-page paths()
|
|
80
|
-
// invocations, so buildNavTree's identity fast-path
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// A `category_no_page` index.mdx carries category metadata only — keep it
|
|
94
|
-
// in the nav tree (built above, used for breadcrumbs) but emit NO route for
|
|
95
|
-
// it. zfb's walker retains every .mdx as a collection entry, so without
|
|
96
|
-
// this explicit skip the metadata file would silently add a route.
|
|
97
|
-
if (entry.data.category_no_page === true) continue;
|
|
98
|
-
const slug = entry.data.slug ?? toRouteSlug(entry.slug);
|
|
99
|
-
const navSection = getNavSectionForSlug(slug);
|
|
100
|
-
const subtree = getNavSubtree(tree, navSection);
|
|
101
|
-
|
|
102
|
-
// Prev/next + frontmatter pagination overrides resolved against THIS
|
|
103
|
-
// route's own `tree`. Latest route — hrefs stay unversioned (no rewrite).
|
|
104
|
-
const { prev: prevNode, next: nextNode } = resolveDocPrevNext(
|
|
105
|
-
tree,
|
|
106
|
-
flattenSubtree(subtree),
|
|
107
|
-
slug,
|
|
108
|
-
entry.data,
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
result.push({
|
|
112
|
-
params: { slug: toSlugParams(slug) },
|
|
113
|
-
props: {
|
|
114
|
-
kind: "entry",
|
|
115
|
-
entry,
|
|
116
|
-
breadcrumbs: buildBreadcrumbs(fullTree, slug, locale),
|
|
117
|
-
prev: prevNode,
|
|
118
|
-
next: nextNode,
|
|
119
|
-
headings: extractHeadings(entry.body ?? ""),
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Auto-generated index pages for categories without index.mdx
|
|
125
|
-
for (const node of collectAutoIndexNodes(tree)) {
|
|
126
|
-
result.push({
|
|
127
|
-
params: { slug: toSlugParams(node.slug) },
|
|
128
|
-
props: {
|
|
129
|
-
kind: "autoIndex",
|
|
130
|
-
autoIndex: node as AutoIndexNode,
|
|
131
|
-
breadcrumbs: buildBreadcrumbs(fullTree, node.slug, locale),
|
|
132
|
-
prev: null,
|
|
133
|
-
next: null,
|
|
134
|
-
headings: [],
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return result;
|
|
64
|
+
// invocations, so both buildNavTree's identity fast-path and the
|
|
65
|
+
// buildDocRouteEntries memo key on them — see pages/lib/_nav-source-docs.ts
|
|
66
|
+
// (#1902).
|
|
67
|
+
const source = resolveNavSource(locale, undefined);
|
|
68
|
+
|
|
69
|
+
return buildDocRouteEntries({
|
|
70
|
+
source,
|
|
71
|
+
locale,
|
|
72
|
+
routeSig: `docs;${locale}`,
|
|
73
|
+
}).map((item) => ({
|
|
74
|
+
params: { slug: item.slugParams },
|
|
75
|
+
props: item.props,
|
|
76
|
+
}));
|
|
140
77
|
}
|
|
141
78
|
|
|
142
79
|
// ---------------------------------------------------------------------------
|
|
@@ -146,86 +83,8 @@ export function paths(): Array<{
|
|
|
146
83
|
type PageArgs = DocPageProps & { params: { slug: string[] } };
|
|
147
84
|
|
|
148
85
|
export default function DocsPage(props: PageArgs): JSX.Element {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
? props.autoIndex.slug
|
|
154
|
-
: (props.entry.data.slug ?? toRouteSlug(props.entry.slug));
|
|
155
|
-
|
|
156
|
-
const title = props.kind === "autoIndex" ? props.autoIndex.label : props.entry.data.title;
|
|
157
|
-
const description = props.kind === "autoIndex" ? props.autoIndex.description : props.entry.data.description;
|
|
158
|
-
|
|
159
|
-
// Locale-aware components bag — creates nav wrappers bound to the active
|
|
160
|
-
// locale so CategoryNav/CategoryTreeNav/SiteTreeNav query the right collection.
|
|
161
|
-
const components = createMdxComponents(locale);
|
|
162
|
-
|
|
163
|
-
// Resolve child hrefs for auto-index pages — latest route keeps the nav
|
|
164
|
-
// node's own docsUrl href (fallback for a noPage parent without an href).
|
|
165
|
-
const autoIndexChildren = props.kind === "autoIndex"
|
|
166
|
-
? props.autoIndex.children
|
|
167
|
-
.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
168
|
-
.map((c: NavNode) => ({
|
|
169
|
-
...c,
|
|
170
|
-
href: c.href ?? docsUrl(c.slug, locale),
|
|
171
|
-
}))
|
|
172
|
-
: [];
|
|
173
|
-
|
|
174
|
-
// Canonical URL — base-prefixed page path, absolutized against siteUrl.
|
|
175
|
-
const currentPath = docsUrl(slug, locale);
|
|
176
|
-
const canonical = absoluteUrl(currentPath);
|
|
177
|
-
|
|
178
|
-
// Persist key: locale + nav-section so the sidebar DOM node is reused
|
|
179
|
-
// across same-locale + same-section navigations only. No sanitizer needed —
|
|
180
|
-
// both lang (BCP-47 locale string) and navSection (filesystem-derived
|
|
181
|
-
// kebab-case slug) come from controlled, trusted sources.
|
|
182
|
-
const navSection = getNavSectionForSlug(slug);
|
|
183
|
-
const hideSidebar = props.kind === "entry" ? props.entry.data.hide_sidebar : undefined;
|
|
184
|
-
const sidebarPersistKey = hideSidebar
|
|
185
|
-
? undefined
|
|
186
|
-
: `sidebar-${locale}-${navSection ?? "default"}`;
|
|
187
|
-
|
|
188
|
-
return (
|
|
189
|
-
<DocPageShell
|
|
190
|
-
kind={props.kind}
|
|
191
|
-
locale={locale}
|
|
192
|
-
slug={slug}
|
|
193
|
-
title={title}
|
|
194
|
-
description={description}
|
|
195
|
-
canonical={canonical}
|
|
196
|
-
breadcrumbs={breadcrumbs}
|
|
197
|
-
prev={prev}
|
|
198
|
-
next={next}
|
|
199
|
-
headings={headings}
|
|
200
|
-
navSection={navSection}
|
|
201
|
-
sidebarPersistKey={sidebarPersistKey}
|
|
202
|
-
hideSidebar={hideSidebar}
|
|
203
|
-
hideToc={props.kind === "entry" ? props.entry.data.hide_toc : undefined}
|
|
204
|
-
currentPath={currentPath}
|
|
205
|
-
versionSwitcher={buildInlineVersionSwitcher(slug, locale)}
|
|
206
|
-
autoIndexLabel={props.kind === "autoIndex" ? props.autoIndex.label : undefined}
|
|
207
|
-
autoIndexChildren={autoIndexChildren}
|
|
208
|
-
metainfoSlot={
|
|
209
|
-
props.kind === "autoIndex" ? <DocMetainfoArea slug={slug} locale={locale} /> : null
|
|
210
|
-
}
|
|
211
|
-
contentHeaderSlot={
|
|
212
|
-
props.kind === "entry" ? (
|
|
213
|
-
<DocContentHeader entry={props.entry} slug={slug} locale={locale} />
|
|
214
|
-
) : undefined
|
|
215
|
-
}
|
|
216
|
-
contentSlot={
|
|
217
|
-
props.kind === "entry" ? <props.entry.Content components={components} /> : undefined
|
|
218
|
-
}
|
|
219
|
-
docHistorySlot={
|
|
220
|
-
props.kind === "entry" && !props.entry.data.unlisted ? (
|
|
221
|
-
<DocHistoryArea
|
|
222
|
-
slug={slug}
|
|
223
|
-
locale={locale}
|
|
224
|
-
entrySlug={props.entry.slug}
|
|
225
|
-
contentDir={settings.docsDir}
|
|
226
|
-
/>
|
|
227
|
-
) : null
|
|
228
|
-
}
|
|
229
|
-
/>
|
|
230
|
-
);
|
|
86
|
+
return renderDocPage(props, {
|
|
87
|
+
locale: defaultLocale,
|
|
88
|
+
docHistoryContentDir: settings.docsDir,
|
|
89
|
+
});
|
|
231
90
|
}
|
|
@@ -37,6 +37,7 @@ import ClientRouterBootstrap from "@/components/client-router-bootstrap";
|
|
|
37
37
|
import DesignTokenPanelBootstrap from "@/components/design-token-panel-bootstrap";
|
|
38
38
|
import ImageEnlarge, { ImageEnlargeSsrFallback } from "@/components/image-enlarge";
|
|
39
39
|
import { PageLoadingOverlay } from "@takazudo/zudo-doc/page-loading";
|
|
40
|
+
// @slot:body-end-islands:imports
|
|
40
41
|
|
|
41
42
|
// Set explicit `displayName` on each default-exported island so zfb's
|
|
42
43
|
// `captureComponentName` produces a stable marker even after the SSR
|
|
@@ -51,6 +52,7 @@ import { PageLoadingOverlay } from "@takazudo/zudo-doc/page-loading";
|
|
|
51
52
|
(DesignTokenPanelBootstrap as { displayName?: string }).displayName =
|
|
52
53
|
"DesignTokenPanelBootstrap";
|
|
53
54
|
(ImageEnlarge as { displayName?: string }).displayName = "ImageEnlarge";
|
|
55
|
+
// @slot:body-end-islands:display-names
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* Default sr-only label rendered as the AiChatModal SSR fallback. This
|
|
@@ -196,6 +198,7 @@ export function BodyEndIslands({
|
|
|
196
198
|
<h2 class="sr-only">AI Assistant</h2>
|
|
197
199
|
{aiChat}
|
|
198
200
|
{imageEnlarge}
|
|
201
|
+
{/* @slot:body-end-islands:extra-islands */}
|
|
199
202
|
</>
|
|
200
203
|
);
|
|
201
204
|
}
|
|
@@ -33,6 +33,17 @@ interface DocContentHeaderProps {
|
|
|
33
33
|
* Only relevant for locale-prefixed and versioned-locale routes.
|
|
34
34
|
*/
|
|
35
35
|
isFallback?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Version slug when rendering a versioned route (e.g. "1.0"); undefined =
|
|
38
|
+
* latest. On versioned pages the date block and tag chips are hidden —
|
|
39
|
+
* the doc-history-meta manifest is built only from the LATEST content
|
|
40
|
+
* dirs, so a bare versioned slug would resolve to the latest file's
|
|
41
|
+
* Created/Updated/Author (wrong data), and tag chips would link to latest
|
|
42
|
+
* tag routes that may not exist for version-only tags (404). Mirrors the
|
|
43
|
+
* #1916 reduced-chrome stance that already hides doc history on
|
|
44
|
+
* versioned pages.
|
|
45
|
+
*/
|
|
46
|
+
version?: string;
|
|
36
47
|
}
|
|
37
48
|
|
|
38
49
|
/**
|
|
@@ -50,6 +61,7 @@ export function DocContentHeader({
|
|
|
50
61
|
slug,
|
|
51
62
|
locale,
|
|
52
63
|
isFallback = false,
|
|
64
|
+
version,
|
|
53
65
|
}: DocContentHeaderProps): JSX.Element {
|
|
54
66
|
return (
|
|
55
67
|
<>
|
|
@@ -57,11 +69,19 @@ export function DocContentHeader({
|
|
|
57
69
|
|
|
58
70
|
{/* Build-time date block (Created / Updated / Author).
|
|
59
71
|
doc-metainfo placement — between <h1> and description.
|
|
60
|
-
Data from `.zfb/doc-history-meta.json` (esbuild-inlined, no fs).
|
|
61
|
-
|
|
72
|
+
Data from `.zfb/doc-history-meta.json` (esbuild-inlined, no fs).
|
|
73
|
+
Hidden on versioned pages — the manifest only covers latest
|
|
74
|
+
content dirs, so a versioned slug would show the LATEST file's
|
|
75
|
+
dates (see the `version` prop doc above). */}
|
|
76
|
+
{!version && (
|
|
77
|
+
<DocMetainfoArea slug={slug} locale={locale} isFallback={isFallback} />
|
|
78
|
+
)}
|
|
62
79
|
|
|
63
|
-
{/* Page-level tag chips — matching doc-tags placement (#1658).
|
|
64
|
-
|
|
80
|
+
{/* Page-level tag chips — matching doc-tags placement (#1658).
|
|
81
|
+
Hidden on versioned pages: tag routes are built from latest
|
|
82
|
+
frontmatter only, so a version-only tag chip would 404 (see the
|
|
83
|
+
`version` prop doc above). */}
|
|
84
|
+
{!version && <DocTagsArea slug={slug} locale={locale} tags={entry.data.tags} />}
|
|
65
85
|
|
|
66
86
|
{/* Fallback notice for non-translated pages */}
|
|
67
87
|
{isFallback && !entry.data.generated && (
|
|
@@ -56,7 +56,8 @@ interface DocHistoryAreaProps {
|
|
|
56
56
|
/**
|
|
57
57
|
* Raw zfb entry slug (relative path without extension), e.g.
|
|
58
58
|
* "getting-started/intro" or "getting-started/index". Appended with
|
|
59
|
-
*
|
|
59
|
+
* the source extension from the build-time manifest (".mdx" fallback)
|
|
60
|
+
* to form the file path passed to buildGitHubSourceUrl.
|
|
60
61
|
* Omit for auto-index pages (no underlying MDX file) — sourceUrl
|
|
61
62
|
* will be suppressed automatically.
|
|
62
63
|
*/
|
|
@@ -130,7 +131,13 @@ export function DocHistoryArea({
|
|
|
130
131
|
// locale, "<localeKey>/<slug>" for non-default locales.
|
|
131
132
|
const composedSlug =
|
|
132
133
|
effectiveHistoryLocale === defaultLocale ? historySlug : `${effectiveHistoryLocale}/${historySlug}`;
|
|
133
|
-
type MetaEntry = {
|
|
134
|
+
type MetaEntry = {
|
|
135
|
+
author: string;
|
|
136
|
+
createdDate: string;
|
|
137
|
+
updatedDate: string;
|
|
138
|
+
/** Source file extension (".mdx" | ".md") — optional in older manifests. */
|
|
139
|
+
ext?: string;
|
|
140
|
+
};
|
|
134
141
|
const meta = (docHistoryMeta as Record<string, MetaEntry>)[composedSlug];
|
|
135
142
|
|
|
136
143
|
// Locale-aware labels for the SSR fallback.
|
|
@@ -156,7 +163,11 @@ export function DocHistoryArea({
|
|
|
156
163
|
const createdDate = meta?.createdDate;
|
|
157
164
|
const updatedDate = meta?.updatedDate;
|
|
158
165
|
|
|
159
|
-
|
|
166
|
+
// Explicit type annotation omitted: inferred JSX return is structurally
|
|
167
|
+
// compatible with zfb's VNode (the ssrFallback prop target). Preact's
|
|
168
|
+
// VNode<{}> generic form is not directly assignable to zfb's VNode at the
|
|
169
|
+
// type level even though the runtime shapes are identical.
|
|
170
|
+
const fallback = (
|
|
160
171
|
<div class="sr-only">
|
|
161
172
|
{author && <span>{author}</span>}
|
|
162
173
|
<span>
|
|
@@ -188,11 +199,16 @@ export function DocHistoryArea({
|
|
|
188
199
|
// Compute the view-source GitHub URL host-side so the v2 BodyFootUtilArea
|
|
189
200
|
// component stays oblivious to project settings. Gate on
|
|
190
201
|
// bodyFootUtilArea.viewSourceLink, and require both entrySlug and contentDir
|
|
191
|
-
// (auto-index pages pass neither).
|
|
202
|
+
// (auto-index pages pass neither). The real source extension comes from the
|
|
203
|
+
// build-time manifest (`ext`, written by pre-build.ts) — the content walkers
|
|
204
|
+
// accept both .mdx and .md, so hardcoding ".mdx" produced broken view-source
|
|
205
|
+
// URLs for .md pages. ".mdx" remains the fallback for entries without a
|
|
206
|
+
// manifest record (untracked files, SKIP_DOC_HISTORY=1, stale manifests).
|
|
192
207
|
const utilSettings = settings.bodyFootUtilArea;
|
|
208
|
+
const sourceExt = meta?.ext ?? ".mdx";
|
|
193
209
|
const sourceUrl =
|
|
194
210
|
utilSettings && utilSettings.viewSourceLink && entrySlug && contentDir
|
|
195
|
-
? buildGitHubSourceUrl(contentDir, entrySlug +
|
|
211
|
+
? buildGitHubSourceUrl(contentDir, entrySlug + sourceExt)
|
|
196
212
|
: null;
|
|
197
213
|
|
|
198
214
|
// Resolve the i18n label host-side; pass the result so the v2 component
|
|
@@ -61,6 +61,16 @@ interface DocMetainfoAreaProps {
|
|
|
61
61
|
slug: string;
|
|
62
62
|
/** Active locale string, e.g. "en", "ja". */
|
|
63
63
|
locale: string;
|
|
64
|
+
/**
|
|
65
|
+
* True when this locale page falls back to the base EN collection
|
|
66
|
+
* (i.e. the slug has no translation for the active locale). When true,
|
|
67
|
+
* the manifest lookup uses defaultLocale so the visible block resolves
|
|
68
|
+
* the bare-slug key — the only key that exists for EN-origin files —
|
|
69
|
+
* matching the dropdown's `effectiveHistoryLocale` derivation in
|
|
70
|
+
* _doc-history-area.tsx. Display formatting (dates + labels) still uses
|
|
71
|
+
* the active locale so JA users see JA formatting on fallback pages.
|
|
72
|
+
*/
|
|
73
|
+
isFallback?: boolean;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
/**
|
|
@@ -76,7 +86,7 @@ interface DocMetainfoAreaProps {
|
|
|
76
86
|
* HTML from build-time data and has no client JS footprint. It sits
|
|
77
87
|
* between `<h1>` and the description `<p>` (doc-metainfo placement).
|
|
78
88
|
*/
|
|
79
|
-
export function DocMetainfoArea({ slug, locale }: DocMetainfoAreaProps): VNode | null {
|
|
89
|
+
export function DocMetainfoArea({ slug, locale, isFallback }: DocMetainfoAreaProps): VNode | null {
|
|
80
90
|
if (!settings.docMetainfo) return null;
|
|
81
91
|
|
|
82
92
|
// Doc-history storage sentinel ("" -> "index"): a root index page has the
|
|
@@ -87,10 +97,20 @@ export function DocMetainfoArea({ slug, locale }: DocMetainfoAreaProps): VNode |
|
|
|
87
97
|
// @/utils/slug `toHistorySlug` and _doc-history-area.tsx. (#1891)
|
|
88
98
|
const historySlug = toHistorySlug(slug);
|
|
89
99
|
|
|
100
|
+
// On EN-fallback locale pages the manifest only has the bare
|
|
101
|
+
// (non-locale-prefixed) key — the prebuild writes locale-prefixed keys
|
|
102
|
+
// only for files physically present in the locale collection. Use
|
|
103
|
+
// defaultLocale for the data lookup when isFallback is true, mirroring
|
|
104
|
+
// `effectiveHistoryLocale` in _doc-history-area.tsx so the visible block
|
|
105
|
+
// and the dropdown agree. Display formatting keeps the active locale.
|
|
106
|
+
const effectiveHistoryLocale = isFallback ? defaultLocale : locale;
|
|
107
|
+
|
|
90
108
|
// Key format: bare slug for default locale, "<locale>/<slug>" for others.
|
|
91
109
|
// Matches the prebuild step's composedSlug logic (pre-build.ts).
|
|
92
110
|
const composedSlug =
|
|
93
|
-
|
|
111
|
+
effectiveHistoryLocale === defaultLocale
|
|
112
|
+
? historySlug
|
|
113
|
+
: `${effectiveHistoryLocale}/${historySlug}`;
|
|
94
114
|
|
|
95
115
|
type MetaEntry = { author: string; createdDate: string; updatedDate: string };
|
|
96
116
|
const meta = (docHistoryMeta as Record<string, MetaEntry>)[composedSlug];
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
// Shared page renderer for the 4 doc catch-all routes.
|
|
4
|
+
//
|
|
5
|
+
// Extracted (#2010) from the near-identical default components of:
|
|
6
|
+
// pages/docs/[[...slug]].tsx
|
|
7
|
+
// pages/[locale]/docs/[[...slug]].tsx
|
|
8
|
+
// pages/v/[version]/docs/[[...slug]].tsx
|
|
9
|
+
// pages/v/[version]/[locale]/docs/[[...slug]].tsx
|
|
10
|
+
//
|
|
11
|
+
// Each route's default export stays a thin adapter that reads its params /
|
|
12
|
+
// route-specific props (locale, version, contentDir, isFallback) and
|
|
13
|
+
// delegates here. Route-specific behavior is parameterized:
|
|
14
|
+
// - `version` present → versioned chrome: versioned canonical URL, version
|
|
15
|
+
// banner, version-aware switcher, auto-index child hrefs kept as the
|
|
16
|
+
// pre-remapped versioned hrefs from paths() (#1916 #2), and doc history
|
|
17
|
+
// hidden until versioned history is supported (#1916 #5).
|
|
18
|
+
// - `version` absent → latest chrome: docsUrl canonical, child hrefs fall
|
|
19
|
+
// back to the nav node's own docsUrl, doc history rendered for listed
|
|
20
|
+
// entries via `docHistoryContentDir`.
|
|
21
|
+
|
|
22
|
+
import { settings } from "@/config/settings";
|
|
23
|
+
import type { VersionConfig } from "@/config/settings";
|
|
24
|
+
import { t, type Locale } from "@/config/i18n";
|
|
25
|
+
import { docsUrl, versionedDocsUrl, absoluteUrl } from "@/utils/base";
|
|
26
|
+
import type { NavNode } from "@/utils/docs";
|
|
27
|
+
import { getNavSectionForSlug } from "@/utils/nav-scope";
|
|
28
|
+
import { toRouteSlug } from "@/utils/slug";
|
|
29
|
+
import type { JSX } from "preact";
|
|
30
|
+
// Shared MDX-tag → Preact-component bag. Includes htmlOverrides
|
|
31
|
+
// (native typography), HtmlPreviewWrapper (Island), and stub bindings
|
|
32
|
+
// for every other custom tag the MDX corpus references — see
|
|
33
|
+
// `pages/_mdx-components.ts` for the full list and rationale.
|
|
34
|
+
import { createMdxComponents } from "../_mdx-components";
|
|
35
|
+
import type { DocPageBaseProps } from "./doc-page-props";
|
|
36
|
+
import { DocHistoryArea } from "./_doc-history-area";
|
|
37
|
+
import { DocMetainfoArea } from "./_doc-metainfo-area";
|
|
38
|
+
import { buildInlineVersionSwitcher } from "./_inline-version-switcher";
|
|
39
|
+
import { DocContentHeader } from "./_doc-content-header";
|
|
40
|
+
import { DocPageShell } from "./_doc-page-shell";
|
|
41
|
+
|
|
42
|
+
export interface RenderDocPageOptions {
|
|
43
|
+
/** Active locale — drives nav wrappers, labels, and URL building. */
|
|
44
|
+
locale: Locale;
|
|
45
|
+
/** Version config when rendering a versioned route; undefined = latest. */
|
|
46
|
+
version?: VersionConfig;
|
|
47
|
+
/** True when this page falls back to the base EN collection (locale
|
|
48
|
+
* routes). Drives the fallback notice + history-area hint. */
|
|
49
|
+
isFallback?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Content directory for the doc-history view-source link (e.g. the active
|
|
52
|
+
* locale's dir, or the base docsDir for EN/fallback pages). Latest routes
|
|
53
|
+
* pass it; versioned routes omit it — doc history is hidden on versioned
|
|
54
|
+
* pages regardless (#1916 #5).
|
|
55
|
+
*/
|
|
56
|
+
docHistoryContentDir?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function renderDocPage(
|
|
60
|
+
props: DocPageBaseProps,
|
|
61
|
+
opts: RenderDocPageOptions,
|
|
62
|
+
): JSX.Element {
|
|
63
|
+
const { breadcrumbs, prev, next, headings } = props;
|
|
64
|
+
const { locale, version, isFallback } = opts;
|
|
65
|
+
|
|
66
|
+
const slug = props.kind === "autoIndex"
|
|
67
|
+
? props.autoIndex.slug
|
|
68
|
+
: (props.entry.data.slug ?? toRouteSlug(props.entry.slug));
|
|
69
|
+
|
|
70
|
+
const title = props.kind === "autoIndex" ? props.autoIndex.label : props.entry.data.title;
|
|
71
|
+
const description = props.kind === "autoIndex" ? props.autoIndex.description : props.entry.data.description;
|
|
72
|
+
|
|
73
|
+
// Locale-aware components bag — creates nav wrappers bound to the active
|
|
74
|
+
// locale so CategoryNav/CategoryTreeNav/SiteTreeNav query the right collection.
|
|
75
|
+
const components = createMdxComponents(locale);
|
|
76
|
+
|
|
77
|
+
// Resolve child hrefs for auto-index pages. Versioned routes: child cards
|
|
78
|
+
// already carry versioned hrefs from paths() (#1916 #2) — just filter to
|
|
79
|
+
// renderable nodes. Latest routes: keep the nav node's own docsUrl href
|
|
80
|
+
// (fallback for a noPage parent without an href).
|
|
81
|
+
const autoIndexChildren = props.kind === "autoIndex"
|
|
82
|
+
? version
|
|
83
|
+
? props.autoIndex.children.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
84
|
+
: props.autoIndex.children
|
|
85
|
+
.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
86
|
+
.map((c: NavNode) => ({
|
|
87
|
+
...c,
|
|
88
|
+
href: c.href ?? docsUrl(c.slug, locale),
|
|
89
|
+
}))
|
|
90
|
+
: [];
|
|
91
|
+
|
|
92
|
+
// Version banner: drives the `<VersionBanner>` element inside
|
|
93
|
+
// DocLayoutWithDefaults when `version.banner` is "unmaintained" or
|
|
94
|
+
// "unreleased". The banner links out to the latest version of the
|
|
95
|
+
// current page (slug-preserving — strips the /v/{version}/ prefix,
|
|
96
|
+
// keeps the /{locale}/ locale prefix).
|
|
97
|
+
const versionBannerType = version?.banner ? version.banner : undefined;
|
|
98
|
+
const versionBannerLatestUrl = versionBannerType
|
|
99
|
+
? docsUrl(slug, locale)
|
|
100
|
+
: undefined;
|
|
101
|
+
const versionBannerLabels = versionBannerType
|
|
102
|
+
? {
|
|
103
|
+
message:
|
|
104
|
+
versionBannerType === "unmaintained"
|
|
105
|
+
? t("version.banner.unmaintained", locale)
|
|
106
|
+
: t("version.banner.unreleased", locale),
|
|
107
|
+
latestLink: t("version.banner.latestLink", locale),
|
|
108
|
+
}
|
|
109
|
+
: undefined;
|
|
110
|
+
|
|
111
|
+
// Canonical URL — base-prefixed page path, absolutized against siteUrl.
|
|
112
|
+
// Versioned pages use the versioned URL as canonical.
|
|
113
|
+
const currentPath = version
|
|
114
|
+
? versionedDocsUrl(slug, version.slug, locale)
|
|
115
|
+
: docsUrl(slug, locale);
|
|
116
|
+
const canonical = absoluteUrl(currentPath);
|
|
117
|
+
|
|
118
|
+
// Persist key: locale + nav-section so the sidebar DOM node is reused
|
|
119
|
+
// across same-locale + same-section navigations only. No sanitizer needed —
|
|
120
|
+
// both lang (BCP-47 locale string) and navSection (filesystem-derived
|
|
121
|
+
// kebab-case slug) come from controlled, trusted sources.
|
|
122
|
+
const navSection = getNavSectionForSlug(slug);
|
|
123
|
+
const hideSidebar = props.kind === "entry" ? props.entry.data.hide_sidebar : undefined;
|
|
124
|
+
const sidebarPersistKey = hideSidebar
|
|
125
|
+
? undefined
|
|
126
|
+
: `sidebar-${locale}-${navSection ?? "default"}`;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<DocPageShell
|
|
130
|
+
kind={props.kind}
|
|
131
|
+
locale={locale}
|
|
132
|
+
slug={slug}
|
|
133
|
+
title={title}
|
|
134
|
+
description={description}
|
|
135
|
+
canonical={canonical}
|
|
136
|
+
breadcrumbs={breadcrumbs}
|
|
137
|
+
prev={prev}
|
|
138
|
+
next={next}
|
|
139
|
+
headings={headings}
|
|
140
|
+
navSection={navSection}
|
|
141
|
+
sidebarPersistKey={sidebarPersistKey}
|
|
142
|
+
hideSidebar={hideSidebar}
|
|
143
|
+
hideToc={props.kind === "entry" ? props.entry.data.hide_toc : undefined}
|
|
144
|
+
currentPath={currentPath}
|
|
145
|
+
currentVersion={version?.slug}
|
|
146
|
+
versionSwitcher={buildInlineVersionSwitcher(slug, locale, version?.slug)}
|
|
147
|
+
versionBanner={versionBannerType}
|
|
148
|
+
versionBannerLatestUrl={versionBannerLatestUrl}
|
|
149
|
+
versionBannerLabels={versionBannerLabels}
|
|
150
|
+
autoIndexLabel={props.kind === "autoIndex" ? props.autoIndex.label : undefined}
|
|
151
|
+
autoIndexChildren={autoIndexChildren}
|
|
152
|
+
metainfoSlot={
|
|
153
|
+
// Versioned gate mirrors DocContentHeader: the doc-history-meta
|
|
154
|
+
// manifest is built from latest dirs only, so a bare versioned slug
|
|
155
|
+
// would surface the LATEST page's Created/Updated/Author.
|
|
156
|
+
!version && props.kind === "autoIndex" ? (
|
|
157
|
+
<DocMetainfoArea slug={slug} locale={locale} isFallback={isFallback} />
|
|
158
|
+
) : null
|
|
159
|
+
}
|
|
160
|
+
contentHeaderSlot={
|
|
161
|
+
props.kind === "entry" ? (
|
|
162
|
+
<DocContentHeader
|
|
163
|
+
entry={props.entry}
|
|
164
|
+
slug={slug}
|
|
165
|
+
locale={locale}
|
|
166
|
+
isFallback={isFallback}
|
|
167
|
+
version={version?.slug}
|
|
168
|
+
/>
|
|
169
|
+
) : undefined
|
|
170
|
+
}
|
|
171
|
+
contentSlot={
|
|
172
|
+
props.kind === "entry" ? <props.entry.Content components={components} /> : undefined
|
|
173
|
+
}
|
|
174
|
+
docHistorySlot={
|
|
175
|
+
// #1916 #5: doc-history hidden on versioned pages until versioned
|
|
176
|
+
// history is supported.
|
|
177
|
+
!version &&
|
|
178
|
+
opts.docHistoryContentDir !== undefined &&
|
|
179
|
+
props.kind === "entry" &&
|
|
180
|
+
!props.entry.data.unlisted ? (
|
|
181
|
+
<DocHistoryArea
|
|
182
|
+
slug={slug}
|
|
183
|
+
locale={locale}
|
|
184
|
+
entrySlug={props.entry.slug}
|
|
185
|
+
contentDir={opts.docHistoryContentDir}
|
|
186
|
+
isFallback={isFallback}
|
|
187
|
+
/>
|
|
188
|
+
) : null
|
|
189
|
+
}
|
|
190
|
+
/>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
@@ -32,6 +32,7 @@ import { DocBodyEnd } from "./_doc-body-end";
|
|
|
32
32
|
import { DocPager } from "./_doc-pager";
|
|
33
33
|
import type { BreadcrumbItem } from "@/utils/docs";
|
|
34
34
|
import type { VersionBannerLabels } from "@takazudo/zudo-doc/i18n-version";
|
|
35
|
+
import type { Locale } from "@/config/i18n";
|
|
35
36
|
import type { extractHeadings } from "./_extract-headings";
|
|
36
37
|
|
|
37
38
|
/** Slots and parameters that vary between the 4 doc routes. */
|
|
@@ -161,7 +162,7 @@ export function DocPageShell(props: DocPageShellProps): JSX.Element {
|
|
|
161
162
|
versionBannerLabels={versionBannerLabels}
|
|
162
163
|
headerOverride={
|
|
163
164
|
<HeaderWithDefaults
|
|
164
|
-
lang={locale}
|
|
165
|
+
lang={locale as Locale}
|
|
165
166
|
currentSlug={props.slug}
|
|
166
167
|
navSection={navSection}
|
|
167
168
|
currentVersion={currentVersion}
|
|
@@ -176,7 +177,7 @@ export function DocPageShell(props: DocPageShellProps): JSX.Element {
|
|
|
176
177
|
sidebarOverride={
|
|
177
178
|
<SidebarWithDefaults
|
|
178
179
|
currentSlug={props.slug}
|
|
179
|
-
lang={locale}
|
|
180
|
+
lang={locale as Locale}
|
|
180
181
|
navSection={navSection}
|
|
181
182
|
currentVersion={currentVersion}
|
|
182
183
|
currentPath={currentPath}
|