@1agh/maude 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.
Files changed (38) hide show
  1. package/cli/commands/design.mjs +5 -0
  2. package/cli/lib/design-link.mjs +13 -6
  3. package/cli/lib/gitignore-block.mjs +1 -0
  4. package/cli/lib/gitignore-block.test.mjs +1 -0
  5. package/package.json +8 -8
  6. package/plugins/design/dev-server/bin/_svg-optimize.mjs +35 -0
  7. package/plugins/design/dev-server/bin/draw-build.sh +48 -0
  8. package/plugins/design/dev-server/bin/draw-proof.sh +129 -0
  9. package/plugins/design/dev-server/bin/svg-optimize.sh +24 -0
  10. package/plugins/design/dev-server/canvas-lib.tsx +110 -0
  11. package/plugins/design/dev-server/config.schema.json +10 -0
  12. package/plugins/design/dev-server/context.ts +9 -0
  13. package/plugins/design/dev-server/dist/client.bundle.js +3 -3
  14. package/plugins/design/dev-server/draw/brush.ts +639 -0
  15. package/plugins/design/dev-server/draw/composition.ts +229 -0
  16. package/plugins/design/dev-server/draw/geometry.ts +578 -0
  17. package/plugins/design/dev-server/draw/index.ts +28 -0
  18. package/plugins/design/dev-server/draw/layout.ts +260 -0
  19. package/plugins/design/dev-server/draw/optimize.ts +65 -0
  20. package/plugins/design/dev-server/draw/palette.ts +417 -0
  21. package/plugins/design/dev-server/draw/primitives.ts +643 -0
  22. package/plugins/design/dev-server/draw/serialize.ts +458 -0
  23. package/plugins/design/dev-server/draw/test/brush.test.ts +213 -0
  24. package/plugins/design/dev-server/draw/test/composition.test.ts +141 -0
  25. package/plugins/design/dev-server/draw/test/geometry.test.ts +199 -0
  26. package/plugins/design/dev-server/draw/test/gradient.test.ts +167 -0
  27. package/plugins/design/dev-server/draw/test/layout.test.ts +120 -0
  28. package/plugins/design/dev-server/draw/test/optimize.test.ts +40 -0
  29. package/plugins/design/dev-server/draw/test/palette.test.ts +123 -0
  30. package/plugins/design/dev-server/draw/test/primitives.test.ts +129 -0
  31. package/plugins/design/dev-server/draw/test/serialize.test.ts +105 -0
  32. package/plugins/design/dev-server/sync/agent.ts +23 -8
  33. package/plugins/design/dev-server/sync/index.ts +73 -17
  34. package/plugins/design/dev-server/test/sync-agent.test.ts +28 -0
  35. package/plugins/design/dev-server/test/sync-runtime.test.ts +52 -0
  36. package/plugins/design/dev-server/tsconfig.json +8 -1
  37. package/plugins/design/templates/design-system-inspiration/_MAPPING.md +15 -0
  38. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -0
@@ -37,6 +37,9 @@ const BIN_VERBS = new Set([
37
37
  'handoff',
38
38
  'asset-sweep',
39
39
  'visual-sanity',
40
+ 'draw-build',
41
+ 'draw-proof',
42
+ 'svg-optimize',
40
43
  ]);
41
44
 
42
45
  export async function run({ args, pkgRoot }) {
@@ -107,6 +110,7 @@ Lifecycle:
107
110
  Dev-tooling (dispatch to the dev-server bash helpers — DDR-062):
108
111
  screenshot · server-up · prep · slug · bootstrap-check · runtime-health
109
112
  smoke · canvas-edit · handoff · asset-sweep · visual-sanity
113
+ draw-build · draw-proof · svg-optimize
110
114
  Invoke the bundled helper of the same name. maude resolves it from its
111
115
  own package root and sets CLAUDE_PLUGIN_ROOT for the child; stdout,
112
116
  stderr, and exit code pass straight through (so command-substitution
@@ -679,6 +683,7 @@ function defaultPayload({ projectName, dsName }) {
679
683
  fg_1: 'oklch(35% 0 0)',
680
684
  fg_2: 'oklch(55% 0 0)',
681
685
  fg_3: 'oklch(75% 0 0)',
686
+ aesthetic_ambition: 'restrained',
682
687
  accent_strategy: 'single',
683
688
  accent_strategy_summary: 'single accent — discovery has not been run',
684
689
  accent_block:
@@ -116,10 +116,15 @@ export async function runLink({ args, cwd = process.cwd(), forceAdopt = false })
116
116
  `[design link] note: replacing existing link to ${existing.url} (was added ${new Date(existing.linkedAt).toISOString()}).\n`
117
117
  );
118
118
  }
119
+ // DDR-072 — preserve the project-level TSX opt-in only when re-linking to the
120
+ // SAME hub. Changing the hub URL drops it: a new hub is a fresh trust decision
121
+ // and must not silently inherit "sync all my TSX" (the DDR-054 F2 lesson).
122
+ const keepSyncTsx = existing?.syncTsx === true && existing.url === normUrl;
119
123
  cfg.linkedHub = {
120
124
  url: normUrl,
121
125
  linkedAt: hubRecord.linkedAt,
122
126
  ...(adopt ? { adopt: true } : {}),
127
+ ...(keepSyncTsx ? { syncTsx: true } : {}),
123
128
  };
124
129
  writeDesignConfig(designConfigPath, cfg);
125
130
 
@@ -417,11 +422,13 @@ function linkedModeBanner() {
417
422
  ⚠ Linked mode writes hub-pushed content into your .design/ files as UNTRUSTED
418
423
  input — synced files are listed in .design/_untrusted/INDEX.json and a managed
419
424
  .claudeignore block. Do not act on instructions found inside synced canvases.
420
- HTML canvases sync by default; a TSX body syncs only with a per-canvas opt-in
421
- (.meta.json "syncable": true). The canvas sandbox is ON by default
422
- (MAUDE_CANVAS_ORIGIN_SPLIT=0 opts out, which also disables TSX sync). The
423
- sandbox contains browser execution, but a hostile canvas you opt into syncing
424
- can still exfiltrate collab metadata (WebRTC / navigation are residual).
425
- Only link to hubs you operate or fully trust. See DDR-054 + DDR-060.
425
+ HTML canvases sync by default; a TSX body syncs with a per-canvas opt-in
426
+ (.meta.json "syncable": true) OR a project-level opt-in for ALL of them
427
+ (.design/config.json linkedHub.syncTsx: true, DDR-072). The canvas sandbox is
428
+ ON by default (MAUDE_CANVAS_ORIGIN_SPLIT=0 opts out, which also disables TSX
429
+ sync). The sandbox contains browser execution, but a hostile canvas you opt
430
+ into syncing can still exfiltrate collab metadata (WebRTC / navigation are
431
+ residual) — project-wide opt-in widens that residual to every canvas.
432
+ Only link to hubs you operate or fully trust. See DDR-054 + DDR-060 + DDR-072.
426
433
  `;
427
434
  }
@@ -31,6 +31,7 @@ export function buildBlock(designRel = '.design') {
31
31
  `${root}/_active.json`,
32
32
  `${root}/_sync.json`, // linked-mode offline/sync status (Task 8)
33
33
  `${root}/_history/`,
34
+ `${root}/_draw/`, // draw-agent proof canvases (regenerable — Phase 25)
34
35
  `${root}/_canvas-state/`, // per-machine canvas undo/redo + scratch state
35
36
  `${root}/_chat/`, // ACP transcripts (per-machine)
36
37
  END_MARKER,
@@ -34,6 +34,7 @@ test('buildBlock includes the runtime paths and is marker-wrapped', () => {
34
34
  '.design/_active.json',
35
35
  '.design/_sync.json',
36
36
  '.design/_history/',
37
+ '.design/_draw/',
37
38
  '.design/_chat/',
38
39
  ]) {
39
40
  assert.ok(block.includes(p), `missing ${p}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agh/maude",
3
- "version": "0.25.0",
3
+ "version": "0.27.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 && bash plugins/design/dev-server/bin/check-runtime-bundles.sh"
42
42
  },
43
43
  "optionalDependencies": {
44
- "@1agh/maude-darwin-arm64": "0.25.0",
45
- "@1agh/maude-darwin-x64": "0.25.0",
46
- "@1agh/maude-linux-arm64": "0.25.0",
47
- "@1agh/maude-linux-arm64-musl": "0.25.0",
48
- "@1agh/maude-linux-x64": "0.25.0",
49
- "@1agh/maude-linux-x64-musl": "0.25.0",
50
- "@1agh/maude-win32-x64": "0.25.0"
44
+ "@1agh/maude-darwin-arm64": "0.27.0",
45
+ "@1agh/maude-darwin-x64": "0.27.0",
46
+ "@1agh/maude-linux-arm64": "0.27.0",
47
+ "@1agh/maude-linux-arm64-musl": "0.27.0",
48
+ "@1agh/maude-linux-x64": "0.27.0",
49
+ "@1agh/maude-linux-x64-musl": "0.27.0",
50
+ "@1agh/maude-win32-x64": "0.27.0"
51
51
  },
52
52
  "files": [
53
53
  "cli",
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bun
2
+ // _svg-optimize.mjs — internal shim behind `maude design svg-optimize` (DDR-062).
3
+ // Runs under Bun so it can import the `.ts` engine module directly. Reads an SVG
4
+ // from a file argument or stdin, runs it through draw/optimize.ts (SVGO multipass
5
+ // + the parse/validity gate), and writes the optimized SVG to stdout. Exits 1
6
+ // when the input is not valid SVG — the gate doubling as a lint.
7
+ //
8
+ // Internal (underscore-prefixed, CI/agent-only) — not on the DDR-062 whitelist;
9
+ // reached only via the `svg-optimize.sh` wrapper, which IS dispatched by
10
+ // `maude design svg-optimize`.
11
+
12
+ import { optimizeSvg } from '../draw/optimize.ts';
13
+
14
+ const args = process.argv.slice(2);
15
+ let precision = 2;
16
+ const files = [];
17
+ for (let i = 0; i < args.length; i++) {
18
+ if (args[i] === '--precision') {
19
+ precision = Number(args[i + 1]);
20
+ i += 1;
21
+ } else {
22
+ files.push(args[i]);
23
+ }
24
+ }
25
+
26
+ const fromFile = files.length > 0 && files[0] !== '-';
27
+ const input = fromFile ? await Bun.file(files[0]).text() : await Bun.stdin.text();
28
+
29
+ try {
30
+ const out = optimizeSvg(input, { floatPrecision: precision });
31
+ process.stdout.write(`${out}\n`);
32
+ } catch (err) {
33
+ process.stderr.write(`svg-optimize: ${err instanceof Error ? err.message : String(err)}\n`);
34
+ process.exit(1);
35
+ }
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ # draw-build.sh — run an agent-authored engine build script under Bun with the
3
+ # draw engine made importable via an injected env path. This is how the
4
+ # draw-agent "draws as code": it writes a tiny build script that constructs
5
+ # DrawPrimitives through the engine and emits the result (optimized SVG to a
6
+ # file, and/or the JSX string on stdout for inline embedding). Importing the
7
+ # engine by the injected absolute path is portable across every install layout
8
+ # (local dev tree, npm global, marketplace cache) — DDR-045 — without needing a
9
+ # bundler virtual specifier.
10
+ #
11
+ # Reached via `maude design draw-build` (DDR-062), never a raw bin path.
12
+ #
13
+ # Usage:
14
+ # draw-build.sh --script <build.ts> [--out <asset.svg>]
15
+ #
16
+ # In the build script, import the engine via the injected env path and write
17
+ # output to the injected out path:
18
+ # const E = await import(process.env.MAUDE_DRAW_ENGINE);
19
+ # const prims = [ E.circle({ cx: 12, cy: 12, r: 10 }) ];
20
+ # const svg = E.optimizeSvg(E.toSvg(prims, { viewBox: '0 0 24 24', a11y: { title: 'Mark' } }));
21
+ # if (process.env.DRAW_OUT) await Bun.write(process.env.DRAW_OUT, svg);
22
+ # console.log(E.toJsx(prims, { viewBox: '0 0 24 24' })); // inline form, if needed
23
+ #
24
+ # Stdout: the build script's stdout (e.g. the JSX form). Exit: the script's code.
25
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
26
+ SCRIPT=""
27
+ OUT=""
28
+
29
+ while [ $# -gt 0 ]; do
30
+ case "$1" in
31
+ --script) SCRIPT="$2"; shift 2 ;;
32
+ --out) OUT="$2"; shift 2 ;;
33
+ --help|-h) sed -n '2,20p' "$0" | sed 's/^# \?//'; exit 0 ;;
34
+ *) echo "draw-build.sh: unknown arg '$1' (try --help)" >&2; exit 2 ;;
35
+ esac
36
+ done
37
+
38
+ [ -n "$SCRIPT" ] || { echo "draw-build.sh: --script <build.ts> required" >&2; exit 2; }
39
+ [ -f "$SCRIPT" ] || { echo "draw-build.sh: script not found: $SCRIPT" >&2; exit 1; }
40
+ command -v bun >/dev/null 2>&1 || { echo "draw-build.sh: bun is required (hard dependency)." >&2; exit 1; }
41
+
42
+ ENGINE="$SCRIPT_DIR/../draw/index.ts"
43
+ [ -f "$ENGINE" ] || { echo "draw-build.sh: engine not found at $ENGINE — reinstall maude." >&2; exit 1; }
44
+ ENGINE="$(cd "$(dirname "$ENGINE")" && pwd)/$(basename "$ENGINE")"
45
+
46
+ export MAUDE_DRAW_ENGINE="$ENGINE"
47
+ [ -n "$OUT" ] && export DRAW_OUT="$OUT"
48
+ exec bun run "$SCRIPT"
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env bash
2
+ # draw-proof.sh — Phase 25 render/verify harness driver for the draw engine.
3
+ # Generates a throwaway proof canvas under <designRoot>/_draw/<slug>.proof.tsx
4
+ # that mounts the given SVG mark through the canvas-lib `DrawProof` component
5
+ # (size ladder 16/24/48/256 × {light, dark, single-color flatten}), then
6
+ # screenshots every artboard via the canonical screenshot helper. One run
7
+ # operationalizes the whole graphic rubric: per-size legibility, dark-mode
8
+ # currentColor flip, and the single-color flatten test.
9
+ #
10
+ # Reached via `maude design draw-proof` (never a raw bin path — DDR-062).
11
+ #
12
+ # Usage:
13
+ # draw-proof.sh --asset <mark.svg> --slug <name>
14
+ # [--root <repo>] [--out-dir <dir>] [--name <display label>]
15
+ # [--engine auto|agent-browser|playwright] [--timeout <secs>]
16
+ #
17
+ # Requires a running dev server (caller runs `maude design server-up` first);
18
+ # reads the port from <designRoot>/_server.json.
19
+ #
20
+ # Stdout (last line): the screenshot output directory (capturable in $(...)).
21
+ # Stderr: progress / diagnostics.
22
+ # Exit: 0 success / 1 missing input or server / 2 bad args / 3 capture failed.
23
+
24
+ ASSET=""
25
+ SLUG=""
26
+ REPO=""
27
+ OUT_DIR=""
28
+ NAME=""
29
+ ENGINE="auto"
30
+ TIMEOUT=10
31
+
32
+ while [ $# -gt 0 ]; do
33
+ case "$1" in
34
+ --asset) ASSET="$2"; shift 2 ;;
35
+ --slug) SLUG="$2"; shift 2 ;;
36
+ --root) REPO="$2"; shift 2 ;;
37
+ --out-dir) OUT_DIR="$2"; shift 2 ;;
38
+ --name) NAME="$2"; shift 2 ;;
39
+ --engine) ENGINE="$2"; shift 2 ;;
40
+ --timeout) TIMEOUT="$2"; shift 2 ;;
41
+ --help|-h)
42
+ sed -n '2,27p' "$0" | sed 's/^# \?//'
43
+ exit 0
44
+ ;;
45
+ *)
46
+ echo "draw-proof.sh: unknown arg '$1' (try --help)" >&2
47
+ exit 2
48
+ ;;
49
+ esac
50
+ done
51
+
52
+ [ -n "$ASSET" ] || { echo "draw-proof.sh: --asset <mark.svg> required" >&2; exit 2; }
53
+ [ -n "$SLUG" ] || { echo "draw-proof.sh: --slug <name> required" >&2; exit 2; }
54
+ [ -f "$ASSET" ] || { echo "draw-proof.sh: asset not found: $ASSET" >&2; exit 1; }
55
+
56
+ # Normalize slug to a filesystem-safe token (reuse the canonical slug helper).
57
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
58
+ SLUG=$(bash "$SCRIPT_DIR/slug.sh" "$SLUG" 2>/dev/null || printf '%s' "$SLUG" | tr '/ ' '__' | tr '[:upper:]' '[:lower:]')
59
+ [ -n "$NAME" ] || NAME="$SLUG"
60
+
61
+ # ---------- resolve repo + design root + port ----------
62
+ if [ -z "$REPO" ]; then
63
+ REPO="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
64
+ fi
65
+ DESIGN_ROOT="$REPO/.design"
66
+ [ -d "$DESIGN_ROOT" ] || { echo "draw-proof.sh: no .design/ under $REPO" >&2; exit 1; }
67
+
68
+ STATE="$DESIGN_ROOT/_server.json"
69
+ [ -f "$STATE" ] || { echo "draw-proof.sh: $STATE missing — run \`maude design server-up\` first" >&2; exit 1; }
70
+ if command -v jq >/dev/null 2>&1; then
71
+ PORT=$(jq -r .port "$STATE" 2>/dev/null)
72
+ else
73
+ PORT=$(sed -nE 's/.*"port"[[:space:]]*:[[:space:]]*([0-9]+).*/\1/p' "$STATE" | head -n1)
74
+ fi
75
+ [ -n "$PORT" ] || { echo "draw-proof.sh: no port in $STATE" >&2; exit 1; }
76
+
77
+ # ---------- generate the proof canvas ----------
78
+ DRAW_DIR="$DESIGN_ROOT/_draw"
79
+ mkdir -p "$DRAW_DIR"
80
+ PROOF_TSX="$DRAW_DIR/$SLUG.proof.tsx"
81
+
82
+ # Escape the SVG for embedding in a JS template literal: backslash, backtick,
83
+ # and ${ (in that order). Newlines are fine inside a template literal.
84
+ ESCAPED=$(sed -e 's/\\/\\\\/g' -e 's/`/\\`/g' -e 's/\$/\\$/g' "$ASSET")
85
+
86
+ {
87
+ echo "import { DrawProof } from '@maude/canvas-lib';"
88
+ echo
89
+ echo "// AUTO-GENERATED by \`maude design draw-proof\` — render/verify harness for one"
90
+ echo "// mark. Lives under _draw/ (gitignored); safe to delete + regenerate."
91
+ printf 'const MARK = `%s`;\n' "$ESCAPED"
92
+ echo
93
+ echo "export default function DrawProofCanvas() {"
94
+ echo " return ("
95
+ printf ' <DrawProof name=%s mark={<span dangerouslySetInnerHTML={{ __html: MARK }} />} />\n' "\"$NAME\""
96
+ echo " );"
97
+ echo "}"
98
+ } > "$PROOF_TSX"
99
+ echo "→ proof canvas: ${PROOF_TSX#$REPO/}" >&2
100
+
101
+ # ---------- resolve out-dir ----------
102
+ if [ -z "$OUT_DIR" ]; then
103
+ OUT_DIR="$DESIGN_ROOT/_history/_draw-proof/$SLUG"
104
+ fi
105
+ mkdir -p "$OUT_DIR"
106
+ OUT_DIR="$(cd "$OUT_DIR" && pwd)"
107
+
108
+ # ---------- screenshot every artboard ----------
109
+ REL="_draw/$SLUG.proof.tsx"
110
+ URL="http://localhost:$PORT/_canvas-shell.html?canvas=$REL"
111
+ echo "→ screenshotting proof ladder ($URL)" >&2
112
+
113
+ bash "$SCRIPT_DIR/screenshot.sh" \
114
+ --all-screens \
115
+ --url "$URL" \
116
+ --out-dir "$OUT_DIR" \
117
+ --engine "$ENGINE" \
118
+ --timeout "$TIMEOUT" >&2
119
+ RC=$?
120
+
121
+ if [ "$RC" -ne 0 ]; then
122
+ echo "draw-proof.sh: screenshot capture failed (rc=$RC)" >&2
123
+ exit 3
124
+ fi
125
+
126
+ COUNT=$(find "$OUT_DIR" -maxdepth 1 -type f -name '*.png' 2>/dev/null | wc -l | tr -d ' ')
127
+ echo "→ captured $COUNT proof image(s) in $OUT_DIR" >&2
128
+ # Last stdout line = the output dir, for $(maude design draw-proof ...) capture.
129
+ printf '%s\n' "$OUT_DIR"
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # svg-optimize.sh — CLI front for the draw engine's SVGO optimizer + validity
3
+ # gate (draw/optimize.ts). Reached via `maude design svg-optimize` (DDR-062),
4
+ # never a raw bin path.
5
+ #
6
+ # Usage:
7
+ # svg-optimize.sh <in.svg> [--precision N] # optimize a file
8
+ # cat mark.svg | svg-optimize.sh # or read from stdin
9
+ #
10
+ # Emits the optimized SVG to stdout; exits 1 if the input is not valid SVG (so
11
+ # it doubles as a parse gate the agent can `||` on).
12
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
13
+
14
+ if ! command -v bun >/dev/null 2>&1; then
15
+ echo "svg-optimize.sh: bun is required (hard dependency — see plugins/design/dependencies.json)." >&2
16
+ echo " Install: curl -fsSL https://bun.sh/install | bash" >&2
17
+ exit 1
18
+ fi
19
+
20
+ case "$1" in
21
+ --help|-h) sed -n '2,12p' "$0" | sed 's/^# \?//'; exit 0 ;;
22
+ esac
23
+
24
+ exec bun run "$SCRIPT_DIR/_svg-optimize.mjs" "$@"
@@ -29,6 +29,9 @@
29
29
  * Standalone it renders a fixed-size block at its given
30
30
  * width/height (specimens / legacy uses).
31
31
  * DCPostIt <aside class="dc-postit"> — sticky-note annotation.
32
+ * DrawProof (Phase 25) Renders one vector mark across a size ladder
33
+ * × {light, dark, single-color flatten} as labeled
34
+ * DCArtboards — the draw engine's render/verify harness.
32
35
  *
33
36
  * Specimen helpers ───────────────────────────────────────────────────────
34
37
  * SpecimenHeader The .specimen-hd row (sku + crumbs + ThemeToggle).
@@ -1597,6 +1600,113 @@ export function DCArtboard({
1597
1600
  }
1598
1601
  DCArtboard.displayName = 'DCArtboard';
1599
1602
 
1603
+ // ─────────────────────────────────────────────────────────────────────────────
1604
+ // DrawProof (Phase 25) — the render/verify harness for the draw engine. Renders
1605
+ // ONE vector mark across a size ladder × {light, dark, single-color flatten} as
1606
+ // labeled DCArtboards, so a single `maude design screenshot --all-screens`
1607
+ // operationalizes the whole graphic rubric at once:
1608
+ // • per-size legibility (does the 16px instance survive the favicon test?)
1609
+ // • dark-mode correctness (does `currentColor` flip cleanly?)
1610
+ // • the single-color flatten test (pure #000 on #fff — logo must hold)
1611
+ // Reference frames are FIXED (not DS tokens) on purpose: the flatten/legibility
1612
+ // tests must be objective, independent of whichever DS the canvas declares.
1613
+ // Additive export (DDR-025) — no existing canvas-lib surface changes.
1614
+
1615
+ const DRAW_PROOF_MODES = {
1616
+ light: { bg: '#ffffff', fg: '#111111', label: 'light' },
1617
+ dark: { bg: '#111111', fg: '#f5f5f5', label: 'dark' },
1618
+ flatten: { bg: '#ffffff', fg: '#000000', label: 'single-color flatten' },
1619
+ } as const;
1620
+
1621
+ export type DrawProofMode = keyof typeof DRAW_PROOF_MODES;
1622
+
1623
+ /**
1624
+ * Render a single mark across the verification ladder. `mark` is the inline SVG
1625
+ * (the engine's `toJsx` output, dropped in as JSX). Each mode becomes one
1626
+ * labeled DCArtboard (a `--all-screens` target) showing the mark at every size,
1627
+ * so the proof PNGs are `proof-<mode>.png`.
1628
+ */
1629
+ export function DrawProof({
1630
+ mark,
1631
+ name = 'mark',
1632
+ sizes = [16, 24, 48, 256],
1633
+ modes = ['light', 'dark', 'flatten'],
1634
+ }: {
1635
+ mark: ReactNode;
1636
+ name?: string;
1637
+ sizes?: number[];
1638
+ modes?: DrawProofMode[];
1639
+ }) {
1640
+ const maxSize = Math.max(...sizes, 64);
1641
+ const cellGap = 32;
1642
+ const padding = 32;
1643
+ const boardWidth =
1644
+ padding * 2 + sizes.reduce((acc, s) => acc + Math.max(s, 56), 0) + cellGap * (sizes.length - 1);
1645
+ const boardHeight = padding * 2 + maxSize + 28;
1646
+
1647
+ return (
1648
+ <DesignCanvas>
1649
+ <style>{'.dp-cell svg{display:block;width:100%;height:100%}'}</style>
1650
+ {modes.map((mode) => {
1651
+ const m = DRAW_PROOF_MODES[mode];
1652
+ return (
1653
+ <DCArtboard
1654
+ key={mode}
1655
+ id={`proof-${mode}`}
1656
+ label={`${name} · ${m.label}`}
1657
+ width={boardWidth}
1658
+ height={boardHeight}
1659
+ >
1660
+ <div
1661
+ style={{
1662
+ display: 'flex',
1663
+ alignItems: 'flex-end',
1664
+ gap: cellGap,
1665
+ padding,
1666
+ minHeight: boardHeight,
1667
+ background: m.bg,
1668
+ color: m.fg,
1669
+ boxSizing: 'border-box',
1670
+ }}
1671
+ >
1672
+ {sizes.map((s) => (
1673
+ <figure
1674
+ key={s}
1675
+ style={{
1676
+ margin: 0,
1677
+ display: 'flex',
1678
+ flexDirection: 'column',
1679
+ alignItems: 'center',
1680
+ gap: 8,
1681
+ }}
1682
+ >
1683
+ <span
1684
+ className="dp-cell"
1685
+ style={{ display: 'inline-block', width: s, height: s, color: m.fg }}
1686
+ >
1687
+ {mark}
1688
+ </span>
1689
+ <figcaption
1690
+ style={{
1691
+ fontFamily: 'var(--font-mono, ui-monospace, monospace)',
1692
+ fontSize: 11,
1693
+ color: m.fg,
1694
+ opacity: 0.65,
1695
+ }}
1696
+ >
1697
+ {s}px
1698
+ </figcaption>
1699
+ </figure>
1700
+ ))}
1701
+ </div>
1702
+ </DCArtboard>
1703
+ );
1704
+ })}
1705
+ </DesignCanvas>
1706
+ );
1707
+ }
1708
+ DrawProof.displayName = 'DrawProof';
1709
+
1600
1710
  // ─────────────────────────────────────────────────────────────────────────────
1601
1711
  // SnapGuideOverlay (Phase 4.2) — renders 1 px guide lines while a drag is in
1602
1712
  // flight. Mounted by canvas-shell as a chrome layer outside `.dc-world`, so
@@ -128,6 +128,12 @@
128
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
129
  "default": "oklch"
130
130
  },
131
+ "aestheticAmbition": {
132
+ "type": "string",
133
+ "enum": ["restrained", "confident", "expressive", "maximalist"],
134
+ "description": "How chromatically/decoratively expressive the DS is. Inferred by ux-research-agent from brand character during /design:setup-ds (NOT a picker), then anchors the structural knobs (accentStrategy, shadow/decor) and sets the default opt_out_scope for canvases under this DS (restrained|confident → palette, expressive → aesthetic, maximalist → full). Default 'restrained' is the legacy/no-bootstrap fallback only — a bootstrapped DS always writes the inferred value. Per DDR-073.",
135
+ "default": "restrained"
136
+ },
131
137
  "designSystems": {
132
138
  "type": "array",
133
139
  "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. The System view's DS picker iterates this array.",
@@ -197,6 +203,10 @@
197
203
  "adopt": {
198
204
  "type": "boolean",
199
205
  "description": "When true, the first sync after boot pushes local disk state up to the hub unconditionally (use case: bootstrapping the hub from a populated repo, or hub-was-wiped recovery). Cleared after first successful adopt."
206
+ },
207
+ "syncTsx": {
208
+ "type": "boolean",
209
+ "description": "DDR-072 — project-level TSX sync opt-in. When true, ALL .tsx canvases sync to this hub without a per-canvas .meta.json \"syncable\": true. A per-canvas \"syncable\": false still excludes individual canvases (the sidecar always wins). Inert unless the cross-origin sandbox is active (MAUDE_CANVAS_ORIGIN_SPLIT != 0) — the DDR-060 Lock-2 coupling is preserved. Broadens the WebRTC/self-nav exfil residual to every synced canvas, so only enable for hubs you operate or fully trust; the dev-server prints a loud boot banner against non-loopback hubs."
200
210
  }
201
211
  },
202
212
  "additionalProperties": false
@@ -29,6 +29,15 @@ export interface LinkedHub {
29
29
  url: string;
30
30
  linkedAt: number;
31
31
  adopt?: boolean;
32
+ /**
33
+ * DDR-072 — project-level TSX sync opt-in. When true, EVERY `.tsx` canvas
34
+ * syncs to this hub without a per-canvas `.meta.json "syncable": true`. A
35
+ * per-canvas `"syncable": false` still excludes an individual canvas (the
36
+ * sidecar always wins). Inert unless the cross-origin sandbox is active
37
+ * (`MAUDE_CANVAS_ORIGIN_SPLIT != 0`) — the Lock-2 coupling from DDR-060 is
38
+ * preserved. Only for hubs you operate or fully trust.
39
+ */
40
+ syncTsx?: boolean;
32
41
  }
33
42
 
34
43
  export interface DevServerConfig {