create-zudo-doc 3.2.0 → 4.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 (137) hide show
  1. package/README.md +3 -2
  2. package/dist/api.d.ts +2 -0
  3. package/dist/api.js +9 -1
  4. package/dist/claude-md-gen.d.ts +8 -0
  5. package/dist/claude-md-gen.js +44 -26
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +11 -1
  8. package/dist/compose.d.ts +14 -20
  9. package/dist/compose.js +15 -25
  10. package/dist/constants.d.ts +6 -0
  11. package/dist/constants.js +123 -1
  12. package/dist/features/body-foot-util.d.ts +8 -4
  13. package/dist/features/body-foot-util.js +8 -4
  14. package/dist/features/claude-resources.d.ts +9 -0
  15. package/dist/features/claude-resources.js +10 -6
  16. package/dist/features/design-token-panel.d.ts +0 -12
  17. package/dist/features/design-token-panel.js +32 -93
  18. package/dist/features/doc-history.d.ts +19 -3
  19. package/dist/features/doc-history.js +49 -60
  20. package/dist/features/doc-tags.d.ts +9 -13
  21. package/dist/features/doc-tags.js +10 -26
  22. package/dist/features/dynamic-page-transition.d.ts +19 -30
  23. package/dist/features/dynamic-page-transition.js +21 -209
  24. package/dist/features/footer-taglist.d.ts +1 -1
  25. package/dist/features/footer-taglist.js +1 -1
  26. package/dist/features/footer.d.ts +3 -2
  27. package/dist/features/footer.js +3 -2
  28. package/dist/features/i18n.d.ts +13 -8
  29. package/dist/features/i18n.js +14 -9
  30. package/dist/features/image-enlarge.d.ts +7 -26
  31. package/dist/features/image-enlarge.js +7 -26
  32. package/dist/features/index.js +2 -0
  33. package/dist/features/llms-txt.d.ts +3 -5
  34. package/dist/features/llms-txt.js +3 -5
  35. package/dist/features/search.d.ts +7 -3
  36. package/dist/features/search.js +7 -3
  37. package/dist/features/sidebar-resizer.d.ts +4 -8
  38. package/dist/features/sidebar-resizer.js +4 -8
  39. package/dist/features/sidebar-toggle.d.ts +7 -7
  40. package/dist/features/sidebar-toggle.js +7 -7
  41. package/dist/features/tag-governance.d.ts +3 -8
  42. package/dist/features/tag-governance.js +37 -9
  43. package/dist/features/tauri.d.ts +13 -10
  44. package/dist/features/tauri.js +16 -52
  45. package/dist/features/theme-pack-switcher.d.ts +11 -0
  46. package/dist/features/theme-pack-switcher.js +13 -0
  47. package/dist/features/versioning.d.ts +12 -24
  48. package/dist/features/versioning.js +13 -39
  49. package/dist/index.js +5 -3
  50. package/dist/preset.d.ts +2 -0
  51. package/dist/preset.js +12 -1
  52. package/dist/prompts.d.ts +2 -0
  53. package/dist/prompts.js +22 -1
  54. package/dist/scaffold.d.ts +13 -6
  55. package/dist/scaffold.js +97 -78
  56. package/dist/utils.d.ts +10 -0
  57. package/dist/utils.js +14 -0
  58. package/dist/zfb-config-gen.d.ts +32 -20
  59. package/dist/zfb-config-gen.js +410 -53
  60. package/package.json +2 -2
  61. package/templates/base/pages/docs/[[...slug]].tsx +64 -0
  62. package/templates/base/pages/index.tsx +6 -41
  63. package/templates/base/src/styles/global.css +15 -340
  64. package/templates/base/tsconfig.json +3 -26
  65. package/templates/features/i18n/files/pages/[locale]/docs/[[...slug]].tsx +92 -0
  66. package/dist/settings-gen.d.ts +0 -2
  67. package/dist/settings-gen.js +0 -320
  68. package/templates/base/.htmlvalidate.json +0 -5
  69. package/templates/base/.zfb/doc-history-meta.json +0 -1
  70. package/templates/base/pages/_data.ts +0 -160
  71. package/templates/base/pages/lib/_body-end-islands.tsx +0 -165
  72. package/templates/base/pages/lib/_chrome.ts +0 -167
  73. package/templates/base/pages/lib/_details.tsx +0 -29
  74. package/templates/base/pages/lib/_doc-route-entries.ts +0 -10
  75. package/templates/base/pages/lib/_extract-headings.ts +0 -37
  76. package/templates/base/pages/lib/_frontmatter-preview-data.ts +0 -27
  77. package/templates/base/pages/lib/_nav-source-cache.ts +0 -100
  78. package/templates/base/pages/lib/_nav-source-docs.ts +0 -17
  79. package/templates/base/pages/lib/_preset-generator.tsx +0 -81
  80. package/templates/base/pages/lib/_route-context.ts +0 -32
  81. package/templates/base/pages/lib/_search-widget.tsx +0 -17
  82. package/templates/base/pages/lib/doc-page-props.ts +0 -30
  83. package/templates/base/pages/lib/locale-merge.ts +0 -59
  84. package/templates/base/scripts/run-b4push.sh +0 -102
  85. package/templates/base/src/components/ai-chat-modal.tsx +0 -18
  86. package/templates/base/src/components/content/code-group.tsx +0 -3
  87. package/templates/base/src/components/content/content-admonition.tsx +0 -4
  88. package/templates/base/src/components/desktop-sidebar-toggle.tsx +0 -15
  89. package/templates/base/src/components/doc-history.tsx +0 -21
  90. package/templates/base/src/components/image-enlarge.tsx +0 -24
  91. package/templates/base/src/components/preset-generator.tsx +0 -14
  92. package/templates/base/src/components/sidebar-toggle.tsx +0 -6
  93. package/templates/base/src/components/sidebar-tree.tsx +0 -6
  94. package/templates/base/src/config/color-scheme-utils.ts +0 -69
  95. package/templates/base/src/config/color-schemes.ts +0 -165
  96. package/templates/base/src/config/docs-schema.ts +0 -95
  97. package/templates/base/src/config/frontmatter-preview-defaults.ts +0 -27
  98. package/templates/base/src/config/frontmatter-preview-renderers.tsx +0 -46
  99. package/templates/base/src/config/i18n.ts +0 -239
  100. package/templates/base/src/config/settings-types.ts +0 -45
  101. package/templates/base/src/config/sidebars.ts +0 -66
  102. package/templates/base/src/config/tag-vocabulary-types.ts +0 -4
  103. package/templates/base/src/config/tag-vocabulary.ts +0 -20
  104. package/templates/base/src/config/z-index-tokens.ts +0 -128
  105. package/templates/base/src/types/docs-entry.ts +0 -28
  106. package/templates/base/src/types/heading.ts +0 -5
  107. package/templates/base/src/types/locale.ts +0 -10
  108. package/templates/base/src/utils/base.ts +0 -164
  109. package/templates/base/src/utils/docs.ts +0 -446
  110. package/templates/base/src/utils/git-info.ts +0 -70
  111. package/templates/base/src/utils/github.ts +0 -22
  112. package/templates/base/src/utils/nav-scope.ts +0 -34
  113. package/templates/base/src/utils/sidebar.ts +0 -36
  114. package/templates/base/src/utils/slug.ts +0 -10
  115. package/templates/base/src/utils/smart-break.tsx +0 -12
  116. package/templates/base/src/utils/tags.ts +0 -83
  117. package/templates/base/zfb-shim.d.ts +0 -183
  118. package/templates/features/bodyFootUtil/files/src/utils/github.ts +0 -22
  119. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/escape-for-mdx.test.ts +0 -42
  120. package/templates/features/claudeResources/files/src/integrations/claude-resources/__tests__/generate.test.ts +0 -752
  121. package/templates/features/claudeResources/files/src/integrations/claude-resources/escape-for-mdx.ts +0 -97
  122. package/templates/features/claudeResources/files/src/integrations/claude-resources/generate.ts +0 -735
  123. package/templates/features/designTokenPanel/files/src/components/design-token-panel-bootstrap.tsx +0 -15
  124. package/templates/features/designTokenPanel/files/src/config/design-token-panel-config.ts +0 -435
  125. package/templates/features/designTokenPanel/files/src/config/design-tokens-manifest.ts +0 -174
  126. package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +0 -30
  127. package/templates/features/docHistory/files/src/components/doc-history.tsx +0 -10
  128. package/templates/features/docHistory/files/src/types/doc-history.ts +0 -7
  129. package/templates/features/dynamicPageTransition/files/src/components/client-router-bootstrap.tsx +0 -72
  130. package/templates/features/i18n/files/pages/[locale]/index.tsx +0 -72
  131. package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +0 -11
  132. package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +0 -6
  133. package/templates/features/tagGovernance/files/scripts/tags-audit.ts +0 -131
  134. package/templates/features/tagGovernance/files/scripts/tags-suggest.ts +0 -428
  135. package/templates/features/tauri/files/src/components/find-bar.tsx +0 -122
  136. package/templates/features/tauri/files/src/components/find-in-page-init.tsx +0 -59
  137. package/templates/features/tauri/files/src/utils/find-in-page.ts +0 -175
@@ -1,216 +1,28 @@
1
1
  /**
2
2
  * Dynamic page transition feature.
3
3
  *
4
- * When enabled:
5
- * - Sets `enableClientRouter={settings.dynamicPageTransition}` on every
6
- * `<DocLayoutWithDefaults>` render site the SSR `<ClientRouter />` render
7
- * self-activates the SPA router on the client as a top-level module side
8
- * effect (primary activation path).
9
- * - Wires the ClientRouterBootstrap island and the PageLoadingOverlay (pure
10
- * SSR) into body-end-islands the island is now a redundant fallback;
11
- * PageLoadingOverlay provides the loading spinner UI.
12
- * - Injects `@import "@takazudo/zudo-doc/page-loading.css"` at the
13
- * `@slot:global-css:imports` anchor (top of global.css, before rule blocks).
14
- * - Injects the `--color-page-loading-overlay` token into `@theme` at the
15
- * `@slot:global-css:theme-tokens` anchor.
16
- * - Injects the View-Transitions CSS into global.css at
17
- * `@slot:global-css:feature-styles`.
18
- *
19
- * Bug fix (#2265): the page-loading overlay CSS (.page-loading-overlay /
20
- * .page-loading-spinner / @keyframes page-loading-spin / [data-zd-nav-pending])
21
- * was never reaching scaffolded projects because it lived unconditionally in
22
- * the host global.css but had no corresponding injection for the template.
23
- * Gating this feature makes both the JS island and the CSS land together.
24
- *
25
- * The page-loading CSS now ships as a package artifact
26
- * `@takazudo/zudo-doc/page-loading.css` (Topic A, #2283) — the generator
27
- * injects a gated `@import` instead of inline rule bodies. The
28
- * `--color-page-loading-overlay` token is defined inline (inside `@theme`) so
29
- * it lives alongside the other project tokens and can be overridden per-project.
30
- *
31
- * The ClientRouterBootstrap component lives in
32
- * templates/features/dynamicPageTransition/files/src/components/ and is
33
- * byte-identical to the host src/components/client-router-bootstrap.tsx.
34
- * The feature-template drift checker enforces this parity automatically.
4
+ * Minimal-scaffold cutover (epic zudolab/zudo-doc#2651, Wave 6 #2660):
5
+ * purely a `zudoDoc({ dynamicPageTransition: true })` field now (see
6
+ * `zfb-config-gen.ts`). The primary activation path
7
+ * `<ClientRouter enableClientRouter={settings.dynamicPageTransition} />`
8
+ * rendered by the package's own `doc-page-shell`/`doclayout` — and the
9
+ * pure-SSR `<PageLoadingOverlay/>` mount are BOTH already handled inside
10
+ * `doc-body-end-islands/index.tsx`, gated on the same flag; the View-
11
+ * Transitions CSS ships unconditionally from `@takazudo/zudo-doc/features.css`.
12
+ *
13
+ * DROPPED: the host `ClientRouterBootstrap` island
14
+ * (`templates/features/dynamicPageTransition/files/src/components/`)
15
+ * its old host-owned mount point (`pages/lib/_body-end-islands.tsx`) no
16
+ * longer exists, and the package's own doc-body-end-islands header comment
17
+ * already calls it "a redundant fallback" once PageLoadingOverlay covers the
18
+ * loading UI. Known gap (documented, out of this generator's scope): the
19
+ * package's SSR-only `<ClientRouter/>` import may still need a "use client"
20
+ * bootstrap to guarantee the click-intercept ships to the browser bundle —
21
+ * same class of issue as tauri's find-in-page island (see tauri.ts). If a
22
+ * project needs it restored, wire a custom island through
23
+ * `settings.chromeBindingsModule`'s `BodyEndIslands` slot.
35
24
  */
36
25
  export const dynamicPageTransitionFeature = () => ({
37
26
  name: "dynamicPageTransition",
38
- injections: [
39
- // 1. Import ClientRouterBootstrap and PageLoadingOverlay.
40
- // Inserted AFTER the `// @slot:body-end-islands:imports` anchor.
41
- {
42
- file: "pages/lib/_body-end-islands.tsx",
43
- anchor: "// @slot:body-end-islands:imports",
44
- position: "after",
45
- content: `import ClientRouterBootstrap from "@/components/client-router-bootstrap";
46
- import { PageLoadingOverlay } from "@takazudo/zudo-doc/page-loading";`,
47
- },
48
- // 2. Stable island marker name for ClientRouterBootstrap.
49
- // Inserted AFTER the `// @slot:body-end-islands:display-names` anchor.
50
- {
51
- file: "pages/lib/_body-end-islands.tsx",
52
- anchor: "// @slot:body-end-islands:display-names",
53
- position: "after",
54
- content: `(ClientRouterBootstrap as { displayName?: string }).displayName =
55
- "ClientRouterBootstrap";`,
56
- },
57
- // 3. Gated island mount + PageLoadingOverlay.
58
- // Inserted AFTER the `{/* @slot:body-end-islands:extra-islands */}` anchor.
59
- // Gated on `settings.dynamicPageTransition` — mirrors the designTokenPanel
60
- // and aiAssistant gating patterns in the same file.
61
- // ClientRouterBootstrap: hydrates on "load" so the SPA-router click
62
- // intercept is registered as soon as the islands runtime mounts the marker
63
- // (zudolab/zudo-doc#1524 W7A fix). PageLoadingOverlay: pure SSR — the
64
- // component emits its overlay div and the inline script that wires
65
- // zfb:before-preparation / zfb:after-swap listeners at runtime.
66
- {
67
- file: "pages/lib/_body-end-islands.tsx",
68
- anchor: "{/* @slot:body-end-islands:extra-islands */}",
69
- position: "after",
70
- content: ` {settings.dynamicPageTransition ? (
71
- <>
72
- {/* Pure SSR — no Island wrap. The component emits its overlay div,
73
- inline styles, and a small inline script that self-wires
74
- zfb:before-preparation / zfb:after-swap listeners at runtime. */}
75
- <PageLoadingOverlay />
76
- {Island({
77
- when: "load",
78
- children: <ClientRouterBootstrap />,
79
- }) as unknown as VNode}
80
- </>
81
- ) : null}`,
82
- },
83
- // 4. Page-loading CSS import — gated at the top-of-file imports anchor.
84
- // `@import` must precede rule blocks in CSS; the `@slot:global-css:imports`
85
- // anchor sits immediately after the content.css import line so this lands
86
- // in a legal position. The CSS itself (overlay, spinner, keyframes) now
87
- // ships as a package artifact `@takazudo/zudo-doc/page-loading.css`
88
- // (see #2283 — Topic A) instead of being duplicated inline here.
89
- {
90
- file: "src/styles/global.css",
91
- anchor: "/* @slot:global-css:imports */",
92
- position: "after",
93
- content: `@import "@takazudo/zudo-doc/page-loading.css";`,
94
- },
95
- // 5. `--color-page-loading-overlay` token — injected inside `@theme`.
96
- // Defined here so each scaffolded project can override it per-project.
97
- // Uses the same `--color-overlay` base token as the dialog backdrops.
98
- {
99
- file: "src/styles/global.css",
100
- anchor: "/* @slot:global-css:theme-tokens */",
101
- position: "after",
102
- content: `--color-page-loading-overlay: color-mix(in oklch, var(--color-overlay) 60%, transparent);`,
103
- },
104
- // 6. View-Transitions CSS (chrome extraction + cross-fade + reduced motion).
105
- // Injected AFTER `/* @slot:global-css:feature-styles */` in global.css.
106
- // These rules are required for SPA page transitions via ClientRouter.
107
- {
108
- file: "src/styles/global.css",
109
- anchor: "/* @slot:global-css:feature-styles */",
110
- position: "after",
111
- content: `/* Root cross-fade for same-document View Transitions (Strategy B).
112
- *
113
- * @view-transition { navigation: auto; } (Strategy A, cross-document) is
114
- * deleted — Strategy B uses <ClientRouter /> which drives same-document
115
- * transitions via document.startViewTransition on each navigation.
116
- *
117
- * Chrome extraction via view-transition-name (Strategy B+, zudolab/zudo-doc#1558):
118
- * <header>, <aside id="desktop-sidebar">, <footer>, and the desktop-sidebar-toggle
119
- * button each carry a data-zfb-transition-persist attribute whose value is keyed
120
- * by locale and nav-section. The four attribute selectors below assign a stable
121
- * view-transition-name based on the attribute value prefix, extracting those
122
- * elements from the root snapshot into their own named layers:
123
- * - header-{lang} → zfb-header
124
- * - sidebar-{locale}-… → zfb-sidebar
125
- * - footer-{lang} → zfb-footer
126
- * - desktop-sidebar-toggle → zfb-sidebar-toggle
127
- *
128
- * Once extracted, the root cross-fade animates only non-chrome content (main,
129
- * article, TOC, etc.). The twelve ::view-transition-{old,new,group}(<name>)
130
- * rules disable animation for all four chrome layers. The group pseudo must be
131
- * neutralised too — even when old/new are static the group container can still
132
- * produce a geometry-morph animation when snapshot size/position differs.
133
- *
134
- * Entry/exit cross-fade (zudolab/zudo-doc#2072): animation: none is only
135
- * correct when the chrome element exists on BOTH pages of a navigation.
136
- * When it exists on one side only (docs page with sidebar → top page
137
- * without), the named group holds a single snapshot — frozen at full
138
- * opacity for the whole transition, then dropped abruptly at finish. The
139
- * :only-child rules below detect that one-sided case and cross-fade the
140
- * lone snapshot in sync with the root content fade (spec-standard
141
- * entry/exit pattern; :only-child outranks the animation: none rules via
142
- * the extra pseudo-class specificity). */
143
-
144
- /* Chrome extraction — assign view-transition-name from data-zfb-transition-persist */
145
- [data-zfb-transition-persist^="header-"] { view-transition-name: zfb-header; }
146
- [data-zfb-transition-persist^="sidebar-"] { view-transition-name: zfb-sidebar; }
147
- [data-zfb-transition-persist^="footer-"] { view-transition-name: zfb-footer; }
148
- [data-zfb-transition-persist="desktop-sidebar-toggle"] { view-transition-name: zfb-sidebar-toggle; }
149
-
150
- /* Disable animation for all four chrome layers (old, new, and group) */
151
- ::view-transition-old(zfb-header),
152
- ::view-transition-new(zfb-header),
153
- ::view-transition-group(zfb-header),
154
- ::view-transition-old(zfb-sidebar),
155
- ::view-transition-new(zfb-sidebar),
156
- ::view-transition-group(zfb-sidebar),
157
- ::view-transition-old(zfb-footer),
158
- ::view-transition-new(zfb-footer),
159
- ::view-transition-group(zfb-footer),
160
- ::view-transition-old(zfb-sidebar-toggle),
161
- ::view-transition-new(zfb-sidebar-toggle),
162
- ::view-transition-group(zfb-sidebar-toggle) { animation: none; }
163
-
164
- /* Entry/exit: chrome element present on one side only (#2072).
165
- * Exit — lone old snapshot fades out with the content cross-fade. */
166
- ::view-transition-old(zfb-header):only-child,
167
- ::view-transition-old(zfb-sidebar):only-child,
168
- ::view-transition-old(zfb-footer):only-child,
169
- ::view-transition-old(zfb-sidebar-toggle):only-child {
170
- animation: 150ms ease-in both contentFadeOut;
171
- }
172
- /* Entry — lone new snapshot fades in with the content cross-fade. */
173
- ::view-transition-new(zfb-header):only-child,
174
- ::view-transition-new(zfb-sidebar):only-child,
175
- ::view-transition-new(zfb-footer):only-child,
176
- ::view-transition-new(zfb-sidebar-toggle):only-child {
177
- animation: 300ms ease-out both contentFadeIn;
178
- }
179
-
180
- /* Root cross-fade rules — animate only the non-chrome snapshot */
181
- ::view-transition-old(root) {
182
- animation: 150ms ease-in both contentFadeOut;
183
- }
184
- ::view-transition-new(root) {
185
- animation: 300ms ease-out both contentFadeIn;
186
- }
187
-
188
- /* Reduced motion: collapse all view-transition animation to an instant
189
- * swap (zudolab/zudo-doc#2086). With every snapshot animation at none the
190
- * transition settles immediately — no cross-fade, no translateY slide.
191
- * Covers the root cross-fade and the #2072 :only-child entry/exit rules;
192
- * the both-sides chrome layers are already animation: none above. Equal
193
- * specificity per selector, but later in source order, so these win.
194
- * ::view-transition-group(root) must be neutralized too (zudolab/zzmod#845):
195
- * the UA animates the root group for its full duration even when the old/new
196
- * root snapshots are at animation: none, leaving a brief frozen,
197
- * non-interactive frame. The chrome groups are already animation: none in the
198
- * unconditional chrome block above, so only the root group is added here. */
199
- @media (prefers-reduced-motion: reduce) {
200
- ::view-transition-old(root),
201
- ::view-transition-new(root),
202
- ::view-transition-group(root),
203
- ::view-transition-old(zfb-header):only-child,
204
- ::view-transition-old(zfb-sidebar):only-child,
205
- ::view-transition-old(zfb-footer):only-child,
206
- ::view-transition-old(zfb-sidebar-toggle):only-child,
207
- ::view-transition-new(zfb-header):only-child,
208
- ::view-transition-new(zfb-sidebar):only-child,
209
- ::view-transition-new(zfb-footer):only-child,
210
- ::view-transition-new(zfb-sidebar-toggle):only-child {
211
- animation: none;
212
- }
213
- }`,
214
- },
215
- ],
27
+ injections: [],
216
28
  });
@@ -2,7 +2,7 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Footer taglist feature.
4
4
  *
5
- * Purely a settings toggle: `settings-gen.ts` emits
5
+ * Purely a settings toggle: `zfb-config-gen.ts` emits
6
6
  * `footer.taglist = { enabled: true, groupBy: "group" }` when selected,
7
7
  * and the footer component (part of the footer pseudo-feature) reads
8
8
  * `settings.footer.taglist` to decide whether to render the column(s).
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Footer taglist feature.
3
3
  *
4
- * Purely a settings toggle: `settings-gen.ts` emits
4
+ * Purely a settings toggle: `zfb-config-gen.ts` emits
5
5
  * `footer.taglist = { enabled: true, groupBy: "group" }` when selected,
6
6
  * and the footer component (part of the footer pseudo-feature) reads
7
7
  * `settings.footer.taglist` to decide whether to render the column(s).
@@ -2,7 +2,8 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Footer pseudo-feature.
4
4
  *
5
- * W7A (#1736): post-cutover, `pages/lib/_footer-with-defaults.tsx` mounts
6
- * the footer unconditionally no doc-layout injection is required.
5
+ * Purely a `zudoDoc({ footer: {...} })` field (see `zfb-config-gen.ts`,
6
+ * triggered by footerNavGroup / footerCopyright / footerTaglist) the
7
+ * footer chrome is fully package-owned. No injection required.
7
8
  */
8
9
  export declare const footerFeature: FeatureModule;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Footer pseudo-feature.
3
3
  *
4
- * W7A (#1736): post-cutover, `pages/lib/_footer-with-defaults.tsx` mounts
5
- * the footer unconditionally no doc-layout injection is required.
4
+ * Purely a `zudoDoc({ footer: {...} })` field (see `zfb-config-gen.ts`,
5
+ * triggered by footerNavGroup / footerCopyright / footerTaglist) the
6
+ * footer chrome is fully package-owned. No injection required.
6
7
  */
7
8
  export const footerFeature = () => ({
8
9
  name: "footer",
@@ -2,16 +2,21 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * i18n feature — gates the locale-prefixed page set.
4
4
  *
5
- * Page templates are shipped under `templates/features/i18n/files/pages/`
6
- * and copied by `composeFeatures copyFeatureFiles` whenever `i18n` is in
7
- * the selected feature set.
5
+ * Locked manifest (#2653 Decision 4, i18n addendum): "i18n ON adds
6
+ * `pages/[locale]/docs/[[...slug]].tsx` (a second doc stub, locale variant)
7
+ * No other pages." That ONE self-contained stub — required for the same
8
+ * injected-DYNAMIC-route dev-mode 404 gap the default-locale stub fixes —
9
+ * is shipped under `templates/features/i18n/files/pages/[locale]/docs/`
10
+ * and copied by `composeFeatures → copyFeatureFiles` whenever `i18n` is
11
+ * selected. The old `pages/[locale]/index.tsx` home-route template is GONE
12
+ * (the locked manifest doesn't want it — the package-owned `/` route
13
+ * already handles every locale via `[[locale]]`-style resolution inside
14
+ * `routes/index.tsx`'s own logic).
8
15
  *
9
- * No injections: header / language-switcher wiring was retired with the
10
- * Astro cutover (#1736 / W7A) — `pages/lib/_header-with-defaults.tsx` now
11
- * gates `LanguageSwitcher` on `Object.keys(settings.locales).length > 0`.
12
- * The pages are locale-agnostic — they iterate `settings.locales` at build
16
+ * No injections: the stub iterates `settings.locales` at build/request
13
17
  * time, so no postProcess regex patching is required for non-default
14
- * languages.
18
+ * languages. Secondary-language content mirrors under
19
+ * `src/content/docs-<lang>/` are seeded by `scaffold.ts`.
15
20
  *
16
21
  * Loud-failure check: per spec-lock Decision 8 (#1737), abort scaffolding
17
22
  * if the feature template dir is missing or empty. Without this guard,
@@ -4,16 +4,21 @@ import { fileURLToPath } from "url";
4
4
  /**
5
5
  * i18n feature — gates the locale-prefixed page set.
6
6
  *
7
- * Page templates are shipped under `templates/features/i18n/files/pages/`
8
- * and copied by `composeFeatures copyFeatureFiles` whenever `i18n` is in
9
- * the selected feature set.
7
+ * Locked manifest (#2653 Decision 4, i18n addendum): "i18n ON adds
8
+ * `pages/[locale]/docs/[[...slug]].tsx` (a second doc stub, locale variant)
9
+ * No other pages." That ONE self-contained stub — required for the same
10
+ * injected-DYNAMIC-route dev-mode 404 gap the default-locale stub fixes —
11
+ * is shipped under `templates/features/i18n/files/pages/[locale]/docs/`
12
+ * and copied by `composeFeatures → copyFeatureFiles` whenever `i18n` is
13
+ * selected. The old `pages/[locale]/index.tsx` home-route template is GONE
14
+ * (the locked manifest doesn't want it — the package-owned `/` route
15
+ * already handles every locale via `[[locale]]`-style resolution inside
16
+ * `routes/index.tsx`'s own logic).
10
17
  *
11
- * No injections: header / language-switcher wiring was retired with the
12
- * Astro cutover (#1736 / W7A) — `pages/lib/_header-with-defaults.tsx` now
13
- * gates `LanguageSwitcher` on `Object.keys(settings.locales).length > 0`.
14
- * The pages are locale-agnostic — they iterate `settings.locales` at build
18
+ * No injections: the stub iterates `settings.locales` at build/request
15
19
  * time, so no postProcess regex patching is required for non-default
16
- * languages.
20
+ * languages. Secondary-language content mirrors under
21
+ * `src/content/docs-<lang>/` are seeded by `scaffold.ts`.
17
22
  *
18
23
  * Loud-failure check: per spec-lock Decision 8 (#1737), abort scaffolding
19
24
  * if the feature template dir is missing or empty. Without this guard,
@@ -31,7 +36,7 @@ export const i18nFeature = (_choices) => {
31
36
  const stat = fs.existsSync(pagesDir) ? fs.statSync(pagesDir) : null;
32
37
  if (!stat || !stat.isDirectory() || fs.readdirSync(pagesDir).length === 0) {
33
38
  throw new Error(`i18n feature template dir is missing or empty: ${pagesDir}\n` +
34
- `Expected to find [locale]/index.tsx.\n` +
39
+ `Expected to find [locale]/docs/[[...slug]].tsx.\n` +
35
40
  `This is a generator bug — please file an issue.`);
36
41
  }
37
42
  return {
@@ -2,31 +2,12 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Image-enlarge feature.
4
4
  *
5
- * W7A (#1736): post-cutover, the image-enlarge island is mounted by the
6
- * pages/lib body-end wrapper (always present; runtime-gated via the
7
- * always-loaded stub-or-real ImageEnlarge component). Image-enlarge CSS
8
- * lives unconditionally in `@takazudo/zudo-doc/features.css` (moved from
9
- * `global.css` in S3 #2348) the selectors only activate when the runtime
10
- * mounts the .zd-enlarge-btn.
11
- *
12
- * S2 (#1825): the server-side figure/button emission was re-implemented via an
13
- * MDX paragraph (p) component override in pages/_mdx-components.ts. Three
14
- * injections installed the ENLARGE_SVG + EnlargeableParagraph code when
15
- * imageEnlarge was enabled.
16
- *
17
- * S8 / #2360 (E2): the full MDX component assembly moved into the
18
- * @takazudo/zudo-doc/mdx-components factory (createMdxComponentsBase). The
19
- * factory reads settings.imageEnlarge at render time and conditionally wraps
20
- * block-level images in <figure class="zd-enlargeable"> — the same logic the
21
- * old injections installed, now handled once inside the package. The three
22
- * _mdx-components.ts injections (enlarge-imports, enlarge-defs,
23
- * enlarge-p-entry) are therefore removed; the feature is a no-op from the
24
- * compose engine's perspective (the runtime gate in the factory is always
25
- * present in the scaffolded template).
26
- *
27
- * S3 (#2348): the ImageEnlarge island and its SSR fallback moved from
28
- * src/components/image-enlarge.tsx into @takazudo/zudo-doc/image-enlarge.
29
- * The pages/lib/_body-end-islands.tsx template now imports them from the
30
- * package, so no file-copy injections exist in this feature module either.
5
+ * Purely a `zudoDoc({ imageEnlarge: true })` field (see `zfb-config-gen.ts`).
6
+ * The island, its SSR fallback, and its CSS are all package-owned
7
+ * (`@takazudo/zudo-doc/image-enlarge`, mounted by
8
+ * `doc-body-end-islands/index.tsx`; CSS from `features.css`) and the MDX
9
+ * `<figure class="zd-enlargeable">` wrapping is handled inside
10
+ * `@takazudo/zudo-doc/mdx-components` at render time, reading
11
+ * `settings.imageEnlarge`. Nothing to inject or copy.
31
12
  */
32
13
  export declare const imageEnlargeFeature: FeatureModule;
@@ -1,32 +1,13 @@
1
1
  /**
2
2
  * Image-enlarge feature.
3
3
  *
4
- * W7A (#1736): post-cutover, the image-enlarge island is mounted by the
5
- * pages/lib body-end wrapper (always present; runtime-gated via the
6
- * always-loaded stub-or-real ImageEnlarge component). Image-enlarge CSS
7
- * lives unconditionally in `@takazudo/zudo-doc/features.css` (moved from
8
- * `global.css` in S3 #2348) the selectors only activate when the runtime
9
- * mounts the .zd-enlarge-btn.
10
- *
11
- * S2 (#1825): the server-side figure/button emission was re-implemented via an
12
- * MDX paragraph (p) component override in pages/_mdx-components.ts. Three
13
- * injections installed the ENLARGE_SVG + EnlargeableParagraph code when
14
- * imageEnlarge was enabled.
15
- *
16
- * S8 / #2360 (E2): the full MDX component assembly moved into the
17
- * @takazudo/zudo-doc/mdx-components factory (createMdxComponentsBase). The
18
- * factory reads settings.imageEnlarge at render time and conditionally wraps
19
- * block-level images in <figure class="zd-enlargeable"> — the same logic the
20
- * old injections installed, now handled once inside the package. The three
21
- * _mdx-components.ts injections (enlarge-imports, enlarge-defs,
22
- * enlarge-p-entry) are therefore removed; the feature is a no-op from the
23
- * compose engine's perspective (the runtime gate in the factory is always
24
- * present in the scaffolded template).
25
- *
26
- * S3 (#2348): the ImageEnlarge island and its SSR fallback moved from
27
- * src/components/image-enlarge.tsx into @takazudo/zudo-doc/image-enlarge.
28
- * The pages/lib/_body-end-islands.tsx template now imports them from the
29
- * package, so no file-copy injections exist in this feature module either.
4
+ * Purely a `zudoDoc({ imageEnlarge: true })` field (see `zfb-config-gen.ts`).
5
+ * The island, its SSR fallback, and its CSS are all package-owned
6
+ * (`@takazudo/zudo-doc/image-enlarge`, mounted by
7
+ * `doc-body-end-islands/index.tsx`; CSS from `features.css`) and the MDX
8
+ * `<figure class="zd-enlargeable">` wrapping is handled inside
9
+ * `@takazudo/zudo-doc/mdx-components` at render time, reading
10
+ * `settings.imageEnlarge`. Nothing to inject or copy.
30
11
  */
31
12
  export const imageEnlargeFeature = () => ({
32
13
  name: "imageEnlarge",
@@ -14,6 +14,7 @@ import { docHistoryFeature } from "./doc-history.js";
14
14
  import { llmsTxtFeature } from "./llms-txt.js";
15
15
  import { claudeResourcesFeature } from "./claude-resources.js";
16
16
  import { designTokenPanelFeature } from "./design-token-panel.js";
17
+ import { themePackSwitcherFeature } from "./theme-pack-switcher.js";
17
18
  import { i18nFeature } from "./i18n.js";
18
19
  import { versioningFeature } from "./versioning.js";
19
20
  import { tauriFeature } from "./tauri.js";
@@ -35,6 +36,7 @@ export const featureModules = {
35
36
  // sidebarFilter — built into sidebar-tree.tsx, stays in base
36
37
  claudeResources: claudeResourcesFeature,
37
38
  designTokenPanel: designTokenPanelFeature,
39
+ themePackSwitcher: themePackSwitcherFeature,
38
40
  sidebarResizer: sidebarResizerFeature,
39
41
  sidebarToggle: sidebarToggleFeature,
40
42
  versioning: versioningFeature,
@@ -2,10 +2,8 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * llms-txt feature.
4
4
  *
5
- * W7A (#1736): the inject() that previously added `<link rel="alternate">`
6
- * tags to `<head>` is dropped per locked spec §6.3 — `_head-with-defaults.tsx`
7
- * does not emit those tags today (neither does host). If a follow-up decides
8
- * to emit them, add the gated `<link>` rendering inside `_head-with-defaults.tsx`
9
- * — not in a feature inject. The plugin itself is wired by `zfb-config-gen.ts`.
5
+ * Purely a `zudoDoc({ llmsTxt: true })` field (see `zfb-config-gen.ts`)
6
+ * `zudoDocPreset()` wires `@takazudo/zudo-doc/plugins/llms-txt` whenever the
7
+ * flag is on. Nothing to inject.
10
8
  */
11
9
  export declare const llmsTxtFeature: FeatureModule;
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * llms-txt feature.
3
3
  *
4
- * W7A (#1736): the inject() that previously added `<link rel="alternate">`
5
- * tags to `<head>` is dropped per locked spec §6.3 — `_head-with-defaults.tsx`
6
- * does not emit those tags today (neither does host). If a follow-up decides
7
- * to emit them, add the gated `<link>` rendering inside `_head-with-defaults.tsx`
8
- * — not in a feature inject. The plugin itself is wired by `zfb-config-gen.ts`.
4
+ * Purely a `zudoDoc({ llmsTxt: true })` field (see `zfb-config-gen.ts`)
5
+ * `zudoDocPreset()` wires `@takazudo/zudo-doc/plugins/llms-txt` whenever the
6
+ * flag is on. Nothing to inject.
9
7
  */
10
8
  export const llmsTxtFeature = () => ({
11
9
  name: "llmsTxt",
@@ -2,8 +2,12 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Search feature.
4
4
  *
5
- * W7A (#1736): post-cutover, `pages/lib/_header-with-defaults.tsx` includes
6
- * the search widget unconditionallyno header injection is required.
7
- * The search-index plugin is still wired by `zfb-config-gen.ts`.
5
+ * The `@takazudo/zudo-doc/plugins/search-index` plugin runs unconditionally
6
+ * (every project gets a `search-index.json`) `zudoDocPreset()` always
7
+ * wires it. This feature only controls whether the header shows the search
8
+ * trigger (`{ type: "component", component: "search" }` in
9
+ * `headerRightItems`, see `zfb-config-gen.ts`) — the widget itself is
10
+ * package-owned (`chrome/derive.tsx`'s `SearchWidget`), so there is nothing
11
+ * left to inject.
8
12
  */
9
13
  export declare const searchFeature: FeatureModule;
@@ -1,9 +1,13 @@
1
1
  /**
2
2
  * Search feature.
3
3
  *
4
- * W7A (#1736): post-cutover, `pages/lib/_header-with-defaults.tsx` includes
5
- * the search widget unconditionallyno header injection is required.
6
- * The search-index plugin is still wired by `zfb-config-gen.ts`.
4
+ * The `@takazudo/zudo-doc/plugins/search-index` plugin runs unconditionally
5
+ * (every project gets a `search-index.json`) `zudoDocPreset()` always
6
+ * wires it. This feature only controls whether the header shows the search
7
+ * trigger (`{ type: "component", component: "search" }` in
8
+ * `headerRightItems`, see `zfb-config-gen.ts`) — the widget itself is
9
+ * package-owned (`chrome/derive.tsx`'s `SearchWidget`), so there is nothing
10
+ * left to inject.
7
11
  */
8
12
  export const searchFeature = () => ({
9
13
  name: "search",
@@ -2,13 +2,9 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Sidebar-resizer feature.
4
4
  *
5
- * W7A (#1736): post-cutover, sidebar resizer pre-paint scripts and runtime
6
- * initialization are runtime-gated on `settings.sidebarResizer` inside the
7
- * pages/lib wrappers — no doc-layout injection is required.
8
- *
9
- * NOTE: spec-lock §6.5 flagged a pre-paint FOUC question for the
10
- * `applySidebarWidth` IIFE that previously ran inline in the layout `<head>`.
11
- * The post-cutover model relies on the pages/lib head wrapper emitting the
12
- * inline script (when present); this feature module no longer injects it.
5
+ * Purely a `zudoDoc({ sidebarResizer: true })` field (see `zfb-config-gen.ts`)
6
+ * the pre-paint script and runtime init are package-owned
7
+ * (`sidebar-resizer/sidebar-resizer-init.tsx`), gated on `settings.sidebarResizer`
8
+ * inside the package's own chrome. Nothing to inject.
13
9
  */
14
10
  export declare const sidebarResizerFeature: FeatureModule;
@@ -1,14 +1,10 @@
1
1
  /**
2
2
  * Sidebar-resizer feature.
3
3
  *
4
- * W7A (#1736): post-cutover, sidebar resizer pre-paint scripts and runtime
5
- * initialization are runtime-gated on `settings.sidebarResizer` inside the
6
- * pages/lib wrappers — no doc-layout injection is required.
7
- *
8
- * NOTE: spec-lock §6.5 flagged a pre-paint FOUC question for the
9
- * `applySidebarWidth` IIFE that previously ran inline in the layout `<head>`.
10
- * The post-cutover model relies on the pages/lib head wrapper emitting the
11
- * inline script (when present); this feature module no longer injects it.
4
+ * Purely a `zudoDoc({ sidebarResizer: true })` field (see `zfb-config-gen.ts`)
5
+ * the pre-paint script and runtime init are package-owned
6
+ * (`sidebar-resizer/sidebar-resizer-init.tsx`), gated on `settings.sidebarResizer`
7
+ * inside the package's own chrome. Nothing to inject.
12
8
  */
13
9
  export const sidebarResizerFeature = () => ({
14
10
  name: "sidebarResizer",
@@ -2,12 +2,12 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Sidebar-toggle feature.
4
4
  *
5
- * W7A (#1736): post-cutover, the sidebar-toggle is hosted by the pages/lib
6
- * wrappers which runtime-gate on `settings.sidebarToggle`. The supporting
7
- * CSS (toggle button positioning, sidebar transitions, `[data-sidebar-hidden]`
8
- * styles) lives unconditionally in `templates/base/src/styles/global.css`
9
- * — the selectors only match when the runtime attaches the
10
- * `data-sidebar-hidden` attribute or mounts the toggle button, so the cost
11
- * to scaffolds without the feature is one inert rule block.
5
+ * Purely a `zudoDoc({ sidebarToggle: true })` field (see `zfb-config-gen.ts`).
6
+ * `DesktopSidebarToggle` is fully package-owned
7
+ * (`@takazudo/zudo-doc/desktop-sidebar-toggle-island`, wired into
8
+ * Header/Sidebar directly) and its CSS ships unconditionally from
9
+ * `@takazudo/zudo-doc/features.css` — the old host re-export shim
10
+ * (`src/components/desktop-sidebar-toggle.tsx`) is gone; nothing to inject
11
+ * or copy.
12
12
  */
13
13
  export declare const sidebarToggleFeature: FeatureModule;
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Sidebar-toggle feature.
3
3
  *
4
- * W7A (#1736): post-cutover, the sidebar-toggle is hosted by the pages/lib
5
- * wrappers which runtime-gate on `settings.sidebarToggle`. The supporting
6
- * CSS (toggle button positioning, sidebar transitions, `[data-sidebar-hidden]`
7
- * styles) lives unconditionally in `templates/base/src/styles/global.css`
8
- * — the selectors only match when the runtime attaches the
9
- * `data-sidebar-hidden` attribute or mounts the toggle button, so the cost
10
- * to scaffolds without the feature is one inert rule block.
4
+ * Purely a `zudoDoc({ sidebarToggle: true })` field (see `zfb-config-gen.ts`).
5
+ * `DesktopSidebarToggle` is fully package-owned
6
+ * (`@takazudo/zudo-doc/desktop-sidebar-toggle-island`, wired into
7
+ * Header/Sidebar directly) and its CSS ships unconditionally from
8
+ * `@takazudo/zudo-doc/features.css` — the old host re-export shim
9
+ * (`src/components/desktop-sidebar-toggle.tsx`) is gone; nothing to inject
10
+ * or copy.
11
11
  */
12
12
  export const sidebarToggleFeature = () => ({
13
13
  name: "sidebarToggle",
@@ -2,13 +2,8 @@ import type { FeatureModule } from "../compose.js";
2
2
  /**
3
3
  * Tag governance feature.
4
4
  *
5
- * Ships the tag audit and suggest scripts. Settings (`tagVocabulary`,
6
- * `tagGovernance`) are emitted by `settings-gen.ts`; devDeps and
7
- * `tags:audit` / `tags:suggest` package scripts are added in
8
- * `scaffold.ts#generatePackageJson`.
9
- *
10
- * `src/config/tag-vocabulary.ts` and `tag-vocabulary-types.ts` stay in the
11
- * base template because `src/utils/tags.ts` and `settings-types.ts` import
12
- * from them regardless of whether governance is on.
5
+ * Writes one project-owned tag vocabulary module. Its named vocabulary export
6
+ * feeds zfb while its default TagCliConfig export feeds both package-owned
7
+ * bins through an explicit `--config` package-script argument.
13
8
  */
14
9
  export declare const tagGovernanceFeature: FeatureModule;