@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":"drop-rules.js","sourceRoot":"","sources":["../../../src/lib/dnd/drop-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DEFAULT_SLOT,\n OUTLET_SLOT,\n getNode,\n getParentInfo,\n gridContainerNode,\n isDescendantOf,\n isStaticProp,\n layoutContainerKind,\n removeNode,\n updateNode,\n} from '@elytracms/core/project-graph'\nimport type {\n ComponentNode,\n GridContainerOptions,\n InsertTarget,\n LayoutContainerIds,\n} from '@elytracms/core/project-graph'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\n\n/**\n * Drag & drop eligibility rules (EC-160 + EC-162, vision §3.1) — pure\n * graph-level functions with **no DOM or UI dependencies**. The canvas and the\n * page tree both resolve drops through `resolveDrop`, so the two surfaces can\n * never disagree about what a manifest slot allow-list permits, what the\n * composition field's top-level vocabulary permits at the root (EC-554), which\n * children a component accepts, or whether a move would create a cycle.\n *\n * The drop-zone model is position-based: `into` a slot, `before`/`after` a\n * sibling along the parent's flow axis, and the EC-162 gesture positions\n * `left`/`right` — dropping *beside* an element wraps both in a two-column\n * `Grid` container (see `applyWrap`) — the component that fills the\n * `layout.grid` role (EC-591); with no such component the gesture is off.\n */\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/**\n * The manifest slice the rules evaluate. Structural (not the full\n * `ComponentManifest`) so tests and future callers can feed minimal lookups.\n */\nexport interface SlotRule {\n name: string\n /** Restrict which component ids may be inserted (omit to allow any). */\n allow?: string[]\n}\n\nexport interface ManifestRules {\n slots: SlotRule[]\n}\n\n/** Resolve a component id to its manifest slot rules (undefined = unknown component). */\nexport type ManifestLookup = (componentId: string) => ManifestRules | undefined\n\n/**\n * The canonical `ManifestRules` view of a full manifest (EC-386): the declared\n * `slots` merged with the blocks-prop-derived slots (`manifestSlots`, EC-190\n * \"slots are blocks props\" — a `blocks`-type prop IS a named child region whose\n * subtrees live in `node.slots[propName]`, carrying the prop's `allow` leash).\n * Every surface that feeds a real `ComponentManifest` into a {@link ManifestLookup}\n * or {@link primaryInsertSlot} must adapt through this — resolving against raw\n * `manifest.slots` silently degrades a `project.Section.body`-style region to a\n * sibling insert. One derivation, so tree insert, DnD, paste, and the canvas\n * affordances can never disagree about which regions exist or what they allow.\n */\nexport function manifestRules(manifest: ComponentManifest | undefined): ManifestRules | undefined {\n return manifest ? { slots: manifestSlots(manifest) } : undefined\n}\n\n/** What is being dragged. */\nexport type DragSubject =\n /** An existing node of the current page — a completed drop is a `move`. */\n | { kind: 'node'; nodeId: string; componentId: string }\n /** A new subtree (blocks-panel tile) — a completed drop is an `insert`. */\n | { kind: 'component'; componentId: string }\n\n/**\n * Where, relative to a reference node, the drop would land. `into`, `before`\n * and `after` are the EC-160 positions; `left`/`right` are the EC-162 gesture\n * positions — \"beside\" the reference, wrapping both elements in a two-column\n * `Grid` container.\n */\nexport type DropPosition = 'into' | 'before' | 'after' | 'left' | 'right'\n\n/** The side of the reference an EC-162 wrap gesture drops on. */\nexport type WrapSide = 'left' | 'right'\n\nexport interface DropLocation {\n /** The reference node id (the hovered node/row). */\n nodeId: string\n position: DropPosition\n}\n\n// ---------------------------------------------------------------------------\n// Outcomes\n// ---------------------------------------------------------------------------\n\nexport type DropRejectionCode =\n /** The reference node does not exist in the tree. */\n | 'missing-target'\n /** `into` on a component that declares no insertable slot. */\n | 'no-slot'\n /** The target slot's manifest allow-list rejects the dragged component. */\n | 'not-allowed'\n /**\n * EC-554: the composition FIELD's top-level `allow` rejects the component\n * landing as a direct child of the composition root (see {@link fieldAllowAt}).\n */\n | 'field-not-allowed'\n /** Moving a node into itself or its own subtree (cycle). */\n | 'own-subtree'\n /** The page root cannot be moved, and nothing can drop beside it. */\n | 'root'\n /** Structurally valid but changes nothing (dropping a node onto its own position). */\n | 'no-op'\n /**\n * EC-591: the wrap-beside gesture needs the component that fills the\n * `layout.grid` role, and the project's component list has none — the gesture\n * is off (the canvas's role notice says so), never a hardcoded fallback id.\n */\n | 'no-grid'\n\nexport interface DropRejection {\n eligible: false\n code: DropRejectionCode\n reason: string\n}\n\n/** An eligible drop: exactly one builder action (move, insert, or wrap) commits it. */\nexport type DropPlan =\n | { eligible: true; kind: 'move'; nodeId: string; target: InsertTarget }\n | { eligible: true; kind: 'insert'; target: InsertTarget }\n /**\n * EC-162 wrap gesture: the reference node and the dragged subject become the\n * two columns of a new `Grid` container, created atomically (`applyWrap`).\n * `nodeId` is present when the subject is an existing node (a move-wrap);\n * absent for blocks-panel components (an insert-wrap via `buildNode`).\n */\n | { eligible: true; kind: 'wrap'; referenceId: string; side: WrapSide; nodeId?: string }\n\nexport type DropResolution = DropPlan | DropRejection\n\nfunction rejected(code: DropRejectionCode, reason: string): DropRejection {\n return { eligible: false, code, reason }\n}\n\n// ---------------------------------------------------------------------------\n// Slot rules\n// ---------------------------------------------------------------------------\n\n/**\n * The slot an `into` drop inserts into: the default `children` slot when\n * declared, else the first authored slot that is not the layout `outlet`.\n * Mirrors the shell's insert-context resolution so click-insert and\n * drag-insert land in the same place.\n */\nexport function primaryInsertSlot(manifest: ManifestRules | undefined): SlotRule | undefined {\n if (!manifest) return undefined\n return (\n manifest.slots.find((s) => s.name === DEFAULT_SLOT) ??\n manifest.slots.find((s) => s.name !== OUTLET_SLOT)\n )\n}\n\n/** True when a component can receive `into` drops at all (declares an insertable slot). */\nexport function canNestInto(getManifest: ManifestLookup, componentId: string): boolean {\n return primaryInsertSlot(getManifest(componentId)) !== undefined\n}\n\n/**\n * True when an allow-list permits the component id — absent = any, a present\n * list (even an empty one) is a closed set (EC-544). The one membership rule the\n * slot leash and the field leash share.\n */\nfunction allowListPermits(allow: readonly string[] | undefined, componentId: string): boolean {\n return !allow || allow.includes(componentId)\n}\n\n/** True when a slot's allow-list (if any) permits the component id. */\nexport function slotAllows(slot: SlotRule | undefined, componentId: string): boolean {\n return allowListPermits(slot?.allow, componentId)\n}\n\n/**\n * The composition FIELD's top-level vocabulary as it applies to a placement under\n * `parentId` (EC-188, EC-554) — the ONE reader of a field's `allow` on the studio\n * side: every insertion surface (the insert offer in `insert-context`, drag-and-drop\n * on the canvas and the page tree incl. the wrap gesture, paste) asks here, so no\n * surface can apply the field leash differently or not at all.\n *\n * A field `allow` governs only the value's TOP level — a direct child of the\n * composition root, mirroring `validateCompositionValue` — so this returns the\n * field vocabulary when `parentId` IS the root node and `undefined` (no field\n * constraint) anywhere deeper. Keyed on node IDENTITY, never on the component id:\n * the composition editor's synthetic root is the `layout.stack` component, which\n * is also a nestable block, and a rule keyed on the component id would leash\n * every nested Stack. EC-544: an omitted `allow` is `undefined` here (any registered\n * component); a declared list is a closed set.\n */\nexport function fieldAllowAt(\n root: ComponentNode,\n parentId: string,\n fieldAllow: readonly string[] | undefined,\n): readonly string[] | undefined {\n return parentId === root.id ? fieldAllow : undefined\n}\n\n/**\n * Which declared leash refuses `componentId` landing under `parentId` in `slot`\n * (EC-545 slot leash, EC-554 field leash) — `null` when both admit it. The field\n * is judged first: it can only refuse at the composition root, where the slot is\n * the invisible synthetic root's and the field is the declaration the author sees.\n */\nexport function placementRefusal(args: {\n root: ComponentNode\n /** The node the placement lands under (the insertion target's `parentId`). */\n parentId: string\n slot: SlotRule | undefined\n fieldAllow: readonly string[] | undefined\n componentId: string\n}): 'field' | 'slot' | null {\n const { root, parentId, slot, fieldAllow, componentId } = args\n if (!allowListPermits(fieldAllowAt(root, parentId, fieldAllow), componentId)) return 'field'\n if (!slotAllows(slot, componentId)) return 'slot'\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a prospective drop into either an executable plan (one `move` or\n * `insert` builder action → one operations command → one undo step) or a\n * structured rejection the UI shows *during* the drag. Pure: same inputs,\n * same answer, on both surfaces.\n */\nexport function resolveDrop(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n /**\n * The components filling the layout roles (EC-591, `ComponentRegistry.layout`) —\n * the wrap gesture builds the `layout.grid` one, and is OFF when nobody fills it.\n * Required so no surface can forget it.\n */\n layout: LayoutContainerIds\n /**\n * The composition field's top-level `allow` (EC-554) — `undefined` when the\n * field declares none. Required so no surface can forget to pass it; judged\n * through {@link fieldAllowAt} for drops whose parent is the root.\n */\n fieldAllow: readonly string[] | undefined\n subject: DragSubject\n location: DropLocation\n}): DropResolution {\n const { root, getManifest, layout, fieldAllow, subject, location } = args\n\n if (subject.kind === 'node' && subject.nodeId === root.id) {\n return rejected('root', 'The page root cannot be moved.')\n }\n\n const reference = getNode(root, location.nodeId)\n if (!reference) {\n return rejected('missing-target', `Node \"${location.nodeId}\" is not on this page.`)\n }\n\n // EC-162: dropping beside the reference wraps both in a two-column Grid.\n if (location.position === 'left' || location.position === 'right') {\n return resolveWrap({ root, getManifest, layout, fieldAllow, subject, reference, side: location.position })\n }\n\n // Resolve the concrete insertion point: parent node, slot, index.\n let parentId: string\n let slotRule: SlotRule | undefined\n let slotName: string\n let index: number\n\n if (location.position === 'into') {\n const manifest = getManifest(reference.componentId)\n const slot = primaryInsertSlot(manifest)\n if (!slot) {\n return rejected('no-slot', `\"${reference.componentId}\" does not accept children.`)\n }\n parentId = reference.id\n slotRule = slot\n slotName = slot.name\n index = (reference.slots?.[slot.name] ?? []).length\n } else {\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'Nothing can be dropped beside the page root.')\n }\n parentId = info.parent.id\n slotName = info.slot\n // Unknown parent manifests impose no allow-list; validation owns that issue.\n slotRule = getManifest(info.parent.componentId)?.slots.find((s) => s.name === info.slot)\n index = info.index + (location.position === 'after' ? 1 : 0)\n }\n\n const refusal = placementRefusal({ root, parentId, slot: slotRule, fieldAllow, componentId: subject.componentId })\n if (refusal === 'field') {\n return rejected(\n 'field-not-allowed',\n `The field's top-level vocabulary does not allow \"${subject.componentId}\".`,\n )\n }\n if (refusal === 'slot') {\n return rejected(\n 'not-allowed',\n `Slot \"${slotName}\" does not allow \"${subject.componentId}\".`,\n )\n }\n\n const target: InsertTarget = { parentId, slot: slotName, index }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'insert', target }\n }\n\n // Cycle prevention: a node can never land inside its own subtree.\n if (subject.nodeId === parentId || isDescendantOf(root, subject.nodeId, parentId)) {\n return rejected('own-subtree', 'A node cannot be dropped into its own subtree.')\n }\n\n // No-op detection: the final position (after same-slot removal compensation,\n // matching `moveNode`) equals the node's current position.\n const current = getParentInfo(root, subject.nodeId)\n if (current && current.parent.id === parentId && current.slot === slotName) {\n const finalIndex = index > current.index ? index - 1 : index\n if (finalIndex === current.index) {\n return rejected('no-op', 'Dropping here would not move the node.')\n }\n }\n\n return { eligible: true, kind: 'move', nodeId: subject.nodeId, target }\n}\n\n// ---------------------------------------------------------------------------\n// Wrap gesture (EC-162): drop beside → two-column Grid container\n// ---------------------------------------------------------------------------\n\n/**\n * The container a \"drop beside\" gesture creates. Canonical choice (EC-161\n * semantics): a `Grid` with two columns, **not** a horizontal `Stack` —\n * EC-161 gives `Grid` the canonical column vocabulary (`columns` is a\n * validated graph value the contextual inspector refines from 2 → n), while\n * a horizontal Stack expresses flow order, not column tracks. The values\n * mirror the Grid manifest defaults (`columns: 2`, `gap: '4'`), so the\n * gesture's graph is exactly what composing the same layout manually would\n * produce.\n */\nexport const WRAP_GRID_OPTIONS: GridContainerOptions = { columns: 2, gap: '4' }\n\n/** Eligibility for a `left`/`right` (wrap-beside) drop. */\nfunction resolveWrap(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n layout: LayoutContainerIds\n fieldAllow: readonly string[] | undefined\n subject: DragSubject\n reference: ComponentNode\n side: WrapSide\n}): DropResolution {\n const { root, getManifest, layout, fieldAllow, subject, reference, side } = args\n\n // EC-591: the gesture builds the `layout.grid` component — a role nobody fills\n // switches it off (the canvas shows the role notice), never a hidden default id.\n const gridId = layout.grid\n if (gridId === undefined) {\n return rejected('no-grid', 'No component fills the layout.grid role — side-by-side columns are unavailable.')\n }\n\n // The Grid takes the reference's place in its parent slot — so the\n // reference needs a parent (the root is the page anchor, never wrapped)\n // and that slot's allow-list must accept a Grid. EC-554: beside a direct\n // child of the composition root the Grid lands AT the root, so the field's\n // top-level vocabulary must accept it too.\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'The page root cannot be wrapped in a container.')\n }\n const parentSlotRule = getManifest(info.parent.componentId)?.slots.find(\n (s) => s.name === info.slot,\n )\n const refusal = placementRefusal({\n root,\n parentId: info.parent.id,\n slot: parentSlotRule,\n fieldAllow,\n componentId: gridId,\n })\n if (refusal === 'field') {\n return rejected('field-not-allowed', \"The field's top-level vocabulary does not allow a Grid container.\")\n }\n if (refusal === 'slot') {\n return rejected('not-allowed', `Slot \"${info.slot}\" does not allow a Grid container.`)\n }\n\n // Both columns land in the Grid's children slot.\n const gridSlot = primaryInsertSlot(getManifest(gridId))\n for (const componentId of [reference.componentId, subject.componentId]) {\n if (!slotAllows(gridSlot, componentId)) {\n return rejected('not-allowed', `A Grid container does not allow \"${componentId}\".`)\n }\n }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side }\n }\n if (subject.nodeId === reference.id) {\n return rejected('no-op', 'A node cannot be placed beside itself.')\n }\n // The reference must not live inside the dragged subtree (the subject is\n // lifted out first — taking the reference with it would orphan the wrap).\n if (isDescendantOf(root, subject.nodeId, reference.id)) {\n return rejected('own-subtree', 'A node cannot wrap an element of its own subtree.')\n }\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side, nodeId: subject.nodeId }\n}\n\n/** What lands beside the reference: an existing node (moved) or a built subtree (inserted). */\nexport type WrapPartner =\n | { kind: 'node'; nodeId: string }\n | { kind: 'subtree'; node: ComponentNode }\n\nexport interface WrapResult {\n root: ComponentNode\n /** The id of the created Grid container. */\n containerId: string\n}\n\n/**\n * Execute a wrap plan as **one** structural edit: lift the partner out of the\n * tree (when it is an existing node) and replace the reference with\n * `gridContainerNode(containerId, WRAP_GRID_OPTIONS, [left, right])` in a\n * single pass. The caller commits the result as one builder action → one\n * operations command → one undo step; EC-161 normalization then runs on the\n * *committed* tree, so the two-child container can never be observed (or\n * persisted) in a half-built state. Returns `null` for structurally invalid\n * input (missing nodes, root reference, reference inside the partner).\n *\n * Determinism (EC-162): the produced container subtree is exactly the\n * `gridContainerNode` constructor output — identical to manual composition.\n */\nexport function applyWrap(args: {\n root: ComponentNode\n /** The `layout.grid` component's id (EC-591) — the container the gesture builds. */\n gridComponentId: string\n referenceId: string\n side: WrapSide\n partner: WrapPartner\n containerId: string\n}): WrapResult | null {\n const { root, gridComponentId, referenceId, side, partner, containerId } = args\n if (referenceId === root.id || !getNode(root, referenceId)) return null\n\n let working = root\n let partnerNode: ComponentNode\n if (partner.kind === 'node') {\n if (partner.nodeId === referenceId || partner.nodeId === root.id) return null\n const node = getNode(root, partner.nodeId)\n if (!node || isDescendantOf(root, partner.nodeId, referenceId)) return null\n partnerNode = node\n working = removeNode(root, partner.nodeId)\n } else {\n partnerNode = partner.node\n }\n\n const next = updateNode(working, referenceId, (reference) =>\n gridContainerNode(\n gridComponentId,\n containerId,\n WRAP_GRID_OPTIONS,\n side === 'left' ? [partnerNode, reference] : [reference, partnerNode],\n ),\n )\n return { root: next, containerId }\n}\n\n// ---------------------------------------------------------------------------\n// Pointer → position bands (shared hit-testing model)\n// ---------------------------------------------------------------------------\n\n/** The axis along which a container lays out its children. */\nexport type FlowAxis = 'vertical' | 'horizontal'\n\nfunction staticPropValue(node: ComponentNode, name: string): unknown {\n const prop = node.props?.[name]\n return isStaticProp(prop) ? prop.value : undefined\n}\n\n/**\n * The flow axis of a container — the axis its children stack along, and\n * therefore the hit-band axis for drops over those children (EC-162 ledger\n * fix: bands follow the parent's flow direction, on both surfaces). Mirrors\n * the renderer: a horizontal Stack flows horizontally; a Grid flows\n * horizontally when it actually has ≥ 2 column tracks (an absent/invalid\n * `columns` renders one column). Everything else is block flow (vertical).\n */\nexport function containerFlowAxis(node: ComponentNode | undefined, layout: LayoutContainerIds): FlowAxis {\n if (!node) return 'vertical'\n const kind = layoutContainerKind(node.componentId, layout)\n if (kind === 'stack') {\n return staticPropValue(node, 'direction') === 'horizontal' ? 'horizontal' : 'vertical'\n }\n if (kind === 'grid') {\n const columns = staticPropValue(node, 'columns')\n return typeof columns === 'number' && columns >= 2 ? 'horizontal' : 'vertical'\n }\n return 'vertical'\n}\n\n/** Cross-axis side-band width (fraction of the box) that triggers the wrap gesture. */\nconst WRAP_BAND = 0.18\n\n/**\n * Map a 2D pointer offset within a drop target's box onto a `DropPosition`\n * (EC-162). `flow` is the flow axis of the target's *parent* — before/after\n * bands run along it. On vertically flowing targets, narrow side bands on the\n * cross axis produce `left`/`right` (the wrap-into-columns gesture, when\n * `wrappable`); children of horizontal containers get left/right bands for\n * plain reorder instead, so \"beside\" already means before/after there.\n */\nexport function dropPositionForPoint(args: {\n /** Pointer offset across the target's width, 0..1. */\n x: number\n /** Pointer offset across the target's height, 0..1. */\n y: number\n /** Flow axis of the target's parent (the axis siblings stack along). */\n flow: FlowAxis\n nestable: boolean\n edges: boolean\n /** Enable the wrap side-bands (the gesture surface opts in). */\n wrappable?: boolean\n}): DropPosition {\n const { x, y, flow, nestable, edges, wrappable = false } = args\n if (!edges) return 'into'\n if (wrappable && flow === 'vertical') {\n if (x < WRAP_BAND) return 'left'\n if (x > 1 - WRAP_BAND) return 'right'\n }\n return dropPositionForPointer({ ratio: flow === 'horizontal' ? x : y, nestable, edges })\n}\n\n/**\n * Map a pointer offset within a drop target's box onto a `DropPosition`.\n * `ratio` is the pointer's offset along the target's main axis, 0..1 — the\n * caller picks the axis (the parent's flow axis; see `containerFlowAxis`).\n *\n * - nestable targets with siblings: edge quarters insert before/after, the\n * middle half nests into the slot\n * - leaf targets: top/bottom halves insert before/after\n * - targets without siblings (the root): always `into`\n */\nexport function dropPositionForPointer(args: {\n ratio: number\n /** Whether `into` is structurally possible (target declares an insert slot). */\n nestable: boolean\n /** Whether before/after exist (target has a parent slot). */\n edges: boolean\n}): DropPosition {\n const { ratio, nestable, edges } = args\n if (!edges) return 'into'\n if (nestable) {\n if (ratio < 0.25) return 'before'\n if (ratio > 0.75) return 'after'\n return 'into'\n }\n return ratio < 0.5 ? 'before' : 'after'\n}\n\n// ---------------------------------------------------------------------------\n// Indicator state (shared visual contract)\n// ---------------------------------------------------------------------------\n\n/**\n * The visual state a surface shows for the current resolution. `blocked` is\n * the explicit rejection state (never a silent failed drop); `null` means show\n * nothing (a no-op drop is inert, not an error).\n */\nexport type DropIndicator = DropPosition | 'blocked' | null\n\nexport function dropIndicatorFor(\n resolution: DropResolution,\n position: DropPosition,\n): DropIndicator {\n if (resolution.eligible) return position\n return resolution.code === 'no-op' ? null : 'blocked'\n}\n"]}
1
+ {"version":3,"file":"drop-rules.js","sourceRoot":"","sources":["../../../src/lib/dnd/drop-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n DEFAULT_SLOT,\n OUTLET_SLOT,\n allowAt,\n getNode,\n getParentInfo,\n gridContainerNode,\n isDescendantOf,\n isStaticProp,\n layoutContainerKind,\n placementRefusal as judgePlacement,\n removeNode,\n slotAllowList,\n updateNode,\n} from '@elytracms/core/project-graph'\nimport type {\n ComponentNode,\n GridContainerOptions,\n InsertTarget,\n LayoutContainerIds,\n PlacementScope,\n} from '@elytracms/core/project-graph'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\n\n/**\n * Drag & drop eligibility rules (EC-160 + EC-162, vision §3.1) — pure\n * graph-level functions with **no DOM or UI dependencies**. The canvas and the\n * page tree both resolve drops through `resolveDrop`, so the two surfaces can\n * never disagree about what a manifest slot allow-list permits, what the\n * composition field's top-level vocabulary permits at the root (EC-554), which\n * children a component accepts, or whether a move would create a cycle.\n *\n * The drop-zone model is position-based: `into` a slot, `before`/`after` a\n * sibling along the parent's flow axis, and the EC-162 gesture positions\n * `left`/`right` — dropping *beside* an element wraps both in a two-column\n * `Grid` container (see `applyWrap`) — the component that fills the\n * `layout.grid` role (EC-591); with no such component the gesture is off.\n */\n\n// ---------------------------------------------------------------------------\n// Inputs\n// ---------------------------------------------------------------------------\n\n/**\n * The manifest slice the rules evaluate. Structural (not the full\n * `ComponentManifest`) so tests and future callers can feed minimal lookups.\n */\nexport interface SlotRule {\n name: string\n /** Restrict which component ids may be inserted (omit to allow any). */\n allow?: string[]\n}\n\nexport interface ManifestRules {\n slots: SlotRule[]\n}\n\n/** Resolve a component id to its manifest slot rules (undefined = unknown component). */\nexport type ManifestLookup = (componentId: string) => ManifestRules | undefined\n\n/**\n * The canonical `ManifestRules` view of a full manifest (EC-386): the declared\n * `slots` merged with the blocks-prop-derived slots (`manifestSlots`, EC-190\n * \"slots are blocks props\" — a `blocks`-type prop IS a named child region whose\n * subtrees live in `node.slots[propName]`, carrying the prop's `allow` leash).\n * Every surface that feeds a real `ComponentManifest` into a {@link ManifestLookup}\n * or {@link primaryInsertSlot} must adapt through this — resolving against raw\n * `manifest.slots` silently degrades a `project.Section.body`-style region to a\n * sibling insert. One derivation, so tree insert, DnD, paste, and the canvas\n * affordances can never disagree about which regions exist or what they allow.\n */\nexport function manifestRules(manifest: ComponentManifest | undefined): ManifestRules | undefined {\n return manifest ? { slots: manifestSlots(manifest) } : undefined\n}\n\n/** What is being dragged. */\nexport type DragSubject =\n /** An existing node of the current page — a completed drop is a `move`. */\n | { kind: 'node'; nodeId: string; componentId: string }\n /** A new subtree (blocks-panel tile) — a completed drop is an `insert`. */\n | { kind: 'component'; componentId: string }\n\n/**\n * Where, relative to a reference node, the drop would land. `into`, `before`\n * and `after` are the EC-160 positions; `left`/`right` are the EC-162 gesture\n * positions — \"beside\" the reference, wrapping both elements in a two-column\n * `Grid` container.\n */\nexport type DropPosition = 'into' | 'before' | 'after' | 'left' | 'right'\n\n/** The side of the reference an EC-162 wrap gesture drops on. */\nexport type WrapSide = 'left' | 'right'\n\nexport interface DropLocation {\n /** The reference node id (the hovered node/row). */\n nodeId: string\n position: DropPosition\n}\n\n// ---------------------------------------------------------------------------\n// Outcomes\n// ---------------------------------------------------------------------------\n\nexport type DropRejectionCode =\n /** The reference node does not exist in the tree. */\n | 'missing-target'\n /** `into` on a component that declares no insertable slot. */\n | 'no-slot'\n /** The target slot's manifest allow-list rejects the dragged component. */\n | 'not-allowed'\n /**\n * EC-554: the composition FIELD's top-level `allow` rejects the component\n * landing as a direct child of the composition root (see {@link placementRefusal}).\n */\n | 'field-not-allowed'\n /**\n * EC-584: the component is not canvas-eligible (server-only, `clientRenderable:\n * false`) — refused wherever it would land, by the same judge the reducer runs.\n */\n | 'server-only'\n /** Moving a node into itself or its own subtree (cycle). */\n | 'own-subtree'\n /** The page root cannot be moved, and nothing can drop beside it. */\n | 'root'\n /** Structurally valid but changes nothing (dropping a node onto its own position). */\n | 'no-op'\n /**\n * EC-591: the wrap-beside gesture needs the component that fills the\n * `layout.grid` role, and the project's component list has none — the gesture\n * is off (the canvas's role notice says so), never a hardcoded fallback id.\n */\n | 'no-grid'\n\nexport interface DropRejection {\n eligible: false\n code: DropRejectionCode\n reason: string\n}\n\n/** An eligible drop: exactly one builder action (move, insert, or wrap) commits it. */\nexport type DropPlan =\n | { eligible: true; kind: 'move'; nodeId: string; target: InsertTarget }\n | { eligible: true; kind: 'insert'; target: InsertTarget }\n /**\n * EC-162 wrap gesture: the reference node and the dragged subject become the\n * two columns of a new `Grid` container, created atomically (`applyWrap`).\n * `nodeId` is present when the subject is an existing node (a move-wrap);\n * absent for blocks-panel components (an insert-wrap via `buildNode`).\n */\n | { eligible: true; kind: 'wrap'; referenceId: string; side: WrapSide; nodeId?: string }\n\nexport type DropResolution = DropPlan | DropRejection\n\nfunction rejected(code: DropRejectionCode, reason: string): DropRejection {\n return { eligible: false, code, reason }\n}\n\n// ---------------------------------------------------------------------------\n// Slot rules\n// ---------------------------------------------------------------------------\n\n/**\n * The slot an `into` drop inserts into: the default `children` slot when\n * declared, else the first authored slot that is not the layout `outlet`.\n * Mirrors the shell's insert-context resolution so click-insert and\n * drag-insert land in the same place.\n */\nexport function primaryInsertSlot(manifest: ManifestRules | undefined): SlotRule | undefined {\n if (!manifest) return undefined\n return (\n manifest.slots.find((s) => s.name === DEFAULT_SLOT) ??\n manifest.slots.find((s) => s.name !== OUTLET_SLOT)\n )\n}\n\n/** True when a component can receive `into` drops at all (declares an insertable slot). */\nexport function canNestInto(getManifest: ManifestLookup, componentId: string): boolean {\n return primaryInsertSlot(getManifest(componentId)) !== undefined\n}\n\n/**\n * Which rule refuses `componentId` landing under `parentId` in `slot` — `null` when\n * the placement is admitted. EC-584: this is a READER of the one core placement judge\n * (`@elytracms/core/project-graph` `placementRefusal` over `allowAt`'s vocabulary —\n * the field's at the composition root, keyed on node identity; the parent's slot\n * below), the SAME call the builder reducer enforces and the publish gate applies.\n * Every insertion surface (the insert offer, drag-and-drop on the canvas and the page\n * tree incl. the wrap gesture, paste) pre-checks here so its indicator says what the\n * sink will do; the reducer refuses whatever a surface forgot.\n *\n * `'field'` / `'slot'` name the vocabulary that refused (the field is the declaration\n * the author sees at the root — the synthetic root's own slot is invisible);\n * `'server-only'` is canvas eligibility, wherever it would land.\n */\nexport function placementRefusal(args: {\n root: ComponentNode\n /** The node the placement lands under (the insertion target's `parentId`). */\n parentId: string\n slot: string\n placement: PlacementScope\n componentId: string\n}): 'field' | 'slot' | 'server-only' | null {\n const { root, parentId, slot, placement, componentId } = args\n const code = judgePlacement({\n componentId,\n view: placement.components.get(componentId),\n allow: allowAt(root, parentId, slot, placement),\n })\n if (code === 'server-only-component') return 'server-only'\n if (code === 'forbidden-component') return parentId === root.id ? 'field' : 'slot'\n return null\n}\n\n/** The rejection a refused placement shows during a drag (EC-584: the judge's verdict). */\nfunction placementRejection(\n refusal: 'field' | 'slot' | 'server-only',\n componentId: string,\n slotName: string,\n): DropRejection {\n if (refusal === 'server-only') {\n return rejected('server-only', `\"${componentId}\" is server-only and cannot be placed on the canvas.`)\n }\n if (refusal === 'field') {\n return rejected('field-not-allowed', `The field's top-level vocabulary does not allow \"${componentId}\".`)\n }\n return rejected('not-allowed', `Slot \"${slotName}\" does not allow \"${componentId}\".`)\n}\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a prospective drop into either an executable plan (one `move` or\n * `insert` builder action → one operations command → one undo step) or a\n * structured rejection the UI shows *during* the drag. Pure: same inputs,\n * same answer, on both surfaces.\n */\nexport function resolveDrop(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n /**\n * The components filling the layout roles (EC-591, `ComponentRegistry.layout`) —\n * the wrap gesture builds the `layout.grid` one, and is OFF when nobody fills it.\n * Required so no surface can forget it.\n */\n layout: LayoutContainerIds\n /**\n * EC-584: what the placement judge reads — the manifest lookup and the composition\n * field's effective top-level vocabulary (the builder store's `placement`).\n * Required so no surface can forget it; the reducer judges the same scope.\n */\n placement: PlacementScope\n subject: DragSubject\n location: DropLocation\n}): DropResolution {\n const { root, getManifest, layout, placement, subject, location } = args\n\n if (subject.kind === 'node' && subject.nodeId === root.id) {\n return rejected('root', 'The page root cannot be moved.')\n }\n\n const reference = getNode(root, location.nodeId)\n if (!reference) {\n return rejected('missing-target', `Node \"${location.nodeId}\" is not on this page.`)\n }\n\n // EC-162: dropping beside the reference wraps both in a two-column Grid.\n if (location.position === 'left' || location.position === 'right') {\n return resolveWrap({ root, getManifest, layout, placement, subject, reference, side: location.position })\n }\n\n // Resolve the concrete insertion point: parent node, slot, index.\n let parentId: string\n let slotName: string\n let index: number\n\n if (location.position === 'into') {\n const manifest = getManifest(reference.componentId)\n const slot = primaryInsertSlot(manifest)\n if (!slot) {\n return rejected('no-slot', `\"${reference.componentId}\" does not accept children.`)\n }\n parentId = reference.id\n slotName = slot.name\n index = (reference.slots?.[slot.name] ?? []).length\n } else {\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'Nothing can be dropped beside the page root.')\n }\n parentId = info.parent.id\n slotName = info.slot\n index = info.index + (location.position === 'after' ? 1 : 0)\n }\n\n // EC-584: a move inside its own slot is a reorder — the placement does not change,\n // so the judge (and the reducer) leave it alone.\n const current = subject.kind === 'node' ? getParentInfo(root, subject.nodeId) : null\n const relocates = !current || current.parent.id !== parentId || current.slot !== slotName\n if (relocates) {\n const refusal = placementRefusal({ root, parentId, slot: slotName, placement, componentId: subject.componentId })\n if (refusal) return placementRejection(refusal, subject.componentId, slotName)\n }\n\n const target: InsertTarget = { parentId, slot: slotName, index }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'insert', target }\n }\n\n // Cycle prevention: a node can never land inside its own subtree.\n if (subject.nodeId === parentId || isDescendantOf(root, subject.nodeId, parentId)) {\n return rejected('own-subtree', 'A node cannot be dropped into its own subtree.')\n }\n\n // No-op detection: the final position (after same-slot removal compensation,\n // matching `moveNode`) equals the node's current position.\n if (current && current.parent.id === parentId && current.slot === slotName) {\n const finalIndex = index > current.index ? index - 1 : index\n if (finalIndex === current.index) {\n return rejected('no-op', 'Dropping here would not move the node.')\n }\n }\n\n return { eligible: true, kind: 'move', nodeId: subject.nodeId, target }\n}\n\n// ---------------------------------------------------------------------------\n// Wrap gesture (EC-162): drop beside → two-column Grid container\n// ---------------------------------------------------------------------------\n\n/**\n * The container a \"drop beside\" gesture creates. Canonical choice (EC-161\n * semantics): a `Grid` with two columns, **not** a horizontal `Stack` —\n * EC-161 gives `Grid` the canonical column vocabulary (`columns` is a\n * validated graph value the contextual inspector refines from 2 → n), while\n * a horizontal Stack expresses flow order, not column tracks. The values\n * mirror the Grid manifest defaults (`columns: 2`, `gap: '4'`), so the\n * gesture's graph is exactly what composing the same layout manually would\n * produce.\n */\nexport const WRAP_GRID_OPTIONS: GridContainerOptions = { columns: 2, gap: '4' }\n\n/** Eligibility for a `left`/`right` (wrap-beside) drop. */\nfunction resolveWrap(args: {\n root: ComponentNode\n getManifest: ManifestLookup\n layout: LayoutContainerIds\n placement: PlacementScope\n subject: DragSubject\n reference: ComponentNode\n side: WrapSide\n}): DropResolution {\n const { root, getManifest, layout, placement, subject, reference, side } = args\n\n // EC-591: the gesture builds the `layout.grid` component — a role nobody fills\n // switches it off (the canvas shows the role notice), never a hidden default id.\n const gridId = layout.grid\n if (gridId === undefined) {\n return rejected('no-grid', 'No component fills the layout.grid role — side-by-side columns are unavailable.')\n }\n\n // The Grid takes the reference's place in its parent slot — so the\n // reference needs a parent (the root is the page anchor, never wrapped)\n // and that slot's allow-list must accept a Grid. EC-554: beside a direct\n // child of the composition root the Grid lands AT the root, so the field's\n // top-level vocabulary must accept it too.\n const info = getParentInfo(root, reference.id)\n if (!info) {\n return rejected('root', 'The page root cannot be wrapped in a container.')\n }\n const refusal = placementRefusal({\n root,\n parentId: info.parent.id,\n slot: info.slot,\n placement,\n componentId: gridId,\n })\n if (refusal === 'field') {\n return rejected('field-not-allowed', \"The field's top-level vocabulary does not allow a Grid container.\")\n }\n if (refusal === 'slot') {\n return rejected('not-allowed', `Slot \"${info.slot}\" does not allow a Grid container.`)\n }\n if (refusal === 'server-only') return placementRejection(refusal, gridId, info.slot)\n\n // Both columns land in the Grid's children slot — judged by the same core judge.\n const gridSlot = primaryInsertSlot(getManifest(gridId))\n const gridSlotAllow = slotAllowList(\n placement.components.get(gridId)?.slots?.find((s) => s.name === (gridSlot?.name ?? DEFAULT_SLOT)),\n )\n for (const componentId of [reference.componentId, subject.componentId]) {\n const code = judgePlacement({ componentId, view: placement.components.get(componentId), allow: gridSlotAllow })\n if (code === 'server-only-component') return placementRejection('server-only', componentId, gridSlot?.name ?? DEFAULT_SLOT)\n if (code === 'forbidden-component') {\n return rejected('not-allowed', `A Grid container does not allow \"${componentId}\".`)\n }\n }\n\n if (subject.kind === 'component') {\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side }\n }\n if (subject.nodeId === reference.id) {\n return rejected('no-op', 'A node cannot be placed beside itself.')\n }\n // The reference must not live inside the dragged subtree (the subject is\n // lifted out first — taking the reference with it would orphan the wrap).\n if (isDescendantOf(root, subject.nodeId, reference.id)) {\n return rejected('own-subtree', 'A node cannot wrap an element of its own subtree.')\n }\n return { eligible: true, kind: 'wrap', referenceId: reference.id, side, nodeId: subject.nodeId }\n}\n\n/** What lands beside the reference: an existing node (moved) or a built subtree (inserted). */\nexport type WrapPartner =\n | { kind: 'node'; nodeId: string }\n | { kind: 'subtree'; node: ComponentNode }\n\nexport interface WrapResult {\n root: ComponentNode\n /** The id of the created Grid container. */\n containerId: string\n}\n\n/**\n * Execute a wrap plan as **one** structural edit: lift the partner out of the\n * tree (when it is an existing node) and replace the reference with\n * `gridContainerNode(containerId, WRAP_GRID_OPTIONS, [left, right])` in a\n * single pass. The caller commits the result as one builder action → one\n * operations command → one undo step; EC-161 normalization then runs on the\n * *committed* tree, so the two-child container can never be observed (or\n * persisted) in a half-built state. Returns `null` for structurally invalid\n * input (missing nodes, root reference, reference inside the partner).\n *\n * Determinism (EC-162): the produced container subtree is exactly the\n * `gridContainerNode` constructor output — identical to manual composition.\n */\nexport function applyWrap(args: {\n root: ComponentNode\n /** The `layout.grid` component's id (EC-591) — the container the gesture builds. */\n gridComponentId: string\n referenceId: string\n side: WrapSide\n partner: WrapPartner\n containerId: string\n}): WrapResult | null {\n const { root, gridComponentId, referenceId, side, partner, containerId } = args\n if (referenceId === root.id || !getNode(root, referenceId)) return null\n\n let working = root\n let partnerNode: ComponentNode\n if (partner.kind === 'node') {\n if (partner.nodeId === referenceId || partner.nodeId === root.id) return null\n const node = getNode(root, partner.nodeId)\n if (!node || isDescendantOf(root, partner.nodeId, referenceId)) return null\n partnerNode = node\n working = removeNode(root, partner.nodeId)\n } else {\n partnerNode = partner.node\n }\n\n const next = updateNode(working, referenceId, (reference) =>\n gridContainerNode(\n gridComponentId,\n containerId,\n WRAP_GRID_OPTIONS,\n side === 'left' ? [partnerNode, reference] : [reference, partnerNode],\n ),\n )\n return { root: next, containerId }\n}\n\n// ---------------------------------------------------------------------------\n// Pointer → position bands (shared hit-testing model)\n// ---------------------------------------------------------------------------\n\n/** The axis along which a container lays out its children. */\nexport type FlowAxis = 'vertical' | 'horizontal'\n\nfunction staticPropValue(node: ComponentNode, name: string): unknown {\n const prop = node.props?.[name]\n return isStaticProp(prop) ? prop.value : undefined\n}\n\n/**\n * The flow axis of a container — the axis its children stack along, and\n * therefore the hit-band axis for drops over those children (EC-162 ledger\n * fix: bands follow the parent's flow direction, on both surfaces). Mirrors\n * the renderer: a horizontal Stack flows horizontally; a Grid flows\n * horizontally when it actually has ≥ 2 column tracks (an absent/invalid\n * `columns` renders one column). Everything else is block flow (vertical).\n */\nexport function containerFlowAxis(node: ComponentNode | undefined, layout: LayoutContainerIds): FlowAxis {\n if (!node) return 'vertical'\n const kind = layoutContainerKind(node.componentId, layout)\n if (kind === 'stack') {\n return staticPropValue(node, 'direction') === 'horizontal' ? 'horizontal' : 'vertical'\n }\n if (kind === 'grid') {\n const columns = staticPropValue(node, 'columns')\n return typeof columns === 'number' && columns >= 2 ? 'horizontal' : 'vertical'\n }\n return 'vertical'\n}\n\n/** Cross-axis side-band width (fraction of the box) that triggers the wrap gesture. */\nconst WRAP_BAND = 0.18\n\n/**\n * Map a 2D pointer offset within a drop target's box onto a `DropPosition`\n * (EC-162). `flow` is the flow axis of the target's *parent* — before/after\n * bands run along it. On vertically flowing targets, narrow side bands on the\n * cross axis produce `left`/`right` (the wrap-into-columns gesture, when\n * `wrappable`); children of horizontal containers get left/right bands for\n * plain reorder instead, so \"beside\" already means before/after there.\n */\nexport function dropPositionForPoint(args: {\n /** Pointer offset across the target's width, 0..1. */\n x: number\n /** Pointer offset across the target's height, 0..1. */\n y: number\n /** Flow axis of the target's parent (the axis siblings stack along). */\n flow: FlowAxis\n nestable: boolean\n edges: boolean\n /** Enable the wrap side-bands (the gesture surface opts in). */\n wrappable?: boolean\n}): DropPosition {\n const { x, y, flow, nestable, edges, wrappable = false } = args\n if (!edges) return 'into'\n if (wrappable && flow === 'vertical') {\n if (x < WRAP_BAND) return 'left'\n if (x > 1 - WRAP_BAND) return 'right'\n }\n return dropPositionForPointer({ ratio: flow === 'horizontal' ? x : y, nestable, edges })\n}\n\n/**\n * Map a pointer offset within a drop target's box onto a `DropPosition`.\n * `ratio` is the pointer's offset along the target's main axis, 0..1 — the\n * caller picks the axis (the parent's flow axis; see `containerFlowAxis`).\n *\n * - nestable targets with siblings: edge quarters insert before/after, the\n * middle half nests into the slot\n * - leaf targets: top/bottom halves insert before/after\n * - targets without siblings (the root): always `into`\n */\nexport function dropPositionForPointer(args: {\n ratio: number\n /** Whether `into` is structurally possible (target declares an insert slot). */\n nestable: boolean\n /** Whether before/after exist (target has a parent slot). */\n edges: boolean\n}): DropPosition {\n const { ratio, nestable, edges } = args\n if (!edges) return 'into'\n if (nestable) {\n if (ratio < 0.25) return 'before'\n if (ratio > 0.75) return 'after'\n return 'into'\n }\n return ratio < 0.5 ? 'before' : 'after'\n}\n\n// ---------------------------------------------------------------------------\n// Indicator state (shared visual contract)\n// ---------------------------------------------------------------------------\n\n/**\n * The visual state a surface shows for the current resolution. `blocked` is\n * the explicit rejection state (never a silent failed drop); `null` means show\n * nothing (a no-op drop is inert, not an error).\n */\nexport type DropIndicator = DropPosition | 'blocked' | null\n\nexport function dropIndicatorFor(\n resolution: DropResolution,\n position: DropPosition,\n): DropIndicator {\n if (resolution.eligible) return position\n return resolution.code === 'no-op' ? null : 'blocked'\n}\n"]}
@@ -13,4 +13,4 @@
13
13
  * (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the
14
14
  * package.json declaration directly; its bundler allows what tsc's rootDir forbids.)
15
15
  */
16
- export declare const ENGINE_VERSION = "0.0.91";
16
+ export declare const ENGINE_VERSION = "0.0.93";
@@ -13,5 +13,5 @@
13
13
  * (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the
14
14
  * package.json declaration directly; its bundler allows what tsc's rootDir forbids.)
15
15
  */
16
- export const ENGINE_VERSION = '0.0.91';
16
+ export const ENGINE_VERSION = '0.0.93';
17
17
  //# sourceMappingURL=engine-version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine-version.js","sourceRoot":"","sources":["../../src/lib/engine-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAA","sourcesContent":["/**\n * The engine version this studio build was compiled from (EC-552) — the client-side\n * half of the deployed-functions version handshake: the boot gate compares it against\n * what `delivery:engineVersion` answers from the deployment.\n *\n * A MIRROR of `package.json`'s `version`, not the declaration itself: client source\n * cannot import `../../package.json` (outside the tsc build's `rootDir`; moving the\n * root would reshape `dist` and every export path). The parity test beside this file\n * fs-reads the real declaration and fails `pnpm verify` the moment the two diverge,\n * so a release cannot ship with a stale mirror. `pnpm release:bump` writes this\n * alongside the five `package.json` versions (EC-608).\n *\n * (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the\n * package.json declaration directly; its bundler allows what tsc's rootDir forbids.)\n */\nexport const ENGINE_VERSION = '0.0.91'\n"]}
1
+ {"version":3,"file":"engine-version.js","sourceRoot":"","sources":["../../src/lib/engine-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAA","sourcesContent":["/**\n * The engine version this studio build was compiled from (EC-552) — the client-side\n * half of the deployed-functions version handshake: the boot gate compares it against\n * what `delivery:engineVersion` answers from the deployment.\n *\n * A MIRROR of `package.json`'s `version`, not the declaration itself: client source\n * cannot import `../../package.json` (outside the tsc build's `rootDir`; moving the\n * root would reshape `dist` and every export path). The parity test beside this file\n * fs-reads the real declaration and fails `pnpm verify` the moment the two diverge,\n * so a release cannot ship with a stale mirror. `pnpm release:bump` writes this\n * alongside the five `package.json` versions (EC-608).\n *\n * (The convex-side stamp does NOT read this mirror — `convex/delivery.ts` imports the\n * package.json declaration directly; its bundler allows what tsc's rootDir forbids.)\n */\nexport const ENGINE_VERSION = '0.0.93'\n"]}
@@ -224,13 +224,19 @@ export const deMessages = {
224
224
  // EC-494 — die Titel der Fehler-Badges im Banner.
225
225
  'operations.errorLabel.validation': 'Konnte nicht gespeichert werden',
226
226
  'operations.errorLabel.not-found': 'Nicht gefunden',
227
- 'operations.errorLabel.conflict': 'Existiert bereits',
227
+ 'operations.errorLabel.conflict': 'Konflikt',
228
228
  'operations.errorLabel.backend-unavailable': 'Verbindungsproblem',
229
229
  'operations.errorLabel.unauthorized': 'Nicht erlaubt',
230
230
  'operations.errorLabel.unknown': 'Etwas ist schiefgelaufen',
231
231
  'operations.error.invalidInput': 'Die Eingabe für „{operation}“ ist ungültig.',
232
232
  'operations.error.notFound': 'Kein {entity} für „{target}“ gefunden.',
233
233
  'operations.error.conflict': '„{target}“ steht im Konflikt mit einem vorhandenen Datensatz.',
234
+ 'operations.error.revisionConflict': '„{target}“ wurde geändert, seit Sie es geladen haben (jetzt Revision {revision}) — laden Sie es neu und übernehmen Sie Ihre Änderung erneut.',
235
+ 'operations.error.revisionConflictGone': '„{target}“ existiert nicht mehr — es wurde gelöscht, seit Sie es geladen haben.',
236
+ 'operations.error.documentExists': '„{target}“ existiert bereits — beim Anlegen wird nie ein Dokument überschrieben.',
237
+ 'operations.error.singletonExists': '„{collection}“ hat bereits sein Dokument „{target}“ — öffnen Sie es, statt ein weiteres anzulegen.',
238
+ 'operations.error.patchConflictOne': '{count} Änderung an „{target}“ wurde abgelehnt: dasselbe Feld wurde geändert, seit Sie das Dokument geladen haben.',
239
+ 'operations.error.patchConflict': '{count} Änderungen an „{target}“ wurden abgelehnt: dieselben Felder wurden geändert, seit Sie das Dokument geladen haben.',
234
240
  'operations.error.backendUnavailable': 'Das Persistenz-Backend ist nicht erreichbar.',
235
241
  'operations.error.unauthorized': 'Die Operation „{operation}“ wurde vom Autorisierungs-Hook abgelehnt.',
236
242
  'operations.error.unknown': 'Es ist ein unbekannter Fehler bei der Operation aufgetreten.',
@@ -239,6 +245,11 @@ export const deMessages = {
239
245
  'operations.error.strictReferencesBlockDeleteOne': '„{target}“ kann nicht gelöscht werden: {count} Dokument verweist über strikte Beziehungsfelder darauf — {named}. Entfernen Sie zuerst diese Verweise.',
240
246
  'operations.error.strictReferencesBlockUnpublish': 'Die Veröffentlichung von „{target}“ kann nicht zurückgenommen werden: {count} Dokumente verweisen über strikte Beziehungsfelder darauf — {named}{more}. Entfernen Sie zuerst diese Verweise.',
241
247
  'operations.error.strictReferencesBlockUnpublishOne': 'Die Veröffentlichung von „{target}“ kann nicht zurückgenommen werden: {count} Dokument verweist über strikte Beziehungsfelder darauf — {named}. Entfernen Sie zuerst diese Verweise.',
248
+ // EC-589 — die Gegenrichtung von `strict: true`: Veröffentlichen verweigert, solange ein striktes Ziel fehlt oder unveröffentlicht ist.
249
+ 'operations.error.strictTargetsUnpublishedOne': 'Das Dokument „{target}“ kann nicht veröffentlicht werden: Ein Ziel einer strikten Beziehung fehlt oder ist unveröffentlicht — veröffentlichen Sie es zuerst.',
250
+ 'operations.error.strictTargetsUnpublished': 'Das Dokument „{target}“ kann nicht veröffentlicht werden: Ziele strikter Beziehungen fehlen oder sind unveröffentlicht — veröffentlichen Sie sie zuerst.',
251
+ 'operations.error.publishNeedsLocales': '„{target}“ kann nicht veröffentlicht werden: Seine Sammlung deklariert lokalisierte Felder ({fields}), und es wurde keine Sprachkonfiguration übergeben, um deren ausgeblendete Werte zu beurteilen.',
252
+ 'operations.error.versionNotPinnable': 'Version {version} von „{target}“ wurde nicht durch eine Veröffentlichung erfasst und kann daher nicht live geschaltet werden — stellen Sie sie im Entwurf wieder her und veröffentlichen Sie diesen.',
242
253
  'operations.errorDetail.andMore': ' und {count} weitere',
243
254
  // EC-494 — Fehler von Inhaltsabfragen (Filterleiste + Listing-Hinweis im Canvas).
244
255
  // „filterable“, „asc“, „desc“, „in“, „empty“ sind Schlüsselwörter aus der Konfiguration
@@ -392,7 +403,6 @@ export const deMessages = {
392
403
  'cms.editor.saveFailed': 'Speichern fehlgeschlagen',
393
404
  'cms.editor.unsavedChanges': 'Nicht gespeicherte Änderungen',
394
405
  'cms.editor.saved': 'Gespeichert',
395
- 'cms.editor.saving': 'Wird gespeichert…',
396
406
  // 0.0.42 (round 3): the standalone "View live" button is retired — opening the live
397
407
  // page now hangs off the header URL row's leaf segment. These strings still drive the
398
408
  // leaf's tooltip (open-live target + the published-gating reasons).
@@ -429,11 +439,6 @@ export const deMessages = {
429
439
  'cms.editor.loadingPublishedVersion': 'Veröffentlichte Version wird geladen…',
430
440
  'cms.editor.noPublishedVersion': 'Keine veröffentlichte Version für dieses Dokument gefunden.',
431
441
  'cms.editor.unsavedChangesGuard': 'Sie haben nicht gespeicherte Änderungen an diesem Datensatz. Speichern Sie sie vor dem Verlassen oder verwerfen Sie sie.',
432
- 'cms.editor.publishUnpublishedRefsTitle': 'Mit nicht veröffentlichten Verweisen veröffentlichen?',
433
- 'cms.editor.publishUnpublishedRefsDescription': 'Diese Seite verweist auf Inhalte, die noch nicht veröffentlicht sind – diese Abschnitte bleiben auf der Live-Seite leer, bis Sie sie ebenfalls veröffentlichen.',
434
- 'cms.editor.publishAlsoHasIssues.one': 'Diese Seite hat außerdem {count} weiteres ungelöstes Problem – siehe Tab „Probleme“.',
435
- 'cms.editor.publishAlsoHasIssues.other': 'Diese Seite hat außerdem {count} weitere ungelöste Probleme – siehe Tab „Probleme“.',
436
- 'cms.editor.publishAnyway': 'Trotzdem veröffentlichen',
437
442
  'cms.editor.readOnlyBadge': 'schreibgeschützt',
438
443
  // EC-318 — connected fields: the per-field localization connection affordance that
439
444
  // replaces EC-176's passive "showing EN" text. Shown only on a localized field in a
@@ -451,8 +456,6 @@ export const deMessages = {
451
456
  'cms.editor.detailsPane': 'Details',
452
457
  'cms.editor.servedAt': 'Ausgeliefert unter {path}',
453
458
  'cms.editor.slugUrlHint': 'Die URL wird aus dem Slug (und dem übergeordneten Element) abgeleitet – Slug festlegen, um sie zu definieren.',
454
- 'cms.editor.embedUnpublishedIssue': 'Bindet das wiederverwendbare Element „{title}“ ein, das nicht veröffentlicht ist – es bleibt auf der Live-Seite leer, bis Sie es veröffentlichen.',
455
- 'cms.editor.embedMissingIssue': 'Bindet ein wiederverwendbares Element ein, das nicht mehr existiert („{id}“) – Einbettung korrigieren oder entfernen.',
456
459
  'cms.editor.defaultGroup': 'Felder',
457
460
  // ─────────────────────────────────────────────────────────────────────────
458
461
  // CMS — issues pane (validation)
@@ -480,6 +483,7 @@ export const deMessages = {
480
483
  'cms.issue.missing-localized-value': 'Fehlende Übersetzung',
481
484
  'cms.issue.route-conflict': 'URL-Konflikt',
482
485
  'cms.issue.redirect-loop': 'Weiterleitungsschleife',
486
+ 'cms.issue.invalid-redirect': 'Doppelte Weiterleitung',
483
487
  'cms.issue.unknown-route-target': 'Fehlende Seite',
484
488
  'cms.issue.hierarchy-cycle': 'Schleife in der Seitenhierarchie',
485
489
  'cms.issue.document-nesting-depth': 'Inhalt zu tief verschachtelt',
@@ -491,6 +495,7 @@ export const deMessages = {
491
495
  'cms.issue.hidden-predicate-error': 'Fehlerhafte Sichtbarkeitsregel',
492
496
  'cms.issue.unaddressable-url': 'Nicht erreichbare URL',
493
497
  'cms.issue.unknown-version': 'Unbekannte Version',
498
+ 'cms.issue.invalid-stored-document': 'Unlesbares gespeichertes Dokument',
494
499
  // EC-443 — issue-card body templates (see messages.en.ts for the fallback contract).
495
500
  'cms.issueBody.missing-required-field': 'Das Pflichtfeld „{field}“ ist nicht ausgefüllt.',
496
501
  'cms.issueBody.missing-localized-value.default': 'Das Pflichtfeld „{field}“ hat keinen Wert für die Standardsprache „{locale}“.',
@@ -528,6 +533,8 @@ export const deMessages = {
528
533
  'cms.issueMessage.relationWrongCollection': 'Das Beziehungsfeld „{field}“ zeigt auf die Sammlung „{got}“, zielt aber auf „{target}“.',
529
534
  'cms.issueMessage.relationMissingDocument': 'Das Beziehungsfeld „{field}“ verweist auf das fehlende Dokument „{id}“ in „{target}“.',
530
535
  'cms.issueMessage.relationUnpublishedDocument': 'Das Beziehungsfeld „{field}“ verweist auf das unveröffentlichte Dokument „{id}“ in „{target}“ — auf der veröffentlichten Website erscheint dort ein Platzhalter.',
536
+ // EC-589 — eine strikte Beziehung (Feld, Unterfeld oder Prop) auf ein unveröffentlichtes Ziel blockiert das Veröffentlichen.
537
+ 'cms.issueMessage.relationStrictUnpublished': 'Das Beziehungsfeld „{field}“ ist strikt: „{id}“ in „{target}“ ist nicht veröffentlicht — dieses Dokument kann erst veröffentlicht werden, wenn es veröffentlicht ist.',
531
538
  'cms.issueMessage.assetNoAssetCollection': 'Das Medienfeld „{field}“ benötigt eine Mediensammlung, es ist aber keine registriert.',
532
539
  'cms.issueMessage.assetSingleValued': 'Das Medienfeld „{field}“ ist einwertig, hat aber {count} Ziele.',
533
540
  'cms.issueMessage.assetExpectsId': 'Das Medienfeld „{field}“ erwartet eine Medien-id.',
@@ -538,7 +545,23 @@ export const deMessages = {
538
545
  'cms.issueMessage.preConvergenceV1': 'Das Feld „{field}“ enthält eine Inhaltsform von vor der Konvergenz (die v1-Hülle „{ version, doc }“) — die Engine liest nur noch die konvergierte Sequenz „{ version: 2, content }“. Dieser Inhalt muss neu eingespielt werden (Re-Seed).',
539
546
  'cms.issueMessage.preConvergenceBareBlocks': 'Das Feld „{field}“ enthält eine Inhaltsform von vor der Konvergenz (eine blanke Komponentenliste) — die Engine liest nur noch die konvergierte Sequenz „{ version: 2, content }“. Dieser Inhalt muss neu eingespielt werden (Re-Seed).',
540
547
  'cms.issueMessage.preConvergencePlaintext': 'Das Feld „{field}“ enthält eine Inhaltsform von vor der Konvergenz (unstrukturierter Alttext) — die Engine liest nur noch die konvergierte Sequenz „{ version: 2, content }“. Dieser Inhalt muss neu eingespielt werden (Re-Seed).',
541
- 'cms.issueMessage.noValidationSession': 'Es ist keine Validierungssitzung eingebunden — die Veröffentlichung ohne Prüfung wird verweigert.',
548
+ 'cms.issueMessage.gateUnknownCollection': 'Das Projekt deklariert keine Collection „{collection}“ — es gibt keine Regeln, nach denen sie veröffentlicht werden könnte.',
549
+ 'cms.issueMessage.gateAssetLibraryUnready': 'Die Medienbibliothek konnte nicht gelesen werden — die Veröffentlichung ungeprüfter Medienverweise wird verweigert. Bitte erneut versuchen.',
550
+ 'cms.issueMessage.gateNoLocales': 'Das Projekt deklariert keine Sprachen — die Veröffentlichung ohne sie wird verweigert.',
551
+ // EC-595 A2 — der Feldkonflikt und die schlanke Feld-Präsenz.
552
+ 'cms.conflict.changedBy': 'Seit Beginn Ihrer Bearbeitung von {name} geändert — Ihr Text bleibt hier, bis Sie entscheiden.',
553
+ 'cms.conflict.removedBy': 'Seit Beginn Ihrer Bearbeitung von {name} entfernt — Ihre Änderung hat nichts mehr, worauf sie sich bezieht.',
554
+ 'cms.conflict.theirs': 'Deren Wert: {value}',
555
+ 'cms.conflict.takeTheirs': 'Deren übernehmen',
556
+ 'cms.conflict.keepMine': 'Meinen behalten',
557
+ 'cms.conflict.someoneElse': 'jemand anderem',
558
+ 'cms.conflict.youInAnotherTab': 'Ihnen (in einem anderen Tab)',
559
+ 'cms.conflict.empty': '(leer)',
560
+ 'cms.conflict.structured': 'ein strukturierter Wert',
561
+ 'cms.conflict.cellRefused': 'Dieses Feld wurde seit dem Laden von jemand anderem geändert — dessen Wert wird angezeigt.',
562
+ 'cms.presence.inField': '{name} ist in diesem Feld',
563
+ 'cms.presence.inFieldMany': '{names} sind in diesem Feld',
564
+ 'cms.presence.someone': 'Jemand',
542
565
  'cms.issueMessage.richTextHoldsArray': 'Das Rich-Text-Feld „{field}“ erwartet einen Rich-Text-Wert („{ version: 2, content }“), enthält aber ein Array.',
543
566
  'cms.issueMessage.richTextHoldsBareDoc': 'Das Rich-Text-Feld „{field}“ erwartet einen Rich-Text-Wert („{ version: 2, content }“), enthält aber ein blankes Rich-Text-Dokument ohne die Hülle „{ version: 2, content }“.',
544
567
  'cms.issueMessage.richTextHoldsObject': 'Das Rich-Text-Feld „{field}“ erwartet einen Rich-Text-Wert („{ version: 2, content }“), enthält aber ein Objekt mit den Schlüsseln „{keys}“.',
@@ -567,13 +590,15 @@ export const deMessages = {
567
590
  'cms.issueMessage.unknownVersion': 'Für das Dokument „{id}“ ist keine Version {version} aufgezeichnet.',
568
591
  'cms.issueMessage.routeConflict': 'Die Routen „{first}“ und „{second}“ treffen auf dieselben URLs zu (Muster „{pattern}“).',
569
592
  'cms.issueMessage.redirectLoop': 'Die Weiterleitungskette ab „{from}“ bildet eine Schleife (…{chain}).',
593
+ 'cms.issueMessage.duplicateRedirect': 'Die Weiterleitung „{redirect}“ (von „{redirectFrom}“) doppelt die Weiterleitung „{kept}“ (von „{keptFrom}“) — ohne abschließende Schrägstriche sind beide „{from}“. „{kept}“ gilt; lösche oder ändere die andere.',
570
594
  'cms.issueMessage.allDynamicShadowsMount': 'Die Route „{route}“ („{pattern}“) besteht nur aus dynamischen Segmenten und trifft damit auf JEDE URL mit {depth} Segmenten zu — die Hierarchie-Einhängung „{mountRoute}“ („{mountPattern}“) wird auf dieser Ebene nur dort herangezogen, wo die Route kein Dokument auflöst. Nichts ist dadurch grundsätzlich unerreichbar, aber der Vorrang entscheidet sich dort am INHALT statt an den Routen: {shadowed} werden stillschweigend verdeckt, sobald ein zusammengesetzter Pfad mit {claimed} kollidiert — und keine Seite erfährt davon. Geben Sie der Route ein statisches Präfix („/blog/:slug“), damit sie einen Namensraum beansprucht statt einer ganzen Ebene.',
571
595
  'cms.issueMessage.mountCannotServe': 'Die Hierarchie-Einhängung „{route}“ („{pattern}“) kann niemals ein Dokument ausliefern: {why}. Der Resolver überspringt eine solche Einhängung stillschweigend, deshalb liefert jede URL unter „{space}“ einen 404, ohne dass ein Hinweis erscheint.',
572
596
  'cms.issueMessage.deadCatchAll': 'Die Route „{route}“ („{pattern}“) liefert in beide Richtungen nichts: Sie endet in einem Catch-all, deshalb verwirft sie jeder Router — ihr Präfix enthält aber ein dynamisches Segment ({dynamic}), deshalb wird auch keine Hierarchie-Einhängung daraus gebaut. Das Präfix einer Einhängung muss statisch sein — deklarieren Sie eine Einhängung pro Sprache (`{ pattern: \'/de/:path*\', locale: \'de\' }`) oder entfernen Sie die Route.',
573
- 'cms.issueMessage.mountConflict': '{opening} — Einhängungen werden in Deklarationsreihenfolge aufgelöst, nicht nach Spezifität; deshalb beansprucht „{earlier}“ (Sammlung „{earlierCollection}“) dort jede URL, die der eigene Baum auflöst, und Dokumente aus „{laterCollection}“ sind an diesen URLs unerreichbar.',
597
+ 'cms.issueMessage.mountConflict': '{opening} — ein exakter Gleichstand, nur durch die Deklarationsreihenfolge getrennt; deshalb beansprucht „{earlier}“ (Sammlung „{earlierCollection}“) dort jede URL, die der eigene Baum auflöst, und Dokumente aus „{laterCollection}“ sind an diesen URLs unerreichbar.',
574
598
  'cms.issueMessage.routeBoundDocumentNotFound': 'Das an die Route gebundene Dokument „{collection}:{id}“ wurde nicht gefunden.',
575
599
  'cms.issueMessage.routeParamNotCaptured': 'Die Route „{route}“ bindet ihr Dokument an den URL-Parameter „:{param}“, den das Muster „{pattern}“ nicht erfasst.',
576
600
  'cms.issueMessage.collectionNotDefined': 'Die Sammlung „{collection}“ ist nicht definiert.',
601
+ 'cms.issueMessage.storedDocumentUnreadable': 'Das gespeicherte Dokument {document} konnte nicht gelesen werden und wird nicht ausgeliefert ({reason}).',
577
602
  'cms.issueMessage.unaddressableLocaleCollision': 'Keine URL erreicht „{id}“ in der Sprache „{locale}“: die Adresse wäre „{url}“, aber das erste Segment „{segment}“ ist ein deklarierter Sprachcode — die Website liest diese URL daher als Sprache „{segment}“, Pfad „{readPath}“. Benennen Sie dieses Segment um — an diesem Dokument oder an der übergeordneten Seite, zu der es gehört — damit es nicht mit einem Sprachcode kollidiert.',
578
603
  'cms.issueDetail.expectsString': 'erwartet eine Zeichenkette',
579
604
  'cms.issueDetail.expectsRichText': 'erwartet Rich-Text-Inhalt (eine Zeichenkette oder einen Rich-Text-Wert)',
@@ -605,7 +630,6 @@ export const deMessages = {
605
630
  'cms.issueDetail.mountCollectionNotDeclared': 'die Sammlung „{collection}“ ist nicht deklariert',
606
631
  'cms.issueDetail.mountCollectionNotHierarchical': 'die Sammlung „{collection}“ hat keine `parent`-Selbstbeziehung und damit keine verschachtelten Pfade zum Zusammensetzen',
607
632
  'cms.issueDetail.mountsSamePath': 'Die Hierarchie-Einhängungen „{earlier}“ („{earlierPattern}“) und „{later}“ („{laterPattern}“) sind beide unter „{space}“ eingehängt',
608
- 'cms.issueDetail.mountsNested': 'Die Hierarchie-Einhängung „{later}“ („{laterPattern}“) liegt innerhalb von „{earlier}“ („{earlierPattern}“), das zuerst deklariert ist und ebenfalls „{space}“ vollständig beansprucht',
609
633
  // ─────────────────────────────────────────────────────────────────────────
610
634
  // CMS — details pane (slot + sidebar fields)
611
635
  // ─────────────────────────────────────────────────────────────────────────
@@ -656,6 +680,9 @@ export const deMessages = {
656
680
  'cms.version.textUnchanged': 'Text unverändert – Formatierung oder Leerzeichen unterscheiden sich.',
657
681
  'cms.version.restoreBlock': 'Diesen Block aus v{version} wiederherstellen',
658
682
  'cms.version.nestedContentChanged': 'Verschachtelter Inhalt geändert.',
683
+ // EC-593 — wo die Diff-Zeile einer verschachtelten Komponente sitzt.
684
+ 'cms.version.nestedInProse': 'im Text',
685
+ 'cms.version.nestedIn': 'in {path}',
659
686
  'cms.version.blockAdded': 'Block hinzugefügt.',
660
687
  'cms.version.blockRemoved': 'Block entfernt.',
661
688
  'cms.version.emptyValue': 'leer',
@@ -756,7 +783,17 @@ export const deMessages = {
756
783
  'cms.asset.altLabel': 'Alt:',
757
784
  'cms.asset.notSet': 'nicht gesetzt',
758
785
  'cms.asset.imageOptions.title': 'Bildoptionen',
759
- 'cms.asset.imageOptions.description': 'Fokuspunkt und Metadaten — für jede Verwendung dieses Bildes gemeinsam.',
786
+ 'cms.asset.imageOptions.description': 'Was zu dieser Verwendung des Bildes gehört, getrennt von dem, was überall für das Bild gilt.',
787
+ 'cms.asset.imageOptions.placementTitle': 'An dieser Stelle',
788
+ 'cms.asset.imageOptions.placementHint': 'Nur diese Verwendung des Bildes — eine andere Seite kann es anders zeigen.',
789
+ 'cms.asset.imageOptions.assetTitle': 'Über dieses Bild',
790
+ 'cms.asset.imageOptions.assetHint': 'Gilt überall, wo dieses Bild verwendet wird — eine Änderung hier ändert jede Verwendung.',
791
+ 'cms.asset.imageOptions.whereUsed': 'Wo es verwendet wird',
792
+ 'cms.asset.replaceFile': 'Datei ersetzen',
793
+ 'cms.asset.replacing': 'Wird ersetzt …',
794
+ 'cms.asset.replaceWrongKind': '„{name}“ nicht verwendet — der Ersatz muss {types} sein, wie die aktuelle Datei.',
795
+ 'cms.asset.replaceSaveFirst': 'Speichern oder verwerfen Sie zuerst die Änderungen an den Metadaten.',
796
+ 'cms.asset.replacedCheckFocal': 'Die Datei wurde ersetzt — prüfen Sie, ob der Fokuspunkt das Motiv noch erfasst.',
760
797
  'cms.asset.focalPoint': 'Fokuspunkt',
761
798
  'cms.asset.resetToCentre': 'Auf Mitte zurücksetzen',
762
799
  'cms.asset.focalPointHint': 'Klicken oder ziehen Sie auf das Bild, um den Fokuspunkt für den Zuschnitt festzulegen.',
@@ -1024,6 +1061,11 @@ export const deMessages = {
1024
1061
  'builder.clipboard.notAllowed': '{component} kann nicht in {destination} eingefügt werden — der Slot lässt es nicht zu.',
1025
1062
  'builder.clipboard.notAllowedByField': '{component} kann nicht auf oberster Ebene von „{field}“ eingefügt werden — das Feld lässt es nicht zu.',
1026
1063
  'builder.clipboard.issueComponent': 'Komponente',
1064
+ // EC-584 — eine abgelehnte Platzierung (der eine Platzierungsrichter im Builder-Reducer).
1065
+ 'builder.placement.notAllowedByField': '{component} ist auf oberster Ebene von „{field}“ nicht erlaubt — das Feld lässt es nicht zu. Nichts wurde geändert.',
1066
+ 'builder.placement.notAllowedInSlot': '{component} ist in {destination} nicht erlaubt — der Slot lässt es nicht zu. Nichts wurde geändert.',
1067
+ 'builder.placement.serverOnly': '{component} wird nur auf dem Server gerendert und kann nicht auf die Arbeitsfläche gesetzt werden. Nichts wurde geändert.',
1068
+ 'builder.placement.dismiss': 'Hinweis schließen',
1027
1069
  // EC-494 — Zwischenablage-Fehler aus der Engine plus der studio-seitige Einfüge-Diff.
1028
1070
  'builder.clipboard.error.corruptJson': 'Der Inhalt der Zwischenablage ist kein gültiges JSON: {detail}',
1029
1071
  'builder.clipboard.error.notEnvelope': 'Der Inhalt der Zwischenablage ist kein Elytra-Fragment-Umschlag.',
@@ -1061,7 +1103,6 @@ export const deMessages = {
1061
1103
  'builder.inspector.slots': 'Slots',
1062
1104
  'builder.inspector.slotsHint': 'Slot-Elemente in der Seitenstruktur hinzufügen oder umsortieren.',
1063
1105
  'builder.inspector.propertiesPane': 'Eigenschaften',
1064
- 'builder.inspector.invalidValue': 'Der Wert entspricht nicht dem Schema der Eigenschaft — die Komponente zeigt ihren eigenen Standard, bis er korrigiert ist.',
1065
1106
  'builder.inspector.invalidJson': 'Ungültiges JSON',
1066
1107
  'builder.layout.title': 'Layout',
1067
1108
  'builder.layout.direction': 'Richtung',
@@ -1106,7 +1147,11 @@ export const deMessages = {
1106
1147
  'builder.canvasTheme.stylesheetErrorNotice': 'Das Canvas-Stylesheet konnte nicht geladen werden — Blöcke werden ungestylt dargestellt. Prüfen Sie den canvas.css-Build des Studios.',
1107
1148
  'builder.canvasTheme.noThemeTokensNotice': 'Das Canvas-Stylesheet definiert keine Theme-Tokens. canvas.css sollte den Full-Tailwind-Theme-Einstieg der Site importieren (siehe docs/theming.md).',
1108
1149
  'builder.canvasTheme.noStylesheetNotice': 'Es ist kein Canvas-Stylesheet konfiguriert — Blöcke werden ungestylt dargestellt. Die App muss eines über setCanvasStylesheetUrl bereitstellen (das create-elytra-Template verdrahtet das).',
1109
- 'builder.composition.preservedNotice': 'Diese Komposition enthält {count} Textelement(e), die der Canvas noch nicht darstellen kann. Sie behalten ihre Position zwischen den Blöcken, bleiben von Canvas-Änderungen unberührt und werden normal veröffentlicht.',
1150
+ // EC-549 — ein Textabschnitt zwischen den Blöcken.
1151
+ 'builder.run.title': 'Text',
1152
+ 'builder.run.items': '{count} Element(e)',
1153
+ 'builder.run.hint': 'Geschriebener Text zwischen den Blöcken — hier bearbeiten; er behält seinen Platz zwischen den Blöcken und wird so veröffentlicht, wie er hier steht.',
1154
+ 'builder.run.delete': 'Text löschen',
1110
1155
  'builder.spacing.gapAfterChild': 'Abstand nach Element {index}',
1111
1156
  'builder.richText.toolbarLabel': 'Textformatierung',
1112
1157
  'builder.richText.linkPlaceholder': 'https://… (leer entfernt)',
@@ -1242,6 +1287,11 @@ export const deMessages = {
1242
1287
  'ai.translate.proposalTitle': 'Übersetzung von {collection}',
1243
1288
  'ai.translate.diffBefore': 'Aktuell',
1244
1289
  'ai.translate.diffAfter': 'Übersetzt',
1290
+ // EC-593 — was ein Übersetzungslauf nicht übersetzen konnte, wird gemeldet, nie stillschweigend übersprungen.
1291
+ 'ai.translate.skippedTitle': 'Nicht übersetzt ({count})',
1292
+ 'ai.translate.skipped.unreadable': 'der gespeicherte Wert konnte nicht gelesen werden',
1293
+ 'ai.translate.skipped.notReturned': 'der Assistent hat dafür keinen Text geliefert',
1294
+ 'ai.translate.skipped.structureChanged': 'die Übersetzung hat Formatierungen, Links oder eingebettete Elemente verloren — das Original wurde beibehalten',
1245
1295
  // EC-378 — the update (edit-existing) proposal
1246
1296
  'ai.update.proposalTitle': 'Änderungen an {collection}',
1247
1297
  'ai.update.diffBefore': 'Aktuell',
@@ -1255,7 +1305,14 @@ export const deMessages = {
1255
1305
  // EC-586 / EC-581 (wave-0092, zone A) — canonical values and the per-FieldType definition
1256
1306
  'cms.issueMessage.nonCanonicalValue': 'Das Feld „{field}“ ist in einer nicht kanonischen Form für ein Feld vom Typ „{type}“ gespeichert — es wurde am Schreibweg vorbei geschrieben, der je Feldtyp genau eine Form speichert. Es wird weiterhin korrekt gelesen; erneutes Speichern des Dokuments legt die kanonische Form ab.',
1257
1307
  'cms.issueMessage.assetHoldsUrl': 'Das Asset-Feld „{field}“ enthält eine URL — ein Asset-Wert ist ein Asset aus der Mediathek, nie eine URL. Laden Sie das Bild in die Mediathek hoch und wählen Sie es aus, oder geben Sie einer Komponente für ein externes Bild eine feste src.',
1308
+ // EC-584 — die AllowList-Prüfung (Konfiguration / Registrierung).
1309
+ 'cms.issueMessage.emptyAllow': 'Das Feld „{field}“ deklariert ein leeres „allow“ — eine Liste erlaubt genau die genannten IDs, eine leere also nichts. Lassen Sie „allow“ weg für die Standard-Auswahl des Feldtyps, oder nennen Sie die Komponenten-IDs.',
1310
+ 'cms.issueMessage.unknownAllowComponent': 'Das Feld „{field}“ erlaubt „{component}“, das keine Komponente dieses Projekts ist — prüfen Sie die ID oder entfernen Sie sie aus „allow“.',
1258
1311
  'cms.issueMessage.blocksNotTopLevel': 'Das Feld „{field}“ ist ein „blocks“-Feld innerhalb eines Objekts — ein blocks-Feld steht nur auf oberster Ebene (eine Arbeitsfläche in einer Wiederholung bricht das Formular, die Validierungspfade und den Auslieferungstyp). Legen Sie ein weiteres blocks-Feld auf oberster Ebene an oder geben Sie einer Komponente eine blocks-Eigenschaft (einen Slot).',
1312
+ 'cms.issueMessage.placementKeyReserved': 'Das Asset-Feld „{asset}“ deklariert das platzierungsbezogene Unterfeld „{field}“, einen Namen, der dem Asset selbst gehört — es käme nie bei einer Komponente an (das „{field}“ des Assets gewinnt). Benennen Sie es um; Alternativtext, Bildnachweis oder KI-Kennzeichnung eines Bildes gehören in die Asset-Sammlung.',
1313
+ 'cms.issueMessage.placementNotScalar': 'Das Asset-Feld „{asset}“ deklariert das platzierungsbezogene Unterfeld „{field}“ vom Typ „{type}“ — eine Platzierung enthält Angaben zu dieser Verwendung des Bildes (eine Bildunterschrift, ein Layout), niemals eine Referenz oder Rich Text.',
1314
+ 'cms.issueMessage.sourceNotOnAssetCollection': 'Das Feld „{field}“ deklariert die Quelle „{source}“ — eine Quelle für eingebettete Metadaten füllt nur ein Feld der obersten Ebene der Asset-Sammlung.',
1315
+ 'cms.issueMessage.sourceTypeMismatch': 'Das Feld „{field}“ deklariert die Quelle „{source}“, die kein Feld vom Typ „{type}“ füllen kann (jede Quelle füllt Text; iptc:digitalSourceType auch einen Wahrheitswert).',
1259
1316
  'cms.issueMessage.uniqueNotTopLevel': 'Das Unterfeld „{field}“ deklariert „unique“ — Eindeutigkeit wird über die Dokumente einer Sammlung hinweg beurteilt und gilt daher nur für Felder auf oberster Ebene.',
1260
1317
  'cms.issueMessage.localizedOnProp': 'Die Komponenten-Eigenschaft „{field}“ deklariert „localized“ — ein sprachabhängiger Wert wird je Sprache im Dokument gespeichert, daher kann nur ein Dokumentfeld auf oberster Ebene sprachabhängig sein (machen Sie das Feld sprachabhängig, das die Komposition enthält).',
1261
1318
  'cms.issueMessage.localizedOnSubField': 'Das Unterfeld „{field}“ deklariert „localized“ — ein sprachabhängiger Wert wird je Sprache im Dokument gespeichert, daher kann nur ein Feld auf oberster Ebene sprachabhängig sein (machen Sie das Feld sprachabhängig, das es enthält).',