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,220 +0,0 @@
1
- "use client";
2
-
3
- // Use preact hook entrypoints directly — the "react" → "preact/compat" alias
4
- // lets us consume React-typed components in this Preact app (configured
5
- // project-wide). Same pattern as src/components/sidebar-tree.tsx.
6
- import { useState } from "preact/hooks";
7
- import type { NavNode } from "@/utils/docs";
8
- import { INDENT, connectorLeft, ConnectorLines, CategoryLinkIcon } from "./tree-nav-shared";
9
- import { ChevronRight } from "@takazudo/zudo-doc/icons";
10
-
11
- // site-tree-nav uses wider padding than the narrow sidebar
12
- const SITE_BASE_PAD = "clamp(0.5rem, 0.8vw, 1rem)";
13
-
14
- function padLeft(depth: number): string {
15
- if (depth === 0) return SITE_BASE_PAD;
16
- return `calc(${depth} * ${INDENT} + 1.25rem + 5px)`;
17
- }
18
-
19
- function reorderTree(tree: NavNode[], order: string[]): NavNode[] {
20
- const map = new Map(tree.map((node) => [node.slug, node]));
21
- const ordered: NavNode[] = [];
22
- for (const slug of order) {
23
- const node = map.get(slug);
24
- if (node) {
25
- ordered.push(node);
26
- map.delete(slug);
27
- }
28
- }
29
- // append unmatched nodes at end
30
- for (const node of map.values()) {
31
- ordered.push(node);
32
- }
33
- return ordered;
34
- }
35
-
36
- interface SiteTreeNavProps {
37
- tree: NavNode[];
38
- ariaLabel?: string;
39
- categoryOrder?: string[];
40
- categoryIgnore?: string[];
41
- }
42
-
43
- export default function SiteTreeNav({
44
- tree,
45
- ariaLabel = "Site index",
46
- categoryOrder,
47
- categoryIgnore,
48
- }: SiteTreeNavProps) {
49
- let processedTree = tree;
50
- if (categoryIgnore) {
51
- const ignoreSet = new Set(categoryIgnore);
52
- processedTree = processedTree.filter((node) => !ignoreSet.has(node.slug));
53
- }
54
- if (categoryOrder) {
55
- processedTree = reorderTree(processedTree, categoryOrder);
56
- }
57
- return (
58
- <nav
59
- aria-label={ariaLabel}
60
- data-site-nav
61
- className="grid gap-vsp-md"
62
- style={{
63
- gridTemplateColumns: "repeat(auto-fill, minmax(min(18rem, 100%), 1fr))",
64
- }}
65
- >
66
- {processedTree.map((node) => (
67
- <div key={node.slug} className="min-w-0 border border-muted pl-hsp-sm py-vsp-2xs">
68
- {node.children.length > 0 ? (
69
- <CategoryNode node={node} depth={0} isLast={true} />
70
- ) : (
71
- <LeafNode node={node} depth={0} isLast={true} />
72
- )}
73
- </div>
74
- ))}
75
- </nav>
76
- );
77
- }
78
-
79
- function NodeList({ nodes, depth }: { nodes: NavNode[]; depth: number }) {
80
- return (
81
- <>
82
- {nodes.map((node, index) => {
83
- const isLast = index === nodes.length - 1;
84
- return node.children.length > 0 ? (
85
- <CategoryNode
86
- key={node.slug}
87
- node={node}
88
- depth={depth}
89
- isLast={isLast}
90
- />
91
- ) : (
92
- <LeafNode
93
- key={node.slug}
94
- node={node}
95
- depth={depth}
96
- isLast={isLast}
97
- />
98
- );
99
- })}
100
- </>
101
- );
102
- }
103
-
104
- function CategoryNode({
105
- node,
106
- depth,
107
- isLast,
108
- }: {
109
- node: NavNode;
110
- depth: number;
111
- isLast: boolean;
112
- }) {
113
- const [open, setOpen] = useState(true);
114
- const toggle = () => setOpen((prev) => !prev);
115
- const paddingLeft = padLeft(depth);
116
-
117
- return (
118
- <div className={`${depth >= 1 && !isLast ? "relative" : ""}`}>
119
- {depth >= 1 && !isLast && open && (
120
- <div
121
- className="absolute border-l border-dashed border-muted z-local-1"
122
- style={{
123
- left: connectorLeft(depth),
124
- top: 0,
125
- bottom: 0,
126
- }}
127
- />
128
- )}
129
- <div className="relative">
130
- <ConnectorLines
131
- depth={depth}
132
- isLast={isLast}
133
- widthScale={2}
134
- topPad="calc(0.15rem + var(--spacing-vsp-xs))"
135
- />
136
- <div
137
- className="flex w-full items-center justify-between text-small font-semibold pt-[0.15rem] text-fg"
138
- style={{ paddingLeft }}
139
- >
140
- {node.href ? (
141
- <a
142
- href={node.href}
143
- className="flex-1 flex items-start gap-hsp-xs py-vsp-xs text-fg hover:text-accent hover:underline focus:underline"
144
- >
145
- {depth === 0 && (
146
- <span className="flex h-[1lh] items-center">
147
- <CategoryLinkIcon className="w-[18px] 2xl:w-[24px]" />
148
- </span>
149
- )}
150
- {node.label}
151
- </a>
152
- ) : (
153
- <button
154
- type="button"
155
- onClick={toggle}
156
- className="flex-1 py-vsp-xs text-left hover:text-accent hover:underline focus:underline"
157
- >
158
- {node.label}
159
- </button>
160
- )}
161
- <button
162
- type="button"
163
- onClick={toggle}
164
- className="aspect-square flex items-center justify-center w-[1.75rem] border-y border-l border-muted hover:underline focus:underline"
165
- aria-expanded={open}
166
- aria-label={open ? `Collapse ${node.label}` : `Expand ${node.label}`}
167
- >
168
- <ChevronRight className={`h-icon-xs w-icon-xs transition-transform duration-150 ${open ? "rotate-90" : ""} text-muted`} />
169
- </button>
170
- </div>
171
- </div>
172
- {open && (
173
- <div>
174
- <NodeList nodes={node.children} depth={depth + 1} />
175
- </div>
176
- )}
177
- </div>
178
- );
179
- }
180
-
181
- function LeafNode({
182
- node,
183
- depth,
184
- isLast,
185
- }: {
186
- node: NavNode;
187
- depth: number;
188
- isLast: boolean;
189
- }) {
190
- if (!node.href) return null;
191
- const isRoot = depth === 0;
192
- const paddingLeft = padLeft(depth);
193
-
194
- const topPad = isRoot
195
- ? "calc(var(--spacing-vsp-xs) + 0.15rem)"
196
- : "var(--spacing-vsp-2xs)";
197
-
198
- return (
199
- <div>
200
- <div className="relative">
201
- <ConnectorLines depth={depth} isLast={isLast} widthScale={2} topPad={topPad} />
202
- <a
203
- href={node.href}
204
- className={isRoot
205
- ? "flex items-start gap-hsp-xs py-[calc(var(--spacing-vsp-xs)+0.15rem)] text-small font-semibold text-fg hover:text-accent hover:underline focus:underline"
206
- : `block py-vsp-2xs ${isLast ? "pb-vsp-xs" : ""} text-small text-fg hover:text-accent hover:underline focus:underline`
207
- }
208
- style={{ paddingLeft }}
209
- >
210
- {isRoot && (
211
- <span className="flex h-[1lh] items-center">
212
- <CategoryLinkIcon className="w-[18px] 2xl:w-[24px]" />
213
- </span>
214
- )}
215
- {node.label}
216
- </a>
217
- </div>
218
- </div>
219
- );
220
- }
@@ -1,47 +0,0 @@
1
- // @ts-check
2
- // zfb plugin module: claude-resources.
3
- //
4
- // Wires `runClaudeResourcesPreStep` (from
5
- // `@takazudo/zudo-doc/integrations/claude-resources`) into zfb's
6
- // `preBuild` lifecycle hook.
7
- //
8
- // Previously this shim spawned a `tsx` subprocess because the integration
9
- // package shipped only TypeScript source (no build step) and `gray-matter`
10
- // pulled in a CJS `require("fs")` that esbuild's ESM-only config-loader
11
- // bundle could not satisfy. Both constraints are now lifted: the package
12
- // ships compiled `dist/` and the plugin host is plain Node (not an esbuild
13
- // bundle), so the runner can be imported directly.
14
-
15
- /** @import { ZfbBuildHookContext, ZfbPlugin } from "@takazudo/zfb/plugins" */
16
-
17
- import { runClaudeResourcesPreStep } from "@takazudo/zudo-doc/integrations/claude-resources";
18
-
19
- const PLUGIN_NAME = "@takazudo/zudo-doc-claude-resources";
20
-
21
- /** @type {ZfbPlugin} */
22
- export default {
23
- name: PLUGIN_NAME,
24
-
25
- /** @param {ZfbBuildHookContext} ctx */
26
- async preBuild(ctx) {
27
- const claudeDir = ctx.options["claudeDir"];
28
- if (typeof claudeDir !== "string" || claudeDir.length === 0) {
29
- throw new Error(
30
- `[${PLUGIN_NAME}] preBuild: options.claudeDir must be a non-empty string (got ${JSON.stringify(claudeDir)})`,
31
- );
32
- }
33
- const projectRootOpt = ctx.options["projectRoot"];
34
- const docsDirOpt = ctx.options["docsDir"];
35
- const result = await runClaudeResourcesPreStep({
36
- claudeDir,
37
- projectRoot:
38
- typeof projectRootOpt === "string" ? projectRootOpt : ctx.projectRoot,
39
- docsDir: typeof docsDirOpt === "string" ? docsDirOpt : "src/content/docs",
40
- });
41
- // Surface a one-line summary so build logs make the generation
42
- // observable.
43
- ctx.logger.info(
44
- `claude-resources: ${result.claudemd} CLAUDE.md, ${result.commands} commands, ${result.skills} skills, ${result.agents} agents`,
45
- );
46
- },
47
- };
@@ -1,111 +0,0 @@
1
- // @ts-check
2
- // zfb plugin module: doc-history.
3
- //
4
- // Wires three lifecycle hooks for the doc-history integration:
5
- //
6
- // preBuild — emits `.zfb/doc-history-meta.json` (per-page git metadata
7
- // consumed at bundle time). Calls `runDocHistoryMetaStep`
8
- // directly; honours `SKIP_DOC_HISTORY=1` via the runner's
9
- // own env check.
10
- //
11
- // postBuild — invokes `runDocHistoryPostBuild` to write
12
- // `<outDir>/doc-history/<slug>.json` files. Skipped by default
13
- // on local builds (opt in with `GEN_DOC_HISTORY=1`); always runs
14
- // in CI; `SKIP_DOC_HISTORY=1` suppresses it everywhere. The
15
- // gating lives in `shouldGeneratePostBuild` (#1986).
16
- //
17
- // devMiddleware — reverse-proxies `/doc-history/*` requests to the
18
- // standalone `@takazudo/zudo-doc-history-server` on port 4322.
19
- //
20
- // Inline functions are not supported by zfb's plugin runtime — see
21
- // `@takazudo/zfb/plugins` source comment. Plugins must be authored as
22
- // standalone modules and referenced from `zfb.config.ts` by `name`.
23
-
24
- /** @import { ZfbBuildHookContext, ZfbDevMiddlewareContext, ZfbPlugin } from "@takazudo/zfb/plugins" */
25
-
26
- import { runDocHistoryMetaStep } from "@takazudo/zudo-doc/integrations/doc-history";
27
- import { runDocHistoryPostBuild } from "@takazudo/zudo-doc/integrations/doc-history";
28
- import { createDocHistoryDevMiddleware } from "@takazudo/zudo-doc/integrations/doc-history";
29
- import { connectToZfbHandler } from "./connect-adapter.mjs";
30
-
31
- /** @type {ZfbPlugin} */
32
- export default {
33
- name: "doc-history",
34
-
35
- /** @param {ZfbBuildHookContext} ctx */
36
- async preBuild(ctx) {
37
- const { docsDir, locales } = ctx.options;
38
- // Validate each locale entry before passing downstream so a misconfigured
39
- // locales map surfaces a clear error instead of a confusing runtime crash.
40
- if (locales != null) {
41
- for (const [key, entry] of Object.entries(/** @type {Record<string, unknown>} */ (locales))) {
42
- if (
43
- entry == null ||
44
- typeof entry !== "object" ||
45
- typeof /** @type {any} */ (entry).dir !== "string" ||
46
- /** @type {any} */ (entry).dir.length === 0
47
- ) {
48
- throw new Error(
49
- `[doc-history] invalid locales entry "${key}": expected { dir: string }`,
50
- );
51
- }
52
- }
53
- }
54
- await runDocHistoryMetaStep({
55
- projectRoot: ctx.projectRoot,
56
- docsDir: typeof docsDir === "string" ? docsDir : "src/content/docs",
57
- locales:
58
- locales != null
59
- ? /** @type {Record<string,{dir:string}>} */ (locales)
60
- : undefined,
61
- });
62
- },
63
-
64
- /** @param {ZfbBuildHookContext} ctx */
65
- async postBuild(ctx) {
66
- try {
67
- await runDocHistoryPostBuild(
68
- /** @type {import("@takazudo/zudo-doc/integrations/doc-history").DocHistoryOptions} */ (/** @type {unknown} */ (ctx.options)),
69
- { outDir: ctx.outDir, logger: ctx.logger },
70
- );
71
- } catch (err) {
72
- // postBuild dropdown JSON is redundant: the parallel build-history CI job
73
- // is the deployed source of truth. Downgrade failures to a warning so a
74
- // transient git/CLI error does not red the build-site job.
75
- const msg = err instanceof Error ? err.message : String(err);
76
- if (ctx.logger?.warn) {
77
- ctx.logger.warn(`[doc-history] postBuild failed (non-fatal): ${msg}`);
78
- } else {
79
- console.warn(`[doc-history] postBuild failed (non-fatal): ${msg}`);
80
- }
81
- }
82
- },
83
-
84
- /** @param {ZfbDevMiddlewareContext} ctx */
85
- devMiddleware(ctx) {
86
- const middleware = createDocHistoryDevMiddleware(
87
- /** @type {import("@takazudo/zudo-doc/integrations/doc-history").DocHistoryOptions} */ (/** @type {unknown} */ (ctx.options)),
88
- ctx.logger,
89
- );
90
- // zfb's `register(path, handler)` matches against the FULL request
91
- // URL (no base-stripping). For a non-root base (e.g. "/my-docs/"),
92
- // requests arrive as `/my-docs/doc-history/foo.json`, so we register
93
- // at the full base-prefixed route. For base="/", the prefix is empty
94
- // and the route is `/doc-history` as expected. The v2 middleware
95
- // itself is base-tolerant (matches via `url.includes("/doc-history/")`)
96
- // and slices from `/doc-history/` onward when proxying upstream.
97
- const basePrefix = stripTrailingSlash(
98
- typeof ctx.options["base"] === "string" ? ctx.options["base"] : "",
99
- );
100
- ctx.register(`${basePrefix}/doc-history`, connectToZfbHandler(middleware));
101
- },
102
- };
103
-
104
- /**
105
- * @param {string} s
106
- * @returns {string}
107
- */
108
- function stripTrailingSlash(s) {
109
- if (typeof s !== "string" || s.length === 0) return "";
110
- return s.endsWith("/") ? s.slice(0, -1) : s;
111
- }
@@ -1,59 +0,0 @@
1
- /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
- // Page module for the locale-prefixed per-tag detail route.
4
- //
5
- // Non-default-locale per-tag detail page. paths() enumerates every
6
- // (locale, tag) combination across all non-default locales defined in
7
- // settings.locales, using the same locale-first + base-fallback merge
8
- // strategy as the Astro original.
9
- //
10
- // paths() contract (zfb ADR-004 — synchronous):
11
- // params: { locale: string; tag: string }
12
- // props: { tagInfo: TagInfo }
13
- //
14
- // Tag collection + rendering are shared with the default-locale route via
15
- // pages/lib/_tag-pages.tsx (#2010) — this file owns only the param shape.
16
- // Fallback strategy: see pages/lib/locale-merge.ts for full details.
17
-
18
- import { settings } from "@/config/settings";
19
- import type { TagInfo } from "@/utils/tags";
20
- import type { JSX } from "preact";
21
- import { collectTagMapForLocale, TagDetailPageView } from "../../../lib/_tag-pages";
22
-
23
- export const frontmatter = { title: "Tag" };
24
-
25
- /** One route per (non-default locale, tag) pair. */
26
- export function paths(): Array<{
27
- params: { locale: string; tag: string };
28
- props: { tagInfo: TagInfo };
29
- }> {
30
- const result: Array<{
31
- params: { locale: string; tag: string };
32
- props: { tagInfo: TagInfo };
33
- }> = [];
34
-
35
- for (const locale of Object.keys(settings.locales)) {
36
- const tagMap = collectTagMapForLocale(locale);
37
-
38
- for (const [tag, tagInfo] of tagMap.entries()) {
39
- result.push({
40
- params: { locale, tag },
41
- props: { tagInfo },
42
- });
43
- }
44
- }
45
-
46
- return result;
47
- }
48
-
49
- interface PageProps {
50
- params: { locale: string; tag: string };
51
- tagInfo: TagInfo;
52
- }
53
-
54
- export default function LocaleDocTagPage({
55
- params,
56
- tagInfo,
57
- }: PageProps): JSX.Element {
58
- return <TagDetailPageView locale={params.locale} tag={params.tag} tagInfo={tagInfo} />;
59
- }
@@ -1,39 +0,0 @@
1
- /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
- // Page module for the locale-prefixed "All Tags" index route.
4
- //
5
- // Non-default-locale "All Tags" index page. paths() emits one route per
6
- // locale defined in settings.locales (the default locale has no prefix — it
7
- // is handled by pages/docs/tags/index.tsx). The component recomputes the tag
8
- // map at render time using a locale-doc + base-doc fallback strategy
9
- // (see pages/lib/locale-merge.ts for the merge logic).
10
- //
11
- // Tag collection + rendering are shared with the default-locale route via
12
- // pages/lib/_tag-pages.tsx (#2010).
13
- //
14
- // paths() contract (zfb ADR-004 — synchronous):
15
- // params: { locale: string }
16
- // props: (none — tag map computed at render time)
17
-
18
- import { settings } from "@/config/settings";
19
- import type { JSX } from "preact";
20
- import { TagsIndexPageView } from "../../../lib/_tag-pages";
21
-
22
- export const frontmatter = { title: "All Tags" };
23
-
24
- /** One route per non-default locale (prefixDefaultLocale: false). */
25
- export function paths(): Array<{ params: { locale: string } }> {
26
- return Object.keys(settings.locales).map((locale) => ({
27
- params: { locale },
28
- }));
29
- }
30
-
31
- interface PageProps {
32
- params: { locale: string };
33
- }
34
-
35
- export default function LocaleTagsIndexPage({
36
- params,
37
- }: PageProps): JSX.Element {
38
- return <TagsIndexPageView locale={params.locale} />;
39
- }
@@ -1,43 +0,0 @@
1
- /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
- // Page module for the default-locale per-tag detail route.
4
- //
5
- // Default-locale per-tag detail page. paths() enumerates one route per
6
- // unique tag in the "docs" collection and passes the resolved TagInfo as a
7
- // prop so the component has zero extra collection reads at render time.
8
- //
9
- // paths() contract (zfb ADR-004 — synchronous):
10
- // params: { tag: string }
11
- // props: { tagInfo: TagInfo }
12
- //
13
- // Tag collection + rendering are shared with the locale-prefixed route via
14
- // pages/lib/_tag-pages.tsx (#2010) — this file owns only the param shape.
15
-
16
- import { defaultLocale } from "@/config/i18n";
17
- import type { TagInfo } from "@/utils/tags";
18
- import type { JSX } from "preact";
19
- import { collectTagMapForLocale, TagDetailPageView } from "../../lib/_tag-pages";
20
-
21
- export const frontmatter = { title: "Tag" };
22
-
23
- /** One entry per unique tag in the default-locale collection. */
24
- export function paths(): Array<{
25
- params: { tag: string };
26
- props: { tagInfo: TagInfo };
27
- }> {
28
- const tagMap = collectTagMapForLocale(defaultLocale);
29
-
30
- return [...tagMap.entries()].map(([tag, tagInfo]) => ({
31
- params: { tag },
32
- props: { tagInfo },
33
- }));
34
- }
35
-
36
- interface PageProps {
37
- params: { tag: string };
38
- tagInfo: TagInfo;
39
- }
40
-
41
- export default function DocTagPage({ params, tagInfo }: PageProps): JSX.Element {
42
- return <TagDetailPageView locale={defaultLocale} tag={params.tag} tagInfo={tagInfo} />;
43
- }
@@ -1,20 +0,0 @@
1
- /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
- // Page module for the default-locale "All Tags" index route.
4
- //
5
- // Default-locale "All Tags" index page. Collects every tag across the
6
- // "docs" collection, sorts them alphabetically, and renders a full tag cloud
7
- // via the v2 TagNav component. No dynamic params — single static route.
8
- //
9
- // Tag collection + rendering are shared with the locale-prefixed route via
10
- // pages/lib/_tag-pages.tsx (#2010).
11
-
12
- import { defaultLocale } from "@/config/i18n";
13
- import type { JSX } from "preact";
14
- import { TagsIndexPageView } from "../../lib/_tag-pages";
15
-
16
- export const frontmatter = { title: "All Tags" };
17
-
18
- export default function DocsTagsIndexPage(): JSX.Element {
19
- return <TagsIndexPageView locale={defaultLocale} />;
20
- }
@@ -1,93 +0,0 @@
1
- // @ts-check
2
- // zfb plugin module: llms-txt.
3
- //
4
- // Wires two lifecycle hooks for the llms-txt integration:
5
- //
6
- // postBuild — invokes `emitLlmsTxt` to write `dist/llms.txt`,
7
- // `dist/llms-full.txt`, and the per-locale variants.
8
- // `siteUrl` is normalised to `undefined` when falsy because
9
- // the runner switches between absolute and root-relative URLs
10
- // based on its presence (matches legacy Astro behaviour).
11
- //
12
- // devMiddleware — serves `/llms.txt`, `/llms-full.txt`, and the per-locale
13
- // `/<code>/llms.txt` / `/<code>/llms-full.txt` variants from
14
- // the on-the-fly `generateLlmsTxt` generator so dev output
15
- // stays in lockstep with the production `emitLlmsTxt`
16
- // byte-for-byte.
17
- //
18
- // `options` carries `{ siteName, siteDescription, base, siteUrl,
19
- // defaultLocaleDir, locales }` from the matching entry in `zfb.config.ts`.
20
- //
21
- // Inline functions are not supported by zfb's plugin runtime; see the
22
- // sibling `doc-history-plugin.mjs` for the rationale.
23
-
24
- /** @import { ZfbBuildHookContext, ZfbDevMiddlewareContext, ZfbPlugin } from "@takazudo/zfb/plugins" */
25
- /** @import { LlmsTxtEmitOptions, LlmsTxtDevMiddlewareOptions } from "@takazudo/zudo-doc/integrations/llms-txt" */
26
-
27
- import { emitLlmsTxt, createLlmsTxtDevMiddleware } from "@takazudo/zudo-doc/integrations/llms-txt";
28
- import { connectToZfbHandler } from "./connect-adapter.mjs";
29
-
30
- /** @type {ZfbPlugin} */
31
- export default {
32
- name: "llms-txt",
33
-
34
- /** @param {ZfbBuildHookContext} ctx */
35
- async postBuild(ctx) {
36
- await emitLlmsTxt(/** @type {LlmsTxtEmitOptions} */ (/** @type {unknown} */ ({
37
- ...ctx.options,
38
- outDir: ctx.outDir,
39
- // siteUrl is normalised to undefined when falsy because the runner
40
- // switches between absolute and root-relative URLs based on its
41
- // presence (matches legacy Astro behaviour).
42
- siteUrl: ctx.options["siteUrl"] || undefined,
43
- logger: ctx.logger,
44
- })));
45
- },
46
-
47
- /** @param {ZfbDevMiddlewareContext} ctx */
48
- devMiddleware(ctx) {
49
- const middleware = createLlmsTxtDevMiddleware(
50
- /** @type {LlmsTxtDevMiddlewareOptions} */ (/** @type {unknown} */ (ctx.options)),
51
- ctx.logger,
52
- );
53
- const handler = connectToZfbHandler(middleware);
54
-
55
- // zfb's `register(path, handler)` matches against the FULL request
56
- // URL (no base-stripping). For a non-root base (e.g. "/my-docs/"),
57
- // requests arrive as `/my-docs/llms.txt` (etc.), so we register
58
- // every route with the base prefix. For base="/", the prefix is
59
- // empty and routes are `/llms.txt` etc. as expected. The middleware
60
- // accepts base-prefixed URLs via the matcher (see `matchLlmsRoute`
61
- // in `dev-middleware.ts`).
62
- const basePrefix = stripTrailingSlash(
63
- typeof ctx.options["base"] === "string" ? ctx.options["base"] : "",
64
- );
65
- ctx.register(`${basePrefix}/llms.txt`, handler);
66
- ctx.register(`${basePrefix}/llms-full.txt`, handler);
67
- const locales = ctx.options["locales"];
68
- if (Array.isArray(locales)) {
69
- for (const locale of locales) {
70
- if (locale && typeof locale === "object" && typeof locale.code === "string") {
71
- ctx.register(`${basePrefix}/${locale.code}/llms.txt`, handler);
72
- ctx.register(`${basePrefix}/${locale.code}/llms-full.txt`, handler);
73
- } else {
74
- const repr = JSON.stringify(locale);
75
- if (ctx.logger?.warn) {
76
- ctx.logger.warn(`[llms-txt] skipping malformed locale entry (expected { code: string }): ${repr}`);
77
- } else {
78
- console.warn(`[llms-txt] skipping malformed locale entry (expected { code: string }): ${repr}`);
79
- }
80
- }
81
- }
82
- }
83
- },
84
- };
85
-
86
- /**
87
- * @param {string} s
88
- * @returns {string}
89
- */
90
- function stripTrailingSlash(s) {
91
- if (typeof s !== "string" || s.length === 0) return "";
92
- return s.endsWith("/") ? s.slice(0, -1) : s;
93
- }
@@ -1,48 +0,0 @@
1
- /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
- // Page module for the locale-prefixed versions index route.
4
- //
5
- // Non-default-locale versions page. paths() emits one route per locale in
6
- // settings.locales. Locale string is passed as a prop to drive label
7
- // translation in the component.
8
- //
9
- // Rendering is shared with the default-locale route via
10
- // pages/lib/_versions-page.tsx (#2010).
11
- //
12
- // paths() contract (zfb ADR-004 — synchronous):
13
- // params: { locale: string }
14
- // props: { locale }
15
-
16
- import { settings } from "@/config/settings";
17
- import type { JSX } from "preact";
18
- import { VersionsPageView } from "../../lib/_versions-page";
19
-
20
- export const frontmatter = { title: "Versions" };
21
-
22
- // ---------------------------------------------------------------------------
23
- // paths() — synchronous (ADR-004)
24
- // ---------------------------------------------------------------------------
25
-
26
- /** One route per non-default locale. */
27
- export function paths(): Array<{
28
- params: { locale: string };
29
- props: { locale: string };
30
- }> {
31
- return Object.keys(settings.locales).map((locale) => ({
32
- params: { locale },
33
- props: { locale },
34
- }));
35
- }
36
-
37
- // ---------------------------------------------------------------------------
38
- // Page component
39
- // ---------------------------------------------------------------------------
40
-
41
- interface PageArgs {
42
- params: { locale: string };
43
- props: { locale: string };
44
- }
45
-
46
- export default function LocaleVersionsPage({ params }: PageArgs): JSX.Element {
47
- return <VersionsPageView locale={params.locale} />;
48
- }