@1agh/maude 0.58.3 → 0.60.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/apps/studio/annotations-layer.tsx +49 -15
- package/apps/studio/bin/_import-asset.mjs +18 -0
- package/apps/studio/bin/_import-figma.mjs +1180 -242
- package/apps/studio/bin/_perf-probe-safari.mjs +332 -0
- package/apps/studio/bin/_perf-probe.mjs +228 -0
- package/apps/studio/bin/_perf-shared.mjs +345 -0
- package/apps/studio/bin/_video-playwright.mjs +17 -4
- package/apps/studio/bin/import-figma.sh +10 -1
- package/apps/studio/bin/perf.sh +228 -0
- package/apps/studio/bin/smoke.sh +49 -5
- package/apps/studio/canvas-lib.tsx +148 -6
- package/apps/studio/client/app.jsx +152 -37
- package/apps/studio/client/panels/SyncPanel.jsx +320 -0
- package/apps/studio/client/panels/TimelinePanel.jsx +29 -1
- package/apps/studio/client/panels/timeline-comp-target.js +101 -0
- package/apps/studio/client/styles/3-shell-maude.css +40 -0
- package/apps/studio/client/styles/4-components.css +4 -4
- package/apps/studio/context.ts +4 -0
- package/apps/studio/dist/client.bundle.js +772 -772
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/exporters/video-encode-lib.ts +8 -5
- package/apps/studio/exporters/video.ts +10 -0
- package/apps/studio/figma/assets.test.ts +92 -0
- package/apps/studio/figma/assets.ts +63 -9
- package/apps/studio/figma/codegen-client.test.ts +276 -0
- package/apps/studio/figma/codegen-client.ts +509 -0
- package/apps/studio/figma/codegen-fonts.test.ts +103 -0
- package/apps/studio/figma/codegen-fonts.ts +195 -0
- package/apps/studio/figma/codegen-values.test.ts +179 -0
- package/apps/studio/figma/codegen-values.ts +270 -0
- package/apps/studio/figma/endpoints.ts +73 -0
- package/apps/studio/figma/fig-decode.test.ts +788 -0
- package/apps/studio/figma/fig-decode.ts +839 -0
- package/apps/studio/figma/fig-differential.test.ts +182 -0
- package/apps/studio/figma/fig-kiwi.ts +410 -0
- package/apps/studio/figma/fig-translator.test.ts +192 -0
- package/apps/studio/figma/fig-vector.test.ts +113 -0
- package/apps/studio/figma/fig-vector.ts +145 -0
- package/apps/studio/figma/fig-zip.ts +270 -0
- package/apps/studio/figma/from-codegen.test.ts +408 -0
- package/apps/studio/figma/from-codegen.ts +1103 -0
- package/apps/studio/figma/sanitize.test.ts +69 -0
- package/apps/studio/figma/sanitize.ts +146 -47
- package/apps/studio/figma/tailwind-map.test.ts +142 -0
- package/apps/studio/figma/tailwind-map.ts +545 -0
- package/apps/studio/figma/to-artboard.ts +41 -1
- package/apps/studio/figma/to-render.ts +25 -3
- package/apps/studio/figma/types.ts +6 -1
- package/apps/studio/http.ts +94 -0
- package/apps/studio/sync/asset-push-worker.ts +84 -0
- package/apps/studio/sync/asset-push.ts +441 -39
- package/apps/studio/sync/asset-sweep.ts +262 -0
- package/apps/studio/sync/connection-state.ts +71 -3
- package/apps/studio/sync/index.ts +39 -6
- package/apps/studio/sync/presentation.ts +21 -0
- package/apps/studio/sync/status.ts +18 -0
- package/apps/studio/sync/supervisor.ts +20 -0
- package/apps/studio/test/canvas-origin-gate.test.ts +13 -0
- package/apps/studio/test/figma-explode.test.ts +438 -0
- package/apps/studio/test/fixtures/perf-canvas.mjs +201 -0
- package/apps/studio/test/import-figma.test.ts +192 -4
- package/apps/studio/test/sync-asset-push-worker.test.ts +183 -0
- package/apps/studio/test/sync-asset-push.test.ts +639 -47
- package/apps/studio/test/sync-asset-sweep.test.ts +243 -0
- package/apps/studio/test/sync-connection-state.test.ts +66 -0
- package/apps/studio/test/sync-panel-surface.test.ts +123 -0
- package/apps/studio/test/sync-resync-routes.test.ts +125 -0
- package/apps/studio/test/sync-status.test.ts +28 -0
- package/apps/studio/test/sync-supervisor.test.ts +46 -0
- package/apps/studio/test/timeline-comp-target.test.ts +139 -0
- package/apps/studio/test/video-comp.test.ts +81 -1
- package/apps/studio/test/video-encode-lib.test.ts +63 -0
- package/apps/studio/use-artboard-drag.tsx +37 -3
- package/apps/studio/video-comp.tsx +51 -0
- package/apps/studio/whats-new.json +87 -0
- package/cli/commands/design.mjs +7 -0
- package/cli/commands/kg.mjs +8 -1
- package/cli/commands/kg.test.mjs +24 -0
- package/cli/lib/figma-codegen-reachability.test.mjs +104 -0
- package/cli/lib/figma-import-controls.test.mjs +70 -0
- package/package.json +8 -8
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// DDR-219 D2 regression guard — the codegen channel is a CONTROL, not an intent.
|
|
2
|
+
//
|
|
3
|
+
// The first draft of DDR-219 routed Figma Dev Mode codegen through the REMOTE
|
|
4
|
+
// MCP (`https://mcp.figma.com/mcp`) and treated our own code's inability to
|
|
5
|
+
// reach it as the safety property. The design-stage review took that apart:
|
|
6
|
+
//
|
|
7
|
+
// - the remote catalog "allowlist" is enforced on a self-reported OAuth
|
|
8
|
+
// `client_name`, and a public bypass for it exists — so it is a waitlist,
|
|
9
|
+
// not a boundary;
|
|
10
|
+
// - the LOCAL Dev Mode server (`http://127.0.0.1:3845/mcp`) takes plain
|
|
11
|
+
// loopback HTTP with NO credential and no catalog gate at all, so nothing
|
|
12
|
+
// but intent stands between a future contributor and `fetch(...)` inside
|
|
13
|
+
// `apps/studio/figma/`;
|
|
14
|
+
// - DDR-185's `curl-local` *permits* that endpoint by construction (its rule
|
|
15
|
+
// is "every resolved address is loopback", and 127.0.0.1 is), and an ACP
|
|
16
|
+
// session auto-approves `Bash(maude:*)` — so the one egress control the
|
|
17
|
+
// session trusts is, for this specific service, an allow rule.
|
|
18
|
+
//
|
|
19
|
+
// Reaching the remote endpoint from product code would put a third-party model's
|
|
20
|
+
// output into an ingestion path that DDR-216 D1 says is deterministic end to
|
|
21
|
+
// end. Reaching the local one from anywhere except the single designated client
|
|
22
|
+
// would make codegen a bulk ingestion route by accident — DDR-219 D1's table
|
|
23
|
+
// silently false, its fallback never firing, and nothing in CI noticing.
|
|
24
|
+
//
|
|
25
|
+
// So both halves are asserted here rather than described in a decision record.
|
|
26
|
+
|
|
27
|
+
import assert from 'node:assert/strict';
|
|
28
|
+
import { execSync } from 'node:child_process';
|
|
29
|
+
import { test } from 'node:test';
|
|
30
|
+
|
|
31
|
+
/** The ONE module allowed to name the local Dev Mode endpoint (DDR-219 D2). */
|
|
32
|
+
const CODEGEN_CLIENT = 'apps/studio/figma/codegen-client.ts';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Tests under the Figma lane may name it too, and the widening is narrow on
|
|
36
|
+
* purpose. `codegen-client.test.ts` asserts the endpoint constant — that IS the
|
|
37
|
+
* control ("the URL is never influenced by input"), and a guard that forbids
|
|
38
|
+
* asserting the thing it guards would push the assertion out of the repo.
|
|
39
|
+
* `from-codegen.test.ts` carries a captured response fixture whose asset
|
|
40
|
+
* constants are loopback URLs, and the property under test is that the converter
|
|
41
|
+
* DISCARDS them — which can only be asserted against a fixture that has them.
|
|
42
|
+
*
|
|
43
|
+
* Neither executes anything against the port. What stays absolutely banned is a
|
|
44
|
+
* second non-test module reaching it, which is the shape that would make codegen
|
|
45
|
+
* a bulk ingestion route by accident.
|
|
46
|
+
*/
|
|
47
|
+
const TEST_FILE_RE = /^apps\/studio\/figma\/[a-z-]+\.test\.ts$/;
|
|
48
|
+
|
|
49
|
+
/** Trees that ship or execute. Prose (`.ai/`, `docs/`, `site/`) may name both. */
|
|
50
|
+
const RUNTIME_TREES = ['apps/studio', 'cli', 'plugins'];
|
|
51
|
+
|
|
52
|
+
/** Source only. `node_modules` vendors binaries that contain arbitrary strings
|
|
53
|
+
* (the bundled agent-sdk `claude` matched `mcp.figma.com` on the first run),
|
|
54
|
+
* and `dist/` is build output, not a code path anyone edits. `-I` drops binary
|
|
55
|
+
* files outright so a vendored blob can never turn this guard into noise —
|
|
56
|
+
* a guard that cries wolf gets deleted, which would be worse than not having it. */
|
|
57
|
+
function grep(pattern, trees) {
|
|
58
|
+
// `|| true` because grep exits 1 on no-match, which is the passing case.
|
|
59
|
+
const out = execSync(
|
|
60
|
+
`grep -rlIE --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git ${JSON.stringify(pattern)} ${trees.join(' ')} 2>/dev/null || true`,
|
|
61
|
+
{ encoding: 'utf8', cwd: new URL('../../', import.meta.url).pathname }
|
|
62
|
+
);
|
|
63
|
+
return out
|
|
64
|
+
.trim()
|
|
65
|
+
.split('\n')
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.filter((f) => !f.endsWith('figma-codegen-reachability.test.mjs'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
test('no runtime code names the REMOTE Figma MCP endpoint', () => {
|
|
71
|
+
const offenders = grep('mcp\\.figma\\.com', RUNTIME_TREES);
|
|
72
|
+
assert.deepEqual(
|
|
73
|
+
offenders,
|
|
74
|
+
[],
|
|
75
|
+
`These files name the remote Figma MCP endpoint. It is banned from every runtime code path (DDR-219 D2, and \`feature-figma-import\` § Out of scope before it): it is reachable only by a catalog-listed agent client, so any use here means a model is in the ingestion path. Codegen goes over the LOCAL server, with apps/studio as the client:\n ${offenders.join('\n ')}`
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('only the designated client names the LOCAL Dev Mode endpoint', () => {
|
|
80
|
+
// Match the endpoint shape, not a bare `3845`, which could legitimately occur
|
|
81
|
+
// as a byte count or a hash fragment.
|
|
82
|
+
const offenders = grep('(localhost|127\\.0\\.0\\.1|:)3845', RUNTIME_TREES).filter(
|
|
83
|
+
(f) => f !== CODEGEN_CLIENT && !TEST_FILE_RE.test(f)
|
|
84
|
+
);
|
|
85
|
+
assert.deepEqual(
|
|
86
|
+
offenders,
|
|
87
|
+
[],
|
|
88
|
+
`Only ${CODEGEN_CLIENT} may reach the local Figma Dev Mode MCP server (DDR-219 D2). A second caller makes codegen an ingestion route by accident — and the local server is UNAUTHENTICATED loopback, so there is no credential step to notice it. If codegen is genuinely needed elsewhere, route it through the client:\n ${offenders.join('\n ')}`
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('no canvas-reachable codegen route exists', () => {
|
|
93
|
+
// A canvas-origin codegen route would hand the untrusted iframe (DDR-054) a
|
|
94
|
+
// primitive that reads the user's open Figma document. It belongs in NEITHER
|
|
95
|
+
// allowlist — the same standing rule DDR-088 sets for every privileged route.
|
|
96
|
+
const offenders = grep('CANVAS_SAFE_API[^\\n]*codegen|codegen[^\\n]*CANVAS_SAFE_API', [
|
|
97
|
+
'apps/studio',
|
|
98
|
+
]);
|
|
99
|
+
assert.deepEqual(
|
|
100
|
+
offenders,
|
|
101
|
+
[],
|
|
102
|
+
`A codegen route appears near CANVAS_SAFE_API. Codegen is privileged-origin only (DDR-219 D2):\n ${offenders.join('\n ')}`
|
|
103
|
+
);
|
|
104
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// DDR-216 D7 + D8 existence guard — the anti-recurrence test.
|
|
2
|
+
//
|
|
3
|
+
// This repo has now written the same mistake into three consecutive decision
|
|
4
|
+
// records: a DDR asserts a control, nobody builds it, and the NEXT DDR cites it
|
|
5
|
+
// as an existing mitigation. DDR-216 D7 promised `/design:edit` a pre-flight
|
|
6
|
+
// untrusted-content banner on an imported canvas; D8 promised Pass A.10 run
|
|
7
|
+
// promoted-to-blocker for `imported-figma`. Both were "shipped" in prose. When
|
|
8
|
+
// DDR-219 leaned on the A.10 promotion as a control that was "still enforced,
|
|
9
|
+
// unchanged", the design-stage review ran one command —
|
|
10
|
+
//
|
|
11
|
+
// grep -rn "imported-figma" plugins/ → zero matches
|
|
12
|
+
//
|
|
13
|
+
// — and the whole chain came apart. A decision record cannot assert its way to a
|
|
14
|
+
// control. This test is the cheapest possible thing that would have caught it,
|
|
15
|
+
// and it is deliberately dumb: it checks the string is REACHABLE from the files
|
|
16
|
+
// that must act on it, not that the behaviour is correct.
|
|
17
|
+
|
|
18
|
+
import assert from 'node:assert/strict';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { test } from 'node:test';
|
|
21
|
+
|
|
22
|
+
const root = new URL('../../', import.meta.url).pathname;
|
|
23
|
+
const read = (p) => readFileSync(root + p, 'utf8');
|
|
24
|
+
|
|
25
|
+
test('DDR-216 D7 — /design:edit banners an imported canvas before reading it', () => {
|
|
26
|
+
const edit = read('plugins/design/commands/edit.md');
|
|
27
|
+
assert.match(
|
|
28
|
+
edit,
|
|
29
|
+
/imported-figma/,
|
|
30
|
+
"/design:edit must detect an `imported-figma` canvas at pre-flight (DDR-216 D7). Without it, several hundred third-party strings enter a tool-holding agent's context with nothing saying what they are."
|
|
31
|
+
);
|
|
32
|
+
assert.match(
|
|
33
|
+
edit,
|
|
34
|
+
/untrusted content, never instructions/i,
|
|
35
|
+
"The banner must say the content is untrusted and is never instructions — that exact framing is the control (DDR-216 D7, DDR-085's whiteboard trust model)."
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('DDR-216 D8 — Pass A.10 is promoted to blocker on an imported canvas', () => {
|
|
40
|
+
const keeper = read('plugins/design/agents/design-system-keeper.md');
|
|
41
|
+
const a10 = keeper.slice(keeper.indexOf('## Pass A.10'));
|
|
42
|
+
assert.notEqual(a10, '', 'Pass A.10 section is missing from design-system-keeper.');
|
|
43
|
+
assert.match(
|
|
44
|
+
a10,
|
|
45
|
+
/imported-figma/,
|
|
46
|
+
'Pass A.10 must special-case `imported-figma` (DDR-216 D8). As a plain warning it is not a gate.'
|
|
47
|
+
);
|
|
48
|
+
assert.match(
|
|
49
|
+
a10,
|
|
50
|
+
/blocker/i,
|
|
51
|
+
'A.10 findings must be blockers on an imported canvas — the mass-drift stacking ladder exists to avoid nagging a human over one deliberate overlay, and that reasoning does not survive a machine-authored file.'
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('A.10 does not let an imported canvas skip via its declared kind', () => {
|
|
56
|
+
// The skip condition is "no kind=\"web\" artboard" — and on an import the
|
|
57
|
+
// TRANSLATOR picks the kind, so the gate would be satisfied by the very code
|
|
58
|
+
// it audits. DDR-216 D8's Round-1 correction named this; the exception closes it.
|
|
59
|
+
const keeper = read('plugins/design/agents/design-system-keeper.md');
|
|
60
|
+
const a10 = keeper.slice(keeper.indexOf('## Pass A.10'), keeper.indexOf('## Pass B'));
|
|
61
|
+
const skipIdx = a10.indexOf('Skip entirely');
|
|
62
|
+
assert.ok(skipIdx > -1, 'A.10 skip condition not found — did the section change shape?');
|
|
63
|
+
assert.match(
|
|
64
|
+
a10.slice(skipIdx, skipIdx + 1200),
|
|
65
|
+
// `[\s\S]` rather than `[^]` — same "any char incl. newline" meaning, but
|
|
66
|
+
// `[^]` is a negated EMPTY class, which biome flags and which reads as a typo.
|
|
67
|
+
/EXCEPTION[\s\S]*imported-figma/,
|
|
68
|
+
'The skip condition must carry an explicit imported-canvas exception, stated next to the skip itself where a reader will actually meet it.'
|
|
69
|
+
);
|
|
70
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.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": {
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
"test:e2e:desktop:parity:cloud": "pnpm --filter @maude/desktop-e2e e2e:parity:cloud"
|
|
55
55
|
},
|
|
56
56
|
"optionalDependencies": {
|
|
57
|
-
"@1agh/maude-darwin-arm64": "0.
|
|
58
|
-
"@1agh/maude-darwin-x64": "0.
|
|
59
|
-
"@1agh/maude-linux-arm64": "0.
|
|
60
|
-
"@1agh/maude-linux-arm64-musl": "0.
|
|
61
|
-
"@1agh/maude-linux-x64": "0.
|
|
62
|
-
"@1agh/maude-linux-x64-musl": "0.
|
|
63
|
-
"@1agh/maude-win32-x64": "0.
|
|
57
|
+
"@1agh/maude-darwin-arm64": "0.60.0",
|
|
58
|
+
"@1agh/maude-darwin-x64": "0.60.0",
|
|
59
|
+
"@1agh/maude-linux-arm64": "0.60.0",
|
|
60
|
+
"@1agh/maude-linux-arm64-musl": "0.60.0",
|
|
61
|
+
"@1agh/maude-linux-x64": "0.60.0",
|
|
62
|
+
"@1agh/maude-linux-x64-musl": "0.60.0",
|
|
63
|
+
"@1agh/maude-win32-x64": "0.60.0"
|
|
64
64
|
},
|
|
65
65
|
"files": [
|
|
66
66
|
"cli",
|