@ecsplico/editor 3.6.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (578) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/adapters/direct-ai.d.ts +4 -4
  3. package/dist/adapters/direct-ai.js +2 -2
  4. package/dist/adapters/direct-ai.js.map +1 -1
  5. package/dist/adapters/document-state.d.ts +1 -1
  6. package/dist/adapters/document-state.js.map +1 -1
  7. package/dist/adapters/forge.d.ts +2 -2
  8. package/dist/adapters/forge.js.map +1 -1
  9. package/dist/adapters/nitro.d.ts +11 -3
  10. package/dist/adapters/nitro.js +3 -1
  11. package/dist/adapters/nitro.js.map +1 -1
  12. package/dist/adapters/shared.d.ts +6 -6
  13. package/dist/adapters/shared.js.map +1 -1
  14. package/dist/config/index.d.ts +14 -14
  15. package/dist/config/index.js +8 -8
  16. package/dist/config/index.js.map +1 -1
  17. package/dist/core/backend-types.d.ts +3 -3
  18. package/dist/core/backend-types.js.map +1 -1
  19. package/dist/core/backend.d.ts +4 -4
  20. package/dist/core/backend.js.map +1 -1
  21. package/dist/core/block-convert.d.ts +1 -1
  22. package/dist/core/block-convert.js +5 -5
  23. package/dist/core/block-convert.js.map +1 -1
  24. package/dist/core/block-transfer.d.ts +8 -4
  25. package/dist/core/block-transfer.js +23 -14
  26. package/dist/core/block-transfer.js.map +1 -1
  27. package/dist/core/capabilities.d.ts +113 -0
  28. package/dist/core/capabilities.js +155 -0
  29. package/dist/core/capabilities.js.map +1 -0
  30. package/dist/core/command-types.d.ts +1 -1
  31. package/dist/core/context.js.map +1 -1
  32. package/dist/core/convert.d.ts +5 -5
  33. package/dist/core/convert.js +5 -5
  34. package/dist/core/convert.js.map +1 -1
  35. package/dist/core/core-feature.js +1 -1
  36. package/dist/core/define-editor.d.ts +2 -2
  37. package/dist/core/define-editor.js +34 -6
  38. package/dist/core/define-editor.js.map +1 -1
  39. package/dist/core/derivation.d.ts +119 -0
  40. package/dist/core/derivation.js +187 -0
  41. package/dist/core/derivation.js.map +1 -0
  42. package/dist/core/derive.d.ts +61 -0
  43. package/dist/core/derive.js +69 -0
  44. package/dist/core/derive.js.map +1 -0
  45. package/dist/core/extension-points.d.ts +16 -0
  46. package/dist/core/feature-context.d.ts +5 -1
  47. package/dist/core/feature-context.js +10 -3
  48. package/dist/core/feature-context.js.map +1 -1
  49. package/dist/core/feature.d.ts +41 -19
  50. package/dist/core/feature.js.map +1 -1
  51. package/dist/core/fences.js +1 -1
  52. package/dist/core/fences.js.map +1 -1
  53. package/dist/core/index.d.ts +6 -2
  54. package/dist/core/index.js +5 -1
  55. package/dist/core/pipeline.d.ts +14 -5
  56. package/dist/core/pipeline.js +19 -7
  57. package/dist/core/pipeline.js.map +1 -1
  58. package/dist/core/properties.d.ts +22 -0
  59. package/dist/core/properties.js +17 -0
  60. package/dist/core/properties.js.map +1 -0
  61. package/dist/core/schema/block-ids.js +5 -5
  62. package/dist/core/schema/block-ids.js.map +1 -1
  63. package/dist/core/schema/index.d.ts +2 -1
  64. package/dist/core/schema/index.js +2 -1
  65. package/dist/core/schema/index.js.map +1 -1
  66. package/dist/core/schema/opaque-fence.d.ts +4 -4
  67. package/dist/core/schema/opaque-fence.js +7 -7
  68. package/dist/core/schema/opaque-fence.js.map +1 -1
  69. package/dist/core/schema/persisted-names.d.ts +58 -0
  70. package/dist/core/schema/persisted-names.js +96 -0
  71. package/dist/core/schema/persisted-names.js.map +1 -0
  72. package/dist/core/toolbar-items.js +1 -1
  73. package/dist/core/toolbar-items.js.map +1 -1
  74. package/dist/core/util.js +5 -1
  75. package/dist/core/util.js.map +1 -1
  76. package/dist/features/ai/agent-ops.js +1 -1
  77. package/dist/features/ai/ai-review.js +1 -1
  78. package/dist/features/ai/assistant.d.ts +4 -4
  79. package/dist/features/ai/assistant.js +1 -1
  80. package/dist/features/ai/assistant.js.map +1 -1
  81. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  82. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
  83. package/dist/features/ai/components/ModelQuickSwitch.js +7 -7
  84. package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -1
  85. package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +4 -4
  86. package/dist/features/ai/components/SettingsAiTab.js +8 -8
  87. package/dist/features/ai/components/SettingsAiTab.js.map +1 -1
  88. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  89. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  90. package/dist/features/ai/document-chat-model.d.ts +2 -2
  91. package/dist/features/ai/document-chat-model.js +5 -5
  92. package/dist/features/ai/document-chat-model.js.map +1 -1
  93. package/dist/features/ai/document-chat.js +2 -2
  94. package/dist/features/ai/document-chat.js.map +1 -1
  95. package/dist/features/ai/feature.d.ts +2 -2
  96. package/dist/features/ai/feature.js +2 -2
  97. package/dist/features/ai/feature.js.map +1 -1
  98. package/dist/features/ai/ghost-autocomplete.d.ts +4 -4
  99. package/dist/features/ai/ghost-autocomplete.js +1 -1
  100. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  101. package/dist/features/ai/index.d.ts +3 -3
  102. package/dist/features/ai/index.js +2 -2
  103. package/dist/features/ai/model-profiles.d.ts +9 -9
  104. package/dist/features/ai/model-profiles.js +13 -13
  105. package/dist/features/ai/model-profiles.js.map +1 -1
  106. package/dist/features/ai/protocol/agent-ops.d.ts +2 -2
  107. package/dist/features/ai/protocol/agent-ops.js +1 -1
  108. package/dist/features/ai/protocol/agent-ops.js.map +1 -1
  109. package/dist/features/ai/protocol/block-lines.d.ts +7 -7
  110. package/dist/features/ai/protocol/block-lines.js +10 -10
  111. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  112. package/dist/features/ai/protocol/chat.d.ts +5 -5
  113. package/dist/features/ai/protocol/chat.js +12 -12
  114. package/dist/features/ai/protocol/chat.js.map +1 -1
  115. package/dist/features/ai/protocol/generate.d.ts +1 -1
  116. package/dist/features/ai/protocol/generate.js +1 -1
  117. package/dist/features/ai/protocol/generate.js.map +1 -1
  118. package/dist/features/ai/protocol/index.d.ts +3 -3
  119. package/dist/features/ai/protocol/index.js +2 -2
  120. package/dist/features/ai/protocol/models.d.ts +3 -3
  121. package/dist/features/ai/protocol/models.js +3 -3
  122. package/dist/features/ai/protocol/models.js.map +1 -1
  123. package/dist/features/ai/protocol/schemas.d.ts +1 -1
  124. package/dist/features/ai/protocol/schemas.js +1 -1
  125. package/dist/features/ai/protocol/schemas.js.map +1 -1
  126. package/dist/features/ai/protocol/tasks.d.ts +5 -5
  127. package/dist/features/ai/protocol/tasks.js +12 -12
  128. package/dist/features/ai/protocol/tasks.js.map +1 -1
  129. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
  130. package/dist/features/ai/react/ModelQuickSwitch.d.ts +4 -4
  131. package/dist/features/ai/react/ModelQuickSwitch.js +6 -6
  132. package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -1
  133. package/dist/features/ai/react/SettingsAiTab.d.ts +1 -1
  134. package/dist/features/ai/react/SettingsAiTab.js +8 -8
  135. package/dist/features/ai/react/SettingsAiTab.js.map +1 -1
  136. package/dist/features/ai/react/SettingsModelProfiles.d.ts +1 -1
  137. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  138. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  139. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  140. package/dist/features/ai/react/use-ai-assistant.js +1 -1
  141. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  142. package/dist/features/ai/react/use-model-profiles.d.ts +5 -5
  143. package/dist/features/ai/react/use-model-profiles.js +2 -2
  144. package/dist/features/ai/react/use-model-profiles.js.map +1 -1
  145. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  146. package/dist/features/ai/use-ai-assistant.js +1 -1
  147. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  148. package/dist/features/ai/use-model-profiles.d.ts +4 -4
  149. package/dist/features/ai/use-model-profiles.js +2 -2
  150. package/dist/features/ai/use-model-profiles.js.map +1 -1
  151. package/dist/features/block-ui/block-actions.d.ts +8 -4
  152. package/dist/features/block-ui/block-actions.js +38 -22
  153. package/dist/features/block-ui/block-actions.js.map +1 -1
  154. package/dist/features/block-ui/block-target.d.ts +29 -0
  155. package/dist/features/block-ui/block-target.js +63 -0
  156. package/dist/features/block-ui/block-target.js.map +1 -0
  157. package/dist/features/block-ui/components/BlockHandles.js +3 -3
  158. package/dist/features/block-ui/components/BlockHandles.js.map +1 -1
  159. package/dist/features/block-ui/components/BlockUi.js +11 -23
  160. package/dist/features/block-ui/components/BlockUi.js.map +1 -1
  161. package/dist/features/block-ui/feature.d.ts +3 -2
  162. package/dist/features/block-ui/feature.js +3 -2
  163. package/dist/features/block-ui/feature.js.map +1 -1
  164. package/dist/features/block-ui/handle-geometry.js +12 -23
  165. package/dist/features/block-ui/handle-geometry.js.map +1 -1
  166. package/dist/features/block-ui/index.d.ts +2 -1
  167. package/dist/features/block-ui/index.js +2 -1
  168. package/dist/features/block-ui/react/BlockHandles.d.ts +1 -1
  169. package/dist/features/block-ui/react/BlockHandles.js +4 -4
  170. package/dist/features/block-ui/react/BlockHandles.js.map +1 -1
  171. package/dist/features/block-ui/react/BlockUi.js +13 -25
  172. package/dist/features/block-ui/react/BlockUi.js.map +1 -1
  173. package/dist/features/comments/model.js +1 -1
  174. package/dist/features/composites/accepted-children.d.ts +5 -4
  175. package/dist/features/composites/accepted-children.js +26 -26
  176. package/dist/features/composites/accepted-children.js.map +1 -1
  177. package/dist/features/composites/ai-brief.d.ts +4 -4
  178. package/dist/features/composites/ai-brief.js +14 -12
  179. package/dist/features/composites/ai-brief.js.map +1 -1
  180. package/dist/features/composites/anchors.d.ts +1 -1
  181. package/dist/features/composites/anchors.js.map +1 -1
  182. package/dist/features/composites/baskets.d.ts +1 -1
  183. package/dist/features/composites/baskets.js.map +1 -1
  184. package/dist/features/composites/bibliography-command.d.ts +1 -1
  185. package/dist/features/composites/bibliography-form.js +1 -1
  186. package/dist/features/composites/bibliography-form.js.map +1 -1
  187. package/dist/features/composites/bibliography.js.map +1 -1
  188. package/dist/features/composites/block-types-json.d.ts +38 -0
  189. package/dist/features/composites/block-types-json.js +122 -0
  190. package/dist/features/composites/block-types-json.js.map +1 -0
  191. package/dist/features/composites/block-types.d.ts +119 -0
  192. package/dist/features/composites/block-types.js +343 -0
  193. package/dist/features/composites/block-types.js.map +1 -0
  194. package/dist/features/composites/capabilities.js +109 -0
  195. package/dist/features/composites/capabilities.js.map +1 -0
  196. package/dist/features/composites/components/BibliographyEditor.js +5 -5
  197. package/dist/features/composites/components/BibliographyEditor.js.map +1 -1
  198. package/dist/features/composites/components/CompositeBasketMenu.js +2 -2
  199. package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -1
  200. package/dist/features/composites/components/CompositeChrome.js +18 -8
  201. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  202. package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
  203. package/dist/features/composites/components/CompositeDetails.js +11 -10
  204. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  205. package/dist/features/composites/components/CompositeDetails.vue.d.ts +1 -1
  206. package/dist/features/composites/components/CompositeFieldEditor.js +7 -7
  207. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  208. package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +5 -5
  209. package/dist/features/composites/components/CompositeInspector.js +136 -113
  210. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  211. package/dist/features/composites/components/CompositeInspector.vue.d.ts +3 -3
  212. package/dist/features/composites/components/CompositeStatusMenu.js +2 -2
  213. package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -1
  214. package/dist/features/composites/components/CompositeSummaryEditor.js +1 -1
  215. package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -1
  216. package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +1 -1
  217. package/dist/features/composites/components/CompositeTable.js +2 -2
  218. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  219. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  220. package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -1
  221. package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +1 -1
  222. package/dist/features/composites/components/CompositeView.js +19 -17
  223. package/dist/features/composites/components/CompositeView.js.map +1 -1
  224. package/dist/features/composites/components/DerivationBadge.js +15 -6
  225. package/dist/features/composites/components/DerivationBadge.js.map +1 -1
  226. package/dist/features/composites/components/FocusChip.js +1 -1
  227. package/dist/features/composites/components/FocusChip.js.map +1 -1
  228. package/dist/features/composites/components/FocusChip.vue.d.ts +1 -1
  229. package/dist/features/composites/components/ReferenceFieldSelect.js +3 -3
  230. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -1
  231. package/dist/features/composites/components/ReferenceView.js +1 -1
  232. package/dist/features/composites/components/ReferenceView.js.map +1 -1
  233. package/dist/features/composites/components/SettingsEditingTab.js +18 -18
  234. package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
  235. package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +3 -3
  236. package/dist/features/composites/convert.d.ts +4 -4
  237. package/dist/features/composites/convert.js +27 -28
  238. package/dist/features/composites/convert.js.map +1 -1
  239. package/dist/features/composites/derived-chrome.js +34 -28
  240. package/dist/features/composites/derived-chrome.js.map +1 -1
  241. package/dist/features/composites/editing-settings-keys.js +6 -6
  242. package/dist/features/composites/editing-settings-keys.js.map +1 -1
  243. package/dist/features/composites/feature.d.ts +10 -10
  244. package/dist/features/composites/feature.js +58 -46
  245. package/dist/features/composites/feature.js.map +1 -1
  246. package/dist/features/composites/fences.d.ts +3 -3
  247. package/dist/features/composites/fences.js +18 -13
  248. package/dist/features/composites/fences.js.map +1 -1
  249. package/dist/features/composites/focus.d.ts +10 -10
  250. package/dist/features/composites/focus.js +11 -11
  251. package/dist/features/composites/focus.js.map +1 -1
  252. package/dist/features/composites/heading-composites.d.ts +5 -5
  253. package/dist/features/composites/heading-composites.js +13 -16
  254. package/dist/features/composites/heading-composites.js.map +1 -1
  255. package/dist/features/composites/index.d.ts +20 -18
  256. package/dist/features/composites/index.js +9 -7
  257. package/dist/features/composites/inspector-model.d.ts +45 -22
  258. package/dist/features/composites/inspector-model.js +53 -14
  259. package/dist/features/composites/inspector-model.js.map +1 -1
  260. package/dist/features/composites/legacy.d.ts +5 -4
  261. package/dist/features/composites/legacy.js +16 -23
  262. package/dist/features/composites/legacy.js.map +1 -1
  263. package/dist/features/composites/markdown-export.js +3 -3
  264. package/dist/features/composites/markdown-export.js.map +1 -1
  265. package/dist/features/composites/merge.d.ts +8 -8
  266. package/dist/features/composites/merge.js +23 -22
  267. package/dist/features/composites/merge.js.map +1 -1
  268. package/dist/features/composites/meta-display.js +5 -5
  269. package/dist/features/composites/meta-display.js.map +1 -1
  270. package/dist/features/composites/meta-keys.d.ts +5 -5
  271. package/dist/features/composites/meta-keys.js +8 -8
  272. package/dist/features/composites/meta-keys.js.map +1 -1
  273. package/dist/features/composites/node-views.d.ts +3 -3
  274. package/dist/features/composites/node-views.js +2 -2
  275. package/dist/features/composites/node-views.js.map +1 -1
  276. package/dist/features/composites/nodes.d.ts +13 -13
  277. package/dist/features/composites/nodes.js +100 -77
  278. package/dist/features/composites/nodes.js.map +1 -1
  279. package/dist/features/composites/outline.d.ts +15 -12
  280. package/dist/features/composites/outline.js +24 -21
  281. package/dist/features/composites/outline.js.map +1 -1
  282. package/dist/features/composites/properties.d.ts +51 -0
  283. package/dist/features/composites/properties.js +89 -0
  284. package/dist/features/composites/properties.js.map +1 -0
  285. package/dist/features/composites/react/BibliographyEditor.js +5 -5
  286. package/dist/features/composites/react/BibliographyEditor.js.map +1 -1
  287. package/dist/features/composites/react/CompositeBasketMenu.js +2 -2
  288. package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -1
  289. package/dist/features/composites/react/CompositeChrome.d.ts +1 -1
  290. package/dist/features/composites/react/CompositeChrome.js +15 -6
  291. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  292. package/dist/features/composites/react/CompositeDetails.d.ts +1 -1
  293. package/dist/features/composites/react/CompositeDetails.js +11 -10
  294. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  295. package/dist/features/composites/react/CompositeFieldEditor.d.ts +5 -5
  296. package/dist/features/composites/react/CompositeFieldEditor.js +6 -6
  297. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  298. package/dist/features/composites/react/CompositeInspector.d.ts +3 -3
  299. package/dist/features/composites/react/CompositeInspector.js +142 -128
  300. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  301. package/dist/features/composites/react/CompositeStatusMenu.js +2 -2
  302. package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -1
  303. package/dist/features/composites/react/CompositeSummaryEditor.d.ts +1 -1
  304. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  305. package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -1
  306. package/dist/features/composites/react/CompositeTable.d.ts +1 -1
  307. package/dist/features/composites/react/CompositeTable.js +3 -3
  308. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  309. package/dist/features/composites/react/CompositeTagsEditor.d.ts +1 -1
  310. package/dist/features/composites/react/CompositeTagsEditor.js +1 -1
  311. package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -1
  312. package/dist/features/composites/react/CompositeView.js +17 -15
  313. package/dist/features/composites/react/CompositeView.js.map +1 -1
  314. package/dist/features/composites/react/DerivationBadge.js +13 -4
  315. package/dist/features/composites/react/DerivationBadge.js.map +1 -1
  316. package/dist/features/composites/react/FocusChip.d.ts +1 -1
  317. package/dist/features/composites/react/FocusChip.js +1 -1
  318. package/dist/features/composites/react/FocusChip.js.map +1 -1
  319. package/dist/features/composites/react/ReferenceFieldSelect.js +3 -3
  320. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -1
  321. package/dist/features/composites/react/ReferenceView.d.ts +1 -1
  322. package/dist/features/composites/react/ReferenceView.js +1 -1
  323. package/dist/features/composites/react/ReferenceView.js.map +1 -1
  324. package/dist/features/composites/react/SettingsEditingTab.d.ts +4 -4
  325. package/dist/features/composites/react/SettingsEditingTab.js +20 -20
  326. package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
  327. package/dist/features/composites/react.js +1 -1
  328. package/dist/features/composites/react.js.map +1 -1
  329. package/dist/features/composites/reference-ai.js +1 -1
  330. package/dist/features/composites/reference-ai.js.map +1 -1
  331. package/dist/features/composites/reference-label.d.ts +3 -3
  332. package/dist/features/composites/reference-label.js +6 -6
  333. package/dist/features/composites/reference-label.js.map +1 -1
  334. package/dist/features/composites/reference-suggestion.d.ts +1 -1
  335. package/dist/features/composites/reference-suggestion.js +3 -3
  336. package/dist/features/composites/reference-suggestion.js.map +1 -1
  337. package/dist/features/composites/reference-text.d.ts +2 -2
  338. package/dist/features/composites/reference-text.js +4 -4
  339. package/dist/features/composites/reference-text.js.map +1 -1
  340. package/dist/features/composites/reference.d.ts +3 -3
  341. package/dist/features/composites/reference.js +1 -1
  342. package/dist/features/composites/reference.js.map +1 -1
  343. package/dist/features/composites/reveal-source.d.ts +3 -3
  344. package/dist/features/composites/reveal-source.js +6 -6
  345. package/dist/features/composites/reveal-source.js.map +1 -1
  346. package/dist/features/composites/rows-model.d.ts +2 -2
  347. package/dist/features/composites/rows-model.js +4 -3
  348. package/dist/features/composites/rows-model.js.map +1 -1
  349. package/dist/features/composites/rows.d.ts +16 -28
  350. package/dist/features/composites/rows.js +20 -54
  351. package/dist/features/composites/rows.js.map +1 -1
  352. package/dist/features/composites/schema.d.ts +12 -11
  353. package/dist/features/composites/schema.js +10 -9
  354. package/dist/features/composites/schema.js.map +1 -1
  355. package/dist/features/composites/source.d.ts +20 -0
  356. package/dist/features/composites/source.js +40 -0
  357. package/dist/features/composites/source.js.map +1 -0
  358. package/dist/features/composites/split.d.ts +11 -11
  359. package/dist/features/composites/split.js +20 -20
  360. package/dist/features/composites/split.js.map +1 -1
  361. package/dist/features/composites/structure-mode.js +3 -3
  362. package/dist/features/composites/structure-mode.js.map +1 -1
  363. package/dist/features/composites/table-model.js +2 -2
  364. package/dist/features/composites/table-model.js.map +1 -1
  365. package/dist/features/composites/use-composite-inspector.js +3 -1
  366. package/dist/features/composites/use-composite-inspector.js.map +1 -1
  367. package/dist/features/composites/use-composite-rows.d.ts +2 -2
  368. package/dist/features/composites/use-composite-rows.js.map +1 -1
  369. package/dist/features/composites/vue.js +1 -1
  370. package/dist/features/composites/vue.js.map +1 -1
  371. package/dist/features/image-studio/components/ImageStudioDialog.js +1 -1
  372. package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
  373. package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +1 -1
  374. package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
  375. package/dist/features/image-studio/feature.d.ts +1 -1
  376. package/dist/features/image-studio/feature.js +1 -1
  377. package/dist/features/image-studio/feature.js.map +1 -1
  378. package/dist/features/image-studio/node-core.d.ts +3 -3
  379. package/dist/features/image-studio/node-core.js +1 -1
  380. package/dist/features/image-studio/node-core.js.map +1 -1
  381. package/dist/features/image-studio/node-store.d.ts +1 -1
  382. package/dist/features/image-studio/node-store.js +1 -1
  383. package/dist/features/image-studio/node-store.js.map +1 -1
  384. package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
  385. package/dist/features/image-studio/react/ImageStudioDialog.js +1 -1
  386. package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
  387. package/dist/features/image-studio/react/ImageStudioView.js +1 -1
  388. package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
  389. package/dist/features/image-studio/react/store.d.ts +3 -3
  390. package/dist/features/image-studio/react/store.js.map +1 -1
  391. package/dist/features/image-studio/react.d.ts +1 -1
  392. package/dist/features/image-studio/react.js +1 -1
  393. package/dist/features/image-studio/react.js.map +1 -1
  394. package/dist/features/image-studio/schema.d.ts +1 -1
  395. package/dist/features/image-studio/schema.js +1 -1
  396. package/dist/features/image-studio/schema.js.map +1 -1
  397. package/dist/features/image-studio/state.d.ts +3 -3
  398. package/dist/features/image-studio/state.js +2 -2
  399. package/dist/features/image-studio/state.js.map +1 -1
  400. package/dist/features/image-studio/store.d.ts +3 -3
  401. package/dist/features/image-studio/vue.d.ts +1 -1
  402. package/dist/features/image-studio/vue.js +1 -1
  403. package/dist/features/image-studio/vue.js.map +1 -1
  404. package/dist/features/media/index.js +1 -1
  405. package/dist/features/media/react/AudioRecorderModal.js +1 -1
  406. package/dist/features/media/react.js +1 -1
  407. package/dist/features/mentions/tag.js +2 -1
  408. package/dist/features/mentions/tag.js.map +1 -1
  409. package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
  410. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  411. package/dist/features/themes/feature.d.ts +1 -1
  412. package/dist/features/themes/feature.js.map +1 -1
  413. package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
  414. package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
  415. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  416. package/dist/features/themes/theme-runtime.js +1 -1
  417. package/dist/features/themes/theme-runtime.js.map +1 -1
  418. package/dist/features/transcript/TranscriptSegmentView.js +1 -1
  419. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  420. package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
  421. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  422. package/dist/features/transcript/segment-node.js.map +1 -1
  423. package/dist/features/transfer/feature.js +1 -1
  424. package/dist/features/transfer/feature.js.map +1 -1
  425. package/dist/features/transfer/transforms.d.ts +4 -4
  426. package/dist/features/transfer/transforms.js +12 -12
  427. package/dist/features/transfer/transforms.js.map +1 -1
  428. package/dist/i18n/de/ai.js +4 -4
  429. package/dist/i18n/de/ai.js.map +1 -1
  430. package/dist/i18n/de/authors.js +1 -1
  431. package/dist/i18n/de/authors.js.map +1 -1
  432. package/dist/i18n/de/comments.js +1 -1
  433. package/dist/i18n/de/comments.js.map +1 -1
  434. package/dist/i18n/de/composites.js +62 -59
  435. package/dist/i18n/de/composites.js.map +1 -1
  436. package/dist/i18n/de/image-studio.js +2 -2
  437. package/dist/i18n/de/image-studio.js.map +1 -1
  438. package/dist/i18n/de/integrations.js +20 -20
  439. package/dist/i18n/de/integrations.js.map +1 -1
  440. package/dist/i18n/de/markdown.js +1 -1
  441. package/dist/i18n/de/markdown.js.map +1 -1
  442. package/dist/i18n/de/palette.js +1 -1
  443. package/dist/i18n/de/palette.js.map +1 -1
  444. package/dist/i18n/de/themes.js +3 -3
  445. package/dist/i18n/de/themes.js.map +1 -1
  446. package/dist/i18n/de/transfer.js +2 -2
  447. package/dist/i18n/de/transfer.js.map +1 -1
  448. package/dist/i18n/de/tree.js +6 -6
  449. package/dist/i18n/de/tree.js.map +1 -1
  450. package/dist/i18n/de.d.ts +101 -98
  451. package/dist/i18n/en/ai.js +2 -2
  452. package/dist/i18n/en/ai.js.map +1 -1
  453. package/dist/i18n/en/composites.js +64 -61
  454. package/dist/i18n/en/composites.js.map +1 -1
  455. package/dist/i18n/en/integrations.js +9 -9
  456. package/dist/i18n/en/integrations.js.map +1 -1
  457. package/dist/i18n/en/markdown.js +1 -1
  458. package/dist/i18n/en/markdown.js.map +1 -1
  459. package/dist/i18n/en/palette.js +1 -1
  460. package/dist/i18n/en/palette.js.map +1 -1
  461. package/dist/i18n/en/themes.js +4 -4
  462. package/dist/i18n/en/themes.js.map +1 -1
  463. package/dist/i18n/en/tree.js +5 -5
  464. package/dist/i18n/en/tree.js.map +1 -1
  465. package/dist/index.d.ts +4 -1
  466. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
  467. package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
  468. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
  469. package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
  470. package/dist/integrations/composite-ai/composite-fill.js +5 -5
  471. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  472. package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
  473. package/dist/integrations/composite-ai/composite-summary.js +5 -5
  474. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  475. package/dist/integrations/composite-ai/derive.d.ts +17 -17
  476. package/dist/integrations/composite-ai/derive.js +27 -26
  477. package/dist/integrations/composite-ai/derive.js.map +1 -1
  478. package/dist/integrations/composite-ai/index.d.ts +14 -15
  479. package/dist/integrations/composite-ai/index.js +98 -77
  480. package/dist/integrations/composite-ai/index.js.map +1 -1
  481. package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
  482. package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
  483. package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
  484. package/dist/integrations/composite-ai/rendition.d.ts +9 -9
  485. package/dist/integrations/composite-ai/rendition.js +27 -27
  486. package/dist/integrations/composite-ai/rendition.js.map +1 -1
  487. package/dist/integrations/composite-ai/split.d.ts +8 -8
  488. package/dist/integrations/composite-ai/split.js +12 -12
  489. package/dist/integrations/composite-ai/split.js.map +1 -1
  490. package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
  491. package/dist/integrations/composite-ai/transfer-transforms.js +12 -12
  492. package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
  493. package/dist/integrations/composite-media/index.d.ts +4 -3
  494. package/dist/integrations/composite-media/index.js +20 -14
  495. package/dist/integrations/composite-media/index.js.map +1 -1
  496. package/dist/markdown/document.js +1 -1
  497. package/dist/markdown/document.js.map +1 -1
  498. package/dist/nuxt/module.d.ts +30 -24
  499. package/dist/nuxt/module.js.map +1 -1
  500. package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
  501. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  502. package/dist/presets/compose.d.ts +6 -6
  503. package/dist/presets/compose.js +8 -5
  504. package/dist/presets/compose.js.map +1 -1
  505. package/dist/react/AdvancedEditor.d.ts +2 -2
  506. package/dist/react/AdvancedEditor.js +1 -1
  507. package/dist/react/AdvancedEditor.js.map +1 -1
  508. package/dist/react/compose.d.ts +1 -1
  509. package/dist/react/compose.js +2 -2
  510. package/dist/react/compose.js.map +1 -1
  511. package/dist/react/composed-surface.d.ts +5 -5
  512. package/dist/react/composed-surface.js +2 -2
  513. package/dist/react/composed-surface.js.map +1 -1
  514. package/dist/server/ai.d.ts +7 -7
  515. package/dist/server/ai.js +1 -1
  516. package/dist/server/ai.js.map +1 -1
  517. package/dist/server/auth.d.ts +1 -1
  518. package/dist/server/collab-state.d.ts +1 -1
  519. package/dist/server/collab-state.js.map +1 -1
  520. package/dist/server/collab.d.ts +4 -4
  521. package/dist/server/collab.js +22 -17
  522. package/dist/server/collab.js.map +1 -1
  523. package/dist/server/handlers/ai.d.ts +7 -4
  524. package/dist/server/handlers/ai.js +13 -17
  525. package/dist/server/handlers/ai.js.map +1 -1
  526. package/dist/server/handlers/context.d.ts +4 -0
  527. package/dist/server/handlers/context.js.map +1 -1
  528. package/dist/server/handlers/editor.d.ts +33 -32
  529. package/dist/server/handlers/editor.js +13 -8
  530. package/dist/server/handlers/editor.js.map +1 -1
  531. package/dist/server/handlers/images.d.ts +1 -1
  532. package/dist/server/handlers/images.js +1 -1
  533. package/dist/server/handlers/images.js.map +1 -1
  534. package/dist/server/host.d.ts +4 -0
  535. package/dist/server/host.js +10 -2
  536. package/dist/server/host.js.map +1 -1
  537. package/dist/server/index.d.ts +3 -3
  538. package/dist/server/ports.d.ts +8 -8
  539. package/dist/server/ports.js.map +1 -1
  540. package/dist/settings/index.d.ts +20 -19
  541. package/dist/settings/index.js +25 -24
  542. package/dist/settings/index.js.map +1 -1
  543. package/dist/settings/migrate.d.ts +18 -0
  544. package/dist/settings/migrate.js +46 -0
  545. package/dist/settings/migrate.js.map +1 -0
  546. package/dist/settings/values.d.ts +12 -12
  547. package/dist/settings/values.js +11 -11
  548. package/dist/settings/values.js.map +1 -1
  549. package/dist/styles/composites.css +10 -10
  550. package/dist/tree/DocumentTree.js +12 -5
  551. package/dist/tree/DocumentTree.js.map +1 -1
  552. package/dist/tree/drop-rules.js +5 -5
  553. package/dist/tree/drop-rules.js.map +1 -1
  554. package/dist/tree/react/DocumentTree.js +14 -5
  555. package/dist/tree/react/DocumentTree.js.map +1 -1
  556. package/dist/tree/tree-host.js +6 -6
  557. package/dist/tree/tree-host.js.map +1 -1
  558. package/dist/tree/tree-items.js +34 -28
  559. package/dist/tree/tree-items.js.map +1 -1
  560. package/dist/tree/tree-model.js +16 -16
  561. package/dist/tree/tree-model.js.map +1 -1
  562. package/dist/tree/tree-rows.js +2 -2
  563. package/dist/tree/tree-rows.js.map +1 -1
  564. package/dist/tree/tree-view-model.d.ts +2 -2
  565. package/dist/tree/tree-view-model.js.map +1 -1
  566. package/dist/vue/AdvancedEditor.js +2 -2
  567. package/dist/vue/AdvancedEditor.js.map +1 -1
  568. package/dist/vue/AdvancedEditor.vue.d.ts +6 -6
  569. package/dist/vue/compose.d.ts +1 -1
  570. package/dist/vue/compose.js +2 -2
  571. package/dist/vue/compose.js.map +1 -1
  572. package/package.json +1 -1
  573. package/dist/features/composites/derivation.d.ts +0 -62
  574. package/dist/features/composites/derivation.js +0 -102
  575. package/dist/features/composites/derivation.js.map +0 -1
  576. package/dist/features/composites/kinds.d.ts +0 -89
  577. package/dist/features/composites/kinds.js +0 -285
  578. package/dist/features/composites/kinds.js.map +0 -1
@@ -1,9 +1,13 @@
1
+ import { capabilitiesOf } from "../../core/capabilities.js";
1
2
  import { metadataOf } from "../../core/metadata.js";
3
+ import { derivationOf } from "../../core/derivation.js";
2
4
  import { createStore } from "../../core/store.js";
3
5
  import { METADATA_LIST } from "../../core/metadata-types.js";
4
6
  import { normalizeTagName } from "../../core/tag-name.js";
5
- import { compositeKindsOf } from "./nodes.js";
6
- import { compositeDescriptionText, compositeOfRow, setCompositeDescription, setCompositeMetaField, setCompositeTags } from "./rows.js";
7
+ import { blockTypesOf } from "./nodes.js";
8
+ import { blockPropertiesAt, setBlockProperty } from "./properties.js";
9
+ import { compositeDescriptionText, compositeOfRow, setCompositeDescription, setCompositeTags } from "./rows.js";
10
+ import { isSourceMissing } from "./derived-chrome.js";
7
11
  import { createCompositeRows } from "./rows-model.js";
8
12
  //#region src/features/composites/inspector-model.ts
9
13
  /** Pause after the last keystroke in the description before the draft is written to the document. */
@@ -23,14 +27,14 @@ function sameState(a, b) {
23
27
  return Object.keys(a).every((key) => Object.is(a[key], b[key]));
24
28
  }
25
29
  /**
26
- * The properties panel of `editor`: which section shows, the focused Baustein's description draft, the tag search
30
+ * The properties panel of `editor`: which section shows, the focused Typed block's description draft, the tag search
27
31
  * and the writes. Owns its own `createCompositeRows(editor)`, so a focus change and the rows it refers to arrive in
28
32
  * one notification and the draft is committed before it is replaced. Create one model per editor (a framework hook
29
33
  * follows an editor that changes); without an editor the state is empty and every action is ignored.
30
34
  *
31
35
  * Section: the caret is either in the host's input (`setDocumentFocused`) or in the editor. Only the editor's `focus`
32
36
  * event moves it to the editor (not `selectionUpdate`: collaboration sync moves the selection while the host's input
33
- * has the focus). A deleted Baustein falls back to the document section.
37
+ * has the focus). A deleted Typed block falls back to the document section.
34
38
  *
35
39
  * `isEditable` is not reactive: `editable` follows it on every editor `transaction`, so whoever calls `setEditable`
36
40
  * must dispatch a transaction afterwards (`EditorSurface` does).
@@ -38,8 +42,8 @@ function sameState(a, b) {
38
42
  * Description: edits go to a local `draft` so the asynchronous re-read of the document never overwrites the text
39
43
  * (and moves the caret) while it is typed. `base` is the document text the draft started from; the write lands only
40
44
  * while the description still holds it, so a peer's change made meanwhile is never replaced. The draft is committed
41
- * 600 ms after typing pauses, on blur, when another Baustein takes the focus (before the draft is replaced), when
42
- * the panel leaves the Baustein section, and on `dispose()`. A commit is skipped when the editor is destroyed or not
45
+ * 600 ms after typing pauses, on blur, when another Typed block takes the focus (before the draft is replaced), when
46
+ * the panel leaves the Typed block section, and on `dispose()`. A commit is skipped when the editor is destroyed or not
43
47
  * editable, and refused when a peer changed the description; the draft then reloads the live text. A draft that is
44
48
  * not dirty follows every change of the focused description.
45
49
  *
@@ -49,11 +53,12 @@ function sameState(a, b) {
49
53
  */
50
54
  function createCompositeInspector(editor) {
51
55
  const rows = createCompositeRows(editor);
52
- const kindOptions = editor && !editor.isDestroyed ? compositeKindsOf(editor).map((kind) => ({
56
+ const blockTypes = editor && !editor.isDestroyed ? blockTypesOf(editor) : [];
57
+ const kindOptions = blockTypes.map((kind) => ({
53
58
  value: kind.id,
54
59
  label: kind.label,
55
60
  icon: kind.icon
56
- })) : [];
61
+ }));
57
62
  const isEditable = () => !!editor && !editor.isDestroyed && editor.isEditable;
58
63
  const focusedRow = () => rows.state.get().focused;
59
64
  let disposed = false;
@@ -74,13 +79,45 @@ function createCompositeInspector(editor) {
74
79
  let pendingSearch;
75
80
  let summarizedRows;
76
81
  let summarized = 0;
82
+ let propertiesNode;
83
+ let propertiesRead = null;
84
+ let derivationRead = null;
77
85
  const store = createStore(snapshot());
86
+ /** The properties of the innermost block at the caret that carries property values; the `properties` capability decides which definitions it has. */
87
+ function propertiesAtCaret() {
88
+ if (!editor || editor.isDestroyed) return null;
89
+ const found = blockPropertiesAt(editor, editor.state.selection.$head, blockTypes);
90
+ if (!found) {
91
+ propertiesNode = void 0;
92
+ return propertiesRead = null;
93
+ }
94
+ if (found.node === propertiesNode && found.properties.pos === propertiesRead?.pos) return propertiesRead;
95
+ propertiesNode = found.node;
96
+ return propertiesRead = found.properties;
97
+ }
98
+ /**
99
+ * The Derivation of the block `propertiesAtCaret` found: shown for any block whose capabilities include `derived`, unless its Block type
100
+ * lists a `derivation` definition (then `properties.definitions` shows it). Kept while node and source-missing state are unchanged.
101
+ */
102
+ function derivationAtCaret(properties) {
103
+ const node = propertiesNode;
104
+ const derivation = editor && !editor.isDestroyed && node && properties && capabilitiesOf(editor, node).has("derived") ? derivationOf(node) : null;
105
+ if (!editor || !node || !derivation || properties?.definitions.some((definition) => definition.type === "derivation")) return derivationRead = null;
106
+ const sourceMissing = isSourceMissing(editor, node);
107
+ if (derivationRead?.node === node && derivationRead.sourceMissing === sourceMissing) return derivationRead;
108
+ return derivationRead = {
109
+ node,
110
+ derivation,
111
+ sourceMissing
112
+ };
113
+ }
78
114
  function snapshot() {
79
115
  const read = rows.state.get();
80
116
  if (read.rows !== summarizedRows) {
81
117
  summarizedRows = read.rows;
82
118
  summarized = read.rows.filter((row) => row.description.trim()).length;
83
119
  }
120
+ const properties = propertiesAtCaret();
84
121
  return {
85
122
  ...read,
86
123
  section,
@@ -90,7 +127,9 @@ function createCompositeInspector(editor) {
90
127
  tagItems,
91
128
  kindOptions,
92
129
  summarized,
93
- hasSummary: !!read.focused?.description.trim()
130
+ hasSummary: !!read.focused?.description.trim(),
131
+ properties,
132
+ derivation: derivationAtCaret(properties)
94
133
  };
95
134
  }
96
135
  function publish() {
@@ -116,8 +155,8 @@ function createCompositeInspector(editor) {
116
155
  return true;
117
156
  }
118
157
  function refreshSection() {
119
- const next = caretIn === "editor" && focusedRow() ? "composite" : "document";
120
- if (section === "composite" && next !== "composite") commit();
158
+ const next = caretIn === "editor" && focusedRow() ? "typedBlock" : "document";
159
+ if (section === "typedBlock" && next !== "typedBlock") commit();
121
160
  section = next;
122
161
  }
123
162
  function followRows() {
@@ -198,7 +237,7 @@ function createCompositeInspector(editor) {
198
237
  const row = focusedRow();
199
238
  const found = editor && row ? compositeOfRow(editor.state.doc, row) : null;
200
239
  if (!editor || !found || kind === row?.kind) return;
201
- editor.commands.setCompositeKind(kind, found.pos);
240
+ editor.commands.changeBlockType(kind, found.pos);
202
241
  },
203
242
  inputDescription(text) {
204
243
  if (disposed) return;
@@ -235,8 +274,8 @@ function createCompositeInspector(editor) {
235
274
  if (!disposed) scheduleSearch();
236
275
  },
237
276
  setField(field, value) {
238
- const row = focusedRow();
239
- if (editor && isEditable() && row && !isStructuredField(field)) setCompositeMetaField(editor, row, field.key, value);
277
+ const target = store.get().properties;
278
+ if (editor && isEditable() && target && !isStructuredField(field)) setBlockProperty(editor, target, field.key, value);
240
279
  },
241
280
  dispose() {
242
281
  if (disposed) return;
@@ -1 +1 @@
1
- {"version":3,"file":"inspector-model.js","names":[],"sources":["../../../src/features/composites/inspector-model.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { metadataOf } from '../../core/metadata'\nimport { METADATA_LIST } from '../../core/metadata-types'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport { normalizeTagName } from '../../core/tag-name'\nimport type { CompositeMetaField } from './kinds'\nimport { compositeKindsOf } from './nodes'\nimport { compositeDescriptionText, compositeOfRow, type CompositeRow, setCompositeDescription, setCompositeMetaField, setCompositeTags } from './rows'\nimport { type CompositeRowsState, createCompositeRows } from './rows-model'\n\n/** Pause after the last keystroke in the description before the draft is written to the document. */\nconst COMMIT_DELAY_MS = 600\n/** Pause after the last change of the tag search term before the metadata source is asked. */\nconst SEARCH_DELAY_MS = 150\n\n/** Which section of the inspector shows: the document's facts, or the properties of the Baustein under the caret. */\nexport type CompositeInspectorSection = 'document' | 'composite'\n\n/** A kind the Baustein can be switched to, as the kind select lists it. */\nexport interface CompositeKindOption {\n value: string\n label: string\n icon: string\n}\n\n/** Everything the inspector renders (see `createCompositeInspector`): the live rows plus the inspector's own state. */\nexport interface CompositeInspectorState extends CompositeRowsState {\n /** `'composite'` only while the caret last was in the editor and a Baustein is focused, else `'document'`. */\n section: CompositeInspectorSection\n /** Whether the editor accepts edits; follows `setEditable` (see `createCompositeInspector`). */\n editable: boolean\n /** The description text as the user sees it in the focused Baustein's textarea; may be ahead of the document until it is committed. */\n draft: string\n /** The text typed into the tag search. */\n searchTerm: string\n /** Tag names the metadata source suggested for the latest search that was not superseded. */\n tagItems: string[]\n /** The kinds the editor was configured with, in configured order; empty without an editor. */\n kindOptions: CompositeKindOption[]\n /** How many Bausteine have a description. */\n summarized: number\n /** The focused Baustein has a description. */\n hasSummary: boolean\n}\n\n/** The inspector model of an editor: observable `state`, the actions of the panel, and `dispose()`. */\nexport interface CompositeInspector {\n readonly state: ReadonlyStore<CompositeInspectorState>\n /**\n * Reports whether the host's document-level input (its title field) has the focus. Only the rising edge switches\n * the section to `'document'`; the section stays there after the input blurs, until the editor takes the focus.\n */\n setDocumentFocused: (focused: boolean) => void\n /** Switches the focused Baustein to `kind`; ignored when it already has it or no Baustein is focused. */\n setKind: (kind: string) => void\n /**\n * The user typed `text` into the description. The draft is written to the document 600 ms after the last call and\n * on `blurDescription`; the Baustein focused at the first call of a pending draft is the one that is written.\n */\n inputDescription: (text: string) => void\n /**\n * The description textarea lost focus: writes the pending draft, then shows what the document holds. The draft\n * shows the normalized text after a write; after a refused write (a peer changed the description since the draft\n * started) it shows the peer's text.\n */\n blurDescription: () => void\n /** Replaces the tags of the focused Baustein. */\n setTags: (tags: readonly string[]) => void\n /** Creates the tag `raw` (normalized) on the focused Baustein and registers it with the metadata source; clears the search term. */\n createTag: (raw: string) => void\n /** Sets the tag search term and schedules a search for it. */\n setSearchTerm: (term: string) => void\n /** Schedules a tag search for the current term (the picker opened). */\n searchTags: () => void\n /**\n * Writes the text `value` to the meta `field` of the focused Baustein; ignored while the editor is not editable and\n * for structured fields (`isStructuredField`), whose values are not text and have their own editors.\n */\n setField: (field: CompositeMetaField, value: unknown) => void\n /** Commits a pending description draft and detaches from the editor. Idempotent; the model is inert afterwards. */\n dispose: () => void\n}\n\n/** Fields whose value is not text (a block ID, a CSL item, a provenance record, a list of choices): the inspector hands them to `CompositeFieldEditor` and never writes a string to them. */\nexport function isStructuredField(field: Pick<CompositeMetaField, 'type'>): boolean {\n return field.type === 'reference' || field.type === 'bibliography' || field.type === 'derivation' || field.type === 'multiselect'\n}\n\n/** The text of `field` in `row`'s meta as an input shows it: strings and numbers, otherwise empty. */\nexport function compositeFieldText(row: Pick<CompositeRow, 'meta'> | null, field: Pick<CompositeMetaField, 'key'>): string {\n const value = row?.meta[field.key]\n return typeof value === 'string' || typeof value === 'number' ? String(value) : ''\n}\n\nfunction sameState(a: CompositeInspectorState, b: CompositeInspectorState): boolean {\n return (Object.keys(a) as (keyof CompositeInspectorState)[]).every(key => Object.is(a[key], b[key]))\n}\n\n/**\n * The properties panel of `editor`: which section shows, the focused Baustein's description draft, the tag search\n * and the writes. Owns its own `createCompositeRows(editor)`, so a focus change and the rows it refers to arrive in\n * one notification and the draft is committed before it is replaced. Create one model per editor (a framework hook\n * follows an editor that changes); without an editor the state is empty and every action is ignored.\n *\n * Section: the caret is either in the host's input (`setDocumentFocused`) or in the editor. Only the editor's `focus`\n * event moves it to the editor (not `selectionUpdate`: collaboration sync moves the selection while the host's input\n * has the focus). A deleted Baustein falls back to the document section.\n *\n * `isEditable` is not reactive: `editable` follows it on every editor `transaction`, so whoever calls `setEditable`\n * must dispatch a transaction afterwards (`EditorSurface` does).\n *\n * Description: edits go to a local `draft` so the asynchronous re-read of the document never overwrites the text\n * (and moves the caret) while it is typed. `base` is the document text the draft started from; the write lands only\n * while the description still holds it, so a peer's change made meanwhile is never replaced. The draft is committed\n * 600 ms after typing pauses, on blur, when another Baustein takes the focus (before the draft is replaced), when\n * the panel leaves the Baustein section, and on `dispose()`. A commit is skipped when the editor is destroyed or not\n * editable, and refused when a peer changed the description; the draft then reloads the live text. A draft that is\n * not dirty follows every change of the focused description.\n *\n * Tag search: each term change restarts a 150 ms debounce; when it fires, the previous search is aborted and the\n * source is asked with a fresh `AbortSignal`. Results of aborted searches are dropped, errors of aborted searches\n * are silent, other errors are logged and keep the old suggestions.\n */\nexport function createCompositeInspector(editor: Editor | undefined): CompositeInspector {\n const rows = createCompositeRows(editor)\n const kindOptions: CompositeKindOption[] = editor && !editor.isDestroyed\n ? compositeKindsOf(editor).map(kind => ({ value: kind.id, label: kind.label, icon: kind.icon }))\n : []\n const isEditable = () => !!editor && !editor.isDestroyed && editor.isEditable\n const focusedRow = () => rows.state.get().focused\n\n let disposed = false\n\n // --- Section ---\n let caretIn: 'document' | 'editor' = 'document'\n let documentFocused = false\n let section: CompositeInspectorSection = 'document'\n let editable = isEditable()\n\n // --- Description draft ---\n let draft = ''\n let base = ''\n let dirty = false\n let pendingRow: CompositeRow | null = null\n let commitTimer: ReturnType<typeof setTimeout> | undefined\n // What the focused Baustein looked like at the last rows notification: a change of the key is a switch, a change of the description a peer's (or our own) write.\n let focusKey = focusedRow()?.key\n let focusDescription = focusedRow()?.description\n\n // --- Tag search ---\n let searchTerm = ''\n let tagItems: string[] = []\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n let pendingSearch: AbortController | undefined\n\n let summarizedRows: CompositeRowsState['rows'] | undefined\n let summarized = 0\n const store = createStore<CompositeInspectorState>(snapshot())\n\n function snapshot(): CompositeInspectorState {\n const read = rows.state.get()\n if (read.rows !== summarizedRows) {\n summarizedRows = read.rows\n summarized = read.rows.filter(row => row.description.trim()).length\n }\n return {\n ...read,\n section,\n editable,\n draft,\n searchTerm,\n tagItems,\n kindOptions,\n summarized,\n hasSummary: !!read.focused?.description.trim(),\n }\n }\n\n function publish() {\n const next = snapshot()\n if (!sameState(store.get(), next)) store.set(next)\n }\n\n function load() {\n dirty = false\n pendingRow = null\n base = focusedRow()?.description ?? ''\n draft = base\n }\n\n /** Writes the pending draft; `false` when there was nothing to write or the write was refused. Does not touch `draft`. */\n function commit(): boolean {\n clearTimeout(commitTimer)\n if (!dirty || !pendingRow) return false\n dirty = false\n const row = pendingRow\n pendingRow = null\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\n if (!setCompositeDescription(editor, row, draft, base)) return false\n base = compositeDescriptionText(draft)\n return true\n }\n\n function refreshSection() {\n const next = caretIn === 'editor' && focusedRow() ? 'composite' : 'document'\n if (section === 'composite' && next !== 'composite') commit()\n section = next\n }\n\n // A switch commits the draft of the previous Baustein first (`draft` still holds its text), then loads the new one.\n // A change of the description reaches an untouched draft, also while the textarea has focus. A draft with unsaved\n // typing keeps it; if a peer changed the description meanwhile, its commit is refused and the peer's text is\n // loaded instead.\n function followRows() {\n const focused = focusedRow()\n const key = focused?.key\n const description = focused?.description\n if (key !== focusKey) {\n focusKey = key\n focusDescription = description\n commit()\n load()\n }\n else if (description !== focusDescription) {\n focusDescription = description\n if (dirty) {\n if (description !== base && !commit()) load()\n }\n else if (description !== base) {\n load()\n }\n }\n refreshSection()\n publish()\n }\n\n function scheduleSearch() {\n clearTimeout(searchTimer)\n searchTimer = setTimeout(async () => {\n if (!editor || editor.isDestroyed) return\n pendingSearch?.abort()\n const controller = pendingSearch = new AbortController()\n try {\n const found = await metadataOf(editor).search(METADATA_LIST.hashtags, { query: searchTerm, editor, signal: controller.signal })\n if (controller.signal.aborted) return\n tagItems = found.map(item => item.label)\n publish()\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Tag-Vorschläge konnten nicht geladen werden', error)\n }\n }, SEARCH_DELAY_MS)\n }\n\n function setSearchTerm(term: string) {\n if (disposed || term === searchTerm) return\n searchTerm = term\n scheduleSearch()\n publish()\n }\n\n const syncEditable = () => {\n const next = isEditable()\n if (next === editable) return\n editable = next\n publish()\n }\n const toEditor = () => {\n caretIn = 'editor'\n refreshSection()\n publish()\n }\n\n load()\n refreshSection()\n const stopRows = rows.state.subscribe(followRows)\n if (editor && !editor.isDestroyed) {\n editor.on('transaction', syncEditable)\n // Only `focus`, not `selectionUpdate`: a collaboration sync can move the selection while the host's input has the focus.\n editor.on('focus', toEditor)\n }\n publish()\n\n return {\n state: store,\n\n setDocumentFocused(focused) {\n if (disposed) return\n const rising = focused && !documentFocused\n documentFocused = focused\n if (!rising) return\n caretIn = 'document'\n refreshSection()\n publish()\n },\n\n setKind(kind) {\n const row = focusedRow()\n const found = editor && row ? compositeOfRow(editor.state.doc, row) : null\n if (!editor || !found || kind === row?.kind) return\n editor.commands.setCompositeKind(kind, found.pos)\n },\n\n inputDescription(text) {\n if (disposed) return\n draft = text\n dirty = true\n pendingRow ??= focusedRow()\n clearTimeout(commitTimer)\n commitTimer = setTimeout(commit, COMMIT_DELAY_MS)\n publish()\n },\n\n blurDescription() {\n if (disposed) return\n if (commit()) draft = base\n else load()\n publish()\n },\n\n setTags(tags) {\n const row = focusedRow()\n if (editor && row) setCompositeTags(editor, row, tags)\n },\n\n // „Neu Tag“ → `neu-tag`. A name that normalizes to an already chosen tag is ignored; only unknown names are reported as created.\n createTag(raw) {\n const row = focusedRow()\n const name = normalizeTagName(raw)\n setSearchTerm('')\n if (!editor || !row || !name || row.tags.includes(name)) return\n setCompositeTags(editor, row, [...row.tags, name])\n if (!tagItems.includes(name)) metadataOf(editor).create(METADATA_LIST.hashtags, { id: name, label: name })\n },\n\n setSearchTerm,\n\n searchTags() {\n if (!disposed) scheduleSearch()\n },\n\n setField(field, value) {\n const row = focusedRow()\n if (editor && isEditable() && row && !isStructuredField(field)) setCompositeMetaField(editor, row, field.key, value)\n },\n\n dispose() {\n if (disposed) return\n commit()\n disposed = true\n clearTimeout(searchTimer)\n pendingSearch?.abort()\n stopRows()\n if (editor && !editor.isDestroyed) {\n editor.off('transaction', syncEditable)\n editor.off('focus', toEditor)\n }\n rows.dispose()\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"inspector-model.js","names":[],"sources":["../../../src/features/composites/inspector-model.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { capabilitiesOf } from '../../core/capabilities'\nimport { type Derivation, derivationOf } from '../../core/derivation'\nimport { metadataOf } from '../../core/metadata'\nimport { METADATA_LIST } from '../../core/metadata-types'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport { normalizeTagName } from '../../core/tag-name'\nimport type { PropertyDefinition } from '../../core/properties'\nimport { blockTypesOf } from './nodes'\nimport { type BlockProperties, blockPropertiesAt, setBlockProperty } from './properties'\nimport { isSourceMissing } from './derived-chrome'\nimport { compositeDescriptionText, compositeOfRow, type CompositeRow, setCompositeDescription, setCompositeTags } from './rows'\nimport { type CompositeRowsState, createCompositeRows } from './rows-model'\n\n/** Pause after the last keystroke in the description before the draft is written to the document. */\nconst COMMIT_DELAY_MS = 600\n/** Pause after the last change of the tag search term before the metadata source is asked. */\nconst SEARCH_DELAY_MS = 150\n\n/** Which section of the inspector shows: the document's facts, or the properties of the Typed block under the caret. */\nexport type CompositeInspectorSection = 'document' | 'typedBlock'\n\n/** A Block type the Typed block can be switched to, as the Block type select lists it. */\nexport interface BlockTypeOption {\n value: string\n label: string\n icon: string\n}\n\n/**\n * The Derivation of the block the Block properties section shows, for a block whose capabilities include `derived`. `node` is\n * that block, for the origin display; `sourceMissing` is the source-missing state of its Derivation (the original was deleted).\n */\nexport interface InspectorDerivation {\n node: PMNode\n derivation: Derivation\n sourceMissing: boolean\n}\n\n/** Everything the inspector renders (see `createCompositeInspector`): the live rows plus the inspector's own state. */\nexport interface CompositeInspectorState extends CompositeRowsState {\n /** `'typedBlock'` only while the caret last was in the editor and a Typed block is focused, else `'document'`. */\n section: CompositeInspectorSection\n /** Whether the editor accepts edits; follows `setEditable` (see `createCompositeInspector`). */\n editable: boolean\n /** The description text as the user sees it in the focused Typed block's textarea; may be ahead of the document until it is committed. */\n draft: string\n /** The text typed into the tag search. */\n searchTerm: string\n /** Tag names the metadata source suggested for the latest search that was not superseded. */\n tagItems: string[]\n /** The kinds the editor was configured with, in configured order; empty without an editor. */\n kindOptions: BlockTypeOption[]\n /** How many Typed blocks have a description. */\n summarized: number\n /** The focused Typed block has a description. */\n hasSummary: boolean\n /**\n * The Block properties section: the properties of the innermost block around the caret that carries property values, with the\n * Property definitions its Block type declares through the `properties` capability (`definitions`; empty when it has none). Null\n * outside such a block. The section is shown for any block that has the capability, not for a particular node type.\n */\n properties: BlockProperties | null\n /**\n * The Derivation of the block in `properties` when it is a Derived block (its capabilities include `derived`), whatever its Block\n * type, with the source-missing state. Null for a block that is not Derived, and for one whose Block type lists a `derivation`\n * Property definition: that definition already shows it among `properties.definitions`, so it never shows twice.\n */\n derivation: InspectorDerivation | null\n}\n\n/** The inspector model of an editor: observable `state`, the actions of the panel, and `dispose()`. */\nexport interface CompositeInspector {\n readonly state: ReadonlyStore<CompositeInspectorState>\n /**\n * Reports whether the host's document-level input (its title field) has the focus. Only the rising edge switches\n * the section to `'document'`; the section stays there after the input blurs, until the editor takes the focus.\n */\n setDocumentFocused: (focused: boolean) => void\n /** Switches the focused Typed block to `kind`; ignored when it already has it or no Typed block is focused. */\n setKind: (kind: string) => void\n /**\n * The user typed `text` into the description. The draft is written to the document 600 ms after the last call and\n * on `blurDescription`; the Typed block focused at the first call of a pending draft is the one that is written.\n */\n inputDescription: (text: string) => void\n /**\n * The description textarea lost focus: writes the pending draft, then shows what the document holds. The draft\n * shows the normalized text after a write; after a refused write (a peer changed the description since the draft\n * started) it shows the peer's text.\n */\n blurDescription: () => void\n /** Replaces the tags of the focused Typed block. */\n setTags: (tags: readonly string[]) => void\n /** Creates the tag `raw` (normalized) on the focused Typed block and registers it with the metadata source; clears the search term. */\n createTag: (raw: string) => void\n /** Sets the tag search term and schedules a search for it. */\n setSearchTerm: (term: string) => void\n /** Schedules a tag search for the current term (the picker opened). */\n searchTags: () => void\n /**\n * Writes the text `value` to the property `field` of the block in `state.properties`; ignored while the editor is not editable and\n * for structured fields (`isStructuredField`), whose values are not text and have their own editors.\n */\n setField: (field: PropertyDefinition, value: unknown) => void\n /** Commits a pending description draft and detaches from the editor. Idempotent; the model is inert afterwards. */\n dispose: () => void\n}\n\n/** Fields whose value is not text (a block ID, a CSL item, a provenance record, a list of choices): the inspector hands them to `CompositeFieldEditor` and never writes a string to them. */\nexport function isStructuredField(field: Pick<PropertyDefinition, 'type'>): boolean {\n return field.type === 'reference' || field.type === 'bibliography' || field.type === 'derivation' || field.type === 'multiselect'\n}\n\n/** The text of `field` in `row`'s meta as an input shows it: strings and numbers, otherwise empty. */\nexport function compositeFieldText(row: Pick<BlockProperties, 'meta'> | null, field: Pick<PropertyDefinition, 'key'>): string {\n const value = row?.meta[field.key]\n return typeof value === 'string' || typeof value === 'number' ? String(value) : ''\n}\n\nfunction sameState(a: CompositeInspectorState, b: CompositeInspectorState): boolean {\n return (Object.keys(a) as (keyof CompositeInspectorState)[]).every(key => Object.is(a[key], b[key]))\n}\n\n/**\n * The properties panel of `editor`: which section shows, the focused Typed block's description draft, the tag search\n * and the writes. Owns its own `createCompositeRows(editor)`, so a focus change and the rows it refers to arrive in\n * one notification and the draft is committed before it is replaced. Create one model per editor (a framework hook\n * follows an editor that changes); without an editor the state is empty and every action is ignored.\n *\n * Section: the caret is either in the host's input (`setDocumentFocused`) or in the editor. Only the editor's `focus`\n * event moves it to the editor (not `selectionUpdate`: collaboration sync moves the selection while the host's input\n * has the focus). A deleted Typed block falls back to the document section.\n *\n * `isEditable` is not reactive: `editable` follows it on every editor `transaction`, so whoever calls `setEditable`\n * must dispatch a transaction afterwards (`EditorSurface` does).\n *\n * Description: edits go to a local `draft` so the asynchronous re-read of the document never overwrites the text\n * (and moves the caret) while it is typed. `base` is the document text the draft started from; the write lands only\n * while the description still holds it, so a peer's change made meanwhile is never replaced. The draft is committed\n * 600 ms after typing pauses, on blur, when another Typed block takes the focus (before the draft is replaced), when\n * the panel leaves the Typed block section, and on `dispose()`. A commit is skipped when the editor is destroyed or not\n * editable, and refused when a peer changed the description; the draft then reloads the live text. A draft that is\n * not dirty follows every change of the focused description.\n *\n * Tag search: each term change restarts a 150 ms debounce; when it fires, the previous search is aborted and the\n * source is asked with a fresh `AbortSignal`. Results of aborted searches are dropped, errors of aborted searches\n * are silent, other errors are logged and keep the old suggestions.\n */\nexport function createCompositeInspector(editor: Editor | undefined): CompositeInspector {\n const rows = createCompositeRows(editor)\n const blockTypes = editor && !editor.isDestroyed ? blockTypesOf(editor) : []\n const kindOptions: BlockTypeOption[] = blockTypes.map(kind => ({ value: kind.id, label: kind.label, icon: kind.icon }))\n const isEditable = () => !!editor && !editor.isDestroyed && editor.isEditable\n const focusedRow = () => rows.state.get().focused\n\n let disposed = false\n\n // --- Section ---\n let caretIn: 'document' | 'editor' = 'document'\n let documentFocused = false\n let section: CompositeInspectorSection = 'document'\n let editable = isEditable()\n\n // --- Description draft ---\n let draft = ''\n let base = ''\n let dirty = false\n let pendingRow: CompositeRow | null = null\n let commitTimer: ReturnType<typeof setTimeout> | undefined\n // What the focused Typed block looked like at the last rows notification: a change of the key is a switch, a change of the description a peer's (or our own) write.\n let focusKey = focusedRow()?.key\n let focusDescription = focusedRow()?.description\n\n // --- Tag search ---\n let searchTerm = ''\n let tagItems: string[] = []\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n let pendingSearch: AbortController | undefined\n\n let summarizedRows: CompositeRowsState['rows'] | undefined\n let summarized = 0\n // The Block properties at the caret, kept while the block's node and position are unchanged so an unrelated notification does not republish.\n let propertiesNode: PMNode | undefined\n let propertiesRead: BlockProperties | null = null\n let derivationRead: InspectorDerivation | null = null\n const store = createStore<CompositeInspectorState>(snapshot())\n\n /** The properties of the innermost block at the caret that carries property values; the `properties` capability decides which definitions it has. */\n function propertiesAtCaret(): BlockProperties | null {\n if (!editor || editor.isDestroyed) return null\n const found = blockPropertiesAt(editor, editor.state.selection.$head, blockTypes)\n if (!found) {\n propertiesNode = undefined\n return propertiesRead = null\n }\n if (found.node === propertiesNode && found.properties.pos === propertiesRead?.pos) return propertiesRead\n propertiesNode = found.node\n return propertiesRead = found.properties\n }\n\n /**\n * The Derivation of the block `propertiesAtCaret` found: shown for any block whose capabilities include `derived`, unless its Block type\n * lists a `derivation` definition (then `properties.definitions` shows it). Kept while node and source-missing state are unchanged.\n */\n function derivationAtCaret(properties: BlockProperties | null): InspectorDerivation | null {\n const node = propertiesNode\n const derivation = editor && !editor.isDestroyed && node && properties && capabilitiesOf(editor, node).has('derived') ? derivationOf(node) : null\n if (!editor || !node || !derivation || properties?.definitions.some(definition => definition.type === 'derivation')) return derivationRead = null\n const sourceMissing = isSourceMissing(editor, node)\n if (derivationRead?.node === node && derivationRead.sourceMissing === sourceMissing) return derivationRead\n return derivationRead = { node, derivation, sourceMissing }\n }\n\n function snapshot(): CompositeInspectorState {\n const read = rows.state.get()\n if (read.rows !== summarizedRows) {\n summarizedRows = read.rows\n summarized = read.rows.filter(row => row.description.trim()).length\n }\n const properties = propertiesAtCaret()\n return {\n ...read,\n section,\n editable,\n draft,\n searchTerm,\n tagItems,\n kindOptions,\n summarized,\n hasSummary: !!read.focused?.description.trim(),\n properties,\n derivation: derivationAtCaret(properties),\n }\n }\n\n function publish() {\n const next = snapshot()\n if (!sameState(store.get(), next)) store.set(next)\n }\n\n function load() {\n dirty = false\n pendingRow = null\n base = focusedRow()?.description ?? ''\n draft = base\n }\n\n /** Writes the pending draft; `false` when there was nothing to write or the write was refused. Does not touch `draft`. */\n function commit(): boolean {\n clearTimeout(commitTimer)\n if (!dirty || !pendingRow) return false\n dirty = false\n const row = pendingRow\n pendingRow = null\n if (!editor || editor.isDestroyed || !editor.isEditable) return false\n if (!setCompositeDescription(editor, row, draft, base)) return false\n base = compositeDescriptionText(draft)\n return true\n }\n\n function refreshSection() {\n const next = caretIn === 'editor' && focusedRow() ? 'typedBlock' : 'document'\n if (section === 'typedBlock' && next !== 'typedBlock') commit()\n section = next\n }\n\n // A switch commits the draft of the previous Typed block first (`draft` still holds its text), then loads the new one.\n // A change of the description reaches an untouched draft, also while the textarea has focus. A draft with unsaved\n // typing keeps it; if a peer changed the description meanwhile, its commit is refused and the peer's text is\n // loaded instead.\n function followRows() {\n const focused = focusedRow()\n const key = focused?.key\n const description = focused?.description\n if (key !== focusKey) {\n focusKey = key\n focusDescription = description\n commit()\n load()\n }\n else if (description !== focusDescription) {\n focusDescription = description\n if (dirty) {\n if (description !== base && !commit()) load()\n }\n else if (description !== base) {\n load()\n }\n }\n refreshSection()\n publish()\n }\n\n function scheduleSearch() {\n clearTimeout(searchTimer)\n searchTimer = setTimeout(async () => {\n if (!editor || editor.isDestroyed) return\n pendingSearch?.abort()\n const controller = pendingSearch = new AbortController()\n try {\n const found = await metadataOf(editor).search(METADATA_LIST.hashtags, { query: searchTerm, editor, signal: controller.signal })\n if (controller.signal.aborted) return\n tagItems = found.map(item => item.label)\n publish()\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Tag-Vorschläge konnten nicht geladen werden', error)\n }\n }, SEARCH_DELAY_MS)\n }\n\n function setSearchTerm(term: string) {\n if (disposed || term === searchTerm) return\n searchTerm = term\n scheduleSearch()\n publish()\n }\n\n const syncEditable = () => {\n const next = isEditable()\n if (next === editable) return\n editable = next\n publish()\n }\n const toEditor = () => {\n caretIn = 'editor'\n refreshSection()\n publish()\n }\n\n load()\n refreshSection()\n const stopRows = rows.state.subscribe(followRows)\n if (editor && !editor.isDestroyed) {\n editor.on('transaction', syncEditable)\n // Only `focus`, not `selectionUpdate`: a collaboration sync can move the selection while the host's input has the focus.\n editor.on('focus', toEditor)\n }\n publish()\n\n return {\n state: store,\n\n setDocumentFocused(focused) {\n if (disposed) return\n const rising = focused && !documentFocused\n documentFocused = focused\n if (!rising) return\n caretIn = 'document'\n refreshSection()\n publish()\n },\n\n setKind(kind) {\n const row = focusedRow()\n const found = editor && row ? compositeOfRow(editor.state.doc, row) : null\n if (!editor || !found || kind === row?.kind) return\n editor.commands.changeBlockType(kind, found.pos)\n },\n\n inputDescription(text) {\n if (disposed) return\n draft = text\n dirty = true\n pendingRow ??= focusedRow()\n clearTimeout(commitTimer)\n commitTimer = setTimeout(commit, COMMIT_DELAY_MS)\n publish()\n },\n\n blurDescription() {\n if (disposed) return\n if (commit()) draft = base\n else load()\n publish()\n },\n\n setTags(tags) {\n const row = focusedRow()\n if (editor && row) setCompositeTags(editor, row, tags)\n },\n\n // „Neu Tag“ → `neu-tag`. A name that normalizes to an already chosen tag is ignored; only unknown names are reported as created.\n createTag(raw) {\n const row = focusedRow()\n const name = normalizeTagName(raw)\n setSearchTerm('')\n if (!editor || !row || !name || row.tags.includes(name)) return\n setCompositeTags(editor, row, [...row.tags, name])\n if (!tagItems.includes(name)) metadataOf(editor).create(METADATA_LIST.hashtags, { id: name, label: name })\n },\n\n setSearchTerm,\n\n searchTags() {\n if (!disposed) scheduleSearch()\n },\n\n setField(field, value) {\n const target = store.get().properties\n if (editor && isEditable() && target && !isStructuredField(field)) setBlockProperty(editor, target, field.key, value)\n },\n\n dispose() {\n if (disposed) return\n commit()\n disposed = true\n clearTimeout(searchTimer)\n pendingSearch?.abort()\n stopRows()\n if (editor && !editor.isDestroyed) {\n editor.off('transaction', syncEditable)\n editor.off('focus', toEditor)\n }\n rows.dispose()\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,kBAAkB;;AAExB,MAAM,kBAAkB;;AA6FxB,SAAgB,kBAAkB,OAAkD;CAClF,OAAO,MAAM,SAAS,eAAe,MAAM,SAAS,kBAAkB,MAAM,SAAS,gBAAgB,MAAM,SAAS;AACtH;;AAGA,SAAgB,mBAAmB,KAA2C,OAAgD;CAC5H,MAAM,QAAQ,KAAK,KAAK,MAAM;CAC9B,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;AAClF;AAEA,SAAS,UAAU,GAA4B,GAAqC;CAClF,OAAQ,OAAO,KAAK,CAAC,CAAC,CAAuC,OAAM,QAAO,OAAO,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC;AACrG;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,yBAAyB,QAAgD;CACvF,MAAM,OAAO,oBAAoB,MAAM;CACvC,MAAM,aAAa,UAAU,CAAC,OAAO,cAAc,aAAa,MAAM,IAAI,CAAC;CAC3E,MAAM,cAAiC,WAAW,KAAI,UAAS;EAAE,OAAO,KAAK;EAAI,OAAO,KAAK;EAAO,MAAM,KAAK;CAAK,EAAE;CACtH,MAAM,mBAAmB,CAAC,CAAC,UAAU,CAAC,OAAO,eAAe,OAAO;CACnE,MAAM,mBAAmB,KAAK,MAAM,IAAI,CAAC,CAAC;CAE1C,IAAI,WAAW;CAGf,IAAI,UAAiC;CACrC,IAAI,kBAAkB;CACtB,IAAI,UAAqC;CACzC,IAAI,WAAW,WAAW;CAG1B,IAAI,QAAQ;CACZ,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,aAAkC;CACtC,IAAI;CAEJ,IAAI,WAAW,WAAW,CAAC,EAAE;CAC7B,IAAI,mBAAmB,WAAW,CAAC,EAAE;CAGrC,IAAI,aAAa;CACjB,IAAI,WAAqB,CAAC;CAC1B,IAAI;CACJ,IAAI;CAEJ,IAAI;CACJ,IAAI,aAAa;CAEjB,IAAI;CACJ,IAAI,iBAAyC;CAC7C,IAAI,iBAA6C;CACjD,MAAM,QAAQ,YAAqC,SAAS,CAAC;;CAG7D,SAAS,oBAA4C;EACnD,IAAI,CAAC,UAAU,OAAO,aAAa,OAAO;EAC1C,MAAM,QAAQ,kBAAkB,QAAQ,OAAO,MAAM,UAAU,OAAO,UAAU;EAChF,IAAI,CAAC,OAAO;GACV,iBAAiB,KAAA;GACjB,OAAO,iBAAiB;EAC1B;EACA,IAAI,MAAM,SAAS,kBAAkB,MAAM,WAAW,QAAQ,gBAAgB,KAAK,OAAO;EAC1F,iBAAiB,MAAM;EACvB,OAAO,iBAAiB,MAAM;CAChC;;;;;CAMA,SAAS,kBAAkB,YAAgE;EACzF,MAAM,OAAO;EACb,MAAM,aAAa,UAAU,CAAC,OAAO,eAAe,QAAQ,cAAc,eAAe,QAAQ,IAAI,CAAC,CAAC,IAAI,SAAS,IAAI,aAAa,IAAI,IAAI;EAC7I,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAc,YAAY,YAAY,MAAK,eAAc,WAAW,SAAS,YAAY,GAAG,OAAO,iBAAiB;EAC7I,MAAM,gBAAgB,gBAAgB,QAAQ,IAAI;EAClD,IAAI,gBAAgB,SAAS,QAAQ,eAAe,kBAAkB,eAAe,OAAO;EAC5F,OAAO,iBAAiB;GAAE;GAAM;GAAY;EAAc;CAC5D;CAEA,SAAS,WAAoC;EAC3C,MAAM,OAAO,KAAK,MAAM,IAAI;EAC5B,IAAI,KAAK,SAAS,gBAAgB;GAChC,iBAAiB,KAAK;GACtB,aAAa,KAAK,KAAK,QAAO,QAAO,IAAI,YAAY,KAAK,CAAC,CAAC,CAAC;EAC/D;EACA,MAAM,aAAa,kBAAkB;EACrC,OAAO;GACL,GAAG;GACH;GACA;GACA;GACA;GACA;GACA;GACA;GACA,YAAY,CAAC,CAAC,KAAK,SAAS,YAAY,KAAK;GAC7C;GACA,YAAY,kBAAkB,UAAU;EAC1C;CACF;CAEA,SAAS,UAAU;EACjB,MAAM,OAAO,SAAS;EACtB,IAAI,CAAC,UAAU,MAAM,IAAI,GAAG,IAAI,GAAG,MAAM,IAAI,IAAI;CACnD;CAEA,SAAS,OAAO;EACd,QAAQ;EACR,aAAa;EACb,OAAO,WAAW,CAAC,EAAE,eAAe;EACpC,QAAQ;CACV;;CAGA,SAAS,SAAkB;EACzB,aAAa,WAAW;EACxB,IAAI,CAAC,SAAS,CAAC,YAAY,OAAO;EAClC,QAAQ;EACR,MAAM,MAAM;EACZ,aAAa;EACb,IAAI,CAAC,UAAU,OAAO,eAAe,CAAC,OAAO,YAAY,OAAO;EAChE,IAAI,CAAC,wBAAwB,QAAQ,KAAK,OAAO,IAAI,GAAG,OAAO;EAC/D,OAAO,yBAAyB,KAAK;EACrC,OAAO;CACT;CAEA,SAAS,iBAAiB;EACxB,MAAM,OAAO,YAAY,YAAY,WAAW,IAAI,eAAe;EACnE,IAAI,YAAY,gBAAgB,SAAS,cAAc,OAAO;EAC9D,UAAU;CACZ;CAMA,SAAS,aAAa;EACpB,MAAM,UAAU,WAAW;EAC3B,MAAM,MAAM,SAAS;EACrB,MAAM,cAAc,SAAS;EAC7B,IAAI,QAAQ,UAAU;GACpB,WAAW;GACX,mBAAmB;GACnB,OAAO;GACP,KAAK;EACP,OACK,IAAI,gBAAgB,kBAAkB;GACzC,mBAAmB;GACnB,IAAI,OACE;QAAA,gBAAgB,QAAQ,CAAC,OAAO,GAAG,KAAK;GAAA,OAEzC,IAAI,gBAAgB,MACvB,KAAK;EAET;EACA,eAAe;EACf,QAAQ;CACV;CAEA,SAAS,iBAAiB;EACxB,aAAa,WAAW;EACxB,cAAc,WAAW,YAAY;GACnC,IAAI,CAAC,UAAU,OAAO,aAAa;GACnC,eAAe,MAAM;GACrB,MAAM,aAAa,gBAAgB,IAAI,gBAAgB;GACvD,IAAI;IACF,MAAM,QAAQ,MAAM,WAAW,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;KAAE,OAAO;KAAY;KAAQ,QAAQ,WAAW;IAAO,CAAC;IAC9H,IAAI,WAAW,OAAO,SAAS;IAC/B,WAAW,MAAM,KAAI,SAAQ,KAAK,KAAK;IACvC,QAAQ;GACV,SACO,OAAO;IACZ,IAAI,CAAC,WAAW,OAAO,SAAS,QAAQ,KAAK,+CAA+C,KAAK;GACnG;EACF,GAAG,eAAe;CACpB;CAEA,SAAS,cAAc,MAAc;EACnC,IAAI,YAAY,SAAS,YAAY;EACrC,aAAa;EACb,eAAe;EACf,QAAQ;CACV;CAEA,MAAM,qBAAqB;EACzB,MAAM,OAAO,WAAW;EACxB,IAAI,SAAS,UAAU;EACvB,WAAW;EACX,QAAQ;CACV;CACA,MAAM,iBAAiB;EACrB,UAAU;EACV,eAAe;EACf,QAAQ;CACV;CAEA,KAAK;CACL,eAAe;CACf,MAAM,WAAW,KAAK,MAAM,UAAU,UAAU;CAChD,IAAI,UAAU,CAAC,OAAO,aAAa;EACjC,OAAO,GAAG,eAAe,YAAY;EAErC,OAAO,GAAG,SAAS,QAAQ;CAC7B;CACA,QAAQ;CAER,OAAO;EACL,OAAO;EAEP,mBAAmB,SAAS;GAC1B,IAAI,UAAU;GACd,MAAM,SAAS,WAAW,CAAC;GAC3B,kBAAkB;GAClB,IAAI,CAAC,QAAQ;GACb,UAAU;GACV,eAAe;GACf,QAAQ;EACV;EAEA,QAAQ,MAAM;GACZ,MAAM,MAAM,WAAW;GACvB,MAAM,QAAQ,UAAU,MAAM,eAAe,OAAO,MAAM,KAAK,GAAG,IAAI;GACtE,IAAI,CAAC,UAAU,CAAC,SAAS,SAAS,KAAK,MAAM;GAC7C,OAAO,SAAS,gBAAgB,MAAM,MAAM,GAAG;EACjD;EAEA,iBAAiB,MAAM;GACrB,IAAI,UAAU;GACd,QAAQ;GACR,QAAQ;GACR,eAAe,WAAW;GAC1B,aAAa,WAAW;GACxB,cAAc,WAAW,QAAQ,eAAe;GAChD,QAAQ;EACV;EAEA,kBAAkB;GAChB,IAAI,UAAU;GACd,IAAI,OAAO,GAAG,QAAQ;QACjB,KAAK;GACV,QAAQ;EACV;EAEA,QAAQ,MAAM;GACZ,MAAM,MAAM,WAAW;GACvB,IAAI,UAAU,KAAK,iBAAiB,QAAQ,KAAK,IAAI;EACvD;EAGA,UAAU,KAAK;GACb,MAAM,MAAM,WAAW;GACvB,MAAM,OAAO,iBAAiB,GAAG;GACjC,cAAc,EAAE;GAChB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,IAAI,KAAK,SAAS,IAAI,GAAG;GACzD,iBAAiB,QAAQ,KAAK,CAAC,GAAG,IAAI,MAAM,IAAI,CAAC;GACjD,IAAI,CAAC,SAAS,SAAS,IAAI,GAAG,WAAW,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;IAAE,IAAI;IAAM,OAAO;GAAK,CAAC;EAC3G;EAEA;EAEA,aAAa;GACX,IAAI,CAAC,UAAU,eAAe;EAChC;EAEA,SAAS,OAAO,OAAO;GACrB,MAAM,SAAS,MAAM,IAAI,CAAC,CAAC;GAC3B,IAAI,UAAU,WAAW,KAAK,UAAU,CAAC,kBAAkB,KAAK,GAAG,iBAAiB,QAAQ,QAAQ,MAAM,KAAK,KAAK;EACtH;EAEA,UAAU;GACR,IAAI,UAAU;GACd,OAAO;GACP,WAAW;GACX,aAAa,WAAW;GACxB,eAAe,MAAM;GACrB,SAAS;GACT,IAAI,UAAU,CAAC,OAAO,aAAa;IACjC,OAAO,IAAI,eAAe,YAAY;IACtC,OAAO,IAAI,SAAS,QAAQ;GAC9B;GACA,KAAK,QAAQ;EACf;CACF;AACF"}
@@ -1,10 +1,11 @@
1
1
  import { JSONContent } from "@tiptap/core";
2
2
  //#region src/features/composites/legacy.d.ts
3
- /** Legacy node name of the composite block; stored Yjs docs containing it are reseeded from migrated JSON. */
4
- export declare const LEGACY_COMPOSITE_NODE = "section";
3
+ /** Whether `meta` (the `meta` attribute of a Typed block) still holds the flat `transcript` string `migrateLegacyContent` turns into a `rendition` child; the stored Block type does not matter. */
4
+ export declare const hasFlatTranscript: (meta: unknown) => boolean;
5
5
  /**
6
- * `json` with legacy node types renamed to the composite family (a legacy block becomes kind `section`) and the flat
7
- * `meta.transcript` of `document` Bausteine moved into a `rendition` child; recursive, returns a copy.
6
+ * `json` as the current schema reads it: every old persisted name replaced by the current one (`migratePersistedNames`:
7
+ * the names in `persisted-names.ts`; block IDs stay) and the flat
8
+ * `meta.transcript` of any Typed block moved into a `rendition` child, whatever its Block type. Idempotent; returns a copy.
8
9
  */
9
10
  export declare function migrateLegacyContent(json: JSONContent): JSONContent;
10
11
  //#endregion
@@ -1,18 +1,12 @@
1
- import { DERIVATION_KEY } from "./meta-keys.js";
2
- import { assetRefOf } from "./derivation.js";
1
+ import { migratePersistedNames } from "../../core/schema/persisted-names.js";
2
+ import { DERIVATION_KEY, assetRefOf } from "../../core/derivation.js";
3
3
  import { compositeJSON } from "./nodes.js";
4
4
  //#region src/features/composites/legacy.ts
5
- const LEGACY_RENAMES = {
6
- section: "composite",
7
- sectionTitle: "compositeTitle",
8
- sectionSummary: "compositeDescription",
9
- sectionTags: "compositeTags"
10
- };
11
- /** Legacy node name of the composite block; stored Yjs docs containing it are reseeded from migrated JSON. */
12
- const LEGACY_COMPOSITE_NODE = "section";
5
+ /** Whether `meta` (the `meta` attribute of a Typed block) still holds the flat `transcript` string `migrateLegacyContent` turns into a `rendition` child; the stored Block type does not matter. */
6
+ const hasFlatTranscript = (meta) => typeof meta === "object" && meta !== null && "transcript" in meta && typeof meta.transcript === "string";
13
7
  /**
14
- * The Lesefassung a `document` Baustein's former `meta.transcript` (a flat string) becomes: a `rendition` child with
15
- * one paragraph per blank-line-separated chunk, derived by transcription from the asset of the first audio/video body
8
+ * The Readable rendition a Typed block's former `meta.transcript` (a flat string stored by earlier releases) becomes: a `rendition`
9
+ * child with one paragraph per blank-line-separated chunk, derived by transcription from the asset of the first audio/video body
16
10
  * child. Hashes are unknown (`''`), so neither „Quelle geändert“ nor „bearbeitet“ is shown for it.
17
11
  */
18
12
  function transcriptRendition(document, transcript) {
@@ -51,19 +45,18 @@ function transcriptRendition(document, transcript) {
51
45
  };
52
46
  }
53
47
  /**
54
- * `json` with legacy node types renamed to the composite family (a legacy block becomes kind `section`) and the flat
55
- * `meta.transcript` of `document` Bausteine moved into a `rendition` child; recursive, returns a copy.
48
+ * `json` as the current schema reads it: every old persisted name replaced by the current one (`migratePersistedNames`:
49
+ * the names in `persisted-names.ts`; block IDs stay) and the flat
50
+ * `meta.transcript` of any Typed block moved into a `rendition` child, whatever its Block type. Idempotent; returns a copy.
56
51
  */
57
52
  function migrateLegacyContent(json) {
58
- const renamed = json.type ? LEGACY_RENAMES[json.type] : void 0;
53
+ return moveTranscripts(migratePersistedNames(json, { earliest: true }));
54
+ }
55
+ /** `json` (already on current names) with every flat `meta.transcript` of a Typed block moved into a `rendition` child; recursive, returns a copy. */
56
+ function moveTranscripts(json) {
59
57
  const result = { ...json };
60
- if (renamed) result.type = renamed;
61
- if (json.type === "section") result.attrs = {
62
- ...json.attrs,
63
- kind: "section"
64
- };
65
- if (json.content) result.content = json.content.map(migrateLegacyContent);
66
- if (result.type === "composite" && result.attrs?.kind === "document" && typeof result.attrs.meta?.transcript === "string") {
58
+ if (json.content) result.content = json.content.map(moveTranscripts);
59
+ if (result.type === "typedBlock" && hasFlatTranscript(result.attrs?.meta)) {
67
60
  const { transcript, ...meta } = result.attrs.meta;
68
61
  result.attrs = {
69
62
  ...result.attrs,
@@ -75,6 +68,6 @@ function migrateLegacyContent(json) {
75
68
  return result;
76
69
  }
77
70
  //#endregion
78
- export { LEGACY_COMPOSITE_NODE, migrateLegacyContent };
71
+ export { hasFlatTranscript, migrateLegacyContent };
79
72
 
80
73
  //# sourceMappingURL=legacy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"legacy.js","names":[],"sources":["../../../src/features/composites/legacy.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport { assetRefOf, DERIVATION_KEY, type Derivation } from './derivation'\nimport { compositeJSON } from './nodes'\n\n// Node names of the former single-purpose block that the composite family replaced (dev data only).\nconst LEGACY_RENAMES: Readonly<Record<string, string>> = {\n section: 'composite',\n sectionTitle: 'compositeTitle',\n sectionSummary: 'compositeDescription',\n sectionTags: 'compositeTags',\n}\n\n/** Legacy node name of the composite block; stored Yjs docs containing it are reseeded from migrated JSON. */\nexport const LEGACY_COMPOSITE_NODE = 'section'\n\n/**\n * The Lesefassung a `document` Baustein's former `meta.transcript` (a flat string) becomes: a `rendition` child with\n * one paragraph per blank-line-separated chunk, derived by transcription from the asset of the first audio/video body\n * child. Hashes are unknown (`''`), so neither „Quelle geändert“ nor „bearbeitet“ is shown for it.\n */\nfunction transcriptRendition(document: JSONContent, transcript: string): JSONContent | null {\n const chunks = transcript.split(/\\n\\s*\\n/).map(chunk => chunk.replace(/\\s*\\n\\s*/g, ' ').trim()).filter(Boolean)\n if (!chunks.length) return null\n const media = document.content?.find(child => child.type === 'audio' || child.type === 'video')\n const importedAt: unknown = document.attrs?.meta?.importedAt\n const derivation: Derivation = {\n from: { asset: assetRefOf(String(media?.attrs?.src ?? ''))?.name ?? '' },\n method: 'transcription',\n createdAt: typeof importedAt === 'string' ? importedAt : '',\n inputHash: '',\n outputHash: '',\n }\n const json = compositeJSON('rendition', chunks.map(text => ({ type: 'paragraph', content: [{ type: 'text', text }] })))\n const [title, ...rest] = json.content!\n return {\n ...json,\n attrs: { ...json.attrs, meta: { [DERIVATION_KEY]: derivation } },\n // i18n-ignore: title of content migrated out of a stored document, not UI text\n content: [{ ...title!, content: [{ type: 'text', text: 'Transkript' }] }, ...rest],\n }\n}\n\n/**\n * `json` with legacy node types renamed to the composite family (a legacy block becomes kind `section`) and the flat\n * `meta.transcript` of `document` Bausteine moved into a `rendition` child; recursive, returns a copy.\n */\nexport function migrateLegacyContent(json: JSONContent): JSONContent {\n const renamed = json.type ? LEGACY_RENAMES[json.type] : undefined\n const result: JSONContent = { ...json }\n if (renamed) result.type = renamed\n if (json.type === LEGACY_COMPOSITE_NODE) result.attrs = { ...json.attrs, kind: 'section' }\n if (json.content) result.content = json.content.map(migrateLegacyContent)\n if (result.type === 'composite' && result.attrs?.kind === 'document' && typeof result.attrs.meta?.transcript === 'string') {\n const { transcript, ...meta } = result.attrs.meta as Record<string, unknown>\n result.attrs = { ...result.attrs, meta }\n const rendition = transcriptRendition(result, String(transcript))\n if (rendition) result.content = [...(result.content ?? []), rendition]\n }\n return result\n}\n"],"mappings":";;;;AAKA,MAAM,iBAAmD;CACvD,SAAS;CACT,cAAc;CACd,gBAAgB;CAChB,aAAa;AACf;;AAGA,MAAa,wBAAwB;;;;;;AAOrC,SAAS,oBAAoB,UAAuB,YAAwC;CAC1F,MAAM,SAAS,WAAW,MAAM,SAAS,CAAC,CAAC,KAAI,UAAS,MAAM,QAAQ,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO;CAC9G,IAAI,CAAC,OAAO,QAAQ,OAAO;CAC3B,MAAM,QAAQ,SAAS,SAAS,MAAK,UAAS,MAAM,SAAS,WAAW,MAAM,SAAS,OAAO;CAC9F,MAAM,aAAsB,SAAS,OAAO,MAAM;CAClD,MAAM,aAAyB;EAC7B,MAAM,EAAE,OAAO,WAAW,OAAO,OAAO,OAAO,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG;EACvE,QAAQ;EACR,WAAW,OAAO,eAAe,WAAW,aAAa;EACzD,WAAW;EACX,YAAY;CACd;CACA,MAAM,OAAO,cAAc,aAAa,OAAO,KAAI,UAAS;EAAE,MAAM;EAAa,SAAS,CAAC;GAAE,MAAM;GAAQ;EAAK,CAAC;CAAE,EAAE,CAAC;CACtH,MAAM,CAAC,OAAO,GAAG,QAAQ,KAAK;CAC9B,OAAO;EACL,GAAG;EACH,OAAO;GAAE,GAAG,KAAK;GAAO,MAAM,GAAG,iBAAiB,WAAW;EAAE;EAE/D,SAAS,CAAC;GAAE,GAAG;GAAQ,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;GAAa,CAAC;EAAE,GAAG,GAAG,IAAI;CACnF;AACF;;;;;AAMA,SAAgB,qBAAqB,MAAgC;CACnE,MAAM,UAAU,KAAK,OAAO,eAAe,KAAK,QAAQ,KAAA;CACxD,MAAM,SAAsB,EAAE,GAAG,KAAK;CACtC,IAAI,SAAS,OAAO,OAAO;CAC3B,IAAI,KAAK,SAAA,WAAgC,OAAO,QAAQ;EAAE,GAAG,KAAK;EAAO,MAAM;CAAU;CACzF,IAAI,KAAK,SAAS,OAAO,UAAU,KAAK,QAAQ,IAAI,oBAAoB;CACxE,IAAI,OAAO,SAAS,eAAe,OAAO,OAAO,SAAS,cAAc,OAAO,OAAO,MAAM,MAAM,eAAe,UAAU;EACzH,MAAM,EAAE,YAAY,GAAG,SAAS,OAAO,MAAM;EAC7C,OAAO,QAAQ;GAAE,GAAG,OAAO;GAAO;EAAK;EACvC,MAAM,YAAY,oBAAoB,QAAQ,OAAO,UAAU,CAAC;EAChE,IAAI,WAAW,OAAO,UAAU,CAAC,GAAI,OAAO,WAAW,CAAC,GAAI,SAAS;CACvE;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"legacy.js","names":[],"sources":["../../../src/features/composites/legacy.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport { assetRefOf, DERIVATION_KEY, type Derivation } from '../../core/derivation'\nimport { migratePersistedNames, TYPED_BLOCK } from '../../core/schema/persisted-names'\nimport { compositeJSON } from './nodes'\n\n/** Whether `meta` (the `meta` attribute of a Typed block) still holds the flat `transcript` string `migrateLegacyContent` turns into a `rendition` child; the stored Block type does not matter. */\nexport const hasFlatTranscript = (meta: unknown): boolean =>\n typeof meta === 'object' && meta !== null && 'transcript' in meta && typeof meta.transcript === 'string'\n\n/**\n * The Readable rendition a Typed block's former `meta.transcript` (a flat string stored by earlier releases) becomes: a `rendition`\n * child with one paragraph per blank-line-separated chunk, derived by transcription from the asset of the first audio/video body\n * child. Hashes are unknown (`''`), so neither „Quelle geändert“ nor „bearbeitet“ is shown for it.\n */\nfunction transcriptRendition(document: JSONContent, transcript: string): JSONContent | null {\n const chunks = transcript.split(/\\n\\s*\\n/).map(chunk => chunk.replace(/\\s*\\n\\s*/g, ' ').trim()).filter(Boolean)\n if (!chunks.length) return null\n const media = document.content?.find(child => child.type === 'audio' || child.type === 'video')\n const importedAt: unknown = document.attrs?.meta?.importedAt\n const derivation: Derivation = {\n from: { asset: assetRefOf(String(media?.attrs?.src ?? ''))?.name ?? '' },\n method: 'transcription',\n createdAt: typeof importedAt === 'string' ? importedAt : '',\n inputHash: '',\n outputHash: '',\n }\n const json = compositeJSON('rendition', chunks.map(text => ({ type: 'paragraph', content: [{ type: 'text', text }] })))\n const [title, ...rest] = json.content!\n return {\n ...json,\n attrs: { ...json.attrs, meta: { [DERIVATION_KEY]: derivation } },\n // i18n-ignore: title of content migrated out of a stored document, not UI text\n content: [{ ...title!, content: [{ type: 'text', text: 'Transkript' }] }, ...rest],\n }\n}\n\n/**\n * `json` as the current schema reads it: every old persisted name replaced by the current one (`migratePersistedNames`:\n * the names in `persisted-names.ts`; block IDs stay) and the flat\n * `meta.transcript` of any Typed block moved into a `rendition` child, whatever its Block type. Idempotent; returns a copy.\n */\nexport function migrateLegacyContent(json: JSONContent): JSONContent {\n return moveTranscripts(migratePersistedNames(json, { earliest: true }))\n}\n\n/** `json` (already on current names) with every flat `meta.transcript` of a Typed block moved into a `rendition` child; recursive, returns a copy. */\nfunction moveTranscripts(json: JSONContent): JSONContent {\n const result: JSONContent = { ...json }\n if (json.content) result.content = json.content.map(moveTranscripts)\n if (result.type === TYPED_BLOCK && hasFlatTranscript(result.attrs?.meta)) {\n const { transcript, ...meta } = result.attrs!.meta as Record<string, unknown>\n result.attrs = { ...result.attrs, meta }\n const rendition = transcriptRendition(result, String(transcript))\n if (rendition) result.content = [...(result.content ?? []), rendition]\n }\n return result\n}\n"],"mappings":";;;;;AAMA,MAAa,qBAAqB,SAChC,OAAO,SAAS,YAAY,SAAS,QAAQ,gBAAgB,QAAQ,OAAO,KAAK,eAAe;;;;;;AAOlG,SAAS,oBAAoB,UAAuB,YAAwC;CAC1F,MAAM,SAAS,WAAW,MAAM,SAAS,CAAC,CAAC,KAAI,UAAS,MAAM,QAAQ,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO;CAC9G,IAAI,CAAC,OAAO,QAAQ,OAAO;CAC3B,MAAM,QAAQ,SAAS,SAAS,MAAK,UAAS,MAAM,SAAS,WAAW,MAAM,SAAS,OAAO;CAC9F,MAAM,aAAsB,SAAS,OAAO,MAAM;CAClD,MAAM,aAAyB;EAC7B,MAAM,EAAE,OAAO,WAAW,OAAO,OAAO,OAAO,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,GAAG;EACvE,QAAQ;EACR,WAAW,OAAO,eAAe,WAAW,aAAa;EACzD,WAAW;EACX,YAAY;CACd;CACA,MAAM,OAAO,cAAc,aAAa,OAAO,KAAI,UAAS;EAAE,MAAM;EAAa,SAAS,CAAC;GAAE,MAAM;GAAQ;EAAK,CAAC;CAAE,EAAE,CAAC;CACtH,MAAM,CAAC,OAAO,GAAG,QAAQ,KAAK;CAC9B,OAAO;EACL,GAAG;EACH,OAAO;GAAE,GAAG,KAAK;GAAO,MAAM,GAAG,iBAAiB,WAAW;EAAE;EAE/D,SAAS,CAAC;GAAE,GAAG;GAAQ,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;GAAa,CAAC;EAAE,GAAG,GAAG,IAAI;CACnF;AACF;;;;;;AAOA,SAAgB,qBAAqB,MAAgC;CACnE,OAAO,gBAAgB,sBAAsB,MAAM,EAAE,UAAU,KAAK,CAAC,CAAC;AACxE;;AAGA,SAAS,gBAAgB,MAAgC;CACvD,MAAM,SAAsB,EAAE,GAAG,KAAK;CACtC,IAAI,KAAK,SAAS,OAAO,UAAU,KAAK,QAAQ,IAAI,eAAe;CACnE,IAAI,OAAO,SAAA,gBAAwB,kBAAkB,OAAO,OAAO,IAAI,GAAG;EACxE,MAAM,EAAE,YAAY,GAAG,SAAS,OAAO,MAAO;EAC9C,OAAO,QAAQ;GAAE,GAAG,OAAO;GAAO;EAAK;EACvC,MAAM,YAAY,oBAAoB,QAAQ,OAAO,UAAU,CAAC;EAChE,IAAI,WAAW,OAAO,UAAU,CAAC,GAAI,OAAO,WAAW,CAAC,GAAI,SAAS;CACvE;CACA,OAAO;AACT"}
@@ -1,12 +1,12 @@
1
1
  import { compositeChromeBlocks, flattenCompositesToHeadings } from "./heading-composites.js";
2
- import { compositeKindsOf } from "./nodes.js";
2
+ import { blockTypesOf } from "./nodes.js";
3
3
  //#region src/features/composites/markdown-export.ts
4
4
  /**
5
5
  * `blocks` as Markdown with every composite turned into a heading, the outermost at `level` (nested ones deeper), then
6
6
  * its description, tags and meta line, then its body.
7
7
  */
8
8
  function headingsMarkdown(editor, blocks, level) {
9
- const nodes = flattenCompositesToHeadings(editor.schema, blocks, true, level, compositeKindsOf(editor)) ?? [...blocks];
9
+ const nodes = flattenCompositesToHeadings(editor.schema, blocks, true, level, blockTypesOf(editor)) ?? [...blocks];
10
10
  return editor.markdown.serialize({
11
11
  type: "doc",
12
12
  content: nodes.map((node) => node.toJSON())
@@ -24,7 +24,7 @@ function documentCompositeMarkdown(editor, node) {
24
24
  ...node.attrs,
25
25
  meta: {}
26
26
  }, node.content);
27
- const chrome = compositeChromeBlocks(schema, withoutMeta, compositeKindsOf(editor));
27
+ const chrome = compositeChromeBlocks(schema, withoutMeta, blockTypesOf(editor));
28
28
  let body = [];
29
29
  node.forEach((child, _offset, index) => {
30
30
  if (index >= 3) body.push(child);
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-export.js","names":[],"sources":["../../../src/features/composites/markdown-export.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { compositeChromeBlocks, flattenCompositesToHeadings } from './heading-composites'\nimport { COMPOSITE_CHROME_COUNT, compositeKindsOf } from './nodes'\n\n/**\n * `blocks` as Markdown with every composite turned into a heading, the outermost at `level` (nested ones deeper), then\n * its description, tags and meta line, then its body.\n */\nexport function headingsMarkdown(editor: Editor, blocks: readonly PMNode[], level: number): string {\n const nodes = flattenCompositesToHeadings(editor.schema, blocks, true, level, compositeKindsOf(editor)) ?? [...blocks]\n return editor.markdown!.serialize({ type: 'doc', content: nodes.map(node => node.toJSON()) })\n}\n\n/**\n * A composite as a Markdown document: its title as `#` heading, description and tags (no meta line: the original file\n * name, type and size describe the import, not the content), then the body with nested composites as `##`, `###`, ….\n */\nexport function documentCompositeMarkdown(editor: Editor, node: PMNode): string {\n const { schema } = editor\n const title = node.child(0)\n const heading = title.content.size ? [schema.nodes.heading!.create({ level: 1 }, title.content)] : []\n const withoutMeta = node.type.create({ ...node.attrs, meta: {} }, node.content)\n const chrome = compositeChromeBlocks(schema, withoutMeta, compositeKindsOf(editor))\n let body: PMNode[] = []\n node.forEach((child, _offset, index) => {\n if (index >= COMPOSITE_CHROME_COUNT) body.push(child)\n })\n if (body.length === 1 && body[0]!.type.name === 'paragraph' && !body[0]!.content.size) body = []\n return headingsMarkdown(editor, [...heading, ...chrome, ...body], 2)\n}\n\n/** Download name for `node`'s Markdown: the imported file's name with its extension replaced by `.md`, else the title. */\nexport function markdownFileName(node: PMNode): string {\n const meta = (node.attrs.meta ?? {}) as Record<string, unknown>\n const filename = typeof meta.filename === 'string' ? meta.filename.trim() : ''\n const dot = filename.lastIndexOf('.')\n const stem = filename ? (dot > 0 ? filename.slice(0, dot) : filename) : node.child(0).textContent.trim() || 'dokument'\n return `${stem}.md`.replace(/[\\\\/:*?\"<>|]+/g, '-')\n}\n"],"mappings":";;;;;;;AASA,SAAgB,iBAAiB,QAAgB,QAA2B,OAAuB;CACjG,MAAM,QAAQ,4BAA4B,OAAO,QAAQ,QAAQ,MAAM,OAAO,iBAAiB,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM;CACrH,OAAO,OAAO,SAAU,UAAU;EAAE,MAAM;EAAO,SAAS,MAAM,KAAI,SAAQ,KAAK,OAAO,CAAC;CAAE,CAAC;AAC9F;;;;;AAMA,SAAgB,0BAA0B,QAAgB,MAAsB;CAC9E,MAAM,EAAE,WAAW;CACnB,MAAM,QAAQ,KAAK,MAAM,CAAC;CAC1B,MAAM,UAAU,MAAM,QAAQ,OAAO,CAAC,OAAO,MAAM,QAAS,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;CACpG,MAAM,cAAc,KAAK,KAAK,OAAO;EAAE,GAAG,KAAK;EAAO,MAAM,CAAC;CAAE,GAAG,KAAK,OAAO;CAC9E,MAAM,SAAS,sBAAsB,QAAQ,aAAa,iBAAiB,MAAM,CAAC;CAClF,IAAI,OAAiB,CAAC;CACtB,KAAK,SAAS,OAAO,SAAS,UAAU;EACtC,IAAI,SAAA,GAAiC,KAAK,KAAK,KAAK;CACtD,CAAC;CACD,IAAI,KAAK,WAAW,KAAK,KAAK,EAAE,CAAE,KAAK,SAAS,eAAe,CAAC,KAAK,EAAE,CAAE,QAAQ,MAAM,OAAO,CAAC;CAC/F,OAAO,iBAAiB,QAAQ;EAAC,GAAG;EAAS,GAAG;EAAQ,GAAG;CAAI,GAAG,CAAC;AACrE;;AAGA,SAAgB,iBAAiB,MAAsB;CACrD,MAAM,OAAQ,KAAK,MAAM,QAAQ,CAAC;CAClC,MAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,SAAS,KAAK,IAAI;CAC5E,MAAM,MAAM,SAAS,YAAY,GAAG;CAEpC,OAAO,GADM,WAAY,MAAM,IAAI,SAAS,MAAM,GAAG,GAAG,IAAI,WAAY,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,KAAK,KAAK,WAC7F,KAAK,QAAQ,kBAAkB,GAAG;AACnD"}
1
+ {"version":3,"file":"markdown-export.js","names":[],"sources":["../../../src/features/composites/markdown-export.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { compositeChromeBlocks, flattenCompositesToHeadings } from './heading-composites'\nimport { COMPOSITE_CHROME_COUNT, blockTypesOf } from './nodes'\n\n/**\n * `blocks` as Markdown with every composite turned into a heading, the outermost at `level` (nested ones deeper), then\n * its description, tags and meta line, then its body.\n */\nexport function headingsMarkdown(editor: Editor, blocks: readonly PMNode[], level: number): string {\n const nodes = flattenCompositesToHeadings(editor.schema, blocks, true, level, blockTypesOf(editor)) ?? [...blocks]\n return editor.markdown!.serialize({ type: 'doc', content: nodes.map(node => node.toJSON()) })\n}\n\n/**\n * A composite as a Markdown document: its title as `#` heading, description and tags (no meta line: the original file\n * name, type and size describe the import, not the content), then the body with nested composites as `##`, `###`, ….\n */\nexport function documentCompositeMarkdown(editor: Editor, node: PMNode): string {\n const { schema } = editor\n const title = node.child(0)\n const heading = title.content.size ? [schema.nodes.heading!.create({ level: 1 }, title.content)] : []\n const withoutMeta = node.type.create({ ...node.attrs, meta: {} }, node.content)\n const chrome = compositeChromeBlocks(schema, withoutMeta, blockTypesOf(editor))\n let body: PMNode[] = []\n node.forEach((child, _offset, index) => {\n if (index >= COMPOSITE_CHROME_COUNT) body.push(child)\n })\n if (body.length === 1 && body[0]!.type.name === 'paragraph' && !body[0]!.content.size) body = []\n return headingsMarkdown(editor, [...heading, ...chrome, ...body], 2)\n}\n\n/** Download name for `node`'s Markdown: the imported file's name with its extension replaced by `.md`, else the title. */\nexport function markdownFileName(node: PMNode): string {\n const meta = (node.attrs.meta ?? {}) as Record<string, unknown>\n const filename = typeof meta.filename === 'string' ? meta.filename.trim() : ''\n const dot = filename.lastIndexOf('.')\n const stem = filename ? (dot > 0 ? filename.slice(0, dot) : filename) : node.child(0).textContent.trim() || 'dokument'\n return `${stem}.md`.replace(/[\\\\/:*?\"<>|]+/g, '-')\n}\n"],"mappings":";;;;;;;AASA,SAAgB,iBAAiB,QAAgB,QAA2B,OAAuB;CACjG,MAAM,QAAQ,4BAA4B,OAAO,QAAQ,QAAQ,MAAM,OAAO,aAAa,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM;CACjH,OAAO,OAAO,SAAU,UAAU;EAAE,MAAM;EAAO,SAAS,MAAM,KAAI,SAAQ,KAAK,OAAO,CAAC;CAAE,CAAC;AAC9F;;;;;AAMA,SAAgB,0BAA0B,QAAgB,MAAsB;CAC9E,MAAM,EAAE,WAAW;CACnB,MAAM,QAAQ,KAAK,MAAM,CAAC;CAC1B,MAAM,UAAU,MAAM,QAAQ,OAAO,CAAC,OAAO,MAAM,QAAS,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;CACpG,MAAM,cAAc,KAAK,KAAK,OAAO;EAAE,GAAG,KAAK;EAAO,MAAM,CAAC;CAAE,GAAG,KAAK,OAAO;CAC9E,MAAM,SAAS,sBAAsB,QAAQ,aAAa,aAAa,MAAM,CAAC;CAC9E,IAAI,OAAiB,CAAC;CACtB,KAAK,SAAS,OAAO,SAAS,UAAU;EACtC,IAAI,SAAA,GAAiC,KAAK,KAAK,KAAK;CACtD,CAAC;CACD,IAAI,KAAK,WAAW,KAAK,KAAK,EAAE,CAAE,KAAK,SAAS,eAAe,CAAC,KAAK,EAAE,CAAE,QAAQ,MAAM,OAAO,CAAC;CAC/F,OAAO,iBAAiB,QAAQ;EAAC,GAAG;EAAS,GAAG;EAAQ,GAAG;CAAI,GAAG,CAAC;AACrE;;AAGA,SAAgB,iBAAiB,MAAsB;CACrD,MAAM,OAAQ,KAAK,MAAM,QAAQ,CAAC;CAClC,MAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,SAAS,KAAK,IAAI;CAC5E,MAAM,MAAM,SAAS,YAAY,GAAG;CAEpC,OAAO,GADM,WAAY,MAAM,IAAI,SAAS,MAAM,GAAG,GAAG,IAAI,WAAY,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,KAAK,KAAK,WAC7F,KAAK,QAAQ,kBAAkB,GAAG;AACnD"}
@@ -1,25 +1,25 @@
1
- import { CompositeKind } from "./kinds.js";
1
+ import { BlockType } from "./block-types.js";
2
2
  import { Extension } from "@tiptap/core";
3
3
  import { Fragment, Node as Node$1 } from "@tiptap/pm/model";
4
4
  //#region src/features/composites/merge.d.ts
5
5
  /**
6
- * What a merge appends to the first Baustein's body: the whole body of `next`, its own sub-Bausteine included, in document
7
- * order. A body that is one empty paragraph (a Baustein nothing was written in) adds nothing.
8
- * Null when `first`'s kind does not accept the kind of a Baustein nested in that body (the merge is refused).
9
- * Where it goes is the command's call: at the end, or, for a kind with `nestedAfterContent`, split by `splitAtSubComposites`.
6
+ * What a merge appends to the first Typed block's body: the whole body of `next`, its own nested Typed blocks included, in
7
+ * document order. A body that is one empty paragraph (a Typed block nothing was written in) adds nothing.
8
+ * Null when `first`'s Block type does not accept the Block type of a Typed block nested in that body (the merge is refused).
9
+ * Where it goes is the command's call: at the end, or, for a Block type with `nestedAfterContent`, split by `splitAtNested`.
10
10
  */
11
- export declare function mergeBody(kinds: readonly CompositeKind[], first: Node$1, next: Node$1): Fragment | null;
11
+ export declare function mergeBody(kinds: readonly BlockType[], first: Node$1, next: Node$1): Fragment | null;
12
12
  declare module '@tiptap/core' {
13
13
  interface Commands<ReturnType> {
14
14
  compositeMerge: {
15
15
  /**
16
- * The Baustein at `pos` (default: the caret's innermost one) absorbs the Baustein that directly follows it in the same parent: that one's body, with its sub-Bausteine, is appended to the end of this one's body and the following Baustein is gone. For a kind with `nestedAfterContent` the following body's own content (the leading blocks that are not Bausteine) goes in right before the first one's first sub-Baustein and only its sub-Bausteine go to the end, so the content stays ahead of the sub-Bausteine. Title, description, tags, id, kind and meta stay those of the first. One transaction. Refused when no Baustein follows, when the first kind does not accept a Baustein nested in the following one, when either Baustein has a kind with `chrome.controls === false` (host-managed), or when the editor is not editable.
16
+ * The Typed block at `pos` (default: the caret's innermost one) absorbs the Typed block that directly follows it in the same parent: that one's body, with its nested Typed blocks, is appended to the end of this one's body and the following Typed block is gone. For a Block type with `nestedAfterContent` (its `container` config) the following body's own content (the leading blocks that are not Typed blocks) goes in right before the first one's first nested Typed block and only its nested Typed blocks go to the end, so the content stays ahead of the nested Typed blocks. Title, description, tags, id, Block type and meta stay those of the first. One transaction. Refused when no Typed block follows, when the first Block type does not accept a Typed block nested in the following one, when either Typed block has a Block type with `chrome.controls === false` (host-managed), or when the editor is not editable.
17
17
  */
18
18
  mergeCompositeWithNext: (pos?: number) => ReturnType;
19
19
  };
20
20
  }
21
21
  }
22
- /** Client-only, no schema: the merge command of Bausteine. */
22
+ /** Client-only, no schema: the merge command of Typed blocks. */
23
23
  export declare const CompositeMerge: Extension<any, any>;
24
24
  //#endregion
25
25
  //# sourceMappingURL=merge.d.ts.map
@@ -1,31 +1,32 @@
1
- import { compositeKind, kindAccepts } from "./kinds.js";
2
- import { compositeAt, compositeKindsOf } from "./nodes.js";
1
+ import "../../core/schema/persisted-names.js";
2
+ import { acceptsChild, blockTypeOf, nestedAfterContentOf } from "./block-types.js";
3
+ import { blockTypesOf, compositeAt } from "./nodes.js";
3
4
  import { outermostComposites } from "./accepted-children.js";
4
5
  import { Extension } from "@tiptap/core";
5
6
  import { Fragment } from "@tiptap/pm/model";
6
7
  //#region src/features/composites/merge.ts
7
8
  /**
8
- * What a merge appends to the first Baustein's body: the whole body of `next`, its own sub-Bausteine included, in document
9
- * order. A body that is one empty paragraph (a Baustein nothing was written in) adds nothing.
10
- * Null when `first`'s kind does not accept the kind of a Baustein nested in that body (the merge is refused).
11
- * Where it goes is the command's call: at the end, or, for a kind with `nestedAfterContent`, split by `splitAtSubComposites`.
9
+ * What a merge appends to the first Typed block's body: the whole body of `next`, its own nested Typed blocks included, in
10
+ * document order. A body that is one empty paragraph (a Typed block nothing was written in) adds nothing.
11
+ * Null when `first`'s Block type does not accept the Block type of a Typed block nested in that body (the merge is refused).
12
+ * Where it goes is the command's call: at the end, or, for a Block type with `nestedAfterContent`, split by `splitAtNested`.
12
13
  */
13
14
  function mergeBody(kinds, first, next) {
14
15
  let chrome = 0;
15
16
  for (let i = 0; i < 3; i++) chrome += next.child(i).nodeSize;
16
17
  const body = next.content.cut(chrome);
17
- const firstKind = String(first.attrs.kind);
18
- for (let i = 0; i < body.childCount; i++) for (const child of outermostComposites(body.child(i))) if (!kindAccepts(kinds, firstKind, String(child.attrs.kind))) return null;
18
+ const firstKind = String(first.attrs.blockType);
19
+ for (let i = 0; i < body.childCount; i++) for (const child of outermostComposites(body.child(i))) if (!acceptsChild(kinds, firstKind, String(child.attrs.blockType))) return null;
19
20
  const only = body.childCount === 1 ? body.firstChild : null;
20
21
  return only?.type.name === "paragraph" && only.content.size === 0 ? Fragment.empty : body;
21
22
  }
22
23
  /**
23
- * Splits a merge body (see `mergeBody`) at its first sub-Baustein: `content` is the leading run of blocks that are not
24
- * Bausteine, `nested` the rest from the first sub-Baustein on (empty when there is none).
24
+ * Splits a merge body (see `mergeBody`) at its first nested Typed block: `content` is the leading run of blocks that are not
25
+ * Typed blocks, `nested` the rest from the first nested Typed block on (empty when there is none).
25
26
  */
26
- function splitAtSubComposites(body) {
27
+ function splitAtNested(body) {
27
28
  let at = 0;
28
- while (at < body.childCount && body.child(at).type.name !== "composite") at++;
29
+ while (at < body.childCount && body.child(at).type.name !== "typedBlock") at++;
29
30
  let size = 0;
30
31
  for (let i = 0; i < at; i++) size += body.child(i).nodeSize;
31
32
  return {
@@ -33,7 +34,7 @@ function splitAtSubComposites(body) {
33
34
  nested: body.cut(size)
34
35
  };
35
36
  }
36
- /** Client-only, no schema: the merge command of Bausteine. */
37
+ /** Client-only, no schema: the merge command of Typed blocks. */
37
38
  const CompositeMerge = Extension.create({
38
39
  name: "compositeMerge",
39
40
  addCommands() {
@@ -43,25 +44,25 @@ const CompositeMerge = Extension.create({
43
44
  if (!found) return false;
44
45
  const $pos = state.doc.resolve(found.pos);
45
46
  const next = $pos.parent.maybeChild($pos.index() + 1);
46
- if (next?.type.name !== "composite") return false;
47
- const kinds = compositeKindsOf(this.editor);
48
- const hostManaged = (node) => compositeKind(kinds, String(node.attrs.kind)).chrome?.controls === false;
47
+ if (next?.type.name !== "typedBlock") return false;
48
+ const kinds = blockTypesOf(this.editor);
49
+ const hostManaged = (node) => blockTypeOf(kinds, String(node.attrs.blockType)).chrome?.controls === false;
49
50
  if (hostManaged(found.node) || hostManaged(next)) return false;
50
51
  const appended = mergeBody(kinds, found.node, next);
51
52
  if (!appended) return false;
52
53
  if (dispatch) {
53
54
  const nextPos = found.pos + found.node.nodeSize;
54
55
  const end = nextPos - 1;
55
- if (compositeKind(kinds, String(found.node.attrs.kind)).nestedAfterContent && appended.size > 0) {
56
- const { content, nested } = splitAtSubComposites(appended);
57
- let subPos = found.pos + 1;
56
+ if (nestedAfterContentOf(blockTypeOf(kinds, String(found.node.attrs.blockType))) && appended.size > 0) {
57
+ const { content, nested } = splitAtNested(appended);
58
+ let nestedPos = found.pos + 1;
58
59
  for (let i = 0; i < found.node.childCount; i++) {
59
60
  const child = found.node.child(i);
60
- if (i >= 3 && child.type.name === "composite") break;
61
- subPos += child.nodeSize;
61
+ if (i >= 3 && child.type.name === "typedBlock") break;
62
+ nestedPos += child.nodeSize;
62
63
  }
63
64
  tr.insert(end, nested);
64
- tr.insert(subPos, content);
65
+ tr.insert(nestedPos, content);
65
66
  } else tr.insert(end, appended);
66
67
  const shifted = nextPos + appended.size;
67
68
  tr.delete(shifted, shifted + next.nodeSize);
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","names":[],"sources":["../../../src/features/composites/merge.ts"],"sourcesContent":["// Merging a Baustein with its next sibling: the counterpart of the split. The pure part (`mergeBody`) knows nothing about\n// the editor; the `CompositeMerge` extension wraps it into the command `mergeCompositeWithNext`.\nimport { Extension } from '@tiptap/core'\nimport { Fragment, type Node as PMNode } from '@tiptap/pm/model'\nimport { outermostComposites } from './accepted-children'\nimport { type CompositeKind, compositeKind, kindAccepts } from './kinds'\nimport { COMPOSITE_CHROME_COUNT, compositeAt, compositeKindsOf } from './nodes'\n\n/**\n * What a merge appends to the first Baustein's body: the whole body of `next`, its own sub-Bausteine included, in document\n * order. A body that is one empty paragraph (a Baustein nothing was written in) adds nothing.\n * Null when `first`'s kind does not accept the kind of a Baustein nested in that body (the merge is refused).\n * Where it goes is the command's call: at the end, or, for a kind with `nestedAfterContent`, split by `splitAtSubComposites`.\n */\nexport function mergeBody(kinds: readonly CompositeKind[], first: PMNode, next: PMNode): Fragment | null {\n let chrome = 0\n for (let i = 0; i < COMPOSITE_CHROME_COUNT; i++) chrome += next.child(i).nodeSize\n const body = next.content.cut(chrome)\n const firstKind = String(first.attrs.kind)\n for (let i = 0; i < body.childCount; i++) {\n for (const child of outermostComposites(body.child(i))) {\n if (!kindAccepts(kinds, firstKind, String(child.attrs.kind))) return null\n }\n }\n const only = body.childCount === 1 ? body.firstChild! : null\n return only?.type.name === 'paragraph' && only.content.size === 0 ? Fragment.empty : body\n}\n\n/**\n * Splits a merge body (see `mergeBody`) at its first sub-Baustein: `content` is the leading run of blocks that are not\n * Bausteine, `nested` the rest from the first sub-Baustein on (empty when there is none).\n */\nfunction splitAtSubComposites(body: Fragment): { content: Fragment, nested: Fragment } {\n let at = 0\n while (at < body.childCount && body.child(at).type.name !== 'composite') at++\n let size = 0\n for (let i = 0; i < at; i++) size += body.child(i).nodeSize\n return { content: body.cut(0, size), nested: body.cut(size) }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n compositeMerge: {\n /**\n * The Baustein at `pos` (default: the caret's innermost one) absorbs the Baustein that directly follows it in the same parent: that one's body, with its sub-Bausteine, is appended to the end of this one's body and the following Baustein is gone. For a kind with `nestedAfterContent` the following body's own content (the leading blocks that are not Bausteine) goes in right before the first one's first sub-Baustein and only its sub-Bausteine go to the end, so the content stays ahead of the sub-Bausteine. Title, description, tags, id, kind and meta stay those of the first. One transaction. Refused when no Baustein follows, when the first kind does not accept a Baustein nested in the following one, when either Baustein has a kind with `chrome.controls === false` (host-managed), or when the editor is not editable.\n */\n mergeCompositeWithNext: (pos?: number) => ReturnType\n }\n }\n}\n\n/** Client-only, no schema: the merge command of Bausteine. */\nexport const CompositeMerge = Extension.create({\n name: 'compositeMerge',\n addCommands() {\n return {\n mergeCompositeWithNext: pos => ({ state, tr, dispatch }) => {\n if (!this.editor.isEditable) return false\n const found = compositeAt(state, pos)\n if (!found) return false\n const $pos = state.doc.resolve(found.pos)\n const next = $pos.parent.maybeChild($pos.index() + 1)\n if (next?.type.name !== 'composite') return false\n const kinds = compositeKindsOf(this.editor)\n // A kind with `chrome.controls === false` is host-managed: neither absorbing nor being absorbed is the editor's call.\n const hostManaged = (node: PMNode) => compositeKind(kinds, String(node.attrs.kind)).chrome?.controls === false\n if (hostManaged(found.node) || hostManaged(next)) return false\n const appended = mergeBody(kinds, found.node, next)\n if (!appended) return false\n if (dispatch) {\n const nextPos = found.pos + found.node.nodeSize\n const end = nextPos - 1\n // `nestedAfterContent`: the content of the second body goes ahead of the first one's sub-Bausteine, the sub-Bausteine of the second one to the end.\n if (compositeKind(kinds, String(found.node.attrs.kind)).nestedAfterContent && appended.size > 0) {\n const { content, nested } = splitAtSubComposites(appended)\n let subPos = found.pos + 1\n for (let i = 0; i < found.node.childCount; i++) {\n const child = found.node.child(i)\n if (i >= COMPOSITE_CHROME_COUNT && child.type.name === 'composite') break\n subPos += child.nodeSize\n }\n // Back to front, so no position moves under the next insert.\n tr.insert(end, nested)\n tr.insert(subPos, content)\n }\n else {\n // The body goes in at the end of the first Baustein, then the second one is cut out behind it.\n tr.insert(end, appended)\n }\n const shifted = nextPos + appended.size\n tr.delete(shifted, shifted + next.nodeSize)\n }\n return true\n },\n }\n },\n})\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,UAAU,OAAiC,OAAe,MAA+B;CACvG,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAA,GAA4B,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC;CACzE,MAAM,OAAO,KAAK,QAAQ,IAAI,MAAM;CACpC,MAAM,YAAY,OAAO,MAAM,MAAM,IAAI;CACzC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,YAAY,KACnC,KAAK,MAAM,SAAS,oBAAoB,KAAK,MAAM,CAAC,CAAC,GACnD,IAAI,CAAC,YAAY,OAAO,WAAW,OAAO,MAAM,MAAM,IAAI,CAAC,GAAG,OAAO;CAGzE,MAAM,OAAO,KAAK,eAAe,IAAI,KAAK,aAAc;CACxD,OAAO,MAAM,KAAK,SAAS,eAAe,KAAK,QAAQ,SAAS,IAAI,SAAS,QAAQ;AACvF;;;;;AAMA,SAAS,qBAAqB,MAAyD;CACrF,IAAI,KAAK;CACT,OAAO,KAAK,KAAK,cAAc,KAAK,MAAM,EAAE,CAAC,CAAC,KAAK,SAAS,aAAa;CACzE,IAAI,OAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC;CACnD,OAAO;EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;EAAG,QAAQ,KAAK,IAAI,IAAI;CAAE;AAC9D;;AAcA,MAAa,iBAAiB,UAAU,OAAO;CAC7C,MAAM;CACN,cAAc;EACZ,OAAO,EACL,yBAAwB,SAAQ,EAAE,OAAO,IAAI,eAAe;GAC1D,IAAI,CAAC,KAAK,OAAO,YAAY,OAAO;GACpC,MAAM,QAAQ,YAAY,OAAO,GAAG;GACpC,IAAI,CAAC,OAAO,OAAO;GACnB,MAAM,OAAO,MAAM,IAAI,QAAQ,MAAM,GAAG;GACxC,MAAM,OAAO,KAAK,OAAO,WAAW,KAAK,MAAM,IAAI,CAAC;GACpD,IAAI,MAAM,KAAK,SAAS,aAAa,OAAO;GAC5C,MAAM,QAAQ,iBAAiB,KAAK,MAAM;GAE1C,MAAM,eAAe,SAAiB,cAAc,OAAO,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,aAAa;GACzG,IAAI,YAAY,MAAM,IAAI,KAAK,YAAY,IAAI,GAAG,OAAO;GACzD,MAAM,WAAW,UAAU,OAAO,MAAM,MAAM,IAAI;GAClD,IAAI,CAAC,UAAU,OAAO;GACtB,IAAI,UAAU;IACZ,MAAM,UAAU,MAAM,MAAM,MAAM,KAAK;IACvC,MAAM,MAAM,UAAU;IAEtB,IAAI,cAAc,OAAO,OAAO,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,sBAAsB,SAAS,OAAO,GAAG;KAC/F,MAAM,EAAE,SAAS,WAAW,qBAAqB,QAAQ;KACzD,IAAI,SAAS,MAAM,MAAM;KACzB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,YAAY,KAAK;MAC9C,MAAM,QAAQ,MAAM,KAAK,MAAM,CAAC;MAChC,IAAI,KAAA,KAA+B,MAAM,KAAK,SAAS,aAAa;MACpE,UAAU,MAAM;KAClB;KAEA,GAAG,OAAO,KAAK,MAAM;KACrB,GAAG,OAAO,QAAQ,OAAO;IAC3B,OAGE,GAAG,OAAO,KAAK,QAAQ;IAEzB,MAAM,UAAU,UAAU,SAAS;IACnC,GAAG,OAAO,SAAS,UAAU,KAAK,QAAQ;GAC5C;GACA,OAAO;EACT,EACF;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"merge.js","names":[],"sources":["../../../src/features/composites/merge.ts"],"sourcesContent":["// Merging a Typed block with its next sibling: the counterpart of the split. The pure part (`mergeBody`) knows nothing about\n// the editor; the `CompositeMerge` extension wraps it into the command `mergeCompositeWithNext`.\nimport { Extension } from '@tiptap/core'\nimport { Fragment, type Node as PMNode } from '@tiptap/pm/model'\nimport { TYPED_BLOCK } from '../../core/schema'\nimport { outermostComposites } from './accepted-children'\nimport { acceptsChild, type BlockType, blockTypeOf, nestedAfterContentOf } from './block-types'\nimport { COMPOSITE_CHROME_COUNT, compositeAt, blockTypesOf } from './nodes'\n\n/**\n * What a merge appends to the first Typed block's body: the whole body of `next`, its own nested Typed blocks included, in\n * document order. A body that is one empty paragraph (a Typed block nothing was written in) adds nothing.\n * Null when `first`'s Block type does not accept the Block type of a Typed block nested in that body (the merge is refused).\n * Where it goes is the command's call: at the end, or, for a Block type with `nestedAfterContent`, split by `splitAtNested`.\n */\nexport function mergeBody(kinds: readonly BlockType[], first: PMNode, next: PMNode): Fragment | null {\n let chrome = 0\n for (let i = 0; i < COMPOSITE_CHROME_COUNT; i++) chrome += next.child(i).nodeSize\n const body = next.content.cut(chrome)\n const firstKind = String(first.attrs.blockType)\n for (let i = 0; i < body.childCount; i++) {\n for (const child of outermostComposites(body.child(i))) {\n if (!acceptsChild(kinds, firstKind, String(child.attrs.blockType))) return null\n }\n }\n const only = body.childCount === 1 ? body.firstChild! : null\n return only?.type.name === 'paragraph' && only.content.size === 0 ? Fragment.empty : body\n}\n\n/**\n * Splits a merge body (see `mergeBody`) at its first nested Typed block: `content` is the leading run of blocks that are not\n * Typed blocks, `nested` the rest from the first nested Typed block on (empty when there is none).\n */\nfunction splitAtNested(body: Fragment): { content: Fragment, nested: Fragment } {\n let at = 0\n while (at < body.childCount && body.child(at).type.name !== TYPED_BLOCK) at++\n let size = 0\n for (let i = 0; i < at; i++) size += body.child(i).nodeSize\n return { content: body.cut(0, size), nested: body.cut(size) }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n compositeMerge: {\n /**\n * The Typed block at `pos` (default: the caret's innermost one) absorbs the Typed block that directly follows it in the same parent: that one's body, with its nested Typed blocks, is appended to the end of this one's body and the following Typed block is gone. For a Block type with `nestedAfterContent` (its `container` config) the following body's own content (the leading blocks that are not Typed blocks) goes in right before the first one's first nested Typed block and only its nested Typed blocks go to the end, so the content stays ahead of the nested Typed blocks. Title, description, tags, id, Block type and meta stay those of the first. One transaction. Refused when no Typed block follows, when the first Block type does not accept a Typed block nested in the following one, when either Typed block has a Block type with `chrome.controls === false` (host-managed), or when the editor is not editable.\n */\n mergeCompositeWithNext: (pos?: number) => ReturnType\n }\n }\n}\n\n/** Client-only, no schema: the merge command of Typed blocks. */\nexport const CompositeMerge = Extension.create({\n name: 'compositeMerge',\n addCommands() {\n return {\n mergeCompositeWithNext: pos => ({ state, tr, dispatch }) => {\n if (!this.editor.isEditable) return false\n const found = compositeAt(state, pos)\n if (!found) return false\n const $pos = state.doc.resolve(found.pos)\n const next = $pos.parent.maybeChild($pos.index() + 1)\n if (next?.type.name !== TYPED_BLOCK) return false\n const kinds = blockTypesOf(this.editor)\n // A kind with `chrome.controls === false` is host-managed: neither absorbing nor being absorbed is the editor's call.\n const hostManaged = (node: PMNode) => blockTypeOf(kinds, String(node.attrs.blockType)).chrome?.controls === false\n if (hostManaged(found.node) || hostManaged(next)) return false\n const appended = mergeBody(kinds, found.node, next)\n if (!appended) return false\n if (dispatch) {\n const nextPos = found.pos + found.node.nodeSize\n const end = nextPos - 1\n // `nestedAfterContent`: the content of the second body goes ahead of the first one's nested Typed blocks, the nested Typed blocks of the second one to the end.\n if (nestedAfterContentOf(blockTypeOf(kinds, String(found.node.attrs.blockType))) && appended.size > 0) {\n const { content, nested } = splitAtNested(appended)\n let nestedPos = found.pos + 1\n for (let i = 0; i < found.node.childCount; i++) {\n const child = found.node.child(i)\n if (i >= COMPOSITE_CHROME_COUNT && child.type.name === TYPED_BLOCK) break\n nestedPos += child.nodeSize\n }\n // Back to front, so no position moves under the next insert.\n tr.insert(end, nested)\n tr.insert(nestedPos, content)\n }\n else {\n // The body goes in at the end of the first Typed block, then the second one is cut out behind it.\n tr.insert(end, appended)\n }\n const shifted = nextPos + appended.size\n tr.delete(shifted, shifted + next.nodeSize)\n }\n return true\n },\n }\n },\n})\n"],"mappings":";;;;;;;;;;;;;AAeA,SAAgB,UAAU,OAA6B,OAAe,MAA+B;CACnG,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAA,GAA4B,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC;CACzE,MAAM,OAAO,KAAK,QAAQ,IAAI,MAAM;CACpC,MAAM,YAAY,OAAO,MAAM,MAAM,SAAS;CAC9C,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,YAAY,KACnC,KAAK,MAAM,SAAS,oBAAoB,KAAK,MAAM,CAAC,CAAC,GACnD,IAAI,CAAC,aAAa,OAAO,WAAW,OAAO,MAAM,MAAM,SAAS,CAAC,GAAG,OAAO;CAG/E,MAAM,OAAO,KAAK,eAAe,IAAI,KAAK,aAAc;CACxD,OAAO,MAAM,KAAK,SAAS,eAAe,KAAK,QAAQ,SAAS,IAAI,SAAS,QAAQ;AACvF;;;;;AAMA,SAAS,cAAc,MAAyD;CAC9E,IAAI,KAAK;CACT,OAAO,KAAK,KAAK,cAAc,KAAK,MAAM,EAAE,CAAC,CAAC,KAAK,SAAA,cAAsB;CACzE,IAAI,OAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC;CACnD,OAAO;EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;EAAG,QAAQ,KAAK,IAAI,IAAI;CAAE;AAC9D;;AAcA,MAAa,iBAAiB,UAAU,OAAO;CAC7C,MAAM;CACN,cAAc;EACZ,OAAO,EACL,yBAAwB,SAAQ,EAAE,OAAO,IAAI,eAAe;GAC1D,IAAI,CAAC,KAAK,OAAO,YAAY,OAAO;GACpC,MAAM,QAAQ,YAAY,OAAO,GAAG;GACpC,IAAI,CAAC,OAAO,OAAO;GACnB,MAAM,OAAO,MAAM,IAAI,QAAQ,MAAM,GAAG;GACxC,MAAM,OAAO,KAAK,OAAO,WAAW,KAAK,MAAM,IAAI,CAAC;GACpD,IAAI,MAAM,KAAK,SAAA,cAAsB,OAAO;GAC5C,MAAM,QAAQ,aAAa,KAAK,MAAM;GAEtC,MAAM,eAAe,SAAiB,YAAY,OAAO,OAAO,KAAK,MAAM,SAAS,CAAC,CAAC,CAAC,QAAQ,aAAa;GAC5G,IAAI,YAAY,MAAM,IAAI,KAAK,YAAY,IAAI,GAAG,OAAO;GACzD,MAAM,WAAW,UAAU,OAAO,MAAM,MAAM,IAAI;GAClD,IAAI,CAAC,UAAU,OAAO;GACtB,IAAI,UAAU;IACZ,MAAM,UAAU,MAAM,MAAM,MAAM,KAAK;IACvC,MAAM,MAAM,UAAU;IAEtB,IAAI,qBAAqB,YAAY,OAAO,OAAO,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,KAAK,SAAS,OAAO,GAAG;KACrG,MAAM,EAAE,SAAS,WAAW,cAAc,QAAQ;KAClD,IAAI,YAAY,MAAM,MAAM;KAC5B,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,YAAY,KAAK;MAC9C,MAAM,QAAQ,MAAM,KAAK,MAAM,CAAC;MAChC,IAAI,KAAA,KAA+B,MAAM,KAAK,SAAA,cAAsB;MACpE,aAAa,MAAM;KACrB;KAEA,GAAG,OAAO,KAAK,MAAM;KACrB,GAAG,OAAO,WAAW,OAAO;IAC9B,OAGE,GAAG,OAAO,KAAK,QAAQ;IAEzB,MAAM,UAAU,UAAU,SAAS;IACnC,GAAG,OAAO,SAAS,UAAU,KAAK,QAAQ;GAC5C;GACA,OAAO;EACT,EACF;CACF;AACF,CAAC"}