@1agh/maude 0.28.0 → 0.29.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 (361) hide show
  1. package/README.md +2 -2
  2. package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -1
  3. package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +286 -6
  4. package/{plugins/design/dev-server → apps/studio}/annotations-layer.tsx +1209 -122
  5. package/{plugins/design/dev-server → apps/studio}/api.ts +486 -32
  6. package/{plugins/design/dev-server → apps/studio}/artboard-activity-overlay.tsx +1 -1
  7. package/{plugins/design/dev-server → apps/studio}/artboard-marquee.tsx +1 -1
  8. package/apps/studio/bin/_motion-sample-playwright.mjs +129 -0
  9. package/{plugins/design/dev-server → apps/studio}/bin/canvas-edit.sh +1 -1
  10. package/{plugins/design/dev-server → apps/studio}/bin/draw-proof.sh +37 -2
  11. package/{plugins/design/dev-server → apps/studio}/bin/preflight.sh +1 -1
  12. package/apps/studio/bin/read-annotations.mjs +438 -0
  13. package/apps/studio/bin/read-annotations.sh +11 -0
  14. package/{plugins/design/dev-server → apps/studio}/bin/runtime-health.sh +1 -1
  15. package/{plugins/design/dev-server → apps/studio}/bin/scenario-report.mjs +1 -1
  16. package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
  17. package/apps/studio/bin/server-up.sh +234 -0
  18. package/apps/studio/bin/to-lottie-verify.html +68 -0
  19. package/apps/studio/bin/to-lottie.sh +128 -0
  20. package/{plugins/design/dev-server → apps/studio}/bin/visual-sanity.sh +19 -3
  21. package/{plugins/design/dev-server → apps/studio}/build.ts +5 -3
  22. package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +1 -1
  23. package/{plugins/design/dev-server → apps/studio}/canvas-comment-mount.tsx +2 -2
  24. package/apps/studio/canvas-create.ts +104 -0
  25. package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +47 -13
  26. package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +60 -1
  27. package/{plugins/design/dev-server → apps/studio}/canvas-lib-inline.ts +1 -1
  28. package/{plugins/design/dev-server → apps/studio}/canvas-lib-resolver.ts +1 -1
  29. package/{plugins/design/dev-server → apps/studio}/canvas-lib.tsx +41 -33
  30. package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +57 -42
  31. package/{plugins/design/dev-server → apps/studio}/client/app.jsx +1713 -530
  32. package/apps/studio/client/canvas-url.js +81 -0
  33. package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
  34. package/{plugins/design/dev-server → apps/studio}/client/index.html +2 -1
  35. package/apps/studio/client/styles/1-tokens-maude.css +296 -0
  36. package/apps/studio/client/styles/3-shell-maude.css +459 -0
  37. package/{plugins/design/dev-server → apps/studio}/client/styles/3-shell.css +129 -0
  38. package/apps/studio/client/styles/4-components-maude.css +74 -0
  39. package/{plugins/design/dev-server → apps/studio}/client/styles/4-components.css +357 -34
  40. package/apps/studio/client/styles/5-maude-overrides.css +123 -0
  41. package/{plugins/design/dev-server → apps/studio}/client/styles/_index.css +9 -0
  42. package/{plugins/design/dev-server → apps/studio}/client/styles.css +1 -1
  43. package/apps/studio/client/tour/overlay.jsx +191 -0
  44. package/apps/studio/client/tour/usage-tour.js +35 -0
  45. package/apps/studio/client/whats-new-seen.js +52 -0
  46. package/apps/studio/client/whats-new.jsx +250 -0
  47. package/{plugins/design/dev-server → apps/studio}/collab/index.ts +4 -4
  48. package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +1 -1
  49. package/{plugins/design/dev-server → apps/studio}/commands/annotation-strokes-command.ts +1 -1
  50. package/{plugins/design/dev-server → apps/studio}/commands/equal-spacing-command.ts +1 -1
  51. package/{plugins/design/dev-server → apps/studio}/commands/move-artboards-command.ts +1 -1
  52. package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +25 -6
  53. package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +2 -2
  54. package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +4 -7
  55. package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +1 -1
  56. package/apps/studio/dist/client.bundle.js +22825 -0
  57. package/apps/studio/dist/comment-mount.js +1960 -0
  58. package/apps/studio/dist/runtime/lib0_decoding.js +1 -0
  59. package/apps/studio/dist/runtime/lib0_encoding.js +1 -0
  60. package/apps/studio/dist/runtime/motion.js +4 -0
  61. package/apps/studio/dist/runtime/motion_react.js +12 -0
  62. package/apps/studio/dist/runtime/pixi-js.js +3397 -0
  63. package/apps/studio/dist/runtime/react-dom.js +1 -0
  64. package/apps/studio/dist/runtime/react-dom_client.js +9 -0
  65. package/apps/studio/dist/runtime/react.js +1 -0
  66. package/apps/studio/dist/runtime/react_jsx-dev-runtime.js +1 -0
  67. package/apps/studio/dist/runtime/react_jsx-runtime.js +1 -0
  68. package/apps/studio/dist/runtime/y-protocols_awareness.js +1 -0
  69. package/apps/studio/dist/runtime/y-protocols_sync.js +1 -0
  70. package/apps/studio/dist/runtime/yjs.js +5 -0
  71. package/apps/studio/dist/styles.css +5695 -0
  72. package/{plugins/design/dev-server → apps/studio}/dom-selection.ts +75 -4
  73. package/apps/studio/draw/animate.ts +333 -0
  74. package/{plugins/design/dev-server → apps/studio}/draw/brush.ts +4 -4
  75. package/{plugins/design/dev-server → apps/studio}/draw/composition.ts +1 -1
  76. package/{plugins/design/dev-server → apps/studio}/draw/geometry.ts +1 -1
  77. package/{plugins/design/dev-server → apps/studio}/draw/index.ts +9 -6
  78. package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +2 -2
  79. package/apps/studio/draw/morph.ts +204 -0
  80. package/{plugins/design/dev-server → apps/studio}/draw/optimize.ts +1 -1
  81. package/{plugins/design/dev-server → apps/studio}/draw/palette.ts +1 -1
  82. package/{plugins/design/dev-server → apps/studio}/draw/primitives.ts +1 -1
  83. package/apps/studio/draw/serialize-animate.ts +324 -0
  84. package/{plugins/design/dev-server → apps/studio}/draw/serialize.ts +9 -3
  85. package/apps/studio/draw/test/animate.test.ts +172 -0
  86. package/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +1 -1
  87. package/apps/studio/draw/test/morph.test.ts +107 -0
  88. package/apps/studio/draw/test/serialize-animate.test.ts +142 -0
  89. package/{plugins/design/dev-server → apps/studio}/draw/test/serialize.test.ts +1 -1
  90. package/{plugins/design/dev-server → apps/studio}/equal-spacing-detector.ts +1 -1
  91. package/{plugins/design/dev-server → apps/studio}/equal-spacing-handles.tsx +7 -29
  92. package/{plugins/design/dev-server → apps/studio}/examples/perf-100-artboards.tsx +1 -2
  93. package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +14 -2
  94. package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +2 -2
  95. package/{plugins/design/dev-server → apps/studio}/http.ts +103 -2
  96. package/{plugins/design/dev-server → apps/studio}/input-router.tsx +8 -1
  97. package/{plugins/design/dev-server → apps/studio}/inspect.ts +20 -3
  98. package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +6 -3
  99. package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +1 -1
  100. package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
  101. package/{plugins/design/dev-server → apps/studio}/server.ts +19 -1
  102. package/{plugins/design/dev-server → apps/studio}/sync/index.ts +6 -6
  103. package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +2 -2
  104. package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +1 -1
  105. package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +1 -2
  106. package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +69 -6
  107. package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +98 -1
  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/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +1 -1
  111. package/apps/studio/test/canvas-create-api.test.ts +361 -0
  112. package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +3 -1
  113. package/{plugins/design/dev-server → apps/studio}/test/canvas-edit.test.ts +1 -1
  114. package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +1 -1
  115. package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +1 -1
  116. package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +2 -3
  117. package/apps/studio/test/canvas-media-drop.test.ts +158 -0
  118. package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +1 -2
  119. package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +18 -3
  120. package/apps/studio/test/canvas-url.test.ts +99 -0
  121. package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +1 -1
  122. package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +1 -1
  123. package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +1 -1
  124. package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +1 -1
  125. package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +1 -2
  126. package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +1 -2
  127. package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +1 -2
  128. package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +1 -2
  129. package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +1 -1
  130. package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +1 -2
  131. package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +1 -2
  132. package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +1 -2
  133. package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +1 -1
  134. package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +1 -1
  135. package/apps/studio/test/index-data-canvas-ds.test.ts +97 -0
  136. package/apps/studio/test/motion-sample.test.ts +35 -0
  137. package/{plugins/design/dev-server → apps/studio}/test/perf-harness.ts +1 -1
  138. package/{plugins/design/dev-server → apps/studio}/test/phase-3.6-smoke.test.ts +3 -1
  139. package/apps/studio/test/read-annotations.test.ts +409 -0
  140. package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle.test.ts +1 -1
  141. package/{plugins/design/dev-server → apps/studio}/test/sanitize-annotation-svg.test.ts +108 -2
  142. package/{plugins/design/dev-server → apps/studio}/test/shared-doc-convergence.test.ts +2 -2
  143. package/{plugins/design/dev-server → apps/studio}/test/shared-doc-migrate.test.ts +2 -3
  144. package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +1 -1
  145. package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +1 -1
  146. package/{plugins/design/dev-server → apps/studio}/test/sync-agent.test.ts +2 -3
  147. package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +2 -3
  148. package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +1 -1
  149. package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +1 -1
  150. package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +1 -2
  151. package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +4 -5
  152. package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +1 -2
  153. package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +3 -8
  154. package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +1 -1
  155. package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +1 -2
  156. package/apps/studio/test/tour-overlay.test.tsx +56 -0
  157. package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +4 -1
  158. package/{plugins/design/dev-server → apps/studio}/test/use-annotation-resize.test.ts +43 -0
  159. package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +1 -1
  160. package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +4 -1
  161. package/apps/studio/test/whats-new.test.ts +101 -0
  162. package/apps/studio/text-imports.d.ts +9 -0
  163. package/{plugins/design/dev-server → apps/studio}/tool-palette.tsx +4 -69
  164. package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -1
  165. package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
  166. package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +2 -2
  167. package/{plugins/design/dev-server → apps/studio}/use-annotation-resize.tsx +41 -12
  168. package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +3 -7
  169. package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +2 -2
  170. package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +2 -2
  171. package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +2 -2
  172. package/apps/studio/use-canvas-media-drop.tsx +343 -0
  173. package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +4 -5
  174. package/{plugins/design/dev-server → apps/studio}/use-cursor-modifiers.tsx +1 -1
  175. package/{plugins/design/dev-server → apps/studio}/use-keyboard-discipline.tsx +5 -2
  176. package/{plugins/design/dev-server → apps/studio}/use-selection-set.tsx +10 -2
  177. package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
  178. package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +4 -2
  179. package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +5 -6
  180. package/apps/studio/whats-new.json +139 -0
  181. package/apps/studio/whats-new.schema.json +70 -0
  182. package/apps/studio/whats-new.ts +87 -0
  183. package/cli/bin/maude.mjs +10 -0
  184. package/cli/commands/design.mjs +84 -10
  185. package/cli/commands/design.test.mjs +133 -1
  186. package/cli/commands/help.mjs +8 -2
  187. package/cli/commands/hub.mjs +3 -3
  188. package/cli/commands/hub.test.mjs +1 -1
  189. package/cli/commands/scenario-report.mjs +2 -9
  190. package/cli/lib/cache.mjs +1 -1
  191. package/cli/lib/config-lint.mjs +1 -1
  192. package/cli/lib/copy-tree.mjs +1 -1
  193. package/cli/lib/design-link.mjs +1 -1
  194. package/cli/lib/flow-design-integration.test.mjs +2 -2
  195. package/cli/lib/gitignore-block.mjs +1 -0
  196. package/cli/lib/gitignore-block.test.mjs +2 -2
  197. package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
  198. package/cli/lib/preflight.mjs +1 -1
  199. package/cli/lib/stack-detect.mjs +1 -1
  200. package/package.json +16 -16
  201. package/plugins/design/templates/_shell.html +3 -3
  202. package/plugins/design/templates/brief-board.tsx.template +61 -0
  203. package/plugins/design/templates/canvas.tsx.template +1 -1
  204. package/plugins/design/templates/design-system-inspiration/SUB-AGENT-PROMPTS.md +18 -3
  205. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -1
  206. package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +1 -1
  207. package/plugins/flow/.claude-plugin/config.schema.json +16 -0
  208. package/plugins/design/dev-server/bin/server-up.sh +0 -135
  209. package/plugins/design/dev-server/dist/client.bundle.js +0 -20692
  210. package/plugins/design/dev-server/dist/comment-mount.js +0 -1924
  211. package/plugins/design/dev-server/dist/runtime/lib0_decoding.js +0 -620
  212. package/plugins/design/dev-server/dist/runtime/lib0_encoding.js +0 -604
  213. package/plugins/design/dev-server/dist/runtime/motion.js +0 -4944
  214. package/plugins/design/dev-server/dist/runtime/motion_react.js +0 -10056
  215. package/plugins/design/dev-server/dist/runtime/pixi-js.js +0 -53104
  216. package/plugins/design/dev-server/dist/runtime/react-dom.js +0 -231
  217. package/plugins/design/dev-server/dist/runtime/react-dom_client.js +0 -10357
  218. package/plugins/design/dev-server/dist/runtime/react.js +0 -535
  219. package/plugins/design/dev-server/dist/runtime/react_jsx-dev-runtime.js +0 -60
  220. package/plugins/design/dev-server/dist/runtime/react_jsx-runtime.js +0 -85
  221. package/plugins/design/dev-server/dist/runtime/y-protocols_awareness.js +0 -376
  222. package/plugins/design/dev-server/dist/runtime/y-protocols_sync.js +0 -100
  223. package/plugins/design/dev-server/dist/runtime/yjs.js +0 -6687
  224. package/plugins/design/dev-server/dist/styles.css +0 -2710
  225. package/{plugins/design/dev-server → apps/studio}/activity.ts +0 -0
  226. package/{plugins/design/dev-server → apps/studio}/bin/_enumerate-artboards-playwright.mjs +0 -0
  227. package/{plugins/design/dev-server → apps/studio}/bin/_html-playwright.mjs +0 -0
  228. package/{plugins/design/dev-server → apps/studio}/bin/_pdf-playwright.mjs +0 -0
  229. package/{plugins/design/dev-server → apps/studio}/bin/_png-playwright.mjs +0 -0
  230. package/{plugins/design/dev-server → apps/studio}/bin/_pptx-playwright.mjs +0 -0
  231. package/{plugins/design/dev-server → apps/studio}/bin/_pw-launch.mjs +0 -0
  232. package/{plugins/design/dev-server → apps/studio}/bin/_screenshot-playwright.mjs +0 -0
  233. package/{plugins/design/dev-server → apps/studio}/bin/_svg-optimize.mjs +0 -0
  234. package/{plugins/design/dev-server → apps/studio}/bin/_svg-playwright.mjs +0 -0
  235. package/{plugins/design/dev-server → apps/studio}/bin/asset-sweep.sh +0 -0
  236. package/{plugins/design/dev-server → apps/studio}/bin/bootstrap-check.sh +0 -0
  237. package/{plugins/design/dev-server → apps/studio}/bin/check-runtime-bundles.sh +0 -0
  238. package/{plugins/design/dev-server → apps/studio}/bin/draw-build.sh +0 -0
  239. package/{plugins/design/dev-server → apps/studio}/bin/handoff.sh +0 -0
  240. package/{plugins/design/dev-server → apps/studio}/bin/prep.sh +0 -0
  241. package/{plugins/design/dev-server → apps/studio}/bin/slug.sh +0 -0
  242. package/{plugins/design/dev-server → apps/studio}/bin/smoke.sh +0 -0
  243. package/{plugins/design/dev-server → apps/studio}/bin/svg-optimize.sh +0 -0
  244. package/{plugins/design/dev-server → apps/studio}/boot-self-heal.ts +0 -0
  245. package/{plugins/design/dev-server → apps/studio}/canvas-build.ts +0 -0
  246. package/{plugins/design/dev-server → apps/studio}/canvas-edit.ts +0 -0
  247. package/{plugins/design/dev-server → apps/studio}/canvas-header.ts +0 -0
  248. package/{plugins/design/dev-server → apps/studio}/canvas-meta.schema.json +0 -0
  249. package/{plugins/design/dev-server → apps/studio}/canvas-pipeline.ts +0 -0
  250. package/{plugins/design/dev-server → apps/studio}/client/hmr.mjs +0 -0
  251. package/{plugins/design/dev-server → apps/studio}/client/iframe-lazy.mjs +0 -0
  252. package/{plugins/design/dev-server → apps/studio}/client/styles/0-reset.css +0 -0
  253. package/{plugins/design/dev-server → apps/studio}/client/styles/1-tokens.css +0 -0
  254. package/{plugins/design/dev-server → apps/studio}/client/styles/2-layout.css +0 -0
  255. package/{plugins/design/dev-server → apps/studio}/client/styles/5-utilities.css +0 -0
  256. package/{plugins/design/dev-server → apps/studio}/collab/ai-activity.ts +0 -0
  257. package/{plugins/design/dev-server → apps/studio}/collab/awareness-bridge.ts +1 -1
  258. package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.ts +0 -0
  259. package/{plugins/design/dev-server → apps/studio}/collab/protocol.ts +0 -0
  260. package/{plugins/design/dev-server → apps/studio}/collab/registry.ts +0 -0
  261. package/{plugins/design/dev-server → apps/studio}/collab/room.ts +0 -0
  262. package/{plugins/design/dev-server → apps/studio}/config.schema.json +0 -0
  263. package/{plugins/design/dev-server → apps/studio}/context.ts +0 -0
  264. package/{plugins/design/dev-server → apps/studio}/dist/runtime/.min-sizes.json +0 -0
  265. package/{plugins/design/dev-server → apps/studio}/draw/test/brush.test.ts +0 -0
  266. package/{plugins/design/dev-server → apps/studio}/draw/test/composition.test.ts +0 -0
  267. package/{plugins/design/dev-server → apps/studio}/draw/test/geometry.test.ts +1 -1
  268. package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.test.ts +0 -0
  269. package/{plugins/design/dev-server → apps/studio}/draw/test/optimize.test.ts +0 -0
  270. package/{plugins/design/dev-server → apps/studio}/draw/test/palette.test.ts +0 -0
  271. package/{plugins/design/dev-server → apps/studio}/draw/test/primitives.test.ts +1 -1
  272. package/{plugins/design/dev-server → apps/studio}/examples/README.md +0 -0
  273. package/{plugins/design/dev-server → apps/studio}/exporters/_browser-bundles.ts +0 -0
  274. package/{plugins/design/dev-server → apps/studio}/exporters/canva-handoff-prompt.ts +0 -0
  275. package/{plugins/design/dev-server → apps/studio}/exporters/canva.ts +0 -0
  276. package/{plugins/design/dev-server → apps/studio}/exporters/html.ts +1 -1
  277. package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +1 -1
  278. package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +1 -1
  279. package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +1 -1
  280. package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
  281. package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +1 -1
  282. package/{plugins/design/dev-server → apps/studio}/exporters/zip.ts +0 -0
  283. package/{plugins/design/dev-server → apps/studio}/fs-watch.ts +0 -0
  284. package/{plugins/design/dev-server → apps/studio}/handoff.ts +0 -0
  285. package/{plugins/design/dev-server → apps/studio}/history.ts +0 -0
  286. package/{plugins/design/dev-server → apps/studio}/hmr-broadcast.ts +0 -0
  287. package/{plugins/design/dev-server → apps/studio}/locator.ts +0 -0
  288. package/{plugins/design/dev-server → apps/studio}/mem.ts +0 -0
  289. package/{plugins/design/dev-server → apps/studio}/runtime-bundle.ts +0 -0
  290. package/{plugins/design/dev-server → apps/studio}/server.mjs +0 -0
  291. package/{plugins/design/dev-server → apps/studio}/sync/agent.ts +1 -1
  292. package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
  293. package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +0 -0
  294. package/{plugins/design/dev-server → apps/studio}/sync/connection-state.ts +0 -0
  295. package/{plugins/design/dev-server → apps/studio}/sync/echo-guard.ts +0 -0
  296. package/{plugins/design/dev-server → apps/studio}/sync/fs-mirror.ts +0 -0
  297. package/{plugins/design/dev-server → apps/studio}/sync/hubs-config.ts +0 -0
  298. package/{plugins/design/dev-server → apps/studio}/sync/migrate-seed.ts +1 -1
  299. package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
  300. package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +3 -3
  301. package/{plugins/design/dev-server → apps/studio}/sync/status.ts +0 -0
  302. package/{plugins/design/dev-server → apps/studio}/sync/untrusted.ts +0 -0
  303. package/{plugins/design/dev-server → apps/studio}/test/_helpers.ts +0 -0
  304. package/{plugins/design/dev-server → apps/studio}/test/active-state.test.ts +0 -0
  305. package/{plugins/design/dev-server → apps/studio}/test/activity.test.ts +0 -0
  306. package/{plugins/design/dev-server → apps/studio}/test/annotation-strokes-command.test.ts +0 -0
  307. package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +2 -2
  308. package/{plugins/design/dev-server → apps/studio}/test/artboard-activity-overlay.test.tsx +0 -0
  309. package/{plugins/design/dev-server → apps/studio}/test/binary-smoke.test.ts +0 -0
  310. package/{plugins/design/dev-server → apps/studio}/test/bundle-smoke.test.ts +0 -0
  311. package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
  312. package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +1 -1
  313. package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.test.ts +0 -0
  314. package/{plugins/design/dev-server → apps/studio}/test/canvas-pipeline.test.ts +0 -0
  315. package/{plugins/design/dev-server → apps/studio}/test/canvas-route.test.ts +0 -0
  316. package/{plugins/design/dev-server → apps/studio}/test/collab-awareness-bridge.test.ts +0 -0
  317. package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
  318. package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +2 -2
  319. package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.test.ts +0 -0
  320. package/{plugins/design/dev-server → apps/studio}/test/comment-mount.test.ts +0 -0
  321. package/{plugins/design/dev-server → apps/studio}/test/context-resolve-tokens.test.ts +0 -0
  322. package/{plugins/design/dev-server → apps/studio}/test/csp-canvas-shell.test.ts +0 -0
  323. package/{plugins/design/dev-server → apps/studio}/test/equal-spacing-detector.test.ts +0 -0
  324. package/{plugins/design/dev-server → apps/studio}/test/exporters/endpoint.test.ts +0 -0
  325. package/{plugins/design/dev-server → apps/studio}/test/exporters/html.test.ts +0 -0
  326. package/{plugins/design/dev-server → apps/studio}/test/exporters/pdf.test.ts +0 -0
  327. package/{plugins/design/dev-server → apps/studio}/test/exporters/png.test.ts +0 -0
  328. package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx-deck.test.ts +0 -0
  329. package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx.test.ts +0 -0
  330. package/{plugins/design/dev-server → apps/studio}/test/exporters/svg.test.ts +0 -0
  331. package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-20-annotations.svg +0 -0
  332. package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-21-annotations.svg +0 -0
  333. package/{plugins/design/dev-server → apps/studio}/test/fs-watch.test.ts +0 -0
  334. package/{plugins/design/dev-server → apps/studio}/test/handoff.test.ts +1 -1
  335. package/{plugins/design/dev-server → apps/studio}/test/history-rollback.test.ts +0 -0
  336. package/{plugins/design/dev-server → apps/studio}/test/hmr-classify.test.ts +0 -0
  337. package/{plugins/design/dev-server → apps/studio}/test/input-router.test.ts +2 -2
  338. package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +1 -1
  339. package/{plugins/design/dev-server → apps/studio}/test/marquee-overlay.test.ts +0 -0
  340. package/{plugins/design/dev-server → apps/studio}/test/move-artboards-command.test.ts +0 -0
  341. package/{plugins/design/dev-server → apps/studio}/test/participants-chrome.test.ts +0 -0
  342. package/{plugins/design/dev-server → apps/studio}/test/paths.test.ts +0 -0
  343. package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle-error-mapping.test.ts +0 -0
  344. package/{plugins/design/dev-server → apps/studio}/test/server-lifecycle.test.ts +0 -0
  345. package/{plugins/design/dev-server → apps/studio}/test/shared-doc-foundation.test.ts +0 -0
  346. package/{plugins/design/dev-server → apps/studio}/test/sync-codec.test.ts +1 -1
  347. package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +1 -1
  348. package/{plugins/design/dev-server → apps/studio}/test/system-endpoint.test.ts +0 -0
  349. package/{plugins/design/dev-server → apps/studio}/test/tool-palette.test.tsx +0 -0
  350. package/{plugins/design/dev-server → apps/studio}/test/undo-stack.test.ts +2 -2
  351. package/{plugins/design/dev-server → apps/studio}/test/use-annotation-selection.test.tsx +0 -0
  352. package/{plugins/design/dev-server → apps/studio}/test/use-artboard-drag.test.ts +3 -3
  353. package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +2 -2
  354. package/{plugins/design/dev-server → apps/studio}/test/use-collab.test.ts +0 -0
  355. package/{plugins/design/dev-server → apps/studio}/test/use-cursor-modifiers.test.ts +0 -0
  356. package/{plugins/design/dev-server → apps/studio}/test/use-keyboard-discipline.test.ts +0 -0
  357. package/{plugins/design/dev-server → apps/studio}/test/use-selection-set.test.tsx +1 -1
  358. package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +2 -2
  359. /package/{plugins/design/dev-server → apps/studio}/test/ws-handshake.test.ts +0 -0
  360. /package/{plugins/design/dev-server → apps/studio}/tsconfig.json +0 -0
  361. /package/{plugins/design/dev-server → apps/studio}/ws.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file dom-selection.ts — selection-from-DOM helpers (leaf module)
3
- * @scope plugins/design/dev-server/dom-selection.ts
3
+ * @scope apps/studio/dom-selection.ts
4
4
  * @purpose Pure DOM → Selection builders shared by the canvas chrome
5
5
  * (canvas-shell.tsx) and the shell-owned comment mount layer
6
6
  * (canvas-comment-mount.tsx). Lives in its own leaf module — no
@@ -114,6 +114,67 @@ export function cssEscape(s: string): string {
114
114
  * defaults to `deriveFile()`; the comment mount layer passes it explicitly
115
115
  * so all three consumers (router, overlay, mount) agree on the same key.
116
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
+
117
178
  export function hoverTargetToSelection(target: HoverTarget, file?: string): Selection {
118
179
  const el = target.el;
119
180
  const rect =
@@ -125,20 +186,30 @@ export function hoverTargetToSelection(target: HoverTarget, file?: string): Sele
125
186
  // anchor exists.
126
187
  const cdId = target.cdId;
127
188
  // Selector resolution order:
128
- // 1. data-cd-id anchor — stable pipeline-stamped id (preferred).
189
+ // 1. data-cd-id anchor — stable pipeline-stamped id (preferred). SCOPED by
190
+ // the hit's artboard (`[data-dc-screen=…] [data-cd-id=…]`) — a component
191
+ // shared across artboards carries the SAME data-cd-id in each, so an
192
+ // unscoped `[data-cd-id]` selector resolves (via querySelector) to the
193
+ // FIRST artboard's instance and the pin/select lands on the wrong board.
194
+ // Prefixing the artboard makes the anchor per-instance.
129
195
  // 2. data-dc-screen — chrome click promoted to whole-artboard select
130
196
  // (T24.5 G8 multi-artboard gesture).
131
197
  // 3. cssPath of the hit — last-resort path string.
132
198
  const selector = cdId
133
- ? `[data-cd-id="${cdId}"]`
134
- : !cdId && target.artboardId
199
+ ? scopedCdSelector(cdId, target.artboardId)
200
+ : target.artboardId
135
201
  ? `[data-dc-screen="${target.artboardId}"]`
136
202
  : cssPath(el);
203
+ // Disambiguate repeated instances within the same artboard (list rows, a
204
+ // reusable used twice) — the index is which `querySelectorAll(selector)`
205
+ // match this element is. cssPath is already unique, so 0 there.
206
+ const index = cdId && typeof document !== 'undefined' ? selectorIndex(document, selector, el) : 0;
137
207
  return {
138
208
  file: file ?? deriveFile(),
139
209
  id: cdId ?? undefined,
140
210
  selector,
141
211
  artboardId: target.artboardId,
212
+ index,
142
213
  tag: el?.tagName.toLowerCase() ?? '',
143
214
  classes: realClasses(el),
144
215
  text: shortText(el, 240),
@@ -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
@@ -23,15 +23,15 @@
23
23
 
24
24
  import type { Rect } from './geometry.ts';
25
25
  import {
26
- path,
26
+ circle,
27
27
  type DrawPrimitive,
28
28
  type DrawStyle,
29
- type Point,
30
- circle,
31
29
  fe,
32
30
  filter,
33
31
  group,
34
32
  line,
33
+ type Point,
34
+ path,
35
35
  } from './primitives.ts';
36
36
 
37
37
  function fmt(n: number): string {
@@ -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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file draw/geometry.ts — Phase 25 geometry-engine math layer
3
- * @scope plugins/design/dev-server/draw/geometry.ts
3
+ * @scope apps/studio/draw/geometry.ts
4
4
  * @purpose Pure, deterministic geometry the LLM must NOT free-hand:
5
5
  * • PCHIP monotone-cubic interpolation (overshoot-free curves —
6
6
  * the whole point vs LLM-guessed Béziers, which wobble);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file draw/index.ts — Phase 25 geometry-engine public surface
3
- * @scope plugins/design/dev-server/draw/index.ts
3
+ * @scope apps/studio/draw/index.ts
4
4
  * @purpose Single import point for the draw engine. The draw-agent and the
5
5
  * `draw-proof` / `svg-optimize` bin helpers import from here:
6
6
  *
@@ -18,11 +18,14 @@
18
18
  * React-free (DDR-067) — nothing here imports react / a `.tsx`.
19
19
  */
20
20
 
21
- export * from './primitives.ts';
22
- export * from './geometry.ts';
23
- export * from './palette.ts';
24
- export * from './composition.ts';
21
+ export * from './animate.ts';
25
22
  export * from './brush.ts';
23
+ export * from './composition.ts';
24
+ export * from './geometry.ts';
26
25
  export * from './layout.ts';
27
- export * from './serialize.ts';
26
+ export * from './morph.ts';
28
27
  export * from './optimize.ts';
28
+ export * from './palette.ts';
29
+ export * from './primitives.ts';
30
+ export * from './serialize.ts';
31
+ export * from './serialize-animate.ts';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file draw/layout.ts — Phase 25 geometry-engine layout / diagram layer
3
- * @scope plugins/design/dev-server/draw/layout.ts
3
+ * @scope apps/studio/draw/layout.ts
4
4
  * @purpose Composition math:
5
5
  * • a deterministic constraint-based label placer (pick the
6
6
  * candidate slot around each anchor that minimizes overlap —
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import { type Rect, routeConnector } from './geometry.ts';
18
- import { type DrawPrimitive, type Point, group, polyline, rect, snap, text } from './primitives.ts';
18
+ import { type DrawPrimitive, group, type Point, polyline, rect, snap, text } from './primitives.ts';
19
19
 
20
20
  // ─────────────────────────────────────────────────────────────────────────────
21
21
  // Grid + modular scale