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.
- package/dist/compose.d.ts +6 -3
- package/dist/compose.js +6 -4
- package/dist/features/image-enlarge.d.ts +22 -12
- package/dist/features/image-enlarge.js +23 -191
- package/dist/scaffold.d.ts +11 -0
- package/dist/scaffold.js +51 -24
- package/dist/settings-gen.js +4 -0
- package/dist/zfb-config-gen.d.ts +16 -10
- package/dist/zfb-config-gen.js +34 -239
- package/package.json +1 -1
- package/templates/base/pages/_mdx-components.ts +64 -185
- package/templates/base/pages/index.tsx +1 -1
- package/templates/base/pages/lib/_body-end-islands.tsx +6 -13
- package/templates/base/pages/lib/_category-nav.tsx +30 -115
- package/templates/base/pages/lib/_category-tree-nav.tsx +38 -65
- package/templates/base/pages/lib/_compose-meta-title.ts +2 -6
- package/templates/base/pages/lib/_doc-body-end.tsx +3 -35
- package/templates/base/pages/lib/_doc-content-header.tsx +10 -111
- package/templates/base/pages/lib/_doc-history-area.tsx +19 -211
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +10 -113
- package/templates/base/pages/lib/_doc-page-renderer.tsx +22 -183
- package/templates/base/pages/lib/_doc-page-shell.tsx +17 -251
- package/templates/base/pages/lib/_doc-pager.tsx +4 -74
- package/templates/base/pages/lib/_doc-route-entries.ts +43 -177
- package/templates/base/pages/lib/_doc-route-paths.ts +16 -101
- package/templates/base/pages/lib/_doc-tags-area.tsx +14 -77
- package/templates/base/pages/lib/_extract-headings.ts +21 -295
- package/templates/base/pages/lib/_footer-with-defaults.tsx +37 -225
- package/templates/base/pages/lib/_frontmatter-preview-data.ts +5 -31
- package/templates/base/pages/lib/_head-with-defaults.tsx +13 -138
- package/templates/base/pages/lib/_header-with-defaults.tsx +24 -324
- package/templates/base/pages/lib/_inline-version-switcher.tsx +16 -78
- package/templates/base/pages/lib/_math-block.tsx +4 -63
- package/templates/base/pages/lib/_nav-data-prep.ts +32 -51
- package/templates/base/pages/lib/_nav-source-cache.ts +12 -57
- package/templates/base/pages/lib/_nav-source-docs.ts +33 -233
- package/templates/base/pages/lib/_search-widget-script.ts +2 -470
- package/templates/base/pages/lib/_search-widget.tsx +9 -195
- package/templates/base/pages/lib/_sidebar-prepaint.tsx +6 -59
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +16 -118
- package/templates/base/pages/lib/_site-tree-nav.tsx +29 -80
- package/templates/base/pages/lib/doc-page-props.ts +26 -44
- package/templates/base/pages/lib/locale-merge.ts +32 -145
- package/templates/base/pages/lib/route-enumerators.ts +52 -286
- package/templates/base/src/components/ai-chat-modal.tsx +9 -8
- package/templates/base/src/components/content/code-group.tsx +3 -76
- package/templates/base/src/components/content/content-admonition.tsx +4 -56
- package/templates/base/src/components/doc-history.tsx +9 -8
- package/templates/base/src/components/image-enlarge.tsx +11 -8
- package/templates/base/src/components/sidebar-toggle.tsx +6 -170
- package/templates/base/src/components/sidebar-tree.tsx +6 -548
- package/templates/base/src/config/color-scheme-utils.ts +34 -158
- package/templates/base/src/config/i18n.ts +9 -0
- package/templates/base/src/config/settings-types.ts +34 -172
- package/templates/base/src/config/z-index-tokens.ts +5 -4
- package/templates/base/src/styles/global.css +40 -587
- package/templates/base/src/utils/base.ts +1 -1
- package/templates/base/src/utils/docs.ts +47 -16
- package/templates/base/src/utils/github.ts +12 -9
- package/templates/base/src/utils/nav-scope.ts +13 -42
- package/templates/base/src/utils/sidebar.ts +18 -86
- package/templates/base/src/utils/slug.ts +10 -53
- package/templates/base/src/utils/smart-break.tsx +12 -120
- package/templates/base/src/utils/tags.ts +25 -68
- package/templates/features/bodyFootUtil/files/src/utils/github.ts +12 -9
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +13 -39
- package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -636
- package/templates/features/docHistory/files/src/types/doc-history.ts +7 -23
- package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +35 -201
- package/templates/features/i18n/files/pages/[locale]/index.tsx +1 -1
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +8 -269
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +6 -99
- package/templates/features/tagGovernance/files/scripts/tags-audit.ts +67 -515
- package/templates/features/versioning/files/pages/lib/_versions-page.tsx +21 -73
- package/templates/base/pages/404.tsx +0 -61
- package/templates/base/pages/robots.txt.tsx +0 -29
- package/templates/base/pages/sitemap.xml.tsx +0 -59
- package/templates/base/plugins/connect-adapter.mjs +0 -169
- package/templates/base/plugins/copy-public-plugin.mjs +0 -58
- package/templates/base/plugins/search-index-plugin.mjs +0 -66
- package/templates/base/scripts/gen-z-index.mjs +0 -157
- package/templates/base/src/components/mermaid-enlarge.tsx +0 -490
- package/templates/base/src/components/site-tree-nav.tsx +0 -220
- package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +0 -47
- package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +0 -111
- package/templates/features/docTags/files/pages/[locale]/docs/tags/[tag].tsx +0 -59
- package/templates/features/docTags/files/pages/[locale]/docs/tags/index.tsx +0 -39
- package/templates/features/docTags/files/pages/docs/tags/[tag].tsx +0 -43
- package/templates/features/docTags/files/pages/docs/tags/index.tsx +0 -20
- package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +0 -93
- package/templates/features/versioning/files/pages/[locale]/docs/versions.tsx +0 -48
- 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
|
-
}
|