@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
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file canvas-lib.tsx — dev-server-bundled canvas library
3
- * @scope plugins/design/dev-server/canvas-lib.tsx
3
+ * @scope apps/studio/canvas-lib.tsx
4
4
  * Ships with the dev-server install; resolved at canvas build time
5
5
  * via the `@maude/canvas-lib` virtual specifier. Per DDR-025, this
6
6
  * is the single source of truth — no project-side copy.
@@ -137,10 +137,9 @@ const ENGINE_CSS = `
137
137
  /* Canvas-shell chrome — the workspace plane + dotted grid follow the Maude
138
138
  chrome theme (--maude-chrome-*), NOT the DS palette. See canvas-shell.tsx
139
139
  HUD_TOKENS_CSS. Artboards (.dc-artboard) keep the DS theme. */
140
- background-color: var(--maude-chrome-bg-1, #f4f1ea);
140
+ background-color: var(--maude-chrome-bg-0, #f4f1ea);
141
141
  background-image:
142
- linear-gradient(var(--maude-chrome-border, rgba(0,0,0,0.08)) 1px, transparent 1px),
143
- linear-gradient(90deg, var(--maude-chrome-border, rgba(0,0,0,0.08)) 1px, transparent 1px);
142
+ radial-gradient(var(--maude-chrome-dot, rgba(0,0,0,0.12)) 1.1px, transparent 1.2px);
144
143
  background-size: 24px 24px;
145
144
  }
146
145
  /* DDR-046 — Snap guides. Sibling kind = confident magenta + glow + distance
@@ -199,6 +198,17 @@ const ENGINE_CSS = `
199
198
  irrelevant under zoom (zoom anchors top-left of the box). will-change
200
199
  hints to the compositor that this layer changes often. */
201
200
  will-change: transform;
201
+ /* Marquee / multi-select drags over artboards must not trigger native text
202
+ selection (it highlighted the canvas content). Editable surfaces re-enable
203
+ it below. */
204
+ user-select: none;
205
+ -webkit-user-select: none;
206
+ }
207
+ .dc-world input,
208
+ .dc-world textarea,
209
+ .dc-world [contenteditable="true"] {
210
+ user-select: text;
211
+ -webkit-user-select: text;
202
212
  }
203
213
  .dc-section-collapsed { display: contents; }
204
214
 
@@ -1162,7 +1172,7 @@ export function useDragStateContext(): DragStateBus | null {
1162
1172
  interface DesignCanvasProps {
1163
1173
  children: ReactNode;
1164
1174
  /** Per-overlay opt-out. `false` hides it; omit or `true` shows it. */
1165
- controls?: { minimap?: boolean; toolbar?: boolean };
1175
+ controls?: { minimap?: boolean; toolbar?: boolean; zoom?: boolean };
1166
1176
  }
1167
1177
 
1168
1178
  /**
@@ -1374,6 +1384,7 @@ function DesignCanvasInner({ children, controls }: DesignCanvasProps) {
1374
1384
  );
1375
1385
 
1376
1386
  const showMiniMap = controls?.minimap !== false;
1387
+ const showZoom = controls?.zoom !== false;
1377
1388
 
1378
1389
  // Drag-state bus (Phase 4.2). Single source of truth: only one artboard
1379
1390
  // drag is active at a time. DCArtboards write here when their local drag
@@ -1445,10 +1456,12 @@ function DesignCanvasInner({ children, controls }: DesignCanvasProps) {
1445
1456
  {children}
1446
1457
  </div>
1447
1458
  {showMiniMap ? <DCMiniMap /> : null}
1448
- {/* DCZoomToolbar is intentionally not rendered here. The Phase 5.1
1449
- ToolPalette absorbs its 4 actions into the unified canvas chrome.
1450
- The component stays exported for back-compat with any consumer that
1451
- still imports it directly. */}
1459
+ {/* Plan C F6 separate bottom-left zoom pill (the design's ZoomHud).
1460
+ Phase 5.1 had folded zoom into the ToolPalette; user feedback wants
1461
+ the design's standalone pill, so DCZoomToolbar renders here again (the
1462
+ ToolPalette's inline zoom is removed in tool-palette.tsx). It carries
1463
+ MORE than the mock — fit + actual-size — so no capability is lost. */}
1464
+ {showZoom ? <DCZoomToolbar /> : null}
1452
1465
  </div>
1453
1466
  );
1454
1467
 
@@ -1900,6 +1913,9 @@ const OVERLAY_CSS = `
1900
1913
  overflow: hidden;
1901
1914
  }
1902
1915
  .dc-mm-hd {
1916
+ display: flex;
1917
+ align-items: center;
1918
+ justify-content: space-between;
1903
1919
  padding: 5px 8px 4px;
1904
1920
  border-bottom: 1px solid var(--maude-chrome-border, rgba(0,0,0,0.08));
1905
1921
  letter-spacing: 0.05em;
@@ -1907,6 +1923,7 @@ const OVERLAY_CSS = `
1907
1923
  font-size: 9px;
1908
1924
  background: var(--maude-chrome-bg-1, #f4f1ea);
1909
1925
  }
1926
+ .dc-mm-count { font-variant-numeric: tabular-nums; color: var(--maude-chrome-fg-2, rgba(40,30,20,0.55)); }
1910
1927
  .dc-mm-body {
1911
1928
  position: relative;
1912
1929
  width: 100%;
@@ -1932,9 +1949,8 @@ const OVERLAY_CSS = `
1932
1949
  }
1933
1950
  .dc-zoom-tb {
1934
1951
  position: absolute;
1935
- left: 50%;
1952
+ left: 16px;
1936
1953
  bottom: 16px;
1937
- transform: translateX(-50%);
1938
1954
  display: flex;
1939
1955
  align-items: stretch;
1940
1956
  background: var(--maude-chrome-bg-0, #ffffff);
@@ -2105,7 +2121,10 @@ export function DCMiniMap() {
2105
2121
  return (
2106
2122
  <div className="dc-mm" aria-hidden="true">
2107
2123
  <div className="dc-mm-hd">
2108
- WORLD MAP · {world.artboards.length}/{world.artboards.length}
2124
+ <span>World</span>
2125
+ <span className="dc-mm-count">
2126
+ {world.artboards.length} / {world.artboards.length}
2127
+ </span>
2109
2128
  </div>
2110
2129
  <div
2111
2130
  className="dc-mm-body"
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file canvas-shell.tsx — universal input-grammar wrapper for TSX canvases
3
- * @scope plugins/design/dev-server/canvas-shell.tsx
3
+ * @scope apps/studio/canvas-shell.tsx
4
4
  * @purpose Mounted by `DesignCanvas` for every canvas. Stacks
5
5
  * SelectionSetProvider + ContextMenuProvider, wires the input
6
6
  * router to provider actions, and renders the floating chrome
@@ -59,12 +59,14 @@ import {
59
59
  import { ContextualToolbar } from './contextual-toolbar.tsx';
60
60
  import { CursorsOverlay } from './cursors-overlay.tsx';
61
61
  import {
62
- cssEscape,
63
62
  cssPath,
64
63
  deriveFile,
65
64
  domPath,
66
65
  hoverTargetToSelection,
67
66
  realClasses,
67
+ resolveSelectionEl,
68
+ scopedCdSelector,
69
+ selectorIndex,
68
70
  shortText,
69
71
  } from './dom-selection.ts';
70
72
  import { EqualSpacingHandles } from './equal-spacing-handles.tsx';
@@ -129,32 +131,46 @@ import { useUndoStack } from './use-undo-stack.tsx';
129
131
  // theme (readInitialTheme() → 'dark'). DDR — mirrors the `--maude-hud-*`
130
132
  // precedent; the `data-maude-theme` attribute is deliberately SEPARATE from the
131
133
  // DS `data-theme` so chrome theming never touches artboard palettes.
134
+ // Plan B (Task 7) — the in-canvas chrome now mirrors the maude DS ladder (indigo
135
+ // accent hue 268, cool-neutral hue 255) so the iframe chrome reads as one product
136
+ // with the rewritten outer shell. Accent-fg is the maude dark navy (NOT white):
137
+ // maude's bright indigo accent needs a dark fg for WCAG contrast (white/indigo
138
+ // ≈ 3:1, fails AA; navy/indigo ≈ 6.3:1). Token-only swap — no overlay logic
139
+ // changes (DDR-054). Values lifted from `.design/system/maude/colors_and_type.css`.
132
140
  const HUD_TOKENS_CSS = `
133
141
  :root,
134
142
  :root[data-maude-theme="dark"] {
135
- --maude-hud-accent: #d63b1f;
136
- --maude-hud-accent-hover: #b8331b;
137
- --maude-hud-accent-active: #962a16;
138
- --maude-hud-accent-fg: #ffffff;
139
- --maude-hud-accent-tint: color-mix(in oklab, #d63b1f 14%, transparent);
140
-
141
- --maude-chrome-bg-0: oklch(13% 0.012 60);
142
- --maude-chrome-bg-1: oklch(17% 0.014 60);
143
- --maude-chrome-bg-2: oklch(20% 0.016 60);
144
- --maude-chrome-fg-0: oklch(94% 0.014 80);
145
- --maude-chrome-fg-1: oklch(78% 0.014 80);
146
- --maude-chrome-border: oklch(28% 0.018 60);
147
- --maude-chrome-shadow: rgba(0, 0, 0, 0.45);
148
- --maude-chrome-font-mono: 'Berkeley Mono', 'TX-02', 'JetBrains Mono', 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
143
+ --maude-hud-accent: oklch(0.680 0.180 268);
144
+ --maude-hud-accent-hover: oklch(0.730 0.170 268);
145
+ --maude-hud-accent-active: oklch(0.630 0.180 268);
146
+ --maude-hud-accent-fg: oklch(0.180 0.030 268);
147
+ --maude-hud-accent-tint: color-mix(in oklab, oklch(0.680 0.180 268) 16%, transparent);
148
+
149
+ --maude-chrome-bg-0: oklch(0.165 0.012 255);
150
+ --maude-chrome-bg-1: oklch(0.198 0.012 255);
151
+ --maude-chrome-bg-2: oklch(0.232 0.013 255);
152
+ --maude-chrome-fg-0: oklch(0.955 0.005 250);
153
+ --maude-chrome-fg-1: oklch(0.790 0.008 250);
154
+ --maude-chrome-border: oklch(0.290 0.012 255);
155
+ --maude-chrome-dot: oklch(0.340 0.012 255);
156
+ --maude-chrome-shadow: rgba(0, 0, 0, 0.46);
157
+ --maude-chrome-font-mono: 'JetBrains Mono', 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;
149
158
  }
150
159
  :root[data-maude-theme="light"] {
151
- --maude-chrome-bg-0: oklch(97.5% 0.008 78);
152
- --maude-chrome-bg-1: oklch(95.5% 0.010 78);
153
- --maude-chrome-bg-2: oklch(93.0% 0.012 78);
154
- --maude-chrome-fg-0: oklch(20% 0.020 50);
155
- --maude-chrome-fg-1: oklch(38% 0.018 50);
156
- --maude-chrome-border: oklch(86% 0.014 70);
157
- --maude-chrome-shadow: color-mix(in oklab, oklch(20% 0.020 50) 14%, transparent);
160
+ --maude-hud-accent: oklch(0.520 0.195 268);
161
+ --maude-hud-accent-hover: oklch(0.470 0.195 268);
162
+ --maude-hud-accent-active: oklch(0.430 0.190 268);
163
+ --maude-hud-accent-fg: oklch(0.995 0.004 268);
164
+ --maude-hud-accent-tint: color-mix(in oklab, oklch(0.520 0.195 268) 12%, transparent);
165
+
166
+ --maude-chrome-bg-0: oklch(0.975 0.004 255);
167
+ --maude-chrome-bg-1: oklch(1.000 0 0);
168
+ --maude-chrome-bg-2: oklch(0.987 0.003 255);
169
+ --maude-chrome-fg-0: oklch(0.225 0.015 260);
170
+ --maude-chrome-fg-1: oklch(0.400 0.014 260);
171
+ --maude-chrome-border: oklch(0.922 0.005 255);
172
+ --maude-chrome-dot: oklch(0.860 0.008 255);
173
+ --maude-chrome-shadow: color-mix(in oklab, oklch(0.225 0.015 260) 14%, transparent);
158
174
  }
159
175
  `;
160
176
 
@@ -286,6 +302,14 @@ const HALO_CSS = `
286
302
  -webkit-font-smoothing: subpixel-antialiased;
287
303
  font-smooth: always;
288
304
  }
305
+ /* Phase 12 (DDR-103) — inline text editing: the element being edited carries an
306
+ accent ring + caret. plaintext-only contenteditable; commit writes to source. */
307
+ [contenteditable].dc-text-editing {
308
+ outline: 2px solid var(--maude-hud-accent, #0d99ff);
309
+ outline-offset: 1px;
310
+ border-radius: 1px;
311
+ cursor: text;
312
+ }
289
313
  `.trim();
290
314
 
291
315
  function ensureHaloStyles(): void {
@@ -306,6 +330,104 @@ function ensureHaloStyles(): void {
306
330
  document.head.appendChild(s);
307
331
  }
308
332
 
333
+ // ─────────────────────────────────────────────────────────────────────────────
334
+ // Layers tree (Phase 12 Task 4, DDR-103). The shell's Layers tab is a browsable
335
+ // tree of the active artboard's stamped (`data-cd-id`) elements. We walk the
336
+ // artboard subtree in the iframe, serialize a nested tree, and post it to the
337
+ // shell; the shell renders it + posts back `select-by-id` / `highlight`.
338
+
339
+ interface LayerNode {
340
+ id: string;
341
+ tag: string;
342
+ label: string;
343
+ type: string;
344
+ /** Occurrence index of this id within the artboard — matches the resolver's
345
+ * `querySelectorAll([data-dc-screen=…] [data-cd-id=…])[index]`. */
346
+ index: number;
347
+ children: LayerNode[];
348
+ }
349
+
350
+ function kebabToTitle(s: string): string {
351
+ return s
352
+ .split('-')
353
+ .filter(Boolean)
354
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
355
+ .join(' ');
356
+ }
357
+
358
+ // Human label: data-dc-element (Title Case) → aria-label → role → tag(.class).
359
+ function layerLabel(el: Element): string {
360
+ const dc = el.getAttribute('data-dc-element');
361
+ if (dc) return kebabToTitle(dc);
362
+ const aria = el.getAttribute('aria-label');
363
+ if (aria) return aria.slice(0, 40);
364
+ const role = el.getAttribute('role');
365
+ if (role) return role;
366
+ const tag = el.tagName.toLowerCase();
367
+ const cls = realClasses(el).split(/\s+/).filter(Boolean)[0];
368
+ return cls ? `${tag}.${cls}` : tag;
369
+ }
370
+
371
+ function layerType(el: Element): string {
372
+ const tag = el.tagName.toLowerCase();
373
+ const role = el.getAttribute('role') ?? '';
374
+ const dc = el.getAttribute('data-dc-element') ?? '';
375
+ if (tag === 'button' || role === 'button' || /(^|-)(btn|cta|button)(-|$)/.test(dc))
376
+ return 'button';
377
+ if (/^h[1-6]$/.test(tag) || role === 'heading') return 'heading';
378
+ if (tag === 'input' || tag === 'textarea' || tag === 'select') return 'input';
379
+ if (tag === 'img' || tag === 'svg' || tag === 'picture' || tag === 'video') return 'image';
380
+ if (tag === 'a') return 'link';
381
+ if (tag === 'ul' || tag === 'ol' || tag === 'li') return 'list';
382
+ if (tag === 'nav') return 'nav';
383
+ if (tag === 'form') return 'form';
384
+ if (tag === 'p' || tag === 'span' || tag === 'label') return 'text';
385
+ return 'box';
386
+ }
387
+
388
+ function serializeArtboardTree(root: Element): LayerNode[] {
389
+ const seen = new Map<string, number>();
390
+ function walk(el: Element): LayerNode[] {
391
+ const out: LayerNode[] = [];
392
+ for (const child of Array.from(el.children)) {
393
+ // Skip dev-server overlay chrome (pins, halos) that lives inside the artboard.
394
+ if (child.closest('.dgn-pin, .dc-cv-halo, .dc-cv-group-bbox')) continue;
395
+ const cd = child.getAttribute('data-cd-id');
396
+ if (cd) {
397
+ const idx = seen.get(cd) ?? 0;
398
+ seen.set(cd, idx + 1);
399
+ out.push({
400
+ id: cd,
401
+ tag: child.tagName.toLowerCase(),
402
+ label: layerLabel(child),
403
+ type: layerType(child),
404
+ index: idx,
405
+ children: walk(child),
406
+ });
407
+ } else {
408
+ // Unstamped wrapper — descend so its stamped descendants still surface.
409
+ out.push(...walk(child));
410
+ }
411
+ }
412
+ return out;
413
+ }
414
+ return walk(root);
415
+ }
416
+
417
+ function postLayersTree(artboardId: string | null | undefined): void {
418
+ if (typeof document === 'undefined' || !artboardId) return;
419
+ const root = document.querySelector(`[data-dc-screen="${CSS.escape(artboardId)}"]`);
420
+ if (!root) return;
421
+ try {
422
+ window.parent.postMessage(
423
+ { dgn: 'layers-tree', artboardId, tree: serializeArtboardTree(root) },
424
+ '*'
425
+ );
426
+ } catch {
427
+ /* detached / cross-origin */
428
+ }
429
+ }
430
+
309
431
  // ─────────────────────────────────────────────────────────────────────────────
310
432
  // Shell
311
433
 
@@ -418,7 +540,7 @@ function CanvasCore({
418
540
  if (!host) return;
419
541
  const onDbl = (e: MouseEvent) => {
420
542
  const t = e.target as Element | null;
421
- if (!t || !t.closest) return;
543
+ if (!t?.closest) return;
422
544
  // Floating chrome / overlays / drawn user content / any artboard
423
545
  // surface → leave alone. Only dblclick that lands on the canvas
424
546
  // background outside every artboard triggers `fit()`.
@@ -501,7 +623,7 @@ function CanvasCore({
501
623
  useEffect(() => {
502
624
  if (!collab) return;
503
625
  const first = selSet.selected[0];
504
- if (!first || !first.selector) {
626
+ if (!first?.selector) {
505
627
  collab.publishAwareness({ selection: null });
506
628
  return;
507
629
  }
@@ -931,7 +1053,7 @@ function applyArtboardFollowers(): void {
931
1053
  function buildRegistry(deps: {
932
1054
  controller: ViewportControllerHandle | null;
933
1055
  clearSelection: () => void;
934
- selSet: { selected: Selection[] };
1056
+ selSet: { selected: Selection[]; replace: (s: Selection | Selection[]) => void };
935
1057
  distributeArtboards: (axis: 'x' | 'y') => void;
936
1058
  alignArtboards: (mode: 'left' | 'right' | 'center-x' | 'top' | 'bottom' | 'center-y') => void;
937
1059
  focusArtboard: (artboardId: string) => void;
@@ -956,12 +1078,16 @@ function buildRegistry(deps: {
956
1078
 
957
1079
  const postComposeForTarget = (target: ContextTarget): void => {
958
1080
  if (typeof window === 'undefined') return;
1081
+ const composeSelector = target.cdId
1082
+ ? scopedCdSelector(target.cdId, target.artboardId)
1083
+ : cssPath(target.el);
959
1084
  const sel: Selection | null = target.el
960
1085
  ? {
961
1086
  file: deriveFile(),
962
1087
  id: target.cdId ?? undefined,
963
- selector: target.cdId ? `[data-cd-id="${target.cdId}"]` : cssPath(target.el),
1088
+ selector: composeSelector,
964
1089
  artboardId: target.artboardId,
1090
+ index: target.cdId ? selectorIndex(document, composeSelector, target.el) : 0,
965
1091
  tag: target.el.tagName.toLowerCase(),
966
1092
  classes: realClasses(target.el),
967
1093
  text: shortText(target.el, 240),
@@ -1089,9 +1215,24 @@ function buildRegistry(deps: {
1089
1215
  id: 'inspect',
1090
1216
  label: 'Inspect',
1091
1217
  shortcut: '⌥I',
1092
- disabled: true,
1093
- onSelect: () => {
1094
- console.warn('[context-menu] TODO: tweaks panel for TSX canvases');
1218
+ onSelect: (target) => {
1219
+ // Phase 12 — select the right-clicked element + open the shell's
1220
+ // right Inspector panel (Inspect / Layers / CSS). Was a disabled TODO
1221
+ // before the inspector shipped.
1222
+ if (target.el) {
1223
+ selSet.replace(
1224
+ hoverTargetToSelection({
1225
+ el: target.el,
1226
+ cdId: target.cdId ?? null,
1227
+ artboardId: target.artboardId ?? null,
1228
+ } as HoverTarget)
1229
+ );
1230
+ }
1231
+ try {
1232
+ window.parent.postMessage({ dgn: 'open-inspector' }, '*');
1233
+ } catch {
1234
+ /* detached / cross-origin */
1235
+ }
1095
1236
  },
1096
1237
  },
1097
1238
  ],
@@ -1228,6 +1369,8 @@ function CanvasRouter({
1228
1369
  const annotSel = useAnnotationSelection();
1229
1370
  const ctxMenu = useContextMenu();
1230
1371
  const undoStack = useUndoStack();
1372
+ // Shell View-menu zoom bridge (dgn:'zoom') — same controller the zoom pill uses.
1373
+ const zoomController = useViewportControllerContext();
1231
1374
 
1232
1375
  // Hover state drives the floating .dc-cv-halo--hover overlay. The overlay
1233
1376
  // itself reads getBoundingClientRect on every rAF tick to follow pan/zoom.
@@ -1283,6 +1426,96 @@ function CanvasRouter({
1283
1426
  if (typeof t === 'string') setTool(t as never);
1284
1427
  return;
1285
1428
  }
1429
+ // Plan C — Edit menu (shell) bridges to the in-canvas undo stack.
1430
+ if (m.dgn === 'undo') {
1431
+ void undoStack.undo();
1432
+ return;
1433
+ }
1434
+ if (m.dgn === 'redo') {
1435
+ void undoStack.redo();
1436
+ return;
1437
+ }
1438
+ // Phase 12 Task 4 (DDR-103) — Layers-tree round-trip. select-by-id resolves
1439
+ // the node + replaces the selection (which posts select-set back, updating
1440
+ // the Inspect/CSS tabs + halos); highlight shows the transient hover halo;
1441
+ // request-layers re-walks + posts the tree for an artboard.
1442
+ if (m.dgn === 'select-by-id') {
1443
+ const mm = m as { id?: string; artboardId?: string | null; index?: number };
1444
+ if (mm.id) {
1445
+ const target = resolveSelectionEl(document, {
1446
+ id: mm.id,
1447
+ artboardId: mm.artboardId,
1448
+ index: mm.index,
1449
+ });
1450
+ if (target) {
1451
+ selSet.replace(
1452
+ hoverTargetToSelection({
1453
+ el: target,
1454
+ cdId: mm.id,
1455
+ artboardId: mm.artboardId ?? null,
1456
+ } as HoverTarget)
1457
+ );
1458
+ (target as HTMLElement).scrollIntoView?.({ block: 'nearest', behavior: 'smooth' });
1459
+ }
1460
+ }
1461
+ return;
1462
+ }
1463
+ if (m.dgn === 'highlight') {
1464
+ const mm = m as { id?: string; artboardId?: string | null; index?: number };
1465
+ setHoverEl(
1466
+ mm.id
1467
+ ? resolveSelectionEl(document, {
1468
+ id: mm.id,
1469
+ artboardId: mm.artboardId,
1470
+ index: mm.index,
1471
+ })
1472
+ : null
1473
+ );
1474
+ return;
1475
+ }
1476
+ // Phase 12.3 (W1.1) — optimistic inline-style apply. The CSS panel posts
1477
+ // this on commit so the selected element updates INSTANTLY, before the
1478
+ // edit-css → file-watcher HMR reload re-renders from source. `value` null/
1479
+ // empty removes the property (the reset path). Purely a live-DOM preview;
1480
+ // the authoritative value is whatever the reload renders from source.
1481
+ // SECURITY (ethical-hacker A1/A2) — accept apply-style ONLY from the parent
1482
+ // shell, never from a canvas self-post. The canvas iframe is untrusted
1483
+ // (DDR-054); without this gate hostile canvas JS could repaint elements AND
1484
+ // (paired with the reload echo-guard) suppress reloads to desync view/source.
1485
+ // The reload-suppression timestamp now lives in _shell.html's closure (NOT a
1486
+ // canvas-writable window global) — set there from the same parent-gated msg.
1487
+ if (m.dgn === 'apply-style') {
1488
+ if (e.source !== window.parent) return;
1489
+ const mm = m as {
1490
+ id?: string;
1491
+ artboardId?: string | null;
1492
+ index?: number;
1493
+ prop?: string;
1494
+ value?: string | null;
1495
+ };
1496
+ if (mm.id && mm.prop) {
1497
+ const target = resolveSelectionEl(document, {
1498
+ id: mm.id,
1499
+ artboardId: mm.artboardId,
1500
+ index: mm.index,
1501
+ });
1502
+ if (target) {
1503
+ const style = (target as HTMLElement).style;
1504
+ try {
1505
+ // Live-DOM preview only; the reload renders the authoritative value.
1506
+ if (mm.value == null || mm.value === '') style.removeProperty(mm.prop);
1507
+ else style.setProperty(mm.prop, mm.value);
1508
+ } catch {
1509
+ /* invalid prop/value — ignore; the reload is the source of truth */
1510
+ }
1511
+ }
1512
+ }
1513
+ return;
1514
+ }
1515
+ if (m.dgn === 'request-layers') {
1516
+ postLayersTree((m as { artboardId?: string }).artboardId ?? null);
1517
+ return;
1518
+ }
1286
1519
  // D9 — canvas-shell chrome follows the Maude chrome theme. The chrome's
1287
1520
  // `--maude-chrome-*` token family is keyed by `data-maude-theme` on the
1288
1521
  // iframe documentElement (see HUD_TOKENS_CSS). This attribute is
@@ -1296,10 +1529,101 @@ function CanvasRouter({
1296
1529
  }
1297
1530
  return;
1298
1531
  }
1532
+ // Shell View-menu zoom items — route to the live viewport controller
1533
+ // (the same methods the in-canvas zoom pill calls).
1534
+ if (m.dgn === 'zoom' && zoomController) {
1535
+ const op = (m as { op?: string }).op;
1536
+ if (op === 'in') zoomController.zoomIn();
1537
+ else if (op === 'out') zoomController.zoomOut();
1538
+ else if (op === 'fit') zoomController.fit();
1539
+ else if (op === 'actual') zoomController.reset();
1540
+ return;
1541
+ }
1299
1542
  };
1300
1543
  window.addEventListener('message', onMessage);
1301
1544
  return () => window.removeEventListener('message', onMessage);
1302
- }, [selSet, annotSel, setTool]);
1545
+ }, [selSet, annotSel, setTool, undoStack, zoomController]);
1546
+
1547
+ // Phase 12 (DDR-103) — double-click a LEAF-TEXT element (children all text
1548
+ // nodes) to edit its copy in place. Commit (blur / Enter) posts `dgn:edit-text`
1549
+ // to the shell, which calls the main-origin /_api/edit-text → editText writes
1550
+ // the escaped JSXText to source; the file-watcher HMR reload re-renders from
1551
+ // the persisted source. Esc restores the original. Mixed/expression elements
1552
+ // are skipped (the engine would refuse them anyway — honest no-op).
1553
+ useEffect(() => {
1554
+ if (typeof document === 'undefined') return;
1555
+ const host = hostRef.current;
1556
+ if (!host) return;
1557
+ let editing: HTMLElement | null = null;
1558
+ let original = '';
1559
+ const onBlur = (): void => commitEdit(true);
1560
+ const onKey = (e: KeyboardEvent): void => {
1561
+ if (e.key === 'Escape') {
1562
+ e.preventDefault();
1563
+ commitEdit(false);
1564
+ } else if (e.key === 'Enter' && !e.shiftKey) {
1565
+ e.preventDefault();
1566
+ (e.currentTarget as HTMLElement | null)?.blur?.();
1567
+ }
1568
+ };
1569
+ function teardown(elx: HTMLElement): void {
1570
+ elx.removeAttribute('contenteditable');
1571
+ elx.classList.remove('dc-text-editing');
1572
+ elx.removeEventListener('blur', onBlur, true);
1573
+ elx.removeEventListener('keydown', onKey, true);
1574
+ }
1575
+ function commitEdit(commit: boolean): void {
1576
+ const elx = editing;
1577
+ if (!elx) return;
1578
+ editing = null;
1579
+ const text = (elx.textContent ?? '').trim();
1580
+ teardown(elx);
1581
+ if (!commit) {
1582
+ elx.textContent = original;
1583
+ return;
1584
+ }
1585
+ if (text === original.trim()) return;
1586
+ const cdId = elx.getAttribute('data-cd-id');
1587
+ if (!cdId) return;
1588
+ try {
1589
+ window.parent.postMessage({ dgn: 'edit-text', id: cdId, file: deriveFile(), text }, '*');
1590
+ } catch {
1591
+ /* detached / cross-origin */
1592
+ }
1593
+ }
1594
+ const onDbl = (e: MouseEvent): void => {
1595
+ if (editing) return;
1596
+ const t = e.target as HTMLElement | null;
1597
+ const stamped = (t?.closest?.('[data-cd-id]') as HTMLElement | null) ?? null;
1598
+ if (!stamped) return;
1599
+ const kids = Array.from(stamped.childNodes);
1600
+ if (kids.length === 0 || !kids.every((n) => n.nodeType === 3)) return; // leaf-text only
1601
+ e.preventDefault();
1602
+ e.stopPropagation();
1603
+ editing = stamped;
1604
+ original = stamped.textContent ?? '';
1605
+ stamped.setAttribute('contenteditable', 'plaintext-only');
1606
+ stamped.classList.add('dc-text-editing');
1607
+ stamped.addEventListener('blur', onBlur, true);
1608
+ stamped.addEventListener('keydown', onKey, true);
1609
+ stamped.focus();
1610
+ try {
1611
+ const range = document.createRange();
1612
+ range.selectNodeContents(stamped);
1613
+ const sel = window.getSelection();
1614
+ sel?.removeAllRanges();
1615
+ sel?.addRange(range);
1616
+ } catch {
1617
+ /* selection API unavailable */
1618
+ }
1619
+ };
1620
+ // Capture phase so we beat the fit-to-view dblclick handler.
1621
+ host.addEventListener('dblclick', onDbl, true);
1622
+ return () => {
1623
+ host.removeEventListener('dblclick', onDbl, true);
1624
+ if (editing) teardown(editing);
1625
+ };
1626
+ }, [hostRef]);
1303
1627
 
1304
1628
  // Cleanup any pending rAF on unmount.
1305
1629
  useEffect(
@@ -1334,6 +1658,8 @@ function CanvasRouter({
1334
1658
  const sel = hoverTargetToSelection(target);
1335
1659
  if (mode === 'replace') selSet.replace(sel);
1336
1660
  else selSet.add(sel);
1661
+ // Phase 12 Task 4 — refresh the Layers tree for the selected artboard.
1662
+ postLayersTree(target.artboardId);
1337
1663
  },
1338
1664
  onContextMenu: ({ clientX, clientY }) => {
1339
1665
  const target = resolveHoverTarget(document, clientX, clientY, { deep: true });
@@ -1725,7 +2051,7 @@ function HoverHalo({ el }: { el: Element | null }) {
1725
2051
  rafRef.current = null;
1726
2052
  const div = ref.current;
1727
2053
  const t = targetRef.current;
1728
- if (!div || !t || !t.isConnected) {
2054
+ if (!div || !t?.isConnected) {
1729
2055
  if (div) div.style.display = 'none';
1730
2056
  return;
1731
2057
  }
@@ -1822,11 +2148,7 @@ function SelectionHalos() {
1822
2148
  for (let i = 0; i < selected.length; i++) {
1823
2149
  const sel = selected[i];
1824
2150
  const child = c.children[i] as HTMLDivElement;
1825
- const el = sel?.id
1826
- ? document.querySelector(`[data-cd-id="${cssEscape(sel.id)}"]`)
1827
- : sel
1828
- ? safeQuery(sel.selector)
1829
- : null;
2151
+ const el = sel ? resolveSelectionEl(document, sel) : null;
1830
2152
  if (!el) {
1831
2153
  child.style.display = 'none';
1832
2154
  continue;
@@ -1879,9 +2201,7 @@ function GroupBbox() {
1879
2201
  let yMax = Number.NEGATIVE_INFINITY;
1880
2202
  let anyHit = false;
1881
2203
  for (const sel of selected) {
1882
- const el = sel.id
1883
- ? document.querySelector(`[data-cd-id="${cssEscape(sel.id)}"]`)
1884
- : safeQuery(sel.selector);
2204
+ const el = resolveSelectionEl(document, sel);
1885
2205
  if (!el) continue;
1886
2206
  // Post-Wave-2: direct artboard drag — the article updates its own
1887
2207
  // `left/top` during drag, so the group bbox just follows via
@@ -1936,11 +2256,3 @@ function classifyContextKind(target: HoverTarget | null): ContextTargetKind {
1936
2256
  if (target.artboardId) return 'artboard-chrome';
1937
2257
  return 'world';
1938
2258
  }
1939
-
1940
- function safeQuery(selector: string): Element | null {
1941
- try {
1942
- return document.querySelector(selector);
1943
- } catch {
1944
- return null;
1945
- }
1946
- }