@nusoft/nuos-build-catalogue 0.25.0 → 0.27.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/cli.js +24 -0
- package/dist/commands/init.js +10 -32
- package/dist/commands/mode.d.ts +24 -0
- package/dist/commands/mode.js +82 -0
- package/dist/commands/render.d.ts +25 -0
- package/dist/commands/render.js +40 -0
- package/dist/render/architecture.d.ts +18 -0
- package/dist/render/architecture.js +74 -0
- package/dist/render/design-system.d.ts +26 -0
- package/dist/render/design-system.js +308 -0
- package/dist/render/html.d.ts +47 -0
- package/dist/render/html.js +258 -0
- package/dist/render/maps.d.ts +18 -0
- package/dist/render/maps.js +67 -0
- package/dist/render/parser.d.ts +54 -0
- package/dist/render/parser.js +162 -0
- package/dist/render/run.d.ts +29 -0
- package/dist/render/run.js +92 -0
- package/dist/render/surfaces.d.ts +23 -0
- package/dist/render/surfaces.js +144 -0
- package/package.json +2 -2
- package/templates/agents/coder.md +7 -5
- package/templates/agents/reviewer.md +6 -4
- package/templates/protocols/build-wu.md +13 -63
- package/templates/protocols/end-of-session.md +9 -3
- package/templates/protocols/persona-new.md +1 -1
- package/templates/protocols/plan-architecture.md +10 -24
- package/templates/protocols/plan-initial-wu.md +6 -26
- package/templates/protocols/plan-maps.md +5 -17
- package/templates/protocols/plan-orientation.md +13 -29
- package/templates/protocols/plan-review.md +2 -0
- package/templates/protocols/plan-uiux.md +35 -86
- package/templates/protocols/start-of-session.md +13 -1
- package/templates/protocols/wu-new.md +1 -1
- package/templates/starter-kit/CLAUDE.md +9 -37
- package/templates/starter-kit/docs/build/OPERATOR-MODES.md +44 -0
- package/templates/starter-kit/methodfile.json +5 -0
package/dist/cli.js
CHANGED
|
@@ -424,6 +424,12 @@ Usage:
|
|
|
424
424
|
|
|
425
425
|
nuos-catalogue plan status show planning progress across the 5-phase arc
|
|
426
426
|
|
|
427
|
+
nuos-catalogue mode print the current operator mode
|
|
428
|
+
nuos-catalogue mode <name> set operator mode: coaching | standard | developer
|
|
429
|
+
|
|
430
|
+
nuos-catalogue render regenerate HTML companion views for visual registers
|
|
431
|
+
nuos-catalogue render <register> just one register: surfaces | design-system | maps | architecture
|
|
432
|
+
|
|
427
433
|
nuos-catalogue swarm status [--limit=N] list recent /build-wu runs
|
|
428
434
|
nuos-catalogue swarm cost aggregate cost across swarm runs
|
|
429
435
|
|
|
@@ -557,6 +563,24 @@ async function main() {
|
|
|
557
563
|
console.error('available: plan status');
|
|
558
564
|
process.exit(1);
|
|
559
565
|
}
|
|
566
|
+
case 'mode': {
|
|
567
|
+
const { cmdMode } = await import('./commands/mode.js');
|
|
568
|
+
const code = await cmdMode({ cwd: process.cwd(), mode: args.positional[0] });
|
|
569
|
+
if (code !== 0)
|
|
570
|
+
process.exit(code);
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
case 'render': {
|
|
574
|
+
const { cmdRender } = await import('./commands/render.js');
|
|
575
|
+
const code = await cmdRender({
|
|
576
|
+
cwd: process.cwd(),
|
|
577
|
+
positional: args.positional[0],
|
|
578
|
+
buildRootFlag: args.flags['build-root'],
|
|
579
|
+
});
|
|
580
|
+
if (code !== 0)
|
|
581
|
+
process.exit(code);
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
560
584
|
case 'swarm': {
|
|
561
585
|
const sub = args.positional[0];
|
|
562
586
|
const { cmdSwarmStatus, cmdSwarmCost } = await import('./commands/swarm.js');
|
package/dist/commands/init.js
CHANGED
|
@@ -517,43 +517,21 @@ async function ensureGitignoreEntries(gitignorePath, log_line) {
|
|
|
517
517
|
? ' · created .gitignore with catalogue rules'
|
|
518
518
|
: ' · appended catalogue rules to .gitignore');
|
|
519
519
|
}
|
|
520
|
-
function renderCatalogueSection(
|
|
520
|
+
function renderCatalogueSection(_projectName) {
|
|
521
521
|
return `## Build catalogue (NuOS Build Method)
|
|
522
522
|
|
|
523
|
-
This project
|
|
523
|
+
This project's memory lives at [docs/build/](docs/build/) — eleven registers in plain Markdown, kept current by two bookend protocols.
|
|
524
524
|
|
|
525
|
-
|
|
525
|
+
- Run \`/start-of-session\` to begin every session, \`/end-of-session\` to close every session.
|
|
526
|
+
- A brand-new project: \`/start-of-session\` detects the empty catalogue and routes through 5 planning phases (Orientation → Architecture → UI/UX + Design System → Maps → Initial Work Units) before building.
|
|
527
|
+
- Onboarding: [docs/build/WELCOME.md](docs/build/WELCOME.md) (5 min); every term defined in [docs/build/GLOSSARY.md](docs/build/GLOSSARY.md).
|
|
526
528
|
|
|
527
|
-
|
|
528
|
-
- [docs/build/GLOSSARY.md](docs/build/GLOSSARY.md) — every term defined once
|
|
529
|
+
### Rules
|
|
529
530
|
|
|
530
|
-
|
|
531
|
+
- **Never close without \`/end-of-session\`** — work not written down is lost.
|
|
532
|
+
- **Never edit an accepted decision file** — file a superseding decision instead (pre-commit hook blocks silent edits).
|
|
533
|
+
- **Never make an architectural decision in conversation without filing it to \`docs/build/decisions/\` first** — drift kills the catalogue.
|
|
531
534
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
\`\`\`text
|
|
535
|
-
/start-of-session — every time you begin working
|
|
536
|
-
/end-of-session — every time you stop
|
|
537
|
-
\`\`\`
|
|
538
|
-
|
|
539
|
-
(\`init\` runs once at the start; you've already done that.)
|
|
540
|
-
|
|
541
|
-
If this is a brand-new project, \`/start-of-session\` will detect the empty catalogue and walk you through 5 short planning phases (Orientation, Architecture, UI/UX + Design System, Maps, Initial Work Units) before any building starts. Each phase is its own session. Take them in order; pause whenever you need to.
|
|
542
|
-
|
|
543
|
-
### The principle that makes it work
|
|
544
|
-
|
|
545
|
-
**Project memory never drifts from project reality.** Every decision made in conversation gets saved before the session ends. Every change to an existing piece flows through a protocol. The pre-commit hook blocks silent edits to accepted decisions; the post-commit hook auto-refreshes the search index after every commit. What the AI finds when you ask "what did we decide about X?" is always current.
|
|
546
|
-
|
|
547
|
-
### What never to do
|
|
548
|
-
|
|
549
|
-
- **Never close a session without \`/end-of-session\`.** Work that isn't written down is work that's lost.
|
|
550
|
-
- **Never edit an accepted decision file.** If something changes, file a new decision that supersedes the old one. The pre-commit hook will block silent edits.
|
|
551
|
-
- **Never make an architectural decision in conversation without filing it.** If you and the AI agree on "let's go with X", file it as a decision *before moving on*. Drift is the failure mode that makes the catalogue worthless.
|
|
552
|
-
|
|
553
|
-
### If you need more
|
|
554
|
-
|
|
555
|
-
- All registers and their templates live under [docs/build/](docs/build/)
|
|
556
|
-
- The full CLI surface (creating work units / decisions / personas / questions / contracts / surfaces from the command line) is documented at [docs/build/WELCOME.md](docs/build/WELCOME.md)
|
|
557
|
-
- To refresh protocols and hooks later (after a CLI upgrade): \`npx @nusoft/nuos-build-catalogue install-protocols\`
|
|
535
|
+
Refresh protocols + hooks after a CLI upgrade with \`npx @nusoft/nuos-build-catalogue install-protocols\`.
|
|
558
536
|
`;
|
|
559
537
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nuos-catalogue mode` — read or set the operator mode in methodfile.json.
|
|
3
|
+
*
|
|
4
|
+
* nuos-catalogue mode → prints the current mode
|
|
5
|
+
* nuos-catalogue mode <name> → sets the mode and stamps modeSelectedAt
|
|
6
|
+
*
|
|
7
|
+
* Valid names: coaching, standard, developer.
|
|
8
|
+
*
|
|
9
|
+
* The mode shapes the *tone* of every operator-facing protocol — see
|
|
10
|
+
* docs/build/OPERATOR-MODES.md for what each mode means. The picker
|
|
11
|
+
* normally runs once at first /start-of-session; this CLI command exists
|
|
12
|
+
* so the operator can change their mind without hand-editing JSON.
|
|
13
|
+
*/
|
|
14
|
+
export declare const VALID_MODES: readonly ["coaching", "standard", "developer"];
|
|
15
|
+
export type OperatorMode = (typeof VALID_MODES)[number];
|
|
16
|
+
export declare function isOperatorMode(v: unknown): v is OperatorMode;
|
|
17
|
+
export interface ModeOptions {
|
|
18
|
+
cwd?: string;
|
|
19
|
+
/** When omitted, the command prints the current mode and exits 0. */
|
|
20
|
+
mode?: string;
|
|
21
|
+
/** Override "now" — used by tests for deterministic timestamps. */
|
|
22
|
+
now?: () => string;
|
|
23
|
+
}
|
|
24
|
+
export declare function cmdMode(options?: ModeOptions): Promise<number>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nuos-catalogue mode` — read or set the operator mode in methodfile.json.
|
|
3
|
+
*
|
|
4
|
+
* nuos-catalogue mode → prints the current mode
|
|
5
|
+
* nuos-catalogue mode <name> → sets the mode and stamps modeSelectedAt
|
|
6
|
+
*
|
|
7
|
+
* Valid names: coaching, standard, developer.
|
|
8
|
+
*
|
|
9
|
+
* The mode shapes the *tone* of every operator-facing protocol — see
|
|
10
|
+
* docs/build/OPERATOR-MODES.md for what each mode means. The picker
|
|
11
|
+
* normally runs once at first /start-of-session; this CLI command exists
|
|
12
|
+
* so the operator can change their mind without hand-editing JSON.
|
|
13
|
+
*/
|
|
14
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
export const VALID_MODES = ['coaching', 'standard', 'developer'];
|
|
18
|
+
export function isOperatorMode(v) {
|
|
19
|
+
return typeof v === 'string' && VALID_MODES.includes(v);
|
|
20
|
+
}
|
|
21
|
+
export async function cmdMode(options = {}) {
|
|
22
|
+
const cwd = options.cwd ?? process.cwd();
|
|
23
|
+
const methodfilePath = path.join(cwd, 'methodfile.json');
|
|
24
|
+
if (!existsSync(methodfilePath)) {
|
|
25
|
+
console.error(`No methodfile.json found at ${cwd}.`);
|
|
26
|
+
console.error('Run `nuos-catalogue init` first to set up a catalogue.');
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
let raw;
|
|
30
|
+
try {
|
|
31
|
+
raw = await readFile(methodfilePath, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
console.error(`Couldn't read methodfile.json: ${err.message}`);
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
let mf;
|
|
38
|
+
try {
|
|
39
|
+
mf = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error(`methodfile.json is not valid JSON: ${err.message}`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
const operator = mf.operator ?? {};
|
|
46
|
+
const current = operator.mode;
|
|
47
|
+
if (options.mode === undefined) {
|
|
48
|
+
if (isOperatorMode(current)) {
|
|
49
|
+
console.log(current);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
console.log('(unset)');
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log('Run `nuos-catalogue mode <coaching|standard|developer>` to set,');
|
|
55
|
+
console.log('or just start /start-of-session and the picker will run automatically.');
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
if (!isOperatorMode(options.mode)) {
|
|
59
|
+
console.error(`unknown mode: ${options.mode}`);
|
|
60
|
+
console.error(`valid modes: ${VALID_MODES.join(', ')}`);
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
const today = (options.now ?? (() => new Date().toISOString().slice(0, 10)))();
|
|
64
|
+
const updated = {
|
|
65
|
+
...mf,
|
|
66
|
+
operator: {
|
|
67
|
+
...operator,
|
|
68
|
+
mode: options.mode,
|
|
69
|
+
modeSelectedAt: today,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
// Preserve a trailing newline to match the JSON convention in the rest of the
|
|
73
|
+
// project (every other written file ends with a newline; tooling like git diff
|
|
74
|
+
// is happier that way).
|
|
75
|
+
const trailingNewline = raw.endsWith('\n') ? '\n' : '';
|
|
76
|
+
await writeFile(methodfilePath, JSON.stringify(updated, null, 2) + trailingNewline, 'utf8');
|
|
77
|
+
const previous = isOperatorMode(current) ? current : '(unset)';
|
|
78
|
+
console.log(`Operator mode: ${previous} → ${options.mode}`);
|
|
79
|
+
console.log(`Tone for every operator-facing protocol now matches '${options.mode}'.`);
|
|
80
|
+
console.log(`See docs/build/OPERATOR-MODES.md for what that means in practice.`);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nuos-catalogue render [register]` — regenerate the HTML companion views
|
|
3
|
+
* for the visual registers (ui-ux, design-system, maps, architecture).
|
|
4
|
+
*
|
|
5
|
+
* nuos-catalogue render # render all four
|
|
6
|
+
* nuos-catalogue render surfaces # render just ui-ux/_view.html
|
|
7
|
+
* nuos-catalogue render design-system # render just design-system/_view.html
|
|
8
|
+
* nuos-catalogue render maps # render just maps/_view.html
|
|
9
|
+
* nuos-catalogue render architecture # render just architecture/_view.html
|
|
10
|
+
*
|
|
11
|
+
* Companion HTML files are *generated artefacts*. The canonical source for
|
|
12
|
+
* every register stays markdown — every agent (architect, coder, reviewer)
|
|
13
|
+
* reads markdown. The HTML exists so the operator can review inherently visual
|
|
14
|
+
* artefacts in their natural medium.
|
|
15
|
+
*/
|
|
16
|
+
export interface RenderCommandOptions {
|
|
17
|
+
cwd?: string;
|
|
18
|
+
/** Optional register name (`surfaces`, `design-system`, `maps`, `architecture`). */
|
|
19
|
+
positional?: string;
|
|
20
|
+
/** Override for --build-root flag. */
|
|
21
|
+
buildRootFlag?: string | boolean;
|
|
22
|
+
/** Override "now" for deterministic test output. */
|
|
23
|
+
now?: () => Date;
|
|
24
|
+
}
|
|
25
|
+
export declare function cmdRender(options?: RenderCommandOptions): Promise<number>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nuos-catalogue render [register]` — regenerate the HTML companion views
|
|
3
|
+
* for the visual registers (ui-ux, design-system, maps, architecture).
|
|
4
|
+
*
|
|
5
|
+
* nuos-catalogue render # render all four
|
|
6
|
+
* nuos-catalogue render surfaces # render just ui-ux/_view.html
|
|
7
|
+
* nuos-catalogue render design-system # render just design-system/_view.html
|
|
8
|
+
* nuos-catalogue render maps # render just maps/_view.html
|
|
9
|
+
* nuos-catalogue render architecture # render just architecture/_view.html
|
|
10
|
+
*
|
|
11
|
+
* Companion HTML files are *generated artefacts*. The canonical source for
|
|
12
|
+
* every register stays markdown — every agent (architect, coder, reviewer)
|
|
13
|
+
* reads markdown. The HTML exists so the operator can review inherently visual
|
|
14
|
+
* artefacts in their natural medium.
|
|
15
|
+
*/
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { resolveBuildRoot } from '../path-resolution.js';
|
|
18
|
+
import { RENDERABLE_REGISTERS, runRender } from '../render/run.js';
|
|
19
|
+
export async function cmdRender(options = {}) {
|
|
20
|
+
const buildRoot = resolveBuildRoot(options.buildRootFlag, { cwd: options.cwd ?? process.cwd() });
|
|
21
|
+
let only;
|
|
22
|
+
if (options.positional && options.positional !== 'all') {
|
|
23
|
+
if (!RENDERABLE_REGISTERS.includes(options.positional)) {
|
|
24
|
+
console.error(`unknown register: ${options.positional}`);
|
|
25
|
+
console.error(`available: ${RENDERABLE_REGISTERS.join(', ')}`);
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
only = [options.positional];
|
|
29
|
+
}
|
|
30
|
+
const report = await runRender({ buildRoot, only, now: options.now });
|
|
31
|
+
for (const r of report.results) {
|
|
32
|
+
const status = r.written ? '✓' : '·';
|
|
33
|
+
const rel = path.relative(process.cwd(), r.outPath);
|
|
34
|
+
console.log(` ${status} ${r.register.padEnd(16)} ${r.detail.padEnd(20)} ${r.written ? rel : ''}`);
|
|
35
|
+
}
|
|
36
|
+
const writtenCount = report.results.filter((r) => r.written).length;
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(`${writtenCount}/${report.results.length} register${report.results.length === 1 ? '' : 's'} rendered.`);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the `architecture/` register as a module gallery with cross-links.
|
|
3
|
+
*
|
|
4
|
+
* Each module becomes a card showing its responsibility (one paragraph), the
|
|
5
|
+
* personas/modules it depends on, and the contracts it owns. The cards link
|
|
6
|
+
* to each other where dependencies are named, so the operator can navigate the
|
|
7
|
+
* module graph visually instead of by grep.
|
|
8
|
+
*/
|
|
9
|
+
export interface ArchitectureRenderResult {
|
|
10
|
+
written: boolean;
|
|
11
|
+
outPath: string;
|
|
12
|
+
moduleCount: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function renderArchitecture(opts: {
|
|
15
|
+
buildRoot: string;
|
|
16
|
+
projectName: string;
|
|
17
|
+
generatedAt: string;
|
|
18
|
+
}): Promise<ArchitectureRenderResult>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the `architecture/` register as a module gallery with cross-links.
|
|
3
|
+
*
|
|
4
|
+
* Each module becomes a card showing its responsibility (one paragraph), the
|
|
5
|
+
* personas/modules it depends on, and the contracts it owns. The cards link
|
|
6
|
+
* to each other where dependencies are named, so the operator can navigate the
|
|
7
|
+
* module graph visually instead of by grep.
|
|
8
|
+
*/
|
|
9
|
+
import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { parseSections, isPlaceholder } from './parser.js';
|
|
13
|
+
import { pageWrapper, card, emptyState, escapeHtml, renderProse, } from './html.js';
|
|
14
|
+
const SKIP_FILES = new Set(['_index.md', 'module-template.md']);
|
|
15
|
+
export async function renderArchitecture(opts) {
|
|
16
|
+
const dir = path.join(opts.buildRoot, 'architecture');
|
|
17
|
+
const outPath = path.join(dir, '_view.html');
|
|
18
|
+
if (!existsSync(dir)) {
|
|
19
|
+
return { written: false, outPath, moduleCount: 0 };
|
|
20
|
+
}
|
|
21
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
22
|
+
const modules = [];
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
if (!entry.isFile() || !entry.name.endsWith('.md'))
|
|
25
|
+
continue;
|
|
26
|
+
if (SKIP_FILES.has(entry.name))
|
|
27
|
+
continue;
|
|
28
|
+
const md = await readFile(path.join(dir, entry.name), 'utf8');
|
|
29
|
+
if (isPlaceholder(md))
|
|
30
|
+
continue;
|
|
31
|
+
const titleMatch = md.match(/^#\s+(.+)$/m);
|
|
32
|
+
modules.push({
|
|
33
|
+
file: entry.name,
|
|
34
|
+
slug: entry.name.replace(/\.md$/, ''),
|
|
35
|
+
title: titleMatch ? titleMatch[1].trim() : entry.name,
|
|
36
|
+
sections: parseSections(md),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
modules.sort((a, b) => a.title.localeCompare(b.title));
|
|
40
|
+
const body = modules.length === 0
|
|
41
|
+
? card('No modules filed yet', emptyState('Phase B of planning produces this content.'))
|
|
42
|
+
: [renderModuleIndex(modules), ...modules.map(renderModuleCard)].join('');
|
|
43
|
+
const html = pageWrapper({
|
|
44
|
+
title: 'Architecture',
|
|
45
|
+
projectName: opts.projectName,
|
|
46
|
+
generatedAt: opts.generatedAt,
|
|
47
|
+
sourceNote: '<code>docs/build/architecture/</code>',
|
|
48
|
+
body,
|
|
49
|
+
});
|
|
50
|
+
await mkdir(dir, { recursive: true });
|
|
51
|
+
await writeFile(outPath, html, 'utf8');
|
|
52
|
+
return { written: true, outPath, moduleCount: modules.length };
|
|
53
|
+
}
|
|
54
|
+
function renderModuleIndex(modules) {
|
|
55
|
+
const items = modules
|
|
56
|
+
.map((m) => `<li><a href="#${escapeHtml(m.slug)}">${escapeHtml(m.title)}</a></li>`)
|
|
57
|
+
.join('');
|
|
58
|
+
return card('Modules', `<ul class="sitemap-list">${items}</ul>`);
|
|
59
|
+
}
|
|
60
|
+
function renderModuleCard(m) {
|
|
61
|
+
const sectionBlocks = [];
|
|
62
|
+
for (const [heading, body] of m.sections) {
|
|
63
|
+
if (heading === '' || heading.toLowerCase() === 'notes')
|
|
64
|
+
continue;
|
|
65
|
+
const rendered = body.trim() ? renderProse(body) : '';
|
|
66
|
+
if (!rendered)
|
|
67
|
+
continue;
|
|
68
|
+
sectionBlocks.push(`<h4>${escapeHtml(heading)}</h4>${rendered}`);
|
|
69
|
+
}
|
|
70
|
+
return `<section id="${escapeHtml(m.slug)}" class="card">
|
|
71
|
+
<h3>${escapeHtml(m.title)}</h3>
|
|
72
|
+
<div class="card__body">${sectionBlocks.join('')}</div>
|
|
73
|
+
</section>`;
|
|
74
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the `design-system/` register as an interactive companion gallery.
|
|
3
|
+
*
|
|
4
|
+
* What gets rendered:
|
|
5
|
+
* - Colour tokens — as visual swatches with the hex value beside the name.
|
|
6
|
+
* - Type scale — each step rendered at its declared px size with its line-height
|
|
7
|
+
* and weight applied so the operator sees the real shape, not a description.
|
|
8
|
+
* - Spacing scale — rendered as horizontal bars at the actual pixel widths.
|
|
9
|
+
* - Radius & elevation — small boxes showing the corner radius applied.
|
|
10
|
+
* - Motion — table only (no animation; would push complexity past MVP scope).
|
|
11
|
+
* - Components + patterns — name list with first-paragraph summary.
|
|
12
|
+
* - Voice + accessibility — first-paragraph summary blocks.
|
|
13
|
+
*
|
|
14
|
+
* Token files use GFM tables with a `Token` column and a value column. The parser
|
|
15
|
+
* looks for either a `Hex` / `Value` / `Size` / `Used for` column shape and falls
|
|
16
|
+
* back to "list the table verbatim" when the shape isn't recognised.
|
|
17
|
+
*/
|
|
18
|
+
export interface DesignSystemRenderResult {
|
|
19
|
+
written: boolean;
|
|
20
|
+
outPath: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function renderDesignSystem(opts: {
|
|
23
|
+
buildRoot: string;
|
|
24
|
+
projectName: string;
|
|
25
|
+
generatedAt: string;
|
|
26
|
+
}): Promise<DesignSystemRenderResult>;
|