create-zudo-doc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/bin/create-zudo-doc.js +2 -0
  4. package/dist/api.d.ts +20 -0
  5. package/dist/api.js +13 -0
  6. package/dist/claude-md-gen.d.ts +2 -0
  7. package/dist/claude-md-gen.js +113 -0
  8. package/dist/cli.d.ts +39 -0
  9. package/dist/cli.js +157 -0
  10. package/dist/compose.d.ts +95 -0
  11. package/dist/compose.js +206 -0
  12. package/dist/constants.d.ts +20 -0
  13. package/dist/constants.js +224 -0
  14. package/dist/features/body-foot-util.d.ts +10 -0
  15. package/dist/features/body-foot-util.js +12 -0
  16. package/dist/features/claude-resources.d.ts +2 -0
  17. package/dist/features/claude-resources.js +6 -0
  18. package/dist/features/design-token-panel.d.ts +14 -0
  19. package/dist/features/design-token-panel.js +27 -0
  20. package/dist/features/doc-history.d.ts +9 -0
  21. package/dist/features/doc-history.js +11 -0
  22. package/dist/features/doc-tags.d.ts +19 -0
  23. package/dist/features/doc-tags.js +33 -0
  24. package/dist/features/footer-taglist.d.ts +14 -0
  25. package/dist/features/footer-taglist.js +17 -0
  26. package/dist/features/footer.d.ts +8 -0
  27. package/dist/features/footer.js +10 -0
  28. package/dist/features/i18n.d.ts +22 -0
  29. package/dist/features/i18n.js +41 -0
  30. package/dist/features/image-enlarge.d.ts +11 -0
  31. package/dist/features/image-enlarge.js +13 -0
  32. package/dist/features/index.d.ts +15 -0
  33. package/dist/features/index.js +53 -0
  34. package/dist/features/llms-txt.d.ts +11 -0
  35. package/dist/features/llms-txt.js +13 -0
  36. package/dist/features/search.d.ts +9 -0
  37. package/dist/features/search.js +11 -0
  38. package/dist/features/sidebar-resizer.d.ts +14 -0
  39. package/dist/features/sidebar-resizer.js +16 -0
  40. package/dist/features/sidebar-toggle.d.ts +13 -0
  41. package/dist/features/sidebar-toggle.js +15 -0
  42. package/dist/features/tag-governance.d.ts +14 -0
  43. package/dist/features/tag-governance.js +16 -0
  44. package/dist/features/tauri-dev.d.ts +2 -0
  45. package/dist/features/tauri-dev.js +25 -0
  46. package/dist/features/tauri.d.ts +11 -0
  47. package/dist/features/tauri.js +52 -0
  48. package/dist/features/versioning.d.ts +27 -0
  49. package/dist/features/versioning.js +43 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +150 -0
  52. package/dist/preset.d.ts +37 -0
  53. package/dist/preset.js +156 -0
  54. package/dist/prompts.d.ts +32 -0
  55. package/dist/prompts.js +248 -0
  56. package/dist/scaffold.d.ts +4 -0
  57. package/dist/scaffold.js +344 -0
  58. package/dist/settings-gen.d.ts +2 -0
  59. package/dist/settings-gen.js +237 -0
  60. package/dist/utils.d.ts +8 -0
  61. package/dist/utils.js +34 -0
  62. package/dist/zfb-config-gen.d.ts +19 -0
  63. package/dist/zfb-config-gen.js +222 -0
  64. package/package.json +65 -0
  65. package/templates/base/.htmlvalidate.json +5 -0
  66. package/templates/base/.zfb/doc-history-meta.json +1 -0
  67. package/templates/base/pages/404.tsx +55 -0
  68. package/templates/base/pages/_data.ts +179 -0
  69. package/templates/base/pages/_mdx-components.ts +249 -0
  70. package/templates/base/pages/docs/[...slug].tsx +448 -0
  71. package/templates/base/pages/index.tsx +158 -0
  72. package/templates/base/pages/lib/_body-end-islands.tsx +201 -0
  73. package/templates/base/pages/lib/_category-nav.tsx +148 -0
  74. package/templates/base/pages/lib/_category-tree-nav.tsx +104 -0
  75. package/templates/base/pages/lib/_compose-meta-title.ts +29 -0
  76. package/templates/base/pages/lib/_details.tsx +30 -0
  77. package/templates/base/pages/lib/_doc-history-area.tsx +178 -0
  78. package/templates/base/pages/lib/_doc-metainfo-area.tsx +100 -0
  79. package/templates/base/pages/lib/_doc-tags-area.tsx +89 -0
  80. package/templates/base/pages/lib/_extract-headings.ts +81 -0
  81. package/templates/base/pages/lib/_footer-with-defaults.tsx +234 -0
  82. package/templates/base/pages/lib/_frontmatter-preview-data.ts +53 -0
  83. package/templates/base/pages/lib/_head-with-defaults.tsx +113 -0
  84. package/templates/base/pages/lib/_header-with-defaults.tsx +386 -0
  85. package/templates/base/pages/lib/_inline-version-switcher.tsx +84 -0
  86. package/templates/base/pages/lib/_math-block.tsx +63 -0
  87. package/templates/base/pages/lib/_nav-source-docs.ts +68 -0
  88. package/templates/base/pages/lib/_preset-generator.tsx +81 -0
  89. package/templates/base/pages/lib/_search-widget-script.ts +388 -0
  90. package/templates/base/pages/lib/_search-widget.tsx +196 -0
  91. package/templates/base/pages/lib/_sidebar-with-defaults.tsx +176 -0
  92. package/templates/base/pages/lib/_site-tree-nav.tsx +128 -0
  93. package/templates/base/pages/lib/locale-merge.ts +58 -0
  94. package/templates/base/pages/lib/route-enumerators.ts +302 -0
  95. package/templates/base/pages/sitemap.xml.tsx +51 -0
  96. package/templates/base/plugins/connect-adapter.mjs +144 -0
  97. package/templates/base/plugins/copy-public-plugin.mjs +50 -0
  98. package/templates/base/plugins/search-index-plugin.mjs +54 -0
  99. package/templates/base/scripts/run-b4push.sh +102 -0
  100. package/templates/base/src/components/ai-chat-modal.tsx +15 -0
  101. package/templates/base/src/components/client-router-bootstrap.tsx +14 -0
  102. package/templates/base/src/components/content/component-map.ts +25 -0
  103. package/templates/base/src/components/content/content-blockquote.tsx +16 -0
  104. package/templates/base/src/components/content/content-code.tsx +117 -0
  105. package/templates/base/src/components/content/content-link.tsx +83 -0
  106. package/templates/base/src/components/content/content-ol.tsx +19 -0
  107. package/templates/base/src/components/content/content-paragraph.tsx +10 -0
  108. package/templates/base/src/components/content/content-strong.tsx +16 -0
  109. package/templates/base/src/components/content/content-table.tsx +18 -0
  110. package/templates/base/src/components/content/content-ul.tsx +18 -0
  111. package/templates/base/src/components/content/heading-h2.tsx +26 -0
  112. package/templates/base/src/components/content/heading-h3.tsx +26 -0
  113. package/templates/base/src/components/content/heading-h4.tsx +26 -0
  114. package/templates/base/src/components/design-token-panel-bootstrap.tsx +15 -0
  115. package/templates/base/src/components/desktop-sidebar-toggle.tsx +15 -0
  116. package/templates/base/src/components/doc-history.tsx +18 -0
  117. package/templates/base/src/components/html-preview/highlighted-code.tsx +74 -0
  118. package/templates/base/src/components/html-preview/html-preview.tsx +108 -0
  119. package/templates/base/src/components/html-preview/preflight.ts +112 -0
  120. package/templates/base/src/components/html-preview/preview-base.tsx +159 -0
  121. package/templates/base/src/components/image-enlarge.tsx +19 -0
  122. package/templates/base/src/components/mobile-toc.tsx +94 -0
  123. package/templates/base/src/components/preset-generator.tsx +14 -0
  124. package/templates/base/src/components/sidebar-toggle.tsx +98 -0
  125. package/templates/base/src/components/sidebar-tree.tsx +543 -0
  126. package/templates/base/src/components/site-tree-nav.tsx +233 -0
  127. package/templates/base/src/components/theme-toggle.tsx +93 -0
  128. package/templates/base/src/components/toc.tsx +63 -0
  129. package/templates/base/src/components/tree-nav-shared.tsx +71 -0
  130. package/templates/base/src/config/color-scheme-utils.ts +182 -0
  131. package/templates/base/src/config/color-schemes.ts +128 -0
  132. package/templates/base/src/config/frontmatter-preview-defaults.ts +24 -0
  133. package/templates/base/src/config/frontmatter-preview-renderers.tsx +46 -0
  134. package/templates/base/src/config/i18n.ts +225 -0
  135. package/templates/base/src/config/settings-types.ts +162 -0
  136. package/templates/base/src/config/sidebars.ts +66 -0
  137. package/templates/base/src/config/tag-vocabulary-types.ts +39 -0
  138. package/templates/base/src/config/tag-vocabulary.ts +20 -0
  139. package/templates/base/src/hooks/use-active-heading.ts +133 -0
  140. package/templates/base/src/plugins/docs-source-map.ts +103 -0
  141. package/templates/base/src/plugins/hast-utils.ts +10 -0
  142. package/templates/base/src/plugins/rehype-code-title.ts +50 -0
  143. package/templates/base/src/plugins/rehype-heading-links.ts +53 -0
  144. package/templates/base/src/plugins/rehype-image-enlarge.ts +113 -0
  145. package/templates/base/src/plugins/rehype-mermaid.ts +41 -0
  146. package/templates/base/src/plugins/rehype-strip-md-extension.ts +58 -0
  147. package/templates/base/src/plugins/remark-admonitions.ts +99 -0
  148. package/templates/base/src/plugins/remark-resolve-markdown-links.ts +127 -0
  149. package/templates/base/src/plugins/url-utils.ts +4 -0
  150. package/templates/base/src/styles/global.css +1066 -0
  151. package/templates/base/src/types/docs-entry.ts +39 -0
  152. package/templates/base/src/types/heading.ts +5 -0
  153. package/templates/base/src/types/locale.ts +10 -0
  154. package/templates/base/src/utils/base.ts +139 -0
  155. package/templates/base/src/utils/content-files.ts +106 -0
  156. package/templates/base/src/utils/dedent.ts +24 -0
  157. package/templates/base/src/utils/docs.ts +335 -0
  158. package/templates/base/src/utils/git-info.ts +70 -0
  159. package/templates/base/src/utils/github.ts +19 -0
  160. package/templates/base/src/utils/header-right-items.ts +38 -0
  161. package/templates/base/src/utils/nav-scope.ts +63 -0
  162. package/templates/base/src/utils/sidebar.ts +104 -0
  163. package/templates/base/src/utils/slug.ts +10 -0
  164. package/templates/base/src/utils/smart-break.tsx +126 -0
  165. package/templates/base/src/utils/tags.ts +126 -0
  166. package/templates/base/tsconfig.json +36 -0
  167. package/templates/features/bodyFootUtil/files/src/utils/github.ts +19 -0
  168. package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +137 -0
  169. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/escape-for-mdx.test.ts +34 -0
  170. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +376 -0
  171. package/templates/features/claudeResources/files/src/integrations/claude-resources/escape-for-mdx.ts +93 -0
  172. package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +586 -0
  173. package/templates/features/designTokenPanel/files/src/components/design-token-panel-bootstrap.tsx +15 -0
  174. package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +99 -0
  175. package/templates/features/designTokenPanel/files/src/config/design-tokens-manifest.ts +177 -0
  176. package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +50 -0
  177. package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +99 -0
  178. package/templates/features/docHistory/files/src/components/doc-history.tsx +598 -0
  179. package/templates/features/docHistory/files/src/types/doc-history.ts +23 -0
  180. package/templates/features/docHistory/files/src/utils/doc-history.ts +180 -0
  181. package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +116 -0
  182. package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +99 -0
  183. package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +101 -0
  184. package/templates/features/docTags/files/pages/docs/tags/index.tsx +86 -0
  185. package/templates/features/i18n/files/pages/[locale]/docs/[...slug].tsx +467 -0
  186. package/templates/features/i18n/files/pages/[locale]/index.tsx +213 -0
  187. package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +248 -0
  188. package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +74 -0
  189. package/templates/features/sidebarResizer/files/src/scripts/sidebar-resizer.ts +185 -0
  190. package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +126 -0
  191. package/templates/features/tagGovernance/files/scripts/tags-audit.ts +576 -0
  192. package/templates/features/tagGovernance/files/scripts/tags-suggest.ts +428 -0
  193. package/templates/features/tauri/files/src/components/find-bar.tsx +122 -0
  194. package/templates/features/tauri/files/src/components/find-in-page-init.tsx +53 -0
  195. package/templates/features/tauri/files/src/utils/find-in-page.ts +175 -0
  196. package/templates/features/tauri/files/src-tauri/Cargo.toml +14 -0
  197. package/templates/features/tauri/files/src-tauri/build.rs +3 -0
  198. package/templates/features/tauri/files/src-tauri/capabilities/default.json +11 -0
  199. package/templates/features/tauri/files/src-tauri/src/main.rs +250 -0
  200. package/templates/features/tauri/files/src-tauri/tauri.conf.json +25 -0
  201. package/templates/features/tauriDev/files/src-tauri-dev/Cargo.toml +15 -0
  202. package/templates/features/tauriDev/files/src-tauri-dev/build.rs +3 -0
  203. package/templates/features/tauriDev/files/src-tauri-dev/capabilities/default.json +7 -0
  204. package/templates/features/tauriDev/files/src-tauri-dev/frontend/index.html +187 -0
  205. package/templates/features/tauriDev/files/src-tauri-dev/icons/icon.png +0 -0
  206. package/templates/features/tauriDev/files/src-tauri-dev/src/main.rs +995 -0
  207. package/templates/features/tauriDev/files/src-tauri-dev/tauri.conf.json +22 -0
  208. package/templates/features/tauriDev/files/src-tauri-dev/test-launch.sh +65 -0
  209. package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +100 -0
  210. package/templates/features/versioning/files/pages/docs/versions.tsx +78 -0
  211. package/templates/features/versioning/files/pages/v/[version]/docs/[...slug].tsx +451 -0
  212. package/templates/features/versioning/files/pages/v/[version]/ja/docs/[...slug].tsx +490 -0
@@ -0,0 +1,206 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ // ---------------------------------------------------------------------------
4
+ // Anchor patterns
5
+ // ---------------------------------------------------------------------------
6
+ /**
7
+ * Regex that matches any line containing an @slot anchor comment.
8
+ *
9
+ * Matches all anchor forms used across the template files:
10
+ * - `// @slot:…` — TypeScript/JS line comment (module scope)
11
+ * - `/* @slot:… *\/` — block comment
12
+ * - `{/* @slot:… *\/}` — JSX expression comment (body region in .tsx)
13
+ * - `<!-- @slot:… -->` — HTML comment (legacy .astro body region)
14
+ * - `# @slot:…` — shell / YAML comment
15
+ */
16
+ const ANCHOR_LINE_RE = /^[ \t]*(?:\{\/\*|\/\/|\/\*|<!--|#)\s*@slot:[^\n]*(?:\*\/\}|\*\/|-->)?[ \t]*\r?\n?$/gm;
17
+ // ---------------------------------------------------------------------------
18
+ // Core functions
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * Apply a list of injections to files in the target directory.
22
+ *
23
+ * Injections are grouped by target file and applied in array order.
24
+ * For "before"/"after" modes the content is inserted relative to the anchor
25
+ * line. For "replace" mode, the content replaces everything between the
26
+ * `:start` and `:end` anchors (exclusive — the anchor lines themselves are
27
+ * also removed).
28
+ */
29
+ export async function applyInjections(targetDir, injections) {
30
+ // Group injections by target file
31
+ const byFile = new Map();
32
+ for (const inj of injections) {
33
+ const list = byFile.get(inj.file) ?? [];
34
+ list.push(inj);
35
+ byFile.set(inj.file, list);
36
+ }
37
+ for (const [relPath, fileInjections] of byFile) {
38
+ const filePath = path.join(targetDir, relPath);
39
+ if (!(await fs.pathExists(filePath)))
40
+ continue;
41
+ let content = await fs.readFile(filePath, "utf-8");
42
+ for (const inj of fileInjections) {
43
+ const mode = inj.position ?? "before";
44
+ if (mode === "replace") {
45
+ // Range-based replacement: find :start and :end anchors
46
+ const startAnchor = inj.anchor; // must end with :start
47
+ const endAnchor = inj.anchor.replace(/:start\b/, ":end");
48
+ const startIdx = content.indexOf(startAnchor);
49
+ const endIdx = content.indexOf(endAnchor);
50
+ if (startIdx === -1 || endIdx === -1)
51
+ continue;
52
+ // Find the full lines containing the anchors
53
+ const startLineBegin = content.lastIndexOf("\n", startIdx) + 1;
54
+ const endLineEnd = content.indexOf("\n", endIdx);
55
+ const actualEnd = endLineEnd === -1 ? content.length : endLineEnd + 1;
56
+ content =
57
+ content.slice(0, startLineBegin) +
58
+ inj.content +
59
+ (inj.content.endsWith("\n") ? "" : "\n") +
60
+ content.slice(actualEnd);
61
+ }
62
+ else {
63
+ // before / after: find the anchor line and insert relative to it
64
+ const anchorIdx = content.indexOf(inj.anchor);
65
+ if (anchorIdx === -1)
66
+ continue;
67
+ const lineStart = content.lastIndexOf("\n", anchorIdx) + 1;
68
+ const lineEnd = content.indexOf("\n", anchorIdx);
69
+ const actualLineEnd = lineEnd === -1 ? content.length : lineEnd + 1;
70
+ if (mode === "before") {
71
+ content =
72
+ content.slice(0, lineStart) +
73
+ inj.content +
74
+ (inj.content.endsWith("\n") ? "" : "\n") +
75
+ content.slice(lineStart);
76
+ }
77
+ else {
78
+ // after
79
+ content =
80
+ content.slice(0, actualLineEnd) +
81
+ inj.content +
82
+ (inj.content.endsWith("\n") ? "" : "\n") +
83
+ content.slice(actualLineEnd);
84
+ }
85
+ }
86
+ }
87
+ await fs.writeFile(filePath, content);
88
+ }
89
+ }
90
+ /**
91
+ * Remove all remaining `@slot:` anchor lines from files in the target
92
+ * directory. Call this after all injections have been applied so that
93
+ * unused anchors don't appear in the generated project.
94
+ */
95
+ export async function cleanAnchors(targetDir, files) {
96
+ for (const relPath of files) {
97
+ const filePath = path.join(targetDir, relPath);
98
+ if (!(await fs.pathExists(filePath)))
99
+ continue;
100
+ const content = await fs.readFile(filePath, "utf-8");
101
+ let cleaned = content.replace(ANCHOR_LINE_RE, "");
102
+ // Collapse runs of 3+ consecutive newlines down to 2 (one blank line)
103
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n");
104
+ if (cleaned !== content) {
105
+ await fs.writeFile(filePath, cleaned);
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Copy a feature's files into the target directory.
111
+ * `featureFilesDir` is the absolute path to the feature's `files/` directory.
112
+ * Each file inside mirrors the project directory structure.
113
+ */
114
+ export async function copyFeatureFiles(featureFilesDir, targetDir) {
115
+ if (!(await fs.pathExists(featureFilesDir)))
116
+ return;
117
+ await fs.copy(featureFilesDir, targetDir, { overwrite: true });
118
+ }
119
+ /**
120
+ * Resolve which features are selected based on UserChoices.
121
+ * Handles special cases like the footer pseudo-feature.
122
+ */
123
+ export function resolveSelectedFeatures(choices, featureModules) {
124
+ const selected = [];
125
+ for (const [name, moduleFn] of Object.entries(featureModules)) {
126
+ // Special case: footer is activated by footerNavGroup, footerCopyright,
127
+ // or footerTaglist (the taglist renders inside the same footer grid).
128
+ if (name === "footer") {
129
+ if (choices.features.includes("footerNavGroup") ||
130
+ choices.features.includes("footerCopyright") ||
131
+ choices.features.includes("footerTaglist")) {
132
+ selected.push(moduleFn(choices));
133
+ }
134
+ continue;
135
+ }
136
+ if (choices.features.includes(name)) {
137
+ selected.push(moduleFn(choices));
138
+ }
139
+ }
140
+ return selected;
141
+ }
142
+ /**
143
+ * Validate that all feature dependencies are satisfied.
144
+ * Throws if a selected feature depends on one that isn't selected.
145
+ */
146
+ export function validateDependencies(features, allSelectedNames) {
147
+ for (const feature of features) {
148
+ if (!feature.dependencies)
149
+ continue;
150
+ for (const dep of feature.dependencies) {
151
+ if (!allSelectedNames.has(dep)) {
152
+ throw new Error(`Feature "${feature.name}" requires "${dep}" but it is not selected.`);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ /** Files that may contain injection anchors and need cleaning.
158
+ *
159
+ * W7A (#1736): the two `.astro` entries were dropped — those files no
160
+ * longer exist in generated projects after the post-cutover .tsx
161
+ * migration. The only surviving anchor target is `global.css`, where
162
+ * `design-token-panel.ts` injects `@import "@takazudo/zdtp/styles.css";`
163
+ * at `@slot:global-css:feature-styles`. The sibling
164
+ * `@slot:global-css:theme-tokens` anchor in the same file is consumed
165
+ * by the color-scheme palette generator and must remain. The .tsx
166
+ * anchor form is still supported in `ANCHOR_LINE_RE` for forward
167
+ * compatibility, but no current feature uses it.
168
+ */
169
+ export const ANCHOR_FILES = ["src/styles/global.css"];
170
+ /**
171
+ * Main composition entry point. Orchestrates the full feature composition
172
+ * pipeline for a generated project.
173
+ *
174
+ * 1. Resolve selected features
175
+ * 2. Validate dependencies
176
+ * 3. Copy feature files
177
+ * 4. Apply all injections
178
+ * 5. Run post-processing hooks
179
+ * 6. Clean up unused anchors
180
+ */
181
+ export async function composeFeatures(targetDir, choices, featureModules, featuresDir) {
182
+ // 1. Resolve
183
+ const features = resolveSelectedFeatures(choices, featureModules);
184
+ const selectedNames = new Set(features.map((f) => f.name));
185
+ // 2. Validate
186
+ validateDependencies(features, selectedNames);
187
+ // 3. Copy feature files
188
+ for (const feature of features) {
189
+ const filesDir = path.join(featuresDir, feature.name, "files");
190
+ await copyFeatureFiles(filesDir, targetDir);
191
+ }
192
+ // 4. Collect and apply all injections
193
+ const allInjections = [];
194
+ for (const feature of features) {
195
+ allInjections.push(...feature.injections);
196
+ }
197
+ await applyInjections(targetDir, allInjections);
198
+ // 5. Post-processing
199
+ for (const feature of features) {
200
+ if (feature.postProcess) {
201
+ await feature.postProcess(targetDir, choices);
202
+ }
203
+ }
204
+ // 6. Clean up unused anchors
205
+ await cleanAnchors(targetDir, ANCHOR_FILES);
206
+ }
@@ -0,0 +1,20 @@
1
+ export interface LightDarkPairing {
2
+ light: string;
3
+ dark: string;
4
+ label: string;
5
+ }
6
+ export declare const LIGHT_DARK_PAIRINGS: LightDarkPairing[];
7
+ export declare const SINGLE_SCHEMES: string[];
8
+ export interface SupportedLang {
9
+ value: string;
10
+ label: string;
11
+ }
12
+ export declare const SUPPORTED_LANGS: SupportedLang[];
13
+ export interface Feature {
14
+ value: string;
15
+ label: string;
16
+ hint: string;
17
+ default: boolean;
18
+ cliFlag: string;
19
+ }
20
+ export declare const FEATURES: Feature[];
@@ -0,0 +1,224 @@
1
+ export const LIGHT_DARK_PAIRINGS = [
2
+ { light: "Default Light", dark: "Default Dark", label: "Default" },
3
+ { light: "GitHub Light", dark: "GitHub Dark", label: "GitHub" },
4
+ { light: "Catppuccin Latte", dark: "Catppuccin Mocha", label: "Catppuccin" },
5
+ { light: "Solarized Light", dark: "Solarized Dark", label: "Solarized" },
6
+ { light: "Rose Pine Dawn", dark: "Rose Pine", label: "Rosé Pine" },
7
+ { light: "Atom One Light", dark: "Atom One Dark", label: "Atom One" },
8
+ { light: "Everforest Light", dark: "Everforest Dark", label: "Everforest" },
9
+ { light: "Gruvbox Light", dark: "Gruvbox Dark", label: "Gruvbox" },
10
+ { light: "Ayu Light", dark: "Ayu Dark", label: "Ayu" },
11
+ ];
12
+ // All available single schemes (dark ones most popular first)
13
+ export const SINGLE_SCHEMES = [
14
+ "Default Dark",
15
+ "Dracula",
16
+ "Catppuccin Mocha",
17
+ "GitHub Dark",
18
+ "Nord",
19
+ "TokyoNight",
20
+ "Gruvbox Dark",
21
+ "Atom One Dark",
22
+ "Rose Pine",
23
+ "Solarized Dark",
24
+ "Material Ocean",
25
+ "Monokai Pro",
26
+ "Everforest Dark",
27
+ "Kanagawa Wave",
28
+ "Night Owl",
29
+ "Ayu Dark",
30
+ "VS Code Dark+",
31
+ "Doom One",
32
+ "Challenger Deep",
33
+ "Catppuccin Frappe",
34
+ "Catppuccin Macchiato",
35
+ "Gruvbox Dark Hard",
36
+ "Rose Pine Moon",
37
+ "GitHub Dark Dimmed",
38
+ "Ayu Mirage",
39
+ "Material Darker",
40
+ "Material Dark",
41
+ "Monokai Remastered",
42
+ "Monokai Vivid",
43
+ "Monokai Soda",
44
+ "Solarized Dark Higher Contrast",
45
+ "Gruvbox Material Dark",
46
+ "Kanagawa Dragon",
47
+ // Light schemes
48
+ "Default Light",
49
+ "GitHub Light",
50
+ "Catppuccin Latte",
51
+ "Solarized Light",
52
+ "Rose Pine Dawn",
53
+ "Atom One Light",
54
+ "Everforest Light",
55
+ "Gruvbox Light",
56
+ "Ayu Light",
57
+ ];
58
+ export const SUPPORTED_LANGS = [
59
+ { value: "en", label: "English" },
60
+ { value: "ja", label: "Japanese" },
61
+ { value: "zh-cn", label: "Chinese (Simplified)" },
62
+ { value: "zh-tw", label: "Chinese (Traditional)" },
63
+ { value: "ko", label: "Korean" },
64
+ { value: "es", label: "Spanish" },
65
+ { value: "fr", label: "French" },
66
+ { value: "de", label: "German" },
67
+ { value: "pt", label: "Portuguese" },
68
+ ];
69
+ export const FEATURES = [
70
+ {
71
+ value: "i18n",
72
+ label: "i18n (multi-language)",
73
+ hint: "Add a secondary language",
74
+ default: false,
75
+ cliFlag: "i18n",
76
+ },
77
+ {
78
+ value: "search",
79
+ label: "Pagefind search",
80
+ hint: "Full-text search",
81
+ default: true,
82
+ cliFlag: "search",
83
+ },
84
+ {
85
+ value: "sidebarFilter",
86
+ label: "Sidebar filter",
87
+ hint: "Real-time sidebar filtering",
88
+ default: true,
89
+ cliFlag: "sidebar-filter",
90
+ },
91
+ {
92
+ value: "claudeResources",
93
+ label: "Claude Resources",
94
+ hint: "Auto-generate Claude Code docs",
95
+ default: false,
96
+ cliFlag: "claude-resources",
97
+ },
98
+ {
99
+ value: "claudeSkills",
100
+ label: "Claude skills (user-facing)",
101
+ hint: "Ship zudo-doc-* Claude Code skills (design-system, translate, version-bump)",
102
+ default: false,
103
+ cliFlag: "claude-skills",
104
+ },
105
+ {
106
+ value: "designTokenPanel",
107
+ label: "Design Token Panel",
108
+ hint: "Interactive tabbed panel for tweaking spacing, font, size, and color tokens",
109
+ default: false,
110
+ cliFlag: "design-token-panel",
111
+ },
112
+ {
113
+ value: "sidebarResizer",
114
+ label: "Sidebar resizer",
115
+ hint: "Draggable sidebar width",
116
+ default: false,
117
+ cliFlag: "sidebar-resizer",
118
+ },
119
+ {
120
+ value: "sidebarToggle",
121
+ label: "Sidebar toggle",
122
+ hint: "Show/hide desktop sidebar",
123
+ default: false,
124
+ cliFlag: "sidebar-toggle",
125
+ },
126
+ {
127
+ value: "versioning",
128
+ label: "Versioning",
129
+ hint: "Multi-version documentation support",
130
+ default: false,
131
+ cliFlag: "versioning",
132
+ },
133
+ {
134
+ value: "docHistory",
135
+ label: "Document history",
136
+ hint: "Show document edit history",
137
+ default: false,
138
+ cliFlag: "doc-history",
139
+ },
140
+ {
141
+ value: "bodyFootUtil",
142
+ label: "Body foot util area",
143
+ hint: "Right-aligned strip below each doc: doc history trigger + View source on GitHub link",
144
+ default: false,
145
+ cliFlag: "body-foot-util",
146
+ },
147
+ {
148
+ value: "llmsTxt",
149
+ label: "llms.txt",
150
+ hint: "Generate llms.txt for LLM consumption",
151
+ default: false,
152
+ cliFlag: "llms-txt",
153
+ },
154
+ {
155
+ value: "skillSymlinker",
156
+ label: "Skill symlinker",
157
+ hint: "Symlink documentation skills",
158
+ default: false,
159
+ cliFlag: "skill-symlinker",
160
+ },
161
+ {
162
+ value: "tauri",
163
+ label: "Tauri desktop app",
164
+ hint: "macOS desktop wrapper with in-page search",
165
+ default: false,
166
+ cliFlag: "tauri",
167
+ },
168
+ {
169
+ value: "tauriDev",
170
+ label: "Tauri dev wrapper (Mode 2)",
171
+ hint: "Configurable desktop dev wrapper for any project",
172
+ default: false,
173
+ cliFlag: "tauri-dev",
174
+ },
175
+ {
176
+ value: "footerNavGroup",
177
+ label: "Footer nav group",
178
+ hint: "Navigation links in the footer",
179
+ default: false,
180
+ cliFlag: "footer-nav-group",
181
+ },
182
+ {
183
+ value: "imageEnlarge",
184
+ label: "Image enlarge",
185
+ hint: "Click-to-enlarge for oversized markdown images",
186
+ default: true,
187
+ cliFlag: "image-enlarge",
188
+ },
189
+ {
190
+ value: "footerCopyright",
191
+ label: "Footer copyright",
192
+ hint: "Copyright notice in the footer",
193
+ default: false,
194
+ cliFlag: "footer-copyright",
195
+ },
196
+ {
197
+ value: "changelog",
198
+ label: "Changelog",
199
+ hint: "Changelog page",
200
+ default: false,
201
+ cliFlag: "changelog",
202
+ },
203
+ {
204
+ value: "tagGovernance",
205
+ label: "Tag governance",
206
+ hint: "Vocabulary-aware tag audit + suggest scripts",
207
+ default: true,
208
+ cliFlag: "tag-governance",
209
+ },
210
+ {
211
+ value: "docTags",
212
+ label: "Doc tags pages",
213
+ hint: "Per-tag and tag-index browsing routes (docs/tags/...)",
214
+ default: false,
215
+ cliFlag: "doc-tags",
216
+ },
217
+ {
218
+ value: "footerTaglist",
219
+ label: "Footer taglist",
220
+ hint: "Grouped tag index in the footer (requires tagGovernance)",
221
+ default: false,
222
+ cliFlag: "footer-taglist",
223
+ },
224
+ ];
@@ -0,0 +1,10 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ /**
3
+ * body-foot-util feature.
4
+ *
5
+ * W7A (#1736): post-cutover, the body-foot-util area is hosted by
6
+ * `pages/lib/_doc-history-area.tsx` which wraps `BodyFootUtilArea`
7
+ * unconditionally. The component itself runtime-gates on
8
+ * `settings.bodyFootUtilArea`, so there is nothing to inject.
9
+ */
10
+ export declare const bodyFootUtilFeature: FeatureModule;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * body-foot-util feature.
3
+ *
4
+ * W7A (#1736): post-cutover, the body-foot-util area is hosted by
5
+ * `pages/lib/_doc-history-area.tsx` which wraps `BodyFootUtilArea`
6
+ * unconditionally. The component itself runtime-gates on
7
+ * `settings.bodyFootUtilArea`, so there is nothing to inject.
8
+ */
9
+ export const bodyFootUtilFeature = () => ({
10
+ name: "bodyFootUtil",
11
+ injections: [],
12
+ });
@@ -0,0 +1,2 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ export declare const claudeResourcesFeature: FeatureModule;
@@ -0,0 +1,6 @@
1
+ export const claudeResourcesFeature = () => ({
2
+ name: "claudeResources",
3
+ injections: [
4
+ // Plugin entry is handled by zfb-config-gen.ts — no shared file injection needed
5
+ ],
6
+ });
@@ -0,0 +1,14 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ /**
3
+ * Design-token-panel (zdtp) feature.
4
+ *
5
+ * W7A (#1736): keeps the single surviving inject() — the zdtp CSS @import
6
+ * at `@slot:global-css:feature-styles`. The two dead .astro injections
7
+ * (doc-layout bootstrap import + header trigger button) are dropped:
8
+ * the bootstrap is loaded by `pages/lib/design-token-panel-bootstrap`-side
9
+ * effect when the feature is enabled, and the header trigger is now
10
+ * rendered by `pages/lib/_header-with-defaults.tsx` from the
11
+ * `settings.headerRightItems` entry `{ type: "trigger", trigger:
12
+ * "design-token-panel" }`.
13
+ */
14
+ export declare const designTokenPanelFeature: FeatureModule;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Design-token-panel (zdtp) feature.
3
+ *
4
+ * W7A (#1736): keeps the single surviving inject() — the zdtp CSS @import
5
+ * at `@slot:global-css:feature-styles`. The two dead .astro injections
6
+ * (doc-layout bootstrap import + header trigger button) are dropped:
7
+ * the bootstrap is loaded by `pages/lib/design-token-panel-bootstrap`-side
8
+ * effect when the feature is enabled, and the header trigger is now
9
+ * rendered by `pages/lib/_header-with-defaults.tsx` from the
10
+ * `settings.headerRightItems` entry `{ type: "trigger", trigger:
11
+ * "design-token-panel" }`.
12
+ */
13
+ export const designTokenPanelFeature = () => ({
14
+ name: "designTokenPanel",
15
+ injections: [
16
+ {
17
+ // Panel chrome CSS — imported here so the rules land in the main page
18
+ // CSS bundle (not a deferred chunk), ensuring the panel renders
19
+ // correctly on first click. Vite library mode strips the source CSS
20
+ // import from the emitted JS, so this CSS-side import is the required
21
+ // pull point. See @takazudo/zdtp PORTABLE-CONTRACT.md §7.
22
+ file: "src/styles/global.css",
23
+ anchor: "/* @slot:global-css:feature-styles */",
24
+ content: `@import "@takazudo/zdtp/styles.css";`,
25
+ },
26
+ ],
27
+ });
@@ -0,0 +1,9 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ /**
3
+ * Doc-history feature.
4
+ *
5
+ * W7A (#1736): post-cutover, `pages/lib/_doc-history-area.tsx` is mounted
6
+ * unconditionally and self-gates on `settings.docHistory`. The plugin entry
7
+ * is wired by `zfb-config-gen.ts`.
8
+ */
9
+ export declare const docHistoryFeature: FeatureModule;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Doc-history feature.
3
+ *
4
+ * W7A (#1736): post-cutover, `pages/lib/_doc-history-area.tsx` is mounted
5
+ * unconditionally and self-gates on `settings.docHistory`. The plugin entry
6
+ * is wired by `zfb-config-gen.ts`.
7
+ */
8
+ export const docHistoryFeature = () => ({
9
+ name: "docHistory",
10
+ injections: [],
11
+ });
@@ -0,0 +1,19 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ /**
3
+ * docTags feature (W7C — #1738).
4
+ *
5
+ * Settings gate: `settings.docTags === true`.
6
+ *
7
+ * Ships feature-conditional pages from
8
+ * `templates/features/docTags/files/pages/`:
9
+ *
10
+ * docs/tags/[tag].tsx
11
+ * docs/tags/index.tsx
12
+ * [locale]/docs/tags/[tag].tsx — only when i18n ALSO selected
13
+ * [locale]/docs/tags/index.tsx — only when i18n ALSO selected
14
+ *
15
+ * `copyFeatureFiles` (compose.ts) auto-emits every file under
16
+ * `files/`; postProcess removes the `[locale]/**` subset when i18n
17
+ * is NOT selected. See W2 spec-lock §Cross-feature interaction.
18
+ */
19
+ export declare const docTagsFeature: FeatureModule;
@@ -0,0 +1,33 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ /**
4
+ * docTags feature (W7C — #1738).
5
+ *
6
+ * Settings gate: `settings.docTags === true`.
7
+ *
8
+ * Ships feature-conditional pages from
9
+ * `templates/features/docTags/files/pages/`:
10
+ *
11
+ * docs/tags/[tag].tsx
12
+ * docs/tags/index.tsx
13
+ * [locale]/docs/tags/[tag].tsx — only when i18n ALSO selected
14
+ * [locale]/docs/tags/index.tsx — only when i18n ALSO selected
15
+ *
16
+ * `copyFeatureFiles` (compose.ts) auto-emits every file under
17
+ * `files/`; postProcess removes the `[locale]/**` subset when i18n
18
+ * is NOT selected. See W2 spec-lock §Cross-feature interaction.
19
+ */
20
+ export const docTagsFeature = (choices) => ({
21
+ name: "docTags",
22
+ injections: [],
23
+ postProcess: async (targetDir) => {
24
+ if (!choices.features.includes("i18n")) {
25
+ // i18n is OFF — strip the locale-scoped tag pages that were
26
+ // copied unconditionally by copyFeatureFiles.
27
+ const localeTagsDir = path.join(targetDir, "pages", "[locale]", "docs", "tags");
28
+ if (await fs.pathExists(localeTagsDir)) {
29
+ await fs.remove(localeTagsDir);
30
+ }
31
+ }
32
+ },
33
+ });
@@ -0,0 +1,14 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ /**
3
+ * Footer taglist feature.
4
+ *
5
+ * Purely a settings toggle: `settings-gen.ts` emits
6
+ * `footer.taglist = { enabled: true, groupBy: "group" }` when selected,
7
+ * and the existing `footer.astro` (part of the footer pseudo-feature) reads
8
+ * `settings.footer.taglist` to decide whether to render the column(s).
9
+ *
10
+ * `footer.astro` is only installed by the footer pseudo-feature, so
11
+ * `resolveSelectedFeatures` treats `footerTaglist` as one of the triggers
12
+ * for that feature.
13
+ */
14
+ export declare const footerTaglistFeature: FeatureModule;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Footer taglist feature.
3
+ *
4
+ * Purely a settings toggle: `settings-gen.ts` emits
5
+ * `footer.taglist = { enabled: true, groupBy: "group" }` when selected,
6
+ * and the existing `footer.astro` (part of the footer pseudo-feature) reads
7
+ * `settings.footer.taglist` to decide whether to render the column(s).
8
+ *
9
+ * `footer.astro` is only installed by the footer pseudo-feature, so
10
+ * `resolveSelectedFeatures` treats `footerTaglist` as one of the triggers
11
+ * for that feature.
12
+ */
13
+ export const footerTaglistFeature = () => ({
14
+ name: "footerTaglist",
15
+ injections: [],
16
+ dependencies: ["tagGovernance"],
17
+ });
@@ -0,0 +1,8 @@
1
+ import type { FeatureModule } from "../compose.js";
2
+ /**
3
+ * Footer pseudo-feature.
4
+ *
5
+ * W7A (#1736): post-cutover, `pages/lib/_footer-with-defaults.tsx` mounts
6
+ * the footer unconditionally — no doc-layout injection is required.
7
+ */
8
+ export declare const footerFeature: FeatureModule;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Footer pseudo-feature.
3
+ *
4
+ * W7A (#1736): post-cutover, `pages/lib/_footer-with-defaults.tsx` mounts
5
+ * the footer unconditionally — no doc-layout injection is required.
6
+ */
7
+ export const footerFeature = () => ({
8
+ name: "footer",
9
+ injections: [],
10
+ });