@1agh/maude 1.0.11 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (567) hide show
  1. package/README.md +3 -1
  2. package/apps/studio/acp/bridge.ts +18 -13
  3. package/apps/studio/acp/index.ts +43 -7
  4. package/apps/studio/acp/transcript-io.ts +394 -0
  5. package/apps/studio/acp/transcript.ts +103 -38
  6. package/apps/studio/annotations-layer.tsx +191 -116
  7. package/apps/studio/annotations-model.ts +39 -0
  8. package/apps/studio/annotations-sync.ts +50 -0
  9. package/apps/studio/api.ts +821 -55
  10. package/apps/studio/bin/_import-figma.mjs +57 -11
  11. package/apps/studio/bin/_seed-fixture.mjs +163 -0
  12. package/apps/studio/bin/annotate.mjs +3 -1
  13. package/apps/studio/bin/server-up.sh +17 -2
  14. package/apps/studio/canvas-edit.ts +391 -7
  15. package/apps/studio/canvas-imports.ts +89 -0
  16. package/apps/studio/canvas-lib.tsx +82 -18
  17. package/apps/studio/canvas-notice-message.ts +16 -0
  18. package/apps/studio/canvas-notifications.tsx +19 -0
  19. package/apps/studio/canvas-shell.tsx +57 -6
  20. package/apps/studio/client/app.jsx +943 -248
  21. package/apps/studio/client/apply-edit-request.ts +55 -0
  22. package/apps/studio/client/export-center.jsx +125 -67
  23. package/apps/studio/client/github.js +38 -1
  24. package/apps/studio/client/index-loader.ts +76 -0
  25. package/apps/studio/client/panels/CloudBar.jsx +52 -6
  26. package/apps/studio/client/panels/GitPanel.jsx +103 -0
  27. package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
  28. package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
  29. package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
  30. package/apps/studio/client/panels/SyncPanel.jsx +238 -4
  31. package/apps/studio/client/panels/TeamProjects.jsx +376 -0
  32. package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
  33. package/apps/studio/client/photo-knobs.jsx +3 -0
  34. package/apps/studio/client/report-bug.jsx +19 -1
  35. package/apps/studio/client/share-dialog.jsx +91 -0
  36. package/apps/studio/client/share-link.js +94 -0
  37. package/apps/studio/client/styles/3-shell-maude.css +69 -18
  38. package/apps/studio/client/styles/4-components.css +44 -1
  39. package/apps/studio/client/tree-row-menu.jsx +48 -3
  40. package/apps/studio/client/whats-new.jsx +26 -32
  41. package/apps/studio/cloud/endpoints.ts +132 -0
  42. package/apps/studio/collab/index.ts +4 -0
  43. package/apps/studio/collab/persistence.ts +137 -17
  44. package/apps/studio/collab/registry.ts +19 -21
  45. package/apps/studio/collab/room.ts +13 -2
  46. package/apps/studio/context.ts +59 -0
  47. package/apps/studio/debug-bundle.ts +13 -0
  48. package/apps/studio/dist/client.bundle.js +1911 -1879
  49. package/apps/studio/dist/runtime/.min-sizes.json +1 -0
  50. package/apps/studio/dist/runtime/sonner.js +1 -0
  51. package/apps/studio/dist/styles.css +1 -1
  52. package/apps/studio/export-dialog.tsx +54 -0
  53. package/apps/studio/exporters/degraded.ts +19 -3
  54. package/apps/studio/exporters/ghostscript.ts +194 -0
  55. package/apps/studio/exporters/pdf-fonts.ts +261 -0
  56. package/apps/studio/exporters/pdf.ts +152 -3
  57. package/apps/studio/exporters/remote.ts +36 -1
  58. package/apps/studio/fs-watch.ts +45 -1
  59. package/apps/studio/http.ts +296 -19
  60. package/apps/studio/inspect.ts +19 -0
  61. package/apps/studio/managed-projects.ts +150 -0
  62. package/apps/studio/mem.ts +45 -9
  63. package/apps/studio/notifications.tsx +292 -0
  64. package/apps/studio/runtime-bundle.ts +2 -0
  65. package/apps/studio/server.ts +69 -6
  66. package/apps/studio/sync/accepted-cold-start.ts +225 -0
  67. package/apps/studio/sync/accepted-link.ts +320 -0
  68. package/apps/studio/sync/action-stage.ts +343 -0
  69. package/apps/studio/sync/agent.ts +104 -45
  70. package/apps/studio/sync/asset-push.ts +1 -47
  71. package/apps/studio/sync/cell-file-events.ts +3 -0
  72. package/apps/studio/sync/codec.ts +192 -34
  73. package/apps/studio/sync/cold-start-apply.ts +7 -0
  74. package/apps/studio/sync/cold-start.ts +118 -22
  75. package/apps/studio/sync/comment-identity.ts +68 -0
  76. package/apps/studio/sync/connection-state.ts +15 -1
  77. package/apps/studio/sync/ctl-provider.ts +15 -2
  78. package/apps/studio/sync/document-discovery.ts +81 -0
  79. package/apps/studio/sync/file-ledger.ts +117 -0
  80. package/apps/studio/sync/file-membership.ts +10 -2
  81. package/apps/studio/sync/file-plane.ts +926 -47
  82. package/apps/studio/sync/index.ts +1786 -36
  83. package/apps/studio/sync/journal-client.ts +13 -1
  84. package/apps/studio/sync/migrate-seed.ts +150 -22
  85. package/apps/studio/sync/poke.ts +4 -2
  86. package/apps/studio/sync/presentation.ts +311 -5
  87. package/apps/studio/sync/projection.ts +820 -23
  88. package/apps/studio/sync/repeated-module.ts +73 -0
  89. package/apps/studio/sync/retry-after.ts +169 -0
  90. package/apps/studio/sync/revision-barrier.ts +129 -0
  91. package/apps/studio/sync/seed-progress.ts +218 -0
  92. package/apps/studio/sync/seed-repair.ts +46 -0
  93. package/apps/studio/sync/source-merge.ts +100 -0
  94. package/apps/studio/sync/source-ops.ts +289 -0
  95. package/apps/studio/sync/source-recovery.ts +70 -0
  96. package/apps/studio/sync/source-validation.ts +56 -0
  97. package/apps/studio/sync/status.ts +201 -6
  98. package/apps/studio/sync/transaction-client.ts +460 -0
  99. package/apps/studio/sync/transport-error.ts +111 -0
  100. package/apps/studio/sync/writer-registry.ts +236 -0
  101. package/apps/studio/text-caret.ts +35 -0
  102. package/apps/studio/undo-hud.tsx +9 -87
  103. package/apps/studio/use-canvas-media-drop.tsx +4 -39
  104. package/apps/studio/whats-new.json +173 -0
  105. package/cli/bin/maude.mjs +2 -0
  106. package/cli/commands/codex.mjs +191 -0
  107. package/cli/commands/harness.mjs +1051 -0
  108. package/cli/commands/help.mjs +13 -0
  109. package/cli/lib/design-link.mjs +190 -2
  110. package/cli/lib/harness/capabilities.mjs +302 -0
  111. package/cli/lib/harness/codex-runtime.mjs +892 -0
  112. package/cli/lib/harness/compatibility.mjs +27 -0
  113. package/cli/lib/harness/discover-claude.mjs +1393 -0
  114. package/cli/lib/harness/managed-state.mjs +1296 -0
  115. package/cli/lib/harness/model.mjs +86 -0
  116. package/cli/lib/harness/secrets.mjs +390 -0
  117. package/cli/lib/harness/targets/codex.mjs +1141 -0
  118. package/cli/lib/harness/targets/opencode.mjs +807 -0
  119. package/cli/lib/harness/transaction.mjs +1099 -0
  120. package/cli/lib/hubs-config.mjs +35 -8
  121. package/cli/templates/harness/opencode/maude-projector.ts +58 -0
  122. package/docs/harness-capability-matrix.md +125 -0
  123. package/docs/harness-environment-projection.md +95 -0
  124. package/package.json +33 -9
  125. package/plugins/design/dependencies.json +3 -3
  126. package/plugins/design/templates/_shell.html +47 -0
  127. package/plugins/flow/.claude-plugin/config.schema.json +8 -0
  128. package/plugins/flow/dependencies.json +3 -3
  129. package/apps/studio/.ai/cache/_stats.json +0 -7
  130. package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
  131. package/apps/studio/bin/_curl-local.test.mjs +0 -280
  132. package/apps/studio/bin/_pw-launch.test.mjs +0 -90
  133. package/apps/studio/bin/_smart-frames.test.mjs +0 -195
  134. package/apps/studio/bin/_transcribe.test.mjs +0 -80
  135. package/apps/studio/bun.lock +0 -928
  136. package/apps/studio/draw/test/animate.test.ts +0 -172
  137. package/apps/studio/draw/test/brush.test.ts +0 -213
  138. package/apps/studio/draw/test/composition.test.ts +0 -141
  139. package/apps/studio/draw/test/geometry.test.ts +0 -199
  140. package/apps/studio/draw/test/gradient.test.ts +0 -167
  141. package/apps/studio/draw/test/layout.test.ts +0 -120
  142. package/apps/studio/draw/test/morph.test.ts +0 -107
  143. package/apps/studio/draw/test/optimize.test.ts +0 -40
  144. package/apps/studio/draw/test/palette.test.ts +0 -123
  145. package/apps/studio/draw/test/primitives.test.ts +0 -129
  146. package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
  147. package/apps/studio/draw/test/serialize.test.ts +0 -105
  148. package/apps/studio/figma/assets.test.ts +0 -464
  149. package/apps/studio/figma/client.test.ts +0 -395
  150. package/apps/studio/figma/codegen-client.test.ts +0 -276
  151. package/apps/studio/figma/codegen-fonts.test.ts +0 -103
  152. package/apps/studio/figma/codegen-values.test.ts +0 -179
  153. package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
  154. package/apps/studio/figma/fig-decode.test.ts +0 -788
  155. package/apps/studio/figma/fig-differential.test.ts +0 -182
  156. package/apps/studio/figma/fig-translator.test.ts +0 -192
  157. package/apps/studio/figma/fig-vector.test.ts +0 -113
  158. package/apps/studio/figma/from-codegen.test.ts +0 -408
  159. package/apps/studio/figma/sanitize.test.ts +0 -325
  160. package/apps/studio/figma/tailwind-map.test.ts +0 -142
  161. package/apps/studio/figma/to-artboard.test.ts +0 -808
  162. package/apps/studio/figma/to-render.test.ts +0 -180
  163. package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
  164. package/apps/studio/figma/to-strokes.test.ts +0 -705
  165. package/apps/studio/figma/to-tokens.test.ts +0 -321
  166. package/apps/studio/figma/url.test.ts +0 -167
  167. package/apps/studio/footage/schema.test.ts +0 -287
  168. package/apps/studio/generation/adapters/groq.test.ts +0 -80
  169. package/apps/studio/generation/audio-library.test.ts +0 -95
  170. package/apps/studio/generation/captions.test.ts +0 -78
  171. package/apps/studio/generation/elevenlabs.test.ts +0 -223
  172. package/apps/studio/generation/gemini.test.ts +0 -350
  173. package/apps/studio/generation/jobs.test.ts +0 -128
  174. package/apps/studio/generation/keys.test.ts +0 -75
  175. package/apps/studio/generation/prefs.test.ts +0 -95
  176. package/apps/studio/generation/types.test.ts +0 -123
  177. package/apps/studio/generation/whisper-models.test.ts +0 -179
  178. package/apps/studio/test/_helpers.ts +0 -108
  179. package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
  180. package/apps/studio/test/acp-activity.test.ts +0 -154
  181. package/apps/studio/test/acp-ai-activity.test.ts +0 -182
  182. package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
  183. package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
  184. package/apps/studio/test/acp-branch-guard.test.ts +0 -123
  185. package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
  186. package/apps/studio/test/acp-bridge.test.ts +0 -545
  187. package/apps/studio/test/acp-capabilities.test.ts +0 -123
  188. package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
  189. package/apps/studio/test/acp-commands.test.ts +0 -119
  190. package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
  191. package/apps/studio/test/acp-elicitation.test.ts +0 -251
  192. package/apps/studio/test/acp-env.test.ts +0 -95
  193. package/apps/studio/test/acp-mode-banner.test.ts +0 -45
  194. package/apps/studio/test/acp-origin-gate.test.ts +0 -160
  195. package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
  196. package/apps/studio/test/acp-permission.test.ts +0 -278
  197. package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
  198. package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
  199. package/apps/studio/test/acp-session-plugins.test.ts +0 -209
  200. package/apps/studio/test/acp-toolgroup.test.ts +0 -76
  201. package/apps/studio/test/acp-transcript-view.test.ts +0 -71
  202. package/apps/studio/test/acp-transcript.test.ts +0 -238
  203. package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
  204. package/apps/studio/test/acp-usage.test.ts +0 -143
  205. package/apps/studio/test/acp-write-gate.test.ts +0 -323
  206. package/apps/studio/test/acp-write-scope.test.ts +0 -533
  207. package/apps/studio/test/active-state.test.ts +0 -186
  208. package/apps/studio/test/activity.test.ts +0 -189
  209. package/apps/studio/test/ai-activity.test.ts +0 -113
  210. package/apps/studio/test/annotate-write.test.ts +0 -1074
  211. package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
  212. package/apps/studio/test/annotations-align.test.ts +0 -88
  213. package/apps/studio/test/annotations-api.test.ts +0 -145
  214. package/apps/studio/test/annotations-bindings.test.ts +0 -262
  215. package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
  216. package/apps/studio/test/annotations-groups.test.ts +0 -231
  217. package/apps/studio/test/annotations-layer.test.ts +0 -1011
  218. package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
  219. package/apps/studio/test/annotations-snap.test.ts +0 -135
  220. package/apps/studio/test/annotations-text-format.test.ts +0 -396
  221. package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
  222. package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
  223. package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
  224. package/apps/studio/test/artboard-kinds.test.tsx +0 -83
  225. package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
  226. package/apps/studio/test/asset-api.test.ts +0 -149
  227. package/apps/studio/test/assets-s3.test.ts +0 -249
  228. package/apps/studio/test/binary-smoke.test.ts +0 -47
  229. package/apps/studio/test/boot-self-heal.test.ts +0 -87
  230. package/apps/studio/test/browse-posture.test.tsx +0 -107
  231. package/apps/studio/test/bundle-smoke.test.ts +0 -46
  232. package/apps/studio/test/camera-reveal.test.tsx +0 -173
  233. package/apps/studio/test/canvas-artifacts.test.ts +0 -125
  234. package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
  235. package/apps/studio/test/canvas-build.test.ts +0 -141
  236. package/apps/studio/test/canvas-create-api.test.ts +0 -676
  237. package/apps/studio/test/canvas-cursors.test.ts +0 -164
  238. package/apps/studio/test/canvas-edit.test.ts +0 -470
  239. package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
  240. package/apps/studio/test/canvas-header.test.ts +0 -127
  241. package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
  242. package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
  243. package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
  244. package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
  245. package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
  246. package/apps/studio/test/canvas-list-watch.test.ts +0 -388
  247. package/apps/studio/test/canvas-media-drop.test.ts +0 -187
  248. package/apps/studio/test/canvas-meta-api.test.ts +0 -707
  249. package/apps/studio/test/canvas-move-api.test.ts +0 -587
  250. package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
  251. package/apps/studio/test/canvas-pipeline.test.ts +0 -180
  252. package/apps/studio/test/canvas-rects.test.ts +0 -198
  253. package/apps/studio/test/canvas-reorder.test.ts +0 -211
  254. package/apps/studio/test/canvas-route.test.ts +0 -179
  255. package/apps/studio/test/canvas-shell-base.test.ts +0 -107
  256. package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
  257. package/apps/studio/test/canvas-url.test.ts +0 -138
  258. package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
  259. package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
  260. package/apps/studio/test/capture-core.test.ts +0 -32
  261. package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
  262. package/apps/studio/test/chat-attachments.test.ts +0 -117
  263. package/apps/studio/test/chat-context.test.ts +0 -129
  264. package/apps/studio/test/chat-markdown.test.tsx +0 -58
  265. package/apps/studio/test/clip-addressing.test.ts +0 -738
  266. package/apps/studio/test/clip-ops.test.ts +0 -922
  267. package/apps/studio/test/cloud-build.test.ts +0 -95
  268. package/apps/studio/test/cloud-connect-note.test.ts +0 -135
  269. package/apps/studio/test/cloud-endpoints.test.ts +0 -590
  270. package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
  271. package/apps/studio/test/cloud-history-posture.test.ts +0 -230
  272. package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
  273. package/apps/studio/test/cloud-renew.test.ts +0 -205
  274. package/apps/studio/test/cloud-session-role.test.ts +0 -161
  275. package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
  276. package/apps/studio/test/cold-start-apply.test.ts +0 -303
  277. package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
  278. package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
  279. package/apps/studio/test/collab-bridge.test.ts +0 -79
  280. package/apps/studio/test/collab-loopback.test.ts +0 -63
  281. package/apps/studio/test/collab-origin-gate.test.ts +0 -323
  282. package/apps/studio/test/collab-protocol.test.ts +0 -146
  283. package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
  284. package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
  285. package/apps/studio/test/collab-room.test.ts +0 -188
  286. package/apps/studio/test/collab-session-survival.test.tsx +0 -176
  287. package/apps/studio/test/collab-stress.test.ts +0 -129
  288. package/apps/studio/test/comment-mount.test.ts +0 -97
  289. package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
  290. package/apps/studio/test/comments-api.test.ts +0 -277
  291. package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
  292. package/apps/studio/test/comments-overlay.test.ts +0 -117
  293. package/apps/studio/test/compile-entry.test.ts +0 -133
  294. package/apps/studio/test/component-map.test.ts +0 -48
  295. package/apps/studio/test/config-projection.test.ts +0 -104
  296. package/apps/studio/test/config-reload.test.ts +0 -230
  297. package/apps/studio/test/config-version.test.ts +0 -88
  298. package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
  299. package/apps/studio/test/convert-to-absolute.test.ts +0 -333
  300. package/apps/studio/test/credential-grammar.test.ts +0 -107
  301. package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
  302. package/apps/studio/test/csrf-write-guard.test.ts +0 -141
  303. package/apps/studio/test/debug-bundle.test.ts +0 -105
  304. package/apps/studio/test/design-setup-readiness.test.ts +0 -155
  305. package/apps/studio/test/detach-component.test.ts +0 -94
  306. package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
  307. package/apps/studio/test/dom-selection.test.ts +0 -198
  308. package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
  309. package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
  310. package/apps/studio/test/edit-persistence.test.ts +0 -91
  311. package/apps/studio/test/edit-scope-api.test.ts +0 -119
  312. package/apps/studio/test/edit-source-command.test.ts +0 -142
  313. package/apps/studio/test/edit-suppress.test.ts +0 -170
  314. package/apps/studio/test/editing-presence.test.ts +0 -103
  315. package/apps/studio/test/element-resize.test.ts +0 -136
  316. package/apps/studio/test/element-structural-api.test.ts +0 -434
  317. package/apps/studio/test/element-structural-edit.test.ts +0 -750
  318. package/apps/studio/test/ensure-browser.test.ts +0 -92
  319. package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
  320. package/apps/studio/test/export-assemble.test.ts +0 -53
  321. package/apps/studio/test/export-browser-lane.test.ts +0 -126
  322. package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
  323. package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
  324. package/apps/studio/test/export-center.test.tsx +0 -287
  325. package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
  326. package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
  327. package/apps/studio/test/export-lane.test.ts +0 -245
  328. package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
  329. package/apps/studio/test/exporters/canva.test.ts +0 -63
  330. package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
  331. package/apps/studio/test/exporters/endpoint.test.ts +0 -121
  332. package/apps/studio/test/exporters/history.test.ts +0 -107
  333. package/apps/studio/test/exporters/html.test.ts +0 -26
  334. package/apps/studio/test/exporters/jobs.test.ts +0 -342
  335. package/apps/studio/test/exporters/pdf.test.ts +0 -85
  336. package/apps/studio/test/exporters/png.test.ts +0 -103
  337. package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
  338. package/apps/studio/test/exporters/pptx.test.ts +0 -31
  339. package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
  340. package/apps/studio/test/exporters/runtime.test.ts +0 -59
  341. package/apps/studio/test/exporters/scope.test.ts +0 -292
  342. package/apps/studio/test/exporters/svg.test.ts +0 -29
  343. package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
  344. package/apps/studio/test/exporters/zip.test.ts +0 -104
  345. package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
  346. package/apps/studio/test/figjam-v3-model.test.ts +0 -342
  347. package/apps/studio/test/figma-explode.test.ts +0 -438
  348. package/apps/studio/test/figma-provenance.test.ts +0 -108
  349. package/apps/studio/test/figma-routes.test.ts +0 -294
  350. package/apps/studio/test/file-lock.test.ts +0 -84
  351. package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
  352. package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
  353. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
  354. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
  355. package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
  356. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
  357. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
  358. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
  359. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
  360. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
  361. package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
  362. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
  363. package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
  364. package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
  365. package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
  366. package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
  367. package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
  368. package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
  369. package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
  370. package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
  371. package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
  372. package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
  373. package/apps/studio/test/footage-store.test.ts +0 -100
  374. package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
  375. package/apps/studio/test/fs-watch.test.ts +0 -41
  376. package/apps/studio/test/gemma-models.test.ts +0 -245
  377. package/apps/studio/test/generate-route.test.ts +0 -106
  378. package/apps/studio/test/git-api.test.ts +0 -0
  379. package/apps/studio/test/git-branches.test.ts +0 -341
  380. package/apps/studio/test/git-cloud-posture.test.ts +0 -59
  381. package/apps/studio/test/git-grouping.test.ts +0 -106
  382. package/apps/studio/test/git-lifecycle.test.ts +0 -100
  383. package/apps/studio/test/git-log-format.test.ts +0 -95
  384. package/apps/studio/test/git-safe-rel.test.ts +0 -132
  385. package/apps/studio/test/git-system-engine.test.ts +0 -135
  386. package/apps/studio/test/git-watch.test.ts +0 -97
  387. package/apps/studio/test/github-api.test.ts +0 -577
  388. package/apps/studio/test/grid-track-handles.test.ts +0 -160
  389. package/apps/studio/test/handoff-static-frames.test.ts +0 -215
  390. package/apps/studio/test/handoff.test.ts +0 -329
  391. package/apps/studio/test/history-rollback.test.ts +0 -88
  392. package/apps/studio/test/hmr-broadcast.test.ts +0 -236
  393. package/apps/studio/test/hmr-classify.test.ts +0 -87
  394. package/apps/studio/test/hub-link.test.ts +0 -80
  395. package/apps/studio/test/identity-cache.test.ts +0 -83
  396. package/apps/studio/test/import-asset-browser.test.ts +0 -64
  397. package/apps/studio/test/import-asset-route.test.ts +0 -71
  398. package/apps/studio/test/import-asset.test.ts +0 -427
  399. package/apps/studio/test/import-brand-browser.test.ts +0 -88
  400. package/apps/studio/test/import-brand-route.test.ts +0 -120
  401. package/apps/studio/test/import-brand.test.ts +0 -206
  402. package/apps/studio/test/import-figma.test.ts +0 -667
  403. package/apps/studio/test/import-tokens.test.ts +0 -722
  404. package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
  405. package/apps/studio/test/input-router.test.ts +0 -757
  406. package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
  407. package/apps/studio/test/inspect-selections.test.ts +0 -219
  408. package/apps/studio/test/knob-props-authored.test.ts +0 -87
  409. package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
  410. package/apps/studio/test/locator.test.ts +0 -214
  411. package/apps/studio/test/marquee-overlay.test.ts +0 -94
  412. package/apps/studio/test/media-commit-chain.test.ts +0 -210
  413. package/apps/studio/test/mode-toggle.test.tsx +0 -226
  414. package/apps/studio/test/motion-sample.test.ts +0 -35
  415. package/apps/studio/test/move-artboards-command.test.ts +0 -108
  416. package/apps/studio/test/participants-chrome.test.ts +0 -65
  417. package/apps/studio/test/paths.test.ts +0 -88
  418. package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
  419. package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
  420. package/apps/studio/test/perf-harness.ts +0 -193
  421. package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
  422. package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
  423. package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
  424. package/apps/studio/test/photo-edit-api.test.ts +0 -201
  425. package/apps/studio/test/photo-filters.test.ts +0 -149
  426. package/apps/studio/test/photo-pipeline.test.ts +0 -106
  427. package/apps/studio/test/photo-taxonomy.test.ts +0 -96
  428. package/apps/studio/test/print-marks.test.ts +0 -113
  429. package/apps/studio/test/print-units.test.ts +0 -173
  430. package/apps/studio/test/read-annotations.test.ts +0 -727
  431. package/apps/studio/test/read-only-gate.test.ts +0 -188
  432. package/apps/studio/test/read-only-role.test.ts +0 -95
  433. package/apps/studio/test/readiness.test.ts +0 -337
  434. package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
  435. package/apps/studio/test/reorder-api.test.ts +0 -210
  436. package/apps/studio/test/repo-concurrency.test.ts +0 -146
  437. package/apps/studio/test/repo-lock.test.ts +0 -350
  438. package/apps/studio/test/report-proxy.test.ts +0 -125
  439. package/apps/studio/test/ripple.test.ts +0 -197
  440. package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
  441. package/apps/studio/test/runtime-bundle.test.ts +0 -69
  442. package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
  443. package/apps/studio/test/scaffold-design.test.ts +0 -122
  444. package/apps/studio/test/server-lifecycle.test.ts +0 -38
  445. package/apps/studio/test/session-runtime-state.test.ts +0 -226
  446. package/apps/studio/test/session-scope.test.ts +0 -100
  447. package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
  448. package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
  449. package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
  450. package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
  451. package/apps/studio/test/shared-doc-projection.test.ts +0 -238
  452. package/apps/studio/test/shell-importmap.test.ts +0 -56
  453. package/apps/studio/test/sizing-mode.test.ts +0 -102
  454. package/apps/studio/test/slash-commands.test.ts +0 -117
  455. package/apps/studio/test/snap-distance-pill.test.ts +0 -68
  456. package/apps/studio/test/spacing-handles.test.ts +0 -138
  457. package/apps/studio/test/specimen-select.test.ts +0 -88
  458. package/apps/studio/test/sync-agent.test.ts +0 -660
  459. package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
  460. package/apps/studio/test/sync-asset-push.test.ts +0 -931
  461. package/apps/studio/test/sync-atomic-write.test.ts +0 -62
  462. package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
  463. package/apps/studio/test/sync-autocommit.test.ts +0 -537
  464. package/apps/studio/test/sync-canvas-path.test.ts +0 -200
  465. package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
  466. package/apps/studio/test/sync-codec.test.ts +0 -230
  467. package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
  468. package/apps/studio/test/sync-cold-start.test.ts +0 -304
  469. package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
  470. package/apps/studio/test/sync-connection-state.test.ts +0 -337
  471. package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
  472. package/apps/studio/test/sync-decide-file.test.ts +0 -420
  473. package/apps/studio/test/sync-doc-name.test.ts +0 -281
  474. package/apps/studio/test/sync-echo-guard.test.ts +0 -96
  475. package/apps/studio/test/sync-file-ledger.test.ts +0 -334
  476. package/apps/studio/test/sync-file-membership.test.ts +0 -347
  477. package/apps/studio/test/sync-file-plane.test.ts +0 -976
  478. package/apps/studio/test/sync-file-pull.test.ts +0 -333
  479. package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
  480. package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
  481. package/apps/studio/test/sync-hardening.test.ts +0 -586
  482. package/apps/studio/test/sync-hub-listing.test.ts +0 -46
  483. package/apps/studio/test/sync-hubs-config.test.ts +0 -133
  484. package/apps/studio/test/sync-incident-replay.test.ts +0 -211
  485. package/apps/studio/test/sync-journal.test.ts +0 -176
  486. package/apps/studio/test/sync-meta-codec.test.ts +0 -199
  487. package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
  488. package/apps/studio/test/sync-move-retirement.test.ts +0 -231
  489. package/apps/studio/test/sync-panel-surface.test.ts +0 -153
  490. package/apps/studio/test/sync-path-pull.test.ts +0 -531
  491. package/apps/studio/test/sync-presentation.test.ts +0 -262
  492. package/apps/studio/test/sync-pull-budget.test.ts +0 -169
  493. package/apps/studio/test/sync-remote-docs.test.ts +0 -333
  494. package/apps/studio/test/sync-resync-routes.test.ts +0 -168
  495. package/apps/studio/test/sync-runtime.test.ts +0 -1675
  496. package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
  497. package/apps/studio/test/sync-settings-routes.test.ts +0 -195
  498. package/apps/studio/test/sync-settings.test.ts +0 -151
  499. package/apps/studio/test/sync-status.test.ts +0 -261
  500. package/apps/studio/test/sync-supervisor.test.ts +0 -262
  501. package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
  502. package/apps/studio/test/sync-trash.test.ts +0 -132
  503. package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
  504. package/apps/studio/test/sync-untrusted.test.ts +0 -103
  505. package/apps/studio/test/system-endpoint.test.ts +0 -144
  506. package/apps/studio/test/text-editability-stamp.test.ts +0 -131
  507. package/apps/studio/test/timeline-comp-target.test.ts +0 -139
  508. package/apps/studio/test/timeline-media-cache.test.ts +0 -41
  509. package/apps/studio/test/timeline-parse.test.ts +0 -184
  510. package/apps/studio/test/timeline-scale.test.ts +0 -65
  511. package/apps/studio/test/timeline-snap.test.ts +0 -85
  512. package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
  513. package/apps/studio/test/tool-palette.test.tsx +0 -55
  514. package/apps/studio/test/tour-overlay.test.tsx +0 -90
  515. package/apps/studio/test/trusted-request-host.test.ts +0 -66
  516. package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
  517. package/apps/studio/test/undo-stack.test.ts +0 -244
  518. package/apps/studio/test/use-agent-presence.test.tsx +0 -117
  519. package/apps/studio/test/use-annotation-resize.test.ts +0 -402
  520. package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
  521. package/apps/studio/test/use-artboard-drag.test.ts +0 -324
  522. package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
  523. package/apps/studio/test/use-collab.test.ts +0 -0
  524. package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
  525. package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
  526. package/apps/studio/test/use-selection-set.test.tsx +0 -166
  527. package/apps/studio/test/use-snap-guides.test.ts +0 -271
  528. package/apps/studio/test/use-tool-mode.test.tsx +0 -186
  529. package/apps/studio/test/use-undo-stack.test.tsx +0 -223
  530. package/apps/studio/test/video-asset.test.ts +0 -163
  531. package/apps/studio/test/video-comp-fixture.test.ts +0 -50
  532. package/apps/studio/test/video-comp.test.ts +0 -270
  533. package/apps/studio/test/video-encode-lib.test.ts +0 -63
  534. package/apps/studio/test/video-render-bridge.test.ts +0 -171
  535. package/apps/studio/test/whats-new.test.ts +0 -113
  536. package/apps/studio/test/whisper-setup.test.ts +0 -97
  537. package/apps/studio/test/workspace-containment.test.ts +0 -382
  538. package/apps/studio/test/workspace-signin.test.ts +0 -292
  539. package/apps/studio/test/ws-handshake.test.ts +0 -33
  540. package/cli/commands/cache.test.mjs +0 -166
  541. package/cli/commands/design-link.test.mjs +0 -419
  542. package/cli/commands/design.test.mjs +0 -240
  543. package/cli/commands/doctor.test.mjs +0 -237
  544. package/cli/commands/hub-workspace.test.mjs +0 -275
  545. package/cli/commands/hub.test.mjs +0 -225
  546. package/cli/commands/kg.test.mjs +0 -142
  547. package/cli/commands/preflight.test.mjs +0 -46
  548. package/cli/lib/argv.test.mjs +0 -46
  549. package/cli/lib/cache.test.mjs +0 -303
  550. package/cli/lib/cell-plan.test.mjs +0 -225
  551. package/cli/lib/config-lint.test.mjs +0 -117
  552. package/cli/lib/ddr-to-kgai.test.mjs +0 -118
  553. package/cli/lib/design-ownership.test.mjs +0 -329
  554. package/cli/lib/fetch-asset.test.mjs +0 -213
  555. package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
  556. package/cli/lib/figma-import-controls.test.mjs +0 -70
  557. package/cli/lib/flow-design-integration.test.mjs +0 -165
  558. package/cli/lib/gitignore-block.test.mjs +0 -136
  559. package/cli/lib/gitignore-drift.test.mjs +0 -156
  560. package/cli/lib/hubs-config.test.mjs +0 -165
  561. package/cli/lib/pkg-root.test.mjs +0 -111
  562. package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
  563. package/cli/lib/plugin-name-namespace.test.mjs +0 -71
  564. package/cli/lib/reconstruct-toolset.test.mjs +0 -194
  565. package/cli/lib/stack-detect.test.mjs +0 -121
  566. package/cli/lib/update-check.test.mjs +0 -32
  567. package/cli/lib/workspace-plan.test.mjs +0 -699
@@ -1,65 +0,0 @@
1
- // Unit: pure helpers from participants-chrome.tsx.
2
-
3
- import { describe, expect, test } from 'bun:test';
4
- import { initialsFor, isOwnEditingEcho } from '../participants-chrome.tsx';
5
- import type { ForeignAwareness } from '../use-collab.tsx';
6
-
7
- const peer = (over: Partial<ForeignAwareness>): ForeignAwareness => ({
8
- clientID: 1,
9
- name: 'Anna',
10
- color: 'oklch(0.7 0.16 145)',
11
- cursor: null,
12
- selection: null,
13
- annotationSelection: [],
14
- viewport: { x: 0, y: 0, zoom: 1 },
15
- editing: null,
16
- ...over,
17
- });
18
-
19
- describe('initialsFor', () => {
20
- test('two-word name → uppercase first letters', () => {
21
- expect(initialsFor('Alice Smith')).toBe('AS');
22
- expect(initialsFor('michał dovrtěl')).toBe('MD');
23
- });
24
-
25
- test('single-word name → first two letters uppercased', () => {
26
- expect(initialsFor('alice')).toBe('AL');
27
- expect(initialsFor('A')).toBe('A');
28
- });
29
-
30
- test('many-word name → first letter of first two words', () => {
31
- expect(initialsFor('John Ronald Reuel Tolkien')).toBe('JR');
32
- });
33
-
34
- test('empty / whitespace → ?', () => {
35
- expect(initialsFor('')).toBe('?');
36
- expect(initialsFor(' ')).toBe('?');
37
- });
38
-
39
- test('handles extra whitespace', () => {
40
- expect(initialsFor(' Alice Smith ')).toBe('AS');
41
- });
42
- });
43
-
44
- describe('isOwnEditingEcho (Phase 30 / DDR-120)', () => {
45
- test('skips MY own server-side editing echo (my name, editing, no cursor/selection)', () => {
46
- expect(isOwnEditingEcho(peer({ name: 'Anna', editing: { since: 1 } }), 'Anna')).toBe(true);
47
- });
48
-
49
- test('keeps a remote peer editing (different name)', () => {
50
- expect(isOwnEditingEcho(peer({ name: 'Bob', editing: { since: 1 } }), 'Anna')).toBe(false);
51
- });
52
-
53
- test('keeps MY peer if it has a live cursor (a real second tab, not the echo)', () => {
54
- expect(
55
- isOwnEditingEcho(
56
- peer({ name: 'Anna', editing: { since: 1 }, cursor: { x: 1, y: 2 } }),
57
- 'Anna'
58
- )
59
- ).toBe(false);
60
- });
61
-
62
- test('keeps a peer that is not editing', () => {
63
- expect(isOwnEditingEcho(peer({ name: 'Anna', editing: null }), 'Anna')).toBe(false);
64
- });
65
- });
@@ -1,88 +0,0 @@
1
- // paths.ts — resolves real disk install root across dev mode, compiled npm
2
- // install, and compiled marketplace install. Phase 19.1 / v0.18.1.
3
-
4
- import { describe, expect, test } from 'bun:test';
5
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
6
- import { tmpdir } from 'node:os';
7
- import { join } from 'node:path';
8
-
9
- import {
10
- CLIENT_DIR,
11
- DESIGN_PLUGIN_DIR,
12
- DEV_SERVER_ROOT,
13
- DIST_DIR,
14
- FLOW_PLUGIN_DIR,
15
- IS_COMPILED_BINARY,
16
- pluginDirFrom,
17
- RUNTIME_BUNDLES_DIR,
18
- } from '../paths.ts';
19
-
20
- describe('paths.ts', () => {
21
- test('DEV_SERVER_ROOT contains http.ts + package.json (canonical anchor)', () => {
22
- expect(DEV_SERVER_ROOT).not.toMatch(/\$bunfs|~BUN/);
23
- expect(Bun.file(`${DEV_SERVER_ROOT}/http.ts`).size).toBeGreaterThan(0);
24
- expect(Bun.file(`${DEV_SERVER_ROOT}/package.json`).size).toBeGreaterThan(0);
25
- });
26
-
27
- test('DIST_DIR + CLIENT_DIR + RUNTIME_BUNDLES_DIR are descendants of DEV_SERVER_ROOT', () => {
28
- expect(DIST_DIR).toBe(`${DEV_SERVER_ROOT}/dist`);
29
- expect(CLIENT_DIR).toBe(`${DEV_SERVER_ROOT}/client`);
30
- expect(RUNTIME_BUNDLES_DIR).toBe(`${DEV_SERVER_ROOT}/dist/runtime`);
31
- });
32
-
33
- test('IS_COMPILED_BINARY is false when running under bun directly (this test)', () => {
34
- // bun:test invokes via `bun`, so import.meta.url is a real file:// path.
35
- expect(IS_COMPILED_BINARY).toBe(false);
36
- });
37
- });
38
-
39
- describe('pluginDirFrom — session-scoped plugin auto-bootstrap resolution (DDR-143)', () => {
40
- // Both simulated layouts keep `plugins/` a sibling of `apps/studio/`, matching
41
- // the dev tree and the desktop Resources bundle (stage-resources.mjs).
42
- function makeLayout(shape: 'desktop-bundle' | 'npm'): { root: string; devServerRoot: string } {
43
- const root = mkdtempSync(join(tmpdir(), 'maude-paths-'));
44
- const devServerRoot = join(root, 'apps', 'studio');
45
- mkdirSync(devServerRoot, { recursive: true });
46
- for (const plugin of ['design', 'flow'] as const) {
47
- const pluginRoot = join(root, 'plugins', plugin);
48
- // Both layouts ship templates (DDR-044).
49
- mkdirSync(join(pluginRoot, 'templates'), { recursive: true });
50
- if (shape === 'desktop-bundle') {
51
- // The desktop bundle stages the full loadable tree incl. the manifest.
52
- mkdirSync(join(pluginRoot, '.claude-plugin'), { recursive: true });
53
- mkdirSync(join(pluginRoot, 'commands'), { recursive: true });
54
- writeFileSync(join(pluginRoot, '.claude-plugin', 'plugin.json'), '{"name":"x"}');
55
- }
56
- // npm layout: NO .claude-plugin/plugin.json (DDR-044 minimal surface).
57
- }
58
- return { root, devServerRoot };
59
- }
60
-
61
- test('desktop Resources layout → resolves both plugin dirs (manifest present)', () => {
62
- const { root, devServerRoot } = makeLayout('desktop-bundle');
63
- try {
64
- expect(pluginDirFrom(devServerRoot, 'design')).toBe(join(root, 'plugins', 'design'));
65
- expect(pluginDirFrom(devServerRoot, 'flow')).toBe(join(root, 'plugins', 'flow'));
66
- } finally {
67
- rmSync(root, { recursive: true, force: true });
68
- }
69
- });
70
-
71
- test('npm/web layout → null (templates ship, but no plugin manifest)', () => {
72
- const { root, devServerRoot } = makeLayout('npm');
73
- try {
74
- expect(pluginDirFrom(devServerRoot, 'design')).toBeNull();
75
- expect(pluginDirFrom(devServerRoot, 'flow')).toBeNull();
76
- } finally {
77
- rmSync(root, { recursive: true, force: true });
78
- }
79
- });
80
-
81
- test('exported constants resolve under the running dev tree (manifest present here)', () => {
82
- // This suite runs from the repo dev tree, where plugins/{design,flow}/ carry
83
- // their manifests — so the module-load-time constants are non-null and point
84
- // at the real plugin roots.
85
- expect(DESIGN_PLUGIN_DIR).toBe(join(DEV_SERVER_ROOT, '..', '..', 'plugins', 'design'));
86
- expect(FLOW_PLUGIN_DIR).toBe(join(DEV_SERVER_ROOT, '..', '..', 'plugins', 'flow'));
87
- });
88
- });
@@ -1,359 +0,0 @@
1
- // feature-2-print-artboards T5 — the load-bearing golden gate: MediaBox ⊇
2
- // BleedBox ⊇ TrimBox with exact expected pt values, plus marks presence.
3
- // Exercises pdf-lib directly (no Playwright/Chromium needed — box-setting
4
- // and mark-drawing are pure pdf-lib API calls); the page size mirrors what
5
- // Chromium's page.pdf() would actually produce for an artboard authored via
6
- // resolvePrintArtboard (96dpi CSS px → pt, same ratio as mm → pt).
7
-
8
- import { describe, expect, test } from 'bun:test';
9
- import { PDFDocument } from 'pdf-lib';
10
-
11
- import { readArtboardPrintProp } from '../canvas-edit.ts';
12
- import {
13
- applyPageFit,
14
- applyPrintBoxesAndMarks,
15
- artboardIdFromCssPath,
16
- type PdfPrintOptions,
17
- parsePageFit,
18
- parsePdfPrintOptions,
19
- resolvePdfDeviceScale,
20
- } from '../exporters/pdf.ts';
21
- import { requiredSlugPt } from '../print/marks.ts';
22
- import {
23
- getPaperPreset,
24
- mmToPt,
25
- pxToPt,
26
- resolvePrintArtboard,
27
- trimSizeMm,
28
- } from '../print/units.ts';
29
-
30
- const NO_MARKS: PdfPrintOptions = {
31
- marks: { crop: false, registration: false, colorBars: false, pageInfo: false },
32
- };
33
-
34
- describe('pdf.ts — artboardIdFromCssPath', () => {
35
- test('extracts the id from the artboard-by-id selector shape', () => {
36
- expect(artboardIdFromCssPath('[data-dc-screen="home"]')).toBe('home');
37
- });
38
-
39
- test('a descendant / :first-of-type selector yields null', () => {
40
- expect(artboardIdFromCssPath('.some .descendant')).toBeNull();
41
- expect(artboardIdFromCssPath('[data-dc-screen]:first-of-type')).toBeNull();
42
- expect(artboardIdFromCssPath('[data-dc-screen]')).toBeNull();
43
- });
44
- });
45
-
46
- describe('pdf.ts — parsePdfPrintOptions', () => {
47
- test('absent/malformed → null (print post-pass off)', () => {
48
- expect(parsePdfPrintOptions(undefined)).toBeNull();
49
- expect(parsePdfPrintOptions(null)).toBeNull();
50
- expect(parsePdfPrintOptions('nope')).toBeNull();
51
- expect(parsePdfPrintOptions([1, 2])).toBeNull();
52
- });
53
-
54
- test('valid shape passes through with marks defaulting false', () => {
55
- expect(parsePdfPrintOptions({})).toEqual({
56
- includeBleed: undefined,
57
- marks: { crop: false, registration: false, colorBars: false, pageInfo: false },
58
- });
59
- });
60
-
61
- test('marks flags coerce to strict booleans (non-true → false)', () => {
62
- expect(parsePdfPrintOptions({ marks: { crop: 'yes', registration: 1 } })).toEqual({
63
- includeBleed: undefined,
64
- marks: { crop: false, registration: false, colorBars: false, pageInfo: false },
65
- });
66
- expect(parsePdfPrintOptions({ marks: { crop: true, registration: true } })).toEqual({
67
- includeBleed: undefined,
68
- marks: { crop: true, registration: true, colorBars: false, pageInfo: false },
69
- });
70
- });
71
-
72
- test('includeBleed passes through only when a strict boolean', () => {
73
- expect(parsePdfPrintOptions({ includeBleed: false })?.includeBleed).toBe(false);
74
- expect(parsePdfPrintOptions({ includeBleed: 'false' })?.includeBleed).toBeUndefined();
75
- });
76
- });
77
-
78
- describe('pdf.ts — parsePageFit', () => {
79
- test('a known paper id passes through', () => {
80
- expect(parsePageFit('a4')).toBe('a4');
81
- expect(parsePageFit('letter')).toBe('letter');
82
- });
83
-
84
- test('unknown id / non-string → null', () => {
85
- expect(parsePageFit('not-a-paper')).toBeNull();
86
- expect(parsePageFit(42)).toBeNull();
87
- expect(parsePageFit(undefined)).toBeNull();
88
- });
89
- });
90
-
91
- // Dogfood follow-up (2026-07-16) — a "vector PDF" still embeds raster
92
- // content (dropped photos, large-format art) as a bitmap whose density is
93
- // set by the capturing context's deviceScaleFactor; `dpi` controls that.
94
- describe('pdf.ts — resolvePdfDeviceScale (raster-content density, dogfood follow-up)', () => {
95
- test("no dpi → 1× (today's unchanged default — NOT png.ts's 2× default)", () => {
96
- expect(resolvePdfDeviceScale({})).toBe(1);
97
- });
98
-
99
- test('dpi=300 → 3.125× (300/96)', () => {
100
- expect(resolvePdfDeviceScale({ dpi: 300 })).toBeCloseTo(300 / 96, 10);
101
- });
102
-
103
- test('dpi=600 → 6.25×', () => {
104
- expect(resolvePdfDeviceScale({ dpi: 600 })).toBeCloseTo(600 / 96, 10);
105
- });
106
-
107
- test("a bare `scale` option (PNG's legacy field) is ignored — PDF has no scale concept", () => {
108
- expect(resolvePdfDeviceScale({ scale: 3 })).toBe(1);
109
- });
110
- });
111
-
112
- describe('pdf-print-boxes — GOLDEN: A4 + 3mm bleed, no marks', () => {
113
- test('MediaBox === BleedBox (full page); TrimBox insets by bleedPt exactly', async () => {
114
- const resolved = resolvePrintArtboard({ paper: 'a4', bleedMm: 3 });
115
- const pageWidthPt = pxToPt(resolved.widthPx);
116
- const pageHeightPt = pxToPt(resolved.heightPx);
117
-
118
- const doc = await PDFDocument.create();
119
- const page = doc.addPage([pageWidthPt, pageHeightPt]);
120
- applyPrintBoxesAndMarks(page, 3, NO_MARKS);
121
-
122
- const bleedPt = mmToPt(3);
123
- const media = page.getMediaBox();
124
- const bleed = page.getBleedBox();
125
- const trim = page.getTrimBox();
126
-
127
- // No marks requested → no slug → MediaBox is exactly the bleed box.
128
- expect(media.x).toBeCloseTo(0, 6);
129
- expect(media.y).toBeCloseTo(0, 6);
130
- expect(media.width).toBeCloseTo(pageWidthPt, 6);
131
- expect(media.height).toBeCloseTo(pageHeightPt, 6);
132
-
133
- expect(bleed.x).toBeCloseTo(0, 6);
134
- expect(bleed.y).toBeCloseTo(0, 6);
135
- expect(bleed.width).toBeCloseTo(pageWidthPt, 6);
136
- expect(bleed.height).toBeCloseTo(pageHeightPt, 6);
137
-
138
- expect(trim.x).toBeCloseTo(bleedPt, 6);
139
- expect(trim.y).toBeCloseTo(bleedPt, 6);
140
- expect(trim.width).toBeCloseTo(pageWidthPt - 2 * bleedPt, 6);
141
- expect(trim.height).toBeCloseTo(pageHeightPt - 2 * bleedPt, 6);
142
-
143
- // The load-bearing nesting invariant, independent of the exact numbers above.
144
- expect(media.x).toBeLessThanOrEqual(bleed.x);
145
- expect(media.y).toBeLessThanOrEqual(bleed.y);
146
- expect(media.x + media.width).toBeGreaterThanOrEqual(bleed.x + bleed.width);
147
- expect(bleed.x).toBeLessThanOrEqual(trim.x);
148
- expect(bleed.y).toBeLessThanOrEqual(trim.y);
149
- expect(bleed.x + bleed.width).toBeGreaterThanOrEqual(trim.x + trim.width);
150
- });
151
- });
152
-
153
- describe('pdf-print-boxes — GOLDEN: A4 + 3mm bleed + crop + registration marks', () => {
154
- test('MediaBox enlarges by the exact required slug, negative origin, content-box unmoved', async () => {
155
- const resolved = resolvePrintArtboard({ paper: 'a4', bleedMm: 3 });
156
- const pageWidthPt = pxToPt(resolved.widthPx);
157
- const pageHeightPt = pxToPt(resolved.heightPx);
158
-
159
- const doc = await PDFDocument.create();
160
- const page = doc.addPage([pageWidthPt, pageHeightPt]);
161
- const printOpts: PdfPrintOptions = {
162
- marks: { crop: true, registration: true, colorBars: false, pageInfo: false },
163
- };
164
- applyPrintBoxesAndMarks(page, 3, printOpts);
165
-
166
- const bleedPt = mmToPt(3);
167
- const slugPt = requiredSlugPt({ crop: true, registration: true });
168
- const media = page.getMediaBox();
169
- const bleed = page.getBleedBox();
170
- const trim = page.getTrimBox();
171
-
172
- // Exact expected pt values — the golden assertion.
173
- expect(media.x).toBeCloseTo(-slugPt, 6);
174
- expect(media.y).toBeCloseTo(-slugPt, 6);
175
- expect(media.width).toBeCloseTo(pageWidthPt + 2 * slugPt, 6);
176
- expect(media.height).toBeCloseTo(pageHeightPt + 2 * slugPt, 6);
177
- // MediaBox's negative origin is the marks slug ONLY — bleed box content
178
- // coordinates are untouched (still start at the page's own (0,0)).
179
- expect(bleed.x).toBeCloseTo(0, 6);
180
- expect(bleed.y).toBeCloseTo(0, 6);
181
- expect(bleed.width).toBeCloseTo(pageWidthPt, 6);
182
- expect(bleed.height).toBeCloseTo(pageHeightPt, 6);
183
- expect(trim.x).toBeCloseTo(bleedPt, 6);
184
- expect(trim.y).toBeCloseTo(bleedPt, 6);
185
- expect(trim.width).toBeCloseTo(pageWidthPt - 2 * bleedPt, 6);
186
- expect(trim.height).toBeCloseTo(pageHeightPt - 2 * bleedPt, 6);
187
-
188
- // MediaBox ⊇ BleedBox ⊇ TrimBox, strictly (marks slug > 0 here).
189
- expect(media.x).toBeLessThan(bleed.x);
190
- expect(media.width + media.x).toBeGreaterThan(bleed.width + bleed.x);
191
- expect(bleed.x).toBeLessThan(trim.x);
192
- expect(bleed.width + bleed.x).toBeGreaterThan(trim.width + trim.x);
193
- });
194
-
195
- test('marks present as vector ops — saved PDF is measurably larger with marks than without', async () => {
196
- const resolved = resolvePrintArtboard({ paper: 'a4', bleedMm: 3 });
197
- const pageWidthPt = pxToPt(resolved.widthPx);
198
- const pageHeightPt = pxToPt(resolved.heightPx);
199
-
200
- const withoutMarks = await PDFDocument.create();
201
- applyPrintBoxesAndMarks(withoutMarks.addPage([pageWidthPt, pageHeightPt]), 3, NO_MARKS);
202
- const withoutBytes = await withoutMarks.save();
203
-
204
- const withMarks = await PDFDocument.create();
205
- applyPrintBoxesAndMarks(withMarks.addPage([pageWidthPt, pageHeightPt]), 3, {
206
- marks: { crop: true, registration: true, colorBars: false, pageInfo: false },
207
- });
208
- const withBytes = await withMarks.save();
209
-
210
- // 8 crop-mark line segments + 4 registration circles (each an approximated
211
- // bezier ellipse) + 8 crosshair segments is a non-trivial amount of vector
212
- // content — the byte-size delta is a reliable proxy without depending on
213
- // pdf-lib's internal (and version-specific) content-stream compression
214
- // format for a literal operator-string grep.
215
- expect(withBytes.byteLength).toBeGreaterThan(withoutBytes.byteLength);
216
- });
217
- });
218
-
219
- describe('pdf-print-boxes — includeBleed:false crops the bleed strip out', () => {
220
- test('MediaBox/BleedBox collapse onto TrimBox (degenerate but valid nesting)', async () => {
221
- const resolved = resolvePrintArtboard({ paper: 'a4', bleedMm: 3 });
222
- const pageWidthPt = pxToPt(resolved.widthPx);
223
- const pageHeightPt = pxToPt(resolved.heightPx);
224
- const bleedPt = mmToPt(3);
225
-
226
- const doc = await PDFDocument.create();
227
- const page = doc.addPage([pageWidthPt, pageHeightPt]);
228
- applyPrintBoxesAndMarks(page, 3, { ...NO_MARKS, includeBleed: false });
229
-
230
- const media = page.getMediaBox();
231
- const bleed = page.getBleedBox();
232
- const trim = page.getTrimBox();
233
-
234
- expect(media.x).toBeCloseTo(bleedPt, 6);
235
- expect(media.y).toBeCloseTo(bleedPt, 6);
236
- expect(media.width).toBeCloseTo(pageWidthPt - 2 * bleedPt, 6);
237
- expect(bleed.x).toBeCloseTo(trim.x, 6);
238
- expect(bleed.width).toBeCloseTo(trim.width, 6);
239
- });
240
- });
241
-
242
- // RCA issue-pdf-print-export-marks-missing — the shape that shipped broken:
243
- // two `kind="print"` artboards sharing one `print` spec via a top-level const
244
- // (AlligatorsAcko.tsx's actual authoring pattern), not a repeated inline
245
- // literal per artboard. The multi-page loop in exporters/pdf.ts `run()`
246
- // re-resolves readArtboardPrintProp independently per written page (keyed by
247
- // the artboard's own data-dc-screen id — see safeArtboardFilename), so
248
- // asserting it resolves per-artboard-id here is the load-bearing regression
249
- // guard: both pages must get the SAME resolved geometry, not just page 1.
250
- describe('readArtboardPrintProp — shared const print spec resolves for every page (multi-page correlation)', () => {
251
- const canvas = [
252
- 'const A1_PRINT = { paper: "a1", orientation: "portrait", bleedMm: 3 } as const;',
253
- 'export default function Demo() {',
254
- ' return (',
255
- ' <DesignCanvas>',
256
- ' <DCArtboard id="acko-front" label="A" kind="print" width={816} height={1146} print={A1_PRINT as any}>',
257
- ' <div>front</div>',
258
- ' </DCArtboard>',
259
- ' <DCArtboard id="acko-back" label="B" kind="print" width={816} height={1146} print={A1_PRINT as any}>',
260
- ' <div>back</div>',
261
- ' </DCArtboard>',
262
- ' </DesignCanvas>',
263
- ' );',
264
- '}',
265
- ].join('\n');
266
-
267
- test('both artboard ids resolve to the same non-null print geometry, driving non-degenerate boxes+marks on each page', async () => {
268
- const front = readArtboardPrintProp('/abs/AlligatorsAcko.tsx', canvas, 'acko-front');
269
- const back = readArtboardPrintProp('/abs/AlligatorsAcko.tsx', canvas, 'acko-back');
270
- expect(front).toEqual({ paper: 'a1', orientation: 'portrait', bleedMm: 3 });
271
- expect(back).toEqual(front);
272
-
273
- const printOpts: PdfPrintOptions = {
274
- marks: { crop: true, registration: true, colorBars: false, pageInfo: false },
275
- };
276
- for (const printProp of [front, back]) {
277
- if (!printProp) throw new Error('expected a resolved print prop');
278
- const bleedMm = typeof printProp.bleedMm === 'number' ? printProp.bleedMm : 0;
279
- const resolved = resolvePrintArtboard({ paper: printProp.paper as string, bleedMm });
280
- const pageWidthPt = pxToPt(resolved.widthPx);
281
- const pageHeightPt = pxToPt(resolved.heightPx);
282
- const doc = await PDFDocument.create();
283
- const page = doc.addPage([pageWidthPt, pageHeightPt]);
284
- applyPrintBoxesAndMarks(page, bleedMm, printOpts);
285
- const media = page.getMediaBox();
286
- const trim = page.getTrimBox();
287
- // Non-degenerate: TrimBox strictly inside MediaBox on every side — the
288
- // exact thing that silently failed to happen when printProp was null.
289
- expect(media.width).toBeGreaterThan(trim.width);
290
- expect(media.height).toBeGreaterThan(trim.height);
291
- }
292
- });
293
- });
294
-
295
- describe('pdf-print-boxes — applyPageFit (non-print artboard scale-to-paper)', () => {
296
- test('the resulting page is sized to the target paper, content preserved', async () => {
297
- const doc = await PDFDocument.create();
298
- // A wide, non-print-sized artboard (e.g. a 1440×1024 digital screen at 96dpi).
299
- const origWidthPt = pxToPt(1440);
300
- const origHeightPt = pxToPt(1024);
301
- doc.addPage([origWidthPt, origHeightPt]);
302
- expect(doc.getPageCount()).toBe(1);
303
-
304
- await applyPageFit(doc, 0, 'a4');
305
-
306
- expect(doc.getPageCount()).toBe(1); // swapped in place, not appended
307
- const page = doc.getPage(0);
308
- // Expected size mirrors applyPageFit's OWN derivation (mmToPt of the
309
- // preset's raw mm dims via trimSizeMm) — NOT a px-rounded roundtrip via
310
- // resolvePrintArtboard, which would introduce a spurious sub-pt mismatch.
311
- const preset = getPaperPreset('a4');
312
- if (!preset) throw new Error('a4 preset missing');
313
- const { widthMm, heightMm } = trimSizeMm(preset, 'portrait');
314
- const { width, height } = page.getSize();
315
- expect(width).toBeCloseTo(mmToPt(widthMm), 6);
316
- expect(height).toBeCloseTo(mmToPt(heightMm), 6);
317
- });
318
-
319
- test('unknown paper id is a no-op (never throws, never mutates)', async () => {
320
- const doc = await PDFDocument.create();
321
- doc.addPage([100, 100]);
322
- await applyPageFit(doc, 0, 'not-a-paper');
323
- expect(doc.getPageCount()).toBe(1);
324
- expect(doc.getPage(0).getSize()).toEqual({ width: 100, height: 100 });
325
- });
326
- });
327
-
328
- // DDR-232 follow-up — the WORKER-lane print gap. pdf.ts read the artboard's
329
- // `print` prop from the canvas source ON DISK; the render worker holds no
330
- // checkout (DDR-230 §1), so every cloud print PDF silently came back with no
331
- // BleedBox/TrimBox and no marks — the fall-through was "not a print artboard".
332
- // The cell now resolves every print artboard's prop up front
333
- // (readAllArtboardPrintProps) and ships it in the job as `options.printProps`.
334
- describe('readAllArtboardPrintProps — what the cell ships to the worker', () => {
335
- const SRC = `import { DesignCanvas, DCSection, DCArtboard } from "@maude/canvas-lib";
336
- export default function C() {
337
- return (
338
- <DesignCanvas>
339
- <DCSection id="s" title="t">
340
- <DCArtboard id="screen" width={400} height={300}><div/></DCArtboard>
341
- <DCArtboard id="flyer" kind="print" print={{ paper: "a6", bleedMm: 3 }} width={397} height={559}><div/></DCArtboard>
342
- <DCArtboard id="poster" kind="print" print={{ paper: "a3" }} width={1123} height={1587}><div/></DCArtboard>
343
- </DCSection>
344
- </DesignCanvas>
345
- );
346
- }
347
- `;
348
- test('collects every print artboard by id and skips screen artboards', async () => {
349
- const { readAllArtboardPrintProps } = await import('../canvas-edit.ts');
350
- const props = readAllArtboardPrintProps('/x/c.tsx', SRC);
351
- expect(Object.keys(props).sort()).toEqual(['flyer', 'poster']);
352
- expect(props.flyer).toEqual({ paper: 'a6', bleedMm: 3 });
353
- expect(props.poster).toEqual({ paper: 'a3' });
354
- });
355
- test('an unparsable canvas contributes nothing rather than throwing', async () => {
356
- const { readAllArtboardPrintProps } = await import('../canvas-edit.ts');
357
- expect(readAllArtboardPrintProps('/x/c.tsx', 'export default <<<')).toEqual({});
358
- });
359
- });
@@ -1,131 +0,0 @@
1
- // peer-selection-follows-camera — the collaboration counterpart of the
2
- // annotation-resize-handle fix (9ba40b96). A peer's selection frame is measured
3
- // off the LOCAL DOM every render, so it only tracks a pan/zoom if the component
4
- // actually re-renders while the camera moves. Both halos are `memo`, and through
5
- // a gesture the peer's awareness is unchanged — so without a viewport prop that
6
- // ticks with the camera, memo bails out and the frame sits frozen in screen
7
- // space while the artboard it outlines slides away underneath it.
8
- //
9
- // Needs a live DOM (memo bailout + a stubbed getBoundingClientRect) — register
10
- // happy-dom + drive a createRoot, same convention as mode-toggle.test.tsx.
11
-
12
- import { afterAll, afterEach, beforeAll, describe, expect, test } from 'bun:test';
13
- import { GlobalRegistrator } from '@happy-dom/global-registrator';
14
-
15
- beforeAll(() => {
16
- GlobalRegistrator.register();
17
- (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
18
- });
19
- afterAll(() => {
20
- GlobalRegistrator.unregister();
21
- });
22
-
23
- import { act, type ReactNode } from 'react';
24
- import { createRoot, type Root } from 'react-dom/client';
25
- import { PeerAnnotationSelection, PeerSelection } from '../cursors-overlay.tsx';
26
- import type { ForeignAwareness } from '../use-collab.tsx';
27
-
28
- // The camera the stubbed measurements project through — stands in for the world
29
- // plane's CSS transform, which is what really moves the target under a pan.
30
- let camera = { x: 0, y: 0, zoom: 1 };
31
-
32
- /** A target element whose screen rect follows `camera`, like real canvas content. */
33
- function target(
34
- attr: string,
35
- value: string,
36
- world: { x: number; y: number; w: number; h: number }
37
- ) {
38
- const el = document.createElement('div');
39
- el.setAttribute(attr, value);
40
- el.getBoundingClientRect = () =>
41
- ({
42
- left: world.x * camera.zoom + camera.x,
43
- top: world.y * camera.zoom + camera.y,
44
- width: world.w * camera.zoom,
45
- height: world.h * camera.zoom,
46
- }) as DOMRect;
47
- document.body.appendChild(el);
48
- return el;
49
- }
50
-
51
- let root: Root | null = null;
52
- let host: HTMLElement | null = null;
53
-
54
- function mount(ui: ReactNode): void {
55
- host = document.createElement('div');
56
- document.body.appendChild(host);
57
- root = createRoot(host);
58
- act(() => {
59
- root?.render(ui);
60
- });
61
- }
62
-
63
- function rerender(ui: ReactNode): void {
64
- act(() => {
65
- root?.render(ui);
66
- });
67
- }
68
-
69
- function frameTransform(): string {
70
- const el = host?.querySelector('.dc-peer-selection') as HTMLElement | null;
71
- return el?.style.transform ?? '';
72
- }
73
-
74
- afterEach(() => {
75
- act(() => {
76
- root?.unmount();
77
- });
78
- host?.remove();
79
- root = null;
80
- host = null;
81
- document.body.innerHTML = '';
82
- camera = { x: 0, y: 0, zoom: 1 };
83
- });
84
-
85
- // One stable object across the whole test — a real pan/zoom does not touch the
86
- // peer's awareness, and that identity is exactly what makes memo bail out.
87
- const peer = {
88
- clientID: 7,
89
- name: 'you@local.test',
90
- color: 'oklch(56% 0.17 50)',
91
- cursor: null,
92
- selection: { cssPath: '[data-cd-id="hero"]' },
93
- annotationSelection: ['stroke-1'],
94
- } as unknown as ForeignAwareness;
95
-
96
- describe('cursors-overlay / PeerSelection', () => {
97
- test('the frame follows a pan', () => {
98
- target('data-cd-id', 'hero', { x: 100, y: 60, w: 200, h: 120 });
99
- mount(<PeerSelection peer={peer} viewport={{ x: 0, y: 0, zoom: 1 }} />);
100
- expect(frameTransform()).toBe('translate(100px, 60px)');
101
-
102
- camera = { x: -40, y: -25, zoom: 1 };
103
- rerender(<PeerSelection peer={peer} viewport={camera} />);
104
- expect(frameTransform()).toBe('translate(60px, 35px)');
105
- });
106
-
107
- test('the frame follows a zoom (position AND size)', () => {
108
- target('data-cd-id', 'hero', { x: 100, y: 60, w: 200, h: 120 });
109
- mount(<PeerSelection peer={peer} viewport={{ x: 0, y: 0, zoom: 1 }} />);
110
-
111
- camera = { x: 0, y: 0, zoom: 2 };
112
- rerender(<PeerSelection peer={peer} viewport={camera} />);
113
- const el = host?.querySelector('.dc-peer-selection') as HTMLElement;
114
- expect(el.style.transform).toBe('translate(200px, 120px)');
115
- expect(el.style.width).toBe('400px');
116
- expect(el.style.height).toBe('240px');
117
- });
118
- });
119
-
120
- describe('cursors-overlay / PeerAnnotationSelection', () => {
121
- test('the stroke halo follows a pan', () => {
122
- target('data-id', 'stroke-1', { x: 100, y: 60, w: 200, h: 120 });
123
- mount(<PeerAnnotationSelection peer={peer} viewport={{ x: 0, y: 0, zoom: 1 }} />);
124
- // Padded 3px so the halo sits OUTSIDE the stroke.
125
- expect(frameTransform()).toBe('translate(97px, 57px)');
126
-
127
- camera = { x: -40, y: -25, zoom: 1 };
128
- rerender(<PeerAnnotationSelection peer={peer} viewport={camera} />);
129
- expect(frameTransform()).toBe('translate(57px, 32px)');
130
- });
131
- });