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,292 +1,58 @@
1
- // Pure URL-enumeration helpers shared by both page paths() functions and the
2
- // sitemap. Extracting these prevents the sitemap from drifting out of sync
3
- // with the actual routes the page modules produce.
4
- //
5
- // Each enumerator returns absolute paths (with settings.base prefix and
6
- // trailing slash applied) as expected by the sitemap and page modules.
7
- // `enumerateAllRoutes()` composes the others and returns a deduped
8
- // Map<url, lastmod> that the sitemap renderer wraps directly.
9
- //
10
- // Design principles:
11
- // - Draft pages are always excluded (never built).
12
- // - Unlisted pages ARE included — they have real HTML files and should
13
- // appear in the sitemap even though they're hidden from nav.
14
- // - toRouteSlug() is applied to all entry ids so category index pages
15
- // (e.g. "getting-started/index" → "getting-started") get correct URLs.
16
- // - Auto-generated category index pages (categories without index.mdx) are
17
- // emitted by building the nav tree and calling collectAutoIndexNodes.
18
-
19
- import { loadDocs } from "../_data";
20
- import { mergeLocaleDocs } from "./locale-merge";
21
- import { resolveNavSource, resolveVersionedLocaleSource } from "./_nav-source-docs";
1
+ // Thin stub — route-enumerators moved to the package (epic #2344, S6).
2
+ // Calls `createRouteEnumerators(ctx)` from @takazudo/zudo-doc/route-enumerators
3
+ // with the host singletons injected, then re-exports the resulting enumeration
4
+ // functions so all existing call sites continue to work unchanged.
5
+
6
+ import { createRouteEnumerators } from "@takazudo/zudo-doc/route-enumerators";
7
+ import type {
8
+ DocsEntryForTags,
9
+ TagInfoForEnum,
10
+ DocPageEntry,
11
+ DocNavNode,
12
+ } from "@takazudo/zudo-doc/route-enumerators";
22
13
  import { settings } from "@/config/settings";
23
14
  import { defaultLocale, type Locale } from "@/config/i18n";
24
- import type { VersionConfig } from "@/config/settings";
25
- import type { DocsEntry } from "@/types/docs-entry";
26
- import { docsUrl, versionedDocsUrl, withBase } from "@/utils/base";
15
+ import { docsUrl, versionedDocsUrl, withBase, isDefaultLocaleOnlyPath } from "@/utils/base";
16
+ import { buildNavTree, collectAutoIndexNodes, type CategoryMeta } from "@/utils/docs";
27
17
  import { collectTags } from "@/utils/tags";
28
18
  import { toRouteSlug } from "@/utils/slug";
29
- import {
30
- buildNavTree,
31
- collectAutoIndexNodes,
32
- } from "@/utils/docs";
33
-
34
- // ---------------------------------------------------------------------------
35
- // enumerateDocsRoutes
36
- // ---------------------------------------------------------------------------
37
-
38
- /**
39
- * Enumerate all doc page URLs for a locale.
40
- *
41
- * For the default locale: loads the "docs" collection directly.
42
- * For non-default locales: inlines a locale-first merge — locale docs take
43
- * priority; base EN docs fill in slugs not covered by the locale collection,
44
- * with default-locale-only paths excluded. A nav-tree pass then adds
45
- * auto-generated category index pages.
46
- *
47
- * Applies toRouteSlug so "category/index" entries become "category/" URLs.
48
- * Returns deduplicated URL strings with base prefix and trailing slash.
49
- */
50
- export function enumerateDocsRoutes(locale: string): string[] {
51
- const urls: string[] = [];
52
-
53
- // Identity-stable nav source — same instances the doc routes use, so the
54
- // nav-tree fast-path applies here too (#1902).
55
- const { docs: allDocs, navDocs, categoryMeta } = resolveNavSource(
56
- locale as Locale,
57
- undefined,
58
- { applyDefaultLocaleOnlyFilter: true, keepUnlisted: true },
59
- );
60
- const tree = buildNavTree(navDocs, locale as Locale, categoryMeta);
61
-
62
- for (const doc of allDocs) {
63
- // A `category_no_page` index.mdx is metadata-only — no route, so no sitemap
64
- // URL. Same exclusion the doc-route paths() apply (zfb retains every .mdx
65
- // as a collection entry, so the skip must be explicit).
66
- if (doc.data.category_no_page === true) continue;
67
- // Canonical route slug via the one shared rule (@/utils/slug). `doc.id` is
68
- // already `toRouteSlug(doc.slug)` (bridged through stripIndexSuffix in
69
- // pages/_data.ts), so a bare root index.mdx is "" here → `/docs/` — the
70
- // canonical root URL (#1891). The `toRouteSlug` fallback is a redundant
71
- // safety re-application of the same rule (idempotent on the already-
72
- // stripped id); kept so this enumerator's slug derivation reads as a
73
- // single explicit call to the canonical helper.
74
- urls.push(docsUrl(doc.data.slug ?? toRouteSlug(doc.id), locale as Locale));
75
- }
76
- for (const node of collectAutoIndexNodes(tree)) {
77
- urls.push(docsUrl(node.slug, locale as Locale));
78
- }
79
-
80
- return [...new Set(urls)];
81
- }
82
-
83
- // ---------------------------------------------------------------------------
84
- // enumerateTagsRoutes
85
- // ---------------------------------------------------------------------------
86
-
87
- /**
88
- * Enumerate tag-index and per-tag URLs for a locale.
89
- *
90
- * Uses the same tag map as the tag pages (unlisted + draft excluded) so the
91
- * sitemap lists exactly the same tag pages that get built.
92
- *
93
- * Returns:
94
- * - /docs/tags/ (or /{locale}/docs/tags/)
95
- * - /docs/tags/{tag}/ (or /{locale}/docs/tags/{tag}/) for each unique tag
96
- */
97
- export function enumerateTagsRoutes(locale: string): string[] {
98
- if (!settings.docTags) return [];
99
-
100
- const urls: string[] = [];
101
-
102
- const tagsBase =
103
- locale === defaultLocale ? "/docs/tags" : `/${locale}/docs/tags`;
104
- urls.push(withBase(tagsBase));
105
-
106
- // Collect tags from the same merged doc set the tag pages use.
107
- // Filter unlisted + draft + category_no_page — mirrors the tag [tag].tsx
108
- // pages so the sitemap lists exactly the tag pages that get built (a
109
- // category_no_page index has no route, so a tag it carries must not coin a
110
- // tag page that links back to it). The category_no_page drop happens AFTER
111
- // the locale merge so a locale override carrying the flag first wins the
112
- // merge — pre-merge filtering would let the unflagged base doc resurface.
113
- let docs: DocsEntry[];
114
- if (locale === defaultLocale) {
115
- docs = loadDocs("docs").filter(
116
- (d) => !d.data.unlisted && !d.data.draft && !d.data.category_no_page,
117
- );
118
- } else {
119
- const result = mergeLocaleDocs({
120
- baseDocs: loadDocs("docs").filter((d) => !d.data.draft),
121
- localeDocs: loadDocs(`docs-${locale}`).filter((d) => !d.data.draft),
122
- applyDefaultLocaleOnlyFilter: true,
123
- });
124
- docs = result.docs.filter((d) => !d.data.category_no_page);
125
- }
126
-
127
- const tagMap = collectTags(docs, (id, data) => data.slug ?? toRouteSlug(id));
128
-
129
- for (const tag of tagMap.keys()) {
130
- // Tag segment URL-encoded — these URLs feed the sitemap, which must
131
- // carry well-formed encoded URLs (e.g. "type:guide" → "type%3Aguide").
132
- // Route params (the page paths() functions) stay raw.
133
- const encoded = encodeURIComponent(tag);
134
- const tagPath =
135
- locale === defaultLocale
136
- ? `/docs/tags/${encoded}`
137
- : `/${locale}/docs/tags/${encoded}`;
138
- urls.push(withBase(tagPath));
139
- }
140
-
141
- return urls;
142
- }
143
-
144
- // ---------------------------------------------------------------------------
145
- // enumerateVersionedRoutes
146
- // ---------------------------------------------------------------------------
147
-
148
- /**
149
- * Enumerate doc URLs for a single (version, locale) combination.
150
- *
151
- * For the default locale: loads `docs-v-${version.slug}`.
152
- * For non-default locales: locale-first merge — locale-specific collection
153
- * takes priority; base EN collection fills in pages not yet translated.
154
- * If the locale collection doesn't exist for this version, all pages fall
155
- * back to the EN base (matching the page module's behaviour).
156
- *
157
- * Returns versioned URLs like /v/{version}/docs/{slug}/ or
158
- * /v/{version}/{locale}/docs/{slug}/.
159
- */
160
- export function enumerateVersionedRoutes(
161
- version: VersionConfig,
162
- locale: string,
163
- ): string[] {
164
- const urls: string[] = [];
165
-
166
- if (locale === defaultLocale) {
167
- // Versioned EN base — identity-stable source (#1902).
168
- const { docs: allDocs, navDocs, categoryMeta } = resolveNavSource(
169
- "en",
170
- version.slug,
171
- );
172
- const tree = buildNavTree(navDocs, "en", categoryMeta);
173
-
174
- for (const doc of allDocs) {
175
- // category_no_page index.mdx → no route, no sitemap URL (see paths()).
176
- if (doc.data.category_no_page === true) continue;
177
- const slug = doc.data.slug ?? toRouteSlug(doc.id);
178
- urls.push(versionedDocsUrl(slug, version.slug));
179
- }
180
- for (const node of collectAutoIndexNodes(tree)) {
181
- urls.push(versionedDocsUrl(node.slug, version.slug));
182
- }
183
- } else {
184
- const localeDir = version.locales?.[locale]?.dir;
19
+ import type { DocsEntry } from "@/types/docs-entry";
20
+ import { loadDocs } from "../_data";
21
+ import { resolveNavSource, resolveVersionedLocaleSource } from "./_nav-source-docs";
22
+ import { mergeLocaleDocs } from "./locale-merge";
185
23
 
186
- // Versioned locale source — locale-first merge over the version's EN base
187
- // (identity-stable; #1902).
188
- const { docs: allDocs, navDocs, categoryMeta } = resolveVersionedLocaleSource(
189
- version.slug,
190
- version.docsDir,
24
+ export const {
25
+ enumerateDocsRoutes,
26
+ enumerateTagsRoutes,
27
+ enumerateVersionedRoutes,
28
+ enumerateAllRoutes,
29
+ } = createRouteEnumerators({
30
+ defaultLocale,
31
+ getLocaleKeys: () => Object.keys(settings.locales),
32
+ getVersions: () => settings.versions,
33
+ getDocTags: () => settings.docTags,
34
+ docsUrl,
35
+ versionedDocsUrl,
36
+ withBase,
37
+ loadDocs,
38
+ isDefaultLocaleOnlyPath,
39
+ // The factory describes collectTags / buildNavTree with the package's minimal
40
+ // structural slots (DocsEntryForTags / DocPageEntry / Map<string, unknown> /
41
+ // TagInfoForEnum). The host helpers are typed against the concrete project
42
+ // types (DocsEntry / NavNode / the Locale union / TagInfo) but are runtime-
43
+ // identical, so the stub adapts them with thin casting wrappers at the
44
+ // injection boundary where the host owns the type knowledge.
45
+ collectTags: (docs: DocsEntryForTags[], slugFn): Map<string, TagInfoForEnum> =>
46
+ collectTags(docs as unknown as DocsEntry[], slugFn),
47
+ toRouteSlug,
48
+ buildNavTree: (docs: DocPageEntry[], locale: string, categoryMeta: Map<string, unknown>) =>
49
+ buildNavTree(
50
+ docs as unknown as DocsEntry[],
191
51
  locale as Locale,
192
- localeDir,
193
- { applyDefaultLocaleOnlyFilter: true, keepUnlisted: true },
194
- );
195
- const tree = buildNavTree(navDocs, locale as Locale, categoryMeta);
196
-
197
- for (const doc of allDocs) {
198
- // category_no_page index.mdx → no route, no sitemap URL (see paths()).
199
- if (doc.data.category_no_page === true) continue;
200
- const slug = doc.data.slug ?? toRouteSlug(doc.id);
201
- urls.push(versionedDocsUrl(slug, version.slug, locale as Locale));
202
- }
203
- for (const node of collectAutoIndexNodes(tree)) {
204
- urls.push(versionedDocsUrl(node.slug, version.slug, locale as Locale));
205
- }
206
- }
207
-
208
- return [...new Set(urls)];
209
- }
210
-
211
- // ---------------------------------------------------------------------------
212
- // enumerateAllRoutes
213
- // ---------------------------------------------------------------------------
214
-
215
- /**
216
- * Compose all route enumerators into a deduped Map<url, lastmod>.
217
- *
218
- * Covers:
219
- * - Site root
220
- * - Default-locale docs + tags
221
- * - Per-locale homepages, docs, and tags
222
- * - Versioned EN docs (for each version in settings.versions)
223
- * - Versioned locale docs (for each locale in settings.locales)
224
- *
225
- * The map keys are absolute paths (with settings.base prefix + trailing
226
- * slash). The sitemap renderer prefixes each with settings.siteUrl.
227
- */
228
- export function enumerateAllRoutes(): Map<string, string> {
229
- const today = new Date().toISOString().split("T")[0] ?? "";
230
- const routes = new Map<string, string>();
231
-
232
- function add(url: string): void {
233
- if (!routes.has(url)) {
234
- routes.set(url, today);
235
- }
236
- }
237
-
238
- // Site root
239
- add(withBase("/"));
240
-
241
- // Default locale docs
242
- for (const url of enumerateDocsRoutes(defaultLocale)) {
243
- add(url);
244
- }
245
-
246
- // Default locale tags
247
- for (const url of enumerateTagsRoutes(defaultLocale)) {
248
- add(url);
249
- }
250
-
251
- // Non-default locales
252
- for (const locale of Object.keys(settings.locales)) {
253
- add(withBase(`/${locale}`));
254
-
255
- for (const url of enumerateDocsRoutes(locale)) {
256
- add(url);
257
- }
258
-
259
- for (const url of enumerateTagsRoutes(locale)) {
260
- add(url);
261
- }
262
- }
263
-
264
- // Versions listing pages — /docs/versions/ and /{locale}/docs/versions/.
265
- // These static utility pages are built by pages/docs/versions.tsx and
266
- // pages/[locale]/docs/versions.tsx whenever versioning is configured.
267
- // They are not part of any content collection so they are added explicitly.
268
- if (settings.versions) {
269
- add(withBase("/docs/versions"));
270
- for (const locale of Object.keys(settings.locales)) {
271
- add(withBase(`/${locale}/docs/versions`));
272
- }
273
- }
274
-
275
- // Versioned docs
276
- if (settings.versions) {
277
- for (const version of settings.versions as VersionConfig[]) {
278
- for (const url of enumerateVersionedRoutes(version, defaultLocale)) {
279
- add(url);
280
- }
281
- // Non-default locales always have versioned pages (they fall back to EN
282
- // when a locale-specific collection is not configured).
283
- for (const locale of Object.keys(settings.locales)) {
284
- for (const url of enumerateVersionedRoutes(version, locale)) {
285
- add(url);
286
- }
287
- }
288
- }
289
- }
290
-
291
- return routes;
292
- }
52
+ categoryMeta as Map<string, CategoryMeta>,
53
+ ) as DocNavNode[],
54
+ collectAutoIndexNodes,
55
+ resolveNavSource,
56
+ resolveVersionedLocaleSource,
57
+ mergeLocaleDocs,
58
+ });
@@ -1,12 +1,11 @@
1
- "use client";
2
-
3
- // W6A stub — no-op default export.
1
+ // W6A stub — no-op default + AiChatModal named exports.
4
2
  //
5
- // The host (zudo-doc showcase) ships a full AI-chat modal island here. In
6
- // generated downstream projects the AI assistant is gated behind a future
7
- // settings flag; for now the file exists so unconditional page imports
8
- // (`pages/lib/_body-end-islands.tsx`) resolve, and the component renders
9
- // nothing. Wire a real implementation by replacing this file.
3
+ // The AiChatModal island now ships from the package
4
+ // (`@takazudo/zudo-doc/ai-chat-modal`); the unconditional
5
+ // `pages/lib/_body-end-islands.tsx` imports it directly from there. This
6
+ // stub exists only so any project-local code that references the
7
+ // `@/components/ai-chat-modal` path still resolves it renders nothing.
8
+ // Replace this file to wire a project-specific modal.
10
9
  import type { JSX } from "preact";
11
10
 
12
11
  function AiChatModal(): JSX.Element | null {
@@ -15,3 +14,5 @@ function AiChatModal(): JSX.Element | null {
15
14
  AiChatModal.displayName = "AiChatModal";
16
15
 
17
16
  export default AiChatModal;
17
+
18
+ export { AiChatModal };
@@ -1,76 +1,3 @@
1
- import { Tabs } from "@takazudo/zudo-doc/code-syntax";
2
- import { TabItem } from "@takazudo/zudo-doc/tab-item";
3
-
4
- /**
5
- * Adapter for zfb's `:::code-group` directive (codeTabs Option A).
6
- *
7
- * zfb emits:
8
- * <CodeGroup tabs={["label1", "label2", ...]}>
9
- * <pre data-lang="ts">{RAW code text}</pre>
10
- * <pre data-lang="js">{RAW code text}</pre>
11
- * </CodeGroup>
12
- *
13
- * The existing <Tabs>/<TabItem> UI uses a children-based API, so this
14
- * component zips the `tabs` label array with the `<pre data-lang>` children
15
- * by index, wrapping each in a <TabItem> with the matching label.
16
- *
17
- * Code inside each <pre> is raw text (NOT syntect-highlighted — the Rust
18
- * pipeline does not run highlight inside code-group fences). We render
19
- * the <pre> inside a <TabItem> with explicit styling via Tailwind tokens
20
- * so it looks like a code block visually.
21
- *
22
- * TabsInit (the companion init script) is present in the layout
23
- * (packages/zudo-doc/src/doclayout/doc-layout-with-defaults.tsx line 433)
24
- * — we rely on it being there; no duplicate needed here.
25
- */
26
-
27
- type Props = {
28
- tabs?: string[];
29
- children?: React.ReactNode;
30
- [key: string]: unknown;
31
- };
32
-
33
- function toArray(children: React.ReactNode): React.ReactNode[] {
34
- if (!children) return [];
35
- if (Array.isArray(children)) return children;
36
- return [children];
37
- }
38
-
39
- export function CodeGroup({ tabs = [], children, name }: Props) {
40
- const childArray = toArray(children);
41
-
42
- // Zip tabs labels with pre children by index. Extra children beyond the
43
- // tabs array (shouldn't happen in normal zfb output) are ignored.
44
- const items = tabs.map((label, i) => {
45
- const child = childArray[i];
46
- return { label, child };
47
- });
48
-
49
- if (items.length === 0) {
50
- // Degenerate: no tabs — render children as-is.
51
- return <>{children}</>;
52
- }
53
-
54
- // zfb forwards `:::code-group{name="x"}` as the `name` prop; Tabs persists
55
- // the active tab per group via `groupId`.
56
- const groupId = typeof name === "string" ? name : undefined;
57
-
58
- return (
59
- <Tabs groupId={groupId}>
60
- {items.map(({ label, child }, i) => (
61
- // value is suffixed with the index so two fences sharing a label
62
- // (e.g. both titled "ts") get distinct stable tab identities; the
63
- // label stays the visible text.
64
- <TabItem
65
- key={`${label}-${i}`}
66
- label={label}
67
- value={`${label}-${i}`}
68
- default={i === 0 ? true : undefined}
69
- >
70
- {/* Raw-code <pre> from zfb: apply code-block visual treatment via tokens */}
71
- <div class="code-group-panel">{child}</div>
72
- </TabItem>
73
- ))}
74
- </Tabs>
75
- );
76
- }
1
+ // Re-export from the shared package — moved to @takazudo/zudo-doc/code-group
2
+ // as part of the package-first migration (epic #2321, S4 #2327).
3
+ export { CodeGroup } from "@takazudo/zudo-doc/code-group";
@@ -1,56 +1,4 @@
1
- /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
- // Real Preact bindings for the admonition MDX tags (Note/Tip/Info/Warning/
4
- // Danger/Caution/Important). Replaces the former literal-VNode
5
- // `makeAdmonitionStub` in pages/_mdx-components.ts with a proper typed
6
- // component — restoring the first-class admonition components the Astro-era
7
- // theme shipped (zudolab/zudo-doc#1456), now on the zfb pipeline.
8
- //
9
- // These tags arrive from two zfb features: the `directives` map emits them from
10
- // `:::note` directives, and `githubAlerts` emits Important/Caution from
11
- // `[!IMPORTANT]`/`[!CAUTION]` blockquotes. The JSX form `<Note title="…">` is
12
- // also authored directly in MDX. All three paths render through here.
13
- //
14
- // Markup contract — KEEP STABLE. The structure below
15
- // <div data-admonition="<variant>" class="admonition admonition-<variant>">
16
- // <p class="admonition-title">…</p>
17
- // <div class="admonition-body">…</div>
18
- // </div>
19
- // is the hook both the design-system CSS (`.admonition-<variant>` rules in
20
- // src/styles/global.css) and the e2e smoke spec (e2e/smoke-admonitions.spec.ts)
21
- // target. The per-variant color + icon live in CSS keyed off `data-admonition`,
22
- // so this component stays presentation-agnostic.
23
- import type { ComponentChildren, VNode } from "preact";
24
-
25
- export type AdmonitionVariant =
26
- | "note"
27
- | "tip"
28
- | "info"
29
- | "warning"
30
- | "danger"
31
- | "caution"
32
- | "important";
33
-
34
- export interface AdmonitionProps {
35
- /** Custom title; falls back to the capitalized variant name (e.g. "Note"). */
36
- title?: string;
37
- children?: ComponentChildren;
38
- }
39
-
40
- /**
41
- * Build the admonition component for a single variant. The title row is always
42
- * rendered — defaulting to the capitalized variant name when the author gives
43
- * no `title` — matching the Astro reference where every callout shows a title.
44
- */
45
- export function makeAdmonition(variant: AdmonitionVariant) {
46
- const defaultTitle = variant.charAt(0).toUpperCase() + variant.slice(1);
47
- return function Admonition({ title, children }: AdmonitionProps): VNode {
48
- const heading = title && title.length > 0 ? title : defaultTitle;
49
- return (
50
- <div data-admonition={variant} class={`admonition admonition-${variant}`}>
51
- <p class="admonition-title">{heading}</p>
52
- <div class="admonition-body">{children}</div>
53
- </div>
54
- );
55
- };
56
- }
1
+ // Re-export from the shared package — moved to @takazudo/zudo-doc/content-admonition
2
+ // as part of the package-first migration (epic #2321, S4 #2327).
3
+ export type { AdmonitionVariant, AdmonitionProps } from "@takazudo/zudo-doc/content-admonition";
4
+ export { makeAdmonition } from "@takazudo/zudo-doc/content-admonition";
@@ -1,13 +1,14 @@
1
- "use client";
2
-
3
1
  // W6A stub — no-op default + DocHistory named exports.
4
2
  //
5
- // When the docHistory feature is enabled, the feature template
6
- // overwrites this file with the real island. Generated projects
7
- // without the feature ship the no-op so the unconditional
8
- // `pages/lib/_doc-history-area` import resolves. The host module also
9
- // exposes `DocHistory` as a named export, so the stub mirrors both
10
- // shapes to keep the import surface stable.
3
+ // The real DocHistory island now ships from the package
4
+ // (`@takazudo/zudo-doc/doc-history`); the unconditional
5
+ // `pages/lib/_doc-history-area.tsx` imports it directly from there. When
6
+ // the docHistory feature is enabled the feature template overwrites this
7
+ // file with a re-export shim pointing at the package island. Generated
8
+ // projects without the feature ship this no-op so any project-local code
9
+ // that references the `@/components/doc-history` path still resolves. The
10
+ // host module exposes `DocHistory` as a named export, so the stub mirrors
11
+ // both the default and named shapes to keep the import surface stable.
11
12
  import type { JSX } from "preact";
12
13
 
13
14
  function DocHistoryComponent(): JSX.Element | null {
@@ -1,12 +1,13 @@
1
- "use client";
2
-
3
- // W6A stub — no-op default + ImageEnlargeSsrFallback named exports.
1
+ // W6A stub — no-op default + ImageEnlarge / ImageEnlargeSsrFallback exports.
4
2
  //
5
- // When the imageEnlarge feature is enabled, the feature template
6
- // overwrites this file with the real island and SSR fallback. Generated
7
- // projects without the feature ship the no-op so the unconditional
8
- // `pages/lib/_body-end-islands` import resolves (the body-end islands
9
- // renderer references both the default and the SSR fallback).
3
+ // The real ImageEnlarge island and its SSR fallback now ship from the
4
+ // package (`@takazudo/zudo-doc/image-enlarge`); the unconditional
5
+ // `pages/lib/_body-end-islands.tsx` imports them directly from there. When
6
+ // the imageEnlarge feature is enabled the feature template overwrites this
7
+ // file with a re-export shim pointing at the package island. Generated
8
+ // projects without the feature ship this no-op so any project-local code
9
+ // that references the `@/components/image-enlarge` path still resolves (the
10
+ // body-end renderer references both the default and the SSR fallback).
10
11
  import type { JSX } from "preact";
11
12
 
12
13
  function ImageEnlarge(): JSX.Element | null {
@@ -16,6 +17,8 @@ ImageEnlarge.displayName = "ImageEnlarge";
16
17
 
17
18
  export default ImageEnlarge;
18
19
 
20
+ export { ImageEnlarge };
21
+
19
22
  export function ImageEnlargeSsrFallback(): JSX.Element | null {
20
23
  return null;
21
24
  }