@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,11 @@
1
+ #!/usr/bin/env bash
2
+ # read-annotations.sh — thin wrapper around read-annotations.mjs (the headless
3
+ # SVG → JSON annotation reader). Reached via `maude design read-annotations`
4
+ # (DDR-062), never a raw bin path. The reader is zero-dep Node, so we exec under
5
+ # `node` (always present — maude itself runs on Node), not Bun.
6
+ #
7
+ # Usage:
8
+ # read-annotations.sh <rel-path> [--root <repo>] [--canvas-state <path>]
9
+ # (see read-annotations.mjs --help for the full contract)
10
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
11
+ exec node "$SCRIPT_DIR/read-annotations.mjs" "$@"
@@ -61,7 +61,7 @@ if [ -z "$REPO" ]; then
61
61
  REPO="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
62
62
  fi
63
63
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
64
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
64
+ PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
65
65
  PREBUILT_DIR="$PLUGIN_ROOT/dev-server/dist/runtime"
66
66
  if [ ! -d "$PREBUILT_DIR" ]; then
67
67
  PREBUILT_DIR="$SCRIPT_DIR/../dist/runtime"
@@ -213,6 +213,55 @@ capture() {
213
213
  fi
214
214
  }
215
215
 
216
+ # ---------- port-bounce recovery (P2.1 / DDR-094) ----------
217
+ # The dev server can respawn on a DIFFERENT port (the prior port got taken, a
218
+ # Tailscale/mDNS hiccup bounced it, a stale _server.json was cleared). A capture
219
+ # launched against the OLD port then fails with ERR_CONNECTION_REFUSED. The
220
+ # running server keeps _server.json current, so on any capture failure we re-read
221
+ # the LIVE port and, if it changed, rewrite $URL and retry once.
222
+ RELIVE_REPO="${ROOT:-${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
223
+ RELIVE_STATE="$RELIVE_REPO/.design/_server.json"
224
+ RELIVED=0
225
+
226
+ read_state_port() {
227
+ [ -f "$RELIVE_STATE" ] || return 1
228
+ if command -v jq >/dev/null 2>&1; then
229
+ jq -r .port "$RELIVE_STATE" 2>/dev/null
230
+ else
231
+ sed -nE 's/.*"port"[[:space:]]*:[[:space:]]*([0-9]+).*/\1/p' "$RELIVE_STATE" 2>/dev/null | head -n1
232
+ fi
233
+ }
234
+
235
+ # Re-read the live port; if it differs from the one in $URL, rewrite $URL and
236
+ # return 0 (a retry is warranted). Returns 1 when nothing changed (don't retry).
237
+ # Only fires ONCE per run (RELIVED guard) so a genuinely-down server still fails.
238
+ relive_url() {
239
+ [ "$RELIVED" -eq 0 ] || return 1
240
+ local cur new
241
+ cur=$(printf '%s' "$URL" | sed -nE 's#https?://[^:/]+:([0-9]+).*#\1#p')
242
+ new=$(read_state_port)
243
+ [ -n "$new" ] || return 1
244
+ if [ -n "$cur" ] && [ "$cur" != "$new" ]; then
245
+ URL=$(printf '%s' "$URL" | sed -E "s#(://[^:/]+:)[0-9]+#\1${new}#")
246
+ RELIVED=1
247
+ echo "→ dev-server bounced ports ($cur → $new); re-reading _server.json and retrying at $URL" >&2
248
+ return 0
249
+ fi
250
+ return 1
251
+ }
252
+
253
+ # capture(), but on failure re-read the live port and retry once if it bounced.
254
+ capture_resilient() {
255
+ local css="$1"
256
+ local out="$2"
257
+ capture "$css" "$out" && return 0
258
+ if relive_url; then
259
+ navigate_once
260
+ capture "$css" "$out" && return 0
261
+ fi
262
+ return 1
263
+ }
264
+
216
265
  # ---------- --all-screens loop ----------
217
266
  if [ $ALL_SCREENS -eq 1 ]; then
218
267
  mkdir -p "$OUT_DIR"
@@ -227,6 +276,17 @@ if [ $ALL_SCREENS -eq 1 ]; then
227
276
  # Strip surrounding quotes, then split on comma.
228
277
  IDS=$(printf '%s' "$raw" | sed 's/^"//; s/"$//' | tr ',' '\n')
229
278
  fi
279
+ # No screens enumerated may mean the page never loaded (port bounce) — try a
280
+ # live-port re-read + one re-navigate before giving up.
281
+ if [ -z "$IDS" ] && relive_url; then
282
+ navigate_once
283
+ if [ "$ENGINE" = "agent-browser" ]; then
284
+ raw=$(agent-browser eval \
285
+ "Array.from(document.querySelectorAll('[data-dc-screen],[data-dc-slot]')).map(e => e.getAttribute('data-dc-screen') || e.getAttribute('data-dc-slot')).filter(Boolean).join(',')" \
286
+ 2>/dev/null)
287
+ IDS=$(printf '%s' "$raw" | sed 's/^"//; s/"$//' | tr ',' '\n')
288
+ fi
289
+ fi
230
290
  if [ -z "$IDS" ]; then
231
291
  echo "✗ no [data-dc-screen]/[data-dc-slot] elements found (or eval unavailable on this engine)" >&2
232
292
  exit 3
@@ -241,7 +301,7 @@ if [ $ALL_SCREENS -eq 1 ]; then
241
301
  agent-browser eval "document.querySelector('[data-dc-screen=\"$ID\"], [data-dc-slot=\"$ID\"]').scrollIntoView({block:'center'})" >/dev/null 2>&1
242
302
  sleep 0.6
243
303
  fi
244
- if capture "[data-dc-screen=\"$ID\"], [data-dc-slot=\"$ID\"]" "$OUT_FILE"; then
304
+ if capture_resilient "[data-dc-screen=\"$ID\"], [data-dc-slot=\"$ID\"]" "$OUT_FILE"; then
245
305
  echo "$OUT_FILE"
246
306
  else
247
307
  echo "✗ failed: $ID" >&2
@@ -254,7 +314,7 @@ fi
254
314
 
255
315
  # ---------- single-shot ----------
256
316
  navigate_once
257
- if capture "$CSS_SEL" "$OUT"; then
317
+ if capture_resilient "$CSS_SEL" "$OUT"; then
258
318
  echo "$OUT"
259
319
  exit 0
260
320
  fi
@@ -55,7 +55,12 @@ fi
55
55
  # `bash server-up.sh` falls back to the postinstall side-channel.
56
56
  # bun + server.ts → dev tree / fallback (DDR-020 made bun authoritative for source).
57
57
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
58
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
58
+ # PLUGIN_ROOT is a 2-levels-under-pkgRoot anchor: plugins/design when `maude
59
+ # design <verb>` sets CLAUDE_PLUGIN_ROOT, else apps/studio ($SCRIPT_DIR/.. — the
60
+ # studio root, also 2-deep) for a direct `bash server-up.sh`. Both keep
61
+ # `$PLUGIN_ROOT/../../cli` = <pkgRoot>/cli. The dev-server tree itself is always
62
+ # $SCRIPT_DIR/.. (apps/studio) — DDR-095 moved it out of plugins/design.
63
+ PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
59
64
  SERVER_TS="$PLUGIN_ROOT/dev-server/server.ts"
60
65
  SERVER_MJS="$PLUGIN_ROOT/dev-server/server.mjs"
61
66
  if [ ! -f "$SERVER_TS" ]; then SERVER_TS="$SCRIPT_DIR/../server.ts"; fi
@@ -66,7 +71,8 @@ if [ ! -f "$SERVER_MJS" ]; then SERVER_MJS="$SCRIPT_DIR/../server.mjs"; fi
66
71
  # `bash server-up.sh` on a production install). MAUDE_FORCE_SOURCE=1 forces source.
67
72
  SERVER_BIN="${MAUDE_DEV_SERVER_BIN:-}"
68
73
  if [ -z "$SERVER_BIN" ] && [ "${MAUDE_FORCE_SOURCE:-0}" != "1" ]; then
69
- # PLUGIN_ROOT=<pkgRoot>/plugins/design, so the side-channel is ../../cli.
74
+ # PLUGIN_ROOT is 2-deep under pkgRoot (plugins/design or apps/studio), so the
75
+ # side-channel is ../../cli either way.
70
76
  # `head -n1` (parity with the Node readers' `.trim()`) tolerates a trailing newline.
71
77
  SC="$PLUGIN_ROOT/../../cli/.platform-binary-path"
72
78
  if [ -f "$SC" ]; then
@@ -154,7 +160,7 @@ fi
154
160
  # Step 1.5 — dependency preflight (source `bun server.ts` path ONLY; runs on cold
155
161
  # start). The compiled binary embeds its deps, so this is skipped when RUNTIME=binary
156
162
  # — it only guards the source fallback. The dev-server's runtime deps live in a
157
- # NESTED package.json (plugins/design/dev-server/) that a global `@1agh/maude` npm
163
+ # NESTED package.json (apps/studio/) that a global `@1agh/maude` npm
158
164
  # install or a fresh `git worktree` does NOT populate; a missing `yjs` (imported at
159
165
  # boot by sync/index.ts) crashes `bun server.ts` AFTER spawn → without this guard we
160
166
  # poll the full ${TIMEOUT}s and report a generic "start timeout", burying the cause
@@ -0,0 +1,68 @@
1
+ <!doctype html>
2
+ <!-- to-lottie-verify.html — self-verify harness for `maude design to-lottie --verify`.
3
+ Renders ANY Lottie JSON through the REAL lottie-web runtime (NOT cairo — cairo
4
+ can't render masks; lottie-web must — DDR-094 rule 5). Generalized from the POC
5
+ verify.html: the Lottie source + the seek frame are query params.
6
+
7
+ Serve this dir (file:// blocks fetch): python3 -m http.server 8200
8
+ Usage: to-lottie-verify.html?src=<file.json>&f=<frame> (omit ?f to autoplay)
9
+ &bg=<css color> — backdrop (default dark) · &w=&h= — stage size px -->
10
+ <html>
11
+ <head>
12
+ <meta charset="utf8" />
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js"></script>
14
+ <style>
15
+ html,
16
+ body {
17
+ margin: 0;
18
+ }
19
+ #stage {
20
+ margin: 20px auto;
21
+ }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <div id="stage"></div>
26
+ <script>
27
+ const p = new URLSearchParams(location.search);
28
+ const src = p.get('src');
29
+ const f = p.get('f');
30
+ const bg = p.get('bg') || '#0a0a14';
31
+ const w = p.get('w') || '400';
32
+ const h = p.get('h') || '400';
33
+ document.body.style.background = bg;
34
+ const stage = document.getElementById('stage');
35
+ stage.style.width = `${w}px`;
36
+ stage.style.height = `${h}px`;
37
+
38
+ if (!src) {
39
+ document.title = 'error-no-src';
40
+ } else {
41
+ fetch(`./${src}`)
42
+ .then((r) => {
43
+ if (!r.ok) throw new Error(`fetch ${src} → ${r.status}`);
44
+ return r.json();
45
+ })
46
+ .then((data) => {
47
+ const anim = lottie.loadAnimation({
48
+ container: stage,
49
+ renderer: 'svg',
50
+ loop: true,
51
+ autoplay: f === null,
52
+ animationData: data,
53
+ });
54
+ anim.addEventListener('DOMLoaded', () => {
55
+ if (f !== null) anim.goToAndStop(parseInt(f, 10), true);
56
+ document.title = `ready${f !== null ? `-${f}` : ''}`;
57
+ });
58
+ anim.addEventListener('error', () => {
59
+ document.title = 'error-render';
60
+ });
61
+ })
62
+ .catch((e) => {
63
+ document.title = `error-${String(e.message || e).slice(0, 40)}`;
64
+ });
65
+ }
66
+ </script>
67
+ </body>
68
+ </html>
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ # to-lottie.sh — emit ONE Lottie from CODE (the maude motion handoff, DDR-094).
3
+ #
4
+ # This is the production-delivery counterpart to draw-build.sh: where draw-build
5
+ # runs an agent-authored *engine* script to emit static SVG/JSX, to-lottie runs an
6
+ # agent-authored *python-lottie generator* to emit ONE `.lottie`/`.json` that
7
+ # renders 1:1 on web (lottie-web / dotlottie-react) AND mobile
8
+ # (lottie-react-native) — same renderer family, 1:1 by construction. It is an
9
+ # EMITTER from the keyframe data, NOT a converter of rendered SVG/SMIL (no reliable
10
+ # one exists — DDR-094 research). The generator encodes the 8 conversion rules; see
11
+ # `plugins/design/commands/to-lottie.md` + `_draw-motion-rules.md`.
12
+ #
13
+ # Reached via `maude design to-lottie` (DDR-062), never a raw bin path.
14
+ #
15
+ # Usage:
16
+ # to-lottie.sh --script <gen.py> --out <mark.json>
17
+ # [--verify] [--frames "0,33,66"] [--root <repo>] [--timeout 12]
18
+ #
19
+ # The generator (gen.py) builds the Lottie via python-lottie and writes it to the
20
+ # path in $MAUDE_LOTTIE_OUT (= --out). python-lottie owns format correctness
21
+ # (beziers, keyframes, gradients, masks, parenting); the agent encodes the
22
+ # conversion rules (per-segment easing incl. overshoot, arc parsing via
23
+ # parse_svg_file, layers[0]=top + reverse, masks, baked sub-loops, coord offset,
24
+ # gradient opacity stops).
25
+ #
26
+ # --verify renders frames through HEADLESS lottie-web (cairo can't render masks —
27
+ # lottie-web must) using the shipped harness + the screenshot helper, so you can
28
+ # read the PNGs and compare to the web reference.
29
+ #
30
+ # Stdout (last line): the output .json path (capturable in $(...)).
31
+ # Exit: 0 ok / 1 missing input or runtime / 2 bad args / 3 generate failed
32
+ # / 4 verify render failed.
33
+
34
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
35
+ GEN=""
36
+ OUT=""
37
+ VERIFY=0
38
+ FRAMES="0,33,66"
39
+ REPO=""
40
+ TIMEOUT=12
41
+
42
+ while [ $# -gt 0 ]; do
43
+ case "$1" in
44
+ --script) GEN="$2"; shift 2 ;;
45
+ --out) OUT="$2"; shift 2 ;;
46
+ --verify) VERIFY=1; shift ;;
47
+ --frames) FRAMES="$2"; shift 2 ;;
48
+ --root) REPO="$2"; shift 2 ;;
49
+ --timeout) TIMEOUT="$2"; shift 2 ;;
50
+ --help|-h) sed -n '2,33p' "$0" | sed 's/^# \?//'; exit 0 ;;
51
+ *) echo "to-lottie.sh: unknown arg '$1' (try --help)" >&2; exit 2 ;;
52
+ esac
53
+ done
54
+
55
+ [ -n "$GEN" ] || { echo "to-lottie.sh: --script <gen.py> required" >&2; exit 2; }
56
+ [ -n "$OUT" ] || { echo "to-lottie.sh: --out <mark.json> required" >&2; exit 2; }
57
+ [ -f "$GEN" ] || { echo "to-lottie.sh: generator not found: $GEN" >&2; exit 1; }
58
+ command -v python3 >/dev/null 2>&1 || { echo "to-lottie.sh: python3 is required." >&2; exit 1; }
59
+
60
+ # ---------- python-lottie venv (cached, idempotent) ----------
61
+ VENV="${MAUDE_LOTTIE_VENV:-${XDG_CACHE_HOME:-$HOME/.cache}/maude/lottie-venv}"
62
+ PY="$VENV/bin/python"
63
+ if [ ! -x "$PY" ]; then
64
+ echo "→ bootstrapping python-lottie venv at $VENV (one-off) …" >&2
65
+ python3 -m venv "$VENV" || { echo "to-lottie.sh: venv create failed" >&2; exit 1; }
66
+ "$VENV/bin/pip" install --quiet --disable-pip-version-check lottie cairosvg pillow \
67
+ || { echo "to-lottie.sh: pip install lottie failed (needs network on first run)" >&2; exit 1; }
68
+ fi
69
+ # Sanity: the `lottie` module must import.
70
+ "$PY" -c 'import lottie' 2>/dev/null || {
71
+ echo "→ (re)installing python-lottie into $VENV …" >&2
72
+ "$VENV/bin/pip" install --quiet --disable-pip-version-check lottie cairosvg pillow \
73
+ || { echo "to-lottie.sh: python-lottie unavailable" >&2; exit 1; }
74
+ }
75
+
76
+ # ---------- generate ----------
77
+ mkdir -p "$(dirname "$OUT")"
78
+ echo "→ generating Lottie: $GEN → $OUT" >&2
79
+ MAUDE_LOTTIE_OUT="$OUT" "$PY" "$GEN" >&2
80
+ GRC=$?
81
+ if [ "$GRC" -ne 0 ] || [ ! -s "$OUT" ]; then
82
+ echo "to-lottie.sh: generator failed (rc=$GRC) or wrote no output to $OUT" >&2
83
+ exit 3
84
+ fi
85
+ # Validity gate: the output must be parseable JSON with the Lottie shape keys.
86
+ "$PY" - "$OUT" <<'PYEOF' >&2 || { echo "to-lottie.sh: output is not a valid Lottie JSON" >&2; exit 3; }
87
+ import json, sys
88
+ d = json.load(open(sys.argv[1]))
89
+ assert isinstance(d, dict) and 'layers' in d and 'op' in d, 'missing Lottie keys (layers/op)'
90
+ print(f" ✓ valid Lottie: {len(d.get('layers', []))} layers, {d.get('op')} frames @ {d.get('fr')}fps")
91
+ PYEOF
92
+
93
+ # ---------- self-verify through headless lottie-web ----------
94
+ if [ "$VERIFY" -eq 1 ]; then
95
+ HARNESS="$SCRIPT_DIR/to-lottie-verify.html"
96
+ [ -f "$HARNESS" ] || { echo "to-lottie.sh: verify harness missing at $HARNESS — reinstall maude." >&2; exit 1; }
97
+ OUT_DIR_ABS="$(cd "$(dirname "$OUT")" && pwd)"
98
+ OUT_BASE="$(basename "$OUT")"
99
+ cp "$HARNESS" "$OUT_DIR_ABS/_to-lottie-verify.html"
100
+ FRAMES_DIR="$OUT_DIR_ABS/_verify-frames"
101
+ mkdir -p "$FRAMES_DIR"
102
+
103
+ # Serve the dir (file:// blocks fetch of the JSON). Pick a free-ish port.
104
+ PORT=$(( ( RANDOM % 2000 ) + 8200 ))
105
+ ( cd "$OUT_DIR_ABS" && "$PY" -m http.server "$PORT" >/dev/null 2>&1 ) &
106
+ SRV_PID=$!
107
+ trap 'kill "$SRV_PID" 2>/dev/null; rm -f "$OUT_DIR_ABS/_to-lottie-verify.html"' EXIT
108
+ sleep 1
109
+
110
+ FAIL=0
111
+ IFS=',' read -r -a FRAME_ARR <<< "$FRAMES"
112
+ for F in "${FRAME_ARR[@]}"; do
113
+ F_TRIM="$(printf '%s' "$F" | tr -d '[:space:]')"
114
+ URL="http://localhost:$PORT/_to-lottie-verify.html?src=$OUT_BASE&f=$F_TRIM"
115
+ PNG="$FRAMES_DIR/frame-$F_TRIM.png"
116
+ echo "→ verify frame $F_TRIM ($URL)" >&2
117
+ bash "$SCRIPT_DIR/screenshot.sh" --url "$URL" --full --out "$PNG" --timeout "$TIMEOUT" >&2 \
118
+ || { echo "✗ verify frame $F_TRIM failed" >&2; FAIL=$((FAIL + 1)); }
119
+ done
120
+ if [ "$FAIL" -gt 0 ]; then
121
+ echo "to-lottie.sh: $FAIL verify frame(s) failed to render" >&2
122
+ exit 4
123
+ fi
124
+ echo "→ verify frames in $FRAMES_DIR — READ them and compare to the web reference (masks render on lottie-web, NOT cairo)" >&2
125
+ fi
126
+
127
+ # Last stdout line = the output path, for $(maude design to-lottie ...) capture.
128
+ printf '%s\n' "$OUT"
@@ -16,7 +16,7 @@
16
16
  // Per DDR-009 (Bun runtime authoritative) + DDR-012 (React 19 unified) + DDR-014 (Lightning CSS).
17
17
 
18
18
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
19
- import { dirname, join, relative, resolve } from 'node:path';
19
+ import { dirname, join, relative } from 'node:path';
20
20
  import { fileURLToPath } from 'node:url';
21
21
 
22
22
  import { browserslistToTargets, bundle as lcssBundle } from 'lightningcss';
@@ -66,8 +66,8 @@ function ensureDist() {
66
66
  }
67
67
 
68
68
  async function readPluginVersion(): Promise<{ version: string }> {
69
- // plugins/design/dev-server/ → plugins/design/.claude-plugin/plugin.json
70
- const manifest = join(ROOT, '..', '.claude-plugin', 'plugin.json');
69
+ // apps/studio/ → ../../plugins/design/.claude-plugin/plugin.json (DDR-095)
70
+ const manifest = join(ROOT, '..', '..', 'plugins', 'design', '.claude-plugin', 'plugin.json');
71
71
  try {
72
72
  const parsed = JSON.parse(await Bun.file(manifest).text()) as { version?: unknown };
73
73
  if (typeof parsed.version === 'string') return { version: parsed.version };
@@ -257,7 +257,9 @@ async function ensureBindingForTarget(oxcSlug: string): Promise<void> {
257
257
  let oxcVersion = '0.131.0';
258
258
  const candidates = [
259
259
  join(ROOT, 'node_modules', 'oxc-parser', 'package.json'),
260
- join(ROOT, '..', '..', '..', 'node_modules', 'oxc-parser', 'package.json'),
260
+ // apps/studio ../../ = repo root → node_modules (DDR-095: was ../../../ at
261
+ // the old plugins/design/dev-server depth).
262
+ join(ROOT, '..', '..', 'node_modules', 'oxc-parser', 'package.json'),
261
263
  ];
262
264
  for (const c of candidates) {
263
265
  if (existsSync(c)) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file canvas-arrowheads.ts — Phase 24 arrow shaft + head geometry
3
- * @scope plugins/design/dev-server/canvas-arrowheads.ts
3
+ * @scope apps/studio/canvas-arrowheads.ts
4
4
  * @purpose Single source of truth for arrow rendering. An ArrowStroke is
5
5
  * reduced to an ordered list of `SvgPrimitive`s (the shaft, then
6
6
  * the start head, then the end head). The serializer formats each
@@ -59,6 +59,28 @@ export interface ArrowGeom {
59
59
  endHead?: ArrowHead;
60
60
  dashed?: boolean;
61
61
  lineType?: ArrowLineType;
62
+ /**
63
+ * FigJam v3 — magnetic binding anchors (normalized over the host bbox;
64
+ * `ArrowBind` is structurally assignable). A BOUND curved arrow exits its
65
+ * host perpendicular to the bound side (cubic with exit normals — the
66
+ * FigJam connector look) and takes the sleeker connector head; unbound
67
+ * arrows keep the legacy byte-identical geometry.
68
+ */
69
+ startBind?: { nx: number; ny: number };
70
+ endBind?: { nx: number; ny: number };
71
+ }
72
+
73
+ /**
74
+ * Exit direction OUT of a bound side — the normalized vector from the host's
75
+ * center toward the anchor magnet. A center anchor (0.5, 0.5) has no exit
76
+ * direction (null → the curve falls back to chord-based control points).
77
+ */
78
+ function bindNormal(b: { nx: number; ny: number }): [number, number] | null {
79
+ const dx = b.nx - 0.5;
80
+ const dy = b.ny - 0.5;
81
+ const len = Math.hypot(dx, dy);
82
+ if (len < 0.05) return null;
83
+ return [dx / len, dy / len];
62
84
  }
63
85
 
64
86
  /**
@@ -73,10 +95,13 @@ export function arrowHeadPoints(
73
95
  y1: number,
74
96
  x2: number,
75
97
  y2: number,
76
- width: number
98
+ width: number,
99
+ // FigJam v3 — optional head length override (sleeker connector heads on
100
+ // BOUND arrows). Default = the legacy byte-frozen formula.
101
+ lenOverride?: number
77
102
  ): string {
78
103
  const angle = Math.atan2(y2 - y1, x2 - x1);
79
- const len = 12 + width * 2;
104
+ const len = lenOverride ?? 12 + width * 2;
80
105
  const wing = Math.PI / 7;
81
106
  const ax = x2 - Math.cos(angle - wing) * len;
82
107
  const ay = y2 - Math.sin(angle - wing) * len;
@@ -154,25 +179,39 @@ function headPrimitives(
154
179
  fromX: number,
155
180
  fromY: number,
156
181
  width: number,
157
- color: string
182
+ color: string,
183
+ lenOverride?: number
158
184
  ): SvgPrimitive[] {
159
185
  switch (head) {
160
186
  case 'none':
161
187
  return [];
162
188
  case 'triangle':
163
- // Legacy filled head — byte-identical to Phase 5.1.
189
+ // Legacy filled head — byte-identical to Phase 5.1 (unless a bound
190
+ // arrow passes the sleeker connector length).
164
191
  return [
165
- { el: 'polyline', points: arrowHeadPoints(fromX, fromY, tipX, tipY, width), fill: color },
192
+ {
193
+ el: 'polyline',
194
+ points: arrowHeadPoints(fromX, fromY, tipX, tipY, width, lenOverride),
195
+ fill: color,
196
+ },
166
197
  ];
167
198
  case 'line':
168
199
  // Open chevron — same 3 points, unfilled (no implicit close).
169
200
  return [
170
- { el: 'polyline', points: arrowHeadPoints(fromX, fromY, tipX, tipY, width), fill: 'none' },
201
+ {
202
+ el: 'polyline',
203
+ points: arrowHeadPoints(fromX, fromY, tipX, tipY, width, lenOverride),
204
+ fill: 'none',
205
+ },
171
206
  ];
172
207
  case 'triangle-outline':
173
208
  // Closed triangle outline — polygon auto-closes the third edge.
174
209
  return [
175
- { el: 'polygon', points: arrowHeadPoints(fromX, fromY, tipX, tipY, width), fill: 'none' },
210
+ {
211
+ el: 'polygon',
212
+ points: arrowHeadPoints(fromX, fromY, tipX, tipY, width, lenOverride),
213
+ fill: 'none',
214
+ },
176
215
  ];
177
216
  case 'diamond':
178
217
  return [
@@ -199,6 +238,7 @@ export function arrowPrimitives(s: ArrowGeom): SvgPrimitive[] {
199
238
  const endHead = s.endHead ?? 'triangle';
200
239
  const dashed = s.dashed ?? false;
201
240
  const lineType = s.lineType ?? 'straight';
241
+ const bound = Boolean(s.startBind || s.endBind);
202
242
  const out: SvgPrimitive[] = [];
203
243
 
204
244
  // Default tangent sources (straight): each head points away from the far end.
@@ -207,6 +247,28 @@ export function arrowPrimitives(s: ArrowGeom): SvgPrimitive[] {
207
247
 
208
248
  if (lineType === 'straight') {
209
249
  out.push({ el: 'line', x1: s.x1, y1: s.y1, x2: s.x2, y2: s.y2, dash: dashed });
250
+ } else if (lineType === 'curved' && bound) {
251
+ // FigJam v3 — connector curve: a cubic whose control points extend along
252
+ // each bound side's EXIT NORMAL (perpendicular out of the side, then
253
+ // toward the target) — the smooth "leaves the box sideways" look. A free
254
+ // / center-anchored end falls back to a chord-aligned control point.
255
+ const dx = s.x2 - s.x1;
256
+ const dy = s.y2 - s.y1;
257
+ const chord = Math.hypot(dx, dy) || 1;
258
+ const reach = Math.min(160, Math.max(24, chord * 0.4));
259
+ const n1 = s.startBind ? bindNormal(s.startBind) : null;
260
+ const n2 = s.endBind ? bindNormal(s.endBind) : null;
261
+ const c1x = s.x1 + (n1 ? n1[0] : dx / chord) * reach;
262
+ const c1y = s.y1 + (n1 ? n1[1] : dy / chord) * reach;
263
+ const c2x = s.x2 + (n2 ? n2[0] : -dx / chord) * reach;
264
+ const c2y = s.y2 + (n2 ? n2[1] : -dy / chord) * reach;
265
+ out.push({
266
+ el: 'path',
267
+ d: `M${s.x1} ${s.y1} C${c1x} ${c1y} ${c2x} ${c2y} ${s.x2} ${s.y2}`,
268
+ dash: dashed,
269
+ });
270
+ startFrom = [c1x, c1y];
271
+ endFrom = [c2x, c2y];
210
272
  } else {
211
273
  const shaft = shaftPath(s.x1, s.y1, s.x2, s.y2, lineType);
212
274
  out.push({ el: 'path', d: shaft.d, dash: dashed });
@@ -214,7 +276,14 @@ export function arrowPrimitives(s: ArrowGeom): SvgPrimitive[] {
214
276
  endFrom = shaft.endFrom;
215
277
  }
216
278
 
217
- out.push(...headPrimitives(startHead, s.x1, s.y1, startFrom[0], startFrom[1], s.width, s.color));
218
- out.push(...headPrimitives(endHead, s.x2, s.y2, endFrom[0], endFrom[1], s.width, s.color));
279
+ // Bound arrows take the sleeker connector head; unbound keep the legacy
280
+ // byte-frozen proportions.
281
+ const headLen = bound ? 9 + s.width * 1.4 : undefined;
282
+ out.push(
283
+ ...headPrimitives(startHead, s.x1, s.y1, startFrom[0], startFrom[1], s.width, s.color, headLen)
284
+ );
285
+ out.push(
286
+ ...headPrimitives(endHead, s.x2, s.y2, endFrom[0], endFrom[1], s.width, s.color, headLen)
287
+ );
219
288
  return out;
220
289
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file canvas-comment-mount.tsx — shell-owned comment layer + mountCanvas
3
- * @scope plugins/design/dev-server/canvas-comment-mount.tsx
3
+ * @scope apps/studio/canvas-comment-mount.tsx
4
4
  * @purpose The canvas mount harness (`_shell.html`) calls `mountCanvas`
5
5
  * instead of rendering the canvas default-export raw. mountCanvas
6
6
  * wraps ANY default export — a `DesignCanvas` UI canvas OR a bare
@@ -0,0 +1,104 @@
1
+ // canvas-create.ts — Phase 22 UI add-on. Pure helpers behind POST /_api/canvas
2
+ // (api.ts createCanvas), which lets the browser file-tree create a blank brief
3
+ // board directly — no slash command, no model cost. Generation (ingesting a
4
+ // board's notes into artboards) stays with Claude via `/design:new`; this only
5
+ // stamps out the empty annotation surface.
6
+ //
7
+ // SINGLE SOURCE — the board envelope is the SAME `brief-board.tsx.template`
8
+ // `/design:new --blank` substitutes. We text-import it so Bun.build inlines the
9
+ // content into the `bun --compile` standalone binary (verified: works at dev
10
+ // runtime AND embeds at build time), sidestepping DDR-045 — no runtime disk read
11
+ // of a file that lives outside `apps/studio/`. The template stayed under the
12
+ // design plugin (plugins/design/templates) when the studio moved (DDR-095).
13
+
14
+ import briefBoardTemplate from '../../plugins/design/templates/brief-board.tsx.template' with {
15
+ type: 'text',
16
+ };
17
+
18
+ export const BRIEF_BOARD_TEMPLATE: string = briefBoardTemplate;
19
+
20
+ /**
21
+ * The ONE security boundary for the create endpoint. A canvas name is the only
22
+ * user-controlled value, and it is interpolated into BOTH a real nested
23
+ * filesystem path (`<group>/<name>.tsx`) AND the generated `.tsx` (JSX text) +
24
+ * `.meta.json` (JSON string). This strict allowlist does triple duty:
25
+ * • path safety — no `/`, `\`, `..`, or leading dot ⇒ no traversal;
26
+ * • template safety — no `<`, `>`, `{`, `}` ⇒ can't break out of JSX text;
27
+ * • JSON safety — no `"` or `\` ⇒ can't break out of a meta string.
28
+ * Unicode letters/numbers are allowed (so "Onboarding Brief" / accented names
29
+ * work); the first char must be a letter/number (excludes leading space/dash/dot).
30
+ */
31
+ const NAME_RE = /^[\p{L}\p{N}][\p{L}\p{N} _-]{0,59}$/u;
32
+
33
+ export interface NameValidation {
34
+ ok: boolean;
35
+ name?: string;
36
+ componentName?: string;
37
+ error?: string;
38
+ }
39
+
40
+ export function validateCanvasName(raw: unknown): NameValidation {
41
+ if (typeof raw !== 'string') return { ok: false, error: 'name must be a string' };
42
+ // NFC-normalize so a decomposed (NFD) name and its composed sibling can't hash
43
+ // to two JS strings that collide to ONE on-disk path on a normalizing filesystem
44
+ // (APFS/HFS+) — which would let the 409 existence guard be bypassed and an
45
+ // existing board silently overwritten (Phase 22 security review, low). Collapse
46
+ // runs of SPACES (the one allowed whitespace) to a single space so the JS slug
47
+ // (`/\s+/→_`) and slug.sh (`tr ' ' '_'`) can't diverge on a double-spaced name
48
+ // (review #4); tabs/newlines stay un-collapsed and are rejected by NAME_RE.
49
+ const name = raw.normalize('NFC').trim().replace(/ +/g, ' ');
50
+ if (!name) return { ok: false, error: 'name is required' };
51
+ if (name.length > 60) return { ok: false, error: 'name must be 60 characters or fewer' };
52
+ if (!NAME_RE.test(name)) {
53
+ return {
54
+ ok: false,
55
+ error: 'name may contain only letters, numbers, spaces, hyphens and underscores',
56
+ };
57
+ }
58
+ // Defense in depth — the regex already forbids these, but assert no path bits
59
+ // survive (a regex edit must never silently re-open traversal).
60
+ if (name.includes('..') || name.includes('/') || name.includes('\\') || name.startsWith('.')) {
61
+ return { ok: false, error: 'name may not contain path separators or leading dots' };
62
+ }
63
+ return { ok: true, name, componentName: componentNameFrom(name) };
64
+ }
65
+
66
+ /** PascalCase a name into a valid JS identifier for the component default export.
67
+ * Non-identifier characters split words; an empty / digit-leading result gets a
68
+ * `Board` prefix so the emitted `export default function <id>()` always parses. */
69
+ export function componentNameFrom(name: string): string {
70
+ // Split on NON-(letter|number) so Unicode letters survive (the validator
71
+ // accepts them) — `Přihlášení` → `Přihlášení`, a valid JS identifier (verified
72
+ // it parses), not the ASCII-mangled `PIhlEn` (review #1). Empty (all-symbol) →
73
+ // `BriefBoard`; a digit-leading result (incl. Unicode digits) → `Board…` so the
74
+ // emitted `export default function <id>()` always parses.
75
+ const pascal = name
76
+ .split(/[^\p{L}\p{N}]+/u)
77
+ .filter(Boolean)
78
+ .map((w) => w[0].toUpperCase() + w.slice(1))
79
+ .join('');
80
+ if (!pascal) return 'BriefBoard';
81
+ if (/^\p{Nd}/u.test(pascal)) return `Board${pascal}`;
82
+ return pascal;
83
+ }
84
+
85
+ export interface BriefBoardOpts {
86
+ name: string;
87
+ componentName: string;
88
+ dsName: string;
89
+ platform: string;
90
+ seedHint: string;
91
+ historyDir: string;
92
+ }
93
+
94
+ /** Substitute the brief-board template placeholders. Safe because `name`
95
+ * (the only user value reaching here) is validated by `validateCanvasName`,
96
+ * and every other field is server-derived (config / fixed strings). */
97
+ export function renderBriefBoard(opts: BriefBoardOpts): string {
98
+ return BRIEF_BOARD_TEMPLATE.replaceAll('{{NAME}}', opts.name)
99
+ .replaceAll('{{COMPONENT_NAME}}', opts.componentName)
100
+ .replaceAll('{{DS_NAME}}', opts.dsName)
101
+ .replaceAll('{{PLATFORM}}', opts.platform)
102
+ .replaceAll('{{SEED_HINT}}', opts.seedHint)
103
+ .replaceAll('{{HISTORY_DIR}}', opts.historyDir);
104
+ }