@1agh/maude 0.27.0 → 0.28.1
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-link.test.mjs +46 -0
- package/cli/commands/design.mjs +69 -3
- package/cli/commands/design.test.mjs +97 -1
- package/cli/commands/doctor.mjs +110 -5
- package/cli/commands/doctor.test.mjs +52 -0
- package/cli/lib/cache.mjs +1 -1
- package/cli/lib/config-lint.mjs +1 -1
- package/cli/lib/copy-tree.mjs +1 -1
- package/cli/lib/design-link.mjs +39 -9
- package/cli/lib/flow-design-integration.test.mjs +1 -1
- package/cli/lib/gitignore-block.test.mjs +2 -2
- package/cli/lib/stack-detect.mjs +1 -1
- package/package.json +9 -9
- package/plugins/design/dev-server/activity.ts +256 -0
- package/plugins/design/dev-server/ai-banner.tsx +4 -0
- package/plugins/design/dev-server/annotations-context-toolbar.tsx +1 -1
- package/plugins/design/dev-server/annotations-layer.tsx +3 -3
- package/plugins/design/dev-server/api.ts +1 -1
- package/plugins/design/dev-server/artboard-activity-overlay.tsx +67 -0
- package/plugins/design/dev-server/bin/scenario-report.mjs +1 -1
- package/plugins/design/dev-server/bin/server-up.sh +102 -9
- package/plugins/design/dev-server/bin/visual-sanity.sh +19 -3
- package/plugins/design/dev-server/canvas-comment-mount.tsx +165 -10
- package/plugins/design/dev-server/canvas-cursors.ts +1 -1
- package/plugins/design/dev-server/canvas-lib.tsx +72 -36
- package/plugins/design/dev-server/canvas-shell.tsx +1 -1
- package/plugins/design/dev-server/collab/awareness-bridge.ts +1 -1
- package/plugins/design/dev-server/collab/index.ts +4 -4
- package/plugins/design/dev-server/collab/persistence.ts +1 -1
- package/plugins/design/dev-server/config.schema.json +2 -1
- package/plugins/design/dev-server/context-menu.tsx +1 -1
- package/plugins/design/dev-server/dist/client.bundle.js +19 -19
- package/plugins/design/dev-server/dist/comment-mount.js +84 -6
- package/plugins/design/dev-server/dist/runtime/lib0_decoding.js +1 -620
- package/plugins/design/dev-server/dist/runtime/lib0_encoding.js +1 -604
- package/plugins/design/dev-server/dist/runtime/motion.js +4 -4944
- package/plugins/design/dev-server/dist/runtime/motion_react.js +10 -10054
- package/plugins/design/dev-server/dist/runtime/pixi-js.js +2775 -52482
- package/plugins/design/dev-server/dist/runtime/react-dom.js +1 -231
- package/plugins/design/dev-server/dist/runtime/react-dom_client.js +9 -10357
- package/plugins/design/dev-server/dist/runtime/react.js +1 -535
- package/plugins/design/dev-server/dist/runtime/react_jsx-dev-runtime.js +1 -60
- package/plugins/design/dev-server/dist/runtime/react_jsx-runtime.js +1 -85
- package/plugins/design/dev-server/dist/runtime/y-protocols_awareness.js +1 -376
- package/plugins/design/dev-server/dist/runtime/y-protocols_sync.js +1 -100
- package/plugins/design/dev-server/dist/runtime/yjs.js +5 -6687
- package/plugins/design/dev-server/draw/brush.ts +3 -3
- package/plugins/design/dev-server/draw/index.ts +5 -5
- package/plugins/design/dev-server/draw/layout.ts +1 -1
- package/plugins/design/dev-server/draw/test/geometry.test.ts +1 -1
- package/plugins/design/dev-server/draw/test/layout.test.ts +1 -1
- package/plugins/design/dev-server/draw/test/primitives.test.ts +1 -1
- package/plugins/design/dev-server/draw/test/serialize.test.ts +1 -1
- package/plugins/design/dev-server/equal-spacing-handles.tsx +1 -1
- package/plugins/design/dev-server/examples/perf-100-artboards.tsx +1 -2
- package/plugins/design/dev-server/export-dialog.tsx +1 -1
- package/plugins/design/dev-server/exporters/html.ts +1 -1
- package/plugins/design/dev-server/exporters/index.ts +2 -2
- package/plugins/design/dev-server/exporters/pdf.ts +1 -1
- package/plugins/design/dev-server/exporters/png.ts +1 -1
- package/plugins/design/dev-server/exporters/pptx.ts +1 -1
- package/plugins/design/dev-server/exporters/svg.ts +1 -1
- package/plugins/design/dev-server/http.ts +1 -1
- package/plugins/design/dev-server/inspect.ts +31 -1
- package/plugins/design/dev-server/marquee-overlay.tsx +1 -1
- package/plugins/design/dev-server/participants-chrome.tsx +86 -1
- package/plugins/design/dev-server/server.ts +11 -2
- package/plugins/design/dev-server/sync/agent.ts +1 -1
- package/plugins/design/dev-server/sync/index.ts +29 -24
- package/plugins/design/dev-server/sync/materialize.ts +2 -2
- package/plugins/design/dev-server/sync/migrate-seed.ts +1 -1
- package/plugins/design/dev-server/sync/projection.ts +3 -3
- package/plugins/design/dev-server/test/activity.test.ts +195 -0
- package/plugins/design/dev-server/test/ai-activity.test.ts +1 -1
- package/plugins/design/dev-server/test/annotations-api.test.ts +1 -2
- package/plugins/design/dev-server/test/annotations-draw-modifiers.test.ts +2 -2
- package/plugins/design/dev-server/test/annotations-layer.test.ts +6 -6
- package/plugins/design/dev-server/test/annotations-roundtrip.test.ts +1 -1
- package/plugins/design/dev-server/test/artboard-activity-overlay.test.tsx +56 -0
- package/plugins/design/dev-server/test/boot-self-heal.test.ts +1 -1
- package/plugins/design/dev-server/test/canvas-cursors.test.ts +1 -1
- package/plugins/design/dev-server/test/canvas-edit.test.ts +1 -1
- package/plugins/design/dev-server/test/canvas-header.test.ts +1 -1
- package/plugins/design/dev-server/test/canvas-hmr-runtime.test.tsx +114 -0
- package/plugins/design/dev-server/test/canvas-lib-inline.test.ts +1 -1
- package/plugins/design/dev-server/test/canvas-lib-resolver.test.ts +1 -2
- package/plugins/design/dev-server/test/canvas-meta-api.test.ts +1 -2
- package/plugins/design/dev-server/test/canvas-origin-gate.test.ts +1 -2
- package/plugins/design/dev-server/test/collab-annotations-bridge.test.ts +1 -1
- package/plugins/design/dev-server/test/collab-bridge.test.ts +1 -1
- package/plugins/design/dev-server/test/collab-protocol.test.ts +2 -2
- package/plugins/design/dev-server/test/collab-room.test.ts +1 -1
- package/plugins/design/dev-server/test/collab-stress.test.ts +1 -1
- package/plugins/design/dev-server/test/comments-api.test.ts +1 -2
- package/plugins/design/dev-server/test/compile-entry.test.ts +1 -2
- package/plugins/design/dev-server/test/exporters/canva.test.ts +1 -2
- package/plugins/design/dev-server/test/exporters/history.test.ts +1 -2
- package/plugins/design/dev-server/test/exporters/pw-launch.test.ts +1 -1
- package/plugins/design/dev-server/test/exporters/scope.test.ts +1 -2
- package/plugins/design/dev-server/test/exporters/zip.test.ts +1 -2
- package/plugins/design/dev-server/test/git-lifecycle.test.ts +1 -2
- package/plugins/design/dev-server/test/handoff-static-frames.test.ts +1 -1
- package/plugins/design/dev-server/test/handoff.test.ts +1 -1
- package/plugins/design/dev-server/test/hmr-broadcast.test.ts +1 -1
- package/plugins/design/dev-server/test/input-router.test.ts +2 -2
- package/plugins/design/dev-server/test/locator.test.ts +1 -1
- package/plugins/design/dev-server/test/runtime-bundle.test.ts +1 -1
- package/plugins/design/dev-server/test/shared-doc-convergence.test.ts +1 -1
- package/plugins/design/dev-server/test/shared-doc-migrate.test.ts +2 -3
- package/plugins/design/dev-server/test/shared-doc-projection.test.ts +1 -1
- package/plugins/design/dev-server/test/snap-distance-pill.test.ts +1 -1
- package/plugins/design/dev-server/test/sync-agent.test.ts +2 -3
- package/plugins/design/dev-server/test/sync-atomic-write.test.ts +2 -3
- package/plugins/design/dev-server/test/sync-codec.test.ts +1 -1
- package/plugins/design/dev-server/test/sync-connection-state.test.ts +1 -1
- package/plugins/design/dev-server/test/sync-echo-guard.test.ts +1 -1
- package/plugins/design/dev-server/test/sync-fs-mirror.test.ts +1 -2
- package/plugins/design/dev-server/test/sync-hardening.test.ts +4 -5
- package/plugins/design/dev-server/test/sync-hubs-config.test.ts +1 -2
- package/plugins/design/dev-server/test/sync-meta-codec.test.ts +1 -1
- package/plugins/design/dev-server/test/sync-runtime.test.ts +37 -14
- package/plugins/design/dev-server/test/sync-status.test.ts +1 -1
- package/plugins/design/dev-server/test/sync-untrusted.test.ts +1 -2
- package/plugins/design/dev-server/test/undo-stack.test.ts +2 -2
- package/plugins/design/dev-server/test/use-agent-presence.test.tsx +117 -0
- package/plugins/design/dev-server/test/use-artboard-drag.test.ts +3 -3
- package/plugins/design/dev-server/test/use-canvas-activity.test.tsx +206 -0
- package/plugins/design/dev-server/test/use-selection-set.test.tsx +1 -1
- package/plugins/design/dev-server/test/use-snap-guides.test.ts +1 -1
- package/plugins/design/dev-server/test/use-undo-stack.test.tsx +2 -2
- package/plugins/design/dev-server/use-agent-presence.tsx +244 -0
- package/plugins/design/dev-server/use-annotation-resize.tsx +1 -5
- package/plugins/design/dev-server/use-annotation-selection.tsx +2 -6
- package/plugins/design/dev-server/use-annotations-visibility.tsx +1 -1
- package/plugins/design/dev-server/use-artboard-drag.tsx +1 -1
- package/plugins/design/dev-server/use-canvas-activity.tsx +252 -0
- package/plugins/design/dev-server/use-collab.tsx +3 -4
- package/plugins/design/dev-server/use-selection-set.tsx +1 -1
- package/plugins/design/dev-server/use-tool-mode.tsx +1 -1
- package/plugins/design/dev-server/use-undo-stack.tsx +4 -5
- package/plugins/design/dev-server/ws.ts +21 -2
- package/plugins/design/templates/_shell.html +108 -3
- package/plugins/design/templates/design-system-inspiration/SUB-AGENT-PROMPTS.md +18 -3
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// activity — Phase 13. fs:any → per-file active|idle tracker + region diff.
|
|
2
|
+
|
|
3
|
+
import { describe, expect, test } from 'bun:test';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ACTIVITY_IDLE_MS,
|
|
7
|
+
type ActivityChange,
|
|
8
|
+
createActivity,
|
|
9
|
+
diffArtboardIds,
|
|
10
|
+
isCanvasFile,
|
|
11
|
+
} from '../activity.ts';
|
|
12
|
+
import { type Context, createBus } from '../context.ts';
|
|
13
|
+
|
|
14
|
+
function mkCtx(): Context {
|
|
15
|
+
const bus = createBus();
|
|
16
|
+
return {
|
|
17
|
+
cfg: {} as Context['cfg'],
|
|
18
|
+
projectLabel: '',
|
|
19
|
+
// No designRoot → refineArtboards() short-circuits; transition tests stay
|
|
20
|
+
// fs-free. The diff is exercised directly via diffArtboardIds() below.
|
|
21
|
+
paths: {} as Context['paths'],
|
|
22
|
+
bus,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
27
|
+
|
|
28
|
+
describe('activity / isCanvasFile', () => {
|
|
29
|
+
test('accepts .tsx + .html canvases under ui/', () => {
|
|
30
|
+
expect(isCanvasFile('ui/Smoke TSX.tsx')).toBe(true);
|
|
31
|
+
expect(isCanvasFile('ui/legacy.html')).toBe(true);
|
|
32
|
+
expect(isCanvasFile('ui/components/Card.tsx')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('rejects non-canvas extensions', () => {
|
|
36
|
+
expect(isCanvasFile('ui/Smoke.css')).toBe(false);
|
|
37
|
+
expect(isCanvasFile('_locator.json')).toBe(false);
|
|
38
|
+
expect(isCanvasFile('ui/shot.png')).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('rejects _-prefixed files + dirs (runtime artifacts, history, draw, smoke)', () => {
|
|
42
|
+
expect(isCanvasFile('_active.json')).toBe(false);
|
|
43
|
+
expect(isCanvasFile('_history/ui/Smoke TSX/0001.tsx')).toBe(false);
|
|
44
|
+
expect(isCanvasFile('_draw/logo.proof.tsx')).toBe(false);
|
|
45
|
+
expect(isCanvasFile('_history/snap.html')).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('rejects SKIP_DIRS segments', () => {
|
|
49
|
+
expect(isCanvasFile('node_modules/react/index.tsx')).toBe(false);
|
|
50
|
+
expect(isCanvasFile('dist/runtime/x.tsx')).toBe(false);
|
|
51
|
+
expect(isCanvasFile('ui/build/out.tsx')).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('rejects DS preview specimens (false-positive guard)', () => {
|
|
55
|
+
expect(isCanvasFile('system/aurora/preview/motion.html')).toBe(false);
|
|
56
|
+
expect(isCanvasFile('system/aurora/preview/motion.tsx')).toBe(false);
|
|
57
|
+
// but a real canvas elsewhere under system/ is fine
|
|
58
|
+
expect(isCanvasFile('system/aurora/showcase.tsx')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('normalizes back-slash + leading slash', () => {
|
|
62
|
+
expect(isCanvasFile('\\ui\\Smoke.tsx')).toBe(true);
|
|
63
|
+
expect(isCanvasFile('/ui/Smoke.tsx')).toBe(true);
|
|
64
|
+
expect(isCanvasFile('')).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('activity / transitions', () => {
|
|
69
|
+
test('idle → active on first canvas fs:any event', () => {
|
|
70
|
+
const ctx = mkCtx();
|
|
71
|
+
const seen: ActivityChange[] = [];
|
|
72
|
+
ctx.bus.on('activity:change', (c) => seen.push(c));
|
|
73
|
+
const act = createActivity(ctx, { idleMs: 40, diff: false });
|
|
74
|
+
|
|
75
|
+
ctx.bus.emit('fs:any', 'ui/Smoke.tsx');
|
|
76
|
+
expect(act.state['ui/Smoke.tsx']?.status).toBe('active');
|
|
77
|
+
expect(seen).toHaveLength(1);
|
|
78
|
+
expect(seen[0]).toMatchObject({ file: 'ui/Smoke.tsx', status: 'active', artboard_ids: null });
|
|
79
|
+
act.stop();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('non-canvas fs:any events are ignored', () => {
|
|
83
|
+
const ctx = mkCtx();
|
|
84
|
+
const seen: ActivityChange[] = [];
|
|
85
|
+
ctx.bus.on('activity:change', (c) => seen.push(c));
|
|
86
|
+
const act = createActivity(ctx, { idleMs: 40, diff: false });
|
|
87
|
+
|
|
88
|
+
ctx.bus.emit('fs:any', 'ui/Smoke.css');
|
|
89
|
+
ctx.bus.emit('fs:any', '_active.json');
|
|
90
|
+
ctx.bus.emit('fs:any', 'system/x/preview/motion.tsx');
|
|
91
|
+
expect(seen).toHaveLength(0);
|
|
92
|
+
expect(act.state['ui/Smoke.css']).toBeUndefined();
|
|
93
|
+
act.stop();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('active → active: rapid events keep the file active past one idle window', async () => {
|
|
97
|
+
const ctx = mkCtx();
|
|
98
|
+
const act = createActivity(ctx, { idleMs: 60, diff: false });
|
|
99
|
+
|
|
100
|
+
ctx.bus.emit('fs:any', 'ui/Smoke.tsx');
|
|
101
|
+
await sleep(40);
|
|
102
|
+
ctx.bus.emit('fs:any', 'ui/Smoke.tsx'); // refresh before the 60 ms idle fires
|
|
103
|
+
await sleep(40); // 80 ms since first event, but only 40 ms since the refresh
|
|
104
|
+
expect(act.state['ui/Smoke.tsx']?.status).toBe('active');
|
|
105
|
+
act.stop();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('active → idle after idleMs of silence, and emits the idle change', async () => {
|
|
109
|
+
const ctx = mkCtx();
|
|
110
|
+
const seen: ActivityChange[] = [];
|
|
111
|
+
ctx.bus.on('activity:change', (c) => seen.push(c));
|
|
112
|
+
const act = createActivity(ctx, { idleMs: 40, diff: false });
|
|
113
|
+
|
|
114
|
+
ctx.bus.emit('fs:any', 'ui/Smoke.tsx');
|
|
115
|
+
await sleep(80);
|
|
116
|
+
expect(act.state['ui/Smoke.tsx']?.status).toBe('idle');
|
|
117
|
+
const last = seen[seen.length - 1];
|
|
118
|
+
expect(last).toMatchObject({ file: 'ui/Smoke.tsx', status: 'idle' });
|
|
119
|
+
act.stop();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('mark() test seam marks active without the bus', () => {
|
|
123
|
+
const ctx = mkCtx();
|
|
124
|
+
const seen: ActivityChange[] = [];
|
|
125
|
+
ctx.bus.on('activity:change', (c) => seen.push(c));
|
|
126
|
+
const act = createActivity(ctx, { idleMs: 40, diff: false });
|
|
127
|
+
|
|
128
|
+
act.mark('ui/Manual.tsx');
|
|
129
|
+
expect(act.state['ui/Manual.tsx']?.status).toBe('active');
|
|
130
|
+
expect(seen[0]?.file).toBe('ui/Manual.tsx');
|
|
131
|
+
act.stop();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('stop() halts the idle timer (no idle emit after stop)', async () => {
|
|
135
|
+
const ctx = mkCtx();
|
|
136
|
+
const seen: ActivityChange[] = [];
|
|
137
|
+
ctx.bus.on('activity:change', (c) => seen.push(c));
|
|
138
|
+
const act = createActivity(ctx, { idleMs: 40, diff: false });
|
|
139
|
+
|
|
140
|
+
ctx.bus.emit('fs:any', 'ui/Smoke.tsx');
|
|
141
|
+
act.stop();
|
|
142
|
+
await sleep(80);
|
|
143
|
+
expect(seen.filter((c) => c.status === 'idle')).toHaveLength(0);
|
|
144
|
+
// stop() also detaches the bus subscription.
|
|
145
|
+
ctx.bus.emit('fs:any', 'ui/Other.tsx');
|
|
146
|
+
expect(act.state['ui/Other.tsx']).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('activity / diffArtboardIds (Task 7)', () => {
|
|
151
|
+
const wrap = (primaryBody: string, secondaryBody: string) =>
|
|
152
|
+
`import { DesignCanvas, DCArtboard } from '@maude/canvas-lib';
|
|
153
|
+
export default function C() {
|
|
154
|
+
return (
|
|
155
|
+
<DesignCanvas>
|
|
156
|
+
<DCArtboard id="primary" label="Primary" width={400} height={300}>${primaryBody}</DCArtboard>
|
|
157
|
+
<DCArtboard id="secondary" label="Secondary" width={400} height={300}>${secondaryBody}</DCArtboard>
|
|
158
|
+
</DesignCanvas>
|
|
159
|
+
);
|
|
160
|
+
}`;
|
|
161
|
+
|
|
162
|
+
test('change confined to one artboard → that id only', () => {
|
|
163
|
+
const prev = wrap('<p>A</p>', '<p>B</p>');
|
|
164
|
+
const next = wrap('<p>A</p>', '<p>B changed</p>');
|
|
165
|
+
expect(diffArtboardIds(prev, next)).toEqual(['secondary']);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('change in both artboards → both ids', () => {
|
|
169
|
+
const prev = wrap('<p>A</p>', '<p>B</p>');
|
|
170
|
+
const next = wrap('<p>A2</p>', '<p>B2</p>');
|
|
171
|
+
expect(diffArtboardIds(prev, next)?.sort()).toEqual(['primary', 'secondary']);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('identical content → null (no region changed)', () => {
|
|
175
|
+
const src = wrap('<p>A</p>', '<p>B</p>');
|
|
176
|
+
expect(diffArtboardIds(src, src)).toBeNull();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('change outside any artboard body → null (file-level fallback)', () => {
|
|
180
|
+
const prev = wrap('<p>A</p>', '<p>B</p>');
|
|
181
|
+
// Added an import line outside any DCArtboard → skeleton differs.
|
|
182
|
+
const next = `import x from 'y';\n${wrap('<p>A</p>', '<p>B</p>')}`;
|
|
183
|
+
expect(diffArtboardIds(prev, next)).toBeNull();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('no DCArtboard markers → null (ambiguous)', () => {
|
|
187
|
+
expect(diffArtboardIds('<div>old</div>', '<div>new</div>')).toBeNull();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('artboard added/removed → null (shell differs)', () => {
|
|
191
|
+
const prev = wrap('<p>A</p>', '<p>B</p>');
|
|
192
|
+
const next = `${wrap('<p>A</p>', '<p>B</p>')}\n// trailing <DCArtboard id="third" label="T" width={1} height={1}>x</DCArtboard>`;
|
|
193
|
+
expect(diffArtboardIds(prev, next)).toBeNull();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { describe, expect, test } from 'bun:test';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { createAiActivity, HEARTBEAT_GRACE_MS } from '../collab/ai-activity.ts';
|
|
6
6
|
import { type Context, createBus } from '../context.ts';
|
|
7
7
|
|
|
8
8
|
function makeCtx(): { ctx: Context; events: { file: string; entry: unknown }[] } {
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
// - PUT rejects oversized bodies (>1 MB)
|
|
9
9
|
// - Unknown method → 405
|
|
10
10
|
|
|
11
|
+
import { describe, expect, test } from 'bun:test';
|
|
11
12
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
13
|
import { join } from 'node:path';
|
|
13
14
|
|
|
14
|
-
import { describe, expect, test } from 'bun:test';
|
|
15
|
-
|
|
16
15
|
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
17
16
|
|
|
18
17
|
const SVG_OK =
|
|
@@ -6,13 +6,13 @@ import { describe, expect, test } from 'bun:test';
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
type ArrowStroke,
|
|
9
|
+
applyDrawModifiers,
|
|
10
|
+
constrainDrawBox,
|
|
9
11
|
type EllipseStroke,
|
|
10
12
|
type PenStroke,
|
|
11
13
|
type PolygonStroke,
|
|
12
14
|
type RectStroke,
|
|
13
15
|
type StickyStroke,
|
|
14
|
-
applyDrawModifiers,
|
|
15
|
-
constrainDrawBox,
|
|
16
16
|
} from '../annotations-layer.tsx';
|
|
17
17
|
|
|
18
18
|
const NONE = { shift: false, alt: false };
|
|
@@ -11,23 +11,23 @@ import { describe, expect, test } from 'bun:test';
|
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
type ArrowStroke,
|
|
14
|
+
arrowHeadPoints,
|
|
14
15
|
type EllipseStroke,
|
|
15
16
|
type PenStroke,
|
|
16
17
|
type PolygonStroke,
|
|
17
|
-
type RectStroke,
|
|
18
|
-
STICKY_PALETTE,
|
|
19
|
-
type StickyStroke,
|
|
20
|
-
type Stroke,
|
|
21
|
-
type TextStroke,
|
|
22
|
-
arrowHeadPoints,
|
|
23
18
|
penPathD,
|
|
24
19
|
polygonPoints,
|
|
25
20
|
polygonVertices,
|
|
21
|
+
type RectStroke,
|
|
26
22
|
rid,
|
|
23
|
+
STICKY_PALETTE,
|
|
24
|
+
type StickyStroke,
|
|
25
|
+
type Stroke,
|
|
27
26
|
strokeBBox,
|
|
28
27
|
strokeHitTest,
|
|
29
28
|
strokesShallowEqual,
|
|
30
29
|
strokesToSvg,
|
|
30
|
+
type TextStroke,
|
|
31
31
|
} from '../annotations-layer.tsx';
|
|
32
32
|
|
|
33
33
|
describe('annotations-layer / penPathD', () => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// artboard-activity-overlay — Phase 13. Presentational overlay contract.
|
|
2
|
+
//
|
|
3
|
+
// The positioned overlay only renders inside a laid-out DesignCanvas (rect
|
|
4
|
+
// resolves after layout effects, which renderToStaticMarkup doesn't run), so we
|
|
5
|
+
// assert the leaf component directly: the rim box, the badge text, and the
|
|
6
|
+
// active→fading toggle. The gating decision (present + artboard scope) is
|
|
7
|
+
// covered in use-canvas-activity.test.tsx.
|
|
8
|
+
|
|
9
|
+
import { describe, expect, test } from 'bun:test';
|
|
10
|
+
|
|
11
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
12
|
+
|
|
13
|
+
import { ArtboardActivityOverlay } from '../artboard-activity-overlay.tsx';
|
|
14
|
+
|
|
15
|
+
const RECT = { x: 10, y: 20, w: 300, h: 200 };
|
|
16
|
+
|
|
17
|
+
describe('artboard-activity-overlay', () => {
|
|
18
|
+
test('renders the rim + badge with the editing label', () => {
|
|
19
|
+
const html = renderToStaticMarkup(
|
|
20
|
+
<ArtboardActivityOverlay rect={RECT} label="Smoke TSX.tsx" active />
|
|
21
|
+
);
|
|
22
|
+
expect(html).toContain('dc-activity-rim');
|
|
23
|
+
expect(html).toContain('dc-activity-badge');
|
|
24
|
+
expect(html).toContain('dc-activity-scan'); // Phase 13.3 full-artboard scan beam
|
|
25
|
+
expect(html).toContain('editing —');
|
|
26
|
+
expect(html).toContain('Smoke TSX.tsx');
|
|
27
|
+
expect(html).toContain('aria-hidden');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('agent color overrides the activity CSS var', () => {
|
|
31
|
+
const html = renderToStaticMarkup(
|
|
32
|
+
<ArtboardActivityOverlay rect={RECT} label="Nimble Ferret" active color="#ff9800" />
|
|
33
|
+
);
|
|
34
|
+
expect(html).toMatch(/--mdcc-activity:\s*#ff9800/);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('active overlay is NOT marked fading', () => {
|
|
38
|
+
const html = renderToStaticMarkup(<ArtboardActivityOverlay rect={RECT} label="x" active />);
|
|
39
|
+
expect(html).not.toContain('data-fading');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('idle overlay carries data-fading for the cross-fade out', () => {
|
|
43
|
+
const html = renderToStaticMarkup(
|
|
44
|
+
<ArtboardActivityOverlay rect={RECT} label="x" active={false} />
|
|
45
|
+
);
|
|
46
|
+
expect(html).toContain('data-fading="true"');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('positions at the artboard world rect', () => {
|
|
50
|
+
const html = renderToStaticMarkup(<ArtboardActivityOverlay rect={RECT} label="x" active />);
|
|
51
|
+
expect(html).toMatch(/left:\s*10px/);
|
|
52
|
+
expect(html).toMatch(/top:\s*20px/);
|
|
53
|
+
expect(html).toMatch(/width:\s*300px/);
|
|
54
|
+
expect(html).toMatch(/height:\s*200px/);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -9,7 +9,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
|
9
9
|
import { tmpdir } from 'node:os';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
|
|
12
|
-
import { type SelfHealOptions
|
|
12
|
+
import { bootSelfHeal, type SelfHealOptions } from '../boot-self-heal.ts';
|
|
13
13
|
|
|
14
14
|
let TMP: string;
|
|
15
15
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, expect, test } from 'bun:test';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { applyEdit, CanvasEditError, editAttribute } from '../canvas-edit.ts';
|
|
9
9
|
import { transpileCanvasSource } from '../canvas-pipeline.ts';
|
|
10
10
|
|
|
11
11
|
const CANVAS = '/abs/Canvas.tsx';
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// canvas-hmr-runtime — Phase 13.1 / DDR-077. The resettable error boundary that
|
|
2
|
+
// keeps the last good render (no white flash) when an agent edit produces a
|
|
3
|
+
// broken intermediate, and recovers when a good build lands.
|
|
4
|
+
//
|
|
5
|
+
// Error boundaries don't run under renderToStaticMarkup (SSR rethrows), so this
|
|
6
|
+
// needs a live DOM. We register happy-dom for THIS file only (like
|
|
7
|
+
// annotations-roundtrip.test.ts) and drive a real createRoot.
|
|
8
|
+
|
|
9
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
10
|
+
import { GlobalRegistrator } from '@happy-dom/global-registrator';
|
|
11
|
+
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
GlobalRegistrator.register();
|
|
14
|
+
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
15
|
+
});
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
GlobalRegistrator.unregister();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
import { act, createElement } from 'react';
|
|
21
|
+
import { createRoot, type Root } from 'react-dom/client';
|
|
22
|
+
|
|
23
|
+
import { CanvasErrorBoundary } from '../canvas-comment-mount.tsx';
|
|
24
|
+
|
|
25
|
+
function Good({ tag }: { tag: string }) {
|
|
26
|
+
return createElement('div', { 'data-good': tag }, `good:${tag}`);
|
|
27
|
+
}
|
|
28
|
+
function Boom(): never {
|
|
29
|
+
throw new Error('render exploded (missing import)');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mount(el: HTMLElement) {
|
|
33
|
+
let root!: Root;
|
|
34
|
+
act(() => {
|
|
35
|
+
root = createRoot(el);
|
|
36
|
+
});
|
|
37
|
+
return root;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('CanvasErrorBoundary', () => {
|
|
41
|
+
test('renders children when they do not throw', () => {
|
|
42
|
+
const host = document.createElement('div');
|
|
43
|
+
const root = mount(host);
|
|
44
|
+
act(() => {
|
|
45
|
+
root.render(
|
|
46
|
+
createElement(
|
|
47
|
+
CanvasErrorBoundary,
|
|
48
|
+
{
|
|
49
|
+
attempt: 0,
|
|
50
|
+
onError: () => {},
|
|
51
|
+
fallback: () => createElement(Good, { tag: 'fallback' }),
|
|
52
|
+
},
|
|
53
|
+
createElement(Good, { tag: 'live' })
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
expect(host.querySelector('[data-good="live"]')).not.toBeNull();
|
|
58
|
+
expect(host.querySelector('[data-good="fallback"]')).toBeNull();
|
|
59
|
+
act(() => root.unmount());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('a throwing child → renders fallback (NOT blank) and fires onError once', () => {
|
|
63
|
+
const host = document.createElement('div');
|
|
64
|
+
const root = mount(host);
|
|
65
|
+
let errors = 0;
|
|
66
|
+
act(() => {
|
|
67
|
+
root.render(
|
|
68
|
+
createElement(
|
|
69
|
+
CanvasErrorBoundary,
|
|
70
|
+
{
|
|
71
|
+
attempt: 0,
|
|
72
|
+
onError: () => {
|
|
73
|
+
errors++;
|
|
74
|
+
},
|
|
75
|
+
fallback: () => createElement(Good, { tag: 'lastgood' }),
|
|
76
|
+
},
|
|
77
|
+
createElement(Boom)
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
// The boundary held a render (the fallback) — the host is NOT empty.
|
|
82
|
+
expect(host.querySelector('[data-good="lastgood"]')).not.toBeNull();
|
|
83
|
+
expect(host.textContent).toContain('good:lastgood');
|
|
84
|
+
expect(errors).toBe(1);
|
|
85
|
+
act(() => root.unmount());
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('a new attempt with a good child recovers (swaps fallback → live)', () => {
|
|
89
|
+
const host = document.createElement('div');
|
|
90
|
+
const root = mount(host);
|
|
91
|
+
const render = (attempt: number, child: ReturnType<typeof createElement>) =>
|
|
92
|
+
act(() => {
|
|
93
|
+
root.render(
|
|
94
|
+
createElement(
|
|
95
|
+
CanvasErrorBoundary,
|
|
96
|
+
{
|
|
97
|
+
attempt,
|
|
98
|
+
onError: () => {},
|
|
99
|
+
fallback: () => createElement(Good, { tag: 'lastgood' }),
|
|
100
|
+
},
|
|
101
|
+
child
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
render(0, createElement(Boom)); // broken build → fallback
|
|
107
|
+
expect(host.querySelector('[data-good="lastgood"]')).not.toBeNull();
|
|
108
|
+
|
|
109
|
+
render(1, createElement(Good, { tag: 'fixed' })); // good build lands
|
|
110
|
+
expect(host.querySelector('[data-good="fixed"]')).not.toBeNull();
|
|
111
|
+
expect(host.querySelector('[data-good="lastgood"]')).toBeNull();
|
|
112
|
+
act(() => root.unmount());
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { describe, expect, test } from 'bun:test';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { buildLibMap, inlineUsedExports, type LibMap } from '../canvas-lib-inline.ts';
|
|
7
7
|
|
|
8
8
|
const LIB_PATH = '/virtual/_lib/canvas-lib.tsx';
|
|
9
9
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// bundled canvas-lib (Phase 3.6.1 Task 2; relocated to dev-server in Phase
|
|
3
3
|
// 4.0.5 per DDR-025).
|
|
4
4
|
|
|
5
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
5
6
|
import { existsSync } from 'node:fs';
|
|
6
7
|
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
|
|
9
|
-
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
10
|
-
|
|
11
10
|
import { buildCanvasModule } from '../canvas-build.ts';
|
|
12
11
|
import {
|
|
13
12
|
CANVAS_LIB_SPECIFIER,
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
// - GET returns the merged meta
|
|
9
9
|
// - Paths that escape repoRoot are 400
|
|
10
10
|
|
|
11
|
+
import { describe, expect, test } from 'bun:test';
|
|
11
12
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
13
|
import { join } from 'node:path';
|
|
13
14
|
|
|
14
|
-
import { describe, expect, test } from 'bun:test';
|
|
15
|
-
|
|
16
15
|
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
17
16
|
|
|
18
17
|
interface MetaShape {
|
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
// designRoot assets + the shell still serve; (4) the shell carries the hardened
|
|
6
6
|
// CSP. See .ai/logs/security-reviews/phase-9.1-t2-f1-cross-origin-reaudit.md.
|
|
7
7
|
|
|
8
|
+
import { describe, expect, test } from 'bun:test';
|
|
8
9
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
10
|
import { join } from 'node:path';
|
|
10
11
|
|
|
11
|
-
import { describe, expect, test } from 'bun:test';
|
|
12
|
-
|
|
13
12
|
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
14
13
|
|
|
15
14
|
async function readCanvasOrigin(designRoot: string): Promise<string> {
|
|
@@ -45,7 +45,7 @@ describe('Registry.syncRoomFromAnnotations', () => {
|
|
|
45
45
|
test('inspector-write origin propagates through doc.update', () => {
|
|
46
46
|
const r = createRegistry(noopCallbacks());
|
|
47
47
|
const room = r.get('echo-slug');
|
|
48
|
-
let lastOrigin: unknown
|
|
48
|
+
let lastOrigin: unknown;
|
|
49
49
|
room.doc.on('update', (_update, origin) => {
|
|
50
50
|
lastOrigin = origin;
|
|
51
51
|
});
|
|
@@ -71,7 +71,7 @@ describe('Registry inspector-bridge', () => {
|
|
|
71
71
|
test('inspector-write origin triggers doc.update broadcast', async () => {
|
|
72
72
|
const r = createRegistry(noopCallbacks());
|
|
73
73
|
const room = r.get('echo-slug');
|
|
74
|
-
let lastOrigin: unknown
|
|
74
|
+
let lastOrigin: unknown;
|
|
75
75
|
room.doc.on('update', (_update, origin) => {
|
|
76
76
|
lastOrigin = origin;
|
|
77
77
|
});
|
|
@@ -8,12 +8,12 @@ import { Awareness } from 'y-protocols/awareness';
|
|
|
8
8
|
import * as Y from 'yjs';
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
MESSAGE_AWARENESS,
|
|
12
|
-
MESSAGE_SYNC,
|
|
13
11
|
encodeAwarenessFrame,
|
|
14
12
|
encodeHandshake,
|
|
15
13
|
encodeSyncUpdate,
|
|
16
14
|
handleMessage,
|
|
15
|
+
MESSAGE_AWARENESS,
|
|
16
|
+
MESSAGE_SYNC,
|
|
17
17
|
} from '../collab/protocol.ts';
|
|
18
18
|
|
|
19
19
|
function readType(frame: Uint8Array): number {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
encodeSyncUpdate,
|
|
13
13
|
handleMessage,
|
|
14
14
|
} from '../collab/protocol.ts';
|
|
15
|
-
import { type RoomCallbacks, type RoomConn
|
|
15
|
+
import { createRoom, type RoomCallbacks, type RoomConn } from '../collab/room.ts';
|
|
16
16
|
|
|
17
17
|
function makeConn(id: string): RoomConn & { recv: Uint8Array[] } {
|
|
18
18
|
const recv: Uint8Array[] = [];
|
|
@@ -19,7 +19,7 @@ import { afterEach, describe, expect, test } from 'bun:test';
|
|
|
19
19
|
import { Awareness } from 'y-protocols/awareness';
|
|
20
20
|
import * as Y from 'yjs';
|
|
21
21
|
|
|
22
|
-
import { type RoomConn
|
|
22
|
+
import { createRoom, type RoomConn } from '../collab/room.ts';
|
|
23
23
|
|
|
24
24
|
const STRESS_MS = Number(process.env.MAUDE_STRESS_MS ?? 10_000);
|
|
25
25
|
const RSS_GROWTH_LIMIT_MB = 20;
|
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
// git sandbox; gracefully empty if git fails)
|
|
12
12
|
// - Reply on unknown id → 404
|
|
13
13
|
|
|
14
|
+
import { describe, expect, test } from 'bun:test';
|
|
14
15
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
15
16
|
import { join } from 'node:path';
|
|
16
17
|
|
|
17
|
-
import { describe, expect, test } from 'bun:test';
|
|
18
|
-
|
|
19
18
|
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
20
19
|
|
|
21
20
|
async function initGit(root: string) {
|
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
// no spawn): given a target, it writes two files (`init-oxc-<slug>.ts` +
|
|
5
5
|
// `server-<slug>.ts`) under dist/.compile-entries/ and returns the entry path.
|
|
6
6
|
|
|
7
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
8
|
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
8
9
|
import { dirname, join } from 'node:path';
|
|
9
10
|
import { fileURLToPath } from 'node:url';
|
|
10
11
|
|
|
11
|
-
import { describe, expect, test } from 'bun:test';
|
|
12
|
-
|
|
13
12
|
import { writeCompileEntry } from '../build.ts';
|
|
14
13
|
|
|
15
14
|
const ROOT = dirname(fileURLToPath(import.meta.url));
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
// + the empty/file-tree branches.
|
|
5
5
|
|
|
6
6
|
import { describe, expect, test } from 'bun:test';
|
|
7
|
-
|
|
8
|
-
import { buildHandoffMarkdown } from '../../exporters/canva-handoff-prompt.ts';
|
|
9
7
|
import { run } from '../../exporters/canva.ts';
|
|
8
|
+
import { buildHandoffMarkdown } from '../../exporters/canva-handoff-prompt.ts';
|
|
10
9
|
|
|
11
10
|
const CTX = {
|
|
12
11
|
designRoot: '/tmp/.design',
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// Phase 6.5 T10 — export history persistence + endpoint.
|
|
2
2
|
|
|
3
|
+
import { describe, expect, test } from 'bun:test';
|
|
3
4
|
import { readFileSync } from 'node:fs';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
|
|
6
|
-
import { describe, expect, test } from 'bun:test';
|
|
7
|
-
|
|
8
7
|
import { bootServer, killProc, makeSandbox, nextPort } from '../_helpers.ts';
|
|
9
8
|
|
|
10
9
|
interface HistoryShape {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { describe, expect, test } from 'bun:test';
|
|
9
9
|
|
|
10
|
-
import { INSTALL_HINT,
|
|
10
|
+
import { INSTALL_HINT, isMissingBrowserError, NO_BROWSER_EXIT } from '../../bin/_pw-launch.mjs';
|
|
11
11
|
|
|
12
12
|
describe('_pw-launch — missing-browser classifier', () => {
|
|
13
13
|
test('matches the real Playwright "Executable doesn\'t exist" message', () => {
|