@ecsplico/editor 2.1.1 → 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 (701) hide show
  1. package/CHANGELOG.md +74 -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/code/CodeSurface.vue.d.ts +3 -3
  41. package/dist/config/index.d.ts +105 -0
  42. package/dist/config/index.js +95 -0
  43. package/dist/config/index.js.map +1 -0
  44. package/dist/core/abilities.js +1 -0
  45. package/dist/core/abilities.js.map +1 -1
  46. package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
  47. package/dist/core/api-routes.js.map +1 -0
  48. package/dist/core/backend-capability.d.ts +7 -0
  49. package/dist/core/backend-capability.js +17 -0
  50. package/dist/core/backend-capability.js.map +1 -0
  51. package/dist/core/backend-types.d.ts +245 -0
  52. package/dist/core/backend-types.js +17 -0
  53. package/dist/core/backend-types.js.map +1 -0
  54. package/dist/core/backend.d.ts +108 -0
  55. package/dist/core/backend.js +14 -0
  56. package/dist/core/backend.js.map +1 -0
  57. package/dist/core/block-convert.d.ts +7 -5
  58. package/dist/core/block-convert.js +28 -5
  59. package/dist/core/block-convert.js.map +1 -1
  60. package/dist/core/command-types.d.ts +89 -0
  61. package/dist/core/commands.d.ts +40 -0
  62. package/dist/core/commands.js +151 -0
  63. package/dist/core/commands.js.map +1 -0
  64. package/dist/core/context.d.ts +17 -20
  65. package/dist/core/context.js +24 -21
  66. package/dist/core/context.js.map +1 -1
  67. package/dist/core/core-feature.d.ts +22 -7
  68. package/dist/core/core-feature.js +51 -18
  69. package/dist/core/core-feature.js.map +1 -1
  70. package/dist/core/define-editor.d.ts +4 -3
  71. package/dist/core/define-editor.js +14 -7
  72. package/dist/core/define-editor.js.map +1 -1
  73. package/dist/core/document-markdown.js +12 -3
  74. package/dist/core/document-markdown.js.map +1 -1
  75. package/dist/core/editor-ui.d.ts +8 -1
  76. package/dist/core/editor-ui.js +9 -8
  77. package/dist/core/editor-ui.js.map +1 -1
  78. package/dist/core/extension-points.d.ts +3 -15
  79. package/dist/core/feature-context.d.ts +8 -7
  80. package/dist/core/feature-context.js +12 -11
  81. package/dist/core/feature-context.js.map +1 -1
  82. package/dist/core/feature.d.ts +35 -19
  83. package/dist/core/feature.js.map +1 -1
  84. package/dist/core/fences.d.ts +2 -1
  85. package/dist/core/fences.js +3 -2
  86. package/dist/core/fences.js.map +1 -1
  87. package/dist/core/fetch-error.d.ts +1 -1
  88. package/dist/core/fetch-error.js +3 -1
  89. package/dist/core/fetch-error.js.map +1 -1
  90. package/dist/core/hash.d.ts +5 -0
  91. package/dist/core/hash.js +11 -0
  92. package/dist/core/hash.js.map +1 -0
  93. package/dist/core/http-backend.d.ts +11 -0
  94. package/dist/core/http-backend.js +195 -0
  95. package/dist/core/http-backend.js.map +1 -0
  96. package/dist/core/index.d.ts +14 -9
  97. package/dist/core/index.js +12 -9
  98. package/dist/core/palette.d.ts +48 -0
  99. package/dist/core/palette.js +95 -0
  100. package/dist/core/palette.js.map +1 -0
  101. package/dist/core/schema/abilities.d.ts +2 -1
  102. package/dist/core/schema/abilities.js +5 -1
  103. package/dist/core/schema/abilities.js.map +1 -1
  104. package/dist/core/schema/index.d.ts +11 -5
  105. package/dist/core/schema/index.js +13 -8
  106. package/dist/core/schema/index.js.map +1 -1
  107. package/dist/core/schema/mark-flags.d.ts +8 -1
  108. package/dist/core/schema/of-features.d.ts +13 -0
  109. package/dist/core/schema/of-features.js +14 -0
  110. package/dist/core/schema/of-features.js.map +1 -0
  111. package/dist/core/slash.d.ts +8 -6
  112. package/dist/core/slash.js +24 -16
  113. package/dist/core/slash.js.map +1 -1
  114. package/dist/core/surface-session.js +13 -5
  115. package/dist/core/surface-session.js.map +1 -1
  116. package/dist/core/toolbar-items.d.ts +10 -4
  117. package/dist/core/toolbar-items.js +269 -195
  118. package/dist/core/toolbar-items.js.map +1 -1
  119. package/dist/core/util.js +11 -0
  120. package/dist/core/util.js.map +1 -0
  121. package/dist/document/DocumentSurface.d.ts +1 -1
  122. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  123. package/dist/features/ai/agent-op-review.d.ts +1 -1
  124. package/dist/features/ai/agent-op-review.js +1 -1
  125. package/dist/features/ai/agent-ops.js +1 -1
  126. package/dist/features/ai/ai-apply.d.ts +2 -2
  127. package/dist/features/ai/ai-apply.js +1 -1
  128. package/dist/features/ai/ai-review.js +1 -1
  129. package/dist/features/ai/ai-sse.d.ts +6 -6
  130. package/dist/features/ai/ai-sse.js +20 -63
  131. package/dist/features/ai/ai-sse.js.map +1 -1
  132. package/dist/features/ai/assistant.d.ts +8 -7
  133. package/dist/features/ai/assistant.js +3 -3
  134. package/dist/features/ai/assistant.js.map +1 -1
  135. package/dist/features/ai/block-actions.d.ts +3 -2
  136. package/dist/features/ai/block-actions.js +3 -2
  137. package/dist/features/ai/block-actions.js.map +1 -1
  138. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  139. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  140. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
  141. package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
  142. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  143. package/dist/features/ai/document-chat-model.d.ts +8 -7
  144. package/dist/features/ai/document-chat-model.js +5 -5
  145. package/dist/features/ai/document-chat-model.js.map +1 -1
  146. package/dist/features/ai/document-chat.js +1 -1
  147. package/dist/features/ai/feature.js +27 -14
  148. package/dist/features/ai/feature.js.map +1 -1
  149. package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
  150. package/dist/features/ai/ghost-autocomplete.js +6 -9
  151. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  152. package/dist/features/ai/index.d.ts +4 -3
  153. package/dist/features/ai/index.js +10 -10
  154. package/dist/features/ai/model-profiles.d.ts +5 -4
  155. package/dist/features/ai/model-profiles.js +2 -2
  156. package/dist/features/ai/model-profiles.js.map +1 -1
  157. package/dist/features/ai/protocol/block-lines.js +1 -1
  158. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  159. package/dist/features/ai/protocol/chat.js +4 -1
  160. package/dist/features/ai/protocol/chat.js.map +1 -1
  161. package/dist/features/ai/protocol/index.d.ts +3 -2
  162. package/dist/features/ai/protocol/models.d.ts +2 -16
  163. package/dist/features/ai/protocol/models.js.map +1 -1
  164. package/dist/features/ai/rate-limit.d.ts +1 -1
  165. package/dist/features/ai/rate-limit.js +3 -1
  166. package/dist/features/ai/rate-limit.js.map +1 -1
  167. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  168. package/dist/features/ai/react/AiSidebar.js +4 -4
  169. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  170. package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
  171. package/dist/features/ai/react/DocumentChatOps.js +1 -1
  172. package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
  173. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  174. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  175. package/dist/features/ai/react/use-ai-assistant.js +3 -3
  176. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  177. package/dist/features/ai/react/use-document-chat.js +2 -2
  178. package/dist/features/ai/react/use-document-chat.js.map +1 -1
  179. package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
  180. package/dist/features/ai/selection-flow.d.ts +1 -1
  181. package/dist/features/ai/selection-flow.js +1 -1
  182. package/dist/features/ai/sidebar-flow.d.ts +1 -1
  183. package/dist/features/ai/sidebar-flow.js +1 -1
  184. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  185. package/dist/features/ai/use-ai-assistant.js +3 -3
  186. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  187. package/dist/features/ai/use-document-chat.js +2 -2
  188. package/dist/features/ai/use-document-chat.js.map +1 -1
  189. package/dist/features/ai/use-model-profiles.d.ts +2 -1
  190. package/dist/features/authors/attribution.d.ts +1 -1
  191. package/dist/features/authors/feature.js +12 -11
  192. package/dist/features/authors/feature.js.map +1 -1
  193. package/dist/features/block-ui/block-actions.d.ts +22 -1
  194. package/dist/features/block-ui/block-actions.js +26 -2
  195. package/dist/features/block-ui/block-actions.js.map +1 -1
  196. package/dist/features/block-ui/components/BlockActionsOverlay.js +33 -4
  197. package/dist/features/block-ui/components/BlockActionsOverlay.js.map +1 -1
  198. package/dist/features/block-ui/components/BlockActionsOverlay.vue.d.ts +4 -0
  199. package/dist/features/block-ui/components/BlockUi.js +1 -1
  200. package/dist/features/block-ui/feature.js +1 -0
  201. package/dist/features/block-ui/feature.js.map +1 -1
  202. package/dist/features/block-ui/index.d.ts +2 -2
  203. package/dist/features/block-ui/index.js +2 -2
  204. package/dist/features/block-ui/react/BlockActionsOverlay.d.ts +4 -0
  205. package/dist/features/block-ui/react/BlockActionsOverlay.js +38 -12
  206. package/dist/features/block-ui/react/BlockActionsOverlay.js.map +1 -1
  207. package/dist/features/block-ui/react/BlockUi.js +1 -1
  208. package/dist/features/collab/anchor-resolver.js +1 -1
  209. package/dist/features/collab/feature.js +2 -0
  210. package/dist/features/collab/feature.js.map +1 -1
  211. package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
  212. package/dist/features/comments/feature.js +12 -11
  213. package/dist/features/comments/feature.js.map +1 -1
  214. package/dist/features/comments/index.d.ts +3 -2
  215. package/dist/features/comments/index.js +1 -1
  216. package/dist/features/comments/margin-layout.d.ts +3 -2
  217. package/dist/features/comments/model.d.ts +3 -67
  218. package/dist/features/comments/model.js +2 -2
  219. package/dist/features/comments/model.js.map +1 -1
  220. package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
  221. package/dist/features/comments/react/use-comments.d.ts +2 -1
  222. package/dist/features/comments/react.d.ts +1 -1
  223. package/dist/features/comments/schema.d.ts +2 -52
  224. package/dist/features/comments/schema.js.map +1 -1
  225. package/dist/features/comments/snapshot-equality.js.map +1 -1
  226. package/dist/features/comments/use-comments.d.ts +3 -2
  227. package/dist/features/comments/vue.d.ts +1 -1
  228. package/dist/features/composites/accepted-children.d.ts +1 -0
  229. package/dist/features/composites/accepted-children.js +16 -1
  230. package/dist/features/composites/accepted-children.js.map +1 -1
  231. package/dist/features/composites/ai-brief.js +31 -0
  232. package/dist/features/composites/ai-brief.js.map +1 -1
  233. package/dist/features/composites/bibliography-command.d.ts +11 -0
  234. package/dist/features/composites/bibliography-form.js +90 -0
  235. package/dist/features/composites/bibliography-form.js.map +1 -0
  236. package/dist/features/composites/bibliography.d.ts +33 -0
  237. package/dist/features/composites/bibliography.js +120 -0
  238. package/dist/features/composites/bibliography.js.map +1 -0
  239. package/dist/features/composites/components/BibliographyEditor.js +261 -0
  240. package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
  241. package/dist/features/composites/components/CompositeChrome.js +75 -26
  242. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  243. package/dist/features/composites/components/CompositeDetails.js +46 -11
  244. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  245. package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
  246. package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
  247. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  248. package/dist/features/composites/components/CompositeInspector.js +17 -5
  249. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  250. package/dist/features/composites/components/CompositeTable.js +13 -4
  251. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  252. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  253. package/dist/features/composites/components/CompositeView.js +11 -1
  254. package/dist/features/composites/components/CompositeView.js.map +1 -1
  255. package/dist/features/composites/components/DerivationBadge.js +103 -0
  256. package/dist/features/composites/components/DerivationBadge.js.map +1 -0
  257. package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
  258. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
  259. package/dist/features/composites/components/ReferenceView.js +122 -0
  260. package/dist/features/composites/components/ReferenceView.js.map +1 -0
  261. package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
  262. package/dist/features/composites/components/SettingsEditingTab.js +1 -1
  263. package/dist/features/composites/convert.js +2 -2
  264. package/dist/features/composites/convert.js.map +1 -1
  265. package/dist/features/composites/derivation.d.ts +61 -0
  266. package/dist/features/composites/derivation.js +98 -0
  267. package/dist/features/composites/derivation.js.map +1 -0
  268. package/dist/features/composites/derived-chrome.js +69 -0
  269. package/dist/features/composites/derived-chrome.js.map +1 -0
  270. package/dist/features/composites/feature.js +159 -109
  271. package/dist/features/composites/feature.js.map +1 -1
  272. package/dist/features/composites/fences.d.ts +4 -3
  273. package/dist/features/composites/fences.js +51 -7
  274. package/dist/features/composites/fences.js.map +1 -1
  275. package/dist/features/composites/index.d.ts +12 -3
  276. package/dist/features/composites/index.js +15 -6
  277. package/dist/features/composites/inspector-model.d.ts +4 -1
  278. package/dist/features/composites/inspector-model.js +8 -4
  279. package/dist/features/composites/inspector-model.js.map +1 -1
  280. package/dist/features/composites/kinds.d.ts +8 -1
  281. package/dist/features/composites/kinds.js +145 -6
  282. package/dist/features/composites/kinds.js.map +1 -1
  283. package/dist/features/composites/legacy.d.ts +4 -1
  284. package/dist/features/composites/legacy.js +56 -1
  285. package/dist/features/composites/legacy.js.map +1 -1
  286. package/dist/features/composites/meta-display.js +40 -1
  287. package/dist/features/composites/meta-display.js.map +1 -1
  288. package/dist/features/composites/meta-keys.d.ts +7 -0
  289. package/dist/features/composites/meta-keys.js +21 -0
  290. package/dist/features/composites/meta-keys.js.map +1 -0
  291. package/dist/features/composites/node-views.d.ts +6 -3
  292. package/dist/features/composites/node-views.js +7 -3
  293. package/dist/features/composites/node-views.js.map +1 -1
  294. package/dist/features/composites/nodes.d.ts +6 -1
  295. package/dist/features/composites/nodes.js +33 -7
  296. package/dist/features/composites/nodes.js.map +1 -1
  297. package/dist/features/composites/react/BibliographyEditor.js +229 -0
  298. package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
  299. package/dist/features/composites/react/CompositeChrome.js +65 -17
  300. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  301. package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
  302. package/dist/features/composites/react/CompositeDetails.js +45 -6
  303. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  304. package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
  305. package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
  306. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  307. package/dist/features/composites/react/CompositeInspector.js +11 -5
  308. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  309. package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
  310. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  311. package/dist/features/composites/react/CompositeTable.js +16 -2
  312. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  313. package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
  314. package/dist/features/composites/react/CompositeView.js +6 -1
  315. package/dist/features/composites/react/CompositeView.js.map +1 -1
  316. package/dist/features/composites/react/DerivationBadge.js +116 -0
  317. package/dist/features/composites/react/DerivationBadge.js.map +1 -0
  318. package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
  319. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
  320. package/dist/features/composites/react/ReferenceView.d.ts +11 -0
  321. package/dist/features/composites/react/ReferenceView.js +115 -0
  322. package/dist/features/composites/react/ReferenceView.js.map +1 -0
  323. package/dist/features/composites/react/SettingsEditingTab.js +2 -2
  324. package/dist/features/composites/react.d.ts +2 -1
  325. package/dist/features/composites/react.js +7 -3
  326. package/dist/features/composites/react.js.map +1 -1
  327. package/dist/features/composites/reference-ai.js +16 -0
  328. package/dist/features/composites/reference-ai.js.map +1 -0
  329. package/dist/features/composites/reference-label.d.ts +19 -0
  330. package/dist/features/composites/reference-label.js +58 -0
  331. package/dist/features/composites/reference-label.js.map +1 -0
  332. package/dist/features/composites/reference-suggestion.d.ts +10 -0
  333. package/dist/features/composites/reference-suggestion.js +41 -0
  334. package/dist/features/composites/reference-suggestion.js.map +1 -0
  335. package/dist/features/composites/reference-text.d.ts +18 -0
  336. package/dist/features/composites/reference-text.js +89 -0
  337. package/dist/features/composites/reference-text.js.map +1 -0
  338. package/dist/features/composites/reference.d.ts +44 -0
  339. package/dist/features/composites/reference.js +148 -0
  340. package/dist/features/composites/reference.js.map +1 -0
  341. package/dist/features/composites/reveal-source.d.ts +32 -0
  342. package/dist/features/composites/reveal-source.js +89 -0
  343. package/dist/features/composites/reveal-source.js.map +1 -0
  344. package/dist/features/composites/rows.d.ts +21 -2
  345. package/dist/features/composites/rows.js +63 -9
  346. package/dist/features/composites/rows.js.map +1 -1
  347. package/dist/features/composites/schema.d.ts +9 -3
  348. package/dist/features/composites/schema.js +11 -4
  349. package/dist/features/composites/schema.js.map +1 -1
  350. package/dist/features/composites/table-model.js +12 -21
  351. package/dist/features/composites/table-model.js.map +1 -1
  352. package/dist/features/composites/vue.d.ts +3 -2
  353. package/dist/features/composites/vue.js +8 -4
  354. package/dist/features/composites/vue.js.map +1 -1
  355. package/dist/features/media/assets.d.ts +2 -0
  356. package/dist/features/media/assets.js +3 -2
  357. package/dist/features/media/assets.js.map +1 -1
  358. package/dist/features/media/components/FileView.js +13 -4
  359. package/dist/features/media/components/FileView.js.map +1 -1
  360. package/dist/features/media/feature.d.ts +3 -3
  361. package/dist/features/media/feature.js +21 -28
  362. package/dist/features/media/feature.js.map +1 -1
  363. package/dist/features/media/file-import.js +2 -2
  364. package/dist/features/media/file-import.js.map +1 -1
  365. package/dist/features/media/index.d.ts +2 -2
  366. package/dist/features/media/index.js +2 -2
  367. package/dist/features/media/react/FileView.js +19 -2
  368. package/dist/features/media/react/FileView.js.map +1 -1
  369. package/dist/features/media/schema.d.ts +8 -0
  370. package/dist/features/media/schema.js +126 -11
  371. package/dist/features/media/schema.js.map +1 -1
  372. package/dist/features/media/upload.d.ts +16 -14
  373. package/dist/features/media/upload.js +13 -19
  374. package/dist/features/media/upload.js.map +1 -1
  375. package/dist/features/mentions/index.d.ts +4 -3
  376. package/dist/features/mentions/index.js +4 -1
  377. package/dist/features/mentions/index.js.map +1 -1
  378. package/dist/features/mentions/mention-text.d.ts +1 -1
  379. package/dist/features/mentions/mention-text.js.map +1 -1
  380. package/dist/features/mentions/mention.d.ts +0 -6
  381. package/dist/features/mentions/mention.js.map +1 -1
  382. package/dist/features/mentions/metadata-defaults.d.ts +2 -2
  383. package/dist/features/mentions/metadata-defaults.js +3 -9
  384. package/dist/features/mentions/metadata-defaults.js.map +1 -1
  385. package/dist/features/mentions/schema.d.ts +2 -1
  386. package/dist/features/mentions/schema.js.map +1 -1
  387. package/dist/features/tables/feature.d.ts +2 -1
  388. package/dist/features/tables/feature.js +90 -70
  389. package/dist/features/tables/feature.js.map +1 -1
  390. package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
  391. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  392. package/dist/features/themes/feature.js +2 -0
  393. package/dist/features/themes/feature.js.map +1 -1
  394. package/dist/features/themes/index.d.ts +2 -1
  395. package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
  396. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  397. package/dist/features/themes/schema.d.ts +2 -6
  398. package/dist/features/themes/schema.js.map +1 -1
  399. package/dist/features/themes/theme-runtime.d.ts +1 -1
  400. package/dist/features/themes/theme-runtime.js +4 -2
  401. package/dist/features/themes/theme-runtime.js.map +1 -1
  402. package/dist/features/transcript/TranscriptSegmentView.js +48 -28
  403. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  404. package/dist/features/transcript/feature.js +21 -19
  405. package/dist/features/transcript/feature.js.map +1 -1
  406. package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
  407. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  408. package/dist/features/transcript/segment-node.d.ts +2 -0
  409. package/dist/features/transcript/segment-node.js +35 -7
  410. package/dist/features/transcript/segment-node.js.map +1 -1
  411. package/dist/i18n/de/composites.js +91 -1
  412. package/dist/i18n/de/composites.js.map +1 -1
  413. package/dist/i18n/de/integrations.js +49 -8
  414. package/dist/i18n/de/integrations.js.map +1 -1
  415. package/dist/i18n/de/media.js +0 -1
  416. package/dist/i18n/de/media.js.map +1 -1
  417. package/dist/i18n/de/palette.js +22 -0
  418. package/dist/i18n/de/palette.js.map +1 -0
  419. package/dist/i18n/de/tables.js +0 -1
  420. package/dist/i18n/de/tables.js.map +1 -1
  421. package/dist/i18n/de/transcript.js +2 -1
  422. package/dist/i18n/de/transcript.js.map +1 -1
  423. package/dist/i18n/de/tree.js +3 -0
  424. package/dist/i18n/de/tree.js.map +1 -1
  425. package/dist/i18n/de.d.ts +157 -11
  426. package/dist/i18n/de.js +3 -1
  427. package/dist/i18n/de.js.map +1 -1
  428. package/dist/i18n/en/composites.js +89 -1
  429. package/dist/i18n/en/composites.js.map +1 -1
  430. package/dist/i18n/en/integrations.js +49 -8
  431. package/dist/i18n/en/integrations.js.map +1 -1
  432. package/dist/i18n/en/media.js +0 -1
  433. package/dist/i18n/en/media.js.map +1 -1
  434. package/dist/i18n/en/palette.js +22 -0
  435. package/dist/i18n/en/palette.js.map +1 -0
  436. package/dist/i18n/en/tables.js +0 -1
  437. package/dist/i18n/en/tables.js.map +1 -1
  438. package/dist/i18n/en/transcript.js +2 -1
  439. package/dist/i18n/en/transcript.js.map +1 -1
  440. package/dist/i18n/en/tree.js +3 -0
  441. package/dist/i18n/en/tree.js.map +1 -1
  442. package/dist/i18n/en.js +3 -1
  443. package/dist/i18n/en.js.map +1 -1
  444. package/dist/index.d.ts +4 -3
  445. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
  446. package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
  447. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
  448. package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
  449. package/dist/integrations/composite-ai/composite-fill.js +97 -56
  450. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  451. package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
  452. package/dist/integrations/composite-ai/composite-summary.js +7 -26
  453. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  454. package/dist/integrations/composite-ai/derive.d.ts +29 -0
  455. package/dist/integrations/composite-ai/derive.js +173 -0
  456. package/dist/integrations/composite-ai/derive.js.map +1 -0
  457. package/dist/integrations/composite-ai/index.d.ts +11 -7
  458. package/dist/integrations/composite-ai/index.js +72 -14
  459. package/dist/integrations/composite-ai/index.js.map +1 -1
  460. package/dist/integrations/composite-ai/rendition.d.ts +45 -0
  461. package/dist/integrations/composite-ai/rendition.js +314 -0
  462. package/dist/integrations/composite-ai/rendition.js.map +1 -0
  463. package/dist/integrations/composite-media/index.d.ts +7 -5
  464. package/dist/integrations/composite-media/index.js +86 -13
  465. package/dist/integrations/composite-media/index.js.map +1 -1
  466. package/dist/integrations/transcript-ai/index.d.ts +1 -1
  467. package/dist/integrations/transcript-ai/index.js +5 -2
  468. package/dist/integrations/transcript-ai/index.js.map +1 -1
  469. package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
  470. package/dist/integrations/transcript-ai/protocol.js +0 -2
  471. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  472. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
  473. package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
  474. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
  475. package/dist/markdown/MarkdownSurface.vue.d.ts +10 -9
  476. package/dist/markdown/MarkdownSurface2.js +3 -6
  477. package/dist/markdown/MarkdownSurface2.js.map +1 -1
  478. package/dist/markdown/document.js +1 -1
  479. package/dist/markdown/source-mode.d.ts +1 -1
  480. package/dist/nuxt/module.d.ts +1402 -54
  481. package/dist/nuxt/module.js +6 -49
  482. package/dist/nuxt/module.js.map +1 -1
  483. package/dist/nuxt/runtime/install-context.d.ts +7 -3
  484. package/dist/nuxt/runtime/install-context.js +14 -8
  485. package/dist/nuxt/runtime/install-context.js.map +1 -1
  486. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
  487. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
  488. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  489. package/dist/presets/compose.d.ts +94 -0
  490. package/dist/presets/compose.js +164 -0
  491. package/dist/presets/compose.js.map +1 -0
  492. package/dist/react/AdvancedEditor.d.ts +15 -15
  493. package/dist/react/AdvancedEditor.js +15 -13
  494. package/dist/react/AdvancedEditor.js.map +1 -1
  495. package/dist/react/EditorCommandPalette.d.ts +13 -0
  496. package/dist/react/EditorCommandPalette.js +118 -0
  497. package/dist/react/EditorCommandPalette.js.map +1 -0
  498. package/dist/react/EditorSettingsModal.d.ts +5 -5
  499. package/dist/react/EditorSettingsModal.js +7 -4
  500. package/dist/react/EditorSettingsModal.js.map +1 -1
  501. package/dist/react/EditorSettingsPanel.d.ts +7 -7
  502. package/dist/react/EditorSettingsPanel.js +8 -5
  503. package/dist/react/EditorSettingsPanel.js.map +1 -1
  504. package/dist/react/EditorSurface.d.ts +7 -7
  505. package/dist/react/EditorSurface.js +10 -6
  506. package/dist/react/EditorSurface.js.map +1 -1
  507. package/dist/react/EditorToolbar.d.ts +4 -6
  508. package/dist/react/EditorToolbar.js +29 -37
  509. package/dist/react/EditorToolbar.js.map +1 -1
  510. package/dist/react/SimpleEditor.d.ts +22 -23
  511. package/dist/react/SimpleEditor.js +21 -7
  512. package/dist/react/SimpleEditor.js.map +1 -1
  513. package/dist/react/advanced.d.ts +4 -3
  514. package/dist/react/advanced.js +2 -2
  515. package/dist/react/compose.d.ts +20 -0
  516. package/dist/react/compose.js +49 -0
  517. package/dist/react/compose.js.map +1 -0
  518. package/dist/react/composed-surface.d.ts +39 -0
  519. package/dist/react/composed-surface.js +71 -0
  520. package/dist/react/composed-surface.js.map +1 -0
  521. package/dist/react/context.js +6 -2
  522. package/dist/react/context.js.map +1 -1
  523. package/dist/react/index.d.ts +2 -1
  524. package/dist/react/index.js +2 -1
  525. package/dist/react/key-labels.js +15 -0
  526. package/dist/react/key-labels.js.map +1 -0
  527. package/dist/react/settings.d.ts +1 -1
  528. package/dist/react/simple.d.ts +2 -2
  529. package/dist/react/ui/command.js +36 -1
  530. package/dist/react/ui/command.js.map +1 -1
  531. package/dist/schema/advanced.d.ts +6 -6
  532. package/dist/schema/advanced.js +26 -29
  533. package/dist/schema/advanced.js.map +1 -1
  534. package/dist/server/ai.d.ts +5 -16
  535. package/dist/server/ai.js.map +1 -1
  536. package/dist/server/auth.d.ts +9 -0
  537. package/dist/server/collab-state.d.ts +1 -1
  538. package/dist/server/collab-state.js.map +1 -1
  539. package/dist/server/collab.d.ts +13 -13
  540. package/dist/server/collab.js +34 -18
  541. package/dist/server/collab.js.map +1 -1
  542. package/dist/server/crossref.js +78 -0
  543. package/dist/server/crossref.js.map +1 -0
  544. package/dist/server/errors.d.ts +1 -1
  545. package/dist/server/errors.js +1 -1
  546. package/dist/server/errors.js.map +1 -1
  547. package/dist/server/extract.js +308 -0
  548. package/dist/server/extract.js.map +1 -0
  549. package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
  550. package/dist/server/h3.js.map +1 -0
  551. package/dist/server/handlers/ai.d.ts +30 -9
  552. package/dist/server/handlers/ai.js +170 -36
  553. package/dist/server/handlers/ai.js.map +1 -1
  554. package/dist/server/handlers/context.d.ts +12 -5
  555. package/dist/server/handlers/context.js +10 -4
  556. package/dist/server/handlers/context.js.map +1 -1
  557. package/dist/server/handlers/editor.d.ts +19 -13
  558. package/dist/server/handlers/editor.js +49 -38
  559. package/dist/server/handlers/editor.js.map +1 -1
  560. package/dist/server/handlers/index.d.ts +4 -4
  561. package/dist/server/handlers/index.js +4 -4
  562. package/dist/server/host.d.ts +16 -25
  563. package/dist/server/host.js +58 -41
  564. package/dist/server/host.js.map +1 -1
  565. package/dist/server/index.d.ts +6 -4
  566. package/dist/server/index.js +3 -2
  567. package/dist/server/pdf.js +97 -0
  568. package/dist/server/pdf.js.map +1 -0
  569. package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
  570. package/dist/server/ports.js +29 -0
  571. package/dist/server/ports.js.map +1 -0
  572. package/dist/server/routes.js +144 -0
  573. package/dist/server/routes.js.map +1 -0
  574. package/dist/settings/index.d.ts +1 -1
  575. package/dist/settings/index.js.map +1 -1
  576. package/dist/styles/composites.css +25 -0
  577. package/dist/tree/DocumentTree.js +56 -29
  578. package/dist/tree/DocumentTree.js.map +1 -1
  579. package/dist/tree/react/DocumentTree.js +23 -0
  580. package/dist/tree/react/DocumentTree.js.map +1 -1
  581. package/dist/tree/tree-host.js +1 -1
  582. package/dist/vue/AdvancedEditor.js +30 -9
  583. package/dist/vue/AdvancedEditor.js.map +1 -1
  584. package/dist/vue/AdvancedEditor.vue.d.ts +32 -12
  585. package/dist/vue/SimpleEditor.js +62 -16
  586. package/dist/vue/SimpleEditor.js.map +1 -1
  587. package/dist/vue/SimpleEditor.vue.d.ts +37 -7
  588. package/dist/vue/advanced.d.ts +3 -2
  589. package/dist/vue/advanced.js +2 -2
  590. package/dist/vue/components/EditorCommandPalette.js +91 -0
  591. package/dist/vue/components/EditorCommandPalette.js.map +1 -0
  592. package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
  593. package/dist/vue/components/EditorSettingsModal.js +10 -6
  594. package/dist/vue/components/EditorSettingsModal.js.map +1 -1
  595. package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
  596. package/dist/vue/components/EditorSettingsPanel.js +10 -6
  597. package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
  598. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
  599. package/dist/vue/components/EditorSurface.js +11 -6
  600. package/dist/vue/components/EditorSurface.js.map +1 -1
  601. package/dist/vue/components/EditorSurface.vue.d.ts +11 -11
  602. package/dist/vue/components/EditorToolbar.js +41 -49
  603. package/dist/vue/components/EditorToolbar.js.map +1 -1
  604. package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
  605. package/dist/vue/compose.d.ts +17 -0
  606. package/dist/vue/compose.js +51 -0
  607. package/dist/vue/compose.js.map +1 -0
  608. package/dist/vue/context-key.d.ts +6 -0
  609. package/dist/vue/context-key.js +6 -0
  610. package/dist/vue/context-key.js.map +1 -0
  611. package/dist/vue/context.d.ts +8 -6
  612. package/dist/vue/context.js +6 -5
  613. package/dist/vue/context.js.map +1 -1
  614. package/dist/vue/index.d.ts +10 -8
  615. package/dist/vue/index.js +10 -8
  616. package/dist/vue/kit/index.d.ts +2 -2
  617. package/dist/vue/kit/nuxt-ui.js +23 -0
  618. package/dist/vue/kit/nuxt-ui.js.map +1 -1
  619. package/dist/vue/kit/types.d.ts +40 -1
  620. package/package.json +30 -16
  621. package/dist/adapters/comments/client.d.ts +0 -64
  622. package/dist/adapters/comments/client.js.map +0 -1
  623. package/dist/adapters/routes.js.map +0 -1
  624. package/dist/core/toolbar.d.ts +0 -74
  625. package/dist/core/toolbar.js +0 -45
  626. package/dist/core/toolbar.js.map +0 -1
  627. package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
  628. package/dist/integrations/composite-ai/document-transcribe.js +0 -89
  629. package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
  630. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
  631. package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
  632. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
  633. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
  634. package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
  635. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
  636. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
  637. package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
  638. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
  639. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
  640. package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
  641. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
  642. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
  643. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
  644. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
  645. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
  646. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
  647. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
  648. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
  649. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
  650. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
  651. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
  652. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
  653. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
  654. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
  655. package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
  656. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
  657. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
  658. package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
  659. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
  660. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
  661. package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
  662. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
  663. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
  664. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
  665. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
  666. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
  667. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
  668. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
  669. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
  670. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
  671. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
  672. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
  673. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
  674. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
  675. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
  676. package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
  677. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
  678. package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
  679. package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
  680. package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
  681. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
  682. package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
  683. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
  684. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
  685. package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
  686. package/dist/presets/advanced.d.ts +0 -33
  687. package/dist/presets/advanced.js +0 -50
  688. package/dist/presets/advanced.js.map +0 -1
  689. package/dist/presets/simple.d.ts +0 -9
  690. package/dist/presets/simple.js +0 -12
  691. package/dist/presets/simple.js.map +0 -1
  692. package/dist/react/advanced-preset.d.ts +0 -11
  693. package/dist/react/advanced-preset.js +0 -31
  694. package/dist/react/advanced-preset.js.map +0 -1
  695. package/dist/vue/advanced-preset.d.ts +0 -13
  696. package/dist/vue/advanced-preset.js +0 -33
  697. package/dist/vue/advanced-preset.js.map +0 -1
  698. package/docs/extending.md +0 -538
  699. package/docs/integration.md +0 -630
  700. package/docs/llms.md +0 -383
  701. package/docs/usage.md +0 -1076
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reveal-source.js","names":[],"sources":["../../../src/features/composites/reveal-source.ts"],"sourcesContent":["// Client-only: show a Baustein's source (the media block of a source-holding kind) at a spot. A time seeks and plays the\n// audio/video; a page number reloads the PDF viewer at that page. Composites must not import media or transcript, so\n// `seekSource` is also registered as an editor command (`SourceReveal`) for the transcript node view to call.\nimport { Extension, type Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport { sourceOf } from './derivation'\nimport { compositeKindsOf } from './nodes'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n sourceReveal: {\n /** Seek and play the media of the source-holding Baustein enclosing `pos` to `seconds`; silent when there is none. */\n seekSource: (pos: number, seconds: number) => ReturnType\n }\n }\n}\n\n/** DOM event the PDF file view listens for; `detail` is the 1-based page to open the viewer at. */\nexport const REVEAL_PAGE_EVENT = 'sk-reveal-page'\n\n/** Where in the source a locator points: a time in seconds (`3:07`) or a PDF page (`S. 12`, `p. 12`, `12`). */\nexport type SourceLocator = { seconds: number } | { page: number }\n\nconst TIME_LOCATOR = /^(\\d+):(\\d{2})$/\nconst PAGE_LOCATOR = /^(?:S\\.|p\\.)?\\s*(\\d+)$/i\n\n/** Parses a reference locator; null for anything that is not a time or a page number. */\nexport function parseLocator(locator: string | null | undefined): SourceLocator | null {\n const text = locator?.trim()\n if (!text) return null\n const time = TIME_LOCATOR.exec(text)\n if (time) return { seconds: Number(time[1]) * 60 + Number(time[2]) }\n const page = PAGE_LOCATOR.exec(text)\n if (page && Number(page[1]) > 0) return { page: Number(page[1]) }\n return null\n}\n\n/** The source block of the composite `composite` at `pos` with its document position, or null. */\nfunction sourceAt(editor: Editor, composite: PMNode, pos: number): { node: PMNode, pos: number } | null {\n const source = sourceOf(composite, compositeKindsOf(editor))\n if (!source) return null\n let sourcePos = pos + 1\n for (let i = 0; i < source.index; i++) sourcePos += composite.child(i).nodeSize\n return { node: source.node, pos: sourcePos }\n}\n\n/** The element of the node view at `pos` that carries the view's own listeners (the framework's wrapper), or null. */\nfunction viewElement(editor: Editor, pos: number): HTMLElement | null {\n const dom = editor.view.nodeDOM(pos)\n if (!(dom instanceof HTMLElement)) return null\n return dom.matches('[data-node-view-wrapper]') ? dom : dom.querySelector<HTMLElement>('[data-node-view-wrapper]') ?? dom\n}\n\nfunction playAt(editor: Editor, sourcePos: number, seconds: number): boolean {\n const dom = editor.view.nodeDOM(sourcePos)\n if (!(dom instanceof HTMLElement)) return false\n const media = dom.matches('audio,video') ? dom as HTMLMediaElement : dom.querySelector<HTMLMediaElement>('audio,video')\n if (!media) return false\n media.currentTime = seconds\n media.play().catch(() => { /* autoplay refused: the seek still happened */ })\n return true\n}\n\n/**\n * Seeks and plays the audio/video of the nearest source-holding Baustein around `pos` (a transcript segment inside a\n * rendition reaches the recording that way). Silent when there is no source or no media element.\n */\nexport function seekSource(editor: Editor, pos: number, seconds: number): void {\n if (!Number.isFinite(seconds) || seconds < 0) return\n const $pos = editor.state.doc.resolve(pos)\n for (let depth = $pos.depth; depth >= 0; depth--) {\n const composite = $pos.node(depth)\n if (composite.type.name !== 'composite') continue\n const source = sourceAt(editor, composite, $pos.before(depth))\n if (!source) continue\n playAt(editor, source.pos, seconds)\n return\n }\n}\n\n/**\n * Shows the source of Baustein `targetId` at `locator`: a `mm:ss` time seeks the media, a page number opens a PDF source\n * at that page; then the source block flashes. Silent when the target or its source is gone.\n */\nexport function revealSource(editor: Editor, targetId: string, locator: string | null): void {\n const target = findBlockById(editor.state.doc, targetId)\n if (!target) return\n const source = sourceAt(editor, target.node, target.pos)\n if (!source) return\n const parsed = parseLocator(locator)\n if (parsed && 'seconds' in parsed) {\n playAt(editor, source.pos, parsed.seconds)\n } else if (parsed && source.node.type.name === 'file' && source.node.attrs.mimeType === 'application/pdf') {\n viewElement(editor, source.pos)?.dispatchEvent(new CustomEvent<number>(REVEAL_PAGE_EVENT, { detail: parsed.page }))\n }\n editor.commands.revealBlock(blockIdOf(source.node) ?? targetId)\n}\n\n/** Registers `seekSource` as an editor command, so features that must not import composites (transcript) can call it. */\nexport const SourceReveal = Extension.create({\n name: 'sourceReveal',\n\n addCommands() {\n return {\n // Does not dispatch: a nested dispatch would leave the command's own transaction stale.\n seekSource: (pos, seconds) => ({ editor }) => {\n seekSource(editor, pos, seconds)\n return true\n },\n }\n },\n})\n"],"mappings":";;;;;;AAmBA,MAAa,oBAAoB;AAKjC,MAAM,eAAe;AACrB,MAAM,eAAe;;AAGrB,SAAgB,aAAa,SAA0D;CACrF,MAAM,OAAO,SAAS,KAAK;CAC3B,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,IAAI,MAAM,OAAO,EAAE,SAAS,OAAO,KAAK,EAAE,IAAI,KAAK,OAAO,KAAK,EAAE,EAAE;CACnE,MAAM,OAAO,aAAa,KAAK,IAAI;CACnC,IAAI,QAAQ,OAAO,KAAK,EAAE,IAAI,GAAG,OAAO,EAAE,MAAM,OAAO,KAAK,EAAE,EAAE;CAChE,OAAO;AACT;;AAGA,SAAS,SAAS,QAAgB,WAAmB,KAAmD;CACtG,MAAM,SAAS,SAAS,WAAW,iBAAiB,MAAM,CAAC;CAC3D,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,YAAY,MAAM;CACtB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,OAAO,KAAK,aAAa,UAAU,MAAM,CAAC,CAAC,CAAC;CACvE,OAAO;EAAE,MAAM,OAAO;EAAM,KAAK;CAAU;AAC7C;;AAGA,SAAS,YAAY,QAAgB,KAAiC;CACpE,MAAM,MAAM,OAAO,KAAK,QAAQ,GAAG;CACnC,IAAI,EAAE,eAAe,cAAc,OAAO;CAC1C,OAAO,IAAI,QAAQ,0BAA0B,IAAI,MAAM,IAAI,cAA2B,0BAA0B,KAAK;AACvH;AAEA,SAAS,OAAO,QAAgB,WAAmB,SAA0B;CAC3E,MAAM,MAAM,OAAO,KAAK,QAAQ,SAAS;CACzC,IAAI,EAAE,eAAe,cAAc,OAAO;CAC1C,MAAM,QAAQ,IAAI,QAAQ,aAAa,IAAI,MAA0B,IAAI,cAAgC,aAAa;CACtH,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,cAAc;CACpB,MAAM,KAAK,CAAC,CAAC,YAAY,CAAkD,CAAC;CAC5E,OAAO;AACT;;;;;AAMA,SAAgB,WAAW,QAAgB,KAAa,SAAuB;CAC7E,IAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,GAAG;CAC9C,MAAM,OAAO,OAAO,MAAM,IAAI,QAAQ,GAAG;CACzC,KAAK,IAAI,QAAQ,KAAK,OAAO,SAAS,GAAG,SAAS;EAChD,MAAM,YAAY,KAAK,KAAK,KAAK;EACjC,IAAI,UAAU,KAAK,SAAS,aAAa;EACzC,MAAM,SAAS,SAAS,QAAQ,WAAW,KAAK,OAAO,KAAK,CAAC;EAC7D,IAAI,CAAC,QAAQ;EACb,OAAO,QAAQ,OAAO,KAAK,OAAO;EAClC;CACF;AACF;;;;;AAMA,SAAgB,aAAa,QAAgB,UAAkB,SAA8B;CAC3F,MAAM,SAAS,cAAc,OAAO,MAAM,KAAK,QAAQ;CACvD,IAAI,CAAC,QAAQ;CACb,MAAM,SAAS,SAAS,QAAQ,OAAO,MAAM,OAAO,GAAG;CACvD,IAAI,CAAC,QAAQ;CACb,MAAM,SAAS,aAAa,OAAO;CACnC,IAAI,UAAU,aAAa,QACzB,OAAO,QAAQ,OAAO,KAAK,OAAO,OAAO;MACpC,IAAI,UAAU,OAAO,KAAK,KAAK,SAAS,UAAU,OAAO,KAAK,MAAM,aAAa,mBACtF,YAAY,QAAQ,OAAO,GAAG,CAAC,EAAE,cAAc,IAAI,YAAoB,mBAAmB,EAAE,QAAQ,OAAO,KAAK,CAAC,CAAC;CAEpH,OAAO,SAAS,YAAY,UAAU,OAAO,IAAI,KAAK,QAAQ;AAChE;;AAGA,MAAa,eAAe,UAAU,OAAO;CAC3C,MAAM;CAEN,cAAc;EACZ,OAAO,EAEL,aAAa,KAAK,aAAa,EAAE,aAAa;GAC5C,WAAW,QAAQ,KAAK,OAAO;GAC/B,OAAO;EACT,EACF;CACF;AACF,CAAC"}
@@ -36,14 +36,33 @@ export interface CompositeMention {
36
36
  label: string;
37
37
  color: string | null;
38
38
  }
39
+ /** A reference to a Baustein: a `reference` chip in the text of block `from`, or a `reference` field of the Baustein `from`. */
40
+ export interface CompositeReference {
41
+ /** Block ID of the referenced Baustein. */
42
+ target: string;
43
+ /** Block ID of the nearest block around the chip (the Baustein itself for a field); `null` when that block has no ID yet. */
44
+ from: string | null;
45
+ /** The chip's locator (`S. 12`, `03:14`); `null` for a field or a chip without one. */
46
+ locator: string | null;
47
+ }
39
48
  /**
40
- * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, and the distinct
41
- * `@mention` chips anywhere in the document. Unknown kinds get the `section` kind's presentation under their own ID.
49
+ * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, the distinct
50
+ * `@mention` chips anywhere in the document and every reference (`reference` chips and `reference` fields) in document
51
+ * order. Unknown kinds get the `section` kind's presentation under their own ID.
42
52
  */
43
53
  export declare function compositeRows(doc: Node$1, kinds: readonly CompositeKind[]): {
44
54
  rows: CompositeRow[];
45
55
  mentions: CompositeMention[];
56
+ references: CompositeReference[];
46
57
  };
58
+ /** A place that refers to a Baustein: the block holding the chip (or the Baustein with the field) and a short text of it. */
59
+ export interface CompositeBacklink {
60
+ from: string;
61
+ locator: string | null;
62
+ excerpt: string;
63
+ }
64
+ /** 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. */
65
+ export declare function compositeBacklinks(doc: Node$1, kinds: readonly CompositeKind[], target: string): CompositeBacklink[];
47
66
  /** The innermost row around document position `pos`, `null` outside of any composite. */
48
67
  export declare function compositeRowAt(rows: readonly CompositeRow[], pos: number): CompositeRow | null;
49
68
  /**
@@ -1,5 +1,6 @@
1
1
  import { blockIdOf, findBlockById } from "../../core/schema/block-ids.js";
2
2
  import { compositeKind } from "./kinds.js";
3
+ import { referenceLabel, referenceTarget } from "./reference-label.js";
3
4
  //#region src/features/composites/rows.ts
4
5
  const COMPOSITE = "composite";
5
6
  /** True when `node`'s content is empty or text only (with any marks, but no inline nodes such as mentions or tags). */
@@ -11,13 +12,15 @@ function isTextOnly(node) {
11
12
  return textOnly;
12
13
  }
13
14
  /**
14
- * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, and the distinct
15
- * `@mention` chips anywhere in the document. Unknown kinds get the `section` kind's presentation under their own ID.
15
+ * Reads every composite of `doc` (also inside quotes and lists) as a row, in document order, the distinct
16
+ * `@mention` chips anywhere in the document and every reference (`reference` chips and `reference` fields) in document
17
+ * order. Unknown kinds get the `section` kind's presentation under their own ID.
16
18
  */
17
19
  function compositeRows(doc, kinds) {
18
20
  const rows = [];
19
21
  const mentions = /* @__PURE__ */ new Map();
20
- function walk(parent, start, depth) {
22
+ const references = [];
23
+ function walk(parent, start, depth, block) {
21
24
  parent.forEach((node, offset) => {
22
25
  const pos = start + offset;
23
26
  if (node.type.name === COMPOSITE) {
@@ -25,7 +28,7 @@ function compositeRows(doc, kinds) {
25
28
  const id = blockIdOf(node);
26
29
  const tags = node.maybeChild(2)?.attrs.tags;
27
30
  const description = node.maybeChild(1);
28
- rows.push({
31
+ const row = {
29
32
  key: id ?? `pos:${pos}`,
30
33
  id,
31
34
  pos,
@@ -40,8 +43,24 @@ function compositeRows(doc, kinds) {
40
43
  meta: node.attrs.meta ?? {},
41
44
  fields: kind.fields,
42
45
  depth
46
+ };
47
+ rows.push(row);
48
+ for (const field of kind.fields) {
49
+ const value = field.type === "reference" ? row.meta[field.key] : void 0;
50
+ if (typeof value === "string" && value) references.push({
51
+ target: value,
52
+ from: id,
53
+ locator: null
54
+ });
55
+ }
56
+ walk(node, pos + 1, depth + 1, id ?? block);
57
+ } else if (node.type.name === "reference") {
58
+ const target = String(node.attrs.target ?? "");
59
+ if (target) references.push({
60
+ target,
61
+ from: block,
62
+ locator: typeof node.attrs.locator === "string" && node.attrs.locator ? node.attrs.locator : null
43
63
  });
44
- walk(node, pos + 1, depth + 1);
45
64
  } else if (node.type.name === "mention") {
46
65
  const mentionId = String(node.attrs.id ?? "");
47
66
  if (mentionId && !mentions.has(mentionId)) mentions.set(mentionId, {
@@ -49,15 +68,50 @@ function compositeRows(doc, kinds) {
49
68
  label: String(node.attrs.label ?? mentionId),
50
69
  color: typeof node.attrs.color === "string" ? node.attrs.color : null
51
70
  });
52
- } else if (!node.isLeaf) walk(node, pos + 1, depth);
71
+ } else if (!node.isLeaf) walk(node, pos + 1, depth, blockIdOf(node) ?? block);
53
72
  });
54
73
  }
55
- walk(doc, 0, 0);
74
+ walk(doc, 0, 0, null);
56
75
  return {
57
76
  rows,
58
- mentions: [...mentions.values()]
77
+ mentions: [...mentions.values()],
78
+ references
59
79
  };
60
80
  }
81
+ const EXCERPT_LENGTH = 40;
82
+ /** 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. */
83
+ function visibleText(doc, kinds, node) {
84
+ return node.textBetween(0, node.content.size, " ", (leaf) => {
85
+ const attrs = leaf.attrs;
86
+ switch (leaf.type.name) {
87
+ case "mention": return `@${String(attrs.label ?? attrs.id ?? "")}`;
88
+ case "tag": return `#${String(attrs.name ?? "")}`;
89
+ case "reference": {
90
+ const target = findBlockById(doc, String(attrs.target ?? ""));
91
+ const label = target?.node.type.name === COMPOSITE ? referenceLabel(referenceTarget(target.node, kinds)) : String(attrs.label ?? "");
92
+ return typeof attrs.locator === "string" && attrs.locator ? `${label}, ${attrs.locator}` : label;
93
+ }
94
+ default: return "";
95
+ }
96
+ });
97
+ }
98
+ /** 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. */
99
+ function compositeBacklinks(doc, kinds, target) {
100
+ const links = [];
101
+ for (const reference of compositeRows(doc, kinds).references) {
102
+ if (reference.target !== target || !reference.from) continue;
103
+ const found = findBlockById(doc, reference.from);
104
+ if (!found) continue;
105
+ const source = found.node.type.name === COMPOSITE ? found.node.firstChild : found.node;
106
+ const text = source ? visibleText(doc, kinds, source).trim() : "";
107
+ links.push({
108
+ from: reference.from,
109
+ locator: reference.locator,
110
+ excerpt: text.length > EXCERPT_LENGTH ? `${text.slice(0, EXCERPT_LENGTH)}…` : text
111
+ });
112
+ }
113
+ return links;
114
+ }
61
115
  /** The innermost row around document position `pos`, `null` outside of any composite. */
62
116
  function compositeRowAt(rows, pos) {
63
117
  let found = null;
@@ -154,6 +208,6 @@ function setCompositeDescription(editor, row, text, expected) {
154
208
  });
155
209
  }
156
210
  //#endregion
157
- export { compositeDescriptionText, compositeMetaWith, compositeOfRow, compositeRowAt, compositeRows, setCompositeDescription, setCompositeMetaField, setCompositeTags };
211
+ export { compositeBacklinks, compositeDescriptionText, compositeMetaWith, compositeOfRow, compositeRowAt, compositeRows, setCompositeDescription, setCompositeMetaField, setCompositeTags };
158
212
 
159
213
  //# sourceMappingURL=rows.js.map
@@ -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"}