@1agh/maude 0.16.0 → 0.17.2
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/README.md +4 -0
- package/cli/cli-wrapper.cjs +0 -0
- package/cli/commands/design.mjs +264 -16
- package/package.json +12 -18
- package/plugins/design/dev-server/annotations-context-toolbar.tsx +8 -8
- package/plugins/design/dev-server/annotations-layer.tsx +8 -10
- package/plugins/design/dev-server/api.ts +41 -0
- package/plugins/design/dev-server/bin/_enumerate-artboards-playwright.mjs +40 -0
- package/plugins/design/dev-server/bin/_html-playwright.mjs +129 -0
- package/plugins/design/dev-server/bin/_pdf-playwright.mjs +105 -0
- package/plugins/design/dev-server/bin/_png-playwright.mjs +143 -0
- package/plugins/design/dev-server/bin/_pptx-playwright.mjs +98 -0
- package/plugins/design/dev-server/bin/_svg-playwright.mjs +141 -0
- package/plugins/design/dev-server/build.ts +118 -6
- package/plugins/design/dev-server/canvas-lib.tsx +12 -13
- package/plugins/design/dev-server/canvas-pipeline.ts +5 -0
- package/plugins/design/dev-server/canvas-shell.tsx +32 -4
- package/plugins/design/dev-server/client/app.jsx +18 -1
- package/plugins/design/dev-server/context-menu.tsx +36 -9
- package/plugins/design/dev-server/dist/client.bundle.js +11 -3
- package/plugins/design/dev-server/export-dialog.tsx +401 -0
- package/plugins/design/dev-server/exporters/_browser-bundles.ts +89 -0
- package/plugins/design/dev-server/exporters/canva-handoff-prompt.ts +74 -0
- package/plugins/design/dev-server/exporters/canva.ts +126 -0
- package/plugins/design/dev-server/exporters/html.ts +103 -0
- package/plugins/design/dev-server/exporters/index.ts +135 -0
- package/plugins/design/dev-server/exporters/pdf.ts +109 -0
- package/plugins/design/dev-server/exporters/png.ts +136 -0
- package/plugins/design/dev-server/exporters/pptx.ts +263 -0
- package/plugins/design/dev-server/exporters/scope.ts +196 -0
- package/plugins/design/dev-server/exporters/svg.ts +122 -0
- package/plugins/design/dev-server/exporters/zip.ts +109 -0
- package/plugins/design/dev-server/http.ts +80 -0
- package/plugins/design/dev-server/inspect.ts +1 -1
- package/plugins/design/dev-server/server.mjs +1 -1
- package/plugins/design/dev-server/test/compile-entry.test.ts +134 -0
- package/plugins/design/dev-server/test/exporters/canva.test.ts +64 -0
- package/plugins/design/dev-server/test/exporters/endpoint.test.ts +121 -0
- package/plugins/design/dev-server/test/exporters/history.test.ts +79 -0
- package/plugins/design/dev-server/test/exporters/html.test.ts +26 -0
- package/plugins/design/dev-server/test/exporters/pdf.test.ts +53 -0
- package/plugins/design/dev-server/test/exporters/png.test.ts +32 -0
- package/plugins/design/dev-server/test/exporters/pptx.test.ts +31 -0
- package/plugins/design/dev-server/test/exporters/scope.test.ts +0 -0
- package/plugins/design/dev-server/test/exporters/svg.test.ts +29 -0
- package/plugins/design/dev-server/test/exporters/zip.test.ts +105 -0
- package/plugins/design/dev-server/tool-palette.tsx +34 -16
- package/plugins/design/templates/_shell.html +33 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Phase 6.5 T4 — SVG adapter contract tests.
|
|
2
|
+
//
|
|
3
|
+
// Real Playwright walk is integration-shape (lands as scenario). Here we
|
|
4
|
+
// cover empty-input + file-tree-rejection.
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
|
+
|
|
8
|
+
import { run } from '../../exporters/svg.ts';
|
|
9
|
+
|
|
10
|
+
const CTX = {
|
|
11
|
+
designRoot: '/tmp/.design',
|
|
12
|
+
repoRoot: '/tmp',
|
|
13
|
+
serverOrigin: 'http://localhost:0',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe('svg adapter — contract', () => {
|
|
17
|
+
test('empty targets → zero-byte SVG placeholder', async () => {
|
|
18
|
+
const r = await run([], {}, CTX);
|
|
19
|
+
expect(r.contentType).toBe('image/svg+xml');
|
|
20
|
+
expect(r.body.byteLength).toBe(0);
|
|
21
|
+
expect(r.filename.endsWith('.svg')).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('file-tree targets → throws', async () => {
|
|
25
|
+
await expect(run([{ kind: 'file-tree', paths: ['ui/Home.tsx'] }], {}, CTX)).rejects.toThrow(
|
|
26
|
+
/element targets/i
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Phase 6.5 T7 — project-raw ZIP adapter tests.
|
|
2
|
+
//
|
|
3
|
+
// Real walk + bundle against a sandboxed designRoot, then unzip the result
|
|
4
|
+
// and diff against expected contents.
|
|
5
|
+
|
|
6
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from 'bun:test';
|
|
11
|
+
import JSZip from 'jszip';
|
|
12
|
+
|
|
13
|
+
import { resolveScope } from '../../exporters/scope.ts';
|
|
14
|
+
import { run } from '../../exporters/zip.ts';
|
|
15
|
+
|
|
16
|
+
function setupTree(): { root: string; designRoot: string } {
|
|
17
|
+
const root = mkdtempSync(join(tmpdir(), 'zip-adapter-'));
|
|
18
|
+
const designRoot = join(root, '.design');
|
|
19
|
+
mkdirSync(join(designRoot, 'ui'), { recursive: true });
|
|
20
|
+
mkdirSync(join(designRoot, 'system', 'project'), { recursive: true });
|
|
21
|
+
mkdirSync(join(designRoot, '_history', 'old'), { recursive: true });
|
|
22
|
+
writeFileSync(join(designRoot, 'config.json'), '{"name":"x"}');
|
|
23
|
+
writeFileSync(join(designRoot, 'README.md'), '# proj');
|
|
24
|
+
writeFileSync(join(designRoot, 'ui', 'Home.tsx'), 'export default ()=>null');
|
|
25
|
+
writeFileSync(join(designRoot, 'system', 'project', 'README.md'), '# ds');
|
|
26
|
+
writeFileSync(join(designRoot, '_history', 'old', 'snap.tsx'), '// snap');
|
|
27
|
+
writeFileSync(join(designRoot, '.DS_Store'), ' ');
|
|
28
|
+
return { root, designRoot };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('zip adapter — project-raw', () => {
|
|
32
|
+
test('bundles designRoot and excludes runtime artefacts by default', async () => {
|
|
33
|
+
const { root, designRoot } = setupTree();
|
|
34
|
+
const targets = await resolveScope({
|
|
35
|
+
scope: 'project-raw',
|
|
36
|
+
activeJson: { active: null, selected: null },
|
|
37
|
+
designRoot,
|
|
38
|
+
repoRoot: root,
|
|
39
|
+
});
|
|
40
|
+
const r = await run(targets, {}, { designRoot, repoRoot: root, serverOrigin: '' });
|
|
41
|
+
expect(r.contentType).toBe('application/zip');
|
|
42
|
+
expect(r.body.byteLength).toBeGreaterThan(0);
|
|
43
|
+
|
|
44
|
+
const unzipped = await JSZip.loadAsync(r.body);
|
|
45
|
+
const names = Object.keys(unzipped.files).sort();
|
|
46
|
+
expect(names).toContain('config.json');
|
|
47
|
+
expect(names).toContain('README.md');
|
|
48
|
+
expect(names).toContain('ui/Home.tsx');
|
|
49
|
+
expect(names).toContain('system/project/README.md');
|
|
50
|
+
expect(names).not.toContain('.DS_Store');
|
|
51
|
+
expect(names.every((n) => !n.startsWith('_history/'))).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('options.exclude prunes additional paths', async () => {
|
|
55
|
+
const { root, designRoot } = setupTree();
|
|
56
|
+
const targets = await resolveScope({
|
|
57
|
+
scope: 'project-raw',
|
|
58
|
+
activeJson: { active: null, selected: null },
|
|
59
|
+
designRoot,
|
|
60
|
+
repoRoot: root,
|
|
61
|
+
});
|
|
62
|
+
const r = await run(
|
|
63
|
+
targets,
|
|
64
|
+
{ exclude: ['ui/**'] },
|
|
65
|
+
{ designRoot, repoRoot: root, serverOrigin: '' }
|
|
66
|
+
);
|
|
67
|
+
const unzipped = await JSZip.loadAsync(r.body);
|
|
68
|
+
const names = Object.keys(unzipped.files);
|
|
69
|
+
expect(names.every((n) => !n.startsWith('ui/'))).toBe(true);
|
|
70
|
+
expect(names).toContain('system/project/README.md');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('options.include narrows to a single subtree', async () => {
|
|
74
|
+
const { root, designRoot } = setupTree();
|
|
75
|
+
const targets = await resolveScope({
|
|
76
|
+
scope: 'project-raw',
|
|
77
|
+
activeJson: { active: null, selected: null },
|
|
78
|
+
designRoot,
|
|
79
|
+
repoRoot: root,
|
|
80
|
+
});
|
|
81
|
+
const r = await run(
|
|
82
|
+
targets,
|
|
83
|
+
{ include: ['system'] },
|
|
84
|
+
{ designRoot, repoRoot: root, serverOrigin: '' }
|
|
85
|
+
);
|
|
86
|
+
const unzipped = await JSZip.loadAsync(r.body);
|
|
87
|
+
const names = Object.keys(unzipped.files);
|
|
88
|
+
expect(names.every((n) => n.startsWith('system/'))).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('empty targets → zero-byte ZIP placeholder', async () => {
|
|
92
|
+
const r = await run([], {}, { designRoot: '/tmp/.design', repoRoot: '/tmp', serverOrigin: '' });
|
|
93
|
+
expect(r.body.byteLength).toBe(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('element targets → throws', async () => {
|
|
97
|
+
await expect(
|
|
98
|
+
run(
|
|
99
|
+
[{ kind: 'element', cssPath: '.x', canvasSlug: 'x', file: 'ui/x.tsx' }],
|
|
100
|
+
{},
|
|
101
|
+
{ designRoot: '/tmp/.design', repoRoot: '/tmp', serverOrigin: '' }
|
|
102
|
+
)
|
|
103
|
+
).rejects.toThrow(/file-tree targets/i);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -32,13 +32,13 @@ const PALETTE_CSS = `
|
|
|
32
32
|
transform: translateX(-50%);
|
|
33
33
|
display: flex;
|
|
34
34
|
align-items: stretch;
|
|
35
|
-
background: var(--bg-1, rgba(255,255,255,0.98));
|
|
36
|
-
border: 1px solid var(--u-
|
|
37
|
-
border-radius:
|
|
38
|
-
box-shadow:
|
|
39
|
-
font-family:
|
|
35
|
+
background: var(--u-bg-2, var(--bg-1, rgba(255,255,255,0.98)));
|
|
36
|
+
border: 1px solid var(--u-fg-0, #1c1917);
|
|
37
|
+
border-radius: 0;
|
|
38
|
+
box-shadow: 4px 4px 0 var(--u-fg-0, #1c1917);
|
|
39
|
+
font-family: var(--u-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
40
40
|
font-size: 12px;
|
|
41
|
-
color: var(--fg-0, #1a1a1a);
|
|
41
|
+
color: var(--u-fg-0, var(--fg-0, #1a1a1a));
|
|
42
42
|
z-index: 6;
|
|
43
43
|
user-select: none;
|
|
44
44
|
/* Intentionally NO overflow:hidden — the zoom popover (.dc-tp-popover) is
|
|
@@ -54,14 +54,14 @@ const PALETTE_CSS = `
|
|
|
54
54
|
}
|
|
55
55
|
.dc-tool-palette .dc-tp-sep {
|
|
56
56
|
width: 1px;
|
|
57
|
-
background: var(--u-border-
|
|
57
|
+
background: var(--u-border-subtle, rgba(0,0,0,0.08));
|
|
58
58
|
margin: 6px 0;
|
|
59
59
|
}
|
|
60
60
|
.dc-tool-palette button {
|
|
61
61
|
appearance: none;
|
|
62
62
|
background: transparent;
|
|
63
63
|
border: 0;
|
|
64
|
-
border-radius:
|
|
64
|
+
border-radius: 0;
|
|
65
65
|
padding: 0;
|
|
66
66
|
width: 32px;
|
|
67
67
|
height: 32px;
|
|
@@ -85,10 +85,10 @@ const PALETTE_CSS = `
|
|
|
85
85
|
min-width: 56px;
|
|
86
86
|
padding: 0 8px;
|
|
87
87
|
font-variant-numeric: tabular-nums;
|
|
88
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
88
|
+
font-family: var(--u-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
89
89
|
font-size: 11px;
|
|
90
90
|
letter-spacing: 0.04em;
|
|
91
|
-
border-radius:
|
|
91
|
+
border-radius: 0;
|
|
92
92
|
display: inline-flex;
|
|
93
93
|
align-items: center;
|
|
94
94
|
gap: 4px;
|
|
@@ -98,21 +98,22 @@ const PALETTE_CSS = `
|
|
|
98
98
|
position: absolute;
|
|
99
99
|
right: 4px;
|
|
100
100
|
bottom: 44px;
|
|
101
|
-
background: var(--bg-1, rgba(255,255,255,0.98));
|
|
102
|
-
border: 1px solid var(--u-
|
|
103
|
-
border-radius:
|
|
104
|
-
box-shadow:
|
|
101
|
+
background: var(--u-bg-2, var(--bg-1, rgba(255,255,255,0.98)));
|
|
102
|
+
border: 1px solid var(--u-fg-0, #1c1917);
|
|
103
|
+
border-radius: 0;
|
|
104
|
+
box-shadow: 4px 4px 0 var(--u-fg-0, #1c1917);
|
|
105
105
|
display: flex;
|
|
106
106
|
flex-direction: column;
|
|
107
107
|
padding: 4px;
|
|
108
108
|
min-width: 160px;
|
|
109
109
|
z-index: 7;
|
|
110
|
+
font-family: var(--u-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
110
111
|
}
|
|
111
112
|
.dc-tp-popover button {
|
|
112
113
|
appearance: none;
|
|
113
114
|
background: transparent;
|
|
114
115
|
border: 0;
|
|
115
|
-
border-radius:
|
|
116
|
+
border-radius: 0;
|
|
116
117
|
padding: 6px 10px;
|
|
117
118
|
text-align: left;
|
|
118
119
|
font: inherit;
|
|
@@ -121,7 +122,6 @@ const PALETTE_CSS = `
|
|
|
121
122
|
display: flex;
|
|
122
123
|
justify-content: space-between;
|
|
123
124
|
gap: 16px;
|
|
124
|
-
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
125
125
|
width: 100%;
|
|
126
126
|
}
|
|
127
127
|
.dc-tp-popover button:hover { background: rgba(0,0,0,0.04); }
|
|
@@ -201,6 +201,24 @@ export function ToolPalette() {
|
|
|
201
201
|
</div>
|
|
202
202
|
<div className="dc-tp-sep" />
|
|
203
203
|
<div className="dc-tp-group">
|
|
204
|
+
<button
|
|
205
|
+
type="button"
|
|
206
|
+
aria-label="Export (⌘E)"
|
|
207
|
+
title="Export (⌘E)"
|
|
208
|
+
onClick={() => {
|
|
209
|
+
// Phase 6.5 T9 — dispatch the same custom event the context-menu
|
|
210
|
+
// uses; the dialog provider opens with the default scope.
|
|
211
|
+
try {
|
|
212
|
+
window.dispatchEvent(new CustomEvent('maude:open-export', { detail: {} }));
|
|
213
|
+
} catch {
|
|
214
|
+
/* ignore — non-window environments */
|
|
215
|
+
}
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
<span aria-hidden="true" style={{ fontSize: 14, lineHeight: 1 }}>
|
|
219
|
+
⬇
|
|
220
|
+
</span>
|
|
221
|
+
</button>
|
|
204
222
|
<button
|
|
205
223
|
type="button"
|
|
206
224
|
aria-label={
|
|
@@ -43,6 +43,32 @@
|
|
|
43
43
|
}
|
|
44
44
|
#canvas-mount-error:empty { display: none; }
|
|
45
45
|
</style>
|
|
46
|
+
<!-- Phase 6.5 export — when the shell is loaded with `&hide-chrome=1`
|
|
47
|
+
(the exporters set this), suppress every dev-server overlay so the
|
|
48
|
+
capture only carries the artboard content. The runtime chrome —
|
|
49
|
+
tool palette, mini-world-map, comment pins, draw layer, snap guides,
|
|
50
|
+
selection halos, the artboard label button — is not part of the
|
|
51
|
+
design and would otherwise show up in PNG / PDF / SVG exports. -->
|
|
52
|
+
<style id="canvas-hide-chrome" media="not all">
|
|
53
|
+
.dc-tool-palette,
|
|
54
|
+
.dc-cv-halo,
|
|
55
|
+
.dc-cv-group-bbox,
|
|
56
|
+
.dc-mini-map,
|
|
57
|
+
.dc-mini-world-map,
|
|
58
|
+
.dc-world-map,
|
|
59
|
+
.dc-snap-guide,
|
|
60
|
+
.dc-annot-overlay,
|
|
61
|
+
.dc-annot-svg,
|
|
62
|
+
.dc-annot-chrome,
|
|
63
|
+
.dc-annot-ctx,
|
|
64
|
+
.cm-layer,
|
|
65
|
+
.cm-pin,
|
|
66
|
+
.cm-thread,
|
|
67
|
+
.cm-composer,
|
|
68
|
+
.dc-artboard-label,
|
|
69
|
+
[data-dc-overlay],
|
|
70
|
+
[data-mdcc-annotations] { display: none !important; }
|
|
71
|
+
</style>
|
|
46
72
|
<!-- Tokens + DS component CSS — loaded by the host app via the canvas's
|
|
47
73
|
.meta.json (set later via window.parent postMessage, OR by the dev-
|
|
48
74
|
server when it knows the active DS). For now we load both common
|
|
@@ -73,6 +99,13 @@
|
|
|
73
99
|
const tokensRel = params.get('tokens') || '';
|
|
74
100
|
const componentsRel = params.get('components') || '';
|
|
75
101
|
const layoutRel = params.get('layout') || '';
|
|
102
|
+
// Phase 6.5 — exporters pass ?hide-chrome=1 to flip the export-mode
|
|
103
|
+
// stylesheet from `media="not all"` to `media="all"`, hiding the
|
|
104
|
+
// dev-server overlays during capture. See `<style id="canvas-hide-chrome">`.
|
|
105
|
+
if (params.get('hide-chrome') === '1') {
|
|
106
|
+
const styleEl = document.getElementById('canvas-hide-chrome');
|
|
107
|
+
if (styleEl) styleEl.media = 'all';
|
|
108
|
+
}
|
|
76
109
|
|
|
77
110
|
function showError(msg) {
|
|
78
111
|
const el = document.getElementById('canvas-mount-error');
|