@elytracms/studio 0.0.91 → 0.0.93

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 (353) hide show
  1. package/convex/asset-alt-migration.test.ts +127 -0
  2. package/convex/asset-replace.test.ts +168 -0
  3. package/convex/asset-revalidate.test.ts +6 -6
  4. package/convex/assets.ts +41 -5
  5. package/convex/cms.ts +537 -177
  6. package/convex/contract-parity.test.ts +173 -19
  7. package/convex/delivery.ts +26 -13
  8. package/convex/document-revalidate.test.ts +17 -16
  9. package/convex/orphan-hydration.test.ts +116 -0
  10. package/convex/presence-cost.test.ts +89 -0
  11. package/convex/rail-write-parity.test.ts +17 -14
  12. package/convex/referenceMaintenance.ts +15 -15
  13. package/convex/schema.ts +70 -5
  14. package/convex/triggers.ts +21 -8
  15. package/convex/write-limits.ts +1 -1
  16. package/dist/components/ai/ai-chat-panel.js +33 -22
  17. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  18. package/dist/components/ai/proposal-card.js +2 -1
  19. package/dist/components/ai/proposal-card.js.map +1 -1
  20. package/dist/components/ai/translate-document-action.d.ts +8 -2
  21. package/dist/components/ai/translate-document-action.js.map +1 -1
  22. package/dist/components/ai/translate-document-dialog.d.ts +1 -1
  23. package/dist/components/ai/translate-document-dialog.js +14 -8
  24. package/dist/components/ai/translate-document-dialog.js.map +1 -1
  25. package/dist/components/ai/translate-skipped.d.ts +19 -0
  26. package/dist/components/ai/translate-skipped.js +46 -0
  27. package/dist/components/ai/translate-skipped.js.map +1 -0
  28. package/dist/components/app-views/default-modules.d.ts +23 -6
  29. package/dist/components/app-views/default-modules.js +33 -14
  30. package/dist/components/app-views/default-modules.js.map +1 -1
  31. package/dist/components/assets/asset-detail.js +18 -8
  32. package/dist/components/assets/asset-detail.js.map +1 -1
  33. package/dist/components/assets/asset-metadata.d.ts +15 -4
  34. package/dist/components/assets/asset-metadata.js +15 -2
  35. package/dist/components/assets/asset-metadata.js.map +1 -1
  36. package/dist/components/assets/asset-search.js +4 -1
  37. package/dist/components/assets/asset-search.js.map +1 -1
  38. package/dist/components/assets/asset-selection.d.ts +0 -8
  39. package/dist/components/assets/asset-selection.js +4 -23
  40. package/dist/components/assets/asset-selection.js.map +1 -1
  41. package/dist/components/assets/asset-thumb.js +3 -2
  42. package/dist/components/assets/asset-thumb.js.map +1 -1
  43. package/dist/components/assets/assets-collection-view-source.d.ts +3 -3
  44. package/dist/components/assets/assets-collection-view-source.js +9 -5
  45. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  46. package/dist/components/assets/assets-store.d.ts +14 -0
  47. package/dist/components/assets/assets-store.js +23 -1
  48. package/dist/components/assets/assets-store.js.map +1 -1
  49. package/dist/components/assets/replace-file.d.ts +22 -0
  50. package/dist/components/assets/replace-file.js +59 -0
  51. package/dist/components/assets/replace-file.js.map +1 -0
  52. package/dist/components/builder/blocks-model.d.ts +14 -26
  53. package/dist/components/builder/blocks-model.js +35 -41
  54. package/dist/components/builder/blocks-model.js.map +1 -1
  55. package/dist/components/builder/blocks-panel.d.ts +7 -3
  56. package/dist/components/builder/blocks-panel.js +6 -5
  57. package/dist/components/builder/blocks-panel.js.map +1 -1
  58. package/dist/components/builder/builder-store.d.ts +126 -30
  59. package/dist/components/builder/builder-store.js +169 -67
  60. package/dist/components/builder/builder-store.js.map +1 -1
  61. package/dist/components/builder/canvas-body.d.ts +30 -0
  62. package/dist/components/builder/canvas-body.js +38 -0
  63. package/dist/components/builder/canvas-body.js.map +1 -0
  64. package/dist/components/builder/canvas-chrome-css.js +7 -0
  65. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  66. package/dist/components/builder/canvas-dnd.d.ts +7 -7
  67. package/dist/components/builder/canvas-dnd.js +5 -5
  68. package/dist/components/builder/canvas-dnd.js.map +1 -1
  69. package/dist/components/builder/canvas-render-context.d.ts +30 -22
  70. package/dist/components/builder/canvas-render-context.js +44 -44
  71. package/dist/components/builder/canvas-render-context.js.map +1 -1
  72. package/dist/components/builder/canvas-runs.d.ts +45 -0
  73. package/dist/components/builder/canvas-runs.js +51 -0
  74. package/dist/components/builder/canvas-runs.js.map +1 -0
  75. package/dist/components/builder/canvas.d.ts +5 -4
  76. package/dist/components/builder/canvas.js +34 -6
  77. package/dist/components/builder/canvas.js.map +1 -1
  78. package/dist/components/builder/clipboard-actions.d.ts +15 -0
  79. package/dist/components/builder/clipboard-actions.js +50 -13
  80. package/dist/components/builder/clipboard-actions.js.map +1 -1
  81. package/dist/components/builder/composition-field-editor.d.ts +10 -16
  82. package/dist/components/builder/composition-field-editor.js +30 -40
  83. package/dist/components/builder/composition-field-editor.js.map +1 -1
  84. package/dist/components/builder/composition-patches.d.ts +7 -0
  85. package/dist/components/builder/composition-patches.js +256 -0
  86. package/dist/components/builder/composition-patches.js.map +1 -0
  87. package/dist/components/builder/delete-key.d.ts +5 -0
  88. package/dist/components/builder/delete-key.js +4 -0
  89. package/dist/components/builder/delete-key.js.map +1 -1
  90. package/dist/components/builder/insert-context.d.ts +17 -25
  91. package/dist/components/builder/insert-context.js +45 -92
  92. package/dist/components/builder/insert-context.js.map +1 -1
  93. package/dist/components/builder/page-tree.d.ts +3 -3
  94. package/dist/components/builder/page-tree.js +3 -3
  95. package/dist/components/builder/page-tree.js.map +1 -1
  96. package/dist/components/builder/placement-refusal.d.ts +18 -0
  97. package/dist/components/builder/placement-refusal.js +46 -0
  98. package/dist/components/builder/placement-refusal.js.map +1 -0
  99. package/dist/components/builder/property-inspector.js +4 -0
  100. package/dist/components/builder/property-inspector.js.map +1 -1
  101. package/dist/components/builder/reuse-context.js +65 -26
  102. package/dist/components/builder/reuse-context.js.map +1 -1
  103. package/dist/components/builder/reuse.d.ts +79 -8
  104. package/dist/components/builder/reuse.js +71 -23
  105. package/dist/components/builder/reuse.js.map +1 -1
  106. package/dist/components/builder/run-inspector.d.ts +11 -0
  107. package/dist/components/builder/run-inspector.js +33 -0
  108. package/dist/components/builder/run-inspector.js.map +1 -0
  109. package/dist/components/cms/asset-field.d.ts +38 -1
  110. package/dist/components/cms/asset-field.js +182 -58
  111. package/dist/components/cms/asset-field.js.map +1 -1
  112. package/dist/components/cms/asset-picker-core.d.ts +16 -0
  113. package/dist/components/cms/asset-picker-core.js +20 -0
  114. package/dist/components/cms/asset-picker-core.js.map +1 -1
  115. package/dist/components/cms/asset-picker.js +12 -18
  116. package/dist/components/cms/asset-picker.js.map +1 -1
  117. package/dist/components/cms/bulk-actions.d.ts +4 -20
  118. package/dist/components/cms/bulk-actions.js +3 -24
  119. package/dist/components/cms/bulk-actions.js.map +1 -1
  120. package/dist/components/cms/cell-edit.d.ts +5 -5
  121. package/dist/components/cms/cell-edit.js.map +1 -1
  122. package/dist/components/cms/cell.js.map +1 -1
  123. package/dist/components/cms/cms-store.d.ts +87 -45
  124. package/dist/components/cms/cms-store.js +347 -203
  125. package/dist/components/cms/cms-store.js.map +1 -1
  126. package/dist/components/cms/collection-table.js +7 -2
  127. package/dist/components/cms/collection-table.js.map +1 -1
  128. package/dist/components/cms/commit-policy.d.ts +24 -0
  129. package/dist/components/cms/commit-policy.js +37 -0
  130. package/dist/components/cms/commit-policy.js.map +1 -0
  131. package/dist/components/cms/detail-editor.js +83 -137
  132. package/dist/components/cms/detail-editor.js.map +1 -1
  133. package/dist/components/cms/document-diff-view.d.ts +3 -1
  134. package/dist/components/cms/document-diff-view.js +12 -7
  135. package/dist/components/cms/document-diff-view.js.map +1 -1
  136. package/dist/components/cms/draft-commit.d.ts +70 -0
  137. package/dist/components/cms/draft-commit.js +82 -0
  138. package/dist/components/cms/draft-commit.js.map +1 -0
  139. package/dist/components/cms/duplicate-document.d.ts +11 -13
  140. package/dist/components/cms/duplicate-document.js +26 -34
  141. package/dist/components/cms/duplicate-document.js.map +1 -1
  142. package/dist/components/cms/field-anchors.d.ts +7 -4
  143. package/dist/components/cms/field-anchors.js +9 -7
  144. package/dist/components/cms/field-anchors.js.map +1 -1
  145. package/dist/components/cms/field-collaboration.d.ts +47 -0
  146. package/dist/components/cms/field-collaboration.js +87 -0
  147. package/dist/components/cms/field-collaboration.js.map +1 -0
  148. package/dist/components/cms/field-control-lib.d.ts +22 -1
  149. package/dist/components/cms/field-control-lib.js +33 -4
  150. package/dist/components/cms/field-control-lib.js.map +1 -1
  151. package/dist/components/cms/field-control.js +17 -14
  152. package/dist/components/cms/field-control.js.map +1 -1
  153. package/dist/components/cms/gate-validation.d.ts +21 -30
  154. package/dist/components/cms/gate-validation.js +11 -22
  155. package/dist/components/cms/gate-validation.js.map +1 -1
  156. package/dist/components/cms/object-array-field.js +4 -4
  157. package/dist/components/cms/object-array-field.js.map +1 -1
  158. package/dist/components/cms/prop-fields.d.ts +7 -2
  159. package/dist/components/cms/prop-fields.js +13 -6
  160. package/dist/components/cms/prop-fields.js.map +1 -1
  161. package/dist/components/cms/reference-confirm.js +10 -4
  162. package/dist/components/cms/reference-confirm.js.map +1 -1
  163. package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
  164. package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
  165. package/dist/components/cms/rich-text/insert.d.ts +20 -9
  166. package/dist/components/cms/rich-text/insert.js +25 -17
  167. package/dist/components/cms/rich-text/insert.js.map +1 -1
  168. package/dist/components/cms/use-document-draft.d.ts +96 -0
  169. package/dist/components/cms/use-document-draft.js +283 -0
  170. package/dist/components/cms/use-document-draft.js.map +1 -0
  171. package/dist/components/cms/use-document-versions.js +2 -2
  172. package/dist/components/cms/use-document-versions.js.map +1 -1
  173. package/dist/components/cms/use-live-url.d.ts +6 -12
  174. package/dist/components/cms/use-live-url.js +16 -15
  175. package/dist/components/cms/use-live-url.js.map +1 -1
  176. package/dist/components/cms/use-publish-controller.d.ts +13 -18
  177. package/dist/components/cms/use-publish-controller.js +24 -41
  178. package/dist/components/cms/use-publish-controller.js.map +1 -1
  179. package/dist/components/cms/use-published-perspective.d.ts +2 -3
  180. package/dist/components/cms/use-published-perspective.js +4 -3
  181. package/dist/components/cms/use-published-perspective.js.map +1 -1
  182. package/dist/components/cms/versions-pane.js +8 -1
  183. package/dist/components/cms/versions-pane.js.map +1 -1
  184. package/dist/components/collection-view/cms-collection-view-source.js +24 -7
  185. package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
  186. package/dist/components/components/transfer-dialog.js +28 -23
  187. package/dist/components/components/transfer-dialog.js.map +1 -1
  188. package/dist/components/components/transfer.d.ts +31 -21
  189. package/dist/components/components/transfer.js +49 -35
  190. package/dist/components/components/transfer.js.map +1 -1
  191. package/dist/components/project-list.js.map +1 -1
  192. package/dist/components/project-session.d.ts +6 -3
  193. package/dist/components/project-session.js +14 -8
  194. package/dist/components/project-session.js.map +1 -1
  195. package/dist/components/project-switcher.js.map +1 -1
  196. package/dist/components/rail-view-source.js +2 -1
  197. package/dist/components/rail-view-source.js.map +1 -1
  198. package/dist/components/section-nav.js +1 -1
  199. package/dist/components/section-nav.js.map +1 -1
  200. package/dist/lib/ai/apply-content.js +56 -116
  201. package/dist/lib/ai/apply-content.js.map +1 -1
  202. package/dist/lib/ai/chat-context.d.ts +8 -0
  203. package/dist/lib/ai/chat-context.js +37 -6
  204. package/dist/lib/ai/chat-context.js.map +1 -1
  205. package/dist/lib/ai/chat-state.d.ts +14 -1
  206. package/dist/lib/ai/chat-state.js.map +1 -1
  207. package/dist/lib/ai/constraints.d.ts +20 -5
  208. package/dist/lib/ai/constraints.js +125 -33
  209. package/dist/lib/ai/constraints.js.map +1 -1
  210. package/dist/lib/ai/fake-provider.d.ts +16 -3
  211. package/dist/lib/ai/fake-provider.js +22 -6
  212. package/dist/lib/ai/fake-provider.js.map +1 -1
  213. package/dist/lib/ai/index.d.ts +2 -0
  214. package/dist/lib/ai/index.js +2 -0
  215. package/dist/lib/ai/index.js.map +1 -1
  216. package/dist/lib/ai/leash.d.ts +34 -0
  217. package/dist/lib/ai/leash.js +28 -0
  218. package/dist/lib/ai/leash.js.map +1 -0
  219. package/dist/lib/ai/provenance.d.ts +1 -1
  220. package/dist/lib/ai/provenance.js.map +1 -1
  221. package/dist/lib/ai/rich-text-input.d.ts +8 -8
  222. package/dist/lib/ai/rich-text-input.js +2 -1
  223. package/dist/lib/ai/rich-text-input.js.map +1 -1
  224. package/dist/lib/ai/rich-text-translate.d.ts +27 -4
  225. package/dist/lib/ai/rich-text-translate.js +28 -10
  226. package/dist/lib/ai/rich-text-translate.js.map +1 -1
  227. package/dist/lib/ai/shape.d.ts +13 -13
  228. package/dist/lib/ai/shape.js +69 -93
  229. package/dist/lib/ai/shape.js.map +1 -1
  230. package/dist/lib/ai/tools.d.ts +36 -41
  231. package/dist/lib/ai/tools.js +85 -87
  232. package/dist/lib/ai/tools.js.map +1 -1
  233. package/dist/lib/ai/translate-document.d.ts +39 -5
  234. package/dist/lib/ai/translate-document.js +160 -51
  235. package/dist/lib/ai/translate-document.js.map +1 -1
  236. package/dist/lib/ai/types.d.ts +34 -23
  237. package/dist/lib/ai/types.js.map +1 -1
  238. package/dist/lib/ai/update-fields.d.ts +21 -12
  239. package/dist/lib/ai/update-fields.js +118 -177
  240. package/dist/lib/ai/update-fields.js.map +1 -1
  241. package/dist/lib/ai/writer-form.d.ts +116 -0
  242. package/dist/lib/ai/writer-form.js +429 -0
  243. package/dist/lib/ai/writer-form.js.map +1 -0
  244. package/dist/lib/ai-current-document.d.ts +11 -11
  245. package/dist/lib/ai-current-document.js +4 -4
  246. package/dist/lib/ai-current-document.js.map +1 -1
  247. package/dist/lib/assets/upload.d.ts +31 -2
  248. package/dist/lib/assets/upload.js +61 -6
  249. package/dist/lib/assets/upload.js.map +1 -1
  250. package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
  251. package/dist/lib/clipboard/os-clipboard.js +8 -4
  252. package/dist/lib/clipboard/os-clipboard.js.map +1 -1
  253. package/dist/lib/clipboard/paste-target.d.ts +18 -9
  254. package/dist/lib/clipboard/paste-target.js +8 -7
  255. package/dist/lib/clipboard/paste-target.js.map +1 -1
  256. package/dist/lib/cms/field-patch.d.ts +15 -0
  257. package/dist/lib/cms/field-patch.js +25 -0
  258. package/dist/lib/cms/field-patch.js.map +1 -0
  259. package/dist/lib/cms/published-documents.d.ts +1 -1
  260. package/dist/lib/cms/published-documents.js +1 -1
  261. package/dist/lib/cms/published-documents.js.map +1 -1
  262. package/dist/lib/cms-views/view-model.d.ts +3 -3
  263. package/dist/lib/collection-view/__fixtures__/document-writes.d.ts +6 -0
  264. package/dist/lib/collection-view/__fixtures__/document-writes.js +23 -0
  265. package/dist/lib/collection-view/__fixtures__/document-writes.js.map +1 -0
  266. package/dist/lib/collection-view/collection-view-source.d.ts +69 -6
  267. package/dist/lib/collection-view/collection-view-source.js.map +1 -1
  268. package/dist/lib/data/collection-content.d.ts +4 -13
  269. package/dist/lib/data/collection-content.js +11 -5
  270. package/dist/lib/data/collection-content.js.map +1 -1
  271. package/dist/lib/dnd/drop-rules.d.ts +25 -31
  272. package/dist/lib/dnd/drop-rules.js +53 -59
  273. package/dist/lib/dnd/drop-rules.js.map +1 -1
  274. package/dist/lib/engine-version.d.ts +1 -1
  275. package/dist/lib/engine-version.js +1 -1
  276. package/dist/lib/engine-version.js.map +1 -1
  277. package/dist/lib/i18n/messages.de.js +72 -15
  278. package/dist/lib/i18n/messages.de.js.map +1 -1
  279. package/dist/lib/i18n/messages.en.d.ts +61 -12
  280. package/dist/lib/i18n/messages.en.js +76 -16
  281. package/dist/lib/i18n/messages.en.js.map +1 -1
  282. package/dist/lib/operations/__fixtures__/write-document.d.ts +30 -0
  283. package/dist/lib/operations/__fixtures__/write-document.js +16 -0
  284. package/dist/lib/operations/__fixtures__/write-document.js.map +1 -0
  285. package/dist/lib/operations/cms-persistence.d.ts +124 -31
  286. package/dist/lib/operations/cms-persistence.js +447 -68
  287. package/dist/lib/operations/cms-persistence.js.map +1 -1
  288. package/dist/lib/operations/config-manifest-lookup.d.ts +35 -0
  289. package/dist/lib/operations/config-manifest-lookup.js +56 -0
  290. package/dist/lib/operations/config-manifest-lookup.js.map +1 -0
  291. package/dist/lib/operations/project-runtime-config.d.ts +39 -0
  292. package/dist/lib/operations/project-runtime-config.js +54 -0
  293. package/dist/lib/operations/project-runtime-config.js.map +1 -0
  294. package/dist/lib/operations/publish-with-revision.d.ts +64 -0
  295. package/dist/lib/operations/publish-with-revision.js +66 -0
  296. package/dist/lib/operations/publish-with-revision.js.map +1 -0
  297. package/dist/lib/operations/runtime-validator.d.ts +32 -0
  298. package/dist/lib/operations/runtime-validator.js +18 -0
  299. package/dist/lib/operations/runtime-validator.js.map +1 -0
  300. package/dist/lib/operations/session-hydration.d.ts +31 -14
  301. package/dist/lib/operations/session-hydration.js +12 -16
  302. package/dist/lib/operations/session-hydration.js.map +1 -1
  303. package/dist/lib/operations/studio-operations.d.ts +59 -2
  304. package/dist/lib/operations/studio-operations.js +76 -14
  305. package/dist/lib/operations/studio-operations.js.map +1 -1
  306. package/dist/lib/persistence/convex-adapter.d.ts +13 -5
  307. package/dist/lib/persistence/convex-adapter.js +189 -74
  308. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  309. package/dist/lib/persistence/convex-client.d.ts +65 -5
  310. package/dist/lib/persistence/convex-client.js +106 -3
  311. package/dist/lib/persistence/convex-client.js.map +1 -1
  312. package/dist/lib/persistence/index.js +17 -18
  313. package/dist/lib/persistence/index.js.map +1 -1
  314. package/dist/lib/persona/labels.js +4 -1
  315. package/dist/lib/persona/labels.js.map +1 -1
  316. package/dist/lib/query/operations-query.d.ts +1 -1
  317. package/dist/lib/query/operations-query.js +1 -1
  318. package/dist/lib/query/operations-query.js.map +1 -1
  319. package/dist/lib/references/impact.d.ts +21 -15
  320. package/dist/lib/references/impact.js +13 -26
  321. package/dist/lib/references/impact.js.map +1 -1
  322. package/dist/lib/references/references-store.d.ts +9 -0
  323. package/dist/lib/references/references-store.js +24 -4
  324. package/dist/lib/references/references-store.js.map +1 -1
  325. package/dist/lib/routes/route-issues.d.ts +6 -6
  326. package/dist/lib/routes/route-issues.js +26 -80
  327. package/dist/lib/routes/route-issues.js.map +1 -1
  328. package/dist/lib/workspace/config-issues.js +2 -1
  329. package/dist/lib/workspace/config-issues.js.map +1 -1
  330. package/dist/lib/workspace/index.d.ts +31 -1
  331. package/dist/lib/workspace/index.js +64 -17
  332. package/dist/lib/workspace/index.js.map +1 -1
  333. package/dist/lib/workspace/switcher-source.d.ts +3 -3
  334. package/dist/lib/workspace/switcher-source.js.map +1 -1
  335. package/dist/lib/workspace/workspace-source.d.ts +2 -2
  336. package/dist/lib/workspace/workspace-source.js +2 -1
  337. package/dist/lib/workspace/workspace-source.js.map +1 -1
  338. package/package.json +3 -3
  339. package/dist/components/cms/cms-composition-validators.d.ts +0 -16
  340. package/dist/components/cms/cms-composition-validators.js +0 -133
  341. package/dist/components/cms/cms-composition-validators.js.map +0 -1
  342. package/dist/components/cms/publish-clean.d.ts +0 -28
  343. package/dist/components/cms/publish-clean.js +0 -38
  344. package/dist/components/cms/publish-clean.js.map +0 -1
  345. package/dist/components/cms/use-save-mode.d.ts +0 -35
  346. package/dist/components/cms/use-save-mode.js +0 -54
  347. package/dist/components/cms/use-save-mode.js.map +0 -1
  348. package/dist/lib/assets/asset-placements.d.ts +0 -28
  349. package/dist/lib/assets/asset-placements.js +0 -83
  350. package/dist/lib/assets/asset-placements.js.map +0 -1
  351. package/dist/lib/cms/document-gate.d.ts +0 -47
  352. package/dist/lib/cms/document-gate.js +0 -61
  353. package/dist/lib/cms/document-gate.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"reference-confirm.js","sourceRoot":"","sources":["../../../src/components/cms/reference-confirm.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { AlertTriangle, ShieldAlert } from 'lucide-react'\nimport { useEffect, useMemo, useState } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport type { DocumentRef } from '@elytracms/core/cms-core'\nimport { buildReferenceImpact, loadInboundReferenceViews } from '@elytracms/studio/lib/references'\nimport type {\n DocumentReferenceImpact,\n ReferenceAction,\n ReferenceDeepLink,\n ReferenceEntryView,\n ReferenceImpact,\n} from '@elytracms/studio/lib/references'\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useCollectionView } from '../collection-view/collection-view-context'\n\n/**\n * The EC-173 reference-aware confirmation dialog for unpublish/delete —\n * single documents (detail editor) and bulk selections (EC-169 bulk bar)\n * alike. It inverts the industry default, visibly:\n *\n * 1. *Before* the action it enumerates every inbound reference that will\n * break (published variant for unpublish, both variants for delete), with\n * the EC-172 labels and deep links to the exact field/node.\n * 2. Proceeding is allowed — weak references are the default. Zero references\n * render a plain confirm; breakage renders \"You can … — N places will show\n * a fallback\".\n * 3. Strict opt-in relation fields (`strict: true`) flip the same dialog into\n * a real block: the proceed button is disabled and every blocking referrer\n * is named and linked. The `@elytracms/core/operations` layer enforces the same\n * rule server-side (structured conflict error) — this dialog is the\n * friendly face, not the only guard.\n *\n * Pre-flight data goes through operations reference queries only (EC-155\n * index, flushed); impact semantics live in `lib/references/impact.ts`.\n */\n\ntype ImpactStatus = 'loading' | 'ready' | 'unavailable'\n\nconst ACTION_LABELS: Record<ReferenceAction, string> = {\n unpublish: translate('cms.editor.unpublish'),\n delete: translate('common.delete'),\n}\n\nexport function ReferenceConfirmDialog({\n projectId,\n action,\n targets,\n open,\n busy = false,\n onOpenChange,\n onConfirm,\n}: {\n projectId: string\n action: ReferenceAction\n /** The documents being acted on (a stable snapshot taken when opening). */\n targets: readonly DocumentRef[]\n open: boolean\n busy?: boolean\n onOpenChange: (open: boolean) => void\n onConfirm: () => void\n}) {\n const source = useCollectionView()\n const [status, setStatus] = useState<ImpactStatus>('loading')\n const [impact, setImpact] = useState<ReferenceImpact | null>(null)\n\n // A stable identity for the acted-on set, so the effect re-runs exactly\n // when the selection (not the array instance) changes.\n const targetsKey = targets.map((ref) => documentKey(ref)).join('\\n')\n\n useEffect(() => {\n if (!open || targets.length === 0) return\n let cancelled = false\n setStatus('loading')\n setImpact(null)\n void (async () => {\n try {\n // Queued session mutations (and the persisted baseline) must land\n // before the pre-flight query — the index is maintained on write.\n await source.flush?.()\n // The source knows how its records are addressed in the reference\n // index (EC-433): documents by default, `{ type: 'asset' }` for the\n // assets rail — so this pre-flight counts the SAME references the\n // rail's detail surface does.\n const views = await loadInboundReferenceViews(projectId, targets, source.referenceTarget)\n if (cancelled) return\n setImpact(buildReferenceImpact(source.collections, action, views))\n setStatus('ready')\n } catch {\n // The index being unreachable must not trap the editor: degrade to a\n // plain confirm with an explicit \"could not check\" warning.\n if (!cancelled) setStatus('unavailable')\n }\n })()\n return () => {\n cancelled = true\n }\n // `targetsKey` stands in for `targets` (stable identity for the acted-on set).\n }, [open, action, targetsKey, projectId])\n\n const actionLabel = ACTION_LABELS[action]\n const subject = useMemo(() => {\n if (targets.length === 1) {\n const ref = targets[0]!\n return `“${source.titleOf(ref)}”`\n }\n return translate('cms.reference.documentCount', { count: targets.length })\n }, [targets, source])\n\n const blocked = status === 'ready' && (impact?.totalBlocking ?? 0) > 0\n const totalBroken = impact?.totalBroken ?? 0\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent data-testid=\"reference-confirm\" className=\"max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>\n {translate('cms.reference.confirmTitle', { action: actionLabel, subject })}\n </DialogTitle>\n <DialogDescription data-testid=\"reference-confirm-summary\">\n {status === 'loading' ? (\n translate('cms.reference.checking')\n ) : status === 'unavailable' ? (\n translate('cms.reference.checkUnavailable')\n ) : blocked ? (\n translate(\n impact!.totalBlocking === 1\n ? 'cms.reference.blocked.one'\n : 'cms.reference.blocked.other',\n { count: impact!.totalBlocking },\n )\n ) : totalBroken === 0 ? (\n <>\n {translate('cms.reference.notReferenced')}\n {action === 'delete'\n ? ` ${translate(\n targets.length === 1\n ? 'cms.reference.permanentDelete.one'\n : 'cms.reference.permanentDelete.other',\n { count: targets.length },\n )}`\n : ''}\n </>\n ) : (\n translate('cms.reference.canProceed', {\n action: translate(\n action === 'unpublish'\n ? 'cms.reference.verb.unpublish'\n : 'cms.reference.verb.delete',\n ),\n summary: impactSummary(targets.length, totalBroken),\n })\n )}\n </DialogDescription>\n </DialogHeader>\n\n {status === 'ready' && impact !== null && totalBroken > 0 ? (\n <ImpactList projectId={projectId} impact={impact} onNavigate={() => onOpenChange(false)} />\n ) : null}\n {status === 'unavailable' ? (\n <Row gap=\"2\" data-testid=\"reference-confirm-unavailable\">\n <AlertTriangle className=\"size-4 shrink-0 text-warning\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate('cms.reference.proceedUnavailable')}\n </Text>\n </Row>\n ) : null}\n\n <DialogFooter>\n <Button\n variant=\"outline\"\n data-testid=\"reference-confirm-cancel\"\n onClick={() => onOpenChange(false)}\n >\n {translate('common.cancel')}\n </Button>\n <Button\n variant={action === 'delete' ? 'destructive' : 'default'}\n disabled={status === 'loading' || blocked || busy}\n data-testid=\"reference-confirm-proceed\"\n onClick={onConfirm}\n >\n {totalBroken > 0 && !blocked\n ? translate('cms.reference.proceedAnyway', { action: actionLabel })\n : actionLabel}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction impactSummary(targetCount: number, totalBroken: number): string {\n const places = translate(\n totalBroken === 1 ? 'cms.reference.placeCount.one' : 'cms.reference.placeCount.other',\n { count: totalBroken },\n )\n // The bulk aggregate: \"N documents, M inbound references will break.\"\n return targetCount > 1\n ? translate('cms.reference.bulkSummary', { count: targetCount, places })\n : places\n}\n\n/**\n * The enumerated impact: flat for a single target, grouped per target for\n * bulk selections (per-item detail, EC-169 honesty rules).\n */\nfunction ImpactList({\n projectId,\n impact,\n onNavigate,\n}: {\n projectId: string\n impact: ReferenceImpact\n onNavigate: () => void\n}) {\n const affected = impact.documents.filter((doc) => doc.broken.length > 0)\n return (\n <Stack gap=\"3\" data-testid=\"reference-confirm-impact\">\n {affected.map((doc) => (\n <ImpactGroup\n key={documentKey(doc.ref)}\n projectId={projectId}\n doc={doc}\n grouped={impact.documents.length > 1}\n onNavigate={onNavigate}\n />\n ))}\n </Stack>\n )\n}\n\nfunction ImpactGroup({\n projectId,\n doc,\n grouped,\n onNavigate,\n}: {\n projectId: string\n doc: DocumentReferenceImpact\n grouped: boolean\n onNavigate: () => void\n}) {\n const source = useCollectionView()\n const blockingKeys = new Set(doc.blocking.map((entry) => entry.key))\n return (\n <Stack gap=\"2\" data-testid={`reference-confirm-doc-${doc.ref.id}`}>\n {grouped ? (\n <Row gap=\"2\">\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\" className=\"uppercase tracking-wide\">\n {source.titleOf(doc.ref)}\n </Text>\n <Badge variant=\"outline\">{doc.broken.length}</Badge>\n </Row>\n ) : null}\n {doc.broken.map((entry) => (\n <ImpactEntryRow\n key={entry.key}\n projectId={projectId}\n entry={entry}\n blocking={blockingKeys.has(entry.key)}\n onNavigate={onNavigate}\n />\n ))}\n </Stack>\n )\n}\n\n/**\n * The shared EC-173 impact rows: one deep-linked row per broken inbound\n * reference, strict-blocking ones flagged. Rendered by the document\n * confirmation dialog (above) and the asset-delete dialog (EC-202) alike — both\n * reduce to \"these places reference the thing you're removing\", so both lean on\n * the same row + deep-link rendering (which already handles asset link kinds).\n */\nexport function ReferenceImpactRows({\n projectId,\n broken,\n blocking = [],\n onNavigate,\n}: {\n projectId: string\n broken: readonly ReferenceEntryView[]\n blocking?: readonly ReferenceEntryView[]\n onNavigate: () => void\n}) {\n const blockingKeys = new Set(blocking.map((entry) => entry.key))\n return (\n <Stack gap=\"2\" data-testid=\"reference-impact-rows\">\n {broken.map((entry) => (\n <ImpactEntryRow\n key={entry.key}\n projectId={projectId}\n entry={entry}\n blocking={blockingKeys.has(entry.key)}\n onNavigate={onNavigate}\n />\n ))}\n </Stack>\n )\n}\n\n/**\n * One affected place: the EC-172 labels with a working deep link to the exact\n * field/node, the entry kind, draft/published visibility — and a strict\n * marker when this entry is what blocks the action.\n */\nfunction ImpactEntryRow({\n projectId,\n entry,\n blocking,\n onNavigate,\n}: {\n projectId: string\n entry: ReferenceEntryView\n blocking: boolean\n onNavigate: () => void\n}) {\n return (\n <Row\n gap=\"2\"\n className={\n blocking\n ? 'rounded-md border border-destructive/50 bg-destructive/5 px-2.5 py-2'\n : 'rounded-md border border-border/70 bg-background/40 px-2.5 py-2'\n }\n data-testid=\"reference-confirm-entry\"\n >\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <ImpactDeepLink\n projectId={projectId}\n link={entry.sourceLink}\n label={entry.sourceLabel}\n primary\n onNavigate={onNavigate}\n />\n <Row gap=\"1\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"muted\" className=\"shrink-0\">\n {translate('cms.reference.atLocation')}\n </Text>\n <ImpactDeepLink\n projectId={projectId}\n link={entry.sourceLink}\n label={entry.locationLabel}\n onNavigate={onNavigate}\n />\n </Row>\n </Stack>\n {blocking ? (\n <Badge variant=\"error\" data-testid=\"reference-confirm-strict\">\n <ShieldAlert className=\"size-3\" /> {translate('cms.reference.strictBadge')}\n </Badge>\n ) : null}\n <Badge variant=\"outline\" className=\"shrink-0\">\n {entry.kindLabel}\n </Badge>\n <Row gap=\"1\" className=\"shrink-0\">\n {entry.draft ? <Badge variant=\"warning\">{translate('cms.reference.draftBadge')}</Badge> : null}\n {entry.published ? (\n <Badge variant=\"success\">{translate('cms.reference.publishedBadge')}</Badge>\n ) : null}\n </Row>\n </Row>\n )\n}\n\n/** A deep link to the exact source location; closes the dialog on navigate. */\nfunction ImpactDeepLink({\n projectId,\n link,\n label,\n primary = false,\n onNavigate,\n}: {\n projectId: string\n link: ReferenceDeepLink | null\n label: string\n primary?: boolean\n onNavigate: () => void\n}) {\n const className = primary\n ? 'min-w-0 truncate text-sm font-medium text-foreground hover:text-primary hover:underline'\n : 'min-w-0 truncate text-xs text-muted-foreground hover:text-primary hover:underline'\n if (link === null) {\n return primary ? (\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n ) : (\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n )\n }\n switch (link.kind) {\n case 'cms-field':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(link.collectionId, link.documentId) }}\n search={link.fieldPath !== undefined ? { field: link.fieldPath } : {}}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n case 'builder-node':\n // The builder redirect (EC-187) only reads `?page=`; the page-graph-only\n // `node` has no home in the document editor and is dropped.\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.builder() }}\n search={link.pageId !== undefined ? { page: link.pageId } : {}}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n case 'component':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.component(link.componentId) }}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n case 'asset':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.assets() }}\n search={{ asset: link.assetId }}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n }\n}\n"]}
1
+ {"version":3,"file":"reference-confirm.js","sourceRoot":"","sources":["../../../src/components/cms/reference-confirm.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { AlertTriangle, ShieldAlert } from 'lucide-react'\nimport { useEffect, useMemo, useState } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport type { DocumentRef } from '@elytracms/core/cms-core'\nimport {\n buildReferenceImpact,\n loadInboundReferenceViews,\n loadStrictReferrers,\n} from '@elytracms/studio/lib/references'\nimport type {\n DocumentReferenceImpact,\n ReferenceAction,\n ReferenceDeepLink,\n ReferenceEntryView,\n ReferenceImpact,\n} from '@elytracms/studio/lib/references'\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useCollectionView } from '../collection-view/collection-view-context'\n\n/**\n * The EC-173 reference-aware confirmation dialog for unpublish/delete —\n * single documents (detail editor) and bulk selections (EC-169 bulk bar)\n * alike. It inverts the industry default, visibly:\n *\n * 1. *Before* the action it enumerates every inbound reference that will\n * break (published variant for unpublish, both variants for delete), with\n * the EC-172 labels and deep links to the exact field/node.\n * 2. Proceeding is allowed — weak references are the default. Zero references\n * render a plain confirm; breakage renders \"You can … — N places will show\n * a fallback\".\n * 3. Strict opt-in relations (`strict: true`, at any position — EC-589) flip the\n * same dialog into a real block: the proceed button is disabled and every\n * blocking referrer is named and linked. The blocking set IS the\n * `@elytracms/core/operations` guard's answer (`references.strictReferrers`), so\n * the dialog and the refusal cannot disagree — this dialog is the friendly face,\n * not the only guard.\n *\n * Pre-flight data goes through operations reference queries only (EC-155\n * index, flushed); impact semantics live in `lib/references/impact.ts`.\n */\n\ntype ImpactStatus = 'loading' | 'ready' | 'unavailable'\n\nconst ACTION_LABELS: Record<ReferenceAction, string> = {\n unpublish: translate('cms.editor.unpublish'),\n delete: translate('common.delete'),\n}\n\nexport function ReferenceConfirmDialog({\n projectId,\n action,\n targets,\n open,\n busy = false,\n onOpenChange,\n onConfirm,\n}: {\n projectId: string\n action: ReferenceAction\n /** The documents being acted on (a stable snapshot taken when opening). */\n targets: readonly DocumentRef[]\n open: boolean\n busy?: boolean\n onOpenChange: (open: boolean) => void\n onConfirm: () => void\n}) {\n const source = useCollectionView()\n const [status, setStatus] = useState<ImpactStatus>('loading')\n const [impact, setImpact] = useState<ReferenceImpact | null>(null)\n\n // A stable identity for the acted-on set, so the effect re-runs exactly\n // when the selection (not the array instance) changes.\n const targetsKey = targets.map((ref) => documentKey(ref)).join('\\n')\n\n useEffect(() => {\n if (!open || targets.length === 0) return\n let cancelled = false\n setStatus('loading')\n setImpact(null)\n void (async () => {\n try {\n // Queued session mutations (and the persisted baseline) must land\n // before the pre-flight query — the index is maintained on write.\n await source.flush?.()\n // The source knows how its records are addressed in the reference\n // index (EC-433): documents by default, `{ type: 'asset' }` for the\n // assets rail — so this pre-flight counts the SAME references the\n // rail's detail surface does.\n const views = await loadInboundReferenceViews(projectId, targets, source.referenceTarget)\n // EC-589: \"blocked\" is the core guard's own answer — strict relations at every\n // position, read off drafts and pinned snapshots. Only document targets can be\n // strictly held (a rail addressing its records otherwise, e.g. assets, cannot).\n const documentTargets = targets.filter(\n (ref) => (source.referenceTarget?.(ref).type ?? 'document') === 'document',\n )\n const strict = await loadStrictReferrers(projectId, documentTargets, action)\n if (cancelled) return\n const strictByKey = new Map(\n documentTargets.map((ref, index) => [documentKey(ref), strict[index] ?? []] as const),\n )\n setImpact(\n buildReferenceImpact(\n action,\n views.map((view) => ({ ...view, strictReferrers: strictByKey.get(documentKey(view.ref)) ?? [] })),\n ),\n )\n setStatus('ready')\n } catch {\n // The index being unreachable must not trap the editor: degrade to a\n // plain confirm with an explicit \"could not check\" warning.\n if (!cancelled) setStatus('unavailable')\n }\n })()\n return () => {\n cancelled = true\n }\n // `targetsKey` stands in for `targets` (stable identity for the acted-on set).\n }, [open, action, targetsKey, projectId])\n\n const actionLabel = ACTION_LABELS[action]\n const subject = useMemo(() => {\n if (targets.length === 1) {\n const ref = targets[0]!\n return `“${source.titleOf(ref)}”`\n }\n return translate('cms.reference.documentCount', { count: targets.length })\n }, [targets, source])\n\n const blocked = status === 'ready' && (impact?.totalBlocking ?? 0) > 0\n const totalBroken = impact?.totalBroken ?? 0\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent data-testid=\"reference-confirm\" className=\"max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>\n {translate('cms.reference.confirmTitle', { action: actionLabel, subject })}\n </DialogTitle>\n <DialogDescription data-testid=\"reference-confirm-summary\">\n {status === 'loading' ? (\n translate('cms.reference.checking')\n ) : status === 'unavailable' ? (\n translate('cms.reference.checkUnavailable')\n ) : blocked ? (\n translate(\n impact!.totalBlocking === 1\n ? 'cms.reference.blocked.one'\n : 'cms.reference.blocked.other',\n { count: impact!.totalBlocking },\n )\n ) : totalBroken === 0 ? (\n <>\n {translate('cms.reference.notReferenced')}\n {action === 'delete'\n ? ` ${translate(\n targets.length === 1\n ? 'cms.reference.permanentDelete.one'\n : 'cms.reference.permanentDelete.other',\n { count: targets.length },\n )}`\n : ''}\n </>\n ) : (\n translate('cms.reference.canProceed', {\n action: translate(\n action === 'unpublish'\n ? 'cms.reference.verb.unpublish'\n : 'cms.reference.verb.delete',\n ),\n summary: impactSummary(targets.length, totalBroken),\n })\n )}\n </DialogDescription>\n </DialogHeader>\n\n {status === 'ready' && impact !== null && totalBroken > 0 ? (\n <ImpactList projectId={projectId} impact={impact} onNavigate={() => onOpenChange(false)} />\n ) : null}\n {status === 'unavailable' ? (\n <Row gap=\"2\" data-testid=\"reference-confirm-unavailable\">\n <AlertTriangle className=\"size-4 shrink-0 text-warning\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate('cms.reference.proceedUnavailable')}\n </Text>\n </Row>\n ) : null}\n\n <DialogFooter>\n <Button\n variant=\"outline\"\n data-testid=\"reference-confirm-cancel\"\n onClick={() => onOpenChange(false)}\n >\n {translate('common.cancel')}\n </Button>\n <Button\n variant={action === 'delete' ? 'destructive' : 'default'}\n disabled={status === 'loading' || blocked || busy}\n data-testid=\"reference-confirm-proceed\"\n onClick={onConfirm}\n >\n {totalBroken > 0 && !blocked\n ? translate('cms.reference.proceedAnyway', { action: actionLabel })\n : actionLabel}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction impactSummary(targetCount: number, totalBroken: number): string {\n const places = translate(\n totalBroken === 1 ? 'cms.reference.placeCount.one' : 'cms.reference.placeCount.other',\n { count: totalBroken },\n )\n // The bulk aggregate: \"N documents, M inbound references will break.\"\n return targetCount > 1\n ? translate('cms.reference.bulkSummary', { count: targetCount, places })\n : places\n}\n\n/**\n * The enumerated impact: flat for a single target, grouped per target for\n * bulk selections (per-item detail, EC-169 honesty rules).\n */\nfunction ImpactList({\n projectId,\n impact,\n onNavigate,\n}: {\n projectId: string\n impact: ReferenceImpact\n onNavigate: () => void\n}) {\n const affected = impact.documents.filter((doc) => doc.broken.length > 0 || doc.blocking.length > 0)\n return (\n <Stack gap=\"3\" data-testid=\"reference-confirm-impact\">\n {affected.map((doc) => (\n <ImpactGroup\n key={documentKey(doc.ref)}\n projectId={projectId}\n doc={doc}\n grouped={impact.documents.length > 1}\n onNavigate={onNavigate}\n />\n ))}\n </Stack>\n )\n}\n\nfunction ImpactGroup({\n projectId,\n doc,\n grouped,\n onNavigate,\n}: {\n projectId: string\n doc: DocumentReferenceImpact\n grouped: boolean\n onNavigate: () => void\n}) {\n const source = useCollectionView()\n const blockingKeys = new Set(doc.blocking.map((entry) => entry.key))\n return (\n <Stack gap=\"2\" data-testid={`reference-confirm-doc-${doc.ref.id}`}>\n {grouped ? (\n <Row gap=\"2\">\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\" className=\"uppercase tracking-wide\">\n {source.titleOf(doc.ref)}\n </Text>\n <Badge variant=\"outline\">{doc.broken.length}</Badge>\n </Row>\n ) : null}\n {/* EC-589: a blocking referrer that does not count as broken (it is acted on\n too) is still named — the core meets it. */}\n {[...doc.broken, ...doc.blocking.filter((entry) => !doc.broken.some((b) => b.key === entry.key))].map(\n (entry) => (\n <ImpactEntryRow\n key={entry.key}\n projectId={projectId}\n entry={entry}\n blocking={blockingKeys.has(entry.key)}\n onNavigate={onNavigate}\n />\n ),\n )}\n </Stack>\n )\n}\n\n/**\n * The shared EC-173 impact rows: one deep-linked row per broken inbound\n * reference, strict-blocking ones flagged. Rendered by the document\n * confirmation dialog (above) and the asset-delete dialog (EC-202) alike — both\n * reduce to \"these places reference the thing you're removing\", so both lean on\n * the same row + deep-link rendering (which already handles asset link kinds).\n */\nexport function ReferenceImpactRows({\n projectId,\n broken,\n blocking = [],\n onNavigate,\n}: {\n projectId: string\n broken: readonly ReferenceEntryView[]\n blocking?: readonly ReferenceEntryView[]\n onNavigate: () => void\n}) {\n const blockingKeys = new Set(blocking.map((entry) => entry.key))\n return (\n <Stack gap=\"2\" data-testid=\"reference-impact-rows\">\n {broken.map((entry) => (\n <ImpactEntryRow\n key={entry.key}\n projectId={projectId}\n entry={entry}\n blocking={blockingKeys.has(entry.key)}\n onNavigate={onNavigate}\n />\n ))}\n </Stack>\n )\n}\n\n/**\n * One affected place: the EC-172 labels with a working deep link to the exact\n * field/node, the entry kind, draft/published visibility — and a strict\n * marker when this entry is what blocks the action.\n */\nfunction ImpactEntryRow({\n projectId,\n entry,\n blocking,\n onNavigate,\n}: {\n projectId: string\n entry: ReferenceEntryView\n blocking: boolean\n onNavigate: () => void\n}) {\n return (\n <Row\n gap=\"2\"\n className={\n blocking\n ? 'rounded-md border border-destructive/50 bg-destructive/5 px-2.5 py-2'\n : 'rounded-md border border-border/70 bg-background/40 px-2.5 py-2'\n }\n data-testid=\"reference-confirm-entry\"\n >\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <ImpactDeepLink\n projectId={projectId}\n link={entry.sourceLink}\n label={entry.sourceLabel}\n primary\n onNavigate={onNavigate}\n />\n <Row gap=\"1\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"muted\" className=\"shrink-0\">\n {translate('cms.reference.atLocation')}\n </Text>\n <ImpactDeepLink\n projectId={projectId}\n link={entry.sourceLink}\n label={entry.locationLabel}\n onNavigate={onNavigate}\n />\n </Row>\n </Stack>\n {blocking ? (\n <Badge variant=\"error\" data-testid=\"reference-confirm-strict\">\n <ShieldAlert className=\"size-3\" /> {translate('cms.reference.strictBadge')}\n </Badge>\n ) : null}\n <Badge variant=\"outline\" className=\"shrink-0\">\n {entry.kindLabel}\n </Badge>\n <Row gap=\"1\" className=\"shrink-0\">\n {entry.draft ? <Badge variant=\"warning\">{translate('cms.reference.draftBadge')}</Badge> : null}\n {entry.published ? (\n <Badge variant=\"success\">{translate('cms.reference.publishedBadge')}</Badge>\n ) : null}\n </Row>\n </Row>\n )\n}\n\n/** A deep link to the exact source location; closes the dialog on navigate. */\nfunction ImpactDeepLink({\n projectId,\n link,\n label,\n primary = false,\n onNavigate,\n}: {\n projectId: string\n link: ReferenceDeepLink | null\n label: string\n primary?: boolean\n onNavigate: () => void\n}) {\n const className = primary\n ? 'min-w-0 truncate text-sm font-medium text-foreground hover:text-primary hover:underline'\n : 'min-w-0 truncate text-xs text-muted-foreground hover:text-primary hover:underline'\n if (link === null) {\n return primary ? (\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n ) : (\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n )\n }\n switch (link.kind) {\n case 'cms-field':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(link.collectionId, link.documentId) }}\n search={link.fieldPath !== undefined ? { field: link.fieldPath } : {}}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n case 'builder-node':\n // The builder redirect (EC-187) only reads `?page=`; the page-graph-only\n // `node` has no home in the document editor and is dropped.\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.builder() }}\n search={link.pageId !== undefined ? { page: link.pageId } : {}}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n case 'component':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.component(link.componentId) }}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n case 'asset':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.assets() }}\n search={{ asset: link.assetId }}\n className={className}\n data-testid=\"reference-confirm-link\"\n onClick={onNavigate}\n >\n {label}\n </Link>\n )\n }\n}\n"]}
@@ -2,7 +2,7 @@ import type { ComponentManifest } from '@elytracms/core/component-registry';
2
2
  interface EmbedPickerProps {
3
3
  open: boolean;
4
4
  onOpenChange: (open: boolean) => void;
5
- /** The field's embed `allow` list (component ids). Empty/absent ⇒ any non-primitive. */
5
+ /** The field's declared embed `allow` (component ids); absent ⇒ the richText default (EC-584: any component that fills no layout role). */
6
6
  allow?: string[];
7
7
  onSelect: (manifest: ComponentManifest) => void;
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"embed-picker.js","sourceRoot":"","sources":["../../../../src/components/cms/rich-text/embed-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { Blocks } from 'lucide-react'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Input,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { localizedStringVariants } from '@elytracms/core/cms-core'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { listEmbeddable } from './insert'\n\ninterface EmbedPickerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n /** The field's embed `allow` list (component ids). Empty/absent ⇒ any non-primitive. */\n allow?: string[]\n onSelect: (manifest: ComponentManifest) => void\n}\n\n/**\n * The component-embed picker dialog (EC-314/EC-189): a searchable list of the\n * components permitted for inline embedding. Rendered once at the editor level and\n * opened by the toolbar button or the `/embed` slash command.\n */\nexport function EmbedPicker({ open, onOpenChange, allow, onSelect }: EmbedPickerProps) {\n const [query, setQuery] = useState('')\n\n const embeddable = useMemo(\n () => listEmbeddable(allow, getStudioRegistry().registry.list()),\n [allow],\n )\n\n const results = useMemo(() => {\n const q = query.trim().toLowerCase()\n if (!q) return embeddable\n return embeddable.filter((m) =>\n [m.id, ...localizedStringVariants(m.title)].some((s) => s.toLowerCase().includes(q)),\n )\n }, [embeddable, query])\n\n function choose(manifest: ComponentManifest) {\n onSelect(manifest)\n onOpenChange(false)\n setQuery('')\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>{translate('richText.embed.add')}</DialogTitle>\n </DialogHeader>\n <Stack gap=\"2\">\n <Input\n autoFocus\n value={query}\n placeholder={translate('richText.link.documentSearch')}\n onChange={(e) => setQuery(e.target.value)}\n />\n <Stack gap=\"none\" className=\"max-h-72 overflow-y-auto\">\n {results.length === 0 ? (\n <Text as=\"span\" size=\"xs\" tone=\"muted\" className=\"px-1 py-2\">\n {translate('richText.embed.empty')}\n </Text>\n ) : (\n results.map((manifest) => (\n <button\n key={manifest.id}\n type=\"button\"\n onClick={() => choose(manifest)}\n className={cn(\n 'flex w-full items-center gap-2.5 rounded-md px-2 py-2 text-left',\n 'hover:bg-muted focus-visible:bg-muted outline-none',\n )}\n >\n <Blocks className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium\">{localizedLabel(manifest.title, manifest.id)}</span>\n <span className=\"truncate text-xs text-muted-foreground\">{manifest.id}</span>\n </span>\n </button>\n ))\n )}\n </Stack>\n </Stack>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
1
+ {"version":3,"file":"embed-picker.js","sourceRoot":"","sources":["../../../../src/components/cms/rich-text/embed-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { Blocks } from 'lucide-react'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n Input,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { localizedStringVariants } from '@elytracms/core/cms-core'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { listEmbeddable } from './insert'\n\ninterface EmbedPickerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n /** The field's declared embed `allow` (component ids); absent ⇒ the richText default (EC-584: any component that fills no layout role). */\n allow?: string[]\n onSelect: (manifest: ComponentManifest) => void\n}\n\n/**\n * The component-embed picker dialog (EC-314/EC-189): a searchable list of the\n * components permitted for inline embedding. Rendered once at the editor level and\n * opened by the toolbar button or the `/embed` slash command.\n */\nexport function EmbedPicker({ open, onOpenChange, allow, onSelect }: EmbedPickerProps) {\n const [query, setQuery] = useState('')\n\n const embeddable = useMemo(\n () => listEmbeddable(allow, getStudioRegistry().registry.list()),\n [allow],\n )\n\n const results = useMemo(() => {\n const q = query.trim().toLowerCase()\n if (!q) return embeddable\n return embeddable.filter((m) =>\n [m.id, ...localizedStringVariants(m.title)].some((s) => s.toLowerCase().includes(q)),\n )\n }, [embeddable, query])\n\n function choose(manifest: ComponentManifest) {\n onSelect(manifest)\n onOpenChange(false)\n setQuery('')\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>{translate('richText.embed.add')}</DialogTitle>\n </DialogHeader>\n <Stack gap=\"2\">\n <Input\n autoFocus\n value={query}\n placeholder={translate('richText.link.documentSearch')}\n onChange={(e) => setQuery(e.target.value)}\n />\n <Stack gap=\"none\" className=\"max-h-72 overflow-y-auto\">\n {results.length === 0 ? (\n <Text as=\"span\" size=\"xs\" tone=\"muted\" className=\"px-1 py-2\">\n {translate('richText.embed.empty')}\n </Text>\n ) : (\n results.map((manifest) => (\n <button\n key={manifest.id}\n type=\"button\"\n onClick={() => choose(manifest)}\n className={cn(\n 'flex w-full items-center gap-2.5 rounded-md px-2 py-2 text-left',\n 'hover:bg-muted focus-visible:bg-muted outline-none',\n )}\n >\n <Blocks className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium\">{localizedLabel(manifest.title, manifest.id)}</span>\n <span className=\"truncate text-xs text-muted-foreground\">{manifest.id}</span>\n </span>\n </button>\n ))\n )}\n </Stack>\n </Stack>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { Editor } from '@tiptap/react';
2
+ import { assetNode } from '@elytracms/core/cms-core';
2
3
  import type { ComponentNode } from '@elytracms/core/project-graph';
3
4
  import type { ComponentManifest } from '@elytracms/core/component-registry';
4
5
  import type { AssetsStore } from '../../assets/assets-store.js';
@@ -14,6 +15,16 @@ import type { AssetsStore } from '../../assets/assets-store.js';
14
15
  * rule from it, like a field's own `accept`.
15
16
  */
16
17
  export declare const INLINE_IMAGE_ACCEPT: readonly string[];
18
+ /**
19
+ * The `image` node attrs for a project asset: the canonical `attrs.ref` plus an
20
+ * editor-display `src` snapshot. NO `alt` (EC-587, ASSETS D19): an asset-pinned image's
21
+ * alt is the asset's, read at render through the one reader — a snapshot here went
22
+ * stale on the first alt edit (and read the legacy column only). Pure, for the test.
23
+ */
24
+ export declare function assetImageAttrs(assetId: string, assets: AssetsStore | null): {
25
+ src: string;
26
+ ref: ReturnType<typeof assetNode>;
27
+ };
17
28
  /** Insert an `image` node for a project asset (canonical `attrs.ref`, snapshot `src`). */
18
29
  export declare function insertAssetImage(editor: Editor, assetId: string, assets: AssetsStore | null): void;
19
30
  /**
@@ -31,14 +42,14 @@ export declare function insertComponentEmbed(editor: Editor, manifest: Component
31
42
  */
32
43
  export declare function embedNodeFromManifest(manifest: ComponentManifest): ComponentNode;
33
44
  /**
34
- * The components embeddable in a rich-text field: the field's `allow` list (a
35
- * closed set — EC-544: an empty one offers nothing), or EVERY registered
36
- * component when absent — the ONE semantic the `blocks` field, the blocks panel
37
- * and the `validateRichTextEmbeds` leash already apply (fields.ts: "absent = any").
38
- * EC-591: the old "every non-primitive" reading keyed on the `base.*` id prefix,
39
- * which no longer means anything (and diverged from the validator — COMPOSITION
40
- * #6); a field that wants a narrower offer declares `allow`. Eligibility is
41
- * `isCanvasEligible`, the one predicate the blocks panel and `elytra previews`
42
- * read too (EC-514) — never a local reading of the manifest flag.
45
+ * The components embeddable in a rich-text field or prop (EC-584, ratified 4b): its
46
+ * declared `allow` (a closed set — EC-544: an empty one offers nothing), else the
47
+ * `richText` TYPE's default, declared once in the per-FieldType definition
48
+ * (`FIELD_TYPES.richText.defaultAllow`): any canvas-eligible component that fills no
49
+ * LAYOUT role — a Stack or Grid mid-paragraph is a layout surface's job. Judged by the
50
+ * ONE core placement judge (role exclusion + `isCanvasEligible`, carried on the
51
+ * manifest view), the same verdict the composition validator reaches — so the embed
52
+ * picker, the toolbar, the slash menu and the inspector's rich-text editor offer
53
+ * exactly what publishes.
43
54
  */
44
55
  export declare function listEmbeddable(allow: string[] | undefined, registered: ComponentManifest[]): ComponentManifest[];
@@ -1,5 +1,7 @@
1
1
  import { assetNode } from '@elytracms/core/cms-core';
2
- import { isCanvasEligible } from '@elytracms/core/component-registry';
2
+ import { FIELD_TYPES } from '@elytracms/core/cms-core';
3
+ import { placementRefusal, resolveAllow } from '@elytracms/core/project-graph';
4
+ import { toManifestView } from '@elytracms/core/component-registry';
3
5
  import { COMPONENT_EMBED_NODE_TYPE } from './component-embed.js';
4
6
  /**
5
7
  * Reference-carrying node insertions shared by the toolbar controls and the slash
@@ -13,16 +15,22 @@ import { COMPONENT_EMBED_NODE_TYPE } from './component-embed.js';
13
15
  * rule from it, like a field's own `accept`.
14
16
  */
15
17
  export const INLINE_IMAGE_ACCEPT = ['image'];
18
+ /**
19
+ * The `image` node attrs for a project asset: the canonical `attrs.ref` plus an
20
+ * editor-display `src` snapshot. NO `alt` (EC-587, ASSETS D19): an asset-pinned image's
21
+ * alt is the asset's, read at render through the one reader — a snapshot here went
22
+ * stale on the first alt edit (and read the legacy column only). Pure, for the test.
23
+ */
24
+ export function assetImageAttrs(assetId, assets) {
25
+ const record = assets?.assets.find((a) => a.id === assetId);
26
+ return { src: record?.url ?? '', ref: assetNode(assetId) };
27
+ }
16
28
  /** Insert an `image` node for a project asset (canonical `attrs.ref`, snapshot `src`). */
17
29
  export function insertAssetImage(editor, assetId, assets) {
18
- const record = assets?.assets.find((a) => a.id === assetId);
19
30
  editor
20
31
  .chain()
21
32
  .focus()
22
- .insertContent({
23
- type: 'image',
24
- attrs: { src: record?.url ?? '', alt: record?.alt ?? '', ref: assetNode(assetId) },
25
- })
33
+ .insertContent({ type: 'image', attrs: assetImageAttrs(assetId, assets) })
26
34
  .run();
27
35
  }
28
36
  /**
@@ -71,19 +79,19 @@ export function embedNodeFromManifest(manifest) {
71
79
  return { id: newEmbedId(), componentId: manifest.id, props: {} };
72
80
  }
73
81
  /**
74
- * The components embeddable in a rich-text field: the field's `allow` list (a
75
- * closed set — EC-544: an empty one offers nothing), or EVERY registered
76
- * component when absent — the ONE semantic the `blocks` field, the blocks panel
77
- * and the `validateRichTextEmbeds` leash already apply (fields.ts: "absent = any").
78
- * EC-591: the old "every non-primitive" reading keyed on the `base.*` id prefix,
79
- * which no longer means anything (and diverged from the validator — COMPOSITION
80
- * #6); a field that wants a narrower offer declares `allow`. Eligibility is
81
- * `isCanvasEligible`, the one predicate the blocks panel and `elytra previews`
82
- * read too (EC-514) — never a local reading of the manifest flag.
82
+ * The components embeddable in a rich-text field or prop (EC-584, ratified 4b): its
83
+ * declared `allow` (a closed set — EC-544: an empty one offers nothing), else the
84
+ * `richText` TYPE's default, declared once in the per-FieldType definition
85
+ * (`FIELD_TYPES.richText.defaultAllow`): any canvas-eligible component that fills no
86
+ * LAYOUT role — a Stack or Grid mid-paragraph is a layout surface's job. Judged by the
87
+ * ONE core placement judge (role exclusion + `isCanvasEligible`, carried on the
88
+ * manifest view), the same verdict the composition validator reaches — so the embed
89
+ * picker, the toolbar, the slash menu and the inspector's rich-text editor offer
90
+ * exactly what publishes.
83
91
  */
84
92
  export function listEmbeddable(allow, registered) {
85
- const permitted = allow ? registered.filter((m) => allow.includes(m.id)) : registered;
86
- return permitted.filter(isCanvasEligible);
93
+ const vocabulary = resolveAllow(allow, FIELD_TYPES.richText.defaultAllow);
94
+ return registered.filter((manifest) => placementRefusal({ componentId: manifest.id, view: toManifestView(manifest), allow: vocabulary }) === null);
87
95
  }
88
96
  /** A unique id for an embedded node (studio runs in the browser; crypto is present). */
89
97
  function newEmbedId() {
@@ -1 +1 @@
1
- {"version":3,"file":"insert.js","sourceRoot":"","sources":["../../../../src/components/cms/rich-text/insert.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Editor } from '@tiptap/react'\nimport { assetNode } from '@elytracms/core/cms-core'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { isCanvasEligible } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { COMPONENT_EMBED_NODE_TYPE } from './component-embed'\nimport type { AssetsStore } from '../../assets/assets-store'\n\n/**\n * Reference-carrying node insertions shared by the toolbar controls and the slash\n * menu (EC-314). Centralised so both entry points produce byte-identical envelopes\n * (the shapes `reference-extraction` indexes).\n */\n\n/**\n * What the rich-text image insert accepts (EC-588): images, in the structured `accept`\n * vocabulary every picker takes — stated once here rather than as a file-input literal\n * at the call site. The picker derives its grid filter, its Upload dialog and its drop\n * rule from it, like a field's own `accept`.\n */\nexport const INLINE_IMAGE_ACCEPT: readonly string[] = ['image']\n\n/** Insert an `image` node for a project asset (canonical `attrs.ref`, snapshot `src`). */\nexport function insertAssetImage(editor: Editor, assetId: string, assets: AssetsStore | null): void {\n const record = assets?.assets.find((a) => a.id === assetId)\n editor\n .chain()\n .focus()\n .insertContent({\n type: 'image',\n attrs: { src: record?.url ?? '', alt: record?.alt ?? '', ref: assetNode(assetId) },\n })\n .run()\n}\n\n/**\n * Insert a `componentEmbed` wrapping a fresh ComponentNode built from manifest\n * defaults. With `{ select: true }` the inserted node is then selected — so a host\n * embed inspector (0.0.41) opens straight onto its props for editing.\n */\nexport function insertComponentEmbed(\n editor: Editor,\n manifest: ComponentManifest,\n options?: { select?: boolean },\n): void {\n const embed = embedNodeFromManifest(manifest)\n editor\n .chain()\n .focus()\n .insertContent({ type: COMPONENT_EMBED_NODE_TYPE, attrs: { node: embed } })\n .run()\n if (options?.select) selectComponentEmbed(editor, embed.id)\n}\n\n/**\n * Select the just-inserted embed by its unique embedded-node id. Located by scan\n * (rather than fragile position arithmetic after `insertContent`, which may split a\n * paragraph or append a trailing block); the id is unique so the first match is it.\n */\nfunction selectComponentEmbed(editor: Editor, id: string): void {\n let pos = -1\n editor.state.doc.descendants((node, at) => {\n if (pos >= 0) return false\n if (node.type.name === COMPONENT_EMBED_NODE_TYPE) {\n const embedded = node.attrs.node as { id?: string } | null\n if (embedded?.id === id) {\n pos = at\n return false\n }\n }\n return true\n })\n if (pos >= 0) editor.commands.setNodeSelection(pos)\n}\n\n/**\n * A fresh embedded ComponentNode (mirrors builder node-factory). EC-591 / ratified\n * 2f: NO props — an absent prop is `undefined`, so the implementation's own default\n * applies; the manifest `default` is a hint, never baked into the stored node.\n */\nexport function embedNodeFromManifest(manifest: ComponentManifest): ComponentNode {\n return { id: newEmbedId(), componentId: manifest.id, props: {} }\n}\n\n/**\n * The components embeddable in a rich-text field: the field's `allow` list (a\n * closed set — EC-544: an empty one offers nothing), or EVERY registered\n * component when absent — the ONE semantic the `blocks` field, the blocks panel\n * and the `validateRichTextEmbeds` leash already apply (fields.ts: \"absent = any\").\n * EC-591: the old \"every non-primitive\" reading keyed on the `base.*` id prefix,\n * which no longer means anything (and diverged from the validator — COMPOSITION\n * #6); a field that wants a narrower offer declares `allow`. Eligibility is\n * `isCanvasEligible`, the one predicate the blocks panel and `elytra previews`\n * read too (EC-514) — never a local reading of the manifest flag.\n */\nexport function listEmbeddable(\n allow: string[] | undefined,\n registered: ComponentManifest[],\n): ComponentManifest[] {\n const permitted = allow ? registered.filter((m) => allow.includes(m.id)) : registered\n return permitted.filter(isCanvasEligible)\n}\n\n/** A unique id for an embedded node (studio runs in the browser; crypto is present). */\nfunction newEmbedId(): string {\n const uuid = globalThis.crypto?.randomUUID?.()\n return uuid ? `embed-${uuid}` : `embed-${Math.random().toString(36).slice(2)}`\n}\n"]}
1
+ {"version":3,"file":"insert.js","sourceRoot":"","sources":["../../../../src/components/cms/rich-text/insert.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Editor } from '@tiptap/react'\nimport { assetNode } from '@elytracms/core/cms-core'\nimport { FIELD_TYPES } from '@elytracms/core/cms-core'\nimport { placementRefusal, resolveAllow } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { toManifestView } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { COMPONENT_EMBED_NODE_TYPE } from './component-embed'\nimport type { AssetsStore } from '../../assets/assets-store'\n\n/**\n * Reference-carrying node insertions shared by the toolbar controls and the slash\n * menu (EC-314). Centralised so both entry points produce byte-identical envelopes\n * (the shapes `reference-extraction` indexes).\n */\n\n/**\n * What the rich-text image insert accepts (EC-588): images, in the structured `accept`\n * vocabulary every picker takes — stated once here rather than as a file-input literal\n * at the call site. The picker derives its grid filter, its Upload dialog and its drop\n * rule from it, like a field's own `accept`.\n */\nexport const INLINE_IMAGE_ACCEPT: readonly string[] = ['image']\n\n/**\n * The `image` node attrs for a project asset: the canonical `attrs.ref` plus an\n * editor-display `src` snapshot. NO `alt` (EC-587, ASSETS D19): an asset-pinned image's\n * alt is the asset's, read at render through the one reader — a snapshot here went\n * stale on the first alt edit (and read the legacy column only). Pure, for the test.\n */\nexport function assetImageAttrs(assetId: string, assets: AssetsStore | null): { src: string; ref: ReturnType<typeof assetNode> } {\n const record = assets?.assets.find((a) => a.id === assetId)\n return { src: record?.url ?? '', ref: assetNode(assetId) }\n}\n\n/** Insert an `image` node for a project asset (canonical `attrs.ref`, snapshot `src`). */\nexport function insertAssetImage(editor: Editor, assetId: string, assets: AssetsStore | null): void {\n editor\n .chain()\n .focus()\n .insertContent({ type: 'image', attrs: assetImageAttrs(assetId, assets) })\n .run()\n}\n\n/**\n * Insert a `componentEmbed` wrapping a fresh ComponentNode built from manifest\n * defaults. With `{ select: true }` the inserted node is then selected — so a host\n * embed inspector (0.0.41) opens straight onto its props for editing.\n */\nexport function insertComponentEmbed(\n editor: Editor,\n manifest: ComponentManifest,\n options?: { select?: boolean },\n): void {\n const embed = embedNodeFromManifest(manifest)\n editor\n .chain()\n .focus()\n .insertContent({ type: COMPONENT_EMBED_NODE_TYPE, attrs: { node: embed } })\n .run()\n if (options?.select) selectComponentEmbed(editor, embed.id)\n}\n\n/**\n * Select the just-inserted embed by its unique embedded-node id. Located by scan\n * (rather than fragile position arithmetic after `insertContent`, which may split a\n * paragraph or append a trailing block); the id is unique so the first match is it.\n */\nfunction selectComponentEmbed(editor: Editor, id: string): void {\n let pos = -1\n editor.state.doc.descendants((node, at) => {\n if (pos >= 0) return false\n if (node.type.name === COMPONENT_EMBED_NODE_TYPE) {\n const embedded = node.attrs.node as { id?: string } | null\n if (embedded?.id === id) {\n pos = at\n return false\n }\n }\n return true\n })\n if (pos >= 0) editor.commands.setNodeSelection(pos)\n}\n\n/**\n * A fresh embedded ComponentNode (mirrors builder node-factory). EC-591 / ratified\n * 2f: NO props — an absent prop is `undefined`, so the implementation's own default\n * applies; the manifest `default` is a hint, never baked into the stored node.\n */\nexport function embedNodeFromManifest(manifest: ComponentManifest): ComponentNode {\n return { id: newEmbedId(), componentId: manifest.id, props: {} }\n}\n\n/**\n * The components embeddable in a rich-text field or prop (EC-584, ratified 4b): its\n * declared `allow` (a closed set — EC-544: an empty one offers nothing), else the\n * `richText` TYPE's default, declared once in the per-FieldType definition\n * (`FIELD_TYPES.richText.defaultAllow`): any canvas-eligible component that fills no\n * LAYOUT role — a Stack or Grid mid-paragraph is a layout surface's job. Judged by the\n * ONE core placement judge (role exclusion + `isCanvasEligible`, carried on the\n * manifest view), the same verdict the composition validator reaches — so the embed\n * picker, the toolbar, the slash menu and the inspector's rich-text editor offer\n * exactly what publishes.\n */\nexport function listEmbeddable(\n allow: string[] | undefined,\n registered: ComponentManifest[],\n): ComponentManifest[] {\n const vocabulary = resolveAllow(allow, FIELD_TYPES.richText.defaultAllow!)\n return registered.filter(\n (manifest) => placementRefusal({ componentId: manifest.id, view: toManifestView(manifest), allow: vocabulary }) === null,\n )\n}\n\n/** A unique id for an embedded node (studio runs in the browser; crypto is present). */\nfunction newEmbedId(): string {\n const uuid = globalThis.crypto?.randomUUID?.()\n return uuid ? `embed-${uuid}` : `embed-${Math.random().toString(36).slice(2)}`\n}\n"]}
@@ -0,0 +1,96 @@
1
+ import type { PresenceEntry } from '@elytracms/core/persistence';
2
+ import type { CmsDocument, CollectionDef, DocumentPatch, FieldDef, Locale, PropFieldLookup, ValuePath } from '@elytracms/core/cms-core';
3
+ import type { CollectionViewSource } from '@elytracms/studio/lib/collection-view/collection-view-source';
4
+ import type { Scheduler, UncommittedPosition } from './draft-commit.js';
5
+ /**
6
+ * EC-595 A2 — the detail editor's draft over the revisioned document (and, for the
7
+ * explicit-save rails, over a record).
8
+ *
9
+ * **Document write model** (the CMS): the draft changes per keystroke (live validation
10
+ * reads it); a field's edit is COMMITTED per its control kind's policy
11
+ * ({@link commitPolicyOf} / {@link createDraftCommitter}) as the patch of the position it
12
+ * wrote — never a document. The canvas commits each action's node patches at once
13
+ * (`compositionPatches`). The draft REBASES whenever the session's document changes
14
+ * (a write result, a colleague's change by subscription, a resolved conflict): it is the
15
+ * session's document with this editor's uncommitted positions laid over it — local wins
16
+ * until committed, then the per-path check decides (EC-595 point 7).
17
+ *
18
+ * **Forced flushes** (the ruling: before unmount, tab hide, publish, preview, an AI
19
+ * proposal, duplicate, transfer): {@link DocumentDraft.flush} commits every uncommitted
20
+ * position and awaits the persister. Unmount and tab hide run it here; the editor's
21
+ * publish, duplicate and the AI tap call it.
22
+ *
23
+ * **Thin presence** (Stage A): a heartbeat naming the focused field's path on focus
24
+ * change and every {@link PRESENCE_HEARTBEAT_MS} (under the 30 s TTL), cleared on
25
+ * unmount; the other sessions' live heartbeats arrive with the subscription.
26
+ *
27
+ * **Record write model** (users, redirects, assets, the account): the draft is edited
28
+ * in place and written whole on the editor's Save button (EC-240) — unchanged.
29
+ */
30
+ /** The presence heartbeat interval — under {@link PRESENCE_TTL_MS} so a present editor never lapses. */
31
+ export declare const PRESENCE_HEARTBEAT_MS = 10000;
32
+ /** An injectable visibility source (the tab-hide flush; tests drive it). */
33
+ export interface VisibilitySource {
34
+ /** Call `onHidden` whenever the tab becomes hidden; returns the unsubscribe. */
35
+ onHidden(onHidden: () => void): () => void;
36
+ }
37
+ export declare const documentVisibility: VisibilitySource;
38
+ /** The patch that writes the value `doc` holds at `path` (unset when absent). */
39
+ export declare function positionPatch(doc: CmsDocument, path: ValuePath): DocumentPatch;
40
+ /**
41
+ * THE rebase (pure): the session's document with the editor's uncommitted positions laid
42
+ * over it, values from the editor's current draft.
43
+ */
44
+ export declare function rebaseDraft(sessionDoc: CmsDocument, draft: CmsDocument, uncommitted: readonly UncommittedPosition[]): CmsDocument;
45
+ /**
46
+ * The reader's "now" for presence liveness: THIS session's own latest heartbeat `at`
47
+ * (written by the same store clock as everyone else's — the playground store runs a
48
+ * deterministic sequential clock, Convex the writers' wall clocks), falling back to the
49
+ * injected clock before the first heartbeat lands. Comparing others' `at` against a
50
+ * different time base would call every colleague stale (measured live in the
51
+ * playground: the store's clock is months behind the browser's).
52
+ */
53
+ export declare function presenceReaderNow(entries: readonly PresenceEntry[], sessionId: string, fallback: string): string;
54
+ export interface DocumentDraft {
55
+ draft: CmsDocument;
56
+ /** Uncommitted (document model) / unsaved (record model) edits exist. */
57
+ dirty: boolean;
58
+ /** The record model: the editor renders a Save button + navigation guard. */
59
+ explicitSave: boolean;
60
+ /** A field's control wrote `value` (the active locale for a localized field). */
61
+ setFieldValue(field: FieldDef, value: unknown): void;
62
+ /** A writer that transforms ONE field position (fill / reset from base). */
63
+ editField(field: FieldDef, update: (draft: CmsDocument) => CmsDocument): void;
64
+ /** The canvas emitted a new value for its field (node patches, committed at once). */
65
+ setCanvasValue(field: FieldDef, value: unknown): void;
66
+ /** The slot alias (a document-level key: a whole-document write at the held revision). */
67
+ setAlias(alias: string | undefined): void;
68
+ /** Field boundary events (focus / blur / Enter) — the commit policy's triggers. */
69
+ focused(field: FieldDef): void;
70
+ blurred(field: FieldDef): void;
71
+ entered(field: FieldDef): void;
72
+ /** The forced commit (sync; the persister serialises what follows) / the record model's Save. */
73
+ save(): void;
74
+ /** The FORCED FLUSH: commit everything uncommitted and await persistence. */
75
+ flush(): Promise<void>;
76
+ /** Adopt a draft from outside (a restored version). */
77
+ replaceDraft(next: CmsDocument): void;
78
+ /** Bumped when the canvas must remount from the draft (a restore, a colleague's change). */
79
+ canvasNonce: number;
80
+ /** The OTHER sessions present on this document, live by the reader's clock. */
81
+ presence: readonly PresenceEntry[];
82
+ }
83
+ export interface UseDocumentDraftInput {
84
+ source: CollectionViewSource;
85
+ collection: CollectionDef;
86
+ doc: CmsDocument;
87
+ locale: Locale;
88
+ lookupManifest: PropFieldLookup;
89
+ /** The composition field the canvas edits on this screen, if any. */
90
+ canvasField?: FieldDef;
91
+ scheduler?: Scheduler;
92
+ visibility?: VisibilitySource;
93
+ /** The reader's clock for presence liveness (ISO); `new Date().toISOString()` by default. */
94
+ now?: () => string;
95
+ }
96
+ export declare function useDocumentDraft(input: UseDocumentDraftInput): DocumentDraft;