@ulysses-ai/create-workspace 0.13.0-beta.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/LICENSE +21 -0
- package/README.md +108 -0
- package/bin/create.mjs +79 -0
- package/lib/git.mjs +26 -0
- package/lib/init.mjs +129 -0
- package/lib/payload.mjs +44 -0
- package/lib/prompts.mjs +113 -0
- package/lib/scaffold.mjs +84 -0
- package/lib/upgrade.mjs +42 -0
- package/package.json +43 -0
- package/template/.claude/agents/aside-researcher.md +48 -0
- package/template/.claude/agents/implementer.md +39 -0
- package/template/.claude/agents/researcher.md +40 -0
- package/template/.claude/agents/reviewer.md +47 -0
- package/template/.claude/hooks/_utils.mjs +196 -0
- package/template/.claude/hooks/_utils.test.mjs +99 -0
- package/template/.claude/hooks/post-compact.mjs +7 -0
- package/template/.claude/hooks/pre-compact.mjs +34 -0
- package/template/.claude/hooks/repo-write-detection.mjs +107 -0
- package/template/.claude/hooks/session-end.mjs +91 -0
- package/template/.claude/hooks/session-start.mjs +150 -0
- package/template/.claude/hooks/subagent-start.mjs +44 -0
- package/template/.claude/hooks/workspace-update-check.mjs +42 -0
- package/template/.claude/hooks/worktree-create.mjs +53 -0
- package/template/.claude/lib/session-frontmatter.mjs +265 -0
- package/template/.claude/lib/session-frontmatter.test.mjs +242 -0
- package/template/.claude/recipes/migrate-from-notion.md +120 -0
- package/template/.claude/rules/agent-rules.md.skip +32 -0
- package/template/.claude/rules/cloud-infrastructure.md.skip +15 -0
- package/template/.claude/rules/coherent-revisions.md +24 -0
- package/template/.claude/rules/documentation.md.skip +13 -0
- package/template/.claude/rules/git-conventions.md +34 -0
- package/template/.claude/rules/honest-pushback.md +56 -0
- package/template/.claude/rules/local-dev-environment.md.skip +60 -0
- package/template/.claude/rules/memory-guidance.md +26 -0
- package/template/.claude/rules/product-integrity.md.skip +24 -0
- package/template/.claude/rules/scope-guard.md.skip +22 -0
- package/template/.claude/rules/superpowers-workflow.md.skip +22 -0
- package/template/.claude/rules/token-economics.md.skip +31 -0
- package/template/.claude/rules/work-item-tracking.md +90 -0
- package/template/.claude/rules/workspace-structure.md +69 -0
- package/template/.claude/scripts/add-repo-to-session.mjs +78 -0
- package/template/.claude/scripts/cleanup-work-session.mjs +108 -0
- package/template/.claude/scripts/create-work-session.mjs +124 -0
- package/template/.claude/scripts/migrate-open-work.mjs +91 -0
- package/template/.claude/scripts/migrate-session-layout.mjs +236 -0
- package/template/.claude/scripts/migrate-session-layout.test.mjs +144 -0
- package/template/.claude/scripts/trackers/github-issues.mjs +170 -0
- package/template/.claude/scripts/trackers/github-issues.test.mjs +190 -0
- package/template/.claude/scripts/trackers/interface.mjs +25 -0
- package/template/.claude/scripts/trackers/interface.test.mjs +40 -0
- package/template/.claude/settings.json +107 -0
- package/template/.claude/skills/aside/SKILL.md +125 -0
- package/template/.claude/skills/braindump/SKILL.md +96 -0
- package/template/.claude/skills/build-docs-site/SKILL.md +323 -0
- package/template/.claude/skills/build-docs-site/checklists/framing.md +221 -0
- package/template/.claude/skills/build-docs-site/checklists/pitfalls.md +228 -0
- package/template/.claude/skills/build-docs-site/checklists/review.md +130 -0
- package/template/.claude/skills/build-docs-site/scripts/bulk-fill-migration.py +393 -0
- package/template/.claude/skills/build-docs-site/scripts/forbidden-word-grep.mjs +159 -0
- package/template/.claude/skills/build-docs-site/scripts/leak-grep.mjs +328 -0
- package/template/.claude/skills/build-docs-site/templates/custom.css.tmpl +212 -0
- package/template/.claude/skills/build-docs-site/templates/docusaurus.config.ts.tmpl +95 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/Arrow.tsx +87 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/Box.tsx +90 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/DiagramContainer.tsx +46 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/Region.tsx +68 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/SectionTitle.tsx +42 -0
- package/template/.claude/skills/build-docs-site/templates/primitives/tokens.ts +67 -0
- package/template/.claude/skills/build-docs-site/templates/sidebars.ts.tmpl +89 -0
- package/template/.claude/skills/build-docs-site/templates/spec.md.tmpl +119 -0
- package/template/.claude/skills/complete-work/SKILL.md +369 -0
- package/template/.claude/skills/handoff/SKILL.md +98 -0
- package/template/.claude/skills/maintenance/SKILL.md +116 -0
- package/template/.claude/skills/pause-work/SKILL.md +98 -0
- package/template/.claude/skills/promote/SKILL.md +77 -0
- package/template/.claude/skills/release/SKILL.md +126 -0
- package/template/.claude/skills/setup-tracker/SKILL.md +117 -0
- package/template/.claude/skills/start-work/SKILL.md +234 -0
- package/template/.claude/skills/sync-work/SKILL.md +73 -0
- package/template/.claude/skills/workspace-init/SKILL.md +420 -0
- package/template/.claude/skills/workspace-update/SKILL.md +108 -0
- package/template/.mcp.json +12 -0
- package/template/CLAUDE.md.tmpl +32 -0
- package/template/_gitignore +28 -0
- package/template/workspace.json.tmpl +15 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* leak-grep.mjs — implementation-detail leak detector for the build-docs-site skill.
|
|
4
|
+
*
|
|
5
|
+
* Scans a project's dependency manifests to derive a list of installed
|
|
6
|
+
* package names, then greps documentation content for any mention of
|
|
7
|
+
* those names. The list is project-accurate — no hardcoded master list.
|
|
8
|
+
*
|
|
9
|
+
* Manifests scanned:
|
|
10
|
+
* - package.json (Node)
|
|
11
|
+
* - pyproject.toml (Python)
|
|
12
|
+
* - requirements.txt (Python)
|
|
13
|
+
* - Cargo.toml (Rust)
|
|
14
|
+
* - Gemfile (Ruby)
|
|
15
|
+
* - go.mod (Go)
|
|
16
|
+
*
|
|
17
|
+
* Excludes Tech Stack appendix files (where implementation choices are
|
|
18
|
+
* the subject) by file path filter.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* node leak-grep.mjs <project-root> <docs-path> [--exclude path1,path2]
|
|
22
|
+
*
|
|
23
|
+
* Output: JSON to stdout
|
|
24
|
+
* {
|
|
25
|
+
* manifestsScanned: [...],
|
|
26
|
+
* packagesFound: [...],
|
|
27
|
+
* hits: [{file, line, term, context}],
|
|
28
|
+
* excluded: [...]
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
33
|
+
import { join, relative, basename } from 'node:path';
|
|
34
|
+
|
|
35
|
+
// ---------- CLI parsing ----------
|
|
36
|
+
|
|
37
|
+
const args = process.argv.slice(2);
|
|
38
|
+
if (args.length < 2) {
|
|
39
|
+
console.error('Usage: leak-grep.mjs <project-root> <docs-path> [--exclude path1,path2]');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const projectRoot = args[0];
|
|
44
|
+
const docsPath = args[1];
|
|
45
|
+
const excludeFlag = args.indexOf('--exclude');
|
|
46
|
+
const userExcludes = excludeFlag >= 0 && args[excludeFlag + 1]
|
|
47
|
+
? args[excludeFlag + 1].split(',')
|
|
48
|
+
: [];
|
|
49
|
+
|
|
50
|
+
// Default excludes — the Tech Stack appendix is where implementation
|
|
51
|
+
// details are the subject. Match common naming conventions.
|
|
52
|
+
const defaultExcludes = [
|
|
53
|
+
'tech-stack',
|
|
54
|
+
'appendix-tech-stack',
|
|
55
|
+
'appendix/tech-stack',
|
|
56
|
+
'technology',
|
|
57
|
+
'stack',
|
|
58
|
+
];
|
|
59
|
+
const excludes = [...defaultExcludes, ...userExcludes];
|
|
60
|
+
|
|
61
|
+
// ---------- Manifest scanning ----------
|
|
62
|
+
|
|
63
|
+
const packageNames = new Set();
|
|
64
|
+
const manifestsScanned = [];
|
|
65
|
+
|
|
66
|
+
function scanManifests(dir) {
|
|
67
|
+
let entries;
|
|
68
|
+
try {
|
|
69
|
+
entries = readdirSync(dir);
|
|
70
|
+
} catch {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
const full = join(dir, entry);
|
|
75
|
+
let stat;
|
|
76
|
+
try {
|
|
77
|
+
stat = statSync(full);
|
|
78
|
+
} catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (stat.isDirectory()) {
|
|
82
|
+
// Skip common ignored directories
|
|
83
|
+
if (['node_modules', '.git', 'dist', 'build', '.next', '.venv', 'venv', '__pycache__'].includes(entry)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
scanManifests(full);
|
|
87
|
+
} else {
|
|
88
|
+
const name = basename(full);
|
|
89
|
+
if (['package.json', 'pyproject.toml', 'requirements.txt', 'Cargo.toml', 'Gemfile', 'go.mod'].includes(name)) {
|
|
90
|
+
const extracted = extractPackages(name, full);
|
|
91
|
+
if (extracted.length > 0) {
|
|
92
|
+
manifestsScanned.push(relative(projectRoot, full));
|
|
93
|
+
extracted.forEach((p) => packageNames.add(p));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function extractPackages(manifestName, fullPath) {
|
|
101
|
+
let content;
|
|
102
|
+
try {
|
|
103
|
+
content = readFileSync(fullPath, 'utf8');
|
|
104
|
+
} catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
switch (manifestName) {
|
|
109
|
+
case 'package.json':
|
|
110
|
+
return extractFromPackageJson(content);
|
|
111
|
+
case 'pyproject.toml':
|
|
112
|
+
return extractFromPyprojectToml(content);
|
|
113
|
+
case 'requirements.txt':
|
|
114
|
+
return extractFromRequirementsTxt(content);
|
|
115
|
+
case 'Cargo.toml':
|
|
116
|
+
return extractFromCargoToml(content);
|
|
117
|
+
case 'Gemfile':
|
|
118
|
+
return extractFromGemfile(content);
|
|
119
|
+
case 'go.mod':
|
|
120
|
+
return extractFromGoMod(content);
|
|
121
|
+
default:
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function extractFromPackageJson(content) {
|
|
127
|
+
try {
|
|
128
|
+
const parsed = JSON.parse(content);
|
|
129
|
+
const all = {
|
|
130
|
+
...parsed.dependencies,
|
|
131
|
+
...parsed.devDependencies,
|
|
132
|
+
...parsed.peerDependencies,
|
|
133
|
+
...parsed.optionalDependencies,
|
|
134
|
+
};
|
|
135
|
+
return Object.keys(all).map((name) => {
|
|
136
|
+
// Strip scope: @scope/name → name (also keep both for matching)
|
|
137
|
+
const stripped = name.startsWith('@') ? name.split('/')[1] || name : name;
|
|
138
|
+
return stripped;
|
|
139
|
+
});
|
|
140
|
+
} catch {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function extractFromPyprojectToml(content) {
|
|
146
|
+
// Naive TOML parsing — look for dependency lines like:
|
|
147
|
+
// name = "..." or "package-name>=1.0" or package-name = "1.0"
|
|
148
|
+
const names = new Set();
|
|
149
|
+
const lines = content.split('\n');
|
|
150
|
+
let inDeps = false;
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
const trimmed = line.trim();
|
|
153
|
+
if (trimmed.startsWith('[')) {
|
|
154
|
+
inDeps = /\[(tool\.poetry\.|project\.|tool\.pdm\.)?(dev-)?dependencies\]/.test(trimmed);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (inDeps) {
|
|
158
|
+
// poetry style: name = "1.0" or {version = "..."}
|
|
159
|
+
const poetryMatch = trimmed.match(/^([a-zA-Z0-9][a-zA-Z0-9_-]*)\s*=/);
|
|
160
|
+
if (poetryMatch) names.add(poetryMatch[1]);
|
|
161
|
+
// pep621 style in dependencies array: "name>=1.0",
|
|
162
|
+
const pep621Match = trimmed.match(/^["']([a-zA-Z0-9][a-zA-Z0-9_-]*)/);
|
|
163
|
+
if (pep621Match) names.add(pep621Match[1]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return [...names];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function extractFromRequirementsTxt(content) {
|
|
170
|
+
const names = [];
|
|
171
|
+
for (const line of content.split('\n')) {
|
|
172
|
+
const trimmed = line.trim();
|
|
173
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
174
|
+
const match = trimmed.match(/^([a-zA-Z0-9][a-zA-Z0-9_-]*)/);
|
|
175
|
+
if (match) names.push(match[1]);
|
|
176
|
+
}
|
|
177
|
+
return names;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function extractFromCargoToml(content) {
|
|
181
|
+
const names = new Set();
|
|
182
|
+
const lines = content.split('\n');
|
|
183
|
+
let inDeps = false;
|
|
184
|
+
for (const line of lines) {
|
|
185
|
+
const trimmed = line.trim();
|
|
186
|
+
if (trimmed.startsWith('[')) {
|
|
187
|
+
inDeps = /\[(dev-|build-)?dependencies\]/.test(trimmed);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (inDeps) {
|
|
191
|
+
const match = trimmed.match(/^([a-zA-Z0-9][a-zA-Z0-9_-]*)\s*=/);
|
|
192
|
+
if (match) names.add(match[1]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return [...names];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function extractFromGemfile(content) {
|
|
199
|
+
const names = [];
|
|
200
|
+
for (const line of content.split('\n')) {
|
|
201
|
+
const match = line.match(/^\s*gem\s+["']([a-zA-Z0-9][a-zA-Z0-9_-]*)["']/);
|
|
202
|
+
if (match) names.push(match[1]);
|
|
203
|
+
}
|
|
204
|
+
return names;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function extractFromGoMod(content) {
|
|
208
|
+
const names = new Set();
|
|
209
|
+
const lines = content.split('\n');
|
|
210
|
+
let inRequire = false;
|
|
211
|
+
for (const line of lines) {
|
|
212
|
+
const trimmed = line.trim();
|
|
213
|
+
if (trimmed.startsWith('require (')) {
|
|
214
|
+
inRequire = true;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (inRequire && trimmed === ')') {
|
|
218
|
+
inRequire = false;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const lineToCheck = trimmed.startsWith('require ') ? trimmed.replace(/^require\s+/, '') : (inRequire ? trimmed : null);
|
|
222
|
+
if (lineToCheck) {
|
|
223
|
+
// last path segment as the package "name"
|
|
224
|
+
const match = lineToCheck.match(/^[a-zA-Z0-9./_-]+/);
|
|
225
|
+
if (match) {
|
|
226
|
+
const segments = match[0].split('/');
|
|
227
|
+
const last = segments[segments.length - 1];
|
|
228
|
+
if (last) names.add(last);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return [...names];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ---------- Doc walking and grepping ----------
|
|
236
|
+
|
|
237
|
+
const hits = [];
|
|
238
|
+
const excludedFiles = [];
|
|
239
|
+
|
|
240
|
+
function isExcluded(filePath) {
|
|
241
|
+
const rel = relative(docsPath, filePath).replace(/\\/g, '/');
|
|
242
|
+
return excludes.some((e) => rel.includes(e));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function walkDocs(dir) {
|
|
246
|
+
let entries;
|
|
247
|
+
try {
|
|
248
|
+
entries = readdirSync(dir);
|
|
249
|
+
} catch {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
for (const entry of entries) {
|
|
253
|
+
const full = join(dir, entry);
|
|
254
|
+
let stat;
|
|
255
|
+
try {
|
|
256
|
+
stat = statSync(full);
|
|
257
|
+
} catch {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (stat.isDirectory()) {
|
|
261
|
+
walkDocs(full);
|
|
262
|
+
} else if (/\.(md|mdx)$/.test(entry)) {
|
|
263
|
+
if (isExcluded(full)) {
|
|
264
|
+
excludedFiles.push(relative(projectRoot, full));
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
grepFile(full);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function grepFile(filePath) {
|
|
273
|
+
let content;
|
|
274
|
+
try {
|
|
275
|
+
content = readFileSync(filePath, 'utf8');
|
|
276
|
+
} catch {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const lines = content.split('\n');
|
|
280
|
+
const relPath = relative(projectRoot, filePath);
|
|
281
|
+
|
|
282
|
+
let inCodeBlock = false;
|
|
283
|
+
for (let i = 0; i < lines.length; i++) {
|
|
284
|
+
const line = lines[i];
|
|
285
|
+
if (line.trim().startsWith('```')) {
|
|
286
|
+
inCodeBlock = !inCodeBlock;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (inCodeBlock) continue; // Don't flag terms in code blocks
|
|
290
|
+
|
|
291
|
+
for (const pkg of packageNames) {
|
|
292
|
+
// Word boundary match, case-sensitive
|
|
293
|
+
// Skip very short package names (< 3 chars) to reduce noise
|
|
294
|
+
if (pkg.length < 3) continue;
|
|
295
|
+
const escaped = pkg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
296
|
+
const regex = new RegExp(`\\b${escaped}\\b`);
|
|
297
|
+
if (regex.test(line)) {
|
|
298
|
+
hits.push({
|
|
299
|
+
file: relPath,
|
|
300
|
+
line: i + 1,
|
|
301
|
+
term: pkg,
|
|
302
|
+
context: line.trim().slice(0, 200),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ---------- Run ----------
|
|
310
|
+
|
|
311
|
+
scanManifests(projectRoot);
|
|
312
|
+
walkDocs(docsPath);
|
|
313
|
+
|
|
314
|
+
const result = {
|
|
315
|
+
manifestsScanned,
|
|
316
|
+
packagesFound: [...packageNames].sort(),
|
|
317
|
+
excluded: excludedFiles,
|
|
318
|
+
hits,
|
|
319
|
+
summary: {
|
|
320
|
+
manifestCount: manifestsScanned.length,
|
|
321
|
+
packageCount: packageNames.size,
|
|
322
|
+
excludedFileCount: excludedFiles.length,
|
|
323
|
+
hitCount: hits.length,
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
console.log(JSON.stringify(result, null, 2));
|
|
328
|
+
process.exit(hits.length > 0 ? 1 : 0);
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation site custom CSS.
|
|
3
|
+
*
|
|
4
|
+
* This file is a template — the build-docs-site skill fills in the
|
|
5
|
+
* brand placeholders (marked with {{...}}) with values from the
|
|
6
|
+
* Phase 1 brand identity question. After substitution the file is a
|
|
7
|
+
* valid Docusaurus custom.css.
|
|
8
|
+
*
|
|
9
|
+
* Sections:
|
|
10
|
+
* 1. Brand fonts (imports)
|
|
11
|
+
* 2. Infima theme overrides (light)
|
|
12
|
+
* 3. Infima theme overrides (dark)
|
|
13
|
+
* 4. Diagram tokens (--dx-*)
|
|
14
|
+
* 5. Diagram class layer (.dx-fill-* / .dx-stroke-*)
|
|
15
|
+
* 6. Diagram container styling
|
|
16
|
+
* 7. Typography polish (justified body, heading spacing, column width)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* ============================================================
|
|
20
|
+
* 1. Brand fonts
|
|
21
|
+
* Replace with @import or @font-face declarations for your fonts.
|
|
22
|
+
* Example: @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
|
|
23
|
+
* ============================================================ */
|
|
24
|
+
{{BRAND_FONTS_IMPORT}}
|
|
25
|
+
|
|
26
|
+
/* ============================================================
|
|
27
|
+
* 2. Infima theme overrides (light mode)
|
|
28
|
+
* ============================================================ */
|
|
29
|
+
:root {
|
|
30
|
+
--ifm-color-primary: {{BRAND_PRIMARY}};
|
|
31
|
+
--ifm-color-primary-dark: {{BRAND_PRIMARY_DARK}};
|
|
32
|
+
--ifm-color-primary-darker: {{BRAND_PRIMARY_DARKER}};
|
|
33
|
+
--ifm-color-primary-darkest: {{BRAND_PRIMARY_DARKEST}};
|
|
34
|
+
--ifm-color-primary-light: {{BRAND_PRIMARY_LIGHT}};
|
|
35
|
+
--ifm-color-primary-lighter: {{BRAND_PRIMARY_LIGHTER}};
|
|
36
|
+
--ifm-color-primary-lightest: {{BRAND_PRIMARY_LIGHTEST}};
|
|
37
|
+
|
|
38
|
+
--ifm-background-color: {{BRAND_BACKGROUND}};
|
|
39
|
+
--ifm-font-color-base: {{BRAND_TEXT}};
|
|
40
|
+
|
|
41
|
+
--ifm-font-family-base: {{BRAND_FONT_BODY}}, system-ui, -apple-system, sans-serif;
|
|
42
|
+
--ifm-heading-font-family: {{BRAND_FONT_HEADING}}, system-ui, -apple-system, sans-serif;
|
|
43
|
+
--ifm-font-family-monospace: {{BRAND_FONT_MONO}}, 'SF Mono', Consolas, monospace;
|
|
44
|
+
|
|
45
|
+
--ifm-code-font-size: 90%;
|
|
46
|
+
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ============================================================
|
|
50
|
+
* 3. Infima theme overrides (dark mode)
|
|
51
|
+
* ============================================================ */
|
|
52
|
+
[data-theme='dark'] {
|
|
53
|
+
--ifm-color-primary: {{BRAND_PRIMARY_DARK_MODE}};
|
|
54
|
+
--ifm-color-primary-dark: {{BRAND_PRIMARY_DARK_MODE_DARK}};
|
|
55
|
+
--ifm-color-primary-darker: {{BRAND_PRIMARY_DARK_MODE_DARKER}};
|
|
56
|
+
--ifm-color-primary-darkest: {{BRAND_PRIMARY_DARK_MODE_DARKEST}};
|
|
57
|
+
--ifm-color-primary-light: {{BRAND_PRIMARY_DARK_MODE_LIGHT}};
|
|
58
|
+
--ifm-color-primary-lighter: {{BRAND_PRIMARY_DARK_MODE_LIGHTER}};
|
|
59
|
+
--ifm-color-primary-lightest: {{BRAND_PRIMARY_DARK_MODE_LIGHTEST}};
|
|
60
|
+
|
|
61
|
+
--ifm-background-color: {{BRAND_BACKGROUND_DARK}};
|
|
62
|
+
--ifm-font-color-base: {{BRAND_TEXT_DARK}};
|
|
63
|
+
|
|
64
|
+
--docusaurus-highlighted-code-line-bg: rgba(255, 255, 255, 0.1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ============================================================
|
|
68
|
+
* 4. Diagram tokens (--dx-*)
|
|
69
|
+
*
|
|
70
|
+
* Semantic colors used by the diagram primitives library.
|
|
71
|
+
* The class layer below resolves these for fill/stroke.
|
|
72
|
+
* Keep in sync with primitives/tokens.ts hex values.
|
|
73
|
+
* ============================================================ */
|
|
74
|
+
:root {
|
|
75
|
+
--dx-primary: {{BRAND_PRIMARY}};
|
|
76
|
+
--dx-accent: {{BRAND_ACCENT}};
|
|
77
|
+
--dx-surface: #FFFFFF;
|
|
78
|
+
--dx-surface-strong: #F2F2F2;
|
|
79
|
+
--dx-text: #1A1A1A;
|
|
80
|
+
--dx-text-muted: #666666;
|
|
81
|
+
--dx-stroke: #CCCCCC;
|
|
82
|
+
--dx-stroke-strong: #888888;
|
|
83
|
+
--dx-diagram-bg: {{BRAND_BACKGROUND}};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
[data-theme='dark'] {
|
|
87
|
+
--dx-primary: {{BRAND_PRIMARY_DARK_MODE}};
|
|
88
|
+
--dx-accent: {{BRAND_ACCENT_DARK_MODE}};
|
|
89
|
+
--dx-surface: #1A1A1A;
|
|
90
|
+
--dx-surface-strong: #2A2A2A;
|
|
91
|
+
--dx-text: #EDEDED;
|
|
92
|
+
--dx-text-muted: #999999;
|
|
93
|
+
--dx-stroke: #444444;
|
|
94
|
+
--dx-stroke-strong: #777777;
|
|
95
|
+
--dx-diagram-bg: {{BRAND_BACKGROUND_DARK}};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ============================================================
|
|
99
|
+
* 5. Diagram class layer
|
|
100
|
+
*
|
|
101
|
+
* CSS variables in `fill="var(--x)"` or inline style do not paint
|
|
102
|
+
* reliably in Chromium's SVG pipeline. The variables resolve correctly
|
|
103
|
+
* through class selectors. Apply via className on SVG elements:
|
|
104
|
+
*
|
|
105
|
+
* <rect className="dx-fill-primary dx-stroke-stroke" />
|
|
106
|
+
* ============================================================ */
|
|
107
|
+
.dx-fill-primary { fill: var(--dx-primary); }
|
|
108
|
+
.dx-fill-accent { fill: var(--dx-accent); }
|
|
109
|
+
.dx-fill-surface { fill: var(--dx-surface); }
|
|
110
|
+
.dx-fill-surface-strong { fill: var(--dx-surface-strong); }
|
|
111
|
+
.dx-fill-text { fill: var(--dx-text); }
|
|
112
|
+
.dx-fill-text-muted { fill: var(--dx-text-muted); }
|
|
113
|
+
.dx-fill-stroke { fill: var(--dx-stroke); }
|
|
114
|
+
.dx-fill-stroke-strong { fill: var(--dx-stroke-strong); }
|
|
115
|
+
|
|
116
|
+
.dx-stroke-primary { stroke: var(--dx-primary); }
|
|
117
|
+
.dx-stroke-accent { stroke: var(--dx-accent); }
|
|
118
|
+
.dx-stroke-surface { stroke: var(--dx-surface); }
|
|
119
|
+
.dx-stroke-surface-strong { stroke: var(--dx-surface-strong); }
|
|
120
|
+
.dx-stroke-text { stroke: var(--dx-text); }
|
|
121
|
+
.dx-stroke-text-muted { stroke: var(--dx-text-muted); }
|
|
122
|
+
.dx-stroke-stroke { stroke: var(--dx-stroke); }
|
|
123
|
+
.dx-stroke-stroke-strong { stroke: var(--dx-stroke-strong); }
|
|
124
|
+
|
|
125
|
+
/* ============================================================
|
|
126
|
+
* 6. Diagram container styling
|
|
127
|
+
* ============================================================ */
|
|
128
|
+
.dx-diagram-container {
|
|
129
|
+
background: var(--dx-diagram-bg);
|
|
130
|
+
border-radius: 8px;
|
|
131
|
+
padding: 24px;
|
|
132
|
+
margin: 2rem 0;
|
|
133
|
+
border: 1px solid var(--dx-stroke);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.dx-diagram-container svg {
|
|
137
|
+
width: 100%;
|
|
138
|
+
height: auto;
|
|
139
|
+
display: block;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.dx-diagram-caption {
|
|
143
|
+
margin-top: 1rem;
|
|
144
|
+
font-size: 0.875rem;
|
|
145
|
+
color: var(--dx-text-muted);
|
|
146
|
+
text-align: center;
|
|
147
|
+
font-style: italic;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ============================================================
|
|
151
|
+
* 7. Typography polish
|
|
152
|
+
*
|
|
153
|
+
* Captured defaults that produce comfortable long-form reading.
|
|
154
|
+
* The chapter-length question in Phase 1 may inform whether you
|
|
155
|
+
* adjust these — these are the Linear/Together defaults.
|
|
156
|
+
* ============================================================ */
|
|
157
|
+
|
|
158
|
+
/* Justified body text with hyphenation */
|
|
159
|
+
.markdown p,
|
|
160
|
+
.markdown li {
|
|
161
|
+
text-align: justify;
|
|
162
|
+
hyphens: auto;
|
|
163
|
+
-webkit-hyphens: auto;
|
|
164
|
+
-ms-hyphens: auto;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Heading spacing — generous top, controlled bottom */
|
|
168
|
+
.markdown h2 {
|
|
169
|
+
margin-top: 3.5rem;
|
|
170
|
+
margin-bottom: 1.25rem;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.markdown h3 {
|
|
174
|
+
margin-top: 2.5rem;
|
|
175
|
+
margin-bottom: 1rem;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.markdown h4 {
|
|
179
|
+
margin-top: 2rem;
|
|
180
|
+
margin-bottom: 0.75rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Collapse spacing between adjacent headings (avoid double-stacking) */
|
|
184
|
+
.markdown h2 + h3,
|
|
185
|
+
.markdown h3 + h4 {
|
|
186
|
+
margin-top: 1rem;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Paragraph and list-item bottom spacing */
|
|
190
|
+
.markdown p {
|
|
191
|
+
margin-bottom: 1.25rem;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.markdown li {
|
|
195
|
+
margin-bottom: 0.4rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.markdown li:last-child {
|
|
199
|
+
margin-bottom: 1.25rem;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Content column width — wide enough for justified text to look even */
|
|
203
|
+
.markdown {
|
|
204
|
+
max-width: 760px;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Code blocks should not be justified */
|
|
208
|
+
.markdown pre,
|
|
209
|
+
.markdown pre code {
|
|
210
|
+
text-align: left;
|
|
211
|
+
hyphens: none;
|
|
212
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { themes as prismThemes } from 'prism-react-renderer';
|
|
2
|
+
import type { Config } from '@docusaurus/types';
|
|
3
|
+
import type * as Preset from '@docusaurus/preset-classic';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Docusaurus configuration for the documentation site.
|
|
7
|
+
* Generated by the build-docs-site skill from a template.
|
|
8
|
+
*
|
|
9
|
+
* Placeholders to substitute:
|
|
10
|
+
* {{SITE_TITLE}} — the site's display title
|
|
11
|
+
* {{SITE_TAGLINE}} — short tagline shown in the navbar/footer
|
|
12
|
+
* {{SITE_URL}} — production URL (e.g., https://docs.example.com)
|
|
13
|
+
* {{SITE_BASE_URL}} — base path (usually '/')
|
|
14
|
+
* {{ORG_NAME}} — GitHub org or owner
|
|
15
|
+
* {{REPO_NAME}} — GitHub repo name (for edit links)
|
|
16
|
+
* {{FAVICON_PATH}} — path to favicon under static/
|
|
17
|
+
* {{COPYRIGHT_OWNER}} — name shown in the footer copyright
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const config: Config = {
|
|
21
|
+
title: '{{SITE_TITLE}}',
|
|
22
|
+
tagline: '{{SITE_TAGLINE}}',
|
|
23
|
+
favicon: '{{FAVICON_PATH}}',
|
|
24
|
+
|
|
25
|
+
url: '{{SITE_URL}}',
|
|
26
|
+
baseUrl: '{{SITE_BASE_URL}}',
|
|
27
|
+
|
|
28
|
+
organizationName: '{{ORG_NAME}}',
|
|
29
|
+
projectName: '{{REPO_NAME}}',
|
|
30
|
+
|
|
31
|
+
onBrokenLinks: 'throw',
|
|
32
|
+
onBrokenMarkdownLinks: 'warn',
|
|
33
|
+
|
|
34
|
+
i18n: {
|
|
35
|
+
defaultLocale: 'en',
|
|
36
|
+
locales: ['en'],
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
presets: [
|
|
40
|
+
[
|
|
41
|
+
'classic',
|
|
42
|
+
{
|
|
43
|
+
docs: {
|
|
44
|
+
// Docs at the site root — no /docs/ prefix
|
|
45
|
+
routeBasePath: '/',
|
|
46
|
+
sidebarPath: './sidebars.ts',
|
|
47
|
+
editUrl: 'https://github.com/{{ORG_NAME}}/{{REPO_NAME}}/tree/main/',
|
|
48
|
+
},
|
|
49
|
+
// Blog disabled — this is a docs-only site
|
|
50
|
+
blog: false,
|
|
51
|
+
theme: {
|
|
52
|
+
customCss: './src/css/custom.css',
|
|
53
|
+
},
|
|
54
|
+
} satisfies Preset.Options,
|
|
55
|
+
],
|
|
56
|
+
],
|
|
57
|
+
|
|
58
|
+
themeConfig: {
|
|
59
|
+
colorMode: {
|
|
60
|
+
defaultMode: 'light',
|
|
61
|
+
disableSwitch: false,
|
|
62
|
+
respectPrefersColorScheme: true,
|
|
63
|
+
},
|
|
64
|
+
docs: {
|
|
65
|
+
sidebar: {
|
|
66
|
+
hideable: true,
|
|
67
|
+
autoCollapseCategories: true,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
navbar: {
|
|
71
|
+
title: '{{SITE_TITLE}}',
|
|
72
|
+
logo: {
|
|
73
|
+
alt: '{{SITE_TITLE}} Logo',
|
|
74
|
+
src: '{{FAVICON_PATH}}',
|
|
75
|
+
},
|
|
76
|
+
items: [
|
|
77
|
+
{
|
|
78
|
+
href: 'https://github.com/{{ORG_NAME}}/{{REPO_NAME}}',
|
|
79
|
+
label: 'GitHub',
|
|
80
|
+
position: 'right',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
footer: {
|
|
85
|
+
style: 'dark',
|
|
86
|
+
copyright: `Copyright © ${new Date().getFullYear()} {{COPYRIGHT_OWNER}}.`,
|
|
87
|
+
},
|
|
88
|
+
prism: {
|
|
89
|
+
theme: prismThemes.github,
|
|
90
|
+
darkTheme: prismThemes.dracula,
|
|
91
|
+
},
|
|
92
|
+
} satisfies Preset.ThemeConfig,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default config;
|