@ecsplico/editor 2.1.2 → 3.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 (692) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/README.md +39 -156
  3. package/dist/adapters/comments/drizzle.d.ts +1 -1
  4. package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
  5. package/dist/adapters/comments/rest-model.js.map +1 -0
  6. package/dist/adapters/comments/types.d.ts +2 -2
  7. package/dist/adapters/document-state.d.ts +23 -0
  8. package/dist/adapters/document-state.js +66 -0
  9. package/dist/adapters/document-state.js.map +1 -0
  10. package/dist/adapters/documents.js +14 -11
  11. package/dist/adapters/documents.js.map +1 -1
  12. package/dist/adapters/drizzle.d.ts +2 -2
  13. package/dist/adapters/drizzle.js +97 -88
  14. package/dist/adapters/drizzle.js.map +1 -1
  15. package/dist/adapters/forge.d.ts +12 -7
  16. package/dist/adapters/forge.js +38 -45
  17. package/dist/adapters/forge.js.map +1 -1
  18. package/dist/adapters/http.d.ts +20 -0
  19. package/dist/adapters/http.js +68 -0
  20. package/dist/adapters/http.js.map +1 -0
  21. package/dist/adapters/index.d.ts +5 -5
  22. package/dist/adapters/json.d.ts +3 -3
  23. package/dist/adapters/json.js +58 -49
  24. package/dist/adapters/json.js.map +1 -1
  25. package/dist/adapters/memory.d.ts +21 -0
  26. package/dist/adapters/memory.js +53 -0
  27. package/dist/adapters/memory.js.map +1 -0
  28. package/dist/adapters/nitro.d.ts +23 -20
  29. package/dist/adapters/nitro.js +58 -163
  30. package/dist/adapters/nitro.js.map +1 -1
  31. package/dist/adapters/react.d.ts +55 -0
  32. package/dist/adapters/react.js +209 -0
  33. package/dist/adapters/react.js.map +1 -0
  34. package/dist/adapters/shared.d.ts +15 -21
  35. package/dist/adapters/shared.js +10 -8
  36. package/dist/adapters/shared.js.map +1 -1
  37. package/dist/adapters/vue.d.ts +15 -30
  38. package/dist/adapters/vue.js +36 -87
  39. package/dist/adapters/vue.js.map +1 -1
  40. package/dist/config/index.d.ts +105 -0
  41. package/dist/config/index.js +95 -0
  42. package/dist/config/index.js.map +1 -0
  43. package/dist/core/abilities.js +1 -0
  44. package/dist/core/abilities.js.map +1 -1
  45. package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
  46. package/dist/core/api-routes.js.map +1 -0
  47. package/dist/core/backend-capability.d.ts +7 -0
  48. package/dist/core/backend-capability.js +17 -0
  49. package/dist/core/backend-capability.js.map +1 -0
  50. package/dist/core/backend-types.d.ts +245 -0
  51. package/dist/core/backend-types.js +17 -0
  52. package/dist/core/backend-types.js.map +1 -0
  53. package/dist/core/backend.d.ts +108 -0
  54. package/dist/core/backend.js +14 -0
  55. package/dist/core/backend.js.map +1 -0
  56. package/dist/core/block-convert.d.ts +7 -5
  57. package/dist/core/block-convert.js +28 -5
  58. package/dist/core/block-convert.js.map +1 -1
  59. package/dist/core/command-types.d.ts +89 -0
  60. package/dist/core/commands.d.ts +40 -0
  61. package/dist/core/commands.js +151 -0
  62. package/dist/core/commands.js.map +1 -0
  63. package/dist/core/context.d.ts +17 -20
  64. package/dist/core/context.js +24 -21
  65. package/dist/core/context.js.map +1 -1
  66. package/dist/core/core-feature.d.ts +22 -7
  67. package/dist/core/core-feature.js +51 -18
  68. package/dist/core/core-feature.js.map +1 -1
  69. package/dist/core/define-editor.d.ts +4 -3
  70. package/dist/core/define-editor.js +14 -7
  71. package/dist/core/define-editor.js.map +1 -1
  72. package/dist/core/document-markdown.js +12 -3
  73. package/dist/core/document-markdown.js.map +1 -1
  74. package/dist/core/editor-ui.d.ts +8 -1
  75. package/dist/core/editor-ui.js +9 -8
  76. package/dist/core/editor-ui.js.map +1 -1
  77. package/dist/core/extension-points.d.ts +3 -15
  78. package/dist/core/feature-context.d.ts +8 -7
  79. package/dist/core/feature-context.js +12 -11
  80. package/dist/core/feature-context.js.map +1 -1
  81. package/dist/core/feature.d.ts +35 -19
  82. package/dist/core/feature.js.map +1 -1
  83. package/dist/core/fences.d.ts +2 -1
  84. package/dist/core/fences.js +3 -2
  85. package/dist/core/fences.js.map +1 -1
  86. package/dist/core/fetch-error.d.ts +1 -1
  87. package/dist/core/fetch-error.js +3 -1
  88. package/dist/core/fetch-error.js.map +1 -1
  89. package/dist/core/hash.d.ts +5 -0
  90. package/dist/core/hash.js +11 -0
  91. package/dist/core/hash.js.map +1 -0
  92. package/dist/core/http-backend.d.ts +11 -0
  93. package/dist/core/http-backend.js +195 -0
  94. package/dist/core/http-backend.js.map +1 -0
  95. package/dist/core/index.d.ts +14 -9
  96. package/dist/core/index.js +12 -9
  97. package/dist/core/palette.d.ts +48 -0
  98. package/dist/core/palette.js +95 -0
  99. package/dist/core/palette.js.map +1 -0
  100. package/dist/core/schema/abilities.d.ts +2 -1
  101. package/dist/core/schema/abilities.js +5 -1
  102. package/dist/core/schema/abilities.js.map +1 -1
  103. package/dist/core/schema/index.d.ts +11 -5
  104. package/dist/core/schema/index.js +13 -8
  105. package/dist/core/schema/index.js.map +1 -1
  106. package/dist/core/schema/mark-flags.d.ts +8 -1
  107. package/dist/core/schema/of-features.d.ts +13 -0
  108. package/dist/core/schema/of-features.js +14 -0
  109. package/dist/core/schema/of-features.js.map +1 -0
  110. package/dist/core/slash.d.ts +8 -6
  111. package/dist/core/slash.js +24 -16
  112. package/dist/core/slash.js.map +1 -1
  113. package/dist/core/surface-session.js +13 -5
  114. package/dist/core/surface-session.js.map +1 -1
  115. package/dist/core/toolbar-items.d.ts +10 -4
  116. package/dist/core/toolbar-items.js +269 -195
  117. package/dist/core/toolbar-items.js.map +1 -1
  118. package/dist/core/util.js +11 -0
  119. package/dist/core/util.js.map +1 -0
  120. package/dist/document/DocumentSurface.d.ts +1 -1
  121. package/dist/features/ai/agent-op-review.d.ts +1 -1
  122. package/dist/features/ai/agent-op-review.js +1 -1
  123. package/dist/features/ai/agent-ops.js +1 -1
  124. package/dist/features/ai/ai-apply.d.ts +2 -2
  125. package/dist/features/ai/ai-apply.js +1 -1
  126. package/dist/features/ai/ai-review.js +1 -1
  127. package/dist/features/ai/ai-sse.d.ts +6 -6
  128. package/dist/features/ai/ai-sse.js +20 -63
  129. package/dist/features/ai/ai-sse.js.map +1 -1
  130. package/dist/features/ai/assistant.d.ts +8 -7
  131. package/dist/features/ai/assistant.js +3 -3
  132. package/dist/features/ai/assistant.js.map +1 -1
  133. package/dist/features/ai/block-actions.d.ts +3 -2
  134. package/dist/features/ai/block-actions.js +3 -2
  135. package/dist/features/ai/block-actions.js.map +1 -1
  136. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  137. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  138. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
  139. package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
  140. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  141. package/dist/features/ai/document-chat-model.d.ts +8 -7
  142. package/dist/features/ai/document-chat-model.js +5 -5
  143. package/dist/features/ai/document-chat-model.js.map +1 -1
  144. package/dist/features/ai/document-chat.js +1 -1
  145. package/dist/features/ai/feature.js +27 -14
  146. package/dist/features/ai/feature.js.map +1 -1
  147. package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
  148. package/dist/features/ai/ghost-autocomplete.js +6 -9
  149. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  150. package/dist/features/ai/index.d.ts +4 -3
  151. package/dist/features/ai/index.js +10 -10
  152. package/dist/features/ai/model-profiles.d.ts +5 -4
  153. package/dist/features/ai/model-profiles.js +2 -2
  154. package/dist/features/ai/model-profiles.js.map +1 -1
  155. package/dist/features/ai/protocol/block-lines.js +1 -1
  156. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  157. package/dist/features/ai/protocol/chat.js +4 -1
  158. package/dist/features/ai/protocol/chat.js.map +1 -1
  159. package/dist/features/ai/protocol/index.d.ts +3 -2
  160. package/dist/features/ai/protocol/models.d.ts +2 -16
  161. package/dist/features/ai/protocol/models.js.map +1 -1
  162. package/dist/features/ai/rate-limit.d.ts +1 -1
  163. package/dist/features/ai/rate-limit.js +3 -1
  164. package/dist/features/ai/rate-limit.js.map +1 -1
  165. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  166. package/dist/features/ai/react/AiSidebar.js +4 -4
  167. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  168. package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
  169. package/dist/features/ai/react/DocumentChatOps.js +1 -1
  170. package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
  171. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  172. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  173. package/dist/features/ai/react/use-ai-assistant.js +3 -3
  174. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  175. package/dist/features/ai/react/use-document-chat.js +2 -2
  176. package/dist/features/ai/react/use-document-chat.js.map +1 -1
  177. package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
  178. package/dist/features/ai/selection-flow.d.ts +1 -1
  179. package/dist/features/ai/selection-flow.js +1 -1
  180. package/dist/features/ai/sidebar-flow.d.ts +1 -1
  181. package/dist/features/ai/sidebar-flow.js +1 -1
  182. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  183. package/dist/features/ai/use-ai-assistant.js +3 -3
  184. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  185. package/dist/features/ai/use-document-chat.js +2 -2
  186. package/dist/features/ai/use-document-chat.js.map +1 -1
  187. package/dist/features/ai/use-model-profiles.d.ts +2 -1
  188. package/dist/features/authors/attribution.d.ts +1 -1
  189. package/dist/features/authors/feature.js +12 -11
  190. package/dist/features/authors/feature.js.map +1 -1
  191. package/dist/features/block-ui/block-actions.d.ts +1 -1
  192. package/dist/features/block-ui/block-actions.js +1 -1
  193. package/dist/features/block-ui/block-actions.js.map +1 -1
  194. package/dist/features/block-ui/components/BlockUi.js +1 -1
  195. package/dist/features/block-ui/feature.js +1 -0
  196. package/dist/features/block-ui/feature.js.map +1 -1
  197. package/dist/features/block-ui/react/BlockUi.js +1 -1
  198. package/dist/features/collab/anchor-resolver.js +1 -1
  199. package/dist/features/collab/feature.js +2 -0
  200. package/dist/features/collab/feature.js.map +1 -1
  201. package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
  202. package/dist/features/comments/feature.js +12 -11
  203. package/dist/features/comments/feature.js.map +1 -1
  204. package/dist/features/comments/index.d.ts +3 -2
  205. package/dist/features/comments/index.js +1 -1
  206. package/dist/features/comments/margin-layout.d.ts +3 -2
  207. package/dist/features/comments/model.d.ts +3 -67
  208. package/dist/features/comments/model.js +2 -2
  209. package/dist/features/comments/model.js.map +1 -1
  210. package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
  211. package/dist/features/comments/react/use-comments.d.ts +2 -1
  212. package/dist/features/comments/react.d.ts +1 -1
  213. package/dist/features/comments/schema.d.ts +2 -52
  214. package/dist/features/comments/schema.js.map +1 -1
  215. package/dist/features/comments/snapshot-equality.js.map +1 -1
  216. package/dist/features/comments/use-comments.d.ts +3 -2
  217. package/dist/features/comments/vue.d.ts +1 -1
  218. package/dist/features/composites/accepted-children.d.ts +1 -0
  219. package/dist/features/composites/accepted-children.js +16 -1
  220. package/dist/features/composites/accepted-children.js.map +1 -1
  221. package/dist/features/composites/ai-brief.js +31 -0
  222. package/dist/features/composites/ai-brief.js.map +1 -1
  223. package/dist/features/composites/bibliography-command.d.ts +11 -0
  224. package/dist/features/composites/bibliography-form.js +90 -0
  225. package/dist/features/composites/bibliography-form.js.map +1 -0
  226. package/dist/features/composites/bibliography.d.ts +33 -0
  227. package/dist/features/composites/bibliography.js +120 -0
  228. package/dist/features/composites/bibliography.js.map +1 -0
  229. package/dist/features/composites/components/BibliographyEditor.js +261 -0
  230. package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
  231. package/dist/features/composites/components/CompositeChrome.js +75 -26
  232. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  233. package/dist/features/composites/components/CompositeDetails.js +46 -11
  234. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  235. package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
  236. package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
  237. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  238. package/dist/features/composites/components/CompositeInspector.js +17 -5
  239. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  240. package/dist/features/composites/components/CompositeTable.js +13 -4
  241. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  242. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  243. package/dist/features/composites/components/CompositeView.js +11 -1
  244. package/dist/features/composites/components/CompositeView.js.map +1 -1
  245. package/dist/features/composites/components/DerivationBadge.js +103 -0
  246. package/dist/features/composites/components/DerivationBadge.js.map +1 -0
  247. package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
  248. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
  249. package/dist/features/composites/components/ReferenceView.js +122 -0
  250. package/dist/features/composites/components/ReferenceView.js.map +1 -0
  251. package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
  252. package/dist/features/composites/components/SettingsEditingTab.js +1 -1
  253. package/dist/features/composites/convert.js +2 -2
  254. package/dist/features/composites/convert.js.map +1 -1
  255. package/dist/features/composites/derivation.d.ts +61 -0
  256. package/dist/features/composites/derivation.js +98 -0
  257. package/dist/features/composites/derivation.js.map +1 -0
  258. package/dist/features/composites/derived-chrome.js +69 -0
  259. package/dist/features/composites/derived-chrome.js.map +1 -0
  260. package/dist/features/composites/feature.js +159 -109
  261. package/dist/features/composites/feature.js.map +1 -1
  262. package/dist/features/composites/fences.d.ts +4 -3
  263. package/dist/features/composites/fences.js +51 -7
  264. package/dist/features/composites/fences.js.map +1 -1
  265. package/dist/features/composites/index.d.ts +12 -3
  266. package/dist/features/composites/index.js +15 -6
  267. package/dist/features/composites/inspector-model.d.ts +4 -1
  268. package/dist/features/composites/inspector-model.js +8 -4
  269. package/dist/features/composites/inspector-model.js.map +1 -1
  270. package/dist/features/composites/kinds.d.ts +8 -1
  271. package/dist/features/composites/kinds.js +145 -6
  272. package/dist/features/composites/kinds.js.map +1 -1
  273. package/dist/features/composites/legacy.d.ts +4 -1
  274. package/dist/features/composites/legacy.js +56 -1
  275. package/dist/features/composites/legacy.js.map +1 -1
  276. package/dist/features/composites/meta-display.js +40 -1
  277. package/dist/features/composites/meta-display.js.map +1 -1
  278. package/dist/features/composites/meta-keys.d.ts +7 -0
  279. package/dist/features/composites/meta-keys.js +21 -0
  280. package/dist/features/composites/meta-keys.js.map +1 -0
  281. package/dist/features/composites/node-views.d.ts +6 -3
  282. package/dist/features/composites/node-views.js +7 -3
  283. package/dist/features/composites/node-views.js.map +1 -1
  284. package/dist/features/composites/nodes.d.ts +6 -1
  285. package/dist/features/composites/nodes.js +33 -7
  286. package/dist/features/composites/nodes.js.map +1 -1
  287. package/dist/features/composites/react/BibliographyEditor.js +229 -0
  288. package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
  289. package/dist/features/composites/react/CompositeChrome.js +65 -17
  290. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  291. package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
  292. package/dist/features/composites/react/CompositeDetails.js +45 -6
  293. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  294. package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
  295. package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
  296. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  297. package/dist/features/composites/react/CompositeInspector.js +11 -5
  298. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  299. package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
  300. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  301. package/dist/features/composites/react/CompositeTable.js +16 -2
  302. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  303. package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
  304. package/dist/features/composites/react/CompositeView.js +6 -1
  305. package/dist/features/composites/react/CompositeView.js.map +1 -1
  306. package/dist/features/composites/react/DerivationBadge.js +116 -0
  307. package/dist/features/composites/react/DerivationBadge.js.map +1 -0
  308. package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
  309. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
  310. package/dist/features/composites/react/ReferenceView.d.ts +11 -0
  311. package/dist/features/composites/react/ReferenceView.js +115 -0
  312. package/dist/features/composites/react/ReferenceView.js.map +1 -0
  313. package/dist/features/composites/react/SettingsEditingTab.js +2 -2
  314. package/dist/features/composites/react.d.ts +2 -1
  315. package/dist/features/composites/react.js +7 -3
  316. package/dist/features/composites/react.js.map +1 -1
  317. package/dist/features/composites/reference-ai.js +16 -0
  318. package/dist/features/composites/reference-ai.js.map +1 -0
  319. package/dist/features/composites/reference-label.d.ts +19 -0
  320. package/dist/features/composites/reference-label.js +58 -0
  321. package/dist/features/composites/reference-label.js.map +1 -0
  322. package/dist/features/composites/reference-suggestion.d.ts +10 -0
  323. package/dist/features/composites/reference-suggestion.js +41 -0
  324. package/dist/features/composites/reference-suggestion.js.map +1 -0
  325. package/dist/features/composites/reference-text.d.ts +18 -0
  326. package/dist/features/composites/reference-text.js +89 -0
  327. package/dist/features/composites/reference-text.js.map +1 -0
  328. package/dist/features/composites/reference.d.ts +44 -0
  329. package/dist/features/composites/reference.js +148 -0
  330. package/dist/features/composites/reference.js.map +1 -0
  331. package/dist/features/composites/reveal-source.d.ts +32 -0
  332. package/dist/features/composites/reveal-source.js +89 -0
  333. package/dist/features/composites/reveal-source.js.map +1 -0
  334. package/dist/features/composites/rows.d.ts +21 -2
  335. package/dist/features/composites/rows.js +63 -9
  336. package/dist/features/composites/rows.js.map +1 -1
  337. package/dist/features/composites/schema.d.ts +9 -3
  338. package/dist/features/composites/schema.js +11 -4
  339. package/dist/features/composites/schema.js.map +1 -1
  340. package/dist/features/composites/table-model.js +12 -21
  341. package/dist/features/composites/table-model.js.map +1 -1
  342. package/dist/features/composites/vue.d.ts +3 -2
  343. package/dist/features/composites/vue.js +8 -4
  344. package/dist/features/composites/vue.js.map +1 -1
  345. package/dist/features/media/assets.d.ts +2 -0
  346. package/dist/features/media/assets.js +3 -2
  347. package/dist/features/media/assets.js.map +1 -1
  348. package/dist/features/media/components/FileView.js +13 -4
  349. package/dist/features/media/components/FileView.js.map +1 -1
  350. package/dist/features/media/feature.d.ts +3 -3
  351. package/dist/features/media/feature.js +21 -28
  352. package/dist/features/media/feature.js.map +1 -1
  353. package/dist/features/media/file-import.js +2 -2
  354. package/dist/features/media/file-import.js.map +1 -1
  355. package/dist/features/media/index.d.ts +2 -2
  356. package/dist/features/media/index.js +2 -2
  357. package/dist/features/media/react/FileView.js +19 -2
  358. package/dist/features/media/react/FileView.js.map +1 -1
  359. package/dist/features/media/schema.d.ts +8 -0
  360. package/dist/features/media/schema.js +126 -11
  361. package/dist/features/media/schema.js.map +1 -1
  362. package/dist/features/media/upload.d.ts +16 -14
  363. package/dist/features/media/upload.js +13 -19
  364. package/dist/features/media/upload.js.map +1 -1
  365. package/dist/features/mentions/index.d.ts +4 -3
  366. package/dist/features/mentions/index.js +4 -1
  367. package/dist/features/mentions/index.js.map +1 -1
  368. package/dist/features/mentions/mention-text.d.ts +1 -1
  369. package/dist/features/mentions/mention-text.js.map +1 -1
  370. package/dist/features/mentions/mention.d.ts +0 -6
  371. package/dist/features/mentions/mention.js.map +1 -1
  372. package/dist/features/mentions/metadata-defaults.d.ts +2 -2
  373. package/dist/features/mentions/metadata-defaults.js +3 -9
  374. package/dist/features/mentions/metadata-defaults.js.map +1 -1
  375. package/dist/features/mentions/schema.d.ts +2 -1
  376. package/dist/features/mentions/schema.js.map +1 -1
  377. package/dist/features/tables/feature.d.ts +2 -1
  378. package/dist/features/tables/feature.js +90 -70
  379. package/dist/features/tables/feature.js.map +1 -1
  380. package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
  381. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  382. package/dist/features/themes/feature.js +2 -0
  383. package/dist/features/themes/feature.js.map +1 -1
  384. package/dist/features/themes/index.d.ts +2 -1
  385. package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
  386. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  387. package/dist/features/themes/schema.d.ts +2 -6
  388. package/dist/features/themes/schema.js.map +1 -1
  389. package/dist/features/themes/theme-runtime.d.ts +1 -1
  390. package/dist/features/themes/theme-runtime.js +4 -2
  391. package/dist/features/themes/theme-runtime.js.map +1 -1
  392. package/dist/features/transcript/TranscriptSegmentView.js +48 -28
  393. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  394. package/dist/features/transcript/feature.js +21 -19
  395. package/dist/features/transcript/feature.js.map +1 -1
  396. package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
  397. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  398. package/dist/features/transcript/segment-node.d.ts +2 -0
  399. package/dist/features/transcript/segment-node.js +35 -7
  400. package/dist/features/transcript/segment-node.js.map +1 -1
  401. package/dist/i18n/de/composites.js +91 -1
  402. package/dist/i18n/de/composites.js.map +1 -1
  403. package/dist/i18n/de/integrations.js +49 -8
  404. package/dist/i18n/de/integrations.js.map +1 -1
  405. package/dist/i18n/de/media.js +0 -1
  406. package/dist/i18n/de/media.js.map +1 -1
  407. package/dist/i18n/de/palette.js +22 -0
  408. package/dist/i18n/de/palette.js.map +1 -0
  409. package/dist/i18n/de/tables.js +0 -1
  410. package/dist/i18n/de/tables.js.map +1 -1
  411. package/dist/i18n/de/transcript.js +2 -1
  412. package/dist/i18n/de/transcript.js.map +1 -1
  413. package/dist/i18n/de/tree.js +3 -0
  414. package/dist/i18n/de/tree.js.map +1 -1
  415. package/dist/i18n/de.d.ts +157 -11
  416. package/dist/i18n/de.js +3 -1
  417. package/dist/i18n/de.js.map +1 -1
  418. package/dist/i18n/en/composites.js +89 -1
  419. package/dist/i18n/en/composites.js.map +1 -1
  420. package/dist/i18n/en/integrations.js +49 -8
  421. package/dist/i18n/en/integrations.js.map +1 -1
  422. package/dist/i18n/en/media.js +0 -1
  423. package/dist/i18n/en/media.js.map +1 -1
  424. package/dist/i18n/en/palette.js +22 -0
  425. package/dist/i18n/en/palette.js.map +1 -0
  426. package/dist/i18n/en/tables.js +0 -1
  427. package/dist/i18n/en/tables.js.map +1 -1
  428. package/dist/i18n/en/transcript.js +2 -1
  429. package/dist/i18n/en/transcript.js.map +1 -1
  430. package/dist/i18n/en/tree.js +3 -0
  431. package/dist/i18n/en/tree.js.map +1 -1
  432. package/dist/i18n/en.js +3 -1
  433. package/dist/i18n/en.js.map +1 -1
  434. package/dist/index.d.ts +4 -3
  435. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
  436. package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
  437. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
  438. package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
  439. package/dist/integrations/composite-ai/composite-fill.js +97 -56
  440. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  441. package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
  442. package/dist/integrations/composite-ai/composite-summary.js +7 -26
  443. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  444. package/dist/integrations/composite-ai/derive.d.ts +29 -0
  445. package/dist/integrations/composite-ai/derive.js +173 -0
  446. package/dist/integrations/composite-ai/derive.js.map +1 -0
  447. package/dist/integrations/composite-ai/index.d.ts +11 -7
  448. package/dist/integrations/composite-ai/index.js +72 -14
  449. package/dist/integrations/composite-ai/index.js.map +1 -1
  450. package/dist/integrations/composite-ai/rendition.d.ts +45 -0
  451. package/dist/integrations/composite-ai/rendition.js +314 -0
  452. package/dist/integrations/composite-ai/rendition.js.map +1 -0
  453. package/dist/integrations/composite-media/index.d.ts +7 -5
  454. package/dist/integrations/composite-media/index.js +86 -13
  455. package/dist/integrations/composite-media/index.js.map +1 -1
  456. package/dist/integrations/transcript-ai/index.d.ts +1 -1
  457. package/dist/integrations/transcript-ai/index.js +5 -2
  458. package/dist/integrations/transcript-ai/index.js.map +1 -1
  459. package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
  460. package/dist/integrations/transcript-ai/protocol.js +0 -2
  461. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  462. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
  463. package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
  464. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
  465. package/dist/markdown/MarkdownSurface.vue.d.ts +6 -5
  466. package/dist/markdown/MarkdownSurface2.js +3 -6
  467. package/dist/markdown/MarkdownSurface2.js.map +1 -1
  468. package/dist/markdown/document.js +1 -1
  469. package/dist/markdown/source-mode.d.ts +1 -1
  470. package/dist/nuxt/module.d.ts +1402 -54
  471. package/dist/nuxt/module.js +6 -49
  472. package/dist/nuxt/module.js.map +1 -1
  473. package/dist/nuxt/runtime/install-context.d.ts +7 -3
  474. package/dist/nuxt/runtime/install-context.js +14 -8
  475. package/dist/nuxt/runtime/install-context.js.map +1 -1
  476. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
  477. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
  478. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  479. package/dist/presets/compose.d.ts +94 -0
  480. package/dist/presets/compose.js +164 -0
  481. package/dist/presets/compose.js.map +1 -0
  482. package/dist/react/AdvancedEditor.d.ts +15 -15
  483. package/dist/react/AdvancedEditor.js +15 -13
  484. package/dist/react/AdvancedEditor.js.map +1 -1
  485. package/dist/react/EditorCommandPalette.d.ts +13 -0
  486. package/dist/react/EditorCommandPalette.js +118 -0
  487. package/dist/react/EditorCommandPalette.js.map +1 -0
  488. package/dist/react/EditorSettingsModal.d.ts +5 -5
  489. package/dist/react/EditorSettingsModal.js +7 -4
  490. package/dist/react/EditorSettingsModal.js.map +1 -1
  491. package/dist/react/EditorSettingsPanel.d.ts +7 -7
  492. package/dist/react/EditorSettingsPanel.js +8 -5
  493. package/dist/react/EditorSettingsPanel.js.map +1 -1
  494. package/dist/react/EditorSurface.d.ts +7 -7
  495. package/dist/react/EditorSurface.js +10 -6
  496. package/dist/react/EditorSurface.js.map +1 -1
  497. package/dist/react/EditorToolbar.d.ts +4 -6
  498. package/dist/react/EditorToolbar.js +29 -37
  499. package/dist/react/EditorToolbar.js.map +1 -1
  500. package/dist/react/SimpleEditor.d.ts +22 -23
  501. package/dist/react/SimpleEditor.js +21 -7
  502. package/dist/react/SimpleEditor.js.map +1 -1
  503. package/dist/react/advanced.d.ts +4 -3
  504. package/dist/react/advanced.js +2 -2
  505. package/dist/react/compose.d.ts +20 -0
  506. package/dist/react/compose.js +49 -0
  507. package/dist/react/compose.js.map +1 -0
  508. package/dist/react/composed-surface.d.ts +39 -0
  509. package/dist/react/composed-surface.js +71 -0
  510. package/dist/react/composed-surface.js.map +1 -0
  511. package/dist/react/context.js +6 -2
  512. package/dist/react/context.js.map +1 -1
  513. package/dist/react/index.d.ts +2 -1
  514. package/dist/react/index.js +2 -1
  515. package/dist/react/key-labels.js +15 -0
  516. package/dist/react/key-labels.js.map +1 -0
  517. package/dist/react/settings.d.ts +1 -1
  518. package/dist/react/simple.d.ts +2 -2
  519. package/dist/react/ui/command.js +36 -1
  520. package/dist/react/ui/command.js.map +1 -1
  521. package/dist/schema/advanced.d.ts +6 -6
  522. package/dist/schema/advanced.js +26 -29
  523. package/dist/schema/advanced.js.map +1 -1
  524. package/dist/server/ai.d.ts +5 -16
  525. package/dist/server/ai.js.map +1 -1
  526. package/dist/server/auth.d.ts +9 -0
  527. package/dist/server/collab-state.d.ts +1 -1
  528. package/dist/server/collab-state.js.map +1 -1
  529. package/dist/server/collab.d.ts +13 -13
  530. package/dist/server/collab.js +34 -18
  531. package/dist/server/collab.js.map +1 -1
  532. package/dist/server/crossref.js +78 -0
  533. package/dist/server/crossref.js.map +1 -0
  534. package/dist/server/errors.d.ts +1 -1
  535. package/dist/server/errors.js +1 -1
  536. package/dist/server/errors.js.map +1 -1
  537. package/dist/server/extract.js +308 -0
  538. package/dist/server/extract.js.map +1 -0
  539. package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
  540. package/dist/server/h3.js.map +1 -0
  541. package/dist/server/handlers/ai.d.ts +30 -9
  542. package/dist/server/handlers/ai.js +170 -36
  543. package/dist/server/handlers/ai.js.map +1 -1
  544. package/dist/server/handlers/context.d.ts +12 -5
  545. package/dist/server/handlers/context.js +10 -4
  546. package/dist/server/handlers/context.js.map +1 -1
  547. package/dist/server/handlers/editor.d.ts +19 -13
  548. package/dist/server/handlers/editor.js +49 -38
  549. package/dist/server/handlers/editor.js.map +1 -1
  550. package/dist/server/handlers/index.d.ts +4 -4
  551. package/dist/server/handlers/index.js +4 -4
  552. package/dist/server/host.d.ts +16 -25
  553. package/dist/server/host.js +58 -41
  554. package/dist/server/host.js.map +1 -1
  555. package/dist/server/index.d.ts +6 -4
  556. package/dist/server/index.js +3 -2
  557. package/dist/server/pdf.js +97 -0
  558. package/dist/server/pdf.js.map +1 -0
  559. package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
  560. package/dist/server/ports.js +29 -0
  561. package/dist/server/ports.js.map +1 -0
  562. package/dist/server/routes.js +144 -0
  563. package/dist/server/routes.js.map +1 -0
  564. package/dist/settings/index.d.ts +1 -1
  565. package/dist/settings/index.js.map +1 -1
  566. package/dist/styles/composites.css +25 -0
  567. package/dist/tree/DocumentTree.js +56 -29
  568. package/dist/tree/DocumentTree.js.map +1 -1
  569. package/dist/tree/react/DocumentTree.js +23 -0
  570. package/dist/tree/react/DocumentTree.js.map +1 -1
  571. package/dist/tree/tree-host.js +2 -2
  572. package/dist/tree/tree-items.js +1 -1
  573. package/dist/vue/AdvancedEditor.js +30 -9
  574. package/dist/vue/AdvancedEditor.js.map +1 -1
  575. package/dist/vue/AdvancedEditor.vue.d.ts +28 -8
  576. package/dist/vue/SimpleEditor.js +62 -16
  577. package/dist/vue/SimpleEditor.js.map +1 -1
  578. package/dist/vue/SimpleEditor.vue.d.ts +37 -7
  579. package/dist/vue/advanced.d.ts +3 -2
  580. package/dist/vue/advanced.js +2 -2
  581. package/dist/vue/components/EditorCommandPalette.js +91 -0
  582. package/dist/vue/components/EditorCommandPalette.js.map +1 -0
  583. package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
  584. package/dist/vue/components/EditorSettingsModal.js +10 -6
  585. package/dist/vue/components/EditorSettingsModal.js.map +1 -1
  586. package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
  587. package/dist/vue/components/EditorSettingsPanel.js +10 -6
  588. package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
  589. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
  590. package/dist/vue/components/EditorSurface.js +11 -6
  591. package/dist/vue/components/EditorSurface.js.map +1 -1
  592. package/dist/vue/components/EditorSurface.vue.d.ts +7 -7
  593. package/dist/vue/components/EditorToolbar.js +41 -49
  594. package/dist/vue/components/EditorToolbar.js.map +1 -1
  595. package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
  596. package/dist/vue/compose.d.ts +17 -0
  597. package/dist/vue/compose.js +51 -0
  598. package/dist/vue/compose.js.map +1 -0
  599. package/dist/vue/context-key.d.ts +6 -0
  600. package/dist/vue/context-key.js +6 -0
  601. package/dist/vue/context-key.js.map +1 -0
  602. package/dist/vue/context.d.ts +8 -6
  603. package/dist/vue/context.js +6 -5
  604. package/dist/vue/context.js.map +1 -1
  605. package/dist/vue/index.d.ts +10 -8
  606. package/dist/vue/index.js +10 -8
  607. package/dist/vue/kit/index.d.ts +2 -2
  608. package/dist/vue/kit/nuxt-ui.js +23 -0
  609. package/dist/vue/kit/nuxt-ui.js.map +1 -1
  610. package/dist/vue/kit/types.d.ts +40 -1
  611. package/package.json +30 -16
  612. package/dist/adapters/comments/client.d.ts +0 -64
  613. package/dist/adapters/comments/client.js.map +0 -1
  614. package/dist/adapters/routes.js.map +0 -1
  615. package/dist/core/toolbar.d.ts +0 -74
  616. package/dist/core/toolbar.js +0 -45
  617. package/dist/core/toolbar.js.map +0 -1
  618. package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
  619. package/dist/integrations/composite-ai/document-transcribe.js +0 -89
  620. package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
  621. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
  622. package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
  623. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
  624. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
  625. package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
  626. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
  627. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
  628. package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
  629. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
  630. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
  631. package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
  632. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
  633. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
  634. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
  635. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
  636. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
  637. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
  638. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
  639. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
  640. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
  641. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
  642. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
  643. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
  644. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
  645. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
  646. package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
  647. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
  648. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
  649. package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
  650. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
  651. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
  652. package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
  653. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
  654. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
  655. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
  656. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
  657. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
  658. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
  659. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
  660. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
  661. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
  662. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
  663. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
  664. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
  665. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
  666. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
  667. package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
  668. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
  669. package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
  670. package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
  671. package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
  672. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
  673. package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
  674. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
  675. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
  676. package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
  677. package/dist/presets/advanced.d.ts +0 -33
  678. package/dist/presets/advanced.js +0 -50
  679. package/dist/presets/advanced.js.map +0 -1
  680. package/dist/presets/simple.d.ts +0 -9
  681. package/dist/presets/simple.js +0 -12
  682. package/dist/presets/simple.js.map +0 -1
  683. package/dist/react/advanced-preset.d.ts +0 -11
  684. package/dist/react/advanced-preset.js +0 -31
  685. package/dist/react/advanced-preset.js.map +0 -1
  686. package/dist/vue/advanced-preset.d.ts +0 -13
  687. package/dist/vue/advanced-preset.js +0 -33
  688. package/dist/vue/advanced-preset.js.map +0 -1
  689. package/docs/extending.md +0 -538
  690. package/docs/integration.md +0 -632
  691. package/docs/llms.md +0 -383
  692. package/docs/usage.md +0 -1076
@@ -1 +1 @@
1
- {"version":3,"file":"rows.js","names":[],"sources":["../../../src/features/composites/rows.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport { type CompositeKind, type CompositeMetaField, compositeKind } from './kinds'\n\n/** One composite („Baustein“) of a document with its metadata, flattened in document order. */\nexport interface CompositeRow {\n /** Block ID, or `pos:<pos>` for a composite without one yet; unique within the document. */\n key: string\n id: string | null\n /** Position of the composite when the row was read; the write helpers re-resolve it, so a stale value is safe. */\n pos: number\n /** Position right after the composite, as of when the row was read. */\n end: number\n kind: string\n kindLabel: string\n icon: string\n /** Title text, empty for an untitled composite. */\n title: string\n /** Text of the description (summary), empty when there is none. */\n description: string\n /**\n * The description holds text only (marks such as bold, author or comment allowed, no mentions or tags); only then\n * can `setCompositeDescription` write it without losing inline content.\n */\n descriptionTextOnly: boolean\n tags: string[]\n meta: Record<string, unknown>\n /** The meta fields of the composite's kind. */\n fields: readonly CompositeMetaField[]\n /** Nesting level among composites, 0 = outermost. */\n depth: number\n}\n\n/** A distinct `@mention` chip of a document. */\nexport interface CompositeMention {\n id: string\n label: string\n color: string | null\n}\n\nconst COMPOSITE = 'composite'\n\n/** True when `node`'s content is empty or text only (with any marks, but no inline nodes such as mentions or tags). */\nfunction isTextOnly(node: PMNode): boolean {\n let textOnly = true\n node.forEach((child) => {\n if (!child.isText) textOnly = false\n })\n return textOnly\n}\n\n/**\n * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, and the distinct\n * `@mention` chips anywhere in the document. Unknown kinds get the `section` kind's presentation under their own ID.\n */\nexport function compositeRows(doc: PMNode, kinds: readonly CompositeKind[]): { rows: CompositeRow[], mentions: CompositeMention[] } {\n const rows: CompositeRow[] = []\n const mentions = new Map<string, CompositeMention>()\n\n function walk(parent: PMNode, start: number, depth: number) {\n parent.forEach((node, offset) => {\n const pos = start + offset\n if (node.type.name === COMPOSITE) {\n const kind = compositeKind(kinds, String(node.attrs.kind))\n const id = blockIdOf(node)\n const tags: unknown = node.maybeChild(2)?.attrs.tags\n const description = node.maybeChild(1)\n rows.push({\n key: id ?? `pos:${pos}`,\n id,\n pos,\n end: pos + node.nodeSize,\n kind: kind.id,\n kindLabel: kind.label,\n icon: kind.icon,\n title: node.firstChild?.textContent.trim() ?? '',\n description: description?.textContent ?? '',\n descriptionTextOnly: description ? isTextOnly(description) : true,\n tags: Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string') : [],\n meta: (node.attrs.meta ?? {}) as Record<string, unknown>,\n fields: kind.fields,\n depth,\n })\n walk(node, pos + 1, depth + 1)\n }\n else if (node.type.name === 'mention') {\n const mentionId = String(node.attrs.id ?? '')\n if (mentionId && !mentions.has(mentionId)) {\n mentions.set(mentionId, {\n id: mentionId,\n label: String(node.attrs.label ?? mentionId),\n color: typeof node.attrs.color === 'string' ? node.attrs.color : null,\n })\n }\n }\n else if (!node.isLeaf) {\n walk(node, pos + 1, depth)\n }\n })\n }\n walk(doc, 0, 0)\n return { rows, mentions: [...mentions.values()] }\n}\n\n/** The innermost row around document position `pos`, `null` outside of any composite. */\nexport function compositeRowAt(rows: readonly CompositeRow[], pos: number): CompositeRow | null {\n let found: CompositeRow | null = null\n // Pre-order: the last match is the innermost composite.\n for (const row of rows) {\n if (row.pos <= pos && pos < row.end) found = row\n }\n return found\n}\n\n/**\n * The composite `row` stands for in `doc`: by block ID, else (the row has no ID yet) by its position.\n * Positions are re-resolved on every call, so a stale `row.pos` is safe for rows with an ID.\n */\nexport function compositeOfRow(doc: PMNode, row: Pick<CompositeRow, 'id' | 'pos'>): { pos: number, node: PMNode } | null {\n if (row.id) {\n const found = findBlockById(doc, row.id)\n return found?.node.type.name === COMPOSITE ? found : null\n }\n const node = doc.nodeAt(row.pos)\n return node?.type.name === COMPOSITE ? { pos: row.pos, node } : null\n}\n\n/**\n * `meta` with `value` stored under `field.key`: a number field converts a numeric string, an empty value (`''`,\n * `null`, `undefined`, `NaN`) removes the key. Returns a new object; `meta` is not changed.\n */\nexport function compositeMetaWith(meta: Record<string, unknown>, field: Pick<CompositeMetaField, 'key' | 'type'>, value: unknown): Record<string, unknown> {\n const next = { ...meta }\n const stored = field.type === 'number' && typeof value === 'string' && value !== '' ? Number(value) : value\n if (stored === '' || stored === null || stored === undefined || Number.isNaN(stored)) delete next[field.key]\n else next[field.key] = stored\n return next\n}\n\n/** Stores `value` under the meta field `key` of the row's composite (see `compositeMetaWith`), in one transaction. */\nexport function setCompositeMetaField(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos' | 'fields'>, key: string, value: unknown): boolean {\n const field = row.fields.find(candidate => candidate.key === key) ?? { key, type: 'text' as const }\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n if (!found) return false\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, meta: compositeMetaWith(found.node.attrs.meta ?? {}, field, value) })\n return true\n })\n}\n\n/** Replaces the tags row of the row's composite (empty names are dropped), in one transaction. */\nexport function setCompositeTags(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, tags: readonly string[]): boolean {\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n const tagsNode = found?.node.maybeChild(2)\n if (!found || !title || !description || tagsNode?.type.name !== 'compositeTags') return false\n const tagsPos = found.pos + 1 + title.nodeSize + description.nodeSize\n tr.setNodeMarkup(tagsPos, undefined, { ...tagsNode.attrs, tags: tags.filter(tag => tag) })\n return true\n })\n}\n\n/** The text `setCompositeDescription` stores for `text`: the description is one line, so line breaks collapse to spaces. */\nexport function compositeDescriptionText(text: string): string {\n return text.replace(/\\s*\\n\\s*/g, ' ').trim()\n}\n\n/**\n * Sets the text of the row's composite description to `compositeDescriptionText(text)`; an empty text empties it.\n * Only the changed span is replaced, so marks on the unchanged text (bold, author, comment) stay. Refuses (`false`)\n * when the description holds a mention or tag (it would be lost), and, when `expected` is given, when the\n * description no longer holds that text (a peer changed it since the caller read it; writing would drop that change).\n */\nexport function setCompositeDescription(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, text: string, expected?: string): boolean {\n const value = compositeDescriptionText(text)\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n if (!found || !title || !description || description.type.name !== 'compositeDescription') return false\n if (!isTextOnly(description)) return false\n const current = description.textContent\n if (expected !== undefined && current !== expected) return false\n if (current === value) return true\n // Text only: every character is one position. Keep the common prefix and suffix, replace what lies between.\n let prefix = 0\n const max = Math.min(current.length, value.length)\n while (prefix < max && current[prefix] === value[prefix]) prefix++\n let suffix = 0\n while (suffix < max - prefix && current[current.length - 1 - suffix] === value[value.length - 1 - suffix]) suffix++\n const start = found.pos + 1 + title.nodeSize + 1\n // `insertText` with an empty text deletes the range; inserted text takes the marks at its start, as typing does.\n tr.insertText(value.slice(prefix, value.length - suffix), start + prefix, start + current.length - suffix)\n return true\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rows.js","names":[],"sources":["../../../src/features/composites/rows.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport { type CompositeKind, type CompositeMetaField, compositeKind } from './kinds'\nimport { referenceLabel, referenceTarget } from './reference-label'\n\n/** One composite („Baustein“) of a document with its metadata, flattened in document order. */\nexport interface CompositeRow {\n /** Block ID, or `pos:<pos>` for a composite without one yet; unique within the document. */\n key: string\n id: string | null\n /** Position of the composite when the row was read; the write helpers re-resolve it, so a stale value is safe. */\n pos: number\n /** Position right after the composite, as of when the row was read. */\n end: number\n kind: string\n kindLabel: string\n icon: string\n /** Title text, empty for an untitled composite. */\n title: string\n /** Text of the description (summary), empty when there is none. */\n description: string\n /**\n * The description holds text only (marks such as bold, author or comment allowed, no mentions or tags); only then\n * can `setCompositeDescription` write it without losing inline content.\n */\n descriptionTextOnly: boolean\n tags: string[]\n meta: Record<string, unknown>\n /** The meta fields of the composite's kind. */\n fields: readonly CompositeMetaField[]\n /** Nesting level among composites, 0 = outermost. */\n depth: number\n}\n\n/** A distinct `@mention` chip of a document. */\nexport interface CompositeMention {\n id: string\n label: string\n color: string | null\n}\n\n/** A reference to a Baustein: a `reference` chip in the text of block `from`, or a `reference` field of the Baustein `from`. */\nexport interface CompositeReference {\n /** Block ID of the referenced Baustein. */\n target: string\n /** Block ID of the nearest block around the chip (the Baustein itself for a field); `null` when that block has no ID yet. */\n from: string | null\n /** The chip's locator (`S. 12`, `03:14`); `null` for a field or a chip without one. */\n locator: string | null\n}\n\nconst COMPOSITE = 'composite'\n\n/** True when `node`'s content is empty or text only (with any marks, but no inline nodes such as mentions or tags). */\nfunction isTextOnly(node: PMNode): boolean {\n let textOnly = true\n node.forEach((child) => {\n if (!child.isText) textOnly = false\n })\n return textOnly\n}\n\n/**\n * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, the distinct\n * `@mention` chips anywhere in the document and every reference (`reference` chips and `reference` fields) in document\n * order. Unknown kinds get the `section` kind's presentation under their own ID.\n */\nexport function compositeRows(doc: PMNode, kinds: readonly CompositeKind[]): { rows: CompositeRow[], mentions: CompositeMention[], references: CompositeReference[] } {\n const rows: CompositeRow[] = []\n const mentions = new Map<string, CompositeMention>()\n const references: CompositeReference[] = []\n\n function walk(parent: PMNode, start: number, depth: number, block: string | null) {\n parent.forEach((node, offset) => {\n const pos = start + offset\n if (node.type.name === COMPOSITE) {\n const kind = compositeKind(kinds, String(node.attrs.kind))\n const id = blockIdOf(node)\n const tags: unknown = node.maybeChild(2)?.attrs.tags\n const description = node.maybeChild(1)\n const row: CompositeRow = {\n key: id ?? `pos:${pos}`,\n id,\n pos,\n end: pos + node.nodeSize,\n kind: kind.id,\n kindLabel: kind.label,\n icon: kind.icon,\n title: node.firstChild?.textContent.trim() ?? '',\n description: description?.textContent ?? '',\n descriptionTextOnly: description ? isTextOnly(description) : true,\n tags: Array.isArray(tags) ? tags.filter((tag): tag is string => typeof tag === 'string') : [],\n meta: (node.attrs.meta ?? {}) as Record<string, unknown>,\n fields: kind.fields,\n depth,\n }\n rows.push(row)\n for (const field of kind.fields) {\n const value = field.type === 'reference' ? row.meta[field.key] : undefined\n if (typeof value === 'string' && value) references.push({ target: value, from: id, locator: null })\n }\n walk(node, pos + 1, depth + 1, id ?? block)\n }\n else if (node.type.name === 'reference') {\n const target = String(node.attrs.target ?? '')\n if (target) references.push({ target, from: block, locator: typeof node.attrs.locator === 'string' && node.attrs.locator ? node.attrs.locator : null })\n }\n else if (node.type.name === 'mention') {\n const mentionId = String(node.attrs.id ?? '')\n if (mentionId && !mentions.has(mentionId)) {\n mentions.set(mentionId, {\n id: mentionId,\n label: String(node.attrs.label ?? mentionId),\n color: typeof node.attrs.color === 'string' ? node.attrs.color : null,\n })\n }\n }\n else if (!node.isLeaf) {\n walk(node, pos + 1, depth, blockIdOf(node) ?? block)\n }\n })\n }\n walk(doc, 0, 0, null)\n return { rows, mentions: [...mentions.values()], references }\n}\n\n/** A place that refers to a Baustein: the block holding the chip (or the Baustein with the field) and a short text of it. */\nexport interface CompositeBacklink {\n from: string\n locator: string | null\n excerpt: string\n}\n\nconst EXCERPT_LENGTH = 40\n\n/** The text of `node` as read on screen: inline atoms count as their label (`@name`, `#tag`, a reference chip's short citation + locator), not as nothing. */\nfunction visibleText(doc: PMNode, kinds: readonly CompositeKind[], node: PMNode): string {\n return node.textBetween(0, node.content.size, ' ', (leaf) => {\n const attrs = leaf.attrs\n switch (leaf.type.name) {\n case 'mention': return `@${String(attrs.label ?? attrs.id ?? '')}`\n case 'tag': return `#${String(attrs.name ?? '')}`\n case 'reference': {\n const target = findBlockById(doc, String(attrs.target ?? ''))\n const label = target?.node.type.name === COMPOSITE ? referenceLabel(referenceTarget(target.node, kinds)) : String(attrs.label ?? '')\n return typeof attrs.locator === 'string' && attrs.locator ? `${label}, ${attrs.locator}` : label\n }\n default: return ''\n }\n })\n}\n\n/** Every reference to the Baustein `target` in document order; references whose enclosing block has no ID (or is gone) are left out, as nothing could reveal them. */\nexport function compositeBacklinks(doc: PMNode, kinds: readonly CompositeKind[], target: string): CompositeBacklink[] {\n const links: CompositeBacklink[] = []\n for (const reference of compositeRows(doc, kinds).references) {\n if (reference.target !== target || !reference.from) continue\n const found = findBlockById(doc, reference.from)\n if (!found) continue\n const source = found.node.type.name === COMPOSITE ? found.node.firstChild : found.node\n const text = source ? visibleText(doc, kinds, source).trim() : ''\n links.push({ from: reference.from, locator: reference.locator, excerpt: text.length > EXCERPT_LENGTH ? `${text.slice(0, EXCERPT_LENGTH)}…` : text })\n }\n return links\n}\n\n/** The innermost row around document position `pos`, `null` outside of any composite. */\nexport function compositeRowAt(rows: readonly CompositeRow[], pos: number): CompositeRow | null {\n let found: CompositeRow | null = null\n // Pre-order: the last match is the innermost composite.\n for (const row of rows) {\n if (row.pos <= pos && pos < row.end) found = row\n }\n return found\n}\n\n/**\n * The composite `row` stands for in `doc`: by block ID, else (the row has no ID yet) by its position.\n * Positions are re-resolved on every call, so a stale `row.pos` is safe for rows with an ID.\n */\nexport function compositeOfRow(doc: PMNode, row: Pick<CompositeRow, 'id' | 'pos'>): { pos: number, node: PMNode } | null {\n if (row.id) {\n const found = findBlockById(doc, row.id)\n return found?.node.type.name === COMPOSITE ? found : null\n }\n const node = doc.nodeAt(row.pos)\n return node?.type.name === COMPOSITE ? { pos: row.pos, node } : null\n}\n\n/**\n * `meta` with `value` stored under `field.key`: a number field converts a numeric string, an empty value (`''`,\n * `null`, `undefined`, `NaN`) removes the key. Returns a new object; `meta` is not changed.\n */\nexport function compositeMetaWith(meta: Record<string, unknown>, field: Pick<CompositeMetaField, 'key' | 'type'>, value: unknown): Record<string, unknown> {\n const next = { ...meta }\n const stored = field.type === 'number' && typeof value === 'string' && value !== '' ? Number(value) : value\n if (stored === '' || stored === null || stored === undefined || Number.isNaN(stored)) delete next[field.key]\n else next[field.key] = stored\n return next\n}\n\n/** Stores `value` under the meta field `key` of the row's composite (see `compositeMetaWith`), in one transaction. */\nexport function setCompositeMetaField(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos' | 'fields'>, key: string, value: unknown): boolean {\n const field = row.fields.find(candidate => candidate.key === key) ?? { key, type: 'text' as const }\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n if (!found) return false\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, meta: compositeMetaWith(found.node.attrs.meta ?? {}, field, value) })\n return true\n })\n}\n\n/** Replaces the tags row of the row's composite (empty names are dropped), in one transaction. */\nexport function setCompositeTags(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, tags: readonly string[]): boolean {\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n const tagsNode = found?.node.maybeChild(2)\n if (!found || !title || !description || tagsNode?.type.name !== 'compositeTags') return false\n const tagsPos = found.pos + 1 + title.nodeSize + description.nodeSize\n tr.setNodeMarkup(tagsPos, undefined, { ...tagsNode.attrs, tags: tags.filter(tag => tag) })\n return true\n })\n}\n\n/** The text `setCompositeDescription` stores for `text`: the description is one line, so line breaks collapse to spaces. */\nexport function compositeDescriptionText(text: string): string {\n return text.replace(/\\s*\\n\\s*/g, ' ').trim()\n}\n\n/**\n * Sets the text of the row's composite description to `compositeDescriptionText(text)`; an empty text empties it.\n * Only the changed span is replaced, so marks on the unchanged text (bold, author, comment) stay. Refuses (`false`)\n * when the description holds a mention or tag (it would be lost), and, when `expected` is given, when the\n * description no longer holds that text (a peer changed it since the caller read it; writing would drop that change).\n */\nexport function setCompositeDescription(editor: Editor, row: Pick<CompositeRow, 'id' | 'pos'>, text: string, expected?: string): boolean {\n const value = compositeDescriptionText(text)\n return editor.commands.command(({ tr }) => {\n const found = compositeOfRow(tr.doc, row)\n const title = found?.node.maybeChild(0)\n const description = found?.node.maybeChild(1)\n if (!found || !title || !description || description.type.name !== 'compositeDescription') return false\n if (!isTextOnly(description)) return false\n const current = description.textContent\n if (expected !== undefined && current !== expected) return false\n if (current === value) return true\n // Text only: every character is one position. Keep the common prefix and suffix, replace what lies between.\n let prefix = 0\n const max = Math.min(current.length, value.length)\n while (prefix < max && current[prefix] === value[prefix]) prefix++\n let suffix = 0\n while (suffix < max - prefix && current[current.length - 1 - suffix] === value[value.length - 1 - suffix]) suffix++\n const start = found.pos + 1 + title.nodeSize + 1\n // `insertText` with an empty text deletes the range; inserted text takes the marks at its start, as typing does.\n tr.insertText(value.slice(prefix, value.length - suffix), start + prefix, start + current.length - suffix)\n return true\n })\n}\n"],"mappings":"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"}
@@ -1,15 +1,21 @@
1
+ import { CSL_TYPES, CslDate, CslItem, CslName, CslType } from "../../core/backend-types.js";
1
2
  import { CompositeAiBrief, compositeAiBrief } from "./ai-brief.js";
2
- import { CompositeKind, CompositeKindAi, CompositeMetaField, compositeKind, defaultCompositeKinds, kindAccepts } from "./kinds.js";
3
+ import { CompositeKind, CompositeKindAi, CompositeMetaField, SOURCE_MEDIA_TYPES, compositeKind, defaultCompositeKinds, kindAccepts } from "./kinds.js";
3
4
  import { COMPOSITE_CHROME_COUNT, CompositeDescriptionNode, CompositeNode, CompositeOptions, CompositeStorage, CompositeTagsNode, CompositeTitleNode, compositeJSON, compositeKindsOf } from "./nodes.js";
5
+ import { REFERENCE_NODE, ReferenceAttrs, ReferenceNode, ReferenceOptions, ReferenceSuggestionPluginKey, referenceMarkdown } from "./reference.js";
4
6
  import { compositeConversions, convertCompositeBlocks } from "./convert.js";
5
7
  import { CompositeFence, FenceSegment, splitCompositeFences } from "../../core/fences.js";
6
8
  import { blocksFromMarkdown, compositeFence, compositeFromMarkdown } from "./fences.js";
7
9
  import { compositeChromeBlocks, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, structureBlocks } from "./heading-composites.js";
10
+ import { BIB_KEY, DERIVATION_KEY } from "./meta-keys.js";
11
+ import { bibOf, bibtexOf, citeFull, citeShort, cslOf, cslTypeOptions, yearOf } from "./bibliography.js";
12
+ import { Derivation, DerivationMethod, DerivationSource, bodyMarkdown, derivationOf, derivationState, renditionsOf, sourceOf } from "./derivation.js";
8
13
  import { LEGACY_COMPOSITE_NODE, migrateLegacyContent } from "./legacy.js";
14
+ import { collectCompositeIds, compositeIdsOf, normalizeReferences, referencesFromText } from "./reference-text.js";
9
15
  import { AnyExtension } from "@tiptap/core";
10
16
  //#region src/features/composites/schema.d.ts
11
17
  /**
12
- * The schema-bearing composite family (composite, title, description, tags) without node views, to append to
18
+ * The schema-bearing composite family (composite, title, description, tags, and the inline `reference` chip) without node views, to append to
13
19
  * `coreSchemaExtensions()`: `getSchema([...coreSchemaExtensions(), ...compositeSchemaExtensions({ kinds })])`. `kinds`
14
20
  * is the host's kind list (default: the built-in kinds in German, `defaultCompositeKinds`); it is storage, not schema.
15
21
  */
@@ -17,5 +23,5 @@ export declare function compositeSchemaExtensions(options?: {
17
23
  kinds?: readonly CompositeKind[];
18
24
  }): AnyExtension[];
19
25
  //#endregion
20
- export { COMPOSITE_CHROME_COUNT, type CompositeAiBrief, CompositeDescriptionNode, type CompositeFence, type CompositeKind, type CompositeKindAi, type CompositeMetaField, CompositeNode, type CompositeOptions, type CompositeStorage, CompositeTagsNode, CompositeTitleNode, type FenceSegment, LEGACY_COMPOSITE_NODE, blocksFromMarkdown, compositeAiBrief, compositeChromeBlocks, compositeConversions, compositeFence, compositeFromMarkdown, compositeJSON, compositeKind, compositeKindsOf, convertCompositeBlocks, defaultCompositeKinds, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, kindAccepts, migrateLegacyContent, splitCompositeFences, structureBlocks };
26
+ export { BIB_KEY, COMPOSITE_CHROME_COUNT, CSL_TYPES, type CompositeAiBrief, CompositeDescriptionNode, type CompositeFence, type CompositeKind, type CompositeKindAi, type CompositeMetaField, CompositeNode, type CompositeOptions, type CompositeStorage, CompositeTagsNode, CompositeTitleNode, type CslDate, type CslItem, type CslName, type CslType, DERIVATION_KEY, type Derivation, type DerivationMethod, type DerivationSource, type FenceSegment, LEGACY_COMPOSITE_NODE, REFERENCE_NODE, type ReferenceAttrs, ReferenceNode, type ReferenceOptions, ReferenceSuggestionPluginKey, SOURCE_MEDIA_TYPES, bibOf, bibtexOf, blocksFromMarkdown, bodyMarkdown, citeFull, citeShort, collectCompositeIds, compositeAiBrief, compositeChromeBlocks, compositeConversions, compositeFence, compositeFromMarkdown, compositeIdsOf, compositeJSON, compositeKind, compositeKindsOf, convertCompositeBlocks, cslOf, cslTypeOptions, defaultCompositeKinds, derivationOf, derivationState, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, kindAccepts, migrateLegacyContent, normalizeReferences, referenceMarkdown, referencesFromText, renditionsOf, sourceOf, splitCompositeFences, structureBlocks, yearOf };
21
27
  //# sourceMappingURL=schema.d.ts.map
@@ -1,14 +1,20 @@
1
1
  import { splitCompositeFences } from "../../core/fences.js";
2
- import { compositeKind, defaultCompositeKinds, kindAccepts } from "./kinds.js";
2
+ import { SOURCE_MEDIA_TYPES, compositeKind, defaultCompositeKinds, kindAccepts } from "./kinds.js";
3
+ import { BIB_KEY, DERIVATION_KEY } from "./meta-keys.js";
3
4
  import { blocksFromMarkdown, compositeFence, compositeFromMarkdown } from "./fences.js";
5
+ import { bodyMarkdown, derivationOf, derivationState, renditionsOf, sourceOf } from "./derivation.js";
4
6
  import { compositeChromeBlocks, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, structureBlocks } from "./heading-composites.js";
5
7
  import { COMPOSITE_CHROME_COUNT, CompositeDescriptionNode, CompositeNode, CompositeTagsNode, CompositeTitleNode, compositeJSON, compositeKindsOf } from "./nodes.js";
8
+ import { REFERENCE_NODE, ReferenceNode, ReferenceSuggestionPluginKey, referenceMarkdown } from "./reference.js";
9
+ import { CSL_TYPES } from "../../core/backend-types.js";
10
+ import { bibOf, bibtexOf, citeFull, citeShort, cslOf, cslTypeOptions, yearOf } from "./bibliography.js";
6
11
  import { compositeAiBrief } from "./ai-brief.js";
7
12
  import { compositeConversions, convertCompositeBlocks } from "./convert.js";
8
13
  import { LEGACY_COMPOSITE_NODE, migrateLegacyContent } from "./legacy.js";
14
+ import { collectCompositeIds, compositeIdsOf, normalizeReferences, referencesFromText } from "./reference-text.js";
9
15
  //#region src/features/composites/schema.ts
10
16
  /**
11
- * The schema-bearing composite family (composite, title, description, tags) without node views, to append to
17
+ * The schema-bearing composite family (composite, title, description, tags, and the inline `reference` chip) without node views, to append to
12
18
  * `coreSchemaExtensions()`: `getSchema([...coreSchemaExtensions(), ...compositeSchemaExtensions({ kinds })])`. `kinds`
13
19
  * is the host's kind list (default: the built-in kinds in German, `defaultCompositeKinds`); it is storage, not schema.
14
20
  */
@@ -17,10 +23,11 @@ function compositeSchemaExtensions(options = {}) {
17
23
  CompositeNode.configure(options.kinds ? { kinds: options.kinds } : {}),
18
24
  CompositeTitleNode,
19
25
  CompositeDescriptionNode,
20
- CompositeTagsNode
26
+ CompositeTagsNode,
27
+ ReferenceNode
21
28
  ];
22
29
  }
23
30
  //#endregion
24
- export { COMPOSITE_CHROME_COUNT, CompositeDescriptionNode, CompositeNode, CompositeTagsNode, CompositeTitleNode, LEGACY_COMPOSITE_NODE, blocksFromMarkdown, compositeAiBrief, compositeChromeBlocks, compositeConversions, compositeFence, compositeFromMarkdown, compositeJSON, compositeKind, compositeKindsOf, compositeSchemaExtensions, convertCompositeBlocks, defaultCompositeKinds, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, kindAccepts, migrateLegacyContent, splitCompositeFences, structureBlocks };
31
+ export { BIB_KEY, COMPOSITE_CHROME_COUNT, CSL_TYPES, CompositeDescriptionNode, CompositeNode, CompositeTagsNode, CompositeTitleNode, DERIVATION_KEY, LEGACY_COMPOSITE_NODE, REFERENCE_NODE, ReferenceNode, ReferenceSuggestionPluginKey, SOURCE_MEDIA_TYPES, bibOf, bibtexOf, blocksFromMarkdown, bodyMarkdown, citeFull, citeShort, collectCompositeIds, compositeAiBrief, compositeChromeBlocks, compositeConversions, compositeFence, compositeFromMarkdown, compositeIdsOf, compositeJSON, compositeKind, compositeKindsOf, compositeSchemaExtensions, convertCompositeBlocks, cslOf, cslTypeOptions, defaultCompositeKinds, derivationOf, derivationState, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, kindAccepts, migrateLegacyContent, normalizeReferences, referenceMarkdown, referencesFromText, renditionsOf, sourceOf, splitCompositeFences, structureBlocks, yearOf };
25
32
 
26
33
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/composites/schema.ts"],"sourcesContent":["// Vue-free entry of the composite feature (`@ecsplico/editor/composites/schema`): the node family, kinds, Markdown\n// fences, legacy migration, heading↔composite transforms and conversions. Servers build their schema from it; it never\n// imports node views, components or the feature factory.\nimport type { AnyExtension } from '@tiptap/core'\nimport { type CompositeKind } from './kinds'\nimport { CompositeNode, CompositeDescriptionNode, CompositeTagsNode, CompositeTitleNode } from './nodes'\n\nexport { type CompositeAiBrief, compositeAiBrief } from './ai-brief'\nexport { compositeConversions, convertCompositeBlocks } from './convert'\nexport { blocksFromMarkdown, type CompositeFence, compositeFence, compositeFromMarkdown, type FenceSegment, splitCompositeFences } from './fences'\nexport { compositeChromeBlocks, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, structureBlocks } from './heading-composites'\nexport { type CompositeKind, type CompositeKindAi, type CompositeMetaField, compositeKind, defaultCompositeKinds, kindAccepts } from './kinds'\nexport { LEGACY_COMPOSITE_NODE, migrateLegacyContent } from './legacy'\nexport {\n COMPOSITE_CHROME_COUNT,\n compositeJSON,\n compositeKindsOf,\n CompositeDescriptionNode,\n CompositeNode,\n type CompositeOptions,\n type CompositeStorage,\n CompositeTagsNode,\n CompositeTitleNode,\n} from './nodes'\n\n/**\n * The schema-bearing composite family (composite, title, description, tags) without node views, to append to\n * `coreSchemaExtensions()`: `getSchema([...coreSchemaExtensions(), ...compositeSchemaExtensions({ kinds })])`. `kinds`\n * is the host's kind list (default: the built-in kinds in German, `defaultCompositeKinds`); it is storage, not schema.\n */\nexport function compositeSchemaExtensions(options: { kinds?: readonly CompositeKind[] } = {}): AnyExtension[] {\n return [CompositeNode.configure(options.kinds ? { kinds: options.kinds } : {}), CompositeTitleNode, CompositeDescriptionNode, CompositeTagsNode]\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,SAAgB,0BAA0B,UAAgD,CAAC,GAAmB;CAC5G,OAAO;EAAC,cAAc,UAAU,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC,CAAC;EAAG;EAAoB;EAA0B;CAAiB;AACjJ"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/composites/schema.ts"],"sourcesContent":["// Vue-free entry of the composite feature (`@ecsplico/editor/composites/schema`): the node family, kinds, Markdown\n// fences, legacy migration, heading↔composite transforms and conversions. Servers build their schema from it; it never\n// imports node views, components or the feature factory.\nimport type { AnyExtension } from '@tiptap/core'\nimport { type CompositeKind } from './kinds'\nimport { CompositeNode, CompositeDescriptionNode, CompositeTagsNode, CompositeTitleNode } from './nodes'\nimport { ReferenceNode } from './reference'\n\nexport { type CompositeAiBrief, compositeAiBrief } from './ai-brief'\nexport { compositeConversions, convertCompositeBlocks } from './convert'\nexport { blocksFromMarkdown, type CompositeFence, compositeFence, compositeFromMarkdown, type FenceSegment, splitCompositeFences } from './fences'\nexport { compositeChromeBlocks, flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange, structureBlocks } from './heading-composites'\nexport { BIB_KEY, bibOf, bibtexOf, citeFull, citeShort, type CslDate, type CslItem, type CslName, type CslType, CSL_TYPES, cslOf, cslTypeOptions, yearOf } from './bibliography'\nexport { bodyMarkdown, DERIVATION_KEY, type Derivation, derivationOf, derivationState, type DerivationMethod, type DerivationSource, renditionsOf, sourceOf } from './derivation'\nexport { type CompositeKind, type CompositeKindAi, type CompositeMetaField, compositeKind, defaultCompositeKinds, kindAccepts, SOURCE_MEDIA_TYPES } from './kinds'\nexport { LEGACY_COMPOSITE_NODE, migrateLegacyContent } from './legacy'\nexport { REFERENCE_NODE, type ReferenceAttrs, referenceMarkdown, ReferenceNode, type ReferenceOptions, ReferenceSuggestionPluginKey } from './reference'\nexport { compositeIdsOf, collectCompositeIds, normalizeReferences, referencesFromText } from './reference-text'\nexport {\n COMPOSITE_CHROME_COUNT,\n compositeJSON,\n compositeKindsOf,\n CompositeDescriptionNode,\n CompositeNode,\n type CompositeOptions,\n type CompositeStorage,\n CompositeTagsNode,\n CompositeTitleNode,\n} from './nodes'\n\n/**\n * The schema-bearing composite family (composite, title, description, tags, and the inline `reference` chip) without node views, to append to\n * `coreSchemaExtensions()`: `getSchema([...coreSchemaExtensions(), ...compositeSchemaExtensions({ kinds })])`. `kinds`\n * is the host's kind list (default: the built-in kinds in German, `defaultCompositeKinds`); it is storage, not schema.\n */\nexport function compositeSchemaExtensions(options: { kinds?: readonly CompositeKind[] } = {}): AnyExtension[] {\n return [CompositeNode.configure(options.kinds ? { kinds: options.kinds } : {}), CompositeTitleNode, CompositeDescriptionNode, CompositeTagsNode, ReferenceNode]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,0BAA0B,UAAgD,CAAC,GAAmB;CAC5G,OAAO;EAAC,cAAc,UAAU,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC,CAAC;EAAG;EAAoB;EAA0B;EAAmB;CAAa;AAChK"}
@@ -1,3 +1,4 @@
1
+ import { fieldDisplay, formatDate } from "./meta-display.js";
1
2
  //#region src/features/composites/table-model.ts
2
3
  /** Id prefix of the columns derived from meta fields. */
3
4
  const FIELD_PREFIX = "meta:";
@@ -10,23 +11,12 @@ const finiteNumber = (value) => {
10
11
  return Number.isFinite(number) ? number : null;
11
12
  };
12
13
  const numberFormat = new Intl.NumberFormat("de-DE");
13
- const dateFormat = new Intl.DateTimeFormat("de-DE", {
14
- day: "2-digit",
15
- month: "2-digit",
16
- year: "numeric"
17
- });
18
14
  /** German, numeric-aware ordering of two sort keys' text. */
19
15
  const collator = new Intl.Collator("de", { numeric: true });
20
16
  /** The field of `row`'s kind with key `key`. */
21
17
  const fieldOf = (row, key) => row.fields.find((field) => field.key === key);
22
- /** `2026-05-01` → `01.05.2026`; parsed by hand because `new Date('2026-05-01')` is UTC and can show the previous day. */
23
- function formatDate(value) {
24
- const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value);
25
- const date = match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : new Date(value);
26
- return Number.isNaN(date.getTime()) ? value : dateFormat.format(date);
27
- }
28
- /** The cell text of `row`'s field `key`; empty when the value is unset or the row's kind does not have the field. */
29
- function fieldText(row, key) {
18
+ /** The cell text of `row`'s field `key`; empty when the value is unset or the row's kind does not have the field. Without `context`, `reference` fields read empty. */
19
+ function fieldText(row, key, context) {
30
20
  const field = fieldOf(row, key);
31
21
  const value = row.meta[key];
32
22
  if (!field || isEmpty(value)) return "";
@@ -36,22 +26,23 @@ function fieldText(row, key) {
36
26
  const number = finiteNumber(value);
37
27
  return number === null ? String(value) : numberFormat.format(number);
38
28
  }
39
- return String(value);
29
+ return fieldDisplay(row, field, context?.doc ?? null, context?.kinds ?? [], context?.t);
40
30
  }
41
- /** The sort key of `row`'s field `key`: numbers as numbers, selects by their label, everything else as text. */
42
- function fieldSort(row, key) {
31
+ /** The sort key of `row`'s field `key`: numbers as numbers, selects, references, citations and provenance by their cell text, everything else as text. */
32
+ function fieldSort(row, key, context) {
43
33
  const field = fieldOf(row, key);
44
34
  const value = row.meta[key];
45
35
  if (!field || isEmpty(value)) return null;
46
36
  if (field.type === "number") return finiteNumber(value) ?? String(value);
47
- if (field.type === "select") return fieldText(row, key);
37
+ if (field.type === "select" || field.type === "reference" || field.type === "bibliography" || field.type === "derivation") return fieldText(row, key, context) || null;
48
38
  return String(value);
49
39
  }
50
40
  /**
51
41
  * One column per field key in use, in kind order; the first kind that has the key decides label and type.
52
42
  * `textarea` fields (long text such as a transcript) get no column: a table cell can only show a truncated fragment.
43
+ * `context` resolves `reference` cells and sort keys.
53
44
  */
54
- function fieldColumnsOf(rows, kinds) {
45
+ function fieldColumnsOf(rows, kinds, context) {
55
46
  const firstRowOfKind = /* @__PURE__ */ new Map();
56
47
  for (const row of rows) if (!firstRowOfKind.has(row.kind)) firstRowOfKind.set(row.kind, row);
57
48
  const seen = /* @__PURE__ */ new Map();
@@ -61,7 +52,7 @@ function fieldColumnsOf(rows, kinds) {
61
52
  id: `${FIELD_PREFIX}${field.key}`,
62
53
  label: field.label,
63
54
  align: field.type === "number" ? "end" : void 0,
64
- sort: (row) => fieldSort(row, field.key),
55
+ sort: (row) => fieldSort(row, field.key, context),
65
56
  field
66
57
  });
67
58
  }
@@ -109,8 +100,8 @@ function toggled(list, value) {
109
100
  return list.includes(value) ? list.filter((item) => item !== value) : [...list, value];
110
101
  }
111
102
  const hasText = (value) => value.trim() !== "";
112
- /** Long text fields show a truncated fragment in a cell, with the full text as its title. */
113
- const isLongText = (field) => field.type === "text" || field.type === "textarea";
103
+ /** Long text fields (and the citation or provenance summaries) show a truncated fragment in a cell, with the full text as its title. */
104
+ const isLongText = (field) => field.type === "text" || field.type === "textarea" || field.type === "reference" || field.type === "bibliography" || field.type === "derivation";
114
105
  //#endregion
115
106
  export { FIELD_PREFIX, FILTER_ALL, collator, columnSpecsOf, fieldColumnsOf, fieldOf, fieldSort, fieldText, hasText, isLongText, sortRows, toggled };
116
107
 
@@ -1 +1 @@
1
- {"version":3,"file":"table-model.js","names":[],"sources":["../../../src/features/composites/table-model.ts"],"sourcesContent":["import type { Translate } from '../../i18n'\nimport type { CompositeMetaField } from './kinds'\nimport type { CompositeRow } from './rows'\n\n// The framework-free part of the Bausteine table (`CompositeTable` on Vue and React): column specs, cell text and sort\n// keys. Columns beyond the fixed ones derive from the meta fields of the kinds present in the document.\n\n/** A sort key; `null` is an empty value, which sorts last in either direction. */\nexport type SortValue = string | number | null\n\nexport interface ColumnSpec {\n id: string\n label: string\n /** Number columns read better flush right, next to their tabular numerals. */\n align?: 'end'\n sort: (row: CompositeRow) => SortValue\n}\n\nexport interface FieldColumn extends ColumnSpec {\n field: CompositeMetaField\n}\n\n/** Id prefix of the columns derived from meta fields. */\nexport const FIELD_PREFIX = 'meta:'\n\n/** The \"no filter\" value of a field's select filter. */\nexport const FILTER_ALL = '__all__'\n\nconst isEmpty = (value: unknown): boolean => value === undefined || value === null || value === ''\nconst finiteNumber = (value: unknown): number | null => {\n if (isEmpty(value)) return null\n const number = Number(value)\n return Number.isFinite(number) ? number : null\n}\nconst numberFormat = new Intl.NumberFormat('de-DE')\nconst dateFormat = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })\n\n/** German, numeric-aware ordering of two sort keys' text. */\nexport const collator = new Intl.Collator('de', { numeric: true })\n\n/** The field of `row`'s kind with key `key`. */\nexport const fieldOf = (row: CompositeRow, key: string): CompositeMetaField | undefined => row.fields.find(field => field.key === key)\n\n/** `2026-05-01` → `01.05.2026`; parsed by hand because `new Date('2026-05-01')` is UTC and can show the previous day. */\nfunction formatDate(value: string): string {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value)\n const date = match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : new Date(value)\n return Number.isNaN(date.getTime()) ? value : dateFormat.format(date)\n}\n\n/** The cell text of `row`'s field `key`; empty when the value is unset or the row's kind does not have the field. */\nexport function fieldText(row: CompositeRow, key: string): string {\n const field = fieldOf(row, key)\n const value = row.meta[key]\n if (!field || isEmpty(value)) return ''\n if (field.type === 'select') return field.options?.find(option => option.value === value)?.label ?? String(value)\n if (field.type === 'date') return formatDate(String(value))\n if (field.type === 'number') {\n const number = finiteNumber(value)\n return number === null ? String(value) : numberFormat.format(number)\n }\n return String(value)\n}\n\n/** The sort key of `row`'s field `key`: numbers as numbers, selects by their label, everything else as text. */\nexport function fieldSort(row: CompositeRow, key: string): SortValue {\n const field = fieldOf(row, key)\n const value = row.meta[key]\n if (!field || isEmpty(value)) return null\n if (field.type === 'number') return finiteNumber(value) ?? String(value)\n // ISO dates order correctly as plain strings; select values are ordered by what the user reads.\n if (field.type === 'select') return fieldText(row, key)\n return String(value)\n}\n\n/**\n * One column per field key in use, in kind order; the first kind that has the key decides label and type.\n * `textarea` fields (long text such as a transcript) get no column: a table cell can only show a truncated fragment.\n */\nexport function fieldColumnsOf(rows: readonly CompositeRow[], kinds: readonly { id: string }[]): FieldColumn[] {\n const firstRowOfKind = new Map<string, CompositeRow>()\n for (const row of rows) if (!firstRowOfKind.has(row.kind)) firstRowOfKind.set(row.kind, row)\n const seen = new Map<string, FieldColumn>()\n for (const kind of kinds) {\n for (const field of firstRowOfKind.get(kind.id)?.fields ?? []) {\n if (field.type === 'textarea' || seen.has(field.key)) continue\n seen.set(field.key, {\n id: `${FIELD_PREFIX}${field.key}`,\n label: field.label,\n align: field.type === 'number' ? 'end' : undefined,\n sort: row => fieldSort(row, field.key),\n field,\n })\n }\n }\n return [...seen.values()]\n}\n\n/** All columns: title and kind, then the field columns, then tags and summary. */\nexport function columnSpecsOf(fieldColumns: readonly FieldColumn[], t: Translate): ColumnSpec[] {\n return [\n { id: 'title', label: t('composites.table.column.composite'), sort: row => row.title.trim() || null },\n { id: 'kind', label: t('composites.table.column.kind'), sort: row => row.kindLabel },\n ...fieldColumns,\n { id: 'tags', label: t('composites.table.column.tags'), sort: row => row.tags.join(', ') || null },\n // Never empty: absence is a value here, so descending puts the summarized Bausteine first.\n { id: 'summary', label: t('composites.table.column.summary'), sort: row => (row.description.trim() ? 1 : 0) },\n ]\n}\n\n/** `rows` ordered by `sort`, descending when `desc`; empty values last whatever the direction. */\nexport function sortRows(rows: readonly CompositeRow[], sort: ColumnSpec['sort'], desc: boolean): CompositeRow[] {\n const factor = desc ? -1 : 1\n return [...rows].sort((a, b) => {\n const x = sort(a)\n const y = sort(b)\n if (x === null || y === null) return x === y ? 0 : x === null ? 1 : -1\n if (typeof x === 'number' && typeof y === 'number') return (x - y) * factor\n return collator.compare(String(x), String(y)) * factor\n })\n}\n\n/** `list` with `value` added, or removed when present. Pure: returns the new list, the caller assigns it. */\nexport function toggled(list: readonly string[], value: string): string[] {\n return list.includes(value) ? list.filter(item => item !== value) : [...list, value]\n}\n\nexport const hasText = (value: string) => value.trim() !== ''\n\n/** Long text fields show a truncated fragment in a cell, with the full text as its title. */\nexport const isLongText = (field: CompositeMetaField) => field.type === 'text' || field.type === 'textarea'\n"],"mappings":"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"}
1
+ {"version":3,"file":"table-model.js","names":[],"sources":["../../../src/features/composites/table-model.ts"],"sourcesContent":["import type { Node as PMNode } from '@tiptap/pm/model'\nimport type { Translate } from '../../i18n'\nimport type { CompositeKind, CompositeMetaField } from './kinds'\nimport { fieldDisplay, formatDate } from './meta-display'\nimport type { CompositeRow } from './rows'\n\n// The framework-free part of the Bausteine table (`CompositeTable` on Vue and React): column specs, cell text and sort\n// keys. Columns beyond the fixed ones derive from the meta fields of the kinds present in the document.\n\n/** A sort key; `null` is an empty value, which sorts last in either direction. */\nexport type SortValue = string | number | null\n\nexport interface ColumnSpec {\n id: string\n label: string\n /** Number columns read better flush right, next to their tabular numerals. */\n align?: 'end'\n sort: (row: CompositeRow) => SortValue\n}\n\nexport interface FieldColumn extends ColumnSpec {\n field: CompositeMetaField\n}\n\n/** Id prefix of the columns derived from meta fields. */\nexport const FIELD_PREFIX = 'meta:'\n\n/** The \"no filter\" value of a field's select filter. */\nexport const FILTER_ALL = '__all__'\n\nconst isEmpty = (value: unknown): boolean => value === undefined || value === null || value === ''\nconst finiteNumber = (value: unknown): number | null => {\n if (isEmpty(value)) return null\n const number = Number(value)\n return Number.isFinite(number) ? number : null\n}\nconst numberFormat = new Intl.NumberFormat('de-DE')\n\n/** What a cell needs beyond its row to resolve `reference` fields and word `derivation` methods: the document, its kinds and the translator. */\nexport interface FieldContext {\n doc: PMNode\n kinds: readonly CompositeKind[]\n t: Translate\n}\n\n/** German, numeric-aware ordering of two sort keys' text. */\nexport const collator = new Intl.Collator('de', { numeric: true })\n\n/** The field of `row`'s kind with key `key`. */\nexport const fieldOf = (row: CompositeRow, key: string): CompositeMetaField | undefined => row.fields.find(field => field.key === key)\n\n/** The cell text of `row`'s field `key`; empty when the value is unset or the row's kind does not have the field. Without `context`, `reference` fields read empty. */\nexport function fieldText(row: CompositeRow, key: string, context?: FieldContext): string {\n const field = fieldOf(row, key)\n const value = row.meta[key]\n if (!field || isEmpty(value)) return ''\n if (field.type === 'select') return field.options?.find(option => option.value === value)?.label ?? String(value)\n if (field.type === 'date') return formatDate(String(value))\n if (field.type === 'number') {\n const number = finiteNumber(value)\n return number === null ? String(value) : numberFormat.format(number)\n }\n return fieldDisplay(row, field, context?.doc ?? null, context?.kinds ?? [], context?.t)\n}\n\n/** The sort key of `row`'s field `key`: numbers as numbers, selects, references, citations and provenance by their cell text, everything else as text. */\nexport function fieldSort(row: CompositeRow, key: string, context?: FieldContext): SortValue {\n const field = fieldOf(row, key)\n const value = row.meta[key]\n if (!field || isEmpty(value)) return null\n if (field.type === 'number') return finiteNumber(value) ?? String(value)\n // ISO dates order correctly as plain strings; select values are ordered by what the user reads.\n if (field.type === 'select' || field.type === 'reference' || field.type === 'bibliography' || field.type === 'derivation') return fieldText(row, key, context) || null\n return String(value)\n}\n\n/**\n * One column per field key in use, in kind order; the first kind that has the key decides label and type.\n * `textarea` fields (long text such as a transcript) get no column: a table cell can only show a truncated fragment.\n * `context` resolves `reference` cells and sort keys.\n */\nexport function fieldColumnsOf(rows: readonly CompositeRow[], kinds: readonly { id: string }[], context?: FieldContext): FieldColumn[] {\n const firstRowOfKind = new Map<string, CompositeRow>()\n for (const row of rows) if (!firstRowOfKind.has(row.kind)) firstRowOfKind.set(row.kind, row)\n const seen = new Map<string, FieldColumn>()\n for (const kind of kinds) {\n for (const field of firstRowOfKind.get(kind.id)?.fields ?? []) {\n if (field.type === 'textarea' || seen.has(field.key)) continue\n seen.set(field.key, {\n id: `${FIELD_PREFIX}${field.key}`,\n label: field.label,\n align: field.type === 'number' ? 'end' : undefined,\n sort: row => fieldSort(row, field.key, context),\n field,\n })\n }\n }\n return [...seen.values()]\n}\n\n/** All columns: title and kind, then the field columns, then tags and summary. */\nexport function columnSpecsOf(fieldColumns: readonly FieldColumn[], t: Translate): ColumnSpec[] {\n return [\n { id: 'title', label: t('composites.table.column.composite'), sort: row => row.title.trim() || null },\n { id: 'kind', label: t('composites.table.column.kind'), sort: row => row.kindLabel },\n ...fieldColumns,\n { id: 'tags', label: t('composites.table.column.tags'), sort: row => row.tags.join(', ') || null },\n // Never empty: absence is a value here, so descending puts the summarized Bausteine first.\n { id: 'summary', label: t('composites.table.column.summary'), sort: row => (row.description.trim() ? 1 : 0) },\n ]\n}\n\n/** `rows` ordered by `sort`, descending when `desc`; empty values last whatever the direction. */\nexport function sortRows(rows: readonly CompositeRow[], sort: ColumnSpec['sort'], desc: boolean): CompositeRow[] {\n const factor = desc ? -1 : 1\n return [...rows].sort((a, b) => {\n const x = sort(a)\n const y = sort(b)\n if (x === null || y === null) return x === y ? 0 : x === null ? 1 : -1\n if (typeof x === 'number' && typeof y === 'number') return (x - y) * factor\n return collator.compare(String(x), String(y)) * factor\n })\n}\n\n/** `list` with `value` added, or removed when present. Pure: returns the new list, the caller assigns it. */\nexport function toggled(list: readonly string[], value: string): string[] {\n return list.includes(value) ? list.filter(item => item !== value) : [...list, value]\n}\n\nexport const hasText = (value: string) => value.trim() !== ''\n\n/** Long text fields (and the citation or provenance summaries) show a truncated fragment in a cell, with the full text as its title. */\nexport const isLongText = (field: CompositeMetaField) => field.type === 'text' || field.type === 'textarea' || field.type === 'reference' || field.type === 'bibliography' || field.type === 'derivation'\n"],"mappings":"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"}
@@ -9,7 +9,8 @@ import _default$5 from "./components/CompositeSummaryEditor.vue.js";
9
9
  import _default$6 from "./components/CompositeTable.vue.js";
10
10
  import _default$7 from "./components/CompositeTagsEditor.vue.js";
11
11
  import _default$8 from "./components/CompositeView.vue.js";
12
- import _default$9 from "./components/SettingsEditingTab.vue.js";
12
+ import _default$9 from "./components/ReferenceView.vue.js";
13
+ import _default$10 from "./components/SettingsEditingTab.vue.js";
13
14
  import { useCompositeAnchors } from "./use-composite-anchors.js";
14
15
  import { CompositeRowsModel, useCompositeRows } from "./use-composite-rows.js";
15
16
  //#region src/features/composites/vue.d.ts
@@ -19,5 +20,5 @@ import { CompositeRowsModel, useCompositeRows } from "./use-composite-rows.js";
19
20
  */
20
21
  export declare function compositeFeature(options?: CompositeFeatureOptions): VueFeature;
21
22
  //#endregion
22
- export { _default as CompositeChrome, _default$1 as CompositeDetails, _default$2 as CompositeFieldEditor, _default$3 as CompositeInspector, type CompositeRowsModel, _default$4 as CompositeStatusMenu, _default$5 as CompositeSummaryEditor, _default$6 as CompositeTable, _default$7 as CompositeTagsEditor, _default$8 as CompositeView, _default$9 as SettingsEditingTab, useCompositeAnchors, useCompositeRows };
23
+ export { _default as CompositeChrome, _default$1 as CompositeDetails, _default$2 as CompositeFieldEditor, _default$3 as CompositeInspector, type CompositeRowsModel, _default$4 as CompositeStatusMenu, _default$5 as CompositeSummaryEditor, _default$6 as CompositeTable, _default$7 as CompositeTagsEditor, _default$8 as CompositeView, _default$9 as ReferenceView, _default$10 as SettingsEditingTab, useCompositeAnchors, useCompositeRows };
23
24
  //# sourceMappingURL=vue.d.ts.map
@@ -8,7 +8,8 @@ import _sfc_main$4 from "./components/CompositeStatusMenu.js";
8
8
  import _sfc_main$7 from "./components/CompositeTagsEditor.js";
9
9
  import _sfc_main from "./components/CompositeChrome.js";
10
10
  import _sfc_main$8 from "./components/CompositeView.js";
11
- import _sfc_main$9 from "./components/SettingsEditingTab.js";
11
+ import _sfc_main$9 from "./components/ReferenceView.js";
12
+ import _sfc_main$10 from "./components/SettingsEditingTab.js";
12
13
  import _sfc_main$3 from "./components/CompositeInspector.js";
13
14
  import _sfc_main$6 from "./components/CompositeTable.js";
14
15
  import { VueNodeViewRenderer } from "@tiptap/vue-3";
@@ -20,11 +21,14 @@ import { VueNodeViewRenderer } from "@tiptap/vue-3";
20
21
  function compositeFeature(options = {}) {
21
22
  return compositeFeatureCore({
22
23
  chrome: _sfc_main,
23
- settingsTab: _sfc_main$9,
24
- nodeViews: { composite: VueNodeViewRenderer(_sfc_main$8) }
24
+ settingsTab: _sfc_main$10,
25
+ nodeViews: {
26
+ composite: VueNodeViewRenderer(_sfc_main$8),
27
+ reference: VueNodeViewRenderer(_sfc_main$9)
28
+ }
25
29
  }, options);
26
30
  }
27
31
  //#endregion
28
- export { _sfc_main as CompositeChrome, _sfc_main$1 as CompositeDetails, _sfc_main$2 as CompositeFieldEditor, _sfc_main$3 as CompositeInspector, _sfc_main$4 as CompositeStatusMenu, _sfc_main$5 as CompositeSummaryEditor, _sfc_main$6 as CompositeTable, _sfc_main$7 as CompositeTagsEditor, _sfc_main$8 as CompositeView, _sfc_main$9 as SettingsEditingTab, compositeFeature, useCompositeAnchors, useCompositeRows };
32
+ export { _sfc_main as CompositeChrome, _sfc_main$1 as CompositeDetails, _sfc_main$2 as CompositeFieldEditor, _sfc_main$3 as CompositeInspector, _sfc_main$4 as CompositeStatusMenu, _sfc_main$5 as CompositeSummaryEditor, _sfc_main$6 as CompositeTable, _sfc_main$7 as CompositeTagsEditor, _sfc_main$8 as CompositeView, _sfc_main$9 as ReferenceView, _sfc_main$10 as SettingsEditingTab, compositeFeature, useCompositeAnchors, useCompositeRows };
29
33
 
30
34
  //# sourceMappingURL=vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vue.js","names":["CompositeChrome","SettingsEditingTab","CompositeView"],"sources":["../../../src/features/composites/vue.ts"],"sourcesContent":["import { VueNodeViewRenderer } from '@tiptap/vue-3'\nimport type { Component } from 'vue'\nimport type { VueFeature } from '../../vue'\nimport CompositeChrome from './components/CompositeChrome.vue'\nimport CompositeView from './components/CompositeView.vue'\nimport SettingsEditingTab from './components/SettingsEditingTab.vue'\nimport { type CompositeFeatureOptions, compositeFeatureCore } from './feature'\n\nexport { default as CompositeChrome } from './components/CompositeChrome.vue'\nexport { default as CompositeDetails } from './components/CompositeDetails.vue'\nexport { default as CompositeFieldEditor } from './components/CompositeFieldEditor.vue'\nexport { default as CompositeInspector } from './components/CompositeInspector.vue'\nexport { default as CompositeStatusMenu } from './components/CompositeStatusMenu.vue'\nexport { default as CompositeSummaryEditor } from './components/CompositeSummaryEditor.vue'\nexport { default as CompositeTable } from './components/CompositeTable.vue'\nexport { default as CompositeTagsEditor } from './components/CompositeTagsEditor.vue'\nexport { default as CompositeView } from './components/CompositeView.vue'\nexport { default as SettingsEditingTab } from './components/SettingsEditingTab.vue'\nexport { useCompositeAnchors } from './use-composite-anchors'\nexport { type CompositeRowsModel, useCompositeRows } from './use-composite-rows'\n\n/**\n * The Vue composites feature (Nuxt UI): see `compositeFeatureCore`. The host places `CompositeTable` and\n * `CompositeInspector` beside the editor.\n */\nexport function compositeFeature(options: CompositeFeatureOptions = {}): VueFeature {\n return compositeFeatureCore<Component>({\n chrome: CompositeChrome,\n settingsTab: SettingsEditingTab,\n nodeViews: { composite: VueNodeViewRenderer(CompositeView) },\n }, options)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,iBAAiB,UAAmC,CAAC,GAAe;CAClF,OAAO,qBAAgC;EACrC,QAAQA;EACR,aAAaC;EACb,WAAW,EAAE,WAAW,oBAAoBC,WAAa,EAAE;CAC7D,GAAG,OAAO;AACZ"}
1
+ {"version":3,"file":"vue.js","names":["CompositeChrome","SettingsEditingTab","CompositeView","ReferenceView"],"sources":["../../../src/features/composites/vue.ts"],"sourcesContent":["import { VueNodeViewRenderer } from '@tiptap/vue-3'\nimport type { Component } from 'vue'\nimport type { VueFeature } from '../../vue'\nimport CompositeChrome from './components/CompositeChrome.vue'\nimport CompositeView from './components/CompositeView.vue'\nimport ReferenceView from './components/ReferenceView.vue'\nimport SettingsEditingTab from './components/SettingsEditingTab.vue'\nimport { type CompositeFeatureOptions, compositeFeatureCore } from './feature'\n\nexport { default as CompositeChrome } from './components/CompositeChrome.vue'\nexport { default as CompositeDetails } from './components/CompositeDetails.vue'\nexport { default as CompositeFieldEditor } from './components/CompositeFieldEditor.vue'\nexport { default as CompositeInspector } from './components/CompositeInspector.vue'\nexport { default as CompositeStatusMenu } from './components/CompositeStatusMenu.vue'\nexport { default as CompositeSummaryEditor } from './components/CompositeSummaryEditor.vue'\nexport { default as CompositeTable } from './components/CompositeTable.vue'\nexport { default as CompositeTagsEditor } from './components/CompositeTagsEditor.vue'\nexport { default as CompositeView } from './components/CompositeView.vue'\nexport { default as ReferenceView } from './components/ReferenceView.vue'\nexport { default as SettingsEditingTab } from './components/SettingsEditingTab.vue'\nexport { useCompositeAnchors } from './use-composite-anchors'\nexport { type CompositeRowsModel, useCompositeRows } from './use-composite-rows'\n\n/**\n * The Vue composites feature (Nuxt UI): see `compositeFeatureCore`. The host places `CompositeTable` and\n * `CompositeInspector` beside the editor.\n */\nexport function compositeFeature(options: CompositeFeatureOptions = {}): VueFeature {\n return compositeFeatureCore<Component>({\n chrome: CompositeChrome,\n settingsTab: SettingsEditingTab,\n nodeViews: { composite: VueNodeViewRenderer(CompositeView), reference: VueNodeViewRenderer(ReferenceView) },\n }, options)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,iBAAiB,UAAmC,CAAC,GAAe;CAClF,OAAO,qBAAgC;EACrC,QAAQA;EACR,aAAaC;EACb,WAAW;GAAE,WAAW,oBAAoBC,WAAa;GAAG,WAAW,oBAAoBC,WAAa;EAAE;CAC5G,GAAG,OAAO;AACZ"}
@@ -6,6 +6,8 @@ export interface AssetRecord {
6
6
  filename: string;
7
7
  mimeType: string;
8
8
  size: number;
9
+ /** SHA-256 (hex) of the stored bytes; absent on records written before hashes were kept. */
10
+ sha256?: string;
9
11
  createdAt: string;
10
12
  }
11
13
  /** `assets.json` of a document package. */
@@ -1,3 +1,4 @@
1
+ import { EDITOR_API } from "../../core/api-routes.js";
1
2
  //#region src/features/media/assets.ts
2
3
  /** `<uuid>.<ext>`: the random name an upload is stored under. */
3
4
  const ASSET_NAME = /^[0-9a-f-]{36}\.[a-z0-9]{1,8}$/;
@@ -5,7 +6,7 @@ const ASSET_NAME = /^[0-9a-f-]{36}\.[a-z0-9]{1,8}$/;
5
6
  const ASSET_DOCUMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
6
7
  /** Owner of uploads made by editors that are not bound to a stored document (e.g. the playground). */
7
8
  const SCRATCH_DOCUMENT_ID = "scratch";
8
- const ASSET_URL_PREFIX = "/api/editor/assets/";
9
+ const ASSET_URL_PREFIX = `${EDITOR_API}/assets/`;
9
10
  /** URL of asset `name` of document `documentId`. */
10
11
  function assetUrl(documentId, name) {
11
12
  return `${ASSET_URL_PREFIX}${documentId}/${name}`;
@@ -13,7 +14,7 @@ function assetUrl(documentId, name) {
13
14
  /** Parses an asset URL (as embedded in `src` attributes); null for anything else. */
14
15
  function assetRefOf(src) {
15
16
  if (!src.startsWith(ASSET_URL_PREFIX)) return null;
16
- const [documentId, name, ...rest] = src.slice(19).split("/");
17
+ const [documentId, name, ...rest] = src.slice(ASSET_URL_PREFIX.length).split("/");
17
18
  if (rest.length || !documentId || !name || !ASSET_DOCUMENT_ID.test(documentId) || !ASSET_NAME.test(name)) return null;
18
19
  return {
19
20
  documentId,
@@ -1 +1 @@
1
- {"version":3,"file":"assets.js","names":[],"sources":["../../../src/features/media/assets.ts"],"sourcesContent":["// Vue-free and server-importable: the asset endpoints, the host store and the client all agree on these shapes.\n// Every uploaded file belongs to one document (a package directory in the host's store) and is addressed by\n// `/api/editor/assets/<documentId>/<name>`.\n\n/** One stored file of a document; `name` is the file name inside the package's `assets/` directory. */\nexport interface AssetRecord {\n name: string\n /** Original file name as uploaded (shown in downloads). */\n filename: string\n mimeType: string\n size: number\n createdAt: string\n}\n\n/** `assets.json` of a document package. */\nexport interface AssetManifest {\n version: 1\n assets: Record<string, AssetRecord>\n}\n\n/** `<uuid>.<ext>`: the random name an upload is stored under. */\nexport const ASSET_NAME = /^[0-9a-f-]{36}\\.[a-z0-9]{1,8}$/\n\n/** Document ids the asset endpoints accept (host document uuids, or `SCRATCH_DOCUMENT_ID`). */\nexport const ASSET_DOCUMENT_ID = /^[A-Za-z0-9_-]{1,64}$/\n\n/** Owner of uploads made by editors that are not bound to a stored document (e.g. the playground). */\nexport const SCRATCH_DOCUMENT_ID = 'scratch'\n\nconst ASSET_URL_PREFIX = '/api/editor/assets/'\n\n/** URL of asset `name` of document `documentId`. */\nexport function assetUrl(documentId: string, name: string): string {\n return `${ASSET_URL_PREFIX}${documentId}/${name}`\n}\n\n/** Parses an asset URL (as embedded in `src` attributes); null for anything else. */\nexport function assetRefOf(src: string): { documentId: string, name: string } | null {\n if (!src.startsWith(ASSET_URL_PREFIX)) return null\n const [documentId, name, ...rest] = src.slice(ASSET_URL_PREFIX.length).split('/')\n if (rest.length || !documentId || !name || !ASSET_DOCUMENT_ID.test(documentId) || !ASSET_NAME.test(name)) return null\n return { documentId, name }\n}\n"],"mappings":";;AAqBA,MAAa,aAAa;;AAG1B,MAAa,oBAAoB;;AAGjC,MAAa,sBAAsB;AAEnC,MAAM,mBAAmB;;AAGzB,SAAgB,SAAS,YAAoB,MAAsB;CACjE,OAAO,GAAG,mBAAmB,WAAW,GAAG;AAC7C;;AAGA,SAAgB,WAAW,KAA0D;CACnF,IAAI,CAAC,IAAI,WAAW,gBAAgB,GAAG,OAAO;CAC9C,MAAM,CAAC,YAAY,MAAM,GAAG,QAAQ,IAAI,MAAM,EAAuB,CAAC,CAAC,MAAM,GAAG;CAChF,IAAI,KAAK,UAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,kBAAkB,KAAK,UAAU,KAAK,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;CACjH,OAAO;EAAE;EAAY;CAAK;AAC5B"}
1
+ {"version":3,"file":"assets.js","names":[],"sources":["../../../src/features/media/assets.ts"],"sourcesContent":["import { EDITOR_API } from '../../core/api-routes'\n\n// Vue-free and server-importable: the asset endpoints, the host store and the client all agree on these shapes.\n// Every uploaded file belongs to one document (a package directory in the host's store) and is addressed by\n// `/api/editor/assets/<documentId>/<name>`.\n\n/** One stored file of a document; `name` is the file name inside the package's `assets/` directory. */\nexport interface AssetRecord {\n name: string\n /** Original file name as uploaded (shown in downloads). */\n filename: string\n mimeType: string\n size: number\n /** SHA-256 (hex) of the stored bytes; absent on records written before hashes were kept. */\n sha256?: string\n createdAt: string\n}\n\n/** `assets.json` of a document package. */\nexport interface AssetManifest {\n version: 1\n assets: Record<string, AssetRecord>\n}\n\n/** `<uuid>.<ext>`: the random name an upload is stored under. */\nexport const ASSET_NAME = /^[0-9a-f-]{36}\\.[a-z0-9]{1,8}$/\n\n/** Document ids the asset endpoints accept (host document uuids, or `SCRATCH_DOCUMENT_ID`). */\nexport const ASSET_DOCUMENT_ID = /^[A-Za-z0-9_-]{1,64}$/\n\n/** Owner of uploads made by editors that are not bound to a stored document (e.g. the playground). */\nexport const SCRATCH_DOCUMENT_ID = 'scratch'\n\nconst ASSET_URL_PREFIX = `${EDITOR_API}/assets/`\n\n/** URL of asset `name` of document `documentId`. */\nexport function assetUrl(documentId: string, name: string): string {\n return `${ASSET_URL_PREFIX}${documentId}/${name}`\n}\n\n/** Parses an asset URL (as embedded in `src` attributes); null for anything else. */\nexport function assetRefOf(src: string): { documentId: string, name: string } | null {\n if (!src.startsWith(ASSET_URL_PREFIX)) return null\n const [documentId, name, ...rest] = src.slice(ASSET_URL_PREFIX.length).split('/')\n if (rest.length || !documentId || !name || !ASSET_DOCUMENT_ID.test(documentId) || !ASSET_NAME.test(name)) return null\n return { documentId, name }\n}\n"],"mappings":";;;AAyBA,MAAa,aAAa;;AAG1B,MAAa,oBAAoB;;AAGjC,MAAa,sBAAsB;AAEnC,MAAM,mBAAmB,GAAG,WAAW;;AAGvC,SAAgB,SAAS,YAAoB,MAAsB;CACjE,OAAO,GAAG,mBAAmB,WAAW,GAAG;AAC7C;;AAGA,SAAgB,WAAW,KAA0D;CACnF,IAAI,CAAC,IAAI,WAAW,gBAAgB,GAAG,OAAO;CAC9C,MAAM,CAAC,YAAY,MAAM,GAAG,QAAQ,IAAI,MAAM,iBAAiB,MAAM,CAAC,CAAC,MAAM,GAAG;CAChF,IAAI,KAAK,UAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,kBAAkB,KAAK,UAAU,KAAK,CAAC,WAAW,KAAK,IAAI,GAAG,OAAO;CACjH,OAAO;EAAE;EAAY;CAAK;AAC5B"}
@@ -1,7 +1,7 @@
1
1
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
2
2
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
3
3
  import { formatBytes } from "../upload.js";
4
- import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, toDisplayString, unref, withCtx } from "vue";
4
+ import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
5
5
  import { NodeViewWrapper, nodeViewProps } from "@tiptap/vue-3";
6
6
  //#region src/features/media/components/FileView.vue
7
7
  const _hoisted_1 = {
@@ -25,11 +25,20 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
25
25
  setup(__props) {
26
26
  const { Button: UButton, Icon: UIcon } = useUiKit();
27
27
  const { t } = useEditorI18n();
28
+ const reveal = ref(null);
29
+ function onRevealPage(event) {
30
+ const detail = event.detail;
31
+ if (typeof detail === "number" && detail > 0) reveal.value = {
32
+ page: detail,
33
+ n: (reveal.value?.n ?? 0) + 1
34
+ };
35
+ }
28
36
  return (_ctx, _cache) => {
29
37
  return openBlock(), createBlock(unref(NodeViewWrapper), {
30
38
  as: "figure",
31
39
  class: normalizeClass(["media-block flex flex-col gap-1", { "is-selected": _ctx.selected }]),
32
- "data-id": _ctx.node.attrs.id || void 0
40
+ "data-id": _ctx.node.attrs.id || void 0,
41
+ onSkRevealPage: onRevealPage
33
42
  }, {
34
43
  default: withCtx(() => [createElementVNode("div", _hoisted_1, [
35
44
  createVNode(unref(UIcon), {
@@ -56,8 +65,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
56
65
  variant: "soft"
57
66
  }, null, 8, ["href", "label"])
58
67
  ]), _ctx.node.attrs.mimeType === "application/pdf" && _ctx.node.attrs.src ? (openBlock(), createElementBlock("object", {
59
- key: 0,
60
- data: _ctx.node.attrs.src,
68
+ key: reveal.value?.n ?? 0,
69
+ data: reveal.value ? `${_ctx.node.attrs.src}#page=${reveal.value.page}` : _ctx.node.attrs.src,
61
70
  type: "application/pdf",
62
71
  contenteditable: "false",
63
72
  class: "mt-2 h-[480px] w-full rounded-md border border-[var(--sk-border)]"
@@ -1 +1 @@
1
- {"version":3,"file":"FileView.js","names":["selected","node","editor","updateAttributes","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createVNode","_createElementBlock","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/media/components/FileView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { formatBytes } from '../upload'\n\ndefineProps(nodeViewProps)\n\nconst { Button: UButton, Icon: UIcon } = useUiKit()\nconst { t } = useEditorI18n()\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"figure\"\n :class=\"['media-block flex flex-col gap-1', { 'is-selected': selected }]\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"flex items-center gap-2\" contenteditable=\"false\">\n <UIcon\n :name=\"node.attrs.mimeType === 'application/pdf' ? 'i-lucide-file-text' : 'i-lucide-file'\"\n class=\"size-5 shrink-0 text-[var(--sk-muted)]\"\n />\n <input\n :value=\"node.attrs.title\"\n :placeholder=\"t('media.view.fileName')\"\n :aria-label=\"t('media.view.fileName')\"\n autocomplete=\"off\"\n :readonly=\"!editor.isEditable\"\n class=\"min-w-0 flex-1 rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @input=\"updateAttributes({ title: ($event.target as HTMLInputElement).value })\"\n >\n <span v-if=\"node.attrs.size\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)]\">{{ formatBytes(node.attrs.size) }}</span>\n <UButton\n :href=\"node.attrs.src\"\n target=\"_blank\"\n :label=\"t('media.view.open')\"\n icon=\"i-lucide-external-link\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"soft\"\n />\n </div>\n <object\n v-if=\"node.attrs.mimeType === 'application/pdf' && node.attrs.src\"\n :data=\"node.attrs.src\"\n type=\"application/pdf\"\n contenteditable=\"false\"\n class=\"mt-2 h-[480px] w-full rounded-md border border-[var(--sk-border)]\"\n />\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EAQA,MAAM,EAAE,QAAQ,SAAS,MAAM,UAAU,SAAS;EAClD,MAAM,EAAE,MAAM,cAAc;;GAI1B,OAAAI,UAAA,GAAAC,YAqCkBC,MAAA,eAAA,GAAA;IApChB,IAAG;IACF,OAAKC,eAAA,CAAA,mCAAA,EAAA,eAAuDP,KAAAA,SAAQ,CAAA,CAAA;IACpE,WAASC,KAAAA,KAAK,MAAM,MAAM,KAAA;;IAE3B,SAAAO,cAwBM,CAxBNC,mBAwBM,OAxBN,YAwBM;KAvBJC,YAGEJ,MAAA,KAAA,GAAA;MAFC,MAAML,KAAAA,KAAK,MAAM,aAAQ,oBAAA,uBAAA;MAC1B,OAAM;;KAERQ,mBAQC,SAAA;MAPE,OAAOR,KAAAA,KAAK,MAAM;MAClB,aAAaK,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAYA,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAa;MACZ,UAAQ,CAAGJ,KAAAA,OAAO;MACnB,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAEC,KAAAA,iBAAgB,EAAA,OAAW,OAAO,OAA4B,MAAK,CAAA;;KAEjEF,KAAAA,KAAK,MAAM,QAAvBG,UAAA,GAAAO,mBAA0H,QAA1H,YAA0HC,gBAAtCN,MAAA,WAAA,CAAW,CAACL,KAAAA,KAAK,MAAM,IAAI,CAAA,GAAA,CAAA,KAAAY,mBAAA,QAAA,IAAA;KAC/GH,YAQEJ,MAAA,OAAA,GAAA;MAPC,MAAML,KAAAA,KAAK,MAAM;MAClB,QAAO;MACN,OAAOK,MAAA,CAAA,CAAC,CAAA,iBAAA;MACT,MAAK;MACL,MAAK;MACL,OAAM;MACN,SAAQ;;IAIJL,CAAAA,GAAAA,KAAAA,KAAK,MAAM,aAAQ,qBAA0BA,KAAAA,KAAK,MAAM,OADhEG,UAAA,GAAAO,mBAME,UAAA;;KAJC,MAAMV,KAAAA,KAAK,MAAM;KAClB,MAAK;KACL,iBAAgB;KAChB,OAAM"}
1
+ {"version":3,"file":"FileView.js","names":["selected","node","editor","updateAttributes","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createVNode","_createElementBlock","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/media/components/FileView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { ref } from 'vue'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { formatBytes } from '../upload'\n\ndefineProps(nodeViewProps)\n\nconst { Button: UButton, Icon: UIcon } = useUiKit()\nconst { t } = useEditorI18n()\n\n// Page the PDF viewer was asked to open at (`revealSource`, locator „S. 3“). `n` counts the requests: the viewer is\n// remounted on every one, so asking for the same page again jumps back after the user scrolled away.\nconst reveal = ref<{ page: number, n: number } | null>(null)\nfunction onRevealPage(event: Event) {\n const detail = (event as CustomEvent<unknown>).detail\n if (typeof detail === 'number' && detail > 0) reveal.value = { page: detail, n: (reveal.value?.n ?? 0) + 1 }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"figure\"\n :class=\"['media-block flex flex-col gap-1', { 'is-selected': selected }]\"\n :data-id=\"node.attrs.id || undefined\"\n @sk-reveal-page=\"onRevealPage\"\n >\n <div class=\"flex items-center gap-2\" contenteditable=\"false\">\n <UIcon\n :name=\"node.attrs.mimeType === 'application/pdf' ? 'i-lucide-file-text' : 'i-lucide-file'\"\n class=\"size-5 shrink-0 text-[var(--sk-muted)]\"\n />\n <input\n :value=\"node.attrs.title\"\n :placeholder=\"t('media.view.fileName')\"\n :aria-label=\"t('media.view.fileName')\"\n autocomplete=\"off\"\n :readonly=\"!editor.isEditable\"\n class=\"min-w-0 flex-1 rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @input=\"updateAttributes({ title: ($event.target as HTMLInputElement).value })\"\n >\n <span v-if=\"node.attrs.size\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)]\">{{ formatBytes(node.attrs.size) }}</span>\n <UButton\n :href=\"node.attrs.src\"\n target=\"_blank\"\n :label=\"t('media.view.open')\"\n icon=\"i-lucide-external-link\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"soft\"\n />\n </div>\n <object\n v-if=\"node.attrs.mimeType === 'application/pdf' && node.attrs.src\"\n :key=\"reveal?.n ?? 0\"\n :data=\"reveal ? `${node.attrs.src}#page=${reveal.page}` : node.attrs.src\"\n type=\"application/pdf\"\n contenteditable=\"false\"\n class=\"mt-2 h-[480px] w-full rounded-md border border-[var(--sk-border)]\"\n />\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EASA,MAAM,EAAE,QAAQ,SAAS,MAAM,UAAU,SAAS;EAClD,MAAM,EAAE,MAAM,cAAc;EAI5B,MAAM,SAAS,IAAwC,IAAI;EAC3D,SAAS,aAAa,OAAc;GAClC,MAAM,SAAU,MAA+B;GAC/C,IAAI,OAAO,WAAW,YAAY,SAAS,GAAG,OAAO,QAAQ;IAAE,MAAM;IAAQ,IAAI,OAAO,OAAO,KAAK,KAAK;GAAE;EAC7G;;GAIE,OAAAI,UAAA,GAAAC,YAuCkBC,MAAA,eAAA,GAAA;IAtChB,IAAG;IACF,OAAKC,eAAA,CAAA,mCAAA,EAAA,eAAuDP,KAAAA,SAAQ,CAAA,CAAA;IACpE,WAASC,KAAAA,KAAK,MAAM,MAAM,KAAA;IAC1B,gBAAgB;;IAEjB,SAAAO,cAwBM,CAxBNC,mBAwBM,OAxBN,YAwBM;KAvBJC,YAGEJ,MAAA,KAAA,GAAA;MAFC,MAAML,KAAAA,KAAK,MAAM,aAAQ,oBAAA,uBAAA;MAC1B,OAAM;;KAERQ,mBAQC,SAAA;MAPE,OAAOR,KAAAA,KAAK,MAAM;MAClB,aAAaK,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAYA,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAa;MACZ,UAAQ,CAAGJ,KAAAA,OAAO;MACnB,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAEC,KAAAA,iBAAgB,EAAA,OAAW,OAAO,OAA4B,MAAK,CAAA;;KAEjEF,KAAAA,KAAK,MAAM,QAAvBG,UAAA,GAAAO,mBAA0H,QAA1H,YAA0HC,gBAAtCN,MAAA,WAAA,CAAW,CAACL,KAAAA,KAAK,MAAM,IAAI,CAAA,GAAA,CAAA,KAAAY,mBAAA,QAAA,IAAA;KAC/GH,YAQEJ,MAAA,OAAA,GAAA;MAPC,MAAML,KAAAA,KAAK,MAAM;MAClB,QAAO;MACN,OAAOK,MAAA,CAAA,CAAC,CAAA,iBAAA;MACT,MAAK;MACL,MAAK;MACL,OAAM;MACN,SAAQ;;IAIJL,CAAAA,GAAAA,KAAAA,KAAK,MAAM,aAAQ,qBAA0BA,KAAAA,KAAK,MAAM,OADhEG,UAAA,GAAAO,mBAOE,UAAA;KALC,KAAK,OAAA,OAAQ,KAAC;KACd,MAAM,OAAA,QAAM,GAAMV,KAAAA,KAAK,MAAM,IAAG,QAAS,OAAA,MAAO,SAASA,KAAAA,KAAK,MAAM;KACrE,MAAK;KACL,iBAAgB;KAChB,OAAM"}
@@ -10,12 +10,12 @@ export interface MediaUi<C> {
10
10
  nodeViews: MediaNodeViews;
11
11
  }
12
12
  /**
13
- * Image, audio, video and file blocks with their views, upload (the surface's `upload` prop, else the document's asset
14
- * endpoint), drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
13
+ * Image, audio, video and file blocks with their views, upload (`backend.assets`, filed under the surface's `documentId`),
14
+ * drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
15
15
  * recorder. Files import through the pipeline: text and Markdown files are parsed into blocks, everything else is
16
16
  * uploaded; the `wrapImport` stage (identity by default) may wrap the blocks, which are then inserted as they are.
17
17
  * Import problems reach the user through the surface's notice seam (`ctx.notify`).
18
- * Needs a backend (or an `upload` function) only when a file is uploaded.
18
+ * Needs a backend with the `assets` capability.
19
19
  */
20
20
  export declare function mediaFeatureCore<C>(ui: MediaUi<C>): EditorFeature<C>;
21
21
  //#endregion
@@ -1,9 +1,8 @@
1
1
  import { ABILITY } from "../../core/schema/abilities.js";
2
- import { acceptFor } from "./schema.js";
2
+ import { ImageNode, acceptFor, mediaSchemaExtensions } from "./schema.js";
3
3
  import { Media, openAudioRecorder, pickFiles, uploadOf } from "./upload.js";
4
4
  import { fileImport } from "./file-import.js";
5
5
  import { mediaBlocks } from "./node-views.js";
6
- import { Image } from "@tiptap/extension-image";
7
6
  import { FileHandler } from "@tiptap/extension-file-handler";
8
7
  //#region src/features/media/feature.ts
9
8
  const PICKERS = [
@@ -27,21 +26,24 @@ const PICKERS = [
27
26
  }
28
27
  ];
29
28
  /**
30
- * Image, audio, video and file blocks with their views, upload (the surface's `upload` prop, else the document's asset
31
- * endpoint), drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
29
+ * Image, audio, video and file blocks with their views, upload (`backend.assets`, filed under the surface's `documentId`),
30
+ * drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
32
31
  * recorder. Files import through the pipeline: text and Markdown files are parsed into blocks, everything else is
33
32
  * uploaded; the `wrapImport` stage (identity by default) may wrap the blocks, which are then inserted as they are.
34
33
  * Import problems reach the user through the surface's notice seam (`ctx.notify`).
35
- * Needs a backend (or an `upload` function) only when a file is uploaded.
34
+ * Needs a backend with the `assets` capability.
36
35
  */
37
36
  function mediaFeatureCore(ui) {
38
37
  const { AudioBlock, VideoBlock, FileBlock } = mediaBlocks(ui.nodeViews);
39
38
  return {
40
39
  id: "media",
40
+ dimension: "content",
41
+ backend: ["assets"],
42
+ schemaExtensions: mediaSchemaExtensions,
41
43
  extensions: (ctx) => {
42
44
  const { importFiles } = fileImport(ctx.notify, ctx.t);
43
45
  return [
44
- Image,
46
+ ImageNode,
45
47
  AudioBlock,
46
48
  VideoBlock,
47
49
  FileBlock,
@@ -56,35 +58,26 @@ function mediaFeatureCore(ui) {
56
58
  })
57
59
  ];
58
60
  },
59
- toolbar: (ctx) => [{
60
- id: "inserts",
61
- order: 50,
62
- items: [...PICKERS.map((picker, index) => ({
63
- id: picker.id,
64
- order: 20 + index * 10,
65
- icon: picker.icon,
66
- label: ctx.t(picker.labelKey),
67
- ability: ABILITY.media,
68
- run: (editor) => void pickAndImport(editor, acceptFor(picker.kind))
69
- })), {
70
- id: "aufnahme",
71
- order: 50,
72
- icon: "i-lucide-mic",
73
- label: ctx.t("media.toolbar.record"),
74
- ability: ABILITY.record,
75
- run: openAudioRecorder
76
- }]
77
- }],
78
- slashCommands: (ctx) => [...PICKERS.map((picker) => ({
61
+ commands: (ctx) => [...PICKERS.map((picker, index) => ({
79
62
  id: picker.id,
80
- label: ctx.t(picker.labelKey),
63
+ toolbar: {
64
+ group: "inserts",
65
+ order: 20 + index * 10
66
+ },
67
+ slash: {},
81
68
  icon: picker.icon,
69
+ label: ctx.t(picker.labelKey),
82
70
  ability: ABILITY.media,
83
71
  run: (editor) => void pickAndImport(editor, acceptFor(picker.kind), editor.state.selection.from)
84
72
  })), {
85
73
  id: "aufnahme",
86
- label: ctx.t("media.slash.record"),
74
+ toolbar: {
75
+ group: "inserts",
76
+ order: 50
77
+ },
78
+ slash: {},
87
79
  icon: "i-lucide-mic",
80
+ label: ctx.t("media.slash.record"),
88
81
  ability: ABILITY.record,
89
82
  run: openAudioRecorder
90
83
  }],