@elytracms/studio 0.0.92 → 0.0.94

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 (599) hide show
  1. package/convex/analytics-cost.test.ts +817 -0
  2. package/convex/asset-alt-migration.test.ts +127 -0
  3. package/convex/asset-replace.test.ts +168 -0
  4. package/convex/asset-revalidate.test.ts +6 -6
  5. package/convex/assets.ts +41 -5
  6. package/convex/auth.ts +5 -2
  7. package/convex/authority.ts +35 -11
  8. package/convex/cms.ts +25 -11
  9. package/convex/contract-parity.test.ts +25 -3
  10. package/convex/delivery.ts +12 -9
  11. package/convex/document-revalidate.test.ts +10 -12
  12. package/convex/guard.ts +9 -7
  13. package/convex/http-routes.test.ts +18 -8
  14. package/convex/http.ts +23 -17
  15. package/convex/ingest.test.ts +326 -0
  16. package/convex/ingest.ts +281 -0
  17. package/convex/invites.ts +13 -5
  18. package/convex/members.ts +10 -4
  19. package/convex/orphan-hydration.test.ts +116 -0
  20. package/convex/rail-write-parity.test.ts +11 -8
  21. package/convex/record-functions.ts +325 -0
  22. package/convex/records.test.ts +156 -0
  23. package/convex/redirect-migration.test.ts +205 -0
  24. package/convex/redirect-revalidate.test.ts +5 -1
  25. package/convex/redirects.ts +120 -79
  26. package/convex/role-policy.test.ts +315 -0
  27. package/convex/roles.ts +83 -0
  28. package/convex/schema.ts +36 -0
  29. package/convex/triggers.ts +21 -1
  30. package/dist/addon.d.ts +71 -0
  31. package/dist/addon.js +67 -0
  32. package/dist/addon.js.map +1 -0
  33. package/dist/components/account/account-settings.js +2 -2
  34. package/dist/components/account/account-settings.js.map +1 -1
  35. package/dist/components/account/credential-panel.js +2 -2
  36. package/dist/components/account/credential-panel.js.map +1 -1
  37. package/dist/components/account/self-account-schema.d.ts +6 -15
  38. package/dist/components/account/self-account-schema.js +52 -34
  39. package/dist/components/account/self-account-schema.js.map +1 -1
  40. package/dist/components/account/self-account-view-source.d.ts +1 -1
  41. package/dist/components/account/self-account-view-source.js +6 -4
  42. package/dist/components/account/self-account-view-source.js.map +1 -1
  43. package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
  44. package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
  45. package/dist/components/ai/ai-chat-panel.js +6 -6
  46. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  47. package/dist/components/ai/ai-cms-panel.js +2 -2
  48. package/dist/components/ai/ai-cms-panel.js.map +1 -1
  49. package/dist/components/ai/ai-panel-toggle.js +4 -4
  50. package/dist/components/ai/ai-panel-toggle.js.map +1 -1
  51. package/dist/components/ai/chat-ui.js +2 -2
  52. package/dist/components/ai/chat-ui.js.map +1 -1
  53. package/dist/components/ai/proposal-card.d.ts +2 -2
  54. package/dist/components/ai/proposal-card.js +4 -3
  55. package/dist/components/ai/proposal-card.js.map +1 -1
  56. package/dist/components/ai/proposal-issues.js +2 -2
  57. package/dist/components/ai/proposal-issues.js.map +1 -1
  58. package/dist/components/ai/translate-document-action.js +2 -2
  59. package/dist/components/ai/translate-document-action.js.map +1 -1
  60. package/dist/components/ai/translate-document-dialog.js +10 -8
  61. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  62. package/dist/components/ai/translate-skipped.d.ts +19 -0
  63. package/dist/components/ai/translate-skipped.js +46 -0
  64. package/dist/components/ai/translate-skipped.js.map +1 -0
  65. package/dist/components/app-views/default-modules.d.ts +50 -2
  66. package/dist/components/app-views/default-modules.js +65 -20
  67. package/dist/components/app-views/default-modules.js.map +1 -1
  68. package/dist/components/app-views/module.d.ts +53 -3
  69. package/dist/components/app-views/module.js +14 -7
  70. package/dist/components/app-views/module.js.map +1 -1
  71. package/dist/components/app-views/sidebar.js +1 -1
  72. package/dist/components/app-views/sidebar.js.map +1 -1
  73. package/dist/components/app-views/studio-config.d.ts +11 -2
  74. package/dist/components/app-views/studio-config.js.map +1 -1
  75. package/dist/components/assets/asset-detail.js +20 -10
  76. package/dist/components/assets/asset-detail.js.map +1 -1
  77. package/dist/components/assets/asset-grid.js +2 -2
  78. package/dist/components/assets/asset-grid.js.map +1 -1
  79. package/dist/components/assets/asset-metadata.d.ts +15 -4
  80. package/dist/components/assets/asset-metadata.js +15 -2
  81. package/dist/components/assets/asset-metadata.js.map +1 -1
  82. package/dist/components/assets/asset-search.js +4 -1
  83. package/dist/components/assets/asset-search.js.map +1 -1
  84. package/dist/components/assets/asset-selection.d.ts +0 -8
  85. package/dist/components/assets/asset-selection.js +4 -23
  86. package/dist/components/assets/asset-selection.js.map +1 -1
  87. package/dist/components/assets/asset-thumb.js +6 -5
  88. package/dist/components/assets/asset-thumb.js.map +1 -1
  89. package/dist/components/assets/assets-collection-view-source.d.ts +5 -5
  90. package/dist/components/assets/assets-collection-view-source.js +9 -5
  91. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  92. package/dist/components/assets/assets-module.js +1 -1
  93. package/dist/components/assets/assets-module.js.map +1 -1
  94. package/dist/components/assets/assets-store.d.ts +16 -2
  95. package/dist/components/assets/assets-store.js +28 -6
  96. package/dist/components/assets/assets-store.js.map +1 -1
  97. package/dist/components/assets/assets-view-preference.js +1 -1
  98. package/dist/components/assets/assets-view-preference.js.map +1 -1
  99. package/dist/components/assets/replace-file.d.ts +22 -0
  100. package/dist/components/assets/replace-file.js +59 -0
  101. package/dist/components/assets/replace-file.js.map +1 -0
  102. package/dist/components/auth-context.d.ts +1 -1
  103. package/dist/components/auth-context.js +6 -6
  104. package/dist/components/auth-context.js.map +1 -1
  105. package/dist/components/builder/blocks-model.d.ts +15 -27
  106. package/dist/components/builder/blocks-model.js +38 -44
  107. package/dist/components/builder/blocks-model.js.map +1 -1
  108. package/dist/components/builder/blocks-panel.d.ts +8 -4
  109. package/dist/components/builder/blocks-panel.js +10 -9
  110. package/dist/components/builder/blocks-panel.js.map +1 -1
  111. package/dist/components/builder/builder-store.d.ts +104 -15
  112. package/dist/components/builder/builder-store.js +157 -32
  113. package/dist/components/builder/builder-store.js.map +1 -1
  114. package/dist/components/builder/canvas-body.d.ts +30 -0
  115. package/dist/components/builder/canvas-body.js +38 -0
  116. package/dist/components/builder/canvas-body.js.map +1 -0
  117. package/dist/components/builder/canvas-breakpoints.js +1 -1
  118. package/dist/components/builder/canvas-breakpoints.js.map +1 -1
  119. package/dist/components/builder/canvas-chrome-css.js +7 -0
  120. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  121. package/dist/components/builder/canvas-dnd.d.ts +9 -9
  122. package/dist/components/builder/canvas-dnd.js +6 -6
  123. package/dist/components/builder/canvas-dnd.js.map +1 -1
  124. package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
  125. package/dist/components/builder/canvas-inline-editing.js.map +1 -1
  126. package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
  127. package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
  128. package/dist/components/builder/canvas-render-context.d.ts +28 -20
  129. package/dist/components/builder/canvas-render-context.js +46 -47
  130. package/dist/components/builder/canvas-render-context.js.map +1 -1
  131. package/dist/components/builder/canvas-runs.d.ts +45 -0
  132. package/dist/components/builder/canvas-runs.js +51 -0
  133. package/dist/components/builder/canvas-runs.js.map +1 -0
  134. package/dist/components/builder/canvas-theme-notice.js +2 -2
  135. package/dist/components/builder/canvas-theme-notice.js.map +1 -1
  136. package/dist/components/builder/canvas.js +35 -8
  137. package/dist/components/builder/canvas.js.map +1 -1
  138. package/dist/components/builder/clipboard-actions.d.ts +16 -1
  139. package/dist/components/builder/clipboard-actions.js +48 -11
  140. package/dist/components/builder/clipboard-actions.js.map +1 -1
  141. package/dist/components/builder/composition-field-editor-lazy.js +1 -1
  142. package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
  143. package/dist/components/builder/composition-field-editor.d.ts +4 -16
  144. package/dist/components/builder/composition-field-editor.js +32 -43
  145. package/dist/components/builder/composition-field-editor.js.map +1 -1
  146. package/dist/components/builder/delete-key.d.ts +5 -0
  147. package/dist/components/builder/delete-key.js +4 -0
  148. package/dist/components/builder/delete-key.js.map +1 -1
  149. package/dist/components/builder/drag-session.d.ts +1 -1
  150. package/dist/components/builder/drag-session.js.map +1 -1
  151. package/dist/components/builder/fixture-thumbnail.js +1 -1
  152. package/dist/components/builder/fixture-thumbnail.js.map +1 -1
  153. package/dist/components/builder/insert-context.d.ts +18 -26
  154. package/dist/components/builder/insert-context.js +47 -94
  155. package/dist/components/builder/insert-context.js.map +1 -1
  156. package/dist/components/builder/layout-inspector.js +1 -1
  157. package/dist/components/builder/layout-inspector.js.map +1 -1
  158. package/dist/components/builder/node-title.js +1 -1
  159. package/dist/components/builder/node-title.js.map +1 -1
  160. package/dist/components/builder/page-tree.d.ts +4 -4
  161. package/dist/components/builder/page-tree.js +8 -8
  162. package/dist/components/builder/page-tree.js.map +1 -1
  163. package/dist/components/builder/placement-refusal.d.ts +18 -0
  164. package/dist/components/builder/placement-refusal.js +46 -0
  165. package/dist/components/builder/placement-refusal.js.map +1 -0
  166. package/dist/components/builder/property-inspector.js +8 -4
  167. package/dist/components/builder/property-inspector.js.map +1 -1
  168. package/dist/components/builder/reuse-context.js +68 -30
  169. package/dist/components/builder/reuse-context.js.map +1 -1
  170. package/dist/components/builder/reuse.d.ts +79 -1
  171. package/dist/components/builder/reuse.js +70 -3
  172. package/dist/components/builder/reuse.js.map +1 -1
  173. package/dist/components/builder/rich-text-toolbar.js +1 -1
  174. package/dist/components/builder/rich-text-toolbar.js.map +1 -1
  175. package/dist/components/builder/run-inspector.d.ts +11 -0
  176. package/dist/components/builder/run-inspector.js +33 -0
  177. package/dist/components/builder/run-inspector.js.map +1 -0
  178. package/dist/components/builder/selection-overlay.js +3 -3
  179. package/dist/components/builder/selection-overlay.js.map +1 -1
  180. package/dist/components/builder/spacing-handles.d.ts +2 -2
  181. package/dist/components/builder/spacing-handles.js +3 -3
  182. package/dist/components/builder/spacing-handles.js.map +1 -1
  183. package/dist/components/cms/asset-field.d.ts +38 -1
  184. package/dist/components/cms/asset-field.js +184 -60
  185. package/dist/components/cms/asset-field.js.map +1 -1
  186. package/dist/components/cms/asset-picker-core.d.ts +16 -0
  187. package/dist/components/cms/asset-picker-core.js +20 -0
  188. package/dist/components/cms/asset-picker-core.js.map +1 -1
  189. package/dist/components/cms/asset-picker.js +14 -20
  190. package/dist/components/cms/asset-picker.js.map +1 -1
  191. package/dist/components/cms/bulk-actions.d.ts +4 -20
  192. package/dist/components/cms/bulk-actions.js +5 -26
  193. package/dist/components/cms/bulk-actions.js.map +1 -1
  194. package/dist/components/cms/bulk-bar.d.ts +2 -2
  195. package/dist/components/cms/bulk-bar.js +2 -2
  196. package/dist/components/cms/bulk-bar.js.map +1 -1
  197. package/dist/components/cms/cell-edit.d.ts +1 -1
  198. package/dist/components/cms/cell-edit.js +1 -1
  199. package/dist/components/cms/cell-edit.js.map +1 -1
  200. package/dist/components/cms/cell.d.ts +1 -1
  201. package/dist/components/cms/cell.js +3 -3
  202. package/dist/components/cms/cell.js.map +1 -1
  203. package/dist/components/cms/cms-index.js +2 -2
  204. package/dist/components/cms/cms-index.js.map +1 -1
  205. package/dist/components/cms/cms-labels.js +2 -2
  206. package/dist/components/cms/cms-labels.js.map +1 -1
  207. package/dist/components/cms/cms-store.d.ts +7 -6
  208. package/dist/components/cms/cms-store.js +23 -43
  209. package/dist/components/cms/cms-store.js.map +1 -1
  210. package/dist/components/cms/collection-table.js +7 -7
  211. package/dist/components/cms/collection-table.js.map +1 -1
  212. package/dist/components/cms/detail-editor.d.ts +5 -2
  213. package/dist/components/cms/detail-editor.js +20 -15
  214. package/dist/components/cms/detail-editor.js.map +1 -1
  215. package/dist/components/cms/details-pane.js +1 -1
  216. package/dist/components/cms/details-pane.js.map +1 -1
  217. package/dist/components/cms/doc-state.js +1 -1
  218. package/dist/components/cms/doc-state.js.map +1 -1
  219. package/dist/components/cms/document-diff-view.d.ts +3 -1
  220. package/dist/components/cms/document-diff-view.js +17 -12
  221. package/dist/components/cms/document-diff-view.js.map +1 -1
  222. package/dist/components/cms/document-editor-context.js +1 -1
  223. package/dist/components/cms/document-editor-context.js.map +1 -1
  224. package/dist/components/cms/document-staging.js +1 -1
  225. package/dist/components/cms/document-staging.js.map +1 -1
  226. package/dist/components/cms/document-url-path.js +2 -2
  227. package/dist/components/cms/document-url-path.js.map +1 -1
  228. package/dist/components/cms/duplicate-document.d.ts +1 -1
  229. package/dist/components/cms/duplicate-document.js.map +1 -1
  230. package/dist/components/cms/editable-cell.js +3 -3
  231. package/dist/components/cms/editable-cell.js.map +1 -1
  232. package/dist/components/cms/field-anchors.d.ts +7 -4
  233. package/dist/components/cms/field-anchors.js +9 -7
  234. package/dist/components/cms/field-anchors.js.map +1 -1
  235. package/dist/components/cms/field-collaboration.d.ts +1 -1
  236. package/dist/components/cms/field-collaboration.js +1 -1
  237. package/dist/components/cms/field-collaboration.js.map +1 -1
  238. package/dist/components/cms/field-connection.d.ts +1 -1
  239. package/dist/components/cms/field-connection.js +2 -2
  240. package/dist/components/cms/field-connection.js.map +1 -1
  241. package/dist/components/cms/field-control-lib.d.ts +15 -1
  242. package/dist/components/cms/field-control-lib.js +23 -4
  243. package/dist/components/cms/field-control-lib.js.map +1 -1
  244. package/dist/components/cms/field-control.js +4 -4
  245. package/dist/components/cms/field-control.js.map +1 -1
  246. package/dist/components/cms/field-renderer.d.ts +1 -1
  247. package/dist/components/cms/field-renderer.js +2 -2
  248. package/dist/components/cms/field-renderer.js.map +1 -1
  249. package/dist/components/cms/filter-builder.d.ts +1 -1
  250. package/dist/components/cms/filter-builder.js +3 -3
  251. package/dist/components/cms/filter-builder.js.map +1 -1
  252. package/dist/components/cms/gate-validation.d.ts +21 -30
  253. package/dist/components/cms/gate-validation.js +11 -32
  254. package/dist/components/cms/gate-validation.js.map +1 -1
  255. package/dist/components/cms/import-export.js +2 -2
  256. package/dist/components/cms/import-export.js.map +1 -1
  257. package/dist/components/cms/issue-copy.js +1 -1
  258. package/dist/components/cms/issue-copy.js.map +1 -1
  259. package/dist/components/cms/issues-pane.js +3 -3
  260. package/dist/components/cms/issues-pane.js.map +1 -1
  261. package/dist/components/cms/object-array-field.js +6 -6
  262. package/dist/components/cms/object-array-field.js.map +1 -1
  263. package/dist/components/cms/prop-fields.d.ts +7 -2
  264. package/dist/components/cms/prop-fields.js +14 -7
  265. package/dist/components/cms/prop-fields.js.map +1 -1
  266. package/dist/components/cms/reference-confirm.d.ts +1 -1
  267. package/dist/components/cms/reference-confirm.js +2 -2
  268. package/dist/components/cms/reference-confirm.js.map +1 -1
  269. package/dist/components/cms/relation-picker.js +2 -2
  270. package/dist/components/cms/relation-picker.js.map +1 -1
  271. package/dist/components/cms/rich-text/block-style.js +1 -1
  272. package/dist/components/cms/rich-text/block-style.js.map +1 -1
  273. package/dist/components/cms/rich-text/component-embed.js +4 -4
  274. package/dist/components/cms/rich-text/component-embed.js.map +1 -1
  275. package/dist/components/cms/rich-text/editor.js +3 -3
  276. package/dist/components/cms/rich-text/editor.js.map +1 -1
  277. package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
  278. package/dist/components/cms/rich-text/embed-picker.js +3 -3
  279. package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
  280. package/dist/components/cms/rich-text/embed-props.js +3 -3
  281. package/dist/components/cms/rich-text/embed-props.js.map +1 -1
  282. package/dist/components/cms/rich-text/extensions.js +1 -1
  283. package/dist/components/cms/rich-text/extensions.js.map +1 -1
  284. package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
  285. package/dist/components/cms/rich-text/font-size-control.js +2 -2
  286. package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
  287. package/dist/components/cms/rich-text/insert.d.ts +20 -9
  288. package/dist/components/cms/rich-text/insert.js +25 -17
  289. package/dist/components/cms/rich-text/insert.js.map +1 -1
  290. package/dist/components/cms/rich-text/lazy.js +1 -1
  291. package/dist/components/cms/rich-text/lazy.js.map +1 -1
  292. package/dist/components/cms/rich-text/link-control.js +2 -2
  293. package/dist/components/cms/rich-text/link-control.js.map +1 -1
  294. package/dist/components/cms/rich-text/slash-command.js +3 -3
  295. package/dist/components/cms/rich-text/slash-command.js.map +1 -1
  296. package/dist/components/cms/rich-text/toolbar.js +1 -1
  297. package/dist/components/cms/rich-text/toolbar.js.map +1 -1
  298. package/dist/components/cms/rich-text/unknown-node.js +2 -2
  299. package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
  300. package/dist/components/cms/rich-text-preview.js +2 -2
  301. package/dist/components/cms/rich-text-preview.js.map +1 -1
  302. package/dist/components/cms/row-actions.d.ts +1 -1
  303. package/dist/components/cms/row-actions.js.map +1 -1
  304. package/dist/components/cms/scalar-value-editor.d.ts +1 -1
  305. package/dist/components/cms/scalar-value-editor.js +2 -2
  306. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  307. package/dist/components/cms/use-discard-controller.d.ts +1 -1
  308. package/dist/components/cms/use-discard-controller.js +1 -1
  309. package/dist/components/cms/use-discard-controller.js.map +1 -1
  310. package/dist/components/cms/use-document-draft.d.ts +7 -1
  311. package/dist/components/cms/use-document-draft.js +16 -2
  312. package/dist/components/cms/use-document-draft.js.map +1 -1
  313. package/dist/components/cms/use-document-versions.js +1 -1
  314. package/dist/components/cms/use-document-versions.js.map +1 -1
  315. package/dist/components/cms/use-field-focus.d.ts +1 -1
  316. package/dist/components/cms/use-field-focus.js +2 -2
  317. package/dist/components/cms/use-field-focus.js.map +1 -1
  318. package/dist/components/cms/use-live-url.d.ts +1 -1
  319. package/dist/components/cms/use-live-url.js +3 -3
  320. package/dist/components/cms/use-live-url.js.map +1 -1
  321. package/dist/components/cms/use-publish-controller.d.ts +2 -3
  322. package/dist/components/cms/use-publish-controller.js +8 -20
  323. package/dist/components/cms/use-publish-controller.js.map +1 -1
  324. package/dist/components/cms/use-published-perspective.d.ts +1 -1
  325. package/dist/components/cms/use-published-perspective.js.map +1 -1
  326. package/dist/components/cms/use-restore-version.d.ts +1 -1
  327. package/dist/components/cms/use-restore-version.js +1 -1
  328. package/dist/components/cms/use-restore-version.js.map +1 -1
  329. package/dist/components/cms/version-compare-lazy.js +1 -1
  330. package/dist/components/cms/version-compare-lazy.js.map +1 -1
  331. package/dist/components/cms/version-compare.js +1 -1
  332. package/dist/components/cms/version-compare.js.map +1 -1
  333. package/dist/components/cms/versions-pane.js +11 -4
  334. package/dist/components/cms/versions-pane.js.map +1 -1
  335. package/dist/components/cms/view-bar.js +3 -3
  336. package/dist/components/cms/view-bar.js.map +1 -1
  337. package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
  338. package/dist/components/collection-view/cms-collection-view-source.js +5 -3
  339. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  340. package/dist/components/collection-view/collection-view-context.d.ts +1 -1
  341. package/dist/components/collection-view/collection-view-context.js +1 -1
  342. package/dist/components/collection-view/collection-view-context.js.map +1 -1
  343. package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
  344. package/dist/components/collection-view/collection-view-surface.js +18 -0
  345. package/dist/components/collection-view/collection-view-surface.js.map +1 -0
  346. package/dist/components/components/component-detail.js +10 -10
  347. package/dist/components/components/component-detail.js.map +1 -1
  348. package/dist/components/components/component-preview.d.ts +2 -2
  349. package/dist/components/components/component-preview.js +5 -5
  350. package/dist/components/components/component-preview.js.map +1 -1
  351. package/dist/components/components/component-status.d.ts +1 -1
  352. package/dist/components/components/component-status.js +1 -1
  353. package/dist/components/components/component-status.js.map +1 -1
  354. package/dist/components/components/components-index.js +2 -2
  355. package/dist/components/components/components-index.js.map +1 -1
  356. package/dist/components/components/field-inspector.js +1 -1
  357. package/dist/components/components/field-inspector.js.map +1 -1
  358. package/dist/components/components/registry-browser.js +3 -3
  359. package/dist/components/components/registry-browser.js.map +1 -1
  360. package/dist/components/components/taxonomy.d.ts +1 -1
  361. package/dist/components/components/taxonomy.js.map +1 -1
  362. package/dist/components/components/transfer-dialog.js +4 -3
  363. package/dist/components/components/transfer-dialog.js.map +1 -1
  364. package/dist/components/components/transfer.d.ts +10 -10
  365. package/dist/components/components/transfer.js +13 -12
  366. package/dist/components/components/transfer.js.map +1 -1
  367. package/dist/components/current-user-menu.js +1 -1
  368. package/dist/components/current-user-menu.js.map +1 -1
  369. package/dist/components/delivery-health-banner.js +2 -2
  370. package/dist/components/delivery-health-banner.js.map +1 -1
  371. package/dist/components/deployment-setup-gate.js +3 -3
  372. package/dist/components/deployment-setup-gate.js.map +1 -1
  373. package/dist/components/dev-login-badge.js +2 -2
  374. package/dist/components/dev-login-badge.js.map +1 -1
  375. package/dist/components/guards.js +1 -1
  376. package/dist/components/guards.js.map +1 -1
  377. package/dist/components/inspector/document-panes.d.ts +54 -0
  378. package/dist/components/inspector/document-panes.js +41 -0
  379. package/dist/components/inspector/document-panes.js.map +1 -0
  380. package/dist/components/inspector/inspector-sidebar.js +1 -1
  381. package/dist/components/inspector/inspector-sidebar.js.map +1 -1
  382. package/dist/components/nav-issues-registry.d.ts +53 -0
  383. package/dist/components/nav-issues-registry.js +69 -0
  384. package/dist/components/nav-issues-registry.js.map +1 -0
  385. package/dist/components/nav-issues.js +4 -4
  386. package/dist/components/nav-issues.js.map +1 -1
  387. package/dist/components/operation-error.d.ts +1 -1
  388. package/dist/components/operation-error.js +4 -4
  389. package/dist/components/operation-error.js.map +1 -1
  390. package/dist/components/playground-badge.js +2 -2
  391. package/dist/components/playground-badge.js.map +1 -1
  392. package/dist/components/project-list.js +3 -3
  393. package/dist/components/project-list.js.map +1 -1
  394. package/dist/components/project-session.d.ts +2 -2
  395. package/dist/components/project-session.js +5 -5
  396. package/dist/components/project-session.js.map +1 -1
  397. package/dist/components/project-switcher.js +2 -2
  398. package/dist/components/project-switcher.js.map +1 -1
  399. package/dist/components/rail-store-status.js +1 -1
  400. package/dist/components/rail-store-status.js.map +1 -1
  401. package/dist/components/rail-view-source.d.ts +6 -5
  402. package/dist/components/rail-view-source.js +4 -3
  403. package/dist/components/rail-view-source.js.map +1 -1
  404. package/dist/components/records/record-lens.d.ts +51 -0
  405. package/dist/components/records/record-lens.js +156 -0
  406. package/dist/components/records/record-lens.js.map +1 -0
  407. package/dist/components/records/record-rail.d.ts +86 -0
  408. package/dist/components/records/record-rail.js +241 -0
  409. package/dist/components/records/record-rail.js.map +1 -0
  410. package/dist/components/redirects/redirects-schema.js +1 -1
  411. package/dist/components/redirects/redirects-schema.js.map +1 -1
  412. package/dist/components/references/references-panel.js +2 -2
  413. package/dist/components/references/references-panel.js.map +1 -1
  414. package/dist/components/section-nav.js +27 -11
  415. package/dist/components/section-nav.js.map +1 -1
  416. package/dist/components/section-state.js +2 -2
  417. package/dist/components/section-state.js.map +1 -1
  418. package/dist/components/section-surface.js +2 -2
  419. package/dist/components/section-surface.js.map +1 -1
  420. package/dist/components/session-expired-overlay.js +3 -3
  421. package/dist/components/session-expired-overlay.js.map +1 -1
  422. package/dist/components/settings-form.js +1 -1
  423. package/dist/components/settings-form.js.map +1 -1
  424. package/dist/components/sign-in-needed-badge.js +1 -1
  425. package/dist/components/sign-in-needed-badge.js.map +1 -1
  426. package/dist/components/sign-in-view.d.ts +2 -2
  427. package/dist/components/sign-in-view.js +5 -5
  428. package/dist/components/sign-in-view.js.map +1 -1
  429. package/dist/components/studio-context.js +3 -3
  430. package/dist/components/studio-context.js.map +1 -1
  431. package/dist/components/studio-project-screen.js +30 -3
  432. package/dist/components/studio-project-screen.js.map +1 -1
  433. package/dist/components/studio-shell.js +6 -5
  434. package/dist/components/studio-shell.js.map +1 -1
  435. package/dist/components/theme-context.d.ts +1 -1
  436. package/dist/components/theme-context.js +2 -2
  437. package/dist/components/theme-context.js.map +1 -1
  438. package/dist/components/use-hydration-phase.d.ts +1 -1
  439. package/dist/components/use-hydration-phase.js +1 -1
  440. package/dist/components/use-hydration-phase.js.map +1 -1
  441. package/dist/components/users/invite-user-bar.d.ts +27 -0
  442. package/dist/components/users/invite-user-bar.js +25 -9
  443. package/dist/components/users/invite-user-bar.js.map +1 -1
  444. package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
  445. package/dist/components/users/role-policy-drift-issues.js +71 -0
  446. package/dist/components/users/role-policy-drift-issues.js.map +1 -0
  447. package/dist/components/users/users-collection-view-source.d.ts +1 -1
  448. package/dist/components/users/users-collection-view-source.js +4 -2
  449. package/dist/components/users/users-collection-view-source.js.map +1 -1
  450. package/dist/components/users/users-module.js +1 -1
  451. package/dist/components/users/users-module.js.map +1 -1
  452. package/dist/components/users/users-schema.d.ts +10 -1
  453. package/dist/components/users/users-schema.js +45 -25
  454. package/dist/components/users/users-schema.js.map +1 -1
  455. package/dist/components/users/users-store.js +3 -3
  456. package/dist/components/users/users-store.js.map +1 -1
  457. package/dist/components/workspace-projects.d.ts +1 -1
  458. package/dist/components/workspace-projects.js +1 -1
  459. package/dist/components/workspace-projects.js.map +1 -1
  460. package/dist/lib/ai/apply-content.js +56 -116
  461. package/dist/lib/ai/apply-content.js.map +1 -1
  462. package/dist/lib/ai/chat-context.d.ts +8 -0
  463. package/dist/lib/ai/chat-context.js +37 -6
  464. package/dist/lib/ai/chat-context.js.map +1 -1
  465. package/dist/lib/ai/chat-state.d.ts +6 -0
  466. package/dist/lib/ai/chat-state.js.map +1 -1
  467. package/dist/lib/ai/constraints.d.ts +20 -5
  468. package/dist/lib/ai/constraints.js +125 -33
  469. package/dist/lib/ai/constraints.js.map +1 -1
  470. package/dist/lib/ai/fake-provider.d.ts +16 -3
  471. package/dist/lib/ai/fake-provider.js +22 -6
  472. package/dist/lib/ai/fake-provider.js.map +1 -1
  473. package/dist/lib/ai/index.d.ts +3 -1
  474. package/dist/lib/ai/index.js +3 -1
  475. package/dist/lib/ai/index.js.map +1 -1
  476. package/dist/lib/ai/leash.d.ts +34 -0
  477. package/dist/lib/ai/leash.js +28 -0
  478. package/dist/lib/ai/leash.js.map +1 -0
  479. package/dist/lib/ai/node-handler.d.ts +1 -1
  480. package/dist/lib/ai/node-handler.js.map +1 -1
  481. package/dist/lib/ai/rich-text-input.d.ts +8 -8
  482. package/dist/lib/ai/rich-text-input.js +2 -1
  483. package/dist/lib/ai/rich-text-input.js.map +1 -1
  484. package/dist/lib/ai/rich-text-translate.d.ts +27 -4
  485. package/dist/lib/ai/rich-text-translate.js +28 -10
  486. package/dist/lib/ai/rich-text-translate.js.map +1 -1
  487. package/dist/lib/ai/shape.d.ts +13 -13
  488. package/dist/lib/ai/shape.js +69 -93
  489. package/dist/lib/ai/shape.js.map +1 -1
  490. package/dist/lib/ai/tools.d.ts +36 -41
  491. package/dist/lib/ai/tools.js +83 -87
  492. package/dist/lib/ai/tools.js.map +1 -1
  493. package/dist/lib/ai/translate-document.d.ts +32 -4
  494. package/dist/lib/ai/translate-document.js +145 -47
  495. package/dist/lib/ai/translate-document.js.map +1 -1
  496. package/dist/lib/ai/types.d.ts +34 -23
  497. package/dist/lib/ai/types.js.map +1 -1
  498. package/dist/lib/ai/update-fields.d.ts +15 -12
  499. package/dist/lib/ai/update-fields.js +112 -188
  500. package/dist/lib/ai/update-fields.js.map +1 -1
  501. package/dist/lib/ai/writer-form.d.ts +116 -0
  502. package/dist/lib/ai/writer-form.js +429 -0
  503. package/dist/lib/ai/writer-form.js.map +1 -0
  504. package/dist/lib/assets/upload.d.ts +31 -2
  505. package/dist/lib/assets/upload.js +61 -6
  506. package/dist/lib/assets/upload.js.map +1 -1
  507. package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
  508. package/dist/lib/clipboard/os-clipboard.js +8 -4
  509. package/dist/lib/clipboard/os-clipboard.js.map +1 -1
  510. package/dist/lib/clipboard/paste-issues.js +1 -1
  511. package/dist/lib/clipboard/paste-issues.js.map +1 -1
  512. package/dist/lib/clipboard/paste-target.d.ts +18 -9
  513. package/dist/lib/clipboard/paste-target.js +10 -9
  514. package/dist/lib/clipboard/paste-target.js.map +1 -1
  515. package/dist/lib/cms/field-patch.d.ts +0 -10
  516. package/dist/lib/cms/field-patch.js +4 -30
  517. package/dist/lib/cms/field-patch.js.map +1 -1
  518. package/dist/lib/cms/published-documents.js +1 -1
  519. package/dist/lib/cms/published-documents.js.map +1 -1
  520. package/dist/lib/cms-views/view-model.d.ts +9 -9
  521. package/dist/lib/cms-views/view-model.js +2 -2
  522. package/dist/lib/cms-views/view-model.js.map +1 -1
  523. package/dist/lib/collection-view/collection-view-source.d.ts +14 -5
  524. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  525. package/dist/lib/data/collection-content.js +3 -2
  526. package/dist/lib/data/collection-content.js.map +1 -1
  527. package/dist/lib/data/content-session-refresh.d.ts +1 -1
  528. package/dist/lib/data/content-session-refresh.js.map +1 -1
  529. package/dist/lib/data/playground-unload-guard.d.ts +1 -1
  530. package/dist/lib/data/playground-unload-guard.js +1 -1
  531. package/dist/lib/data/playground-unload-guard.js.map +1 -1
  532. package/dist/lib/dnd/drop-rules.d.ts +25 -31
  533. package/dist/lib/dnd/drop-rules.js +53 -59
  534. package/dist/lib/dnd/drop-rules.js.map +1 -1
  535. package/dist/lib/engine-version.d.ts +1 -1
  536. package/dist/lib/engine-version.js +1 -1
  537. package/dist/lib/engine-version.js.map +1 -1
  538. package/dist/lib/i18n/i18n.js +1 -1
  539. package/dist/lib/i18n/i18n.js.map +1 -1
  540. package/dist/lib/i18n/messages.de.js +44 -3
  541. package/dist/lib/i18n/messages.de.js.map +1 -1
  542. package/dist/lib/i18n/messages.en.d.ts +38 -2
  543. package/dist/lib/i18n/messages.en.js +51 -3
  544. package/dist/lib/i18n/messages.en.js.map +1 -1
  545. package/dist/lib/operations/project-runtime-config.d.ts +34 -6
  546. package/dist/lib/operations/project-runtime-config.js +50 -10
  547. package/dist/lib/operations/project-runtime-config.js.map +1 -1
  548. package/dist/lib/operations/publish-with-revision.d.ts +33 -1
  549. package/dist/lib/operations/publish-with-revision.js +32 -0
  550. package/dist/lib/operations/publish-with-revision.js.map +1 -1
  551. package/dist/lib/operations/runtime-validator.d.ts +19 -68
  552. package/dist/lib/operations/runtime-validator.js +12 -138
  553. package/dist/lib/operations/runtime-validator.js.map +1 -1
  554. package/dist/lib/operations/studio-operations.d.ts +17 -2
  555. package/dist/lib/operations/studio-operations.js +18 -11
  556. package/dist/lib/operations/studio-operations.js.map +1 -1
  557. package/dist/lib/persistence/convex-adapter.d.ts +16 -3
  558. package/dist/lib/persistence/convex-adapter.js +83 -47
  559. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  560. package/dist/lib/persistence/index.js +14 -1
  561. package/dist/lib/persistence/index.js.map +1 -1
  562. package/dist/lib/persona/labels.js +4 -2
  563. package/dist/lib/persona/labels.js.map +1 -1
  564. package/dist/lib/query/operations-query.d.ts +1 -1
  565. package/dist/lib/query/operations-query.js +1 -1
  566. package/dist/lib/query/operations-query.js.map +1 -1
  567. package/dist/lib/references/reference-model.js +1 -1
  568. package/dist/lib/references/reference-model.js.map +1 -1
  569. package/dist/lib/references/references-store.js +1 -1
  570. package/dist/lib/references/references-store.js.map +1 -1
  571. package/dist/lib/workspace/index.d.ts +14 -11
  572. package/dist/lib/workspace/index.js +20 -24
  573. package/dist/lib/workspace/index.js.map +1 -1
  574. package/dist/mount.d.ts +28 -0
  575. package/dist/mount.js +27 -0
  576. package/dist/mount.js.map +1 -0
  577. package/dist/router/root.js +10 -10
  578. package/dist/router/root.js.map +1 -1
  579. package/dist/ui/badge.d.ts +1 -1
  580. package/dist/ui/button.d.ts +2 -2
  581. package/package.json +15 -11
  582. package/dist/components/cms/cms-composition-validators.d.ts +0 -16
  583. package/dist/components/cms/cms-composition-validators.js +0 -133
  584. package/dist/components/cms/cms-composition-validators.js.map +0 -1
  585. package/dist/components/cms/publish-clean.d.ts +0 -28
  586. package/dist/components/cms/publish-clean.js +0 -38
  587. package/dist/components/cms/publish-clean.js.map +0 -1
  588. package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
  589. package/dist/components/redirects/redirects-collection-view-source.js +0 -146
  590. package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
  591. package/dist/components/redirects/redirects-module.d.ts +0 -21
  592. package/dist/components/redirects/redirects-module.js +0 -40
  593. package/dist/components/redirects/redirects-module.js.map +0 -1
  594. package/dist/components/redirects/redirects-store.d.ts +0 -68
  595. package/dist/components/redirects/redirects-store.js +0 -132
  596. package/dist/components/redirects/redirects-store.js.map +0 -1
  597. package/dist/lib/assets/asset-placements.d.ts +0 -28
  598. package/dist/lib/assets/asset-placements.js +0 -83
  599. package/dist/lib/assets/asset-placements.js.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from 'react';
3
- import { AlertTriangle, Pilcrow, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react';
3
+ import { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react';
4
4
  import { DEFAULT_SLOT, mergeBlockRoots, splitBlockRoots } from '@elytracms/core/project-graph';
5
- import { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core';
5
+ import { DRAFT_PERSPECTIVE, fieldAllowList } from '@elytracms/core/cms-core';
6
6
  import { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui';
7
- import { cn } from '@elytracms/studio/lib/utils';
8
- import { translate } from '@elytracms/studio/lib/i18n';
9
- import { getStudioRegistry } from '@elytracms/studio/lib/registry';
7
+ import { cn } from '../../lib/utils.js';
8
+ import { translate } from '../../lib/i18n/index.js';
9
+ import { getStudioRegistry } from '../../lib/registry/index.js';
10
10
  import { BuilderProvider, useBuilder } from './builder-store.js';
11
11
  import { useCanvasViewports } from './canvas-breakpoints.js';
12
12
  import { BuilderCanvas } from './canvas.js';
@@ -18,6 +18,7 @@ import { insertablePositions, resolveInsertContext, resolveInsertContextAt } fro
18
18
  import { InsertAffordanceProvider } from './insert-affordance-context.js';
19
19
  import { PropSyncProvider } from './prop-sync-context.js';
20
20
  import { ReuseProvider } from './reuse-context.js';
21
+ import { PlacementRefusalNotice } from './placement-refusal.js';
21
22
  import { fieldLabel } from '../cms/cms-labels.js';
22
23
  /**
23
24
  * EC-188 — the block-field surface: one canvas engine mounted on a composition
@@ -69,11 +70,13 @@ export function mergeCompositionValue(roots, preserved) {
69
70
  }
70
71
  /**
71
72
  * The field scope the editor's store carries (EC-554): the field's label (named
72
- * by refusal notices) and its top-level `allow`, passed through verbatim — an
73
- * omitted `allow` stays `undefined` (EC-544: any registered component).
73
+ * by refusal notices) and its EFFECTIVE top-level vocabulary (EC-584 — read through
74
+ * the per-FieldType definition, `fieldAllowList`: the declared `allow`, else the
75
+ * `blocks` default, any canvas-eligible component).
74
76
  */
75
77
  export function compositionFieldScope(field) {
76
- return { label: fieldLabel(field), allow: field.allow };
78
+ // A blocks field always has a vocabulary (its type declares the default).
79
+ return { label: fieldLabel(field), allow: fieldAllowList(field) };
77
80
  }
78
81
  /**
79
82
  * Wrap the field value's component roots in a synthetic root container (EC-287).
@@ -139,35 +142,22 @@ export function CompositionFieldEditor({ field, value, onChange, readOnly, trail
139
142
  const fieldScope = useMemo(() => compositionFieldScope(field), [field]);
140
143
  if (!initialRoot)
141
144
  return _jsx(NoStackCanvasNotice, {});
142
- return (_jsxs(BuilderProvider, { composition: initialRoot, field: fieldScope, layout: layout, children: [_jsx(CompositionLift, { preserved: initial.preserved, onChange: onChange, readOnly: readOnly }), _jsx(ReuseProvider, { children: _jsx(PropSyncProvider, { children: _jsx(CompositionEditorShell, { readOnly: readOnly, trailingPanes: trailingPanes, preservedCount: initial.preserved.length, roleNotices: notices, conflicts: conflicts }) }) })] }));
143
- }
144
- /**
145
- * EC-549 interim (scope item 3, the honesty floor): a mixed sequence's typography
146
- * items are preserved for write-back but NOT rendered on the canvas — and a canvas
147
- * that silently shows half the sequence lets an editor rearrange around content
148
- * they cannot see. Until typography items are first-class, path-addressed canvas
149
- * items (EC-549's main scope), the surface must SAY what it is not showing: a
150
- * persistent, non-dismissible count above the canvas. Deliberately NOT the
151
- * dismissible overlay shape (`CanvasThemeNotice`) — this states a standing truth
152
- * about the VALUE, not a one-time migration hint, and it must not cover blocks.
153
- */
154
- export function PreservedItemsNotice({ count }) {
155
- if (count === 0)
156
- return null;
157
- return (_jsxs(Row, { gap: "2", align: "center", "data-testid": "composition-preserved-notice", className: "shrink-0 border-b border-border bg-muted/40 px-3 py-1.5", children: [_jsx(Pilcrow, { "aria-hidden": true, className: "size-3.5 shrink-0 text-muted-foreground" }), _jsx(Text, { size: "xs", tone: "muted", children: translate('builder.composition.preservedNotice', { count }) })] }));
145
+ return (_jsxs(BuilderProvider, { composition: initialRoot, preserved: initial.preserved, field: fieldScope, components: registry.lookup, layout: layout, children: [_jsx(CompositionLift, { onChange: onChange, readOnly: readOnly }), _jsx(ReuseProvider, { children: _jsx(PropSyncProvider, { children: _jsx(CompositionEditorShell, { readOnly: readOnly, trailingPanes: trailingPanes, roleNotices: notices, conflicts: conflicts }) }) })] }));
158
146
  }
159
147
  /**
160
148
  * Lifts committed edits out of the scoped store into the CMS draft. The first
161
149
  * render establishes the (container-normalized) baseline so simply opening the
162
150
  * tab never marks the document dirty; every subsequent change to the block list
163
- * emits the merged v2 sequence value (EC-547) — the canvas roots spliced back
164
- * between the preserved typography items. Cardinality (`multiple`) no longer
165
- * shapes the stored value: it is a validation constraint over the one envelope
166
- * (EC-500 D2), so the bare-object special case for `multiple: false` is gone.
151
+ * OR to the preserved typography (EC-549: a run deleted or edited, or an undo of
152
+ * either) emits the merged v2 sequence value (EC-547) — the canvas roots spliced
153
+ * back between the preserved typography items, both read from the store.
154
+ * Cardinality (`multiple`) no longer shapes the stored value: it is a validation
155
+ * constraint over the one envelope (EC-500 D2).
167
156
  */
168
- function CompositionLift({ preserved, onChange, readOnly, }) {
157
+ function CompositionLift({ onChange, readOnly, }) {
169
158
  const builder = useBuilder();
170
159
  const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS;
160
+ const preserved = builder.preserved;
171
161
  const onChangeRef = useRef(onChange);
172
162
  onChangeRef.current = onChange;
173
163
  const baselineRef = useRef(null);
@@ -180,12 +170,13 @@ function CompositionLift({ preserved, onChange, readOnly, }) {
180
170
  // discarded on switch.
181
171
  if (readOnly)
182
172
  return;
183
- if (baselineRef.current === null) {
184
- baselineRef.current = blocks;
173
+ const baseline = baselineRef.current;
174
+ if (baseline === null) {
175
+ baselineRef.current = { blocks, preserved: preserved };
185
176
  return;
186
177
  }
187
- if (blocks !== baselineRef.current) {
188
- baselineRef.current = blocks;
178
+ if (blocks !== baseline.blocks || preserved !== baseline.preserved) {
179
+ baselineRef.current = { blocks, preserved: preserved };
189
180
  onChangeRef.current(mergeCompositionValue(blocks, preserved));
190
181
  }
191
182
  }, [blocks, preserved, readOnly]);
@@ -217,13 +208,9 @@ function BreakpointSwitcher() {
217
208
  * field. It keeps spatial/preview controls — including the breakpoint
218
209
  * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.
219
210
  */
220
- function CompositionEditorShell({ readOnly, trailingPanes, preservedCount = 0, roleNotices, conflicts, }) {
211
+ function CompositionEditorShell({ readOnly, trailingPanes, roleNotices, conflicts, }) {
221
212
  const builder = useBuilder();
222
213
  const dispatch = builder.dispatch;
223
- // The field's top-level component vocabulary (EC-188) — the leash applied to
224
- // inserts at the composition root so forbidden components aren't even offered.
225
- // Read from the store's field scope (EC-554), the same value DnD and paste judge.
226
- const fieldAllow = builder.field.allow;
227
214
  const [insertContext, setInsertContext] = useState(null);
228
215
  const [insertOpen, setInsertOpen] = useState(false);
229
216
  const rootId = builder.present.id;
@@ -246,7 +233,9 @@ function CompositionEditorShell({ readOnly, trailingPanes, preservedCount = 0, r
246
233
  // (even empty "No allowed components match") insert dialog. Both now resolve
247
234
  // the identical target + allow set for the same selection.
248
235
  function openInsert(nodeId) {
249
- const ctx = resolveInsertContext(builder, nodeId, fieldAllow);
236
+ // EC-584: the offer reads the store's placement scope — the field's vocabulary at
237
+ // the composition root, each slot's below — the same scope the reducer judges by.
238
+ const ctx = resolveInsertContext(builder, nodeId);
250
239
  if (!ctx)
251
240
  return;
252
241
  if (nodeId !== builder.selectedId)
@@ -260,9 +249,9 @@ function CompositionEditorShell({ readOnly, trailingPanes, preservedCount = 0, r
260
249
  // the panel re-deriving it from the selection). Re-memoized on every store change
261
250
  // so the closures read the live graph.
262
251
  const affordanceApi = useMemo(() => ({
263
- positionsFor: (nodeId) => insertablePositions(builder, nodeId, fieldAllow),
252
+ positionsFor: (nodeId) => insertablePositions(builder, nodeId),
264
253
  openAt: (nodeId, position) => {
265
- const ctx = resolveInsertContextAt(builder, nodeId, position, fieldAllow);
254
+ const ctx = resolveInsertContextAt(builder, nodeId, position);
266
255
  if (!ctx)
267
256
  return;
268
257
  if (nodeId !== builder.selectedId)
@@ -270,7 +259,7 @@ function CompositionEditorShell({ readOnly, trailingPanes, preservedCount = 0, r
270
259
  setInsertContext(ctx);
271
260
  setInsertOpen(true);
272
261
  },
273
- }), [builder, fieldAllow, dispatch]);
274
- return (_jsx(InsertAffordanceProvider, { value: affordanceApi, children: _jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "shrink-0 border-b border-border", children: _jsxs(Toolbar, { children: [_jsxs(ToolbarGroup, { children: [_jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": translate('builder.canvas.undo'), title: translate('builder.canvas.undo'), disabled: readOnly || !builder.canUndo, onClick: () => dispatch({ type: 'undo' }), children: _jsx(Undo2, {}) }), _jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": translate('builder.canvas.redo'), title: translate('builder.canvas.redo'), disabled: readOnly || !builder.canRedo, onClick: () => dispatch({ type: 'redo' }), children: _jsx(Redo2, {}) })] }), _jsx(ToolbarSeparator, {}), _jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly, onClick: () => openInsert(builder.selectedId ?? rootId), children: [_jsx(Plus, {}), " ", translate('builder.canvas.insert')] }), _jsx(Spacer, {}), _jsx(BreakpointSwitcher, {}), _jsx(Spacer, {})] }) }), _jsxs("div", { className: "grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border", children: [_jsx(Panel, { className: cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60'), children: _jsx(PageTree, { onAddChild: openInsert }) }), _jsxs("div", { className: cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60'), children: [conflicts ? _jsx("div", { className: "shrink-0 border-b border-border p-2", children: conflicts }) : null, _jsx(PreservedItemsNotice, { count: preservedCount }), _jsx(CanvasRoleNotices, { notices: roleNotices ?? [] }), _jsx(BuilderCanvas, { perspective: readOnly ? 'published' : DRAFT_PERSPECTIVE })] }), _jsx(InspectorSidebar, { panes: panes })] }), _jsx(InsertMenu, { context: insertContext, open: insertOpen, onOpenChange: setInsertOpen })] }) }));
262
+ }), [builder, dispatch]);
263
+ return (_jsx(InsertAffordanceProvider, { value: affordanceApi, children: _jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "shrink-0 border-b border-border", children: _jsxs(Toolbar, { children: [_jsxs(ToolbarGroup, { children: [_jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": translate('builder.canvas.undo'), title: translate('builder.canvas.undo'), disabled: readOnly || !builder.canUndo, onClick: () => dispatch({ type: 'undo' }), children: _jsx(Undo2, {}) }), _jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": translate('builder.canvas.redo'), title: translate('builder.canvas.redo'), disabled: readOnly || !builder.canRedo, onClick: () => dispatch({ type: 'redo' }), children: _jsx(Redo2, {}) })] }), _jsx(ToolbarSeparator, {}), _jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly, onClick: () => openInsert(builder.selectedId ?? rootId), children: [_jsx(Plus, {}), " ", translate('builder.canvas.insert')] }), _jsx(Spacer, {}), _jsx(BreakpointSwitcher, {}), _jsx(Spacer, {})] }) }), _jsxs("div", { className: "grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border", children: [_jsx(Panel, { className: cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60'), children: _jsx(PageTree, { onAddChild: openInsert }) }), _jsxs("div", { className: cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60'), children: [conflicts ? _jsx("div", { className: "shrink-0 border-b border-border p-2", children: conflicts }) : null, _jsx(PlacementRefusalNotice, { refusal: builder.refusal, fieldLabel: builder.field.label, onDismiss: () => dispatch({ type: 'clearRefusal' }) }), _jsx(CanvasRoleNotices, { notices: roleNotices ?? [] }), _jsx(BuilderCanvas, { perspective: readOnly ? 'published' : DRAFT_PERSPECTIVE })] }), _jsx(InspectorSidebar, { panes: panes })] }), _jsx(InsertMenu, { context: insertContext, open: insertOpen, onOpenChange: setInsertOpen })] }) }));
275
264
  }
276
265
  //# sourceMappingURL=composition-field-editor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composition-field-editor.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { AlertTriangle, Pilcrow, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react'\nimport { DEFAULT_SLOT, mergeBlockRoots, splitBlockRoots } from '@elytracms/core/project-graph'\nimport type { ComponentNode, PreservedSequenceItem, SplitBlockRoots } from '@elytracms/core/project-graph'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { BlocksFieldDef } from '@elytracms/core/cms-core'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { BlockNode, BlockSequenceValue } from '@elytracms/core/rich-text'\nimport { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport type { MessageKey } from '@elytracms/studio/lib/i18n'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { BuilderProvider, useBuilder } from './builder-store'\nimport type { CompositionFieldScope } from './builder-store'\nimport { useCanvasViewports } from './canvas-breakpoints'\nimport type { ViewportOption } from './canvas-breakpoints'\nimport { BuilderCanvas } from './canvas'\nimport { PageTree } from './page-tree'\nimport { PropertyInspector } from './property-inspector'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { InsertMenu } from './insert-menu'\nimport type { InsertContext } from './insert-menu'\nimport { insertablePositions, resolveInsertContext, resolveInsertContextAt } from './insert-context'\nimport type { InsertPosition } from './insert-context'\nimport { InsertAffordanceProvider } from './insert-affordance-context'\nimport type { InsertAffordanceApi } from './insert-affordance-context'\nimport { PropSyncProvider } from './prop-sync-context'\nimport { ReuseProvider } from './reuse-context'\nimport { fieldLabel } from '../cms/cms-labels'\n\n/**\n * EC-188 — the block-field surface: one canvas engine mounted on a composition\n * **field value** (AD-12), not the project graph.\n *\n * The composition tree is wrapped in a synthetic single-page graph and edited by\n * a SCOPED `BuilderProvider` with **no `projectId`** (so the graph persister is a\n * no-op — builder-store.tsx). Every committed edit is lifted out via `onChange`\n * into the CMS document draft; the document's own Save persists it (the tree is\n * stored as document data, `v.any()`).\n *\n * EC-547 (EC-500 D1–D3): the stored value is the converged v2 block sequence\n * `{ version: 2, content: BlockNode[] }`. The canvas edits the sequence's\n * `elytra/component` nodes; typography nodes survive the session byte-for-byte\n * and keep their order relative to components on write-back (see\n * {@link splitCompositionValue} / {@link mergeCompositionValue}). Legacy bare\n * node/array values are still ACCEPTED on read during the staging window, but\n * every write emits v2. This reuses the real canvas + inspector +\n * blocks panel unchanged — the same EC-158–164 mechanics — without touching the\n * project-wide builder store. Surfaced on a dedicated \"Canvas\" fieldset tab in\n * the CMS detail editor so the page builder gets full screen real estate rather\n * than being crammed into the field list.\n */\n\nconst SYNTH_ROOT_ID = 'composition.root'\n\n/**\n * A sequence item the canvas does not natively edit (EC-547 / EC-500 D3):\n * typography nodes, unknown/future node kinds, even entries the v2 schema would\n * reject — the editor is not a validator, so they are carried BYTE-FOR-BYTE and\n * re-merged on write-back rather than parsed, rewritten, or dropped.\n * `componentPrefix` records how many component nodes preceded the item in the\n * original sequence — the anchor {@link mergeCompositionValue} splices by.\n */\nexport type PreservedBlockNode = PreservedSequenceItem<BlockNode>\n\n/** A field value split for canvas editing: component roots + everything else. */\nexport type SplitCompositionValue = SplitBlockRoots<BlockNode>\n\n/**\n * Split a stored blocks-field value into the component nodes the canvas edits\n * and the sequence items it must preserve (EC-547, the EC-500 round-trip law:\n * a projection may only rewrite nodes it natively edits). Typography items are\n * deliberately NOT surfaced as editable canvas blocks in v1 (EC-500 D3) — they\n * survive the editing session and keep their order on write-back.\n *\n * EC-576: the logic is core's {@link splitBlockRoots} — the ONE reader every\n * blocks writer shares. This is the canvas's typed view of it: project-graph\n * sits below rich-text and cannot name {@link BlockNode}, so the carried items\n * arrive `unknown`; they are typed here, not checked (preservation over parsing).\n */\nexport function splitCompositionValue(value: unknown): SplitCompositionValue {\n return splitBlockRoots(value) as SplitCompositionValue\n}\n\n/**\n * Merge canvas-edited component roots back into the stored sequence (EC-547).\n * The merge rule — preserved items anchored to their ordinal position between\n * components, every root tagged, the write always v2 — is core's\n * {@link mergeBlockRoots} (EC-576: the same merge every blocks writer goes\n * through, via `editBlockRoots`). Typed here as the rich-text value.\n */\nexport function mergeCompositionValue(\n roots: readonly ComponentNode[],\n preserved: readonly PreservedBlockNode[],\n): BlockSequenceValue {\n return mergeBlockRoots(roots, preserved)\n}\n\n/**\n * The field scope the editor's store carries (EC-554): the field's label (named\n * by refusal notices) and its top-level `allow`, passed through verbatim — an\n * omitted `allow` stays `undefined` (EC-544: any registered component).\n */\nexport function compositionFieldScope(field: BlocksFieldDef): CompositionFieldScope {\n return { label: fieldLabel(field), allow: field.allow }\n}\n\n/**\n * Wrap the field value's component roots in a synthetic root container (EC-287).\n * The builder store edits this single root-bearing composition directly; its\n * children are the field's component blocks, lifted back out via `onChange`.\n * EC-591: the root is the component that fills the `layout.stack` role — the\n * project's own, from its component list — never a hardcoded engine id.\n */\nfunction buildSyntheticRoot(roots: readonly ComponentNode[], stackComponentId: string): ComponentNode {\n return {\n id: SYNTH_ROOT_ID,\n componentId: stackComponentId,\n slots: { [DEFAULT_SLOT]: [...roots] },\n }\n}\n\n/**\n * The canvas notices for roles nobody in the component list fills (EC-591) — each\n * names the gesture that is OFF because of it, so a missing role is visible, never\n * a silent dead affordance. Only gestures the canvas actually has are listed: the\n * `layout.stack` role is the canvas itself (see {@link CompositionFieldEditor}),\n * `layout.grid` the wrap-beside gesture, `text` the seeded layout presets.\n * (`richText` — the typography projection, EC-549 — and `image` — asset drop —\n * have no canvas gesture yet, so there is nothing to switch off.) Pure.\n */\nexport function canvasRoleNotices(registry: ComponentRegistry): MessageKey[] {\n const notices: MessageKey[] = []\n if (!registry.roleComponent('layout.grid')) notices.push('builder.roles.noGrid')\n if (!registry.roleComponent('text')) notices.push('builder.roles.noText')\n return notices\n}\n\n/** The role notices above the canvas (EC-591) — a standing truth about the component list. */\nexport function CanvasRoleNotices({ notices }: { notices: readonly MessageKey[] }) {\n if (notices.length === 0) return null\n return (\n <div data-testid=\"canvas-role-notices\" className=\"shrink-0 border-b border-border bg-muted/40\">\n {notices.map((key) => (\n <Row key={key} gap=\"2\" align=\"center\" className=\"px-3 py-1.5\">\n <AlertTriangle aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate(key)}\n </Text>\n </Row>\n ))}\n </div>\n )\n}\n\n/**\n * The canvas in place of the editor when nobody fills `layout.stack` (EC-591): the\n * blocks of a composition field compose inside that component, so there is no\n * canvas without it. The value is not touched — nothing mounts that could write.\n */\nexport function NoStackCanvasNotice() {\n return (\n <Panel data-testid=\"canvas-no-stack\" className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <Row gap=\"2\" align=\"center\" className=\"max-w-prose\">\n <AlertTriangle aria-hidden className=\"size-4 shrink-0 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n {translate('builder.roles.noStack')}\n </Text>\n </Row>\n </Panel>\n )\n}\n\nexport function CompositionFieldEditor({\n field,\n value,\n onChange,\n readOnly,\n trailingPanes,\n conflicts,\n}: {\n field: BlocksFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n /**\n * EC-595 A2 — the field's per-field conflicts (a refused node patch: \"changed by X —\n * take theirs / keep mine\"), rendered above the canvas: the canvas IS the field.\n */\n conflicts?: ReactNode\n /** Inspector panes the document editor appends after the canvas-supplied\n * `Properties` pane (EC-224 — the frame-owned `Versions` pane). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n // Split the value ONCE at mount (EC-547): component nodes become the canvas\n // roots; every other sequence item is preserved aside for write-back. The\n // editor is uncontrolled thereafter — it is the source of truth while open and\n // lifts changes out via onChange; re-opening the tab remounts from the current\n // draft. (BuilderProvider reads `composition` only in its lazy initial state,\n // so later prop changes are ignored regardless.)\n const initial = useMemo(() => splitCompositionValue(value), [])\n // EC-591: the roles come from the project's component list (the registry).\n const registry = getStudioRegistry().registry\n const stack = registry.roleComponent('layout.stack')\n const layout = registry.layout\n const notices = useMemo(() => canvasRoleNotices(registry), [registry])\n const initialRoot = useMemo(\n () => (stack ? buildSyntheticRoot(initial.roots, stack.id) : null),\n [initial, stack],\n )\n // EC-554: the field's `allow` travels WITH the store, so every placement\n // surface (canvas + tree DnD, paste, the insert offer) judges the same value.\n const fieldScope = useMemo(() => compositionFieldScope(field), [field])\n\n if (!initialRoot) return <NoStackCanvasNotice />\n\n return (\n <BuilderProvider composition={initialRoot} field={fieldScope} layout={layout}>\n <CompositionLift preserved={initial.preserved} onChange={onChange} readOnly={readOnly} />\n <ReuseProvider>\n {/* EC-408: the field↔component selection-sync channel wraps BOTH the canvas\n and the inspector (they reach each other through nothing else). */}\n <PropSyncProvider>\n <CompositionEditorShell\n readOnly={readOnly}\n trailingPanes={trailingPanes}\n preservedCount={initial.preserved.length}\n roleNotices={notices}\n conflicts={conflicts}\n />\n </PropSyncProvider>\n </ReuseProvider>\n </BuilderProvider>\n )\n}\n\n/**\n * EC-549 interim (scope item 3, the honesty floor): a mixed sequence's typography\n * items are preserved for write-back but NOT rendered on the canvas — and a canvas\n * that silently shows half the sequence lets an editor rearrange around content\n * they cannot see. Until typography items are first-class, path-addressed canvas\n * items (EC-549's main scope), the surface must SAY what it is not showing: a\n * persistent, non-dismissible count above the canvas. Deliberately NOT the\n * dismissible overlay shape (`CanvasThemeNotice`) — this states a standing truth\n * about the VALUE, not a one-time migration hint, and it must not cover blocks.\n */\nexport function PreservedItemsNotice({ count }: { count: number }) {\n if (count === 0) return null\n return (\n <Row\n gap=\"2\"\n align=\"center\"\n data-testid=\"composition-preserved-notice\"\n className=\"shrink-0 border-b border-border bg-muted/40 px-3 py-1.5\"\n >\n <Pilcrow aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate('builder.composition.preservedNotice', { count })}\n </Text>\n </Row>\n )\n}\n\n/**\n * Lifts committed edits out of the scoped store into the CMS draft. The first\n * render establishes the (container-normalized) baseline so simply opening the\n * tab never marks the document dirty; every subsequent change to the block list\n * emits the merged v2 sequence value (EC-547) — the canvas roots spliced back\n * between the preserved typography items. Cardinality (`multiple`) no longer\n * shapes the stored value: it is a validation constraint over the one envelope\n * (EC-500 D2), so the bare-object special case for `multiple: false` is gone.\n */\nfunction CompositionLift({\n preserved,\n onChange,\n readOnly,\n}: {\n preserved: readonly PreservedBlockNode[]\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const builder = useBuilder()\n const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const baselineRef = useRef<readonly ComponentNode[] | null>(null)\n\n useEffect(() => {\n // EC-226: never lift edits out of a READ-ONLY canvas (the Published view).\n // The canvas's keyboard listeners (clipboard paste/cut) aren't blocked by the\n // visual `pointer-events-none`, so without this a Cmd+V could mutate the\n // graph; refusing to lift keeps the document draft intact regardless. The\n // editor remounts per perspective, so any transient in-canvas change is\n // discarded on switch.\n if (readOnly) return\n if (baselineRef.current === null) {\n baselineRef.current = blocks\n return\n }\n if (blocks !== baselineRef.current) {\n baselineRef.current = blocks\n onChangeRef.current(mergeCompositionValue(blocks, preserved))\n }\n }, [blocks, preserved, readOnly])\n\n return null\n}\n\nconst EMPTY_BLOCKS: readonly ComponentNode[] = []\n\n/** A breakpoint preview-width toggle (EC-211/EC-250). Switching the viewport\n * resizes only the preview frame (not an undoable edit, writes no graph command).\n * The label is the breakpoint name (`Full`/`sm`/`md`/…) with the real px width in\n * the tooltip — so the switcher reflects the site's actual screens, not a fixed\n * desktop/tablet/phone triple. */\nfunction ViewportButton({ option }: { option: ViewportOption }) {\n const builder = useBuilder()\n const active = builder.viewport === option.key\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.viewportPreviewAria', { label: option.label })}\n aria-pressed={active}\n title={\n option.px !== undefined\n ? `${option.label} · ${option.px}px`\n : translate('builder.canvas.viewportWidthTitle', { label: option.label })\n }\n onClick={() => builder.dispatch({ type: 'viewport', viewport: option.key })}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\n/** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a\n * visible validation issue when a project declared breakpoints but none are usable. */\nfunction BreakpointSwitcher() {\n const { viewports, issue } = useCanvasViewports()\n return (\n <Row gap=\"1\" align=\"center\">\n <ToolbarGroup>\n {viewports.map((v) => (\n <ViewportButton key={v.key} option={v} />\n ))}\n </ToolbarGroup>\n {issue ? (\n <Row gap=\"1\" align=\"center\" className=\"text-amber-600 dark:text-amber-400\" title={issue}>\n <AlertTriangle aria-hidden className=\"size-3.5\" />\n <Text size=\"xs\">{translate('builder.canvas.breakpointsIssueLabel')}</Text>\n </Row>\n ) : null}\n </Row>\n )\n}\n\n/**\n * The slim editor surface — the canvas + layers + inspector, plus Insert and\n * undo/redo. It deliberately drops the project-builder *lifecycle* chrome\n * (publish, versions, AI, page SEO): the document owns the lifecycle, not the\n * field. It keeps spatial/preview controls — including the breakpoint\n * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.\n */\nfunction CompositionEditorShell({\n readOnly,\n trailingPanes,\n preservedCount = 0,\n roleNotices,\n conflicts,\n}: {\n readOnly: boolean\n /** EC-595 A2 — the field's conflict notices (see {@link CompositionFieldEditor}). */\n conflicts?: ReactNode\n /** Document-level panes appended after `Properties` (EC-224). */\n trailingPanes?: readonly InspectorPane[]\n /** Preserved (non-component) sequence items the canvas cannot show (EC-549 interim). */\n preservedCount?: number\n /** Notices for the roles nobody fills (EC-591) — see {@link canvasRoleNotices}. */\n roleNotices?: readonly MessageKey[]\n}) {\n const builder = useBuilder()\n const dispatch = builder.dispatch\n // The field's top-level component vocabulary (EC-188) — the leash applied to\n // inserts at the composition root so forbidden components aren't even offered.\n // Read from the store's field scope (EC-554), the same value DnD and paste judge.\n const fieldAllow = builder.field.allow\n const [insertContext, setInsertContext] = useState<InsertContext | null>(null)\n const [insertOpen, setInsertOpen] = useState(false)\n const rootId = builder.present.id\n\n // The canvas SUPPLIES the `Properties` pane (EC-224); the document editor\n // appends its own (`Versions`). Properties dims under read-only just as the\n // whole inspector column used to — but the appended panes stay interactive.\n const propertiesPane = useMemo<InspectorPane>(\n () => ({\n id: 'properties',\n label: translate('builder.inspector.propertiesPane'),\n icon: <SlidersHorizontal className=\"size-3.5\" />,\n render: () => (\n <div className={cn('h-full min-h-0 overflow-hidden', readOnly && 'pointer-events-none opacity-60')}>\n <PropertyInspector />\n </div>\n ),\n }),\n [readOnly],\n )\n const panes = useMemo<InspectorPane[]>(\n () => [propertiesPane, ...(trailingPanes ?? [])],\n [propertiesPane, trailingPanes],\n )\n\n // The single insert entry point for BOTH the toolbar and the page tree's\n // \"Add child\" (EC-383). It selects its target first so `builder.selectedId`\n // is the one source of truth for where an insert lands: the toolbar resolves\n // `builder.selectedId ?? rootId` while Add-child passes an explicit row, and\n // without this sync clicking Add-child (which otherwise leaves selection put)\n // could target a different node than the toolbar — surfacing as a mismatched\n // (even empty \"No allowed components match\") insert dialog. Both now resolve\n // the identical target + allow set for the same selection.\n function openInsert(nodeId: string) {\n const ctx = resolveInsertContext(builder, nodeId, fieldAllow)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n }\n\n // EC-405: the in-canvas \"+\" affordances resolve the SAME slot rules through the\n // shared position-carrying insert-context resolution, and open the panel at the\n // clicked edge (an explicit position that survives to the insertion, rather than\n // the panel re-deriving it from the selection). Re-memoized on every store change\n // so the closures read the live graph.\n const affordanceApi = useMemo<InsertAffordanceApi>(\n () => ({\n positionsFor: (nodeId: string) => insertablePositions(builder, nodeId, fieldAllow),\n openAt: (nodeId: string, position: InsertPosition) => {\n const ctx = resolveInsertContextAt(builder, nodeId, position, fieldAllow)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n },\n }),\n [builder, fieldAllow, dispatch],\n )\n\n return (\n <InsertAffordanceProvider value={affordanceApi}>\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border\">\n <Toolbar>\n <ToolbarGroup>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.undo')}\n title={translate('builder.canvas.undo')}\n disabled={readOnly || !builder.canUndo}\n onClick={() => dispatch({ type: 'undo' })}\n >\n <Undo2 />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.redo')}\n title={translate('builder.canvas.redo')}\n disabled={readOnly || !builder.canRedo}\n onClick={() => dispatch({ type: 'redo' })}\n >\n <Redo2 />\n </Button>\n </ToolbarGroup>\n <ToolbarSeparator />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={readOnly}\n onClick={() => openInsert(builder.selectedId ?? rootId)}\n >\n <Plus /> {translate('builder.canvas.insert')}\n </Button>\n {/* Breakpoint preview-width switcher (EC-211/EC-250), centered over the\n canvas via flanking spacers. A preview control, not an edit — stays\n active even in read-only published preview. */}\n <Spacer />\n <BreakpointSwitcher />\n <Spacer />\n </Toolbar>\n </div>\n\n <div className=\"grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border\">\n <Panel className={cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60')}>\n <PageTree onAddChild={openInsert} />\n </Panel>\n <div className={cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60')}>\n {conflicts ? <div className=\"shrink-0 border-b border-border p-2\">{conflicts}</div> : null}\n <PreservedItemsNotice count={preservedCount} />\n <CanvasRoleNotices notices={roleNotices ?? []} />\n {/* EC-300/AD-16: the read-only Published view resolves references against\n the published perspective (honest preview); the editable Draft view\n resolves drafts (a just-made reusable shows immediately). */}\n <BuilderCanvas perspective={readOnly ? 'published' : DRAFT_PERSPECTIVE} />\n </div>\n <InspectorSidebar panes={panes} />\n </div>\n\n <InsertMenu context={insertContext} open={insertOpen} onOpenChange={setInsertOpen} />\n </div>\n </InsertAffordanceProvider>\n )\n}\n"]}
1
+ {"version":3,"file":"composition-field-editor.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react'\nimport { DEFAULT_SLOT, mergeBlockRoots, splitBlockRoots } from '@elytracms/core/project-graph'\nimport type { ComponentNode, PreservedSequenceItem, SplitBlockRoots } from '@elytracms/core/project-graph'\nimport { DRAFT_PERSPECTIVE, fieldAllowList } from '@elytracms/core/cms-core'\nimport type { BlocksFieldDef } from '@elytracms/core/cms-core'\nimport type { ComponentRegistry } from '@elytracms/core/component-registry'\nimport type { BlockNode, BlockSequenceValue } from '@elytracms/core/rich-text'\nimport { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\nimport { translate } from '../../lib/i18n'\nimport type { MessageKey } from '../../lib/i18n'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { BuilderProvider, useBuilder } from './builder-store'\nimport type { CompositionFieldScope } from './builder-store'\nimport { useCanvasViewports } from './canvas-breakpoints'\nimport type { ViewportOption } from './canvas-breakpoints'\nimport { BuilderCanvas } from './canvas'\nimport { PageTree } from './page-tree'\nimport { PropertyInspector } from './property-inspector'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { InsertMenu } from './insert-menu'\nimport type { InsertContext } from './insert-menu'\nimport { insertablePositions, resolveInsertContext, resolveInsertContextAt } from './insert-context'\nimport type { InsertPosition } from './insert-context'\nimport { InsertAffordanceProvider } from './insert-affordance-context'\nimport type { InsertAffordanceApi } from './insert-affordance-context'\nimport { PropSyncProvider } from './prop-sync-context'\nimport { ReuseProvider } from './reuse-context'\nimport { PlacementRefusalNotice } from './placement-refusal'\nimport { fieldLabel } from '../cms/cms-labels'\n\n/**\n * EC-188 — the block-field surface: one canvas engine mounted on a composition\n * **field value** (AD-12), not the project graph.\n *\n * The composition tree is wrapped in a synthetic single-page graph and edited by\n * a SCOPED `BuilderProvider` with **no `projectId`** (so the graph persister is a\n * no-op — builder-store.tsx). Every committed edit is lifted out via `onChange`\n * into the CMS document draft; the document's own Save persists it (the tree is\n * stored as document data, `v.any()`).\n *\n * EC-547 (EC-500 D1–D3): the stored value is the converged v2 block sequence\n * `{ version: 2, content: BlockNode[] }`. The canvas edits the sequence's\n * `elytra/component` nodes; typography nodes survive the session byte-for-byte\n * and keep their order relative to components on write-back (see\n * {@link splitCompositionValue} / {@link mergeCompositionValue}). Legacy bare\n * node/array values are still ACCEPTED on read during the staging window, but\n * every write emits v2. This reuses the real canvas + inspector +\n * blocks panel unchanged — the same EC-158–164 mechanics — without touching the\n * project-wide builder store. Surfaced on a dedicated \"Canvas\" fieldset tab in\n * the CMS detail editor so the page builder gets full screen real estate rather\n * than being crammed into the field list.\n */\n\nconst SYNTH_ROOT_ID = 'composition.root'\n\n/**\n * A sequence item the canvas does not natively edit (EC-547 / EC-500 D3):\n * typography nodes, unknown/future node kinds, even entries the v2 schema would\n * reject — the editor is not a validator, so they are carried BYTE-FOR-BYTE and\n * re-merged on write-back rather than parsed, rewritten, or dropped.\n * `componentPrefix` records how many component nodes preceded the item in the\n * original sequence — the anchor {@link mergeCompositionValue} splices by.\n */\nexport type PreservedBlockNode = PreservedSequenceItem<BlockNode>\n\n/** A field value split for canvas editing: component roots + everything else. */\nexport type SplitCompositionValue = SplitBlockRoots<BlockNode>\n\n/**\n * Split a stored blocks-field value into the component nodes the canvas edits\n * and the sequence items it must preserve (EC-547, the EC-500 round-trip law:\n * a projection may only rewrite nodes it natively edits). Typography items are\n * deliberately NOT surfaced as editable canvas blocks in v1 (EC-500 D3) — they\n * survive the editing session and keep their order on write-back.\n *\n * EC-576: the logic is core's {@link splitBlockRoots} — the ONE reader every\n * blocks writer shares. This is the canvas's typed view of it: project-graph\n * sits below rich-text and cannot name {@link BlockNode}, so the carried items\n * arrive `unknown`; they are typed here, not checked (preservation over parsing).\n */\nexport function splitCompositionValue(value: unknown): SplitCompositionValue {\n return splitBlockRoots(value) as SplitCompositionValue\n}\n\n/**\n * Merge canvas-edited component roots back into the stored sequence (EC-547).\n * The merge rule — preserved items anchored to their ordinal position between\n * components, every root tagged, the write always v2 — is core's\n * {@link mergeBlockRoots} (EC-576: the same merge every blocks writer goes\n * through, via `editBlockRoots`). Typed here as the rich-text value.\n */\nexport function mergeCompositionValue(\n roots: readonly ComponentNode[],\n preserved: readonly PreservedBlockNode[],\n): BlockSequenceValue {\n return mergeBlockRoots(roots, preserved)\n}\n\n/**\n * The field scope the editor's store carries (EC-554): the field's label (named\n * by refusal notices) and its EFFECTIVE top-level vocabulary (EC-584 — read through\n * the per-FieldType definition, `fieldAllowList`: the declared `allow`, else the\n * `blocks` default, any canvas-eligible component).\n */\nexport function compositionFieldScope(field: BlocksFieldDef): CompositionFieldScope {\n // A blocks field always has a vocabulary (its type declares the default).\n return { label: fieldLabel(field), allow: fieldAllowList(field)! }\n}\n\n/**\n * Wrap the field value's component roots in a synthetic root container (EC-287).\n * The builder store edits this single root-bearing composition directly; its\n * children are the field's component blocks, lifted back out via `onChange`.\n * EC-591: the root is the component that fills the `layout.stack` role — the\n * project's own, from its component list — never a hardcoded engine id.\n */\nfunction buildSyntheticRoot(roots: readonly ComponentNode[], stackComponentId: string): ComponentNode {\n return {\n id: SYNTH_ROOT_ID,\n componentId: stackComponentId,\n slots: { [DEFAULT_SLOT]: [...roots] },\n }\n}\n\n/**\n * The canvas notices for roles nobody in the component list fills (EC-591) — each\n * names the gesture that is OFF because of it, so a missing role is visible, never\n * a silent dead affordance. Only gestures the canvas actually has are listed: the\n * `layout.stack` role is the canvas itself (see {@link CompositionFieldEditor}),\n * `layout.grid` the wrap-beside gesture, `text` the seeded layout presets.\n * (`richText` — the typography projection, EC-549 — and `image` — asset drop —\n * have no canvas gesture yet, so there is nothing to switch off.) Pure.\n */\nexport function canvasRoleNotices(registry: ComponentRegistry): MessageKey[] {\n const notices: MessageKey[] = []\n if (!registry.roleComponent('layout.grid')) notices.push('builder.roles.noGrid')\n if (!registry.roleComponent('text')) notices.push('builder.roles.noText')\n return notices\n}\n\n/** The role notices above the canvas (EC-591) — a standing truth about the component list. */\nexport function CanvasRoleNotices({ notices }: { notices: readonly MessageKey[] }) {\n if (notices.length === 0) return null\n return (\n <div data-testid=\"canvas-role-notices\" className=\"shrink-0 border-b border-border bg-muted/40\">\n {notices.map((key) => (\n <Row key={key} gap=\"2\" align=\"center\" className=\"px-3 py-1.5\">\n <AlertTriangle aria-hidden className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate(key)}\n </Text>\n </Row>\n ))}\n </div>\n )\n}\n\n/**\n * The canvas in place of the editor when nobody fills `layout.stack` (EC-591): the\n * blocks of a composition field compose inside that component, so there is no\n * canvas without it. The value is not touched — nothing mounts that could write.\n */\nexport function NoStackCanvasNotice() {\n return (\n <Panel data-testid=\"canvas-no-stack\" className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <Row gap=\"2\" align=\"center\" className=\"max-w-prose\">\n <AlertTriangle aria-hidden className=\"size-4 shrink-0 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n {translate('builder.roles.noStack')}\n </Text>\n </Row>\n </Panel>\n )\n}\n\nexport function CompositionFieldEditor({\n field,\n value,\n onChange,\n readOnly,\n trailingPanes,\n conflicts,\n}: {\n field: BlocksFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n /**\n * EC-595 A2 — the field's per-field conflicts (a refused node patch: \"changed by X —\n * take theirs / keep mine\"), rendered above the canvas: the canvas IS the field.\n */\n conflicts?: ReactNode\n /** Inspector panes the document editor appends after the canvas-supplied\n * `Properties` pane (EC-224 — the frame-owned `Versions` pane). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n // Split the value ONCE at mount (EC-547): component nodes become the canvas\n // roots; every other sequence item is preserved aside for write-back. The\n // editor is uncontrolled thereafter — it is the source of truth while open and\n // lifts changes out via onChange; re-opening the tab remounts from the current\n // draft. (BuilderProvider reads `composition` only in its lazy initial state,\n // so later prop changes are ignored regardless.)\n const initial = useMemo(() => splitCompositionValue(value), [])\n // EC-591: the roles come from the project's component list (the registry).\n const registry = getStudioRegistry().registry\n const stack = registry.roleComponent('layout.stack')\n const layout = registry.layout\n const notices = useMemo(() => canvasRoleNotices(registry), [registry])\n const initialRoot = useMemo(\n () => (stack ? buildSyntheticRoot(initial.roots, stack.id) : null),\n [initial, stack],\n )\n // EC-554: the field's `allow` travels WITH the store, so every placement\n // surface (canvas + tree DnD, paste, the insert offer) judges the same value.\n const fieldScope = useMemo(() => compositionFieldScope(field), [field])\n\n if (!initialRoot) return <NoStackCanvasNotice />\n\n return (\n <BuilderProvider\n composition={initialRoot}\n preserved={initial.preserved}\n field={fieldScope}\n components={registry.lookup}\n layout={layout}\n >\n <CompositionLift onChange={onChange} readOnly={readOnly} />\n <ReuseProvider>\n {/* EC-408: the field↔component selection-sync channel wraps BOTH the canvas\n and the inspector (they reach each other through nothing else). */}\n <PropSyncProvider>\n <CompositionEditorShell\n readOnly={readOnly}\n trailingPanes={trailingPanes}\n roleNotices={notices}\n conflicts={conflicts}\n />\n </PropSyncProvider>\n </ReuseProvider>\n </BuilderProvider>\n )\n}\n\n/**\n * Lifts committed edits out of the scoped store into the CMS draft. The first\n * render establishes the (container-normalized) baseline so simply opening the\n * tab never marks the document dirty; every subsequent change to the block list\n * OR to the preserved typography (EC-549: a run deleted or edited, or an undo of\n * either) emits the merged v2 sequence value (EC-547) — the canvas roots spliced\n * back between the preserved typography items, both read from the store.\n * Cardinality (`multiple`) no longer shapes the stored value: it is a validation\n * constraint over the one envelope (EC-500 D2).\n */\nfunction CompositionLift({\n onChange,\n readOnly,\n}: {\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const builder = useBuilder()\n const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS\n const preserved = builder.preserved\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const baselineRef = useRef<{ blocks: readonly ComponentNode[]; preserved: readonly PreservedBlockNode[] } | null>(null)\n\n useEffect(() => {\n // EC-226: never lift edits out of a READ-ONLY canvas (the Published view).\n // The canvas's keyboard listeners (clipboard paste/cut) aren't blocked by the\n // visual `pointer-events-none`, so without this a Cmd+V could mutate the\n // graph; refusing to lift keeps the document draft intact regardless. The\n // editor remounts per perspective, so any transient in-canvas change is\n // discarded on switch.\n if (readOnly) return\n const baseline = baselineRef.current\n if (baseline === null) {\n baselineRef.current = { blocks, preserved: preserved as readonly PreservedBlockNode[] }\n return\n }\n if (blocks !== baseline.blocks || preserved !== baseline.preserved) {\n baselineRef.current = { blocks, preserved: preserved as readonly PreservedBlockNode[] }\n onChangeRef.current(mergeCompositionValue(blocks, preserved as readonly PreservedBlockNode[]))\n }\n }, [blocks, preserved, readOnly])\n\n return null\n}\n\nconst EMPTY_BLOCKS: readonly ComponentNode[] = []\n\n/** A breakpoint preview-width toggle (EC-211/EC-250). Switching the viewport\n * resizes only the preview frame (not an undoable edit, writes no graph command).\n * The label is the breakpoint name (`Full`/`sm`/`md`/…) with the real px width in\n * the tooltip — so the switcher reflects the site's actual screens, not a fixed\n * desktop/tablet/phone triple. */\nfunction ViewportButton({ option }: { option: ViewportOption }) {\n const builder = useBuilder()\n const active = builder.viewport === option.key\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.viewportPreviewAria', { label: option.label })}\n aria-pressed={active}\n title={\n option.px !== undefined\n ? `${option.label} · ${option.px}px`\n : translate('builder.canvas.viewportWidthTitle', { label: option.label })\n }\n onClick={() => builder.dispatch({ type: 'viewport', viewport: option.key })}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\n/** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a\n * visible validation issue when a project declared breakpoints but none are usable. */\nfunction BreakpointSwitcher() {\n const { viewports, issue } = useCanvasViewports()\n return (\n <Row gap=\"1\" align=\"center\">\n <ToolbarGroup>\n {viewports.map((v) => (\n <ViewportButton key={v.key} option={v} />\n ))}\n </ToolbarGroup>\n {issue ? (\n <Row gap=\"1\" align=\"center\" className=\"text-amber-600 dark:text-amber-400\" title={issue}>\n <AlertTriangle aria-hidden className=\"size-3.5\" />\n <Text size=\"xs\">{translate('builder.canvas.breakpointsIssueLabel')}</Text>\n </Row>\n ) : null}\n </Row>\n )\n}\n\n/**\n * The slim editor surface — the canvas + layers + inspector, plus Insert and\n * undo/redo. It deliberately drops the project-builder *lifecycle* chrome\n * (publish, versions, AI, page SEO): the document owns the lifecycle, not the\n * field. It keeps spatial/preview controls — including the breakpoint\n * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.\n */\nfunction CompositionEditorShell({\n readOnly,\n trailingPanes,\n roleNotices,\n conflicts,\n}: {\n readOnly: boolean\n /** EC-595 A2 — the field's conflict notices (see {@link CompositionFieldEditor}). */\n conflicts?: ReactNode\n /** Document-level panes appended after `Properties` (EC-224). */\n trailingPanes?: readonly InspectorPane[]\n /** Notices for the roles nobody fills (EC-591) — see {@link canvasRoleNotices}. */\n roleNotices?: readonly MessageKey[]\n}) {\n const builder = useBuilder()\n const dispatch = builder.dispatch\n const [insertContext, setInsertContext] = useState<InsertContext | null>(null)\n const [insertOpen, setInsertOpen] = useState(false)\n const rootId = builder.present.id\n\n // The canvas SUPPLIES the `Properties` pane (EC-224); the document editor\n // appends its own (`Versions`). Properties dims under read-only just as the\n // whole inspector column used to — but the appended panes stay interactive.\n const propertiesPane = useMemo<InspectorPane>(\n () => ({\n id: 'properties',\n label: translate('builder.inspector.propertiesPane'),\n icon: <SlidersHorizontal className=\"size-3.5\" />,\n render: () => (\n <div className={cn('h-full min-h-0 overflow-hidden', readOnly && 'pointer-events-none opacity-60')}>\n <PropertyInspector />\n </div>\n ),\n }),\n [readOnly],\n )\n const panes = useMemo<InspectorPane[]>(\n () => [propertiesPane, ...(trailingPanes ?? [])],\n [propertiesPane, trailingPanes],\n )\n\n // The single insert entry point for BOTH the toolbar and the page tree's\n // \"Add child\" (EC-383). It selects its target first so `builder.selectedId`\n // is the one source of truth for where an insert lands: the toolbar resolves\n // `builder.selectedId ?? rootId` while Add-child passes an explicit row, and\n // without this sync clicking Add-child (which otherwise leaves selection put)\n // could target a different node than the toolbar — surfacing as a mismatched\n // (even empty \"No allowed components match\") insert dialog. Both now resolve\n // the identical target + allow set for the same selection.\n function openInsert(nodeId: string) {\n // EC-584: the offer reads the store's placement scope — the field's vocabulary at\n // the composition root, each slot's below — the same scope the reducer judges by.\n const ctx = resolveInsertContext(builder, nodeId)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n }\n\n // EC-405: the in-canvas \"+\" affordances resolve the SAME slot rules through the\n // shared position-carrying insert-context resolution, and open the panel at the\n // clicked edge (an explicit position that survives to the insertion, rather than\n // the panel re-deriving it from the selection). Re-memoized on every store change\n // so the closures read the live graph.\n const affordanceApi = useMemo<InsertAffordanceApi>(\n () => ({\n positionsFor: (nodeId: string) => insertablePositions(builder, nodeId),\n openAt: (nodeId: string, position: InsertPosition) => {\n const ctx = resolveInsertContextAt(builder, nodeId, position)\n if (!ctx) return\n if (nodeId !== builder.selectedId) dispatch({ type: 'select', id: nodeId })\n setInsertContext(ctx)\n setInsertOpen(true)\n },\n }),\n [builder, dispatch],\n )\n\n return (\n <InsertAffordanceProvider value={affordanceApi}>\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border\">\n <Toolbar>\n <ToolbarGroup>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.undo')}\n title={translate('builder.canvas.undo')}\n disabled={readOnly || !builder.canUndo}\n onClick={() => dispatch({ type: 'undo' })}\n >\n <Undo2 />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('builder.canvas.redo')}\n title={translate('builder.canvas.redo')}\n disabled={readOnly || !builder.canRedo}\n onClick={() => dispatch({ type: 'redo' })}\n >\n <Redo2 />\n </Button>\n </ToolbarGroup>\n <ToolbarSeparator />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={readOnly}\n onClick={() => openInsert(builder.selectedId ?? rootId)}\n >\n <Plus /> {translate('builder.canvas.insert')}\n </Button>\n {/* Breakpoint preview-width switcher (EC-211/EC-250), centered over the\n canvas via flanking spacers. A preview control, not an edit — stays\n active even in read-only published preview. */}\n <Spacer />\n <BreakpointSwitcher />\n <Spacer />\n </Toolbar>\n </div>\n\n <div className=\"grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border\">\n <Panel className={cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60')}>\n <PageTree onAddChild={openInsert} />\n </Panel>\n <div className={cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60')}>\n {conflicts ? <div className=\"shrink-0 border-b border-border p-2\">{conflicts}</div> : null}\n {/* EC-584: a placement the reducer refused, with its reason — never a silent no-op. */}\n <PlacementRefusalNotice\n refusal={builder.refusal}\n fieldLabel={builder.field.label}\n onDismiss={() => dispatch({ type: 'clearRefusal' })}\n />\n <CanvasRoleNotices notices={roleNotices ?? []} />\n {/* EC-300/AD-16: the read-only Published view resolves references against\n the published perspective (honest preview); the editable Draft view\n resolves drafts (a just-made reusable shows immediately). */}\n <BuilderCanvas perspective={readOnly ? 'published' : DRAFT_PERSPECTIVE} />\n </div>\n <InspectorSidebar panes={panes} />\n </div>\n\n <InsertMenu context={insertContext} open={insertOpen} onOpenChange={setInsertOpen} />\n </div>\n </InsertAffordanceProvider>\n )\n}\n"]}
@@ -23,6 +23,11 @@ export interface DeleteKeyListenerOptions {
23
23
  getRootId: () => string | null;
24
24
  /** Extra suppression (e.g. a read-only published preview). */
25
25
  isDisabled?: () => boolean;
26
+ /**
27
+ * EC-549: remove the selected typography RUN, when one is selected (it takes
28
+ * precedence — a run and a node are never both selected); returns whether it did.
29
+ */
30
+ removeSelectedRun?: () => boolean;
26
31
  }
27
32
  interface ListenerTarget {
28
33
  addEventListener(type: string, listener: (e: Event) => void): void;
@@ -34,6 +34,10 @@ export function attachDeleteKeyListener(target, opts) {
34
34
  return;
35
35
  if (isEditingContext(event.target))
36
36
  return;
37
+ if (opts.removeSelectedRun?.()) {
38
+ event.preventDefault();
39
+ return;
40
+ }
37
41
  const selectedId = opts.getSelectedId();
38
42
  if (!selectedId || selectedId === opts.getRootId())
39
43
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"delete-key.js","sourceRoot":"","sources":["../../../src/components/builder/delete-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAE3D;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA0B;IACzD,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAA;AACtC,CAAC;AAkBD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAsB,EACtB,IAA8B;IAE9B,MAAM,SAAS,GAAG,CAAC,CAAQ,EAAQ,EAAE;QACnC,MAAM,KAAK,GAAG,CAAkB,CAAA;QAChC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW;YAAE,OAAM;QAC/D,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM;YAAE,OAAM;QAC1D,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,KAAK;YAAE,OAAM;QACxC,IAAI,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAM;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAA;QACvC,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,IAAI,CAAC,SAAS,EAAE;YAAE,OAAM;QAC1D,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACzB,CAAC,CAAA;IACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;IAC7C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;AAC/D,CAAC","sourcesContent":["import { clipboardEventBlocked } from './clipboard-actions'\n\n/**\n * Delete/Backspace removes the selected node (EC-404, scope 3) — the gap the\n * findings batch surfaced: the studio had no keyboard remove at all except cut\n * (⌘X). This sits beside the clipboard listeners (`attachClipboardListeners`),\n * wired on the same two surfaces (the studio window for the page tree, the\n * iframe document for the canvas), and reuses the SAME editing-context guard so\n * pressing Backspace while typing in an input or an inline text/rich-text editor\n * edits the text — never deletes the block.\n */\n\n/**\n * True when a key event's target owns its own text-editing behavior — a form\n * field or any contenteditable (which includes the EC-158/EC-159 inline\n * editors). The single guard for the Delete/Backspace remove, kept identical to\n * the clipboard shortcuts' scope so the two can never disagree.\n */\nexport function isEditingContext(target: EventTarget | null): boolean {\n return clipboardEventBlocked(target)\n}\n\nexport interface DeleteKeyListenerOptions {\n /** The current selection — the remove subject. */\n getSelectedId: () => string | null\n /** Dispatch the remove (the existing `remove` tree action; one undo restores). */\n remove: (id: string) => void\n /** The id that must never be removed (the composition root). */\n getRootId: () => string | null\n /** Extra suppression (e.g. a read-only published preview). */\n isDisabled?: () => boolean\n}\n\ninterface ListenerTarget {\n addEventListener(type: string, listener: (e: Event) => void): void\n removeEventListener(type: string, listener: (e: Event) => void): void\n}\n\n/**\n * Wire Delete/Backspace → remove the selected node. Inert when the event\n * targets an editing context, when nothing (or the root) is selected, or when\n * disabled. Modifier combinations are ignored so it never shadows a browser\n * shortcut (⌘Delete etc.).\n */\nexport function attachDeleteKeyListener(\n target: ListenerTarget,\n opts: DeleteKeyListenerOptions,\n): () => void {\n const onKeyDown = (e: Event): void => {\n const event = e as KeyboardEvent\n if (event.key !== 'Delete' && event.key !== 'Backspace') return\n if (event.metaKey || event.ctrlKey || event.altKey) return\n if (opts.isDisabled?.() ?? false) return\n if (isEditingContext(event.target)) return\n const selectedId = opts.getSelectedId()\n if (!selectedId || selectedId === opts.getRootId()) return\n event.preventDefault()\n opts.remove(selectedId)\n }\n target.addEventListener('keydown', onKeyDown)\n return () => target.removeEventListener('keydown', onKeyDown)\n}\n"]}
1
+ {"version":3,"file":"delete-key.js","sourceRoot":"","sources":["../../../src/components/builder/delete-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAE3D;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA0B;IACzD,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAA;AACtC,CAAC;AAuBD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAsB,EACtB,IAA8B;IAE9B,MAAM,SAAS,GAAG,CAAC,CAAQ,EAAQ,EAAE;QACnC,MAAM,KAAK,GAAG,CAAkB,CAAA;QAChC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW;YAAE,OAAM;QAC/D,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM;YAAE,OAAM;QAC1D,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,KAAK;YAAE,OAAM;QACxC,IAAI,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAM;QAC1C,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,EAAE,CAAC;YAC/B,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,OAAM;QACR,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAA;QACvC,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,IAAI,CAAC,SAAS,EAAE;YAAE,OAAM;QAC1D,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACzB,CAAC,CAAA;IACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;IAC7C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;AAC/D,CAAC","sourcesContent":["import { clipboardEventBlocked } from './clipboard-actions'\n\n/**\n * Delete/Backspace removes the selected node (EC-404, scope 3) — the gap the\n * findings batch surfaced: the studio had no keyboard remove at all except cut\n * (⌘X). This sits beside the clipboard listeners (`attachClipboardListeners`),\n * wired on the same two surfaces (the studio window for the page tree, the\n * iframe document for the canvas), and reuses the SAME editing-context guard so\n * pressing Backspace while typing in an input or an inline text/rich-text editor\n * edits the text — never deletes the block.\n */\n\n/**\n * True when a key event's target owns its own text-editing behavior — a form\n * field or any contenteditable (which includes the EC-158/EC-159 inline\n * editors). The single guard for the Delete/Backspace remove, kept identical to\n * the clipboard shortcuts' scope so the two can never disagree.\n */\nexport function isEditingContext(target: EventTarget | null): boolean {\n return clipboardEventBlocked(target)\n}\n\nexport interface DeleteKeyListenerOptions {\n /** The current selection — the remove subject. */\n getSelectedId: () => string | null\n /** Dispatch the remove (the existing `remove` tree action; one undo restores). */\n remove: (id: string) => void\n /** The id that must never be removed (the composition root). */\n getRootId: () => string | null\n /** Extra suppression (e.g. a read-only published preview). */\n isDisabled?: () => boolean\n /**\n * EC-549: remove the selected typography RUN, when one is selected (it takes\n * precedence — a run and a node are never both selected); returns whether it did.\n */\n removeSelectedRun?: () => boolean\n}\n\ninterface ListenerTarget {\n addEventListener(type: string, listener: (e: Event) => void): void\n removeEventListener(type: string, listener: (e: Event) => void): void\n}\n\n/**\n * Wire Delete/Backspace → remove the selected node. Inert when the event\n * targets an editing context, when nothing (or the root) is selected, or when\n * disabled. Modifier combinations are ignored so it never shadows a browser\n * shortcut (⌘Delete etc.).\n */\nexport function attachDeleteKeyListener(\n target: ListenerTarget,\n opts: DeleteKeyListenerOptions,\n): () => void {\n const onKeyDown = (e: Event): void => {\n const event = e as KeyboardEvent\n if (event.key !== 'Delete' && event.key !== 'Backspace') return\n if (event.metaKey || event.ctrlKey || event.altKey) return\n if (opts.isDisabled?.() ?? false) return\n if (isEditingContext(event.target)) return\n if (opts.removeSelectedRun?.()) {\n event.preventDefault()\n return\n }\n const selectedId = opts.getSelectedId()\n if (!selectedId || selectedId === opts.getRootId()) return\n event.preventDefault()\n opts.remove(selectedId)\n }\n target.addEventListener('keydown', onKeyDown)\n return () => target.removeEventListener('keydown', onKeyDown)\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { ComponentNode } from '@elytracms/core/project-graph';
2
- import type { DragSubject } from '@elytracms/studio/lib/dnd/drop-rules';
2
+ import type { DragSubject } from '../../lib/dnd/drop-rules.js';
3
3
  /**
4
4
  * The one live drag session (EC-160). HTML5 `dataTransfer` payloads are
5
5
  * unreadable during `dragover`, so the canvas, page tree, and blocks panel
@@ -1 +1 @@
1
- {"version":3,"file":"drag-session.js","sourceRoot":"","sources":["../../../src/components/builder/drag-session.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,6BAA6B,CAAA;AAiB5D,IAAI,MAAM,GAAuB,IAAI,CAAA;AACrC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAe,CAAA;AAC3C,MAAM,cAAc,GAAG,IAAI,GAAG,EAAiB,CAAA;AAE/C,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,OAAoB,EAAE,YAAiC;IAC/E,MAAM,GAAG,OAAO,CAAA;IAChB,IAAI,YAAY,EAAE,CAAC;QACjB,YAAY,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACpD,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACjD,YAAY,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;IAChF,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,cAAc,CAAC;QAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;AAC/D,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,OAAoB;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAM;IACnB,MAAM,OAAO,GAAG,MAAM,CAAA;IACtB,MAAM,GAAG,IAAI,CAAA;IACb,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,YAAY,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AACtE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,gBAAgB,CAAC,QAAqB;IACpD,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC1B,OAAO,GAAG,EAAE;QACV,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAuB;IACxD,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC5B,OAAO,GAAG,EAAE;QACV,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC,CAAA;AACH,CAAC","sourcesContent":["import type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { DragSubject } from '@elytracms/studio/lib/dnd/drop-rules'\n\n/**\n * The one live drag session (EC-160). HTML5 `dataTransfer` payloads are\n * unreadable during `dragover`, so the canvas, page tree, and blocks panel\n * share this module-level session instead: the source begins it, every\n * surface reads it to resolve drop eligibility (via the pure rules in\n * `lib/dnd/drop-rules`), and exactly one place ends it — the drop handler\n * (`dropped`) or the source's `dragend` (`cancelled`). `endDrag` is\n * idempotent, so the source's trailing `dragend` after a successful drop\n * cannot double-commit or double-notify.\n *\n * The iframe canvas shares the parent window's JS realm (listeners are wired\n * from the parent), so one module instance covers all surfaces.\n */\n\n/** Custom MIME marking Elytra drags (lets surfaces ignore foreign drags). */\nexport const ELYTRA_DND_MIME = 'application/x-elytra-canvas'\n\nexport type DragSource = 'canvas' | 'tree' | 'blocks'\nexport type DragOutcome = 'dropped' | 'cancelled'\n\nexport interface DragSession {\n subject: DragSubject\n source: DragSource\n /** Human label (drag text fallback). */\n label: string\n /** For `component` subjects: builds the subtree to insert (ids reassigned by the store). */\n buildNode?: () => ComponentNode\n}\n\ntype EndListener = (outcome: DragOutcome, session: DragSession) => void\ntype StartListener = (session: DragSession) => void\n\nlet active: DragSession | null = null\nconst endListeners = new Set<EndListener>()\nconst startListeners = new Set<StartListener>()\n\n/** Begin a drag session and stamp the dataTransfer (Firefox requires data to drag). */\nexport function beginDrag(session: DragSession, dataTransfer: DataTransfer | null): void {\n active = session\n if (dataTransfer) {\n dataTransfer.setData(ELYTRA_DND_MIME, session.label)\n dataTransfer.setData('text/plain', session.label)\n dataTransfer.effectAllowed = session.subject.kind === 'node' ? 'move' : 'copy'\n }\n for (const listener of [...startListeners]) listener(session)\n}\n\n/** The live session, or null when nothing Elytra-owned is being dragged. */\nexport function activeDrag(): DragSession | null {\n return active\n}\n\n/**\n * End the live session (no-op when none is active) and notify subscribers.\n * Call with `dropped` from the drop handler that dispatched the action, and\n * with `cancelled` from the source's `dragend`.\n */\nexport function endDrag(outcome: DragOutcome): void {\n if (!active) return\n const session = active\n active = null\n for (const listener of [...endListeners]) listener(outcome, session)\n}\n\n/** Subscribe to session end (indicator cleanup, blocks-panel close-on-drop). */\nexport function onDragSessionEnd(listener: EndListener): () => void {\n endListeners.add(listener)\n return () => {\n endListeners.delete(listener)\n }\n}\n\n/**\n * Subscribe to session start — fired for EVERY drag source (canvas node, tree\n * row, blocks-panel tile). The canvas insertion affordances (EC-405) use this to\n * hide while a drag is live: the DnD before/after/into indicators own the \"where\n * it lands\" story during a drag, so a competing \"+\" would be noise.\n */\nexport function onDragSessionStart(listener: StartListener): () => void {\n startListeners.add(listener)\n return () => {\n startListeners.delete(listener)\n }\n}\n"]}
1
+ {"version":3,"file":"drag-session.js","sourceRoot":"","sources":["../../../src/components/builder/drag-session.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,6BAA6B,CAAA;AAiB5D,IAAI,MAAM,GAAuB,IAAI,CAAA;AACrC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAe,CAAA;AAC3C,MAAM,cAAc,GAAG,IAAI,GAAG,EAAiB,CAAA;AAE/C,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,OAAoB,EAAE,YAAiC;IAC/E,MAAM,GAAG,OAAO,CAAA;IAChB,IAAI,YAAY,EAAE,CAAC;QACjB,YAAY,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACpD,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACjD,YAAY,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;IAChF,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,cAAc,CAAC;QAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;AAC/D,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,OAAoB;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAM;IACnB,MAAM,OAAO,GAAG,MAAM,CAAA;IACtB,MAAM,GAAG,IAAI,CAAA;IACb,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,YAAY,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AACtE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,gBAAgB,CAAC,QAAqB;IACpD,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC1B,OAAO,GAAG,EAAE;QACV,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAuB;IACxD,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC5B,OAAO,GAAG,EAAE;QACV,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC,CAAA;AACH,CAAC","sourcesContent":["import type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { DragSubject } from '../../lib/dnd/drop-rules'\n\n/**\n * The one live drag session (EC-160). HTML5 `dataTransfer` payloads are\n * unreadable during `dragover`, so the canvas, page tree, and blocks panel\n * share this module-level session instead: the source begins it, every\n * surface reads it to resolve drop eligibility (via the pure rules in\n * `lib/dnd/drop-rules`), and exactly one place ends it — the drop handler\n * (`dropped`) or the source's `dragend` (`cancelled`). `endDrag` is\n * idempotent, so the source's trailing `dragend` after a successful drop\n * cannot double-commit or double-notify.\n *\n * The iframe canvas shares the parent window's JS realm (listeners are wired\n * from the parent), so one module instance covers all surfaces.\n */\n\n/** Custom MIME marking Elytra drags (lets surfaces ignore foreign drags). */\nexport const ELYTRA_DND_MIME = 'application/x-elytra-canvas'\n\nexport type DragSource = 'canvas' | 'tree' | 'blocks'\nexport type DragOutcome = 'dropped' | 'cancelled'\n\nexport interface DragSession {\n subject: DragSubject\n source: DragSource\n /** Human label (drag text fallback). */\n label: string\n /** For `component` subjects: builds the subtree to insert (ids reassigned by the store). */\n buildNode?: () => ComponentNode\n}\n\ntype EndListener = (outcome: DragOutcome, session: DragSession) => void\ntype StartListener = (session: DragSession) => void\n\nlet active: DragSession | null = null\nconst endListeners = new Set<EndListener>()\nconst startListeners = new Set<StartListener>()\n\n/** Begin a drag session and stamp the dataTransfer (Firefox requires data to drag). */\nexport function beginDrag(session: DragSession, dataTransfer: DataTransfer | null): void {\n active = session\n if (dataTransfer) {\n dataTransfer.setData(ELYTRA_DND_MIME, session.label)\n dataTransfer.setData('text/plain', session.label)\n dataTransfer.effectAllowed = session.subject.kind === 'node' ? 'move' : 'copy'\n }\n for (const listener of [...startListeners]) listener(session)\n}\n\n/** The live session, or null when nothing Elytra-owned is being dragged. */\nexport function activeDrag(): DragSession | null {\n return active\n}\n\n/**\n * End the live session (no-op when none is active) and notify subscribers.\n * Call with `dropped` from the drop handler that dispatched the action, and\n * with `cancelled` from the source's `dragend`.\n */\nexport function endDrag(outcome: DragOutcome): void {\n if (!active) return\n const session = active\n active = null\n for (const listener of [...endListeners]) listener(outcome, session)\n}\n\n/** Subscribe to session end (indicator cleanup, blocks-panel close-on-drop). */\nexport function onDragSessionEnd(listener: EndListener): () => void {\n endListeners.add(listener)\n return () => {\n endListeners.delete(listener)\n }\n}\n\n/**\n * Subscribe to session start — fired for EVERY drag source (canvas node, tree\n * row, blocks-panel tile). The canvas insertion affordances (EC-405) use this to\n * hide while a drag is live: the DnD before/after/into indicators own the \"where\n * it lands\" story during a drag, so a competing \"+\" would be noise.\n */\nexport function onDragSessionStart(listener: StartListener): () => void {\n startListeners.add(listener)\n return () => {\n startListeners.delete(listener)\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '@elytracms/studio/lib/utils';
2
+ import { cn } from '../../lib/utils.js';
3
3
  import { tileMonogram } from './blocks-model.js';
4
4
  /**
5
5
  * Blocks-panel tile previews (EC-439 AD-2): previews are ARTIFACTS, never
@@ -1 +1 @@
1
- {"version":3,"file":"fixture-thumbnail.js","sourceRoot":"","sources":["../../../src/components/builder/fixture-thumbnail.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C;;;;;;;;;;;;GAYG;AAEH,4FAA4F;AAC5F,MAAM,aAAa,GAAG,wDAAwD,CAAA;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACpC,GAAG,EACH,GAAG,EACH,SAAS,GAOV;IACC,OAAO,CACL,gFAGE,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC,EACvC,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,YAKjC,cACE,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG;YACR,sEAAsE;YACtE,8BAA8B;YAC9B,SAAS,EAAE,KAAK,EAChB,SAAS,EAAC,4DAA4D,GACtE,GACE,CACP,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAyC;IAC9F,OAAO,CACL,2GAIE,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,yBAAyB,EAAE,SAAS,CAAC,EAClE,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,YAEjC,eAAM,SAAS,EAAC,6HAA6H,YAC1I,YAAY,CAAC,KAAK,CAAC,GACf,GACH,CACP,CAAA;AACH,CAAC","sourcesContent":["import { cn } from '@elytracms/studio/lib/utils'\nimport { tileMonogram } from './blocks-model'\n\n/**\n * Blocks-panel tile previews (EC-439 AD-2): previews are ARTIFACTS, never\n * runtime renders. A tile shows a curated `manifest.previewImage` (EC-409) or\n * an EC-440-generated fixture image through {@link PreviewImageThumbnail}, and\n * falls back to the deliberate icon+name placeholder\n * ({@link PlaceholderThumbnail}) when neither exists. The in-document live\n * fixture render (`ScaledFixtureThumbnail`, EC-164) is gone: since EC-414 the\n * consumer theme lives only in the canvas iframe, so no in-studio-document\n * render of consumer components can ever be styled correctly again.\n *\n * One frame per tile (AD-3): the tile's outer border is the ONLY border — the\n * preview inset is a borderless, tinted, rounded area.\n */\n\n/** The shared borderless 16/10 inset both preview tiers and the placeholder render into. */\nconst INSET_CLASSES = 'relative w-full overflow-hidden rounded-md bg-muted/40'\n\n/**\n * Preview-image tile (EC-409 curated / EC-440 generated): renders the resolved\n * `previewUrl` in the shared 16/10 inset, object-fit cover. The image is never\n * a fetch the engine performs, just an `<img>` with the consumer's URL string;\n * `alt` is the component title (already-localized manifest data).\n */\nexport function PreviewImageThumbnail({\n src,\n alt,\n className,\n}: {\n /** The resolved preview URL (curated `manifest.previewImage` or a generated artifact), passed through verbatim. */\n src: string\n /** The component title (localized manifest data — no new catalog string). */\n alt: string\n className?: string\n}) {\n return (\n <div\n data-block-thumbnail\n data-block-thumbnail-image\n className={cn(INSET_CLASSES, className)}\n style={{ aspectRatio: '16 / 10' }}\n >\n {/* Deliberately NOT loading=\"lazy\": native lazy images need rendering\n frames to trigger (occluded/headless windows never load them), and a\n tile preview is a small asset. */}\n <img\n src={src}\n alt={alt}\n // The surrounding tile is the drag source; a natively draggable <img>\n // would hijack the tile drag.\n draggable={false}\n className=\"pointer-events-none h-full w-full select-none object-cover\"\n />\n </div>\n )\n}\n\n/**\n * The icon+name placeholder (EC-439 AD-2, tier 3): a deliberate glyph\n * treatment — a centered two-letter monogram derived from the component title\n * in a muted disc — never an empty box and never a lying render. Decorative\n * only (`aria-hidden`): the tile itself carries the accessible name.\n */\nexport function PlaceholderThumbnail({ title, className }: { title: string; className?: string }) {\n return (\n <div\n aria-hidden\n data-block-thumbnail\n data-block-thumbnail-placeholder\n className={cn(INSET_CLASSES, 'grid place-items-center', className)}\n style={{ aspectRatio: '16 / 10' }}\n >\n <span className=\"grid size-10 select-none place-items-center rounded-full bg-muted text-sm font-semibold tracking-wide text-muted-foreground\">\n {tileMonogram(title)}\n </span>\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"fixture-thumbnail.js","sourceRoot":"","sources":["../../../src/components/builder/fixture-thumbnail.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C;;;;;;;;;;;;GAYG;AAEH,4FAA4F;AAC5F,MAAM,aAAa,GAAG,wDAAwD,CAAA;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACpC,GAAG,EACH,GAAG,EACH,SAAS,GAOV;IACC,OAAO,CACL,gFAGE,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC,EACvC,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,YAKjC,cACE,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG;YACR,sEAAsE;YACtE,8BAA8B;YAC9B,SAAS,EAAE,KAAK,EAChB,SAAS,EAAC,4DAA4D,GACtE,GACE,CACP,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAyC;IAC9F,OAAO,CACL,2GAIE,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,yBAAyB,EAAE,SAAS,CAAC,EAClE,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,YAEjC,eAAM,SAAS,EAAC,6HAA6H,YAC1I,YAAY,CAAC,KAAK,CAAC,GACf,GACH,CACP,CAAA;AACH,CAAC","sourcesContent":["import { cn } from '../../lib/utils'\nimport { tileMonogram } from './blocks-model'\n\n/**\n * Blocks-panel tile previews (EC-439 AD-2): previews are ARTIFACTS, never\n * runtime renders. A tile shows a curated `manifest.previewImage` (EC-409) or\n * an EC-440-generated fixture image through {@link PreviewImageThumbnail}, and\n * falls back to the deliberate icon+name placeholder\n * ({@link PlaceholderThumbnail}) when neither exists. The in-document live\n * fixture render (`ScaledFixtureThumbnail`, EC-164) is gone: since EC-414 the\n * consumer theme lives only in the canvas iframe, so no in-studio-document\n * render of consumer components can ever be styled correctly again.\n *\n * One frame per tile (AD-3): the tile's outer border is the ONLY border — the\n * preview inset is a borderless, tinted, rounded area.\n */\n\n/** The shared borderless 16/10 inset both preview tiers and the placeholder render into. */\nconst INSET_CLASSES = 'relative w-full overflow-hidden rounded-md bg-muted/40'\n\n/**\n * Preview-image tile (EC-409 curated / EC-440 generated): renders the resolved\n * `previewUrl` in the shared 16/10 inset, object-fit cover. The image is never\n * a fetch the engine performs, just an `<img>` with the consumer's URL string;\n * `alt` is the component title (already-localized manifest data).\n */\nexport function PreviewImageThumbnail({\n src,\n alt,\n className,\n}: {\n /** The resolved preview URL (curated `manifest.previewImage` or a generated artifact), passed through verbatim. */\n src: string\n /** The component title (localized manifest data — no new catalog string). */\n alt: string\n className?: string\n}) {\n return (\n <div\n data-block-thumbnail\n data-block-thumbnail-image\n className={cn(INSET_CLASSES, className)}\n style={{ aspectRatio: '16 / 10' }}\n >\n {/* Deliberately NOT loading=\"lazy\": native lazy images need rendering\n frames to trigger (occluded/headless windows never load them), and a\n tile preview is a small asset. */}\n <img\n src={src}\n alt={alt}\n // The surrounding tile is the drag source; a natively draggable <img>\n // would hijack the tile drag.\n draggable={false}\n className=\"pointer-events-none h-full w-full select-none object-cover\"\n />\n </div>\n )\n}\n\n/**\n * The icon+name placeholder (EC-439 AD-2, tier 3): a deliberate glyph\n * treatment — a centered two-letter monogram derived from the component title\n * in a muted disc — never an empty box and never a lying render. Decorative\n * only (`aria-hidden`): the tile itself carries the accessible name.\n */\nexport function PlaceholderThumbnail({ title, className }: { title: string; className?: string }) {\n return (\n <div\n aria-hidden\n data-block-thumbnail\n data-block-thumbnail-placeholder\n className={cn(INSET_CLASSES, 'grid place-items-center', className)}\n style={{ aspectRatio: '16 / 10' }}\n >\n <span className=\"grid size-10 select-none place-items-center rounded-full bg-muted text-sm font-semibold tracking-wide text-muted-foreground\">\n {tileMonogram(title)}\n </span>\n </div>\n )\n}\n"]}
@@ -1,5 +1,5 @@
1
- import type { ComponentNode } from '@elytracms/core/project-graph';
2
- import type { StudioRegistry } from '@elytracms/studio/lib/registry';
1
+ import type { ComponentNode, PlacementScope } from '@elytracms/core/project-graph';
2
+ import type { StudioRegistry } from '../../lib/registry/index.js';
3
3
  import type { InsertContext } from './insert-menu.js';
4
4
  /**
5
5
  * Where an insert lands: the canvas insert-context resolution, shared by every
@@ -9,22 +9,18 @@ import type { InsertContext } from './insert-menu.js';
9
9
  * Canvas editor (`composition-field-editor.tsx`, EC-188/AD-12) can own it without
10
10
  * importing the dead shell. No UI here — pure graph/registry resolution.
11
11
  */
12
+ /** What the resolution reads from the builder store: the tree and its placement scope (EC-584). */
13
+ export interface InsertScope {
14
+ present: ComponentNode;
15
+ placement: PlacementScope;
16
+ }
12
17
  /**
13
- * Resolve where an insert lands: into the node's child slot, else as a sibling.
14
- *
15
- * `fieldAllow` (EC-188) is the composition field's TOP-LEVEL vocabulary
16
- * (`field.allow`). It constrains ONLY inserts that land as a direct child of the
17
- * active page root — the top level of a composition field value — where it is
18
- * intersected into `slotAllow` so forbidden components are never even offered,
19
- * mirroring the top-level leash in `validateCompositionValue`. Deeper inserts keep
20
- * following each component's own slot `allow`. Callers that omit `fieldAllow` get
21
- * an unconstrained top level. EC-554: which placements the field governs is read
22
- * through `fieldAllowAt` — the SAME reader drag-and-drop and paste judge with, so
23
- * the offer and the placement can never disagree.
18
+ * Resolve where an insert lands: into the node's child slot, else as a sibling. The
19
+ * offered vocabulary is the landing position's (see {@link contextAt}) — the SAME
20
+ * reader the reducer, drag-and-drop and paste judge with, so the offer and the
21
+ * placement can never disagree.
24
22
  */
25
- export declare function resolveInsertContext(builder: {
26
- present: ComponentNode;
27
- }, nodeId: string, fieldAllow?: readonly string[], studio?: StudioRegistry): InsertContext | null;
23
+ export declare function resolveInsertContext(builder: InsertScope, nodeId: string, studio?: StudioRegistry): InsertContext | null;
28
24
  /**
29
25
  * An in-canvas insertion position relative to a reference node (EC-405) — the
30
26
  * DnD position vocabulary (`lib/dnd/drop-rules`), narrowed to the three the
@@ -42,17 +38,15 @@ export type InsertPosition = 'before' | 'after' | 'into';
42
38
  * {@link primaryInsertSlot} (the same slot a drop `into` picks; `null` when the
43
39
  * node accepts no children), `before`/`after` use the reference's parent slot at
44
40
  * `index`/`index + 1` — so this surface and drag-insert can never disagree about
45
- * where a position lands or what it allows. `fieldAllow` (EC-188) still constrains
46
- * ONLY a direct child of the composition root, mirroring `resolveInsertContext`.
41
+ * where a position lands or what it allows (EC-584: the vocabulary is `allowAt`'s,
42
+ * exactly as in `resolveInsertContext`).
47
43
  */
48
- export declare function resolveInsertContextAt(builder: {
49
- present: ComponentNode;
50
- }, nodeId: string, position: InsertPosition, fieldAllow?: readonly string[], studio?: StudioRegistry): InsertContext | null;
44
+ export declare function resolveInsertContextAt(builder: InsertScope, nodeId: string, position: InsertPosition, studio?: StudioRegistry): InsertContext | null;
51
45
  /**
52
46
  * Which affordance positions are actually insertable at `nodeId` (EC-405 scope 3):
53
47
  * a position is offered only when it structurally exists AND at least one registry
54
- * component may land there (the SAME slot-rule filter the tree/DnD and the blocks
55
- * panel apply — {@link hasInsertableComponent} over the resolved `slotAllow`), so a
48
+ * component may land there (the SAME judge the tree/DnD, the blocks panel and the
49
+ * reducer apply — {@link hasInsertableComponent} over the resolved `allow`), so a
56
50
  * blocked spot shows no dead button.
57
51
  *
58
52
  * An EMPTY, insertable container surfaces ONLY `into` — the meaningful action there
@@ -62,6 +56,4 @@ export declare function resolveInsertContextAt(builder: {
62
56
  * child already carries its own edge affordances, so a filled container needs no
63
57
  * center "+").
64
58
  */
65
- export declare function insertablePositions(builder: {
66
- present: ComponentNode;
67
- }, nodeId: string, fieldAllow?: readonly string[], studio?: StudioRegistry): InsertPosition[];
59
+ export declare function insertablePositions(builder: InsertScope, nodeId: string, studio?: StudioRegistry): InsertPosition[];