@ecsplico/editor 3.5.1 → 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 (583) hide show
  1. package/CHANGELOG.md +71 -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/code/CodeSurface.vue.d.ts +3 -3
  15. package/dist/config/index.d.ts +14 -14
  16. package/dist/config/index.js +8 -8
  17. package/dist/config/index.js.map +1 -1
  18. package/dist/core/backend-types.d.ts +3 -3
  19. package/dist/core/backend-types.js.map +1 -1
  20. package/dist/core/backend.d.ts +4 -4
  21. package/dist/core/backend.js.map +1 -1
  22. package/dist/core/block-convert.d.ts +1 -1
  23. package/dist/core/block-convert.js +5 -5
  24. package/dist/core/block-convert.js.map +1 -1
  25. package/dist/core/block-transfer.d.ts +8 -4
  26. package/dist/core/block-transfer.js +23 -14
  27. package/dist/core/block-transfer.js.map +1 -1
  28. package/dist/core/capabilities.d.ts +113 -0
  29. package/dist/core/capabilities.js +155 -0
  30. package/dist/core/capabilities.js.map +1 -0
  31. package/dist/core/command-types.d.ts +1 -1
  32. package/dist/core/context.js.map +1 -1
  33. package/dist/core/convert.d.ts +5 -5
  34. package/dist/core/convert.js +5 -5
  35. package/dist/core/convert.js.map +1 -1
  36. package/dist/core/core-feature.js +1 -1
  37. package/dist/core/define-editor.d.ts +2 -2
  38. package/dist/core/define-editor.js +34 -6
  39. package/dist/core/define-editor.js.map +1 -1
  40. package/dist/core/derivation.d.ts +119 -0
  41. package/dist/core/derivation.js +187 -0
  42. package/dist/core/derivation.js.map +1 -0
  43. package/dist/core/derive.d.ts +61 -0
  44. package/dist/core/derive.js +69 -0
  45. package/dist/core/derive.js.map +1 -0
  46. package/dist/core/extension-points.d.ts +16 -0
  47. package/dist/core/feature-context.d.ts +5 -1
  48. package/dist/core/feature-context.js +10 -3
  49. package/dist/core/feature-context.js.map +1 -1
  50. package/dist/core/feature.d.ts +41 -19
  51. package/dist/core/feature.js.map +1 -1
  52. package/dist/core/fences.js +1 -1
  53. package/dist/core/fences.js.map +1 -1
  54. package/dist/core/index.d.ts +6 -2
  55. package/dist/core/index.js +5 -1
  56. package/dist/core/pipeline.d.ts +14 -5
  57. package/dist/core/pipeline.js +19 -7
  58. package/dist/core/pipeline.js.map +1 -1
  59. package/dist/core/properties.d.ts +22 -0
  60. package/dist/core/properties.js +17 -0
  61. package/dist/core/properties.js.map +1 -0
  62. package/dist/core/schema/block-ids.js +5 -5
  63. package/dist/core/schema/block-ids.js.map +1 -1
  64. package/dist/core/schema/index.d.ts +2 -1
  65. package/dist/core/schema/index.js +2 -1
  66. package/dist/core/schema/index.js.map +1 -1
  67. package/dist/core/schema/opaque-fence.d.ts +4 -4
  68. package/dist/core/schema/opaque-fence.js +7 -7
  69. package/dist/core/schema/opaque-fence.js.map +1 -1
  70. package/dist/core/schema/persisted-names.d.ts +58 -0
  71. package/dist/core/schema/persisted-names.js +96 -0
  72. package/dist/core/schema/persisted-names.js.map +1 -0
  73. package/dist/core/toolbar-items.js +1 -1
  74. package/dist/core/toolbar-items.js.map +1 -1
  75. package/dist/core/util.js +5 -1
  76. package/dist/core/util.js.map +1 -1
  77. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  78. package/dist/features/ai/agent-ops.js +1 -1
  79. package/dist/features/ai/ai-review.js +1 -1
  80. package/dist/features/ai/assistant.d.ts +4 -4
  81. package/dist/features/ai/assistant.js +1 -1
  82. package/dist/features/ai/assistant.js.map +1 -1
  83. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  84. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
  85. package/dist/features/ai/components/ModelQuickSwitch.js +7 -7
  86. package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -1
  87. package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +4 -4
  88. package/dist/features/ai/components/SettingsAiTab.js +8 -8
  89. package/dist/features/ai/components/SettingsAiTab.js.map +1 -1
  90. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  91. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  92. package/dist/features/ai/document-chat-model.d.ts +2 -2
  93. package/dist/features/ai/document-chat-model.js +5 -5
  94. package/dist/features/ai/document-chat-model.js.map +1 -1
  95. package/dist/features/ai/document-chat.js +2 -2
  96. package/dist/features/ai/document-chat.js.map +1 -1
  97. package/dist/features/ai/feature.d.ts +2 -2
  98. package/dist/features/ai/feature.js +2 -2
  99. package/dist/features/ai/feature.js.map +1 -1
  100. package/dist/features/ai/ghost-autocomplete.d.ts +4 -4
  101. package/dist/features/ai/ghost-autocomplete.js +1 -1
  102. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  103. package/dist/features/ai/index.d.ts +3 -3
  104. package/dist/features/ai/index.js +2 -2
  105. package/dist/features/ai/model-profiles.d.ts +9 -9
  106. package/dist/features/ai/model-profiles.js +13 -13
  107. package/dist/features/ai/model-profiles.js.map +1 -1
  108. package/dist/features/ai/protocol/agent-ops.d.ts +2 -2
  109. package/dist/features/ai/protocol/agent-ops.js +1 -1
  110. package/dist/features/ai/protocol/agent-ops.js.map +1 -1
  111. package/dist/features/ai/protocol/block-lines.d.ts +7 -7
  112. package/dist/features/ai/protocol/block-lines.js +10 -10
  113. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  114. package/dist/features/ai/protocol/chat.d.ts +5 -5
  115. package/dist/features/ai/protocol/chat.js +12 -12
  116. package/dist/features/ai/protocol/chat.js.map +1 -1
  117. package/dist/features/ai/protocol/generate.d.ts +1 -1
  118. package/dist/features/ai/protocol/generate.js +1 -1
  119. package/dist/features/ai/protocol/generate.js.map +1 -1
  120. package/dist/features/ai/protocol/index.d.ts +3 -3
  121. package/dist/features/ai/protocol/index.js +2 -2
  122. package/dist/features/ai/protocol/models.d.ts +3 -3
  123. package/dist/features/ai/protocol/models.js +3 -3
  124. package/dist/features/ai/protocol/models.js.map +1 -1
  125. package/dist/features/ai/protocol/schemas.d.ts +1 -1
  126. package/dist/features/ai/protocol/schemas.js +1 -1
  127. package/dist/features/ai/protocol/schemas.js.map +1 -1
  128. package/dist/features/ai/protocol/tasks.d.ts +5 -5
  129. package/dist/features/ai/protocol/tasks.js +12 -12
  130. package/dist/features/ai/protocol/tasks.js.map +1 -1
  131. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
  132. package/dist/features/ai/react/ModelQuickSwitch.d.ts +4 -4
  133. package/dist/features/ai/react/ModelQuickSwitch.js +6 -6
  134. package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -1
  135. package/dist/features/ai/react/SettingsAiTab.d.ts +1 -1
  136. package/dist/features/ai/react/SettingsAiTab.js +8 -8
  137. package/dist/features/ai/react/SettingsAiTab.js.map +1 -1
  138. package/dist/features/ai/react/SettingsModelProfiles.d.ts +1 -1
  139. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  140. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  141. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  142. package/dist/features/ai/react/use-ai-assistant.js +1 -1
  143. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  144. package/dist/features/ai/react/use-model-profiles.d.ts +5 -5
  145. package/dist/features/ai/react/use-model-profiles.js +2 -2
  146. package/dist/features/ai/react/use-model-profiles.js.map +1 -1
  147. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  148. package/dist/features/ai/use-ai-assistant.js +1 -1
  149. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  150. package/dist/features/ai/use-model-profiles.d.ts +4 -4
  151. package/dist/features/ai/use-model-profiles.js +2 -2
  152. package/dist/features/ai/use-model-profiles.js.map +1 -1
  153. package/dist/features/block-ui/block-actions.d.ts +8 -4
  154. package/dist/features/block-ui/block-actions.js +38 -22
  155. package/dist/features/block-ui/block-actions.js.map +1 -1
  156. package/dist/features/block-ui/block-target.d.ts +29 -0
  157. package/dist/features/block-ui/block-target.js +63 -0
  158. package/dist/features/block-ui/block-target.js.map +1 -0
  159. package/dist/features/block-ui/components/BlockHandles.js +3 -3
  160. package/dist/features/block-ui/components/BlockHandles.js.map +1 -1
  161. package/dist/features/block-ui/components/BlockUi.js +11 -23
  162. package/dist/features/block-ui/components/BlockUi.js.map +1 -1
  163. package/dist/features/block-ui/feature.d.ts +3 -2
  164. package/dist/features/block-ui/feature.js +3 -2
  165. package/dist/features/block-ui/feature.js.map +1 -1
  166. package/dist/features/block-ui/handle-geometry.js +12 -23
  167. package/dist/features/block-ui/handle-geometry.js.map +1 -1
  168. package/dist/features/block-ui/index.d.ts +2 -1
  169. package/dist/features/block-ui/index.js +2 -1
  170. package/dist/features/block-ui/react/BlockHandles.d.ts +1 -1
  171. package/dist/features/block-ui/react/BlockHandles.js +4 -4
  172. package/dist/features/block-ui/react/BlockHandles.js.map +1 -1
  173. package/dist/features/block-ui/react/BlockUi.js +13 -25
  174. package/dist/features/block-ui/react/BlockUi.js.map +1 -1
  175. package/dist/features/comments/model.js +1 -1
  176. package/dist/features/composites/accepted-children.d.ts +5 -4
  177. package/dist/features/composites/accepted-children.js +26 -26
  178. package/dist/features/composites/accepted-children.js.map +1 -1
  179. package/dist/features/composites/ai-brief.d.ts +4 -4
  180. package/dist/features/composites/ai-brief.js +14 -12
  181. package/dist/features/composites/ai-brief.js.map +1 -1
  182. package/dist/features/composites/anchors.d.ts +1 -1
  183. package/dist/features/composites/anchors.js.map +1 -1
  184. package/dist/features/composites/baskets.d.ts +1 -1
  185. package/dist/features/composites/baskets.js.map +1 -1
  186. package/dist/features/composites/bibliography-command.d.ts +1 -1
  187. package/dist/features/composites/bibliography-form.js +1 -1
  188. package/dist/features/composites/bibliography-form.js.map +1 -1
  189. package/dist/features/composites/bibliography.js.map +1 -1
  190. package/dist/features/composites/block-types-json.d.ts +38 -0
  191. package/dist/features/composites/block-types-json.js +122 -0
  192. package/dist/features/composites/block-types-json.js.map +1 -0
  193. package/dist/features/composites/block-types.d.ts +119 -0
  194. package/dist/features/composites/block-types.js +343 -0
  195. package/dist/features/composites/block-types.js.map +1 -0
  196. package/dist/features/composites/capabilities.js +109 -0
  197. package/dist/features/composites/capabilities.js.map +1 -0
  198. package/dist/features/composites/components/BibliographyEditor.js +5 -5
  199. package/dist/features/composites/components/BibliographyEditor.js.map +1 -1
  200. package/dist/features/composites/components/CompositeBasketMenu.js +2 -2
  201. package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -1
  202. package/dist/features/composites/components/CompositeChrome.js +18 -8
  203. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  204. package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
  205. package/dist/features/composites/components/CompositeDetails.js +11 -10
  206. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  207. package/dist/features/composites/components/CompositeDetails.vue.d.ts +1 -1
  208. package/dist/features/composites/components/CompositeFieldEditor.js +7 -7
  209. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  210. package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +5 -5
  211. package/dist/features/composites/components/CompositeInspector.js +136 -113
  212. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  213. package/dist/features/composites/components/CompositeInspector.vue.d.ts +3 -3
  214. package/dist/features/composites/components/CompositeStatusMenu.js +2 -2
  215. package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -1
  216. package/dist/features/composites/components/CompositeSummaryEditor.js +1 -1
  217. package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -1
  218. package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +1 -1
  219. package/dist/features/composites/components/CompositeTable.js +2 -2
  220. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  221. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  222. package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -1
  223. package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +1 -1
  224. package/dist/features/composites/components/CompositeView.js +19 -17
  225. package/dist/features/composites/components/CompositeView.js.map +1 -1
  226. package/dist/features/composites/components/DerivationBadge.js +15 -6
  227. package/dist/features/composites/components/DerivationBadge.js.map +1 -1
  228. package/dist/features/composites/components/FocusChip.js +1 -1
  229. package/dist/features/composites/components/FocusChip.js.map +1 -1
  230. package/dist/features/composites/components/FocusChip.vue.d.ts +1 -1
  231. package/dist/features/composites/components/ReferenceFieldSelect.js +3 -3
  232. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -1
  233. package/dist/features/composites/components/ReferenceView.js +1 -1
  234. package/dist/features/composites/components/ReferenceView.js.map +1 -1
  235. package/dist/features/composites/components/SettingsEditingTab.js +18 -18
  236. package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
  237. package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +3 -3
  238. package/dist/features/composites/convert.d.ts +4 -4
  239. package/dist/features/composites/convert.js +27 -28
  240. package/dist/features/composites/convert.js.map +1 -1
  241. package/dist/features/composites/derived-chrome.js +34 -28
  242. package/dist/features/composites/derived-chrome.js.map +1 -1
  243. package/dist/features/composites/editing-settings-keys.js +6 -6
  244. package/dist/features/composites/editing-settings-keys.js.map +1 -1
  245. package/dist/features/composites/feature.d.ts +10 -10
  246. package/dist/features/composites/feature.js +58 -46
  247. package/dist/features/composites/feature.js.map +1 -1
  248. package/dist/features/composites/fences.d.ts +3 -3
  249. package/dist/features/composites/fences.js +18 -13
  250. package/dist/features/composites/fences.js.map +1 -1
  251. package/dist/features/composites/focus.d.ts +10 -10
  252. package/dist/features/composites/focus.js +11 -11
  253. package/dist/features/composites/focus.js.map +1 -1
  254. package/dist/features/composites/heading-composites.d.ts +5 -5
  255. package/dist/features/composites/heading-composites.js +13 -16
  256. package/dist/features/composites/heading-composites.js.map +1 -1
  257. package/dist/features/composites/index.d.ts +20 -18
  258. package/dist/features/composites/index.js +9 -7
  259. package/dist/features/composites/inspector-model.d.ts +45 -22
  260. package/dist/features/composites/inspector-model.js +53 -14
  261. package/dist/features/composites/inspector-model.js.map +1 -1
  262. package/dist/features/composites/legacy.d.ts +5 -4
  263. package/dist/features/composites/legacy.js +16 -23
  264. package/dist/features/composites/legacy.js.map +1 -1
  265. package/dist/features/composites/markdown-export.js +3 -3
  266. package/dist/features/composites/markdown-export.js.map +1 -1
  267. package/dist/features/composites/merge.d.ts +8 -7
  268. package/dist/features/composites/merge.js +39 -12
  269. package/dist/features/composites/merge.js.map +1 -1
  270. package/dist/features/composites/meta-display.js +5 -5
  271. package/dist/features/composites/meta-display.js.map +1 -1
  272. package/dist/features/composites/meta-keys.d.ts +5 -5
  273. package/dist/features/composites/meta-keys.js +8 -8
  274. package/dist/features/composites/meta-keys.js.map +1 -1
  275. package/dist/features/composites/node-views.d.ts +3 -3
  276. package/dist/features/composites/node-views.js +2 -2
  277. package/dist/features/composites/node-views.js.map +1 -1
  278. package/dist/features/composites/nodes.d.ts +13 -13
  279. package/dist/features/composites/nodes.js +100 -77
  280. package/dist/features/composites/nodes.js.map +1 -1
  281. package/dist/features/composites/outline.d.ts +15 -12
  282. package/dist/features/composites/outline.js +24 -21
  283. package/dist/features/composites/outline.js.map +1 -1
  284. package/dist/features/composites/properties.d.ts +51 -0
  285. package/dist/features/composites/properties.js +89 -0
  286. package/dist/features/composites/properties.js.map +1 -0
  287. package/dist/features/composites/react/BibliographyEditor.js +5 -5
  288. package/dist/features/composites/react/BibliographyEditor.js.map +1 -1
  289. package/dist/features/composites/react/CompositeBasketMenu.js +2 -2
  290. package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -1
  291. package/dist/features/composites/react/CompositeChrome.d.ts +1 -1
  292. package/dist/features/composites/react/CompositeChrome.js +15 -6
  293. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  294. package/dist/features/composites/react/CompositeDetails.d.ts +1 -1
  295. package/dist/features/composites/react/CompositeDetails.js +11 -10
  296. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  297. package/dist/features/composites/react/CompositeFieldEditor.d.ts +5 -5
  298. package/dist/features/composites/react/CompositeFieldEditor.js +6 -6
  299. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  300. package/dist/features/composites/react/CompositeInspector.d.ts +3 -3
  301. package/dist/features/composites/react/CompositeInspector.js +142 -128
  302. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  303. package/dist/features/composites/react/CompositeStatusMenu.js +2 -2
  304. package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -1
  305. package/dist/features/composites/react/CompositeSummaryEditor.d.ts +1 -1
  306. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  307. package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -1
  308. package/dist/features/composites/react/CompositeTable.d.ts +1 -1
  309. package/dist/features/composites/react/CompositeTable.js +3 -3
  310. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  311. package/dist/features/composites/react/CompositeTagsEditor.d.ts +1 -1
  312. package/dist/features/composites/react/CompositeTagsEditor.js +1 -1
  313. package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -1
  314. package/dist/features/composites/react/CompositeView.js +17 -15
  315. package/dist/features/composites/react/CompositeView.js.map +1 -1
  316. package/dist/features/composites/react/DerivationBadge.js +13 -4
  317. package/dist/features/composites/react/DerivationBadge.js.map +1 -1
  318. package/dist/features/composites/react/FocusChip.d.ts +1 -1
  319. package/dist/features/composites/react/FocusChip.js +1 -1
  320. package/dist/features/composites/react/FocusChip.js.map +1 -1
  321. package/dist/features/composites/react/ReferenceFieldSelect.js +3 -3
  322. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -1
  323. package/dist/features/composites/react/ReferenceView.d.ts +1 -1
  324. package/dist/features/composites/react/ReferenceView.js +1 -1
  325. package/dist/features/composites/react/ReferenceView.js.map +1 -1
  326. package/dist/features/composites/react/SettingsEditingTab.d.ts +4 -4
  327. package/dist/features/composites/react/SettingsEditingTab.js +20 -20
  328. package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
  329. package/dist/features/composites/react.js +1 -1
  330. package/dist/features/composites/react.js.map +1 -1
  331. package/dist/features/composites/reference-ai.js +1 -1
  332. package/dist/features/composites/reference-ai.js.map +1 -1
  333. package/dist/features/composites/reference-label.d.ts +3 -3
  334. package/dist/features/composites/reference-label.js +6 -6
  335. package/dist/features/composites/reference-label.js.map +1 -1
  336. package/dist/features/composites/reference-suggestion.d.ts +1 -1
  337. package/dist/features/composites/reference-suggestion.js +3 -3
  338. package/dist/features/composites/reference-suggestion.js.map +1 -1
  339. package/dist/features/composites/reference-text.d.ts +2 -2
  340. package/dist/features/composites/reference-text.js +4 -4
  341. package/dist/features/composites/reference-text.js.map +1 -1
  342. package/dist/features/composites/reference.d.ts +3 -3
  343. package/dist/features/composites/reference.js +1 -1
  344. package/dist/features/composites/reference.js.map +1 -1
  345. package/dist/features/composites/reveal-source.d.ts +3 -3
  346. package/dist/features/composites/reveal-source.js +6 -6
  347. package/dist/features/composites/reveal-source.js.map +1 -1
  348. package/dist/features/composites/rows-model.d.ts +2 -2
  349. package/dist/features/composites/rows-model.js +4 -3
  350. package/dist/features/composites/rows-model.js.map +1 -1
  351. package/dist/features/composites/rows.d.ts +16 -28
  352. package/dist/features/composites/rows.js +20 -54
  353. package/dist/features/composites/rows.js.map +1 -1
  354. package/dist/features/composites/schema.d.ts +12 -11
  355. package/dist/features/composites/schema.js +10 -9
  356. package/dist/features/composites/schema.js.map +1 -1
  357. package/dist/features/composites/source.d.ts +20 -0
  358. package/dist/features/composites/source.js +40 -0
  359. package/dist/features/composites/source.js.map +1 -0
  360. package/dist/features/composites/split.d.ts +11 -11
  361. package/dist/features/composites/split.js +20 -20
  362. package/dist/features/composites/split.js.map +1 -1
  363. package/dist/features/composites/structure-mode.js +3 -3
  364. package/dist/features/composites/structure-mode.js.map +1 -1
  365. package/dist/features/composites/table-model.js +2 -2
  366. package/dist/features/composites/table-model.js.map +1 -1
  367. package/dist/features/composites/use-composite-inspector.js +3 -1
  368. package/dist/features/composites/use-composite-inspector.js.map +1 -1
  369. package/dist/features/composites/use-composite-rows.d.ts +2 -2
  370. package/dist/features/composites/use-composite-rows.js.map +1 -1
  371. package/dist/features/composites/vue.js +1 -1
  372. package/dist/features/composites/vue.js.map +1 -1
  373. package/dist/features/image-studio/components/ImageStudioDialog.js +1 -1
  374. package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
  375. package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +1 -1
  376. package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
  377. package/dist/features/image-studio/feature.d.ts +1 -1
  378. package/dist/features/image-studio/feature.js +1 -1
  379. package/dist/features/image-studio/feature.js.map +1 -1
  380. package/dist/features/image-studio/node-core.d.ts +3 -3
  381. package/dist/features/image-studio/node-core.js +1 -1
  382. package/dist/features/image-studio/node-core.js.map +1 -1
  383. package/dist/features/image-studio/node-store.d.ts +1 -1
  384. package/dist/features/image-studio/node-store.js +1 -1
  385. package/dist/features/image-studio/node-store.js.map +1 -1
  386. package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
  387. package/dist/features/image-studio/react/ImageStudioDialog.js +1 -1
  388. package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
  389. package/dist/features/image-studio/react/ImageStudioView.js +1 -1
  390. package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
  391. package/dist/features/image-studio/react/store.d.ts +3 -3
  392. package/dist/features/image-studio/react/store.js.map +1 -1
  393. package/dist/features/image-studio/react.d.ts +1 -1
  394. package/dist/features/image-studio/react.js +1 -1
  395. package/dist/features/image-studio/react.js.map +1 -1
  396. package/dist/features/image-studio/schema.d.ts +1 -1
  397. package/dist/features/image-studio/schema.js +1 -1
  398. package/dist/features/image-studio/schema.js.map +1 -1
  399. package/dist/features/image-studio/state.d.ts +3 -3
  400. package/dist/features/image-studio/state.js +2 -2
  401. package/dist/features/image-studio/state.js.map +1 -1
  402. package/dist/features/image-studio/store.d.ts +3 -3
  403. package/dist/features/image-studio/vue.d.ts +1 -1
  404. package/dist/features/image-studio/vue.js +1 -1
  405. package/dist/features/image-studio/vue.js.map +1 -1
  406. package/dist/features/media/index.js +1 -1
  407. package/dist/features/media/react/AudioRecorderModal.js +1 -1
  408. package/dist/features/media/react.js +1 -1
  409. package/dist/features/mentions/tag.js +2 -1
  410. package/dist/features/mentions/tag.js.map +1 -1
  411. package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
  412. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  413. package/dist/features/themes/feature.d.ts +1 -1
  414. package/dist/features/themes/feature.js.map +1 -1
  415. package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
  416. package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
  417. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  418. package/dist/features/themes/theme-runtime.js +1 -1
  419. package/dist/features/themes/theme-runtime.js.map +1 -1
  420. package/dist/features/transcript/TranscriptSegmentView.js +1 -1
  421. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  422. package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
  423. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  424. package/dist/features/transcript/segment-node.js.map +1 -1
  425. package/dist/features/transfer/feature.js +1 -1
  426. package/dist/features/transfer/feature.js.map +1 -1
  427. package/dist/features/transfer/transforms.d.ts +4 -4
  428. package/dist/features/transfer/transforms.js +12 -12
  429. package/dist/features/transfer/transforms.js.map +1 -1
  430. package/dist/i18n/de/ai.js +4 -4
  431. package/dist/i18n/de/ai.js.map +1 -1
  432. package/dist/i18n/de/authors.js +1 -1
  433. package/dist/i18n/de/authors.js.map +1 -1
  434. package/dist/i18n/de/comments.js +1 -1
  435. package/dist/i18n/de/comments.js.map +1 -1
  436. package/dist/i18n/de/composites.js +62 -59
  437. package/dist/i18n/de/composites.js.map +1 -1
  438. package/dist/i18n/de/image-studio.js +2 -2
  439. package/dist/i18n/de/image-studio.js.map +1 -1
  440. package/dist/i18n/de/integrations.js +20 -20
  441. package/dist/i18n/de/integrations.js.map +1 -1
  442. package/dist/i18n/de/markdown.js +1 -1
  443. package/dist/i18n/de/markdown.js.map +1 -1
  444. package/dist/i18n/de/palette.js +1 -1
  445. package/dist/i18n/de/palette.js.map +1 -1
  446. package/dist/i18n/de/themes.js +3 -3
  447. package/dist/i18n/de/themes.js.map +1 -1
  448. package/dist/i18n/de/transfer.js +2 -2
  449. package/dist/i18n/de/transfer.js.map +1 -1
  450. package/dist/i18n/de/tree.js +6 -6
  451. package/dist/i18n/de/tree.js.map +1 -1
  452. package/dist/i18n/de.d.ts +101 -98
  453. package/dist/i18n/en/ai.js +2 -2
  454. package/dist/i18n/en/ai.js.map +1 -1
  455. package/dist/i18n/en/composites.js +64 -61
  456. package/dist/i18n/en/composites.js.map +1 -1
  457. package/dist/i18n/en/integrations.js +9 -9
  458. package/dist/i18n/en/integrations.js.map +1 -1
  459. package/dist/i18n/en/markdown.js +1 -1
  460. package/dist/i18n/en/markdown.js.map +1 -1
  461. package/dist/i18n/en/palette.js +1 -1
  462. package/dist/i18n/en/palette.js.map +1 -1
  463. package/dist/i18n/en/themes.js +4 -4
  464. package/dist/i18n/en/themes.js.map +1 -1
  465. package/dist/i18n/en/tree.js +5 -5
  466. package/dist/i18n/en/tree.js.map +1 -1
  467. package/dist/index.d.ts +4 -1
  468. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
  469. package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
  470. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
  471. package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
  472. package/dist/integrations/composite-ai/composite-fill.js +5 -5
  473. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  474. package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
  475. package/dist/integrations/composite-ai/composite-summary.js +5 -5
  476. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  477. package/dist/integrations/composite-ai/derive.d.ts +17 -17
  478. package/dist/integrations/composite-ai/derive.js +27 -26
  479. package/dist/integrations/composite-ai/derive.js.map +1 -1
  480. package/dist/integrations/composite-ai/index.d.ts +14 -15
  481. package/dist/integrations/composite-ai/index.js +98 -77
  482. package/dist/integrations/composite-ai/index.js.map +1 -1
  483. package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
  484. package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
  485. package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
  486. package/dist/integrations/composite-ai/rendition.d.ts +9 -9
  487. package/dist/integrations/composite-ai/rendition.js +27 -27
  488. package/dist/integrations/composite-ai/rendition.js.map +1 -1
  489. package/dist/integrations/composite-ai/split.d.ts +8 -8
  490. package/dist/integrations/composite-ai/split.js +12 -12
  491. package/dist/integrations/composite-ai/split.js.map +1 -1
  492. package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
  493. package/dist/integrations/composite-ai/transfer-transforms.js +12 -12
  494. package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
  495. package/dist/integrations/composite-media/index.d.ts +4 -3
  496. package/dist/integrations/composite-media/index.js +20 -14
  497. package/dist/integrations/composite-media/index.js.map +1 -1
  498. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  499. package/dist/markdown/document.js +1 -1
  500. package/dist/markdown/document.js.map +1 -1
  501. package/dist/nuxt/module.d.ts +30 -24
  502. package/dist/nuxt/module.js.map +1 -1
  503. package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
  504. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  505. package/dist/presets/compose.d.ts +6 -6
  506. package/dist/presets/compose.js +8 -5
  507. package/dist/presets/compose.js.map +1 -1
  508. package/dist/react/AdvancedEditor.d.ts +2 -2
  509. package/dist/react/AdvancedEditor.js +1 -1
  510. package/dist/react/AdvancedEditor.js.map +1 -1
  511. package/dist/react/compose.d.ts +1 -1
  512. package/dist/react/compose.js +2 -2
  513. package/dist/react/compose.js.map +1 -1
  514. package/dist/react/composed-surface.d.ts +5 -5
  515. package/dist/react/composed-surface.js +2 -2
  516. package/dist/react/composed-surface.js.map +1 -1
  517. package/dist/server/ai.d.ts +7 -7
  518. package/dist/server/ai.js +1 -1
  519. package/dist/server/ai.js.map +1 -1
  520. package/dist/server/auth.d.ts +1 -1
  521. package/dist/server/collab-state.d.ts +1 -1
  522. package/dist/server/collab-state.js.map +1 -1
  523. package/dist/server/collab.d.ts +4 -4
  524. package/dist/server/collab.js +22 -17
  525. package/dist/server/collab.js.map +1 -1
  526. package/dist/server/handlers/ai.d.ts +9 -6
  527. package/dist/server/handlers/ai.js +13 -17
  528. package/dist/server/handlers/ai.js.map +1 -1
  529. package/dist/server/handlers/context.d.ts +4 -0
  530. package/dist/server/handlers/context.js.map +1 -1
  531. package/dist/server/handlers/editor.d.ts +33 -32
  532. package/dist/server/handlers/editor.js +13 -8
  533. package/dist/server/handlers/editor.js.map +1 -1
  534. package/dist/server/handlers/images.d.ts +1 -1
  535. package/dist/server/handlers/images.js +1 -1
  536. package/dist/server/handlers/images.js.map +1 -1
  537. package/dist/server/host.d.ts +4 -0
  538. package/dist/server/host.js +10 -2
  539. package/dist/server/host.js.map +1 -1
  540. package/dist/server/index.d.ts +3 -3
  541. package/dist/server/ports.d.ts +8 -8
  542. package/dist/server/ports.js.map +1 -1
  543. package/dist/settings/index.d.ts +20 -19
  544. package/dist/settings/index.js +25 -24
  545. package/dist/settings/index.js.map +1 -1
  546. package/dist/settings/migrate.d.ts +18 -0
  547. package/dist/settings/migrate.js +46 -0
  548. package/dist/settings/migrate.js.map +1 -0
  549. package/dist/settings/values.d.ts +12 -12
  550. package/dist/settings/values.js +11 -11
  551. package/dist/settings/values.js.map +1 -1
  552. package/dist/styles/composites.css +10 -10
  553. package/dist/tree/DocumentTree.js +12 -5
  554. package/dist/tree/DocumentTree.js.map +1 -1
  555. package/dist/tree/drop-rules.js +5 -5
  556. package/dist/tree/drop-rules.js.map +1 -1
  557. package/dist/tree/react/DocumentTree.js +14 -5
  558. package/dist/tree/react/DocumentTree.js.map +1 -1
  559. package/dist/tree/tree-host.js +6 -6
  560. package/dist/tree/tree-host.js.map +1 -1
  561. package/dist/tree/tree-items.js +34 -28
  562. package/dist/tree/tree-items.js.map +1 -1
  563. package/dist/tree/tree-model.js +16 -16
  564. package/dist/tree/tree-model.js.map +1 -1
  565. package/dist/tree/tree-rows.js +2 -2
  566. package/dist/tree/tree-rows.js.map +1 -1
  567. package/dist/tree/tree-view-model.d.ts +2 -2
  568. package/dist/tree/tree-view-model.js.map +1 -1
  569. package/dist/vue/AdvancedEditor.js +2 -2
  570. package/dist/vue/AdvancedEditor.js.map +1 -1
  571. package/dist/vue/AdvancedEditor.vue.d.ts +10 -10
  572. package/dist/vue/SimpleEditor.vue.d.ts +4 -4
  573. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  574. package/dist/vue/compose.d.ts +1 -1
  575. package/dist/vue/compose.js +2 -2
  576. package/dist/vue/compose.js.map +1 -1
  577. package/package.json +1 -1
  578. package/dist/features/composites/derivation.d.ts +0 -62
  579. package/dist/features/composites/derivation.js +0 -102
  580. package/dist/features/composites/derivation.js.map +0 -1
  581. package/dist/features/composites/kinds.d.ts +0 -83
  582. package/dist/features/composites/kinds.js +0 -285
  583. package/dist/features/composites/kinds.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"accepted-children.js","names":[],"sources":["../../../src/features/composites/accepted-children.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type Transaction } from '@tiptap/pm/state'\nimport { AddMarkStep, AttrStep, RemoveMarkStep, ReplaceStep, type Step } from '@tiptap/pm/transform'\nimport type { InsertGuard } from '../../core/command-types'\nimport type { Notifier } from '../../core/notify'\nimport { createTranslator, type Translate } from '../../i18n'\nimport { type CompositeKind, compositeKind, kindAccepts } from './kinds'\nimport { compositeKindsOf } from './nodes'\n\n/** One Baustein nested in a Baustein that does not accept its kind (both kind ids). */\nexport interface NestingViolation {\n parent: string\n child: string\n}\n\n/** The short sentence for a refused nesting, naming both kinds by label (German: „Notiz nimmt keine Aufgabe auf.“). */\nexport function nestingReason(kinds: readonly CompositeKind[], parent: string, child: string, t: Translate): string {\n return t('composites.nesting.refused', { parent: compositeKind(kinds, parent).label, child: compositeKind(kinds, child).label })\n}\n\n/**\n * The Bausteine of `node`'s subtree, outermost first: `node` itself when it is a Baustein, otherwise the Bausteine\n * directly inside it (not those nested in another Baustein).\n */\nexport function outermostComposites(node: PMNode): PMNode[] {\n if (node.type.name === 'composite') return [node]\n const found: PMNode[] = []\n node.descendants((child) => {\n if (child.type.name === 'composite') {\n found.push(child)\n return false\n }\n return !child.isTextblock\n })\n return found\n}\n\n/**\n * Every Baustein in `doc` whose nearest Baustein ancestor (quotes and lists in between do not count) does not accept\n * its kind, in document order. Top-level Bausteine have no parent and never violate.\n */\nexport function nestingViolations(doc: PMNode, kinds: readonly CompositeKind[]): NestingViolation[] {\n const found: NestingViolation[] = []\n function walk(node: PMNode, parentKind: string | null) {\n node.forEach((child) => {\n if (child.isTextblock || child.isLeaf) return\n if (child.type.name === 'composite') {\n const kind = String(child.attrs.kind)\n if (parentKind !== null && !kindAccepts(kinds, parentKind, kind)) found.push({ parent: parentKind, child: kind })\n walk(child, kind)\n }\n else walk(child, parentKind)\n })\n }\n walk(doc, null)\n return found\n}\n\n/**\n * The insert guard of the composites feature: a Baustein of kind `creates.attrs.kind` may be created only where the\n * nearest Baustein around the insertion point (`$parent` and outward; quotes and lists in between do not count)\n * accepts that kind, or where there is none. Uses `kindAccepts`, the rule `AcceptedChildren` enforces on transactions\n * through `nestingViolations`, so the `/` menu and toolbar offer exactly what an edit would not be refused for.\n */\nexport const compositeInsertGuard: InsertGuard = (editor, $parent, creates) => {\n const child = creates.attrs?.kind\n if (creates.type !== 'composite' || typeof child !== 'string') return true\n for (let depth = $parent.depth; depth >= 0; depth--) {\n const node = $parent.node(depth)\n if (node.type.name === 'composite') return kindAccepts(compositeKindsOf(editor), String(node.attrs.kind), child)\n }\n return true\n}\n\n/** A step that cannot change which Baustein sits in which: marks, text typed or deleted inside one textblock, attributes other than `kind`. */\nfunction keepsNesting(step: Step, before: PMNode): boolean {\n if (step instanceof AddMarkStep || step instanceof RemoveMarkStep) return true\n if (step instanceof AttrStep) return step.attr !== 'kind'\n if (step instanceof ReplaceStep) {\n const { slice } = step\n if (slice.openStart !== 0 || slice.openEnd !== 0 || !slice.content.content.every(child => child.isInline)) return false\n const $from = before.resolve(step.from)\n return $from.parent.isTextblock && $from.sameParent(before.resolve(step.to))\n }\n return false\n}\n\n/**\n * Transactions that were not made by this user's edit: collaboration (y-sync marks remote changes and Yjs undo/redo\n * with `isChangeOrigin`) and prosemirror-history's undo/redo. They replay what was allowed, or what others wrote.\n */\nfunction isReplay(tr: Transaction): boolean {\n const sync = tr.getMeta('y-sync$') as { isChangeOrigin?: boolean } | undefined\n return sync?.isChangeOrigin === true || tr.getMeta('history$') !== undefined\n}\n\n/** The pair of kinds that occurs more often in `after` than in `before`, if any. */\nfunction addedViolation(before: readonly NestingViolation[], after: readonly NestingViolation[]): NestingViolation | undefined {\n const counts = new Map<string, number>()\n const key = (v: NestingViolation) => JSON.stringify([v.parent, v.child])\n for (const v of before) counts.set(key(v), (counts.get(key(v)) ?? 0) + 1)\n for (const v of after) {\n const left = counts.get(key(v)) ?? 0\n if (left === 0) return v\n counts.set(key(v), left - 1)\n }\n return after[0]\n}\n\nexport interface AcceptedChildrenOptions {\n /** Shows the notice of a refused edit (default: dropped). The composites feature passes the surface's `ctx.notify`. */\n notify: Notifier\n /** The translator of the notice. The composites feature passes the surface's `ctx.t`. */\n t: Translate\n}\n\n/**\n * Enforces the kinds' accepted children (`CompositeKind.accepts`) for every local edit at one place: a transaction that\n * leaves the document with more Bausteine nested in a Baustein that does not accept them than before is refused — block\n * drag, „In Baustein umwandeln“, paste, AI inserts and review acceptance, `moveBlockInto` — and the user is told why.\n * Existing violations stay: transactions that add none (typing, undoing) pass, and so do collaboration replays, undo/redo\n * and loading a whole document. Client-only, no schema: kinds are read from the editor (`compositeKindsOf`).\n */\nexport const AcceptedChildren = Extension.create<AcceptedChildrenOptions>({\n name: 'acceptedChildren',\n addOptions: () => ({ notify: () => {}, t: createTranslator() }),\n addProseMirrorPlugins() {\n const { editor } = this\n const { notify, t } = this.options\n return [new Plugin({\n key: new PluginKey('acceptedChildren'),\n filterTransaction(tr, state) {\n if (!tr.docChanged || isReplay(tr)) return true\n // `setContent` loads a whole new document; that is not an edit of the existing one.\n if (tr.steps.some((step, index) => step instanceof ReplaceStep && step.from === 0 && step.to === tr.docs[index]!.content.size)) return true\n if (tr.steps.every((step, index) => keepsNesting(step, tr.docs[index]!))) return true\n const kinds = compositeKindsOf(editor)\n const before = nestingViolations(state.doc, kinds)\n const after = nestingViolations(tr.doc, kinds)\n if (after.length <= before.length) return true\n const added = addedViolation(before, after)\n notify({\n id: 'composites-accepted-children',\n title: t('composites.nesting.title'),\n ...(added && { description: nestingReason(kinds, added.parent, added.child, t) }),\n kind: 'warning',\n icon: 'i-lucide-ban',\n })\n return false\n },\n })]\n },\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"accepted-children.js","names":[],"sources":["../../../src/features/composites/accepted-children.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type Transaction } from '@tiptap/pm/state'\nimport { AddMarkStep, AttrStep, RemoveMarkStep, ReplaceStep, type Step } from '@tiptap/pm/transform'\nimport type { InsertGuard } from '../../core/command-types'\nimport type { Notifier } from '../../core/notify'\nimport { createTranslator, type Translate } from '../../i18n'\nimport { acceptsChild, type BlockType, blockTypeOf } from './block-types'\nimport { blockTypesOf } from './nodes'\n\n/** One Typed block nested in a Typed block that does not accept its Block type (both Block type ids). */\nexport interface NestingViolation {\n parent: string\n child: string\n}\n\n/** The short sentence for a refused nesting, naming both kinds by label (German: „Notiz nimmt keine Aufgabe auf.“). */\nexport function nestingReason(kinds: readonly BlockType[], parent: string, child: string, t: Translate): string {\n return t('composites.nesting.refused', { parent: blockTypeOf(kinds, parent).label, child: blockTypeOf(kinds, child).label })\n}\n\n/**\n * The Typed blocks of `node`'s subtree, outermost first: `node` itself when it is a Typed block, otherwise the Typed blocks\n * directly inside it (not those nested in another Typed block).\n */\nexport function outermostComposites(node: PMNode): PMNode[] {\n if (node.type.name === 'typedBlock') return [node]\n const found: PMNode[] = []\n node.descendants((child) => {\n if (child.type.name === 'typedBlock') {\n found.push(child)\n return false\n }\n return !child.isTextblock\n })\n return found\n}\n\n/**\n * Every Typed block in `doc` whose nearest Typed block ancestor (quotes and lists in between do not count) does not accept\n * its Block type, in document order. Top-level Typed blocks have no parent and never violate.\n */\nexport function nestingViolations(doc: PMNode, kinds: readonly BlockType[]): NestingViolation[] {\n const found: NestingViolation[] = []\n function walk(node: PMNode, parentKind: string | null) {\n node.forEach((child) => {\n if (child.isTextblock || child.isLeaf) return\n if (child.type.name === 'typedBlock') {\n const kind = String(child.attrs.blockType)\n if (parentKind !== null && !acceptsChild(kinds, parentKind, kind)) found.push({ parent: parentKind, child: kind })\n walk(child, kind)\n }\n else walk(child, parentKind)\n })\n }\n walk(doc, null)\n return found\n}\n\n/**\n * The insert guard of the composites feature: a Typed block of Block type `creates.attrs.blockType` may be created only where the\n * nearest Typed block around the insertion point (`$parent` and outward; quotes and lists in between do not count)\n * accepts that kind, or where there is none. Uses `acceptsChild`, the rule `AcceptedChildren` enforces on transactions\n * through `nestingViolations`, so the `/` menu and toolbar offer exactly what an edit would not be refused for.\n */\nexport const compositeInsertGuard: InsertGuard = (editor, $parent, creates) => {\n const child = creates.attrs?.blockType\n if (creates.type !== 'typedBlock' || typeof child !== 'string') return true\n for (let depth = $parent.depth; depth >= 0; depth--) {\n const node = $parent.node(depth)\n if (node.type.name === 'typedBlock') return acceptsChild(blockTypesOf(editor), String(node.attrs.blockType), child)\n }\n return true\n}\n\n/** A step that cannot change which Typed block sits in which: marks, text typed or deleted inside one textblock, attributes other than `blockType`. */\nfunction keepsNesting(step: Step, before: PMNode): boolean {\n if (step instanceof AddMarkStep || step instanceof RemoveMarkStep) return true\n if (step instanceof AttrStep) return step.attr !== 'blockType'\n if (step instanceof ReplaceStep) {\n const { slice } = step\n if (slice.openStart !== 0 || slice.openEnd !== 0 || !slice.content.content.every(child => child.isInline)) return false\n const $from = before.resolve(step.from)\n return $from.parent.isTextblock && $from.sameParent(before.resolve(step.to))\n }\n return false\n}\n\n/**\n * Transactions that were not made by this user's edit: collaboration (y-sync marks remote changes and Yjs undo/redo\n * with `isChangeOrigin`) and prosemirror-history's undo/redo. They replay what was allowed, or what others wrote.\n */\nfunction isReplay(tr: Transaction): boolean {\n const sync = tr.getMeta('y-sync$') as { isChangeOrigin?: boolean } | undefined\n return sync?.isChangeOrigin === true || tr.getMeta('history$') !== undefined\n}\n\n/** The pair of kinds that occurs more often in `after` than in `before`, if any. */\nfunction addedViolation(before: readonly NestingViolation[], after: readonly NestingViolation[]): NestingViolation | undefined {\n const counts = new Map<string, number>()\n const key = (v: NestingViolation) => JSON.stringify([v.parent, v.child])\n for (const v of before) counts.set(key(v), (counts.get(key(v)) ?? 0) + 1)\n for (const v of after) {\n const left = counts.get(key(v)) ?? 0\n if (left === 0) return v\n counts.set(key(v), left - 1)\n }\n return after[0]\n}\n\nexport interface AcceptedChildrenOptions {\n /** Shows the notice of a refused edit (default: dropped). The composites feature passes the surface's `ctx.notify`. */\n notify: Notifier\n /** The translator of the notice. The composites feature passes the surface's `ctx.t`. */\n t: Translate\n}\n\n/**\n * Enforces the kinds' accepted children (`BlockType.accepts`) for every local edit at one place: a transaction that\n * leaves the document with more Typed blocks nested in a Typed block that does not accept them than before is refused — block\n * drag, „In Block umwandeln“, paste, AI inserts and review acceptance, `moveBlockInto` — and the user is told why.\n * Existing violations stay: transactions that add none (typing, undoing) pass, and so do collaboration replays, undo/redo\n * and loading a whole document. Client-only, no schema: kinds are read from the editor (`blockTypesOf`).\n */\nexport const AcceptedChildren = Extension.create<AcceptedChildrenOptions>({\n name: 'acceptedChildren',\n addOptions: () => ({ notify: () => {}, t: createTranslator() }),\n addProseMirrorPlugins() {\n const { editor } = this\n const { notify, t } = this.options\n return [new Plugin({\n key: new PluginKey('acceptedChildren'),\n filterTransaction(tr, state) {\n if (!tr.docChanged || isReplay(tr)) return true\n // `setContent` loads a whole new document; that is not an edit of the existing one.\n if (tr.steps.some((step, index) => step instanceof ReplaceStep && step.from === 0 && step.to === tr.docs[index]!.content.size)) return true\n if (tr.steps.every((step, index) => keepsNesting(step, tr.docs[index]!))) return true\n const kinds = blockTypesOf(editor)\n const before = nestingViolations(state.doc, kinds)\n const after = nestingViolations(tr.doc, kinds)\n if (after.length <= before.length) return true\n const added = addedViolation(before, after)\n notify({\n id: 'composites-accepted-children',\n title: t('composites.nesting.title'),\n ...(added && { description: nestingReason(kinds, added.parent, added.child, t) }),\n kind: 'warning',\n icon: 'i-lucide-ban',\n })\n return false\n },\n })]\n },\n})\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Editor } from "@tiptap/core";
2
2
  //#region src/features/composites/ai-brief.d.ts
3
- /** What one Baustein tells the model: kind brief, place in the hierarchy, chrome, field values, resolved instruction and skeleton, body. */
3
+ /** What one Typed block tells the model: Block type brief, place in the hierarchy, chrome, property values, resolved instruction and AI content template, body. */
4
4
  export interface CompositeAiBrief {
5
5
  kind: {
6
6
  id: string;
@@ -20,12 +20,12 @@ export interface CompositeAiBrief {
20
20
  }[];
21
21
  /** `meta[ai.instructionsField]` when non-empty, else `ai.instructions`, else ''. */
22
22
  instructions: string;
23
- /** `ai.skeleton` with `{key}` replaced by the string value of `meta[key]` (declared fields only); '' without a skeleton. */
24
- skeleton: string;
23
+ /** The Block type's AI `contentTemplate` with `{key}` replaced by the string value of `meta[key]` (declared properties only); '' without a template. */
24
+ contentTemplate: string;
25
25
  /** Body blocks as Markdown ('' for a body without text). */
26
26
  body: string;
27
27
  }
28
- /** The brief of the composite at `pos`; null unless the node there is a composite whose kind has `ai`. */
28
+ /** The brief of the composite at `pos`; null unless the node there is a composite whose Block type has the `ai-brief` capability. */
29
29
  export declare function compositeAiBrief(editor: Editor, pos: number): CompositeAiBrief | null;
30
30
  //#endregion
31
31
  //# sourceMappingURL=ai-brief.d.ts.map
@@ -1,28 +1,30 @@
1
1
  import { findBlockById } from "../../core/schema/block-ids.js";
2
- import { compositeKind } from "./kinds.js";
3
- import { derivationOf } from "./derivation.js";
4
- import { compositeKindsOf } from "./nodes.js";
2
+ import { capabilitiesOf, capabilityConfigOf } from "../../core/capabilities.js";
3
+ import { derivationOf } from "../../core/derivation.js";
4
+ import { blockTypeOf } from "./block-types.js";
5
+ import { blockTypesOf } from "./nodes.js";
5
6
  import { citeFull } from "./bibliography.js";
6
7
  //#region src/features/composites/ai-brief.ts
7
8
  const PLACEHOLDER = /\{([\p{L}\p{N}_-]+)\}/gu;
8
9
  const isFilled = (value) => typeof value === "string" && value !== "" || typeof value === "number";
9
- /** The brief of the composite at `pos`; null unless the node there is a composite whose kind has `ai`. */
10
+ /** The brief of the composite at `pos`; null unless the node there is a composite whose Block type has the `ai-brief` capability. */
10
11
  function compositeAiBrief(editor, pos) {
11
12
  const node = editor.state.doc.nodeAt(pos);
12
- if (node?.type.name !== "composite" || node.childCount < 3) return null;
13
- const kind = compositeKind(compositeKindsOf(editor), String(node.attrs.kind));
14
- const ai = kind.ai;
13
+ if (node?.type.name !== "typedBlock" || node.childCount < 3 || !capabilitiesOf(editor, node).has("ai-brief")) return null;
14
+ const kind = blockTypeOf(blockTypesOf(editor), String(node.attrs.blockType));
15
+ const ai = capabilityConfigOf(editor, node, "ai-brief");
15
16
  if (!ai) return null;
17
+ const definitions = capabilityConfigOf(editor, node, "properties")?.definitions ?? [];
16
18
  const $pos = editor.state.doc.resolve(pos);
17
19
  const path = [];
18
20
  for (let depth = 1; depth <= $pos.depth; depth++) {
19
21
  const ancestor = $pos.node(depth);
20
- if (ancestor.type.name === "composite") path.push(ancestor.child(0).textContent.trim());
22
+ if (ancestor.type.name === "typedBlock") path.push(ancestor.child(0).textContent.trim());
21
23
  }
22
24
  const tags = node.child(2).attrs.tags;
23
25
  const body = Array.from({ length: node.childCount - 3 }, (_, i) => node.child(3 + i));
24
26
  const meta = node.attrs.meta ?? {};
25
- const fields = kind.fields.flatMap((field) => {
27
+ const fields = definitions.flatMap((field) => {
26
28
  const raw = meta[field.key];
27
29
  if (field.type === "bibliography") {
28
30
  const bib = raw;
@@ -37,7 +39,7 @@ function compositeAiBrief(editor, pos) {
37
39
  }
38
40
  if (field.type === "reference") {
39
41
  const target = typeof raw === "string" && raw ? findBlockById(editor.state.doc, raw)?.node : void 0;
40
- const title = target?.type.name === "composite" ? target.child(0).textContent.trim() : "";
42
+ const title = target?.type.name === "typedBlock" ? target.child(0).textContent.trim() : "";
41
43
  return title ? [{
42
44
  key: field.key,
43
45
  label: field.label,
@@ -68,7 +70,7 @@ function compositeAiBrief(editor, pos) {
68
70
  value: field.type === "select" ? field.options?.find((option) => option.value === value)?.label ?? value : value
69
71
  }];
70
72
  });
71
- const keys = new Set(kind.fields.map((field) => field.key));
73
+ const keys = new Set(definitions.map((field) => field.key));
72
74
  const own = ai.instructionsField ? String(meta[ai.instructionsField] ?? "").trim() : "";
73
75
  return {
74
76
  kind: {
@@ -82,7 +84,7 @@ function compositeAiBrief(editor, pos) {
82
84
  tags: Array.isArray(tags) ? tags.filter((tag) => typeof tag === "string") : [],
83
85
  fields,
84
86
  instructions: own || ai.instructions?.trim() || "",
85
- skeleton: ai.skeleton?.replace(PLACEHOLDER, (match, key) => keys.has(key) && isFilled(meta[key]) ? String(meta[key]) : match) ?? "",
87
+ contentTemplate: ai.contentTemplate?.replace(PLACEHOLDER, (match, key) => keys.has(key) && isFilled(meta[key]) ? String(meta[key]) : match) ?? "",
86
88
  body: body.some((child) => child.textContent.trim()) ? editor.markdown.serialize({
87
89
  type: "doc",
88
90
  content: body.map((child) => child.toJSON())
@@ -1 +1 @@
1
- {"version":3,"file":"ai-brief.js","names":[],"sources":["../../../src/features/composites/ai-brief.ts"],"sourcesContent":["// Vue-free: what one Baustein tells the model (read by „Baustein füllen (KI)“, see `integrations/composite-ai`).\nimport type { Editor, JSONContent } from '@tiptap/core'\n// Types only: brings in the `editor.markdown` augmentation of `@tiptap/markdown` for contexts that do not otherwise load it.\nimport type {} from '@tiptap/markdown'\nimport { findBlockById } from '../../core/schema/block-ids'\nimport { type CslItem, citeFull } from './bibliography'\nimport { derivationOf } from './derivation'\nimport { compositeKind } from './kinds'\nimport { COMPOSITE_CHROME_COUNT, compositeKindsOf } from './nodes'\n\n/** What one Baustein tells the model: kind brief, place in the hierarchy, chrome, field values, resolved instruction and skeleton, body. */\nexport interface CompositeAiBrief {\n kind: { id: string, label: string, purpose?: string }\n /** Titles of the enclosing composites, outermost first (empty at document level). */\n path: string[]\n title: string\n description: string\n tags: string[]\n /** Declared fields with a non-empty value, `select` values as their option label. */\n fields: { key: string, label: string, value: string }[]\n /** `meta[ai.instructionsField]` when non-empty, else `ai.instructions`, else ''. */\n instructions: string\n /** `ai.skeleton` with `{key}` replaced by the string value of `meta[key]` (declared fields only); '' without a skeleton. */\n skeleton: string\n /** Body blocks as Markdown ('' for a body without text). */\n body: string\n}\n\nconst PLACEHOLDER = /\\{([\\p{L}\\p{N}_-]+)\\}/gu\n\nconst isFilled = (value: unknown): value is string | number => (typeof value === 'string' && value !== '') || typeof value === 'number'\n\n/** The brief of the composite at `pos`; null unless the node there is a composite whose kind has `ai`. */\nexport function compositeAiBrief(editor: Editor, pos: number): CompositeAiBrief | null {\n const node = editor.state.doc.nodeAt(pos)\n if (node?.type.name !== 'composite' || node.childCount < COMPOSITE_CHROME_COUNT) return null\n const kind = compositeKind(compositeKindsOf(editor), String(node.attrs.kind))\n const ai = kind.ai\n if (!ai) return null\n\n const $pos = editor.state.doc.resolve(pos)\n const path: string[] = []\n for (let depth = 1; depth <= $pos.depth; depth++) {\n const ancestor = $pos.node(depth)\n if (ancestor.type.name === 'composite') path.push(ancestor.child(0).textContent.trim())\n }\n\n const tags: unknown = node.child(2).attrs.tags\n const body = Array.from({ length: node.childCount - COMPOSITE_CHROME_COUNT }, (_, i) => node.child(COMPOSITE_CHROME_COUNT + i))\n const meta: Record<string, unknown> = node.attrs.meta ?? {}\n\n const fields = kind.fields.flatMap((field) => {\n const raw = meta[field.key]\n if (field.type === 'bibliography') {\n const bib = raw as Partial<CslItem> | null | undefined\n return bib && typeof bib === 'object' && typeof bib.type === 'string' ? [{ key: field.key, label: field.label, value: citeFull({ ...(bib as CslItem), title: node.child(0).textContent }) }] : []\n }\n if (field.type === 'reference') {\n const target = typeof raw === 'string' && raw ? findBlockById(editor.state.doc, raw)?.node : undefined\n const title = target?.type.name === 'composite' ? target.child(0).textContent.trim() : ''\n return title ? [{ key: field.key, label: field.label, value: title }] : []\n }\n if (field.type === 'derivation') {\n const derivation = derivationOf(node)\n return derivation ? [{ key: field.key, label: field.label, value: `${derivation.method} from ${'asset' in derivation.from ? derivation.from.asset : derivation.from.block}` }] : []\n }\n if (field.type === 'multiselect') {\n const chosen = Array.isArray(raw) ? raw.filter((item): item is string => typeof item === 'string') : []\n const value = chosen.map(item => field.options?.find(option => option.value === item)?.label ?? item).join(', ')\n return value ? [{ key: field.key, label: field.label, value }] : []\n }\n if (!isFilled(raw)) return []\n const value = String(raw)\n return [{ key: field.key, label: field.label, value: field.type === 'select' ? (field.options?.find(option => option.value === value)?.label ?? value) : value }]\n })\n const keys = new Set(kind.fields.map(field => field.key))\n const own = ai.instructionsField ? String(meta[ai.instructionsField] ?? '').trim() : ''\n\n return {\n kind: { id: kind.id, label: kind.label, ...(ai.purpose && { purpose: ai.purpose }) },\n path,\n title: node.child(0).textContent,\n description: node.child(1).textContent,\n tags: Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string') : [],\n fields,\n instructions: own || ai.instructions?.trim() || '',\n skeleton: ai.skeleton?.replace(PLACEHOLDER, (match, key: string) => keys.has(key) && isFilled(meta[key]) ? String(meta[key]) : match) ?? '',\n body: body.some(child => child.textContent.trim())\n ? editor.markdown!.serialize({ type: 'doc', content: body.map(child => child.toJSON() as JSONContent) }).trim()\n : '',\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-brief.js","names":[],"sources":["../../../src/features/composites/ai-brief.ts"],"sourcesContent":["// Vue-free: what one Typed block tells the model (read by the AI fill of a Typed block, see `integrations/composite-ai`).\nimport type { Editor, JSONContent } from '@tiptap/core'\n// Types only: brings in the `editor.markdown` augmentation of `@tiptap/markdown` for contexts that do not otherwise load it.\nimport type {} from '@tiptap/markdown'\nimport { capabilitiesOf, capabilityConfigOf } from '../../core/capabilities'\nimport { findBlockById } from '../../core/schema/block-ids'\nimport { type CslItem, citeFull } from './bibliography'\nimport { derivationOf } from '../../core/derivation'\nimport { type BlockTypeAi, blockTypeOf, type PropertiesConfig } from './block-types'\nimport { COMPOSITE_CHROME_COUNT, blockTypesOf } from './nodes'\n\n/** What one Typed block tells the model: Block type brief, place in the hierarchy, chrome, property values, resolved instruction and AI content template, body. */\nexport interface CompositeAiBrief {\n kind: { id: string, label: string, purpose?: string }\n /** Titles of the enclosing composites, outermost first (empty at document level). */\n path: string[]\n title: string\n description: string\n tags: string[]\n /** Declared fields with a non-empty value, `select` values as their option label. */\n fields: { key: string, label: string, value: string }[]\n /** `meta[ai.instructionsField]` when non-empty, else `ai.instructions`, else ''. */\n instructions: string\n /** The Block type's AI `contentTemplate` with `{key}` replaced by the string value of `meta[key]` (declared properties only); '' without a template. */\n contentTemplate: string\n /** Body blocks as Markdown ('' for a body without text). */\n body: string\n}\n\nconst PLACEHOLDER = /\\{([\\p{L}\\p{N}_-]+)\\}/gu\n\nconst isFilled = (value: unknown): value is string | number => (typeof value === 'string' && value !== '') || typeof value === 'number'\n\n/** The brief of the composite at `pos`; null unless the node there is a composite whose Block type has the `ai-brief` capability. */\nexport function compositeAiBrief(editor: Editor, pos: number): CompositeAiBrief | null {\n const node = editor.state.doc.nodeAt(pos)\n if (node?.type.name !== 'typedBlock' || node.childCount < COMPOSITE_CHROME_COUNT || !capabilitiesOf(editor, node).has('ai-brief')) return null\n const kind = blockTypeOf(blockTypesOf(editor), String(node.attrs.blockType))\n const ai = capabilityConfigOf<BlockTypeAi>(editor, node, 'ai-brief')\n if (!ai) return null\n const definitions = capabilityConfigOf<PropertiesConfig>(editor, node, 'properties')?.definitions ?? []\n\n const $pos = editor.state.doc.resolve(pos)\n const path: string[] = []\n for (let depth = 1; depth <= $pos.depth; depth++) {\n const ancestor = $pos.node(depth)\n if (ancestor.type.name === 'typedBlock') path.push(ancestor.child(0).textContent.trim())\n }\n\n const tags: unknown = node.child(2).attrs.tags\n const body = Array.from({ length: node.childCount - COMPOSITE_CHROME_COUNT }, (_, i) => node.child(COMPOSITE_CHROME_COUNT + i))\n const meta: Record<string, unknown> = node.attrs.meta ?? {}\n\n const fields = definitions.flatMap((field) => {\n const raw = meta[field.key]\n if (field.type === 'bibliography') {\n const bib = raw as Partial<CslItem> | null | undefined\n return bib && typeof bib === 'object' && typeof bib.type === 'string' ? [{ key: field.key, label: field.label, value: citeFull({ ...(bib as CslItem), title: node.child(0).textContent }) }] : []\n }\n if (field.type === 'reference') {\n const target = typeof raw === 'string' && raw ? findBlockById(editor.state.doc, raw)?.node : undefined\n const title = target?.type.name === 'typedBlock' ? target.child(0).textContent.trim() : ''\n return title ? [{ key: field.key, label: field.label, value: title }] : []\n }\n if (field.type === 'derivation') {\n const derivation = derivationOf(node)\n return derivation ? [{ key: field.key, label: field.label, value: `${derivation.method} from ${'asset' in derivation.from ? derivation.from.asset : derivation.from.block}` }] : []\n }\n if (field.type === 'multiselect') {\n const chosen = Array.isArray(raw) ? raw.filter((item): item is string => typeof item === 'string') : []\n const value = chosen.map(item => field.options?.find(option => option.value === item)?.label ?? item).join(', ')\n return value ? [{ key: field.key, label: field.label, value }] : []\n }\n if (!isFilled(raw)) return []\n const value = String(raw)\n return [{ key: field.key, label: field.label, value: field.type === 'select' ? (field.options?.find(option => option.value === value)?.label ?? value) : value }]\n })\n const keys = new Set(definitions.map(field => field.key))\n const own = ai.instructionsField ? String(meta[ai.instructionsField] ?? '').trim() : ''\n\n return {\n kind: { id: kind.id, label: kind.label, ...(ai.purpose && { purpose: ai.purpose }) },\n path,\n title: node.child(0).textContent,\n description: node.child(1).textContent,\n tags: Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string') : [],\n fields,\n instructions: own || ai.instructions?.trim() || '',\n contentTemplate: ai.contentTemplate?.replace(PLACEHOLDER, (match, key: string) => keys.has(key) && isFilled(meta[key]) ? String(meta[key]) : match) ?? '',\n body: body.some(child => child.textContent.trim())\n ? editor.markdown!.serialize({ type: 'doc', content: body.map(child => child.toJSON() as JSONContent) }).trim()\n : '',\n }\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { ReadonlyStore } from "../../core/store.js";
2
2
  import { CompositeRow } from "./rows.js";
3
3
  import { Editor } from "@tiptap/core";
4
4
  //#region src/features/composites/anchors.d.ts
5
- /** Where a Baustein sits in the scroll container's content box (so an overlay inside the container scrolls with it). */
5
+ /** Where a Typed block sits in the scroll container's content box (so an overlay inside the container scrolls with it). */
6
6
  export interface CompositeAnchor {
7
7
  /** The composite box's right edge. */
8
8
  right: number;
@@ -1 +1 @@
1
- {"version":3,"file":"anchors.js","names":[],"sources":["../../../src/features/composites/anchors.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport { compositeOfRow, type CompositeRow } from './rows'\n\n/** Where a Baustein sits in the scroll container's content box (so an overlay inside the container scrolls with it). */\nexport interface CompositeAnchor {\n /** The composite box's right edge. */\n right: number\n /** Vertical centre of the title's last line and the x right after its last character. */\n lastCenter: number\n lineEnd: number\n titleEmpty: boolean\n /** The kind icon at the front (`.composite__toggle`); all `0` when the node view has none. */\n toggleTop: number\n toggleLeft: number\n toggleSize: number\n}\n\n/** The editor's own box in the same coordinates: its right edge, where the chips end. */\nexport interface CompositeFrame {\n right: number\n}\n\n/** The measurements of `observeCompositeAnchors`, as observable stores. */\nexport interface CompositeAnchorsHandle {\n /** The editor's scroll container (positioned `relative` while observed); `null` while the editor is not laid out. */\n readonly host: ReadonlyStore<HTMLElement | null>\n /** By row key. Empty while the editor is not laid out (hidden by a table view, say). */\n readonly anchors: ReadonlyStore<ReadonlyMap<string, CompositeAnchor>>\n readonly frame: ReadonlyStore<CompositeFrame | null>\n /** Measures again on the next animation frame; call when the rows changed. */\n refresh: () => void\n /** Stops observing and gives the host its `position` back (idempotent). */\n dispose: () => void\n}\n\nfunction scrollParentOf(el: HTMLElement): HTMLElement | null {\n for (let node = el.parentElement; node; node = node.parentElement) {\n if (/(auto|scroll)/.test(getComputedStyle(node).overflowY)) return node\n }\n return null\n}\n\n/**\n * The `.composite` section of a composite's node DOM: the node view's own element (Vue), or the child of the wrapper\n * element a React node view renders into.\n */\nexport function compositeSectionOf(dom: Element): Element | null {\n return dom.matches('.composite') ? dom : dom.querySelector(':scope > .composite')\n}\n\nconst sameAnchor = (a: CompositeAnchor, b: CompositeAnchor) => (Object.keys(a) as (keyof CompositeAnchor)[]).every(key => a[key] === b[key])\n\nconst NO_ANCHORS: ReadonlyMap<string, CompositeAnchor> = new Map()\n\n/**\n * Measures the title line and box of every row's composite, relative to the editor's scroll container (`host`), kept\n * current after transactions, resizes and `refresh()`. An overlay portalled into `host` and positioned `absolute`\n * from these numbers scrolls with the document and needs no scroll listener. `rows` is read at every measurement.\n * Create one per editor.\n */\nexport function observeCompositeAnchors(editor: Editor | undefined, rows: () => readonly CompositeRow[]): CompositeAnchorsHandle {\n const host = createStore<HTMLElement | null>(null)\n const anchors = createStore<ReadonlyMap<string, CompositeAnchor>>(NO_ANCHORS)\n const frame = createStore<CompositeFrame | null>(null)\n\n let raf = 0\n let disposed = false\n const schedule = () => {\n if (!raf && !disposed) raf = requestAnimationFrame(measure)\n }\n\n /** The scroller this observer gave `position: relative` (only if it was static), so it can be put back. */\n let positioned: HTMLElement | null = null\n const restoreHostPosition = () => {\n if (positioned) positioned.style.position = ''\n positioned = null\n }\n\n const clear = () => {\n anchors.set(NO_ANCHORS)\n frame.set(null)\n }\n\n function measure() {\n raf = 0\n if (disposed) return\n if (!editor || editor.isDestroyed || !editor.view.dom.isConnected || !editor.view.dom.getClientRects().length) return clear()\n const { view, state } = editor\n const scroller = scrollParentOf(view.dom)\n if (!scroller) return clear()\n if (host.get() !== scroller) {\n restoreHostPosition()\n if (getComputedStyle(scroller).position === 'static') {\n scroller.style.position = 'relative'\n positioned = scroller\n }\n host.set(scroller)\n }\n const box = scroller.getBoundingClientRect()\n const ox = scroller.scrollLeft - box.left - scroller.clientLeft\n const oy = scroller.scrollTop - box.top - scroller.clientTop\n\n const right = view.dom.getBoundingClientRect().right + ox\n if (frame.get()?.right !== right) frame.set({ right })\n\n const next = new Map<string, CompositeAnchor>()\n for (const row of rows()) {\n const found = compositeOfRow(state.doc, row)\n const el = found ? view.nodeDOM(found.pos) : null\n const title = found?.node.firstChild\n if (!found || !title || !(el instanceof HTMLElement)) continue\n const rect = el.getBoundingClientRect()\n if (!rect.height) continue\n const titleEl = el.querySelector<HTMLElement>('.composite-title')\n const titleRect = (titleEl ?? el).getBoundingClientRect()\n const lineHeight = titleEl ? parseFloat(getComputedStyle(titleEl).lineHeight) || titleRect.height : titleRect.height\n const toggle = compositeSectionOf(el)?.querySelector<HTMLElement>(':scope > .composite__toggle') ?? null\n const toggleRect = toggle ? toggle.getBoundingClientRect() : { top: 0, left: 0, width: 0, height: 0 }\n let caret = { left: titleRect.left, top: titleRect.top, bottom: titleRect.top + lineHeight }\n try {\n // Position right after the title's last character: the end of the title's own content.\n caret = view.coordsAtPos(found.pos + title.nodeSize)\n }\n catch {\n // Not laid out yet: the title block's own box is a good enough anchor until the next frame.\n }\n next.set(row.key, {\n right: rect.right + ox,\n lastCenter: (caret.top + caret.bottom) / 2 + oy,\n lineEnd: caret.left + ox,\n titleEmpty: title.content.size === 0,\n toggleTop: toggle ? toggleRect.top + oy : 0,\n toggleLeft: toggle ? toggleRect.left + ox : 0,\n toggleSize: toggle ? Math.max(toggleRect.width, toggleRect.height) : 0,\n })\n }\n const prev = anchors.get()\n let changed = prev.size !== next.size\n if (!changed) {\n for (const [key, anchor] of next) {\n const before = prev.get(key)\n if (!before || !sameAnchor(before, anchor)) {\n changed = true\n break\n }\n }\n }\n if (changed) anchors.set(next)\n }\n\n let observer: ResizeObserver | undefined\n if (editor && !editor.isDestroyed) {\n editor.on('transaction', schedule)\n observer = new ResizeObserver(schedule)\n observer.observe(editor.view.dom)\n window.addEventListener('resize', schedule)\n schedule()\n }\n\n return {\n host,\n anchors,\n frame,\n refresh: schedule,\n dispose() {\n if (disposed) return\n disposed = true\n editor?.off('transaction', schedule)\n observer?.disconnect()\n window.removeEventListener('resize', schedule)\n restoreHostPosition()\n if (raf) cancelAnimationFrame(raf)\n raf = 0\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"anchors.js","names":[],"sources":["../../../src/features/composites/anchors.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport { compositeOfRow, type CompositeRow } from './rows'\n\n/** Where a Typed block sits in the scroll container's content box (so an overlay inside the container scrolls with it). */\nexport interface CompositeAnchor {\n /** The composite box's right edge. */\n right: number\n /** Vertical centre of the title's last line and the x right after its last character. */\n lastCenter: number\n lineEnd: number\n titleEmpty: boolean\n /** The kind icon at the front (`.composite__toggle`); all `0` when the node view has none. */\n toggleTop: number\n toggleLeft: number\n toggleSize: number\n}\n\n/** The editor's own box in the same coordinates: its right edge, where the chips end. */\nexport interface CompositeFrame {\n right: number\n}\n\n/** The measurements of `observeCompositeAnchors`, as observable stores. */\nexport interface CompositeAnchorsHandle {\n /** The editor's scroll container (positioned `relative` while observed); `null` while the editor is not laid out. */\n readonly host: ReadonlyStore<HTMLElement | null>\n /** By row key. Empty while the editor is not laid out (hidden by a table view, say). */\n readonly anchors: ReadonlyStore<ReadonlyMap<string, CompositeAnchor>>\n readonly frame: ReadonlyStore<CompositeFrame | null>\n /** Measures again on the next animation frame; call when the rows changed. */\n refresh: () => void\n /** Stops observing and gives the host its `position` back (idempotent). */\n dispose: () => void\n}\n\nfunction scrollParentOf(el: HTMLElement): HTMLElement | null {\n for (let node = el.parentElement; node; node = node.parentElement) {\n if (/(auto|scroll)/.test(getComputedStyle(node).overflowY)) return node\n }\n return null\n}\n\n/**\n * The `.composite` section of a composite's node DOM: the node view's own element (Vue), or the child of the wrapper\n * element a React node view renders into.\n */\nexport function compositeSectionOf(dom: Element): Element | null {\n return dom.matches('.composite') ? dom : dom.querySelector(':scope > .composite')\n}\n\nconst sameAnchor = (a: CompositeAnchor, b: CompositeAnchor) => (Object.keys(a) as (keyof CompositeAnchor)[]).every(key => a[key] === b[key])\n\nconst NO_ANCHORS: ReadonlyMap<string, CompositeAnchor> = new Map()\n\n/**\n * Measures the title line and box of every row's composite, relative to the editor's scroll container (`host`), kept\n * current after transactions, resizes and `refresh()`. An overlay portalled into `host` and positioned `absolute`\n * from these numbers scrolls with the document and needs no scroll listener. `rows` is read at every measurement.\n * Create one per editor.\n */\nexport function observeCompositeAnchors(editor: Editor | undefined, rows: () => readonly CompositeRow[]): CompositeAnchorsHandle {\n const host = createStore<HTMLElement | null>(null)\n const anchors = createStore<ReadonlyMap<string, CompositeAnchor>>(NO_ANCHORS)\n const frame = createStore<CompositeFrame | null>(null)\n\n let raf = 0\n let disposed = false\n const schedule = () => {\n if (!raf && !disposed) raf = requestAnimationFrame(measure)\n }\n\n /** The scroller this observer gave `position: relative` (only if it was static), so it can be put back. */\n let positioned: HTMLElement | null = null\n const restoreHostPosition = () => {\n if (positioned) positioned.style.position = ''\n positioned = null\n }\n\n const clear = () => {\n anchors.set(NO_ANCHORS)\n frame.set(null)\n }\n\n function measure() {\n raf = 0\n if (disposed) return\n if (!editor || editor.isDestroyed || !editor.view.dom.isConnected || !editor.view.dom.getClientRects().length) return clear()\n const { view, state } = editor\n const scroller = scrollParentOf(view.dom)\n if (!scroller) return clear()\n if (host.get() !== scroller) {\n restoreHostPosition()\n if (getComputedStyle(scroller).position === 'static') {\n scroller.style.position = 'relative'\n positioned = scroller\n }\n host.set(scroller)\n }\n const box = scroller.getBoundingClientRect()\n const ox = scroller.scrollLeft - box.left - scroller.clientLeft\n const oy = scroller.scrollTop - box.top - scroller.clientTop\n\n const right = view.dom.getBoundingClientRect().right + ox\n if (frame.get()?.right !== right) frame.set({ right })\n\n const next = new Map<string, CompositeAnchor>()\n for (const row of rows()) {\n const found = compositeOfRow(state.doc, row)\n const el = found ? view.nodeDOM(found.pos) : null\n const title = found?.node.firstChild\n if (!found || !title || !(el instanceof HTMLElement)) continue\n const rect = el.getBoundingClientRect()\n if (!rect.height) continue\n const titleEl = el.querySelector<HTMLElement>('.composite-title')\n const titleRect = (titleEl ?? el).getBoundingClientRect()\n const lineHeight = titleEl ? parseFloat(getComputedStyle(titleEl).lineHeight) || titleRect.height : titleRect.height\n const toggle = compositeSectionOf(el)?.querySelector<HTMLElement>(':scope > .composite__toggle') ?? null\n const toggleRect = toggle ? toggle.getBoundingClientRect() : { top: 0, left: 0, width: 0, height: 0 }\n let caret = { left: titleRect.left, top: titleRect.top, bottom: titleRect.top + lineHeight }\n try {\n // Position right after the title's last character: the end of the title's own content.\n caret = view.coordsAtPos(found.pos + title.nodeSize)\n }\n catch {\n // Not laid out yet: the title block's own box is a good enough anchor until the next frame.\n }\n next.set(row.key, {\n right: rect.right + ox,\n lastCenter: (caret.top + caret.bottom) / 2 + oy,\n lineEnd: caret.left + ox,\n titleEmpty: title.content.size === 0,\n toggleTop: toggle ? toggleRect.top + oy : 0,\n toggleLeft: toggle ? toggleRect.left + ox : 0,\n toggleSize: toggle ? Math.max(toggleRect.width, toggleRect.height) : 0,\n })\n }\n const prev = anchors.get()\n let changed = prev.size !== next.size\n if (!changed) {\n for (const [key, anchor] of next) {\n const before = prev.get(key)\n if (!before || !sameAnchor(before, anchor)) {\n changed = true\n break\n }\n }\n }\n if (changed) anchors.set(next)\n }\n\n let observer: ResizeObserver | undefined\n if (editor && !editor.isDestroyed) {\n editor.on('transaction', schedule)\n observer = new ResizeObserver(schedule)\n observer.observe(editor.view.dom)\n window.addEventListener('resize', schedule)\n schedule()\n }\n\n return {\n host,\n anchors,\n frame,\n refresh: schedule,\n dispose() {\n if (disposed) return\n disposed = true\n editor?.off('transaction', schedule)\n observer?.disconnect()\n window.removeEventListener('resize', schedule)\n restoreHostPosition()\n if (raf) cancelAnimationFrame(raf)\n raf = 0\n },\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { Translate } from "../../i18n/index.js";
2
2
  import { Node } from "@tiptap/pm/model";
3
3
  //#region src/features/composites/baskets.d.ts
4
- /** A colour basket a Baustein can be put into; the host app defines the set (`compositeFeature({ baskets })`). */
4
+ /** A colour basket a Typed block can be put into; the host app defines the set (`compositeFeature({ baskets })`). */
5
5
  export interface Basket {
6
6
  id: string;
7
7
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"baskets.js","names":[],"sources":["../../../src/features/composites/baskets.ts"],"sourcesContent":["// Vue-free and server-importable: the colour baskets a Baustein can be put into, and how a node carries its basket and its hidden title.\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { createTranslator, type Translate } from '../../i18n'\nimport { BASKET_KEY, TITLE_HIDDEN_KEY } from './meta-keys'\n\n/** A colour basket a Baustein can be put into; the host app defines the set (`compositeFeature({ baskets })`). */\nexport interface Basket {\n id: string\n label: string\n /** CSS colour; the chrome dot and the row stripe use it. */\n color: string\n}\n\n/** Ids of the built-in baskets. */\nexport const BASKET_IDS = ['red', 'orange', 'yellow', 'green', 'blue'] as const\n\n/** The built-in baskets in the language of `t`; their colours are the `--sk-basket-<id>` CSS variables, which hosts override. Memoized per translator. */\nexport function defaultBaskets(t: Translate): readonly Basket[] {\n let baskets = DEFAULT_BASKETS.get(t)\n if (!baskets) DEFAULT_BASKETS.set(t, baskets = BASKET_IDS.map(id => ({ id, label: t(`composites.basket.${id}`), color: `var(--sk-basket-${id})` })))\n return baskets\n}\n\nconst DEFAULT_BASKETS = new WeakMap<Translate, readonly Basket[]>()\n\n/** The built-in baskets in the default language, for server-side and context-free code (`Composite`'s default option, an editor without a context). */\nexport const fallbackBaskets = (): readonly Basket[] => defaultBaskets(DEFAULT_TRANSLATE)\n\nconst DEFAULT_TRANSLATE = createTranslator()\n\n/** What carries a composite's meta: the node itself or a row read from it (`CompositeRow`). */\ntype MetaSource = PMNode | { meta: Record<string, unknown> }\n\nfunction metaOf(source: MetaSource): Record<string, unknown> {\n const meta: unknown = 'attrs' in source ? source.attrs.meta : source.meta\n return meta && typeof meta === 'object' ? meta as Record<string, unknown> : {}\n}\n\n/** Id of the basket `source` (a composite node or its row) is in, or null. */\nexport function basketOf(source: MetaSource): string | null {\n const basket = metaOf(source)[BASKET_KEY]\n return typeof basket === 'string' && basket ? basket : null\n}\n\n/** Whether the title of `source` (a composite node or its row) is hidden. */\nexport function titleHiddenOf(source: MetaSource): boolean {\n return metaOf(source)[TITLE_HIDDEN_KEY] === true\n}\n\n/** The basket with id `id` among `baskets`; `undefined` for no id or an id the set does not offer. */\nexport function basketById(baskets: readonly Basket[], id: string | null | undefined): Basket | undefined {\n return id ? baskets.find(basket => basket.id === id) : undefined\n}\n"],"mappings":";;;;AAcA,MAAa,aAAa;CAAC;CAAO;CAAU;CAAU;CAAS;AAAM;;AAGrE,SAAgB,eAAe,GAAiC;CAC9D,IAAI,UAAU,gBAAgB,IAAI,CAAC;CACnC,IAAI,CAAC,SAAS,gBAAgB,IAAI,GAAG,UAAU,WAAW,KAAI,QAAO;EAAE;EAAI,OAAO,EAAE,qBAAqB,IAAI;EAAG,OAAO,mBAAmB,GAAG;CAAG,EAAE,CAAC;CACnJ,OAAO;AACT;AAEA,MAAM,kCAAkB,IAAI,QAAsC;;AAGlE,MAAa,wBAA2C,eAAe,iBAAiB;AAExF,MAAM,oBAAoB,iBAAiB;AAK3C,SAAS,OAAO,QAA6C;CAC3D,MAAM,OAAgB,WAAW,SAAS,OAAO,MAAM,OAAO,OAAO;CACrE,OAAO,QAAQ,OAAO,SAAS,WAAW,OAAkC,CAAC;AAC/E;;AAGA,SAAgB,SAAS,QAAmC;CAC1D,MAAM,SAAS,OAAO,MAAM,CAAC,CAAC;CAC9B,OAAO,OAAO,WAAW,YAAY,SAAS,SAAS;AACzD;;AAGA,SAAgB,cAAc,QAA6B;CACzD,OAAO,OAAO,MAAM,CAAC,CAAC,sBAAsB;AAC9C;;AAGA,SAAgB,WAAW,SAA4B,IAAmD;CACxG,OAAO,KAAK,QAAQ,MAAK,WAAU,OAAO,OAAO,EAAE,IAAI,KAAA;AACzD"}
1
+ {"version":3,"file":"baskets.js","names":[],"sources":["../../../src/features/composites/baskets.ts"],"sourcesContent":["// Vue-free and server-importable: the colour baskets a Typed block can be put into, and how a node carries its basket and its hidden title.\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { createTranslator, type Translate } from '../../i18n'\nimport { BASKET_KEY, TITLE_HIDDEN_KEY } from './meta-keys'\n\n/** A colour basket a Typed block can be put into; the host app defines the set (`compositeFeature({ baskets })`). */\nexport interface Basket {\n id: string\n label: string\n /** CSS colour; the chrome dot and the row stripe use it. */\n color: string\n}\n\n/** Ids of the built-in baskets. */\nexport const BASKET_IDS = ['red', 'orange', 'yellow', 'green', 'blue'] as const\n\n/** The built-in baskets in the language of `t`; their colours are the `--sk-basket-<id>` CSS variables, which hosts override. Memoized per translator. */\nexport function defaultBaskets(t: Translate): readonly Basket[] {\n let baskets = DEFAULT_BASKETS.get(t)\n if (!baskets) DEFAULT_BASKETS.set(t, baskets = BASKET_IDS.map(id => ({ id, label: t(`composites.basket.${id}`), color: `var(--sk-basket-${id})` })))\n return baskets\n}\n\nconst DEFAULT_BASKETS = new WeakMap<Translate, readonly Basket[]>()\n\n/** The built-in baskets in the default language, for server-side and context-free code (`Composite`'s default option, an editor without a context). */\nexport const fallbackBaskets = (): readonly Basket[] => defaultBaskets(DEFAULT_TRANSLATE)\n\nconst DEFAULT_TRANSLATE = createTranslator()\n\n/** What carries a composite's meta: the node itself or a row read from it (`CompositeRow`). */\ntype MetaSource = PMNode | { meta: Record<string, unknown> }\n\nfunction metaOf(source: MetaSource): Record<string, unknown> {\n const meta: unknown = 'attrs' in source ? source.attrs.meta : source.meta\n return meta && typeof meta === 'object' ? meta as Record<string, unknown> : {}\n}\n\n/** Id of the basket `source` (a composite node or its row) is in, or null. */\nexport function basketOf(source: MetaSource): string | null {\n const basket = metaOf(source)[BASKET_KEY]\n return typeof basket === 'string' && basket ? basket : null\n}\n\n/** Whether the title of `source` (a composite node or its row) is hidden. */\nexport function titleHiddenOf(source: MetaSource): boolean {\n return metaOf(source)[TITLE_HIDDEN_KEY] === true\n}\n\n/** The basket with id `id` among `baskets`; `undefined` for no id or an id the set does not offer. */\nexport function basketById(baskets: readonly Basket[], id: string | null | undefined): Basket | undefined {\n return id ? baskets.find(basket => basket.id === id) : undefined\n}\n"],"mappings":";;;;AAcA,MAAa,aAAa;CAAC;CAAO;CAAU;CAAU;CAAS;AAAM;;AAGrE,SAAgB,eAAe,GAAiC;CAC9D,IAAI,UAAU,gBAAgB,IAAI,CAAC;CACnC,IAAI,CAAC,SAAS,gBAAgB,IAAI,GAAG,UAAU,WAAW,KAAI,QAAO;EAAE;EAAI,OAAO,EAAE,qBAAqB,IAAI;EAAG,OAAO,mBAAmB,GAAG;CAAG,EAAE,CAAC;CACnJ,OAAO;AACT;AAEA,MAAM,kCAAkB,IAAI,QAAsC;;AAGlE,MAAa,wBAA2C,eAAe,iBAAiB;AAExF,MAAM,oBAAoB,iBAAiB;AAK3C,SAAS,OAAO,QAA6C;CAC3D,MAAM,OAAgB,WAAW,SAAS,OAAO,MAAM,OAAO,OAAO;CACrE,OAAO,QAAQ,OAAO,SAAS,WAAW,OAAkC,CAAC;AAC/E;;AAGA,SAAgB,SAAS,QAAmC;CAC1D,MAAM,SAAS,OAAO,MAAM,CAAC,CAAC;CAC9B,OAAO,OAAO,WAAW,YAAY,SAAS,SAAS;AACzD;;AAGA,SAAgB,cAAc,QAA6B;CACzD,OAAO,OAAO,MAAM,CAAC,CAAC,sBAAsB;AAC9C;;AAGA,SAAgB,WAAW,SAA4B,IAAmD;CACxG,OAAO,KAAK,QAAQ,MAAK,WAAU,OAAO,OAAO,EAAE,IAAI,KAAA;AACzD"}
@@ -3,7 +3,7 @@ import "@tiptap/core";
3
3
  declare module '@tiptap/core' {
4
4
  interface Commands<ReturnType> {
5
5
  compositeBibliographyFetch: {
6
- /** Fetches the bibliographic data of the Baustein at `pos` into its `meta.bib` (`ai.extract`, method `bibliography`). Registered by `composite-ai`. */
6
+ /** Fetches the bibliographic data of the Typed block at `pos` into its `meta.bib` (`ai.extract`, method `bibliography`). Registered by `composite-ai`. */
7
7
  fetchCompositeBibliography: (pos: number) => ReturnType;
8
8
  };
9
9
  }
@@ -15,7 +15,7 @@ const MANAGED_KEYS = {
15
15
  "URL": true,
16
16
  "abstract": true
17
17
  };
18
- /** Never stored in `meta.bib`: the block id and the title belong to the Baustein. */
18
+ /** Never stored in `meta.bib`: the block id and the title belong to the Typed block. */
19
19
  const FOREIGN_KEYS = {
20
20
  id: true,
21
21
  title: true
@@ -1 +1 @@
1
- {"version":3,"file":"bibliography-form.js","names":[],"sources":["../../../src/features/composites/bibliography-form.ts"],"sourcesContent":["// Vue-free: the form model of the bibliography editor (`BibliographyEditor` on Vue and React) — a CSL item as flat strings and back.\nimport { type CslItem, type CslName, type CslType, yearOf } from './bibliography'\n\n/** The CSL item as the editor's inputs hold it: one string per input. */\nexport interface BibForm {\n type: CslType\n /** One person per line: `Family, Given`; a line without a comma is an institution or an unparsed name. */\n authors: string\n year: string\n containerTitle: string\n volume: string\n issue: string\n page: string\n publisher: string\n doi: string\n isbn: string\n url: string\n abstract: string\n}\n\n/** Keys of the CSL item the form edits; every other stored key is kept as it is. */\nconst MANAGED_KEYS: Record<string, true> = { 'type': true, 'author': true, 'issued': true, 'container-title': true, 'volume': true, 'issue': true, 'page': true, 'publisher': true, 'DOI': true, 'ISBN': true, 'URL': true, 'abstract': true }\n/** Never stored in `meta.bib`: the block id and the title belong to the Baustein. */\nconst FOREIGN_KEYS: Record<string, true> = { id: true, title: true }\n\n/** The names of `authors`, one per non-empty line. */\nexport function parseAuthors(text: string): CslName[] {\n return text.split('\\n').map(line => line.trim()).filter(Boolean).map((line) => {\n const comma = line.indexOf(',')\n if (comma < 0) return { literal: line }\n const family = line.slice(0, comma).trim()\n const given = line.slice(comma + 1).trim()\n return family ? { family, ...(given && { given }) } : { literal: line }\n })\n}\n\n/** `names` as the text `parseAuthors` reads back: `Family, Given` per line. */\nexport function authorsText(names: readonly CslName[] | undefined): string {\n return (names ?? []).map(name => name.family ? (name.given ? `${name.family}, ${name.given}` : `${name.family},`) : (name.literal ?? '')).filter(Boolean).join('\\n')\n}\n\n/** The form values of `item`; without an item the defaults of an empty form. */\nexport function bibFormOf(item: CslItem | null | undefined): BibForm {\n const year = item ? yearOf(item) : null\n return {\n type: item?.type ?? 'article-journal',\n authors: authorsText(item?.author),\n year: year === null ? '' : String(year),\n containerTitle: item?.['container-title'] ?? '',\n volume: item?.volume ?? '',\n issue: item?.issue ?? '',\n page: item?.page ?? '',\n publisher: item?.publisher ?? '',\n doi: item?.DOI ?? '',\n isbn: item?.ISBN ?? '',\n url: item?.URL ?? '',\n abstract: item?.abstract ?? '',\n }\n}\n\n/**\n * The complete CSL item for `form`: no `title` and no empty strings. Keys the form does not edit (`editor`,\n * `publisher-place`, `language`, …) are taken over from `base`; an unchanged year keeps the stored date parts (month, day).\n */\nexport function bibItemOf(form: BibForm, base?: CslItem | null): CslItem {\n const text = (value: string) => value.trim() || undefined\n const author = parseAuthors(form.authors)\n const year = Number.parseInt(form.year, 10)\n const keptDate = base?.issued && yearOf(base) === year ? base.issued : undefined\n const fields: Record<string, unknown> = {\n author: author.length ? author : undefined,\n issued: Number.isFinite(year) ? (keptDate ?? { 'date-parts': [[year]] }) : undefined,\n 'container-title': text(form.containerTitle),\n volume: text(form.volume),\n issue: text(form.issue),\n page: text(form.page),\n publisher: text(form.publisher),\n DOI: text(form.doi),\n ISBN: text(form.isbn),\n URL: text(form.url),\n abstract: text(form.abstract),\n }\n const kept = Object.entries(base ?? {}).filter(([key, value]) => !MANAGED_KEYS[key] && !FOREIGN_KEYS[key] && value !== '' && value !== undefined && value !== null)\n const entries = [...Object.entries(fields).filter(([, value]) => value !== undefined), ...kept]\n return { type: form.type, ...Object.fromEntries(entries) }\n}\n"],"mappings":";;;AAqBA,MAAM,eAAqC;CAAE,QAAQ;CAAM,UAAU;CAAM,UAAU;CAAM,mBAAmB;CAAM,UAAU;CAAM,SAAS;CAAM,QAAQ;CAAM,aAAa;CAAM,OAAO;CAAM,QAAQ;CAAM,OAAO;CAAM,YAAY;AAAK;;AAE7O,MAAM,eAAqC;CAAE,IAAI;CAAM,OAAO;AAAK;;AAGnE,SAAgB,aAAa,MAAyB;CACpD,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,KAAI,SAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,SAAS;EAC7E,MAAM,QAAQ,KAAK,QAAQ,GAAG;EAC9B,IAAI,QAAQ,GAAG,OAAO,EAAE,SAAS,KAAK;EACtC,MAAM,SAAS,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK;EACzC,MAAM,QAAQ,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK;EACzC,OAAO,SAAS;GAAE;GAAQ,GAAI,SAAS,EAAE,MAAM;EAAG,IAAI,EAAE,SAAS,KAAK;CACxE,CAAC;AACH;;AAGA,SAAgB,YAAY,OAA+C;CACzE,QAAQ,SAAS,CAAC,EAAA,CAAG,KAAI,SAAQ,KAAK,SAAU,KAAK,QAAQ,GAAG,KAAK,OAAO,IAAI,KAAK,UAAU,GAAG,KAAK,OAAO,KAAO,KAAK,WAAW,EAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;AACrK;;AAGA,SAAgB,UAAU,MAA2C;CACnE,MAAM,OAAO,OAAO,OAAO,IAAI,IAAI;CACnC,OAAO;EACL,MAAM,MAAM,QAAQ;EACpB,SAAS,YAAY,MAAM,MAAM;EACjC,MAAM,SAAS,OAAO,KAAK,OAAO,IAAI;EACtC,gBAAgB,OAAO,sBAAsB;EAC7C,QAAQ,MAAM,UAAU;EACxB,OAAO,MAAM,SAAS;EACtB,MAAM,MAAM,QAAQ;EACpB,WAAW,MAAM,aAAa;EAC9B,KAAK,MAAM,OAAO;EAClB,MAAM,MAAM,QAAQ;EACpB,KAAK,MAAM,OAAO;EAClB,UAAU,MAAM,YAAY;CAC9B;AACF;;;;;AAMA,SAAgB,UAAU,MAAe,MAAgC;CACvE,MAAM,QAAQ,UAAkB,MAAM,KAAK,KAAK,KAAA;CAChD,MAAM,SAAS,aAAa,KAAK,OAAO;CACxC,MAAM,OAAO,OAAO,SAAS,KAAK,MAAM,EAAE;CAC1C,MAAM,WAAW,MAAM,UAAU,OAAO,IAAI,MAAM,OAAO,KAAK,SAAS,KAAA;CACvE,MAAM,SAAkC;EACtC,QAAQ,OAAO,SAAS,SAAS,KAAA;EACjC,QAAQ,OAAO,SAAS,IAAI,IAAK,YAAY,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,IAAK,KAAA;EAC3E,mBAAmB,KAAK,KAAK,cAAc;EAC3C,QAAQ,KAAK,KAAK,MAAM;EACxB,OAAO,KAAK,KAAK,KAAK;EACtB,MAAM,KAAK,KAAK,IAAI;EACpB,WAAW,KAAK,KAAK,SAAS;EAC9B,KAAK,KAAK,KAAK,GAAG;EAClB,MAAM,KAAK,KAAK,IAAI;EACpB,KAAK,KAAK,KAAK,GAAG;EAClB,UAAU,KAAK,KAAK,QAAQ;CAC9B;CACA,MAAM,OAAO,OAAO,QAAQ,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,aAAa,QAAQ,CAAC,aAAa,QAAQ,UAAU,MAAM,UAAU,KAAA,KAAa,UAAU,IAAI;CAClK,MAAM,UAAU,CAAC,GAAG,OAAO,QAAQ,MAAM,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,GAAG,GAAG,IAAI;CAC9F,OAAO;EAAE,MAAM,KAAK;EAAM,GAAG,OAAO,YAAY,OAAO;CAAE;AAC3D"}
1
+ {"version":3,"file":"bibliography-form.js","names":[],"sources":["../../../src/features/composites/bibliography-form.ts"],"sourcesContent":["// Vue-free: the form model of the bibliography editor (`BibliographyEditor` on Vue and React) — a CSL item as flat strings and back.\nimport { type CslItem, type CslName, type CslType, yearOf } from './bibliography'\n\n/** The CSL item as the editor's inputs hold it: one string per input. */\nexport interface BibForm {\n type: CslType\n /** One person per line: `Family, Given`; a line without a comma is an institution or an unparsed name. */\n authors: string\n year: string\n containerTitle: string\n volume: string\n issue: string\n page: string\n publisher: string\n doi: string\n isbn: string\n url: string\n abstract: string\n}\n\n/** Keys of the CSL item the form edits; every other stored key is kept as it is. */\nconst MANAGED_KEYS: Record<string, true> = { 'type': true, 'author': true, 'issued': true, 'container-title': true, 'volume': true, 'issue': true, 'page': true, 'publisher': true, 'DOI': true, 'ISBN': true, 'URL': true, 'abstract': true }\n/** Never stored in `meta.bib`: the block id and the title belong to the Typed block. */\nconst FOREIGN_KEYS: Record<string, true> = { id: true, title: true }\n\n/** The names of `authors`, one per non-empty line. */\nexport function parseAuthors(text: string): CslName[] {\n return text.split('\\n').map(line => line.trim()).filter(Boolean).map((line) => {\n const comma = line.indexOf(',')\n if (comma < 0) return { literal: line }\n const family = line.slice(0, comma).trim()\n const given = line.slice(comma + 1).trim()\n return family ? { family, ...(given && { given }) } : { literal: line }\n })\n}\n\n/** `names` as the text `parseAuthors` reads back: `Family, Given` per line. */\nexport function authorsText(names: readonly CslName[] | undefined): string {\n return (names ?? []).map(name => name.family ? (name.given ? `${name.family}, ${name.given}` : `${name.family},`) : (name.literal ?? '')).filter(Boolean).join('\\n')\n}\n\n/** The form values of `item`; without an item the defaults of an empty form. */\nexport function bibFormOf(item: CslItem | null | undefined): BibForm {\n const year = item ? yearOf(item) : null\n return {\n type: item?.type ?? 'article-journal',\n authors: authorsText(item?.author),\n year: year === null ? '' : String(year),\n containerTitle: item?.['container-title'] ?? '',\n volume: item?.volume ?? '',\n issue: item?.issue ?? '',\n page: item?.page ?? '',\n publisher: item?.publisher ?? '',\n doi: item?.DOI ?? '',\n isbn: item?.ISBN ?? '',\n url: item?.URL ?? '',\n abstract: item?.abstract ?? '',\n }\n}\n\n/**\n * The complete CSL item for `form`: no `title` and no empty strings. Keys the form does not edit (`editor`,\n * `publisher-place`, `language`, …) are taken over from `base`; an unchanged year keeps the stored date parts (month, day).\n */\nexport function bibItemOf(form: BibForm, base?: CslItem | null): CslItem {\n const text = (value: string) => value.trim() || undefined\n const author = parseAuthors(form.authors)\n const year = Number.parseInt(form.year, 10)\n const keptDate = base?.issued && yearOf(base) === year ? base.issued : undefined\n const fields: Record<string, unknown> = {\n author: author.length ? author : undefined,\n issued: Number.isFinite(year) ? (keptDate ?? { 'date-parts': [[year]] }) : undefined,\n 'container-title': text(form.containerTitle),\n volume: text(form.volume),\n issue: text(form.issue),\n page: text(form.page),\n publisher: text(form.publisher),\n DOI: text(form.doi),\n ISBN: text(form.isbn),\n URL: text(form.url),\n abstract: text(form.abstract),\n }\n const kept = Object.entries(base ?? {}).filter(([key, value]) => !MANAGED_KEYS[key] && !FOREIGN_KEYS[key] && value !== '' && value !== undefined && value !== null)\n const entries = [...Object.entries(fields).filter(([, value]) => value !== undefined), ...kept]\n return { type: form.type, ...Object.fromEntries(entries) }\n}\n"],"mappings":";;;AAqBA,MAAM,eAAqC;CAAE,QAAQ;CAAM,UAAU;CAAM,UAAU;CAAM,mBAAmB;CAAM,UAAU;CAAM,SAAS;CAAM,QAAQ;CAAM,aAAa;CAAM,OAAO;CAAM,QAAQ;CAAM,OAAO;CAAM,YAAY;AAAK;;AAE7O,MAAM,eAAqC;CAAE,IAAI;CAAM,OAAO;AAAK;;AAGnE,SAAgB,aAAa,MAAyB;CACpD,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,KAAI,SAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,SAAS;EAC7E,MAAM,QAAQ,KAAK,QAAQ,GAAG;EAC9B,IAAI,QAAQ,GAAG,OAAO,EAAE,SAAS,KAAK;EACtC,MAAM,SAAS,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK;EACzC,MAAM,QAAQ,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK;EACzC,OAAO,SAAS;GAAE;GAAQ,GAAI,SAAS,EAAE,MAAM;EAAG,IAAI,EAAE,SAAS,KAAK;CACxE,CAAC;AACH;;AAGA,SAAgB,YAAY,OAA+C;CACzE,QAAQ,SAAS,CAAC,EAAA,CAAG,KAAI,SAAQ,KAAK,SAAU,KAAK,QAAQ,GAAG,KAAK,OAAO,IAAI,KAAK,UAAU,GAAG,KAAK,OAAO,KAAO,KAAK,WAAW,EAAG,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;AACrK;;AAGA,SAAgB,UAAU,MAA2C;CACnE,MAAM,OAAO,OAAO,OAAO,IAAI,IAAI;CACnC,OAAO;EACL,MAAM,MAAM,QAAQ;EACpB,SAAS,YAAY,MAAM,MAAM;EACjC,MAAM,SAAS,OAAO,KAAK,OAAO,IAAI;EACtC,gBAAgB,OAAO,sBAAsB;EAC7C,QAAQ,MAAM,UAAU;EACxB,OAAO,MAAM,SAAS;EACtB,MAAM,MAAM,QAAQ;EACpB,WAAW,MAAM,aAAa;EAC9B,KAAK,MAAM,OAAO;EAClB,MAAM,MAAM,QAAQ;EACpB,KAAK,MAAM,OAAO;EAClB,UAAU,MAAM,YAAY;CAC9B;AACF;;;;;AAMA,SAAgB,UAAU,MAAe,MAAgC;CACvE,MAAM,QAAQ,UAAkB,MAAM,KAAK,KAAK,KAAA;CAChD,MAAM,SAAS,aAAa,KAAK,OAAO;CACxC,MAAM,OAAO,OAAO,SAAS,KAAK,MAAM,EAAE;CAC1C,MAAM,WAAW,MAAM,UAAU,OAAO,IAAI,MAAM,OAAO,KAAK,SAAS,KAAA;CACvE,MAAM,SAAkC;EACtC,QAAQ,OAAO,SAAS,SAAS,KAAA;EACjC,QAAQ,OAAO,SAAS,IAAI,IAAK,YAAY,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,IAAK,KAAA;EAC3E,mBAAmB,KAAK,KAAK,cAAc;EAC3C,QAAQ,KAAK,KAAK,MAAM;EACxB,OAAO,KAAK,KAAK,KAAK;EACtB,MAAM,KAAK,KAAK,IAAI;EACpB,WAAW,KAAK,KAAK,SAAS;EAC9B,KAAK,KAAK,KAAK,GAAG;EAClB,MAAM,KAAK,KAAK,IAAI;EACpB,KAAK,KAAK,KAAK,GAAG;EAClB,UAAU,KAAK,KAAK,QAAQ;CAC9B;CACA,MAAM,OAAO,OAAO,QAAQ,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,WAAW,CAAC,aAAa,QAAQ,CAAC,aAAa,QAAQ,UAAU,MAAM,UAAU,KAAA,KAAa,UAAU,IAAI;CAClK,MAAM,UAAU,CAAC,GAAG,OAAO,QAAQ,MAAM,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,GAAG,GAAG,IAAI;CAC9F,OAAO;EAAE,MAAM,KAAK;EAAM,GAAG,OAAO,YAAY,OAAO;CAAE;AAC3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"bibliography.js","names":[],"sources":["../../../src/features/composites/bibliography.ts"],"sourcesContent":["// Vue-free and server-importable: bibliographic data of a composite („Baustein“) as CSL-JSON in `meta.bib`, and its citation and BibTeX renderings.\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf } from '../../core/schema/block-ids'\nimport type { MessageKey, Translate } from '../../i18n'\nimport { CSL_TYPES, type CslItem, type CslName, type CslType } from '../../core/backend-types'\nimport { BIB_KEY } from './meta-keys'\n\n// The CSL types live in `core/backend-types.ts` (the `ai.extract` wire type names them); this module is their feature-side entry.\nexport { CSL_TYPES, type CslDate, type CslItem, type CslName, type CslType } from '../../core/backend-types'\n\nexport { BIB_KEY }\n\n/** The select options of the CSL types, labelled through `t`. */\nexport function cslTypeOptions(t: Translate): { value: CslType, label: string }[] {\n return CSL_TYPES.map(value => ({ value, label: t(`composites.bib.type.${value}` as MessageKey) }))\n}\n\n/** The bibliographic data of the composite `node` (`meta.bib` when an object with a string `type`), else `null`. */\nexport function bibOf(node: PMNode): CslItem | null {\n const bib: unknown = node.attrs.meta?.[BIB_KEY]\n if (!bib || typeof bib !== 'object' || Array.isArray(bib)) return null\n return 'type' in bib && typeof bib.type === 'string' ? bib as CslItem : null\n}\n\n/** The full CSL-JSON item of the composite `node`: its `bib` plus the block id and the title (first child). */\nexport function cslOf(node: PMNode): (CslItem & { id: string, title: string }) | null {\n const bib = bibOf(node)\n if (!bib) return null\n return { ...bib, id: blockIdOf(node) ?? '', title: node.childCount > 0 ? node.child(0).textContent : '' }\n}\n\n/** The year of the first date part of `issued`, or `null`. */\nexport function yearOf(csl: Pick<CslItem, 'issued'>): number | null {\n const year = csl.issued?.['date-parts']?.[0]?.[0]\n return typeof year === 'number' && Number.isFinite(year) ? year : null\n}\n\nconst familyOf = (name: CslName): string => (name.family ?? name.literal ?? '').trim()\n\nconst SHORT_TITLE_LENGTH = 40\n\n/** \"Doe 2024\", \"Doe & Roe 2024\", \"Doe et al. 2024\"; without authors the first 40 characters of `title`; without a year no year. */\nexport function citeShort(csl: Pick<CslItem, 'author' | 'issued'>, title: string): string {\n const families = (csl.author ?? []).map(familyOf).filter(Boolean)\n const who = families.length === 0\n ? title.trim().slice(0, SHORT_TITLE_LENGTH)\n : families.length === 1\n ? families[0]!\n : families.length === 2 ? `${families[0]} & ${families[1]}` : `${families[0]} et al.`\n const year = yearOf(csl)\n return year === null ? who : `${who} ${year}`.trim()\n}\n\nconst initialsOf = (given: string): string => given.split(/[\\s-]+/).filter(Boolean).map(word => `${[...word][0]!.toUpperCase()}.`).join(' ')\n\nfunction apaName(name: CslName): string {\n const family = (name.family ?? '').trim()\n if (!family) return (name.literal ?? '').trim()\n const initials = initialsOf((name.given ?? '').trim())\n return initials ? `${family}, ${initials}` : family\n}\n\nconst withStop = (text: string): string => /[.!?]$/.test(text) ? text : `${text}.`\n\n/** An APA-like reference: \"Doe, J., Roe, R. (2024). Title. Journal, 12(3), 45–67. https://doi.org/…\"; empty parts are left out. */\nexport function citeFull(csl: CslItem & { title: string }): string {\n const authors = (csl.author ?? []).map(apaName).filter(Boolean).join(', ')\n const year = yearOf(csl)\n const head = [authors, year === null ? '' : `(${year})`].filter(Boolean).join(' ')\n const volume = [csl.volume, csl.issue ? `(${csl.issue})` : ''].filter(Boolean).join('')\n const container = [csl['container-title'], volume, csl.page].map(part => part?.trim()).filter(Boolean).join(', ')\n const doi = csl.DOI?.trim()\n const link = doi ? (/^https?:\\/\\//i.test(doi) ? doi : `https://doi.org/${doi}`) : csl.URL?.trim()\n const publisher = [csl['publisher-place'], csl.publisher].map(part => part?.trim()).filter(Boolean).join(': ')\n return [\n head && withStop(head),\n csl.title.trim() && withStop(csl.title.trim()),\n container && withStop(container),\n // A book or report is located by its publisher; a journal article by its container.\n !container && publisher && withStop(publisher),\n link,\n ].filter(Boolean).join(' ')\n}\n\nconst BIBTEX_ENTRY: Partial<Record<CslType, string>> = {\n 'article-journal': 'article',\n book: 'book',\n chapter: 'incollection',\n 'paper-conference': 'inproceedings',\n thesis: 'phdthesis',\n report: 'techreport',\n}\n\n/** Lowercase ASCII letters and digits of `text`: diacritics folded, everything else dropped. */\nconst asciiKey = (text: string): string => text.normalize('NFD').replace(/\\p{M}/gu, '').toLowerCase().replace(/[^a-z0-9]/g, '')\n\nconst bibtexName = (name: CslName): string => {\n const family = (name.family ?? '').trim()\n if (!family) return (name.literal ?? '').trim()\n const given = (name.given ?? '').trim()\n return given ? `${family}, ${given}` : family\n}\n\n/** The citation key: first author's family name + year + first title word (all lowercase ASCII). */\nfunction bibtexKey(csl: CslItem & { id: string, title: string }): string {\n const first = csl.author?.[0]\n const family = first ? asciiKey(familyOf(first)) : ''\n const year = yearOf(csl)\n const word = asciiKey(csl.title.trim().split(/\\s+/)[0] ?? '')\n return `${family}${year ?? ''}${word}` || asciiKey(csl.id) || 'ref'\n}\n\n/** The item as one BibTeX entry (`@article{doe2024title, … }`), one field per line; empty fields are left out. */\nexport function bibtexOf(csl: CslItem & { id: string, title: string }): string {\n const entry = BIBTEX_ENTRY[csl.type] ?? 'misc'\n const container = csl['container-title']?.trim()\n const fields: [string, string | undefined][] = [\n ['title', csl.title.trim()],\n ['author', (csl.author ?? []).map(bibtexName).filter(Boolean).join(' and ')],\n ['year', yearOf(csl)?.toString()],\n ['journal', entry === 'article' ? container : undefined],\n ['booktitle', entry === 'incollection' || entry === 'inproceedings' ? container : undefined],\n ['volume', csl.volume?.trim()],\n ['number', csl.issue?.trim()],\n ['pages', csl.page?.trim().replace(/\\s*[-–—]+\\s*/g, '--')],\n [entry === 'phdthesis' ? 'school' : entry === 'techreport' ? 'institution' : 'publisher', csl.publisher?.trim()],\n ['address', csl['publisher-place']?.trim()],\n ['doi', csl.DOI?.trim()],\n ['isbn', csl.ISBN?.trim()],\n ['url', csl.URL?.trim()],\n ]\n const lines = fields.filter(([, value]) => value).map(([name, value]) => ` ${name} = {${value}},`)\n return `@${entry}{${bibtexKey(csl)},\\n${lines.join('\\n')}\\n}\\n`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bibliography.js","names":[],"sources":["../../../src/features/composites/bibliography.ts"],"sourcesContent":["// Vue-free and server-importable: bibliographic data of a composite (a Typed block) as CSL-JSON in `meta.bib`, and its citation and BibTeX renderings.\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf } from '../../core/schema/block-ids'\nimport type { MessageKey, Translate } from '../../i18n'\nimport { CSL_TYPES, type CslItem, type CslName, type CslType } from '../../core/backend-types'\nimport { BIB_KEY } from './meta-keys'\n\n// The CSL types live in `core/backend-types.ts` (the `ai.extract` wire type names them); this module is their feature-side entry.\nexport { CSL_TYPES, type CslDate, type CslItem, type CslName, type CslType } from '../../core/backend-types'\n\nexport { BIB_KEY }\n\n/** The select options of the CSL types, labelled through `t`. */\nexport function cslTypeOptions(t: Translate): { value: CslType, label: string }[] {\n return CSL_TYPES.map(value => ({ value, label: t(`composites.bib.type.${value}` as MessageKey) }))\n}\n\n/** The bibliographic data of the composite `node` (`meta.bib` when an object with a string `type`), else `null`. */\nexport function bibOf(node: PMNode): CslItem | null {\n const bib: unknown = node.attrs.meta?.[BIB_KEY]\n if (!bib || typeof bib !== 'object' || Array.isArray(bib)) return null\n return 'type' in bib && typeof bib.type === 'string' ? bib as CslItem : null\n}\n\n/** The full CSL-JSON item of the composite `node`: its `bib` plus the block id and the title (first child). */\nexport function cslOf(node: PMNode): (CslItem & { id: string, title: string }) | null {\n const bib = bibOf(node)\n if (!bib) return null\n return { ...bib, id: blockIdOf(node) ?? '', title: node.childCount > 0 ? node.child(0).textContent : '' }\n}\n\n/** The year of the first date part of `issued`, or `null`. */\nexport function yearOf(csl: Pick<CslItem, 'issued'>): number | null {\n const year = csl.issued?.['date-parts']?.[0]?.[0]\n return typeof year === 'number' && Number.isFinite(year) ? year : null\n}\n\nconst familyOf = (name: CslName): string => (name.family ?? name.literal ?? '').trim()\n\nconst SHORT_TITLE_LENGTH = 40\n\n/** \"Doe 2024\", \"Doe & Roe 2024\", \"Doe et al. 2024\"; without authors the first 40 characters of `title`; without a year no year. */\nexport function citeShort(csl: Pick<CslItem, 'author' | 'issued'>, title: string): string {\n const families = (csl.author ?? []).map(familyOf).filter(Boolean)\n const who = families.length === 0\n ? title.trim().slice(0, SHORT_TITLE_LENGTH)\n : families.length === 1\n ? families[0]!\n : families.length === 2 ? `${families[0]} & ${families[1]}` : `${families[0]} et al.`\n const year = yearOf(csl)\n return year === null ? who : `${who} ${year}`.trim()\n}\n\nconst initialsOf = (given: string): string => given.split(/[\\s-]+/).filter(Boolean).map(word => `${[...word][0]!.toUpperCase()}.`).join(' ')\n\nfunction apaName(name: CslName): string {\n const family = (name.family ?? '').trim()\n if (!family) return (name.literal ?? '').trim()\n const initials = initialsOf((name.given ?? '').trim())\n return initials ? `${family}, ${initials}` : family\n}\n\nconst withStop = (text: string): string => /[.!?]$/.test(text) ? text : `${text}.`\n\n/** An APA-like reference: \"Doe, J., Roe, R. (2024). Title. Journal, 12(3), 45–67. https://doi.org/…\"; empty parts are left out. */\nexport function citeFull(csl: CslItem & { title: string }): string {\n const authors = (csl.author ?? []).map(apaName).filter(Boolean).join(', ')\n const year = yearOf(csl)\n const head = [authors, year === null ? '' : `(${year})`].filter(Boolean).join(' ')\n const volume = [csl.volume, csl.issue ? `(${csl.issue})` : ''].filter(Boolean).join('')\n const container = [csl['container-title'], volume, csl.page].map(part => part?.trim()).filter(Boolean).join(', ')\n const doi = csl.DOI?.trim()\n const link = doi ? (/^https?:\\/\\//i.test(doi) ? doi : `https://doi.org/${doi}`) : csl.URL?.trim()\n const publisher = [csl['publisher-place'], csl.publisher].map(part => part?.trim()).filter(Boolean).join(': ')\n return [\n head && withStop(head),\n csl.title.trim() && withStop(csl.title.trim()),\n container && withStop(container),\n // A book or report is located by its publisher; a journal article by its container.\n !container && publisher && withStop(publisher),\n link,\n ].filter(Boolean).join(' ')\n}\n\nconst BIBTEX_ENTRY: Partial<Record<CslType, string>> = {\n 'article-journal': 'article',\n book: 'book',\n chapter: 'incollection',\n 'paper-conference': 'inproceedings',\n thesis: 'phdthesis',\n report: 'techreport',\n}\n\n/** Lowercase ASCII letters and digits of `text`: diacritics folded, everything else dropped. */\nconst asciiKey = (text: string): string => text.normalize('NFD').replace(/\\p{M}/gu, '').toLowerCase().replace(/[^a-z0-9]/g, '')\n\nconst bibtexName = (name: CslName): string => {\n const family = (name.family ?? '').trim()\n if (!family) return (name.literal ?? '').trim()\n const given = (name.given ?? '').trim()\n return given ? `${family}, ${given}` : family\n}\n\n/** The citation key: first author's family name + year + first title word (all lowercase ASCII). */\nfunction bibtexKey(csl: CslItem & { id: string, title: string }): string {\n const first = csl.author?.[0]\n const family = first ? asciiKey(familyOf(first)) : ''\n const year = yearOf(csl)\n const word = asciiKey(csl.title.trim().split(/\\s+/)[0] ?? '')\n return `${family}${year ?? ''}${word}` || asciiKey(csl.id) || 'ref'\n}\n\n/** The item as one BibTeX entry (`@article{doe2024title, … }`), one field per line; empty fields are left out. */\nexport function bibtexOf(csl: CslItem & { id: string, title: string }): string {\n const entry = BIBTEX_ENTRY[csl.type] ?? 'misc'\n const container = csl['container-title']?.trim()\n const fields: [string, string | undefined][] = [\n ['title', csl.title.trim()],\n ['author', (csl.author ?? []).map(bibtexName).filter(Boolean).join(' and ')],\n ['year', yearOf(csl)?.toString()],\n ['journal', entry === 'article' ? container : undefined],\n ['booktitle', entry === 'incollection' || entry === 'inproceedings' ? container : undefined],\n ['volume', csl.volume?.trim()],\n ['number', csl.issue?.trim()],\n ['pages', csl.page?.trim().replace(/\\s*[-–—]+\\s*/g, '--')],\n [entry === 'phdthesis' ? 'school' : entry === 'techreport' ? 'institution' : 'publisher', csl.publisher?.trim()],\n ['address', csl['publisher-place']?.trim()],\n ['doi', csl.DOI?.trim()],\n ['isbn', csl.ISBN?.trim()],\n ['url', csl.URL?.trim()],\n ]\n const lines = fields.filter(([, value]) => value).map(([name, value]) => ` ${name} = {${value}},`)\n return `@${entry}{${bibtexKey(csl)},\\n${lines.join('\\n')}\\n}\\n`\n}\n"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { CapabilityRegistry } from "../../core/capabilities.js";
2
+ import { BlockType } from "./block-types.js";
3
+ //#region src/features/composites/block-types-json.d.ts
4
+ /** One thing wrong in a Block type file. `typeId` is the `id` of the entry (null when it has none), `capability` the capability whose config is wrong (null for a problem of the entry itself). */
5
+ export interface BlockTypeJsonIssue {
6
+ /** Position of the entry in the file's array; -1 for a problem of the file itself. */
7
+ index: number;
8
+ typeId: string | null;
9
+ capability: string | null;
10
+ /** Where in the entry (or in the capability's config) the problem is, e.g. `definitions[0].type`; empty for the whole entry. */
11
+ path: string;
12
+ message: string;
13
+ }
14
+ /** Thrown by `parseBlockTypes`: lists every issue of the file, one per line, each naming the Block type, the capability and the path. */
15
+ export declare class BlockTypeJsonError extends Error {
16
+ readonly issues: readonly BlockTypeJsonIssue[];
17
+ constructor(issues: readonly BlockTypeJsonIssue[]);
18
+ }
19
+ /**
20
+ * Reads the Block types of a JSON file: `input` is the file's text or its parsed value, an array of `BlockType`. Every entry is
21
+ * checked against the capability registry: its fields, a unique `id`, each capability it lists exists and is listed once, and its
22
+ * `config` passes the capability's `configSchema` (a capability without one takes no config). Throws `BlockTypeJsonError` with
23
+ * all issues of the file at once, each naming the Block type, the capability and the path.
24
+ *
25
+ * `registry`: the capabilities a Block type may list; default the built-in ones and those of the composites feature. A host whose
26
+ * features declare more capabilities passes `preset.capabilities`. The result is what the `blockTypes` option takes
27
+ * (`mergeBlockTypes` layers it over the built-in types).
28
+ */
29
+ export declare function parseBlockTypes(input: unknown, options?: {
30
+ registry?: CapabilityRegistry;
31
+ }): BlockType[];
32
+ /**
33
+ * Layers `extra` over `base`: a type of `extra` whose id `base` has replaces it in place, the others follow `base`. The host's own file
34
+ * over the built-in types: `mergeBlockTypes(defaultBlockTypes(t), parseBlockTypes(file))`.
35
+ */
36
+ export declare function mergeBlockTypes(base: readonly BlockType[], extra: readonly BlockType[]): BlockType[];
37
+ //#endregion
38
+ //# sourceMappingURL=block-types-json.d.ts.map
@@ -0,0 +1,122 @@
1
+ import { pathText } from "../../core/util.js";
2
+ import { BUILT_IN_BLOCK_CAPABILITIES, capabilityIssues, createCapabilityRegistry } from "../../core/capabilities.js";
3
+ import { compositeBlockCapabilities } from "./capabilities.js";
4
+ import { z } from "zod";
5
+ //#region src/features/composites/block-types-json.ts
6
+ /** Thrown by `parseBlockTypes`: lists every issue of the file, one per line, each naming the Block type, the capability and the path. */
7
+ var BlockTypeJsonError = class extends Error {
8
+ issues;
9
+ constructor(issues) {
10
+ super(issues.map(describe).join("\n"));
11
+ this.name = "BlockTypeJsonError";
12
+ this.issues = issues;
13
+ }
14
+ };
15
+ function describe({ index, typeId, capability, path, message }) {
16
+ if (index < 0) return `Block type file: ${message}`;
17
+ const type = typeId === null ? `Block type entry ${index}` : `Block type "${typeId}"`;
18
+ const where = capability === null ? path : `capability "${capability}" config${path ? `, ${path}` : ""}`;
19
+ return `${type}${where ? `: ${where}` : ""}: ${message}`;
20
+ }
21
+ const entrySchema = z.strictObject({
22
+ id: z.string().regex(/^[\p{L}\p{N}_-]{1,50}$/u, "must be 1 to 50 letters, digits, \"_\" or \"-\""),
23
+ label: z.string().min(1),
24
+ icon: z.string().min(1),
25
+ titlePlaceholder: z.string().optional(),
26
+ descriptionPlaceholder: z.string().optional(),
27
+ chrome: z.strictObject({
28
+ tags: z.boolean().optional(),
29
+ tone: z.string().optional(),
30
+ controls: z.boolean().optional()
31
+ }).optional(),
32
+ capabilities: z.array(z.strictObject({
33
+ id: z.string().min(1),
34
+ config: z.unknown().optional()
35
+ }))
36
+ });
37
+ let defaultRegistry;
38
+ /** The built-in capabilities and those of the composites feature: what a Block type file may name without a host feature adding more. */
39
+ function builtInRegistry() {
40
+ return defaultRegistry ??= createCapabilityRegistry([...BUILT_IN_BLOCK_CAPABILITIES, ...compositeBlockCapabilities()]);
41
+ }
42
+ /**
43
+ * Reads the Block types of a JSON file: `input` is the file's text or its parsed value, an array of `BlockType`. Every entry is
44
+ * checked against the capability registry: its fields, a unique `id`, each capability it lists exists and is listed once, and its
45
+ * `config` passes the capability's `configSchema` (a capability without one takes no config). Throws `BlockTypeJsonError` with
46
+ * all issues of the file at once, each naming the Block type, the capability and the path.
47
+ *
48
+ * `registry`: the capabilities a Block type may list; default the built-in ones and those of the composites feature. A host whose
49
+ * features declare more capabilities passes `preset.capabilities`. The result is what the `blockTypes` option takes
50
+ * (`mergeBlockTypes` layers it over the built-in types).
51
+ */
52
+ function parseBlockTypes(input, options = {}) {
53
+ const registry = options.registry ?? builtInRegistry();
54
+ const fileIssue = (message) => new BlockTypeJsonError([{
55
+ index: -1,
56
+ typeId: null,
57
+ capability: null,
58
+ path: "",
59
+ message
60
+ }]);
61
+ let value = input;
62
+ if (typeof input === "string") try {
63
+ value = JSON.parse(input);
64
+ } catch (error) {
65
+ throw fileIssue(`not valid JSON (${error instanceof Error ? error.message : String(error)})`);
66
+ }
67
+ if (!Array.isArray(value)) throw fileIssue("expected a JSON array of Block types");
68
+ const issues = [];
69
+ const types = [];
70
+ const firstIndex = /* @__PURE__ */ new Map();
71
+ value.forEach((entry, index) => {
72
+ const typeId = entry && typeof entry === "object" && "id" in entry && typeof entry.id === "string" ? entry.id : null;
73
+ const parsed = entrySchema.safeParse(entry);
74
+ if (!parsed.success) {
75
+ for (const issue of parsed.error.issues) issues.push({
76
+ index,
77
+ typeId,
78
+ capability: null,
79
+ path: pathText(issue.path),
80
+ message: issue.message
81
+ });
82
+ return;
83
+ }
84
+ const { id } = parsed.data;
85
+ const first = firstIndex.get(id);
86
+ if (first !== void 0) {
87
+ issues.push({
88
+ index,
89
+ typeId: id,
90
+ capability: null,
91
+ path: "id",
92
+ message: `duplicate Block type id (first defined by entry ${first})`
93
+ });
94
+ return;
95
+ }
96
+ firstIndex.set(id, index);
97
+ const found = capabilityIssues(registry, parsed.data);
98
+ for (const { capability, path, message } of found) issues.push({
99
+ index,
100
+ typeId: id,
101
+ capability,
102
+ path,
103
+ message
104
+ });
105
+ if (!found.length) types.push(parsed.data);
106
+ });
107
+ if (issues.length) throw new BlockTypeJsonError(issues);
108
+ return types;
109
+ }
110
+ /**
111
+ * Layers `extra` over `base`: a type of `extra` whose id `base` has replaces it in place, the others follow `base`. The host's own file
112
+ * over the built-in types: `mergeBlockTypes(defaultBlockTypes(t), parseBlockTypes(file))`.
113
+ */
114
+ function mergeBlockTypes(base, extra) {
115
+ const replacements = new Map(extra.map((type) => [type.id, type]));
116
+ const baseIds = new Set(base.map((type) => type.id));
117
+ return [...base.map((type) => replacements.get(type.id) ?? type), ...extra.filter((type) => !baseIds.has(type.id))];
118
+ }
119
+ //#endregion
120
+ export { BlockTypeJsonError, mergeBlockTypes, parseBlockTypes };
121
+
122
+ //# sourceMappingURL=block-types-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-types-json.js","names":[],"sources":["../../../src/features/composites/block-types-json.ts"],"sourcesContent":["// Vue-free and server-importable: Block types as JSON. A host keeps its Block types in a file (`BlockType[]`: plain data, presentation\n// and `capabilities` with their config, no functions), the browser and the server read the same file through `parseBlockTypes`, and\n// the result goes to the editor through the existing `blockTypes` option.\nimport { z } from 'zod'\nimport { BUILT_IN_BLOCK_CAPABILITIES, type CapabilityRegistry, capabilityIssues, createCapabilityRegistry } from '../../core/capabilities'\nimport { pathText } from '../../core/util'\nimport type { BlockType } from './block-types'\nimport { compositeBlockCapabilities } from './capabilities'\n\n/** One thing wrong in a Block type file. `typeId` is the `id` of the entry (null when it has none), `capability` the capability whose config is wrong (null for a problem of the entry itself). */\nexport interface BlockTypeJsonIssue {\n /** Position of the entry in the file's array; -1 for a problem of the file itself. */\n index: number\n typeId: string | null\n capability: string | null\n /** Where in the entry (or in the capability's config) the problem is, e.g. `definitions[0].type`; empty for the whole entry. */\n path: string\n message: string\n}\n\n/** Thrown by `parseBlockTypes`: lists every issue of the file, one per line, each naming the Block type, the capability and the path. */\nexport class BlockTypeJsonError extends Error {\n readonly issues: readonly BlockTypeJsonIssue[]\n\n constructor(issues: readonly BlockTypeJsonIssue[]) {\n super(issues.map(describe).join('\\n'))\n this.name = 'BlockTypeJsonError'\n this.issues = issues\n }\n}\n\n// i18n-ignore-start: developer-facing errors of a host's config file, not UI text\nfunction describe({ index, typeId, capability, path, message }: BlockTypeJsonIssue): string {\n if (index < 0) return `Block type file: ${message}`\n const type = typeId === null ? `Block type entry ${index}` : `Block type \"${typeId}\"`\n const where = capability === null ? path : `capability \"${capability}\" config${path ? `, ${path}` : ''}`\n return `${type}${where ? `: ${where}` : ''}: ${message}`\n}\n// i18n-ignore-end\n\n/** Type ids go into Markdown fences and AI requests, so they are letters, digits, `_` and `-`. */\nconst TYPE_ID = /^[\\p{L}\\p{N}_-]{1,50}$/u\n\nconst entrySchema = z.strictObject({\n id: z.string().regex(TYPE_ID, 'must be 1 to 50 letters, digits, \"_\" or \"-\"'),\n label: z.string().min(1),\n icon: z.string().min(1),\n titlePlaceholder: z.string().optional(),\n descriptionPlaceholder: z.string().optional(),\n chrome: z.strictObject({ tags: z.boolean().optional(), tone: z.string().optional(), controls: z.boolean().optional() }).optional(),\n capabilities: z.array(z.strictObject({ id: z.string().min(1), config: z.unknown().optional() })),\n})\n\nlet defaultRegistry: CapabilityRegistry | undefined\n\n/** The built-in capabilities and those of the composites feature: what a Block type file may name without a host feature adding more. */\nfunction builtInRegistry(): CapabilityRegistry {\n return defaultRegistry ??= createCapabilityRegistry([...BUILT_IN_BLOCK_CAPABILITIES, ...compositeBlockCapabilities()])\n}\n\n/**\n * Reads the Block types of a JSON file: `input` is the file's text or its parsed value, an array of `BlockType`. Every entry is\n * checked against the capability registry: its fields, a unique `id`, each capability it lists exists and is listed once, and its\n * `config` passes the capability's `configSchema` (a capability without one takes no config). Throws `BlockTypeJsonError` with\n * all issues of the file at once, each naming the Block type, the capability and the path.\n *\n * `registry`: the capabilities a Block type may list; default the built-in ones and those of the composites feature. A host whose\n * features declare more capabilities passes `preset.capabilities`. The result is what the `blockTypes` option takes\n * (`mergeBlockTypes` layers it over the built-in types).\n */\nexport function parseBlockTypes(input: unknown, options: { registry?: CapabilityRegistry } = {}): BlockType[] {\n const registry = options.registry ?? builtInRegistry()\n const fileIssue = (message: string): BlockTypeJsonError => new BlockTypeJsonError([{ index: -1, typeId: null, capability: null, path: '', message }])\n let value = input\n if (typeof input === 'string') {\n try {\n value = JSON.parse(input)\n }\n catch (error) {\n // i18n-ignore: developer-facing error of a host's config file\n throw fileIssue(`not valid JSON (${error instanceof Error ? error.message : String(error)})`)\n }\n }\n // i18n-ignore: developer-facing error of a host's config file\n if (!Array.isArray(value)) throw fileIssue('expected a JSON array of Block types')\n\n const issues: BlockTypeJsonIssue[] = []\n const types: BlockType[] = []\n const firstIndex = new Map<string, number>()\n value.forEach((entry: unknown, index) => {\n const typeId = entry && typeof entry === 'object' && 'id' in entry && typeof entry.id === 'string' ? entry.id : null\n const parsed = entrySchema.safeParse(entry)\n if (!parsed.success) {\n for (const issue of parsed.error.issues) issues.push({ index, typeId, capability: null, path: pathText(issue.path), message: issue.message })\n return\n }\n const { id } = parsed.data\n const first = firstIndex.get(id)\n if (first !== undefined) {\n // i18n-ignore: developer-facing error of a host's config file\n issues.push({ index, typeId: id, capability: null, path: 'id', message: `duplicate Block type id (first defined by entry ${first})` })\n return\n }\n firstIndex.set(id, index)\n\n const found = capabilityIssues(registry, parsed.data)\n for (const { capability, path, message } of found) issues.push({ index, typeId: id, capability, path, message })\n if (!found.length) types.push(parsed.data)\n })\n\n if (issues.length) throw new BlockTypeJsonError(issues)\n return types\n}\n\n/**\n * Layers `extra` over `base`: a type of `extra` whose id `base` has replaces it in place, the others follow `base`. The host's own file\n * over the built-in types: `mergeBlockTypes(defaultBlockTypes(t), parseBlockTypes(file))`.\n */\nexport function mergeBlockTypes(base: readonly BlockType[], extra: readonly BlockType[]): BlockType[] {\n const replacements = new Map(extra.map(type => [type.id, type]))\n const baseIds = new Set(base.map(type => type.id))\n return [...base.map(type => replacements.get(type.id) ?? type), ...extra.filter(type => !baseIds.has(type.id))]\n}\n"],"mappings":"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"}