@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
@@ -0,0 +1,139 @@
1
+ {
2
+ "$schema": "./whats-new.schema.json",
3
+ "entries": [
4
+ {
5
+ "id": "studio-full-functionality",
6
+ "version": "0.29.0",
7
+ "date": "2026-06-08",
8
+ "kind": "feature",
9
+ "title": "The studio is fully wired now",
10
+ "summary": "Everything in the chrome is live: the top bar shows the real zoom + artboard count, the status bar always shows hub-sync, and your avatar (plus Claude when it's editing) appears in the menubar. The ⌘K palette grew real Canvas/Tools commands (New canvas, Export, Handoff, Draw), there's a proper Export & handoff dialog (PNG/PDF/SVG/HTML/shadcn/ZIP) right in the shell, and a new display-only Inspector panel (Inspect · Layers · CSS) reads any element you ⌘-click.",
11
+ "surface": "design-ui",
12
+ "tour": [
13
+ {
14
+ "target": ".st-mb-right",
15
+ "title": "Live status, finally",
16
+ "body": "Zoom, artboard count, presence and hub-sync now reflect the real state — no more static placeholders."
17
+ },
18
+ {
19
+ "target": ".st-rpanel",
20
+ "title": "Inspector",
21
+ "body": "⌘-click an element in the canvas, then press I — pos, size, ancestry and markup, read-only (live CSS knobs land in Phase 12)."
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "id": "draw-animation-lottie",
27
+ "version": "0.29.0",
28
+ "date": "2026-06-08",
29
+ "kind": "feature",
30
+ "title": "Animate draw marks → ship one Lottie",
31
+ "summary": "/design:draw now handles motion briefs (morph, pulse, blink) and proves the animation is actually live. /design:to-lottie productionizes it into one Lottie that plays 1:1 on web and React Native.",
32
+ "surface": "design-ui"
33
+ },
34
+ {
35
+ "id": "studio-maude-ds-redesign",
36
+ "version": "0.29.0",
37
+ "date": "2026-06-08",
38
+ "kind": "feature",
39
+ "title": "The studio, redesigned in maude",
40
+ "summary": "The canvas browser chrome is now built in the maude design system itself — a dark-first studio shell with a branded menubar + real dropdowns, a collapsible file-tree sidebar with a rail, a cohesive bottom context bar, a maude-styled comments panel, and a clean light handoff. New: a ⌘K command palette for jumping to the design-system view, comments, theme, reload, and help. The in-canvas chrome (tools, minimap, selection halos) was re-skinned to the same indigo accent so the whole surface reads as one product.",
41
+ "surface": "design-ui",
42
+ "tour": [
43
+ {
44
+ "target": ".st-menubar .st-brand",
45
+ "title": "Built in maude",
46
+ "body": "The studio chrome now dogfoods the maude DS — one cohesive material across the menubar, sidebar, canvas and status bar."
47
+ },
48
+ {
49
+ "target": ".st-menubar .st-whatsnew",
50
+ "title": "Press ⌘K",
51
+ "body": "A new command palette jumps you to the design-system view, comments, theme toggle, reload, and help — no mouse needed."
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "id": "annotation-tooling-polish",
57
+ "version": "0.29.0",
58
+ "date": "2026-06-08",
59
+ "kind": "improvement",
60
+ "title": "Richer annotations — highlighter, lists & text shortcuts",
61
+ "summary": "Annotations got a full polish pass: a new highlighter tool (translucent marker with multiply blend), dashed strokes on shapes, FigJam-style sticky corner, multi-line text with Enter, italic/underline/lists, and Cmd+B/I/U formatting shortcuts that work the same way in sticky notes, shapes, and the text tool.",
62
+ "surface": "design-ui",
63
+ "tour": [
64
+ {
65
+ "target": ".dc-tool-palette button[aria-label^='Highlighter']",
66
+ "title": "New: Highlighter",
67
+ "body": "A translucent marker pen (I) — overlapping strokes darken with multiply blend. Pick hue and nib width from the chrome below."
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "id": "canvas-media-images-links",
73
+ "version": "0.29.0",
74
+ "date": "2026-06-08",
75
+ "kind": "feature",
76
+ "title": "Drop images & paste links onto the canvas",
77
+ "summary": "Drag a screenshot from your desktop — or paste a clipboard image or a URL — straight onto the canvas. Images sit alongside your annotations; links render as a tidy preview chip you can open in one click.",
78
+ "surface": "design-ui"
79
+ },
80
+ {
81
+ "id": "in-app-whats-new-tour",
82
+ "version": "0.29.0",
83
+ "date": "2026-06-08",
84
+ "kind": "feature",
85
+ "title": "What's New, right in the canvas browser",
86
+ "summary": "The Maude UI now tells you what shipped — a dismissible notice plus a panel you can reopen from the menubar. Each closed-out feature adds an entry automatically.",
87
+ "surface": "design-ui",
88
+ "tour": [
89
+ {
90
+ "target": "[data-tour='whatsnew']",
91
+ "title": "Your What's New badge",
92
+ "body": "This ✦ badge lights up when a new maude version ships features. Click it any time to reopen this panel."
93
+ },
94
+ {
95
+ "target": "[data-tour='help']",
96
+ "title": "Tours live in Help too",
97
+ "body": "Open Help to replay the full \"how Maude works\" walkthrough whenever you want."
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ "id": "canvas-create-delete-from-browser",
103
+ "version": "0.28.1",
104
+ "date": "2026-06-03",
105
+ "kind": "feature",
106
+ "title": "Create & delete canvases from the browser",
107
+ "summary": "Spin up a blank brief-board straight from the file tree (+ board) and remove canvases without leaving the UI — no /design:new round-trip needed.",
108
+ "learnMore": "https://github.com/1aGh/maude#readme",
109
+ "surface": "design-ui"
110
+ },
111
+ {
112
+ "id": "canvas-activity-overlay",
113
+ "version": "0.28.0",
114
+ "date": "2026-06-02",
115
+ "kind": "feature",
116
+ "title": "Live canvas activity overlay",
117
+ "summary": "See at a glance which artboard Claude (or a teammate) is editing — a soft rim + badge highlights the active canvas and fades when work settles.",
118
+ "surface": "design-ui"
119
+ },
120
+ {
121
+ "id": "draw-as-code-svg-agent",
122
+ "version": "0.28.0",
123
+ "date": "2026-05-30",
124
+ "kind": "feature",
125
+ "title": "Draw production SVGs as code",
126
+ "summary": "/design:draw generates logos, icons, and diagrams through a deterministic geometry engine — no hand-guessed path data — and proofs them across the 16/24/48/256 verify ladder.",
127
+ "surface": "design-ui"
128
+ },
129
+ {
130
+ "id": "self-hostable-hub-sync",
131
+ "version": "0.27.0",
132
+ "date": "2026-05-28",
133
+ "kind": "feature",
134
+ "title": "Self-hostable hub + live file sync",
135
+ "summary": "Run your own Maude hub and co-edit canvases with bidirectional file sync, offline-aware status, and conflict handling.",
136
+ "surface": "design-ui"
137
+ }
138
+ ]
139
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/1aGh/maude/main/apps/studio/whats-new.schema.json",
4
+ "title": "Maude What's New feed",
5
+ "description": "User-facing product-update entries surfaced in the Maude UI (and mirrored to the docs site). Single source of truth — ships with the dev-server, appended on /flow:done, stamped at release. See .ai/plans/feature-in-app-whats-new-tour.md (DDR-A).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["entries"],
9
+ "properties": {
10
+ "$schema": { "type": "string" },
11
+ "entries": {
12
+ "type": "array",
13
+ "items": { "$ref": "#/$defs/entry" }
14
+ }
15
+ },
16
+ "$defs": {
17
+ "entry": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "required": ["id", "kind", "title", "summary"],
21
+ "properties": {
22
+ "id": {
23
+ "type": "string",
24
+ "description": "Stable slug (usually the plan slug). Used for de-dup and as the React key.",
25
+ "pattern": "^[a-z0-9][a-z0-9-]*$"
26
+ },
27
+ "version": {
28
+ "type": ["string", "null"],
29
+ "description": "Semver the feature shipped in, or null while pending (stamped at release).",
30
+ "pattern": "^\\d+\\.\\d+\\.\\d+"
31
+ },
32
+ "date": {
33
+ "type": ["string", "null"],
34
+ "description": "Release date YYYY-MM-DD, or null while pending."
35
+ },
36
+ "kind": { "enum": ["feature", "improvement", "usage", "fix"] },
37
+ "title": { "type": "string", "minLength": 1 },
38
+ "summary": { "type": "string", "minLength": 1 },
39
+ "learnMore": {
40
+ "type": "string",
41
+ "description": "Optional URL to docs / release notes. Constrained to http(s) — the feed renders this as an <a href>, so a javascript:/data: scheme is rejected at the data-contract layer (defense-in-depth alongside React's URL sanitizer).",
42
+ "pattern": "^https?://"
43
+ },
44
+ "surface": {
45
+ "type": "string",
46
+ "description": "Which UI surface this targets (e.g. design-ui). Future-proofing."
47
+ },
48
+ "tour": {
49
+ "type": "array",
50
+ "description": "Optional spotlight steps that highlight the new UI (consumed by the tour engine — Phase 3).",
51
+ "items": { "$ref": "#/$defs/tourStep" }
52
+ }
53
+ }
54
+ },
55
+ "tourStep": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": ["target", "title", "body"],
59
+ "properties": {
60
+ "target": {
61
+ "type": "string",
62
+ "description": "CSS selector or [data-tour=\"<key>\"] anchor to spotlight."
63
+ },
64
+ "title": { "type": "string", "minLength": 1 },
65
+ "body": { "type": "string", "minLength": 1 },
66
+ "placement": { "enum": ["top", "bottom", "left", "right", "auto"] }
67
+ }
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,87 @@
1
+ // whats-new.ts — server-side loader for the Maude "What's New" feed.
2
+ //
3
+ // Single source of truth: `<DEV_SERVER_ROOT>/whats-new.json` (DDR-A). The feed
4
+ // describes **Maude's own product** updates — it is resolved from the maude
5
+ // package root (via paths.ts, NOT cwd), so the dev-server surfaces the same
6
+ // product news whether it runs in this repo or a downstream user's project.
7
+ //
8
+ // Disk paths MUST come from paths.ts (DDR-045) — never
9
+ // `dirname(fileURLToPath(import.meta.url))`, which is the virtual `/$bunfs/root`
10
+ // inside a `bun --compile` standalone binary.
11
+
12
+ import { existsSync, readFileSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { DEV_SERVER_ROOT } from './paths.ts';
15
+
16
+ export type WhatsNewKind = 'feature' | 'improvement' | 'usage' | 'fix';
17
+
18
+ export interface WhatsNewTourStep {
19
+ target: string;
20
+ title: string;
21
+ body: string;
22
+ placement?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
23
+ }
24
+
25
+ export interface WhatsNewEntry {
26
+ id: string;
27
+ version: string | null;
28
+ date: string | null;
29
+ kind: WhatsNewKind;
30
+ title: string;
31
+ summary: string;
32
+ learnMore?: string;
33
+ surface?: string;
34
+ tour?: WhatsNewTourStep[];
35
+ }
36
+
37
+ export interface WhatsNewFeed {
38
+ /** The installed maude version (key the client compares "seen" against). */
39
+ version: string;
40
+ entries: WhatsNewEntry[];
41
+ }
42
+
43
+ let cache: WhatsNewFeed | null = null;
44
+
45
+ /**
46
+ * Read the installed maude version from the design plugin manifest.
47
+ * `apps/studio/` → `plugins/design/.claude-plugin/plugin.json`.
48
+ * That manifest ships in BOTH npm installs and marketplace-cache clones
49
+ * (same rationale as build.ts `readPluginVersion`). Falls back to `dev`.
50
+ */
51
+ export function resolveMaudeVersion(root: string = DEV_SERVER_ROOT): string {
52
+ const manifest = join(root, '..', '..', 'plugins', 'design', '.claude-plugin', 'plugin.json');
53
+ try {
54
+ const parsed = JSON.parse(readFileSync(manifest, 'utf8')) as { version?: unknown };
55
+ if (typeof parsed.version === 'string') return parsed.version;
56
+ } catch {
57
+ /* fall through */
58
+ }
59
+ return 'dev';
60
+ }
61
+
62
+ /**
63
+ * Load `<root>/whats-new.json`. Fails soft to an empty entry list — a missing
64
+ * or malformed feed must never 500 the dev-server (it's a non-critical chrome
65
+ * surface). Caches the default-root result; pass `{ root }` for an uncached
66
+ * read (tests) and `{ fresh: true }` to bypass the cache.
67
+ */
68
+ export function loadWhatsNew(opts: { root?: string; fresh?: boolean } = {}): WhatsNewFeed {
69
+ const usingDefaultRoot = opts.root === undefined;
70
+ if (cache && usingDefaultRoot && !opts.fresh) return cache;
71
+
72
+ const root = opts.root ?? DEV_SERVER_ROOT;
73
+ const file = join(root, 'whats-new.json');
74
+ let entries: WhatsNewEntry[] = [];
75
+ try {
76
+ if (existsSync(file)) {
77
+ const parsed = JSON.parse(readFileSync(file, 'utf8')) as { entries?: unknown };
78
+ if (Array.isArray(parsed.entries)) entries = parsed.entries as WhatsNewEntry[];
79
+ }
80
+ } catch {
81
+ /* malformed feed → empty, never throw */
82
+ }
83
+
84
+ const feed: WhatsNewFeed = { version: resolveMaudeVersion(root), entries };
85
+ if (usingDefaultRoot) cache = feed;
86
+ return feed;
87
+ }
package/cli/bin/maude.mjs CHANGED
@@ -50,6 +50,16 @@ async function main(argv) {
50
50
  return run({ args: [], pkgRoot: PKG_ROOT });
51
51
  }
52
52
 
53
+ // `maude studio [...]` — top-level alias for `maude design serve` (DDR-095:
54
+ // the verb now matches the apps/studio home). Checked BEFORE the COMMANDS map
55
+ // so it never falls through to the unknown-command branch; it is deliberately
56
+ // NOT a COMMANDS entry (that would dispatch design.run with positional[0]
57
+ // wrong). `maude design serve` keeps working unchanged.
58
+ if (cmd === 'studio') {
59
+ const { run } = await COMMANDS.design();
60
+ return run({ args: ['serve', ...args.slice(1)], pkgRoot: PKG_ROOT });
61
+ }
62
+
53
63
  const loader = COMMANDS[cmd];
54
64
  if (!loader) {
55
65
  process.stderr.write(
@@ -1,6 +1,6 @@
1
1
  import { execSync, spawn, spawnSync } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
3
- import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
2
+ import { chmodSync, existsSync, readFileSync } from 'node:fs';
3
+ import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
4
4
  import { createRequire } from 'node:module';
5
5
  import { basename, dirname, join, resolve } from 'node:path';
6
6
  import { parseArgs } from '../lib/argv.mjs';
@@ -40,8 +40,15 @@ const BIN_VERBS = new Set([
40
40
  'draw-build',
41
41
  'draw-proof',
42
42
  'svg-optimize',
43
+ 'to-lottie',
44
+ 'read-annotations',
43
45
  ]);
44
46
 
47
+ // Bin verbs that boot the dev-server (directly, or by shelling into server-up.sh).
48
+ // For these we resolve the compiled platform binary and hand its path to the
49
+ // helper via MAUDE_DEV_SERVER_BIN — see runBinDispatch + DDR-084.
50
+ const BOOT_VERBS = new Set(['server-up', 'visual-sanity', 'smoke']);
51
+
45
52
  export async function run({ args, pkgRoot }) {
46
53
  const { positional } = parseArgs(args);
47
54
  const sub = positional[0];
@@ -81,18 +88,35 @@ function runBinDispatch(verb, { args, pkgRoot }) {
81
88
  process.stderr.write(`maude design: "${verb}" is not a dev-tooling verb.\n${usage()}`);
82
89
  process.exit(2);
83
90
  }
91
+ // CLAUDE_PLUGIN_ROOT stays the design *plugin* dir (plugins/design) — that's
92
+ // its true meaning, and it remains 2 levels under pkgRoot so the helpers'
93
+ // `$PLUGIN_ROOT/../../cli` side-channel + `$PLUGIN_ROOT/../..` pkg-root math
94
+ // still resolve. The bin scripts themselves now live under apps/studio/bin
95
+ // (DDR-095) and resolve the dev-server tree via $SCRIPT_DIR/.. regardless.
84
96
  const pluginRoot = join(pkgRoot, 'plugins', 'design');
85
- const script = join(pluginRoot, 'dev-server', 'bin', `${verb}.sh`);
97
+ const script = join(pkgRoot, 'apps', 'studio', 'bin', `${verb}.sh`);
86
98
  if (!existsSync(script)) {
87
99
  process.stderr.write(
88
- `maude design ${verb}: helper not found at ${script}. Reinstall maude (the dev-server bin ships in the npm package).\n`
100
+ `maude design ${verb}: helper not found at ${script}. Reinstall maude (the studio bin ships in the npm package).\n`
89
101
  );
90
102
  process.exit(1);
91
103
  }
92
104
  const rest = args.slice(args.indexOf(verb) + 1); // everything after the verb token
105
+ const childEnv = { ...process.env, CLAUDE_PLUGIN_ROOT: pluginRoot };
106
+ // For verbs that boot the dev-server, resolve the compiled platform binary the
107
+ // SAME way `maude design serve` does and hand the path down. The binary embeds
108
+ // yjs + every runtime dep; `bun server.ts` from source does NOT (the deps live
109
+ // in a nested package.json the npm tarball excludes), so on a production install
110
+ // server.ts crashes at boot on `yjs`. server-up.sh prefers MAUDE_DEV_SERVER_BIN
111
+ // when set; absent ⇒ dev tree ⇒ it falls back to `bun server.ts`. Helpers that
112
+ // shell into server-up.sh (visual-sanity, smoke) inherit it. DDR-084.
113
+ if (BOOT_VERBS.has(verb) && !process.env.MAUDE_DEV_SERVER_BIN) {
114
+ const bin = resolveServerBinary({ pkgRoot });
115
+ if (bin) childEnv.MAUDE_DEV_SERVER_BIN = bin;
116
+ }
93
117
  const child = spawnSync('bash', [script, ...rest], {
94
118
  stdio: 'inherit',
95
- env: { ...process.env, CLAUDE_PLUGIN_ROOT: pluginRoot },
119
+ env: childEnv,
96
120
  });
97
121
  if (child.error) {
98
122
  process.stderr.write(`maude design ${verb}: ${child.error.message}\n`);
@@ -110,7 +134,7 @@ Lifecycle:
110
134
  Dev-tooling (dispatch to the dev-server bash helpers — DDR-062):
111
135
  screenshot · server-up · prep · slug · bootstrap-check · runtime-health
112
136
  smoke · canvas-edit · handoff · asset-sweep · visual-sanity
113
- draw-build · draw-proof · svg-optimize
137
+ draw-build · draw-proof · svg-optimize · to-lottie · read-annotations
114
138
  Invoke the bundled helper of the same name. maude resolves it from its
115
139
  own package root and sets CLAUDE_PLUGIN_ROOT for the child; stdout,
116
140
  stderr, and exit code pass straight through (so command-substitution
@@ -123,7 +147,8 @@ Dev-tooling (dispatch to the dev-server bash helpers — DDR-062):
123
147
 
124
148
  serve [--port N] [--root PATH]
125
149
  Start the design plugin's dev server in the current repo. Equivalent
126
- to invoking 'claude-design-server'. Forwards all remaining args.
150
+ to 'maude studio' (top-level alias) or invoking 'claude-design-server'.
151
+ Forwards all remaining args.
127
152
 
128
153
  init [--name <slug>] [--ds <name>] [--force] [--dry-run]
129
154
  [--no-discovery | --discovery-payload <path>]
@@ -242,8 +267,8 @@ async function runServe({ args, pkgRoot }) {
242
267
  process.exit(1);
243
268
  }
244
269
 
245
- const tsEntry = resolve(pkgRoot, 'plugins', 'design', 'dev-server', 'server.ts');
246
- const mjsEntry = resolve(pkgRoot, 'plugins', 'design', 'dev-server', 'server.mjs');
270
+ const tsEntry = resolve(pkgRoot, 'apps', 'studio', 'server.ts');
271
+ const mjsEntry = resolve(pkgRoot, 'apps', 'studio', 'server.mjs');
247
272
 
248
273
  const hasBun = await new Promise((res) => {
249
274
  const probe = spawn('bun', ['--version'], { stdio: 'ignore' });
@@ -295,6 +320,55 @@ function detectPlatformSlug() {
295
320
  return null;
296
321
  }
297
322
 
323
+ // Read-only resolution of the compiled platform binary the dev-server should
324
+ // boot as in a production install (it embeds yjs + every runtime dep). Mirrors
325
+ // runServe's order — side-channel cache (postinstall) → lazy resolve of the
326
+ // @1agh/maude-<slug> sibling/nested package — but performs NO caching and NEVER
327
+ // hard-fails: returns the path, or null when none is found. A null result means
328
+ // "fall back to `bun server.ts` from source" (correct in the local dev tree).
329
+ // Honors MAUDE_FORCE_SOURCE=1 so maintainers hacking on the dev-server source
330
+ // still boot their working copy. Used by runBinDispatch to hand the path to
331
+ // server-up.sh; runServe keeps its own inline copy (it also caches + hard-fails).
332
+ // Structural allowlist for a path we're about to spawn (DDR-084 hardening). The
333
+ // compiled binary is ALWAYS named `maude` / `maude.exe` inside a `maude-<slug>/`
334
+ // dir (the @1agh/maude-<slug> package, or the dev-tree `packages/maude-<slug>/`).
335
+ // The side-channel file's content is the one attacker-influenceable input in a
336
+ // shared-prefix / poisoned-clone layout — a path outside this shape is ignored
337
+ // (caller falls back to source), so the file can only DENY the binary, never
338
+ // redirect the spawn to an arbitrary executable. Paths `lazyResolveBinary`
339
+ // constructs always conform, so this never rejects a legit resolve.
340
+ export function isPlausiblePlatformBinary(p) {
341
+ if (!p) return false;
342
+ const b = basename(p);
343
+ if (b !== 'maude' && b !== 'maude.exe') return false;
344
+ return /^maude-[a-z0-9-]+$/.test(basename(dirname(p)));
345
+ }
346
+
347
+ export function resolveServerBinary({ pkgRoot }) {
348
+ if (process.env.MAUDE_FORCE_SOURCE === '1') return null;
349
+ // In the local source checkout, return null so server-up.sh boots
350
+ // `bun server.ts` from source — the maintainer is editing the dev-server and
351
+ // needs their working copy live, not a stale compiled binary that happens to
352
+ // be installed via pnpm. A production install has no `packages/` dir, so this
353
+ // never short-circuits there. (Preserves server-up's historic dev-tree
354
+ // behavior — it always ran source before DDR-084.)
355
+ if (isLocalDevTree(pkgRoot, { existsSync })) return null;
356
+ const sideChannel = resolve(pkgRoot, 'cli', '.platform-binary-path');
357
+ try {
358
+ if (existsSync(sideChannel)) {
359
+ const candidate = readFileSync(sideChannel, 'utf8').trim();
360
+ // Existence AND the structural allowlist — a poisoned side-channel pointing
361
+ // at an arbitrary executable is ignored (falls through to lazy resolve).
362
+ if (candidate && existsSync(candidate) && isPlausiblePlatformBinary(candidate)) {
363
+ return candidate;
364
+ }
365
+ }
366
+ } catch {
367
+ /* fall through to lazy resolve */
368
+ }
369
+ return lazyResolveBinary({ pkgRoot, fs: { existsSync, chmodSync } }).binPath;
370
+ }
371
+
298
372
  function lazyResolveBinary({ pkgRoot, fs }) {
299
373
  const slug = detectPlatformSlug();
300
374
  if (!slug) return { binPath: null, slug: null };
@@ -348,7 +422,7 @@ function checkDevDeps({ pkgRoot }) {
348
422
  // with a less actionable error.
349
423
  const required = ['magic-string', 'oxc-parser'];
350
424
  const missing = [];
351
- const paths = [resolve(pkgRoot, 'plugins', 'design', 'dev-server'), resolve(pkgRoot)];
425
+ const paths = [resolve(pkgRoot, 'apps', 'studio'), resolve(pkgRoot)];
352
426
  for (const dep of required) {
353
427
  try {
354
428
  require.resolve(dep, { paths });
@@ -8,11 +8,12 @@
8
8
 
9
9
  import assert from 'node:assert/strict';
10
10
  import { spawnSync } from 'node:child_process';
11
- import { mkdtempSync, rmSync } from 'node:fs';
11
+ import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
12
12
  import { tmpdir } from 'node:os';
13
13
  import { dirname, join, resolve } from 'node:path';
14
14
  import { test } from 'node:test';
15
15
  import { fileURLToPath } from 'node:url';
16
+ import { isPlausiblePlatformBinary, resolveServerBinary } from './design.mjs';
16
17
 
17
18
  const BIN = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'maude.mjs');
18
19
 
@@ -54,3 +55,134 @@ test('design help lists the dev-tooling verbs', () => {
54
55
  assert.match(res.stdout, new RegExp(verb), `usage should mention ${verb}`);
55
56
  }
56
57
  });
58
+
59
+ // resolveServerBinary — the dev-server boot-runtime resolver (DDR-084). server-up
60
+ // must boot the compiled platform binary on a production install (it embeds yjs +
61
+ // every dep; `bun server.ts` from source can't resolve them — the historic
62
+ // yjs-at-boot crash), but keep using SOURCE in the local dev tree so a maintainer's
63
+ // edits are live.
64
+ function fakePkgRoot({ devTree = false, sideChannelBin = null } = {}) {
65
+ const root = mkdtempSync(join(tmpdir(), 'maude-pkgroot-'));
66
+ if (devTree) {
67
+ mkdirSync(join(root, 'packages', 'maude-darwin-arm64'), { recursive: true });
68
+ writeFileSync(join(root, 'packages', 'maude-darwin-arm64', 'package.json'), '{}');
69
+ }
70
+ if (sideChannelBin) {
71
+ mkdirSync(join(root, 'cli'), { recursive: true });
72
+ writeFileSync(join(root, 'cli', '.platform-binary-path'), sideChannelBin);
73
+ }
74
+ return root;
75
+ }
76
+
77
+ test('resolveServerBinary → null in the local dev tree (packages/ present) — boots source', () => {
78
+ const root = fakePkgRoot({ devTree: true });
79
+ try {
80
+ delete process.env.MAUDE_FORCE_SOURCE;
81
+ assert.equal(resolveServerBinary({ pkgRoot: root }), null);
82
+ } finally {
83
+ rmSync(root, { recursive: true, force: true });
84
+ }
85
+ });
86
+
87
+ // Build an executable in a layout the allowlist accepts (`maude-<slug>/maude`)
88
+ // or rejects (anything else), under a throwaway dir.
89
+ function fakeBinary({ conforming = true } = {}) {
90
+ const home = mkdtempSync(join(tmpdir(), 'maude-bin-'));
91
+ const dir = join(home, conforming ? 'maude-darwin-arm64' : 'evil');
92
+ mkdirSync(dir, { recursive: true });
93
+ const bin = join(dir, 'maude');
94
+ writeFileSync(bin, '#!/bin/sh\n');
95
+ chmodSync(bin, 0o755);
96
+ return { home, bin };
97
+ }
98
+
99
+ test('resolveServerBinary → null when MAUDE_FORCE_SOURCE=1 (maintainer override)', () => {
100
+ const { home, bin } = fakeBinary();
101
+ const root = fakePkgRoot({ sideChannelBin: bin }); // NOT a dev tree
102
+ const prev = process.env.MAUDE_FORCE_SOURCE;
103
+ try {
104
+ process.env.MAUDE_FORCE_SOURCE = '1';
105
+ assert.equal(resolveServerBinary({ pkgRoot: root }), null);
106
+ } finally {
107
+ if (prev === undefined) delete process.env.MAUDE_FORCE_SOURCE;
108
+ else process.env.MAUDE_FORCE_SOURCE = prev;
109
+ rmSync(root, { recursive: true, force: true });
110
+ rmSync(home, { recursive: true, force: true });
111
+ }
112
+ });
113
+
114
+ test('resolveServerBinary → side-channel path on a production install (no packages/)', () => {
115
+ const { home, bin } = fakeBinary(); // conforming maude-<slug>/maude layout
116
+ const root = fakePkgRoot({ sideChannelBin: bin }); // production shape
117
+ const prev = process.env.MAUDE_FORCE_SOURCE;
118
+ try {
119
+ delete process.env.MAUDE_FORCE_SOURCE;
120
+ assert.equal(resolveServerBinary({ pkgRoot: root }), bin);
121
+ } finally {
122
+ if (prev !== undefined) process.env.MAUDE_FORCE_SOURCE = prev;
123
+ rmSync(root, { recursive: true, force: true });
124
+ rmSync(home, { recursive: true, force: true });
125
+ }
126
+ });
127
+
128
+ test('resolveServerBinary → REJECTS a side-channel pointing outside the maude-<slug>/ layout (DDR-084 allowlist)', () => {
129
+ const { home, bin } = fakeBinary({ conforming: false }); // <tmp>/evil/maude
130
+ const root = fakePkgRoot({ sideChannelBin: bin }); // production shape, no real platform pkg
131
+ const prev = process.env.MAUDE_FORCE_SOURCE;
132
+ try {
133
+ delete process.env.MAUDE_FORCE_SOURCE;
134
+ // Poisoned side-channel is ignored → falls through to lazyResolve, which finds
135
+ // nothing in the fake root → null (server-up then boots source, not the planted bin).
136
+ assert.equal(resolveServerBinary({ pkgRoot: root }), null);
137
+ } finally {
138
+ if (prev !== undefined) process.env.MAUDE_FORCE_SOURCE = prev;
139
+ rmSync(root, { recursive: true, force: true });
140
+ rmSync(home, { recursive: true, force: true });
141
+ }
142
+ });
143
+
144
+ test('isPlausiblePlatformBinary — accepts maude-<slug>/maude, rejects everything else', () => {
145
+ assert.equal(isPlausiblePlatformBinary('/x/node_modules/@1agh/maude-darwin-arm64/maude'), true);
146
+ assert.equal(isPlausiblePlatformBinary('/x/packages/maude-linux-x64-musl/maude'), true);
147
+ assert.equal(isPlausiblePlatformBinary('/x/maude-win32-x64/maude.exe'), true);
148
+ assert.equal(isPlausiblePlatformBinary('/tmp/evil/maude'), false); // parent not maude-<slug>
149
+ assert.equal(isPlausiblePlatformBinary('/x/maude-darwin-arm64/evil'), false); // basename not maude
150
+ assert.equal(isPlausiblePlatformBinary(''), false);
151
+ assert.equal(isPlausiblePlatformBinary(null), false);
152
+ });
153
+
154
+ // DDR-095 — `maude studio` is a top-level alias for `maude design serve`. Both
155
+ // must resolve the same server-entry path. We prove parity by booting each
156
+ // against a temp dir with no .design/: the dev-server fails loud (exit 1) with
157
+ // an identical first-line signature, which only happens if `studio` routed
158
+ // through design-serve resolution (`apps/studio/server.{ts,mjs}` / the binary).
159
+ test('`maude studio` aliases `maude design serve` (identical boot resolution)', () => {
160
+ const BIN_PATH = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'maude.mjs');
161
+ function bootAgainstEmpty(argv) {
162
+ const cwd = mkdtempSync(join(tmpdir(), 'maude-studio-'));
163
+ try {
164
+ const r = spawnSync(process.execPath, [BIN_PATH, ...argv, '--root', cwd], {
165
+ encoding: 'utf8',
166
+ timeout: 30000,
167
+ });
168
+ return { status: r.status, firstErr: (r.stderr || '').split('\n').find(Boolean) ?? '' };
169
+ } finally {
170
+ rmSync(cwd, { recursive: true, force: true });
171
+ }
172
+ }
173
+ const viaDesign = bootAgainstEmpty(['design', 'serve']);
174
+ const viaStudio = bootAgainstEmpty(['studio']);
175
+ // Both fail loud (no .design/) — same exit code, same first-line diagnostic.
176
+ assert.notEqual(viaStudio.status, 0, `studio should not boot cleanly: ${viaStudio.firstErr}`);
177
+ assert.equal(viaStudio.status, viaDesign.status);
178
+ assert.equal(viaStudio.firstErr, viaDesign.firstErr);
179
+ });
180
+
181
+ test('`maude studio` is recognized (not an unknown command)', () => {
182
+ const BIN_PATH = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'maude.mjs');
183
+ const r = spawnSync(process.execPath, [BIN_PATH, 'studio', '--root', '/nonexistent-xyz'], {
184
+ encoding: 'utf8',
185
+ timeout: 30000,
186
+ });
187
+ assert.doesNotMatch(r.stderr || '', /unknown command/);
188
+ });
@@ -29,9 +29,14 @@ COMMANDS
29
29
  contract — see DDR-061); 'list' shows layers + sizes; 'stats' shows
30
30
  hit-rate; 'inspect' pretty-prints entries; 'clear' wipes a layer or all.
31
31
 
32
+ studio [--port N] [--root PATH]
33
+ Alias for 'maude design serve' — boot the canvas studio (the design
34
+ dev server) in the current repo. Forwards all remaining args.
35
+
32
36
  design serve [--port N] [--root PATH]
33
37
  Start the design plugin's dev server in the current repo. Equivalent
34
- to invoking 'claude-design-server'. Forwards all remaining args.
38
+ to 'maude studio' / invoking 'claude-design-server'. Forwards all
39
+ remaining args.
35
40
 
36
41
  design init [--name <slug>] [--ds <name>] [--force] [--dry-run]
37
42
  [--no-discovery | --discovery-payload <path>]
@@ -43,7 +48,7 @@ COMMANDS
43
48
 
44
49
  design <screenshot|server-up|prep|slug|bootstrap-check|runtime-health
45
50
  |smoke|canvas-edit|handoff|asset-sweep|visual-sanity> [args]
46
- Dispatch to the dev-server's bundled bash helper of the same name.
51
+ Dispatch to the studio's bundled bash helper of the same name.
47
52
  maude resolves it from its own package root and sets CLAUDE_PLUGIN_ROOT
48
53
  for the child; stdout/stderr/exit-code pass straight through. This is
49
54
  the contract plugin markdown uses instead of a raw bin path (DDR-062).
@@ -88,6 +93,7 @@ EXAMPLES
88
93
  maude config get motion.complex
89
94
  maude cache list
90
95
  maude cache clear research/domain
96
+ maude studio --port 4399
91
97
  maude design serve --port 4399
92
98
  maude design init --no-discovery --name acme-app
93
99
  maude design slug "Some Canvas Name"