@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
@@ -4,12 +4,11 @@
4
4
  // `_active.json` shapes. `project-raw` exercises the fs walk inside a
5
5
  // throwaway temp dir; the other three branches don't touch disk.
6
6
 
7
+ import { describe, expect, test } from 'bun:test';
7
8
  import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
8
9
  import { tmpdir } from 'node:os';
9
10
  import { join } from 'node:path';
10
11
 
11
- import { describe, expect, test } from 'bun:test';
12
-
13
12
  import { type ActiveJsonShape, resolveScope } from '../../exporters/scope.ts';
14
13
 
15
14
  function emptyActive(): ActiveJsonShape {
@@ -3,11 +3,10 @@
3
3
  // Real walk + bundle against a sandboxed designRoot, then unzip the result
4
4
  // and diff against expected contents.
5
5
 
6
+ import { describe, expect, test } from 'bun:test';
6
7
  import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
7
8
  import { tmpdir } from 'node:os';
8
9
  import { join } from 'node:path';
9
-
10
- import { describe, expect, test } from 'bun:test';
11
10
  import JSZip from 'jszip';
12
11
 
13
12
  import { resolveScope } from '../../exporters/scope.ts';
@@ -8,12 +8,11 @@
8
8
  // already exposes flushAll(). The integration with .git/HEAD writes is
9
9
  // exercised manually in the Task 7 acceptance test (Phase 8 plan validate row).
10
10
 
11
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
11
12
  import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
12
13
  import { tmpdir } from 'node:os';
13
14
  import path from 'node:path';
14
15
 
15
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
16
-
17
16
  import { createGitLifecycle } from '../collab/git-lifecycle.ts';
18
17
  import { createRegistry } from '../collab/registry.ts';
19
18
  import type { RoomCallbacks } from '../collab/room.ts';
@@ -5,7 +5,7 @@
5
5
  import { describe, expect, test } from 'bun:test';
6
6
 
7
7
  import { buildLibMap, inlineUsedExports } from '../canvas-lib-inline.ts';
8
- import { HANDOFF_STATIC_FRAME_EXPORTS, applyHandoffStaticOverrides } from '../handoff.ts';
8
+ import { applyHandoffStaticOverrides, HANDOFF_STATIC_FRAME_EXPORTS } from '../handoff.ts';
9
9
 
10
10
  const LIB_PATH = '/virtual/_lib/canvas-lib.tsx';
11
11
 
@@ -9,12 +9,12 @@ import path from 'node:path';
9
9
 
10
10
  import { transpileCanvasSource } from '../canvas-pipeline.ts';
11
11
  import {
12
- type RegistryItem,
13
12
  classifyImports,
14
13
  collectClassNames,
15
14
  emitRegistryItem,
16
15
  filterComponentsCss,
17
16
  filterTokensCss,
17
+ type RegistryItem,
18
18
  stripDataCdId,
19
19
  writeRegistryItem,
20
20
  } from '../handoff.ts';
@@ -3,7 +3,7 @@
3
3
  import { describe, expect, test } from 'bun:test';
4
4
 
5
5
  import { type Context, createBus } from '../context.ts';
6
- import { HMR_DEBOUNCE_MS, type HmrMessage, createHmrBroadcaster } from '../hmr-broadcast.ts';
6
+ import { createHmrBroadcaster, HMR_DEBOUNCE_MS, type HmrMessage } from '../hmr-broadcast.ts';
7
7
 
8
8
  function mkCtx(): Context {
9
9
  const bus = createBus();
@@ -4,11 +4,11 @@ import { describe, expect, test } from 'bun:test';
4
4
 
5
5
  import {
6
6
  type ClassifyInput,
7
- DRAG_THRESHOLD_PX,
8
- type Tool,
9
7
  classify,
10
8
  crossedDragThreshold,
9
+ DRAG_THRESHOLD_PX,
11
10
  isEditableTarget,
11
+ type Tool,
12
12
  } from '../input-router.tsx';
13
13
 
14
14
  const base = (over: Partial<ClassifyInput>): ClassifyInput => ({
@@ -4,9 +4,9 @@ import { tmpdir } from 'node:os';
4
4
  import path from 'node:path';
5
5
 
6
6
  import {
7
- type LocatorMap,
8
7
  canvasSlug,
9
8
  clearLocatorSlug,
9
+ type LocatorMap,
10
10
  readLocator,
11
11
  readLocatorFile,
12
12
  writeLocator,
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { describe, expect, test } from 'bun:test';
10
10
 
11
- import { RUNTIME_PACKAGES, getRuntimeBundle, packageForSlug, slugFor } from '../runtime-bundle.ts';
11
+ import { getRuntimeBundle, packageForSlug, RUNTIME_PACKAGES, slugFor } from '../runtime-bundle.ts';
12
12
 
13
13
  describe('runtime-bundle', () => {
14
14
  test('builds all four sub-bundles successfully', async () => {
@@ -24,11 +24,11 @@ import * as Y from 'yjs';
24
24
 
25
25
  import { Y_TYPES } from '../collab/persistence.ts';
26
26
  import {
27
- Y_SYNC_TYPES,
28
27
  applyCommentsToDoc,
29
28
  applyCssToDoc,
30
29
  applyHtmlToDoc,
31
30
  applyMetaToDoc,
31
+ Y_SYNC_TYPES,
32
32
  } from '../sync/codec.ts';
33
33
  import { createEchoGuard, hashBytes } from '../sync/echo-guard.ts';
34
34
  import { materialize, materializeCanonical } from '../sync/materialize.ts';
@@ -4,14 +4,13 @@
4
4
  // populated from ONE source. These tests prove migrateSeed never duplicates
5
5
  // comments and is idempotent across re-runs (server restarts).
6
6
 
7
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
7
8
  import { mkdtempSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
8
9
  import { tmpdir } from 'node:os';
9
10
  import { join } from 'node:path';
10
-
11
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
12
11
  import * as Y from 'yjs';
13
12
 
14
- import { Y_TYPES, createPersistence } from '../collab/persistence.ts';
13
+ import { createPersistence, Y_TYPES } from '../collab/persistence.ts';
15
14
  import type { Context } from '../context.ts';
16
15
  import { applyCommentsToDoc, applyHtmlToDoc } from '../sync/codec.ts';
17
16
  import { docIsEmpty, migrateSeed } from '../sync/migrate-seed.ts';
@@ -22,7 +22,7 @@ import { describe, expect, test } from 'bun:test';
22
22
  import * as Y from 'yjs';
23
23
 
24
24
  import { Y_TYPES } from '../collab/persistence.ts';
25
- import { MAX_HTML_BYTES, applyHtmlToDoc } from '../sync/codec.ts';
25
+ import { applyHtmlToDoc, MAX_HTML_BYTES } from '../sync/codec.ts';
26
26
  import { createEchoGuard, hashBytes } from '../sync/echo-guard.ts';
27
27
  import { ORIGINS } from '../sync/origins.ts';
28
28
  import { createDocProjection } from '../sync/projection.ts';
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, expect, test } from 'bun:test';
7
7
 
8
- import { type Rect, type SnapOptions, computeSnap } from '../use-snap-guides.tsx';
8
+ import { computeSnap, type Rect, type SnapOptions } from '../use-snap-guides.tsx';
9
9
 
10
10
  const DEFAULTS: SnapOptions = { gridSize: 40, tolerance: 8, disabled: false };
11
11
  const rect = (x: number, y: number, w = 100, h = 80): Rect => ({ x, y, w, h });
@@ -10,15 +10,14 @@
10
10
  // This lets us validate the whole bidi loop, echo prevention, and the
11
11
  // 100-event stress scenario from the plan without booting a real hub.
12
12
 
13
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
13
14
  import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
14
15
  import { tmpdir } from 'node:os';
15
16
  import { join } from 'node:path';
16
-
17
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
18
17
  import * as Y from 'yjs';
19
18
 
20
19
  import { type CanvasSyncAgent, createCanvasSyncAgent } from '../sync/agent.ts';
21
- import { Y_SYNC_TYPES, applyHtmlToDoc, htmlFromDoc } from '../sync/codec.ts';
20
+ import { applyHtmlToDoc, htmlFromDoc, Y_SYNC_TYPES } from '../sync/codec.ts';
22
21
  import { createEchoGuard, hashBytes } from '../sync/echo-guard.ts';
23
22
  import { createFsReader } from '../sync/fs-mirror.ts';
24
23
 
@@ -1,11 +1,10 @@
1
1
  // Atomic-write unit tests — Phase 9 Task 4.
2
2
 
3
- import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
4
+ import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
4
5
  import { tmpdir } from 'node:os';
5
6
  import { join } from 'node:path';
6
7
 
7
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
8
-
9
8
  import { atomicWrite } from '../sync/atomic-write.ts';
10
9
 
11
10
  let dir: string;
@@ -8,13 +8,13 @@ import * as Y from 'yjs';
8
8
 
9
9
  import { Y_TYPES } from '../collab/persistence.ts';
10
10
  import {
11
- Y_SYNC_TYPES,
12
11
  annotationsFromDoc,
13
12
  applyAnnotationsToDoc,
14
13
  applyCommentsToDoc,
15
14
  applyHtmlToDoc,
16
15
  commentsFromDoc,
17
16
  htmlFromDoc,
17
+ Y_SYNC_TYPES,
18
18
  } from '../sync/codec.ts';
19
19
 
20
20
  describe('HTML codec', () => {
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, expect, test } from 'bun:test';
7
7
 
8
- import { type SyncStatusSnapshot, createConnectionMonitor } from '../sync/connection-state.ts';
8
+ import { createConnectionMonitor, type SyncStatusSnapshot } from '../sync/connection-state.ts';
9
9
 
10
10
  /** A controllable timer queue: setTimer enqueues, advance() fires due timers. */
11
11
  function fakeClock() {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { describe, expect, test } from 'bun:test';
4
4
 
5
- import { ECHO_TTL_MS, createEchoGuard, hashBytes } from '../sync/echo-guard.ts';
5
+ import { createEchoGuard, ECHO_TTL_MS, hashBytes } from '../sync/echo-guard.ts';
6
6
 
7
7
  describe('hashBytes', () => {
8
8
  test('stable hex SHA-256 of bytes', () => {
@@ -1,11 +1,10 @@
1
1
  // fs-mirror unit tests — Phase 9 Task 4.
2
2
 
3
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
3
4
  import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4
5
  import { tmpdir } from 'node:os';
5
6
  import { join } from 'node:path';
6
7
 
7
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
8
-
9
8
  import { hashBytes } from '../sync/echo-guard.ts';
10
9
  import { createFsReader } from '../sync/fs-mirror.ts';
11
10
 
@@ -3,6 +3,7 @@
3
3
  // Targets the 8 quick-win fixes recorded in DDR-054 §2 against the findings
4
4
  // in `.ai/logs/security-reviews/phase-9-task-4-bidi-fs-sync-{defender,attacker}.md`.
5
5
 
6
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
6
7
  import {
7
8
  chmodSync,
8
9
  existsSync,
@@ -15,20 +16,18 @@ import {
15
16
  } from 'node:fs';
16
17
  import { tmpdir } from 'node:os';
17
18
  import { join } from 'node:path';
18
-
19
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
20
19
  import * as Y from 'yjs';
21
20
 
22
21
  import { createCanvasSyncAgent } from '../sync/agent.ts';
23
22
  import { atomicWrite } from '../sync/atomic-write.ts';
24
23
  import {
25
- MAX_ANNOTATIONS_BYTES,
26
- MAX_COMMENTS_BYTES,
27
- MAX_HTML_BYTES,
28
24
  applyAnnotationsToDoc,
29
25
  applyCommentsToDoc,
30
26
  applyHtmlToDoc,
31
27
  htmlFromDoc,
28
+ MAX_ANNOTATIONS_BYTES,
29
+ MAX_COMMENTS_BYTES,
30
+ MAX_HTML_BYTES,
32
31
  } from '../sync/codec.ts';
33
32
  import { createEchoGuard, hashBytes } from '../sync/echo-guard.ts';
34
33
  import { createFsReader } from '../sync/fs-mirror.ts';
@@ -1,11 +1,10 @@
1
1
  // hubs-config reader tests — Phase 9 Task 4.
2
2
 
3
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
3
4
  import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4
5
  import { tmpdir } from 'node:os';
5
6
  import { join } from 'node:path';
6
7
 
7
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
8
-
9
8
  import { getHubToken, hubsConfigPath, loadHubsConfig, normalizeUrl } from '../sync/hubs-config.ts';
10
9
 
11
10
  let dir: string;
@@ -10,10 +10,10 @@ import { describe, expect, test } from 'bun:test';
10
10
  import * as Y from 'yjs';
11
11
 
12
12
  import {
13
- META_LOCAL_KEYS,
14
13
  applyCssToDoc,
15
14
  applyMetaToDoc,
16
15
  cssFromDoc,
16
+ META_LOCAL_KEYS,
17
17
  mergeSharedMetaIntoLocal,
18
18
  metaFromDoc,
19
19
  } from '../sync/codec.ts';
@@ -5,6 +5,7 @@
5
5
  // honors linkedHub config, discovers canvases, wires up agents, and
6
6
  // dispatches fs events through the bus correctly.
7
7
 
8
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
8
9
  import {
9
10
  chmodSync,
10
11
  existsSync,
@@ -16,8 +17,6 @@ import {
16
17
  } from 'node:fs';
17
18
  import { tmpdir } from 'node:os';
18
19
  import { join } from 'node:path';
19
-
20
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
21
20
  import { Awareness } from 'y-protocols/awareness';
22
21
  import * as Y from 'yjs';
23
22
 
@@ -29,10 +28,10 @@ import { createBus } from '../context.ts';
29
28
  import { createConnectionMonitor } from '../sync/connection-state.ts';
30
29
  import {
31
30
  type AwarenessRegistry,
32
- type SyncProvider,
33
31
  buildNoSyncablePayload,
34
32
  createSyncRuntime,
35
33
  discoverCanvases,
34
+ type SyncProvider,
36
35
  scanCanvases,
37
36
  toWsUrl,
38
37
  } from '../sync/index.ts';
@@ -108,11 +107,7 @@ function makeCtx(linkedHub?: DevServerConfig['linkedHub'], canvasOrigin?: string
108
107
  }
109
108
 
110
109
  function inMemoryProviderFactory(): {
111
- factory: (args: {
112
- url: string;
113
- token: string;
114
- documentName: string;
115
- }) => SyncProvider;
110
+ factory: (args: { url: string; token: string; documentName: string }) => SyncProvider;
116
111
  peerOf: (slug: string) => Y.Doc;
117
112
  } {
118
113
  // Map of slug -> { local, peer } Y.Docs cross-linked via applyUpdate.
@@ -783,11 +778,11 @@ describe('scanCanvases', () => {
783
778
  });
784
779
  });
785
780
 
786
- // DDR-072 — project-level TSX opt-in (linkedHub.syncTsx). A .tsx with no
787
- // explicit sidecar verdict defaults to syncable when the project flag is on;
788
- // the per-canvas sidecar still wins; the sandbox coupling is preserved.
789
- describe('scanCanvases — project-level syncTsx (DDR-072)', () => {
790
- test('syncTsx:true + sandbox ON admits a .tsx WITHOUT a per-canvas opt-in', async () => {
781
+ // DDR-079 (was DDR-072) — TSX sync defaults ON. A .tsx with no explicit sidecar
782
+ // verdict syncs by default; `syncTsx: false` opts the project out; the per-canvas
783
+ // sidecar still wins; the Lock-2 sandbox coupling is preserved.
784
+ describe('scanCanvases — project-level syncTsx default-on (DDR-079)', () => {
785
+ test('explicit syncTsx:true + sandbox ON admits a .tsx WITHOUT a per-canvas opt-in', async () => {
791
786
  const ctx = makeCtx(
792
787
  { url: 'https://h.example.com', linkedAt: 1, syncTsx: true },
793
788
  'http://localhost:9'
@@ -826,13 +821,41 @@ describe('scanCanvases — project-level syncTsx (DDR-072)', () => {
826
821
  expect(slugs).not.toContain('ui-secret'); // explicit opt-out wins
827
822
  });
828
823
 
829
- test('without the flag (syncTsx absent) a .tsx still needs the per-canvas opt-in', async () => {
824
+ test('DDR-079: without the flag (syncTsx absent) a .tsx is admitted BY DEFAULT', async () => {
830
825
  const ctx = makeCtx({ url: 'https://h.example.com', linkedAt: 1 }, 'http://localhost:9');
831
826
  writeFileSync(join(ctx.paths.designRoot, 'ui', 'plain.tsx'), 'export default () => null;');
832
827
  const scan = await scanCanvases(ctx);
828
+ expect(scan.canvases.map((c) => c.slug)).toContain('ui-plain'); // default ON
829
+ expect(scan.tsxCount).toBe(0);
830
+ });
831
+
832
+ test('DDR-079: syncTsx:false opts the whole project OUT (.tsx not admitted)', async () => {
833
+ const ctx = makeCtx(
834
+ { url: 'https://h.example.com', linkedAt: 1, syncTsx: false },
835
+ 'http://localhost:9'
836
+ );
837
+ writeFileSync(join(ctx.paths.designRoot, 'ui', 'plain.tsx'), 'export default () => null;');
838
+ const scan = await scanCanvases(ctx);
833
839
  expect(scan.canvases.map((c) => c.slug)).not.toContain('ui-plain');
834
840
  expect(scan.tsxCount).toBe(1);
835
841
  });
842
+
843
+ test('DDR-079: per-canvas "syncable": true still admits one .tsx even when project opted out', async () => {
844
+ const ctx = makeCtx(
845
+ { url: 'https://h.example.com', linkedAt: 1, syncTsx: false },
846
+ 'http://localhost:9'
847
+ );
848
+ writeFileSync(join(ctx.paths.designRoot, 'ui', 'keep.tsx'), 'export default () => null;');
849
+ writeFileSync(
850
+ join(ctx.paths.designRoot, 'ui', 'keep.meta.json'),
851
+ JSON.stringify({ syncable: true, title: 'Keep' })
852
+ );
853
+ writeFileSync(join(ctx.paths.designRoot, 'ui', 'drop.tsx'), 'export default () => null;');
854
+ const scan = await scanCanvases(ctx);
855
+ const slugs = scan.canvases.map((c) => c.slug);
856
+ expect(slugs).toContain('ui-keep'); // sidecar opt-in wins over project opt-out
857
+ expect(slugs).not.toContain('ui-drop'); // project opted out
858
+ });
836
859
  });
837
860
 
838
861
  describe('buildNoSyncablePayload', () => {
@@ -3,7 +3,7 @@
3
3
  import { describe, expect, test } from 'bun:test';
4
4
 
5
5
  import type { SyncStatusSnapshot } from '../sync/connection-state.ts';
6
- import { type SyncStatusPayload, createSyncStatusStore } from '../sync/status.ts';
6
+ import { createSyncStatusStore, type SyncStatusPayload } from '../sync/status.ts';
7
7
 
8
8
  function snap(partial: Partial<SyncStatusSnapshot> = {}): SyncStatusSnapshot {
9
9
  return {
@@ -3,12 +3,11 @@
3
3
  // for the syncable set, cleared when empty, stale entries dropped on re-write,
4
4
  // and user-authored `.claudeignore` content outside the block preserved.
5
5
 
6
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
6
7
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
7
8
  import { tmpdir } from 'node:os';
8
9
  import { join } from 'node:path';
9
10
 
10
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
11
-
12
11
  import type { Context } from '../context.ts';
13
12
  import type { CanvasDescriptor } from '../sync/index.ts';
14
13
  import { clearUntrustedMarkers, writeUntrustedMarkers } from '../sync/untrusted.ts';
@@ -1,14 +1,14 @@
1
1
  import { afterEach, describe, expect, test } from 'bun:test';
2
2
 
3
3
  import {
4
- type CommandRecord,
5
- MAX_DEPTH,
6
4
  _clearBuilderRegistry,
7
5
  _clearStackStore,
6
+ type CommandRecord,
8
7
  canRedo,
9
8
  canUndo,
10
9
  createUndoStackState,
11
10
  loadStackState,
11
+ MAX_DEPTH,
12
12
  peekRedo,
13
13
  peekUndo,
14
14
  rebuildCommand,
@@ -0,0 +1,117 @@
1
+ // use-agent-presence — Phase 13.2. Funny-name/color derivation + provider gating.
2
+ // Live wiring (postMessage / WS subscription) is skipped via the `initialAgent`
3
+ // seed, so this stays SSR-renderable (renderToStaticMarkup capture pattern).
4
+
5
+ import { describe, expect, test } from 'bun:test';
6
+
7
+ import type { ReactNode } from 'react';
8
+ import { renderToStaticMarkup } from 'react-dom/server';
9
+
10
+ import {
11
+ type AgentPresence,
12
+ AgentPresenceProvider,
13
+ agentFunnyName,
14
+ deriveAgent,
15
+ useAgentPresence,
16
+ } from '../use-agent-presence.tsx';
17
+
18
+ describe('use-agent-presence / agentFunnyName', () => {
19
+ test('deterministic — same seed → same name', () => {
20
+ expect(agentFunnyName('Claude:1700000000000')).toBe(agentFunnyName('Claude:1700000000000'));
21
+ });
22
+
23
+ test('shape is "Adjective Animal"', () => {
24
+ expect(agentFunnyName('seed-x')).toMatch(/^[A-Z][a-z]+ [A-Z][a-z]+$/);
25
+ });
26
+
27
+ test('different seeds usually differ (no constant collapse)', () => {
28
+ const names = new Set(
29
+ Array.from({ length: 24 }, (_, i) => agentFunnyName(`Claude:${1700000000000 + i * 7919}`))
30
+ );
31
+ // Not all 24 identical — the generator actually spreads.
32
+ expect(names.size).toBeGreaterThan(5);
33
+ });
34
+
35
+ test('empty seed is handled (no throw, stable)', () => {
36
+ expect(agentFunnyName('')).toBe(agentFunnyName(''));
37
+ });
38
+ });
39
+
40
+ describe('use-agent-presence / deriveAgent', () => {
41
+ const entry = {
42
+ file: 'ui/Foo.tsx',
43
+ author: 'Claude (acting for Ada)',
44
+ startedAt: 1700000000000,
45
+ lastHeartbeat: 1700000000000,
46
+ };
47
+
48
+ test('id = author:startedAt; name = funny; color present; author preserved', () => {
49
+ const a = deriveAgent(entry);
50
+ expect(a.id).toBe('Claude (acting for Ada):1700000000000');
51
+ expect(a.name).toBe(agentFunnyName(a.id));
52
+ expect(a.author).toBe('Claude (acting for Ada)');
53
+ expect(a.startedAt).toBe(1700000000000);
54
+ expect(typeof a.color).toBe('string');
55
+ expect(a.color.length).toBeGreaterThan(0);
56
+ });
57
+
58
+ test('same entry → identical derived agent (stable across heartbeats)', () => {
59
+ expect(deriveAgent(entry)).toEqual(deriveAgent({ ...entry, lastHeartbeat: 9999999999999 }));
60
+ });
61
+ });
62
+
63
+ describe('use-agent-presence / provider gating', () => {
64
+ function Wrap({
65
+ initialAgent,
66
+ children,
67
+ }: {
68
+ initialAgent: AgentPresence | null;
69
+ children: ReactNode;
70
+ }) {
71
+ return <AgentPresenceProvider initialAgent={initialAgent}>{children}</AgentPresenceProvider>;
72
+ }
73
+
74
+ test('seeded agent flows through the hook', () => {
75
+ const seeded = deriveAgent({
76
+ file: 'ui/Foo.tsx',
77
+ author: 'Claude',
78
+ startedAt: 42,
79
+ lastHeartbeat: 42,
80
+ });
81
+ let got: AgentPresence | null = null;
82
+ function Probe() {
83
+ got = useAgentPresence();
84
+ return null;
85
+ }
86
+ renderToStaticMarkup(
87
+ <Wrap initialAgent={seeded}>
88
+ <Probe />
89
+ </Wrap>
90
+ );
91
+ expect(got).toEqual(seeded);
92
+ });
93
+
94
+ test('null seed → no agent', () => {
95
+ let got: AgentPresence | null = { id: 'x' } as AgentPresence;
96
+ function Probe() {
97
+ got = useAgentPresence();
98
+ return null;
99
+ }
100
+ renderToStaticMarkup(
101
+ <Wrap initialAgent={null}>
102
+ <Probe />
103
+ </Wrap>
104
+ );
105
+ expect(got).toBeNull();
106
+ });
107
+
108
+ test('outside the provider the hook is inert (null)', () => {
109
+ let got: AgentPresence | null = { id: 'x' } as AgentPresence;
110
+ function Probe() {
111
+ got = useAgentPresence();
112
+ return null;
113
+ }
114
+ renderToStaticMarkup(<Probe />);
115
+ expect(got).toBeNull();
116
+ });
117
+ });
@@ -8,14 +8,14 @@
8
8
  import { describe, expect, test } from 'bun:test';
9
9
 
10
10
  import {
11
+ commitFromState,
12
+ computeFollowers,
13
+ computeOthers,
11
14
  DEFAULT_GRID_SIZE,
12
15
  DEFAULT_SNAP_TOLERANCE,
13
16
  DRAG_THRESHOLD_PX,
14
17
  type DragEvent,
15
18
  type DragState,
16
- commitFromState,
17
- computeFollowers,
18
- computeOthers,
19
19
  dragReducer,
20
20
  selectionsToArtboardIds,
21
21
  } from '../use-artboard-drag.tsx';