@1agh/maude 0.28.1 → 0.30.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 (372) hide show
  1. package/README.md +3 -3
  2. package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -2
  3. package/apps/studio/annotations-align.ts +149 -0
  4. package/apps/studio/annotations-bindings.ts +197 -0
  5. package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +681 -151
  6. package/apps/studio/annotations-groups.ts +270 -0
  7. package/apps/studio/annotations-layer.tsx +4537 -0
  8. package/apps/studio/annotations-model.ts +2077 -0
  9. package/apps/studio/annotations-snap.ts +125 -0
  10. package/{plugins/design/dev-server → apps/studio}/api.ts +616 -70
  11. package/{plugins/design/dev-server → apps/studio}/artboard-activity-overlay.tsx +1 -1
  12. package/{plugins/design/dev-server → apps/studio}/artboard-marquee.tsx +1 -1
  13. package/apps/studio/bin/_motion-sample-playwright.mjs +129 -0
  14. package/{plugins/design/dev-server → apps/studio}/bin/_png-playwright.mjs +1 -1
  15. package/apps/studio/bin/annotate.mjs +732 -0
  16. package/apps/studio/bin/annotate.sh +17 -0
  17. package/{plugins/design/dev-server → apps/studio}/bin/canvas-edit.sh +1 -1
  18. package/{plugins/design/dev-server → apps/studio}/bin/draw-proof.sh +37 -2
  19. package/{plugins/design/dev-server → apps/studio}/bin/preflight.sh +1 -1
  20. package/apps/studio/bin/read-annotations.mjs +573 -0
  21. package/apps/studio/bin/read-annotations.sh +11 -0
  22. package/{plugins/design/dev-server → apps/studio}/bin/runtime-health.sh +1 -1
  23. package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
  24. package/{plugins/design/dev-server → apps/studio}/bin/server-up.sh +9 -3
  25. package/apps/studio/bin/to-lottie-verify.html +68 -0
  26. package/apps/studio/bin/to-lottie.sh +128 -0
  27. package/{plugins/design/dev-server → apps/studio}/build.ts +6 -4
  28. package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +79 -10
  29. package/{plugins/design/dev-server → apps/studio}/canvas-comment-mount.tsx +1 -1
  30. package/apps/studio/canvas-create.ts +104 -0
  31. package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +48 -12
  32. package/{plugins/design/dev-server → apps/studio}/canvas-edit.ts +257 -7
  33. package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +165 -1
  34. package/{plugins/design/dev-server → apps/studio}/canvas-lib-inline.ts +1 -1
  35. package/{plugins/design/dev-server → apps/studio}/canvas-lib-resolver.ts +1 -1
  36. package/{plugins/design/dev-server → apps/studio}/canvas-lib.tsx +31 -12
  37. package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +360 -48
  38. package/apps/studio/client/app.jsx +6742 -0
  39. package/apps/studio/client/canvas-url.js +81 -0
  40. package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
  41. package/apps/studio/client/index.html +16 -0
  42. package/apps/studio/client/styles/1-tokens-maude.css +296 -0
  43. package/apps/studio/client/styles/3-shell-maude.css +1161 -0
  44. package/{plugins/design/dev-server → apps/studio}/client/styles/3-shell.css +129 -0
  45. package/apps/studio/client/styles/4-components-maude.css +74 -0
  46. package/{plugins/design/dev-server → apps/studio}/client/styles/4-components.css +396 -34
  47. package/apps/studio/client/styles/5-maude-overrides.css +137 -0
  48. package/{plugins/design/dev-server → apps/studio}/client/styles/_index.css +9 -0
  49. package/{plugins/design/dev-server → apps/studio}/client/styles.css +1 -1
  50. package/apps/studio/client/tour/overlay.jsx +279 -0
  51. package/apps/studio/client/tour/usage-tour.js +48 -0
  52. package/apps/studio/client/whats-new-seen.js +52 -0
  53. package/apps/studio/client/whats-new.jsx +250 -0
  54. package/{plugins/design/dev-server → apps/studio}/commands/annotation-strokes-command.ts +2 -2
  55. package/{plugins/design/dev-server → apps/studio}/commands/equal-spacing-command.ts +1 -1
  56. package/{plugins/design/dev-server → apps/studio}/commands/move-artboards-command.ts +2 -2
  57. package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +32 -11
  58. package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +5 -4
  59. package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +18 -7
  60. package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +21 -21
  61. package/apps/studio/dist/client.bundle.js +25545 -0
  62. package/apps/studio/dist/comment-mount.js +2046 -0
  63. package/apps/studio/dist/styles.css +8018 -0
  64. package/apps/studio/dom-selection.ts +342 -0
  65. package/apps/studio/draw/animate.ts +333 -0
  66. package/{plugins/design/dev-server → apps/studio}/draw/brush.ts +1 -1
  67. package/{plugins/design/dev-server → apps/studio}/draw/composition.ts +1 -1
  68. package/{plugins/design/dev-server → apps/studio}/draw/geometry.ts +1 -1
  69. package/{plugins/design/dev-server → apps/studio}/draw/index.ts +4 -1
  70. package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +1 -1
  71. package/apps/studio/draw/morph.ts +204 -0
  72. package/{plugins/design/dev-server → apps/studio}/draw/optimize.ts +1 -1
  73. package/{plugins/design/dev-server → apps/studio}/draw/palette.ts +1 -1
  74. package/{plugins/design/dev-server → apps/studio}/draw/primitives.ts +1 -1
  75. package/apps/studio/draw/serialize-animate.ts +324 -0
  76. package/{plugins/design/dev-server → apps/studio}/draw/serialize.ts +9 -3
  77. package/apps/studio/draw/test/animate.test.ts +172 -0
  78. package/apps/studio/draw/test/morph.test.ts +107 -0
  79. package/apps/studio/draw/test/serialize-animate.test.ts +142 -0
  80. package/{plugins/design/dev-server → apps/studio}/equal-spacing-detector.ts +1 -1
  81. package/{plugins/design/dev-server → apps/studio}/equal-spacing-handles.tsx +7 -29
  82. package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +16 -4
  83. package/{plugins/design/dev-server → apps/studio}/history.ts +20 -3
  84. package/{plugins/design/dev-server → apps/studio}/http.ts +226 -6
  85. package/{plugins/design/dev-server → apps/studio}/input-router.tsx +15 -1
  86. package/{plugins/design/dev-server → apps/studio}/inspect.ts +50 -4
  87. package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +5 -2
  88. package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +2 -8
  89. package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
  90. package/{plugins/design/dev-server → apps/studio}/server.ts +23 -2
  91. package/{plugins/design/dev-server → apps/studio}/sync/agent.ts +231 -57
  92. package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +45 -0
  93. package/apps/studio/sync/cold-start.ts +158 -0
  94. package/{plugins/design/dev-server → apps/studio}/sync/connection-state.ts +58 -2
  95. package/{plugins/design/dev-server → apps/studio}/sync/index.ts +563 -221
  96. package/apps/studio/sync/journal.ts +190 -0
  97. package/apps/studio/sync/migrate-seed.ts +321 -0
  98. package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +22 -3
  99. package/{plugins/design/dev-server → apps/studio}/sync/status.ts +15 -4
  100. package/{plugins/design/dev-server → apps/studio}/test/activity.test.ts +1 -7
  101. package/apps/studio/test/annotate-write.test.ts +184 -0
  102. package/apps/studio/test/annotations-align.test.ts +88 -0
  103. package/apps/studio/test/annotations-bindings.test.ts +124 -0
  104. package/apps/studio/test/annotations-groups.test.ts +231 -0
  105. package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +63 -0
  106. package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +97 -0
  107. package/apps/studio/test/annotations-snap.test.ts +79 -0
  108. package/apps/studio/test/annotations-text-format.test.ts +396 -0
  109. package/apps/studio/test/asset-api.test.ts +149 -0
  110. package/apps/studio/test/canvas-create-api.test.ts +361 -0
  111. package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +2 -0
  112. package/apps/studio/test/canvas-edit.test.ts +319 -0
  113. package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +1 -1
  114. package/apps/studio/test/canvas-media-drop.test.ts +158 -0
  115. package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +25 -1
  116. package/apps/studio/test/canvas-url.test.ts +99 -0
  117. package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +0 -2
  118. package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +2 -7
  119. package/{plugins/design/dev-server → apps/studio}/test/csp-canvas-shell.test.ts +5 -0
  120. package/apps/studio/test/csrf-write-guard.test.ts +52 -0
  121. package/apps/studio/test/figjam-v3-model.test.ts +342 -0
  122. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +1 -0
  123. package/apps/studio/test/index-data-canvas-ds.test.ts +97 -0
  124. package/apps/studio/test/motion-sample.test.ts +35 -0
  125. package/{plugins/design/dev-server → apps/studio}/test/perf-harness.ts +1 -1
  126. package/{plugins/design/dev-server → apps/studio}/test/phase-3.6-smoke.test.ts +3 -1
  127. package/apps/studio/test/read-annotations.test.ts +409 -0
  128. package/{plugins/design/dev-server → apps/studio}/test/sanitize-annotation-svg.test.ts +108 -2
  129. package/{plugins/design/dev-server → apps/studio}/test/shared-doc-convergence.test.ts +1 -1
  130. package/apps/studio/test/shared-doc-migrate.test.ts +369 -0
  131. package/apps/studio/test/sync-agent.test.ts +575 -0
  132. package/{plugins/design/dev-server → apps/studio}/test/sync-codec.test.ts +65 -0
  133. package/apps/studio/test/sync-cold-start.test.ts +244 -0
  134. package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +68 -0
  135. package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +0 -8
  136. package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +0 -1
  137. package/apps/studio/test/sync-incident-replay.test.ts +211 -0
  138. package/apps/studio/test/sync-journal.test.ts +176 -0
  139. package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +360 -5
  140. package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +66 -0
  141. package/apps/studio/test/tour-overlay.test.tsx +56 -0
  142. package/apps/studio/test/use-annotation-resize.test.ts +402 -0
  143. package/{plugins/design/dev-server → apps/studio}/test/use-artboard-drag.test.ts +0 -1
  144. package/{plugins/design/dev-server → apps/studio}/test/use-collab.test.ts +0 -0
  145. package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +8 -2
  146. package/apps/studio/test/whats-new.test.ts +101 -0
  147. package/apps/studio/text-imports.d.ts +9 -0
  148. package/{plugins/design/dev-server → apps/studio}/tool-palette.tsx +15 -70
  149. package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -2
  150. package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
  151. package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +5 -3
  152. package/apps/studio/use-annotation-resize.tsx +912 -0
  153. package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +17 -5
  154. package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +1 -1
  155. package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +1 -1
  156. package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +3 -3
  157. package/apps/studio/use-canvas-media-drop.tsx +343 -0
  158. package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +21 -16
  159. package/{plugins/design/dev-server → apps/studio}/use-cursor-modifiers.tsx +1 -1
  160. package/{plugins/design/dev-server → apps/studio}/use-keyboard-discipline.tsx +5 -2
  161. package/{plugins/design/dev-server → apps/studio}/use-selection-set.tsx +18 -1
  162. package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
  163. package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +8 -2
  164. package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +1 -1
  165. package/apps/studio/whats-new.json +264 -0
  166. package/apps/studio/whats-new.schema.json +86 -0
  167. package/apps/studio/whats-new.ts +87 -0
  168. package/cli/bin/maude.mjs +10 -0
  169. package/cli/commands/design-link.test.mjs +84 -0
  170. package/cli/commands/design.mjs +16 -7
  171. package/cli/commands/design.test.mjs +40 -0
  172. package/cli/commands/help.mjs +8 -2
  173. package/cli/commands/hub.mjs +3 -3
  174. package/cli/commands/hub.test.mjs +1 -1
  175. package/cli/commands/scenario-report.mjs +2 -9
  176. package/cli/lib/design-link.mjs +51 -1
  177. package/cli/lib/flow-design-integration.test.mjs +1 -1
  178. package/cli/lib/gitignore-block.mjs +1 -0
  179. package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
  180. package/cli/lib/preflight.mjs +1 -1
  181. package/package.json +15 -15
  182. package/plugins/design/templates/_shell.html +28 -4
  183. package/plugins/design/templates/brief-board.tsx.template +61 -0
  184. package/plugins/design/templates/canvas.tsx.template +1 -1
  185. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -1
  186. package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +1 -1
  187. package/plugins/flow/.claude-plugin/config.schema.json +16 -0
  188. package/plugins/design/dev-server/annotations-layer.tsx +0 -3402
  189. package/plugins/design/dev-server/client/app.jsx +0 -2985
  190. package/plugins/design/dev-server/client/index.html +0 -15
  191. package/plugins/design/dev-server/dist/client.bundle.js +0 -20692
  192. package/plugins/design/dev-server/dist/comment-mount.js +0 -1924
  193. package/plugins/design/dev-server/dist/styles.css +0 -2710
  194. package/plugins/design/dev-server/dom-selection.ts +0 -156
  195. package/plugins/design/dev-server/sync/migrate-seed.ts +0 -163
  196. package/plugins/design/dev-server/test/canvas-edit.test.ts +0 -139
  197. package/plugins/design/dev-server/test/shared-doc-migrate.test.ts +0 -159
  198. package/plugins/design/dev-server/test/sync-agent.test.ts +0 -305
  199. package/plugins/design/dev-server/test/use-annotation-resize.test.ts +0 -200
  200. package/plugins/design/dev-server/use-annotation-resize.tsx +0 -449
  201. /package/{plugins/design/dev-server → apps/studio}/activity.ts +0 -0
  202. /package/{plugins/design/dev-server → apps/studio}/bin/_enumerate-artboards-playwright.mjs +0 -0
  203. /package/{plugins/design/dev-server → apps/studio}/bin/_html-playwright.mjs +0 -0
  204. /package/{plugins/design/dev-server → apps/studio}/bin/_pdf-playwright.mjs +0 -0
  205. /package/{plugins/design/dev-server → apps/studio}/bin/_pptx-playwright.mjs +0 -0
  206. /package/{plugins/design/dev-server → apps/studio}/bin/_pw-launch.mjs +0 -0
  207. /package/{plugins/design/dev-server → apps/studio}/bin/_screenshot-playwright.mjs +0 -0
  208. /package/{plugins/design/dev-server → apps/studio}/bin/_svg-optimize.mjs +0 -0
  209. /package/{plugins/design/dev-server → apps/studio}/bin/_svg-playwright.mjs +0 -0
  210. /package/{plugins/design/dev-server → apps/studio}/bin/asset-sweep.sh +0 -0
  211. /package/{plugins/design/dev-server → apps/studio}/bin/bootstrap-check.sh +0 -0
  212. /package/{plugins/design/dev-server → apps/studio}/bin/check-runtime-bundles.sh +0 -0
  213. /package/{plugins/design/dev-server → apps/studio}/bin/draw-build.sh +0 -0
  214. /package/{plugins/design/dev-server → apps/studio}/bin/handoff.sh +0 -0
  215. /package/{plugins/design/dev-server → apps/studio}/bin/prep.sh +0 -0
  216. /package/{plugins/design/dev-server → apps/studio}/bin/scenario-report.mjs +0 -0
  217. /package/{plugins/design/dev-server → apps/studio}/bin/slug.sh +0 -0
  218. /package/{plugins/design/dev-server → apps/studio}/bin/smoke.sh +0 -0
  219. /package/{plugins/design/dev-server → apps/studio}/bin/svg-optimize.sh +0 -0
  220. /package/{plugins/design/dev-server → apps/studio}/bin/visual-sanity.sh +0 -0
  221. /package/{plugins/design/dev-server → apps/studio}/boot-self-heal.ts +0 -0
  222. /package/{plugins/design/dev-server → apps/studio}/canvas-build.ts +0 -0
  223. /package/{plugins/design/dev-server → apps/studio}/canvas-header.ts +0 -0
  224. /package/{plugins/design/dev-server → apps/studio}/canvas-meta.schema.json +0 -0
  225. /package/{plugins/design/dev-server → apps/studio}/canvas-pipeline.ts +0 -0
  226. /package/{plugins/design/dev-server → apps/studio}/client/hmr.mjs +0 -0
  227. /package/{plugins/design/dev-server → apps/studio}/client/iframe-lazy.mjs +0 -0
  228. /package/{plugins/design/dev-server → apps/studio}/client/styles/0-reset.css +0 -0
  229. /package/{plugins/design/dev-server → apps/studio}/client/styles/1-tokens.css +0 -0
  230. /package/{plugins/design/dev-server → apps/studio}/client/styles/2-layout.css +0 -0
  231. /package/{plugins/design/dev-server → apps/studio}/client/styles/5-utilities.css +0 -0
  232. /package/{plugins/design/dev-server → apps/studio}/collab/ai-activity.ts +0 -0
  233. /package/{plugins/design/dev-server → apps/studio}/collab/awareness-bridge.ts +0 -0
  234. /package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.ts +0 -0
  235. /package/{plugins/design/dev-server → apps/studio}/collab/index.ts +0 -0
  236. /package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +0 -0
  237. /package/{plugins/design/dev-server → apps/studio}/collab/protocol.ts +0 -0
  238. /package/{plugins/design/dev-server → apps/studio}/collab/registry.ts +0 -0
  239. /package/{plugins/design/dev-server → apps/studio}/collab/room.ts +0 -0
  240. /package/{plugins/design/dev-server → apps/studio}/config.schema.json +0 -0
  241. /package/{plugins/design/dev-server → apps/studio}/context.ts +0 -0
  242. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/.min-sizes.json +0 -0
  243. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_decoding.js +0 -0
  244. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_encoding.js +0 -0
  245. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion.js +0 -0
  246. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion_react.js +0 -0
  247. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/pixi-js.js +0 -0
  248. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom.js +0 -0
  249. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom_client.js +0 -0
  250. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react.js +0 -0
  251. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-dev-runtime.js +0 -0
  252. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-runtime.js +0 -0
  253. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_awareness.js +0 -0
  254. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_sync.js +0 -0
  255. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/yjs.js +0 -0
  256. /package/{plugins/design/dev-server → apps/studio}/draw/test/brush.test.ts +0 -0
  257. /package/{plugins/design/dev-server → apps/studio}/draw/test/composition.test.ts +0 -0
  258. /package/{plugins/design/dev-server → apps/studio}/draw/test/geometry.test.ts +0 -0
  259. /package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.test.ts +0 -0
  260. /package/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +0 -0
  261. /package/{plugins/design/dev-server → apps/studio}/draw/test/optimize.test.ts +0 -0
  262. /package/{plugins/design/dev-server → apps/studio}/draw/test/palette.test.ts +0 -0
  263. /package/{plugins/design/dev-server → apps/studio}/draw/test/primitives.test.ts +0 -0
  264. /package/{plugins/design/dev-server → apps/studio}/draw/test/serialize.test.ts +0 -0
  265. /package/{plugins/design/dev-server → apps/studio}/examples/README.md +0 -0
  266. /package/{plugins/design/dev-server → apps/studio}/examples/perf-100-artboards.tsx +0 -0
  267. /package/{plugins/design/dev-server → apps/studio}/exporters/_browser-bundles.ts +0 -0
  268. /package/{plugins/design/dev-server → apps/studio}/exporters/canva-handoff-prompt.ts +0 -0
  269. /package/{plugins/design/dev-server → apps/studio}/exporters/canva.ts +0 -0
  270. /package/{plugins/design/dev-server → apps/studio}/exporters/html.ts +0 -0
  271. /package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +0 -0
  272. /package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +0 -0
  273. /package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +0 -0
  274. /package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +0 -0
  275. /package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
  276. /package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +0 -0
  277. /package/{plugins/design/dev-server → apps/studio}/exporters/zip.ts +0 -0
  278. /package/{plugins/design/dev-server → apps/studio}/fs-watch.ts +0 -0
  279. /package/{plugins/design/dev-server → apps/studio}/handoff.ts +0 -0
  280. /package/{plugins/design/dev-server → apps/studio}/hmr-broadcast.ts +0 -0
  281. /package/{plugins/design/dev-server → apps/studio}/locator.ts +0 -0
  282. /package/{plugins/design/dev-server → apps/studio}/mem.ts +0 -0
  283. /package/{plugins/design/dev-server → apps/studio}/runtime-bundle.ts +0 -0
  284. /package/{plugins/design/dev-server → apps/studio}/server.mjs +0 -0
  285. /package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
  286. /package/{plugins/design/dev-server → apps/studio}/sync/echo-guard.ts +0 -0
  287. /package/{plugins/design/dev-server → apps/studio}/sync/fs-mirror.ts +0 -0
  288. /package/{plugins/design/dev-server → apps/studio}/sync/hubs-config.ts +0 -0
  289. /package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +0 -0
  290. /package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
  291. /package/{plugins/design/dev-server → apps/studio}/sync/untrusted.ts +0 -0
  292. /package/{plugins/design/dev-server → apps/studio}/test/_helpers.ts +0 -0
  293. /package/{plugins/design/dev-server → apps/studio}/test/active-state.test.ts +0 -0
  294. /package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +0 -0
  295. /package/{plugins/design/dev-server → apps/studio}/test/annotation-strokes-command.test.ts +0 -0
  296. /package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +0 -0
  297. /package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +0 -0
  298. /package/{plugins/design/dev-server → apps/studio}/test/artboard-activity-overlay.test.tsx +0 -0
  299. /package/{plugins/design/dev-server → apps/studio}/test/binary-smoke.test.ts +0 -0
  300. /package/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +0 -0
  301. /package/{plugins/design/dev-server → apps/studio}/test/bundle-smoke.test.ts +0 -0
  302. /package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
  303. /package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +0 -0
  304. /package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +0 -0
  305. /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +0 -0
  306. /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.test.ts +0 -0
  307. /package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +0 -0
  308. /package/{plugins/design/dev-server → apps/studio}/test/canvas-pipeline.test.ts +0 -0
  309. /package/{plugins/design/dev-server → apps/studio}/test/canvas-route.test.ts +0 -0
  310. /package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +0 -0
  311. /package/{plugins/design/dev-server → apps/studio}/test/collab-awareness-bridge.test.ts +0 -0
  312. /package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
  313. /package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +0 -0
  314. /package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.test.ts +0 -0
  315. /package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +0 -0
  316. /package/{plugins/design/dev-server → apps/studio}/test/comment-mount.test.ts +0 -0
  317. /package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +0 -0
  318. /package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +0 -0
  319. /package/{plugins/design/dev-server → apps/studio}/test/context-resolve-tokens.test.ts +0 -0
  320. /package/{plugins/design/dev-server → apps/studio}/test/equal-spacing-detector.test.ts +0 -0
  321. /package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +0 -0
  322. /package/{plugins/design/dev-server → apps/studio}/test/exporters/endpoint.test.ts +0 -0
  323. /package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +0 -0
  324. /package/{plugins/design/dev-server → apps/studio}/test/exporters/html.test.ts +0 -0
  325. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pdf.test.ts +0 -0
  326. /package/{plugins/design/dev-server → apps/studio}/test/exporters/png.test.ts +0 -0
  327. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx-deck.test.ts +0 -0
  328. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx.test.ts +0 -0
  329. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +0 -0
  330. /package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +0 -0
  331. /package/{plugins/design/dev-server → apps/studio}/test/exporters/svg.test.ts +0 -0
  332. /package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +0 -0
  333. /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-20-annotations.svg +0 -0
  334. /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-21-annotations.svg +0 -0
  335. /package/{plugins/design/dev-server → apps/studio}/test/fs-watch.test.ts +0 -0
  336. /package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +0 -0
  337. /package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +0 -0
  338. /package/{plugins/design/dev-server → apps/studio}/test/handoff.test.ts +0 -0
  339. /package/{plugins/design/dev-server → apps/studio}/test/history-rollback.test.ts +0 -0
  340. /package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +0 -0
  341. /package/{plugins/design/dev-server → apps/studio}/test/hmr-classify.test.ts +0 -0
  342. /package/{plugins/design/dev-server → apps/studio}/test/input-router.test.ts +0 -0
  343. /package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +0 -0
  344. /package/{plugins/design/dev-server → apps/studio}/test/marquee-overlay.test.ts +0 -0
  345. /package/{plugins/design/dev-server → apps/studio}/test/move-artboards-command.test.ts +0 -0
  346. /package/{plugins/design/dev-server → apps/studio}/test/participants-chrome.test.ts +0 -0
  347. /package/{plugins/design/dev-server → apps/studio}/test/paths.test.ts +0 -0
  348. /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle-error-mapping.test.ts +0 -0
  349. /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle.test.ts +0 -0
  350. /package/{plugins/design/dev-server → apps/studio}/test/server-lifecycle.test.ts +0 -0
  351. /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-foundation.test.ts +0 -0
  352. /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +0 -0
  353. /package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +0 -0
  354. /package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +0 -0
  355. /package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +0 -0
  356. /package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +0 -0
  357. /package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +0 -0
  358. /package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +0 -0
  359. /package/{plugins/design/dev-server → apps/studio}/test/system-endpoint.test.ts +0 -0
  360. /package/{plugins/design/dev-server → apps/studio}/test/tool-palette.test.tsx +0 -0
  361. /package/{plugins/design/dev-server → apps/studio}/test/undo-stack.test.ts +0 -0
  362. /package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +0 -0
  363. /package/{plugins/design/dev-server → apps/studio}/test/use-annotation-selection.test.tsx +0 -0
  364. /package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +0 -0
  365. /package/{plugins/design/dev-server → apps/studio}/test/use-cursor-modifiers.test.ts +0 -0
  366. /package/{plugins/design/dev-server → apps/studio}/test/use-keyboard-discipline.test.ts +0 -0
  367. /package/{plugins/design/dev-server → apps/studio}/test/use-selection-set.test.tsx +0 -0
  368. /package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +0 -0
  369. /package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +0 -0
  370. /package/{plugins/design/dev-server → apps/studio}/test/ws-handshake.test.ts +0 -0
  371. /package/{plugins/design/dev-server → apps/studio}/tsconfig.json +0 -0
  372. /package/{plugins/design/dev-server → apps/studio}/ws.ts +0 -0
@@ -0,0 +1,342 @@
1
+ /**
2
+ * @file dom-selection.ts — selection-from-DOM helpers (leaf module)
3
+ * @scope apps/studio/dom-selection.ts
4
+ * @purpose Pure DOM → Selection builders shared by the canvas chrome
5
+ * (canvas-shell.tsx) and the shell-owned comment mount layer
6
+ * (canvas-comment-mount.tsx). Lives in its own leaf module — no
7
+ * React, no canvas-lib import — so both consumers can lift the
8
+ * same `hoverTargetToSelection` / `deriveFile` logic without a
9
+ * cycle and without bundling the heavy DesignCanvas tree into the
10
+ * lite comment mount.
11
+ */
12
+
13
+ import type { HoverTarget } from './input-router.tsx';
14
+ import type { Selection } from './use-selection-set.tsx';
15
+
16
+ /**
17
+ * Canvas file path for the current page. Under the mount harness the page is
18
+ * `/_canvas-shell.html?canvas=<rel>&designRel=<root>`; for legacy `.html`
19
+ * mocks it's the served file path itself.
20
+ */
21
+ export function deriveFile(): string | undefined {
22
+ if (typeof window === 'undefined') return undefined;
23
+ try {
24
+ const p = window.location.pathname;
25
+ if (p === '/_canvas-shell.html' || p === '/_canvas-shell') {
26
+ const qs = new URLSearchParams(window.location.search);
27
+ const canvas = qs.get('canvas') ?? '';
28
+ const designRel = (qs.get('designRel') ?? '.design').replace(/^\/+|\/+$/g, '');
29
+ return canvas ? `${designRel}/${canvas}` : undefined;
30
+ }
31
+ return decodeURIComponent(p).replace(/^\//, '');
32
+ } catch {
33
+ return undefined;
34
+ }
35
+ }
36
+
37
+ export function realClasses(el: Element | null): string {
38
+ if (!el) return '';
39
+ return (el.getAttribute('class') ?? '')
40
+ .trim()
41
+ .split(/\s+/)
42
+ .filter((c) => c && !c.startsWith('dgn-') && !c.startsWith('dc-cv-'))
43
+ .join(' ');
44
+ }
45
+
46
+ export function shortText(el: Element | null, max: number): string {
47
+ if (!el) return '';
48
+ const t = ((el as HTMLElement).innerText || el.textContent || '').replace(/\s+/g, ' ').trim();
49
+ return t.length > max ? `${t.slice(0, max - 1)}…` : t;
50
+ }
51
+
52
+ export function cssPath(el: Element | null): string {
53
+ if (!el) return '';
54
+ const path: string[] = [];
55
+ let cur: Element | null = el;
56
+ while (cur && cur.nodeType === 1 && path.length < 8) {
57
+ const dscEl = cur.getAttribute?.('data-dc-element');
58
+ if (dscEl) {
59
+ path.unshift(`[data-dc-element="${dscEl}"]`);
60
+ break;
61
+ }
62
+ const dscSc = cur.getAttribute?.('data-dc-screen');
63
+ if (dscSc) {
64
+ path.unshift(`[data-dc-screen="${dscSc}"]`);
65
+ break;
66
+ }
67
+ let sel = cur.nodeName.toLowerCase();
68
+ if (cur.id) {
69
+ sel = `#${cur.id}`;
70
+ path.unshift(sel);
71
+ break;
72
+ }
73
+ const cls = realClasses(cur).split(/\s+/).filter(Boolean).slice(0, 2);
74
+ if (cls.length) sel += `.${cls.join('.')}`;
75
+ let sib = 1;
76
+ let n: Element | null = cur.previousElementSibling;
77
+ while (n) {
78
+ sib++;
79
+ n = n.previousElementSibling;
80
+ }
81
+ sel += `:nth-child(${sib})`;
82
+ path.unshift(sel);
83
+ cur = cur.parentElement;
84
+ }
85
+ return path.join(' > ');
86
+ }
87
+
88
+ export function domPath(el: Element | null): string[] {
89
+ const hops: string[] = [];
90
+ let cur = el;
91
+ while (cur && cur.nodeType === 1 && hops.length < 8) {
92
+ let label = cur.nodeName.toLowerCase();
93
+ const dEl = cur.getAttribute?.('data-dc-element');
94
+ const dSc = cur.getAttribute?.('data-dc-screen');
95
+ if (dEl) label += `[data-dc-element="${dEl}"]`;
96
+ else if (dSc) label += `[data-dc-screen="${dSc}"]`;
97
+ else if (cur.id) label += `#${cur.id}`;
98
+ const cls = realClasses(cur).split(/\s+/).filter(Boolean).slice(0, 2);
99
+ if (cls.length && !dEl && !dSc) label += `.${cls.join('.')}`;
100
+ hops.unshift(label);
101
+ cur = cur.parentElement;
102
+ }
103
+ return hops;
104
+ }
105
+
106
+ export function cssEscape(s: string): string {
107
+ // Minimal CSS.escape polyfill — only handles chars actually present in
108
+ // pipeline-stamped IDs (alphanumerics + `-` + `_`).
109
+ return s.replace(/[^a-zA-Z0-9_-]/g, (c) => `\\${c}`);
110
+ }
111
+
112
+ /**
113
+ * Build the wire-shape `Selection` for a resolved hover target. `file`
114
+ * defaults to `deriveFile()`; the comment mount layer passes it explicitly
115
+ * so all three consumers (router, overlay, mount) agree on the same key.
116
+ */
117
+ /**
118
+ * The artboard-scoped data-cd-id selector. A component shared across artboards
119
+ * carries the SAME data-cd-id in each, so a bare `[data-cd-id="…"]` resolves
120
+ * (via querySelector) to the FIRST artboard. Prefixing the hit's artboard makes
121
+ * the anchor per-instance. Shared by EVERY selector builder + resolver so they
122
+ * can't drift (the original fix only patched one of ~8 sites).
123
+ */
124
+ export function scopedCdSelector(cdId: string, artboardId?: string | null): string {
125
+ return artboardId
126
+ ? `[data-dc-screen="${artboardId}"] [data-cd-id="${cdId}"]`
127
+ : `[data-cd-id="${cdId}"]`;
128
+ }
129
+
130
+ /**
131
+ * Occurrence index of `el` among `doc.querySelectorAll(selector)`. Even with an
132
+ * artboard-scoped data-cd-id selector, a component repeated WITHIN one artboard
133
+ * (a list row, or a reusable used twice) produces several matches — the index
134
+ * is the only thing that makes the anchor truly unique per DOM instance.
135
+ */
136
+ export function selectorIndex(doc: Document, selector: string, el: Element | null): number {
137
+ if (!el) return 0;
138
+ try {
139
+ const all = doc.querySelectorAll(selector);
140
+ for (let i = 0; i < all.length; i++) {
141
+ if (all[i] === el) return i;
142
+ }
143
+ } catch {
144
+ /* malformed selector */
145
+ }
146
+ return 0;
147
+ }
148
+
149
+ /**
150
+ * Resolve a stored Selection to its live element, artboard-scoped. Prefers the
151
+ * id+artboardId scoped selector (the robust path), then the stored `selector`
152
+ * (already scoped for recent selections; a legacy fallback for old comments).
153
+ * Every halo / pin / toolbar / spacing-handle resolver routes through this so a
154
+ * shared component anchors to the instance the user actually clicked.
155
+ */
156
+ export function resolveSelectionEl(
157
+ doc: Document,
158
+ sel: { id?: string | null; selector?: string | null; artboardId?: string | null; index?: number }
159
+ ): Element | null {
160
+ const at = (selector: string): Element | null => {
161
+ try {
162
+ const all = doc.querySelectorAll(selector);
163
+ if (!all.length) return null;
164
+ const i = sel.index && sel.index > 0 && sel.index < all.length ? sel.index : 0;
165
+ return all[i] ?? all[0];
166
+ } catch {
167
+ return null;
168
+ }
169
+ };
170
+ if (sel.id) {
171
+ const el = at(scopedCdSelector(sel.id, sel.artboardId));
172
+ if (el) return el;
173
+ }
174
+ if (sel.selector) return at(sel.selector);
175
+ return null;
176
+ }
177
+
178
+ /**
179
+ * Phase 12.2 — style maps for the CSS-knob properties. `authored` is what the
180
+ * element sets INLINE (React renders `style={{padding:8}}` → `style="padding:8px"`),
181
+ * so the knob pre-fills the EDITABLE source value and is blank when unset — not
182
+ * the noisy resolved default (`656.003px`, `rgb(0,0,0)`). `computed` is the
183
+ * resolved value, shown only as a faint placeholder hint. Empty for a detached
184
+ * node / SSR.
185
+ */
186
+ // Phase 12.3 (W2.2 fix) — EVERY property the CSS panel has a control for, so each
187
+ // reads back into `authored` (pre-fills the right control) and is excluded from
188
+ // `customStyles`. The earlier short list omitted the box-model LONGHANDS
189
+ // (`padding-top`, `margin-left`, …) + the Layout/border longhands, so a value the
190
+ // panel wrote (e.g. an alt-scrub `padding-top`) fell through to customStyles and
191
+ // the box-model widget showed it as a "custom CSS property" instead of in the box.
192
+ const KNOB_PROPS = [
193
+ // Layout
194
+ 'display',
195
+ 'flex-direction',
196
+ 'align-items',
197
+ 'justify-content',
198
+ 'gap',
199
+ // Typography
200
+ 'font-family',
201
+ 'color',
202
+ 'font-size',
203
+ 'font-weight',
204
+ 'line-height',
205
+ 'letter-spacing',
206
+ 'text-align',
207
+ // Spacing — shorthand (for customStyles exclusion + whole-side authoring) AND
208
+ // the 8 longhands the box-model widget actually reads/writes.
209
+ 'margin',
210
+ 'margin-top',
211
+ 'margin-right',
212
+ 'margin-bottom',
213
+ 'margin-left',
214
+ 'padding',
215
+ 'padding-top',
216
+ 'padding-right',
217
+ 'padding-bottom',
218
+ 'padding-left',
219
+ // Size
220
+ 'width',
221
+ 'height',
222
+ 'max-width',
223
+ // Appearance
224
+ 'background-color',
225
+ 'border-radius',
226
+ 'border-top-left-radius',
227
+ 'border-top-right-radius',
228
+ 'border-bottom-left-radius',
229
+ 'border-bottom-right-radius',
230
+ 'border-width',
231
+ 'border-style',
232
+ 'border-color',
233
+ 'box-shadow',
234
+ 'opacity',
235
+ ] as const;
236
+
237
+ // Phase 12.3 — HTML attributes the custom-attribute hatch may have written, so a
238
+ // just-added `data-*`/`aria-*`/`role`/`title` round-trips back into a panel row.
239
+ // Excludes the structural ones the panel manages elsewhere (style, class,
240
+ // data-cd-id pipeline anchor, the data-dc-* canvas chrome markers).
241
+ const ATTR_SKIP = /^(style|class|data-cd-id|data-dc-|data-dcid$)/;
242
+
243
+ function styleMapsFor(el: Element | null): {
244
+ authored: Record<string, string>;
245
+ computed: Record<string, string>;
246
+ customStyles: Record<string, string>;
247
+ attrs: Record<string, string>;
248
+ } {
249
+ if (!el || typeof window === 'undefined' || !window.getComputedStyle) {
250
+ return { authored: {}, computed: {}, customStyles: {}, attrs: {} };
251
+ }
252
+ try {
253
+ const inline = (el as HTMLElement).style;
254
+ const cs = window.getComputedStyle(el as HTMLElement);
255
+ const authored: Record<string, string> = {};
256
+ const computed: Record<string, string> = {};
257
+ const knob = new Set<string>(KNOB_PROPS as readonly string[]);
258
+ // Curated knob props: authored value (knob pre-fill) + computed (placeholder).
259
+ for (const p of KNOB_PROPS) {
260
+ const a = inline.getPropertyValue(p);
261
+ if (a) authored[p] = a.trim();
262
+ const c = cs.getPropertyValue(p);
263
+ if (c) computed[p] = c.trim();
264
+ }
265
+ // Every OTHER authored inline property → customStyles, so the panel can show
266
+ // a custom CSS property the user added that no curated row covers. EXCLUDE
267
+ // the panel-managed FAMILIES wholesale: setting `border`/`border-radius` (or
268
+ // their 3-way shorthands) makes the CSSOM expand them to the per-side
269
+ // longhands — `border-top-width`, `border-left-color`, … — which the panel
270
+ // controls but can't enumerate in the knob set. Without the family guard they
271
+ // leak into "custom CSS properties" (the same class of bug fixed for spacing).
272
+ const MANAGED_FAMILY = /^(margin|padding|border)(-|$)/;
273
+ const customStyles: Record<string, string> = {};
274
+ for (let i = 0; i < inline.length; i++) {
275
+ const p = inline.item(i);
276
+ if (!p || knob.has(p) || MANAGED_FAMILY.test(p)) continue;
277
+ const v = inline.getPropertyValue(p);
278
+ if (v) customStyles[p] = v.trim();
279
+ }
280
+ // Custom HTML attributes (the escape-hatch surface).
281
+ const attrs: Record<string, string> = {};
282
+ for (const a of Array.from((el as HTMLElement).attributes)) {
283
+ if (ATTR_SKIP.test(a.name)) continue;
284
+ attrs[a.name] = a.value;
285
+ }
286
+ return { authored, computed, customStyles, attrs };
287
+ } catch {
288
+ return { authored: {}, computed: {}, customStyles: {}, attrs: {} };
289
+ }
290
+ }
291
+
292
+ export function hoverTargetToSelection(target: HoverTarget, file?: string): Selection {
293
+ const el = target.el;
294
+ const rect =
295
+ el && (el as HTMLElement).getBoundingClientRect
296
+ ? (el as HTMLElement).getBoundingClientRect()
297
+ : null;
298
+ // `cdId` is the hit element's OWN data-cd-id (deep mode); resolver never
299
+ // climbs to an ancestor. Falls back to cssPath of the hit when no stable
300
+ // anchor exists.
301
+ const cdId = target.cdId;
302
+ // Selector resolution order:
303
+ // 1. data-cd-id anchor — stable pipeline-stamped id (preferred). SCOPED by
304
+ // the hit's artboard (`[data-dc-screen=…] [data-cd-id=…]`) — a component
305
+ // shared across artboards carries the SAME data-cd-id in each, so an
306
+ // unscoped `[data-cd-id]` selector resolves (via querySelector) to the
307
+ // FIRST artboard's instance and the pin/select lands on the wrong board.
308
+ // Prefixing the artboard makes the anchor per-instance.
309
+ // 2. data-dc-screen — chrome click promoted to whole-artboard select
310
+ // (T24.5 G8 multi-artboard gesture).
311
+ // 3. cssPath of the hit — last-resort path string.
312
+ const selector = cdId
313
+ ? scopedCdSelector(cdId, target.artboardId)
314
+ : target.artboardId
315
+ ? `[data-dc-screen="${target.artboardId}"]`
316
+ : cssPath(el);
317
+ // Disambiguate repeated instances within the same artboard (list rows, a
318
+ // reusable used twice) — the index is which `querySelectorAll(selector)`
319
+ // match this element is. cssPath is already unique, so 0 there.
320
+ const index = cdId && typeof document !== 'undefined' ? selectorIndex(document, selector, el) : 0;
321
+ return {
322
+ file: file ?? deriveFile(),
323
+ id: cdId ?? undefined,
324
+ selector,
325
+ artboardId: target.artboardId,
326
+ index,
327
+ tag: el?.tagName.toLowerCase() ?? '',
328
+ classes: realClasses(el),
329
+ text: shortText(el, 240),
330
+ dom_path: domPath(el),
331
+ bounds: rect
332
+ ? {
333
+ x: Math.round(rect.left),
334
+ y: Math.round(rect.top),
335
+ w: Math.round(rect.width),
336
+ h: Math.round(rect.height),
337
+ }
338
+ : null,
339
+ html: el ? (el.outerHTML ?? '').slice(0, 4000) : '',
340
+ ...styleMapsFor(el),
341
+ };
342
+ }
@@ -0,0 +1,333 @@
1
+ /**
2
+ * @file draw/animate.ts — draw animation IR (keyframe timeline)
3
+ * @scope apps/studio/draw/animate.ts
4
+ * @purpose The cross-platform ANIMATION source of truth. Where `primitives.ts`
5
+ * names *shape*, this module names *time*: a keyframe `Timeline` of
6
+ * property `Track`s on any node. It extends the DDR-067 single-source
7
+ * invariant from static geometry to motion — one IR feeds the web
8
+ * authoring serializers (SMIL + motion/JSX, `serialize.ts`) AND the
9
+ * production `toLottie()` emitter (DDR-094).
10
+ *
11
+ * Vocabulary:
12
+ * Keyframe — { t, value, ease? } at an ABSOLUTE time (seconds).
13
+ * Track — keyframes on one property of one node (`d` morph as a
14
+ * vertex array, `transform`, `opacity`, gradient stops).
15
+ * Timeline — { dur, begin?, repeat?, tracks, stagger? }.
16
+ *
17
+ * Choreography ("animate sequences of anything, scalable") is built
18
+ * from pure combinators — `sequence` (in series), `parallel` (at
19
+ * once), `stagger` (offset each track by index·delay). Easing is
20
+ * per-segment CSS `cubic-bezier`, evaluated with a Newton-Raphson
21
+ * solver so authoring tokens (incl. overshoot, `y>1`) round-trip
22
+ * exactly into SMIL `keySplines` / Lottie `i`/`o` handles.
23
+ *
24
+ * DEPENDENCY RULE (DDR-067): React-free root. Imports only the pure
25
+ * `Point` type from `primitives.ts`; nothing from react or a `.tsx`.
26
+ */
27
+
28
+ import type { Point } from './primitives.ts';
29
+
30
+ // ─────────────────────────────────────────────────────────────────────────────
31
+ // Easing — CSS cubic-bezier(x1,y1,x2,y2) timing function
32
+ // ─────────────────────────────────────────────────────────────────────────────
33
+
34
+ /**
35
+ * The four control-point handles of a CSS `cubic-bezier(x1,y1,x2,y2)` easing.
36
+ * `x` components are clamped to [0,1] (CSS constraint); `y` may exceed [0,1]
37
+ * to express overshoot/anticipation (`--ease-out (0.34,1.42,…)` "snap").
38
+ */
39
+ export type CubicBezierHandles = readonly [x1: number, y1: number, x2: number, y2: number];
40
+
41
+ /** `linear` — the identity timing function. */
42
+ export const LINEAR: CubicBezierHandles = [0, 0, 1, 1];
43
+
44
+ /** One axis of a cubic Bézier with P0=0, P3=1: B(t) = 3(1-t)²t·c1 + 3(1-t)t²·c2 + t³. */
45
+ function bezierAxis(t: number, c1: number, c2: number): number {
46
+ const mt = 1 - t;
47
+ return 3 * mt * mt * t * c1 + 3 * mt * t * t * c2 + t * t * t;
48
+ }
49
+
50
+ /** dB/dt for the same axis — used by the Newton-Raphson x→t solve. */
51
+ function bezierAxisDeriv(t: number, c1: number, c2: number): number {
52
+ const mt = 1 - t;
53
+ return 3 * mt * mt * c1 + 6 * mt * t * (c2 - c1) + 3 * t * t * (1 - c2);
54
+ }
55
+
56
+ /**
57
+ * Evaluate a CSS cubic-bezier easing: given progress `x` ∈ [0,1] along the
58
+ * timeline, return the eased output `y`. Solves `bezierX(t) = x` for the curve
59
+ * parameter `t` (Newton-Raphson, bisection fallback), then returns `bezierY(t)`.
60
+ * `linear` short-circuits. Output `y` is NOT clamped — overshoot is preserved.
61
+ */
62
+ export function easeBezier(handles: CubicBezierHandles, x: number): number {
63
+ const [x1, y1, x2, y2] = handles;
64
+ if (x <= 0) return 0;
65
+ if (x >= 1) return 1;
66
+ // Linear fast-path (both handles on the diagonal).
67
+ if (x1 === y1 && x2 === y2) return x;
68
+
69
+ // Solve bezierX(t) = x for t.
70
+ let t = x;
71
+ for (let i = 0; i < 8; i++) {
72
+ const xe = bezierAxis(t, x1, x2) - x;
73
+ if (Math.abs(xe) < 1e-7) break;
74
+ const dx = bezierAxisDeriv(t, x1, x2);
75
+ if (Math.abs(dx) < 1e-7) break;
76
+ t -= xe / dx;
77
+ }
78
+ // Bisection fallback if Newton left the [0,1] domain or stalled.
79
+ if (t < 0 || t > 1 || Number.isNaN(t)) {
80
+ let lo = 0;
81
+ let hi = 1;
82
+ t = x;
83
+ for (let i = 0; i < 32; i++) {
84
+ t = (lo + hi) / 2;
85
+ const xe = bezierAxis(t, x1, x2);
86
+ if (Math.abs(xe - x) < 1e-7) break;
87
+ if (xe < x) lo = t;
88
+ else hi = t;
89
+ }
90
+ }
91
+ return bezierAxis(t, y1, y2);
92
+ }
93
+
94
+ // ─────────────────────────────────────────────────────────────────────────────
95
+ // IR types
96
+ // ─────────────────────────────────────────────────────────────────────────────
97
+
98
+ /**
99
+ * A value a track can interpolate:
100
+ * • `number` — opacity, scalar scale/rotate, a single stop offset.
101
+ * • `number[]` — element-wise (gradient stop offsets/opacities, a transform
102
+ * decomposed into [tx,ty,scale,rotate], …).
103
+ * • `Point[]` — a `d` shape morph as a vertex array (the cross-renderer
104
+ * interpolable form; see {@link lerpVertices}).
105
+ * • `string` — a discrete value (a `transform` string, a color); NOT
106
+ * numerically interpolated — held until the next keyframe.
107
+ */
108
+ export type TrackValue = number | number[] | Point[] | string;
109
+
110
+ /** A single keyframe at an ABSOLUTE time `t` (seconds from the track's begin). */
111
+ export interface Keyframe<V extends TrackValue = TrackValue> {
112
+ /** Absolute time in seconds. */
113
+ t: number;
114
+ value: V;
115
+ /**
116
+ * Easing of the segment STARTING at this keyframe (→ the next one), mirroring
117
+ * SMIL `keySplines` (one spline per interval) and Lottie out/in tangents.
118
+ * Omit for `linear`. Ignored on the final keyframe.
119
+ */
120
+ ease?: CubicBezierHandles;
121
+ }
122
+
123
+ /** Animatable property names the serializers understand. Open for extension. */
124
+ export type TrackProperty =
125
+ | 'd'
126
+ | 'transform'
127
+ | 'opacity'
128
+ | 'fill'
129
+ | 'stroke'
130
+ | 'gradientStops'
131
+ | (string & {});
132
+
133
+ /** Keyframes on one property of one node. */
134
+ export interface Track<V extends TrackValue = TrackValue> {
135
+ /** The animated property (`d`, `opacity`, `transform`, …). */
136
+ property: TrackProperty;
137
+ /** `id` of the node this track drives (the serializer targets it). */
138
+ target?: string;
139
+ /** Keyframes, kept sorted ascending by `t` (constructors enforce this). */
140
+ keyframes: Keyframe<V>[];
141
+ }
142
+
143
+ /** Repeat policy. `'indefinite'` ⇒ SMIL `indefinite` / Lottie loop. */
144
+ export type Repeat = number | 'indefinite';
145
+
146
+ /** A timeline: a bundle of property tracks sharing a clock. */
147
+ export interface Timeline {
148
+ /** Total wall-clock duration in seconds (the choreography envelope). */
149
+ dur: number;
150
+ /** Start offset in seconds (default 0). */
151
+ begin?: number;
152
+ /** Repeat count or `'indefinite'`. */
153
+ repeat?: Repeat;
154
+ tracks: Track[];
155
+ /**
156
+ * If set, each track's keyframes are offset by `index · stagger` seconds at
157
+ * resolve time (see {@link resolveStagger}). A declarative shorthand for
158
+ * {@link stagger}; resolved (baked into keyframe `t`) before serialization.
159
+ */
160
+ stagger?: number;
161
+ }
162
+
163
+ // ─────────────────────────────────────────────────────────────────────────────
164
+ // Constructors
165
+ // ─────────────────────────────────────────────────────────────────────────────
166
+
167
+ /** Build a track, sorting keyframes by time (defensive — callers may be loose). */
168
+ export function track<V extends TrackValue>(
169
+ property: TrackProperty,
170
+ keyframes: Keyframe<V>[],
171
+ target?: string
172
+ ): Track<V> {
173
+ const sorted = [...keyframes].sort((a, b) => a.t - b.t);
174
+ const out: Track<V> = { property, keyframes: sorted };
175
+ if (target !== undefined) out.target = target;
176
+ return out;
177
+ }
178
+
179
+ /** Build a timeline; `dur` defaults to the latest keyframe time across tracks. */
180
+ export function timeline(o: {
181
+ tracks: Track[];
182
+ dur?: number;
183
+ begin?: number;
184
+ repeat?: Repeat;
185
+ stagger?: number;
186
+ }): Timeline {
187
+ const dur = o.dur ?? trackSpan(o.tracks);
188
+ const out: Timeline = { dur, tracks: o.tracks };
189
+ if (o.begin !== undefined) out.begin = o.begin;
190
+ if (o.repeat !== undefined) out.repeat = o.repeat;
191
+ if (o.stagger !== undefined) out.stagger = o.stagger;
192
+ return out;
193
+ }
194
+
195
+ /** The latest keyframe time across a set of tracks (0 if none). */
196
+ export function trackSpan(tracks: Track[]): number {
197
+ let max = 0;
198
+ for (const tr of tracks) {
199
+ const last = tr.keyframes[tr.keyframes.length - 1];
200
+ if (last && last.t > max) max = last.t;
201
+ }
202
+ return max;
203
+ }
204
+
205
+ // ─────────────────────────────────────────────────────────────────────────────
206
+ // Interpolation
207
+ // ─────────────────────────────────────────────────────────────────────────────
208
+
209
+ /**
210
+ * Vertex-array interpolation for `d` morphs. REQUIRES equal length — the
211
+ * cross-renderer interpolability constraint (an identical command template /
212
+ * fixed vertex count). Throws on mismatch so a bad morph fails loud at author
213
+ * time rather than producing a degenerate tween.
214
+ */
215
+ export function lerpVertices(a: Point[], b: Point[], u: number): Point[] {
216
+ if (a.length !== b.length) {
217
+ throw new Error(
218
+ `lerpVertices: vertex count mismatch (${a.length} vs ${b.length}); ` +
219
+ 'morph endpoints must share a fixed vertex count'
220
+ );
221
+ }
222
+ return a.map((p, i) => ({ x: p.x + (b[i].x - p.x) * u, y: p.y + (b[i].y - p.y) * u }));
223
+ }
224
+
225
+ /** Interpolate two same-shaped {@link TrackValue}s by `u` ∈ [0,1]. */
226
+ export function lerpValue(a: TrackValue, b: TrackValue, u: number): TrackValue {
227
+ if (typeof a === 'number' && typeof b === 'number') return a + (b - a) * u;
228
+ if (typeof a === 'string' || typeof b === 'string') return u < 1 ? a : b; // discrete hold
229
+ if (Array.isArray(a) && Array.isArray(b)) {
230
+ if (a.length !== b.length) {
231
+ throw new Error(`lerpValue: array length mismatch (${a.length} vs ${b.length})`);
232
+ }
233
+ if (a.length === 0) return [];
234
+ // Point[] (vertex array) vs number[].
235
+ if (typeof a[0] === 'object') return lerpVertices(a as Point[], b as Point[], u);
236
+ return (a as number[]).map((n, i) => n + ((b as number[])[i] - n) * u);
237
+ }
238
+ throw new Error('lerpValue: incompatible value kinds');
239
+ }
240
+
241
+ /**
242
+ * Sample a track at absolute time `tSec`. Clamps outside the keyframe span
243
+ * (hold first / hold last). Applies the per-segment easing of the leading
244
+ * keyframe, then interpolates. Returns `undefined` for an empty track.
245
+ */
246
+ export function sampleTrack(tr: Track, tSec: number): TrackValue | undefined {
247
+ const kfs = tr.keyframes;
248
+ if (kfs.length === 0) return undefined;
249
+ if (kfs.length === 1 || tSec <= kfs[0].t) return kfs[0].value;
250
+ const last = kfs[kfs.length - 1];
251
+ if (tSec >= last.t) return last.value;
252
+
253
+ let i = 0;
254
+ while (i < kfs.length - 1 && kfs[i + 1].t <= tSec) i++;
255
+ const a = kfs[i];
256
+ const b = kfs[i + 1];
257
+ const span = b.t - a.t;
258
+ const x = span > 0 ? (tSec - a.t) / span : 0;
259
+ const u = a.ease ? easeBezier(a.ease, x) : x;
260
+ return lerpValue(a.value, b.value, u);
261
+ }
262
+
263
+ // ─────────────────────────────────────────────────────────────────────────────
264
+ // Choreography combinators (pure)
265
+ // ─────────────────────────────────────────────────────────────────────────────
266
+
267
+ /** Shift every keyframe in a track by `dt` seconds (immutable). */
268
+ export function shiftTrack(tr: Track, dt: number): Track {
269
+ if (dt === 0) return tr;
270
+ const out: Track = {
271
+ property: tr.property,
272
+ keyframes: tr.keyframes.map((k) => ({ ...k, t: k.t + dt })),
273
+ };
274
+ if (tr.target !== undefined) out.target = tr.target;
275
+ return out;
276
+ }
277
+
278
+ /**
279
+ * Bake a timeline's declarative `stagger` field into its tracks (keyframe `t`
280
+ * += index·stagger), clearing the field and growing `dur` to fit. A no-op when
281
+ * `stagger` is unset/zero. Idempotent on an already-resolved timeline.
282
+ */
283
+ export function resolveStagger(tl: Timeline): Timeline {
284
+ if (!tl.stagger) return tl;
285
+ const tracks = tl.tracks.map((tr, i) => shiftTrack(tr, i * (tl.stagger as number)));
286
+ const out: Timeline = { dur: Math.max(tl.dur, trackSpan(tracks)), tracks };
287
+ if (tl.begin !== undefined) out.begin = tl.begin;
288
+ if (tl.repeat !== undefined) out.repeat = tl.repeat;
289
+ return out;
290
+ }
291
+
292
+ /**
293
+ * Stagger a list of tracks: track `i` is offset by `i · delay` seconds. The
294
+ * imperative form of {@link Timeline.stagger}. Returns the offset tracks (sort
295
+ * order preserved).
296
+ */
297
+ export function stagger(tracks: Track[], delay: number): Track[] {
298
+ return tracks.map((tr, i) => shiftTrack(tr, i * delay));
299
+ }
300
+
301
+ /**
302
+ * Compose timelines IN PARALLEL — all start together; the result's tracks are
303
+ * the union (each first resolved for its own `stagger`/`begin`). `dur` is the
304
+ * max. The shape for "these things animate at once".
305
+ */
306
+ export function parallel(...timelines: Timeline[]): Timeline {
307
+ const tracks: Track[] = [];
308
+ let dur = 0;
309
+ for (const raw of timelines) {
310
+ const tl = resolveStagger(raw);
311
+ const b = tl.begin ?? 0;
312
+ for (const tr of tl.tracks) tracks.push(shiftTrack(tr, b));
313
+ dur = Math.max(dur, b + tl.dur);
314
+ }
315
+ return { dur, tracks };
316
+ }
317
+
318
+ /**
319
+ * Compose timelines IN SERIES — each starts where the previous ended (its own
320
+ * `begin` adds extra gap). The shape for "first this, then that". Durations
321
+ * sum (plus gaps); tracks are shifted onto the shared clock.
322
+ */
323
+ export function sequence(...timelines: Timeline[]): Timeline {
324
+ const tracks: Track[] = [];
325
+ let cursor = 0;
326
+ for (const raw of timelines) {
327
+ const tl = resolveStagger(raw);
328
+ const start = cursor + (tl.begin ?? 0);
329
+ for (const tr of tl.tracks) tracks.push(shiftTrack(tr, start));
330
+ cursor = start + tl.dur;
331
+ }
332
+ return { dur: cursor, tracks };
333
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file draw/brush.ts — Phase 25.2 brush layer
3
- * @scope plugins/design/dev-server/draw/brush.ts
3
+ * @scope apps/studio/draw/brush.ts
4
4
  * @purpose "Brush" expression for a pure-vector engine, three levels:
5
5
  * L1 roughenFilter() — feTurbulence → feDisplacementMap edge
6
6
  * texture (dry-brush / ink / charcoal roughness) you apply to
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file draw/composition.ts — Phase 25.1 compositional scaffolding layer
3
- * @scope plugins/design/dev-server/draw/composition.ts
3
+ * @scope apps/studio/draw/composition.ts
4
4
  * @purpose The missing answer to "blob soup": deterministic COMPOSITION.
5
5
  * Random placement is empirically catastrophic — only ~3–12% of
6
6
  * randomly-placed multi-element layouts even avoid overlap (Shiripour