@1agh/maude 0.19.0 → 0.20.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/package.json +9 -9
- package/plugins/design/dev-server/annotations-context-toolbar.tsx +126 -44
- package/plugins/design/dev-server/annotations-layer.tsx +343 -112
- package/plugins/design/dev-server/api.ts +70 -17
- package/plugins/design/dev-server/artboard-marquee.tsx +174 -0
- package/plugins/design/dev-server/bin/asset-sweep.sh +180 -0
- package/plugins/design/dev-server/bin/visual-sanity.sh +221 -0
- package/plugins/design/dev-server/canvas-lib.tsx +691 -119
- package/plugins/design/dev-server/canvas-shell.tsx +818 -42
- package/plugins/design/dev-server/client/app.jsx +173 -52
- package/plugins/design/dev-server/client/hmr.mjs +28 -0
- package/plugins/design/dev-server/client/styles/1-tokens.css +15 -8
- package/plugins/design/dev-server/client/styles/4-components.css +32 -2
- package/plugins/design/dev-server/commands/annotation-strokes-command.ts +127 -0
- package/plugins/design/dev-server/commands/equal-spacing-command.ts +28 -0
- package/plugins/design/dev-server/commands/move-artboards-command.ts +158 -0
- package/plugins/design/dev-server/comments-overlay.tsx +12 -4
- package/plugins/design/dev-server/config.schema.json +31 -5
- package/plugins/design/dev-server/context-menu.tsx +3 -3
- package/plugins/design/dev-server/context.ts +37 -3
- package/plugins/design/dev-server/contextual-toolbar.tsx +241 -0
- package/plugins/design/dev-server/dist/client.bundle.js +212 -103
- package/plugins/design/dev-server/dist/runtime/motion.js +165 -0
- package/plugins/design/dev-server/dist/runtime/motion_react.js +409 -0
- package/plugins/design/dev-server/dist/styles.css +38 -2
- package/plugins/design/dev-server/equal-spacing-detector.ts +98 -0
- package/plugins/design/dev-server/equal-spacing-handles.tsx +289 -0
- package/plugins/design/dev-server/export-dialog.tsx +3 -3
- package/plugins/design/dev-server/handoff.ts +24 -0
- package/plugins/design/dev-server/http.ts +41 -2
- package/plugins/design/dev-server/input-router.tsx +74 -10
- package/plugins/design/dev-server/marquee-overlay.tsx +282 -0
- package/plugins/design/dev-server/runtime-bundle.ts +7 -0
- package/plugins/design/dev-server/server.mjs +84 -18
- package/plugins/design/dev-server/test/annotation-strokes-command.test.ts +149 -0
- package/plugins/design/dev-server/test/annotations-layer.test.ts +44 -0
- package/plugins/design/dev-server/test/canvas-lib-motion.test.ts +131 -0
- package/plugins/design/dev-server/test/context-resolve-tokens.test.ts +97 -0
- package/plugins/design/dev-server/test/equal-spacing-detector.test.ts +93 -0
- package/plugins/design/dev-server/test/input-router.test.ts +87 -1
- package/plugins/design/dev-server/test/marquee-overlay.test.ts +94 -0
- package/plugins/design/dev-server/test/move-artboards-command.test.ts +108 -0
- package/plugins/design/dev-server/test/snap-distance-pill.test.ts +68 -0
- package/plugins/design/dev-server/test/system-endpoint.test.ts +144 -0
- package/plugins/design/dev-server/test/undo-stack.test.ts +211 -0
- package/plugins/design/dev-server/test/use-cursor-modifiers.test.ts +59 -0
- package/plugins/design/dev-server/test/use-keyboard-discipline.test.ts +27 -0
- package/plugins/design/dev-server/test/use-undo-stack.test.tsx +193 -0
- package/plugins/design/dev-server/tool-palette.tsx +71 -15
- package/plugins/design/dev-server/undo-hud.tsx +95 -0
- package/plugins/design/dev-server/undo-stack.ts +240 -0
- package/plugins/design/dev-server/use-annotation-resize.tsx +295 -0
- package/plugins/design/dev-server/use-artboard-drag.tsx +6 -3
- package/plugins/design/dev-server/use-cursor-modifiers.tsx +122 -0
- package/plugins/design/dev-server/use-keyboard-discipline.tsx +125 -0
- package/plugins/design/dev-server/use-snap-guides.tsx +25 -1
- package/plugins/design/dev-server/use-tool-mode.tsx +31 -2
- package/plugins/design/dev-server/use-undo-stack.tsx +355 -0
- package/plugins/design/templates/_shell.html +17 -6
- package/plugins/design/templates/design-system-inspiration/SUB-AGENT-PROMPTS.md +245 -0
- package/plugins/design/templates/design-system-inspiration/_MAPPING.md +2 -2
- package/plugins/design/templates/design-system-inspiration/core/preview/_components.css.tpl +129 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +63 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/motion.css.tpl +106 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/motion.tsx.tpl +208 -0
- /package/plugins/design/templates/design-system-inspiration/core/preview/{motion.html → .archive/motion.html} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"start": "bun run plugins/design/dev-server/server.ts",
|
|
21
|
-
"dev": "bun run plugins/design/dev-server/server.ts --port
|
|
21
|
+
"dev": "bun run plugins/design/dev-server/server.ts --port 4555",
|
|
22
22
|
"dev:site": "pnpm --filter @maude/site dev --port 4398",
|
|
23
23
|
"build": "pnpm -r --if-present run build",
|
|
24
24
|
"build:binary": "bun run plugins/design/dev-server/build.ts --release",
|
|
@@ -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.20.0",
|
|
45
|
+
"@1agh/maude-darwin-x64": "0.20.0",
|
|
46
|
+
"@1agh/maude-linux-arm64": "0.20.0",
|
|
47
|
+
"@1agh/maude-linux-arm64-musl": "0.20.0",
|
|
48
|
+
"@1agh/maude-linux-x64": "0.20.0",
|
|
49
|
+
"@1agh/maude-linux-x64-musl": "0.20.0",
|
|
50
|
+
"@1agh/maude-win32-x64": "0.20.0"
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"cli",
|
|
@@ -25,9 +25,25 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
25
25
|
import { type Stroke, useStrokesStore } from './annotations-layer.tsx';
|
|
26
26
|
import { useAnnotationSelectionOptional } from './use-annotation-selection.tsx';
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// G6 — FigJam-style 11-color palette. Same colors for both stroke and fill
|
|
29
|
+
// so the swatch row identity stays consistent when the user flips the
|
|
30
|
+
// Stroke|Fill mode toggle (only the target attribute changes, not the
|
|
31
|
+
// visual vocabulary).
|
|
32
|
+
const PALETTE = [
|
|
33
|
+
'#1E1E1E', // black
|
|
34
|
+
'#757575', // gray
|
|
35
|
+
'#E03E3E', // red
|
|
36
|
+
'#F26B3F', // orange
|
|
37
|
+
'#F2C744', // yellow
|
|
38
|
+
'#4CAF50', // green
|
|
39
|
+
'#14B8A6', // teal
|
|
40
|
+
'#3B82F6', // blue
|
|
41
|
+
'#8B5CF6', // purple
|
|
42
|
+
'#EC4899', // pink
|
|
43
|
+
'#FFFFFF', // white
|
|
44
|
+
] as const;
|
|
29
45
|
|
|
30
|
-
const FILL_PALETTE =
|
|
46
|
+
const FILL_PALETTE = PALETTE;
|
|
31
47
|
|
|
32
48
|
const TOOLBAR_CSS = `
|
|
33
49
|
.dc-annot-ctx {
|
|
@@ -36,11 +52,11 @@ const TOOLBAR_CSS = `
|
|
|
36
52
|
display: flex;
|
|
37
53
|
align-items: center;
|
|
38
54
|
gap: 6px;
|
|
39
|
-
background: var(--u-bg-
|
|
55
|
+
background: var(--u-bg-0, var(--bg-0, rgba(255,255,255,0.98)));
|
|
40
56
|
border: 1px solid var(--u-fg-0, #1c1917);
|
|
41
|
-
border-radius:
|
|
57
|
+
border-radius: 8px;
|
|
42
58
|
padding: 6px 8px;
|
|
43
|
-
box-shadow:
|
|
59
|
+
box-shadow: 0 6px 24px color-mix(in oklab, var(--u-fg-0, #1c1917) 10%, transparent);
|
|
44
60
|
font-family: var(--u-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
45
61
|
font-size: 12px;
|
|
46
62
|
color: var(--u-fg-0, var(--fg-0, #1a1a1a));
|
|
@@ -94,6 +110,42 @@ const TOOLBAR_CSS = `
|
|
|
94
110
|
.dc-annot-ctx-btn:hover { background: rgba(0,0,0,0.04); }
|
|
95
111
|
.dc-annot-ctx-btn--danger { color: #b3271a; }
|
|
96
112
|
.dc-annot-ctx-btn--danger:hover { background: rgba(214,59,31,0.08); }
|
|
113
|
+
|
|
114
|
+
/* T30 / G_S1 — collapsed Stroke|Fill mode toggle. Shown only when the
|
|
115
|
+
selection supports both stroke + fill (rect / ellipse). When the toggle is
|
|
116
|
+
present the swatch row beneath shows ONE palette at a time. */
|
|
117
|
+
.dc-annot-ctx-mode {
|
|
118
|
+
display: inline-flex;
|
|
119
|
+
border-radius: 6px;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
border: 1px solid rgba(0,0,0,0.18);
|
|
122
|
+
}
|
|
123
|
+
.dc-annot-ctx-mode-btn {
|
|
124
|
+
appearance: none;
|
|
125
|
+
background: transparent;
|
|
126
|
+
border: 0;
|
|
127
|
+
padding: 3px 8px;
|
|
128
|
+
font: inherit;
|
|
129
|
+
color: inherit;
|
|
130
|
+
cursor: pointer;
|
|
131
|
+
font-size: 11px;
|
|
132
|
+
letter-spacing: 0.02em;
|
|
133
|
+
text-transform: uppercase;
|
|
134
|
+
line-height: 1;
|
|
135
|
+
}
|
|
136
|
+
.dc-annot-ctx-mode-btn[aria-pressed="true"] {
|
|
137
|
+
background: color-mix(in oklab, var(--accent, #d63b1f) 14%, transparent);
|
|
138
|
+
color: var(--accent, #d63b1f);
|
|
139
|
+
font-weight: 600;
|
|
140
|
+
}
|
|
141
|
+
.dc-annot-ctx-mode-btn:not([aria-pressed="true"]):hover {
|
|
142
|
+
background: rgba(0,0,0,0.04);
|
|
143
|
+
}
|
|
144
|
+
/* Trailing overflow Delete — quiet by default, expressive on hover.
|
|
145
|
+
Keyboard Backspace remains the primary delete affordance. */
|
|
146
|
+
.dc-annot-ctx-overflow {
|
|
147
|
+
margin-left: auto;
|
|
148
|
+
}
|
|
97
149
|
`.trim();
|
|
98
150
|
|
|
99
151
|
function ensureToolbarStyles(): void {
|
|
@@ -122,6 +174,8 @@ function unionRect(rects: DOMRect[]): { x: number; y: number; w: number; h: numb
|
|
|
122
174
|
return { x: xMin, y: yMin, w: xMax - xMin, h: yMax - yMin };
|
|
123
175
|
}
|
|
124
176
|
|
|
177
|
+
type SwatchMode = 'stroke' | 'fill';
|
|
178
|
+
|
|
125
179
|
export function AnnotationContextToolbar() {
|
|
126
180
|
ensureToolbarStyles();
|
|
127
181
|
const annotSel = useAnnotationSelectionOptional();
|
|
@@ -129,6 +183,10 @@ export function AnnotationContextToolbar() {
|
|
|
129
183
|
const ref = useRef<HTMLDivElement | null>(null);
|
|
130
184
|
const rafRef = useRef<number | null>(null);
|
|
131
185
|
const [, force] = useState({});
|
|
186
|
+
// T30 / G_S1 — collapsed Stroke|Fill toggle state. Defaults to 'stroke';
|
|
187
|
+
// auto-reverts to 'stroke' whenever caps.fill is false so the toggle
|
|
188
|
+
// can't get stranded on a hidden mode after the selection changes type.
|
|
189
|
+
const [swatchMode, setSwatchMode] = useState<SwatchMode>('stroke');
|
|
132
190
|
|
|
133
191
|
const selectedStrokes = useMemo<Stroke[]>(() => {
|
|
134
192
|
if (!annotSel || !store) return [];
|
|
@@ -147,7 +205,10 @@ export function AnnotationContextToolbar() {
|
|
|
147
205
|
return { color: false, fill: false, thickness: false, fontSize: false };
|
|
148
206
|
}
|
|
149
207
|
const allFillable = selectedStrokes.every((s) => s.tool === 'rect' || s.tool === 'ellipse');
|
|
150
|
-
|
|
208
|
+
// T20 — rect + ellipse now carry stroke weight too.
|
|
209
|
+
const allThickness = selectedStrokes.every(
|
|
210
|
+
(s) => s.tool === 'pen' || s.tool === 'arrow' || s.tool === 'rect' || s.tool === 'ellipse'
|
|
211
|
+
);
|
|
151
212
|
const anyText = selectedStrokes.some((s) => s.tool === 'text');
|
|
152
213
|
return {
|
|
153
214
|
color: true,
|
|
@@ -226,7 +287,8 @@ export function AnnotationContextToolbar() {
|
|
|
226
287
|
(w: number) => {
|
|
227
288
|
if (!store) return;
|
|
228
289
|
for (const s of selectedStrokes) {
|
|
229
|
-
|
|
290
|
+
// T20 — rect + ellipse now carry stroke weight.
|
|
291
|
+
if (s.tool === 'pen' || s.tool === 'arrow' || s.tool === 'rect' || s.tool === 'ellipse') {
|
|
230
292
|
store.updateStroke(s.id, { width: w } as Partial<Stroke>);
|
|
231
293
|
}
|
|
232
294
|
}
|
|
@@ -263,13 +325,26 @@ export function AnnotationContextToolbar() {
|
|
|
263
325
|
: undefined;
|
|
264
326
|
const uniqThickness = caps.thickness
|
|
265
327
|
? uniformValue(
|
|
266
|
-
selectedStrokes.map((s) =>
|
|
328
|
+
selectedStrokes.map((s) =>
|
|
329
|
+
s.tool === 'pen' || s.tool === 'arrow' || s.tool === 'rect' || s.tool === 'ellipse'
|
|
330
|
+
? s.width
|
|
331
|
+
: undefined
|
|
332
|
+
)
|
|
267
333
|
)
|
|
268
334
|
: undefined;
|
|
269
335
|
const uniqFontSize = caps.fontSize
|
|
270
336
|
? uniformValue(selectedStrokes.map((s) => (s.tool === 'text' ? s.fontSize : undefined)))
|
|
271
337
|
: undefined;
|
|
272
338
|
|
|
339
|
+
// T30 / G_S1 — when caps.fill is false we never enter fill mode. The
|
|
340
|
+
// useEffect below could call setSwatchMode('stroke') but reading the
|
|
341
|
+
// effective mode inline avoids an extra render cycle.
|
|
342
|
+
const effectiveMode: SwatchMode = caps.fill ? swatchMode : 'stroke';
|
|
343
|
+
const showPalette = effectiveMode === 'stroke' ? PALETTE : FILL_PALETTE;
|
|
344
|
+
const onSwatchClick =
|
|
345
|
+
effectiveMode === 'stroke' ? (c: string) => setColor(c) : (c: string) => setFill(c);
|
|
346
|
+
const activeValue = effectiveMode === 'stroke' ? uniqColor : (uniqFill ?? null);
|
|
347
|
+
|
|
273
348
|
return (
|
|
274
349
|
<div
|
|
275
350
|
ref={ref}
|
|
@@ -278,52 +353,60 @@ export function AnnotationContextToolbar() {
|
|
|
278
353
|
aria-label="Annotation properties"
|
|
279
354
|
style={{ display: 'flex', top: -9999, left: -9999 }}
|
|
280
355
|
>
|
|
281
|
-
{
|
|
356
|
+
{caps.fill ? (
|
|
357
|
+
<>
|
|
358
|
+
<div className="dc-annot-ctx-mode" role="radiogroup" aria-label="Swatch target">
|
|
359
|
+
<button
|
|
360
|
+
type="button"
|
|
361
|
+
className="dc-annot-ctx-mode-btn"
|
|
362
|
+
aria-pressed={effectiveMode === 'stroke'}
|
|
363
|
+
onClick={() => setSwatchMode('stroke')}
|
|
364
|
+
>
|
|
365
|
+
Stroke
|
|
366
|
+
</button>
|
|
367
|
+
<button
|
|
368
|
+
type="button"
|
|
369
|
+
className="dc-annot-ctx-mode-btn"
|
|
370
|
+
aria-pressed={effectiveMode === 'fill'}
|
|
371
|
+
onClick={() => setSwatchMode('fill')}
|
|
372
|
+
>
|
|
373
|
+
Fill
|
|
374
|
+
</button>
|
|
375
|
+
</div>
|
|
376
|
+
<div className="dc-annot-ctx-sep" />
|
|
377
|
+
</>
|
|
378
|
+
) : null}
|
|
379
|
+
{effectiveMode === 'fill' ? (
|
|
380
|
+
<button
|
|
381
|
+
type="button"
|
|
382
|
+
className="dc-annot-ctx-sw dc-annot-ctx-fill--none"
|
|
383
|
+
aria-label="No fill"
|
|
384
|
+
aria-pressed={uniqFill == null}
|
|
385
|
+
title="No fill"
|
|
386
|
+
onClick={() => setFill(null)}
|
|
387
|
+
/>
|
|
388
|
+
) : null}
|
|
389
|
+
{showPalette.map((c) => (
|
|
282
390
|
<button
|
|
283
391
|
key={c}
|
|
284
392
|
type="button"
|
|
285
393
|
className="dc-annot-ctx-sw"
|
|
286
|
-
aria-label={
|
|
287
|
-
aria-pressed={
|
|
288
|
-
title={
|
|
394
|
+
aria-label={`${effectiveMode === 'stroke' ? 'Color' : 'Fill'} ${c}`}
|
|
395
|
+
aria-pressed={activeValue === c}
|
|
396
|
+
title={`${effectiveMode === 'stroke' ? 'Color' : 'Fill'} ${c}`}
|
|
289
397
|
style={{ background: c }}
|
|
290
|
-
onClick={() =>
|
|
398
|
+
onClick={() => onSwatchClick(c)}
|
|
291
399
|
/>
|
|
292
400
|
))}
|
|
293
|
-
{caps.
|
|
294
|
-
<>
|
|
295
|
-
<div className="dc-annot-ctx-sep" />
|
|
296
|
-
<button
|
|
297
|
-
type="button"
|
|
298
|
-
className="dc-annot-ctx-sw dc-annot-ctx-fill--none"
|
|
299
|
-
aria-label="No fill"
|
|
300
|
-
aria-pressed={uniqFill == null}
|
|
301
|
-
title="No fill"
|
|
302
|
-
onClick={() => setFill(null)}
|
|
303
|
-
/>
|
|
304
|
-
{FILL_PALETTE.map((c) => (
|
|
305
|
-
<button
|
|
306
|
-
key={c}
|
|
307
|
-
type="button"
|
|
308
|
-
className="dc-annot-ctx-sw"
|
|
309
|
-
aria-label={`Fill ${c}`}
|
|
310
|
-
aria-pressed={uniqFill === c}
|
|
311
|
-
title={`Fill ${c}`}
|
|
312
|
-
style={{ background: c }}
|
|
313
|
-
onClick={() => setFill(c)}
|
|
314
|
-
/>
|
|
315
|
-
))}
|
|
316
|
-
</>
|
|
317
|
-
) : null}
|
|
318
|
-
{caps.thickness ? (
|
|
401
|
+
{caps.thickness && effectiveMode === 'stroke' ? (
|
|
319
402
|
<>
|
|
320
403
|
<div className="dc-annot-ctx-sep" />
|
|
321
404
|
<button
|
|
322
405
|
type="button"
|
|
323
406
|
className="dc-annot-ctx-btn"
|
|
324
|
-
aria-pressed={uniqThickness ===
|
|
325
|
-
title="Thin (
|
|
326
|
-
onClick={() => setThickness(
|
|
407
|
+
aria-pressed={uniqThickness === 3}
|
|
408
|
+
title="Thin (3px)"
|
|
409
|
+
onClick={() => setThickness(3)}
|
|
327
410
|
>
|
|
328
411
|
Thin
|
|
329
412
|
</button>
|
|
@@ -370,10 +453,9 @@ export function AnnotationContextToolbar() {
|
|
|
370
453
|
</button>
|
|
371
454
|
</>
|
|
372
455
|
) : null}
|
|
373
|
-
<div className="dc-annot-ctx-sep" />
|
|
374
456
|
<button
|
|
375
457
|
type="button"
|
|
376
|
-
className="dc-annot-ctx-btn dc-annot-ctx-btn--danger"
|
|
458
|
+
className="dc-annot-ctx-btn dc-annot-ctx-btn--danger dc-annot-ctx-overflow"
|
|
377
459
|
title="Delete (Backspace)"
|
|
378
460
|
aria-label="Delete selected annotations"
|
|
379
461
|
onClick={remove}
|