@1agh/maude 0.28.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/README.md +3 -3
  2. package/{plugins/design/dev-server → apps/studio}/ai-banner.tsx +1 -2
  3. package/apps/studio/annotations-align.ts +149 -0
  4. package/apps/studio/annotations-bindings.ts +197 -0
  5. package/{plugins/design/dev-server → apps/studio}/annotations-context-toolbar.tsx +681 -151
  6. package/apps/studio/annotations-groups.ts +270 -0
  7. package/apps/studio/annotations-layer.tsx +4537 -0
  8. package/apps/studio/annotations-model.ts +2077 -0
  9. package/apps/studio/annotations-snap.ts +125 -0
  10. package/{plugins/design/dev-server → apps/studio}/api.ts +616 -70
  11. package/{plugins/design/dev-server → apps/studio}/artboard-activity-overlay.tsx +1 -1
  12. package/{plugins/design/dev-server → apps/studio}/artboard-marquee.tsx +1 -1
  13. package/apps/studio/bin/_motion-sample-playwright.mjs +129 -0
  14. package/{plugins/design/dev-server → apps/studio}/bin/_png-playwright.mjs +1 -1
  15. package/apps/studio/bin/annotate.mjs +732 -0
  16. package/apps/studio/bin/annotate.sh +17 -0
  17. package/{plugins/design/dev-server → apps/studio}/bin/canvas-edit.sh +1 -1
  18. package/{plugins/design/dev-server → apps/studio}/bin/draw-proof.sh +37 -2
  19. package/{plugins/design/dev-server → apps/studio}/bin/preflight.sh +1 -1
  20. package/apps/studio/bin/read-annotations.mjs +573 -0
  21. package/apps/studio/bin/read-annotations.sh +11 -0
  22. package/{plugins/design/dev-server → apps/studio}/bin/runtime-health.sh +1 -1
  23. package/{plugins/design/dev-server → apps/studio}/bin/screenshot.sh +62 -2
  24. package/{plugins/design/dev-server → apps/studio}/bin/server-up.sh +9 -3
  25. package/apps/studio/bin/to-lottie-verify.html +68 -0
  26. package/apps/studio/bin/to-lottie.sh +128 -0
  27. package/{plugins/design/dev-server → apps/studio}/build.ts +6 -4
  28. package/{plugins/design/dev-server → apps/studio}/canvas-arrowheads.ts +79 -10
  29. package/{plugins/design/dev-server → apps/studio}/canvas-comment-mount.tsx +1 -1
  30. package/apps/studio/canvas-create.ts +104 -0
  31. package/{plugins/design/dev-server → apps/studio}/canvas-cursors.ts +48 -12
  32. package/{plugins/design/dev-server → apps/studio}/canvas-edit.ts +257 -7
  33. package/{plugins/design/dev-server → apps/studio}/canvas-icons.tsx +165 -1
  34. package/{plugins/design/dev-server → apps/studio}/canvas-lib-inline.ts +1 -1
  35. package/{plugins/design/dev-server → apps/studio}/canvas-lib-resolver.ts +1 -1
  36. package/{plugins/design/dev-server → apps/studio}/canvas-lib.tsx +31 -12
  37. package/{plugins/design/dev-server → apps/studio}/canvas-shell.tsx +360 -48
  38. package/apps/studio/client/app.jsx +6742 -0
  39. package/apps/studio/client/canvas-url.js +81 -0
  40. package/{plugins/design/dev-server → apps/studio}/client/comments-overlay.css +41 -27
  41. package/apps/studio/client/index.html +16 -0
  42. package/apps/studio/client/styles/1-tokens-maude.css +296 -0
  43. package/apps/studio/client/styles/3-shell-maude.css +1161 -0
  44. package/{plugins/design/dev-server → apps/studio}/client/styles/3-shell.css +129 -0
  45. package/apps/studio/client/styles/4-components-maude.css +74 -0
  46. package/{plugins/design/dev-server → apps/studio}/client/styles/4-components.css +396 -34
  47. package/apps/studio/client/styles/5-maude-overrides.css +137 -0
  48. package/{plugins/design/dev-server → apps/studio}/client/styles/_index.css +9 -0
  49. package/{plugins/design/dev-server → apps/studio}/client/styles.css +1 -1
  50. package/apps/studio/client/tour/overlay.jsx +279 -0
  51. package/apps/studio/client/tour/usage-tour.js +48 -0
  52. package/apps/studio/client/whats-new-seen.js +52 -0
  53. package/apps/studio/client/whats-new.jsx +250 -0
  54. package/{plugins/design/dev-server → apps/studio}/commands/annotation-strokes-command.ts +2 -2
  55. package/{plugins/design/dev-server → apps/studio}/commands/equal-spacing-command.ts +1 -1
  56. package/{plugins/design/dev-server → apps/studio}/commands/move-artboards-command.ts +2 -2
  57. package/{plugins/design/dev-server → apps/studio}/comments-overlay.tsx +32 -11
  58. package/{plugins/design/dev-server → apps/studio}/context-menu.tsx +5 -4
  59. package/{plugins/design/dev-server → apps/studio}/contextual-toolbar.tsx +18 -7
  60. package/{plugins/design/dev-server → apps/studio}/cursors-overlay.tsx +21 -21
  61. package/apps/studio/dist/client.bundle.js +25545 -0
  62. package/apps/studio/dist/comment-mount.js +2046 -0
  63. package/apps/studio/dist/styles.css +8018 -0
  64. package/apps/studio/dom-selection.ts +342 -0
  65. package/apps/studio/draw/animate.ts +333 -0
  66. package/{plugins/design/dev-server → apps/studio}/draw/brush.ts +1 -1
  67. package/{plugins/design/dev-server → apps/studio}/draw/composition.ts +1 -1
  68. package/{plugins/design/dev-server → apps/studio}/draw/geometry.ts +1 -1
  69. package/{plugins/design/dev-server → apps/studio}/draw/index.ts +4 -1
  70. package/{plugins/design/dev-server → apps/studio}/draw/layout.ts +1 -1
  71. package/apps/studio/draw/morph.ts +204 -0
  72. package/{plugins/design/dev-server → apps/studio}/draw/optimize.ts +1 -1
  73. package/{plugins/design/dev-server → apps/studio}/draw/palette.ts +1 -1
  74. package/{plugins/design/dev-server → apps/studio}/draw/primitives.ts +1 -1
  75. package/apps/studio/draw/serialize-animate.ts +324 -0
  76. package/{plugins/design/dev-server → apps/studio}/draw/serialize.ts +9 -3
  77. package/apps/studio/draw/test/animate.test.ts +172 -0
  78. package/apps/studio/draw/test/morph.test.ts +107 -0
  79. package/apps/studio/draw/test/serialize-animate.test.ts +142 -0
  80. package/{plugins/design/dev-server → apps/studio}/equal-spacing-detector.ts +1 -1
  81. package/{plugins/design/dev-server → apps/studio}/equal-spacing-handles.tsx +7 -29
  82. package/{plugins/design/dev-server → apps/studio}/export-dialog.tsx +16 -4
  83. package/{plugins/design/dev-server → apps/studio}/history.ts +20 -3
  84. package/{plugins/design/dev-server → apps/studio}/http.ts +226 -6
  85. package/{plugins/design/dev-server → apps/studio}/input-router.tsx +15 -1
  86. package/{plugins/design/dev-server → apps/studio}/inspect.ts +50 -4
  87. package/{plugins/design/dev-server → apps/studio}/marquee-overlay.tsx +5 -2
  88. package/{plugins/design/dev-server → apps/studio}/participants-chrome.tsx +2 -8
  89. package/{plugins/design/dev-server → apps/studio}/paths.ts +15 -11
  90. package/{plugins/design/dev-server → apps/studio}/server.ts +23 -2
  91. package/{plugins/design/dev-server → apps/studio}/sync/agent.ts +231 -57
  92. package/{plugins/design/dev-server → apps/studio}/sync/codec.ts +45 -0
  93. package/apps/studio/sync/cold-start.ts +158 -0
  94. package/{plugins/design/dev-server → apps/studio}/sync/connection-state.ts +58 -2
  95. package/{plugins/design/dev-server → apps/studio}/sync/index.ts +563 -221
  96. package/apps/studio/sync/journal.ts +190 -0
  97. package/apps/studio/sync/migrate-seed.ts +321 -0
  98. package/{plugins/design/dev-server → apps/studio}/sync/projection.ts +22 -3
  99. package/{plugins/design/dev-server → apps/studio}/sync/status.ts +15 -4
  100. package/{plugins/design/dev-server → apps/studio}/test/activity.test.ts +1 -7
  101. package/apps/studio/test/annotate-write.test.ts +184 -0
  102. package/apps/studio/test/annotations-align.test.ts +88 -0
  103. package/apps/studio/test/annotations-bindings.test.ts +124 -0
  104. package/apps/studio/test/annotations-groups.test.ts +231 -0
  105. package/{plugins/design/dev-server → apps/studio}/test/annotations-layer.test.ts +63 -0
  106. package/{plugins/design/dev-server → apps/studio}/test/annotations-roundtrip.test.ts +97 -0
  107. package/apps/studio/test/annotations-snap.test.ts +79 -0
  108. package/apps/studio/test/annotations-text-format.test.ts +396 -0
  109. package/apps/studio/test/asset-api.test.ts +149 -0
  110. package/apps/studio/test/canvas-create-api.test.ts +361 -0
  111. package/{plugins/design/dev-server → apps/studio}/test/canvas-cursors.test.ts +2 -0
  112. package/apps/studio/test/canvas-edit.test.ts +319 -0
  113. package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-resolver.test.ts +1 -1
  114. package/apps/studio/test/canvas-media-drop.test.ts +158 -0
  115. package/{plugins/design/dev-server → apps/studio}/test/canvas-origin-gate.test.ts +25 -1
  116. package/apps/studio/test/canvas-url.test.ts +99 -0
  117. package/{plugins/design/dev-server → apps/studio}/test/collab-bridge.test.ts +0 -2
  118. package/{plugins/design/dev-server → apps/studio}/test/collab-room.test.ts +2 -7
  119. package/{plugins/design/dev-server → apps/studio}/test/csp-canvas-shell.test.ts +5 -0
  120. package/apps/studio/test/csrf-write-guard.test.ts +52 -0
  121. package/apps/studio/test/figjam-v3-model.test.ts +342 -0
  122. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +1 -0
  123. package/apps/studio/test/index-data-canvas-ds.test.ts +97 -0
  124. package/apps/studio/test/motion-sample.test.ts +35 -0
  125. package/{plugins/design/dev-server → apps/studio}/test/perf-harness.ts +1 -1
  126. package/{plugins/design/dev-server → apps/studio}/test/phase-3.6-smoke.test.ts +3 -1
  127. package/apps/studio/test/read-annotations.test.ts +409 -0
  128. package/{plugins/design/dev-server → apps/studio}/test/sanitize-annotation-svg.test.ts +108 -2
  129. package/{plugins/design/dev-server → apps/studio}/test/shared-doc-convergence.test.ts +1 -1
  130. package/apps/studio/test/shared-doc-migrate.test.ts +369 -0
  131. package/apps/studio/test/sync-agent.test.ts +575 -0
  132. package/{plugins/design/dev-server → apps/studio}/test/sync-codec.test.ts +65 -0
  133. package/apps/studio/test/sync-cold-start.test.ts +244 -0
  134. package/{plugins/design/dev-server → apps/studio}/test/sync-connection-state.test.ts +68 -0
  135. package/{plugins/design/dev-server → apps/studio}/test/sync-hardening.test.ts +0 -8
  136. package/{plugins/design/dev-server → apps/studio}/test/sync-hubs-config.test.ts +0 -1
  137. package/apps/studio/test/sync-incident-replay.test.ts +211 -0
  138. package/apps/studio/test/sync-journal.test.ts +176 -0
  139. package/{plugins/design/dev-server → apps/studio}/test/sync-runtime.test.ts +360 -5
  140. package/{plugins/design/dev-server → apps/studio}/test/sync-status.test.ts +66 -0
  141. package/apps/studio/test/tour-overlay.test.tsx +56 -0
  142. package/apps/studio/test/use-annotation-resize.test.ts +402 -0
  143. package/{plugins/design/dev-server → apps/studio}/test/use-artboard-drag.test.ts +0 -1
  144. package/{plugins/design/dev-server → apps/studio}/test/use-collab.test.ts +0 -0
  145. package/{plugins/design/dev-server → apps/studio}/test/use-tool-mode.test.tsx +8 -2
  146. package/apps/studio/test/whats-new.test.ts +101 -0
  147. package/apps/studio/text-imports.d.ts +9 -0
  148. package/{plugins/design/dev-server → apps/studio}/tool-palette.tsx +15 -70
  149. package/{plugins/design/dev-server → apps/studio}/undo-hud.tsx +1 -2
  150. package/{plugins/design/dev-server → apps/studio}/undo-stack.ts +1 -1
  151. package/{plugins/design/dev-server → apps/studio}/use-agent-presence.tsx +5 -3
  152. package/apps/studio/use-annotation-resize.tsx +912 -0
  153. package/{plugins/design/dev-server → apps/studio}/use-annotation-selection.tsx +17 -5
  154. package/{plugins/design/dev-server → apps/studio}/use-annotations-visibility.tsx +1 -1
  155. package/{plugins/design/dev-server → apps/studio}/use-artboard-drag.tsx +1 -1
  156. package/{plugins/design/dev-server → apps/studio}/use-canvas-activity.tsx +3 -3
  157. package/apps/studio/use-canvas-media-drop.tsx +343 -0
  158. package/{plugins/design/dev-server → apps/studio}/use-collab.tsx +21 -16
  159. package/{plugins/design/dev-server → apps/studio}/use-cursor-modifiers.tsx +1 -1
  160. package/{plugins/design/dev-server → apps/studio}/use-keyboard-discipline.tsx +5 -2
  161. package/{plugins/design/dev-server → apps/studio}/use-selection-set.tsx +18 -1
  162. package/{plugins/design/dev-server → apps/studio}/use-snap-guides.tsx +1 -1
  163. package/{plugins/design/dev-server → apps/studio}/use-tool-mode.tsx +8 -2
  164. package/{plugins/design/dev-server → apps/studio}/use-undo-stack.tsx +1 -1
  165. package/apps/studio/whats-new.json +264 -0
  166. package/apps/studio/whats-new.schema.json +86 -0
  167. package/apps/studio/whats-new.ts +87 -0
  168. package/cli/bin/maude.mjs +10 -0
  169. package/cli/commands/design-link.test.mjs +84 -0
  170. package/cli/commands/design.mjs +16 -7
  171. package/cli/commands/design.test.mjs +40 -0
  172. package/cli/commands/help.mjs +8 -2
  173. package/cli/commands/hub.mjs +3 -3
  174. package/cli/commands/hub.test.mjs +1 -1
  175. package/cli/commands/scenario-report.mjs +2 -9
  176. package/cli/lib/design-link.mjs +51 -1
  177. package/cli/lib/flow-design-integration.test.mjs +1 -1
  178. package/cli/lib/gitignore-block.mjs +1 -0
  179. package/cli/lib/plugin-cli-reachability.test.mjs +8 -7
  180. package/cli/lib/preflight.mjs +1 -1
  181. package/package.json +15 -15
  182. package/plugins/design/templates/_shell.html +28 -4
  183. package/plugins/design/templates/brief-board.tsx.template +61 -0
  184. package/plugins/design/templates/canvas.tsx.template +1 -1
  185. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +1 -1
  186. package/plugins/design/templates/design-system-inspiration/core/preview/_motion-readme.md.tpl +1 -1
  187. package/plugins/flow/.claude-plugin/config.schema.json +16 -0
  188. package/plugins/design/dev-server/annotations-layer.tsx +0 -3402
  189. package/plugins/design/dev-server/client/app.jsx +0 -2985
  190. package/plugins/design/dev-server/client/index.html +0 -15
  191. package/plugins/design/dev-server/dist/client.bundle.js +0 -20692
  192. package/plugins/design/dev-server/dist/comment-mount.js +0 -1924
  193. package/plugins/design/dev-server/dist/styles.css +0 -2710
  194. package/plugins/design/dev-server/dom-selection.ts +0 -156
  195. package/plugins/design/dev-server/sync/migrate-seed.ts +0 -163
  196. package/plugins/design/dev-server/test/canvas-edit.test.ts +0 -139
  197. package/plugins/design/dev-server/test/shared-doc-migrate.test.ts +0 -159
  198. package/plugins/design/dev-server/test/sync-agent.test.ts +0 -305
  199. package/plugins/design/dev-server/test/use-annotation-resize.test.ts +0 -200
  200. package/plugins/design/dev-server/use-annotation-resize.tsx +0 -449
  201. /package/{plugins/design/dev-server → apps/studio}/activity.ts +0 -0
  202. /package/{plugins/design/dev-server → apps/studio}/bin/_enumerate-artboards-playwright.mjs +0 -0
  203. /package/{plugins/design/dev-server → apps/studio}/bin/_html-playwright.mjs +0 -0
  204. /package/{plugins/design/dev-server → apps/studio}/bin/_pdf-playwright.mjs +0 -0
  205. /package/{plugins/design/dev-server → apps/studio}/bin/_pptx-playwright.mjs +0 -0
  206. /package/{plugins/design/dev-server → apps/studio}/bin/_pw-launch.mjs +0 -0
  207. /package/{plugins/design/dev-server → apps/studio}/bin/_screenshot-playwright.mjs +0 -0
  208. /package/{plugins/design/dev-server → apps/studio}/bin/_svg-optimize.mjs +0 -0
  209. /package/{plugins/design/dev-server → apps/studio}/bin/_svg-playwright.mjs +0 -0
  210. /package/{plugins/design/dev-server → apps/studio}/bin/asset-sweep.sh +0 -0
  211. /package/{plugins/design/dev-server → apps/studio}/bin/bootstrap-check.sh +0 -0
  212. /package/{plugins/design/dev-server → apps/studio}/bin/check-runtime-bundles.sh +0 -0
  213. /package/{plugins/design/dev-server → apps/studio}/bin/draw-build.sh +0 -0
  214. /package/{plugins/design/dev-server → apps/studio}/bin/handoff.sh +0 -0
  215. /package/{plugins/design/dev-server → apps/studio}/bin/prep.sh +0 -0
  216. /package/{plugins/design/dev-server → apps/studio}/bin/scenario-report.mjs +0 -0
  217. /package/{plugins/design/dev-server → apps/studio}/bin/slug.sh +0 -0
  218. /package/{plugins/design/dev-server → apps/studio}/bin/smoke.sh +0 -0
  219. /package/{plugins/design/dev-server → apps/studio}/bin/svg-optimize.sh +0 -0
  220. /package/{plugins/design/dev-server → apps/studio}/bin/visual-sanity.sh +0 -0
  221. /package/{plugins/design/dev-server → apps/studio}/boot-self-heal.ts +0 -0
  222. /package/{plugins/design/dev-server → apps/studio}/canvas-build.ts +0 -0
  223. /package/{plugins/design/dev-server → apps/studio}/canvas-header.ts +0 -0
  224. /package/{plugins/design/dev-server → apps/studio}/canvas-meta.schema.json +0 -0
  225. /package/{plugins/design/dev-server → apps/studio}/canvas-pipeline.ts +0 -0
  226. /package/{plugins/design/dev-server → apps/studio}/client/hmr.mjs +0 -0
  227. /package/{plugins/design/dev-server → apps/studio}/client/iframe-lazy.mjs +0 -0
  228. /package/{plugins/design/dev-server → apps/studio}/client/styles/0-reset.css +0 -0
  229. /package/{plugins/design/dev-server → apps/studio}/client/styles/1-tokens.css +0 -0
  230. /package/{plugins/design/dev-server → apps/studio}/client/styles/2-layout.css +0 -0
  231. /package/{plugins/design/dev-server → apps/studio}/client/styles/5-utilities.css +0 -0
  232. /package/{plugins/design/dev-server → apps/studio}/collab/ai-activity.ts +0 -0
  233. /package/{plugins/design/dev-server → apps/studio}/collab/awareness-bridge.ts +0 -0
  234. /package/{plugins/design/dev-server → apps/studio}/collab/git-lifecycle.ts +0 -0
  235. /package/{plugins/design/dev-server → apps/studio}/collab/index.ts +0 -0
  236. /package/{plugins/design/dev-server → apps/studio}/collab/persistence.ts +0 -0
  237. /package/{plugins/design/dev-server → apps/studio}/collab/protocol.ts +0 -0
  238. /package/{plugins/design/dev-server → apps/studio}/collab/registry.ts +0 -0
  239. /package/{plugins/design/dev-server → apps/studio}/collab/room.ts +0 -0
  240. /package/{plugins/design/dev-server → apps/studio}/config.schema.json +0 -0
  241. /package/{plugins/design/dev-server → apps/studio}/context.ts +0 -0
  242. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/.min-sizes.json +0 -0
  243. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_decoding.js +0 -0
  244. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/lib0_encoding.js +0 -0
  245. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion.js +0 -0
  246. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/motion_react.js +0 -0
  247. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/pixi-js.js +0 -0
  248. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom.js +0 -0
  249. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react-dom_client.js +0 -0
  250. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react.js +0 -0
  251. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-dev-runtime.js +0 -0
  252. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/react_jsx-runtime.js +0 -0
  253. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_awareness.js +0 -0
  254. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/y-protocols_sync.js +0 -0
  255. /package/{plugins/design/dev-server → apps/studio}/dist/runtime/yjs.js +0 -0
  256. /package/{plugins/design/dev-server → apps/studio}/draw/test/brush.test.ts +0 -0
  257. /package/{plugins/design/dev-server → apps/studio}/draw/test/composition.test.ts +0 -0
  258. /package/{plugins/design/dev-server → apps/studio}/draw/test/geometry.test.ts +0 -0
  259. /package/{plugins/design/dev-server → apps/studio}/draw/test/gradient.test.ts +0 -0
  260. /package/{plugins/design/dev-server → apps/studio}/draw/test/layout.test.ts +0 -0
  261. /package/{plugins/design/dev-server → apps/studio}/draw/test/optimize.test.ts +0 -0
  262. /package/{plugins/design/dev-server → apps/studio}/draw/test/palette.test.ts +0 -0
  263. /package/{plugins/design/dev-server → apps/studio}/draw/test/primitives.test.ts +0 -0
  264. /package/{plugins/design/dev-server → apps/studio}/draw/test/serialize.test.ts +0 -0
  265. /package/{plugins/design/dev-server → apps/studio}/examples/README.md +0 -0
  266. /package/{plugins/design/dev-server → apps/studio}/examples/perf-100-artboards.tsx +0 -0
  267. /package/{plugins/design/dev-server → apps/studio}/exporters/_browser-bundles.ts +0 -0
  268. /package/{plugins/design/dev-server → apps/studio}/exporters/canva-handoff-prompt.ts +0 -0
  269. /package/{plugins/design/dev-server → apps/studio}/exporters/canva.ts +0 -0
  270. /package/{plugins/design/dev-server → apps/studio}/exporters/html.ts +0 -0
  271. /package/{plugins/design/dev-server → apps/studio}/exporters/index.ts +0 -0
  272. /package/{plugins/design/dev-server → apps/studio}/exporters/pdf.ts +0 -0
  273. /package/{plugins/design/dev-server → apps/studio}/exporters/png.ts +0 -0
  274. /package/{plugins/design/dev-server → apps/studio}/exporters/pptx.ts +0 -0
  275. /package/{plugins/design/dev-server → apps/studio}/exporters/scope.ts +0 -0
  276. /package/{plugins/design/dev-server → apps/studio}/exporters/svg.ts +0 -0
  277. /package/{plugins/design/dev-server → apps/studio}/exporters/zip.ts +0 -0
  278. /package/{plugins/design/dev-server → apps/studio}/fs-watch.ts +0 -0
  279. /package/{plugins/design/dev-server → apps/studio}/handoff.ts +0 -0
  280. /package/{plugins/design/dev-server → apps/studio}/hmr-broadcast.ts +0 -0
  281. /package/{plugins/design/dev-server → apps/studio}/locator.ts +0 -0
  282. /package/{plugins/design/dev-server → apps/studio}/mem.ts +0 -0
  283. /package/{plugins/design/dev-server → apps/studio}/runtime-bundle.ts +0 -0
  284. /package/{plugins/design/dev-server → apps/studio}/server.mjs +0 -0
  285. /package/{plugins/design/dev-server → apps/studio}/sync/atomic-write.ts +0 -0
  286. /package/{plugins/design/dev-server → apps/studio}/sync/echo-guard.ts +0 -0
  287. /package/{plugins/design/dev-server → apps/studio}/sync/fs-mirror.ts +0 -0
  288. /package/{plugins/design/dev-server → apps/studio}/sync/hubs-config.ts +0 -0
  289. /package/{plugins/design/dev-server → apps/studio}/sync/materialize.ts +0 -0
  290. /package/{plugins/design/dev-server → apps/studio}/sync/origins.ts +0 -0
  291. /package/{plugins/design/dev-server → apps/studio}/sync/untrusted.ts +0 -0
  292. /package/{plugins/design/dev-server → apps/studio}/test/_helpers.ts +0 -0
  293. /package/{plugins/design/dev-server → apps/studio}/test/active-state.test.ts +0 -0
  294. /package/{plugins/design/dev-server → apps/studio}/test/ai-activity.test.ts +0 -0
  295. /package/{plugins/design/dev-server → apps/studio}/test/annotation-strokes-command.test.ts +0 -0
  296. /package/{plugins/design/dev-server → apps/studio}/test/annotations-api.test.ts +0 -0
  297. /package/{plugins/design/dev-server → apps/studio}/test/annotations-draw-modifiers.test.ts +0 -0
  298. /package/{plugins/design/dev-server → apps/studio}/test/artboard-activity-overlay.test.tsx +0 -0
  299. /package/{plugins/design/dev-server → apps/studio}/test/binary-smoke.test.ts +0 -0
  300. /package/{plugins/design/dev-server → apps/studio}/test/boot-self-heal.test.ts +0 -0
  301. /package/{plugins/design/dev-server → apps/studio}/test/bundle-smoke.test.ts +0 -0
  302. /package/{plugins/design/dev-server → apps/studio}/test/canvas-build.test.ts +0 -0
  303. /package/{plugins/design/dev-server → apps/studio}/test/canvas-header.test.ts +0 -0
  304. /package/{plugins/design/dev-server → apps/studio}/test/canvas-hmr-runtime.test.tsx +0 -0
  305. /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-inline.test.ts +0 -0
  306. /package/{plugins/design/dev-server → apps/studio}/test/canvas-lib-motion.test.ts +0 -0
  307. /package/{plugins/design/dev-server → apps/studio}/test/canvas-meta-api.test.ts +0 -0
  308. /package/{plugins/design/dev-server → apps/studio}/test/canvas-pipeline.test.ts +0 -0
  309. /package/{plugins/design/dev-server → apps/studio}/test/canvas-route.test.ts +0 -0
  310. /package/{plugins/design/dev-server → apps/studio}/test/collab-annotations-bridge.test.ts +0 -0
  311. /package/{plugins/design/dev-server → apps/studio}/test/collab-awareness-bridge.test.ts +0 -0
  312. /package/{plugins/design/dev-server → apps/studio}/test/collab-loopback.test.ts +0 -0
  313. /package/{plugins/design/dev-server → apps/studio}/test/collab-protocol.test.ts +0 -0
  314. /package/{plugins/design/dev-server → apps/studio}/test/collab-reseed-guard.test.ts +0 -0
  315. /package/{plugins/design/dev-server → apps/studio}/test/collab-stress.test.ts +0 -0
  316. /package/{plugins/design/dev-server → apps/studio}/test/comment-mount.test.ts +0 -0
  317. /package/{plugins/design/dev-server → apps/studio}/test/comments-api.test.ts +0 -0
  318. /package/{plugins/design/dev-server → apps/studio}/test/compile-entry.test.ts +0 -0
  319. /package/{plugins/design/dev-server → apps/studio}/test/context-resolve-tokens.test.ts +0 -0
  320. /package/{plugins/design/dev-server → apps/studio}/test/equal-spacing-detector.test.ts +0 -0
  321. /package/{plugins/design/dev-server → apps/studio}/test/exporters/canva.test.ts +0 -0
  322. /package/{plugins/design/dev-server → apps/studio}/test/exporters/endpoint.test.ts +0 -0
  323. /package/{plugins/design/dev-server → apps/studio}/test/exporters/history.test.ts +0 -0
  324. /package/{plugins/design/dev-server → apps/studio}/test/exporters/html.test.ts +0 -0
  325. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pdf.test.ts +0 -0
  326. /package/{plugins/design/dev-server → apps/studio}/test/exporters/png.test.ts +0 -0
  327. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx-deck.test.ts +0 -0
  328. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pptx.test.ts +0 -0
  329. /package/{plugins/design/dev-server → apps/studio}/test/exporters/pw-launch.test.ts +0 -0
  330. /package/{plugins/design/dev-server → apps/studio}/test/exporters/scope.test.ts +0 -0
  331. /package/{plugins/design/dev-server → apps/studio}/test/exporters/svg.test.ts +0 -0
  332. /package/{plugins/design/dev-server → apps/studio}/test/exporters/zip.test.ts +0 -0
  333. /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-20-annotations.svg +0 -0
  334. /package/{plugins/design/dev-server → apps/studio}/test/fixtures/phase-21-annotations.svg +0 -0
  335. /package/{plugins/design/dev-server → apps/studio}/test/fs-watch.test.ts +0 -0
  336. /package/{plugins/design/dev-server → apps/studio}/test/git-lifecycle.test.ts +0 -0
  337. /package/{plugins/design/dev-server → apps/studio}/test/handoff-static-frames.test.ts +0 -0
  338. /package/{plugins/design/dev-server → apps/studio}/test/handoff.test.ts +0 -0
  339. /package/{plugins/design/dev-server → apps/studio}/test/history-rollback.test.ts +0 -0
  340. /package/{plugins/design/dev-server → apps/studio}/test/hmr-broadcast.test.ts +0 -0
  341. /package/{plugins/design/dev-server → apps/studio}/test/hmr-classify.test.ts +0 -0
  342. /package/{plugins/design/dev-server → apps/studio}/test/input-router.test.ts +0 -0
  343. /package/{plugins/design/dev-server → apps/studio}/test/locator.test.ts +0 -0
  344. /package/{plugins/design/dev-server → apps/studio}/test/marquee-overlay.test.ts +0 -0
  345. /package/{plugins/design/dev-server → apps/studio}/test/move-artboards-command.test.ts +0 -0
  346. /package/{plugins/design/dev-server → apps/studio}/test/participants-chrome.test.ts +0 -0
  347. /package/{plugins/design/dev-server → apps/studio}/test/paths.test.ts +0 -0
  348. /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle-error-mapping.test.ts +0 -0
  349. /package/{plugins/design/dev-server → apps/studio}/test/runtime-bundle.test.ts +0 -0
  350. /package/{plugins/design/dev-server → apps/studio}/test/server-lifecycle.test.ts +0 -0
  351. /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-foundation.test.ts +0 -0
  352. /package/{plugins/design/dev-server → apps/studio}/test/shared-doc-projection.test.ts +0 -0
  353. /package/{plugins/design/dev-server → apps/studio}/test/snap-distance-pill.test.ts +0 -0
  354. /package/{plugins/design/dev-server → apps/studio}/test/sync-atomic-write.test.ts +0 -0
  355. /package/{plugins/design/dev-server → apps/studio}/test/sync-echo-guard.test.ts +0 -0
  356. /package/{plugins/design/dev-server → apps/studio}/test/sync-fs-mirror.test.ts +0 -0
  357. /package/{plugins/design/dev-server → apps/studio}/test/sync-meta-codec.test.ts +0 -0
  358. /package/{plugins/design/dev-server → apps/studio}/test/sync-untrusted.test.ts +0 -0
  359. /package/{plugins/design/dev-server → apps/studio}/test/system-endpoint.test.ts +0 -0
  360. /package/{plugins/design/dev-server → apps/studio}/test/tool-palette.test.tsx +0 -0
  361. /package/{plugins/design/dev-server → apps/studio}/test/undo-stack.test.ts +0 -0
  362. /package/{plugins/design/dev-server → apps/studio}/test/use-agent-presence.test.tsx +0 -0
  363. /package/{plugins/design/dev-server → apps/studio}/test/use-annotation-selection.test.tsx +0 -0
  364. /package/{plugins/design/dev-server → apps/studio}/test/use-canvas-activity.test.tsx +0 -0
  365. /package/{plugins/design/dev-server → apps/studio}/test/use-cursor-modifiers.test.ts +0 -0
  366. /package/{plugins/design/dev-server → apps/studio}/test/use-keyboard-discipline.test.ts +0 -0
  367. /package/{plugins/design/dev-server → apps/studio}/test/use-selection-set.test.tsx +0 -0
  368. /package/{plugins/design/dev-server → apps/studio}/test/use-snap-guides.test.ts +0 -0
  369. /package/{plugins/design/dev-server → apps/studio}/test/use-undo-stack.test.tsx +0 -0
  370. /package/{plugins/design/dev-server → apps/studio}/test/ws-handshake.test.ts +0 -0
  371. /package/{plugins/design/dev-server → apps/studio}/tsconfig.json +0 -0
  372. /package/{plugins/design/dev-server → apps/studio}/ws.ts +0 -0
@@ -0,0 +1,250 @@
1
+ // whats-new.jsx — Maude UI "What's New" surfaces.
2
+ //
3
+ // useWhatsNew(version) → headless handle (fetch + seen-state + open-state)
4
+ // <WhatsNewBadge> → menubar entry point (✦ + unseen-count dot)
5
+ // <WhatsNewToast> → first-run notice (bottom-left, mirrors ai-banner.tsx)
6
+ // <WhatsNewPanel> → list of entries (reuses the help-modal backdrop)
7
+ //
8
+ // Styling lives in client/styles/4-components.css (.mdcc-wn-*). Spotlight tours
9
+ // (entry.tour[]) land in Phase 3 — the panel/toast leave a hook for them.
10
+
11
+ import { useCallback, useEffect, useState } from 'react';
12
+ import { bestSeenVersion, computeUnseen } from './whats-new-seen.js';
13
+
14
+ const WN_SEEN = 'mdcc-whatsnew-seen';
15
+ const WN_TOAST = 'mdcc-whatsnew-toast-dismissed';
16
+
17
+ // learnMore renders as <a href> in the privileged main origin. Only bind an
18
+ // http(s) URL — belt-and-suspenders next to React's built-in URL sanitizer and
19
+ // the schema's pattern (security review, defense-in-depth).
20
+ function isSafeHref(url) {
21
+ return typeof url === 'string' && /^https?:\/\//i.test(url);
22
+ }
23
+
24
+ function readSeen(currentVersion) {
25
+ try {
26
+ const v = localStorage.getItem(WN_SEEN);
27
+ if (v) return v;
28
+ // First ever run: acknowledge the current version silently so a brand-new
29
+ // user isn't greeted with the entire backlog as "new". Only true upgrades
30
+ // (and pending dev entries) surface after this.
31
+ localStorage.setItem(WN_SEEN, currentVersion);
32
+ } catch {}
33
+ return currentVersion;
34
+ }
35
+
36
+ function readToastDismissed() {
37
+ try {
38
+ return localStorage.getItem(WN_TOAST);
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ export function useWhatsNew(currentVersion) {
45
+ const [entries, setEntries] = useState([]);
46
+ const [feedVersion, setFeedVersion] = useState(currentVersion);
47
+ const [seenVersion, setSeenVersion] = useState(() => readSeen(currentVersion));
48
+ const [toastDismissed, setToastDismissed] = useState(() => readToastDismissed());
49
+ const [panelOpen, setPanelOpen] = useState(false);
50
+
51
+ useEffect(() => {
52
+ let cancelled = false;
53
+ fetch('/_api/whats-new')
54
+ .then((r) => r.json())
55
+ .then((j) => {
56
+ if (cancelled) return;
57
+ setEntries(Array.isArray(j?.entries) ? j.entries : []);
58
+ if (typeof j?.version === 'string') setFeedVersion(j.version);
59
+ })
60
+ .catch(() => {
61
+ /* offline / restart — no banner */
62
+ });
63
+ return () => {
64
+ cancelled = true;
65
+ };
66
+ }, []);
67
+
68
+ const unseen = computeUnseen(entries, seenVersion);
69
+ const showToast = unseen.length > 0 && toastDismissed !== feedVersion && !panelOpen;
70
+ const toastEntry = unseen[0] ?? null;
71
+
72
+ const markAllSeen = useCallback(() => {
73
+ const next = bestSeenVersion(entries, feedVersion);
74
+ setSeenVersion(next);
75
+ setToastDismissed(feedVersion);
76
+ try {
77
+ localStorage.setItem(WN_SEEN, next);
78
+ localStorage.setItem(WN_TOAST, feedVersion);
79
+ } catch {}
80
+ }, [entries, feedVersion]);
81
+
82
+ const dismissToast = useCallback(() => {
83
+ setToastDismissed(feedVersion);
84
+ try {
85
+ localStorage.setItem(WN_TOAST, feedVersion);
86
+ } catch {}
87
+ }, [feedVersion]);
88
+
89
+ const openPanel = useCallback(() => setPanelOpen(true), []);
90
+ const closePanel = useCallback(() => {
91
+ setPanelOpen(false);
92
+ markAllSeen();
93
+ }, [markAllSeen]);
94
+
95
+ return {
96
+ entries,
97
+ unseen,
98
+ feedVersion,
99
+ panelOpen,
100
+ openPanel,
101
+ closePanel,
102
+ showToast,
103
+ toastEntry,
104
+ dismissToast,
105
+ markAllSeen,
106
+ };
107
+ }
108
+
109
+ export function WhatsNewBadge({ count = 0, onOpen }) {
110
+ return (
111
+ <button
112
+ type="button"
113
+ className="mdcc-wn-badge"
114
+ data-tour="whatsnew"
115
+ onClick={onOpen}
116
+ title="What's new"
117
+ aria-label={count > 0 ? `What's new — ${count} new` : "What's new"}
118
+ >
119
+ <span aria-hidden="true">✦</span>
120
+ <span className="mdcc-wn-badge__label">New</span>
121
+ {count > 0 && (
122
+ <span className="mdcc-wn-badge__dot" aria-hidden="true">
123
+ {count}
124
+ </span>
125
+ )}
126
+ </button>
127
+ );
128
+ }
129
+
130
+ export function WhatsNewToast({ wn }) {
131
+ if (!wn.showToast || !wn.toastEntry) return null;
132
+ const e = wn.toastEntry;
133
+ const total = wn.unseen.length;
134
+ // Plan C P6 — restyled to the maude `.st-toast` family (was `mdcc-wn-toast`),
135
+ // matching `.design/ui/Studio.tsx` AB-D. Behavior unchanged.
136
+ return (
137
+ <div className="st-toast" role="status" aria-live="polite">
138
+ <button
139
+ type="button"
140
+ className="st-toast-close"
141
+ aria-label="Dismiss"
142
+ onClick={wn.dismissToast}
143
+ >
144
+ ×
145
+ </button>
146
+ <div className="st-toast-hd">
147
+ <span aria-hidden="true">✦</span>
148
+ What's new{e.version ? ` · v${e.version}` : ''}
149
+ </div>
150
+ <div className="st-toast-title">{e.title}</div>
151
+ <div className="st-toast-txt">{e.summary}</div>
152
+ <div className="st-toast-actions">
153
+ <button type="button" className="btn btn--primary btn--sm" onClick={wn.openPanel}>
154
+ {total > 1 ? `See all (${total})` : 'Details'}
155
+ </button>
156
+ <button type="button" className="btn btn--ghost btn--sm" onClick={wn.dismissToast}>
157
+ Dismiss
158
+ </button>
159
+ </div>
160
+ </div>
161
+ );
162
+ }
163
+
164
+ export function WhatsNewPanel({ wn, onStartTour }) {
165
+ const { panelOpen, closePanel } = wn;
166
+ useEffect(() => {
167
+ if (!panelOpen) return;
168
+ function onKey(e) {
169
+ if (e.key === 'Escape') closePanel();
170
+ }
171
+ window.addEventListener('keydown', onKey);
172
+ return () => window.removeEventListener('keydown', onKey);
173
+ }, [panelOpen, closePanel]);
174
+
175
+ if (!panelOpen) return null;
176
+ const unseenIds = new Set(wn.unseen.map((e) => e.id));
177
+ return (
178
+ <div
179
+ className="help-modal-backdrop"
180
+ role="presentation"
181
+ onMouseDown={(e) => {
182
+ if (e.target === e.currentTarget) closePanel();
183
+ }}
184
+ >
185
+ <div
186
+ className="mdcc-wn-panel"
187
+ role="dialog"
188
+ aria-modal="true"
189
+ aria-labelledby="mdcc-wn-title"
190
+ >
191
+ <header className="help-modal-hd">
192
+ <span className="title" id="mdcc-wn-title">
193
+ ✦ What’s new in maude
194
+ </span>
195
+ <button
196
+ type="button"
197
+ className="help-modal-close"
198
+ aria-label="Close (Esc)"
199
+ onClick={closePanel}
200
+ >
201
+ ×
202
+ </button>
203
+ </header>
204
+ <div className="mdcc-wn-panel__body">
205
+ {wn.entries.length === 0 ? (
206
+ <p className="mdcc-wn-empty">Nothing new yet.</p>
207
+ ) : (
208
+ <ul className="mdcc-wn-list">
209
+ {wn.entries.map((e) => (
210
+ <li
211
+ key={e.id}
212
+ className={'mdcc-wn-item' + (unseenIds.has(e.id) ? ' is-unseen' : '')}
213
+ >
214
+ <div className="mdcc-wn-item__hd">
215
+ <span className={'mdcc-wn-kind mdcc-wn-kind--' + e.kind}>{e.kind}</span>
216
+ <span className="mdcc-wn-item__title">{e.title}</span>
217
+ <span className="mdcc-wn-item__ver">{e.version ? `v${e.version}` : 'next'}</span>
218
+ </div>
219
+ <p className="mdcc-wn-item__summary">{e.summary}</p>
220
+ {isSafeHref(e.learnMore) && (
221
+ <a
222
+ className="mdcc-wn-item__more"
223
+ href={e.learnMore}
224
+ target="_blank"
225
+ rel="noreferrer"
226
+ >
227
+ Learn more →
228
+ </a>
229
+ )}
230
+ {onStartTour && Array.isArray(e.tour) && e.tour.length > 0 && (
231
+ <button
232
+ type="button"
233
+ className="mdcc-wn-item__tour"
234
+ onClick={() => {
235
+ closePanel();
236
+ onStartTour(e.tour);
237
+ }}
238
+ >
239
+ ▶ Take tour
240
+ </button>
241
+ )}
242
+ </li>
243
+ ))}
244
+ </ul>
245
+ )}
246
+ </div>
247
+ </div>
248
+ </div>
249
+ );
250
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @file commands/annotation-strokes-command.ts — undo entry for annotation
3
3
  * stroke add / erase / batch-translate.
4
- * @scope plugins/design/dev-server/commands/annotation-strokes-command.ts
4
+ * @scope apps/studio/commands/annotation-strokes-command.ts
5
5
  * @purpose Records a `Stroke[]` pair (before/after) and routes both
6
6
  * directions through a single `putFn`. The server endpoint is
7
7
  * `PUT /_api/annotations` which replaces the entire SVG; we
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import type { Stroke } from '../annotations-layer.tsx';
23
- import type { CommandRecord, CommandSinks, EditCommand } from '../undo-stack.ts';
23
+ import type { CommandRecord, EditCommand } from '../undo-stack.ts';
24
24
  import { registerCommand } from '../undo-stack.ts';
25
25
 
26
26
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @file commands/equal-spacing-command.ts — undo label helpers for
3
3
  * equal-spacing / align gestures.
4
- * @scope plugins/design/dev-server/commands/equal-spacing-command.ts
4
+ * @scope apps/studio/commands/equal-spacing-command.ts
5
5
  * @purpose Distribute + align both call `dragBus.commitPositions(moved)`,
6
6
  * which wraps the move in a generic MoveArtboardsCommand. Per
7
7
  * DDR-050 the underlying command type is shared (the inverse
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file commands/move-artboards-command.ts — undo entry for artboard moves
3
- * @scope plugins/design/dev-server/commands/move-artboards-command.ts
3
+ * @scope apps/studio/commands/move-artboards-command.ts
4
4
  * @purpose Reversible record of an artboard-layout PATCH. Pairs the full
5
5
  * `before` and `after` layout snapshots with an injected
6
6
  * `patchFn` (in production, `applyArtboardLayout`; in tests, a
@@ -17,7 +17,7 @@
17
17
  * stale layout.
18
18
  */
19
19
 
20
- import type { CommandRecord, CommandSinks, EditCommand } from '../undo-stack.ts';
20
+ import type { CommandRecord, EditCommand } from '../undo-stack.ts';
21
21
  import { registerCommand } from '../undo-stack.ts';
22
22
 
23
23
  export interface ArtboardLayoutEntry {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file comments-overlay.tsx — FigJam-style in-place comments overlay
3
- * @scope plugins/design/dev-server/comments-overlay.tsx
3
+ * @scope apps/studio/comments-overlay.tsx
4
4
  * @purpose Renders DS-styled comment pins (Phase 6 Task 2), the in-place
5
5
  * composer bubble (Task 3), and the thread popover (Task 4)
6
6
  * inside the canvas iframe. Sibling to `annotations-layer` —
@@ -81,6 +81,9 @@ export interface OverlayComment {
81
81
  id: string;
82
82
  file: string;
83
83
  selector: string;
84
+ /** Occurrence index among `querySelectorAll(selector)` — disambiguates a
85
+ * component repeated within one artboard. Absent (old comments) → first. */
86
+ index?: number;
84
87
  bounds: OverlayBounds | null;
85
88
  text: string;
86
89
  status: 'open' | 'resolved';
@@ -138,7 +141,10 @@ function deriveFile(): string | null {
138
141
  // halo chrome at z-index 5) instead of being portaled into `.dc-world` where
139
142
  // it would lose the stacking battle.
140
143
 
141
- function screenRectFor(selector: string): {
144
+ function screenRectFor(
145
+ selector: string,
146
+ index?: number
147
+ ): {
142
148
  x: number;
143
149
  y: number;
144
150
  w: number;
@@ -147,11 +153,15 @@ function screenRectFor(selector: string): {
147
153
  if (!selector) return null;
148
154
  let el: HTMLElement | null = null;
149
155
  try {
150
- el = document.querySelector(selector) as HTMLElement | null;
156
+ // index disambiguates a component repeated within one artboard (querySelector
157
+ // alone would always grab the first match). Absent/0 → first.
158
+ const all = document.querySelectorAll(selector);
159
+ const i = index && index > 0 && index < all.length ? index : 0;
160
+ el = (all[i] ?? all[0] ?? null) as HTMLElement | null;
151
161
  } catch {
152
162
  return null;
153
163
  }
154
- if (!el || !el.isConnected) return null;
164
+ if (!el?.isConnected) return null;
155
165
  const r = el.getBoundingClientRect();
156
166
  if (r.width === 0 && r.height === 0) return null;
157
167
  return { x: r.left, y: r.top, w: r.width, h: r.height };
@@ -194,7 +204,7 @@ export function CommentsOverlay(): React.ReactNode {
194
204
  const mirrorSelection = useCallback(
195
205
  (comment: OverlayComment | undefined) => {
196
206
  if (!selSet) return;
197
- if (!comment || !comment.selector) {
207
+ if (!comment?.selector) {
198
208
  selSet.clear();
199
209
  return;
200
210
  }
@@ -288,7 +298,7 @@ export function CommentsOverlay(): React.ReactNode {
288
298
  const detail = (
289
299
  e as CustomEvent<{ selection?: ComposeSelection; clientX?: number; clientY?: number }>
290
300
  ).detail;
291
- if (!detail || !detail.selection) return;
301
+ if (!detail?.selection) return;
292
302
  setComposer({
293
303
  selection: detail.selection,
294
304
  clientX: typeof detail.clientX === 'number' ? detail.clientX : 0,
@@ -316,6 +326,7 @@ export function CommentsOverlay(): React.ReactNode {
316
326
  const payload = {
317
327
  file: sel.file,
318
328
  selector: sel.selector,
329
+ index: sel.index,
319
330
  dom_path: sel.dom_path,
320
331
  tag: sel.tag,
321
332
  classes: sel.classes,
@@ -372,7 +383,9 @@ export function CommentsOverlay(): React.ReactNode {
372
383
  const indexById = useMemo(() => {
373
384
  const m = new Map<string, number>();
374
385
  const all = comments.slice().sort((a, b) => a.created.localeCompare(b.created));
375
- all.forEach((c, i) => m.set(c.id, i + 1));
386
+ all.forEach((c, i) => {
387
+ m.set(c.id, i + 1);
388
+ });
376
389
  return m;
377
390
  }, [comments]);
378
391
 
@@ -453,6 +466,7 @@ export function CommentsOverlay(): React.ReactNode {
453
466
  return (
454
467
  <CommentThread
455
468
  comment={focused}
469
+ sequence={indexById.get(focused.id) ?? 0}
456
470
  onClose={() => {
457
471
  setFocusedId(null);
458
472
  // Drop the canvas halo when the thread closes — symmetric with
@@ -745,7 +759,7 @@ function CommentPin({
745
759
  // Live screen-coord lookup mirrors SelectionHalos in canvas-shell.tsx.
746
760
  // Falls back to stored bounds (a screen-coord capture at create time)
747
761
  // when the target element is gone — better than vanishing entirely.
748
- let pos = screenRectFor(comment.selector);
762
+ let pos = screenRectFor(comment.selector, comment.index);
749
763
  if (!pos && comment.bounds) {
750
764
  pos = {
751
765
  x: comment.bounds.x,
@@ -772,7 +786,7 @@ function CommentPin({
772
786
  return () => {
773
787
  if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
774
788
  };
775
- }, [comment.selector, comment.bounds]);
789
+ }, [comment.selector, comment.bounds, comment.index]);
776
790
 
777
791
  const author = comment.author?.trim() || 'unknown';
778
792
  const label = `Comment ${sequence} by ${author}`;
@@ -925,12 +939,14 @@ function CommentComposer({
925
939
 
926
940
  function CommentThread({
927
941
  comment,
942
+ sequence,
928
943
  onClose,
929
944
  onPatch,
930
945
  onDelete,
931
946
  onReply,
932
947
  }: {
933
948
  comment: OverlayComment;
949
+ sequence: number;
934
950
  onClose: () => void;
935
951
  onPatch: (patch: Record<string, unknown>) => void;
936
952
  onDelete: () => void;
@@ -1027,6 +1043,11 @@ function CommentThread({
1027
1043
  >
1028
1044
  <div className="cm-thread__head" id={headId}>
1029
1045
  <div className="cm-thread__head-row">
1046
+ {/* Plan C P18 — pin/sequence badge in the popover header (parity with
1047
+ `.design/ui/Studio.tsx` thread popover). */}
1048
+ <span className="cm-thread__seq" aria-hidden="true">
1049
+ {sequence}
1050
+ </span>
1030
1051
  <span className="cm-thread__author">{comment.author?.trim() || 'unknown'}</span>
1031
1052
  <span className="cm-thread__time">{formatRelativeTime(comment.created)}</span>
1032
1053
  <button
@@ -1159,7 +1180,7 @@ function computeThreadAnchor(comment: OverlayComment): { x: number; y: number }
1159
1180
  // Resolve target's live screen rect; popover drops below the pin with small
1160
1181
  // breathing room. Stored bounds (capture-time screen coords) are the
1161
1182
  // last-resort fallback for orphaned pins.
1162
- const rect = comment.selector ? screenRectFor(comment.selector) : null;
1183
+ const rect = comment.selector ? screenRectFor(comment.selector, comment.index) : null;
1163
1184
  if (rect) {
1164
1185
  // Pin sits at (rect.right - 12, rect.top - 12). Place popover at the same
1165
1186
  // x for visual continuity, 16px below the top so it clears the pin.
@@ -1184,7 +1205,7 @@ function computeAnchor(state: ComposerState): { x: number; y: number } {
1184
1205
  return { x: state.clientX, y: state.clientY + 8 };
1185
1206
  }
1186
1207
  if (state.selection.selector) {
1187
- const rect = screenRectFor(state.selection.selector);
1208
+ const rect = screenRectFor(state.selection.selector, state.selection.index);
1188
1209
  if (rect) {
1189
1210
  return { x: rect.x, y: rect.y + rect.h + 8 };
1190
1211
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file context-menu.tsx — Phase 4.1 right-click context menu
3
- * @scope plugins/design/dev-server/context-menu.tsx
3
+ * @scope apps/studio/context-menu.tsx
4
4
  * @purpose Floating, context-aware right-click menu. The router calls
5
5
  * `openContextMenu({ clientX, clientY, target })`; the menu
6
6
  * resolves the section list via the ContextRegistry and renders
@@ -237,7 +237,9 @@ const MENU_CSS = `
237
237
  .dc-context-menu .dc-menu-flyout.is-flip { left: auto; right: calc(100% + 3px); }
238
238
  `.trim();
239
239
 
240
- function ensureMenuStyles(): void {
240
+ // Exported (FigJam v3) the annotation layer's right-click menu reuses the
241
+ // same .dc-context-menu visual language without duplicating the stylesheet.
242
+ export function ensureMenuStyles(): void {
241
243
  if (typeof document === 'undefined') return;
242
244
  if (document.getElementById('dc-context-menu-css')) return;
243
245
  const s = document.createElement('style');
@@ -342,7 +344,7 @@ function ContextMenuView({
342
344
  el.querySelectorAll<HTMLButtonElement>('button.dc-menu-item:not([disabled])')
343
345
  );
344
346
  if (items.length === 0) return;
345
- const idx = items.findIndex((b) => b === document.activeElement);
347
+ const idx = items.indexOf(document.activeElement);
346
348
  const nextIdx =
347
349
  e.key === 'ArrowDown'
348
350
  ? (idx + 1) % items.length
@@ -368,7 +370,6 @@ function ContextMenuView({
368
370
  {sections.map((section, si) => {
369
371
  const sectionKey = section.map((i) => i.id).join('|') || `s${si}`;
370
372
  return (
371
- // biome-ignore lint/a11y/useSemanticElements: ARIA group within role="menu"; no native equivalent.
372
373
  <div key={sectionKey} role="group">
373
374
  {si > 0 ? <div className="dc-menu-sep" aria-hidden="true" /> : null}
374
375
  {section.map((item) => (
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file contextual-toolbar.tsx — T30 (Wave 3)
3
- * @scope plugins/design/dev-server/contextual-toolbar.tsx
3
+ * @scope apps/studio/contextual-toolbar.tsx
4
4
  * @purpose Selection-anchored floating chrome for ELEMENT selections
5
5
  * (cd-id entries in the selection set). Mirrors the
6
6
  * MultiArtboardToolbar pattern but scopes to user content inside
@@ -30,6 +30,7 @@
30
30
 
31
31
  import { useEffect, useRef } from 'react';
32
32
 
33
+ import { resolveSelectionEl } from './dom-selection.ts';
33
34
  import { useSelectionSet } from './use-selection-set.tsx';
34
35
 
35
36
  const CTX_TOOLBAR_CSS = `
@@ -159,9 +160,7 @@ export function ContextualToolbar() {
159
160
  let yMax = Number.NEGATIVE_INFINITY;
160
161
  let any = false;
161
162
  for (const sel of elementSelections) {
162
- const node = sel.id
163
- ? document.querySelector(`[data-cd-id="${sel.id}"]`)
164
- : document.querySelector(sel.selector);
163
+ const node = resolveSelectionEl(document, sel);
165
164
  if (!node) continue;
166
165
  const r = (node as HTMLElement).getBoundingClientRect();
167
166
  if (r.width === 0 && r.height === 0) continue;
@@ -208,6 +207,20 @@ export function ContextualToolbar() {
208
207
  return (
209
208
  <div ref={ref} className="dc-elem-ctx-tb" role="toolbar" aria-label="Element actions">
210
209
  <span className="dc-elem-ctx-count">{count === 1 ? '1 element' : `${count} elements`}</span>
210
+ <button
211
+ type="button"
212
+ title="Inspect — open the right panel (Inspect / Layers / CSS)"
213
+ onClick={() => {
214
+ // Phase 12 — open the shell's Inspector on the current selection.
215
+ try {
216
+ window.parent.postMessage({ dgn: 'open-inspector' }, '*');
217
+ } catch {
218
+ /* detached / cross-origin */
219
+ }
220
+ }}
221
+ >
222
+ Inspect
223
+ </button>
211
224
  <button
212
225
  type="button"
213
226
  title="Copy CSS selector"
@@ -228,9 +241,7 @@ export function ContextualToolbar() {
228
241
  title="Add comment on this element"
229
242
  onClick={() => {
230
243
  if (!primary) return;
231
- const node = primary.id
232
- ? document.querySelector(`[data-cd-id="${primary.id}"]`)
233
- : document.querySelector(primary.selector);
244
+ const node = resolveSelectionEl(document, primary);
234
245
  openComposerForSelection(primary, node);
235
246
  }}
236
247
  >
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @file cursors-overlay.tsx — foreign peer cursors rendered on the canvas
3
- * @scope plugins/design/dev-server/cursors-overlay.tsx
3
+ * @scope apps/studio/cursors-overlay.tsx
4
4
  * @purpose Renders one colored SVG arrow + name label per foreign peer at
5
5
  * their published world coords, transformed through the local
6
6
  * viewport so the cursor anchors to canvas content (not to my
@@ -43,24 +43,28 @@ const CURSOR_CSS = `
43
43
  }
44
44
  .dc-cursor-arrow {
45
45
  display: block;
46
- filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25));
46
+ filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.4));
47
47
  }
48
+ /* DS colors-presence .cur-label recipe — hue pill, mono 10px, dark
49
+ * accent-fg text (never white-on-hue, which washed out on light hues).
50
+ * NOTE: keep this comment backtick-free — it lives inside the CURSOR_CSS
51
+ * template literal and a stray backtick closes it. */
48
52
  .dc-cursor-label {
49
53
  position: absolute;
50
- top: 18px;
51
- left: 14px;
52
- padding: 2px 6px;
53
- font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
54
+ /* specimen geometry: the pill hangs below-right of the 15px triangle */
55
+ top: 14px;
56
+ left: 10px;
57
+ padding: 1px 8px;
58
+ font-family: var(--font-mono, ui-monospace, monospace);
54
59
  font-weight: 500;
55
- font-size: 11px;
56
- line-height: 1.2;
57
- color: var(--maude-hud-accent-fg, #fff);
58
- border-radius: var(--radius-sm, 2px);
60
+ font-size: 10px;
61
+ line-height: 1.4;
62
+ color: var(--maude-hud-accent-fg, oklch(0.180 0.030 268));
63
+ border-radius: 999px;
59
64
  white-space: nowrap;
60
65
  max-width: 180px;
61
66
  overflow: hidden;
62
67
  text-overflow: ellipsis;
63
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
64
68
  }
65
69
  .dc-peer-selection {
66
70
  position: absolute;
@@ -117,20 +121,16 @@ const Cursor = memo(function Cursor({ peer, viewport }: CursorProps): JSX.Elemen
117
121
  const screenY = peer.cursor.y * viewport.zoom + viewport.y;
118
122
  return (
119
123
  <div className="dc-cursor" style={{ transform: `translate(${screenX}px, ${screenY}px)` }}>
124
+ {/* DS colors-presence Pointer — one plain triangle glyph, tinted by its
125
+ owner (the specimen's exact 24-grid path, no tail/notch). */}
120
126
  <svg
121
127
  className="dc-cursor-arrow"
122
- width="20"
123
- height="22"
124
- viewBox="0 0 20 22"
128
+ width="15"
129
+ height="15"
130
+ viewBox="0 0 24 24"
125
131
  aria-hidden="true"
126
132
  >
127
- <path
128
- d="M 2 2 L 2 18 L 6 14 L 9 21 L 12 20 L 9 13 L 14 13 Z"
129
- fill={peer.color}
130
- stroke="#fff"
131
- strokeWidth="1.2"
132
- strokeLinejoin="round"
133
- />
133
+ <path d="M4 3 L20 11.5 L12.5 13 L10 21 Z" fill={peer.color} />
134
134
  </svg>
135
135
  <div className="dc-cursor-label" style={{ background: peer.color }}>
136
136
  {peer.name}