@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.
Files changed (143) hide show
  1. package/cli/commands/design-link.test.mjs +46 -0
  2. package/cli/commands/design.mjs +69 -3
  3. package/cli/commands/design.test.mjs +97 -1
  4. package/cli/commands/doctor.mjs +110 -5
  5. package/cli/commands/doctor.test.mjs +52 -0
  6. package/cli/lib/cache.mjs +1 -1
  7. package/cli/lib/config-lint.mjs +1 -1
  8. package/cli/lib/copy-tree.mjs +1 -1
  9. package/cli/lib/design-link.mjs +39 -9
  10. package/cli/lib/flow-design-integration.test.mjs +1 -1
  11. package/cli/lib/gitignore-block.test.mjs +2 -2
  12. package/cli/lib/stack-detect.mjs +1 -1
  13. package/package.json +9 -9
  14. package/plugins/design/dev-server/activity.ts +256 -0
  15. package/plugins/design/dev-server/ai-banner.tsx +4 -0
  16. package/plugins/design/dev-server/annotations-context-toolbar.tsx +1 -1
  17. package/plugins/design/dev-server/annotations-layer.tsx +3 -3
  18. package/plugins/design/dev-server/api.ts +1 -1
  19. package/plugins/design/dev-server/artboard-activity-overlay.tsx +67 -0
  20. package/plugins/design/dev-server/bin/scenario-report.mjs +1 -1
  21. package/plugins/design/dev-server/bin/server-up.sh +102 -9
  22. package/plugins/design/dev-server/bin/visual-sanity.sh +19 -3
  23. package/plugins/design/dev-server/canvas-comment-mount.tsx +165 -10
  24. package/plugins/design/dev-server/canvas-cursors.ts +1 -1
  25. package/plugins/design/dev-server/canvas-lib.tsx +72 -36
  26. package/plugins/design/dev-server/canvas-shell.tsx +1 -1
  27. package/plugins/design/dev-server/collab/awareness-bridge.ts +1 -1
  28. package/plugins/design/dev-server/collab/index.ts +4 -4
  29. package/plugins/design/dev-server/collab/persistence.ts +1 -1
  30. package/plugins/design/dev-server/config.schema.json +2 -1
  31. package/plugins/design/dev-server/context-menu.tsx +1 -1
  32. package/plugins/design/dev-server/dist/client.bundle.js +19 -19
  33. package/plugins/design/dev-server/dist/comment-mount.js +84 -6
  34. package/plugins/design/dev-server/dist/runtime/lib0_decoding.js +1 -620
  35. package/plugins/design/dev-server/dist/runtime/lib0_encoding.js +1 -604
  36. package/plugins/design/dev-server/dist/runtime/motion.js +4 -4944
  37. package/plugins/design/dev-server/dist/runtime/motion_react.js +10 -10054
  38. package/plugins/design/dev-server/dist/runtime/pixi-js.js +2775 -52482
  39. package/plugins/design/dev-server/dist/runtime/react-dom.js +1 -231
  40. package/plugins/design/dev-server/dist/runtime/react-dom_client.js +9 -10357
  41. package/plugins/design/dev-server/dist/runtime/react.js +1 -535
  42. package/plugins/design/dev-server/dist/runtime/react_jsx-dev-runtime.js +1 -60
  43. package/plugins/design/dev-server/dist/runtime/react_jsx-runtime.js +1 -85
  44. package/plugins/design/dev-server/dist/runtime/y-protocols_awareness.js +1 -376
  45. package/plugins/design/dev-server/dist/runtime/y-protocols_sync.js +1 -100
  46. package/plugins/design/dev-server/dist/runtime/yjs.js +5 -6687
  47. package/plugins/design/dev-server/draw/brush.ts +3 -3
  48. package/plugins/design/dev-server/draw/index.ts +5 -5
  49. package/plugins/design/dev-server/draw/layout.ts +1 -1
  50. package/plugins/design/dev-server/draw/test/geometry.test.ts +1 -1
  51. package/plugins/design/dev-server/draw/test/layout.test.ts +1 -1
  52. package/plugins/design/dev-server/draw/test/primitives.test.ts +1 -1
  53. package/plugins/design/dev-server/draw/test/serialize.test.ts +1 -1
  54. package/plugins/design/dev-server/equal-spacing-handles.tsx +1 -1
  55. package/plugins/design/dev-server/examples/perf-100-artboards.tsx +1 -2
  56. package/plugins/design/dev-server/export-dialog.tsx +1 -1
  57. package/plugins/design/dev-server/exporters/html.ts +1 -1
  58. package/plugins/design/dev-server/exporters/index.ts +2 -2
  59. package/plugins/design/dev-server/exporters/pdf.ts +1 -1
  60. package/plugins/design/dev-server/exporters/png.ts +1 -1
  61. package/plugins/design/dev-server/exporters/pptx.ts +1 -1
  62. package/plugins/design/dev-server/exporters/svg.ts +1 -1
  63. package/plugins/design/dev-server/http.ts +1 -1
  64. package/plugins/design/dev-server/inspect.ts +31 -1
  65. package/plugins/design/dev-server/marquee-overlay.tsx +1 -1
  66. package/plugins/design/dev-server/participants-chrome.tsx +86 -1
  67. package/plugins/design/dev-server/server.ts +11 -2
  68. package/plugins/design/dev-server/sync/agent.ts +1 -1
  69. package/plugins/design/dev-server/sync/index.ts +29 -24
  70. package/plugins/design/dev-server/sync/materialize.ts +2 -2
  71. package/plugins/design/dev-server/sync/migrate-seed.ts +1 -1
  72. package/plugins/design/dev-server/sync/projection.ts +3 -3
  73. package/plugins/design/dev-server/test/activity.test.ts +195 -0
  74. package/plugins/design/dev-server/test/ai-activity.test.ts +1 -1
  75. package/plugins/design/dev-server/test/annotations-api.test.ts +1 -2
  76. package/plugins/design/dev-server/test/annotations-draw-modifiers.test.ts +2 -2
  77. package/plugins/design/dev-server/test/annotations-layer.test.ts +6 -6
  78. package/plugins/design/dev-server/test/annotations-roundtrip.test.ts +1 -1
  79. package/plugins/design/dev-server/test/artboard-activity-overlay.test.tsx +56 -0
  80. package/plugins/design/dev-server/test/boot-self-heal.test.ts +1 -1
  81. package/plugins/design/dev-server/test/canvas-cursors.test.ts +1 -1
  82. package/plugins/design/dev-server/test/canvas-edit.test.ts +1 -1
  83. package/plugins/design/dev-server/test/canvas-header.test.ts +1 -1
  84. package/plugins/design/dev-server/test/canvas-hmr-runtime.test.tsx +114 -0
  85. package/plugins/design/dev-server/test/canvas-lib-inline.test.ts +1 -1
  86. package/plugins/design/dev-server/test/canvas-lib-resolver.test.ts +1 -2
  87. package/plugins/design/dev-server/test/canvas-meta-api.test.ts +1 -2
  88. package/plugins/design/dev-server/test/canvas-origin-gate.test.ts +1 -2
  89. package/plugins/design/dev-server/test/collab-annotations-bridge.test.ts +1 -1
  90. package/plugins/design/dev-server/test/collab-bridge.test.ts +1 -1
  91. package/plugins/design/dev-server/test/collab-protocol.test.ts +2 -2
  92. package/plugins/design/dev-server/test/collab-room.test.ts +1 -1
  93. package/plugins/design/dev-server/test/collab-stress.test.ts +1 -1
  94. package/plugins/design/dev-server/test/comments-api.test.ts +1 -2
  95. package/plugins/design/dev-server/test/compile-entry.test.ts +1 -2
  96. package/plugins/design/dev-server/test/exporters/canva.test.ts +1 -2
  97. package/plugins/design/dev-server/test/exporters/history.test.ts +1 -2
  98. package/plugins/design/dev-server/test/exporters/pw-launch.test.ts +1 -1
  99. package/plugins/design/dev-server/test/exporters/scope.test.ts +1 -2
  100. package/plugins/design/dev-server/test/exporters/zip.test.ts +1 -2
  101. package/plugins/design/dev-server/test/git-lifecycle.test.ts +1 -2
  102. package/plugins/design/dev-server/test/handoff-static-frames.test.ts +1 -1
  103. package/plugins/design/dev-server/test/handoff.test.ts +1 -1
  104. package/plugins/design/dev-server/test/hmr-broadcast.test.ts +1 -1
  105. package/plugins/design/dev-server/test/input-router.test.ts +2 -2
  106. package/plugins/design/dev-server/test/locator.test.ts +1 -1
  107. package/plugins/design/dev-server/test/runtime-bundle.test.ts +1 -1
  108. package/plugins/design/dev-server/test/shared-doc-convergence.test.ts +1 -1
  109. package/plugins/design/dev-server/test/shared-doc-migrate.test.ts +2 -3
  110. package/plugins/design/dev-server/test/shared-doc-projection.test.ts +1 -1
  111. package/plugins/design/dev-server/test/snap-distance-pill.test.ts +1 -1
  112. package/plugins/design/dev-server/test/sync-agent.test.ts +2 -3
  113. package/plugins/design/dev-server/test/sync-atomic-write.test.ts +2 -3
  114. package/plugins/design/dev-server/test/sync-codec.test.ts +1 -1
  115. package/plugins/design/dev-server/test/sync-connection-state.test.ts +1 -1
  116. package/plugins/design/dev-server/test/sync-echo-guard.test.ts +1 -1
  117. package/plugins/design/dev-server/test/sync-fs-mirror.test.ts +1 -2
  118. package/plugins/design/dev-server/test/sync-hardening.test.ts +4 -5
  119. package/plugins/design/dev-server/test/sync-hubs-config.test.ts +1 -2
  120. package/plugins/design/dev-server/test/sync-meta-codec.test.ts +1 -1
  121. package/plugins/design/dev-server/test/sync-runtime.test.ts +37 -14
  122. package/plugins/design/dev-server/test/sync-status.test.ts +1 -1
  123. package/plugins/design/dev-server/test/sync-untrusted.test.ts +1 -2
  124. package/plugins/design/dev-server/test/undo-stack.test.ts +2 -2
  125. package/plugins/design/dev-server/test/use-agent-presence.test.tsx +117 -0
  126. package/plugins/design/dev-server/test/use-artboard-drag.test.ts +3 -3
  127. package/plugins/design/dev-server/test/use-canvas-activity.test.tsx +206 -0
  128. package/plugins/design/dev-server/test/use-selection-set.test.tsx +1 -1
  129. package/plugins/design/dev-server/test/use-snap-guides.test.ts +1 -1
  130. package/plugins/design/dev-server/test/use-undo-stack.test.tsx +2 -2
  131. package/plugins/design/dev-server/use-agent-presence.tsx +244 -0
  132. package/plugins/design/dev-server/use-annotation-resize.tsx +1 -5
  133. package/plugins/design/dev-server/use-annotation-selection.tsx +2 -6
  134. package/plugins/design/dev-server/use-annotations-visibility.tsx +1 -1
  135. package/plugins/design/dev-server/use-artboard-drag.tsx +1 -1
  136. package/plugins/design/dev-server/use-canvas-activity.tsx +252 -0
  137. package/plugins/design/dev-server/use-collab.tsx +3 -4
  138. package/plugins/design/dev-server/use-selection-set.tsx +1 -1
  139. package/plugins/design/dev-server/use-tool-mode.tsx +1 -1
  140. package/plugins/design/dev-server/use-undo-stack.tsx +4 -5
  141. package/plugins/design/dev-server/ws.ts +21 -2
  142. package/plugins/design/templates/_shell.html +108 -3
  143. package/plugins/design/templates/design-system-inspiration/SUB-AGENT-PROMPTS.md +18 -3
@@ -27,9 +27,12 @@
27
27
  */
28
28
 
29
29
  import {
30
+ Component,
30
31
  type ComponentType,
31
- type ReactNode,
32
32
  createElement,
33
+ Fragment,
34
+ type ReactNode,
35
+ useCallback,
33
36
  useEffect,
34
37
  useMemo,
35
38
  useRef,
@@ -347,6 +350,27 @@ export interface MountCanvasOptions {
347
350
  commentsEnabled: boolean;
348
351
  }
349
352
 
353
+ // The lite provider tree wrapping a canvas component (tool + selection +
354
+ // comment layer). Pulled out so both the live render and the error-fallback
355
+ // render build the identical envelope.
356
+ //
357
+ // NB: the CanvasActivityProvider (Phase 13 / DDR-029) is NOT mounted here — it
358
+ // lives inside `DesignCanvas` (canvas-lib). comment-mount.js and canvas-lib are
359
+ // SEPARATE bundles, so a context provided here would be a different instance
360
+ // from the one DCArtboard (canvas-lib) consumes. Same reasoning the real
361
+ // ToolProvider lives in DesignCanvas, not in this layer's MaybeToolProvider.
362
+ function buildCanvasTree(Canvas: ComponentType, file: string | undefined): ReactNode {
363
+ return createElement(
364
+ MaybeToolProvider,
365
+ null,
366
+ createElement(
367
+ MaybeSelectionSetProvider,
368
+ null,
369
+ createElement(CommentHost, { file }, createElement(Canvas))
370
+ )
371
+ );
372
+ }
373
+
350
374
  export function mountCanvas(Canvas: ComponentType, opts: MountCanvasOptions): void {
351
375
  const root = createRoot(opts.rootEl);
352
376
  if (!opts.commentsEnabled) {
@@ -354,15 +378,146 @@ export function mountCanvas(Canvas: ComponentType, opts: MountCanvasOptions): vo
354
378
  return;
355
379
  }
356
380
  const file = opts.file ?? deriveFile();
357
- root.render(
381
+ root.render(createElement(CanvasHmrRuntime, { initialCanvas: Canvas, file }));
382
+ }
383
+
384
+ // ─────────────────────────────────────────────────────────────────────────────
385
+ // Phase 13.1 / DDR-077 — HMR error resilience during agent editing.
386
+ //
387
+ // When an agent (`/design:edit` / `/design:new`) live-edits a canvas, a
388
+ // half-saved file (missing import, undefined symbol, transpile error) used to
389
+ // blank the canvas to white: the shell did `location.reload()` straight into the
390
+ // broken module. The shell now soft-reloads (import-before-swap, see
391
+ // `templates/_shell.html`) so a build/import error never tears down the good
392
+ // render. This runtime closes the remaining gap — a *render-time* throw — by
393
+ // keeping the last good canvas mounted via an error boundary and surfacing a
394
+ // "holding last good" toast instead of a white screen. Strictly gated by the
395
+ // shell on agent-active; manual edits keep the plain reload (so this is inert
396
+ // for solo hand-editing). The runtime exposes its swap/hold API on
397
+ // `window.__maudeCanvasRuntime` (the same window-handshake style the shell
398
+ // already uses for `__canvas_rel__` etc.).
399
+
400
+ export interface CanvasRuntimeApi {
401
+ /** Swap in a freshly-imported canvas module's default export (success path). */
402
+ remount: (next: ComponentType) => void;
403
+ /** Show/hide the "holding last good" toast (build-error path from the shell). */
404
+ setHolding: (on: boolean, message?: string) => void;
405
+ }
406
+
407
+ const RUNTIME_KEY = '__maudeCanvasRuntime';
408
+
409
+ /**
410
+ * Fires `onOk` only when its subtree COMMITS — i.e. renders without throwing. A
411
+ * render-time throw in the canvas unwinds to the boundary before this commits,
412
+ * so `onOk` never runs and `lastGood` is not advanced to a broken module.
413
+ */
414
+ function OkSignal({
415
+ Canvas,
416
+ file,
417
+ onOk,
418
+ }: {
419
+ Canvas: ComponentType;
420
+ file: string | undefined;
421
+ onOk: () => void;
422
+ }) {
423
+ // biome-ignore lint/correctness/useExhaustiveDependencies: fire once per mount (key=attempt remounts this).
424
+ useEffect(() => {
425
+ onOk();
426
+ }, []);
427
+ return buildCanvasTree(Canvas, file);
428
+ }
429
+
430
+ export class CanvasErrorBoundary extends Component<
431
+ {
432
+ attempt: number;
433
+ onError: () => void;
434
+ fallback: () => ReactNode;
435
+ children: ReactNode;
436
+ },
437
+ { hasError: boolean }
438
+ > {
439
+ state = { hasError: false };
440
+ static getDerivedStateFromError(): { hasError: boolean } {
441
+ return { hasError: true };
442
+ }
443
+ componentDidCatch(): void {
444
+ this.props.onError();
445
+ }
446
+ componentDidUpdate(prev: { attempt: number }): void {
447
+ // A new canvas (new attempt) arrived → clear the error and try rendering it.
448
+ if (prev.attempt !== this.props.attempt && this.state.hasError) {
449
+ this.setState({ hasError: false });
450
+ }
451
+ }
452
+ render(): ReactNode {
453
+ if (this.state.hasError) return this.props.fallback();
454
+ return this.props.children;
455
+ }
456
+ }
457
+
458
+ function HmrHoldingToast({ message }: { message?: string }): ReactNode {
459
+ return createElement(
460
+ 'div',
461
+ {
462
+ className: 'dc-hmr-holding',
463
+ role: 'status',
464
+ 'aria-live': 'polite',
465
+ title: message ? `Holding last working render — ${message}` : 'Holding last working render',
466
+ },
467
+ '⏸ build error — držím poslední funkční verzi'
468
+ );
469
+ }
470
+
471
+ function CanvasHmrRuntime({
472
+ initialCanvas,
473
+ file,
474
+ }: {
475
+ initialCanvas: ComponentType;
476
+ file: string | undefined;
477
+ }): ReactNode {
478
+ const [{ canvas, attempt }, setCanvasState] = useState({ canvas: initialCanvas, attempt: 0 });
479
+ const [holding, setHoldingState] = useState<{ on: boolean; message?: string }>({ on: false });
480
+ const lastGood = useRef<ComponentType | null>(null);
481
+ const canvasRef = useRef(canvas);
482
+ canvasRef.current = canvas;
483
+
484
+ // Publish the runtime API for the shell HMR client.
485
+ useEffect(() => {
486
+ const api: CanvasRuntimeApi = {
487
+ remount: (next) => setCanvasState((s) => ({ canvas: next, attempt: s.attempt + 1 })),
488
+ setHolding: (on, message) => setHoldingState(on ? { on: true, message } : { on: false }),
489
+ };
490
+ (window as unknown as Record<string, unknown>)[RUNTIME_KEY] = api;
491
+ return () => {
492
+ (window as unknown as Record<string, unknown>)[RUNTIME_KEY] = undefined;
493
+ };
494
+ }, []);
495
+
496
+ const handleOk = useCallback(() => {
497
+ lastGood.current = canvasRef.current;
498
+ // The new canvas rendered clean → drop any holding state.
499
+ setHoldingState((h) => (h.on ? { on: false } : h));
500
+ }, []);
501
+
502
+ const handleError = useCallback(() => {
503
+ setHoldingState({ on: true, message: 'render error' });
504
+ }, []);
505
+
506
+ const fallback = useCallback((): ReactNode => {
507
+ const LG = lastGood.current;
508
+ return LG ? buildCanvasTree(LG, file) : null;
509
+ }, [file]);
510
+
511
+ return createElement(
512
+ Fragment,
513
+ null,
358
514
  createElement(
359
- MaybeToolProvider,
360
- null,
361
- createElement(
362
- MaybeSelectionSetProvider,
363
- null,
364
- createElement(CommentHost, { file }, createElement(Canvas))
365
- )
366
- )
515
+ CanvasErrorBoundary,
516
+ { attempt, onError: handleError, fallback },
517
+ // key=attempt → each soft-reload remounts a fresh subtree (and resets the
518
+ // boundary's child), matching the clean-slate semantics of a full reload.
519
+ createElement(OkSignal, { key: attempt, Canvas: canvas, file, onOk: handleOk })
520
+ ),
521
+ holding.on ? createElement(HmrHoldingToast, { message: holding.message }) : null
367
522
  );
368
523
  }
@@ -168,6 +168,6 @@ export const TOOL_CURSORS: Record<Tool, string> = Object.freeze({
168
168
  */
169
169
  export function resolveToolCursor(token: unknown): string | null {
170
170
  if (typeof token !== 'string' || !/^[a-z-]+$/.test(token)) return null;
171
- if (!Object.prototype.hasOwnProperty.call(TOOL_CURSORS, token)) return null;
171
+ if (!Object.hasOwn(TOOL_CURSORS, token)) return null;
172
172
  return TOOL_CURSORS[token as Tool];
173
173
  }
@@ -67,14 +67,19 @@
67
67
  * deprecation log at dev-server boot.
68
68
  */
69
69
 
70
+ import {
71
+ AnimatePresence as _MotionAnimatePresence,
72
+ motion as _motionImpl,
73
+ useReducedMotion as _useReducedMotion,
74
+ } from 'motion/react';
70
75
  import {
71
76
  type CSSProperties,
77
+ createContext,
72
78
  Fragment,
79
+ isValidElement,
73
80
  type ReactNode,
74
81
  type PointerEvent as ReactPointerEvent,
75
82
  type RefObject,
76
- createContext,
77
- isValidElement,
78
83
  useCallback,
79
84
  useContext,
80
85
  useEffect,
@@ -84,18 +89,19 @@ import {
84
89
  useState,
85
90
  } from 'react';
86
91
 
87
- import {
88
- AnimatePresence as _MotionAnimatePresence,
89
- motion as _motionImpl,
90
- useReducedMotion as _useReducedMotion,
91
- } from 'motion/react';
92
-
92
+ import { ArtboardActivityOverlay } from './artboard-activity-overlay.tsx';
93
93
  import { CanvasShell } from './canvas-shell.tsx';
94
94
  import {
95
95
  buildMoveArtboardsRecord,
96
96
  diffLayoutPositions,
97
97
  } from './commands/move-artboards-command.ts';
98
+ import { AgentPresenceProvider, useAgentPresence } from './use-agent-presence.tsx';
98
99
  import { type DragState, useArtboardDrag } from './use-artboard-drag.tsx';
100
+ import {
101
+ CanvasActivityProvider,
102
+ matchesArtboard,
103
+ useCanvasActivity,
104
+ } from './use-canvas-activity.tsx';
99
105
  import { CollabProvider, canvasSlugFromPath } from './use-collab.tsx';
100
106
  import { useSelectionSetOptional } from './use-selection-set.tsx';
101
107
  import { MaybeToolProvider, useToolModeOptional } from './use-tool-mode.tsx';
@@ -1192,9 +1198,22 @@ export function DesignCanvas(props: DesignCanvasProps) {
1192
1198
  </MaybeToolProvider>
1193
1199
  );
1194
1200
  return (
1195
- <UndoStackProvider canvasFile={canvasFile}>
1196
- {collabSlug ? <CollabProvider slug={collabSlug}>{inner}</CollabProvider> : inner}
1197
- </UndoStackProvider>
1201
+ // Phase 13 / DDR-029 — the activity context MUST be provided here (canvas-lib
1202
+ // bundle) so DCArtboard's `useCanvasActivity()` reads the SAME context
1203
+ // instance. Providing it from comment-mount.js (a separate bundle) would
1204
+ // create a second ActivityContext the consumer never sees. `canvasFile` is
1205
+ // normalized to the server's design-root-relative activity key inside the
1206
+ // provider (falls back to window.__canvas_rel__).
1207
+ <CanvasActivityProvider file={canvasFile}>
1208
+ {/* Phase 13.2 / DDR-078 — agent presence. Same canvas-lib-bundle rule as
1209
+ the activity context: provided here so DCArtboard + ParticipantsChrome
1210
+ (both canvas-lib) read one context instance. */}
1211
+ <AgentPresenceProvider file={canvasFile}>
1212
+ <UndoStackProvider canvasFile={canvasFile}>
1213
+ {collabSlug ? <CollabProvider slug={collabSlug}>{inner}</CollabProvider> : inner}
1214
+ </UndoStackProvider>
1215
+ </AgentPresenceProvider>
1216
+ </CanvasActivityProvider>
1198
1217
  );
1199
1218
  }
1200
1219
  DesignCanvas.displayName = 'DesignCanvas';
@@ -1510,6 +1529,12 @@ export function DCArtboard({
1510
1529
  const toolMode = useToolModeOptional();
1511
1530
  const selSet = useSelectionSetOptional();
1512
1531
  const dragBus = useDragStateContext();
1532
+ // Phase 13 / DDR-029 — live "agent works here" overlay. Inert (present=false)
1533
+ // outside CanvasActivityProvider, so specimens / legacy mounts never show it.
1534
+ const activity = useCanvasActivity();
1535
+ // Phase 13.2 / DDR-078 — when an agent is the editor, tint the overlay with
1536
+ // its presence color + show its funny name instead of the generic file label.
1537
+ const agent = useAgentPresence();
1513
1538
  const rect = ctx ? ctx.rectFor(id) : null;
1514
1539
 
1515
1540
  // Drag hook — always called (hook rules). Inert outside DesignCanvas
@@ -1583,19 +1608,40 @@ export function DCArtboard({
1583
1608
 
1584
1609
  const handleProps = dragHook.bindHandle();
1585
1610
 
1611
+ // Phase 13 — overlay when THIS canvas is active and the change scope includes
1612
+ // this artboard (null scope = file-level = every artboard). Rendered as a
1613
+ // world-coord sibling of the <article> so it pans/zooms with the artboard.
1614
+ const showActivity = activity.present && matchesArtboard(activity.artboardIds, id);
1615
+ // Agent-driven → funny name + agent color; manual edit → file label + default hue.
1616
+ const activityLabel = agent
1617
+ ? agent.name
1618
+ : activity.artboardIds
1619
+ ? `${activity.fileLabel}:${label}`
1620
+ : activity.fileLabel;
1621
+
1586
1622
  return (
1587
- <article
1588
- className={`dc-artboard dc-positioned${isInDrag ? ' dc-dragging' : ''}`}
1589
- data-dc-screen={id}
1590
- aria-current={isActive ? 'true' : undefined}
1591
- style={{ left: liveX, top: liveY, width: rect.w, height: rect.h }}
1592
- {...handleProps}
1593
- >
1594
- <button type="button" className="dc-artboard-label sku" aria-label={`Artboard ${label}`}>
1595
- {label}
1596
- </button>
1597
- <div className="dc-artboard-body">{children}</div>
1598
- </article>
1623
+ <>
1624
+ <article
1625
+ className={`dc-artboard dc-positioned${isInDrag ? ' dc-dragging' : ''}`}
1626
+ data-dc-screen={id}
1627
+ aria-current={isActive ? 'true' : undefined}
1628
+ style={{ left: liveX, top: liveY, width: rect.w, height: rect.h }}
1629
+ {...handleProps}
1630
+ >
1631
+ <button type="button" className="dc-artboard-label sku" aria-label={`Artboard ${label}`}>
1632
+ {label}
1633
+ </button>
1634
+ <div className="dc-artboard-body">{children}</div>
1635
+ </article>
1636
+ {showActivity ? (
1637
+ <ArtboardActivityOverlay
1638
+ rect={{ x: liveX, y: liveY, w: rect.w, h: rect.h }}
1639
+ label={activityLabel}
1640
+ active={activity.active}
1641
+ color={agent?.color}
1642
+ />
1643
+ ) : null}
1644
+ </>
1599
1645
  );
1600
1646
  }
1601
1647
  DCArtboard.displayName = 'DCArtboard';
@@ -2157,11 +2203,7 @@ export function SpecimenHeader({
2157
2203
  }
2158
2204
 
2159
2205
  /** `<dl class="specimen-meta">` ladder. */
2160
- export function SpecimenMeta({
2161
- entries,
2162
- }: {
2163
- entries: Array<{ label: string; value: ReactNode }>;
2164
- }) {
2206
+ export function SpecimenMeta({ entries }: { entries: Array<{ label: string; value: ReactNode }> }) {
2165
2207
  return (
2166
2208
  <dl className="specimen-meta">
2167
2209
  {entries.map(({ label, value }) => (
@@ -2180,13 +2222,7 @@ export function KbdHint({ children }: { children: ReactNode }) {
2180
2222
  }
2181
2223
 
2182
2224
  /** Inline `var(--name)` value visualiser — small chip + token name. */
2183
- export function TokenChip({
2184
- name,
2185
- swatch,
2186
- }: {
2187
- name: string;
2188
- swatch?: boolean;
2189
- }) {
2225
+ export function TokenChip({ name, swatch }: { name: string; swatch?: boolean }) {
2190
2226
  return (
2191
2227
  <span className="token-chip" data-token={name}>
2192
2228
  {swatch ? (
@@ -2716,7 +2752,7 @@ export function ReducedMotionToggle() {
2716
2752
  }
2717
2753
 
2718
2754
  export {
2755
+ _MotionAnimatePresence as AnimatePresence,
2719
2756
  _motionImpl as motion,
2720
2757
  _useReducedMotion as useReducedMotion,
2721
- _MotionAnimatePresence as AnimatePresence,
2722
2758
  };
@@ -42,10 +42,10 @@ import { ArtboardMarqueeOverlay } from './artboard-marquee.tsx';
42
42
  import {
43
43
  type ArtboardRect,
44
44
  SnapGuideOverlay,
45
- type ViewportControllerHandle,
46
45
  useArtboardsContext,
47
46
  useDragStateContext,
48
47
  useViewportControllerContext,
48
+ type ViewportControllerHandle,
49
49
  } from './canvas-lib.tsx';
50
50
  import { type AlignMode, alignLabel, equalSpacingLabel } from './commands/equal-spacing-command.ts';
51
51
  import {
@@ -27,8 +27,8 @@
27
27
  // originating clientID — the random 32-bit ids make local↔remote collisions
28
28
  // negligible.
29
29
 
30
- import { applyAwarenessUpdate, encodeAwarenessUpdate } from 'y-protocols/awareness';
31
30
  import type { Awareness } from 'y-protocols/awareness';
31
+ import { applyAwarenessUpdate, encodeAwarenessUpdate } from 'y-protocols/awareness';
32
32
 
33
33
  interface AwarenessChange {
34
34
  added: number[];
@@ -7,13 +7,13 @@ import path from 'node:path';
7
7
  import type { Api } from '../api.ts';
8
8
  import type { Context } from '../context.ts';
9
9
 
10
- import { Y_TYPES, createPersistence } from './persistence.ts';
11
- import { type Registry, createRegistry } from './registry.ts';
10
+ import { createPersistence, Y_TYPES } from './persistence.ts';
11
+ import { createRegistry, type Registry } from './registry.ts';
12
12
 
13
- export { Y_TYPES };
13
+ export type { CollabConn } from './protocol.ts';
14
14
  export type { Registry } from './registry.ts';
15
15
  export type { Room, RoomConn } from './room.ts';
16
- export type { CollabConn } from './protocol.ts';
16
+ export { Y_TYPES };
17
17
 
18
18
  export interface Collab {
19
19
  registry: Registry;
@@ -8,7 +8,7 @@ import * as Y from 'yjs';
8
8
 
9
9
  import type { Api } from '../api.ts';
10
10
  import type { Context } from '../context.ts';
11
- import { type RoomCallbacks, ensureStateDir } from './room.ts';
11
+ import { ensureStateDir, type RoomCallbacks } from './room.ts';
12
12
 
13
13
  /**
14
14
  * Y.Doc shared-type names. Frozen on Task 1 so client + server agree even
@@ -206,7 +206,8 @@
206
206
  },
207
207
  "syncTsx": {
208
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."
209
+ "default": true,
210
+ "description": "DDR-079 (supersedes DDR-072) — project-level TSX sync, ON BY DEFAULT. Omit the field to sync ALL .tsx canvases to this hub (the common case: a linked teammate sees the project's TSX without a hidden opt-in). Set false to opt the whole project OUT. A per-canvas .meta.json \"syncable\": false still excludes one canvas (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. TSX bodies execute, so syncing broadens a WebRTC/self-nav exfil residual to every synced canvas — link only hubs you operate or trust; the dev-server prints a loud boot banner against non-loopback hubs."
210
211
  }
211
212
  },
212
213
  "additionalProperties": false
@@ -20,8 +20,8 @@
20
20
  */
21
21
 
22
22
  import {
23
- type ReactNode,
24
23
  createContext,
24
+ type ReactNode,
25
25
  useCallback,
26
26
  useContext,
27
27
  useEffect,