create-zudo-doc 0.2.0 → 0.2.1
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/preset.js +11 -0
- package/dist/prompts.js +2 -6
- package/dist/scaffold.js +15 -9
- package/dist/settings-gen.js +7 -7
- 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/_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 +51 -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/sidebar-toggle.tsx +1 -1
- package/templates/base/src/components/sidebar-tree.tsx +10 -4
- 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/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 +28 -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/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/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/components/theme-toggle.tsx +0 -107
- 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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @jsxImportSource preact */
|
|
3
3
|
// Page module for the default-locale per-tag detail route.
|
|
4
4
|
//
|
|
5
|
-
// Default-locale
|
|
5
|
+
// Default-locale per-tag detail page. paths() enumerates one route per
|
|
6
6
|
// unique tag in the "docs" collection and passes the resolved TagInfo as a
|
|
7
7
|
// prop so the component has zero extra collection reads at render time.
|
|
8
8
|
//
|
|
@@ -10,29 +10,13 @@
|
|
|
10
10
|
// params: { tag: string }
|
|
11
11
|
// props: { tagInfo: TagInfo }
|
|
12
12
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// render: DocCardGrid with pre-resolved TagInfo.docs items
|
|
13
|
+
// Tag collection + rendering are shared with the locale-prefixed route via
|
|
14
|
+
// pages/lib/_tag-pages.tsx (#2010) — this file owns only the param shape.
|
|
16
15
|
|
|
17
|
-
import {
|
|
18
|
-
import { collectTags } from "@/utils/tags";
|
|
16
|
+
import { defaultLocale } from "@/config/i18n";
|
|
19
17
|
import type { TagInfo } from "@/utils/tags";
|
|
20
|
-
import { toRouteSlug } from "@/utils/slug";
|
|
21
|
-
import { t, defaultLocale } from "@/config/i18n";
|
|
22
|
-
import { settings } from "@/config/settings";
|
|
23
|
-
import { withBase, docsUrl } from "@/utils/base";
|
|
24
|
-
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
25
|
-
import { Breadcrumb } from "@takazudo/zudo-doc/breadcrumb";
|
|
26
|
-
import type { BreadcrumbItem } from "@takazudo/zudo-doc/breadcrumb";
|
|
27
|
-
import { DocCardGrid } from "@takazudo/zudo-doc/nav-indexing";
|
|
28
18
|
import type { JSX } from "preact";
|
|
29
|
-
import {
|
|
30
|
-
import { FooterWithDefaults } from "../../lib/_footer-with-defaults";
|
|
31
|
-
import { HeaderWithDefaults } from "../../lib/_header-with-defaults";
|
|
32
|
-
import { HeadWithDefaults } from "../../lib/_head-with-defaults";
|
|
33
|
-
import { composeMetaTitle } from "../../lib/_compose-meta-title";
|
|
34
|
-
import { DocHistoryArea } from "../../lib/_doc-history-area";
|
|
35
|
-
import { BodyEndIslands } from "../../lib/_body-end-islands";
|
|
19
|
+
import { collectTagMapForLocale, TagDetailPageView } from "../../lib/_tag-pages";
|
|
36
20
|
|
|
37
21
|
export const frontmatter = { title: "Tag" };
|
|
38
22
|
|
|
@@ -41,14 +25,7 @@ export function paths(): Array<{
|
|
|
41
25
|
params: { tag: string };
|
|
42
26
|
props: { tagInfo: TagInfo };
|
|
43
27
|
}> {
|
|
44
|
-
const
|
|
45
|
-
// category_no_page index.mdx builds no route — drop it so a tag it carries
|
|
46
|
-
// doesn't render a DocCard linking to a non-existent /docs/<cat>/ page.
|
|
47
|
-
const docs = allDocs.filter(
|
|
48
|
-
(doc) =>
|
|
49
|
-
!doc.data.unlisted && !doc.data.draft && !doc.data.category_no_page,
|
|
50
|
-
);
|
|
51
|
-
const tagMap = collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
|
|
28
|
+
const tagMap = collectTagMapForLocale(defaultLocale);
|
|
52
29
|
|
|
53
30
|
return [...tagMap.entries()].map(([tag, tagInfo]) => ({
|
|
54
31
|
params: { tag },
|
|
@@ -62,44 +39,5 @@ interface PageProps {
|
|
|
62
39
|
}
|
|
63
40
|
|
|
64
41
|
export default function DocTagPage({ params, tagInfo }: PageProps): JSX.Element {
|
|
65
|
-
|
|
66
|
-
const locale = defaultLocale;
|
|
67
|
-
|
|
68
|
-
const countText =
|
|
69
|
-
tagInfo.count === 1
|
|
70
|
-
? t("doc.pageCountSingle", locale).replace("{count}", String(tagInfo.count))
|
|
71
|
-
: t("doc.pageCount", locale).replace("{count}", String(tagInfo.count));
|
|
72
|
-
|
|
73
|
-
const pageTitle = `${t("doc.taggedWith", locale)}: ${tag}`;
|
|
74
|
-
|
|
75
|
-
const breadcrumbItems: BreadcrumbItem[] = [
|
|
76
|
-
{ label: "Docs" },
|
|
77
|
-
{ label: t("doc.allTags", locale), href: withBase("/docs/tags") },
|
|
78
|
-
{ label: tag },
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
const cardItems = tagInfo.docs.map((doc) => ({
|
|
82
|
-
href: docsUrl(doc.slug, locale),
|
|
83
|
-
title: doc.title,
|
|
84
|
-
description: doc.description,
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<DocLayoutWithDefaults
|
|
89
|
-
title={composeMetaTitle(pageTitle)}
|
|
90
|
-
head={<HeadWithDefaults title={pageTitle} />}
|
|
91
|
-
noindex={settings.noindex}
|
|
92
|
-
hideSidebar={true}
|
|
93
|
-
hideToc={true}
|
|
94
|
-
headerOverride={<HeaderWithDefaults lang={locale} currentPath={withBase(`/docs/tags/${tag}`)} />}
|
|
95
|
-
breadcrumbOverride={<Breadcrumb items={breadcrumbItems} />}
|
|
96
|
-
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
97
|
-
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
98
|
-
>
|
|
99
|
-
<h1 class="text-heading font-bold mb-vsp-xs">{pageTitle}</h1>
|
|
100
|
-
<p class="text-muted mb-vsp-lg">{countText}</p>
|
|
101
|
-
<DocCardGrid ariaLabel={pageTitle} items={cardItems} />
|
|
102
|
-
<DocHistoryArea slug={`tags/${tag}`} locale={locale} />
|
|
103
|
-
</DocLayoutWithDefaults>
|
|
104
|
-
);
|
|
42
|
+
return <TagDetailPageView locale={defaultLocale} tag={params.tag} tagInfo={tagInfo} />;
|
|
105
43
|
}
|
|
@@ -2,89 +2,19 @@
|
|
|
2
2
|
/** @jsxImportSource preact */
|
|
3
3
|
// Page module for the default-locale "All Tags" index route.
|
|
4
4
|
//
|
|
5
|
-
// Default-locale
|
|
5
|
+
// Default-locale "All Tags" index page. Collects every tag across the
|
|
6
6
|
// "docs" collection, sorts them alphabetically, and renders a full tag cloud
|
|
7
7
|
// via the v2 TagNav component. No dynamic params — single static route.
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// → collectTags() builds { tag → { count, docs[] } }
|
|
12
|
-
// → sort by tag preserves sort parity with Astro original
|
|
13
|
-
// → TagNav variant="all" renders the chip cloud
|
|
9
|
+
// Tag collection + rendering are shared with the locale-prefixed route via
|
|
10
|
+
// pages/lib/_tag-pages.tsx (#2010).
|
|
14
11
|
|
|
15
|
-
import {
|
|
16
|
-
import { collectTags } from "@/utils/tags";
|
|
17
|
-
import { toRouteSlug } from "@/utils/slug";
|
|
18
|
-
import { t, defaultLocale } from "@/config/i18n";
|
|
19
|
-
import { withBase } from "@/utils/base";
|
|
20
|
-
import { settings } from "@/config/settings";
|
|
21
|
-
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
22
|
-
import { Breadcrumb } from "@takazudo/zudo-doc/breadcrumb";
|
|
23
|
-
import type { BreadcrumbItem } from "@takazudo/zudo-doc/breadcrumb";
|
|
24
|
-
import { TagNav } from "@takazudo/zudo-doc/nav-indexing";
|
|
25
|
-
import type { TagItem, TagNavLabels } from "@takazudo/zudo-doc/nav-indexing";
|
|
12
|
+
import { defaultLocale } from "@/config/i18n";
|
|
26
13
|
import type { JSX } from "preact";
|
|
27
|
-
import {
|
|
28
|
-
import { FooterWithDefaults } from "../../lib/_footer-with-defaults";
|
|
29
|
-
import { HeaderWithDefaults } from "../../lib/_header-with-defaults";
|
|
30
|
-
import { HeadWithDefaults } from "../../lib/_head-with-defaults";
|
|
31
|
-
import { composeMetaTitle } from "../../lib/_compose-meta-title";
|
|
32
|
-
import { DocHistoryArea } from "../../lib/_doc-history-area";
|
|
33
|
-
import { BodyEndIslands } from "../../lib/_body-end-islands";
|
|
14
|
+
import { TagsIndexPageView } from "../../lib/_tag-pages";
|
|
34
15
|
|
|
35
16
|
export const frontmatter = { title: "All Tags" };
|
|
36
17
|
|
|
37
18
|
export default function DocsTagsIndexPage(): JSX.Element {
|
|
38
|
-
|
|
39
|
-
const pageTitle = t("doc.allTags", locale);
|
|
40
|
-
|
|
41
|
-
const allDocs = bridgeDocsEntries(getCollection<ZfbDocsData>("docs"), "docs");
|
|
42
|
-
// category_no_page index.mdx builds no route — drop it so a tag it carries
|
|
43
|
-
// doesn't inflate the tag list with a card linking to a non-existent page.
|
|
44
|
-
const docs = allDocs.filter(
|
|
45
|
-
(doc) =>
|
|
46
|
-
!doc.data.unlisted && !doc.data.draft && !doc.data.category_no_page,
|
|
47
|
-
);
|
|
48
|
-
const tagMap = collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
|
|
49
|
-
|
|
50
|
-
const labels: TagNavLabels = {
|
|
51
|
-
tags: t("doc.tags", locale),
|
|
52
|
-
taggedWith: t("doc.taggedWith", locale),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Sort alphabetically — matches documented tag-nav sort order.
|
|
56
|
-
const tags: TagItem[] = [...tagMap.values()]
|
|
57
|
-
.sort((a, b) => a.tag.localeCompare(b.tag, locale))
|
|
58
|
-
.map((info) => ({
|
|
59
|
-
tag: info.tag,
|
|
60
|
-
count: info.count,
|
|
61
|
-
href: withBase(`/docs/tags/${info.tag}`),
|
|
62
|
-
}));
|
|
63
|
-
|
|
64
|
-
const breadcrumbItems: BreadcrumbItem[] = [
|
|
65
|
-
{ label: "Docs" },
|
|
66
|
-
{ label: pageTitle },
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<DocLayoutWithDefaults
|
|
71
|
-
title={composeMetaTitle(pageTitle)}
|
|
72
|
-
head={<HeadWithDefaults title={pageTitle} />}
|
|
73
|
-
noindex={settings.noindex}
|
|
74
|
-
hideSidebar={true}
|
|
75
|
-
hideToc={true}
|
|
76
|
-
headerOverride={<HeaderWithDefaults lang={locale} currentPath={withBase("/docs/tags")} />}
|
|
77
|
-
breadcrumbOverride={<Breadcrumb items={breadcrumbItems} />}
|
|
78
|
-
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
79
|
-
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
80
|
-
>
|
|
81
|
-
<h1 class="text-heading font-bold mb-vsp-lg">{pageTitle}</h1>
|
|
82
|
-
{!settings.docTags || tags.length === 0 ? (
|
|
83
|
-
<p class="text-muted">{t("doc.noTags", locale)}</p>
|
|
84
|
-
) : (
|
|
85
|
-
<TagNav variant="all" tags={tags} labels={labels} />
|
|
86
|
-
)}
|
|
87
|
-
<DocHistoryArea slug="tags" locale={locale} />
|
|
88
|
-
</DocLayoutWithDefaults>
|
|
89
|
-
);
|
|
19
|
+
return <TagsIndexPageView locale={defaultLocale} />;
|
|
90
20
|
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
// Shared data + renderers for the doc-tags pages (#2010).
|
|
4
|
+
//
|
|
5
|
+
// Collapses the per-locale page pairs:
|
|
6
|
+
// pages/docs/tags/[tag].tsx + pages/[locale]/docs/tags/[tag].tsx
|
|
7
|
+
// pages/docs/tags/index.tsx + pages/[locale]/docs/tags/index.tsx
|
|
8
|
+
// into locale-parameterized helpers. The page files stay thin shells that own
|
|
9
|
+
// only their paths() param shapes; URL prefixes and the default-vs-locale
|
|
10
|
+
// data path branch live here.
|
|
11
|
+
//
|
|
12
|
+
// Data-path branch (kept exactly as the original pages had it):
|
|
13
|
+
// - Default locale: the "docs" collection directly, filtered
|
|
14
|
+
// unlisted/draft/category_no_page before tag collection.
|
|
15
|
+
// - Non-default locale: locale-first merge with base fallback
|
|
16
|
+
// (see locale-merge.ts) — draft-filtered inputs, unlisted dropped by the
|
|
17
|
+
// merge default, category_no_page filtered AFTER the merge so a locale
|
|
18
|
+
// override carrying the flag first wins the merge (suppressing the base
|
|
19
|
+
// doc); pre-merge filtering would drop it from localeSlugSet and the
|
|
20
|
+
// unflagged base doc would resurface as a card linking to a locale route
|
|
21
|
+
// the docs route never builds.
|
|
22
|
+
|
|
23
|
+
import { collectTags } from "@/utils/tags";
|
|
24
|
+
import type { TagInfo } from "@/utils/tags";
|
|
25
|
+
import { toRouteSlug } from "@/utils/slug";
|
|
26
|
+
import { t, defaultLocale } from "@/config/i18n";
|
|
27
|
+
import { settings } from "@/config/settings";
|
|
28
|
+
import { withBase, docsUrl } from "@/utils/base";
|
|
29
|
+
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
30
|
+
import { Breadcrumb } from "@takazudo/zudo-doc/breadcrumb";
|
|
31
|
+
import type { BreadcrumbItem } from "@takazudo/zudo-doc/breadcrumb";
|
|
32
|
+
import { DocCardGrid, TagNav } from "@takazudo/zudo-doc/nav-indexing";
|
|
33
|
+
import type { TagItem, TagNavLabels } from "@takazudo/zudo-doc/nav-indexing";
|
|
34
|
+
import type { JSX } from "preact";
|
|
35
|
+
import { FooterWithDefaults } from "./_footer-with-defaults";
|
|
36
|
+
import { HeaderWithDefaults } from "./_header-with-defaults";
|
|
37
|
+
import { HeadWithDefaults } from "./_head-with-defaults";
|
|
38
|
+
import { composeMetaTitle } from "./_compose-meta-title";
|
|
39
|
+
import { DocHistoryArea } from "./_doc-history-area";
|
|
40
|
+
import { BodyEndIslands } from "./_body-end-islands";
|
|
41
|
+
import { stableDocs, memoizeDerived } from "./_nav-source-cache";
|
|
42
|
+
import { mergeLocaleDocs } from "./locale-merge";
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Tag collection
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build the { tag → TagInfo } map for one locale, preserving each original
|
|
50
|
+
* page's exact data path (see the module-header branch notes).
|
|
51
|
+
*
|
|
52
|
+
* Memoized on the snapshot-anchored stableDocs identity (same pattern as
|
|
53
|
+
* _nav-source-cache) so the collection bridging and tag aggregation run once
|
|
54
|
+
* per locale per build rather than once per built tag page.
|
|
55
|
+
*/
|
|
56
|
+
export function collectTagMapForLocale(locale: string): Map<string, TagInfo> {
|
|
57
|
+
if (locale === defaultLocale) {
|
|
58
|
+
const baseDocs = stableDocs("docs");
|
|
59
|
+
return memoizeDerived([baseDocs], "tagMap;default", () => {
|
|
60
|
+
// category_no_page index.mdx builds no route — drop it so a tag it carries
|
|
61
|
+
// doesn't render a DocCard linking to a non-existent /docs/<cat>/ page.
|
|
62
|
+
const docs = baseDocs.filter(
|
|
63
|
+
(doc) =>
|
|
64
|
+
!doc.data.unlisted && !doc.data.draft && !doc.data.category_no_page,
|
|
65
|
+
);
|
|
66
|
+
return collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const baseDocs = stableDocs("docs");
|
|
71
|
+
const localeDocs = stableDocs(`docs-${locale}`);
|
|
72
|
+
return memoizeDerived([baseDocs, localeDocs], `tagMap;${locale}`, () => {
|
|
73
|
+
const { docs: mergedDocs } = mergeLocaleDocs({
|
|
74
|
+
baseDocs: baseDocs.filter((d) => !d.data.draft),
|
|
75
|
+
localeDocs: localeDocs.filter((d) => !d.data.draft),
|
|
76
|
+
applyDefaultLocaleOnlyFilter: true,
|
|
77
|
+
});
|
|
78
|
+
const docs = mergedDocs.filter((d) => !d.data.category_no_page);
|
|
79
|
+
return collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Shared renderers
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
/** URL prefix for a locale: "" for the default locale, "/{locale}" otherwise. */
|
|
88
|
+
function localePrefix(locale: string): string {
|
|
89
|
+
return locale === defaultLocale ? "" : `/${locale}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Per-tag detail page (chip target). */
|
|
93
|
+
export function TagDetailPageView({
|
|
94
|
+
locale,
|
|
95
|
+
tag,
|
|
96
|
+
tagInfo,
|
|
97
|
+
}: {
|
|
98
|
+
locale: string;
|
|
99
|
+
tag: string;
|
|
100
|
+
tagInfo: TagInfo;
|
|
101
|
+
}): JSX.Element {
|
|
102
|
+
const isDefault = locale === defaultLocale;
|
|
103
|
+
const prefix = localePrefix(locale);
|
|
104
|
+
|
|
105
|
+
const countText =
|
|
106
|
+
tagInfo.count === 1
|
|
107
|
+
? t("doc.pageCountSingle", locale).replace("{count}", String(tagInfo.count))
|
|
108
|
+
: t("doc.pageCount", locale).replace("{count}", String(tagInfo.count));
|
|
109
|
+
|
|
110
|
+
const pageTitle = `${t("doc.taggedWith", locale)}: ${tag}`;
|
|
111
|
+
|
|
112
|
+
const breadcrumbItems: BreadcrumbItem[] = [
|
|
113
|
+
{ label: "Docs" },
|
|
114
|
+
{ label: t("doc.allTags", locale), href: withBase(`${prefix}/docs/tags`) },
|
|
115
|
+
{ label: tag },
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const cardItems = tagInfo.docs.map((doc) => ({
|
|
119
|
+
href: docsUrl(doc.slug, locale),
|
|
120
|
+
title: doc.title,
|
|
121
|
+
description: doc.description,
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<DocLayoutWithDefaults
|
|
126
|
+
title={composeMetaTitle(pageTitle)}
|
|
127
|
+
head={<HeadWithDefaults title={pageTitle} />}
|
|
128
|
+
// The original default-locale page omitted `lang` entirely; passing
|
|
129
|
+
// undefined relies on Preact treating an undefined prop as absent.
|
|
130
|
+
lang={isDefault ? undefined : locale}
|
|
131
|
+
noindex={settings.noindex}
|
|
132
|
+
hideSidebar={true}
|
|
133
|
+
hideToc={true}
|
|
134
|
+
// Tag segment URL-encoded — emitted href/path sites only; route params
|
|
135
|
+
// stay raw (e.g. "type:guide" → "type%3Aguide").
|
|
136
|
+
headerOverride={<HeaderWithDefaults lang={locale} currentPath={withBase(`${prefix}/docs/tags/${encodeURIComponent(tag)}`)} />}
|
|
137
|
+
breadcrumbOverride={<Breadcrumb items={breadcrumbItems} />}
|
|
138
|
+
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
139
|
+
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
140
|
+
>
|
|
141
|
+
<h1 class="text-heading font-bold mb-vsp-xs">{pageTitle}</h1>
|
|
142
|
+
<p class="text-muted mb-vsp-lg">{countText}</p>
|
|
143
|
+
<DocCardGrid ariaLabel={pageTitle} items={cardItems} />
|
|
144
|
+
<DocHistoryArea slug={`tags/${tag}`} locale={locale} />
|
|
145
|
+
</DocLayoutWithDefaults>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** "All Tags" index page — computes the tag map at render time (matching the
|
|
150
|
+
* original pages, which had no props from paths()). */
|
|
151
|
+
export function TagsIndexPageView({ locale }: { locale: string }): JSX.Element {
|
|
152
|
+
const isDefault = locale === defaultLocale;
|
|
153
|
+
const prefix = localePrefix(locale);
|
|
154
|
+
const pageTitle = t("doc.allTags", locale);
|
|
155
|
+
|
|
156
|
+
const tagMap = collectTagMapForLocale(locale);
|
|
157
|
+
|
|
158
|
+
const labels: TagNavLabels = {
|
|
159
|
+
tags: t("doc.tags", locale),
|
|
160
|
+
taggedWith: t("doc.taggedWith", locale),
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Sort alphabetically using the page locale — matches documented tag-nav sort order.
|
|
164
|
+
const tags: TagItem[] = [...tagMap.values()]
|
|
165
|
+
.sort((a, b) => a.tag.localeCompare(b.tag, locale))
|
|
166
|
+
.map((info) => ({
|
|
167
|
+
tag: info.tag,
|
|
168
|
+
count: info.count,
|
|
169
|
+
// Tag segment URL-encoded — href sites only; route params stay raw.
|
|
170
|
+
href: withBase(`${prefix}/docs/tags/${encodeURIComponent(info.tag)}`),
|
|
171
|
+
}));
|
|
172
|
+
|
|
173
|
+
const breadcrumbItems: BreadcrumbItem[] = [
|
|
174
|
+
{ label: "Docs" },
|
|
175
|
+
{ label: pageTitle },
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<DocLayoutWithDefaults
|
|
180
|
+
title={composeMetaTitle(pageTitle)}
|
|
181
|
+
head={<HeadWithDefaults title={pageTitle} />}
|
|
182
|
+
// Same undefined-≡-absent reliance as TagDetailPageView above.
|
|
183
|
+
lang={isDefault ? undefined : locale}
|
|
184
|
+
noindex={settings.noindex}
|
|
185
|
+
hideSidebar={true}
|
|
186
|
+
hideToc={true}
|
|
187
|
+
headerOverride={<HeaderWithDefaults lang={locale} currentPath={withBase(`${prefix}/docs/tags`)} />}
|
|
188
|
+
breadcrumbOverride={<Breadcrumb items={breadcrumbItems} />}
|
|
189
|
+
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
190
|
+
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
191
|
+
>
|
|
192
|
+
<h1 class="text-heading font-bold mb-vsp-lg">{pageTitle}</h1>
|
|
193
|
+
{!settings.docTags || tags.length === 0 ? (
|
|
194
|
+
<p class="text-muted">{t("doc.noTags", locale)}</p>
|
|
195
|
+
) : (
|
|
196
|
+
<TagNav variant="all" tags={tags} labels={labels} />
|
|
197
|
+
)}
|
|
198
|
+
<DocHistoryArea slug="tags" locale={locale} />
|
|
199
|
+
</DocLayoutWithDefaults>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
@@ -23,29 +23,18 @@
|
|
|
23
23
|
// - Non-default locales emit /{locale}/docs/{slug}.
|
|
24
24
|
// - Locale-first merge: locale docs take priority; base EN docs fill in
|
|
25
25
|
// pages not translated yet (shown with a fallback notice).
|
|
26
|
+
//
|
|
27
|
+
// Enumeration + per-entry derived data are built by the shared, memoized
|
|
28
|
+
// buildDocRouteEntries (#2010); rendering by the shared renderDocPage. This
|
|
29
|
+
// file owns only the route's nav source and the param/prop shapes.
|
|
26
30
|
|
|
27
31
|
import { settings } from "@/config/settings";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
buildNavTree,
|
|
31
|
-
buildBreadcrumbs,
|
|
32
|
-
collectAutoIndexNodes,
|
|
33
|
-
type NavNode,
|
|
34
|
-
} from "@/utils/docs";
|
|
35
|
-
import { getNavSectionForSlug, getNavSubtree } from "@/utils/nav-scope";
|
|
36
|
-
import { toRouteSlug, toSlugParams } from "@/utils/slug";
|
|
37
|
-
// Shared MDX components bag — see `pages/_mdx-components.ts`.
|
|
38
|
-
import { createMdxComponents } from "../../_mdx-components";
|
|
32
|
+
import { getLocaleConfig, type Locale } from "@/config/i18n";
|
|
39
33
|
import type { JSX } from "preact";
|
|
40
34
|
import { resolveNavSource } from "../../lib/_nav-source-docs";
|
|
41
|
-
import {
|
|
42
|
-
import
|
|
43
|
-
import {
|
|
44
|
-
import { DocMetainfoArea } from "../../lib/_doc-metainfo-area";
|
|
45
|
-
import { buildInlineVersionSwitcher } from "../../lib/_inline-version-switcher";
|
|
46
|
-
import { DocContentHeader } from "../../lib/_doc-content-header";
|
|
47
|
-
import { DocPageShell } from "../../lib/_doc-page-shell";
|
|
48
|
-
import { resolveDocPrevNext, flattenSubtree } from "../../lib/_doc-route-paths";
|
|
35
|
+
import type { DocPageEntryProps, DocPageAutoIndexProps } from "../../lib/doc-page-props";
|
|
36
|
+
import { buildDocRouteEntries } from "../../lib/_doc-route-entries";
|
|
37
|
+
import { renderDocPage } from "../../lib/_doc-page-renderer";
|
|
49
38
|
|
|
50
39
|
export const frontmatter = { title: "Docs" };
|
|
51
40
|
|
|
@@ -53,8 +42,6 @@ export const frontmatter = { title: "Docs" };
|
|
|
53
42
|
// Types
|
|
54
43
|
// ---------------------------------------------------------------------------
|
|
55
44
|
|
|
56
|
-
// DocPageEntry, AutoIndexNode imported from pages/lib/doc-page-props.ts
|
|
57
|
-
|
|
58
45
|
/** Route-specific extra fields — present on both branches of the union. */
|
|
59
46
|
interface LocaleDocPageExtra {
|
|
60
47
|
/** Content directory for the active locale (or base EN for fallbacks). */
|
|
@@ -81,8 +68,9 @@ type DocPageProps =
|
|
|
81
68
|
* 4. Track fallback slugs for the fallback-notice banner.
|
|
82
69
|
* 5. Build nav tree, compute breadcrumbs and prev/next for each entry.
|
|
83
70
|
*
|
|
84
|
-
* Fallback
|
|
85
|
-
* the "not yet translated" notice (matching
|
|
71
|
+
* Fallback detection (`isFallback`) comes from the merge's localeSlugSet —
|
|
72
|
+
* the component uses it to show the "not yet translated" notice (matching
|
|
73
|
+
* the Astro original).
|
|
86
74
|
*/
|
|
87
75
|
export function paths(): Array<{
|
|
88
76
|
params: { locale: string; slug: string[] };
|
|
@@ -94,85 +82,36 @@ export function paths(): Array<{
|
|
|
94
82
|
}> = [];
|
|
95
83
|
|
|
96
84
|
for (const locale of Object.keys(settings.locales) as string[]) {
|
|
97
|
-
const localeConfig =
|
|
85
|
+
const localeConfig = getLocaleConfig(locale);
|
|
98
86
|
const contentDir = localeConfig?.dir ?? settings.docsDir;
|
|
99
87
|
|
|
100
88
|
// Identity-stable, locale-first merge with EN fallback. The same `docs` /
|
|
101
89
|
// `navDocs` / `categoryMeta` instances are reused across this route's many
|
|
102
|
-
// per-page paths() invocations so buildNavTree's identity fast-path
|
|
103
|
-
// the key
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// A `category_no_page` index.mdx is metadata-only — kept in the nav tree
|
|
122
|
-
// for breadcrumbs but emits no route (zfb retains every .mdx as a
|
|
123
|
-
// collection entry, so the skip must be explicit).
|
|
124
|
-
if (entry.data.category_no_page === true) continue;
|
|
125
|
-
// Canonical route slug via the one shared rule (@/utils/slug). `entry.id`
|
|
126
|
-
// is already `toRouteSlug(entry.slug)` (bridgeEntries → stripIndexSuffix →
|
|
127
|
-
// toRouteSlug), so this is identical to the previous `entry.id` form for
|
|
128
|
-
// every entry — but stating it explicitly removes the historical id-vs-
|
|
129
|
-
// toRouteSlug asymmetry with the EN route and the component below, all of
|
|
130
|
-
// which now yield "" for a root index (URL /{locale}/docs/ — #1891).
|
|
131
|
-
const slug = entry.data.slug ?? toRouteSlug(entry.slug);
|
|
132
|
-
const isFallback = fallbackSlugs.has(slug);
|
|
133
|
-
const entryContentDir = isFallback ? settings.docsDir : contentDir;
|
|
134
|
-
|
|
135
|
-
const navSection = getNavSectionForSlug(slug);
|
|
136
|
-
const subtree = getNavSubtree(tree, navSection);
|
|
137
|
-
|
|
138
|
-
// Prev/next + pagination overrides against THIS locale's own `tree`.
|
|
139
|
-
// Latest content (no version) — hrefs stay unversioned (no rewrite).
|
|
140
|
-
const { prev: prevNode, next: nextNode } = resolveDocPrevNext(
|
|
141
|
-
tree,
|
|
142
|
-
flattenSubtree(subtree),
|
|
143
|
-
slug,
|
|
144
|
-
entry.data,
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
result.push({
|
|
148
|
-
params: { locale, slug: toSlugParams(slug) },
|
|
149
|
-
props: {
|
|
150
|
-
kind: "entry",
|
|
151
|
-
entry,
|
|
152
|
-
contentDir: entryContentDir,
|
|
153
|
-
isFallback,
|
|
154
|
-
breadcrumbs: buildBreadcrumbs(fullTree, slug, locale),
|
|
155
|
-
prev: prevNode,
|
|
156
|
-
next: nextNode,
|
|
157
|
-
headings: extractHeadings(entry.body ?? ""),
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Auto-generated index pages for categories without index.mdx
|
|
163
|
-
for (const node of collectAutoIndexNodes(tree)) {
|
|
90
|
+
// per-page paths() invocations so both buildNavTree's identity fast-path
|
|
91
|
+
// and the buildDocRouteEntries memo key on them — see
|
|
92
|
+
// pages/lib/_nav-source-docs.ts (#1902).
|
|
93
|
+
const source = resolveNavSource(locale as Locale, undefined, {
|
|
94
|
+
applyDefaultLocaleOnlyFilter: true,
|
|
95
|
+
keepUnlisted: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
for (const item of buildDocRouteEntries({
|
|
99
|
+
source,
|
|
100
|
+
locale: locale as Locale,
|
|
101
|
+
routeSig: `locale-docs;${locale}`,
|
|
102
|
+
})) {
|
|
103
|
+
// isFallback: page came from base docs, not the locale collection.
|
|
104
|
+
// Always false for autoIndex items (item.isFallback already is).
|
|
105
|
+
const extra: LocaleDocPageExtra = {
|
|
106
|
+
contentDir: item.isFallback ? settings.docsDir : contentDir,
|
|
107
|
+
isFallback: item.isFallback,
|
|
108
|
+
};
|
|
164
109
|
result.push({
|
|
165
|
-
params: { locale, slug:
|
|
166
|
-
props:
|
|
167
|
-
kind
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
isFallback: false,
|
|
171
|
-
breadcrumbs: buildBreadcrumbs(fullTree, node.slug, locale),
|
|
172
|
-
prev: null,
|
|
173
|
-
next: null,
|
|
174
|
-
headings: [],
|
|
175
|
-
},
|
|
110
|
+
params: { locale, slug: item.slugParams },
|
|
111
|
+
props:
|
|
112
|
+
item.props.kind === "entry"
|
|
113
|
+
? { ...item.props, ...extra }
|
|
114
|
+
: { ...item.props, ...extra },
|
|
176
115
|
});
|
|
177
116
|
}
|
|
178
117
|
}
|
|
@@ -187,86 +126,9 @@ export function paths(): Array<{
|
|
|
187
126
|
type PageArgs = DocPageProps & { params: { locale: string; slug: string[] } };
|
|
188
127
|
|
|
189
128
|
export default function LocaleDocsPage(props: PageArgs): JSX.Element {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
: (props.entry.data.slug ?? toRouteSlug(props.entry.slug));
|
|
196
|
-
|
|
197
|
-
const title = props.kind === "autoIndex" ? props.autoIndex.label : props.entry.data.title;
|
|
198
|
-
const description = props.kind === "autoIndex" ? props.autoIndex.description : props.entry.data.description;
|
|
199
|
-
|
|
200
|
-
// Locale-aware components bag — creates nav wrappers bound to the active
|
|
201
|
-
// locale so CategoryNav/CategoryTreeNav/SiteTreeNav query the right collection.
|
|
202
|
-
const components = createMdxComponents(locale);
|
|
203
|
-
|
|
204
|
-
// Latest content (no version) — keep the nav node's own docsUrl href.
|
|
205
|
-
const autoIndexChildren = props.kind === "autoIndex"
|
|
206
|
-
? props.autoIndex.children
|
|
207
|
-
.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
208
|
-
.map((c: NavNode) => ({
|
|
209
|
-
...c,
|
|
210
|
-
href: c.href ?? docsUrl(c.slug, locale),
|
|
211
|
-
}))
|
|
212
|
-
: [];
|
|
213
|
-
|
|
214
|
-
// Canonical URL — base-prefixed locale page path, absolutized against siteUrl.
|
|
215
|
-
const currentPath = docsUrl(slug, locale);
|
|
216
|
-
const canonical = absoluteUrl(currentPath);
|
|
217
|
-
|
|
218
|
-
// Persist key: locale + nav-section so the sidebar DOM node is reused
|
|
219
|
-
// across same-locale + same-section navigations only. No sanitizer needed —
|
|
220
|
-
// both lang (BCP-47 locale string) and navSection (filesystem-derived
|
|
221
|
-
// kebab-case slug) come from controlled, trusted sources.
|
|
222
|
-
const navSection = getNavSectionForSlug(slug);
|
|
223
|
-
const hideSidebar = props.kind === "entry" ? props.entry.data.hide_sidebar : undefined;
|
|
224
|
-
const sidebarPersistKey = hideSidebar
|
|
225
|
-
? undefined
|
|
226
|
-
: `sidebar-${locale}-${navSection ?? "default"}`;
|
|
227
|
-
|
|
228
|
-
return (
|
|
229
|
-
<DocPageShell
|
|
230
|
-
kind={props.kind}
|
|
231
|
-
locale={locale}
|
|
232
|
-
slug={slug}
|
|
233
|
-
title={title}
|
|
234
|
-
description={description}
|
|
235
|
-
canonical={canonical}
|
|
236
|
-
breadcrumbs={breadcrumbs}
|
|
237
|
-
prev={prev}
|
|
238
|
-
next={next}
|
|
239
|
-
headings={headings}
|
|
240
|
-
navSection={navSection}
|
|
241
|
-
sidebarPersistKey={sidebarPersistKey}
|
|
242
|
-
hideSidebar={hideSidebar}
|
|
243
|
-
hideToc={props.kind === "entry" ? props.entry.data.hide_toc : undefined}
|
|
244
|
-
currentPath={currentPath}
|
|
245
|
-
versionSwitcher={buildInlineVersionSwitcher(slug, locale)}
|
|
246
|
-
autoIndexLabel={props.kind === "autoIndex" ? props.autoIndex.label : undefined}
|
|
247
|
-
autoIndexChildren={autoIndexChildren}
|
|
248
|
-
metainfoSlot={
|
|
249
|
-
props.kind === "autoIndex" ? <DocMetainfoArea slug={slug} locale={locale} /> : null
|
|
250
|
-
}
|
|
251
|
-
contentHeaderSlot={
|
|
252
|
-
props.kind === "entry" ? (
|
|
253
|
-
<DocContentHeader entry={props.entry} slug={slug} locale={locale} isFallback={isFallback} />
|
|
254
|
-
) : undefined
|
|
255
|
-
}
|
|
256
|
-
contentSlot={
|
|
257
|
-
props.kind === "entry" ? <props.entry.Content components={components} /> : undefined
|
|
258
|
-
}
|
|
259
|
-
docHistorySlot={
|
|
260
|
-
props.kind === "entry" && !props.entry.data.unlisted ? (
|
|
261
|
-
<DocHistoryArea
|
|
262
|
-
slug={slug}
|
|
263
|
-
locale={locale}
|
|
264
|
-
entrySlug={props.entry.slug}
|
|
265
|
-
contentDir={contentDir}
|
|
266
|
-
isFallback={isFallback}
|
|
267
|
-
/>
|
|
268
|
-
) : null
|
|
269
|
-
}
|
|
270
|
-
/>
|
|
271
|
-
);
|
|
129
|
+
return renderDocPage(props, {
|
|
130
|
+
locale: props.params.locale as Locale,
|
|
131
|
+
isFallback: props.isFallback,
|
|
132
|
+
docHistoryContentDir: props.contentDir,
|
|
133
|
+
});
|
|
272
134
|
}
|