@1agh/maude 0.17.2 → 0.18.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/cli/commands/design.mjs +92 -29
- package/package.json +8 -8
- package/plugins/design/dev-server/bin/screenshot.sh +12 -0
- package/plugins/design/dev-server/boot-self-heal.ts +90 -0
- package/plugins/design/dev-server/build.ts +18 -2
- package/plugins/design/dev-server/config.schema.json +12 -0
- package/plugins/design/dev-server/dist/client.bundle.js +3 -3
- package/plugins/design/dev-server/runtime-bundle.ts +29 -1
- package/plugins/design/dev-server/server.ts +6 -0
- package/plugins/design/dev-server/test/boot-self-heal.test.ts +112 -0
- package/plugins/design/dev-server/test/runtime-bundle-error-mapping.test.ts +43 -0
- package/plugins/design/templates/canvas.tsx.template +7 -7
- package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html +1 -1
- package/plugins/design/templates/design-system-inspiration/core/README.philosophy.md.tpl +11 -7
- package/plugins/design/templates/design-system-inspiration/core/SKILL.md.tpl +4 -3
- package/plugins/design/templates/design-system-inspiration/core/colors_and_type.css.tpl +61 -57
- package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +2 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/colors-accent.html +4 -4
- package/plugins/design/templates/design-system-inspiration/meta/presence-multiplayer.html +1 -1
- package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-showcase.html +3 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-showcase.html +1 -1
- package/plugins/design/templates/design-system-inspiration/theme-both/colors-themes-side-by-side.html +6 -6
- package/plugins/design/templates/design-system-inspiration/universal/components-dialogs.html +3 -2
- package/plugins/design/templates/design-system-inspiration/universal/logo.html +2 -1
package/cli/commands/design.mjs
CHANGED
|
@@ -508,6 +508,13 @@ function escapeReg(s) {
|
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
function defaultPayload({ projectName, dsName }) {
|
|
511
|
+
// `--no-discovery` mode emits a deliberately NEUTRAL skeleton:
|
|
512
|
+
// achromatic grayscale, system fonts only, equal spacing/radii steps,
|
|
513
|
+
// single accent set to neutral graphite. The intent is for the project
|
|
514
|
+
// to immediately overwrite these with discovery-driven values via
|
|
515
|
+
// `/design:setup-ds`. These are NOT a "recommended starter palette" —
|
|
516
|
+
// they're an explicit placeholder that should look unfinished, so the
|
|
517
|
+
// designer is nudged toward actual decisions (DDR-026, 2026-05-25).
|
|
511
518
|
return {
|
|
512
519
|
project_name: projectName,
|
|
513
520
|
project_label: titleCase(projectName),
|
|
@@ -515,7 +522,7 @@ function defaultPayload({ projectName, dsName }) {
|
|
|
515
522
|
ds_skill_name: `${dsName}-design`,
|
|
516
523
|
ds_description: `Default design system for ${projectName}.`,
|
|
517
524
|
root_class: 'app',
|
|
518
|
-
theme_default: '
|
|
525
|
+
theme_default: 'light',
|
|
519
526
|
theme_extra: '',
|
|
520
527
|
handoff_targets: '[]',
|
|
521
528
|
active_families: JSON.stringify(['accent']),
|
|
@@ -528,38 +535,94 @@ function defaultPayload({ projectName, dsName }) {
|
|
|
528
535
|
platform_hard_rules: '',
|
|
529
536
|
content_tone: 'direct-terse',
|
|
530
537
|
mood_references_block: '_(not specified — extend during the next /design:setup-ds run)_',
|
|
531
|
-
type_scale_summary: '8-step ladder,
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
538
|
+
type_scale_summary: '8-step ladder, populated by discovery.',
|
|
539
|
+
type_scale_summary_compact: '8-step (modular ratio)',
|
|
540
|
+
voice_tone_block: 'Voice not declared yet — capture during /design:setup-ds.',
|
|
541
|
+
voice_tone_summary: 'not-declared',
|
|
542
|
+
iconography_summary: 'Icon family not declared yet — capture during /design:setup-ds.',
|
|
535
543
|
hard_rules_from_system_readme: '_(see system/<ds>/README.md Hard rules section)_',
|
|
536
544
|
hard_rules_block:
|
|
537
|
-
'- WCAG 2.1 AA contrast\n- No off-token
|
|
545
|
+
'- WCAG 2.1 AA contrast\n- No off-token values in canvases\n- Real product strings only — no placeholder copy',
|
|
538
546
|
iso_timestamp: new Date().toISOString(),
|
|
539
|
-
//
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
547
|
+
// ─── Neutral-skeleton tokens (light theme grayscale, no chroma) ───
|
|
548
|
+
bg_0: 'oklch(100% 0 0)',
|
|
549
|
+
bg_1: 'oklch(97% 0 0)',
|
|
550
|
+
bg_2: 'oklch(94% 0 0)',
|
|
551
|
+
bg_3: 'oklch(90% 0 0)',
|
|
552
|
+
bg_4: 'oklch(85% 0 0)',
|
|
553
|
+
border_subtle: 'oklch(92% 0 0)',
|
|
554
|
+
border_default: 'oklch(85% 0 0)',
|
|
555
|
+
border_strong: 'oklch(70% 0 0)',
|
|
556
|
+
border_strategy: 'fixed-token',
|
|
557
|
+
fg_0: 'oklch(15% 0 0)',
|
|
558
|
+
fg_1: 'oklch(35% 0 0)',
|
|
559
|
+
fg_2: 'oklch(55% 0 0)',
|
|
560
|
+
fg_3: 'oklch(75% 0 0)',
|
|
561
|
+
accent_strategy: 'single',
|
|
562
|
+
accent_strategy_summary: 'single accent — discovery has not been run',
|
|
563
|
+
accent_block:
|
|
564
|
+
' --accent: oklch(35% 0 0);\n --accent-hover: oklch(30% 0 0);\n --accent-active: oklch(25% 0 0);\n --accent-fg: oklch(98% 0 0);',
|
|
565
|
+
accent_rules_block:
|
|
566
|
+
'Single accent family (`--accent`, `--accent-hover`, `--accent-active`, `--accent-fg`). Project has not run discovery yet — the accent is set to neutral graphite as a placeholder. Run `/design:setup-ds` to pick a real palette.',
|
|
567
|
+
accent_rules_summary: 'Single family until discovery runs.',
|
|
568
|
+
color_space: 'oklch',
|
|
569
|
+
color_space_summary: 'OKLCH (default)',
|
|
570
|
+
color_space_block:
|
|
571
|
+
'**Color space:** OKLCH (default). Override via `config.colorSpace` if your project requires HSL, hex, or LAB.',
|
|
572
|
+
status_success: 'oklch(55% 0 0)',
|
|
573
|
+
status_warn: 'oklch(55% 0 0)',
|
|
574
|
+
status_error: 'oklch(55% 0 0)',
|
|
575
|
+
status_info: 'oklch(55% 0 0)',
|
|
576
|
+
presence_online: 'var(--status-success)',
|
|
577
|
+
presence_away: 'var(--status-warn)',
|
|
578
|
+
presence_offline: 'var(--fg-3)',
|
|
579
|
+
shadow_sm: 'none',
|
|
580
|
+
shadow_md: 'none',
|
|
581
|
+
shadow_lg: 'none',
|
|
582
|
+
radius_xs: '0',
|
|
583
|
+
radius_sm: '0',
|
|
584
|
+
radius_md: '0',
|
|
585
|
+
radius_lg: '0',
|
|
586
|
+
radius_xl: '0',
|
|
587
|
+
radius_pill: '999px',
|
|
588
|
+
space_0: '0',
|
|
589
|
+
space_1: '4px',
|
|
590
|
+
space_2: '8px',
|
|
591
|
+
space_3: '12px',
|
|
592
|
+
space_4: '16px',
|
|
593
|
+
space_5: '24px',
|
|
594
|
+
space_6: '32px',
|
|
595
|
+
space_7: '48px',
|
|
596
|
+
space_8: '64px',
|
|
597
|
+
font_display: 'system-ui, sans-serif',
|
|
598
|
+
font_body: 'system-ui, sans-serif',
|
|
599
|
+
font_mono: 'ui-monospace, monospace',
|
|
600
|
+
type_xs: '12px',
|
|
601
|
+
lh_xs: '16px',
|
|
602
|
+
type_sm: '13px',
|
|
603
|
+
lh_sm: '18px',
|
|
604
|
+
type_base: '14px',
|
|
605
|
+
lh_base: '20px',
|
|
606
|
+
type_md: '16px',
|
|
607
|
+
lh_md: '22px',
|
|
608
|
+
type_lg: '18px',
|
|
609
|
+
lh_lg: '26px',
|
|
610
|
+
type_xl: '22px',
|
|
611
|
+
lh_xl: '30px',
|
|
612
|
+
type_2xl: '28px',
|
|
613
|
+
lh_2xl: '36px',
|
|
614
|
+
type_3xl: '36px',
|
|
615
|
+
lh_3xl: '44px',
|
|
616
|
+
dur_flip: '120ms',
|
|
617
|
+
dur_panel: '200ms',
|
|
618
|
+
dur_route: '240ms',
|
|
562
619
|
dur_soft: '320ms',
|
|
620
|
+
ease_out_curve: 'ease-out',
|
|
621
|
+
ease_in_out_curve: 'ease-in-out',
|
|
622
|
+
layout_max_w: '1200px',
|
|
623
|
+
layout_gutter: 'var(--space-4)',
|
|
624
|
+
touch_target_rule:
|
|
625
|
+
'44×44 minimum on touch surfaces (iOS HIG); 48×48 if the primary platform is Android (Material). N/A on desktop-only systems.',
|
|
563
626
|
// Empty-state template fields
|
|
564
627
|
empty_subject: 'items',
|
|
565
628
|
empty_supporting: "When you create your first one, it'll show up here.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"prepublishOnly": "bash scripts/check-version-parity.sh"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@1agh/maude-darwin-arm64": "0.
|
|
45
|
-
"@1agh/maude-darwin-x64": "0.
|
|
46
|
-
"@1agh/maude-linux-arm64": "0.
|
|
47
|
-
"@1agh/maude-linux-arm64-musl": "0.
|
|
48
|
-
"@1agh/maude-linux-x64": "0.
|
|
49
|
-
"@1agh/maude-linux-x64-musl": "0.
|
|
50
|
-
"@1agh/maude-win32-x64": "0.
|
|
44
|
+
"@1agh/maude-darwin-arm64": "0.18.0",
|
|
45
|
+
"@1agh/maude-darwin-x64": "0.18.0",
|
|
46
|
+
"@1agh/maude-linux-arm64": "0.18.0",
|
|
47
|
+
"@1agh/maude-linux-arm64-musl": "0.18.0",
|
|
48
|
+
"@1agh/maude-linux-x64": "0.18.0",
|
|
49
|
+
"@1agh/maude-linux-x64-musl": "0.18.0",
|
|
50
|
+
"@1agh/maude-win32-x64": "0.18.0"
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"cli",
|
|
@@ -64,6 +64,18 @@ else
|
|
|
64
64
|
[ -z "$OUT" ] && { echo "screenshot.sh: --out required for single-shot modes" >&2; exit 2; }
|
|
65
65
|
fi
|
|
66
66
|
|
|
67
|
+
# TSX specimens cannot be opened via file:// — the browser would see raw JSX.
|
|
68
|
+
# They must go through the dev-server route (http://localhost:PORT/<rel>),
|
|
69
|
+
# which transpiles via _canvas-shell.html?canvas=<rel>. Phase 19 / DDR-044.
|
|
70
|
+
case "$URL" in
|
|
71
|
+
file://*.tsx)
|
|
72
|
+
echo "screenshot.sh: TSX specimens cannot be screenshot via file:// (the browser sees raw JSX)." >&2
|
|
73
|
+
echo " Use --port instead — the dev-server compiles TSX through _canvas-shell.html?canvas=<rel>." >&2
|
|
74
|
+
echo " Example: screenshot.sh --port 4399 --full --out shot.png (set the active canvas in the browser first)" >&2
|
|
75
|
+
exit 2
|
|
76
|
+
;;
|
|
77
|
+
esac
|
|
78
|
+
|
|
67
79
|
# ---------- url resolution ----------
|
|
68
80
|
if [ -z "$URL" ]; then
|
|
69
81
|
REPO="${ROOT:-${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Boot-time self-heal — covers the gap between marketplace-cache installs
|
|
2
|
+
// (git clone, honors .gitignore, no `npm install`) and what server.ts needs
|
|
3
|
+
// at runtime. Two artifacts can be missing on a fresh clone:
|
|
4
|
+
//
|
|
5
|
+
// - dist/client.bundle.js → 404 on /_client/*
|
|
6
|
+
// - node_modules/react → 500 on /_canvas-runtime/*
|
|
7
|
+
//
|
|
8
|
+
// Per DDR-044 we commit the bundle + styles, so dist/ should be present.
|
|
9
|
+
// For node_modules/ we self-heal: detect missing react, run `bun install
|
|
10
|
+
// --production`. Opt out with MAUDE_NO_AUTOBUILD=1 for read-only-filesystem
|
|
11
|
+
// deployments (e.g. immutable infra).
|
|
12
|
+
//
|
|
13
|
+
// Phase 19. DDR-044.
|
|
14
|
+
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
|
|
19
|
+
export interface SelfHealOptions {
|
|
20
|
+
/** Plugin install directory (the dev-server root). Defaults to this file's dir. */
|
|
21
|
+
here?: string;
|
|
22
|
+
/** Defaults to process.env.MAUDE_NO_AUTOBUILD === '1'. */
|
|
23
|
+
optOut?: boolean;
|
|
24
|
+
/** Defaults to Bun.spawn; tests override. */
|
|
25
|
+
spawn?: (cmd: readonly string[], cwd: string) => Promise<{ code: number }>;
|
|
26
|
+
/** Defaults to console.error; tests override to capture. */
|
|
27
|
+
log?: (msg: string) => void;
|
|
28
|
+
/** Defaults to process.exit; tests override to assert. */
|
|
29
|
+
exit?: (code: number) => never;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SelfHealResult {
|
|
33
|
+
ran: ('install' | 'build')[];
|
|
34
|
+
skipped: 'all-present' | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function bootSelfHeal(opts: SelfHealOptions = {}): Promise<SelfHealResult> {
|
|
38
|
+
const here = opts.here ?? dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
const optOut = opts.optOut ?? process.env.MAUDE_NO_AUTOBUILD === '1';
|
|
40
|
+
const log = opts.log ?? ((m) => console.error(m));
|
|
41
|
+
const exit =
|
|
42
|
+
opts.exit ??
|
|
43
|
+
((code: number) => {
|
|
44
|
+
process.exit(code);
|
|
45
|
+
});
|
|
46
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
47
|
+
|
|
48
|
+
const distMissing = !existsSync(join(here, 'dist', 'client.bundle.js'));
|
|
49
|
+
const depsMissing = !existsSync(join(here, 'node_modules', 'react', 'package.json'));
|
|
50
|
+
|
|
51
|
+
if (!distMissing && !depsMissing) return { ran: [], skipped: 'all-present' };
|
|
52
|
+
|
|
53
|
+
if (optOut) {
|
|
54
|
+
const missing = [
|
|
55
|
+
distMissing ? 'dist/client.bundle.js (run `bun run build.ts`)' : null,
|
|
56
|
+
depsMissing ? 'node_modules/react (run `bun install --production`)' : null,
|
|
57
|
+
]
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.join('\n - ');
|
|
60
|
+
log(`\n ⚠ first-boot artifacts missing and MAUDE_NO_AUTOBUILD=1 is set:\n - ${missing}\n`);
|
|
61
|
+
exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const ran: ('install' | 'build')[] = [];
|
|
65
|
+
if (depsMissing) {
|
|
66
|
+
log(' ⚠ first-boot: installing runtime deps (one-time, ~15s)…');
|
|
67
|
+
const { code } = await spawn(['bun', 'install', '--production'], here);
|
|
68
|
+
if (code !== 0) {
|
|
69
|
+
log(` ⚠ \`bun install --production\` exited ${code}. Set MAUDE_NO_AUTOBUILD=1 and run manually.`);
|
|
70
|
+
exit(1);
|
|
71
|
+
}
|
|
72
|
+
ran.push('install');
|
|
73
|
+
}
|
|
74
|
+
if (distMissing) {
|
|
75
|
+
log(' ⚠ first-boot: building client assets (one-time, ~2s)…');
|
|
76
|
+
const { code } = await spawn(['bun', 'run', 'build.ts'], here);
|
|
77
|
+
if (code !== 0) {
|
|
78
|
+
log(` ⚠ \`bun run build.ts\` exited ${code}. Set MAUDE_NO_AUTOBUILD=1 and run manually.`);
|
|
79
|
+
exit(1);
|
|
80
|
+
}
|
|
81
|
+
ran.push('build');
|
|
82
|
+
}
|
|
83
|
+
return { ran, skipped: null };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function defaultSpawn(cmd: readonly string[], cwd: string): Promise<{ code: number }> {
|
|
87
|
+
const proc = Bun.spawn([...cmd], { cwd, stdout: 'inherit', stderr: 'inherit' });
|
|
88
|
+
const code = await proc.exited;
|
|
89
|
+
return { code };
|
|
90
|
+
}
|
|
@@ -65,13 +65,29 @@ function ensureDist() {
|
|
|
65
65
|
if (!existsSync(DIST)) mkdirSync(DIST, { recursive: true });
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
async function readPluginVersion(): Promise<{ version: string }> {
|
|
69
|
+
// plugins/design/dev-server/ → plugins/design/.claude-plugin/plugin.json
|
|
70
|
+
const manifest = join(ROOT, '..', '.claude-plugin', 'plugin.json');
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(await Bun.file(manifest).text()) as { version?: unknown };
|
|
73
|
+
if (typeof parsed.version === 'string') return { version: parsed.version };
|
|
74
|
+
} catch {
|
|
75
|
+
/* fall through to dev default */
|
|
76
|
+
}
|
|
77
|
+
return { version: 'dev' };
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
// ---------- (a) Client JSX bundle ----------
|
|
69
81
|
|
|
70
82
|
async function buildClient(): Promise<{ outBytes: number; outPath: string }> {
|
|
71
83
|
ensureDist();
|
|
72
84
|
const outPath = join(DIST, 'client.bundle.js');
|
|
73
|
-
//
|
|
74
|
-
|
|
85
|
+
// Wordmark sub-line version. Read from plugins/design/.claude-plugin/plugin.json —
|
|
86
|
+
// that file ships in both npm installs AND marketplace-cache clones (it's the
|
|
87
|
+
// plugin manifest, always present). The old `../../../package.json` hop
|
|
88
|
+
// resolved to the repo root in dev but ENOENT'd in marketplace caches where
|
|
89
|
+
// the maude bundle isn't installed as a package. DDR-044, Phase 19.
|
|
90
|
+
const pkg = await readPluginVersion();
|
|
75
91
|
const result = await Bun.build({
|
|
76
92
|
entrypoints: [join(ROOT, 'client/app.jsx')],
|
|
77
93
|
outdir: DIST,
|
|
@@ -116,6 +116,18 @@
|
|
|
116
116
|
},
|
|
117
117
|
"default": ["accent"]
|
|
118
118
|
},
|
|
119
|
+
"accentStrategy": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"description": "How many --accent* families exist. The completeness-critic C7 gates the actual family count against this declaration. Default: 'single' (backwards-compatible). 'chromatic-N' is parameterized — write the literal N (e.g. 'chromatic-5'). Per DDR-043.",
|
|
122
|
+
"pattern": "^(single|paired|chromatic-[1-9][0-9]?)$",
|
|
123
|
+
"default": "single"
|
|
124
|
+
},
|
|
125
|
+
"colorSpace": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"enum": ["oklch", "hsl", "hex", "lab"],
|
|
128
|
+
"description": "Color space the project uses in tokens CSS. The completeness-critic V2 gates the actual token format against this declaration. Default: 'oklch' (backwards-compatible). Per DDR-043.",
|
|
129
|
+
"default": "oklch"
|
|
130
|
+
},
|
|
119
131
|
"designSystems": {
|
|
120
132
|
"type": "array",
|
|
121
133
|
"description": "Design systems available in this project. Single-DS projects have one entry; multi-DS projects (marketing vs. admin vs. mobile) list each here. Each canvas's .meta.json declares which DS it uses via the 'designSystem' field.",
|