create-zudo-doc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/bin/create-zudo-doc.js +2 -0
- package/dist/api.d.ts +20 -0
- package/dist/api.js +13 -0
- package/dist/claude-md-gen.d.ts +2 -0
- package/dist/claude-md-gen.js +113 -0
- package/dist/cli.d.ts +39 -0
- package/dist/cli.js +157 -0
- package/dist/compose.d.ts +95 -0
- package/dist/compose.js +206 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.js +224 -0
- package/dist/features/body-foot-util.d.ts +10 -0
- package/dist/features/body-foot-util.js +12 -0
- package/dist/features/claude-resources.d.ts +2 -0
- package/dist/features/claude-resources.js +6 -0
- package/dist/features/design-token-panel.d.ts +14 -0
- package/dist/features/design-token-panel.js +27 -0
- package/dist/features/doc-history.d.ts +9 -0
- package/dist/features/doc-history.js +11 -0
- package/dist/features/doc-tags.d.ts +19 -0
- package/dist/features/doc-tags.js +33 -0
- package/dist/features/footer-taglist.d.ts +14 -0
- package/dist/features/footer-taglist.js +17 -0
- package/dist/features/footer.d.ts +8 -0
- package/dist/features/footer.js +10 -0
- package/dist/features/i18n.d.ts +22 -0
- package/dist/features/i18n.js +41 -0
- package/dist/features/image-enlarge.d.ts +11 -0
- package/dist/features/image-enlarge.js +13 -0
- package/dist/features/index.d.ts +15 -0
- package/dist/features/index.js +53 -0
- package/dist/features/llms-txt.d.ts +11 -0
- package/dist/features/llms-txt.js +13 -0
- package/dist/features/search.d.ts +9 -0
- package/dist/features/search.js +11 -0
- package/dist/features/sidebar-resizer.d.ts +14 -0
- package/dist/features/sidebar-resizer.js +16 -0
- package/dist/features/sidebar-toggle.d.ts +13 -0
- package/dist/features/sidebar-toggle.js +15 -0
- package/dist/features/tag-governance.d.ts +14 -0
- package/dist/features/tag-governance.js +16 -0
- package/dist/features/tauri-dev.d.ts +2 -0
- package/dist/features/tauri-dev.js +25 -0
- package/dist/features/tauri.d.ts +11 -0
- package/dist/features/tauri.js +52 -0
- package/dist/features/versioning.d.ts +27 -0
- package/dist/features/versioning.js +43 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +150 -0
- package/dist/preset.d.ts +37 -0
- package/dist/preset.js +156 -0
- package/dist/prompts.d.ts +32 -0
- package/dist/prompts.js +248 -0
- package/dist/scaffold.d.ts +4 -0
- package/dist/scaffold.js +344 -0
- package/dist/settings-gen.d.ts +2 -0
- package/dist/settings-gen.js +237 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +34 -0
- package/dist/zfb-config-gen.d.ts +19 -0
- package/dist/zfb-config-gen.js +222 -0
- package/package.json +65 -0
- package/templates/base/.htmlvalidate.json +5 -0
- package/templates/base/.zfb/doc-history-meta.json +1 -0
- package/templates/base/pages/404.tsx +55 -0
- package/templates/base/pages/_data.ts +179 -0
- package/templates/base/pages/_mdx-components.ts +249 -0
- package/templates/base/pages/docs/[...slug].tsx +448 -0
- package/templates/base/pages/index.tsx +158 -0
- package/templates/base/pages/lib/_body-end-islands.tsx +201 -0
- package/templates/base/pages/lib/_category-nav.tsx +148 -0
- package/templates/base/pages/lib/_category-tree-nav.tsx +104 -0
- package/templates/base/pages/lib/_compose-meta-title.ts +29 -0
- package/templates/base/pages/lib/_details.tsx +30 -0
- package/templates/base/pages/lib/_doc-history-area.tsx +178 -0
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +100 -0
- package/templates/base/pages/lib/_doc-tags-area.tsx +89 -0
- package/templates/base/pages/lib/_extract-headings.ts +81 -0
- package/templates/base/pages/lib/_footer-with-defaults.tsx +234 -0
- package/templates/base/pages/lib/_frontmatter-preview-data.ts +53 -0
- package/templates/base/pages/lib/_head-with-defaults.tsx +113 -0
- package/templates/base/pages/lib/_header-with-defaults.tsx +386 -0
- package/templates/base/pages/lib/_inline-version-switcher.tsx +84 -0
- package/templates/base/pages/lib/_math-block.tsx +63 -0
- package/templates/base/pages/lib/_nav-source-docs.ts +68 -0
- package/templates/base/pages/lib/_preset-generator.tsx +81 -0
- package/templates/base/pages/lib/_search-widget-script.ts +388 -0
- package/templates/base/pages/lib/_search-widget.tsx +196 -0
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +176 -0
- package/templates/base/pages/lib/_site-tree-nav.tsx +128 -0
- package/templates/base/pages/lib/locale-merge.ts +58 -0
- package/templates/base/pages/lib/route-enumerators.ts +302 -0
- package/templates/base/pages/sitemap.xml.tsx +51 -0
- package/templates/base/plugins/connect-adapter.mjs +144 -0
- package/templates/base/plugins/copy-public-plugin.mjs +50 -0
- package/templates/base/plugins/search-index-plugin.mjs +54 -0
- package/templates/base/scripts/run-b4push.sh +102 -0
- package/templates/base/src/components/ai-chat-modal.tsx +15 -0
- package/templates/base/src/components/client-router-bootstrap.tsx +14 -0
- package/templates/base/src/components/content/component-map.ts +25 -0
- package/templates/base/src/components/content/content-blockquote.tsx +16 -0
- package/templates/base/src/components/content/content-code.tsx +117 -0
- package/templates/base/src/components/content/content-link.tsx +83 -0
- package/templates/base/src/components/content/content-ol.tsx +19 -0
- package/templates/base/src/components/content/content-paragraph.tsx +10 -0
- package/templates/base/src/components/content/content-strong.tsx +16 -0
- package/templates/base/src/components/content/content-table.tsx +18 -0
- package/templates/base/src/components/content/content-ul.tsx +18 -0
- package/templates/base/src/components/content/heading-h2.tsx +26 -0
- package/templates/base/src/components/content/heading-h3.tsx +26 -0
- package/templates/base/src/components/content/heading-h4.tsx +26 -0
- package/templates/base/src/components/design-token-panel-bootstrap.tsx +15 -0
- package/templates/base/src/components/desktop-sidebar-toggle.tsx +15 -0
- package/templates/base/src/components/doc-history.tsx +18 -0
- package/templates/base/src/components/html-preview/highlighted-code.tsx +74 -0
- package/templates/base/src/components/html-preview/html-preview.tsx +108 -0
- package/templates/base/src/components/html-preview/preflight.ts +112 -0
- package/templates/base/src/components/html-preview/preview-base.tsx +159 -0
- package/templates/base/src/components/image-enlarge.tsx +19 -0
- package/templates/base/src/components/mobile-toc.tsx +94 -0
- package/templates/base/src/components/preset-generator.tsx +14 -0
- package/templates/base/src/components/sidebar-toggle.tsx +98 -0
- package/templates/base/src/components/sidebar-tree.tsx +543 -0
- package/templates/base/src/components/site-tree-nav.tsx +233 -0
- package/templates/base/src/components/theme-toggle.tsx +93 -0
- package/templates/base/src/components/toc.tsx +63 -0
- package/templates/base/src/components/tree-nav-shared.tsx +71 -0
- package/templates/base/src/config/color-scheme-utils.ts +182 -0
- package/templates/base/src/config/color-schemes.ts +128 -0
- package/templates/base/src/config/frontmatter-preview-defaults.ts +24 -0
- package/templates/base/src/config/frontmatter-preview-renderers.tsx +46 -0
- package/templates/base/src/config/i18n.ts +225 -0
- package/templates/base/src/config/settings-types.ts +162 -0
- package/templates/base/src/config/sidebars.ts +66 -0
- package/templates/base/src/config/tag-vocabulary-types.ts +39 -0
- package/templates/base/src/config/tag-vocabulary.ts +20 -0
- package/templates/base/src/hooks/use-active-heading.ts +133 -0
- package/templates/base/src/plugins/docs-source-map.ts +103 -0
- package/templates/base/src/plugins/hast-utils.ts +10 -0
- package/templates/base/src/plugins/rehype-code-title.ts +50 -0
- package/templates/base/src/plugins/rehype-heading-links.ts +53 -0
- package/templates/base/src/plugins/rehype-image-enlarge.ts +113 -0
- package/templates/base/src/plugins/rehype-mermaid.ts +41 -0
- package/templates/base/src/plugins/rehype-strip-md-extension.ts +58 -0
- package/templates/base/src/plugins/remark-admonitions.ts +99 -0
- package/templates/base/src/plugins/remark-resolve-markdown-links.ts +127 -0
- package/templates/base/src/plugins/url-utils.ts +4 -0
- package/templates/base/src/styles/global.css +1066 -0
- package/templates/base/src/types/docs-entry.ts +39 -0
- package/templates/base/src/types/heading.ts +5 -0
- package/templates/base/src/types/locale.ts +10 -0
- package/templates/base/src/utils/base.ts +139 -0
- package/templates/base/src/utils/content-files.ts +106 -0
- package/templates/base/src/utils/dedent.ts +24 -0
- package/templates/base/src/utils/docs.ts +335 -0
- package/templates/base/src/utils/git-info.ts +70 -0
- package/templates/base/src/utils/github.ts +19 -0
- package/templates/base/src/utils/header-right-items.ts +38 -0
- package/templates/base/src/utils/nav-scope.ts +63 -0
- package/templates/base/src/utils/sidebar.ts +104 -0
- package/templates/base/src/utils/slug.ts +10 -0
- package/templates/base/src/utils/smart-break.tsx +126 -0
- package/templates/base/src/utils/tags.ts +126 -0
- package/templates/base/tsconfig.json +36 -0
- package/templates/features/bodyFootUtil/files/src/utils/github.ts +19 -0
- package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +137 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/escape-for-mdx.test.ts +34 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +376 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/escape-for-mdx.ts +93 -0
- package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +586 -0
- package/templates/features/designTokenPanel/files/src/components/design-token-panel-bootstrap.tsx +15 -0
- package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +99 -0
- package/templates/features/designTokenPanel/files/src/config/design-tokens-manifest.ts +177 -0
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +50 -0
- package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +99 -0
- package/templates/features/docHistory/files/src/components/doc-history.tsx +598 -0
- package/templates/features/docHistory/files/src/types/doc-history.ts +23 -0
- package/templates/features/docHistory/files/src/utils/doc-history.ts +180 -0
- package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +116 -0
- package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +99 -0
- package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +101 -0
- package/templates/features/docTags/files/pages/docs/tags/index.tsx +86 -0
- package/templates/features/i18n/files/pages/[locale]/docs/[...slug].tsx +467 -0
- package/templates/features/i18n/files/pages/[locale]/index.tsx +213 -0
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +248 -0
- package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +74 -0
- package/templates/features/sidebarResizer/files/src/scripts/sidebar-resizer.ts +185 -0
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +126 -0
- package/templates/features/tagGovernance/files/scripts/tags-audit.ts +576 -0
- package/templates/features/tagGovernance/files/scripts/tags-suggest.ts +428 -0
- package/templates/features/tauri/files/src/components/find-bar.tsx +122 -0
- package/templates/features/tauri/files/src/components/find-in-page-init.tsx +53 -0
- package/templates/features/tauri/files/src/utils/find-in-page.ts +175 -0
- package/templates/features/tauri/files/src-tauri/Cargo.toml +14 -0
- package/templates/features/tauri/files/src-tauri/build.rs +3 -0
- package/templates/features/tauri/files/src-tauri/capabilities/default.json +11 -0
- package/templates/features/tauri/files/src-tauri/src/main.rs +250 -0
- package/templates/features/tauri/files/src-tauri/tauri.conf.json +25 -0
- package/templates/features/tauriDev/files/src-tauri-dev/Cargo.toml +15 -0
- package/templates/features/tauriDev/files/src-tauri-dev/build.rs +3 -0
- package/templates/features/tauriDev/files/src-tauri-dev/capabilities/default.json +7 -0
- package/templates/features/tauriDev/files/src-tauri-dev/frontend/index.html +187 -0
- package/templates/features/tauriDev/files/src-tauri-dev/icons/icon.png +0 -0
- package/templates/features/tauriDev/files/src-tauri-dev/src/main.rs +995 -0
- package/templates/features/tauriDev/files/src-tauri-dev/tauri.conf.json +22 -0
- package/templates/features/tauriDev/files/src-tauri-dev/test-launch.sh +65 -0
- package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +100 -0
- package/templates/features/versioning/files/pages/docs/versions.tsx +78 -0
- package/templates/features/versioning/files/pages/v/[version]/docs/[...slug].tsx +451 -0
- package/templates/features/versioning/files/pages/v/[version]/ja/docs/[...slug].tsx +490 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
// Port of src/pages/[locale]/docs/[...slug].astro → zfb page module.
|
|
4
|
+
//
|
|
5
|
+
// Non-default-locale catch-all docs route. paths() emits one route per
|
|
6
|
+
// (locale, slug) combination — one locale from settings.locales per each
|
|
7
|
+
// doc in that locale's merged collection (locale-first + base fallback).
|
|
8
|
+
//
|
|
9
|
+
// paths() contract (zfb ADR-004 — synchronous):
|
|
10
|
+
// params: { locale: string; slug: string[] }
|
|
11
|
+
// props: { entry, autoIndex, contentDir, isFallback, breadcrumbs, prev, next }
|
|
12
|
+
//
|
|
13
|
+
// i18n / locale routing:
|
|
14
|
+
// - Default locale (EN) is handled by pages/docs/[...slug].tsx
|
|
15
|
+
// (prefixDefaultLocale: false).
|
|
16
|
+
// - Non-default locales emit /{locale}/docs/{slug}.
|
|
17
|
+
// - Locale-first merge: locale docs take priority; base EN docs fill in
|
|
18
|
+
// pages not translated yet (shown with a fallback notice).
|
|
19
|
+
|
|
20
|
+
import { getCollection } from "zfb/content";
|
|
21
|
+
import type { CollectionEntry } from "zfb/content";
|
|
22
|
+
import type { DocsEntry } from "@/types/docs-entry";
|
|
23
|
+
import { settings } from "@/config/settings";
|
|
24
|
+
import { t, getContentDir } from "@/config/i18n";
|
|
25
|
+
import { docsUrl, isDefaultLocaleOnlyPath } from "@/utils/base";
|
|
26
|
+
import {
|
|
27
|
+
buildNavTree,
|
|
28
|
+
buildBreadcrumbs,
|
|
29
|
+
flattenTree,
|
|
30
|
+
findNode,
|
|
31
|
+
loadCategoryMeta,
|
|
32
|
+
collectAutoIndexNodes,
|
|
33
|
+
isNavVisible,
|
|
34
|
+
type NavNode,
|
|
35
|
+
type BreadcrumbItem,
|
|
36
|
+
} from "@/utils/docs";
|
|
37
|
+
import { getNavSectionForSlug, getNavSubtree } from "@/utils/nav-scope";
|
|
38
|
+
import { toRouteSlug } 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
|
+
import { FrontmatterPreview } from "@takazudo/zudo-doc/metainfo";
|
|
43
|
+
import { frontmatterRenderers } from "@/config/frontmatter-preview-renderers";
|
|
44
|
+
// Shared MDX components bag — see `pages/_mdx-components.ts`.
|
|
45
|
+
import { createMdxComponents } from "../../_mdx-components";
|
|
46
|
+
import type { JSX } from "preact";
|
|
47
|
+
import { bridgeEntries } from "../../_data";
|
|
48
|
+
import { extractHeadings } from "../../lib/_extract-headings";
|
|
49
|
+
import { FooterWithDefaults } from "../../lib/_footer-with-defaults";
|
|
50
|
+
import { DocHistoryArea } from "../../lib/_doc-history-area";
|
|
51
|
+
import { BodyEndIslands } from "../../lib/_body-end-islands";
|
|
52
|
+
import { DocMetainfoArea } from "../../lib/_doc-metainfo-area";
|
|
53
|
+
import { DocTagsArea } from "../../lib/_doc-tags-area";
|
|
54
|
+
import { SidebarWithDefaults } from "../../lib/_sidebar-with-defaults";
|
|
55
|
+
import { HeaderWithDefaults } from "../../lib/_header-with-defaults";
|
|
56
|
+
import { HeadWithDefaults } from "../../lib/_head-with-defaults";
|
|
57
|
+
import { buildFrontmatterPreviewEntries } from "../../lib/_frontmatter-preview-data";
|
|
58
|
+
import { composeMetaTitle } from "../../lib/_compose-meta-title";
|
|
59
|
+
import { buildInlineVersionSwitcher } from "../../lib/_inline-version-switcher";
|
|
60
|
+
import DesktopSidebarToggle from "@/components/desktop-sidebar-toggle";
|
|
61
|
+
import { SidebarResizerInit } from "@takazudo/zudo-doc/sidebar-resizer";
|
|
62
|
+
import type { VNode } from "preact";
|
|
63
|
+
import { Island } from "@takazudo/zfb";
|
|
64
|
+
|
|
65
|
+
export const frontmatter = { title: "Docs" };
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Types
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
interface DocPageEntry extends DocsEntry {
|
|
72
|
+
Content: CollectionEntry<unknown>["Content"];
|
|
73
|
+
module_specifier: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface AutoIndexNode extends NavNode {
|
|
77
|
+
children: NavNode[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface DocPageProps {
|
|
81
|
+
entry: DocPageEntry | null;
|
|
82
|
+
autoIndex?: AutoIndexNode;
|
|
83
|
+
/** Content directory for the active locale (or base EN for fallbacks). */
|
|
84
|
+
contentDir: string;
|
|
85
|
+
/** True when this page falls back to the base EN collection. */
|
|
86
|
+
isFallback: boolean;
|
|
87
|
+
breadcrumbs: BreadcrumbItem[];
|
|
88
|
+
prev: NavNode | null;
|
|
89
|
+
next: NavNode | null;
|
|
90
|
+
/** Depth-2/3/4 headings extracted from the MDX body, for SSG TOC links. */
|
|
91
|
+
headings: ReturnType<typeof extractHeadings>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// paths() — synchronous (ADR-004)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Emit one route per (non-default locale, slug) combination.
|
|
100
|
+
*
|
|
101
|
+
* Merge strategy:
|
|
102
|
+
* 1. Load locale docs (e.g. "docs-ja").
|
|
103
|
+
* 2. Load base EN docs ("docs").
|
|
104
|
+
* 3. Locale docs take priority; base EN fills in slugs not translated.
|
|
105
|
+
* 4. Track fallback slugs for the fallback-notice banner.
|
|
106
|
+
* 5. Build nav tree, compute breadcrumbs and prev/next for each entry.
|
|
107
|
+
*
|
|
108
|
+
* Fallback slug set drives `isFallback` which the component uses to show
|
|
109
|
+
* the "not yet translated" notice (matching the Astro original).
|
|
110
|
+
*/
|
|
111
|
+
export function paths(): Array<{
|
|
112
|
+
params: { locale: string; slug: string[] };
|
|
113
|
+
props: DocPageProps;
|
|
114
|
+
}> {
|
|
115
|
+
const result: Array<{
|
|
116
|
+
params: { locale: string; slug: string[] };
|
|
117
|
+
props: DocPageProps;
|
|
118
|
+
}> = [];
|
|
119
|
+
|
|
120
|
+
for (const locale of Object.keys(settings.locales) as string[]) {
|
|
121
|
+
const localeConfig = (settings.locales as Record<string, { dir: string }>)[locale];
|
|
122
|
+
const contentDir = localeConfig?.dir ?? settings.docsDir;
|
|
123
|
+
|
|
124
|
+
// Load locale + base docs, filter drafts
|
|
125
|
+
const localeDocs = ((bridgeEntries(getCollection(`docs-${locale}`), `docs-${locale}`) as unknown as DocPageEntry[])).filter(
|
|
126
|
+
(d) => !d.data.draft,
|
|
127
|
+
);
|
|
128
|
+
const baseDocs = ((bridgeEntries(getCollection("docs"), "docs") as unknown as DocPageEntry[])).filter(
|
|
129
|
+
(d) => !d.data.draft,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const localeSlugSet = new Set(localeDocs.map((d) => d.data.slug ?? toRouteSlug(d.slug)));
|
|
133
|
+
const fallbackDocs = baseDocs.filter(
|
|
134
|
+
(d) => !localeSlugSet.has(d.data.slug ?? toRouteSlug(d.slug)) && !isDefaultLocaleOnlyPath(`/docs/${d.data.slug ?? toRouteSlug(d.slug)}`),
|
|
135
|
+
);
|
|
136
|
+
const fallbackSlugs = new Set(fallbackDocs.map((d) => d.data.slug ?? toRouteSlug(d.slug)));
|
|
137
|
+
const allDocs = [...localeDocs, ...fallbackDocs];
|
|
138
|
+
|
|
139
|
+
// Merge category metadata: base first, locale overrides
|
|
140
|
+
const baseCategoryMeta = loadCategoryMeta(settings.docsDir);
|
|
141
|
+
const localeCategoryMeta = loadCategoryMeta(contentDir);
|
|
142
|
+
const categoryMeta = new Map([...baseCategoryMeta, ...localeCategoryMeta]);
|
|
143
|
+
|
|
144
|
+
const navDocs = allDocs.filter(isNavVisible);
|
|
145
|
+
const tree = buildNavTree(navDocs as unknown as DocsEntry[], locale, categoryMeta);
|
|
146
|
+
const fullTree = buildNavTree(allDocs as unknown as DocsEntry[], locale, categoryMeta);
|
|
147
|
+
|
|
148
|
+
// Regular doc pages
|
|
149
|
+
for (const entry of allDocs) {
|
|
150
|
+
const slug = entry.data.slug ?? toRouteSlug(entry.slug);
|
|
151
|
+
const isFallback = fallbackSlugs.has(slug);
|
|
152
|
+
const entryContentDir = isFallback ? settings.docsDir : contentDir;
|
|
153
|
+
|
|
154
|
+
const navSection = getNavSectionForSlug(slug);
|
|
155
|
+
const subtree = getNavSubtree(tree, navSection);
|
|
156
|
+
const flat = flattenTree(subtree);
|
|
157
|
+
const idx = flat.findIndex((n) => n.slug === slug);
|
|
158
|
+
|
|
159
|
+
let prevNode = idx > 0 ? flat[idx - 1] ?? null : null;
|
|
160
|
+
let nextNode = idx >= 0 && idx < flat.length - 1 ? flat[idx + 1] ?? null : null;
|
|
161
|
+
|
|
162
|
+
if (entry.data.pagination_prev !== undefined) {
|
|
163
|
+
if (entry.data.pagination_prev === null) {
|
|
164
|
+
prevNode = null;
|
|
165
|
+
} else {
|
|
166
|
+
const found = findNode(tree, entry.data.pagination_prev);
|
|
167
|
+
prevNode = found ?? prevNode;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (entry.data.pagination_next !== undefined) {
|
|
171
|
+
if (entry.data.pagination_next === null) {
|
|
172
|
+
nextNode = null;
|
|
173
|
+
} else {
|
|
174
|
+
const found = findNode(tree, entry.data.pagination_next);
|
|
175
|
+
nextNode = found ?? nextNode;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
result.push({
|
|
180
|
+
params: { locale, slug: slug.split("/") },
|
|
181
|
+
props: {
|
|
182
|
+
entry,
|
|
183
|
+
contentDir: entryContentDir,
|
|
184
|
+
isFallback,
|
|
185
|
+
breadcrumbs: buildBreadcrumbs(fullTree, slug, locale),
|
|
186
|
+
prev: prevNode,
|
|
187
|
+
next: nextNode,
|
|
188
|
+
headings: extractHeadings(entry.body ?? ""),
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Auto-generated index pages for categories without index.mdx
|
|
194
|
+
for (const node of collectAutoIndexNodes(tree)) {
|
|
195
|
+
result.push({
|
|
196
|
+
params: { locale, slug: node.slug.split("/") },
|
|
197
|
+
props: {
|
|
198
|
+
entry: null,
|
|
199
|
+
autoIndex: node as AutoIndexNode,
|
|
200
|
+
contentDir,
|
|
201
|
+
isFallback: false,
|
|
202
|
+
breadcrumbs: buildBreadcrumbs(fullTree, node.slug, locale),
|
|
203
|
+
prev: null,
|
|
204
|
+
next: null,
|
|
205
|
+
headings: [],
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Page component
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
interface PageArgs {
|
|
219
|
+
params: { locale: string; slug: string[] };
|
|
220
|
+
entry: DocPageProps["entry"];
|
|
221
|
+
autoIndex?: DocPageProps["autoIndex"];
|
|
222
|
+
contentDir: DocPageProps["contentDir"];
|
|
223
|
+
isFallback: DocPageProps["isFallback"];
|
|
224
|
+
breadcrumbs: DocPageProps["breadcrumbs"];
|
|
225
|
+
prev: DocPageProps["prev"];
|
|
226
|
+
next: DocPageProps["next"];
|
|
227
|
+
headings: DocPageProps["headings"];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export default function LocaleDocsPage({ params, entry, autoIndex, contentDir, isFallback, breadcrumbs, prev, next, headings }: PageArgs): JSX.Element {
|
|
231
|
+
const locale = params.locale;
|
|
232
|
+
|
|
233
|
+
const slug = autoIndex
|
|
234
|
+
? autoIndex.slug
|
|
235
|
+
: (entry!.data.slug ?? toRouteSlug(entry!.slug));
|
|
236
|
+
|
|
237
|
+
const title = autoIndex ? autoIndex.label : entry!.data.title;
|
|
238
|
+
const description = autoIndex ? autoIndex.description : entry!.data.description;
|
|
239
|
+
|
|
240
|
+
// Locale-aware components bag — creates nav wrappers bound to the active
|
|
241
|
+
// locale so CategoryNav/CategoryTreeNav/SiteTreeNav query the right collection.
|
|
242
|
+
const components = createMdxComponents(locale);
|
|
243
|
+
|
|
244
|
+
const autoIndexChildren = autoIndex
|
|
245
|
+
? autoIndex.children
|
|
246
|
+
.filter((c: NavNode) => c.hasPage || c.children.length > 0)
|
|
247
|
+
.map((c: NavNode) => ({
|
|
248
|
+
...c,
|
|
249
|
+
href: c.href ?? docsUrl(c.slug, locale),
|
|
250
|
+
}))
|
|
251
|
+
: [];
|
|
252
|
+
|
|
253
|
+
// Canonical URL — only when siteUrl is configured.
|
|
254
|
+
const pageUrl = docsUrl(slug, locale);
|
|
255
|
+
const canonical = settings.siteUrl
|
|
256
|
+
? settings.siteUrl.replace(/\/$/, "") + pageUrl
|
|
257
|
+
: undefined;
|
|
258
|
+
|
|
259
|
+
// Persist key: locale + nav-section so the sidebar DOM node is reused
|
|
260
|
+
// across same-locale + same-section navigations only. No sanitizer needed —
|
|
261
|
+
// both lang (BCP-47 locale string) and navSection (filesystem-derived
|
|
262
|
+
// kebab-case slug) come from controlled, trusted sources.
|
|
263
|
+
const navSection = getNavSectionForSlug(slug);
|
|
264
|
+
const hideSidebar = entry?.data?.hide_sidebar;
|
|
265
|
+
const sidebarPersistKey = hideSidebar
|
|
266
|
+
? undefined
|
|
267
|
+
: `sidebar-${locale}-${navSection ?? "default"}`;
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<DocLayoutWithDefaults
|
|
271
|
+
title={composeMetaTitle(title)}
|
|
272
|
+
description={description}
|
|
273
|
+
head={<HeadWithDefaults title={title} description={description} canonical={canonical} />}
|
|
274
|
+
lang={locale}
|
|
275
|
+
noindex={settings.noindex}
|
|
276
|
+
hideSidebar={hideSidebar}
|
|
277
|
+
hideToc={entry?.data?.hide_toc}
|
|
278
|
+
headings={headings}
|
|
279
|
+
canonical={canonical}
|
|
280
|
+
sidebarPersistKey={sidebarPersistKey}
|
|
281
|
+
headerOverride={
|
|
282
|
+
<HeaderWithDefaults
|
|
283
|
+
lang={locale}
|
|
284
|
+
currentSlug={slug}
|
|
285
|
+
navSection={getNavSectionForSlug(slug)}
|
|
286
|
+
currentPath={docsUrl(slug, locale)}
|
|
287
|
+
/>
|
|
288
|
+
}
|
|
289
|
+
breadcrumbOverride={
|
|
290
|
+
breadcrumbs.length > 0 ? (
|
|
291
|
+
<Breadcrumb
|
|
292
|
+
items={breadcrumbs}
|
|
293
|
+
rightSlot={buildInlineVersionSwitcher(slug, locale)}
|
|
294
|
+
/>
|
|
295
|
+
) : undefined
|
|
296
|
+
}
|
|
297
|
+
sidebarOverride={
|
|
298
|
+
<SidebarWithDefaults
|
|
299
|
+
currentSlug={slug}
|
|
300
|
+
lang={locale}
|
|
301
|
+
navSection={getNavSectionForSlug(slug)}
|
|
302
|
+
currentPath={docsUrl(slug, locale)}
|
|
303
|
+
/>
|
|
304
|
+
}
|
|
305
|
+
afterSidebar={
|
|
306
|
+
settings.sidebarToggle ? (
|
|
307
|
+
<>
|
|
308
|
+
<script dangerouslySetInnerHTML={{
|
|
309
|
+
__html: `(function(){try{if(localStorage.getItem('zudo-doc-sidebar-visible')==='false'){document.documentElement.setAttribute('data-sidebar-hidden','');}}catch(e){}})();`,
|
|
310
|
+
}} />
|
|
311
|
+
{Island({
|
|
312
|
+
when: "load",
|
|
313
|
+
children: <DesktopSidebarToggle />,
|
|
314
|
+
}) as unknown as VNode}
|
|
315
|
+
</>
|
|
316
|
+
) : undefined
|
|
317
|
+
}
|
|
318
|
+
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
319
|
+
bodyEndComponents={
|
|
320
|
+
<>
|
|
321
|
+
<BodyEndIslands basePath={settings.base ?? "/"} />
|
|
322
|
+
{settings.sidebarResizer && <SidebarResizerInit />}
|
|
323
|
+
</>
|
|
324
|
+
}
|
|
325
|
+
>
|
|
326
|
+
{autoIndex ? (
|
|
327
|
+
/* Auto-index page: category without an index.mdx.
|
|
328
|
+
Fragment (not <div>) so children become direct children of
|
|
329
|
+
<article class="zd-content">, picking up the flow-space rule
|
|
330
|
+
(.zd-content > :where(* + *) { margin-top: var(--flow-space) }).
|
|
331
|
+
Wrapping in <div> would make h1/description p children-of-children
|
|
332
|
+
and the flow gap (~24px) would never apply — see #1460. */
|
|
333
|
+
<>
|
|
334
|
+
<h1 class="text-heading font-bold mb-vsp-xs">{autoIndex.label}</h1>
|
|
335
|
+
|
|
336
|
+
{/* Build-time date block — chrome parity (#1461). Auto-index pages
|
|
337
|
+
previously rendered without doc-meta; reference site shows it on
|
|
338
|
+
every docs page. The component returns null when no manifest
|
|
339
|
+
entry exists for this slug. */}
|
|
340
|
+
<DocMetainfoArea slug={slug} locale={locale} />
|
|
341
|
+
|
|
342
|
+
{autoIndex.description && (
|
|
343
|
+
<p class="mb-vsp-lg text-title text-muted">
|
|
344
|
+
{autoIndex.description}
|
|
345
|
+
</p>
|
|
346
|
+
)}
|
|
347
|
+
<NavCardGrid children={autoIndexChildren} />
|
|
348
|
+
</>
|
|
349
|
+
) : (
|
|
350
|
+
/* Regular doc page. Fragment (not <div>) for the same reason as
|
|
351
|
+
the auto-index branch above — see #1460. */
|
|
352
|
+
<>
|
|
353
|
+
<h1 class="text-heading font-bold mb-vsp-xs">{entry!.data.title}</h1>
|
|
354
|
+
|
|
355
|
+
{/* Build-time date block (Created / Updated / Author). Mirrors the
|
|
356
|
+
Astro `doc-metainfo.astro` placement — between <h1> and description.
|
|
357
|
+
Data from `.zfb/doc-history-meta.json` (esbuild-inlined, no fs). */}
|
|
358
|
+
<DocMetainfoArea slug={slug} locale={locale} />
|
|
359
|
+
|
|
360
|
+
{/* Page-level tag chips — mirroring doc-tags.astro placement (#1658). */}
|
|
361
|
+
<DocTagsArea slug={slug} locale={locale} tags={entry!.data.tags} />
|
|
362
|
+
|
|
363
|
+
{/* Fallback notice for non-translated pages */}
|
|
364
|
+
{isFallback && !entry!.data.generated && (
|
|
365
|
+
<div
|
|
366
|
+
class="mb-vsp-md border border-info/30 bg-info/5 px-hsp-lg py-vsp-sm text-small text-muted rounded"
|
|
367
|
+
role="note"
|
|
368
|
+
>
|
|
369
|
+
{t("doc.fallbackNotice", locale)}
|
|
370
|
+
</div>
|
|
371
|
+
)}
|
|
372
|
+
|
|
373
|
+
{entry!.data.description && (
|
|
374
|
+
<p class="mb-vsp-lg text-title text-muted">
|
|
375
|
+
{entry!.data.description}
|
|
376
|
+
</p>
|
|
377
|
+
)}
|
|
378
|
+
|
|
379
|
+
{/* Frontmatter preview — non-system, custom keys only. Returns
|
|
380
|
+
null when the entries array is empty, so pages without
|
|
381
|
+
custom frontmatter emit nothing. Custom per-key renderers
|
|
382
|
+
from frontmatter-preview-renderers.tsx produce styled cells
|
|
383
|
+
(pills, badges, etc.) instead of plain text. */}
|
|
384
|
+
<FrontmatterPreview
|
|
385
|
+
entries={buildFrontmatterPreviewEntries(entry!.data)}
|
|
386
|
+
title={t("frontmatter.preview.title", locale)}
|
|
387
|
+
keyColLabel={t("frontmatter.preview.keyCol", locale)}
|
|
388
|
+
valueColLabel={t("frontmatter.preview.valueCol", locale)}
|
|
389
|
+
renderers={frontmatterRenderers}
|
|
390
|
+
data={entry!.data as Record<string, unknown>}
|
|
391
|
+
locale={locale}
|
|
392
|
+
/>
|
|
393
|
+
|
|
394
|
+
{entry && <entry.Content components={components} />}
|
|
395
|
+
|
|
396
|
+
{/* Prev / Next pagination — placed before the document utilities
|
|
397
|
+
section to match the Astro reference order: content → pager →
|
|
398
|
+
view-source / history. In the Astro layout, BodyFootUtilArea was
|
|
399
|
+
rendered by the doc-layout wrapper after the <slot /> content,
|
|
400
|
+
so the pager (inside the slot) came first. Fixes #1535. */}
|
|
401
|
+
<nav class="mt-vsp-2xl grid grid-cols-2 gap-hsp-xl">
|
|
402
|
+
{prev ? (
|
|
403
|
+
<a
|
|
404
|
+
href={prev.href}
|
|
405
|
+
class="group border border-muted rounded-lg p-hsp-lg hover:border-accent"
|
|
406
|
+
>
|
|
407
|
+
<div class="flex items-center gap-hsp-xs text-caption text-muted mb-vsp-2xs">
|
|
408
|
+
<svg
|
|
409
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
410
|
+
class="h-[1.125rem] w-[1.125rem]"
|
|
411
|
+
fill="none"
|
|
412
|
+
viewBox="0 0 24 24"
|
|
413
|
+
stroke="currentColor"
|
|
414
|
+
stroke-width="2"
|
|
415
|
+
>
|
|
416
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
|
|
417
|
+
</svg>
|
|
418
|
+
<span class="no-underline">{t("nav.previous", locale)}</span>
|
|
419
|
+
</div>
|
|
420
|
+
<p class="text-small font-semibold underline group-hover:text-accent">
|
|
421
|
+
{prev.label}
|
|
422
|
+
</p>
|
|
423
|
+
</a>
|
|
424
|
+
) : (
|
|
425
|
+
<div />
|
|
426
|
+
)}
|
|
427
|
+
{next ? (
|
|
428
|
+
<a
|
|
429
|
+
href={next.href}
|
|
430
|
+
class="group border border-muted rounded-lg p-hsp-lg hover:border-accent text-right"
|
|
431
|
+
>
|
|
432
|
+
<div class="flex items-center justify-end gap-hsp-xs text-caption text-muted mb-vsp-2xs">
|
|
433
|
+
<span class="no-underline">{t("nav.next", locale)}</span>
|
|
434
|
+
<svg
|
|
435
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
436
|
+
class="h-[1.125rem] w-[1.125rem]"
|
|
437
|
+
fill="none"
|
|
438
|
+
viewBox="0 0 24 24"
|
|
439
|
+
stroke="currentColor"
|
|
440
|
+
stroke-width="2"
|
|
441
|
+
>
|
|
442
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
|
|
443
|
+
</svg>
|
|
444
|
+
</div>
|
|
445
|
+
<p class="text-small font-semibold underline group-hover:text-accent">
|
|
446
|
+
{next.label}
|
|
447
|
+
</p>
|
|
448
|
+
</a>
|
|
449
|
+
) : (
|
|
450
|
+
<div />
|
|
451
|
+
)}
|
|
452
|
+
</nav>
|
|
453
|
+
|
|
454
|
+
{/* Document utilities (revision history + view-source link) — skipped for unlisted pages */}
|
|
455
|
+
{!entry!.data.unlisted && (
|
|
456
|
+
<DocHistoryArea
|
|
457
|
+
slug={slug}
|
|
458
|
+
locale={locale}
|
|
459
|
+
entrySlug={entry!.slug}
|
|
460
|
+
contentDir={contentDir}
|
|
461
|
+
/>
|
|
462
|
+
)}
|
|
463
|
+
</>
|
|
464
|
+
)}
|
|
465
|
+
</DocLayoutWithDefaults>
|
|
466
|
+
);
|
|
467
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
/** @jsxImportSource preact */
|
|
3
|
+
// Port of src/pages/[locale]/index.astro → zfb page module.
|
|
4
|
+
//
|
|
5
|
+
// Non-default-locale site index. paths() emits one route per locale defined
|
|
6
|
+
// in settings.locales (never the default locale — that is handled by
|
|
7
|
+
// pages/index.tsx since prefixDefaultLocale is false).
|
|
8
|
+
//
|
|
9
|
+
// paths() contract (zfb ADR-004 — synchronous):
|
|
10
|
+
// params: { locale: string } — e.g. "ja"
|
|
11
|
+
// props: { locale } — resolved locale passed to component
|
|
12
|
+
//
|
|
13
|
+
// Data flow (inside component — sync per ADR-004):
|
|
14
|
+
// getCollection(`docs-${locale}`) + base fallback merge
|
|
15
|
+
// → buildNavTree() → groupSatelliteNodes()
|
|
16
|
+
// → collectTags() → tag section
|
|
17
|
+
|
|
18
|
+
import { getCollection } from "zfb/content";
|
|
19
|
+
import type { DocsEntry } from "@/types/docs-entry";
|
|
20
|
+
import { settings } from "@/config/settings";
|
|
21
|
+
import { t } from "@/config/i18n";
|
|
22
|
+
import { withBase, isDefaultLocaleOnlyPath } from "@/utils/base";
|
|
23
|
+
import {
|
|
24
|
+
buildNavTree,
|
|
25
|
+
groupSatelliteNodes,
|
|
26
|
+
isNavVisible,
|
|
27
|
+
loadCategoryMeta,
|
|
28
|
+
} from "@/utils/docs";
|
|
29
|
+
import { getCategoryOrder } from "@/utils/nav-scope";
|
|
30
|
+
import { collectTags } from "@/utils/tags";
|
|
31
|
+
import { toRouteSlug } from "@/utils/slug";
|
|
32
|
+
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
33
|
+
import type { JSX } from "preact";
|
|
34
|
+
import type { VNode } from "preact";
|
|
35
|
+
import { Island } from "@takazudo/zfb";
|
|
36
|
+
import SiteTreeNav from "@/components/site-tree-nav";
|
|
37
|
+
import { bridgeEntries } from "../_data";
|
|
38
|
+
import { FooterWithDefaults } from "../lib/_footer-with-defaults";
|
|
39
|
+
import { HeaderWithDefaults } from "../lib/_header-with-defaults";
|
|
40
|
+
import { HeadWithDefaults } from "../lib/_head-with-defaults";
|
|
41
|
+
import { composeMetaTitle } from "../lib/_compose-meta-title";
|
|
42
|
+
import { BodyEndIslands } from "../lib/_body-end-islands";
|
|
43
|
+
|
|
44
|
+
export const frontmatter = { title: "Home" };
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// paths() — synchronous (ADR-004)
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
/** Emit one route per non-default locale. */
|
|
51
|
+
export function paths(): Array<{
|
|
52
|
+
params: { locale: string };
|
|
53
|
+
props: { locale: string };
|
|
54
|
+
}> {
|
|
55
|
+
return Object.keys(settings.locales).map((locale) => ({
|
|
56
|
+
params: { locale },
|
|
57
|
+
props: { locale },
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// helpers
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Merge locale docs with base (EN) fallbacks.
|
|
67
|
+
* Mirrors the merge strategy in src/utils/locale-docs.ts.
|
|
68
|
+
*/
|
|
69
|
+
function mergeLocaleDocs(locale: string): DocsEntry[] {
|
|
70
|
+
const localeDocs = ((bridgeEntries(getCollection(`docs-${locale}`), `docs-${locale}`) as unknown as DocsEntry[])).filter(
|
|
71
|
+
(d) => !d.data.draft,
|
|
72
|
+
);
|
|
73
|
+
const baseDocs = ((bridgeEntries(getCollection("docs"), "docs") as unknown as DocsEntry[])).filter(
|
|
74
|
+
(d) => !d.data.draft,
|
|
75
|
+
);
|
|
76
|
+
const localeSlugSet = new Set(localeDocs.map((d) => d.data.slug ?? toRouteSlug(d.slug)));
|
|
77
|
+
const fallbackDocs = baseDocs.filter((d) => !localeSlugSet.has(d.data.slug ?? toRouteSlug(d.slug)));
|
|
78
|
+
const filteredFallback = fallbackDocs.filter((d) => {
|
|
79
|
+
const slug = d.data.slug ?? toRouteSlug(d.slug);
|
|
80
|
+
return !isDefaultLocaleOnlyPath(`/docs/${slug}/`);
|
|
81
|
+
});
|
|
82
|
+
return [...localeDocs, ...filteredFallback];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Page component
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
interface PageArgs {
|
|
90
|
+
params: { locale: string };
|
|
91
|
+
props: { locale: string };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default function LocaleIndexPage({ params }: PageArgs): JSX.Element {
|
|
95
|
+
const locale = params.locale;
|
|
96
|
+
|
|
97
|
+
const allDocs = mergeLocaleDocs(locale);
|
|
98
|
+
const localeConfig = (settings.locales as Record<string, { dir: string }>)[locale];
|
|
99
|
+
const categoryMeta = localeConfig
|
|
100
|
+
? loadCategoryMeta(localeConfig.dir)
|
|
101
|
+
: loadCategoryMeta(settings.docsDir);
|
|
102
|
+
|
|
103
|
+
const navDocs = allDocs.filter(isNavVisible);
|
|
104
|
+
const tree = buildNavTree(navDocs, locale, categoryMeta);
|
|
105
|
+
const categoryOrder = getCategoryOrder();
|
|
106
|
+
const groupedTree = groupSatelliteNodes(tree, categoryOrder);
|
|
107
|
+
|
|
108
|
+
const tagCount = collectTags(
|
|
109
|
+
navDocs,
|
|
110
|
+
(id, data) => data.slug ?? toRouteSlug(id),
|
|
111
|
+
).size;
|
|
112
|
+
|
|
113
|
+
const ctaNav = settings.headerNav[0] ?? null;
|
|
114
|
+
const overview = ctaNav ? withBase(`/${locale}${ctaNav.path}`) : null;
|
|
115
|
+
const logoUrl = withBase("/img/logo.svg");
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<DocLayoutWithDefaults
|
|
119
|
+
title={composeMetaTitle(settings.siteName)}
|
|
120
|
+
head={<HeadWithDefaults title={settings.siteName} />}
|
|
121
|
+
lang={locale}
|
|
122
|
+
noindex={settings.noindex}
|
|
123
|
+
hideSidebar={true}
|
|
124
|
+
hideToc={true}
|
|
125
|
+
headerOverride={<HeaderWithDefaults lang={locale} currentPath={withBase(`/${locale}/`)} />}
|
|
126
|
+
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
127
|
+
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
128
|
+
>
|
|
129
|
+
{/* Hero: logo left, title+desc+links right, block centered */}
|
|
130
|
+
<div class="flex justify-center mb-vsp-xl">
|
|
131
|
+
<div class="flex flex-col items-center text-center gap-hsp-md lg:flex-row lg:text-left lg:gap-hsp-xl">
|
|
132
|
+
<img
|
|
133
|
+
src={logoUrl}
|
|
134
|
+
alt={settings.siteName}
|
|
135
|
+
class="w-[320px] max-w-full aspect-[1200/630] shrink-0"
|
|
136
|
+
/>
|
|
137
|
+
<div>
|
|
138
|
+
<h1 class="text-heading font-bold mb-vsp-2xs">{settings.siteName}</h1>
|
|
139
|
+
<p class="text-muted text-small mb-vsp-sm">{settings.siteDescription}</p>
|
|
140
|
+
<div class="flex items-center justify-center lg:justify-start gap-hsp-md text-small">
|
|
141
|
+
{overview && (
|
|
142
|
+
<>
|
|
143
|
+
<a href={overview} class="text-fg underline hover:text-accent">
|
|
144
|
+
{t("nav.overview", locale)}
|
|
145
|
+
</a>
|
|
146
|
+
<span class="text-muted">/</span>
|
|
147
|
+
</>
|
|
148
|
+
)}
|
|
149
|
+
{settings.githubUrl && (
|
|
150
|
+
<>
|
|
151
|
+
<a
|
|
152
|
+
href={settings.githubUrl as string}
|
|
153
|
+
class="inline-flex items-center gap-[0.3em] text-fg underline hover:text-accent"
|
|
154
|
+
target="_blank"
|
|
155
|
+
rel="noopener noreferrer"
|
|
156
|
+
>
|
|
157
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" class="w-[1em] h-[1em] shrink-0">
|
|
158
|
+
<path
|
|
159
|
+
fill="currentColor"
|
|
160
|
+
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
|
|
161
|
+
/>
|
|
162
|
+
</svg>
|
|
163
|
+
GitHub
|
|
164
|
+
</a>
|
|
165
|
+
<span class="text-muted">/</span>
|
|
166
|
+
</>
|
|
167
|
+
)}
|
|
168
|
+
{/* @Takazudo link — ported from pages/index.tsx (refs #1453).
|
|
169
|
+
The locale home was missing this trailing item, leaving a
|
|
170
|
+
dangling "/" separator after GitHub. */}
|
|
171
|
+
<a
|
|
172
|
+
href="https://x.com/Takazudo"
|
|
173
|
+
class="text-fg underline hover:text-accent"
|
|
174
|
+
target="_blank"
|
|
175
|
+
rel="noopener noreferrer"
|
|
176
|
+
>
|
|
177
|
+
@Takazudo
|
|
178
|
+
</a>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Sitemap grid — SiteTreeNav island mirrors the EN home (refs #1453).
|
|
185
|
+
The locale home was using DocsSitemap (vertical <details> list);
|
|
186
|
+
replaced with the same SiteTreeNav island used by pages/index.tsx. */}
|
|
187
|
+
{Island({
|
|
188
|
+
when: "idle",
|
|
189
|
+
children: (
|
|
190
|
+
<SiteTreeNav
|
|
191
|
+
tree={groupedTree}
|
|
192
|
+
categoryOrder={categoryOrder}
|
|
193
|
+
categoryIgnore={["inbox", "develop"]}
|
|
194
|
+
/>
|
|
195
|
+
),
|
|
196
|
+
}) as unknown as VNode}
|
|
197
|
+
|
|
198
|
+
{settings.docTags && tagCount > 0 && (
|
|
199
|
+
<section class="mt-vsp-xl">
|
|
200
|
+
<h2 class="text-title font-bold mb-vsp-md">
|
|
201
|
+
{t("doc.allTags", locale)}
|
|
202
|
+
</h2>
|
|
203
|
+
<a
|
|
204
|
+
href={withBase(`/${locale}/docs/tags`)}
|
|
205
|
+
class="text-accent underline hover:text-accent-hover"
|
|
206
|
+
>
|
|
207
|
+
{t("doc.allTags", locale)}
|
|
208
|
+
</a>
|
|
209
|
+
</section>
|
|
210
|
+
)}
|
|
211
|
+
</DocLayoutWithDefaults>
|
|
212
|
+
);
|
|
213
|
+
}
|