create-zudo-doc 0.2.21 → 1.0.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.
Files changed (92) hide show
  1. package/dist/compose.d.ts +6 -3
  2. package/dist/compose.js +6 -4
  3. package/dist/features/image-enlarge.d.ts +22 -12
  4. package/dist/features/image-enlarge.js +23 -191
  5. package/dist/scaffold.d.ts +11 -0
  6. package/dist/scaffold.js +51 -24
  7. package/dist/settings-gen.js +4 -0
  8. package/dist/zfb-config-gen.d.ts +16 -10
  9. package/dist/zfb-config-gen.js +34 -239
  10. package/package.json +1 -1
  11. package/templates/base/pages/_mdx-components.ts +64 -185
  12. package/templates/base/pages/index.tsx +1 -1
  13. package/templates/base/pages/lib/_body-end-islands.tsx +6 -13
  14. package/templates/base/pages/lib/_category-nav.tsx +30 -115
  15. package/templates/base/pages/lib/_category-tree-nav.tsx +38 -65
  16. package/templates/base/pages/lib/_compose-meta-title.ts +2 -6
  17. package/templates/base/pages/lib/_doc-body-end.tsx +3 -35
  18. package/templates/base/pages/lib/_doc-content-header.tsx +10 -111
  19. package/templates/base/pages/lib/_doc-history-area.tsx +19 -211
  20. package/templates/base/pages/lib/_doc-metainfo-area.tsx +10 -113
  21. package/templates/base/pages/lib/_doc-page-renderer.tsx +22 -183
  22. package/templates/base/pages/lib/_doc-page-shell.tsx +17 -251
  23. package/templates/base/pages/lib/_doc-pager.tsx +4 -74
  24. package/templates/base/pages/lib/_doc-route-entries.ts +43 -177
  25. package/templates/base/pages/lib/_doc-route-paths.ts +16 -101
  26. package/templates/base/pages/lib/_doc-tags-area.tsx +14 -77
  27. package/templates/base/pages/lib/_extract-headings.ts +21 -295
  28. package/templates/base/pages/lib/_footer-with-defaults.tsx +37 -225
  29. package/templates/base/pages/lib/_frontmatter-preview-data.ts +5 -31
  30. package/templates/base/pages/lib/_head-with-defaults.tsx +13 -138
  31. package/templates/base/pages/lib/_header-with-defaults.tsx +24 -324
  32. package/templates/base/pages/lib/_inline-version-switcher.tsx +16 -78
  33. package/templates/base/pages/lib/_math-block.tsx +4 -63
  34. package/templates/base/pages/lib/_nav-data-prep.ts +32 -51
  35. package/templates/base/pages/lib/_nav-source-cache.ts +12 -57
  36. package/templates/base/pages/lib/_nav-source-docs.ts +33 -233
  37. package/templates/base/pages/lib/_search-widget-script.ts +2 -470
  38. package/templates/base/pages/lib/_search-widget.tsx +9 -195
  39. package/templates/base/pages/lib/_sidebar-prepaint.tsx +6 -59
  40. package/templates/base/pages/lib/_sidebar-with-defaults.tsx +16 -118
  41. package/templates/base/pages/lib/_site-tree-nav.tsx +29 -80
  42. package/templates/base/pages/lib/doc-page-props.ts +26 -44
  43. package/templates/base/pages/lib/locale-merge.ts +32 -145
  44. package/templates/base/pages/lib/route-enumerators.ts +52 -286
  45. package/templates/base/src/components/ai-chat-modal.tsx +9 -8
  46. package/templates/base/src/components/content/code-group.tsx +3 -76
  47. package/templates/base/src/components/content/content-admonition.tsx +4 -56
  48. package/templates/base/src/components/doc-history.tsx +9 -8
  49. package/templates/base/src/components/image-enlarge.tsx +11 -8
  50. package/templates/base/src/components/sidebar-toggle.tsx +6 -170
  51. package/templates/base/src/components/sidebar-tree.tsx +6 -548
  52. package/templates/base/src/config/color-scheme-utils.ts +34 -158
  53. package/templates/base/src/config/i18n.ts +9 -0
  54. package/templates/base/src/config/settings-types.ts +34 -172
  55. package/templates/base/src/config/z-index-tokens.ts +5 -4
  56. package/templates/base/src/styles/global.css +40 -587
  57. package/templates/base/src/utils/base.ts +1 -1
  58. package/templates/base/src/utils/docs.ts +47 -16
  59. package/templates/base/src/utils/github.ts +12 -9
  60. package/templates/base/src/utils/nav-scope.ts +13 -42
  61. package/templates/base/src/utils/sidebar.ts +18 -86
  62. package/templates/base/src/utils/slug.ts +10 -53
  63. package/templates/base/src/utils/smart-break.tsx +12 -120
  64. package/templates/base/src/utils/tags.ts +25 -68
  65. package/templates/features/bodyFootUtil/files/src/utils/github.ts +12 -9
  66. package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +13 -39
  67. package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -636
  68. package/templates/features/docHistory/files/src/types/doc-history.ts +7 -23
  69. package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +35 -201
  70. package/templates/features/i18n/files/pages/[locale]/index.tsx +1 -1
  71. package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +8 -269
  72. package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +6 -99
  73. package/templates/features/tagGovernance/files/scripts/tags-audit.ts +67 -515
  74. package/templates/features/versioning/files/pages/lib/_versions-page.tsx +21 -73
  75. package/templates/base/pages/404.tsx +0 -61
  76. package/templates/base/pages/robots.txt.tsx +0 -29
  77. package/templates/base/pages/sitemap.xml.tsx +0 -59
  78. package/templates/base/plugins/connect-adapter.mjs +0 -169
  79. package/templates/base/plugins/copy-public-plugin.mjs +0 -58
  80. package/templates/base/plugins/search-index-plugin.mjs +0 -66
  81. package/templates/base/scripts/gen-z-index.mjs +0 -157
  82. package/templates/base/src/components/mermaid-enlarge.tsx +0 -490
  83. package/templates/base/src/components/site-tree-nav.tsx +0 -220
  84. package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +0 -47
  85. package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +0 -111
  86. package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +0 -59
  87. package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +0 -39
  88. package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +0 -43
  89. package/templates/features/docTags/files/pages/docs/tags/index.tsx +0 -20
  90. package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +0 -93
  91. package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +0 -48
  92. package/templates/features/versioning/files/pages/docs/versions.tsx +0 -20
@@ -1,119 +1,34 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
- // Host-side MDX wrapper for <CategoryNav category="..." />.
4
- //
5
- // Data-resolution steps performed before forwarding to the v2 CategoryNav component:
6
- // 1. Load docs for the active locale (or defaultLocale when not passed).
7
- // 2. Build the nav tree with buildNavTree().
8
- // 3. Find the target category node via findNode().
9
- // 4. Filter to children with hasPage === true.
10
- // 5. Forward the resolved children to the v2 CategoryNav component.
11
- //
12
- // All data access is synchronous (ADR-004 zfb content snapshot contract)
13
- // via loadDocs() from pages/_data.ts.
14
- //
15
- // The `lang` prop is injected by createMdxComponents() in
16
- // pages/_mdx-components.ts so locale routes get locale-aware nav data.
17
-
18
- import type { JSX } from "preact";
19
- import { CategoryNav as CategoryNavV2 } from "@takazudo/zudo-doc/nav-indexing";
20
- import type { NavNode as V2NavNode } from "@takazudo/zudo-doc/nav-indexing/types";
21
- import {
22
- buildNavTree,
23
- findNode,
24
- firstRoutedHref,
25
- } from "@/utils/docs";
26
- import { defaultLocale, type Locale } from "@/config/i18n";
3
+ // Thin stub — category-nav moved to the package (epic #2344, S8).
4
+ // Calls `createCategoryNavWrapper(deps)` from @takazudo/zudo-doc/category-nav
5
+ // with host singletons injected, then re-exports the resulting component so
6
+ // all existing call sites continue to work unchanged.
7
+
8
+ import { createCategoryNavWrapper } from "@takazudo/zudo-doc/category-nav";
9
+ import { buildNavTree, findNode, firstRoutedHref } from "@/utils/docs";
10
+ import { defaultLocale } from "@/config/i18n";
27
11
  import { resolveNavSource } from "./_nav-source-docs";
28
12
 
29
- export interface CategoryNavWrapperProps {
30
- /**
31
- * Slug of the category whose immediate children should be listed, e.g.
32
- * "getting-started" or "guides/layout-demos".
33
- */
34
- category?: string;
35
- /**
36
- * Explicit list of top-level category slugs to render as cards. Use this
37
- * when the target categories are not children of a single parent node in
38
- * the nav tree (e.g. "claude-md", "claude-skills" are top-level siblings
39
- * of "claude", not children). Each slug is resolved to its nav node; nodes
40
- * not found in the tree are silently skipped.
41
- *
42
- * A `category_no_page` category has no route of its own, so its card links to
43
- * the first routed descendant page (via firstRoutedHref); categories with no
44
- * reachable page are skipped rather than emitting a dead link.
45
- */
46
- categories?: string[];
47
- /**
48
- * Active locale. Injected via createMdxComponents() closure.
49
- * Defaults to defaultLocale when not provided.
50
- */
51
- lang?: Locale | string;
52
- /** Optional extra CSS classes forwarded to the <nav> element. */
53
- class?: string;
54
- }
55
-
56
- /**
57
- * MDX wrapper for CategoryNav. Resolves nav tree data host-side and forwards
58
- * the resolved category children into the v2 CategoryNav component.
59
- *
60
- * Supports two modes:
61
- * - `category`: resolves immediate children of a single category node.
62
- * - `categories`: resolves an explicit list of top-level slugs as cards.
63
- * Use this when the target categories are siblings in the nav tree rather
64
- * than children of a common parent (e.g. claude-md / claude-skills are
65
- * top-level peers of claude, not children of it). A noPage category card
66
- * links to its first routed descendant page (it has no route of its own).
67
- *
68
- * Returns null when no visible children are resolved.
69
- */
70
- export function CategoryNavWrapper({
71
- category,
72
- categories,
73
- lang = defaultLocale,
74
- class: className,
75
- }: CategoryNavWrapperProps): JSX.Element | null {
76
- const locale = lang as Locale;
77
-
78
- // No defaultLocaleOnly filter — category cards intentionally show all EN
79
- // pages even if they match defaultLocaleOnlyPrefixes (the option signature in
80
- // resolveNavSource keeps this variant from colliding with the sidebar's).
81
- const { navDocs, categoryMeta } = resolveNavSource(locale, undefined, {
82
- keepUnlisted: true,
83
- });
84
- const tree = buildNavTree(navDocs, locale, categoryMeta);
85
-
86
- let children: V2NavNode[];
87
-
88
- if (categories !== undefined) {
89
- // Explicit slug list mode: resolve each slug to its nav node and build a
90
- // card for it. A `category_no_page` category has no route of its own
91
- // (collectAutoIndexNodes skips noPage nodes), so its card links to the
92
- // first routed descendant page; categories with no reachable page are
93
- // skipped rather than emitting a dead link.
94
- children = categories
95
- .map((slug): V2NavNode | null => {
96
- const node = findNode(tree, slug);
97
- if (!node) return null;
98
- const href = node.href ?? firstRoutedHref(node);
99
- if (!href) return null;
100
- return {
101
- label: node.label,
102
- description: node.description,
103
- href,
104
- hasPage: true,
105
- children: [],
106
- };
107
- })
108
- .filter((n): n is V2NavNode => n !== null);
109
- } else if (category !== undefined) {
110
- const categoryNode = findNode(tree, category);
111
- children = (categoryNode?.children.filter((c) => c.hasPage) ?? []) as V2NavNode[];
112
- } else {
113
- return null;
114
- }
115
-
116
- if (children.length === 0) return null;
117
-
118
- return <CategoryNavV2 children={children} class={className} />;
119
- }
13
+ export type { CategoryNavWrapperProps } from "@takazudo/zudo-doc/category-nav";
14
+
15
+ export const CategoryNavWrapper = createCategoryNavWrapper({
16
+ defaultLocale,
17
+ resolveNavSource: resolveNavSource as (
18
+ lang: string,
19
+ currentVersion: string | undefined,
20
+ options?: { applyDefaultLocaleOnlyFilter?: boolean; keepUnlisted?: boolean },
21
+ ) => import("@takazudo/zudo-doc/category-nav").CategoryNavSource,
22
+ buildNavTree: buildNavTree as (
23
+ docs: unknown[],
24
+ locale: string,
25
+ categoryMeta: Map<string, unknown>,
26
+ ) => import("@takazudo/zudo-doc/category-nav").CategoryNavNode[],
27
+ findNode: findNode as (
28
+ tree: import("@takazudo/zudo-doc/category-nav").CategoryNavNode[],
29
+ slug: string,
30
+ ) => import("@takazudo/zudo-doc/category-nav").CategoryNavNode | undefined,
31
+ firstRoutedHref: firstRoutedHref as (
32
+ node: import("@takazudo/zudo-doc/category-nav").CategoryNavNode,
33
+ ) => string | undefined,
34
+ });
@@ -1,72 +1,45 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
- // Host-side MDX wrapper for <CategoryTreeNav category="..." />.
4
- //
5
- // Data-resolution steps performed before forwarding to the v2 CategoryTreeNav component:
6
- // 1. Load docs for the active locale (defaultLocale when not passed).
7
- // 2. Build the full nav tree with buildNavTree() + groupSatelliteNodes()
8
- // (category slug is passed as the grouping prefix list).
9
- // 3. Find the target category node via findNode().
10
- // 4. Filter to children with hasPage === true or children.length > 0.
11
- // 5. Forward the resolved children to the v2 CategoryTreeNav component.
12
- //
13
- // All data access is synchronous (ADR-004 zfb content snapshot contract).
14
- // The `lang` prop is injected by createMdxComponents() in
15
- // pages/_mdx-components.ts so locale routes get locale-aware nav data.
3
+ // Thin stub — category-tree-nav moved to the package (epic #2344, S8).
4
+ // Calls `createCategoryTreeNavWrapper(deps)` from
5
+ // @takazudo/zudo-doc/category-tree-nav with host singletons injected,
6
+ // then re-exports the resulting component so all existing call sites continue
7
+ // to work unchanged.
16
8
 
17
- import type { JSX } from "preact";
18
- import { CategoryTreeNav as CategoryTreeNavV2 } from "@takazudo/zudo-doc/nav-indexing";
19
- import {
20
- buildNavTree,
21
- groupSatelliteNodes,
22
- findNode,
23
- } from "@/utils/docs";
9
+ import { createCategoryTreeNavWrapper } from "@takazudo/zudo-doc/category-tree-nav";
10
+ import type {
11
+ CategoryTreeNavNode,
12
+ CategoryTreeNavSource,
13
+ } from "@takazudo/zudo-doc/category-tree-nav";
14
+ import { buildNavTree, groupSatelliteNodes, findNode, type NavNode } from "@/utils/docs";
24
15
  import { defaultLocale, type Locale } from "@/config/i18n";
16
+ import type { DocsEntry } from "@/types/docs-entry";
17
+ import type { CategoryMeta } from "@/utils/docs";
25
18
  import { resolveNavSource } from "./_nav-source-docs";
26
19
 
27
- export interface CategoryTreeNavWrapperProps {
28
- /**
29
- * Slug of the category whose children should be rendered as a tree,
30
- * e.g. "guides" or "getting-started".
31
- */
32
- category: string;
33
- /**
34
- * Active locale. Injected via createMdxComponents() closure.
35
- * Defaults to defaultLocale when not provided.
36
- */
37
- lang?: Locale | string;
38
- }
20
+ export type { CategoryTreeNavWrapperProps } from "@takazudo/zudo-doc/category-tree-nav";
39
21
 
40
- /**
41
- * MDX wrapper for CategoryTreeNav. Resolves nav tree data host-side and
42
- * forwards the resolved category children into the v2 CategoryTreeNav
43
- * component.
44
- *
45
- * Returns null when the category is not found or has no renderable children —
46
- * matching the original Astro component's guard.
47
- */
48
- export function CategoryTreeNavWrapper({
49
- category,
50
- lang = defaultLocale,
51
- }: CategoryTreeNavWrapperProps): JSX.Element | null {
52
- const locale = lang as Locale;
53
-
54
- // No defaultLocaleOnly filter — tree nav intentionally shows all EN pages
55
- // (same variant as _category-nav.tsx).
56
- const { navDocs, categoryMeta } = resolveNavSource(locale, undefined, {
57
- keepUnlisted: true,
58
- });
59
- const rawTree = buildNavTree(navDocs, locale, categoryMeta);
60
- // groupSatelliteNodes with [category] groups satellite nodes under the
61
- // target category matching the original Astro component.
62
- const tree = groupSatelliteNodes(rawTree, [category]);
63
-
64
- const categoryNode = findNode(tree, category);
65
- const children =
66
- categoryNode?.children.filter((c) => c.hasPage || c.children.length > 0) ??
67
- [];
68
-
69
- if (children.length === 0) return null;
70
-
71
- return <CategoryTreeNavV2 children={children} />;
72
- }
22
+ // The factory describes its injected nav helpers with the minimal
23
+ // CategoryTreeNavNode view (no `slug` / `position`), while the host helpers are
24
+ // typed against the full NavNode. At runtime the nodes are real NavNodes; the
25
+ // wrappers re-view them as the minimal type at the injection boundary (cast via
26
+ // `unknown` because NavNode is a structural supertype of CategoryTreeNavNode,
27
+ // so a direct cast is rejected as non-overlapping).
28
+ export const CategoryTreeNavWrapper = createCategoryTreeNavWrapper({
29
+ defaultLocale,
30
+ resolveNavSource: resolveNavSource as (
31
+ lang: string,
32
+ currentVersion: string | undefined,
33
+ options?: { applyDefaultLocaleOnlyFilter?: boolean; keepUnlisted?: boolean },
34
+ ) => CategoryTreeNavSource,
35
+ buildNavTree: (docs: unknown[], locale: string, categoryMeta: Map<string, unknown>) =>
36
+ buildNavTree(
37
+ docs as DocsEntry[],
38
+ locale as Locale,
39
+ categoryMeta as Map<string, CategoryMeta>,
40
+ ) as unknown as CategoryTreeNavNode[],
41
+ groupSatelliteNodes: (tree: CategoryTreeNavNode[], prefixes: string[]) =>
42
+ groupSatelliteNodes(tree as unknown as NavNode[], prefixes) as unknown as CategoryTreeNavNode[],
43
+ findNode: (tree: CategoryTreeNavNode[], slug: string) =>
44
+ findNode(tree as unknown as NavNode[], slug) as unknown as CategoryTreeNavNode | undefined,
45
+ });
@@ -19,11 +19,7 @@
19
19
  * has it in practice), fall back to the bare title.
20
20
  *
21
21
  */
22
+ import { createComposeMetaTitle } from "@takazudo/zudo-doc/compose-meta-title";
22
23
  import { settings } from "@/config/settings";
23
24
 
24
- export function composeMetaTitle(title: string): string {
25
- const siteName = settings.siteName;
26
- if (!siteName) return title;
27
- if (title === siteName) return siteName;
28
- return `${title} | ${siteName}`;
29
- }
25
+ export const composeMetaTitle = createComposeMetaTitle(settings.siteName);
@@ -1,40 +1,8 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
- // Shared bodyEndComponents block for doc pages.
4
- //
5
- // Extracted from the four doc-route render bodies (pages/docs/[...slug].tsx,
6
- // pages/[locale]/docs/[...slug].tsx, pages/v/[version]/docs/[...slug].tsx,
7
- // pages/v/[version]/[locale]/docs/[...slug].tsx) where the block was
8
- // duplicated verbatim across all four routes.
9
- //
10
- // Wraps BodyEndIslands + SidebarResizerInit (the latter conditional on
11
- // settings.sidebarResizer) and forwards the basePath prop that
12
- // BodyEndIslands requires for API URL construction.
13
-
14
- import type { JSX } from "preact";
3
+ // Host thin-stub see @takazudo/zudo-doc/doc-body-end (epic #2344, S7).
15
4
  import { settings } from "@/config/settings";
5
+ import { createDocBodyEnd } from "@takazudo/zudo-doc/doc-body-end";
16
6
  import { BodyEndIslands } from "./_body-end-islands";
17
- import { SidebarResizerInit } from "@takazudo/zudo-doc/sidebar-resizer";
18
7
 
19
- /**
20
- * The `bodyEndComponents` slot content shared by all four doc-route page
21
- * components: `BodyEndIslands` (modal overlays, optional client-router
22
- * bootstrap when `dynamicPageTransition` is on, image-enlarge) and the
23
- * optional `SidebarResizerInit` drag handle. Whether the client-router
24
- * bootstrap island is included depends on `settings.dynamicPageTransition`
25
- * (gated inside `BodyEndIslands`). The SSR `<ClientRouter />` mounted by
26
- * `DocLayout` self-activates when `enableClientRouter` is `true`; the
27
- * island here is redundant for that activation path and kept only for the
28
- * `when="load"` hydration slot.
29
- */
30
- export function DocBodyEnd(): JSX.Element {
31
- return (
32
- <>
33
- <BodyEndIslands basePath={settings.base ?? "/"} />
34
- {/* SidebarResizerInit: attach drag handle to #desktop-sidebar on load
35
- and on AFTER_NAVIGATE_EVENT (zfb:after-swap under the Strategy B
36
- SPA navigation model). Idempotent — safe on every page. */}
37
- {settings.sidebarResizer && <SidebarResizerInit />}
38
- </>
39
- );
40
- }
8
+ export const DocBodyEnd = createDocBodyEnd({ settings, BodyEndIslands });
@@ -1,118 +1,17 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
- // Shared content header block for entry doc pages.
4
- //
5
- // Extracted from the four doc-route render bodies (pages/docs/[...slug].tsx,
6
- // pages/[locale]/docs/[...slug].tsx, pages/v/[version]/docs/[...slug].tsx,
7
- // pages/v/[version]/[locale]/docs/[...slug].tsx) where the block was
8
- // duplicated verbatim across all four entry branches.
9
- //
10
- // Covers the block from the <h1> through <FrontmatterPreview> (inclusive).
11
- // The MDX <Content /> and DocHistoryArea remain inline in each route because
12
- // they differ per route (e.g. the unlisted guard, contentDir param).
13
-
14
- import type { JSX } from "preact";
3
+ // Host thin-stub see @takazudo/zudo-doc/doc-content-header (epic #2344, S7).
15
4
  import { t } from "@/config/i18n";
16
- import { FrontmatterPreview } from "@takazudo/zudo-doc/metainfo";
17
5
  import { frontmatterRenderers } from "@/config/frontmatter-preview-renderers";
6
+ import { createDocContentHeader } from "@takazudo/zudo-doc/doc-content-header";
7
+ import { buildFrontmatterPreviewEntries } from "./_frontmatter-preview-data";
18
8
  import { DocMetainfoArea } from "./_doc-metainfo-area";
19
9
  import { DocTagsArea } from "./_doc-tags-area";
20
- import { buildFrontmatterPreviewEntries } from "./_frontmatter-preview-data";
21
- import type { DocPageEntry } from "./doc-page-props";
22
-
23
- interface DocContentHeaderProps {
24
- /** The full content entry (title, description, tags, frontmatter data). */
25
- entry: DocPageEntry;
26
- /** Canonical page slug for DocMetainfoArea / DocTagsArea lookups. */
27
- slug: string;
28
- /** Active locale string, e.g. "en", "ja". */
29
- locale: string;
30
- /**
31
- * True when this page falls back to the base EN collection for a locale
32
- * route. Renders the "not yet translated" notice. Defaults to false.
33
- * Only relevant for locale-prefixed and versioned-locale routes.
34
- */
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;
47
- }
48
-
49
- /**
50
- * Content header block for entry doc pages: h1, doc-metainfo, tag chips,
51
- * optional locale-fallback notice, description paragraph, and frontmatter
52
- * preview table.
53
- *
54
- * Placement: at the top of the article `<Fragment>`, before
55
- * `<props.entry.Content />` and the pager. The MDX content and DocHistoryArea
56
- * remain in the route component so per-route variations (unlisted gate,
57
- * contentDir) stay co-located with their context.
58
- */
59
- export function DocContentHeader({
60
- entry,
61
- slug,
62
- locale,
63
- isFallback = false,
64
- version,
65
- }: DocContentHeaderProps): JSX.Element {
66
- return (
67
- <>
68
- <h1 class="text-heading font-bold mb-vsp-xs">{entry.data.title}</h1>
69
-
70
- {/* Build-time date block (Created / Updated / Author).
71
- doc-metainfo placement — between <h1> and description.
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
- )}
79
-
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} />}
85
-
86
- {/* Fallback notice for non-translated pages */}
87
- {isFallback && !entry.data.generated && (
88
- <div
89
- class="mb-vsp-md border border-info/30 bg-info/5 px-hsp-lg py-vsp-sm text-small text-muted rounded"
90
- role="note"
91
- >
92
- {t("doc.fallbackNotice", locale)}
93
- </div>
94
- )}
95
-
96
- {entry.data.description && (
97
- <p class="mb-vsp-lg text-title text-muted">
98
- {entry.data.description}
99
- </p>
100
- )}
101
10
 
102
- {/* Frontmatter preview non-system, custom keys only. Returns
103
- null when the entries array is empty, so pages without
104
- custom frontmatter emit nothing. Custom per-key renderers
105
- from frontmatter-preview-renderers.tsx produce styled cells
106
- (pills, badges, etc.) instead of plain text. */}
107
- <FrontmatterPreview
108
- entries={buildFrontmatterPreviewEntries(entry.data)}
109
- title={t("frontmatter.preview.title", locale)}
110
- keyColLabel={t("frontmatter.preview.keyCol", locale)}
111
- valueColLabel={t("frontmatter.preview.valueCol", locale)}
112
- renderers={frontmatterRenderers}
113
- data={entry.data as Record<string, unknown>}
114
- locale={locale}
115
- />
116
- </>
117
- );
118
- }
11
+ export const DocContentHeader = createDocContentHeader({
12
+ t,
13
+ buildFrontmatterPreviewEntries,
14
+ frontmatterRenderers,
15
+ DocMetainfoArea,
16
+ DocTagsArea,
17
+ });