create-zudo-doc 3.1.0 → 3.3.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 (127) hide show
  1. package/dist/claude-md-gen.d.ts +8 -0
  2. package/dist/claude-md-gen.js +43 -26
  3. package/dist/compose.d.ts +14 -20
  4. package/dist/compose.js +15 -25
  5. package/dist/features/body-foot-util.d.ts +8 -4
  6. package/dist/features/body-foot-util.js +8 -4
  7. package/dist/features/claude-resources.d.ts +12 -0
  8. package/dist/features/claude-resources.js +13 -6
  9. package/dist/features/design-token-panel.d.ts +0 -12
  10. package/dist/features/design-token-panel.js +32 -93
  11. package/dist/features/doc-history.d.ts +18 -3
  12. package/dist/features/doc-history.js +45 -60
  13. package/dist/features/doc-tags.d.ts +9 -13
  14. package/dist/features/doc-tags.js +10 -26
  15. package/dist/features/dynamic-page-transition.d.ts +19 -30
  16. package/dist/features/dynamic-page-transition.js +21 -209
  17. package/dist/features/footer-taglist.d.ts +1 -1
  18. package/dist/features/footer-taglist.js +1 -1
  19. package/dist/features/footer.d.ts +3 -2
  20. package/dist/features/footer.js +3 -2
  21. package/dist/features/i18n.d.ts +13 -8
  22. package/dist/features/i18n.js +14 -9
  23. package/dist/features/image-enlarge.d.ts +7 -26
  24. package/dist/features/image-enlarge.js +7 -26
  25. package/dist/features/llms-txt.d.ts +3 -5
  26. package/dist/features/llms-txt.js +3 -5
  27. package/dist/features/search.d.ts +7 -3
  28. package/dist/features/search.js +7 -3
  29. package/dist/features/sidebar-resizer.d.ts +4 -8
  30. package/dist/features/sidebar-resizer.js +4 -8
  31. package/dist/features/sidebar-toggle.d.ts +7 -7
  32. package/dist/features/sidebar-toggle.js +7 -7
  33. package/dist/features/tag-governance.d.ts +14 -7
  34. package/dist/features/tag-governance.js +52 -8
  35. package/dist/features/tauri.d.ts +13 -10
  36. package/dist/features/tauri.js +16 -52
  37. package/dist/features/versioning.d.ts +12 -24
  38. package/dist/features/versioning.js +13 -39
  39. package/dist/index.js +2 -3
  40. package/dist/preset.d.ts +1 -0
  41. package/dist/preset.js +5 -0
  42. package/dist/prompts.d.ts +2 -0
  43. package/dist/prompts.js +1 -0
  44. package/dist/scaffold.d.ts +13 -6
  45. package/dist/scaffold.js +78 -59
  46. package/dist/utils.d.ts +10 -0
  47. package/dist/utils.js +14 -0
  48. package/dist/zfb-config-gen.d.ts +32 -20
  49. package/dist/zfb-config-gen.js +400 -53
  50. package/package.json +2 -2
  51. package/templates/base/pages/docs/[[...slug]].tsx +59 -0
  52. package/templates/base/pages/index.tsx +6 -41
  53. package/templates/base/scripts/setup-doc-skill.sh +261 -0
  54. package/templates/base/src/styles/global.css +15 -340
  55. package/templates/base/tsconfig.json +3 -26
  56. package/templates/features/i18n/files/pages/[locale]/docs/[[...slug]].tsx +88 -0
  57. package/templates/features/tagGovernance/files/scripts/tags-suggest.ts +3 -1
  58. package/dist/settings-gen.d.ts +0 -2
  59. package/dist/settings-gen.js +0 -319
  60. package/templates/base/.htmlvalidate.json +0 -5
  61. package/templates/base/.zfb/doc-history-meta.json +0 -1
  62. package/templates/base/pages/_data.ts +0 -160
  63. package/templates/base/pages/lib/_body-end-islands.tsx +0 -165
  64. package/templates/base/pages/lib/_chrome.ts +0 -167
  65. package/templates/base/pages/lib/_details.tsx +0 -29
  66. package/templates/base/pages/lib/_doc-route-entries.ts +0 -10
  67. package/templates/base/pages/lib/_extract-headings.ts +0 -37
  68. package/templates/base/pages/lib/_frontmatter-preview-data.ts +0 -27
  69. package/templates/base/pages/lib/_nav-source-cache.ts +0 -100
  70. package/templates/base/pages/lib/_nav-source-docs.ts +0 -17
  71. package/templates/base/pages/lib/_preset-generator.tsx +0 -81
  72. package/templates/base/pages/lib/_route-context.ts +0 -32
  73. package/templates/base/pages/lib/_search-widget.tsx +0 -17
  74. package/templates/base/pages/lib/doc-page-props.ts +0 -30
  75. package/templates/base/pages/lib/locale-merge.ts +0 -59
  76. package/templates/base/scripts/run-b4push.sh +0 -102
  77. package/templates/base/src/components/ai-chat-modal.tsx +0 -18
  78. package/templates/base/src/components/content/code-group.tsx +0 -3
  79. package/templates/base/src/components/content/content-admonition.tsx +0 -4
  80. package/templates/base/src/components/desktop-sidebar-toggle.tsx +0 -15
  81. package/templates/base/src/components/doc-history.tsx +0 -21
  82. package/templates/base/src/components/image-enlarge.tsx +0 -24
  83. package/templates/base/src/components/preset-generator.tsx +0 -14
  84. package/templates/base/src/components/sidebar-toggle.tsx +0 -6
  85. package/templates/base/src/components/sidebar-tree.tsx +0 -6
  86. package/templates/base/src/config/color-scheme-utils.ts +0 -69
  87. package/templates/base/src/config/color-schemes.ts +0 -165
  88. package/templates/base/src/config/docs-schema.ts +0 -95
  89. package/templates/base/src/config/frontmatter-preview-defaults.ts +0 -27
  90. package/templates/base/src/config/frontmatter-preview-renderers.tsx +0 -46
  91. package/templates/base/src/config/i18n.ts +0 -239
  92. package/templates/base/src/config/settings-types.ts +0 -45
  93. package/templates/base/src/config/sidebars.ts +0 -66
  94. package/templates/base/src/config/tag-vocabulary-types.ts +0 -4
  95. package/templates/base/src/config/tag-vocabulary.ts +0 -20
  96. package/templates/base/src/config/z-index-tokens.ts +0 -128
  97. package/templates/base/src/types/docs-entry.ts +0 -28
  98. package/templates/base/src/types/heading.ts +0 -5
  99. package/templates/base/src/types/locale.ts +0 -10
  100. package/templates/base/src/utils/base.ts +0 -164
  101. package/templates/base/src/utils/docs.ts +0 -446
  102. package/templates/base/src/utils/git-info.ts +0 -70
  103. package/templates/base/src/utils/github.ts +0 -22
  104. package/templates/base/src/utils/nav-scope.ts +0 -34
  105. package/templates/base/src/utils/sidebar.ts +0 -36
  106. package/templates/base/src/utils/slug.ts +0 -10
  107. package/templates/base/src/utils/smart-break.tsx +0 -12
  108. package/templates/base/src/utils/tags.ts +0 -83
  109. package/templates/base/zfb-shim.d.ts +0 -178
  110. package/templates/features/bodyFootUtil/files/src/utils/github.ts +0 -22
  111. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/escape-for-mdx.test.ts +0 -42
  112. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +0 -752
  113. package/templates/features/claudeResources/files/src/integrations/claude-resources/escape-for-mdx.ts +0 -97
  114. package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +0 -735
  115. package/templates/features/designTokenPanel/files/src/components/design-token-panel-bootstrap.tsx +0 -15
  116. package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +0 -435
  117. package/templates/features/designTokenPanel/files/src/config/design-tokens-manifest.ts +0 -174
  118. package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +0 -30
  119. package/templates/features/docHistory/files/src/components/doc-history.tsx +0 -10
  120. package/templates/features/docHistory/files/src/types/doc-history.ts +0 -7
  121. package/templates/features/dynamicPageTransition/files/src/components/client-router-bootstrap.tsx +0 -72
  122. package/templates/features/i18n/files/pages/[locale]/index.tsx +0 -72
  123. package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +0 -11
  124. package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +0 -6
  125. package/templates/features/tauri/files/src/components/find-bar.tsx +0 -122
  126. package/templates/features/tauri/files/src/components/find-in-page-init.tsx +0 -59
  127. package/templates/features/tauri/files/src/utils/find-in-page.ts +0 -175
@@ -0,0 +1,88 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ // Locked manifest (#2653 Decision 4, i18n addendum): the locale-prefixed
4
+ // counterpart of pages/docs/[[...slug]].tsx — required for the same reason
5
+ // (injected DYNAMIC routes 404 in `zfb dev`). Self-contained: only the three
6
+ // sanctioned package entrypoints — no `pages/lib`, no `@/config`. Mirrors
7
+ // the package's own `routes/locale-docs-slug.tsx` shape, rebuilt from the
8
+ // route-context payload instead of the package-internal `_context.js`.
9
+ //
10
+ // Per-locale content dir + fallback notice (ported from
11
+ // packages/zudo-doc/src/routes/locale-docs-slug.tsx and the showcase's
12
+ // pages/[locale]/docs/[[...slug]].tsx): each route carries the locale's own
13
+ // content directory (`getLocaleConfig(locale).dir`) and an `isFallback` flag.
14
+ // A page that only exists in the default locale is served as an untranslated
15
+ // FALLBACK — it must (a) read doc-history from the DEFAULT-locale content dir
16
+ // (not the translated one, which has no such file), and (b) thread
17
+ // `isFallback` so the chrome renders the "not translated yet" notice. Dropping
18
+ // either — as this stub previously did by hardcoding `docsDir` for every
19
+ // locale and never passing `isFallback` — silently breaks translated pages.
20
+ //
21
+ // docHistory note: same as the default-locale stub — when docHistory is
22
+ // selected, the generator patches this file too.
23
+
24
+ import type { JSX } from "preact";
25
+ import { routeContext } from "virtual:zudo-doc-route-context";
26
+ import {
27
+ createRouteContext,
28
+ type RouteContextPayload,
29
+ } from "@takazudo/zudo-doc/route-context";
30
+ import { createChrome } from "@takazudo/zudo-doc/chrome";
31
+
32
+ const ctx = routeContext as unknown as RouteContextPayload;
33
+ const routeCtx = createRouteContext(ctx);
34
+ const { renderDocPage } = createChrome(routeCtx);
35
+
36
+ export const frontmatter = { title: "Docs" };
37
+
38
+ export function paths(): Array<{
39
+ params: { locale: string; slug: string[] };
40
+ props: unknown;
41
+ }> {
42
+ const result: Array<{
43
+ params: { locale: string; slug: string[] };
44
+ props: unknown;
45
+ }> = [];
46
+
47
+ for (const locale of Object.keys(routeCtx.settings.locales)) {
48
+ const contentDir =
49
+ routeCtx.getLocaleConfig(locale)?.dir ?? routeCtx.settings.docsDir;
50
+ const source = routeCtx.resolveNavSource(locale, undefined, {
51
+ applyDefaultLocaleOnlyFilter: true,
52
+ keepUnlisted: true,
53
+ });
54
+ for (const item of routeCtx.buildDocRouteEntries({
55
+ source,
56
+ locale,
57
+ routeSig: `locale-docs;${locale}`,
58
+ })) {
59
+ result.push({
60
+ params: { locale, slug: item.slugParams },
61
+ props: {
62
+ ...(item.props as Record<string, unknown>),
63
+ // Fallback pages exist only in the default locale, so their
64
+ // doc-history lives under the EN docsDir; translated pages read
65
+ // their own locale dir.
66
+ contentDir: item.isFallback ? routeCtx.settings.docsDir : contentDir,
67
+ isFallback: item.isFallback,
68
+ },
69
+ });
70
+ }
71
+ }
72
+
73
+ return result;
74
+ }
75
+
76
+ type PageArgs = {
77
+ params: { locale: string; slug: string[] };
78
+ contentDir: string;
79
+ isFallback: boolean;
80
+ } & Record<string, unknown>;
81
+
82
+ export default function LocaleDocsPage(props: PageArgs): JSX.Element {
83
+ return renderDocPage(props as never, {
84
+ locale: props.params.locale,
85
+ isFallback: props.isFallback,
86
+ docHistoryContentDir: props.contentDir,
87
+ });
88
+ }
@@ -16,7 +16,9 @@ import { parseArgs } from "node:util";
16
16
  import matter from "gray-matter";
17
17
 
18
18
  import { tagVocabulary } from "../src/config/tag-vocabulary";
19
- import type { TagVocabularyEntry } from "../src/config/tag-vocabulary-types";
19
+ // Minimal-scaffold cutover (epic zudolab/zudo-doc#2651): the standalone
20
+ // `tag-vocabulary-types.ts` shim is gone — the type ships from the package.
21
+ import type { TagVocabularyEntry } from "@takazudo/zudo-doc/settings";
20
22
 
21
23
  const DEFAULT_HOST = "http://localhost:11434";
22
24
  const DEFAULT_MODEL = "qwen2.5:7b";
@@ -1,2 +0,0 @@
1
- import type { UserChoices } from "./prompts.js";
2
- export declare function generateSettingsFile(choices: UserChoices): string;
@@ -1,319 +0,0 @@
1
- import { capitalize, getLangLabel, getSecondaryLang } from "./utils.js";
2
- export function generateSettingsFile(choices) {
3
- const lines = [];
4
- // Import types from settings-types (copied from template src/config/)
5
- lines.push(`export type {`);
6
- lines.push(` HeaderNavChildItem,`);
7
- lines.push(` HeaderNavItem,`);
8
- lines.push(` HeaderRightItem,`);
9
- lines.push(` ColorModeConfig,`);
10
- lines.push(` HtmlPreviewConfig,`);
11
- lines.push(` LocaleConfig,`);
12
- lines.push(` VersionConfig,`);
13
- lines.push(` FooterConfig,`);
14
- lines.push(` FrontmatterPreviewConfig,`);
15
- lines.push(` BodyFootUtilAreaConfig,`);
16
- lines.push(` TagPlacement,`);
17
- lines.push(` TagGovernanceMode,`);
18
- lines.push(` TagVocabularyEntry,`);
19
- lines.push(` MetaTagsConfig,`);
20
- lines.push(`} from "./settings-types";`);
21
- lines.push(`import type {`);
22
- lines.push(` HeaderNavItem,`);
23
- lines.push(` HeaderRightItem,`);
24
- lines.push(` ColorModeConfig,`);
25
- lines.push(` HtmlPreviewConfig,`);
26
- lines.push(` LocaleConfig,`);
27
- lines.push(` VersionConfig,`);
28
- lines.push(` FooterConfig,`);
29
- lines.push(` FrontmatterPreviewConfig,`);
30
- lines.push(` BodyFootUtilAreaConfig,`);
31
- lines.push(` TagPlacement,`);
32
- lines.push(` TagGovernanceMode,`);
33
- lines.push(` MetaTagsConfig,`);
34
- lines.push(`} from "./settings-types";`);
35
- lines.push(``);
36
- lines.push(`export const settings = {`);
37
- if (choices.colorSchemeMode === "single") {
38
- lines.push(` colorScheme: ${JSON.stringify(choices.singleScheme ?? "Default Dark")},`);
39
- lines.push(` colorMode: false as ColorModeConfig | false,`);
40
- }
41
- else {
42
- lines.push(` colorScheme: ${JSON.stringify(choices.darkScheme ?? "Default Dark")},`);
43
- lines.push(` colorMode: {`);
44
- lines.push(` defaultMode: ${JSON.stringify(choices.defaultMode ?? "dark")},`);
45
- lines.push(` lightScheme: ${JSON.stringify(choices.lightScheme ?? "Default Light")},`);
46
- lines.push(` darkScheme: ${JSON.stringify(choices.darkScheme ?? "Default Dark")},`);
47
- lines.push(` respectPrefersColorScheme: ${choices.respectPrefersColorScheme ?? true},`);
48
- lines.push(` } satisfies ColorModeConfig as ColorModeConfig | false,`);
49
- }
50
- lines.push(` siteName: ${JSON.stringify(capitalize(choices.projectName.replace(/-/g, " ")))},`);
51
- lines.push(` siteDescription: "" as string,`);
52
- lines.push(` base: "/",`);
53
- lines.push(` trailingSlash: false as boolean,`);
54
- lines.push(` noindex: ${choices.features.includes("noindex")} as boolean,`);
55
- lines.push(` editUrl: false as string | false,`);
56
- const rawGithubUrl = (choices.githubUrl ?? "").trim();
57
- if (rawGithubUrl) {
58
- lines.push(` githubUrl: ${JSON.stringify(rawGithubUrl)} as string | false,`);
59
- }
60
- else {
61
- lines.push(` githubUrl: false as string | false,`);
62
- }
63
- lines.push(` siteUrl: "" as string,`);
64
- lines.push(` metaTags: {`);
65
- if (choices.metaTags) {
66
- const mt = choices.metaTags;
67
- lines.push(` description: ${mt.description !== undefined ? mt.description : true},`);
68
- lines.push(` keywords: ${mt.keywords !== undefined ? JSON.stringify(mt.keywords) : false},`);
69
- lines.push(` ogImage: ${mt.ogImage !== undefined ? JSON.stringify(mt.ogImage) : false},`);
70
- lines.push(` ogSiteName: ${mt.ogSiteName !== undefined ? mt.ogSiteName : true},`);
71
- if (mt.twitterCard) {
72
- lines.push(` twitterCard: ${JSON.stringify(mt.twitterCard)},`);
73
- if (mt.twitterSite) {
74
- lines.push(` twitterSite: ${JSON.stringify(mt.twitterSite)},`);
75
- }
76
- if (mt.twitterCreator) {
77
- lines.push(` twitterCreator: ${JSON.stringify(mt.twitterCreator)},`);
78
- }
79
- }
80
- else {
81
- lines.push(` twitterCard: false,`);
82
- }
83
- }
84
- else {
85
- lines.push(` description: true,`);
86
- lines.push(` keywords: false,`);
87
- lines.push(` ogImage: false,`);
88
- lines.push(` ogSiteName: true,`);
89
- lines.push(` twitterCard: false,`);
90
- }
91
- lines.push(` } satisfies MetaTagsConfig as MetaTagsConfig,`);
92
- lines.push(` docsDir: "src/content/docs",`);
93
- lines.push(` defaultLocale: ${JSON.stringify(choices.defaultLang ?? "en")} as const,`);
94
- if (choices.features.includes("i18n")) {
95
- const secondaryLang = getSecondaryLang(choices.defaultLang);
96
- const secondaryLabel = getLangLabel(secondaryLang);
97
- lines.push(` locales: {`);
98
- lines.push(` ${secondaryLang}: { label: ${JSON.stringify(secondaryLabel)}, dir: "src/content/docs-${secondaryLang}" },`);
99
- lines.push(` } satisfies Record<string, LocaleConfig>,`);
100
- }
101
- else {
102
- // `as`, not `satisfies`: satisfies keeps the inferred type at literal {},
103
- // so Object.entries(settings.locales) in the generated zfb.config.ts
104
- // yields unknown values and `zfb check` fails with TS18046 (#2053).
105
- lines.push(` locales: {} as Record<string, LocaleConfig>,`);
106
- }
107
- // mermaid is controlled by the markdown.features block in zfb.config.ts
108
- // (zfb next.12+). This field is retained for compatibility with framework
109
- // components that still read settings.mermaid. See the markdown.features
110
- // block in the generated zfb.config.ts for the canonical opt-in.
111
- lines.push(` mermaid: true,`);
112
- lines.push(` sitemap: false,`);
113
- lines.push(` docMetainfo: false,`);
114
- lines.push(` docTags: ${choices.features.includes("docTags")},`);
115
- lines.push(` tagPlacement: "after-title" as TagPlacement,`);
116
- if (choices.features.includes("tagGovernance")) {
117
- lines.push(` tagGovernance: "warn" as TagGovernanceMode,`);
118
- lines.push(` tagVocabulary: true as boolean,`);
119
- }
120
- else {
121
- lines.push(` tagGovernance: "off" as TagGovernanceMode,`);
122
- lines.push(` tagVocabulary: false as boolean,`);
123
- }
124
- // Default false — fresh scaffolds typically don't need live frontmatter preview;
125
- // users opt in once they're ready to wire up the preview panel.
126
- lines.push(` frontmatterPreview: false as FrontmatterPreviewConfig | false,`);
127
- if (choices.features.includes("llmsTxt")) {
128
- lines.push(` llmsTxt: true,`);
129
- }
130
- else {
131
- lines.push(` llmsTxt: false,`);
132
- }
133
- lines.push(` changelogs: false,`);
134
- lines.push(` math: false,`);
135
- lines.push(` cjkFriendly: ${choices.cjkFriendly ?? false} as boolean,`);
136
- lines.push(` onBrokenMarkdownLinks: "warn" as "warn" | "error" | "ignore",`);
137
- lines.push(` aiAssistant: false as boolean,`);
138
- // When the user wires up `pages/api/ai-chat.tsx` (not shipped in any
139
- // scaffold variant — W6A spec-lock Decision 5), this toggle short-circuits
140
- // the endpoint with a fixed "disabled" reply. Default `false` here — the
141
- // showcase ships with `true` because it deploys without an Anthropic key,
142
- // but a fresh scaffold has `aiAssistant: false` and the user only enables
143
- // the chat once they're wiring up a real `ANTHROPIC_API_KEY`. Defaulting
144
- // demo mode off avoids silently disabling chat for them.
145
- lines.push(` aiChatDemoMode: false as boolean,`);
146
- lines.push(` aiChatAllowedOrigins: [] as string[],`);
147
- lines.push(` aiChatGlobalDailyLimit: false as number | false,`);
148
- if (choices.features.includes("docHistory")) {
149
- lines.push(` docHistory: true,`);
150
- }
151
- else {
152
- lines.push(` docHistory: false,`);
153
- }
154
- // Package-owned route injection is on by default since the fast-follow (#2372).
155
- // Generated projects include the field explicitly so settings.ts stays in sync
156
- // with src/config/settings.ts (checked by check-fixture-settings-drift.mjs).
157
- lines.push(` packageOwnedRoutes: true,`);
158
- if (choices.features.includes("bodyFootUtil")) {
159
- lines.push(` bodyFootUtilArea: {`);
160
- lines.push(` docHistory: ${choices.features.includes("docHistory")},`);
161
- lines.push(` viewSourceLink: ${Boolean(rawGithubUrl)},`);
162
- lines.push(` } satisfies BodyFootUtilAreaConfig as BodyFootUtilAreaConfig | false,`);
163
- }
164
- else {
165
- lines.push(` bodyFootUtilArea: false as BodyFootUtilAreaConfig | false,`);
166
- }
167
- if (choices.features.includes("designTokenPanel")) {
168
- lines.push(` designTokenPanel: true as boolean,`);
169
- }
170
- else {
171
- lines.push(` designTokenPanel: false as boolean,`);
172
- }
173
- lines.push(` tocMinDepth: 2 as number,`);
174
- lines.push(` tocMaxDepth: 4 as number,`);
175
- // Heading-ID (anchor) strategy — single source of truth shared by
176
- // zfb.config.ts (markdown.features.headingIds) and the host TOC builder
177
- // (pages/lib/_extract-headings.ts). "hierarchical" emits ancestor-prefixed
178
- // anchors (foo / foo-moo / foo-moo-mew); "flat" is zfb's legacy scheme.
179
- // Default to "hierarchical": safe for greenfield (no existing deep links to
180
- // break) and the recommended scheme (upstream zfb#871).
181
- lines.push(` headingIdStrategy: "hierarchical" as "flat" | "hierarchical",`);
182
- if (choices.features.includes("sidebarResizer")) {
183
- lines.push(` sidebarResizer: true as boolean,`);
184
- }
185
- else {
186
- lines.push(` sidebarResizer: false as boolean,`);
187
- }
188
- if (choices.features.includes("sidebarToggle")) {
189
- lines.push(` sidebarToggle: true as boolean,`);
190
- }
191
- else {
192
- lines.push(` sidebarToggle: false as boolean,`);
193
- }
194
- if (choices.features.includes("imageEnlarge")) {
195
- lines.push(` imageEnlarge: true as boolean,`);
196
- }
197
- else {
198
- lines.push(` imageEnlarge: false as boolean,`);
199
- }
200
- if (choices.features.includes("dynamicPageTransition")) {
201
- lines.push(` dynamicPageTransition: true as boolean,`);
202
- }
203
- else {
204
- lines.push(` dynamicPageTransition: false as boolean,`);
205
- }
206
- lines.push(` htmlPreview: undefined as HtmlPreviewConfig | undefined,`);
207
- if (choices.features.includes("versioning")) {
208
- lines.push(` versions: [] satisfies VersionConfig[] as VersionConfig[] | false,`);
209
- }
210
- else {
211
- lines.push(` versions: false as VersionConfig[] | false,`);
212
- }
213
- if (choices.features.includes("claudeResources")) {
214
- lines.push(` claudeResources: {`);
215
- lines.push(` claudeDir: ".claude",`);
216
- lines.push(` } as { claudeDir: string; projectRoot?: string; scanRoot?: string } | false,`);
217
- }
218
- else {
219
- lines.push(` claudeResources: false as { claudeDir: string; projectRoot?: string; scanRoot?: string } | false,`);
220
- }
221
- if (choices.features.includes("claudeResources")) {
222
- lines.push(` defaultLocaleOnlyPrefixes: [`);
223
- lines.push(` "/docs/claude-md/",`);
224
- lines.push(` "/docs/claude-skills/",`);
225
- lines.push(` "/docs/claude-agents/",`);
226
- lines.push(` "/docs/claude-commands/",`);
227
- lines.push(` ] as string[],`);
228
- }
229
- else {
230
- lines.push(` defaultLocaleOnlyPrefixes: [] as string[],`);
231
- }
232
- if (choices.features.includes("footerNavGroup") ||
233
- choices.features.includes("footerCopyright") ||
234
- choices.features.includes("footerTaglist")) {
235
- lines.push(` footer: {`);
236
- if (choices.features.includes("footerNavGroup")) {
237
- lines.push(` links: [`);
238
- lines.push(` {`);
239
- lines.push(` title: "Docs",`);
240
- lines.push(` items: [`);
241
- lines.push(` { label: "Getting Started", href: "/docs/getting-started" },`);
242
- lines.push(` ],`);
243
- lines.push(` },`);
244
- lines.push(` ],`);
245
- }
246
- else {
247
- lines.push(` links: [],`);
248
- }
249
- if (choices.features.includes("footerCopyright")) {
250
- lines.push(` copyright: "Copyright © ${new Date().getFullYear()} Your Name. Built with zudo-doc.",`);
251
- }
252
- if (choices.features.includes("footerTaglist")) {
253
- lines.push(` taglist: {`);
254
- lines.push(` enabled: true,`);
255
- lines.push(` groupBy: "group",`);
256
- lines.push(` },`);
257
- }
258
- lines.push(` } satisfies FooterConfig as FooterConfig | false,`);
259
- }
260
- else {
261
- lines.push(` footer: false as FooterConfig | false,`);
262
- }
263
- lines.push(` headerNav: [`);
264
- lines.push(` { label: "Getting Started", path: "/docs/getting-started", categoryMatch: "getting-started" },`);
265
- // The "claude" categoryMatch is load-bearing beyond the header link: getCategoryOrder()
266
- // derives the satellite-grouping prefixes from headerNav, so without this entry
267
- // groupSatelliteNodes() never nests claude-md/claude-skills/... under the "claude"
268
- // overview node and they spread out as separate top-level cards on the index sitemap.
269
- if (choices.features.includes("claudeResources")) {
270
- lines.push(` { label: "Claude", path: "/docs/claude", categoryMatch: "claude" },`);
271
- }
272
- if (choices.features.includes("changelog")) {
273
- lines.push(` { label: "Changelog", path: "/docs/changelog", categoryMatch: "changelog" },`);
274
- }
275
- lines.push(` ] satisfies HeaderNavItem[] as HeaderNavItem[],`);
276
- lines.push(` headerRightItems: [`);
277
- if (choices.headerRightItems !== undefined) {
278
- // User-supplied override (including empty array): emit each entry verbatim,
279
- // in the chosen order. An empty array means "no header-right items" — honor it.
280
- // DEFENSIVE STRIP: drop any "design-token-panel" trigger when the
281
- // designTokenPanel feature is off — the type "design-token-panel" is absent
282
- // from HeaderRightTriggerName in a feature-off scaffold (gated via
283
- // @slot:settings-types:trigger-names), so emitting it would cause a TS error
284
- // (zudolab/zudo-doc#2162).
285
- for (const item of choices.headerRightItems) {
286
- if (item.type === "trigger" &&
287
- item.trigger === "design-token-panel" &&
288
- !choices.features.includes("designTokenPanel")) {
289
- continue;
290
- }
291
- if (item.type === "trigger") {
292
- lines.push(` { type: "trigger", trigger: ${JSON.stringify(item.trigger)} },`);
293
- }
294
- else {
295
- lines.push(` { type: "component", component: ${JSON.stringify(item.component)} },`);
296
- }
297
- }
298
- }
299
- else {
300
- // Default fallback: hardcoded order, gated on selected features.
301
- if (choices.features.includes("designTokenPanel")) {
302
- lines.push(` { type: "trigger", trigger: "design-token-panel" },`);
303
- }
304
- if (choices.features.includes("versioning")) {
305
- lines.push(` { type: "component", component: "version-switcher" },`);
306
- }
307
- lines.push(` { type: "component", component: "github-link" },`);
308
- lines.push(` { type: "component", component: "theme-toggle" },`);
309
- if (choices.features.includes("search")) {
310
- lines.push(` { type: "component", component: "search" },`);
311
- }
312
- if (choices.features.includes("i18n")) {
313
- lines.push(` { type: "component", component: "language-switcher" },`);
314
- }
315
- }
316
- lines.push(` ] satisfies HeaderRightItem[] as HeaderRightItem[],`);
317
- lines.push(`};`);
318
- return lines.join("\n") + "\n";
319
- }
@@ -1,5 +0,0 @@
1
- {
2
- "rules": {
3
- "element-permitted-content": "error"
4
- }
5
- }
@@ -1 +0,0 @@
1
- {}
@@ -1,160 +0,0 @@
1
- // pages/_data.ts — zfb-compatible data helpers for doc page modules.
2
- //
3
- // Provides the bridge between zfb's CollectionEntry (from "zfb/content") and
4
- // the utility functions in @/utils/docs that expect DocsEntry (which carries
5
- // an `id` field mirroring Astro's collection entry id).
6
- //
7
- // Sync convention (ADR-004):
8
- // getCollection() resolves from the pre-loaded ContentSnapshot without an
9
- // async boundary. paths() exports call getDocs() without await. The Promise
10
- // wrapper on the type is a v0 artefact — the synchronous snapshot path is
11
- // the production contract.
12
-
13
- import { getCollection } from "zfb/content";
14
- import type { CollectionEntry } from "zfb/content";
15
- import type { DocsData } from "@/config/docs-schema";
16
- import type { DocsEntry } from "@/types/docs-entry";
17
- import type { DocPageEntry } from "./lib/doc-page-props";
18
- import { toRouteSlug } from "@/utils/slug";
19
-
20
- // ---------------------------------------------------------------------------
21
- // Types
22
- // ---------------------------------------------------------------------------
23
-
24
- /**
25
- * Frontmatter shape shared by all docs collections (EN, locale, versioned).
26
- *
27
- * Re-exported alias for `DocsData` (the `z.infer`-derived type from
28
- * `src/config/docs-schema.ts`) so call sites that import `ZfbDocsData` from
29
- * `pages/_data` continue to work without changes.
30
- *
31
- * The `[key: string]: unknown` index signature comes from `.passthrough()` on
32
- * the zod schema — custom frontmatter keys remain accessible downstream (e.g.
33
- * for frontmatter-preview) without extra casting.
34
- */
35
- export type ZfbDocsData = DocsData;
36
-
37
- /**
38
- * zfb collection entry augmented with the `id` and `collection` fields that
39
- * @/utils/docs utility functions (buildNavTree, buildBreadcrumbs, …) expect
40
- * from DocsEntry.
41
- *
42
- * `id` is bridged from `slug` — in Astro, `id` was the file-path identifier
43
- * (e.g. "getting-started/intro"); in zfb, the same role is played by `slug`.
44
- * Mapping them keeps the utility functions working without modification.
45
- */
46
- export type ZfbDocsEntry = CollectionEntry<ZfbDocsData> & {
47
- /** Bridged from `slug` for @/utils/docs compat. */
48
- id: string;
49
- /** Collection name, e.g. "docs", "docs-ja", "docs-v-1.0". */
50
- collection: string;
51
- };
52
-
53
- // ---------------------------------------------------------------------------
54
- // Loaders
55
- // ---------------------------------------------------------------------------
56
-
57
- /**
58
- * Load docs from a named collection synchronously (ADR-004 sync contract).
59
- *
60
- * `getCollection` resolves from the ContentSnapshot when called inside a
61
- * paths() evaluation. The `as unknown as` cast converts the nominal Promise
62
- * wrapper to a plain array — safe because the snapshot path is synchronous.
63
- *
64
- * The returned entries include:
65
- * - All CollectionEntry fields (slug, data, body, module_specifier, Content)
66
- * - `id` — same value as `slug`, for @/utils/docs compat
67
- * - `collection` — the collection name, for DocsEntry compat
68
- */
69
- export function getDocs(collectionName: string): ZfbDocsEntry[] {
70
- const entries = getCollection<ZfbDocsData>(collectionName);
71
- return entries.map((e) => ({
72
- ...e,
73
- // Astro-compat: strip a trailing `/index` from the entry id so
74
- // `getting-started/index.mdx` → id "getting-started" (matching
75
- // Astro 5's `glob()` collection loader). Downstream nav helpers
76
- // (`buildNavTree`, `buildBreadcrumbs`, …) keyed off the stripped
77
- // form long before zfb existed; emitting the unstripped slug here
78
- // produces ambiguous-URL collisions at paths()-expansion time.
79
- id: stripIndexSuffix(e.slug),
80
- collection: collectionName,
81
- }));
82
- }
83
-
84
- // The `id` field bridged onto every entry is the canonical route slug, so it
85
- // routes through the one shared rule (`toRouteSlug` in @/utils/slug) — bare
86
- // root `index` → "" (URL /docs/), nested `x/index` → "x". Previously this was
87
- // a standalone copy of the strip logic (the lone "" dissenter of the five
88
- // index-stripping sites); consolidating it here means there is one source of
89
- // truth. See @/utils/slug for the canonical-root rationale (#1891 / #1873).
90
- function stripIndexSuffix(slug: string): string {
91
- return toRouteSlug(slug);
92
- }
93
-
94
- /**
95
- * Augment a raw zfb collection result with the Astro-style
96
- * `id`/`collection` fields that downstream `@/utils/docs` helpers
97
- * (and the `DocPageEntry` extender shape used by `[...slug].tsx`
98
- * pages) expect. Use this when a page needs a typed array more
99
- * specific than `DocsEntry` — pages that only need `DocsEntry[]`
100
- * can use [`loadDocs`] / [`getDocs`] directly.
101
- */
102
- export function bridgeEntries<T = ZfbDocsData>(
103
- entries: ReadonlyArray<CollectionEntry<T>>,
104
- collectionName: string,
105
- ): Array<CollectionEntry<T> & { id: string; collection: string }> {
106
- return entries.map((e) => ({
107
- ...e,
108
- id: stripIndexSuffix(e.slug),
109
- collection: collectionName,
110
- }));
111
- }
112
-
113
- /**
114
- * Typed bridge from a raw zfb collection result to `DocPageEntry[]`.
115
- *
116
- * This is the **single, justified** cast at the zfb/DocsEntry boundary.
117
- * `CollectionEntry<ZfbDocsData> & { id, collection }` structurally satisfies
118
- * `DocPageEntry` because:
119
- * - `id` and `collection` are added by `bridgeEntries`
120
- * - `data` (ZfbDocsData) structurally satisfies `DocsEntry.data` (all
121
- * required/optional fields are present; the index signature is wider)
122
- * - `body`, `slug`, `module_specifier`, `Content` are provided by
123
- * `CollectionEntry<ZfbDocsData>`
124
- * The plain `as DocPageEntry[]` (not `as unknown as`) is intentional — it
125
- * expresses that this is a well-understood structural subtype relationship,
126
- * not an escape from the type system. The zfb type is the source of truth;
127
- * DocsEntry/DocPageEntry are local compatibility shapes for @/utils/docs.
128
- */
129
- export function bridgeDocsEntries(
130
- entries: ReadonlyArray<CollectionEntry<ZfbDocsData>>,
131
- collectionName: string,
132
- ): DocPageEntry[] {
133
- return bridgeEntries(entries, collectionName) as DocPageEntry[];
134
- }
135
-
136
- /**
137
- * One-shot helper for paths()/render-time pages that just need a
138
- * `DocsEntry[]` for `@/utils/docs` consumption — wraps `getDocs` so
139
- * call sites stay one-line. Use this from any page that previously did
140
- * `getCollection("docs") as unknown as DocsEntry[]` — that idiom
141
- * silently dropped the `id`/`collection` fields the utility helpers
142
- * read, which threw `Cannot read properties of undefined` at runtime.
143
- *
144
- * `ZfbDocsEntry` structurally satisfies `DocsEntry`: it carries `id`,
145
- * `collection`, `data` (ZfbDocsData satisfies DocsEntry.data field-for-
146
- * field), `body`, plus the zfb-specific extras (`slug`, `Content`, etc.)
147
- * that DocsEntry does not require.
148
- */
149
- export function loadDocs(collectionName: string): DocsEntry[] {
150
- return getDocs(collectionName);
151
- }
152
-
153
- /**
154
- * Filter out draft entries.
155
- * Drafts are always excluded in static-build paths() context.
156
- */
157
- export function filterDrafts(entries: ZfbDocsEntry[]): ZfbDocsEntry[] {
158
- return entries.filter((e) => !e.data.draft);
159
- }
160
-