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,265 +1,60 @@
1
1
  /**
2
2
  * Programmatically generate zfb.config.ts from user choices.
3
3
  *
4
- * W7A (#1736): emits zfb plugins in the host's INLINE-OBJECT shape
5
- * `{ name: "./plugins/<plugin>.mjs", options: {...} }` — not the
6
- * pre-cutover factory-import pattern (`import { fooPlugin } from
7
- * "./src/integrations/foo"`). Inline functions are not supported by zfb's
8
- * plugin runtime (see `@takazudo/zfb/plugins` source); plugins MUST be
9
- * authored as standalone `.mjs` modules referenced from `zfb.config.ts`
10
- * by `name`. The plugin source files are shipped by the base/feature
11
- * templates under `plugins/<plugin>.mjs` and `templates/features/<feature>/
12
- * files/plugins/<plugin>.mjs`.
4
+ * S5b (#2329): collapsed to the thin preset-based shape that mirrors the
5
+ * showcase `zfb.config.ts` after S5a. All collection wiring, plugin
6
+ * descriptors, markdown features, codeHighlight, resolveMarkdownLinks, and
7
+ * trailingSlash are now owned by `zudoDocPreset()` in
8
+ * `@takazudo/zudo-doc/preset`. The generated config spreads the preset
9
+ * result into `defineConfig` and keeps only the project-owned shell fields
10
+ * (`framework`, `port`, `tailwind`, `base`).
13
11
  *
14
12
  * Replaces the former astro-config-gen.ts + content-config-gen.ts pair.
15
13
  * In the zfb world, content-collection schemas live inside zfb.config.ts
16
14
  * itself — there is no separate content.config.ts.
15
+ *
16
+ * `_choices` is intentionally unused: post-S5b the emitted config is a
17
+ * CONSTANT. All feature variation is driven by `settings.*` (read at
18
+ * zfb-load time inside `zudoDocPreset()`), so the generated file is byte
19
+ * identical for every feature combination. The parameter is retained only
20
+ * for call-site compatibility (`scaffold.ts` passes `choices`). Do NOT add
21
+ * feature-gated branches here — wire new feature behaviour into
22
+ * `packages/zudo-doc/src/preset.ts` and the project's `settings.ts` instead.
17
23
  */
18
- export function generateZfbConfig(choices) {
19
- const hasDocHistory = choices.features.includes("docHistory");
20
- const hasLlmsTxt = choices.features.includes("llmsTxt");
21
- const hasClaudeResources = choices.features.includes("claudeResources");
24
+ export function generateZfbConfig(_choices) {
22
25
  const lines = [];
23
26
  // --- Imports ---
24
- lines.push(`import { z } from "zod";`);
25
27
  lines.push(`import { defineConfig } from "zfb/config";`);
28
+ lines.push(`import { zudoDocPreset } from "@takazudo/zudo-doc/preset";`);
26
29
  lines.push(`import { settings } from "./src/config/settings";`);
27
30
  lines.push(`import { buildDocsSchema } from "./src/config/docs-schema";`);
28
31
  lines.push(``);
29
- // --- Schema definition — delegated to the single source of truth ---
30
- // buildDocsSchema() lives in src/config/docs-schema.ts and is shared by
31
- // pages/_data.ts (ZfbDocsData alias) and src/types/docs-entry.ts (DocsData).
32
- // tagGovernance projects: docs-schema.ts reads settings + tagVocabulary
33
- // internally, so the generated zfb.config.ts needs no extra import or
34
- // inline buildTagsSchema — the schema builder encapsulates all of that.
35
- lines.push(`const docsSchema = buildDocsSchema();`);
36
- lines.push(``);
37
- lines.push(`const docsSchemaJson = z.toJSONSchema(docsSchema) as Record<string, unknown>;`);
38
- lines.push(``);
39
- // --- Collection type ---
40
- lines.push(`interface CollectionEntryShape {`);
41
- lines.push(` name: string;`);
42
- lines.push(` path: string;`);
43
- lines.push(` schema: Record<string, unknown>;`);
44
- lines.push(`}`);
45
- lines.push(``);
46
- // --- Collections array ---
47
- lines.push(`const collections: CollectionEntryShape[] = [];`);
48
- lines.push(``);
49
- lines.push(`collections.push({ name: "docs", path: settings.docsDir, schema: docsSchemaJson });`);
50
- lines.push(``);
51
- // Locale collections — empty loop when locales is {} (i18n disabled).
52
- lines.push(`for (const [code, config] of Object.entries(settings.locales)) {`);
53
- lines.push(` collections.push({ name: \`docs-\${code}\`, path: config.dir, schema: docsSchemaJson });`);
54
- lines.push(`}`);
55
- lines.push(``);
56
- // Version collections — outer `if` short-circuits when versions is false.
57
- lines.push(`if (settings.versions) {`);
58
- lines.push(` for (const version of settings.versions) {`);
59
- lines.push(` collections.push({`);
60
- lines.push(` name: \`docs-v-\${version.slug}\`,`);
61
- lines.push(` path: version.docsDir,`);
62
- lines.push(` schema: docsSchemaJson,`);
63
- lines.push(` });`);
64
- lines.push(` if (version.locales) {`);
65
- lines.push(` for (const [code, config] of Object.entries(version.locales)) {`);
66
- lines.push(` collections.push({`);
67
- lines.push(` name: \`docs-v-\${version.slug}-\${code}\`,`);
68
- lines.push(` path: config.dir,`);
69
- lines.push(` schema: docsSchemaJson,`);
70
- lines.push(` });`);
71
- lines.push(` }`);
72
- lines.push(` }`);
73
- lines.push(` }`);
74
- lines.push(`}`);
75
- lines.push(``);
76
- // --- Locale helpers used by integrationPlugins (always emitted because
77
- // search-index + copy-public are always-on; locale-shaped data is
78
- // consumed by search-index even when there's only the default locale).
79
- lines.push(`const localeArray = Object.entries(settings.locales).map(([code, locale]) => ({`);
80
- lines.push(` code,`);
81
- lines.push(` dir: locale.dir,`);
82
- lines.push(`}));`);
83
- lines.push(`const localeRecord = Object.fromEntries(`);
84
- lines.push(` Object.entries(settings.locales).map(([code, locale]) => [code, { dir: locale.dir }]),`);
85
- lines.push(`);`);
86
- lines.push(``);
87
- // --- Plugins — inline-object shape matches host. Each entry's `name`
88
- // is a relative path to a `.mjs` plugin module shipped by the
89
- // base/feature templates. zfb's plugin runtime resolves the module
90
- // and dispatches lifecycle hooks (preBuild / postBuild / devMiddleware)
91
- // on its default export. ---
92
- lines.push(`const integrationPlugins = [`);
93
- if (hasClaudeResources) {
94
- lines.push(` ...(settings.claudeResources`);
95
- lines.push(` ? [`);
96
- lines.push(` {`);
97
- lines.push(` name: "./plugins/claude-resources-plugin.mjs",`);
98
- lines.push(` options: {`);
99
- lines.push(` claudeDir: settings.claudeResources.claudeDir,`);
100
- lines.push(` projectRoot: settings.claudeResources.projectRoot,`);
101
- lines.push(` docsDir: settings.docsDir,`);
102
- lines.push(` },`);
103
- lines.push(` },`);
104
- lines.push(` ]`);
105
- lines.push(` : []),`);
106
- }
107
- if (hasDocHistory) {
108
- lines.push(` ...(settings.docHistory`);
109
- lines.push(` ? [`);
110
- lines.push(` {`);
111
- lines.push(` name: "./plugins/doc-history-plugin.mjs",`);
112
- lines.push(` options: {`);
113
- lines.push(` docsDir: settings.docsDir,`);
114
- lines.push(` locales: localeRecord,`);
115
- lines.push(` base: settings.base,`);
116
- lines.push(` },`);
117
- lines.push(` },`);
118
- lines.push(` ]`);
119
- lines.push(` : []),`);
120
- }
121
- // search-index is always-on (matches host) — emits dist/search-index.json
122
- // even when no <Search /> widget mounts; ~few-KB cost is acceptable and
123
- // keeps the dev-middleware route registered for the always-mounted
124
- // search widget in pages/lib/_header-with-defaults.tsx.
125
- lines.push(` {`);
126
- lines.push(` name: "./plugins/search-index-plugin.mjs",`);
127
- lines.push(` options: {`);
128
- lines.push(` docsDir: settings.docsDir,`);
129
- lines.push(` locales: localeRecord,`);
130
- lines.push(` base: settings.base,`);
131
- lines.push(` },`);
132
- lines.push(` },`);
133
- if (hasLlmsTxt) {
134
- lines.push(` ...(settings.llmsTxt`);
135
- lines.push(` ? [`);
136
- lines.push(` {`);
137
- lines.push(` name: "./plugins/llms-txt-plugin.mjs",`);
138
- lines.push(` options: {`);
139
- lines.push(` siteName: settings.siteName,`);
140
- lines.push(` siteDescription: settings.siteDescription,`);
141
- lines.push(` base: settings.base,`);
142
- lines.push(` siteUrl: settings.siteUrl,`);
143
- lines.push(` defaultLocaleDir: settings.docsDir,`);
144
- lines.push(` locales: localeArray,`);
145
- lines.push(` },`);
146
- lines.push(` },`);
147
- lines.push(` ]`);
148
- lines.push(` : []),`);
149
- }
150
- // copy-public is always-on (matches host) — workaround for upstream zfb
151
- // gap where `zfb build` does not copy `public/` to dist/. Empty/missing
152
- // public/ is a no-op, so the cost to projects without public/ is zero.
153
- lines.push(` {`);
154
- lines.push(` name: "./plugins/copy-public-plugin.mjs",`);
155
- lines.push(` options: {`);
156
- lines.push(` publicDir: "public",`);
157
- lines.push(` },`);
158
- lines.push(` },`);
159
- lines.push(`];`);
32
+ // --- Directive vocabulary ---
33
+ // The seven canonical directives registered in pages/_mdx-components.ts.
34
+ // "details" routes to DetailsWrapper a collapsible, NOT an admonition.
35
+ lines.push(`const directiveVocabulary = {`);
36
+ lines.push(` note: "Note",`);
37
+ lines.push(` tip: "Tip",`);
38
+ lines.push(` info: "Info",`);
39
+ lines.push(` warning: "Warning",`);
40
+ lines.push(` danger: "Danger",`);
41
+ lines.push(` caution: "Caution",`);
42
+ lines.push(` details: "Details",`);
43
+ lines.push(`};`);
160
44
  lines.push(``);
161
45
  // --- Export ---
162
46
  lines.push(`export default defineConfig({`);
47
+ lines.push(` // ── Host-owned shell fields ──────────────────────────────────────────────`);
163
48
  lines.push(` framework: "preact",`);
164
49
  lines.push(` // Pin the dev/preview port — zfb defaults to 3000, but the generated`);
165
50
  lines.push(` // CLAUDE.md and the Tauri dev wrappers assume 4321.`);
166
51
  lines.push(` port: 4321,`);
167
52
  lines.push(` tailwind: { enabled: true },`);
168
- lines.push(` collections,`);
169
- lines.push(` stripMdExt: true,`);
170
- lines.push(` resolveMarkdownLinks: {`);
171
- lines.push(` enabled: true,`);
172
- lines.push(` dirs: [`);
173
- lines.push(` { dir: settings.docsDir, routePrefix: "/docs/" },`);
174
- lines.push(` ...Object.entries(settings.locales).map(([code, locale]) => ({`);
175
- lines.push(` dir: locale.dir,`);
176
- lines.push(` routePrefix: \`/\${code}/docs/\`,`);
177
- lines.push(` })),`);
178
- lines.push(` // Versioned collections: each version's EN dir + per-locale dirs.`);
179
- lines.push(` ...(settings.versions`);
180
- lines.push(` ? settings.versions.flatMap((version) => [`);
181
- lines.push(` { dir: version.docsDir, routePrefix: \`/v/\${version.slug}/docs/\` },`);
182
- lines.push(` ...Object.entries(version.locales ?? {}).map(([code, locale]) => ({`);
183
- lines.push(` dir: locale.dir,`);
184
- lines.push(` routePrefix: \`/v/\${version.slug}/\${code}/docs/\`,`);
185
- lines.push(` })),`);
186
- lines.push(` ])`);
187
- lines.push(` : []),`);
188
- lines.push(` ],`);
189
- lines.push(` onBrokenLinks: "warn",`);
190
- lines.push(` },`);
53
+ lines.push(` // Public URL prefix for <link rel="stylesheet"> and <script> tags.`);
191
54
  lines.push(` base: settings.base,`);
192
- lines.push(` trailingSlash: settings.trailingSlash,`);
193
- // codeHighlight — dual-theme syntect output so CSS can resolve tokens via
194
- // light-dark(var(--shiki-light), var(--shiki-dark)) and code blocks follow
195
- // the active light/dark toggle with no client JS. Theme names are syntect
196
- // built-ins shipped with zfb (not Shiki names). Matches the host config.
197
- lines.push(` codeHighlight: {`);
198
- lines.push(` themeLight: "base16-ocean.light",`);
199
- lines.push(` themeDark: "base16-ocean.dark",`);
200
- lines.push(` },`);
201
- // markdown.features block — mirrors the zfb next.13 opt-in model.
202
- //
203
- // Value-shape rule (empirically verified against the next.13 Rust loader):
204
- // object-typed features (githubAutolinks, codeEnrichment, imageDimensions,
205
- // linkValidation) REJECT the `true` shorthand and must be given an options
206
- // object (`{}` or fields). Boolean-OR-object features (githubAlerts,
207
- // readingTime, codeTabs, mermaid, headingMarkerToc) accept `true`.
208
- // (directives is object-typed — it takes a `name → component` map, not `true`.)
209
- // Note: imageEnlarge was formerly a Boolean-OR-object feature here, but
210
- // next.18 hard-removed it from the Rust config schema — it is now
211
- // re-implemented in userland via an MDX p-override in pages/_mdx-components.ts
212
- // (gated on settings.imageEnlarge). Do NOT add imageEnlarge back here.
213
- //
214
- // Intentionally omitted features (known-blocked at zfb next.13):
215
- // - tocExport: injects indented `export const toc = [...]` that MDX
216
- // parses as content, breaking esbuild with "Expected }" across the
217
- // whole corpus. Re-enable when the upstream Rust pass emits the
218
- // export at column 0. (Filed as zudolab/zudo-doc#1814.)
219
- // - ruby: the `^{...}` annotation syntax 500s the SSR render at next.13.
220
- // A registered stub cannot fix it — the error is inside zfb's Rust
221
- // ruby pass. Re-enable when the upstream crate is fixed. (#1815.)
222
- // - transclude: `:::include{file="..."}` 500s SSR (no registered
223
- // <include> renderer); `![[...]]` wikilink form is a no-op.
224
- // Re-enable when a transclude renderer is wired.
225
- //
226
- // githubAutolinks is omitted intentionally: the showcase hardcodes
227
- // `repo: "zudolab/zudo-doc"` but a scaffolded project belongs to a
228
- // different repo. Users can add `githubAutolinks: { repo: "owner/repo" }`
229
- // to their zfb.config.ts after scaffolding.
230
- lines.push(` markdown: {`);
231
- lines.push(` features: {`);
232
- lines.push(` // Former-Core features (were always-on before zfb next.12).`);
233
- lines.push(` // imageEnlarge was a former-Core feature but was hard-removed in zfb`);
234
- lines.push(` // next.18 — it is now re-implemented via an MDX p-override.`);
235
- lines.push(` // Admonitions recipe: register the :::name directive vocabulary`);
236
- lines.push(` // (note/tip/info/warning/danger/caution/details) → components.`);
237
- lines.push(` directives: {`);
238
- lines.push(` note: "Note",`);
239
- lines.push(` tip: "Tip",`);
240
- lines.push(` info: "Info",`);
241
- lines.push(` warning: "Warning",`);
242
- lines.push(` danger: "Danger",`);
243
- lines.push(` caution: "Caution",`);
244
- lines.push(` details: "Details", // collapsible — routes to DetailsWrapper`);
245
- lines.push(` },`);
246
- lines.push(` mermaid: true,`);
247
- lines.push(` headingMarkerToc: true,`);
248
- lines.push(` // Safe opt-in features.`);
249
- lines.push(` githubAlerts: true,`);
250
- lines.push(` readingTime: true,`);
251
- lines.push(` codeEnrichment: {},`);
252
- lines.push(` codeTabs: true,`);
253
- lines.push(` imageDimensions: {},`);
254
- lines.push(` // warn-only link validation — failOnBroken: false never fails the build.`);
255
- lines.push(` linkValidation: { failOnBroken: false },`);
256
- lines.push(` // Heading-ID (anchor) strategy — single source of truth in`);
257
- lines.push(` // settings.headingIdStrategy, also mirrored by the host TOC builder`);
258
- lines.push(` // (pages/lib/_extract-headings.ts) so TOC anchors match rendered ids.`);
259
- lines.push(` headingIds: { strategy: settings.headingIdStrategy },`);
260
- lines.push(` },`);
261
- lines.push(` },`);
262
- lines.push(` plugins: integrationPlugins,`);
55
+ lines.push(``);
56
+ lines.push(` // ── Preset-owned fields (content collections, plugins, markdown, …) ────────`);
57
+ lines.push(` ...zudoDocPreset({ settings, buildDocsSchema, directiveVocabulary }),`);
263
58
  lines.push(`});`);
264
59
  return lines.join("\n") + "\n";
265
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zudo-doc",
3
- "version": "0.2.21",
3
+ "version": "1.0.0",
4
4
  "description": "Create a new zudo-doc documentation site",
5
5
  "license": "MIT",
6
6
  "author": "Takeshi Takatsudo",
@@ -1,117 +1,52 @@
1
- // Shared MDX-component bag used by every doc-route page that renders
2
- // `<entry.Content components={...} />`.
1
+ // Project MDX-components assembly thin wrapper over the package factory.
3
2
  //
4
- // ## Why a shared helper
3
+ // The bulk of the components bag (htmlOverrides, admonitions, CodeGroup,
4
+ // Tabs/TabItem, MathBlock, the img base-rewrite, the enlargeable-`p` override)
5
+ // now ships from `@takazudo/zudo-doc/mdx-components` (package-first migration,
6
+ // epic #2321, S8 #2332). This file keeps ONLY what is genuinely project-bound:
5
7
  //
6
- // Pre-S4e the page-side `components` map only carried `htmlOverrides` plus
7
- // `HtmlPreview`, because the zfb content bridge wasn't installed and every
8
- // `<entry.Content>` call took the raw-markdown `<pre data-zfb-content-fallback>`
9
- // path. Now that the bridge IS installed (zudo-doc#506), the compiled
10
- // MDXContent functions fire for every entry and the MDX emitter wraps
11
- // every named-tag access with:
12
- //
13
- // const CategoryNav2 = _components.CategoryNav ?? components.CategoryNav;
14
- // if (!CategoryNav2) throw new Error("MDX requires `CategoryNav` to be passed via the `components` prop");
15
- //
16
- // So any tag the MDX corpus uses but the page omits → 500 at render time.
17
- //
18
- // ## Strategy
19
- //
20
- // This module ships stub bindings for tags not yet ported to `@takazudo/zudo-doc`
21
- // (render nothing), and real Preact bindings for tags whose ports are complete.
22
- // As real components land, they replace their stub here and propagate to every page automatically.
23
- //
24
- // `htmlOverrides` (basic typography — h2/h3/h4/p/a/ul/ol/blockquote/strong/table)
25
- // and `HtmlPreview: HtmlPreviewWrapper` (Island wrapper) stay in their
26
- // non-stub form because their Preact bindings already exist.
27
- //
28
- // ## Locale-aware bindings (createMdxComponents factory)
29
- //
30
- // CategoryNav, CategoryTreeNav, SiteTreeNav, and SiteTreeNavDemo resolve nav
31
- // tree data at render time. Since the same MDX content is rendered for both
32
- // default-locale and non-default-locale pages, these components need to know
33
- // which locale to use when building the nav tree.
34
- //
35
- // The `createMdxComponents(lang)` factory returns a components map with
36
- // locale-bound wrappers for these nav components. Page modules should call it
37
- // with the active locale instead of using the static `mdxComponents` export.
38
- // The static export still exists for backward compatibility (using defaultLocale).
8
+ // - the 4 locale-bound nav wrappers (CategoryNav / CategoryTreeNav /
9
+ // SiteTreeNav / SiteTreeNavDemo) they depend on the project's content
10
+ // collections + nav-tree utilities, so they cannot live in the package.
11
+ // They are passed into the factory as `navData`; the factory injects the
12
+ // active `locale` so `/ja` resolves the JA collection (the load-bearing
13
+ // locale thread — a static global mdx-components slot would render the EN
14
+ // tree on every `/ja` page, which is why this stays a per-call factory).
15
+ // - project `extras` spread last: HtmlPreview (with the host's global
16
+ // config), Details, the Island SSR pass-through, the PresetGenerator SSR
17
+ // shell, and the pure-showcase stubs (Avatar/Button/Card/MyComponent/
18
+ // PageLayout) that appear only as illustrative MDX prose.
39
19
 
40
20
  import type { ComponentChildren } from "preact";
41
- // @slot:mdx-components:enlarge-imports
42
- import { htmlOverrides } from "@takazudo/zudo-doc/content";
21
+ import { createMdxComponents as createMdxComponentsBase } from "@takazudo/zudo-doc/mdx-components";
43
22
  import { HtmlPreviewWrapper, type HtmlPreviewWrapperProps } from "@takazudo/zudo-doc/html-preview-wrapper";
44
- import { Tabs } from "@takazudo/zudo-doc/code-syntax";
45
- import { TabItem } from "@takazudo/zudo-doc/tab-item";
46
23
  import { defaultLocale, type Locale } from "@/config/i18n";
47
24
  import { settings } from "@/config/settings";
48
- import { withBase } from "@/utils/base";
49
25
  import { CategoryNavWrapper } from "./lib/_category-nav";
50
26
  import { CategoryTreeNavWrapper } from "./lib/_category-tree-nav";
51
27
  import { SiteTreeNavWrapper } from "./lib/_site-tree-nav";
52
28
  import { DetailsWrapper } from "./lib/_details";
53
29
  import { PresetGeneratorFallback } from "./lib/_preset-generator";
54
- import { MathBlock } from "./lib/_math-block";
55
- import { CodeGroup } from "@/components/content/code-group";
56
- import { makeAdmonition } from "@/components/content/content-admonition";
57
30
 
58
- /**
59
- * MDX `<img>` override rewrites root-relative src attributes to include the
60
- * configured site base path (settings.base). Without this, an MDX image like
61
- * `![alt](/img/foo.webp)` emits `src="/img/foo.webp"` which 404s when the
62
- * site is deployed under a sub-path prefix (e.g. /my-docs/).
63
- *
64
- * Only root-relative paths (starting with "/") are rewritten; external URLs,
65
- * protocol-relative URLs ("//…"), and data URIs pass through unchanged. The
66
- * withBase() call is generic — it reads settings.base at build time and applies
67
- * whatever prefix is configured.
68
- *
69
- * Note: `srcset` attributes are NOT rewritten here because the current MDX
70
- * corpus does not use srcset (standard markdown `![alt](src)` syntax produces
71
- * only `src`). If srcset with root-relative URLs is ever introduced, extend
72
- * this override to rewrite each srcset candidate URL as well.
73
- */
74
- function ContentImg(props: Record<string, unknown>) {
75
- const src = props.src;
76
- const rewrittenSrc =
77
- typeof src === "string" && src.startsWith("/") && !src.startsWith("//")
78
- ? withBase(src)
79
- : src;
80
- // Strip the "no-enlarge" sentinel from the rendered DOM — it is read by the
81
- // p-override before ContentImg is called (the VNode is still unlaunched at
82
- // that point), so we must delete it here to avoid leaking the sentinel into
83
- // the img title attribute.
84
- const { title, ...restProps } = props;
85
- const finalTitle = title === "no-enlarge" ? undefined : title;
86
- const mergedProps: Record<string, unknown> = { ...restProps, src: rewrittenSrc };
87
- if (finalTitle !== undefined) {
88
- mergedProps.title = finalTitle;
89
- }
90
- return { type: "img", props: mergedProps, key: null, constructor: undefined };
91
- }
31
+ const HtmlPreviewWithGlobalConfig = (props: HtmlPreviewWrapperProps) =>
32
+ HtmlPreviewWrapper({ globalConfig: settings.htmlPreview ?? null, ...props });
92
33
 
93
34
  /**
94
- * MDX-tag stub: renders nothing. Returning `null` keeps the rendered
95
- * tree intact (Preact's null-vnode path) without leaking placeholder
96
- * markup into the SSR output.
35
+ * MDX-tag stub: renders nothing. Returning `null` keeps the rendered tree
36
+ * intact (Preact's null-vnode path) without leaking placeholder markup into
37
+ * the SSR output.
97
38
  */
98
39
  const MdxStub = (_props: unknown) => null;
99
40
 
100
41
  /**
101
42
  * SSR-pass-through wrapper for `<Island when="load|idle|visible">`.
102
43
  *
103
- * In the zfb build the zfb `<Island>` component is unavailable, so the
104
- * MDX corpus tags resolve to this binding instead. Rendering the
105
- * children directly ensures that any server-renderable content nested
106
- * inside `<Island>` (headings, paragraphs, etc.) appears in the SSR
107
- * HTML. Client-only inner components that are themselves wrapped in an
108
- * SSR-skip placeholder will emit their own placeholder markup; this
109
- * wrapper does not suppress them.
110
- *
111
- * The `when` prop is intentionally ignored at render time — it is only
112
- * meaningful to the zfb hydration runtime on the client, which reads
113
- * the `data-when` attribute on the inner SSR-skip placeholder div (if
114
- * present) rather than on this wrapper.
44
+ * In the zfb build the zfb `<Island>` component is unavailable here, so the
45
+ * MDX corpus tag resolves to this binding instead. Rendering the children
46
+ * directly ensures server-renderable content nested inside `<Island>`
47
+ * (headings, paragraphs, etc.) appears in the SSR HTML. The `when` prop is
48
+ * ignored at render time it is only meaningful to the zfb hydration runtime
49
+ * on the client, which reads `data-when` on the inner SSR-skip placeholder.
115
50
  */
116
51
  function IslandWrapper(props: {
117
52
  when?: "load" | "idle" | "visible" | "media";
@@ -120,104 +55,48 @@ function IslandWrapper(props: {
120
55
  return props.children ?? null;
121
56
  }
122
57
 
123
- // @slot:mdx-components:enlarge-defs
124
- const HtmlPreviewWithGlobalConfig = (props: HtmlPreviewWrapperProps) =>
125
- HtmlPreviewWrapper({ globalConfig: settings.htmlPreview ?? null, ...props });
126
-
127
58
  /**
128
59
  * Build a locale-aware MDX components map for the given locale.
129
60
  *
130
- * Nav components (CategoryNav, CategoryTreeNav, SiteTreeNav, SiteTreeNavDemo)
131
- * resolve nav tree data at render time and need the active locale so they
132
- * query the right collection. The factory closes over `lang` and returns
133
- * locale-bound wrapper functions.
134
- *
135
- * Page modules should call createMdxComponents(locale) instead of importing
136
- * the static mdxComponents export.
61
+ * Delegates the package-resident components to the `@takazudo/zudo-doc`
62
+ * factory and supplies the project-bound pieces:
63
+ * - `navData`: the 4 locale-aware nav wrappers (the factory injects `lang`).
64
+ * - `extras`: HtmlPreview (host-configured), Details, Island pass-through,
65
+ * PresetGenerator SSR shell, and the showcase stubs.
137
66
  *
138
- * Components map includes:
139
- * - `htmlOverrides`element-level overrides for native tags (h2..h4,
140
- * p, a, ul/ol, blockquote, strong, table). Defined in
141
- * `@takazudo/zudo-doc/content`.
142
- * - `HtmlPreview` — Island-wrapped preview component.
143
- * - Real Preact wrappers for CategoryNav, CategoryTreeNav, SiteTreeNav,
144
- * SiteTreeNavDemo, and Details.
145
- * - `Island` — SSR pass-through wrapper so children render server-side.
146
- * - `PresetGenerator` — SSR fallback shell that renders the 8 h3 sections;
147
- * interactive form hydrates client-side via SSR-skip placeholder.
148
- * - Stub bindings for every other custom tag the MDX corpus references.
149
- *
150
- * Keep this list in sync with the corpus when new MDX tags appear.
151
- * `pnpm exec grep -rohE '<[A-Z][a-zA-Z]+' src/content/` enumerates them.
67
+ * Page modules should call createMdxComponents(locale) — not the static
68
+ * mdxComponents exportso each render gets the locale-correct map.
152
69
  */
153
70
  export function createMdxComponents(lang: Locale | string = defaultLocale) {
154
- // Locale-bound wrappers — close over `lang` so each wrapper queries
155
- // the correct collection without needing a prop.
156
- const CategoryNavBound = (props: Record<string, unknown>) =>
157
- CategoryNavWrapper({ ...(props as Parameters<typeof CategoryNavWrapper>[0]), lang });
158
- const CategoryTreeNavBound = (props: Record<string, unknown>) =>
159
- CategoryTreeNavWrapper({ ...(props as Parameters<typeof CategoryTreeNavWrapper>[0]), lang });
160
- const SiteTreeNavBound = (props: Record<string, unknown>) =>
161
- SiteTreeNavWrapper({ ...(props as Parameters<typeof SiteTreeNavWrapper>[0]), lang });
162
-
163
- return {
164
- ...htmlOverrides,
165
- // img override: rewrites root-relative src to include the site base path.
166
- // Required when settings.base is a sub-path (e.g. /my-docs/) so that
167
- // MDX images like ![alt](/img/foo.webp) resolve correctly on the deployed
168
- // site. withBase() is generic any configured base value works.
169
- img: ContentImg,
170
- // @slot:mdx-components:enlarge-p-entry
171
- HtmlPreview: HtmlPreviewWithGlobalConfig,
172
- // Admonitions real typed Preact components (src/components/content/
173
- // content-admonition.tsx) emitting the `.admonition` / `data-admonition`
174
- // structure the design-system CSS targets. The `directives` map in
175
- // zfb.config.ts emits these tags from `:::note` directives; `<Note
176
- // title="…">` JSX form is also authored directly.
177
- Note: makeAdmonition("note"),
178
- Tip: makeAdmonition("tip"),
179
- Info: makeAdmonition("info"),
180
- Warning: makeAdmonition("warning"),
181
- Danger: makeAdmonition("danger"),
182
- // github-alerts [!IMPORTANT] and [!CAUTION] map to these variants.
183
- // Without these bindings, those two alert variants 500 the SSR render.
184
- Important: makeAdmonition("important"),
185
- Caution: makeAdmonition("caution"),
186
- // codeTabs Option A: zfb emits <CodeGroup tabs={[...]}> for :::code-group.
187
- // The framework does not ship this component; we implement it here and map
188
- // the tabs[] + <pre data-lang> children to the existing Tabs/TabItem UI.
189
- CodeGroup: CodeGroup as unknown as (props: Record<string, unknown>) => unknown,
190
- // Showcase / nav helpers — real Preact wrappers replacing MdxStub.
191
- CategoryNav: CategoryNavBound,
192
- CategoryTreeNav: CategoryTreeNavBound,
193
- SiteTreeNav: SiteTreeNavBound,
194
- SiteTreeNavDemo: SiteTreeNavBound,
195
- Details: DetailsWrapper,
196
- Tabs,
197
- TabItem,
198
- // Math rendering — KaTeX via server-side katex.renderToString().
199
- // The math-equations.mdx content files write <MathBlock> JSX directly
200
- // (instead of $$…$$) because the zfb Rust emitter does not yet support
201
- // remark-math math nodes (zudo-front-builder #93).
202
- MathBlock,
203
- SmartBreak: MdxStub,
204
- // Island: pass children through so server-renderable content nested
205
- // inside <Island> appears in SSR HTML. See IslandWrapper comment above.
206
- Island: IslandWrapper,
207
- // PresetGenerator: render the 8 section headings as static SSR HTML for
208
- // a11y/SEO section structure and no-JS layout stability. The interactive
209
- // form loads client-side via the SSR-skip placeholder inside
210
- // PresetGeneratorFallback (see pages/lib/_preset-generator.tsx).
211
- PresetGenerator: PresetGeneratorFallback,
212
- // Pure showcase placeholders (Avatar/Button/Card/MyComponent/PageLayout
213
- // appear only inside MDX prose as illustrative examples — never
214
- // implemented as real components).
215
- Avatar: MdxStub,
216
- Button: MdxStub,
217
- Card: MdxStub,
218
- MyComponent: MdxStub,
219
- PageLayout: MdxStub,
220
- };
71
+ return createMdxComponentsBase({
72
+ settings,
73
+ locale: lang,
74
+ navData: {
75
+ CategoryNav: CategoryNavWrapper as unknown as (props: Record<string, unknown>) => unknown,
76
+ CategoryTreeNav: CategoryTreeNavWrapper as unknown as (props: Record<string, unknown>) => unknown,
77
+ SiteTreeNav: SiteTreeNavWrapper as unknown as (props: Record<string, unknown>) => unknown,
78
+ },
79
+ extras: {
80
+ HtmlPreview: HtmlPreviewWithGlobalConfig,
81
+ Details: DetailsWrapper,
82
+ // SmartBreak: corpus tag with no visual rendering render nothing.
83
+ SmartBreak: MdxStub,
84
+ // Island: pass children through so server-renderable content nested
85
+ // inside <Island> appears in SSR HTML. See IslandWrapper comment above.
86
+ Island: IslandWrapper,
87
+ // PresetGenerator: SSR fallback shell that renders the 8 section headings;
88
+ // the interactive form hydrates client-side via the SSR-skip placeholder
89
+ // inside PresetGeneratorFallback (see pages/lib/_preset-generator.tsx).
90
+ PresetGenerator: PresetGeneratorFallback,
91
+ // Pure showcase placeholders appear only inside MDX prose as
92
+ // illustrative examples, never implemented as real components.
93
+ Avatar: MdxStub,
94
+ Button: MdxStub,
95
+ Card: MdxStub,
96
+ MyComponent: MdxStub,
97
+ PageLayout: MdxStub,
98
+ },
99
+ });
221
100
  }
222
101
 
223
102
  /**
@@ -27,7 +27,7 @@ import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
27
27
  import type { JSX } from "preact";
28
28
  import type { VNode } from "preact";
29
29
  import { Island } from "@takazudo/zfb";
30
- import SiteTreeNav from "@/components/site-tree-nav";
30
+ import { SiteTreeNav } from "@takazudo/zudo-doc/site-tree-nav-island";
31
31
  import { FooterWithDefaults } from "./lib/_footer-with-defaults";
32
32
  import { HeaderWithDefaults } from "./lib/_header-with-defaults";
33
33
  import { HeadWithDefaults } from "./lib/_head-with-defaults";
@@ -32,21 +32,14 @@ import type { VNode, JSX } from "preact";
32
32
  import { Island } from "@takazudo/zfb";
33
33
  import { settings } from "@/config/settings";
34
34
 
35
- import AiChatModal from "@/components/ai-chat-modal";
36
- import ImageEnlarge, { ImageEnlargeSsrFallback } from "@/components/image-enlarge";
37
- import MermaidEnlarge, { MermaidEnlargeSsrFallback } from "@/components/mermaid-enlarge";
35
+ import { AiChatModal } from "@takazudo/zudo-doc/ai-chat-modal";
36
+ import { ImageEnlarge, ImageEnlargeSsrFallback } from "@takazudo/zudo-doc/image-enlarge";
37
+ import { MermaidEnlarge, MermaidEnlargeSsrFallback } from "@takazudo/zudo-doc/mermaid-enlarge";
38
38
  // @slot:body-end-islands:imports
39
39
 
40
- // Set explicit `displayName` on each default-exported island so zfb's
41
- // `captureComponentName` produces a stable marker even after the SSR
42
- // pipeline runs the components through a function-name-rewriting layer.
43
- // The marker must match the third-arg literal that zfb's scanner records
44
- // for the same source-level identifier (zfb PR #150). esbuild preserves
45
- // function names by default, but the explicit assignment is a
46
- // belt-and-braces guard for production minification regressions.
47
- (AiChatModal as { displayName?: string }).displayName = "AiChatModal";
48
- (ImageEnlarge as { displayName?: string }).displayName = "ImageEnlarge";
49
- (MermaidEnlarge as { displayName?: string }).displayName = "MermaidEnlarge";
40
+ // AiChatModal, ImageEnlarge, MermaidEnlarge pin displayName internally in the
41
+ // package. Optional feature islands that still need explicit call-site pinning
42
+ // inject their displayName assignment at the slot below only when selected.
50
43
  // @slot:body-end-islands:display-names
51
44
 
52
45
  /**