create-zudo-doc 4.0.0 → 4.2.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/api.d.ts +11 -0
- package/dist/api.js +15 -0
- package/dist/claude-md-gen.d.ts +1 -1
- package/dist/claude-md-gen.js +9 -2
- package/dist/preset.d.ts +12 -0
- package/dist/preset.js +88 -66
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.js +87 -11
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +23 -0
- package/package.json +1 -1
- package/templates/base/scripts/setup-doc-skill.sh +45 -8
- package/templates/features/claudeSkills/files/.claude/skills/zudo-doc-design-system/SKILL.md +98 -0
- package/templates/features/claudeSkills/files/.claude/skills/zudo-doc-translate/SKILL.md +99 -0
- package/templates/features/claudeSkills/files/.claude/skills/zudo-doc-version-bump/SKILL.md +315 -0
- package/templates/features/tauri/files/src-tauri/tauri.conf.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PresetHeaderRightItem, PresetMetaTagsConfig } from "./preset.js";
|
|
1
2
|
export type { UserChoices } from "./prompts.js";
|
|
2
3
|
export interface CreateOptions {
|
|
3
4
|
projectName: string;
|
|
@@ -15,7 +16,17 @@ export interface CreateOptions {
|
|
|
15
16
|
/** GitHub repository URL — drives the header GitHub link and body-foot
|
|
16
17
|
* "View source on GitHub" link. Empty = disabled. */
|
|
17
18
|
githubUrl?: string;
|
|
19
|
+
/** Enable remark-cjk-friendly plugin (intelligent spacing around CJK text). Default: false. */
|
|
20
|
+
cjkFriendly?: boolean;
|
|
21
|
+
/** Minify production HTML output. Default: true. */
|
|
22
|
+
minifyHtml?: boolean;
|
|
18
23
|
packageManager: "pnpm" | "npm" | "yarn" | "bun";
|
|
24
|
+
/** Header-right items override, validated against the v1 preset allowlist
|
|
25
|
+
* (see `validateHeaderRightItems`). When omitted, keeps the package default. */
|
|
26
|
+
headerRightItems?: PresetHeaderRightItem[];
|
|
27
|
+
/** Meta tags config, validated against the v1 preset shape
|
|
28
|
+
* (see `validateMetaTags`). When omitted, keeps the package defaults. */
|
|
29
|
+
metaTags?: PresetMetaTagsConfig;
|
|
19
30
|
/** Install dependencies after scaffolding (default: false) */
|
|
20
31
|
install?: boolean;
|
|
21
32
|
/**
|
package/dist/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { SINGLE_SCHEMES, THEME_PACKS } from "./constants.js";
|
|
3
|
+
import { validateHeaderRightItems, validateMetaTags } from "./preset.js";
|
|
3
4
|
import { scaffold } from "./scaffold.js";
|
|
4
5
|
import { initGitRepo, installDependencies, validateProjectName } from "./utils.js";
|
|
5
6
|
export async function createZudoDoc(options) {
|
|
@@ -30,6 +31,20 @@ export async function createZudoDoc(options) {
|
|
|
30
31
|
const catalog = THEME_PACKS.map((t) => t.slug).join(", ");
|
|
31
32
|
throw new Error(`Unknown theme pack "${rest.themePack}". Available: ${catalog}`);
|
|
32
33
|
}
|
|
34
|
+
// Validate headerRightItems/metaTags shape like the preset (preset.ts) path
|
|
35
|
+
// does (#2922) — the two validators are shared via preset.ts so this can
|
|
36
|
+
// never drift from `validatePreset()`'s rules. Booleans (cjkFriendly,
|
|
37
|
+
// minifyHtml) need no validation.
|
|
38
|
+
if (rest.headerRightItems !== undefined) {
|
|
39
|
+
const err = validateHeaderRightItems(rest.headerRightItems);
|
|
40
|
+
if (err)
|
|
41
|
+
throw new Error(err);
|
|
42
|
+
}
|
|
43
|
+
if (rest.metaTags !== undefined) {
|
|
44
|
+
const err = validateMetaTags(rest.metaTags);
|
|
45
|
+
if (err)
|
|
46
|
+
throw new Error(err);
|
|
47
|
+
}
|
|
33
48
|
const choices = { ...rest, defaultLang: rest.defaultLang ?? "en" };
|
|
34
49
|
await scaffold(choices);
|
|
35
50
|
const targetDir = path.resolve(process.cwd(), choices.projectName);
|
package/dist/claude-md-gen.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { UserChoices } from "./prompts.js";
|
|
|
4
4
|
* zudolab/zudo-doc#2651, Wave 6 #2660). Rewritten from scratch — the old
|
|
5
5
|
* generator described a 64-file project (`src/components/admonitions/`,
|
|
6
6
|
* `src/layouts/`, `src/utils/`, per-page `pages/lib/*` wiring) that no
|
|
7
|
-
* longer exists. The scaffolded project is now ~
|
|
7
|
+
* longer exists. The scaffolded project is now ~13 files; almost
|
|
8
8
|
* everything referenced here lives in `node_modules/@takazudo/zudo-doc`.
|
|
9
9
|
*/
|
|
10
10
|
export declare function generateCLAUDEFile(choices: UserChoices): string;
|
package/dist/claude-md-gen.js
CHANGED
|
@@ -4,7 +4,7 @@ import { capitalize, pmRunCommand } from "./utils.js";
|
|
|
4
4
|
* zudolab/zudo-doc#2651, Wave 6 #2660). Rewritten from scratch — the old
|
|
5
5
|
* generator described a 64-file project (`src/components/admonitions/`,
|
|
6
6
|
* `src/layouts/`, `src/utils/`, per-page `pages/lib/*` wiring) that no
|
|
7
|
-
* longer exists. The scaffolded project is now ~
|
|
7
|
+
* longer exists. The scaffolded project is now ~13 files; almost
|
|
8
8
|
* everything referenced here lives in `node_modules/@takazudo/zudo-doc`.
|
|
9
9
|
*/
|
|
10
10
|
export function generateCLAUDEFile(choices) {
|
|
@@ -28,7 +28,14 @@ export function generateCLAUDEFile(choices) {
|
|
|
28
28
|
lines.push(`## Commands`);
|
|
29
29
|
lines.push(``);
|
|
30
30
|
const pm = choices.packageManager;
|
|
31
|
-
|
|
31
|
+
if (choices.features.includes("docHistory")) {
|
|
32
|
+
lines.push(`- \`${pmRunCommand(pm, "dev")}\` — runs the zfb dev server (port 4321) and the doc-history API server (port 4322) concurrently via \`run-p\` (\`${pmRunCommand(pm, "dev:zfb")}\` / \`${pmRunCommand(pm, "dev:history")}\` individually)`);
|
|
33
|
+
lines.push(`- \`${pmRunCommand(pm, "dev:network")}\` — same, but zfb binds \`--host 0.0.0.0\` for LAN access (\`${pmRunCommand(pm, "dev:zfb:network")}\` individually); the doc-history server stays loopback-only and LAN clients reach it through zfb's \`/doc-history/*\` dev proxy`);
|
|
34
|
+
lines.push(`- \`run-p\` swallows trailing args, so other zfb flags don't forward through \`${pmRunCommand(pm, "dev")}\` — pass them directly instead: \`${pm} run dev:zfb -- <flags>\``);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
lines.push(`- \`${pmRunCommand(pm, "dev")}\` — zfb dev server (port 4321)`);
|
|
38
|
+
}
|
|
32
39
|
lines.push(`- \`${pmRunCommand(pm, "build")}\` — static HTML export to \`dist/\``);
|
|
33
40
|
lines.push(`- \`${pmRunCommand(pm, "check")}\` — TypeScript type checking`);
|
|
34
41
|
lines.push(`- \`${pmRunCommand(pm, "preview")}\` — serve the built \`dist/\``);
|
package/dist/preset.d.ts
CHANGED
|
@@ -26,6 +26,18 @@ export interface PresetMetaTagsConfig {
|
|
|
26
26
|
twitterSite?: string;
|
|
27
27
|
twitterCreator?: string;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Validates a `headerRightItems` value against the v1 preset allowlist.
|
|
31
|
+
* Shared by `validatePreset()` (JSON preset path) and `createZudoDoc()`
|
|
32
|
+
* (programmatic API path, #2922) so the two entry points can never drift.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateHeaderRightItems(items: unknown): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Validates a `metaTags` value's sub-field types. Shared by `validatePreset()`
|
|
37
|
+
* (JSON preset path) and `createZudoDoc()` (programmatic API path, #2922) so
|
|
38
|
+
* the two entry points can never drift.
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateMetaTags(metaTags: unknown): string | null;
|
|
29
41
|
export interface PresetJson {
|
|
30
42
|
projectName?: string;
|
|
31
43
|
defaultLang?: string;
|
package/dist/preset.js
CHANGED
|
@@ -12,6 +12,88 @@ const VALID_HEADER_RIGHT_TRIGGERS = new Set([
|
|
|
12
12
|
"design-token-panel",
|
|
13
13
|
"ai-chat",
|
|
14
14
|
]);
|
|
15
|
+
/**
|
|
16
|
+
* Validates a `headerRightItems` value against the v1 preset allowlist.
|
|
17
|
+
* Shared by `validatePreset()` (JSON preset path) and `createZudoDoc()`
|
|
18
|
+
* (programmatic API path, #2922) so the two entry points can never drift.
|
|
19
|
+
*/
|
|
20
|
+
export function validateHeaderRightItems(items) {
|
|
21
|
+
if (!Array.isArray(items)) {
|
|
22
|
+
return `"headerRightItems" must be an array`;
|
|
23
|
+
}
|
|
24
|
+
for (let i = 0; i < items.length; i++) {
|
|
25
|
+
const item = items[i];
|
|
26
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
27
|
+
return `headerRightItems[${i}] must be an object`;
|
|
28
|
+
}
|
|
29
|
+
const t = item.type;
|
|
30
|
+
if (t === "link" || t === "html") {
|
|
31
|
+
return `headerRightItems[${i}] type "${t}" is not supported in presets (v1) — edit zfb.config.ts after scaffold`;
|
|
32
|
+
}
|
|
33
|
+
if (t === "component") {
|
|
34
|
+
const component = item.component;
|
|
35
|
+
if (typeof component !== "string") {
|
|
36
|
+
return `headerRightItems[${i}].component must be a string`;
|
|
37
|
+
}
|
|
38
|
+
if (!VALID_HEADER_RIGHT_COMPONENTS.has(component)) {
|
|
39
|
+
return `headerRightItems[${i}] unknown component "${component}". Allowed: ${[
|
|
40
|
+
...VALID_HEADER_RIGHT_COMPONENTS,
|
|
41
|
+
].join(", ")}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (t === "trigger") {
|
|
45
|
+
const trigger = item.trigger;
|
|
46
|
+
if (typeof trigger !== "string") {
|
|
47
|
+
return `headerRightItems[${i}].trigger must be a string`;
|
|
48
|
+
}
|
|
49
|
+
if (!VALID_HEADER_RIGHT_TRIGGERS.has(trigger)) {
|
|
50
|
+
return `headerRightItems[${i}] unknown trigger "${trigger}". Allowed: ${[
|
|
51
|
+
...VALID_HEADER_RIGHT_TRIGGERS,
|
|
52
|
+
].join(", ")}`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return `headerRightItems[${i}] must have type "component" or "trigger" (got ${JSON.stringify(t)})`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validates a `metaTags` value's sub-field types. Shared by `validatePreset()`
|
|
63
|
+
* (JSON preset path) and `createZudoDoc()` (programmatic API path, #2922) so
|
|
64
|
+
* the two entry points can never drift.
|
|
65
|
+
*/
|
|
66
|
+
export function validateMetaTags(metaTags) {
|
|
67
|
+
if (typeof metaTags !== "object" || metaTags === null || Array.isArray(metaTags)) {
|
|
68
|
+
return `"metaTags" must be an object`;
|
|
69
|
+
}
|
|
70
|
+
const mt = metaTags;
|
|
71
|
+
if (mt.description !== undefined && typeof mt.description !== "boolean") {
|
|
72
|
+
return `"metaTags.description" must be a boolean`;
|
|
73
|
+
}
|
|
74
|
+
if (mt.keywords !== undefined && mt.keywords !== false && typeof mt.keywords !== "string") {
|
|
75
|
+
return `"metaTags.keywords" must be a string or false`;
|
|
76
|
+
}
|
|
77
|
+
if (mt.ogImage !== undefined && mt.ogImage !== false && typeof mt.ogImage !== "string") {
|
|
78
|
+
return `"metaTags.ogImage" must be a string or false`;
|
|
79
|
+
}
|
|
80
|
+
if (mt.ogSiteName !== undefined && typeof mt.ogSiteName !== "boolean") {
|
|
81
|
+
return `"metaTags.ogSiteName" must be a boolean`;
|
|
82
|
+
}
|
|
83
|
+
if (mt.twitterCard !== undefined &&
|
|
84
|
+
mt.twitterCard !== false &&
|
|
85
|
+
mt.twitterCard !== "summary" &&
|
|
86
|
+
mt.twitterCard !== "summary_large_image") {
|
|
87
|
+
return `"metaTags.twitterCard" must be "summary", "summary_large_image", or false`;
|
|
88
|
+
}
|
|
89
|
+
if (mt.twitterSite !== undefined && typeof mt.twitterSite !== "string") {
|
|
90
|
+
return `"metaTags.twitterSite" must be a string`;
|
|
91
|
+
}
|
|
92
|
+
if (mt.twitterCreator !== undefined && typeof mt.twitterCreator !== "string") {
|
|
93
|
+
return `"metaTags.twitterCreator" must be a string`;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
15
97
|
export function loadPreset(pathOrStdin) {
|
|
16
98
|
let raw;
|
|
17
99
|
if (pathOrStdin === "-") {
|
|
@@ -85,74 +167,14 @@ export function validatePreset(json) {
|
|
|
85
167
|
return `"minifyHtml" must be a boolean in preset`;
|
|
86
168
|
}
|
|
87
169
|
if (p.headerRightItems !== undefined) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for (let i = 0; i < p.headerRightItems.length; i++) {
|
|
92
|
-
const item = p.headerRightItems[i];
|
|
93
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
94
|
-
return `headerRightItems[${i}] must be an object`;
|
|
95
|
-
}
|
|
96
|
-
const t = item.type;
|
|
97
|
-
if (t === "link" || t === "html") {
|
|
98
|
-
return `headerRightItems[${i}] type "${t}" is not supported in presets (v1) — edit settings.ts after scaffold`;
|
|
99
|
-
}
|
|
100
|
-
if (t === "component") {
|
|
101
|
-
const component = item.component;
|
|
102
|
-
if (typeof component !== "string") {
|
|
103
|
-
return `headerRightItems[${i}].component must be a string`;
|
|
104
|
-
}
|
|
105
|
-
if (!VALID_HEADER_RIGHT_COMPONENTS.has(component)) {
|
|
106
|
-
return `headerRightItems[${i}] unknown component "${component}". Allowed: ${[
|
|
107
|
-
...VALID_HEADER_RIGHT_COMPONENTS,
|
|
108
|
-
].join(", ")}`;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else if (t === "trigger") {
|
|
112
|
-
const trigger = item.trigger;
|
|
113
|
-
if (typeof trigger !== "string") {
|
|
114
|
-
return `headerRightItems[${i}].trigger must be a string`;
|
|
115
|
-
}
|
|
116
|
-
if (!VALID_HEADER_RIGHT_TRIGGERS.has(trigger)) {
|
|
117
|
-
return `headerRightItems[${i}] unknown trigger "${trigger}". Allowed: ${[
|
|
118
|
-
...VALID_HEADER_RIGHT_TRIGGERS,
|
|
119
|
-
].join(", ")}`;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
return `headerRightItems[${i}] must have type "component" or "trigger" (got ${JSON.stringify(t)})`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
170
|
+
const err = validateHeaderRightItems(p.headerRightItems);
|
|
171
|
+
if (err)
|
|
172
|
+
return err;
|
|
126
173
|
}
|
|
127
174
|
if (p.metaTags !== undefined) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const mt = p.metaTags;
|
|
132
|
-
if (mt.description !== undefined && typeof mt.description !== "boolean") {
|
|
133
|
-
return `"metaTags.description" must be a boolean`;
|
|
134
|
-
}
|
|
135
|
-
if (mt.keywords !== undefined && mt.keywords !== false && typeof mt.keywords !== "string") {
|
|
136
|
-
return `"metaTags.keywords" must be a string or false`;
|
|
137
|
-
}
|
|
138
|
-
if (mt.ogImage !== undefined && mt.ogImage !== false && typeof mt.ogImage !== "string") {
|
|
139
|
-
return `"metaTags.ogImage" must be a string or false`;
|
|
140
|
-
}
|
|
141
|
-
if (mt.ogSiteName !== undefined && typeof mt.ogSiteName !== "boolean") {
|
|
142
|
-
return `"metaTags.ogSiteName" must be a boolean`;
|
|
143
|
-
}
|
|
144
|
-
if (mt.twitterCard !== undefined &&
|
|
145
|
-
mt.twitterCard !== false &&
|
|
146
|
-
mt.twitterCard !== "summary" &&
|
|
147
|
-
mt.twitterCard !== "summary_large_image") {
|
|
148
|
-
return `"metaTags.twitterCard" must be "summary", "summary_large_image", or false`;
|
|
149
|
-
}
|
|
150
|
-
if (mt.twitterSite !== undefined && typeof mt.twitterSite !== "string") {
|
|
151
|
-
return `"metaTags.twitterSite" must be a string`;
|
|
152
|
-
}
|
|
153
|
-
if (mt.twitterCreator !== undefined && typeof mt.twitterCreator !== "string") {
|
|
154
|
-
return `"metaTags.twitterCreator" must be a string`;
|
|
155
|
-
}
|
|
175
|
+
const err = validateMetaTags(p.metaTags);
|
|
176
|
+
if (err)
|
|
177
|
+
return err;
|
|
156
178
|
}
|
|
157
179
|
// Cross-field validation
|
|
158
180
|
if (p.colorSchemeMode === "single" && (p.lightScheme || p.darkScheme)) {
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -18,5 +18,5 @@ export { getSecondaryLang };
|
|
|
18
18
|
*
|
|
19
19
|
* Bumped in lockstep by scripts/release-create-zudo-doc.sh.
|
|
20
20
|
*/
|
|
21
|
-
export declare const ZUDO_DOC_PIN = "^4.
|
|
21
|
+
export declare const ZUDO_DOC_PIN = "^4.2.0";
|
|
22
22
|
export declare function scaffold(choices: UserChoices): Promise<void>;
|
package/dist/scaffold.js
CHANGED
|
@@ -5,7 +5,7 @@ import { generateZfbConfig } from "./zfb-config-gen.js";
|
|
|
5
5
|
import { generateCLAUDEFile } from "./claude-md-gen.js";
|
|
6
6
|
import { composeFeatures } from "./compose.js";
|
|
7
7
|
import { featureModules } from "./features/index.js";
|
|
8
|
-
import { capitalize, getSecondaryLang, pmRunCommand } from "./utils.js";
|
|
8
|
+
import { capitalize, getSecondaryLang, hasAncestorPnpmWorkspace, pmRunCommand, } from "./utils.js";
|
|
9
9
|
export { getSecondaryLang };
|
|
10
10
|
/**
|
|
11
11
|
* Pinned `@takazudo/zudo-doc` version used by `generatePackageJson()`.
|
|
@@ -24,7 +24,7 @@ export { getSecondaryLang };
|
|
|
24
24
|
*
|
|
25
25
|
* Bumped in lockstep by scripts/release-create-zudo-doc.sh.
|
|
26
26
|
*/
|
|
27
|
-
export const ZUDO_DOC_PIN = "^4.
|
|
27
|
+
export const ZUDO_DOC_PIN = "^4.2.0";
|
|
28
28
|
/**
|
|
29
29
|
* Files in `templates/base/**` that must not be copied by the unconditional
|
|
30
30
|
* base mirror. Each entry is matched against the path relative to
|
|
@@ -208,8 +208,6 @@ export async function scaffold(choices) {
|
|
|
208
208
|
const templatesDir = path.join(pkgRoot, "templates");
|
|
209
209
|
const baseDir = path.join(templatesDir, "base");
|
|
210
210
|
const featuresDir = path.join(templatesDir, "features");
|
|
211
|
-
// Still needed for source-checkout-only assets such as Claude skills.
|
|
212
|
-
const monorepoRoot = path.resolve(pkgRoot, "../..");
|
|
213
211
|
await fs.ensureDir(targetDir);
|
|
214
212
|
// 1. Copy base template
|
|
215
213
|
// Honour EXCLUDE_FROM_MIRROR so paths like `pages/api/**` (worker-only SSR
|
|
@@ -227,19 +225,31 @@ export async function scaffold(choices) {
|
|
|
227
225
|
}
|
|
228
226
|
// 2b. Copy user-facing Claude Code skills when enabled
|
|
229
227
|
// Ships the curated zudo-doc-* skills (design-system, translate, version-bump)
|
|
230
|
-
// from the
|
|
228
|
+
// from the package's own templates/ (npm `files` cannot reach outside the
|
|
229
|
+
// package dir, so these are committed, scaffold-authored variants of the
|
|
230
|
+
// monorepo's .claude/skills/, not read from there directly and not
|
|
231
|
+
// byte-identical to it — each was rewritten to describe the scaffold-real
|
|
232
|
+
// flow (no monorepo-only paths/scripts) instead of the monorepo's own
|
|
233
|
+
// flow — see #2921 (original copy step) and epic #2946 (variant
|
|
234
|
+
// conversion, #2947/#2948).
|
|
231
235
|
if (choices.features.includes("claudeSkills")) {
|
|
232
236
|
const userFacingSkills = [
|
|
233
237
|
"zudo-doc-design-system",
|
|
234
238
|
"zudo-doc-translate",
|
|
235
239
|
"zudo-doc-version-bump",
|
|
236
240
|
];
|
|
241
|
+
const skillsTemplateDir = path.join(featuresDir, "claudeSkills/files/.claude/skills");
|
|
237
242
|
for (const skill of userFacingSkills) {
|
|
238
|
-
const skillSrc = path.join(
|
|
243
|
+
const skillSrc = path.join(skillsTemplateDir, skill);
|
|
239
244
|
const skillDest = path.join(targetDir, ".claude/skills", skill);
|
|
240
245
|
if (await fs.pathExists(skillSrc)) {
|
|
241
246
|
await fs.copy(skillSrc, skillDest);
|
|
242
247
|
}
|
|
248
|
+
else {
|
|
249
|
+
// Defensive only — unreachable in a healthy publish, since the
|
|
250
|
+
// template files are committed alongside this source.
|
|
251
|
+
console.warn(`claudeSkills: missing template source for "${skill}", skipping`);
|
|
252
|
+
}
|
|
243
253
|
}
|
|
244
254
|
}
|
|
245
255
|
const defaultLang = choices.defaultLang;
|
|
@@ -305,6 +315,7 @@ export async function scaffold(choices) {
|
|
|
305
315
|
"node_modules",
|
|
306
316
|
"dist",
|
|
307
317
|
".zfb",
|
|
318
|
+
".zfb-build/",
|
|
308
319
|
"",
|
|
309
320
|
"# macOS",
|
|
310
321
|
".DS_Store",
|
|
@@ -356,6 +367,33 @@ export async function scaffold(choices) {
|
|
|
356
367
|
// under a strict trust policy without disabling the guard wholesale. Pinned to
|
|
357
368
|
// the exact known-safe version so a future undici-types bump is re-reviewed.
|
|
358
369
|
await fs.outputFile(path.join(targetDir, ".npmrc"), "trust-policy-exclude[]=undici-types@6.21.0\n");
|
|
370
|
+
// Emit a pnpm-workspace.yaml disabling pnpm 11's minimumReleaseAge gate.
|
|
371
|
+
// pnpm >= 11 defaults minimumReleaseAge to 1440 (1 day), which blocks
|
|
372
|
+
// `pnpm install`/CI from resolving a freshly-published @takazudo bump for
|
|
373
|
+
// a full day; the built-in minimumReleaseAgeExclude matcher can't be
|
|
374
|
+
// pointed at this project's peer-nested lockfile keys (upstream pnpm
|
|
375
|
+
// limitation), so the gate is disabled outright rather than excluded
|
|
376
|
+
// per-package. As of pnpm 11, non-auth/registry settings like this one
|
|
377
|
+
// live in pnpm-workspace.yaml, not .npmrc (.npmrc is auth/registry only).
|
|
378
|
+
// Skipped when an ANCESTOR directory already has a pnpm-workspace.yaml
|
|
379
|
+
// (e.g. scaffolding into `apps/docs/` under an existing pnpm monorepo) —
|
|
380
|
+
// pnpm resolves the nearest pnpm-workspace.yaml upward from cwd as the
|
|
381
|
+
// workspace root, so writing a new one here would carve the generated
|
|
382
|
+
// project out of the parent workspace instead of joining it. Mirrors
|
|
383
|
+
// `initGitRepo`'s "never nest" precedent (see utils.ts).
|
|
384
|
+
if (!hasAncestorPnpmWorkspace(targetDir)) {
|
|
385
|
+
await fs.outputFile(path.join(targetDir, "pnpm-workspace.yaml"), "# pnpm 11 defaults minimumReleaseAge to 1440min; its exclude matcher can't match this project's peer-nested lockfile keys (upstream pnpm bug), so disable the gate outright.\nminimumReleaseAge: 0\n");
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// Ancestor already has a pnpm-workspace.yaml: we deliberately do NOT write
|
|
389
|
+
// our own (it would carve this project out of the parent workspace — see
|
|
390
|
+
// above). But then pnpm applies the PARENT workspace's minimumReleaseAge
|
|
391
|
+
// (1440min by default in pnpm 11), so freshly-published @takazudo bumps
|
|
392
|
+
// still can't install for a day — the exact failure this file guards
|
|
393
|
+
// against. We can't safely edit the parent's config, so instruct the user
|
|
394
|
+
// to disable the gate there instead of silently leaving them blocked.
|
|
395
|
+
console.warn("pnpm-workspace.yaml exists in an ancestor directory — skipping the generated one to avoid nesting a second workspace. If `pnpm install` blocks freshly-published @takazudo releases, add `minimumReleaseAge: 0` to your parent pnpm-workspace.yaml.");
|
|
396
|
+
}
|
|
359
397
|
const claudeContent = generateCLAUDEFile(choices);
|
|
360
398
|
await fs.outputFile(path.join(targetDir, "CLAUDE.md"), claudeContent);
|
|
361
399
|
// 4. Compose features (copy feature files + inject into shared files)
|
|
@@ -512,12 +550,12 @@ function generatePackageJson(choices) {
|
|
|
512
550
|
// singleton across host-callable wiring (zfb#1652/#1653). The zfb family
|
|
513
551
|
// must stay in lockstep because the WASM browser entry depends on its
|
|
514
552
|
// resource-aware island pipeline.
|
|
515
|
-
"@takazudo/zfb": "0.1.0-next.
|
|
516
|
-
"@takazudo/zfb-runtime": "0.1.0-next.
|
|
553
|
+
"@takazudo/zfb": "0.1.0-next.89",
|
|
554
|
+
"@takazudo/zfb-runtime": "0.1.0-next.89",
|
|
517
555
|
// zfb-adapter-cloudflare — required for any route with `prerender = false`.
|
|
518
556
|
// Pinned in lockstep with @takazudo/zfb.
|
|
519
|
-
"@takazudo/zfb-adapter-cloudflare": "0.1.0-next.
|
|
520
|
-
"@takazudo/zfb-md-wasm": "0.1.0-next.
|
|
557
|
+
"@takazudo/zfb-adapter-cloudflare": "0.1.0-next.89",
|
|
558
|
+
"@takazudo/zfb-md-wasm": "0.1.0-next.89",
|
|
521
559
|
// @takazudo/zudo-doc — published from this monorepo via
|
|
522
560
|
// .github/workflows/publish-zudo-doc.yml. The pin here is bumped in
|
|
523
561
|
// lockstep by scripts/release-create-zudo-doc.sh whenever zudo-doc's
|
|
@@ -622,10 +660,16 @@ function generatePackageJson(choices) {
|
|
|
622
660
|
// doc-history helpers, which in turn import
|
|
623
661
|
// @takazudo/zudo-doc-history-server/git-history. Without this dep the
|
|
624
662
|
// plugin host fails at init with ERR_MODULE_NOT_FOUND — W8A (#1739).
|
|
625
|
-
deps["@takazudo/zudo-doc-history-server"] = "^4.
|
|
663
|
+
deps["@takazudo/zudo-doc-history-server"] = "^4.2.0";
|
|
626
664
|
// tsx is no longer needed here: the relocated package plugin imports the
|
|
627
665
|
// runner directly (no `tsx -e` spawn) since the package ships compiled
|
|
628
666
|
// dist/ — package-first migration #2321 (#2337).
|
|
667
|
+
// npm-run-all2 provides `run-p`, used by the docHistory `dev` script
|
|
668
|
+
// (below) to run the zfb dev server and the doc-history API server
|
|
669
|
+
// concurrently — otherwise the :4322 proxy target never starts and the
|
|
670
|
+
// feature silently looks broken (#2926). Same maintained fork/pin this
|
|
671
|
+
// monorepo's own root package.json uses.
|
|
672
|
+
devDeps["npm-run-all2"] = "^7.0.2";
|
|
629
673
|
}
|
|
630
674
|
// claudeResources: tsx is no longer needed. The relocated package plugin
|
|
631
675
|
// (@takazudo/zudo-doc/plugins/claude-resources) imports the runner directly
|
|
@@ -644,6 +688,38 @@ function generatePackageJson(choices) {
|
|
|
644
688
|
preview: "zfb preview",
|
|
645
689
|
check: "zfb check",
|
|
646
690
|
};
|
|
691
|
+
if (choices.features.includes("docHistory")) {
|
|
692
|
+
// A docHistory-enabled project needs the zfb dev server AND the
|
|
693
|
+
// doc-history API server (:4322, proxied by the zfb doc-history plugin)
|
|
694
|
+
// running concurrently — otherwise the Created/Updated/Author block
|
|
695
|
+
// silently never appears in dev (#2926). `doc-history-server` is the bin
|
|
696
|
+
// shipped by the @takazudo/zudo-doc-history-server dep added above;
|
|
697
|
+
// `run-p` (npm-run-all2, added to devDependencies above) runs both.
|
|
698
|
+
scripts.dev = "run-p dev:zfb dev:history";
|
|
699
|
+
scripts["dev:zfb"] = "zfb dev";
|
|
700
|
+
// run-p swallows trailing args and npm-run-all2 v7's `{@}` placeholder
|
|
701
|
+
// strips flag names, so `pnpm dev -- --host 0.0.0.0` is silently ignored
|
|
702
|
+
// (verified in issue #2940) — dev:network is a dedicated LAN-bound script
|
|
703
|
+
// instead. Only zfb binds 0.0.0.0; the history server stays loopback-only
|
|
704
|
+
// and LAN clients reach it through zfb's `/doc-history/*` dev proxy.
|
|
705
|
+
scripts["dev:zfb:network"] = "zfb dev --host 0.0.0.0";
|
|
706
|
+
scripts["dev:network"] = "run-p dev:zfb:network dev:history";
|
|
707
|
+
// Relative --content-dir/--locale paths are resolved by resolveContentPath
|
|
708
|
+
// (packages/doc-history-server/src/args.ts) against INIT_CWD (falling back
|
|
709
|
+
// to process.cwd()) — correct for the supported invocation (`<pm> dev` /
|
|
710
|
+
// `<pm> run dev` from the project root, which is what run-p's child
|
|
711
|
+
// processes inherit). It resolves against the WRONG directory only if this
|
|
712
|
+
// generated project is itself nested inside a larger pnpm/npm workspace
|
|
713
|
+
// and dev:history is invoked via `<pm> --filter <this-package> ...` from
|
|
714
|
+
// that outer workspace root — an unsupported, non-generator invocation
|
|
715
|
+
// path, not the default `<pm> dev`.
|
|
716
|
+
let devHistoryScript = "doc-history-server --port 4322 --content-dir src/content/docs";
|
|
717
|
+
if (choices.features.includes("i18n")) {
|
|
718
|
+
const secondaryLang = getSecondaryLang(choices.defaultLang);
|
|
719
|
+
devHistoryScript += ` --locale ${secondaryLang}:src/content/docs-${secondaryLang}`;
|
|
720
|
+
}
|
|
721
|
+
scripts["dev:history"] = devHistoryScript;
|
|
722
|
+
}
|
|
647
723
|
if (choices.features.includes("tagGovernance")) {
|
|
648
724
|
// Both package-owned bins load the same explicit project config. `--`
|
|
649
725
|
// supplied by pnpm/npm is preserved by the runners for forwarded options.
|
package/dist/utils.d.ts
CHANGED
|
@@ -34,6 +34,18 @@ export type GitInitResult = {
|
|
|
34
34
|
* none configured, so a normal user's commit keeps their own identity.
|
|
35
35
|
*/
|
|
36
36
|
export declare function initGitRepo(dir: string): GitInitResult;
|
|
37
|
+
/**
|
|
38
|
+
* Whether an ANCESTOR of `dir` (walking up from `dir`'s parent, not `dir`
|
|
39
|
+
* itself — `dir` is the fresh project root and never has one yet) already
|
|
40
|
+
* has a `pnpm-workspace.yaml`. Mirrors `initGitRepo`'s "never nest"
|
|
41
|
+
* precedent above: pnpm resolves the nearest `pnpm-workspace.yaml` upward
|
|
42
|
+
* from cwd as the workspace root, so writing a new one inside an existing
|
|
43
|
+
* pnpm monorepo (e.g. scaffolding into `apps/docs/` under a parent
|
|
44
|
+
* workspace) would carve the generated project out of that parent
|
|
45
|
+
* workspace's install/lockfile instead of joining it (codex-review finding,
|
|
46
|
+
* #2923).
|
|
47
|
+
*/
|
|
48
|
+
export declare function hasAncestorPnpmWorkspace(dir: string): boolean;
|
|
37
49
|
export declare function capitalize(str: string): string;
|
|
38
50
|
/** Get a short uppercase label for a language code (e.g. "en" → "EN", "zh-cn" → "ZH-CN"). */
|
|
39
51
|
export declare function getLangLabel(langCode: string): string;
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execSync, execFileSync } from "child_process";
|
|
2
2
|
import fs from "fs-extra";
|
|
3
|
+
import path from "path";
|
|
3
4
|
// Project-name grammar (locked by F4 — S4 #2013):
|
|
4
5
|
// /^[a-z0-9][a-z0-9._-]*$/, max 214 chars, unscoped, used as both directory
|
|
5
6
|
// name and package name. Mirrors npm's unscoped-name rules + max path safety.
|
|
@@ -107,6 +108,28 @@ export function initGitRepo(dir) {
|
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether an ANCESTOR of `dir` (walking up from `dir`'s parent, not `dir`
|
|
113
|
+
* itself — `dir` is the fresh project root and never has one yet) already
|
|
114
|
+
* has a `pnpm-workspace.yaml`. Mirrors `initGitRepo`'s "never nest"
|
|
115
|
+
* precedent above: pnpm resolves the nearest `pnpm-workspace.yaml` upward
|
|
116
|
+
* from cwd as the workspace root, so writing a new one inside an existing
|
|
117
|
+
* pnpm monorepo (e.g. scaffolding into `apps/docs/` under a parent
|
|
118
|
+
* workspace) would carve the generated project out of that parent
|
|
119
|
+
* workspace's install/lockfile instead of joining it (codex-review finding,
|
|
120
|
+
* #2923).
|
|
121
|
+
*/
|
|
122
|
+
export function hasAncestorPnpmWorkspace(dir) {
|
|
123
|
+
let current = path.dirname(path.resolve(dir));
|
|
124
|
+
while (true) {
|
|
125
|
+
if (fs.existsSync(path.join(current, "pnpm-workspace.yaml")))
|
|
126
|
+
return true;
|
|
127
|
+
const parent = path.dirname(current);
|
|
128
|
+
if (parent === current)
|
|
129
|
+
return false; // reached the filesystem root
|
|
130
|
+
current = parent;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
110
133
|
export function capitalize(str) {
|
|
111
134
|
return str.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
112
135
|
}
|
package/package.json
CHANGED
|
@@ -74,6 +74,16 @@ fi
|
|
|
74
74
|
# Use the main worktree path so symlinks survive worktree removal
|
|
75
75
|
REPO_ROOT="$(git -C "$ROOT_DIR" worktree list | head -1 | awk '{print $1}')"
|
|
76
76
|
|
|
77
|
+
# Path from the repository root to the project directory: "" at the repo
|
|
78
|
+
# root, "doc/" (trailing slash) when the project lives in a subdirectory
|
|
79
|
+
# (nested layout, #2918). `rev-parse --show-prefix` is relative to the
|
|
80
|
+
# CURRENT worktree's top level, so combining it with REPO_ROOT (the MAIN
|
|
81
|
+
# worktree root, above) reconstructs the equivalent path there even when
|
|
82
|
+
# running from inside a different worktree.
|
|
83
|
+
PROJECT_PREFIX="$(git -C "$ROOT_DIR" rev-parse --show-prefix)"
|
|
84
|
+
REPO_DOCS_DIR="$REPO_ROOT/${PROJECT_PREFIX}src/content/docs"
|
|
85
|
+
REPO_DOCS_JA_DIR="$REPO_ROOT/${PROJECT_PREFIX}src/content/docs-ja"
|
|
86
|
+
|
|
77
87
|
DOCS_DIR="$ROOT_DIR/src/content/docs"
|
|
78
88
|
|
|
79
89
|
# Validate docs directory exists
|
|
@@ -107,6 +117,33 @@ for dir in "$DOCS_DIR"/*/; do
|
|
|
107
117
|
"
|
|
108
118
|
done
|
|
109
119
|
|
|
120
|
+
# The SKILL.md "Format"/"Verify" steps must only reference commands that
|
|
121
|
+
# actually exist on the running project's package.json — a fresh
|
|
122
|
+
# create-zudo-doc scaffold has no format script at all, while this repo's own
|
|
123
|
+
# showcase exposes `format` (there is no `format:md` script anywhere, #2918).
|
|
124
|
+
# `format:md` is checked too in case a downstream project defines its own
|
|
125
|
+
# script under that literal name.
|
|
126
|
+
FORMAT_SCRIPT="$(node -e "
|
|
127
|
+
const scripts = (require('$ROOT_DIR/package.json').scripts) || {};
|
|
128
|
+
console.log(
|
|
129
|
+
scripts.format ? 'format'
|
|
130
|
+
: scripts['format:mdx'] ? 'format:mdx'
|
|
131
|
+
: scripts['format:md'] ? 'format:md'
|
|
132
|
+
: ''
|
|
133
|
+
);
|
|
134
|
+
")"
|
|
135
|
+
if [ -n "$FORMAT_SCRIPT" ]; then
|
|
136
|
+
FORMAT_STEP="Run \`pnpm $FORMAT_SCRIPT\` to format the new/changed MDX files."
|
|
137
|
+
else
|
|
138
|
+
FORMAT_STEP="No format script is configured for this project; formatting is optional."
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Name the project directory explicitly so the instruction resolves correctly
|
|
142
|
+
# even when the project is nested inside a larger git repo (running `pnpm
|
|
143
|
+
# build` from the outer repo root would otherwise invoke the wrong
|
|
144
|
+
# package.json, #2918).
|
|
145
|
+
VERIFY_STEP="Run \`pnpm build\` from \`$ROOT_DIR\` to confirm the site builds correctly."
|
|
146
|
+
|
|
110
147
|
resolve_targets() {
|
|
111
148
|
case "$TARGET_MODE" in
|
|
112
149
|
claude) echo "claude" ;;
|
|
@@ -145,12 +182,12 @@ generate_skill() {
|
|
|
145
182
|
|
|
146
183
|
mkdir -p "$skill_dir"
|
|
147
184
|
|
|
148
|
-
ensure_symlink "$skill_dir/docs" "$
|
|
149
|
-
echo " [$target] Created docs symlink -> $
|
|
185
|
+
ensure_symlink "$skill_dir/docs" "$REPO_DOCS_DIR"
|
|
186
|
+
echo " [$target] Created docs symlink -> $REPO_DOCS_DIR"
|
|
150
187
|
|
|
151
188
|
if [ "$HAS_JA" = "true" ]; then
|
|
152
|
-
ensure_symlink "$skill_dir/docs-ja" "$
|
|
153
|
-
echo " [$target] Created docs-ja symlink -> $
|
|
189
|
+
ensure_symlink "$skill_dir/docs-ja" "$REPO_DOCS_JA_DIR"
|
|
190
|
+
echo " [$target] Created docs-ja symlink -> $REPO_DOCS_JA_DIR"
|
|
154
191
|
fi
|
|
155
192
|
|
|
156
193
|
cat > "$skill_dir/SKILL.md" << SKILLEOF
|
|
@@ -167,7 +204,7 @@ argument-hint: "[-u|--update] [topic keyword, e.g., 'configuration', 'sidebar',
|
|
|
167
204
|
# $PROJECT_NAME Documentation Reference
|
|
168
205
|
|
|
169
206
|
Look up documentation from the $PROJECT_NAME project for $assistant_label.
|
|
170
|
-
Documentation base path: \`src/content/docs\` (relative to
|
|
207
|
+
Documentation base path: \`src/content/docs\` (relative to the project root: \`$ROOT_DIR\`)
|
|
171
208
|
|
|
172
209
|
## Mode Detection
|
|
173
210
|
|
|
@@ -197,7 +234,7 @@ The user has new information and wants to add or update documentation in this re
|
|
|
197
234
|
the topic. Read them to understand what is already covered.
|
|
198
235
|
3. **Decide create vs update**: If an existing article covers the topic, update
|
|
199
236
|
it. Otherwise, create a new \`.mdx\` file in the appropriate subdirectory.
|
|
200
|
-
4. **Write the content**: Follow the doc-authoring rules in
|
|
237
|
+
4. **Write the content**: Follow the doc-authoring rules in this project's CLAUDE.md (\`$ROOT_DIR/CLAUDE.md\`):
|
|
201
238
|
- Required frontmatter: \`title\` (string). Always set \`sidebar_position\`.
|
|
202
239
|
Optional: \`description\`, \`sidebar_label\`, \`tags\`, etc.
|
|
203
240
|
- Do NOT use \`# h1\` in content — the frontmatter \`title\` renders as h1.
|
|
@@ -210,8 +247,8 @@ The user has new information and wants to add or update documentation in this re
|
|
|
210
247
|
\`docs-ja/\` mirroring the English directory structure. Keep code blocks,
|
|
211
248
|
Mermaid diagrams, and \`<HtmlPreview>\` blocks identical — only translate
|
|
212
249
|
surrounding prose. Exception: pages with \`generated: true\` skip translation.
|
|
213
|
-
6. **Format**:
|
|
214
|
-
7. **Verify**:
|
|
250
|
+
6. **Format**: ${FORMAT_STEP}
|
|
251
|
+
7. **Verify**: ${VERIFY_STEP}
|
|
215
252
|
|
|
216
253
|
## Documentation Structure
|
|
217
254
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zudo-doc-design-system
|
|
3
|
+
description: "Project-specific CSS and component rules for zudo-doc. Must be consulted before writing or editing CSS, Tailwind classes, color tokens, or component markup in this project. Covers: component-first strategy, design token system, three-tier color architecture, and palette index convention. Triggered by 'design system', 'zudo-doc-design-system', 'zudo-doc-css-wisdom' (old name)."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "[topic: tokens, colors, component-first, palette]"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# zudo-doc CSS & Component Rules
|
|
9
|
+
|
|
10
|
+
**IMPORTANT**: These rules are mandatory for all code changes in this project that touch CSS, Tailwind classes, color tokens, or component markup. Read the relevant section before making changes.
|
|
11
|
+
|
|
12
|
+
## How to Use
|
|
13
|
+
|
|
14
|
+
Based on the topic, read the specific reference doc. These pages aren't
|
|
15
|
+
scaffolded into a fresh project — they're the published zudo-doc showcase
|
|
16
|
+
docs, kept up to date at the source:
|
|
17
|
+
|
|
18
|
+
| Topic | Where to look |
|
|
19
|
+
|-------|------|
|
|
20
|
+
| Spacing, typography, layout tokens | https://zudo-doc.takazudomodular.com/docs/reference/design-system |
|
|
21
|
+
| Component-first methodology | https://zudo-doc.takazudomodular.com/docs/reference/component-first |
|
|
22
|
+
| Color tokens, palette, schemes | https://zudo-doc.takazudomodular.com/docs/reference/color |
|
|
23
|
+
|
|
24
|
+
For the actual token values in THIS project, the source of truth is local:
|
|
25
|
+
`src/styles/global.css` (the `@theme` override block) plus the shipped
|
|
26
|
+
`@takazudo/zudo-doc/theme.css` and `@takazudo/zudo-doc/content.css` (imported
|
|
27
|
+
by `global.css` — read them via `node_modules/@takazudo/zudo-doc/dist/` for
|
|
28
|
+
the resolved token names/values).
|
|
29
|
+
|
|
30
|
+
Read ONLY the section relevant to your task. Apply its rules strictly.
|
|
31
|
+
|
|
32
|
+
## Quick Rules (always apply)
|
|
33
|
+
|
|
34
|
+
### Component First (no custom CSS classes)
|
|
35
|
+
|
|
36
|
+
- **NEVER** create CSS module files, custom class names, or separate stylesheets
|
|
37
|
+
- **ALWAYS** use Tailwind utility classes directly in component markup
|
|
38
|
+
- The component itself is the abstraction — `.card`, `.btn-primary` are forbidden
|
|
39
|
+
- Use props for variants, not CSS modifiers
|
|
40
|
+
|
|
41
|
+
### Design Tokens (no arbitrary values)
|
|
42
|
+
|
|
43
|
+
- **NEVER** use Tailwind default colors (`bg-gray-500`, `text-blue-600`) — they are reset to `initial`
|
|
44
|
+
- **NEVER** use arbitrary values (`text-[0.875rem]`, `p-[1.2rem]`) when a token exists
|
|
45
|
+
- **ALWAYS** use project tokens: `text-fg`, `bg-surface`, `border-muted`, `p-hsp-md`, `text-small`
|
|
46
|
+
- Spacing: `hsp-*` (horizontal), `vsp-*` (vertical) — see the design-system reference doc above for the full list
|
|
47
|
+
- Typography: `text-caption`, `text-small`, `text-body`, `text-heading` etc.
|
|
48
|
+
|
|
49
|
+
### Color Tokens (three-tier system)
|
|
50
|
+
|
|
51
|
+
- **Tier 1** (ramps): shared `base` (5 stops), `accent` (3 stops), and `state` (`danger`/`success`/`warning`/`info`) OKLCH ramps — no Tailwind utility reaches these directly (no `p0`–`p15`-style classes); they only feed Tier 2
|
|
52
|
+
- **Tier 2** (semantic): `text-fg`, `bg-surface`, `border-muted`, `text-accent` — the only Tailwind-facing color tokens; prefer these always
|
|
53
|
+
- **NEVER** use hardcoded hex values in components
|
|
54
|
+
- Both bundled schemes (`Default Light`, `Default Dark`) share the same ramps; only their per-mode wiring (`map`) differs. This project doesn't own a copy of the ramp/map definitions — they're package-owned, shipped compiled under `node_modules/@takazudo/zudo-doc/dist/color-schemes-defaults/`. Only override the `@theme` tokens you actually need to change, in `src/styles/global.css`
|
|
55
|
+
|
|
56
|
+
### Search & highlight tokens (role-split)
|
|
57
|
+
|
|
58
|
+
Highlight roles are deliberately split across dedicated semantic tokens — do **not** share one token across unrelated highlight UIs.
|
|
59
|
+
|
|
60
|
+
- `matched-keyword-bg` / `matched-keyword-fg` — background and foreground of the search panel `<mark>` element. Driven by `--color-matched-keyword-bg` / `--color-matched-keyword-fg`; live-editable in the Design Token Panel, if that feature is enabled. This is the single source of truth for "why is this color yellow in the search results" — the panel swatch matches the rendered highlight 1:1.
|
|
61
|
+
- `warning` — drives admonitions (`:::warning`), find-in-page (`.find-match`, `.find-match-active`), and any UI that is semantically a warning. Do **not** reuse it for new UI-chrome highlights.
|
|
62
|
+
|
|
63
|
+
**Rule**: when a new highlight role appears (new kind of mark, new pill, new callout), add a dedicated semantic token rather than bolting it onto `--color-warning` or another existing token. Each visible highlight color should map to exactly one panel swatch.
|
|
64
|
+
|
|
65
|
+
### Hover-state underline for link-like elements
|
|
66
|
+
|
|
67
|
+
Any element that navigates (rendered as `<a href>` or behaves as a link) MUST have `hover:underline focus-visible:underline`. Keyboard users need the same affordance as mouse users — never add `hover:underline` without the `focus-visible:underline` pair.
|
|
68
|
+
|
|
69
|
+
- **Links (do underline)**: doc content links, sidebar items, header main-nav, header overflow menu items, color-tweak panel unselected tabs, search result rows, footer links, doc history entries, breadcrumb trails, mobile TOC entries.
|
|
70
|
+
- **Controls (do NOT underline)**: buttons, toggles, sidebar resizer, palette selectors, color swatches, close icons. These use border/bg hover instead.
|
|
71
|
+
|
|
72
|
+
Precedent to copy the pattern from: every package-owned interactive
|
|
73
|
+
component already follows it (sidebar tree nav, header nav, breadcrumb
|
|
74
|
+
trail, and more, all shipped from `@takazudo/zudo-doc`) — see the live
|
|
75
|
+
showcase at https://zudo-doc.takazudomodular.com for a working reference.
|
|
76
|
+
Match the same hover/focus pairing in any new component you add under
|
|
77
|
+
`src/components/`, or in a package component you swizzle out via
|
|
78
|
+
`zudo-doc eject <component>`.
|
|
79
|
+
|
|
80
|
+
### Light-mode / dark-mode contrast
|
|
81
|
+
|
|
82
|
+
- Every color token must resolve to a legible foreground/background pairing
|
|
83
|
+
in BOTH bundled schemes (`Default Light`, `Default Dark`) — never
|
|
84
|
+
hardcode a value that only works in one mode.
|
|
85
|
+
- Prefer the Tier 2 semantic tokens (`text-fg`, `bg-surface`, `border-muted`,
|
|
86
|
+
...) over any raw color — they're the only values guaranteed to be
|
|
87
|
+
re-mapped per scheme.
|
|
88
|
+
- When adding a new token, define it for both scheme `map`s (or as a
|
|
89
|
+
scheme-agnostic ramp value) so light/dark parity is never accidental.
|
|
90
|
+
|
|
91
|
+
### Server-rendered Preact vs client islands
|
|
92
|
+
|
|
93
|
+
- All components in this project are Preact `.tsx` — there are no `.astro`
|
|
94
|
+
files.
|
|
95
|
+
- Default to **server-rendered Preact `.tsx`** (no `client:*` directive) —
|
|
96
|
+
emits zero JS.
|
|
97
|
+
- Promote to a **client island** only when interactivity is needed
|
|
98
|
+
- Both follow the same utility-class approach
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zudo-doc-translate
|
|
3
|
+
description: Translate zudo-doc documentation between English and Japanese with project-specific conventions.
|
|
4
|
+
triggers:
|
|
5
|
+
- translate
|
|
6
|
+
- 翻訳
|
|
7
|
+
- i18n
|
|
8
|
+
- en to ja
|
|
9
|
+
- ja to en
|
|
10
|
+
- translate docs
|
|
11
|
+
- ドキュメント翻訳
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# zudo-doc Translation Skill
|
|
15
|
+
|
|
16
|
+
Translate documentation between English and Japanese following project-specific conventions.
|
|
17
|
+
|
|
18
|
+
## i18n Structure
|
|
19
|
+
|
|
20
|
+
This skill only applies when this project's i18n feature is enabled. A
|
|
21
|
+
non-i18n scaffold has no secondary-locale content directory (e.g. no
|
|
22
|
+
`docs-ja/`) and no locale-prefixed routes — there's nothing to translate.
|
|
23
|
+
Check `zfb.config.ts`'s `zudoDoc({...})` call: if it sets a non-empty
|
|
24
|
+
`locales` field, i18n is on.
|
|
25
|
+
|
|
26
|
+
- Default-locale docs: `src/content/docs/` — routes at `/docs/...` (the
|
|
27
|
+
default locale is never prefixed)
|
|
28
|
+
- Secondary-locale docs: `src/content/docs-<locale>/` — routes at
|
|
29
|
+
`/<locale>/docs/...`. For the common preset (English default, Japanese
|
|
30
|
+
secondary) this is `src/content/docs-ja/` at `/ja/docs/...`; the rest of
|
|
31
|
+
this skill assumes that pairing, but confirm the actual locale code and
|
|
32
|
+
directory from `zfb.config.ts` if this project uses a different one
|
|
33
|
+
- Directory structures must mirror each other exactly (same filenames, same folder hierarchy)
|
|
34
|
+
- Locale configuration lives in this project's `zfb.config.ts`, inside the
|
|
35
|
+
`zudoDoc({...})` call: `defaultLocale` (the default locale code — always
|
|
36
|
+
unprefixed, no separate flag needed) and `locales` (a map of additional
|
|
37
|
+
locale code → `{ label, dir }`; each entry becomes a `/<code>/docs/...`
|
|
38
|
+
route tree)
|
|
39
|
+
|
|
40
|
+
## Translation Rules
|
|
41
|
+
|
|
42
|
+
### Keep in English (do NOT translate)
|
|
43
|
+
|
|
44
|
+
- Component names: `<Note>`, `<Tip>`, `<Info>`, `<Warning>`, `<Danger>`, `<Tabs>`, `<TabItem>`, `<Details>`
|
|
45
|
+
- Code blocks — code is universal
|
|
46
|
+
- File paths: `src/content/docs/...`, `.claude/skills/...`, etc.
|
|
47
|
+
- CLI commands: `<pm> run dev`, `<pm> run build`, etc. (`<pm>` = this project's package manager)
|
|
48
|
+
- Technical terms that are standard in English (e.g., component, props, frontmatter, slug)
|
|
49
|
+
- Frontmatter field keys (`title`, `description`, `sidebar_position`, `category`)
|
|
50
|
+
|
|
51
|
+
### Translate
|
|
52
|
+
|
|
53
|
+
- Frontmatter field values (e.g., the `title` value, the `description` value)
|
|
54
|
+
- The `title` prop of admonition components (e.g., `<Note title="注意">`)
|
|
55
|
+
- Prose content, headings, list items, table cells (except as noted below)
|
|
56
|
+
|
|
57
|
+
### Table conventions
|
|
58
|
+
|
|
59
|
+
- In tables with a "Required" column: use **"Yes"** / **"No"** directly, NOT "はい" / "いいえ" — Japanese conversational yes/no is unnatural in technical documentation
|
|
60
|
+
|
|
61
|
+
### Internal links
|
|
62
|
+
|
|
63
|
+
- Adjust link paths when translating:
|
|
64
|
+
- En→Ja: `/docs/getting-started` → `/ja/docs/getting-started`
|
|
65
|
+
- Ja→En: `/ja/docs/getting-started` → `/docs/getting-started`
|
|
66
|
+
|
|
67
|
+
## File Naming
|
|
68
|
+
|
|
69
|
+
- Japanese files use the **same filenames** as English (e.g., `writing-docs.mdx`)
|
|
70
|
+
- Only the parent directory differs: `docs/` vs `docs-ja/`
|
|
71
|
+
- Example: `src/content/docs/guides/writing-docs.mdx` → `src/content/docs-ja/guides/writing-docs.mdx`
|
|
72
|
+
|
|
73
|
+
## Workflow
|
|
74
|
+
|
|
75
|
+
### En→Ja Translation
|
|
76
|
+
|
|
77
|
+
1. Read the English source file from `src/content/docs/`
|
|
78
|
+
2. Check if the corresponding Japanese file already exists in `src/content/docs-ja/`
|
|
79
|
+
- If it exists, read it first — use it as a base and update from the English source rather than overwriting from scratch
|
|
80
|
+
- If it does not exist, create the file at the equivalent path in `src/content/docs-ja/`
|
|
81
|
+
3. Translate the content following the rules above
|
|
82
|
+
4. Verify internal links point to `/ja/docs/...`
|
|
83
|
+
|
|
84
|
+
### Ja→En Translation
|
|
85
|
+
|
|
86
|
+
1. Read the Japanese source file from `src/content/docs-ja/`
|
|
87
|
+
2. Check if the corresponding English file already exists in `src/content/docs/`
|
|
88
|
+
- If it exists, read it first — use it as a base and update from the Japanese source rather than overwriting from scratch
|
|
89
|
+
- If it does not exist, create the file at the equivalent path in `src/content/docs/`
|
|
90
|
+
3. Translate the content following the rules above
|
|
91
|
+
4. Verify internal links point to `/docs/...` (no `/ja/` prefix)
|
|
92
|
+
|
|
93
|
+
### Post-Translation Checks
|
|
94
|
+
|
|
95
|
+
- Frontmatter keys are unchanged (only values translated)
|
|
96
|
+
- All admonition component names remain in English
|
|
97
|
+
- Code blocks are untouched
|
|
98
|
+
- Internal links use the correct locale prefix
|
|
99
|
+
- Directory structure mirrors the source language
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zudo-doc-version-bump
|
|
3
|
+
description: >-
|
|
4
|
+
Bump package version, update changelog and versioned docs snapshot (if those features are
|
|
5
|
+
enabled), commit, tag, and create a GitHub release. Use when: (1) User says 'version bump',
|
|
6
|
+
'bump version', 'release', or 'zudo-doc-version-bump', (2) User wants to create a new release
|
|
7
|
+
of this project.
|
|
8
|
+
user-invocable: true
|
|
9
|
+
disable-model-invocation: true
|
|
10
|
+
argument-description: "Optional: major, minor, or patch to skip the proposal step"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /zudo-doc-version-bump
|
|
14
|
+
|
|
15
|
+
Bump the version, update changelog content (if the changelog feature is enabled), optionally
|
|
16
|
+
archive the current docs as a versioned snapshot (if the versioning feature is enabled), commit,
|
|
17
|
+
tag, and create a GitHub release.
|
|
18
|
+
|
|
19
|
+
## Preconditions
|
|
20
|
+
|
|
21
|
+
Before doing anything else, verify ALL of the following. If any check fails, stop and tell the
|
|
22
|
+
user.
|
|
23
|
+
|
|
24
|
+
1. Current branch is `main` (or your project's default branch)
|
|
25
|
+
2. Working tree is clean (`git status --porcelain` returns empty)
|
|
26
|
+
|
|
27
|
+
Find the latest version tag, if any:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git tag -l 'v*' --sort=-v:refname | head -1
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
A freshly scaffolded project has no tags yet — that's expected on the first release. If no tag
|
|
34
|
+
is found, do NOT ask the user to create one first; proceed straight to analyzing the full commit
|
|
35
|
+
history in the next step instead.
|
|
36
|
+
|
|
37
|
+
## Analyze changes since last tag
|
|
38
|
+
|
|
39
|
+
If a previous tag was found, diff against it:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git log <last-tag>..HEAD --oneline
|
|
43
|
+
git diff <last-tag>..HEAD --stat
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If no tag exists yet (first release), analyze the whole history instead:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git log --oneline
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Categorize each commit by its conventional-commit prefix:
|
|
53
|
+
|
|
54
|
+
- **Breaking Changes**: commits with an exclamation mark suffix (e.g. `feat!:`) or BREAKING CHANGE in body
|
|
55
|
+
- **Features**: `feat:` prefix
|
|
56
|
+
- **Bug Fixes**: `fix:` prefix
|
|
57
|
+
- **Other Changes**: everything else (`docs:`, `chore:`, `refactor:`, `ci:`, `test:`, `style:`, `perf:`, etc.)
|
|
58
|
+
|
|
59
|
+
## Propose version bump
|
|
60
|
+
|
|
61
|
+
Based on the changes:
|
|
62
|
+
|
|
63
|
+
- If there are breaking changes → propose **major** bump
|
|
64
|
+
- If there are features (no breaking) → propose **minor** bump
|
|
65
|
+
- Otherwise → propose **patch** bump
|
|
66
|
+
|
|
67
|
+
If the user passed an argument (`major`, `minor`, or `patch`), use that directly instead of proposing.
|
|
68
|
+
|
|
69
|
+
Present the proposal to the user:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
Proposed bump: {current} → {new} ({type})
|
|
73
|
+
|
|
74
|
+
Breaking Changes:
|
|
75
|
+
- description (hash)
|
|
76
|
+
|
|
77
|
+
Features:
|
|
78
|
+
- description (hash)
|
|
79
|
+
|
|
80
|
+
Bug Fixes:
|
|
81
|
+
- description (hash)
|
|
82
|
+
|
|
83
|
+
Other Changes:
|
|
84
|
+
- description (hash)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Only show sections that have entries. **Wait for user confirmation before proceeding.**
|
|
88
|
+
|
|
89
|
+
If this is a **major** version bump and the project has the versioning feature enabled (see
|
|
90
|
+
"Archive docs as a versioned snapshot" below), ask the user whether they want to archive the
|
|
91
|
+
current docs as a versioned snapshot before continuing.
|
|
92
|
+
|
|
93
|
+
## Bump the version
|
|
94
|
+
|
|
95
|
+
A `create-zudo-doc` scaffold has no bundled version-bump script — bump `package.json` directly:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm version {NEW_VERSION} --no-git-tag-version
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`npm version` is a plain `npm` CLI feature (Node always ships `npm`), so this works no matter
|
|
102
|
+
which package manager (`<pm>`) the project otherwise uses. `--no-git-tag-version` stops it from
|
|
103
|
+
also creating a commit/tag — this skill handles that later, once everything else is ready. A
|
|
104
|
+
direct edit of the `"version"` field in `package.json` works identically if preferred.
|
|
105
|
+
|
|
106
|
+
## Update the changelog (if enabled)
|
|
107
|
+
|
|
108
|
+
Check whether the project has a changelog page:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
test -f src/content/docs/changelog/index.mdx && echo "changelog present"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
If that file does not exist, the changelog feature is not in use — skip this whole section and
|
|
115
|
+
go straight to "Archive docs as a versioned snapshot".
|
|
116
|
+
|
|
117
|
+
Unlike a per-version file, a scaffolded changelog is a single `index.mdx` page. Add the new
|
|
118
|
+
release as a section **above** the existing ones (newest first), using only the categories that
|
|
119
|
+
have entries.
|
|
120
|
+
|
|
121
|
+
### English changelog (`src/content/docs/changelog/index.mdx`)
|
|
122
|
+
|
|
123
|
+
```mdx
|
|
124
|
+
## {NEW_VERSION}
|
|
125
|
+
|
|
126
|
+
### Breaking Changes
|
|
127
|
+
|
|
128
|
+
- Description (commit-hash)
|
|
129
|
+
|
|
130
|
+
### Features
|
|
131
|
+
|
|
132
|
+
- Description (commit-hash)
|
|
133
|
+
|
|
134
|
+
### Bug Fixes
|
|
135
|
+
|
|
136
|
+
- Description (commit-hash)
|
|
137
|
+
|
|
138
|
+
### Other Changes
|
|
139
|
+
|
|
140
|
+
- Description (commit-hash)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
On the very first bump, the page still has the scaffold's starter `## Unreleased` section —
|
|
144
|
+
replace that heading with `## {NEW_VERSION}` rather than adding a second heading.
|
|
145
|
+
|
|
146
|
+
### Japanese changelog (`src/content/docs-ja/changelog/index.mdx`)
|
|
147
|
+
|
|
148
|
+
Only applies when i18n is enabled (the `docs-ja` directory exists). If it doesn't, skip this file.
|
|
149
|
+
|
|
150
|
+
```mdx
|
|
151
|
+
## {NEW_VERSION}
|
|
152
|
+
|
|
153
|
+
### 破壊的変更
|
|
154
|
+
|
|
155
|
+
- Description (commit-hash)
|
|
156
|
+
|
|
157
|
+
### 機能
|
|
158
|
+
|
|
159
|
+
- Description (commit-hash)
|
|
160
|
+
|
|
161
|
+
### バグ修正
|
|
162
|
+
|
|
163
|
+
- Description (commit-hash)
|
|
164
|
+
|
|
165
|
+
### その他の変更
|
|
166
|
+
|
|
167
|
+
- Description (commit-hash)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
On the first bump, replace the starter `## 未リリース` heading the same way.
|
|
171
|
+
|
|
172
|
+
Rules:
|
|
173
|
+
|
|
174
|
+
- Only include sections that have entries
|
|
175
|
+
- Each entry should be the commit subject with the short hash in parentheses
|
|
176
|
+
|
|
177
|
+
## Archive docs as a versioned snapshot (if enabled, major bumps only)
|
|
178
|
+
|
|
179
|
+
Only relevant when the project has the versioning feature enabled — check for a `versions`
|
|
180
|
+
array in `zfb.config.ts`'s `zudoDoc({...})` call. If `versions` is absent, `false`, or the user
|
|
181
|
+
declined the snapshot offer above, skip this whole section.
|
|
182
|
+
|
|
183
|
+
1. Derive the old version's slug by dropping the patch component, e.g. `0.1.0` → `0.1`,
|
|
184
|
+
`1.2.3` → `1.2`.
|
|
185
|
+
2. Copy the current docs into a versioned directory:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cp -r src/content/docs src/content/docs-v{OLD_SLUG}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
3. If i18n is enabled (a `docs-ja` directory exists), also copy the Japanese docs:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
cp -r src/content/docs-ja src/content/docs-v{OLD_SLUG}-ja
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
4. Add an entry to the `versions` array in `zfb.config.ts`:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
versions: [
|
|
201
|
+
{
|
|
202
|
+
slug: "{OLD_SLUG}",
|
|
203
|
+
label: "{OLD_VERSION}",
|
|
204
|
+
docsDir: "src/content/docs-v{OLD_SLUG}",
|
|
205
|
+
locales: {
|
|
206
|
+
ja: { dir: "src/content/docs-v{OLD_SLUG}-ja" },
|
|
207
|
+
},
|
|
208
|
+
banner: "unmaintained",
|
|
209
|
+
},
|
|
210
|
+
// ...existing versions
|
|
211
|
+
],
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Drop the `locales` block entirely when i18n is not enabled.
|
|
215
|
+
|
|
216
|
+
5. `src/content/docs/` (and `src/content/docs-ja/`) now represent the new, latest version —
|
|
217
|
+
no further action needed there. The version switcher and versions listing page pick up the
|
|
218
|
+
new entry automatically at build time; nothing else needs wiring.
|
|
219
|
+
|
|
220
|
+
## Build and test
|
|
221
|
+
|
|
222
|
+
Run the project's pre-push validation script:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
<pm> run b4push
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
If anything fails, fix the issue and re-run. Do not proceed with committing until all checks
|
|
229
|
+
pass. (In a freshly scaffolded project this script may just be a `check`-then-`build` stub —
|
|
230
|
+
that's fine; expand it into a richer pipeline as the project's testing needs grow.)
|
|
231
|
+
|
|
232
|
+
## Commit changes
|
|
233
|
+
|
|
234
|
+
Stage and commit **all** version bump changes:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
git add package.json
|
|
238
|
+
git add src/content/docs/changelog/index.mdx src/content/docs-ja/changelog/index.mdx 2>/dev/null
|
|
239
|
+
git add src/content/docs-v* 2>/dev/null
|
|
240
|
+
# Also stage any other modified files (e.g. formatting fixes from the build/test step)
|
|
241
|
+
git diff --name-only | xargs -r git add
|
|
242
|
+
git commit -m "chore: Bump version to v{NEW_VERSION}"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Push and wait for CI (if configured)
|
|
246
|
+
|
|
247
|
+
Push the commits first (without the tag):
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
git push
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
If the project has CI configured (e.g. a GitHub Actions workflow), wait for it to pass. Use
|
|
254
|
+
`gh run list --branch main --limit 1 --json status,conclusion,headSha` and verify the `headSha`
|
|
255
|
+
matches the pushed commit. Poll every 30 seconds, with a **maximum of 10 minutes**. If CI is
|
|
256
|
+
still running after 10 minutes, ask the user whether to keep waiting or proceed. If the project
|
|
257
|
+
has no CI workflow, skip straight to tagging.
|
|
258
|
+
|
|
259
|
+
If CI fails, investigate the failure with `gh run view <run-id> --log-failed`, fix the issue,
|
|
260
|
+
commit, and push again.
|
|
261
|
+
|
|
262
|
+
**Do not tag or publish until CI is green (or there is no CI to wait for).**
|
|
263
|
+
|
|
264
|
+
## Tag, push tag, and create GitHub release
|
|
265
|
+
|
|
266
|
+
**Ask the user for confirmation before tagging.**
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
git tag v{NEW_VERSION}
|
|
270
|
+
git push --tags
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
After pushing the tag, create a GitHub release. If the changelog feature is enabled, pull the
|
|
274
|
+
section you just wrote out of the single changelog page as the release notes:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
NOTES=$(awk -v ver="## {NEW_VERSION}" '$0==ver{f=1;next} f&&/^## /{f=0} f' src/content/docs/changelog/index.mdx)
|
|
278
|
+
gh release create v{NEW_VERSION} --title "v{NEW_VERSION}" --notes "$NOTES"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
If the changelog feature is off, write the release notes directly from the categorized commit
|
|
282
|
+
analysis instead:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
gh release create v{NEW_VERSION} --title "v{NEW_VERSION}" --notes "..."
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Publish to npm (if applicable)
|
|
289
|
+
|
|
290
|
+
If the package is **not** marked as `"private": true` in `package.json`, tell the user to publish:
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
The package is ready for npm publishing. Run:
|
|
294
|
+
|
|
295
|
+
<pm> publish
|
|
296
|
+
|
|
297
|
+
(This requires browser-based 2FA and must be done manually.)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
If the package is `"private": true`, skip this step and inform the user:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
Package is marked as private — skipping npm publish.
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Done
|
|
307
|
+
|
|
308
|
+
Report the summary:
|
|
309
|
+
|
|
310
|
+
- Version bumped: `{OLD_VERSION}` → `{NEW_VERSION}`
|
|
311
|
+
- Changelog updated (EN + JA, if the changelog feature is enabled)
|
|
312
|
+
- Docs snapshot created (if the versioning feature is enabled and a snapshot was taken)
|
|
313
|
+
- Git tag: `v{NEW_VERSION}`
|
|
314
|
+
- GitHub release: link to the release
|
|
315
|
+
- npm publish status (published / skipped for private package)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"app": {
|
|
11
11
|
"windows": [],
|
|
12
12
|
"security": {
|
|
13
|
-
"csp": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; img-src 'self' data:; font-src 'self' data: https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; script-src 'self' 'unsafe-inline' https://esm.sh; connect-src 'self' https://esm.sh https://cdn.jsdelivr.net"
|
|
13
|
+
"csp": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; img-src 'self' data:; font-src 'self' data: https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; script-src 'self' 'unsafe-inline' https://esm.sh https://cdn.jsdelivr.net; connect-src 'self' https://esm.sh https://cdn.jsdelivr.net"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"bundle": {
|