@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
@@ -1,7 +1,7 @@
1
1
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
2
2
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
3
3
  import { formatBytes } from "../upload.js";
4
- import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, toDisplayString, unref, withCtx } from "vue";
4
+ import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
5
5
  import { NodeViewWrapper, nodeViewProps } from "@tiptap/vue-3";
6
6
  //#region src/features/media/components/FileView.vue
7
7
  const _hoisted_1 = {
@@ -25,11 +25,20 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
25
25
  setup(__props) {
26
26
  const { Button: UButton, Icon: UIcon } = useUiKit();
27
27
  const { t } = useEditorI18n();
28
+ const reveal = ref(null);
29
+ function onRevealPage(event) {
30
+ const detail = event.detail;
31
+ if (typeof detail === "number" && detail > 0) reveal.value = {
32
+ page: detail,
33
+ n: (reveal.value?.n ?? 0) + 1
34
+ };
35
+ }
28
36
  return (_ctx, _cache) => {
29
37
  return openBlock(), createBlock(unref(NodeViewWrapper), {
30
38
  as: "figure",
31
39
  class: normalizeClass(["media-block flex flex-col gap-1", { "is-selected": _ctx.selected }]),
32
- "data-id": _ctx.node.attrs.id || void 0
40
+ "data-id": _ctx.node.attrs.id || void 0,
41
+ onSkRevealPage: onRevealPage
33
42
  }, {
34
43
  default: withCtx(() => [createElementVNode("div", _hoisted_1, [
35
44
  createVNode(unref(UIcon), {
@@ -56,8 +65,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
56
65
  variant: "soft"
57
66
  }, null, 8, ["href", "label"])
58
67
  ]), _ctx.node.attrs.mimeType === "application/pdf" && _ctx.node.attrs.src ? (openBlock(), createElementBlock("object", {
59
- key: 0,
60
- data: _ctx.node.attrs.src,
68
+ key: reveal.value?.n ?? 0,
69
+ data: reveal.value ? `${_ctx.node.attrs.src}#page=${reveal.value.page}` : _ctx.node.attrs.src,
61
70
  type: "application/pdf",
62
71
  contenteditable: "false",
63
72
  class: "mt-2 h-[480px] w-full rounded-md border border-[var(--sk-border)]"
@@ -1 +1 @@
1
- {"version":3,"file":"FileView.js","names":["selected","node","editor","updateAttributes","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createVNode","_createElementBlock","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/media/components/FileView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { formatBytes } from '../upload'\n\ndefineProps(nodeViewProps)\n\nconst { Button: UButton, Icon: UIcon } = useUiKit()\nconst { t } = useEditorI18n()\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"figure\"\n :class=\"['media-block flex flex-col gap-1', { 'is-selected': selected }]\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"flex items-center gap-2\" contenteditable=\"false\">\n <UIcon\n :name=\"node.attrs.mimeType === 'application/pdf' ? 'i-lucide-file-text' : 'i-lucide-file'\"\n class=\"size-5 shrink-0 text-[var(--sk-muted)]\"\n />\n <input\n :value=\"node.attrs.title\"\n :placeholder=\"t('media.view.fileName')\"\n :aria-label=\"t('media.view.fileName')\"\n autocomplete=\"off\"\n :readonly=\"!editor.isEditable\"\n class=\"min-w-0 flex-1 rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @input=\"updateAttributes({ title: ($event.target as HTMLInputElement).value })\"\n >\n <span v-if=\"node.attrs.size\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)]\">{{ formatBytes(node.attrs.size) }}</span>\n <UButton\n :href=\"node.attrs.src\"\n target=\"_blank\"\n :label=\"t('media.view.open')\"\n icon=\"i-lucide-external-link\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"soft\"\n />\n </div>\n <object\n v-if=\"node.attrs.mimeType === 'application/pdf' && node.attrs.src\"\n :data=\"node.attrs.src\"\n type=\"application/pdf\"\n contenteditable=\"false\"\n class=\"mt-2 h-[480px] w-full rounded-md border border-[var(--sk-border)]\"\n />\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EAQA,MAAM,EAAE,QAAQ,SAAS,MAAM,UAAU,SAAS;EAClD,MAAM,EAAE,MAAM,cAAc;;GAI1B,OAAAI,UAAA,GAAAC,YAqCkBC,MAAA,eAAA,GAAA;IApChB,IAAG;IACF,OAAKC,eAAA,CAAA,mCAAA,EAAA,eAAuDP,KAAAA,SAAQ,CAAA,CAAA;IACpE,WAASC,KAAAA,KAAK,MAAM,MAAM,KAAA;;IAE3B,SAAAO,cAwBM,CAxBNC,mBAwBM,OAxBN,YAwBM;KAvBJC,YAGEJ,MAAA,KAAA,GAAA;MAFC,MAAML,KAAAA,KAAK,MAAM,aAAQ,oBAAA,uBAAA;MAC1B,OAAM;;KAERQ,mBAQC,SAAA;MAPE,OAAOR,KAAAA,KAAK,MAAM;MAClB,aAAaK,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAYA,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAa;MACZ,UAAQ,CAAGJ,KAAAA,OAAO;MACnB,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAEC,KAAAA,iBAAgB,EAAA,OAAW,OAAO,OAA4B,MAAK,CAAA;;KAEjEF,KAAAA,KAAK,MAAM,QAAvBG,UAAA,GAAAO,mBAA0H,QAA1H,YAA0HC,gBAAtCN,MAAA,WAAA,CAAW,CAACL,KAAAA,KAAK,MAAM,IAAI,CAAA,GAAA,CAAA,KAAAY,mBAAA,QAAA,IAAA;KAC/GH,YAQEJ,MAAA,OAAA,GAAA;MAPC,MAAML,KAAAA,KAAK,MAAM;MAClB,QAAO;MACN,OAAOK,MAAA,CAAA,CAAC,CAAA,iBAAA;MACT,MAAK;MACL,MAAK;MACL,OAAM;MACN,SAAQ;;IAIJL,CAAAA,GAAAA,KAAAA,KAAK,MAAM,aAAQ,qBAA0BA,KAAAA,KAAK,MAAM,OADhEG,UAAA,GAAAO,mBAME,UAAA;;KAJC,MAAMV,KAAAA,KAAK,MAAM;KAClB,MAAK;KACL,iBAAgB;KAChB,OAAM"}
1
+ {"version":3,"file":"FileView.js","names":["selected","node","editor","updateAttributes","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createVNode","_createElementBlock","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/media/components/FileView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { ref } from 'vue'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { formatBytes } from '../upload'\n\ndefineProps(nodeViewProps)\n\nconst { Button: UButton, Icon: UIcon } = useUiKit()\nconst { t } = useEditorI18n()\n\n// Page the PDF viewer was asked to open at (`revealSource`, locator „S. 3“). `n` counts the requests: the viewer is\n// remounted on every one, so asking for the same page again jumps back after the user scrolled away.\nconst reveal = ref<{ page: number, n: number } | null>(null)\nfunction onRevealPage(event: Event) {\n const detail = (event as CustomEvent<unknown>).detail\n if (typeof detail === 'number' && detail > 0) reveal.value = { page: detail, n: (reveal.value?.n ?? 0) + 1 }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n as=\"figure\"\n :class=\"['media-block flex flex-col gap-1', { 'is-selected': selected }]\"\n :data-id=\"node.attrs.id || undefined\"\n @sk-reveal-page=\"onRevealPage\"\n >\n <div class=\"flex items-center gap-2\" contenteditable=\"false\">\n <UIcon\n :name=\"node.attrs.mimeType === 'application/pdf' ? 'i-lucide-file-text' : 'i-lucide-file'\"\n class=\"size-5 shrink-0 text-[var(--sk-muted)]\"\n />\n <input\n :value=\"node.attrs.title\"\n :placeholder=\"t('media.view.fileName')\"\n :aria-label=\"t('media.view.fileName')\"\n autocomplete=\"off\"\n :readonly=\"!editor.isEditable\"\n class=\"min-w-0 flex-1 rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @input=\"updateAttributes({ title: ($event.target as HTMLInputElement).value })\"\n >\n <span v-if=\"node.attrs.size\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)]\">{{ formatBytes(node.attrs.size) }}</span>\n <UButton\n :href=\"node.attrs.src\"\n target=\"_blank\"\n :label=\"t('media.view.open')\"\n icon=\"i-lucide-external-link\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"soft\"\n />\n </div>\n <object\n v-if=\"node.attrs.mimeType === 'application/pdf' && node.attrs.src\"\n :key=\"reveal?.n ?? 0\"\n :data=\"reveal ? `${node.attrs.src}#page=${reveal.page}` : node.attrs.src\"\n type=\"application/pdf\"\n contenteditable=\"false\"\n class=\"mt-2 h-[480px] w-full rounded-md border border-[var(--sk-border)]\"\n />\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EASA,MAAM,EAAE,QAAQ,SAAS,MAAM,UAAU,SAAS;EAClD,MAAM,EAAE,MAAM,cAAc;EAI5B,MAAM,SAAS,IAAwC,IAAI;EAC3D,SAAS,aAAa,OAAc;GAClC,MAAM,SAAU,MAA+B;GAC/C,IAAI,OAAO,WAAW,YAAY,SAAS,GAAG,OAAO,QAAQ;IAAE,MAAM;IAAQ,IAAI,OAAO,OAAO,KAAK,KAAK;GAAE;EAC7G;;GAIE,OAAAI,UAAA,GAAAC,YAuCkBC,MAAA,eAAA,GAAA;IAtChB,IAAG;IACF,OAAKC,eAAA,CAAA,mCAAA,EAAA,eAAuDP,KAAAA,SAAQ,CAAA,CAAA;IACpE,WAASC,KAAAA,KAAK,MAAM,MAAM,KAAA;IAC1B,gBAAgB;;IAEjB,SAAAO,cAwBM,CAxBNC,mBAwBM,OAxBN,YAwBM;KAvBJC,YAGEJ,MAAA,KAAA,GAAA;MAFC,MAAML,KAAAA,KAAK,MAAM,aAAQ,oBAAA,uBAAA;MAC1B,OAAM;;KAERQ,mBAQC,SAAA;MAPE,OAAOR,KAAAA,KAAK,MAAM;MAClB,aAAaK,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAYA,MAAA,CAAA,CAAC,CAAA,qBAAA;MACd,cAAa;MACZ,UAAQ,CAAGJ,KAAAA,OAAO;MACnB,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAEC,KAAAA,iBAAgB,EAAA,OAAW,OAAO,OAA4B,MAAK,CAAA;;KAEjEF,KAAAA,KAAK,MAAM,QAAvBG,UAAA,GAAAO,mBAA0H,QAA1H,YAA0HC,gBAAtCN,MAAA,WAAA,CAAW,CAACL,KAAAA,KAAK,MAAM,IAAI,CAAA,GAAA,CAAA,KAAAY,mBAAA,QAAA,IAAA;KAC/GH,YAQEJ,MAAA,OAAA,GAAA;MAPC,MAAML,KAAAA,KAAK,MAAM;MAClB,QAAO;MACN,OAAOK,MAAA,CAAA,CAAC,CAAA,iBAAA;MACT,MAAK;MACL,MAAK;MACL,OAAM;MACN,SAAQ;;IAIJL,CAAAA,GAAAA,KAAAA,KAAK,MAAM,aAAQ,qBAA0BA,KAAAA,KAAK,MAAM,OADhEG,UAAA,GAAAO,mBAOE,UAAA;KALC,KAAK,OAAA,OAAQ,KAAC;KACd,MAAM,OAAA,QAAM,GAAMV,KAAAA,KAAK,MAAM,IAAG,QAAS,OAAA,MAAO,SAASA,KAAAA,KAAK,MAAM;KACrE,MAAK;KACL,iBAAgB;KAChB,OAAM"}
@@ -10,12 +10,12 @@ export interface MediaUi<C> {
10
10
  nodeViews: MediaNodeViews;
11
11
  }
12
12
  /**
13
- * Image, audio, video and file blocks with their views, upload (the surface's `upload` prop, else the document's asset
14
- * endpoint), drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
13
+ * Image, audio, video and file blocks with their views, upload (`backend.assets`, filed under the surface's `documentId`),
14
+ * drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
15
15
  * recorder. Files import through the pipeline: text and Markdown files are parsed into blocks, everything else is
16
16
  * uploaded; the `wrapImport` stage (identity by default) may wrap the blocks, which are then inserted as they are.
17
17
  * Import problems reach the user through the surface's notice seam (`ctx.notify`).
18
- * Needs a backend (or an `upload` function) only when a file is uploaded.
18
+ * Needs a backend with the `assets` capability.
19
19
  */
20
20
  export declare function mediaFeatureCore<C>(ui: MediaUi<C>): EditorFeature<C>;
21
21
  //#endregion
@@ -1,9 +1,8 @@
1
1
  import { ABILITY } from "../../core/schema/abilities.js";
2
- import { acceptFor } from "./schema.js";
2
+ import { ImageNode, acceptFor, mediaSchemaExtensions } from "./schema.js";
3
3
  import { Media, openAudioRecorder, pickFiles, uploadOf } from "./upload.js";
4
4
  import { fileImport } from "./file-import.js";
5
5
  import { mediaBlocks } from "./node-views.js";
6
- import { Image } from "@tiptap/extension-image";
7
6
  import { FileHandler } from "@tiptap/extension-file-handler";
8
7
  //#region src/features/media/feature.ts
9
8
  const PICKERS = [
@@ -27,21 +26,24 @@ const PICKERS = [
27
26
  }
28
27
  ];
29
28
  /**
30
- * Image, audio, video and file blocks with their views, upload (the surface's `upload` prop, else the document's asset
31
- * endpoint), drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
29
+ * Image, audio, video and file blocks with their views, upload (`backend.assets`, filed under the surface's `documentId`),
30
+ * drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or
32
31
  * recorder. Files import through the pipeline: text and Markdown files are parsed into blocks, everything else is
33
32
  * uploaded; the `wrapImport` stage (identity by default) may wrap the blocks, which are then inserted as they are.
34
33
  * Import problems reach the user through the surface's notice seam (`ctx.notify`).
35
- * Needs a backend (or an `upload` function) only when a file is uploaded.
34
+ * Needs a backend with the `assets` capability.
36
35
  */
37
36
  function mediaFeatureCore(ui) {
38
37
  const { AudioBlock, VideoBlock, FileBlock } = mediaBlocks(ui.nodeViews);
39
38
  return {
40
39
  id: "media",
40
+ dimension: "content",
41
+ backend: ["assets"],
42
+ schemaExtensions: mediaSchemaExtensions,
41
43
  extensions: (ctx) => {
42
44
  const { importFiles } = fileImport(ctx.notify, ctx.t);
43
45
  return [
44
- Image,
46
+ ImageNode,
45
47
  AudioBlock,
46
48
  VideoBlock,
47
49
  FileBlock,
@@ -56,35 +58,26 @@ function mediaFeatureCore(ui) {
56
58
  })
57
59
  ];
58
60
  },
59
- toolbar: (ctx) => [{
60
- id: "inserts",
61
- order: 50,
62
- items: [...PICKERS.map((picker, index) => ({
63
- id: picker.id,
64
- order: 20 + index * 10,
65
- icon: picker.icon,
66
- label: ctx.t(picker.labelKey),
67
- ability: ABILITY.media,
68
- run: (editor) => void pickAndImport(editor, acceptFor(picker.kind))
69
- })), {
70
- id: "aufnahme",
71
- order: 50,
72
- icon: "i-lucide-mic",
73
- label: ctx.t("media.toolbar.record"),
74
- ability: ABILITY.record,
75
- run: openAudioRecorder
76
- }]
77
- }],
78
- slashCommands: (ctx) => [...PICKERS.map((picker) => ({
61
+ commands: (ctx) => [...PICKERS.map((picker, index) => ({
79
62
  id: picker.id,
80
- label: ctx.t(picker.labelKey),
63
+ toolbar: {
64
+ group: "inserts",
65
+ order: 20 + index * 10
66
+ },
67
+ slash: {},
81
68
  icon: picker.icon,
69
+ label: ctx.t(picker.labelKey),
82
70
  ability: ABILITY.media,
83
71
  run: (editor) => void pickAndImport(editor, acceptFor(picker.kind), editor.state.selection.from)
84
72
  })), {
85
73
  id: "aufnahme",
86
- label: ctx.t("media.slash.record"),
74
+ toolbar: {
75
+ group: "inserts",
76
+ order: 50
77
+ },
78
+ slash: {},
87
79
  icon: "i-lucide-mic",
80
+ label: ctx.t("media.slash.record"),
88
81
  ability: ABILITY.record,
89
82
  run: openAudioRecorder
90
83
  }],
@@ -1 +1 @@
1
- {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/media/feature.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { FileHandler } from '@tiptap/extension-file-handler'\nimport { Image } from '@tiptap/extension-image'\nimport type { EditorFeature, SlashCommandItem, ToolbarItem } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport type { MessageKey } from '../../i18n'\nimport { fileImport } from './file-import'\nimport { type MediaNodeViews, mediaBlocks } from './node-views'\nimport { acceptFor, type MediaKind } from './schema'\nimport { Media, openAudioRecorder, pickFiles, uploadOf } from './upload'\n\n/** What a framework binding supplies to the media feature. */\nexport interface MediaUi<C> {\n /** The „Audio aufnehmen“ modal; mounted as an overlay, opened by `openAudioRecorder(editor)`. */\n recorderModal: C\n /** The audio, video and file block views of the framework. */\n nodeViews: MediaNodeViews\n}\n\nconst PICKERS: readonly { id: string, labelKey: MessageKey, icon: string, kind: MediaKind }[] = [\n { id: 'bild', labelKey: 'media.picker.image', icon: 'i-lucide-image', kind: 'image' },\n { id: 'audio', labelKey: 'media.picker.audio', icon: 'i-lucide-file-audio', kind: 'audio' },\n { id: 'video', labelKey: 'media.picker.video', icon: 'i-lucide-video', kind: 'video' },\n]\n\n/**\n * Image, audio, video and file blocks with their views, upload (the surface's `upload` prop, else the document's asset\n * endpoint), drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or\n * recorder. Files import through the pipeline: text and Markdown files are parsed into blocks, everything else is\n * uploaded; the `wrapImport` stage (identity by default) may wrap the blocks, which are then inserted as they are.\n * Import problems reach the user through the surface's notice seam (`ctx.notify`).\n * Needs a backend (or an `upload` function) only when a file is uploaded.\n */\nexport function mediaFeatureCore<C>(ui: MediaUi<C>): EditorFeature<C> {\n const { AudioBlock, VideoBlock, FileBlock } = mediaBlocks(ui.nodeViews)\n return {\n id: 'media',\n extensions: (ctx) => {\n const { importFiles } = fileImport(ctx.notify, ctx.t)\n return [\n Image,\n AudioBlock,\n VideoBlock,\n FileBlock,\n Media.configure({ upload: uploadOf(ctx), importFiles }),\n FileHandler.configure({\n consumePasteEvent: true,\n onDrop: (editor, files, pos) => importFiles(editor, files, pos),\n onPaste: (editor, files) => importFiles(editor, files),\n }),\n ]\n },\n toolbar: ctx => [{\n id: 'inserts',\n order: 50,\n items: [\n ...PICKERS.map((picker, index): ToolbarItem<C> => ({\n id: picker.id,\n order: 20 + index * 10,\n icon: picker.icon,\n label: ctx.t(picker.labelKey),\n ability: ABILITY.media,\n run: editor => void pickAndImport(editor, acceptFor(picker.kind)),\n })),\n { id: 'aufnahme', order: 50, icon: 'i-lucide-mic', label: ctx.t('media.toolbar.record'), ability: ABILITY.record, run: openAudioRecorder },\n ],\n }],\n slashCommands: (ctx): SlashCommandItem[] => [\n ...PICKERS.map((picker): SlashCommandItem => ({\n id: picker.id,\n label: ctx.t(picker.labelKey),\n icon: picker.icon,\n ability: ABILITY.media,\n run: editor => void pickAndImport(editor, acceptFor(picker.kind), editor.state.selection.from),\n })),\n { id: 'aufnahme', label: ctx.t('media.slash.record'), icon: 'i-lucide-mic', ability: ABILITY.record, run: openAudioRecorder },\n ],\n overlays: [ui.recorderModal],\n }\n}\n\nasync function pickAndImport(editor: Editor, accept: string, pos?: number): Promise<void> {\n const files = await pickFiles(accept)\n if (files.length && !editor.isDestroyed) await editor.storage.media.importFiles(editor, files, pos)\n}\n"],"mappings":";;;;;;;;AAmBA,MAAM,UAA0F;CAC9F;EAAE,IAAI;EAAQ,UAAU;EAAsB,MAAM;EAAkB,MAAM;CAAQ;CACpF;EAAE,IAAI;EAAS,UAAU;EAAsB,MAAM;EAAuB,MAAM;CAAQ;CAC1F;EAAE,IAAI;EAAS,UAAU;EAAsB,MAAM;EAAkB,MAAM;CAAQ;AACvF;;;;;;;;;AAUA,SAAgB,iBAAoB,IAAkC;CACpE,MAAM,EAAE,YAAY,YAAY,cAAc,YAAY,GAAG,SAAS;CACtE,OAAO;EACL,IAAI;EACJ,aAAa,QAAQ;GACnB,MAAM,EAAE,gBAAgB,WAAW,IAAI,QAAQ,IAAI,CAAC;GACpD,OAAO;IACL;IACA;IACA;IACA;IACA,MAAM,UAAU;KAAE,QAAQ,SAAS,GAAG;KAAG;IAAY,CAAC;IACtD,YAAY,UAAU;KACpB,mBAAmB;KACnB,SAAS,QAAQ,OAAO,QAAQ,YAAY,QAAQ,OAAO,GAAG;KAC9D,UAAU,QAAQ,UAAU,YAAY,QAAQ,KAAK;IACvD,CAAC;GACH;EACF;EACA,UAAS,QAAO,CAAC;GACf,IAAI;GACJ,OAAO;GACP,OAAO,CACL,GAAG,QAAQ,KAAK,QAAQ,WAA2B;IACjD,IAAI,OAAO;IACX,OAAO,KAAK,QAAQ;IACpB,MAAM,OAAO;IACb,OAAO,IAAI,EAAE,OAAO,QAAQ;IAC5B,SAAS,QAAQ;IACjB,MAAK,WAAU,KAAK,cAAc,QAAQ,UAAU,OAAO,IAAI,CAAC;GAClE,EAAE,GACF;IAAE,IAAI;IAAY,OAAO;IAAI,MAAM;IAAgB,OAAO,IAAI,EAAE,sBAAsB;IAAG,SAAS,QAAQ;IAAQ,KAAK;GAAkB,CAC3I;EACF,CAAC;EACD,gBAAgB,QAA4B,CAC1C,GAAG,QAAQ,KAAK,YAA8B;GAC5C,IAAI,OAAO;GACX,OAAO,IAAI,EAAE,OAAO,QAAQ;GAC5B,MAAM,OAAO;GACb,SAAS,QAAQ;GACjB,MAAK,WAAU,KAAK,cAAc,QAAQ,UAAU,OAAO,IAAI,GAAG,OAAO,MAAM,UAAU,IAAI;EAC/F,EAAE,GACF;GAAE,IAAI;GAAY,OAAO,IAAI,EAAE,oBAAoB;GAAG,MAAM;GAAgB,SAAS,QAAQ;GAAQ,KAAK;EAAkB,CAC9H;EACA,UAAU,CAAC,GAAG,aAAa;CAC7B;AACF;AAEA,eAAe,cAAc,QAAgB,QAAgB,KAA6B;CACxF,MAAM,QAAQ,MAAM,UAAU,MAAM;CACpC,IAAI,MAAM,UAAU,CAAC,OAAO,aAAa,MAAM,OAAO,QAAQ,MAAM,YAAY,QAAQ,OAAO,GAAG;AACpG"}
1
+ {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/media/feature.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { FileHandler } from '@tiptap/extension-file-handler'\nimport type { CommandDef, EditorFeature } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport type { MessageKey } from '../../i18n'\nimport { fileImport } from './file-import'\nimport { type MediaNodeViews, mediaBlocks } from './node-views'\nimport { acceptFor, ImageNode, mediaSchemaExtensions, type MediaKind } from './schema'\nimport { Media, openAudioRecorder, pickFiles, uploadOf } from './upload'\n\n/** What a framework binding supplies to the media feature. */\nexport interface MediaUi<C> {\n /** The „Audio aufnehmen“ modal; mounted as an overlay, opened by `openAudioRecorder(editor)`. */\n recorderModal: C\n /** The audio, video and file block views of the framework. */\n nodeViews: MediaNodeViews\n}\n\nconst PICKERS: readonly { id: string, labelKey: MessageKey, icon: string, kind: MediaKind }[] = [\n { id: 'bild', labelKey: 'media.picker.image', icon: 'i-lucide-image', kind: 'image' },\n { id: 'audio', labelKey: 'media.picker.audio', icon: 'i-lucide-file-audio', kind: 'audio' },\n { id: 'video', labelKey: 'media.picker.video', icon: 'i-lucide-video', kind: 'video' },\n]\n\n/**\n * Image, audio, video and file blocks with their views, upload (`backend.assets`, filed under the surface's `documentId`),\n * drop/paste of files, the audio recorder, and the toolbar and `/` entries that open the file picker or\n * recorder. Files import through the pipeline: text and Markdown files are parsed into blocks, everything else is\n * uploaded; the `wrapImport` stage (identity by default) may wrap the blocks, which are then inserted as they are.\n * Import problems reach the user through the surface's notice seam (`ctx.notify`).\n * Needs a backend with the `assets` capability.\n */\nexport function mediaFeatureCore<C>(ui: MediaUi<C>): EditorFeature<C> {\n const { AudioBlock, VideoBlock, FileBlock } = mediaBlocks(ui.nodeViews)\n return {\n id: 'media',\n dimension: 'content',\n backend: ['assets'],\n schemaExtensions: mediaSchemaExtensions,\n extensions: (ctx) => {\n const { importFiles } = fileImport(ctx.notify, ctx.t)\n return [\n ImageNode,\n AudioBlock,\n VideoBlock,\n FileBlock,\n Media.configure({ upload: uploadOf(ctx), importFiles }),\n FileHandler.configure({\n consumePasteEvent: true,\n onDrop: (editor, files, pos) => importFiles(editor, files, pos),\n onPaste: (editor, files) => importFiles(editor, files),\n }),\n ]\n },\n commands: (ctx): CommandDef<C>[] => [\n ...PICKERS.map((picker, index): CommandDef<C> => ({\n id: picker.id,\n toolbar: { group: 'inserts', order: 20 + index * 10 },\n slash: {},\n icon: picker.icon,\n label: ctx.t(picker.labelKey),\n ability: ABILITY.media,\n // The caret at the click: the picker takes a while and the document may change meanwhile.\n run: editor => void pickAndImport(editor, acceptFor(picker.kind), editor.state.selection.from),\n })),\n { id: 'aufnahme', toolbar: { group: 'inserts', order: 50 }, slash: {}, icon: 'i-lucide-mic', label: ctx.t('media.slash.record'), ability: ABILITY.record, run: openAudioRecorder },\n ],\n overlays: [ui.recorderModal],\n }\n}\n\nasync function pickAndImport(editor: Editor, accept: string, pos?: number): Promise<void> {\n const files = await pickFiles(accept)\n if (files.length && !editor.isDestroyed) await editor.storage.media.importFiles(editor, files, pos)\n}\n"],"mappings":";;;;;;;AAkBA,MAAM,UAA0F;CAC9F;EAAE,IAAI;EAAQ,UAAU;EAAsB,MAAM;EAAkB,MAAM;CAAQ;CACpF;EAAE,IAAI;EAAS,UAAU;EAAsB,MAAM;EAAuB,MAAM;CAAQ;CAC1F;EAAE,IAAI;EAAS,UAAU;EAAsB,MAAM;EAAkB,MAAM;CAAQ;AACvF;;;;;;;;;AAUA,SAAgB,iBAAoB,IAAkC;CACpE,MAAM,EAAE,YAAY,YAAY,cAAc,YAAY,GAAG,SAAS;CACtE,OAAO;EACL,IAAI;EACJ,WAAW;EACX,SAAS,CAAC,QAAQ;EAClB,kBAAkB;EAClB,aAAa,QAAQ;GACnB,MAAM,EAAE,gBAAgB,WAAW,IAAI,QAAQ,IAAI,CAAC;GACpD,OAAO;IACL;IACA;IACA;IACA;IACA,MAAM,UAAU;KAAE,QAAQ,SAAS,GAAG;KAAG;IAAY,CAAC;IACtD,YAAY,UAAU;KACpB,mBAAmB;KACnB,SAAS,QAAQ,OAAO,QAAQ,YAAY,QAAQ,OAAO,GAAG;KAC9D,UAAU,QAAQ,UAAU,YAAY,QAAQ,KAAK;IACvD,CAAC;GACH;EACF;EACA,WAAW,QAAyB,CAClC,GAAG,QAAQ,KAAK,QAAQ,WAA0B;GAChD,IAAI,OAAO;GACX,SAAS;IAAE,OAAO;IAAW,OAAO,KAAK,QAAQ;GAAG;GACpD,OAAO,CAAC;GACR,MAAM,OAAO;GACb,OAAO,IAAI,EAAE,OAAO,QAAQ;GAC5B,SAAS,QAAQ;GAEjB,MAAK,WAAU,KAAK,cAAc,QAAQ,UAAU,OAAO,IAAI,GAAG,OAAO,MAAM,UAAU,IAAI;EAC/F,EAAE,GACF;GAAE,IAAI;GAAY,SAAS;IAAE,OAAO;IAAW,OAAO;GAAG;GAAG,OAAO,CAAC;GAAG,MAAM;GAAgB,OAAO,IAAI,EAAE,oBAAoB;GAAG,SAAS,QAAQ;GAAQ,KAAK;EAAkB,CACnL;EACA,UAAU,CAAC,GAAG,aAAa;CAC7B;AACF;AAEA,eAAe,cAAc,QAAgB,QAAgB,KAA6B;CACxF,MAAM,QAAQ,MAAM,UAAU,MAAM;CACpC,IAAI,MAAM,UAAU,CAAC,OAAO,aAAa,MAAM,OAAO,QAAQ,MAAM,YAAY,QAAQ,OAAO,GAAG;AACpG"}
@@ -19,8 +19,8 @@ async function blocksOf(editor, file) {
19
19
  const blocks = structureBlocks(editor, parseBlocks(editor, await file.text()).blocks);
20
20
  return blocks.length ? blocks : [{ type: "paragraph" }];
21
21
  }
22
- const { url } = await editor.storage.media.upload(file);
23
- return [mediaNodeFor(file, url) ?? fileNodeFor(file, url)];
22
+ const { url, sha256 } = await editor.storage.media.upload(file);
23
+ return [mediaNodeFor(file, url, sha256) ?? fileNodeFor(file, url, sha256)];
24
24
  }
25
25
  /**
26
26
  * Imports files into the editor. Text and Markdown files are parsed through the editor's block pipeline into blocks;
@@ -1 +1 @@
1
- {"version":3,"file":"file-import.js","names":[],"sources":["../../../src/features/media/file-import.ts"],"sourcesContent":["import type { Editor, JSONContent } from '@tiptap/core'\nimport type { Translate } from '../../i18n'\nimport { captureRange, fetchErrorMessage, type Notifier, parseBlocks, releaseRanges, resolveRange, structureBlocks, wrapImportBlocks } from '../../core'\nimport { mediaMime } from './schema'\nimport { fileNodeFor, type ImportFilesFn, mediaNodeFor } from './upload'\n\n/** Text files are read into the document, bigger ones are refused. */\nexport const MAX_TEXT_BYTES = 2 * 1024 * 1024\n/** Browsers report `''` (or something else) as the type of `.md` files, so the name decides too. */\nconst TEXT_EXTENSION = /\\.(md|markdown|txt)$/i\n\n/** Whether `file` is imported as text (parsed Markdown) rather than uploaded. */\nexport function isTextFile(file: File): boolean {\n const mime = mediaMime(file.type)\n return mime === 'text/markdown' || mime === 'text/plain' || TEXT_EXTENSION.test(file.name)\n}\n\n/** Blocks for `file`: its text parsed through the editor's block pipeline (parse, normalize, segment, structure), or the uploaded media/file block. */\nasync function blocksOf(editor: Editor, file: File): Promise<JSONContent[]> {\n if (isTextFile(file)) {\n const blocks = structureBlocks(editor, parseBlocks(editor, await file.text()).blocks)\n return blocks.length ? blocks : [{ type: 'paragraph' }]\n }\n const { url } = await editor.storage.media.upload(file)\n return [mediaNodeFor(file, url) ?? fileNodeFor(file, url)]\n}\n\n/**\n * Imports files into the editor. Text and Markdown files are parsed through the editor's block pipeline into blocks;\n * all other files are uploaded through `editor.storage.media.upload` and appear as their media or file block. The\n * blocks go through the pipeline's `wrapImport` stage (identity unless an installed feature wraps imports) and are\n * inserted as they are. Problems (an oversized text file, a failed upload) are reported through `notify` and the file\n * is skipped. Framework-free: the Vue and React bindings pass their notifier and translator (`useFileImport()`), the feature passes `ctx.notify` and `ctx.t`.\n */\nexport function fileImport(notify: Notifier, t: Translate): { importFiles: ImportFilesFn } {\n // Sequential so files land in selection order; a failed file is reported and skipped. A drop position is anchored,\n // since uploads take a while and the document may change meanwhile; later files follow the first one, because\n // `insertContentAt` leaves the selection right after what it inserted. Resolves whether every file was imported.\n async function importFiles(editor: Editor, files: File[] | FileList, pos?: number): Promise<boolean> {\n let anchor = pos === undefined ? null : captureRange(editor, { from: pos, to: pos })\n let allImported = true\n try {\n for (const file of Array.from(files)) {\n if (isTextFile(file) && file.size > MAX_TEXT_BYTES) {\n notify({ title: t('media.import.tooLarge.title'), description: t('media.import.tooLarge.description', { name: file.name }), kind: 'error' })\n allImported = false\n continue\n }\n try {\n const blocks = wrapImportBlocks(editor, file, await blocksOf(editor, file))\n if (editor.isDestroyed) return false\n const at = (anchor && resolveRange(editor.state, anchor)?.from) ?? editor.state.selection.to\n editor.chain().focus().insertContentAt(at, blocks).run()\n if (anchor) releaseRanges(editor, [anchor])\n anchor = null\n }\n catch (error) {\n notify({ title: t('media.import.failed'), description: fetchErrorMessage(error), kind: 'error' })\n allImported = false\n }\n }\n }\n finally {\n if (anchor) releaseRanges(editor, [anchor])\n }\n return allImported\n }\n\n return { importFiles }\n}\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;;AAE9B,MAAM,iBAAiB;;AAGvB,SAAgB,WAAW,MAAqB;CAC9C,MAAM,OAAO,UAAU,KAAK,IAAI;CAChC,OAAO,SAAS,mBAAmB,SAAS,gBAAgB,eAAe,KAAK,KAAK,IAAI;AAC3F;;AAGA,eAAe,SAAS,QAAgB,MAAoC;CAC1E,IAAI,WAAW,IAAI,GAAG;EACpB,MAAM,SAAS,gBAAgB,QAAQ,YAAY,QAAQ,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM;EACpF,OAAO,OAAO,SAAS,SAAS,CAAC,EAAE,MAAM,YAAY,CAAC;CACxD;CACA,MAAM,EAAE,QAAQ,MAAM,OAAO,QAAQ,MAAM,OAAO,IAAI;CACtD,OAAO,CAAC,aAAa,MAAM,GAAG,KAAK,YAAY,MAAM,GAAG,CAAC;AAC3D;;;;;;;;AASA,SAAgB,WAAW,QAAkB,GAA8C;CAIzF,eAAe,YAAY,QAAgB,OAA0B,KAAgC;EACnG,IAAI,SAAS,QAAQ,KAAA,IAAY,OAAO,aAAa,QAAQ;GAAE,MAAM;GAAK,IAAI;EAAI,CAAC;EACnF,IAAI,cAAc;EAClB,IAAI;GACF,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;IACpC,IAAI,WAAW,IAAI,KAAK,KAAK,OAAA,SAAuB;KAClD,OAAO;MAAE,OAAO,EAAE,6BAA6B;MAAG,aAAa,EAAE,qCAAqC,EAAE,MAAM,KAAK,KAAK,CAAC;MAAG,MAAM;KAAQ,CAAC;KAC3I,cAAc;KACd;IACF;IACA,IAAI;KACF,MAAM,SAAS,iBAAiB,QAAQ,MAAM,MAAM,SAAS,QAAQ,IAAI,CAAC;KAC1E,IAAI,OAAO,aAAa,OAAO;KAC/B,MAAM,MAAM,UAAU,aAAa,OAAO,OAAO,MAAM,CAAC,EAAE,SAAS,OAAO,MAAM,UAAU;KAC1F,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,gBAAgB,IAAI,MAAM,CAAC,CAAC,IAAI;KACvD,IAAI,QAAQ,cAAc,QAAQ,CAAC,MAAM,CAAC;KAC1C,SAAS;IACX,SACO,OAAO;KACZ,OAAO;MAAE,OAAO,EAAE,qBAAqB;MAAG,aAAa,kBAAkB,KAAK;MAAG,MAAM;KAAQ,CAAC;KAChG,cAAc;IAChB;GACF;EACF,UACQ;GACN,IAAI,QAAQ,cAAc,QAAQ,CAAC,MAAM,CAAC;EAC5C;EACA,OAAO;CACT;CAEA,OAAO,EAAE,YAAY;AACvB"}
1
+ {"version":3,"file":"file-import.js","names":[],"sources":["../../../src/features/media/file-import.ts"],"sourcesContent":["import type { Editor, JSONContent } from '@tiptap/core'\nimport type { Translate } from '../../i18n'\nimport { captureRange, fetchErrorMessage, type Notifier, parseBlocks, releaseRanges, resolveRange, structureBlocks, wrapImportBlocks } from '../../core'\nimport { mediaMime } from './schema'\nimport { fileNodeFor, type ImportFilesFn, mediaNodeFor } from './upload'\n\n/** Text files are read into the document, bigger ones are refused. */\nexport const MAX_TEXT_BYTES = 2 * 1024 * 1024\n/** Browsers report `''` (or something else) as the type of `.md` files, so the name decides too. */\nconst TEXT_EXTENSION = /\\.(md|markdown|txt)$/i\n\n/** Whether `file` is imported as text (parsed Markdown) rather than uploaded. */\nexport function isTextFile(file: File): boolean {\n const mime = mediaMime(file.type)\n return mime === 'text/markdown' || mime === 'text/plain' || TEXT_EXTENSION.test(file.name)\n}\n\n/** Blocks for `file`: its text parsed through the editor's block pipeline (parse, normalize, segment, structure), or the uploaded media/file block. */\nasync function blocksOf(editor: Editor, file: File): Promise<JSONContent[]> {\n if (isTextFile(file)) {\n const blocks = structureBlocks(editor, parseBlocks(editor, await file.text()).blocks)\n return blocks.length ? blocks : [{ type: 'paragraph' }]\n }\n const { url, sha256 } = await editor.storage.media.upload(file)\n return [mediaNodeFor(file, url, sha256) ?? fileNodeFor(file, url, sha256)]\n}\n\n/**\n * Imports files into the editor. Text and Markdown files are parsed through the editor's block pipeline into blocks;\n * all other files are uploaded through `editor.storage.media.upload` and appear as their media or file block. The\n * blocks go through the pipeline's `wrapImport` stage (identity unless an installed feature wraps imports) and are\n * inserted as they are. Problems (an oversized text file, a failed upload) are reported through `notify` and the file\n * is skipped. Framework-free: the Vue and React bindings pass their notifier and translator (`useFileImport()`), the feature passes `ctx.notify` and `ctx.t`.\n */\nexport function fileImport(notify: Notifier, t: Translate): { importFiles: ImportFilesFn } {\n // Sequential so files land in selection order; a failed file is reported and skipped. A drop position is anchored,\n // since uploads take a while and the document may change meanwhile; later files follow the first one, because\n // `insertContentAt` leaves the selection right after what it inserted. Resolves whether every file was imported.\n async function importFiles(editor: Editor, files: File[] | FileList, pos?: number): Promise<boolean> {\n let anchor = pos === undefined ? null : captureRange(editor, { from: pos, to: pos })\n let allImported = true\n try {\n for (const file of Array.from(files)) {\n if (isTextFile(file) && file.size > MAX_TEXT_BYTES) {\n notify({ title: t('media.import.tooLarge.title'), description: t('media.import.tooLarge.description', { name: file.name }), kind: 'error' })\n allImported = false\n continue\n }\n try {\n const blocks = wrapImportBlocks(editor, file, await blocksOf(editor, file))\n if (editor.isDestroyed) return false\n const at = (anchor && resolveRange(editor.state, anchor)?.from) ?? editor.state.selection.to\n editor.chain().focus().insertContentAt(at, blocks).run()\n if (anchor) releaseRanges(editor, [anchor])\n anchor = null\n }\n catch (error) {\n notify({ title: t('media.import.failed'), description: fetchErrorMessage(error), kind: 'error' })\n allImported = false\n }\n }\n }\n finally {\n if (anchor) releaseRanges(editor, [anchor])\n }\n return allImported\n }\n\n return { importFiles }\n}\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;;AAE9B,MAAM,iBAAiB;;AAGvB,SAAgB,WAAW,MAAqB;CAC9C,MAAM,OAAO,UAAU,KAAK,IAAI;CAChC,OAAO,SAAS,mBAAmB,SAAS,gBAAgB,eAAe,KAAK,KAAK,IAAI;AAC3F;;AAGA,eAAe,SAAS,QAAgB,MAAoC;CAC1E,IAAI,WAAW,IAAI,GAAG;EACpB,MAAM,SAAS,gBAAgB,QAAQ,YAAY,QAAQ,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM;EACpF,OAAO,OAAO,SAAS,SAAS,CAAC,EAAE,MAAM,YAAY,CAAC;CACxD;CACA,MAAM,EAAE,KAAK,WAAW,MAAM,OAAO,QAAQ,MAAM,OAAO,IAAI;CAC9D,OAAO,CAAC,aAAa,MAAM,KAAK,MAAM,KAAK,YAAY,MAAM,KAAK,MAAM,CAAC;AAC3E;;;;;;;;AASA,SAAgB,WAAW,QAAkB,GAA8C;CAIzF,eAAe,YAAY,QAAgB,OAA0B,KAAgC;EACnG,IAAI,SAAS,QAAQ,KAAA,IAAY,OAAO,aAAa,QAAQ;GAAE,MAAM;GAAK,IAAI;EAAI,CAAC;EACnF,IAAI,cAAc;EAClB,IAAI;GACF,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;IACpC,IAAI,WAAW,IAAI,KAAK,KAAK,OAAA,SAAuB;KAClD,OAAO;MAAE,OAAO,EAAE,6BAA6B;MAAG,aAAa,EAAE,qCAAqC,EAAE,MAAM,KAAK,KAAK,CAAC;MAAG,MAAM;KAAQ,CAAC;KAC3I,cAAc;KACd;IACF;IACA,IAAI;KACF,MAAM,SAAS,iBAAiB,QAAQ,MAAM,MAAM,SAAS,QAAQ,IAAI,CAAC;KAC1E,IAAI,OAAO,aAAa,OAAO;KAC/B,MAAM,MAAM,UAAU,aAAa,OAAO,OAAO,MAAM,CAAC,EAAE,SAAS,OAAO,MAAM,UAAU;KAC1F,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,gBAAgB,IAAI,MAAM,CAAC,CAAC,IAAI;KACvD,IAAI,QAAQ,cAAc,QAAQ,CAAC,MAAM,CAAC;KAC1C,SAAS;IACX,SACO,OAAO;KACZ,OAAO;MAAE,OAAO,EAAE,qBAAqB;MAAG,aAAa,kBAAkB,KAAK;MAAG,MAAM;KAAQ,CAAC;KAChG,cAAc;IAChB;GACF;EACF,UACQ;GACN,IAAI,QAAQ,cAAc,QAAQ,CAAC,MAAM,CAAC;EAC5C;EACA,OAAO;CACT;CAEA,OAAO,EAAE,YAAY;AACvB"}
@@ -1,7 +1,7 @@
1
1
  import { MAX_MEDIA_BYTES, MEDIA_TYPES, MediaKind, acceptFor, mediaKindOf, mediaMime } from "./schema.js";
2
2
  import { AudioRecorder, AudioRecorderState, AudioRecorderStatus, createAudioRecorder } from "./audio-recorder.js";
3
- import { AUDIO_RECORDER_OPEN, ImportFilesFn, Media, fileNodeFor, formatBytes, mediaNodeFor, openAudioRecorder, pickFiles, postUpload, uploadOf } from "./upload.js";
3
+ import { AUDIO_RECORDER_OPEN, ImportFilesFn, Media, fileNodeFor, formatBytes, mediaNodeFor, openAudioRecorder, pickFiles, uploadOf } from "./upload.js";
4
4
  import { MAX_TEXT_BYTES, fileImport, isTextFile } from "./file-import.js";
5
5
  import { MediaNodeViews, mediaBlocks, playerOptions } from "./node-views.js";
6
6
  import { MediaUi, mediaFeatureCore } from "./feature.js";
7
- export { AUDIO_RECORDER_OPEN, type AudioRecorder, type AudioRecorderState, type AudioRecorderStatus, type ImportFilesFn, MAX_MEDIA_BYTES, MAX_TEXT_BYTES, MEDIA_TYPES, Media, type MediaKind, type MediaNodeViews, type MediaUi, acceptFor, createAudioRecorder, fileImport, fileNodeFor, formatBytes, isTextFile, mediaBlocks, mediaFeatureCore, mediaKindOf, mediaMime, mediaNodeFor, openAudioRecorder, pickFiles, playerOptions, postUpload, uploadOf };
7
+ export { AUDIO_RECORDER_OPEN, type AudioRecorder, type AudioRecorderState, type AudioRecorderStatus, type ImportFilesFn, MAX_MEDIA_BYTES, MAX_TEXT_BYTES, MEDIA_TYPES, Media, type MediaKind, type MediaNodeViews, type MediaUi, acceptFor, createAudioRecorder, fileImport, fileNodeFor, formatBytes, isTextFile, mediaBlocks, mediaFeatureCore, mediaKindOf, mediaMime, mediaNodeFor, openAudioRecorder, pickFiles, playerOptions, uploadOf };
@@ -1,7 +1,7 @@
1
1
  import { MAX_MEDIA_BYTES, MEDIA_TYPES, acceptFor, mediaKindOf, mediaMime } from "./schema.js";
2
2
  import { createAudioRecorder } from "./audio-recorder.js";
3
- import { AUDIO_RECORDER_OPEN, Media, fileNodeFor, formatBytes, mediaNodeFor, openAudioRecorder, pickFiles, postUpload, uploadOf } from "./upload.js";
3
+ import { AUDIO_RECORDER_OPEN, Media, fileNodeFor, formatBytes, mediaNodeFor, openAudioRecorder, pickFiles, uploadOf } from "./upload.js";
4
4
  import { MAX_TEXT_BYTES, fileImport, isTextFile } from "./file-import.js";
5
5
  import { mediaBlocks, playerOptions } from "./node-views.js";
6
6
  import { mediaFeatureCore } from "./feature.js";
7
- export { AUDIO_RECORDER_OPEN, MAX_MEDIA_BYTES, MAX_TEXT_BYTES, MEDIA_TYPES, Media, acceptFor, createAudioRecorder, fileImport, fileNodeFor, formatBytes, isTextFile, mediaBlocks, mediaFeatureCore, mediaKindOf, mediaMime, mediaNodeFor, openAudioRecorder, pickFiles, playerOptions, postUpload, uploadOf };
7
+ export { AUDIO_RECORDER_OPEN, MAX_MEDIA_BYTES, MAX_TEXT_BYTES, MEDIA_TYPES, Media, acceptFor, createAudioRecorder, fileImport, fileNodeFor, formatBytes, isTextFile, mediaBlocks, mediaFeatureCore, mediaKindOf, mediaMime, mediaNodeFor, openAudioRecorder, pickFiles, playerOptions, uploadOf };
@@ -4,6 +4,7 @@ import { Icon } from "../../../react/Icon.js";
4
4
  import { Button } from "../../../react/ui/button.js";
5
5
  import { formatBytes } from "../upload.js";
6
6
  import { TitleField } from "./TitleField.js";
7
+ import { useEffect, useRef, useState } from "react";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  import { NodeViewWrapper } from "@tiptap/react";
9
10
  //#region src/features/media/react/FileView.tsx
@@ -11,7 +12,23 @@ import { NodeViewWrapper } from "@tiptap/react";
11
12
  function FileView({ node, selected, editor, updateAttributes }) {
12
13
  const { t } = useEditorI18n();
13
14
  const isPdf = node.attrs.mimeType === "application/pdf";
15
+ const [reveal, setReveal] = useState(null);
16
+ const figure = useRef(null);
17
+ useEffect(() => {
18
+ const el = figure.current;
19
+ if (!el) return;
20
+ const onReveal = (event) => {
21
+ const detail = event.detail;
22
+ if (typeof detail === "number" && detail > 0) setReveal((prev) => ({
23
+ page: detail,
24
+ n: (prev?.n ?? 0) + 1
25
+ }));
26
+ };
27
+ el.addEventListener("sk-reveal-page", onReveal);
28
+ return () => el.removeEventListener("sk-reveal-page", onReveal);
29
+ }, []);
14
30
  return /* @__PURE__ */ jsxs(NodeViewWrapper, {
31
+ ref: figure,
15
32
  as: "figure",
16
33
  className: cn("media-block flex flex-col gap-1", selected && "is-selected"),
17
34
  "data-id": node.attrs.id || void 0,
@@ -48,11 +65,11 @@ function FileView({ node, selected, editor, updateAttributes }) {
48
65
  })
49
66
  ]
50
67
  }), isPdf && node.attrs.src ? /* @__PURE__ */ jsx("object", {
51
- data: node.attrs.src,
68
+ data: reveal ? `${node.attrs.src}#page=${reveal.page}` : node.attrs.src,
52
69
  type: "application/pdf",
53
70
  contentEditable: false,
54
71
  className: "mt-2 h-[480px] w-full rounded-md border border-border"
55
- }) : null]
72
+ }, reveal?.n ?? 0) : null]
56
73
  });
57
74
  }
58
75
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"FileView.js","names":[],"sources":["../../../../src/features/media/react/FileView.tsx"],"sourcesContent":["import { NodeViewWrapper, type NodeViewProps } from '@tiptap/react'\nimport { Icon } from '../../../react/Icon'\nimport { Button } from '../../../react/ui/button'\nimport { cn } from '../../../react/ui/utils'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { formatBytes } from '../upload'\nimport { TitleField } from './TitleField'\n\n/** The file block: icon, name field, size and „Öffnen“ link; PDFs are embedded below. */\nexport function FileView({ node, selected, editor, updateAttributes }: NodeViewProps) {\n const { t } = useEditorI18n()\n const isPdf = node.attrs.mimeType === 'application/pdf'\n return (\n <NodeViewWrapper\n as=\"figure\"\n className={cn('media-block flex flex-col gap-1', selected && 'is-selected')}\n data-id={node.attrs.id || undefined}\n >\n <div className=\"flex items-center gap-2\" contentEditable={false}>\n <Icon name={isPdf ? 'i-lucide-file-text' : 'i-lucide-file'} className=\"size-5 text-muted-foreground\" />\n <TitleField\n value={node.attrs.title}\n placeholder={t('media.view.fileName')}\n label={t('media.view.fileName')}\n readOnly={!editor.isEditable}\n className=\"min-w-0 flex-1\"\n onChange={title => updateAttributes({ title })}\n />\n {node.attrs.size ? <span className=\"shrink-0 text-xs text-muted-foreground/60\">{formatBytes(node.attrs.size)}</span> : null}\n <Button asChild size=\"xs\" variant=\"secondary\">\n <a href={node.attrs.src} target=\"_blank\" rel=\"noopener noreferrer\">\n <Icon name=\"i-lucide-external-link\" />\n {t('media.view.open')}\n </a>\n </Button>\n </div>\n {isPdf && node.attrs.src\n ? <object data={node.attrs.src} type=\"application/pdf\" contentEditable={false} className=\"mt-2 h-[480px] w-full rounded-md border border-border\" />\n : null}\n </NodeViewWrapper>\n )\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,SAAS,EAAE,MAAM,UAAU,QAAQ,oBAAmC;CACpF,MAAM,EAAE,MAAM,cAAc;CAC5B,MAAM,QAAQ,KAAK,MAAM,aAAa;CACtC,OACE,qBAAC,iBAAD;EACE,IAAG;EACH,WAAW,GAAG,mCAAmC,YAAY,aAAa;EAC1E,WAAS,KAAK,MAAM,MAAM,KAAA;EAH5B,UAAA,CAKE,qBAAC,OAAD;GAAK,WAAU;GAA0B,iBAAiB;GAA1D,UAAA;IACE,oBAAC,MAAD;KAAM,MAAM,QAAQ,uBAAuB;KAAiB,WAAU;IAAgC,CAAA;IACtG,oBAAC,YAAD;KACE,OAAO,KAAK,MAAM;KAClB,aAAa,EAAE,qBAAqB;KACpC,OAAO,EAAE,qBAAqB;KAC9B,UAAU,CAAC,OAAO;KAClB,WAAU;KACV,WAAU,UAAS,iBAAiB,EAAE,MAAM,CAAC;IAC9C,CAAA;IACA,KAAK,MAAM,OAAO,oBAAC,QAAD;KAAM,WAAU;KAA6C,UAAA,YAAY,KAAK,MAAM,IAAI;IAAQ,CAAA,IAAI;IACvH,oBAAC,QAAD;KAAQ,SAAA;KAAQ,MAAK;KAAK,SAAQ;KAChC,UAAA,qBAAC,KAAD;MAAG,MAAM,KAAK,MAAM;MAAK,QAAO;MAAS,KAAI;MAA7C,UAAA,CACE,oBAAC,MAAD,EAAM,MAAK,yBAA0B,CAAA,GACpC,EAAE,iBAAiB,CACnB;;IACG,CAAA;GACL;EACJ,CAAA,GAAA,SAAS,KAAK,MAAM,MACjB,oBAAC,UAAD;GAAQ,MAAM,KAAK,MAAM;GAAK,MAAK;GAAkB,iBAAiB;GAAO,WAAU;EAAyD,CAAA,IAChJ,IACW;;AAErB"}
1
+ {"version":3,"file":"FileView.js","names":[],"sources":["../../../../src/features/media/react/FileView.tsx"],"sourcesContent":["import { NodeViewWrapper, type NodeViewProps } from '@tiptap/react'\nimport { useEffect, useRef, useState } from 'react'\nimport { Icon } from '../../../react/Icon'\nimport { Button } from '../../../react/ui/button'\nimport { cn } from '../../../react/ui/utils'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { formatBytes } from '../upload'\nimport { TitleField } from './TitleField'\n\n/** The file block: icon, name field, size and „Öffnen“ link; PDFs are embedded below. */\nexport function FileView({ node, selected, editor, updateAttributes }: NodeViewProps) {\n const { t } = useEditorI18n()\n const isPdf = node.attrs.mimeType === 'application/pdf'\n // Page the PDF viewer was asked to open at (`revealSource`, locator „S. 3“). `n` counts the requests: the viewer is\n // remounted on every one, so asking for the same page again jumps back after the user scrolled away.\n const [reveal, setReveal] = useState<{ page: number, n: number } | null>(null)\n const figure = useRef<HTMLElement>(null)\n useEffect(() => {\n const el = figure.current\n if (!el) return\n const onReveal = (event: Event) => {\n const detail = (event as CustomEvent<unknown>).detail\n if (typeof detail === 'number' && detail > 0) setReveal(prev => ({ page: detail, n: (prev?.n ?? 0) + 1 }))\n }\n // Event name of `revealSource` in features/composites/reveal-source.ts (media must not import composites).\n el.addEventListener('sk-reveal-page', onReveal)\n return () => el.removeEventListener('sk-reveal-page', onReveal)\n }, [])\n\n return (\n <NodeViewWrapper\n ref={figure}\n as=\"figure\"\n className={cn('media-block flex flex-col gap-1', selected && 'is-selected')}\n data-id={node.attrs.id || undefined}\n >\n <div className=\"flex items-center gap-2\" contentEditable={false}>\n <Icon name={isPdf ? 'i-lucide-file-text' : 'i-lucide-file'} className=\"size-5 text-muted-foreground\" />\n <TitleField\n value={node.attrs.title}\n placeholder={t('media.view.fileName')}\n label={t('media.view.fileName')}\n readOnly={!editor.isEditable}\n className=\"min-w-0 flex-1\"\n onChange={title => updateAttributes({ title })}\n />\n {node.attrs.size ? <span className=\"shrink-0 text-xs text-muted-foreground/60\">{formatBytes(node.attrs.size)}</span> : null}\n <Button asChild size=\"xs\" variant=\"secondary\">\n <a href={node.attrs.src} target=\"_blank\" rel=\"noopener noreferrer\">\n <Icon name=\"i-lucide-external-link\" />\n {t('media.view.open')}\n </a>\n </Button>\n </div>\n {isPdf && node.attrs.src\n ? <object key={reveal?.n ?? 0} data={reveal ? `${node.attrs.src}#page=${reveal.page}` : node.attrs.src} type=\"application/pdf\" contentEditable={false} className=\"mt-2 h-[480px] w-full rounded-md border border-border\" />\n : null}\n </NodeViewWrapper>\n )\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,SAAS,EAAE,MAAM,UAAU,QAAQ,oBAAmC;CACpF,MAAM,EAAE,MAAM,cAAc;CAC5B,MAAM,QAAQ,KAAK,MAAM,aAAa;CAGtC,MAAM,CAAC,QAAQ,aAAa,SAA6C,IAAI;CAC7E,MAAM,SAAS,OAAoB,IAAI;CACvC,gBAAgB;EACd,MAAM,KAAK,OAAO;EAClB,IAAI,CAAC,IAAI;EACT,MAAM,YAAY,UAAiB;GACjC,MAAM,SAAU,MAA+B;GAC/C,IAAI,OAAO,WAAW,YAAY,SAAS,GAAG,WAAU,UAAS;IAAE,MAAM;IAAQ,IAAI,MAAM,KAAK,KAAK;GAAE,EAAE;EAC3G;EAEA,GAAG,iBAAiB,kBAAkB,QAAQ;EAC9C,aAAa,GAAG,oBAAoB,kBAAkB,QAAQ;CAChE,GAAG,CAAC,CAAC;CAEL,OACE,qBAAC,iBAAD;EACE,KAAK;EACL,IAAG;EACH,WAAW,GAAG,mCAAmC,YAAY,aAAa;EAC1E,WAAS,KAAK,MAAM,MAAM,KAAA;EAJ5B,UAAA,CAME,qBAAC,OAAD;GAAK,WAAU;GAA0B,iBAAiB;GAA1D,UAAA;IACE,oBAAC,MAAD;KAAM,MAAM,QAAQ,uBAAuB;KAAiB,WAAU;IAAgC,CAAA;IACtG,oBAAC,YAAD;KACE,OAAO,KAAK,MAAM;KAClB,aAAa,EAAE,qBAAqB;KACpC,OAAO,EAAE,qBAAqB;KAC9B,UAAU,CAAC,OAAO;KAClB,WAAU;KACV,WAAU,UAAS,iBAAiB,EAAE,MAAM,CAAC;IAC9C,CAAA;IACA,KAAK,MAAM,OAAO,oBAAC,QAAD;KAAM,WAAU;KAA6C,UAAA,YAAY,KAAK,MAAM,IAAI;IAAQ,CAAA,IAAI;IACvH,oBAAC,QAAD;KAAQ,SAAA;KAAQ,MAAK;KAAK,SAAQ;KAChC,UAAA,qBAAC,KAAD;MAAG,MAAM,KAAK,MAAM;MAAK,QAAO;MAAS,KAAI;MAA7C,UAAA,CACE,oBAAC,MAAD,EAAM,MAAK,yBAA0B,CAAA,GACpC,EAAE,iBAAiB,CACnB;;IACG,CAAA;GACL;EACJ,CAAA,GAAA,SAAS,KAAK,MAAM,MACjB,oBAAC,UAAD;GAA6B,MAAM,SAAS,GAAG,KAAK,MAAM,IAAI,QAAQ,OAAO,SAAS,KAAK,MAAM;GAAK,MAAK;GAAkB,iBAAiB;GAAO,WAAU;EAAyD,GAA3M,QAAQ,KAAK,CAA8L,IACxN,IACW;;AAErB"}
@@ -3,6 +3,12 @@ import { AnyExtension, Node } from "@tiptap/core";
3
3
  //#region src/features/media/schema.d.ts
4
4
  /** The media nodes (image, audio, video, file) of the document schema. */
5
5
  export declare const mediaSchemaExtensions: () => AnyExtension[];
6
+ /**
7
+ * The image node with a `hash` attribute. Markdown: `![alt](src "title")`, followed by ` <!-- {"hash":"…"} -->` when the
8
+ * block has a hash; only that form is claimed by `imageBlockTokenizer` (a block-level token that still yields an `image`
9
+ * token), a plain image keeps the stock inline parse.
10
+ */
11
+ export declare const ImageNode: Node<import("@tiptap/extension-image").ImageOptions, any>;
6
12
  export type MediaKind = 'image' | 'audio' | 'video' | 'file';
7
13
  /** Uploadable MIME types → file extension on disk and media kind. `<uuid>.<ext>` asset names derive from this. */
8
14
  export declare const MEDIA_TYPES: Readonly<Record<string, {
@@ -22,6 +28,7 @@ export interface MediaAttrs {
22
28
  src: string;
23
29
  title?: string;
24
30
  mimeType?: string;
31
+ hash?: string | null;
25
32
  }
26
33
  declare module '@tiptap/core' {
27
34
  interface Commands<ReturnType> {
@@ -51,6 +58,7 @@ export interface FileAttrs {
51
58
  title?: string;
52
59
  mimeType?: string;
53
60
  size?: number;
61
+ hash?: string | null;
54
62
  }
55
63
  /** A generic uploaded file (PDF, office document, …) that has no player: shown as a card with an open button. */
56
64
  export declare const FileNode: Node<any, any>;
@@ -3,13 +3,97 @@ import { ASSET_DOCUMENT_ID, ASSET_NAME, SCRATCH_DOCUMENT_ID, assetRefOf, assetUr
3
3
  import { Node, mergeAttributes } from "@tiptap/core";
4
4
  import { Image } from "@tiptap/extension-image";
5
5
  //#region src/features/media/schema.ts
6
+ /** SHA-256 (hex) of the uploaded bytes, kept on media blocks so derived content can tell when its source changed; null when unknown (memory backend, older documents). */
7
+ const hashAttribute = {
8
+ default: null,
9
+ parseHTML: (el) => el.getAttribute("data-hash"),
10
+ renderHTML: (attrs) => attrs.hash ? { "data-hash": attrs.hash } : {}
11
+ };
6
12
  /** The media nodes (image, audio, video, file) of the document schema. */
7
13
  const mediaSchemaExtensions = () => [
8
- Image,
14
+ ImageNode,
9
15
  AudioNode,
10
16
  VideoNode,
11
17
  FileNode
12
18
  ];
19
+ /**
20
+ * The image node with a `hash` attribute. Markdown: `![alt](src "title")`, followed by ` <!-- {"hash":"…"} -->` when the
21
+ * block has a hash; only that form is claimed by `imageBlockTokenizer` (a block-level token that still yields an `image`
22
+ * token), a plain image keeps the stock inline parse.
23
+ */
24
+ const ImageNode = Image.extend({
25
+ addAttributes() {
26
+ return {
27
+ ...this.parent?.(),
28
+ hash: hashAttribute
29
+ };
30
+ },
31
+ markdownTokenizer: {
32
+ name: "imageBlock",
33
+ level: "block",
34
+ start: (src) => src.search(new RegExp(IMAGE_BLOCK_PATTERN, "m")),
35
+ tokenize: (src) => {
36
+ const match = new RegExp(IMAGE_BLOCK_PATTERN).exec(src);
37
+ if (!match) return void 0;
38
+ return {
39
+ type: "image",
40
+ raw: match[0],
41
+ text: match[1] ?? "",
42
+ href: match[2] ?? "",
43
+ title: match[3],
44
+ attrs: parseAttrComment(match[4])
45
+ };
46
+ }
47
+ },
48
+ parseMarkdown: (token, helpers) => helpers.createNode("image", {
49
+ src: token.href,
50
+ title: token.title,
51
+ alt: token.text,
52
+ hash: typeof token.attrs?.hash === "string" ? token.attrs.hash : null
53
+ }),
54
+ renderMarkdown: (node) => {
55
+ const { src = "", alt = "", title = "" } = node.attrs ?? {};
56
+ return `![${alt}](${src}${title ? ` "${title}"` : ""})${attrComment({ hash: node.attrs?.hash })}`;
57
+ }
58
+ });
59
+ const ATTR_COMMENT_PATTERN = "(?: <!-- (\\{.*?\\}) -->)?[ \\t]*(?:\\n|$)";
60
+ const IMAGE_BLOCK_PATTERN = `^!\\[([^\\]\\n]*)\\]\\(([^)\\s]*)(?: "([^"\\n]*)")?\\) <!-- (\\{.*?\\}) -->[ \\t]*(?:\\n|$)`;
61
+ /** ` <!-- {"hash":"…"} -->` for the attributes that are set (empty string when none); `>` is escaped so the JSON cannot end the comment. */
62
+ function attrComment(attrs) {
63
+ const set = Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== null && value !== void 0 && value !== "" && value !== 0));
64
+ return Object.keys(set).length ? ` <!-- ${JSON.stringify(set).replace(/>/g, "\\u003e")} -->` : "";
65
+ }
66
+ /** The attributes of an `attrComment`; empty for a missing or malformed one. */
67
+ function parseAttrComment(json) {
68
+ if (!json) return {};
69
+ try {
70
+ const value = JSON.parse(json);
71
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
72
+ } catch {
73
+ return {};
74
+ }
75
+ }
76
+ /** Block tokenizer for `[<label>: title](src)` lines of one media node; `labels` are the accepted labels (regex alternatives). */
77
+ function mediaTokenizer(name, labels) {
78
+ const pattern = `^\\[(?:${labels})(?:: ((?:\\\\.|[^\\]\\\\\\n])*))?\\]\\(([^)\\s]*)\\)${ATTR_COMMENT_PATTERN}`;
79
+ return {
80
+ name,
81
+ level: "block",
82
+ start: (src) => src.search(new RegExp(pattern, "m")),
83
+ tokenize: (src) => {
84
+ const match = new RegExp(pattern).exec(src);
85
+ if (!match) return void 0;
86
+ return {
87
+ type: name,
88
+ raw: match[0],
89
+ title: (match[1] ?? "").replace(/\\(.)/g, "$1"),
90
+ href: match[2] ?? "",
91
+ attrs: parseAttrComment(match[3])
92
+ };
93
+ }
94
+ };
95
+ }
96
+ const stringAttr = (value) => typeof value === "string" ? value : "";
13
97
  /** Uploadable MIME types → file extension on disk and media kind. `<uuid>.<ext>` asset names derive from this. */
14
98
  const MEDIA_TYPES = {
15
99
  "image/png": {
@@ -97,11 +181,25 @@ const mediaAttributes = {
97
181
  default: "",
98
182
  parseHTML: (el) => el.getAttribute("data-mime") ?? "",
99
183
  renderHTML: (attrs) => ({ "data-mime": attrs.mimeType })
100
- }
184
+ },
185
+ hash: hashAttribute
101
186
  };
102
- /** Markdown export (AI context, `getMarkdown()`): a link labelled with the kind and, when there is one, the title. */
103
- function mediaMarkdown(label, title, src) {
104
- return `[${title ? `${label}: ${String(title).replace(/[[\]\\]/g, "\\$&")}` : label}](${String(src ?? "")})`;
187
+ /** Markdown export (AI context, `getMarkdown()`, source mode): a link labelled with the kind and, when there is one, the title; `extra` and the hash go into the trailing attribute comment. */
188
+ function mediaMarkdown(label, attrs, extra) {
189
+ const title = attrs?.title;
190
+ return `[${title ? `${label}: ${String(title).replace(/[[\]\\]/g, "\\$&")}` : label}](${String(attrs?.src ?? "")})${attrComment({
191
+ ...extra,
192
+ hash: attrs?.hash
193
+ })}`;
194
+ }
195
+ /** Node attributes of an audio/video Markdown token. */
196
+ function mediaAttrsOfToken(token) {
197
+ return {
198
+ src: token.href,
199
+ title: token.title,
200
+ mimeType: stringAttr(token.attrs?.mimeType),
201
+ hash: stringAttr(token.attrs?.hash) || null
202
+ };
105
203
  }
106
204
  const AudioNode = Node.create({
107
205
  name: "audio",
@@ -115,7 +213,9 @@ const AudioNode = Node.create({
115
213
  controls: "true",
116
214
  preload: "metadata"
117
215
  })],
118
- renderMarkdown: (node) => mediaMarkdown("Audio", node.attrs?.title || "Aufnahme", node.attrs?.src),
216
+ markdownTokenizer: mediaTokenizer("audio", "Audio"),
217
+ parseMarkdown: (token, helpers) => helpers.createNode("audio", mediaAttrsOfToken(token)),
218
+ renderMarkdown: (node) => mediaMarkdown("Audio", node.attrs, { mimeType: node.attrs?.mimeType }),
119
219
  addCommands() {
120
220
  return { insertAudio: (attrs) => ({ commands }) => commands.insertContent({
121
221
  type: this.name,
@@ -135,7 +235,9 @@ const VideoNode = Node.create({
135
235
  controls: "true",
136
236
  preload: "metadata"
137
237
  })],
138
- renderMarkdown: (node) => mediaMarkdown("Video", node.attrs?.title, node.attrs?.src),
238
+ markdownTokenizer: mediaTokenizer("video", "Video"),
239
+ parseMarkdown: (token, helpers) => helpers.createNode("video", mediaAttrsOfToken(token)),
240
+ renderMarkdown: (node) => mediaMarkdown("Video", node.attrs, { mimeType: node.attrs?.mimeType }),
139
241
  addCommands() {
140
242
  return { insertVideo: (attrs) => ({ commands }) => commands.insertContent({
141
243
  type: this.name,
@@ -166,7 +268,8 @@ const FileNode = Node.create({
166
268
  size: {
167
269
  default: 0,
168
270
  parseHTML: (el) => Number(el.getAttribute("data-size")) || 0
169
- }
271
+ },
272
+ hash: hashAttribute
170
273
  }),
171
274
  parseHTML: () => [{ tag: "a[data-type=\"file\"]" }],
172
275
  renderHTML: ({ node }) => [
@@ -176,11 +279,23 @@ const FileNode = Node.create({
176
279
  "href": node.attrs.src,
177
280
  "download": node.attrs.title,
178
281
  "data-mime": node.attrs.mimeType,
179
- "data-size": String(node.attrs.size)
282
+ "data-size": String(node.attrs.size),
283
+ ...node.attrs.hash ? { "data-hash": node.attrs.hash } : {}
180
284
  },
181
285
  node.attrs.title || "Datei"
182
286
  ],
183
- renderMarkdown: (node) => `[Datei: ${String(node.attrs?.title || "Datei").replace(/[[\]\\]/g, "\\$&")}](${String(node.attrs?.src ?? "")})`,
287
+ markdownTokenizer: mediaTokenizer("file", "Datei|File"),
288
+ parseMarkdown: (token, helpers) => helpers.createNode("file", {
289
+ src: token.href,
290
+ title: token.title,
291
+ mimeType: stringAttr(token.attrs?.mimeType),
292
+ size: typeof token.attrs?.size === "number" ? token.attrs.size : 0,
293
+ hash: stringAttr(token.attrs?.hash) || null
294
+ }),
295
+ renderMarkdown: (node) => mediaMarkdown("Datei", node.attrs, {
296
+ mimeType: node.attrs?.mimeType,
297
+ size: node.attrs?.size
298
+ }),
184
299
  addCommands() {
185
300
  return { insertFile: (attrs) => ({ commands }) => commands.insertContent({
186
301
  type: this.name,
@@ -189,6 +304,6 @@ const FileNode = Node.create({
189
304
  }
190
305
  });
191
306
  //#endregion
192
- export { ASSET_DOCUMENT_ID, ASSET_NAME, AudioNode, FileNode, MAX_MEDIA_BYTES, MEDIA_TYPES, SCRATCH_DOCUMENT_ID, VideoNode, acceptFor, assetRefOf, assetUrl, mediaKindOf, mediaMime, mediaSchemaExtensions };
307
+ export { ASSET_DOCUMENT_ID, ASSET_NAME, AudioNode, FileNode, ImageNode, MAX_MEDIA_BYTES, MEDIA_TYPES, SCRATCH_DOCUMENT_ID, VideoNode, acceptFor, assetRefOf, assetUrl, mediaKindOf, mediaMime, mediaSchemaExtensions };
193
308
 
194
309
  //# sourceMappingURL=schema.js.map