@ecsplico/editor 2.1.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (692) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/README.md +39 -156
  3. package/dist/adapters/comments/drizzle.d.ts +1 -1
  4. package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
  5. package/dist/adapters/comments/rest-model.js.map +1 -0
  6. package/dist/adapters/comments/types.d.ts +2 -2
  7. package/dist/adapters/document-state.d.ts +23 -0
  8. package/dist/adapters/document-state.js +66 -0
  9. package/dist/adapters/document-state.js.map +1 -0
  10. package/dist/adapters/documents.js +14 -11
  11. package/dist/adapters/documents.js.map +1 -1
  12. package/dist/adapters/drizzle.d.ts +2 -2
  13. package/dist/adapters/drizzle.js +97 -88
  14. package/dist/adapters/drizzle.js.map +1 -1
  15. package/dist/adapters/forge.d.ts +12 -7
  16. package/dist/adapters/forge.js +38 -45
  17. package/dist/adapters/forge.js.map +1 -1
  18. package/dist/adapters/http.d.ts +20 -0
  19. package/dist/adapters/http.js +68 -0
  20. package/dist/adapters/http.js.map +1 -0
  21. package/dist/adapters/index.d.ts +5 -5
  22. package/dist/adapters/json.d.ts +3 -3
  23. package/dist/adapters/json.js +58 -49
  24. package/dist/adapters/json.js.map +1 -1
  25. package/dist/adapters/memory.d.ts +21 -0
  26. package/dist/adapters/memory.js +53 -0
  27. package/dist/adapters/memory.js.map +1 -0
  28. package/dist/adapters/nitro.d.ts +23 -20
  29. package/dist/adapters/nitro.js +58 -163
  30. package/dist/adapters/nitro.js.map +1 -1
  31. package/dist/adapters/react.d.ts +55 -0
  32. package/dist/adapters/react.js +209 -0
  33. package/dist/adapters/react.js.map +1 -0
  34. package/dist/adapters/shared.d.ts +15 -21
  35. package/dist/adapters/shared.js +10 -8
  36. package/dist/adapters/shared.js.map +1 -1
  37. package/dist/adapters/vue.d.ts +15 -30
  38. package/dist/adapters/vue.js +36 -87
  39. package/dist/adapters/vue.js.map +1 -1
  40. package/dist/config/index.d.ts +105 -0
  41. package/dist/config/index.js +95 -0
  42. package/dist/config/index.js.map +1 -0
  43. package/dist/core/abilities.js +1 -0
  44. package/dist/core/abilities.js.map +1 -1
  45. package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
  46. package/dist/core/api-routes.js.map +1 -0
  47. package/dist/core/backend-capability.d.ts +7 -0
  48. package/dist/core/backend-capability.js +17 -0
  49. package/dist/core/backend-capability.js.map +1 -0
  50. package/dist/core/backend-types.d.ts +245 -0
  51. package/dist/core/backend-types.js +17 -0
  52. package/dist/core/backend-types.js.map +1 -0
  53. package/dist/core/backend.d.ts +108 -0
  54. package/dist/core/backend.js +14 -0
  55. package/dist/core/backend.js.map +1 -0
  56. package/dist/core/block-convert.d.ts +7 -5
  57. package/dist/core/block-convert.js +28 -5
  58. package/dist/core/block-convert.js.map +1 -1
  59. package/dist/core/command-types.d.ts +89 -0
  60. package/dist/core/commands.d.ts +40 -0
  61. package/dist/core/commands.js +151 -0
  62. package/dist/core/commands.js.map +1 -0
  63. package/dist/core/context.d.ts +17 -20
  64. package/dist/core/context.js +24 -21
  65. package/dist/core/context.js.map +1 -1
  66. package/dist/core/core-feature.d.ts +22 -7
  67. package/dist/core/core-feature.js +51 -18
  68. package/dist/core/core-feature.js.map +1 -1
  69. package/dist/core/define-editor.d.ts +4 -3
  70. package/dist/core/define-editor.js +14 -7
  71. package/dist/core/define-editor.js.map +1 -1
  72. package/dist/core/document-markdown.js +12 -3
  73. package/dist/core/document-markdown.js.map +1 -1
  74. package/dist/core/editor-ui.d.ts +8 -1
  75. package/dist/core/editor-ui.js +9 -8
  76. package/dist/core/editor-ui.js.map +1 -1
  77. package/dist/core/extension-points.d.ts +3 -15
  78. package/dist/core/feature-context.d.ts +8 -7
  79. package/dist/core/feature-context.js +12 -11
  80. package/dist/core/feature-context.js.map +1 -1
  81. package/dist/core/feature.d.ts +35 -19
  82. package/dist/core/feature.js.map +1 -1
  83. package/dist/core/fences.d.ts +2 -1
  84. package/dist/core/fences.js +3 -2
  85. package/dist/core/fences.js.map +1 -1
  86. package/dist/core/fetch-error.d.ts +1 -1
  87. package/dist/core/fetch-error.js +3 -1
  88. package/dist/core/fetch-error.js.map +1 -1
  89. package/dist/core/hash.d.ts +5 -0
  90. package/dist/core/hash.js +11 -0
  91. package/dist/core/hash.js.map +1 -0
  92. package/dist/core/http-backend.d.ts +11 -0
  93. package/dist/core/http-backend.js +195 -0
  94. package/dist/core/http-backend.js.map +1 -0
  95. package/dist/core/index.d.ts +14 -9
  96. package/dist/core/index.js +12 -9
  97. package/dist/core/palette.d.ts +48 -0
  98. package/dist/core/palette.js +95 -0
  99. package/dist/core/palette.js.map +1 -0
  100. package/dist/core/schema/abilities.d.ts +2 -1
  101. package/dist/core/schema/abilities.js +5 -1
  102. package/dist/core/schema/abilities.js.map +1 -1
  103. package/dist/core/schema/index.d.ts +11 -5
  104. package/dist/core/schema/index.js +13 -8
  105. package/dist/core/schema/index.js.map +1 -1
  106. package/dist/core/schema/mark-flags.d.ts +8 -1
  107. package/dist/core/schema/of-features.d.ts +13 -0
  108. package/dist/core/schema/of-features.js +14 -0
  109. package/dist/core/schema/of-features.js.map +1 -0
  110. package/dist/core/slash.d.ts +8 -6
  111. package/dist/core/slash.js +24 -16
  112. package/dist/core/slash.js.map +1 -1
  113. package/dist/core/surface-session.js +13 -5
  114. package/dist/core/surface-session.js.map +1 -1
  115. package/dist/core/toolbar-items.d.ts +10 -4
  116. package/dist/core/toolbar-items.js +269 -195
  117. package/dist/core/toolbar-items.js.map +1 -1
  118. package/dist/core/util.js +11 -0
  119. package/dist/core/util.js.map +1 -0
  120. package/dist/document/DocumentSurface.d.ts +1 -1
  121. package/dist/features/ai/agent-op-review.d.ts +1 -1
  122. package/dist/features/ai/agent-op-review.js +1 -1
  123. package/dist/features/ai/agent-ops.js +1 -1
  124. package/dist/features/ai/ai-apply.d.ts +2 -2
  125. package/dist/features/ai/ai-apply.js +1 -1
  126. package/dist/features/ai/ai-review.js +1 -1
  127. package/dist/features/ai/ai-sse.d.ts +6 -6
  128. package/dist/features/ai/ai-sse.js +20 -63
  129. package/dist/features/ai/ai-sse.js.map +1 -1
  130. package/dist/features/ai/assistant.d.ts +8 -7
  131. package/dist/features/ai/assistant.js +3 -3
  132. package/dist/features/ai/assistant.js.map +1 -1
  133. package/dist/features/ai/block-actions.d.ts +3 -2
  134. package/dist/features/ai/block-actions.js +3 -2
  135. package/dist/features/ai/block-actions.js.map +1 -1
  136. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  137. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  138. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
  139. package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
  140. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  141. package/dist/features/ai/document-chat-model.d.ts +8 -7
  142. package/dist/features/ai/document-chat-model.js +5 -5
  143. package/dist/features/ai/document-chat-model.js.map +1 -1
  144. package/dist/features/ai/document-chat.js +1 -1
  145. package/dist/features/ai/feature.js +27 -14
  146. package/dist/features/ai/feature.js.map +1 -1
  147. package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
  148. package/dist/features/ai/ghost-autocomplete.js +6 -9
  149. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  150. package/dist/features/ai/index.d.ts +4 -3
  151. package/dist/features/ai/index.js +10 -10
  152. package/dist/features/ai/model-profiles.d.ts +5 -4
  153. package/dist/features/ai/model-profiles.js +2 -2
  154. package/dist/features/ai/model-profiles.js.map +1 -1
  155. package/dist/features/ai/protocol/block-lines.js +1 -1
  156. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  157. package/dist/features/ai/protocol/chat.js +4 -1
  158. package/dist/features/ai/protocol/chat.js.map +1 -1
  159. package/dist/features/ai/protocol/index.d.ts +3 -2
  160. package/dist/features/ai/protocol/models.d.ts +2 -16
  161. package/dist/features/ai/protocol/models.js.map +1 -1
  162. package/dist/features/ai/rate-limit.d.ts +1 -1
  163. package/dist/features/ai/rate-limit.js +3 -1
  164. package/dist/features/ai/rate-limit.js.map +1 -1
  165. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  166. package/dist/features/ai/react/AiSidebar.js +4 -4
  167. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  168. package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
  169. package/dist/features/ai/react/DocumentChatOps.js +1 -1
  170. package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
  171. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  172. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  173. package/dist/features/ai/react/use-ai-assistant.js +3 -3
  174. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  175. package/dist/features/ai/react/use-document-chat.js +2 -2
  176. package/dist/features/ai/react/use-document-chat.js.map +1 -1
  177. package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
  178. package/dist/features/ai/selection-flow.d.ts +1 -1
  179. package/dist/features/ai/selection-flow.js +1 -1
  180. package/dist/features/ai/sidebar-flow.d.ts +1 -1
  181. package/dist/features/ai/sidebar-flow.js +1 -1
  182. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  183. package/dist/features/ai/use-ai-assistant.js +3 -3
  184. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  185. package/dist/features/ai/use-document-chat.js +2 -2
  186. package/dist/features/ai/use-document-chat.js.map +1 -1
  187. package/dist/features/ai/use-model-profiles.d.ts +2 -1
  188. package/dist/features/authors/attribution.d.ts +1 -1
  189. package/dist/features/authors/feature.js +12 -11
  190. package/dist/features/authors/feature.js.map +1 -1
  191. package/dist/features/block-ui/block-actions.d.ts +1 -1
  192. package/dist/features/block-ui/block-actions.js +1 -1
  193. package/dist/features/block-ui/block-actions.js.map +1 -1
  194. package/dist/features/block-ui/components/BlockUi.js +1 -1
  195. package/dist/features/block-ui/feature.js +1 -0
  196. package/dist/features/block-ui/feature.js.map +1 -1
  197. package/dist/features/block-ui/react/BlockUi.js +1 -1
  198. package/dist/features/collab/anchor-resolver.js +1 -1
  199. package/dist/features/collab/feature.js +2 -0
  200. package/dist/features/collab/feature.js.map +1 -1
  201. package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
  202. package/dist/features/comments/feature.js +12 -11
  203. package/dist/features/comments/feature.js.map +1 -1
  204. package/dist/features/comments/index.d.ts +3 -2
  205. package/dist/features/comments/index.js +1 -1
  206. package/dist/features/comments/margin-layout.d.ts +3 -2
  207. package/dist/features/comments/model.d.ts +3 -67
  208. package/dist/features/comments/model.js +2 -2
  209. package/dist/features/comments/model.js.map +1 -1
  210. package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
  211. package/dist/features/comments/react/use-comments.d.ts +2 -1
  212. package/dist/features/comments/react.d.ts +1 -1
  213. package/dist/features/comments/schema.d.ts +2 -52
  214. package/dist/features/comments/schema.js.map +1 -1
  215. package/dist/features/comments/snapshot-equality.js.map +1 -1
  216. package/dist/features/comments/use-comments.d.ts +3 -2
  217. package/dist/features/comments/vue.d.ts +1 -1
  218. package/dist/features/composites/accepted-children.d.ts +1 -0
  219. package/dist/features/composites/accepted-children.js +16 -1
  220. package/dist/features/composites/accepted-children.js.map +1 -1
  221. package/dist/features/composites/ai-brief.js +31 -0
  222. package/dist/features/composites/ai-brief.js.map +1 -1
  223. package/dist/features/composites/bibliography-command.d.ts +11 -0
  224. package/dist/features/composites/bibliography-form.js +90 -0
  225. package/dist/features/composites/bibliography-form.js.map +1 -0
  226. package/dist/features/composites/bibliography.d.ts +33 -0
  227. package/dist/features/composites/bibliography.js +120 -0
  228. package/dist/features/composites/bibliography.js.map +1 -0
  229. package/dist/features/composites/components/BibliographyEditor.js +261 -0
  230. package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
  231. package/dist/features/composites/components/CompositeChrome.js +75 -26
  232. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  233. package/dist/features/composites/components/CompositeDetails.js +46 -11
  234. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  235. package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
  236. package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
  237. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  238. package/dist/features/composites/components/CompositeInspector.js +17 -5
  239. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  240. package/dist/features/composites/components/CompositeTable.js +13 -4
  241. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  242. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  243. package/dist/features/composites/components/CompositeView.js +11 -1
  244. package/dist/features/composites/components/CompositeView.js.map +1 -1
  245. package/dist/features/composites/components/DerivationBadge.js +103 -0
  246. package/dist/features/composites/components/DerivationBadge.js.map +1 -0
  247. package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
  248. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
  249. package/dist/features/composites/components/ReferenceView.js +122 -0
  250. package/dist/features/composites/components/ReferenceView.js.map +1 -0
  251. package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
  252. package/dist/features/composites/components/SettingsEditingTab.js +1 -1
  253. package/dist/features/composites/convert.js +2 -2
  254. package/dist/features/composites/convert.js.map +1 -1
  255. package/dist/features/composites/derivation.d.ts +61 -0
  256. package/dist/features/composites/derivation.js +98 -0
  257. package/dist/features/composites/derivation.js.map +1 -0
  258. package/dist/features/composites/derived-chrome.js +69 -0
  259. package/dist/features/composites/derived-chrome.js.map +1 -0
  260. package/dist/features/composites/feature.js +159 -109
  261. package/dist/features/composites/feature.js.map +1 -1
  262. package/dist/features/composites/fences.d.ts +4 -3
  263. package/dist/features/composites/fences.js +51 -7
  264. package/dist/features/composites/fences.js.map +1 -1
  265. package/dist/features/composites/index.d.ts +12 -3
  266. package/dist/features/composites/index.js +15 -6
  267. package/dist/features/composites/inspector-model.d.ts +4 -1
  268. package/dist/features/composites/inspector-model.js +8 -4
  269. package/dist/features/composites/inspector-model.js.map +1 -1
  270. package/dist/features/composites/kinds.d.ts +8 -1
  271. package/dist/features/composites/kinds.js +145 -6
  272. package/dist/features/composites/kinds.js.map +1 -1
  273. package/dist/features/composites/legacy.d.ts +4 -1
  274. package/dist/features/composites/legacy.js +56 -1
  275. package/dist/features/composites/legacy.js.map +1 -1
  276. package/dist/features/composites/meta-display.js +40 -1
  277. package/dist/features/composites/meta-display.js.map +1 -1
  278. package/dist/features/composites/meta-keys.d.ts +7 -0
  279. package/dist/features/composites/meta-keys.js +21 -0
  280. package/dist/features/composites/meta-keys.js.map +1 -0
  281. package/dist/features/composites/node-views.d.ts +6 -3
  282. package/dist/features/composites/node-views.js +7 -3
  283. package/dist/features/composites/node-views.js.map +1 -1
  284. package/dist/features/composites/nodes.d.ts +6 -1
  285. package/dist/features/composites/nodes.js +33 -7
  286. package/dist/features/composites/nodes.js.map +1 -1
  287. package/dist/features/composites/react/BibliographyEditor.js +229 -0
  288. package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
  289. package/dist/features/composites/react/CompositeChrome.js +65 -17
  290. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  291. package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
  292. package/dist/features/composites/react/CompositeDetails.js +45 -6
  293. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  294. package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
  295. package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
  296. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  297. package/dist/features/composites/react/CompositeInspector.js +11 -5
  298. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  299. package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
  300. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  301. package/dist/features/composites/react/CompositeTable.js +16 -2
  302. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  303. package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
  304. package/dist/features/composites/react/CompositeView.js +6 -1
  305. package/dist/features/composites/react/CompositeView.js.map +1 -1
  306. package/dist/features/composites/react/DerivationBadge.js +116 -0
  307. package/dist/features/composites/react/DerivationBadge.js.map +1 -0
  308. package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
  309. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
  310. package/dist/features/composites/react/ReferenceView.d.ts +11 -0
  311. package/dist/features/composites/react/ReferenceView.js +115 -0
  312. package/dist/features/composites/react/ReferenceView.js.map +1 -0
  313. package/dist/features/composites/react/SettingsEditingTab.js +2 -2
  314. package/dist/features/composites/react.d.ts +2 -1
  315. package/dist/features/composites/react.js +7 -3
  316. package/dist/features/composites/react.js.map +1 -1
  317. package/dist/features/composites/reference-ai.js +16 -0
  318. package/dist/features/composites/reference-ai.js.map +1 -0
  319. package/dist/features/composites/reference-label.d.ts +19 -0
  320. package/dist/features/composites/reference-label.js +58 -0
  321. package/dist/features/composites/reference-label.js.map +1 -0
  322. package/dist/features/composites/reference-suggestion.d.ts +10 -0
  323. package/dist/features/composites/reference-suggestion.js +41 -0
  324. package/dist/features/composites/reference-suggestion.js.map +1 -0
  325. package/dist/features/composites/reference-text.d.ts +18 -0
  326. package/dist/features/composites/reference-text.js +89 -0
  327. package/dist/features/composites/reference-text.js.map +1 -0
  328. package/dist/features/composites/reference.d.ts +44 -0
  329. package/dist/features/composites/reference.js +148 -0
  330. package/dist/features/composites/reference.js.map +1 -0
  331. package/dist/features/composites/reveal-source.d.ts +32 -0
  332. package/dist/features/composites/reveal-source.js +89 -0
  333. package/dist/features/composites/reveal-source.js.map +1 -0
  334. package/dist/features/composites/rows.d.ts +21 -2
  335. package/dist/features/composites/rows.js +63 -9
  336. package/dist/features/composites/rows.js.map +1 -1
  337. package/dist/features/composites/schema.d.ts +9 -3
  338. package/dist/features/composites/schema.js +11 -4
  339. package/dist/features/composites/schema.js.map +1 -1
  340. package/dist/features/composites/table-model.js +12 -21
  341. package/dist/features/composites/table-model.js.map +1 -1
  342. package/dist/features/composites/vue.d.ts +3 -2
  343. package/dist/features/composites/vue.js +8 -4
  344. package/dist/features/composites/vue.js.map +1 -1
  345. package/dist/features/media/assets.d.ts +2 -0
  346. package/dist/features/media/assets.js +3 -2
  347. package/dist/features/media/assets.js.map +1 -1
  348. package/dist/features/media/components/FileView.js +13 -4
  349. package/dist/features/media/components/FileView.js.map +1 -1
  350. package/dist/features/media/feature.d.ts +3 -3
  351. package/dist/features/media/feature.js +21 -28
  352. package/dist/features/media/feature.js.map +1 -1
  353. package/dist/features/media/file-import.js +2 -2
  354. package/dist/features/media/file-import.js.map +1 -1
  355. package/dist/features/media/index.d.ts +2 -2
  356. package/dist/features/media/index.js +2 -2
  357. package/dist/features/media/react/FileView.js +19 -2
  358. package/dist/features/media/react/FileView.js.map +1 -1
  359. package/dist/features/media/schema.d.ts +8 -0
  360. package/dist/features/media/schema.js +126 -11
  361. package/dist/features/media/schema.js.map +1 -1
  362. package/dist/features/media/upload.d.ts +16 -14
  363. package/dist/features/media/upload.js +13 -19
  364. package/dist/features/media/upload.js.map +1 -1
  365. package/dist/features/mentions/index.d.ts +4 -3
  366. package/dist/features/mentions/index.js +4 -1
  367. package/dist/features/mentions/index.js.map +1 -1
  368. package/dist/features/mentions/mention-text.d.ts +1 -1
  369. package/dist/features/mentions/mention-text.js.map +1 -1
  370. package/dist/features/mentions/mention.d.ts +0 -6
  371. package/dist/features/mentions/mention.js.map +1 -1
  372. package/dist/features/mentions/metadata-defaults.d.ts +2 -2
  373. package/dist/features/mentions/metadata-defaults.js +3 -9
  374. package/dist/features/mentions/metadata-defaults.js.map +1 -1
  375. package/dist/features/mentions/schema.d.ts +2 -1
  376. package/dist/features/mentions/schema.js.map +1 -1
  377. package/dist/features/tables/feature.d.ts +2 -1
  378. package/dist/features/tables/feature.js +90 -70
  379. package/dist/features/tables/feature.js.map +1 -1
  380. package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
  381. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  382. package/dist/features/themes/feature.js +2 -0
  383. package/dist/features/themes/feature.js.map +1 -1
  384. package/dist/features/themes/index.d.ts +2 -1
  385. package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
  386. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  387. package/dist/features/themes/schema.d.ts +2 -6
  388. package/dist/features/themes/schema.js.map +1 -1
  389. package/dist/features/themes/theme-runtime.d.ts +1 -1
  390. package/dist/features/themes/theme-runtime.js +4 -2
  391. package/dist/features/themes/theme-runtime.js.map +1 -1
  392. package/dist/features/transcript/TranscriptSegmentView.js +48 -28
  393. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  394. package/dist/features/transcript/feature.js +21 -19
  395. package/dist/features/transcript/feature.js.map +1 -1
  396. package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
  397. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  398. package/dist/features/transcript/segment-node.d.ts +2 -0
  399. package/dist/features/transcript/segment-node.js +35 -7
  400. package/dist/features/transcript/segment-node.js.map +1 -1
  401. package/dist/i18n/de/composites.js +91 -1
  402. package/dist/i18n/de/composites.js.map +1 -1
  403. package/dist/i18n/de/integrations.js +49 -8
  404. package/dist/i18n/de/integrations.js.map +1 -1
  405. package/dist/i18n/de/media.js +0 -1
  406. package/dist/i18n/de/media.js.map +1 -1
  407. package/dist/i18n/de/palette.js +22 -0
  408. package/dist/i18n/de/palette.js.map +1 -0
  409. package/dist/i18n/de/tables.js +0 -1
  410. package/dist/i18n/de/tables.js.map +1 -1
  411. package/dist/i18n/de/transcript.js +2 -1
  412. package/dist/i18n/de/transcript.js.map +1 -1
  413. package/dist/i18n/de/tree.js +3 -0
  414. package/dist/i18n/de/tree.js.map +1 -1
  415. package/dist/i18n/de.d.ts +157 -11
  416. package/dist/i18n/de.js +3 -1
  417. package/dist/i18n/de.js.map +1 -1
  418. package/dist/i18n/en/composites.js +89 -1
  419. package/dist/i18n/en/composites.js.map +1 -1
  420. package/dist/i18n/en/integrations.js +49 -8
  421. package/dist/i18n/en/integrations.js.map +1 -1
  422. package/dist/i18n/en/media.js +0 -1
  423. package/dist/i18n/en/media.js.map +1 -1
  424. package/dist/i18n/en/palette.js +22 -0
  425. package/dist/i18n/en/palette.js.map +1 -0
  426. package/dist/i18n/en/tables.js +0 -1
  427. package/dist/i18n/en/tables.js.map +1 -1
  428. package/dist/i18n/en/transcript.js +2 -1
  429. package/dist/i18n/en/transcript.js.map +1 -1
  430. package/dist/i18n/en/tree.js +3 -0
  431. package/dist/i18n/en/tree.js.map +1 -1
  432. package/dist/i18n/en.js +3 -1
  433. package/dist/i18n/en.js.map +1 -1
  434. package/dist/index.d.ts +4 -3
  435. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
  436. package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
  437. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
  438. package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
  439. package/dist/integrations/composite-ai/composite-fill.js +97 -56
  440. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  441. package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
  442. package/dist/integrations/composite-ai/composite-summary.js +7 -26
  443. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  444. package/dist/integrations/composite-ai/derive.d.ts +29 -0
  445. package/dist/integrations/composite-ai/derive.js +173 -0
  446. package/dist/integrations/composite-ai/derive.js.map +1 -0
  447. package/dist/integrations/composite-ai/index.d.ts +11 -7
  448. package/dist/integrations/composite-ai/index.js +72 -14
  449. package/dist/integrations/composite-ai/index.js.map +1 -1
  450. package/dist/integrations/composite-ai/rendition.d.ts +45 -0
  451. package/dist/integrations/composite-ai/rendition.js +314 -0
  452. package/dist/integrations/composite-ai/rendition.js.map +1 -0
  453. package/dist/integrations/composite-media/index.d.ts +7 -5
  454. package/dist/integrations/composite-media/index.js +86 -13
  455. package/dist/integrations/composite-media/index.js.map +1 -1
  456. package/dist/integrations/transcript-ai/index.d.ts +1 -1
  457. package/dist/integrations/transcript-ai/index.js +5 -2
  458. package/dist/integrations/transcript-ai/index.js.map +1 -1
  459. package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
  460. package/dist/integrations/transcript-ai/protocol.js +0 -2
  461. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  462. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
  463. package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
  464. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
  465. package/dist/markdown/MarkdownSurface.vue.d.ts +6 -5
  466. package/dist/markdown/MarkdownSurface2.js +3 -6
  467. package/dist/markdown/MarkdownSurface2.js.map +1 -1
  468. package/dist/markdown/document.js +1 -1
  469. package/dist/markdown/source-mode.d.ts +1 -1
  470. package/dist/nuxt/module.d.ts +1402 -54
  471. package/dist/nuxt/module.js +6 -49
  472. package/dist/nuxt/module.js.map +1 -1
  473. package/dist/nuxt/runtime/install-context.d.ts +7 -3
  474. package/dist/nuxt/runtime/install-context.js +14 -8
  475. package/dist/nuxt/runtime/install-context.js.map +1 -1
  476. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
  477. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
  478. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  479. package/dist/presets/compose.d.ts +94 -0
  480. package/dist/presets/compose.js +164 -0
  481. package/dist/presets/compose.js.map +1 -0
  482. package/dist/react/AdvancedEditor.d.ts +15 -15
  483. package/dist/react/AdvancedEditor.js +15 -13
  484. package/dist/react/AdvancedEditor.js.map +1 -1
  485. package/dist/react/EditorCommandPalette.d.ts +13 -0
  486. package/dist/react/EditorCommandPalette.js +118 -0
  487. package/dist/react/EditorCommandPalette.js.map +1 -0
  488. package/dist/react/EditorSettingsModal.d.ts +5 -5
  489. package/dist/react/EditorSettingsModal.js +7 -4
  490. package/dist/react/EditorSettingsModal.js.map +1 -1
  491. package/dist/react/EditorSettingsPanel.d.ts +7 -7
  492. package/dist/react/EditorSettingsPanel.js +8 -5
  493. package/dist/react/EditorSettingsPanel.js.map +1 -1
  494. package/dist/react/EditorSurface.d.ts +7 -7
  495. package/dist/react/EditorSurface.js +10 -6
  496. package/dist/react/EditorSurface.js.map +1 -1
  497. package/dist/react/EditorToolbar.d.ts +4 -6
  498. package/dist/react/EditorToolbar.js +29 -37
  499. package/dist/react/EditorToolbar.js.map +1 -1
  500. package/dist/react/SimpleEditor.d.ts +22 -23
  501. package/dist/react/SimpleEditor.js +21 -7
  502. package/dist/react/SimpleEditor.js.map +1 -1
  503. package/dist/react/advanced.d.ts +4 -3
  504. package/dist/react/advanced.js +2 -2
  505. package/dist/react/compose.d.ts +20 -0
  506. package/dist/react/compose.js +49 -0
  507. package/dist/react/compose.js.map +1 -0
  508. package/dist/react/composed-surface.d.ts +39 -0
  509. package/dist/react/composed-surface.js +71 -0
  510. package/dist/react/composed-surface.js.map +1 -0
  511. package/dist/react/context.js +6 -2
  512. package/dist/react/context.js.map +1 -1
  513. package/dist/react/index.d.ts +2 -1
  514. package/dist/react/index.js +2 -1
  515. package/dist/react/key-labels.js +15 -0
  516. package/dist/react/key-labels.js.map +1 -0
  517. package/dist/react/settings.d.ts +1 -1
  518. package/dist/react/simple.d.ts +2 -2
  519. package/dist/react/ui/command.js +36 -1
  520. package/dist/react/ui/command.js.map +1 -1
  521. package/dist/schema/advanced.d.ts +6 -6
  522. package/dist/schema/advanced.js +26 -29
  523. package/dist/schema/advanced.js.map +1 -1
  524. package/dist/server/ai.d.ts +5 -16
  525. package/dist/server/ai.js.map +1 -1
  526. package/dist/server/auth.d.ts +9 -0
  527. package/dist/server/collab-state.d.ts +1 -1
  528. package/dist/server/collab-state.js.map +1 -1
  529. package/dist/server/collab.d.ts +13 -13
  530. package/dist/server/collab.js +34 -18
  531. package/dist/server/collab.js.map +1 -1
  532. package/dist/server/crossref.js +78 -0
  533. package/dist/server/crossref.js.map +1 -0
  534. package/dist/server/errors.d.ts +1 -1
  535. package/dist/server/errors.js +1 -1
  536. package/dist/server/errors.js.map +1 -1
  537. package/dist/server/extract.js +308 -0
  538. package/dist/server/extract.js.map +1 -0
  539. package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
  540. package/dist/server/h3.js.map +1 -0
  541. package/dist/server/handlers/ai.d.ts +30 -9
  542. package/dist/server/handlers/ai.js +170 -36
  543. package/dist/server/handlers/ai.js.map +1 -1
  544. package/dist/server/handlers/context.d.ts +12 -5
  545. package/dist/server/handlers/context.js +10 -4
  546. package/dist/server/handlers/context.js.map +1 -1
  547. package/dist/server/handlers/editor.d.ts +19 -13
  548. package/dist/server/handlers/editor.js +49 -38
  549. package/dist/server/handlers/editor.js.map +1 -1
  550. package/dist/server/handlers/index.d.ts +4 -4
  551. package/dist/server/handlers/index.js +4 -4
  552. package/dist/server/host.d.ts +16 -25
  553. package/dist/server/host.js +58 -41
  554. package/dist/server/host.js.map +1 -1
  555. package/dist/server/index.d.ts +6 -4
  556. package/dist/server/index.js +3 -2
  557. package/dist/server/pdf.js +97 -0
  558. package/dist/server/pdf.js.map +1 -0
  559. package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
  560. package/dist/server/ports.js +29 -0
  561. package/dist/server/ports.js.map +1 -0
  562. package/dist/server/routes.js +144 -0
  563. package/dist/server/routes.js.map +1 -0
  564. package/dist/settings/index.d.ts +1 -1
  565. package/dist/settings/index.js.map +1 -1
  566. package/dist/styles/composites.css +25 -0
  567. package/dist/tree/DocumentTree.js +56 -29
  568. package/dist/tree/DocumentTree.js.map +1 -1
  569. package/dist/tree/react/DocumentTree.js +23 -0
  570. package/dist/tree/react/DocumentTree.js.map +1 -1
  571. package/dist/tree/tree-host.js +2 -2
  572. package/dist/tree/tree-items.js +1 -1
  573. package/dist/vue/AdvancedEditor.js +30 -9
  574. package/dist/vue/AdvancedEditor.js.map +1 -1
  575. package/dist/vue/AdvancedEditor.vue.d.ts +28 -8
  576. package/dist/vue/SimpleEditor.js +62 -16
  577. package/dist/vue/SimpleEditor.js.map +1 -1
  578. package/dist/vue/SimpleEditor.vue.d.ts +37 -7
  579. package/dist/vue/advanced.d.ts +3 -2
  580. package/dist/vue/advanced.js +2 -2
  581. package/dist/vue/components/EditorCommandPalette.js +91 -0
  582. package/dist/vue/components/EditorCommandPalette.js.map +1 -0
  583. package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
  584. package/dist/vue/components/EditorSettingsModal.js +10 -6
  585. package/dist/vue/components/EditorSettingsModal.js.map +1 -1
  586. package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
  587. package/dist/vue/components/EditorSettingsPanel.js +10 -6
  588. package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
  589. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
  590. package/dist/vue/components/EditorSurface.js +11 -6
  591. package/dist/vue/components/EditorSurface.js.map +1 -1
  592. package/dist/vue/components/EditorSurface.vue.d.ts +7 -7
  593. package/dist/vue/components/EditorToolbar.js +41 -49
  594. package/dist/vue/components/EditorToolbar.js.map +1 -1
  595. package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
  596. package/dist/vue/compose.d.ts +17 -0
  597. package/dist/vue/compose.js +51 -0
  598. package/dist/vue/compose.js.map +1 -0
  599. package/dist/vue/context-key.d.ts +6 -0
  600. package/dist/vue/context-key.js +6 -0
  601. package/dist/vue/context-key.js.map +1 -0
  602. package/dist/vue/context.d.ts +8 -6
  603. package/dist/vue/context.js +6 -5
  604. package/dist/vue/context.js.map +1 -1
  605. package/dist/vue/index.d.ts +10 -8
  606. package/dist/vue/index.js +10 -8
  607. package/dist/vue/kit/index.d.ts +2 -2
  608. package/dist/vue/kit/nuxt-ui.js +23 -0
  609. package/dist/vue/kit/nuxt-ui.js.map +1 -1
  610. package/dist/vue/kit/types.d.ts +40 -1
  611. package/package.json +30 -16
  612. package/dist/adapters/comments/client.d.ts +0 -64
  613. package/dist/adapters/comments/client.js.map +0 -1
  614. package/dist/adapters/routes.js.map +0 -1
  615. package/dist/core/toolbar.d.ts +0 -74
  616. package/dist/core/toolbar.js +0 -45
  617. package/dist/core/toolbar.js.map +0 -1
  618. package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
  619. package/dist/integrations/composite-ai/document-transcribe.js +0 -89
  620. package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
  621. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
  622. package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
  623. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
  624. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
  625. package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
  626. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
  627. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
  628. package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
  629. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
  630. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
  631. package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
  632. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
  633. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
  634. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
  635. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
  636. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
  637. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
  638. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
  639. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
  640. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
  641. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
  642. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
  643. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
  644. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
  645. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
  646. package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
  647. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
  648. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
  649. package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
  650. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
  651. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
  652. package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
  653. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
  654. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
  655. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
  656. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
  657. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
  658. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
  659. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
  660. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
  661. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
  662. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
  663. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
  664. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
  665. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
  666. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
  667. package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
  668. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
  669. package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
  670. package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
  671. package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
  672. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
  673. package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
  674. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
  675. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
  676. package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
  677. package/dist/presets/advanced.d.ts +0 -33
  678. package/dist/presets/advanced.js +0 -50
  679. package/dist/presets/advanced.js.map +0 -1
  680. package/dist/presets/simple.d.ts +0 -9
  681. package/dist/presets/simple.js +0 -12
  682. package/dist/presets/simple.js.map +0 -1
  683. package/dist/react/advanced-preset.d.ts +0 -11
  684. package/dist/react/advanced-preset.js +0 -31
  685. package/dist/react/advanced-preset.js.map +0 -1
  686. package/dist/vue/advanced-preset.d.ts +0 -13
  687. package/dist/vue/advanced-preset.js +0 -33
  688. package/dist/vue/advanced-preset.js.map +0 -1
  689. package/docs/extending.md +0 -538
  690. package/docs/integration.md +0 -632
  691. package/docs/llms.md +0 -383
  692. package/docs/usage.md +0 -1076
@@ -1 +1 @@
1
- {"version":3,"file":"upload.js","names":[],"sources":["../../../src/features/media/upload.ts"],"sourcesContent":["import { type Editor, Extension, type JSONContent } from '@tiptap/core'\nimport type { FeatureContext, UploadFn } from '../../core'\nimport { SCRATCH_DOCUMENT_ID } from './assets'\nimport { mediaKindOf, mediaMime } from './schema'\n\n/** `importFiles` of the media feature: imports `files` at `pos` (default: the selection), toasting failures; resolves whether every file was imported. */\nexport type ImportFilesFn = (editor: Editor, files: File[] | FileList, pos?: number) => Promise<boolean>\n\n/** Default upload: POSTs `file` to the asset endpoint of document `documentId` through the context's HTTP client (size limits per kind, see schema.ts). */\nexport async function postUpload(fetch: FeatureContext['fetch'], documentId: string, file: File): Promise<{ url: string }> {\n const body = new FormData()\n body.append('file', file)\n return await fetch<{ url: string }>(`/api/editor/assets/${documentId}`, { method: 'POST', body })\n}\n\n/** The upload of a surface: the host's `upload` prop, else the asset endpoint of its `documentId` (default: the shared `scratch` owner). */\nexport const uploadOf = (ctx: FeatureContext): UploadFn =>\n ctx.upload ?? (file => postUpload(ctx.fetch, ctx.documentId ?? SCRATCH_DOCUMENT_ID, file))\n\n/** Makes the upload and the file import reachable as `editor.storage.media` (drop/paste, toolbar, slash menu, recorder). */\nexport const Media = Extension.create<{ upload: UploadFn, importFiles: ImportFilesFn }, { upload: UploadFn, importFiles: ImportFilesFn }>({\n name: 'media',\n\n addOptions() {\n return {\n upload: () => Promise.reject(new Error('@ecsplico/editor: Media.configure({ upload }) fehlt')),\n importFiles: () => Promise.reject(new Error('@ecsplico/editor: Media.configure({ importFiles }) fehlt')),\n }\n },\n\n addStorage() {\n return { upload: this.options.upload, importFiles: this.options.importFiles }\n },\n})\n\ndeclare module '@tiptap/core' {\n interface Storage {\n media: { upload: UploadFn, importFiles: ImportFilesFn }\n }\n}\n\n/** The node that embeds uploaded `file` from `url`; null for files uploads do not accept. */\nexport function mediaNodeFor(file: File, url: string): JSONContent | null {\n const kind = mediaKindOf(file.type)\n if (!kind) return null\n if (kind === 'image') return { type: 'image', attrs: { src: url, alt: file.name } }\n return { type: kind, attrs: { src: url, title: file.name, mimeType: mediaMime(file.type) } }\n}\n\n/** The file block for an uploaded `file` that has no player or image node (PDF, office documents, …). */\nexport function fileNodeFor(file: File, url: string): JSONContent {\n return {\n type: 'file',\n attrs: { src: url, title: file.name, mimeType: mediaMime(file.type) || 'application/octet-stream', size: file.size },\n }\n}\n\n/** Human-readable byte count: 1024-based, one decimal (`1536` → `1.5 KB`), units B/KB/MB/GB. */\nexport function formatBytes(bytes: number): string {\n const units = ['B', 'KB', 'MB', 'GB']\n let value = Math.max(0, bytes)\n let unit = 0\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit++\n }\n return unit === 0 ? `${value} B` : `${value.toFixed(1)} ${units[unit]}`\n}\n\n/** Transaction meta the recorder modal (AudioRecorderModal) listens for: open it, inserting at the current caret. */\nexport const AUDIO_RECORDER_OPEN = 'audioRecorderOpen'\n\nexport function openAudioRecorder(editor: Editor): void {\n editor.view.dispatch(editor.state.tr.setMeta(AUDIO_RECORDER_OPEN, true).setMeta('addToHistory', false))\n}\n\n/** Opens the browser's file picker; resolves the chosen files, `[]` when the picker is cancelled. */\nexport function pickFiles(accept: string, multiple = true): Promise<File[]> {\n const { promise, resolve } = Promise.withResolvers<File[]>()\n const input = document.createElement('input')\n input.type = 'file'\n input.accept = accept\n input.multiple = multiple\n input.hidden = true\n // Attached while open: some Safari versions fire no `change` for detached inputs.\n document.body.append(input)\n const done = (files: File[]) => {\n input.remove()\n resolve(files)\n }\n input.addEventListener('change', () => done(Array.from(input.files ?? [])), { once: true })\n input.addEventListener('cancel', () => done([]), { once: true })\n input.click()\n return promise\n}\n"],"mappings":";;;;;AASA,eAAsB,WAAW,OAAgC,YAAoB,MAAsC;CACzH,MAAM,OAAO,IAAI,SAAS;CAC1B,KAAK,OAAO,QAAQ,IAAI;CACxB,OAAO,MAAM,MAAuB,sBAAsB,cAAc;EAAE,QAAQ;EAAQ;CAAK,CAAC;AAClG;;AAGA,MAAa,YAAY,QACvB,IAAI,YAAW,SAAQ,WAAW,IAAI,OAAO,IAAI,cAAA,WAAmC,IAAI;;AAG1F,MAAa,QAAQ,UAAU,OAA2G;CACxI,MAAM;CAEN,aAAa;EACX,OAAO;GACL,cAAc,QAAQ,uBAAO,IAAI,MAAM,qDAAqD,CAAC;GAC7F,mBAAmB,QAAQ,uBAAO,IAAI,MAAM,0DAA0D,CAAC;EACzG;CACF;CAEA,aAAa;EACX,OAAO;GAAE,QAAQ,KAAK,QAAQ;GAAQ,aAAa,KAAK,QAAQ;EAAY;CAC9E;AACF,CAAC;;AASD,SAAgB,aAAa,MAAY,KAAiC;CACxE,MAAM,OAAO,YAAY,KAAK,IAAI;CAClC,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,SAAS,SAAS,OAAO;EAAE,MAAM;EAAS,OAAO;GAAE,KAAK;GAAK,KAAK,KAAK;EAAK;CAAE;CAClF,OAAO;EAAE,MAAM;EAAM,OAAO;GAAE,KAAK;GAAK,OAAO,KAAK;GAAM,UAAU,UAAU,KAAK,IAAI;EAAE;CAAE;AAC7F;;AAGA,SAAgB,YAAY,MAAY,KAA0B;CAChE,OAAO;EACL,MAAM;EACN,OAAO;GAAE,KAAK;GAAK,OAAO,KAAK;GAAM,UAAU,UAAU,KAAK,IAAI,KAAK;GAA4B,MAAM,KAAK;EAAK;CACrH;AACF;;AAGA,SAAgB,YAAY,OAAuB;CACjD,MAAM,QAAQ;EAAC;EAAK;EAAM;EAAM;CAAI;CACpC,IAAI,QAAQ,KAAK,IAAI,GAAG,KAAK;CAC7B,IAAI,OAAO;CACX,OAAO,SAAS,QAAQ,OAAO,MAAM,SAAS,GAAG;EAC/C,SAAS;EACT;CACF;CACA,OAAO,SAAS,IAAI,GAAG,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAE,GAAG,MAAM;AAClE;;AAGA,MAAa,sBAAsB;AAEnC,SAAgB,kBAAkB,QAAsB;CACtD,OAAO,KAAK,SAAS,OAAO,MAAM,GAAG,QAAQ,qBAAqB,IAAI,CAAC,CAAC,QAAQ,gBAAgB,KAAK,CAAC;AACxG;;AAGA,SAAgB,UAAU,QAAgB,WAAW,MAAuB;CAC1E,MAAM,EAAE,SAAS,YAAY,QAAQ,cAAsB;CAC3D,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,OAAO;CACb,MAAM,SAAS;CACf,MAAM,WAAW;CACjB,MAAM,SAAS;CAEf,SAAS,KAAK,OAAO,KAAK;CAC1B,MAAM,QAAQ,UAAkB;EAC9B,MAAM,OAAO;EACb,QAAQ,KAAK;CACf;CACA,MAAM,iBAAiB,gBAAgB,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;CAC1F,MAAM,iBAAiB,gBAAgB,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,MAAM;CACZ,OAAO;AACT"}
1
+ {"version":3,"file":"upload.js","names":[],"sources":["../../../src/features/media/upload.ts"],"sourcesContent":["import { type Editor, Extension, type JSONContent } from '@tiptap/core'\nimport type { FeatureContext } from '../../core'\nimport { SCRATCH_DOCUMENT_ID } from './assets'\nimport { mediaKindOf, mediaMime } from './schema'\n\n/** Stores a file and resolves with the URL it is addressed by. */\ntype UploadFile = (file: File) => Promise<{ url: string, sha256?: string }>\n\n/** `importFiles` of the media feature: imports `files` at `pos` (default: the selection), toasting failures; resolves whether every file was imported. */\nexport type ImportFilesFn = (editor: Editor, files: File[] | FileList, pos?: number) => Promise<boolean>\n\n/** The upload of a surface: the backend's asset store for its `documentId` (default: the shared `scratch` owner; size limits per kind, see schema.ts). */\nexport const uploadOf = (ctx: FeatureContext): UploadFile =>\n file => ctx.backend.assets\n ? ctx.backend.assets.upload(ctx.documentId ?? SCRATCH_DOCUMENT_ID, file)\n : Promise.reject(new Error(`@ecsplico/editor: backend \"${ctx.backend.name}\" has no assets capability`))\n\n/** Makes the upload and the file import reachable as `editor.storage.media` (drop/paste, toolbar, slash menu, recorder). */\nexport const Media = Extension.create<{ upload: UploadFile, importFiles: ImportFilesFn }, { upload: UploadFile, importFiles: ImportFilesFn }>({\n name: 'media',\n\n addOptions() {\n return {\n upload: () => Promise.reject(new Error('@ecsplico/editor: Media.configure({ upload }) fehlt')),\n importFiles: () => Promise.reject(new Error('@ecsplico/editor: Media.configure({ importFiles }) fehlt')),\n }\n },\n\n addStorage() {\n return { upload: this.options.upload, importFiles: this.options.importFiles }\n },\n})\n\ndeclare module '@tiptap/core' {\n interface Storage {\n media: { upload: UploadFile, importFiles: ImportFilesFn }\n }\n}\n\n/** The node that embeds uploaded `file` from `url`, remembering the upload's `hash` (SHA-256 hex; none when the backend did not report one); null for files uploads do not accept. */\nexport function mediaNodeFor(file: File, url: string, hash?: string): JSONContent | null {\n const kind = mediaKindOf(file.type)\n if (!kind) return null\n if (kind === 'image') return { type: 'image', attrs: { src: url, alt: file.name, hash: hash ?? null } }\n return { type: kind, attrs: { src: url, title: file.name, mimeType: mediaMime(file.type), hash: hash ?? null } }\n}\n\n/** The file block for an uploaded `file` that has no player or image node (PDF, office documents, …); `hash` as in `mediaNodeFor`. */\nexport function fileNodeFor(file: File, url: string, hash?: string): JSONContent {\n return {\n type: 'file',\n attrs: { src: url, title: file.name, mimeType: mediaMime(file.type) || 'application/octet-stream', size: file.size, hash: hash ?? null },\n }\n}\n\n/** Human-readable byte count: 1024-based, one decimal (`1536` → `1.5 KB`), units B/KB/MB/GB. */\nexport function formatBytes(bytes: number): string {\n const units = ['B', 'KB', 'MB', 'GB']\n let value = Math.max(0, bytes)\n let unit = 0\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024\n unit++\n }\n return unit === 0 ? `${value} B` : `${value.toFixed(1)} ${units[unit]}`\n}\n\n/** Transaction meta the recorder modal (AudioRecorderModal) listens for: open it, inserting at the current caret. */\nexport const AUDIO_RECORDER_OPEN = 'audioRecorderOpen'\n\nexport function openAudioRecorder(editor: Editor): void {\n editor.view.dispatch(editor.state.tr.setMeta(AUDIO_RECORDER_OPEN, true).setMeta('addToHistory', false))\n}\n\n/** Opens the browser's file picker; resolves the chosen files, `[]` when the picker is cancelled. */\nexport function pickFiles(accept: string, multiple = true): Promise<File[]> {\n const { promise, resolve } = Promise.withResolvers<File[]>()\n const input = document.createElement('input')\n input.type = 'file'\n input.accept = accept\n input.multiple = multiple\n input.hidden = true\n // Attached while open: some Safari versions fire no `change` for detached inputs.\n document.body.append(input)\n const done = (files: File[]) => {\n input.remove()\n resolve(files)\n }\n input.addEventListener('change', () => done(Array.from(input.files ?? [])), { once: true })\n input.addEventListener('cancel', () => done([]), { once: true })\n input.click()\n return promise\n}\n"],"mappings":";;;;;AAYA,MAAa,YAAY,SACvB,SAAQ,IAAI,QAAQ,SAChB,IAAI,QAAQ,OAAO,OAAO,IAAI,cAAA,WAAmC,IAAI,IACrE,QAAQ,uBAAO,IAAI,MAAM,8BAA8B,IAAI,QAAQ,KAAK,2BAA2B,CAAC;;AAG1G,MAAa,QAAQ,UAAU,OAA+G;CAC5I,MAAM;CAEN,aAAa;EACX,OAAO;GACL,cAAc,QAAQ,uBAAO,IAAI,MAAM,qDAAqD,CAAC;GAC7F,mBAAmB,QAAQ,uBAAO,IAAI,MAAM,0DAA0D,CAAC;EACzG;CACF;CAEA,aAAa;EACX,OAAO;GAAE,QAAQ,KAAK,QAAQ;GAAQ,aAAa,KAAK,QAAQ;EAAY;CAC9E;AACF,CAAC;;AASD,SAAgB,aAAa,MAAY,KAAa,MAAmC;CACvF,MAAM,OAAO,YAAY,KAAK,IAAI;CAClC,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,SAAS,SAAS,OAAO;EAAE,MAAM;EAAS,OAAO;GAAE,KAAK;GAAK,KAAK,KAAK;GAAM,MAAM,QAAQ;EAAK;CAAE;CACtG,OAAO;EAAE,MAAM;EAAM,OAAO;GAAE,KAAK;GAAK,OAAO,KAAK;GAAM,UAAU,UAAU,KAAK,IAAI;GAAG,MAAM,QAAQ;EAAK;CAAE;AACjH;;AAGA,SAAgB,YAAY,MAAY,KAAa,MAA4B;CAC/E,OAAO;EACL,MAAM;EACN,OAAO;GAAE,KAAK;GAAK,OAAO,KAAK;GAAM,UAAU,UAAU,KAAK,IAAI,KAAK;GAA4B,MAAM,KAAK;GAAM,MAAM,QAAQ;EAAK;CACzI;AACF;;AAGA,SAAgB,YAAY,OAAuB;CACjD,MAAM,QAAQ;EAAC;EAAK;EAAM;EAAM;CAAI;CACpC,IAAI,QAAQ,KAAK,IAAI,GAAG,KAAK;CAC7B,IAAI,OAAO;CACX,OAAO,SAAS,QAAQ,OAAO,MAAM,SAAS,GAAG;EAC/C,SAAS;EACT;CACF;CACA,OAAO,SAAS,IAAI,GAAG,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAE,GAAG,MAAM;AAClE;;AAGA,MAAa,sBAAsB;AAEnC,SAAgB,kBAAkB,QAAsB;CACtD,OAAO,KAAK,SAAS,OAAO,MAAM,GAAG,QAAQ,qBAAqB,IAAI,CAAC,CAAC,QAAQ,gBAAgB,KAAK,CAAC;AACxG;;AAGA,SAAgB,UAAU,QAAgB,WAAW,MAAuB;CAC1E,MAAM,EAAE,SAAS,YAAY,QAAQ,cAAsB;CAC3D,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,OAAO;CACb,MAAM,SAAS;CACf,MAAM,WAAW;CACjB,MAAM,SAAS;CAEf,SAAS,KAAK,OAAO,KAAK;CAC1B,MAAM,QAAQ,UAAkB;EAC9B,MAAM,OAAO;EACb,QAAQ,KAAK;CACf;CACA,MAAM,iBAAiB,gBAAgB,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;CAC1F,MAAM,iBAAiB,gBAAgB,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,MAAM;CACZ,OAAO;AACT"}
@@ -1,9 +1,10 @@
1
1
  import { EditorFeature } from "../../core/feature.js";
2
- import "../../core/index.js";
3
- import { MentionAttrs, MentionNode, MentionUser } from "./mention.js";
2
+ import { MentionUser } from "../../core/backend-types.js";
3
+ import { MentionAttrs, MentionNode } from "./mention.js";
4
4
  import { MentionTarget, mentionUsersIn, mentionsFromText } from "./mention-text.js";
5
5
  import { TAG_NODE, TagAttrs, TagNode, TagOptions, TagSuggestionItem, TagSuggestionPluginKey, collectTagNames, normalizeTagName, tagsFromText } from "./tag.js";
6
6
  import "./schema.js";
7
+ import "../../core/index.js";
7
8
  import { mentionSuggestion } from "./mention-suggestion.js";
8
9
  import { mentionsMetadata } from "./metadata-defaults.js";
9
10
  import { normalizeMentions } from "./normalize.js";
@@ -11,7 +12,7 @@ import { tagSuggestion } from "./tag-suggestion.js";
11
12
  //#region src/features/mentions/index.d.ts
12
13
  /**
13
14
  * `@user` mention chips and `#tag` chips with their suggestion popups. Without a host list, `mentions` and `hashtags`
14
- * come from `/api/editor/users` and `/api/editor/tags` through the context's HTTP client, and only once a popup is used.
15
+ * come from the context's backend (`users` and `tags`; the HTTP backend asks `/api/editor/users` and `/api/editor/tags`), and only once a popup is used.
15
16
  * Contributes the `normalize` stage: `@name` / `#tag` text of imported or AI-written Markdown becomes chips when it
16
17
  * matches a known user or an existing tag. Contributes no components (`EditorFeature<never>`), so it fits a preset of
17
18
  * any framework layer.
@@ -2,6 +2,7 @@ import { metadataOf } from "../../core/metadata.js";
2
2
  import { MentionNode } from "./mention.js";
3
3
  import { TAG_NODE, TagNode, TagSuggestionPluginKey, collectTagNames, normalizeTagName, tagsFromText } from "./tag.js";
4
4
  import { mentionUsersIn, mentionsFromText } from "./mention-text.js";
5
+ import { mentionsSchemaExtensions } from "./schema.js";
5
6
  import { mentionSuggestion } from "./mention-suggestion.js";
6
7
  import { mentionsMetadata } from "./metadata-defaults.js";
7
8
  import { normalizeMentions } from "./normalize.js";
@@ -9,7 +10,7 @@ import { tagSuggestion } from "./tag-suggestion.js";
9
10
  //#region src/features/mentions/index.ts
10
11
  /**
11
12
  * `@user` mention chips and `#tag` chips with their suggestion popups. Without a host list, `mentions` and `hashtags`
12
- * come from `/api/editor/users` and `/api/editor/tags` through the context's HTTP client, and only once a popup is used.
13
+ * come from the context's backend (`users` and `tags`; the HTTP backend asks `/api/editor/users` and `/api/editor/tags`), and only once a popup is used.
13
14
  * Contributes the `normalize` stage: `@name` / `#tag` text of imported or AI-written Markdown becomes chips when it
14
15
  * matches a known user or an existing tag. Contributes no components (`EditorFeature<never>`), so it fits a preset of
15
16
  * any framework layer.
@@ -17,6 +18,8 @@ import { tagSuggestion } from "./tag-suggestion.js";
17
18
  function mentionsFeature() {
18
19
  return {
19
20
  id: "mentions",
21
+ dimension: "content",
22
+ schemaExtensions: mentionsSchemaExtensions,
20
23
  extensions: (ctx) => [MentionNode.configure({ suggestion: mentionSuggestion(ctx.t) }), TagNode.configure({
21
24
  suggestion: tagSuggestion(ctx.t),
22
25
  onCreate: (editor, name) => metadataOf(editor).create("hashtags", {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/features/mentions/index.ts"],"sourcesContent":["import type { EditorFeature } from '../../core'\nimport { metadataOf } from '../../core'\nimport { mentionSuggestion } from './mention-suggestion'\nimport { mentionsMetadata } from './metadata-defaults'\nimport { MentionNode } from './mention'\nimport { normalizeMentions } from './normalize'\nimport { tagSuggestion } from './tag-suggestion'\nimport { TagNode } from './tag'\n\nexport { mentionSuggestion } from './mention-suggestion'\nexport { mentionsMetadata } from './metadata-defaults'\nexport { normalizeMentions } from './normalize'\nexport { tagSuggestion } from './tag-suggestion'\nexport {\n collectTagNames,\n type MentionAttrs,\n MentionNode,\n type MentionTarget,\n mentionsFromText,\n mentionUsersIn,\n type MentionUser,\n normalizeTagName,\n TAG_NODE,\n TagNode,\n type TagAttrs,\n type TagOptions,\n type TagSuggestionItem,\n tagsFromText,\n TagSuggestionPluginKey,\n} from './schema'\n\n/**\n * `@user` mention chips and `#tag` chips with their suggestion popups. Without a host list, `mentions` and `hashtags`\n * come from `/api/editor/users` and `/api/editor/tags` through the context's HTTP client, and only once a popup is used.\n * Contributes the `normalize` stage: `@name` / `#tag` text of imported or AI-written Markdown becomes chips when it\n * matches a known user or an existing tag. Contributes no components (`EditorFeature<never>`), so it fits a preset of\n * any framework layer.\n */\nexport function mentionsFeature(): EditorFeature<never> {\n return {\n id: 'mentions',\n extensions: ctx => [\n MentionNode.configure({ suggestion: mentionSuggestion(ctx.t) }),\n TagNode.configure({\n suggestion: tagSuggestion(ctx.t),\n onCreate: (editor, name) => metadataOf(editor).create('hashtags', { id: name, label: name }),\n }),\n ],\n metadataDefaults: ctx => mentionsMetadata(ctx),\n blocks: { normalize: normalizeMentions },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAsCA,SAAgB,kBAAwC;CACtD,OAAO;EACL,IAAI;EACJ,aAAY,QAAO,CACjB,YAAY,UAAU,EAAE,YAAY,kBAAkB,IAAI,CAAC,EAAE,CAAC,GAC9D,QAAQ,UAAU;GAChB,YAAY,cAAc,IAAI,CAAC;GAC/B,WAAW,QAAQ,SAAS,WAAW,MAAM,CAAC,CAAC,OAAO,YAAY;IAAE,IAAI;IAAM,OAAO;GAAK,CAAC;EAC7F,CAAC,CACH;EACA,mBAAkB,QAAO,iBAAiB,GAAG;EAC7C,QAAQ,EAAE,WAAW,kBAAkB;CACzC;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/features/mentions/index.ts"],"sourcesContent":["import type { EditorFeature } from '../../core'\nimport { metadataOf } from '../../core'\nimport { mentionSuggestion } from './mention-suggestion'\nimport { mentionsMetadata } from './metadata-defaults'\nimport { MentionNode } from './mention'\nimport { mentionsSchemaExtensions } from './schema'\nimport { normalizeMentions } from './normalize'\nimport { tagSuggestion } from './tag-suggestion'\nimport { TagNode } from './tag'\n\nexport { mentionSuggestion } from './mention-suggestion'\nexport { mentionsMetadata } from './metadata-defaults'\nexport { normalizeMentions } from './normalize'\nexport { tagSuggestion } from './tag-suggestion'\nexport {\n collectTagNames,\n type MentionAttrs,\n MentionNode,\n type MentionTarget,\n mentionsFromText,\n mentionUsersIn,\n type MentionUser,\n normalizeTagName,\n TAG_NODE,\n TagNode,\n type TagAttrs,\n type TagOptions,\n type TagSuggestionItem,\n tagsFromText,\n TagSuggestionPluginKey,\n} from './schema'\n\n/**\n * `@user` mention chips and `#tag` chips with their suggestion popups. Without a host list, `mentions` and `hashtags`\n * come from the context's backend (`users` and `tags`; the HTTP backend asks `/api/editor/users` and `/api/editor/tags`), and only once a popup is used.\n * Contributes the `normalize` stage: `@name` / `#tag` text of imported or AI-written Markdown becomes chips when it\n * matches a known user or an existing tag. Contributes no components (`EditorFeature<never>`), so it fits a preset of\n * any framework layer.\n */\nexport function mentionsFeature(): EditorFeature<never> {\n return {\n id: 'mentions',\n dimension: 'content',\n schemaExtensions: mentionsSchemaExtensions,\n extensions: ctx => [\n MentionNode.configure({ suggestion: mentionSuggestion(ctx.t) }),\n TagNode.configure({\n suggestion: tagSuggestion(ctx.t),\n onCreate: (editor, name) => metadataOf(editor).create('hashtags', { id: name, label: name }),\n }),\n ],\n metadataDefaults: ctx => mentionsMetadata(ctx),\n blocks: { normalize: normalizeMentions },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuCA,SAAgB,kBAAwC;CACtD,OAAO;EACL,IAAI;EACJ,WAAW;EACX,kBAAkB;EAClB,aAAY,QAAO,CACjB,YAAY,UAAU,EAAE,YAAY,kBAAkB,IAAI,CAAC,EAAE,CAAC,GAC9D,QAAQ,UAAU;GAChB,YAAY,cAAc,IAAI,CAAC;GAC/B,WAAW,QAAQ,SAAS,WAAW,MAAM,CAAC,CAAC,OAAO,YAAY;IAAE,IAAI;IAAM,OAAO;GAAK,CAAC;EAC7F,CAAC,CACH;EACA,mBAAkB,QAAO,iBAAiB,GAAG;EAC7C,QAAQ,EAAE,WAAW,kBAAkB;CACzC;AACF"}
@@ -1,4 +1,4 @@
1
- import { MentionUser } from "./mention.js";
1
+ import { MentionUser } from "../../core/backend-types.js";
2
2
  import { JSONContent } from "@tiptap/core";
3
3
  //#region src/features/mentions/mention-text.d.ts
4
4
  /** A user `@username` may resolve to: a registered user, or the attrs of an existing chip (colour possibly unset). */
@@ -1 +1 @@
1
- {"version":3,"file":"mention-text.js","names":[],"sources":["../../../src/features/mentions/mention-text.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { MentionUser } from './mention'\n\n/** A user `@username` may resolve to: a registered user, or the attrs of an existing chip (colour possibly unset). */\nexport type MentionTarget = Pick<MentionUser, 'id' | 'username'> & { color: string | null }\n\n/**\n * Turns `@username` tokens in text nodes into `mention` nodes (attrs id, label = username, color) — for\n * AI-generated Markdown, whose parser only knows the `[@ …]` shortcode. Matches known users only,\n * case-insensitively; unknown `@foo`, e-mail addresses and text in code blocks, inline code or links stay plain.\n * Returns the input unchanged when nothing matched.\n */\nexport function mentionsFromText(json: JSONContent, users: readonly MentionTarget[]): JSONContent {\n const byName = new Map(users.filter(u => u.username).map(u => [u.username.toLowerCase(), u]))\n if (!byName.size) return json\n const names = [...byName.keys()].sort((a, b) => b.length - a.length).map(n => n.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n // Not preceded by a word char, `@` or `.` (e-mail), not followed by a word char or `-`; a trailing `.` ends the sentence.\n const token = new RegExp(`(?<![\\\\p{L}\\\\p{N}_@.])@(${names.join('|')})(?![\\\\p{L}\\\\p{N}_-]|\\\\.[\\\\p{L}\\\\p{N}_])`, 'giu')\n\n const splitText = (node: JSONContent): JSONContent[] => {\n const text = node.text ?? ''\n // Text in inline code or a link stays literal.\n if (!text.includes('@') || node.marks?.some(m => m.type === 'code' || m.type === 'link')) return [node]\n const out: JSONContent[] = []\n let last = 0\n for (const match of text.matchAll(token)) {\n const at = match.index ?? 0\n const user = byName.get(match[1]!.toLowerCase())!\n if (at > last) out.push({ ...node, text: text.slice(last, at) })\n out.push({\n type: 'mention',\n attrs: { id: user.id, label: user.username, color: user.color },\n ...(node.marks?.length ? { marks: node.marks } : {}),\n })\n last = at + match[0].length\n }\n if (!out.length) return [node]\n if (last < text.length) out.push({ ...node, text: text.slice(last) })\n return out\n }\n\n const walk = (node: JSONContent): JSONContent => {\n if (!node.content || node.type === 'codeBlock') return node\n let changed = false\n const content = node.content.flatMap((child) => {\n const next = child.type === 'text' ? splitText(child) : [walk(child)]\n if (next.length !== 1 || next[0] !== child) changed = true\n return next\n })\n return changed ? { ...node, content } : node\n }\n return walk(json)\n}\n\n/** Users of the mention chips inside a JSON tree, e.g. the range an AI result replaces (so echoed `@name`s stay chips). */\nexport function mentionUsersIn(json: JSONContent | JSONContent[]): MentionTarget[] {\n const found = new Map<string, MentionTarget>()\n const visit = (node: JSONContent) => {\n if (node.type === 'mention' && node.attrs?.id && node.attrs.label) {\n found.set(String(node.attrs.id), { id: String(node.attrs.id), username: String(node.attrs.label), color: node.attrs.color ?? null })\n }\n node.content?.forEach(visit)\n }\n ;(Array.isArray(json) ? json : [json]).forEach(visit)\n return [...found.values()]\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,iBAAiB,MAAmB,OAA8C;CAChG,MAAM,SAAS,IAAI,IAAI,MAAM,QAAO,MAAK,EAAE,QAAQ,CAAC,CAAC,KAAI,MAAK,CAAC,EAAE,SAAS,YAAY,GAAG,CAAC,CAAC,CAAC;CAC5F,IAAI,CAAC,OAAO,MAAM,OAAO;CACzB,MAAM,QAAQ,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,KAAI,MAAK,EAAE,QAAQ,uBAAuB,MAAM,CAAC;CAEtH,MAAM,QAAQ,IAAI,OAAO,2BAA2B,MAAM,KAAK,GAAG,EAAE,2CAA2C,KAAK;CAEpH,MAAM,aAAa,SAAqC;EACtD,MAAM,OAAO,KAAK,QAAQ;EAE1B,IAAI,CAAC,KAAK,SAAS,GAAG,KAAK,KAAK,OAAO,MAAK,MAAK,EAAE,SAAS,UAAU,EAAE,SAAS,MAAM,GAAG,OAAO,CAAC,IAAI;EACtG,MAAM,MAAqB,CAAC;EAC5B,IAAI,OAAO;EACX,KAAK,MAAM,SAAS,KAAK,SAAS,KAAK,GAAG;GACxC,MAAM,KAAK,MAAM,SAAS;GAC1B,MAAM,OAAO,OAAO,IAAI,MAAM,EAAE,CAAE,YAAY,CAAC;GAC/C,IAAI,KAAK,MAAM,IAAI,KAAK;IAAE,GAAG;IAAM,MAAM,KAAK,MAAM,MAAM,EAAE;GAAE,CAAC;GAC/D,IAAI,KAAK;IACP,MAAM;IACN,OAAO;KAAE,IAAI,KAAK;KAAI,OAAO,KAAK;KAAU,OAAO,KAAK;IAAM;IAC9D,GAAI,KAAK,OAAO,SAAS,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;GACpD,CAAC;GACD,OAAO,KAAK,MAAM,EAAE,CAAC;EACvB;EACA,IAAI,CAAC,IAAI,QAAQ,OAAO,CAAC,IAAI;EAC7B,IAAI,OAAO,KAAK,QAAQ,IAAI,KAAK;GAAE,GAAG;GAAM,MAAM,KAAK,MAAM,IAAI;EAAE,CAAC;EACpE,OAAO;CACT;CAEA,MAAM,QAAQ,SAAmC;EAC/C,IAAI,CAAC,KAAK,WAAW,KAAK,SAAS,aAAa,OAAO;EACvD,IAAI,UAAU;EACd,MAAM,UAAU,KAAK,QAAQ,SAAS,UAAU;GAC9C,MAAM,OAAO,MAAM,SAAS,SAAS,UAAU,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;GACpE,IAAI,KAAK,WAAW,KAAK,KAAK,OAAO,OAAO,UAAU;GACtD,OAAO;EACT,CAAC;EACD,OAAO,UAAU;GAAE,GAAG;GAAM;EAAQ,IAAI;CAC1C;CACA,OAAO,KAAK,IAAI;AAClB;;AAGA,SAAgB,eAAe,MAAoD;CACjF,MAAM,wBAAQ,IAAI,IAA2B;CAC7C,MAAM,SAAS,SAAsB;EACnC,IAAI,KAAK,SAAS,aAAa,KAAK,OAAO,MAAM,KAAK,MAAM,OAC1D,MAAM,IAAI,OAAO,KAAK,MAAM,EAAE,GAAG;GAAE,IAAI,OAAO,KAAK,MAAM,EAAE;GAAG,UAAU,OAAO,KAAK,MAAM,KAAK;GAAG,OAAO,KAAK,MAAM,SAAS;EAAK,CAAC;EAErI,KAAK,SAAS,QAAQ,KAAK;CAC7B;CACC,CAAC,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI,EAAA,CAAG,QAAQ,KAAK;CACpD,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAC3B"}
1
+ {"version":3,"file":"mention-text.js","names":[],"sources":["../../../src/features/mentions/mention-text.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { MentionUser } from '../../core/backend-types'\n\n/** A user `@username` may resolve to: a registered user, or the attrs of an existing chip (colour possibly unset). */\nexport type MentionTarget = Pick<MentionUser, 'id' | 'username'> & { color: string | null }\n\n/**\n * Turns `@username` tokens in text nodes into `mention` nodes (attrs id, label = username, color) — for\n * AI-generated Markdown, whose parser only knows the `[@ …]` shortcode. Matches known users only,\n * case-insensitively; unknown `@foo`, e-mail addresses and text in code blocks, inline code or links stay plain.\n * Returns the input unchanged when nothing matched.\n */\nexport function mentionsFromText(json: JSONContent, users: readonly MentionTarget[]): JSONContent {\n const byName = new Map(users.filter(u => u.username).map(u => [u.username.toLowerCase(), u]))\n if (!byName.size) return json\n const names = [...byName.keys()].sort((a, b) => b.length - a.length).map(n => n.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n // Not preceded by a word char, `@` or `.` (e-mail), not followed by a word char or `-`; a trailing `.` ends the sentence.\n const token = new RegExp(`(?<![\\\\p{L}\\\\p{N}_@.])@(${names.join('|')})(?![\\\\p{L}\\\\p{N}_-]|\\\\.[\\\\p{L}\\\\p{N}_])`, 'giu')\n\n const splitText = (node: JSONContent): JSONContent[] => {\n const text = node.text ?? ''\n // Text in inline code or a link stays literal.\n if (!text.includes('@') || node.marks?.some(m => m.type === 'code' || m.type === 'link')) return [node]\n const out: JSONContent[] = []\n let last = 0\n for (const match of text.matchAll(token)) {\n const at = match.index ?? 0\n const user = byName.get(match[1]!.toLowerCase())!\n if (at > last) out.push({ ...node, text: text.slice(last, at) })\n out.push({\n type: 'mention',\n attrs: { id: user.id, label: user.username, color: user.color },\n ...(node.marks?.length ? { marks: node.marks } : {}),\n })\n last = at + match[0].length\n }\n if (!out.length) return [node]\n if (last < text.length) out.push({ ...node, text: text.slice(last) })\n return out\n }\n\n const walk = (node: JSONContent): JSONContent => {\n if (!node.content || node.type === 'codeBlock') return node\n let changed = false\n const content = node.content.flatMap((child) => {\n const next = child.type === 'text' ? splitText(child) : [walk(child)]\n if (next.length !== 1 || next[0] !== child) changed = true\n return next\n })\n return changed ? { ...node, content } : node\n }\n return walk(json)\n}\n\n/** Users of the mention chips inside a JSON tree, e.g. the range an AI result replaces (so echoed `@name`s stay chips). */\nexport function mentionUsersIn(json: JSONContent | JSONContent[]): MentionTarget[] {\n const found = new Map<string, MentionTarget>()\n const visit = (node: JSONContent) => {\n if (node.type === 'mention' && node.attrs?.id && node.attrs.label) {\n found.set(String(node.attrs.id), { id: String(node.attrs.id), username: String(node.attrs.label), color: node.attrs.color ?? null })\n }\n node.content?.forEach(visit)\n }\n ;(Array.isArray(json) ? json : [json]).forEach(visit)\n return [...found.values()]\n}\n"],"mappings":";;;;;;;AAYA,SAAgB,iBAAiB,MAAmB,OAA8C;CAChG,MAAM,SAAS,IAAI,IAAI,MAAM,QAAO,MAAK,EAAE,QAAQ,CAAC,CAAC,KAAI,MAAK,CAAC,EAAE,SAAS,YAAY,GAAG,CAAC,CAAC,CAAC;CAC5F,IAAI,CAAC,OAAO,MAAM,OAAO;CACzB,MAAM,QAAQ,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,KAAI,MAAK,EAAE,QAAQ,uBAAuB,MAAM,CAAC;CAEtH,MAAM,QAAQ,IAAI,OAAO,2BAA2B,MAAM,KAAK,GAAG,EAAE,2CAA2C,KAAK;CAEpH,MAAM,aAAa,SAAqC;EACtD,MAAM,OAAO,KAAK,QAAQ;EAE1B,IAAI,CAAC,KAAK,SAAS,GAAG,KAAK,KAAK,OAAO,MAAK,MAAK,EAAE,SAAS,UAAU,EAAE,SAAS,MAAM,GAAG,OAAO,CAAC,IAAI;EACtG,MAAM,MAAqB,CAAC;EAC5B,IAAI,OAAO;EACX,KAAK,MAAM,SAAS,KAAK,SAAS,KAAK,GAAG;GACxC,MAAM,KAAK,MAAM,SAAS;GAC1B,MAAM,OAAO,OAAO,IAAI,MAAM,EAAE,CAAE,YAAY,CAAC;GAC/C,IAAI,KAAK,MAAM,IAAI,KAAK;IAAE,GAAG;IAAM,MAAM,KAAK,MAAM,MAAM,EAAE;GAAE,CAAC;GAC/D,IAAI,KAAK;IACP,MAAM;IACN,OAAO;KAAE,IAAI,KAAK;KAAI,OAAO,KAAK;KAAU,OAAO,KAAK;IAAM;IAC9D,GAAI,KAAK,OAAO,SAAS,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;GACpD,CAAC;GACD,OAAO,KAAK,MAAM,EAAE,CAAC;EACvB;EACA,IAAI,CAAC,IAAI,QAAQ,OAAO,CAAC,IAAI;EAC7B,IAAI,OAAO,KAAK,QAAQ,IAAI,KAAK;GAAE,GAAG;GAAM,MAAM,KAAK,MAAM,IAAI;EAAE,CAAC;EACpE,OAAO;CACT;CAEA,MAAM,QAAQ,SAAmC;EAC/C,IAAI,CAAC,KAAK,WAAW,KAAK,SAAS,aAAa,OAAO;EACvD,IAAI,UAAU;EACd,MAAM,UAAU,KAAK,QAAQ,SAAS,UAAU;GAC9C,MAAM,OAAO,MAAM,SAAS,SAAS,UAAU,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;GACpE,IAAI,KAAK,WAAW,KAAK,KAAK,OAAO,OAAO,UAAU;GACtD,OAAO;EACT,CAAC;EACD,OAAO,UAAU;GAAE,GAAG;GAAM;EAAQ,IAAI;CAC1C;CACA,OAAO,KAAK,IAAI;AAClB;;AAGA,SAAgB,eAAe,MAAoD;CACjF,MAAM,wBAAQ,IAAI,IAA2B;CAC7C,MAAM,SAAS,SAAsB;EACnC,IAAI,KAAK,SAAS,aAAa,KAAK,OAAO,MAAM,KAAK,MAAM,OAC1D,MAAM,IAAI,OAAO,KAAK,MAAM,EAAE,GAAG;GAAE,IAAI,OAAO,KAAK,MAAM,EAAE;GAAG,UAAU,OAAO,KAAK,MAAM,KAAK;GAAG,OAAO,KAAK,MAAM,SAAS;EAAK,CAAC;EAErI,KAAK,SAAS,QAAQ,KAAK;CAC7B;CACC,CAAC,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI,EAAA,CAAG,QAAQ,KAAK;CACpD,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAC3B"}
@@ -1,10 +1,4 @@
1
1
  //#region src/features/mentions/mention.d.ts
2
- /** A user as offered for `@` mentions (`GET /api/editor/users`); never carries credentials. */
3
- export interface MentionUser {
4
- id: string;
5
- username: string;
6
- color: string;
7
- }
8
2
  /** Attributes of a mention chip: `label` is the username, `color` the user's colour, both copied in at insert time. */
9
3
  export type MentionAttrs = {
10
4
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"mention.js","names":[],"sources":["../../../src/features/mentions/mention.ts"],"sourcesContent":["import { Mention } from '@tiptap/extension-mention'\nimport { HEX_COLOR } from '../../core/schema/hex-color'\n\n/** A user as offered for `@` mentions (`GET /api/editor/users`); never carries credentials. */\nexport interface MentionUser {\n id: string\n username: string\n color: string\n}\n\n/** Attributes of a mention chip: `label` is the username, `color` the user's colour, both copied in at insert time. */\nexport type MentionAttrs = {\n id: string\n label: string\n color: string | null\n}\n\n/**\n * Schema part of the `@username` chip, shared by the client editor and the server (collab schema, Markdown).\n * The client adds the suggestion popup via `.configure({ suggestion })` in the mentions feature.\n */\nexport const MentionNode = Mention.extend({\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: { class: 'mention' } }\n },\n addAttributes() {\n return {\n ...this.parent?.(),\n color: {\n default: null,\n parseHTML: el => el.getAttribute('data-color'),\n renderHTML: attrs => HEX_COLOR.test(String(attrs.color ?? ''))\n ? { 'data-color': attrs.color, 'style': `--mention-color: ${attrs.color}` }\n : {},\n },\n }\n },\n // Markdown export (editor.getMarkdown(), AI context/selection) shows the chip as plain `@username`.\n renderMarkdown: node => `@${node.attrs?.label ?? node.attrs?.id ?? ''}`,\n})\n"],"mappings":";;;;;;;AAqBA,MAAa,cAAc,QAAQ,OAAO;CACxC,aAAa;EACX,OAAO;GAAE,GAAG,KAAK,OAAQ;GAAG,gBAAgB,EAAE,OAAO,UAAU;EAAE;CACnE;CACA,gBAAgB;EACd,OAAO;GACL,GAAG,KAAK,SAAS;GACjB,OAAO;IACL,SAAS;IACT,YAAW,OAAM,GAAG,aAAa,YAAY;IAC7C,aAAY,UAAS,UAAU,KAAK,OAAO,MAAM,SAAS,EAAE,CAAC,IACzD;KAAE,cAAc,MAAM;KAAO,SAAS,oBAAoB,MAAM;IAAQ,IACxE,CAAC;GACP;EACF;CACF;CAEA,iBAAgB,SAAQ,IAAI,KAAK,OAAO,SAAS,KAAK,OAAO,MAAM;AACrE,CAAC"}
1
+ {"version":3,"file":"mention.js","names":[],"sources":["../../../src/features/mentions/mention.ts"],"sourcesContent":["import { Mention } from '@tiptap/extension-mention'\nimport { HEX_COLOR } from '../../core/schema/hex-color'\n\n/** Attributes of a mention chip: `label` is the username, `color` the user's colour, both copied in at insert time. */\nexport type MentionAttrs = {\n id: string\n label: string\n color: string | null\n}\n\n/**\n * Schema part of the `@username` chip, shared by the client editor and the server (collab schema, Markdown).\n * The client adds the suggestion popup via `.configure({ suggestion })` in the mentions feature.\n */\nexport const MentionNode = Mention.extend({\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: { class: 'mention' } }\n },\n addAttributes() {\n return {\n ...this.parent?.(),\n color: {\n default: null,\n parseHTML: el => el.getAttribute('data-color'),\n renderHTML: attrs => HEX_COLOR.test(String(attrs.color ?? ''))\n ? { 'data-color': attrs.color, 'style': `--mention-color: ${attrs.color}` }\n : {},\n },\n }\n },\n // Markdown export (editor.getMarkdown(), AI context/selection) shows the chip as plain `@username`.\n renderMarkdown: node => `@${node.attrs?.label ?? node.attrs?.id ?? ''}`,\n})\n"],"mappings":";;;;;;;AAcA,MAAa,cAAc,QAAQ,OAAO;CACxC,aAAa;EACX,OAAO;GAAE,GAAG,KAAK,OAAQ;GAAG,gBAAgB,EAAE,OAAO,UAAU;EAAE;CACnE;CACA,gBAAgB;EACd,OAAO;GACL,GAAG,KAAK,SAAS;GACjB,OAAO;IACL,SAAS;IACT,YAAW,OAAM,GAAG,aAAa,YAAY;IAC7C,aAAY,UAAS,UAAU,KAAK,OAAO,MAAM,SAAS,EAAE,CAAC,IACzD;KAAE,cAAc,MAAM;KAAO,SAAS,oBAAoB,MAAM;IAAQ,IACxE,CAAC;GACP;EACF;CACF;CAEA,iBAAgB,SAAQ,IAAI,KAAK,OAAO,SAAS,KAAK,OAAO,MAAM;AACrE,CAAC"}
@@ -2,7 +2,7 @@ import { EditorMetadata } from "../../core/metadata-types.js";
2
2
  import { FeatureContext } from "../../core/feature.js";
3
3
  import "../../core/index.js";
4
4
  //#region src/features/mentions/metadata-defaults.d.ts
5
- /** Built-in `mentions` and `hashtags` lists when the host supplies none: users and tags from the editor API, through the context's HTTP client. */
6
- export declare function mentionsMetadata(ctx: Pick<FeatureContext, 'fetch'>): EditorMetadata;
5
+ /** Built-in `mentions` and `hashtags` lists when the host supplies none: users and tags from the context's backend (`users`, `tags`); a backend without the capability offers none. */
6
+ export declare function mentionsMetadata(ctx: Pick<FeatureContext, 'backend'>): EditorMetadata;
7
7
  //#endregion
8
8
  //# sourceMappingURL=metadata-defaults.d.ts.map
@@ -1,23 +1,17 @@
1
1
  import { normalizeTagName } from "./tag.js";
2
2
  //#region src/features/mentions/metadata-defaults.ts
3
- /** Built-in `mentions` and `hashtags` lists when the host supplies none: users and tags from the editor API, through the context's HTTP client. */
3
+ /** Built-in `mentions` and `hashtags` lists when the host supplies none: users and tags from the context's backend (`users`, `tags`); a backend without the capability offers none. */
4
4
  function mentionsMetadata(ctx) {
5
5
  return {
6
6
  mentions: async ({ query, signal }) => {
7
- return (await ctx.fetch("/api/editor/users", {
8
- query: { q: query },
9
- signal
10
- })).map(({ id, username, color }) => ({
7
+ return (await ctx.backend.users?.search(query, signal) ?? []).map(({ id, username, color }) => ({
11
8
  id,
12
9
  label: username,
13
10
  color
14
11
  }));
15
12
  },
16
13
  hashtags: async ({ query, signal }) => {
17
- return (await ctx.fetch("/api/editor/tags", {
18
- query: { q: normalizeTagName(query) },
19
- signal
20
- })).map((tag) => ({
14
+ return (await ctx.backend.tags?.search(normalizeTagName(query), signal) ?? []).map((tag) => ({
21
15
  id: tag,
22
16
  label: tag
23
17
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"metadata-defaults.js","names":[],"sources":["../../../src/features/mentions/metadata-defaults.ts"],"sourcesContent":["import type { EditorMetadata, FeatureContext } from '../../core'\nimport { normalizeTagName } from './tag'\n\n/** Built-in `mentions` and `hashtags` lists when the host supplies none: users and tags from the editor API, through the context's HTTP client. */\nexport function mentionsMetadata(ctx: Pick<FeatureContext, 'fetch'>): EditorMetadata {\n return {\n mentions: async ({ query, signal }) => {\n const users = await ctx.fetch<{ id: string, username: string, color: string }[]>('/api/editor/users', { query: { q: query }, signal })\n return users.map(({ id, username, color }) => ({ id, label: username, color }))\n },\n hashtags: async ({ query, signal }) => {\n const tags = await ctx.fetch<string[]>('/api/editor/tags', { query: { q: normalizeTagName(query) }, signal })\n return tags.map(tag => ({ id: tag, label: tag }))\n },\n }\n}\n"],"mappings":";;;AAIA,SAAgB,iBAAiB,KAAoD;CACnF,OAAO;EACL,UAAU,OAAO,EAAE,OAAO,aAAa;GAErC,QAAO,MADa,IAAI,MAAyD,qBAAqB;IAAE,OAAO,EAAE,GAAG,MAAM;IAAG;GAAO,CAAC,EAAA,CACxH,KAAK,EAAE,IAAI,UAAU,aAAa;IAAE;IAAI,OAAO;IAAU;GAAM,EAAE;EAChF;EACA,UAAU,OAAO,EAAE,OAAO,aAAa;GAErC,QAAO,MADY,IAAI,MAAgB,oBAAoB;IAAE,OAAO,EAAE,GAAG,iBAAiB,KAAK,EAAE;IAAG;GAAO,CAAC,EAAA,CAChG,KAAI,SAAQ;IAAE,IAAI;IAAK,OAAO;GAAI,EAAE;EAClD;CACF;AACF"}
1
+ {"version":3,"file":"metadata-defaults.js","names":[],"sources":["../../../src/features/mentions/metadata-defaults.ts"],"sourcesContent":["import type { EditorMetadata, FeatureContext } from '../../core'\nimport { normalizeTagName } from './tag'\n\n/** Built-in `mentions` and `hashtags` lists when the host supplies none: users and tags from the context's backend (`users`, `tags`); a backend without the capability offers none. */\nexport function mentionsMetadata(ctx: Pick<FeatureContext, 'backend'>): EditorMetadata {\n return {\n mentions: async ({ query, signal }) => {\n const users = await ctx.backend.users?.search(query, signal) ?? []\n return users.map(({ id, username, color }) => ({ id, label: username, color }))\n },\n hashtags: async ({ query, signal }) => {\n const tags = await ctx.backend.tags?.search(normalizeTagName(query), signal) ?? []\n return tags.map(tag => ({ id: tag, label: tag }))\n },\n }\n}\n"],"mappings":";;;AAIA,SAAgB,iBAAiB,KAAsD;CACrF,OAAO;EACL,UAAU,OAAO,EAAE,OAAO,aAAa;GAErC,QADc,MAAM,IAAI,QAAQ,OAAO,OAAO,OAAO,MAAM,KAAK,CAAC,EAAA,CACpD,KAAK,EAAE,IAAI,UAAU,aAAa;IAAE;IAAI,OAAO;IAAU;GAAM,EAAE;EAChF;EACA,UAAU,OAAO,EAAE,OAAO,aAAa;GAErC,QADa,MAAM,IAAI,QAAQ,MAAM,OAAO,iBAAiB,KAAK,GAAG,MAAM,KAAK,CAAC,EAAA,CACrE,KAAI,SAAQ;IAAE,IAAI;IAAK,OAAO;GAAI,EAAE;EAClD;CACF;AACF"}
@@ -1,4 +1,5 @@
1
- import { MentionAttrs, MentionNode, MentionUser } from "./mention.js";
1
+ import { MentionUser } from "../../core/backend-types.js";
2
+ import { MentionAttrs, MentionNode } from "./mention.js";
2
3
  import { MentionTarget, mentionUsersIn, mentionsFromText } from "./mention-text.js";
3
4
  import { TAG_NODE, TagAttrs, TagNode, TagOptions, TagSuggestionItem, TagSuggestionPluginKey, collectTagNames, normalizeTagName, tagsFromText } from "./tag.js";
4
5
  import { AnyExtension } from "@tiptap/core";
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/mentions/schema.ts"],"sourcesContent":["import type { AnyExtension } from '@tiptap/core'\nimport { MentionNode } from './mention'\nimport { TagNode } from './tag'\n\n// Vue-free and server-importable (`@ecsplico/editor/mentions/schema`): the mention and tag nodes, their Markdown\n// forms and the text helpers that turn `@name` / `#tag` back into chips. The suggestion popups live in the feature.\n\nexport { type MentionAttrs, MentionNode, type MentionUser } from './mention'\nexport { type MentionTarget, mentionsFromText, mentionUsersIn } from './mention-text'\nexport { collectTagNames, normalizeTagName, TAG_NODE, TagNode, type TagAttrs, type TagOptions, type TagSuggestionItem, tagsFromText, TagSuggestionPluginKey } from './tag'\n\n/** The mention and tag nodes of the document schema. */\nexport const mentionsSchemaExtensions = (): AnyExtension[] => [MentionNode, TagNode]\n"],"mappings":";;;;;AAYA,MAAa,iCAAiD,CAAC,aAAa,OAAO"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/mentions/schema.ts"],"sourcesContent":["import type { AnyExtension } from '@tiptap/core'\nimport { MentionNode } from './mention'\nimport { TagNode } from './tag'\n\n// Vue-free and server-importable (`@ecsplico/editor/mentions/schema`): the mention and tag nodes, their Markdown\n// forms and the text helpers that turn `@name` / `#tag` back into chips. The suggestion popups live in the feature.\n\nexport type { MentionUser } from '../../core/backend-types'\nexport { type MentionAttrs, MentionNode } from './mention'\nexport { type MentionTarget, mentionsFromText, mentionUsersIn } from './mention-text'\nexport { collectTagNames, normalizeTagName, TAG_NODE, TagNode, type TagAttrs, type TagOptions, type TagSuggestionItem, tagsFromText, TagSuggestionPluginKey } from './tag'\n\n/** The mention and tag nodes of the document schema. */\nexport const mentionsSchemaExtensions = (): AnyExtension[] => [MentionNode, TagNode]\n"],"mappings":";;;;;AAaA,MAAa,iCAAiD,CAAC,aAAa,OAAO"}
@@ -6,7 +6,8 @@ export declare const TABLE_TOOLBAR_GROUP = "table";
6
6
  /**
7
7
  * GFM tables and task lists: the table and task-list nodes (Markdown round-trips through `@tiptap/markdown`, tables as
8
8
  * pipe tables, tasks as `- [ ]`/`- [x]`), the toolbar group `table` (insert 3×3, add row, add column, delete row, delete
9
- * column, delete table) and the slash entries `table.insert` and `task.insert`. All are offered where the caret has
9
+ * column, delete table) and the slash entries `table.insert` and `task.insert`: one command each, the table commands
10
+ * on the toolbar, `insertTable` and `task.insert` also in the `/` menu. All are offered where the caret has
10
11
  * `block.content`. Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.
11
12
  */
12
13
  export declare function tablesFeature(): EditorFeature<never>;
@@ -1,5 +1,5 @@
1
1
  import { ABILITY } from "../../core/schema/abilities.js";
2
- import { commandItem } from "../../core/toolbar.js";
2
+ import { command } from "../../core/commands.js";
3
3
  import { tablesSchemaExtensions } from "./schema.js";
4
4
  //#region src/features/tables/feature.ts
5
5
  /** The toolbar group the tables feature contributes; its position follows the core groups (`blocks` is 40). */
@@ -10,88 +10,108 @@ const INSERT = {
10
10
  cols: 3,
11
11
  withHeaderRow: true
12
12
  };
13
- const toolbar = (ctx) => [{
13
+ const toolbarGroups = [{
14
14
  id: TABLE_TOOLBAR_GROUP,
15
15
  order: 60,
16
- items: [
17
- {
18
- ...commandItem({
19
- id: "insertTable",
20
- order: 10,
21
- icon: "i-lucide-table",
22
- label: ctx.t("tables.toolbar.insert"),
23
- ability: ABILITY.blockContent,
24
- command: (c) => c.insertTable(INSERT)
25
- }),
26
- disabled: (editor) => editor.isActive("table") || !editor.can().insertTable(INSERT)
27
- },
28
- commandItem({
29
- id: "addRow",
30
- order: 20,
31
- icon: "i-lucide-between-horizontal-end",
32
- label: ctx.t("tables.toolbar.addRow"),
33
- ability: ABILITY.blockContent,
34
- command: (c) => c.addRowAfter()
35
- }),
36
- commandItem({
37
- id: "addColumn",
38
- order: 30,
39
- icon: "i-lucide-between-vertical-end",
40
- label: ctx.t("tables.toolbar.addColumn"),
41
- ability: ABILITY.blockContent,
42
- command: (c) => c.addColumnAfter()
43
- }),
44
- commandItem({
45
- id: "deleteRow",
46
- order: 40,
47
- icon: "i-lucide-table-rows-split",
48
- label: ctx.t("tables.toolbar.deleteRow"),
49
- ability: ABILITY.blockContent,
50
- command: (c) => c.deleteRow()
51
- }),
52
- commandItem({
53
- id: "deleteColumn",
54
- order: 50,
55
- icon: "i-lucide-table-columns-split",
56
- label: ctx.t("tables.toolbar.deleteColumn"),
16
+ labelKey: "palette.group.table"
17
+ }];
18
+ const commands = (ctx) => [
19
+ {
20
+ ...command({
21
+ id: "insertTable",
22
+ toolbar: {
23
+ group: TABLE_TOOLBAR_GROUP,
24
+ order: 10
25
+ },
26
+ slash: { id: "table.insert" },
27
+ creates: { type: "table" },
28
+ icon: "i-lucide-table",
29
+ label: ctx.t("tables.toolbar.insert"),
57
30
  ability: ABILITY.blockContent,
58
- command: (c) => c.deleteColumn()
31
+ command: (c) => c.insertTable(INSERT)
59
32
  }),
60
- commandItem({
61
- id: "deleteTable",
62
- order: 60,
63
- icon: "i-lucide-trash-2",
64
- label: ctx.t("tables.toolbar.deleteTable"),
65
- ability: ABILITY.blockContent,
66
- command: (c) => c.deleteTable()
67
- })
68
- ]
69
- }];
70
- const slashCommands = (ctx) => [{
71
- id: "table.insert",
72
- label: ctx.t("tables.slash.table"),
73
- icon: "i-lucide-table",
74
- ability: ABILITY.blockContent,
75
- run: (editor) => void editor.chain().focus().insertTable(INSERT).run()
76
- }, {
77
- id: "task.insert",
78
- label: ctx.t("tables.slash.tasks"),
79
- icon: "i-lucide-list-checks",
80
- ability: ABILITY.blockContent,
81
- run: (editor) => void editor.chain().focus().toggleTaskList().run()
82
- }];
33
+ can: (editor) => !editor.isActive("table") && editor.can().insertTable(INSERT)
34
+ },
35
+ command({
36
+ id: "addRow",
37
+ toolbar: {
38
+ group: TABLE_TOOLBAR_GROUP,
39
+ order: 20
40
+ },
41
+ icon: "i-lucide-between-horizontal-end",
42
+ label: ctx.t("tables.toolbar.addRow"),
43
+ ability: ABILITY.blockContent,
44
+ command: (c) => c.addRowAfter()
45
+ }),
46
+ command({
47
+ id: "addColumn",
48
+ toolbar: {
49
+ group: TABLE_TOOLBAR_GROUP,
50
+ order: 30
51
+ },
52
+ icon: "i-lucide-between-vertical-end",
53
+ label: ctx.t("tables.toolbar.addColumn"),
54
+ ability: ABILITY.blockContent,
55
+ command: (c) => c.addColumnAfter()
56
+ }),
57
+ command({
58
+ id: "deleteRow",
59
+ toolbar: {
60
+ group: TABLE_TOOLBAR_GROUP,
61
+ order: 40
62
+ },
63
+ icon: "i-lucide-table-rows-split",
64
+ label: ctx.t("tables.toolbar.deleteRow"),
65
+ ability: ABILITY.blockContent,
66
+ command: (c) => c.deleteRow()
67
+ }),
68
+ command({
69
+ id: "deleteColumn",
70
+ toolbar: {
71
+ group: TABLE_TOOLBAR_GROUP,
72
+ order: 50
73
+ },
74
+ icon: "i-lucide-table-columns-split",
75
+ label: ctx.t("tables.toolbar.deleteColumn"),
76
+ ability: ABILITY.blockContent,
77
+ command: (c) => c.deleteColumn()
78
+ }),
79
+ command({
80
+ id: "deleteTable",
81
+ toolbar: {
82
+ group: TABLE_TOOLBAR_GROUP,
83
+ order: 60
84
+ },
85
+ icon: "i-lucide-trash-2",
86
+ label: ctx.t("tables.toolbar.deleteTable"),
87
+ ability: ABILITY.blockContent,
88
+ command: (c) => c.deleteTable()
89
+ }),
90
+ command({
91
+ id: "task.insert",
92
+ slash: { id: "task.insert" },
93
+ creates: { type: "taskList" },
94
+ icon: "i-lucide-list-checks",
95
+ label: ctx.t("tables.slash.tasks"),
96
+ ability: ABILITY.blockContent,
97
+ command: (c) => c.toggleTaskList()
98
+ })
99
+ ];
83
100
  /**
84
101
  * GFM tables and task lists: the table and task-list nodes (Markdown round-trips through `@tiptap/markdown`, tables as
85
102
  * pipe tables, tasks as `- [ ]`/`- [x]`), the toolbar group `table` (insert 3×3, add row, add column, delete row, delete
86
- * column, delete table) and the slash entries `table.insert` and `task.insert`. All are offered where the caret has
103
+ * column, delete table) and the slash entries `table.insert` and `task.insert`: one command each, the table commands
104
+ * on the toolbar, `insertTable` and `task.insert` also in the `/` menu. All are offered where the caret has
87
105
  * `block.content`. Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.
88
106
  */
89
107
  function tablesFeature() {
90
108
  return {
91
109
  id: "tables",
110
+ dimension: "content",
111
+ schemaExtensions: tablesSchemaExtensions,
92
112
  extensions: () => tablesSchemaExtensions(),
93
- toolbar,
94
- slashCommands
113
+ toolbarGroups,
114
+ commands
95
115
  };
96
116
  }
97
117
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/tables/feature.ts"],"sourcesContent":["import { commandItem, type EditorFeature, type FeatureContext, type SlashCommandItem, type ToolbarGroup } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { tablesSchemaExtensions } from './schema'\n\n/** The toolbar group the tables feature contributes; its position follows the core groups (`blocks` is 40). */\nexport const TABLE_TOOLBAR_GROUP = 'table'\n\n/** Columns, rows and header row of a table the toolbar and the slash menu insert. */\nconst INSERT = { rows: 3, cols: 3, withHeaderRow: true } as const\n\nconst toolbar = (ctx: FeatureContext): ToolbarGroup<never>[] => [{\n id: TABLE_TOOLBAR_GROUP,\n order: 60,\n items: [\n {\n ...commandItem({ id: 'insertTable', order: 10, icon: 'i-lucide-table', label: ctx.t('tables.toolbar.insert'), ability: ABILITY.blockContent, command: c => c.insertTable(INSERT) }),\n // GFM has no nested tables: inside one the button is off.\n disabled: editor => editor.isActive('table') || !editor.can().insertTable(INSERT),\n },\n commandItem({ id: 'addRow', order: 20, icon: 'i-lucide-between-horizontal-end', label: ctx.t('tables.toolbar.addRow'), ability: ABILITY.blockContent, command: c => c.addRowAfter() }),\n commandItem({ id: 'addColumn', order: 30, icon: 'i-lucide-between-vertical-end', label: ctx.t('tables.toolbar.addColumn'), ability: ABILITY.blockContent, command: c => c.addColumnAfter() }),\n commandItem({ id: 'deleteRow', order: 40, icon: 'i-lucide-table-rows-split', label: ctx.t('tables.toolbar.deleteRow'), ability: ABILITY.blockContent, command: c => c.deleteRow() }),\n commandItem({ id: 'deleteColumn', order: 50, icon: 'i-lucide-table-columns-split', label: ctx.t('tables.toolbar.deleteColumn'), ability: ABILITY.blockContent, command: c => c.deleteColumn() }),\n commandItem({ id: 'deleteTable', order: 60, icon: 'i-lucide-trash-2', label: ctx.t('tables.toolbar.deleteTable'), ability: ABILITY.blockContent, command: c => c.deleteTable() }),\n ],\n}]\n\nconst slashCommands = (ctx: FeatureContext): SlashCommandItem[] => [\n { id: 'table.insert', label: ctx.t('tables.slash.table'), icon: 'i-lucide-table', ability: ABILITY.blockContent, run: editor => void editor.chain().focus().insertTable(INSERT).run() },\n { id: 'task.insert', label: ctx.t('tables.slash.tasks'), icon: 'i-lucide-list-checks', ability: ABILITY.blockContent, run: editor => void editor.chain().focus().toggleTaskList().run() },\n]\n\n/**\n * GFM tables and task lists: the table and task-list nodes (Markdown round-trips through `@tiptap/markdown`, tables as\n * pipe tables, tasks as `- [ ]`/`- [x]`), the toolbar group `table` (insert 3×3, add row, add column, delete row, delete\n * column, delete table) and the slash entries `table.insert` and `task.insert`. All are offered where the caret has\n * `block.content`. Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.\n */\nexport function tablesFeature(): EditorFeature<never> {\n return {\n id: 'tables',\n extensions: () => tablesSchemaExtensions(),\n toolbar,\n slashCommands,\n }\n}\n"],"mappings":";;;;;AAKA,MAAa,sBAAsB;;AAGnC,MAAM,SAAS;CAAE,MAAM;CAAG,MAAM;CAAG,eAAe;AAAK;AAEvD,MAAM,WAAW,QAA+C,CAAC;CAC/D,IAAI;CACJ,OAAO;CACP,OAAO;EACL;GACE,GAAG,YAAY;IAAE,IAAI;IAAe,OAAO;IAAI,MAAM;IAAkB,OAAO,IAAI,EAAE,uBAAuB;IAAG,SAAS,QAAQ;IAAc,UAAS,MAAK,EAAE,YAAY,MAAM;GAAE,CAAC;GAElL,WAAU,WAAU,OAAO,SAAS,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,YAAY,MAAM;EAClF;EACA,YAAY;GAAE,IAAI;GAAU,OAAO;GAAI,MAAM;GAAmC,OAAO,IAAI,EAAE,uBAAuB;GAAG,SAAS,QAAQ;GAAc,UAAS,MAAK,EAAE,YAAY;EAAE,CAAC;EACrL,YAAY;GAAE,IAAI;GAAa,OAAO;GAAI,MAAM;GAAiC,OAAO,IAAI,EAAE,0BAA0B;GAAG,SAAS,QAAQ;GAAc,UAAS,MAAK,EAAE,eAAe;EAAE,CAAC;EAC5L,YAAY;GAAE,IAAI;GAAa,OAAO;GAAI,MAAM;GAA6B,OAAO,IAAI,EAAE,0BAA0B;GAAG,SAAS,QAAQ;GAAc,UAAS,MAAK,EAAE,UAAU;EAAE,CAAC;EACnL,YAAY;GAAE,IAAI;GAAgB,OAAO;GAAI,MAAM;GAAgC,OAAO,IAAI,EAAE,6BAA6B;GAAG,SAAS,QAAQ;GAAc,UAAS,MAAK,EAAE,aAAa;EAAE,CAAC;EAC/L,YAAY;GAAE,IAAI;GAAe,OAAO;GAAI,MAAM;GAAoB,OAAO,IAAI,EAAE,4BAA4B;GAAG,SAAS,QAAQ;GAAc,UAAS,MAAK,EAAE,YAAY;EAAE,CAAC;CAClL;AACF,CAAC;AAED,MAAM,iBAAiB,QAA4C,CACjE;CAAE,IAAI;CAAgB,OAAO,IAAI,EAAE,oBAAoB;CAAG,MAAM;CAAkB,SAAS,QAAQ;CAAc,MAAK,WAAU,KAAK,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,YAAY,MAAM,CAAC,CAAC,IAAI;AAAE,GACtL;CAAE,IAAI;CAAe,OAAO,IAAI,EAAE,oBAAoB;CAAG,MAAM;CAAwB,SAAS,QAAQ;CAAc,MAAK,WAAU,KAAK,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,IAAI;AAAE,CAC1L;;;;;;;AAQA,SAAgB,gBAAsC;CACpD,OAAO;EACL,IAAI;EACJ,kBAAkB,uBAAuB;EACzC;EACA;CACF;AACF"}
1
+ {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/tables/feature.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type CommandDef, command, type EditorFeature, type FeatureContext, type ToolbarGroupDef } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { tablesSchemaExtensions } from './schema'\n\n/** The toolbar group the tables feature contributes; its position follows the core groups (`blocks` is 40). */\nexport const TABLE_TOOLBAR_GROUP = 'table'\n\n/** Columns, rows and header row of a table the toolbar and the slash menu insert. */\nconst INSERT = { rows: 3, cols: 3, withHeaderRow: true } as const\n\nconst toolbarGroups: ToolbarGroupDef[] = [{ id: TABLE_TOOLBAR_GROUP, order: 60, labelKey: 'palette.group.table' }]\n\nconst commands = (ctx: FeatureContext): CommandDef<never>[] => [\n {\n ...command({\n id: 'insertTable',\n toolbar: { group: TABLE_TOOLBAR_GROUP, order: 10 },\n slash: { id: 'table.insert' },\n creates: { type: 'table' },\n icon: 'i-lucide-table',\n label: ctx.t('tables.toolbar.insert'),\n ability: ABILITY.blockContent,\n command: c => c.insertTable(INSERT),\n }),\n // GFM has no nested tables: inside one the command is off.\n can: (editor: Editor) => !editor.isActive('table') && editor.can().insertTable(INSERT),\n },\n command({ id: 'addRow', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 20 }, icon: 'i-lucide-between-horizontal-end', label: ctx.t('tables.toolbar.addRow'), ability: ABILITY.blockContent, command: c => c.addRowAfter() }),\n command({ id: 'addColumn', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 30 }, icon: 'i-lucide-between-vertical-end', label: ctx.t('tables.toolbar.addColumn'), ability: ABILITY.blockContent, command: c => c.addColumnAfter() }),\n command({ id: 'deleteRow', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 40 }, icon: 'i-lucide-table-rows-split', label: ctx.t('tables.toolbar.deleteRow'), ability: ABILITY.blockContent, command: c => c.deleteRow() }),\n command({ id: 'deleteColumn', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 50 }, icon: 'i-lucide-table-columns-split', label: ctx.t('tables.toolbar.deleteColumn'), ability: ABILITY.blockContent, command: c => c.deleteColumn() }),\n command({ id: 'deleteTable', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 60 }, icon: 'i-lucide-trash-2', label: ctx.t('tables.toolbar.deleteTable'), ability: ABILITY.blockContent, command: c => c.deleteTable() }),\n command({ id: 'task.insert', slash: { id: 'task.insert' }, creates: { type: 'taskList' }, icon: 'i-lucide-list-checks', label: ctx.t('tables.slash.tasks'), ability: ABILITY.blockContent, command: c => c.toggleTaskList() }),\n]\n\n/**\n * GFM tables and task lists: the table and task-list nodes (Markdown round-trips through `@tiptap/markdown`, tables as\n * pipe tables, tasks as `- [ ]`/`- [x]`), the toolbar group `table` (insert 3×3, add row, add column, delete row, delete\n * column, delete table) and the slash entries `table.insert` and `task.insert`: one command each, the table commands\n * on the toolbar, `insertTable` and `task.insert` also in the `/` menu. All are offered where the caret has\n * `block.content`. Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.\n */\nexport function tablesFeature(): EditorFeature<never> {\n return {\n id: 'tables',\n dimension: 'content',\n schemaExtensions: tablesSchemaExtensions,\n extensions: () => tablesSchemaExtensions(),\n toolbarGroups,\n commands,\n }\n}\n"],"mappings":";;;;;AAMA,MAAa,sBAAsB;;AAGnC,MAAM,SAAS;CAAE,MAAM;CAAG,MAAM;CAAG,eAAe;AAAK;AAEvD,MAAM,gBAAmC,CAAC;CAAE,IAAI;CAAqB,OAAO;CAAI,UAAU;AAAsB,CAAC;AAEjH,MAAM,YAAY,QAA6C;CAC7D;EACE,GAAG,QAAQ;GACT,IAAI;GACJ,SAAS;IAAE,OAAO;IAAqB,OAAO;GAAG;GACjD,OAAO,EAAE,IAAI,eAAe;GAC5B,SAAS,EAAE,MAAM,QAAQ;GACzB,MAAM;GACN,OAAO,IAAI,EAAE,uBAAuB;GACpC,SAAS,QAAQ;GACjB,UAAS,MAAK,EAAE,YAAY,MAAM;EACpC,CAAC;EAED,MAAM,WAAmB,CAAC,OAAO,SAAS,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,YAAY,MAAM;CACvF;CACA,QAAQ;EAAE,IAAI;EAAU,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAmC,OAAO,IAAI,EAAE,uBAAuB;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,YAAY;CAAE,CAAC;CAC1N,QAAQ;EAAE,IAAI;EAAa,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAiC,OAAO,IAAI,EAAE,0BAA0B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,eAAe;CAAE,CAAC;CACjO,QAAQ;EAAE,IAAI;EAAa,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAA6B,OAAO,IAAI,EAAE,0BAA0B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,UAAU;CAAE,CAAC;CACxN,QAAQ;EAAE,IAAI;EAAgB,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAgC,OAAO,IAAI,EAAE,6BAA6B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,aAAa;CAAE,CAAC;CACpO,QAAQ;EAAE,IAAI;EAAe,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAoB,OAAO,IAAI,EAAE,4BAA4B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,YAAY;CAAE,CAAC;CACrN,QAAQ;EAAE,IAAI;EAAe,OAAO,EAAE,IAAI,cAAc;EAAG,SAAS,EAAE,MAAM,WAAW;EAAG,MAAM;EAAwB,OAAO,IAAI,EAAE,oBAAoB;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,eAAe;CAAE,CAAC;AAC/N;;;;;;;;AASA,SAAgB,gBAAsC;CACpD,OAAO;EACL,IAAI;EACJ,WAAW;EACX,kBAAkB;EAClB,kBAAkB,uBAAuB;EACzC;EACA;CACF;AACF"}
@@ -19,14 +19,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
19
19
  __name: "SettingsAppearanceTab",
20
20
  setup(__props) {
21
21
  const { settings } = useEditorSettings();
22
- const { fetch } = useEditorContext();
22
+ const { backend } = useEditorContext();
23
23
  const { Badge: UBadge, Button: UButton, FormField: UFormField, Input: UInput, Select: USelect, notifier } = useUiKit();
24
24
  const notify = notifier();
25
25
  const { t } = useEditorI18n();
26
26
  /** USelect has no `null` value: „Standard“ travels as this sentinel (no theme id can contain underscores). */
27
27
  const themes = shallowRef([]);
28
28
  async function loadThemes() {
29
- themes.value = await fetch("/api/editor/themes");
29
+ themes.value = await backend.themes?.list() ?? [];
30
30
  }
31
31
  onMounted(async () => {
32
32
  try {
@@ -59,14 +59,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
59
59
  async function upload() {
60
60
  const css = cssFile.value;
61
61
  if (!css || uploading.value) return;
62
- const body = new FormData();
63
- body.append("css", css);
64
- if (manifestFile.value) body.append("manifest", manifestFile.value);
65
62
  uploading.value = true;
66
63
  try {
67
- const record = await fetch("/api/editor/themes", {
68
- method: "POST",
69
- body
64
+ const record = await backend.themes.store({
65
+ css,
66
+ ...manifestFile.value && { manifest: manifestFile.value }
70
67
  });
71
68
  cssFile.value = null;
72
69
  manifestFile.value = null;
@@ -96,7 +93,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
96
93
  if (removing.value) return;
97
94
  removing.value = record.id;
98
95
  try {
99
- await fetch(`/api/editor/themes/${encodeURIComponent(record.id)}`, { method: "DELETE" });
96
+ await backend.themes.remove(record.id);
100
97
  if (settings.value.appearance.theme === record.id) settings.value.appearance.theme = null;
101
98
  notify({
102
99
  title: t("themes.delete.done", { name: record.name }),
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsAppearanceTab.js","names":["_openBlock","_createElementBlock","_createVNode","SettingsSection","_unref","_withCtx","_createElementVNode","_withModifiers","_createBlock","_Fragment","_renderList","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/themes/components/SettingsAppearanceTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, ref, shallowRef } from 'vue'\nimport SettingsSection from '../../../vue/components/SettingsSection.vue'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the Nuxt UI look. */\nconst { settings } = useEditorSettings()\nconst { fetch } = useEditorContext()\nconst { Badge: UBadge, Button: UButton, FormField: UFormField, Input: UInput, Select: USelect, notifier } = useUiKit()\nconst notify = notifier()\nconst { t } = useEditorI18n()\n\n/** USelect has no `null` value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\nconst themes = shallowRef<ThemeRecord[]>([])\n\nasync function loadThemes() {\n themes.value = await fetch<ThemeRecord[]>('/api/editor/themes')\n}\n\nonMounted(async () => {\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n})\n\nconst items = computed(() => [\n { label: t('themes.standard'), value: STANDARD },\n ...themes.value.map(({ id, name, author }) => ({ label: author ? `${name} – ${author}` : name, value: id })),\n])\n\nconst theme = computed({\n get: () => settings.value.appearance.theme ?? STANDARD,\n set: (value: string) => {\n settings.value.appearance.theme = value === STANDARD ? null : value\n },\n})\n\nconst cssFile = shallowRef<File | null>(null)\nconst manifestFile = shallowRef<File | null>(null)\nconst uploading = ref(false)\n/** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its model). */\nconst inputKey = ref(0)\n\nfunction pickedFile(event: Event): File | null {\n return (event.target as HTMLInputElement).files?.[0] ?? null\n}\n\nasync function upload() {\n const css = cssFile.value\n if (!css || uploading.value) return\n const body = new FormData()\n body.append('css', css)\n if (manifestFile.value) body.append('manifest', manifestFile.value)\n uploading.value = true\n try {\n const record = await fetch<ThemeRecord>('/api/editor/themes', { method: 'POST', body })\n cssFile.value = null\n manifestFile.value = null\n inputKey.value++\n settings.value.appearance.theme = record.id\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n uploading.value = false\n }\n}\n\nconst removing = ref<string | null>(null)\n\nasync function remove(record: ThemeRecord) {\n if (removing.value) return\n removing.value = record.id\n try {\n await fetch(`/api/editor/themes/${encodeURIComponent(record.id)}`, { method: 'DELETE' })\n if (settings.value.appearance.theme === record.id) settings.value.appearance.theme = null\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n removing.value = null\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-8\">\n <SettingsSection :title=\"t('themes.title')\">\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.select.label')\"\n :description=\"t('themes.select.description')\"\n >\n <USelect v-model=\"theme\" :items=\"items\" class=\"w-64\" />\n </UFormField>\n\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.upload.label')\"\n :description=\"t('themes.upload.description')\"\n >\n <form class=\"flex w-64 flex-col gap-2\" @submit.prevent=\"upload\">\n <UFormField :label=\"t('themes.upload.css')\" required size=\"xs\">\n <UInput\n :key=\"`css-${inputKey}`\"\n type=\"file\"\n accept=\".css,text/css\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"cssFile = pickedFile($event)\"\n />\n </UFormField>\n <UFormField :label=\"t('themes.upload.manifest')\" size=\"xs\">\n <UInput\n :key=\"`manifest-${inputKey}`\"\n type=\"file\"\n accept=\".json,application/json\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"manifestFile = pickedFile($event)\"\n />\n </UFormField>\n <UButton\n type=\"submit\"\n icon=\"i-lucide-upload\"\n :label=\"t('themes.upload.submit')\"\n class=\"self-start\"\n :disabled=\"!cssFile\"\n :loading=\"uploading\"\n />\n </form>\n </UFormField>\n </SettingsSection>\n\n <SettingsSection v-if=\"themes.length\" :title=\"t('themes.library')\">\n <div v-for=\"record in themes\" :key=\"record.id\" class=\"flex items-center gap-3 py-2\">\n <div class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate text-sm font-medium text-[var(--sk-fg-highlighted)]\">{{ record.name }}</span>\n <span v-if=\"record.version || record.author\" class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ [record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ') }}\n </span>\n </div>\n <UBadge v-if=\"record.builtin\" color=\"neutral\" variant=\"subtle\" :label=\"t('themes.builtin')\" />\n <UButton\n v-else\n icon=\"i-lucide-trash-2\"\n variant=\"ghost\"\n color=\"error\"\n :aria-label=\"t('themes.delete.label', { name: record.name })\"\n :loading=\"removing === record.id\"\n :disabled=\"removing !== null\"\n @click=\"remove(record)\"\n />\n </div>\n </SettingsSection>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"SettingsAppearanceTab.js","names":["_openBlock","_createElementBlock","_createVNode","SettingsSection","_unref","_withCtx","_createElementVNode","_withModifiers","_createBlock","_Fragment","_renderList","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/themes/components/SettingsAppearanceTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, ref, shallowRef } from 'vue'\nimport SettingsSection from '../../../vue/components/SettingsSection.vue'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the Nuxt UI look. */\nconst { settings } = useEditorSettings()\nconst { backend } = useEditorContext()\nconst { Badge: UBadge, Button: UButton, FormField: UFormField, Input: UInput, Select: USelect, notifier } = useUiKit()\nconst notify = notifier()\nconst { t } = useEditorI18n()\n\n/** USelect has no `null` value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\nconst themes = shallowRef<ThemeRecord[]>([])\n\nasync function loadThemes() {\n themes.value = await backend.themes?.list() ?? []\n}\n\nonMounted(async () => {\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n})\n\nconst items = computed(() => [\n { label: t('themes.standard'), value: STANDARD },\n ...themes.value.map(({ id, name, author }) => ({ label: author ? `${name} – ${author}` : name, value: id })),\n])\n\nconst theme = computed({\n get: () => settings.value.appearance.theme ?? STANDARD,\n set: (value: string) => {\n settings.value.appearance.theme = value === STANDARD ? null : value\n },\n})\n\nconst cssFile = shallowRef<File | null>(null)\nconst manifestFile = shallowRef<File | null>(null)\nconst uploading = ref(false)\n/** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its model). */\nconst inputKey = ref(0)\n\nfunction pickedFile(event: Event): File | null {\n return (event.target as HTMLInputElement).files?.[0] ?? null\n}\n\nasync function upload() {\n const css = cssFile.value\n if (!css || uploading.value) return\n uploading.value = true\n try {\n // The themes feature needs the `themes` capability, so the tab is only mounted where the backend has it.\n const record = await backend.themes!.store({ css, ...(manifestFile.value && { manifest: manifestFile.value }) })\n cssFile.value = null\n manifestFile.value = null\n inputKey.value++\n settings.value.appearance.theme = record.id\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n uploading.value = false\n }\n}\n\nconst removing = ref<string | null>(null)\n\nasync function remove(record: ThemeRecord) {\n if (removing.value) return\n removing.value = record.id\n try {\n await backend.themes!.remove(record.id)\n if (settings.value.appearance.theme === record.id) settings.value.appearance.theme = null\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n removing.value = null\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-8\">\n <SettingsSection :title=\"t('themes.title')\">\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.select.label')\"\n :description=\"t('themes.select.description')\"\n >\n <USelect v-model=\"theme\" :items=\"items\" class=\"w-64\" />\n </UFormField>\n\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.upload.label')\"\n :description=\"t('themes.upload.description')\"\n >\n <form class=\"flex w-64 flex-col gap-2\" @submit.prevent=\"upload\">\n <UFormField :label=\"t('themes.upload.css')\" required size=\"xs\">\n <UInput\n :key=\"`css-${inputKey}`\"\n type=\"file\"\n accept=\".css,text/css\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"cssFile = pickedFile($event)\"\n />\n </UFormField>\n <UFormField :label=\"t('themes.upload.manifest')\" size=\"xs\">\n <UInput\n :key=\"`manifest-${inputKey}`\"\n type=\"file\"\n accept=\".json,application/json\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"manifestFile = pickedFile($event)\"\n />\n </UFormField>\n <UButton\n type=\"submit\"\n icon=\"i-lucide-upload\"\n :label=\"t('themes.upload.submit')\"\n class=\"self-start\"\n :disabled=\"!cssFile\"\n :loading=\"uploading\"\n />\n </form>\n </UFormField>\n </SettingsSection>\n\n <SettingsSection v-if=\"themes.length\" :title=\"t('themes.library')\">\n <div v-for=\"record in themes\" :key=\"record.id\" class=\"flex items-center gap-3 py-2\">\n <div class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate text-sm font-medium text-[var(--sk-fg-highlighted)]\">{{ record.name }}</span>\n <span v-if=\"record.version || record.author\" class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ [record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ') }}\n </span>\n </div>\n <UBadge v-if=\"record.builtin\" color=\"neutral\" variant=\"subtle\" :label=\"t('themes.builtin')\" />\n <UButton\n v-else\n icon=\"i-lucide-trash-2\"\n variant=\"ghost\"\n color=\"error\"\n :aria-label=\"t('themes.delete.label', { name: record.name })\"\n :loading=\"removing === record.id\"\n :disabled=\"removing !== null\"\n @click=\"remove(record)\"\n />\n </div>\n </SettingsSection>\n </div>\n</template>\n"],"mappings":"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"}
@@ -10,6 +10,8 @@ import { startObsidianTheme } from "./theme-runtime.js";
10
10
  function themesFeatureCore(ui) {
11
11
  return {
12
12
  id: "themes",
13
+ dimension: "interaction",
14
+ backend: ["themes"],
13
15
  setup: (ctx) => ui.lifetime ? ui.lifetime(ctx, () => startObsidianTheme(ctx)) : startObsidianTheme(ctx),
14
16
  settingsTabs: (ctx) => [{
15
17
  id: "appearance",
@@ -1 +1 @@
1
- {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/themes/feature.ts"],"sourcesContent":["import type { EditorFeature, FeatureContext } from '../../core'\nimport { startObsidianTheme } from './theme-runtime'\n\n/** What a framework binding supplies to the themes feature. */\nexport interface ThemesUi<C> {\n /** The „Darstellung“ settings tab component of the framework. */\n settingsTab: C\n /**\n * Decides how long the theme runtime lives. Receives the feature context and `start` (starts the shared, reference-counted\n * runtime and returns its idempotent release); its return value is the feature's `setup` result (the teardown run when\n * the surface unmounts). Default: `start()` itself, i.e. the runtime lives as long as the surface is mounted.\n */\n lifetime?: (ctx: FeatureContext, start: () => () => void) => void | (() => void)\n}\n\n/**\n * Site-wide Obsidian themes: the theme named by the setting `appearance.theme` (`null` = the standard look) is\n * fetched from `GET /api/editor/themes/:id`, scoped to `.obsidian-theme` and injected as one `<style data-tiptap-theme>`\n * in the document head. `<html>` carries `obsidian-theme theme-dark|theme-light` following the app's colour mode\n * (`html.dark`, else the system preference), so the bridge stylesheet `styles/themes.css` maps Obsidian's variables onto\n * every UI token (the whole app) and onto the editor content. Needs an adapter with `listThemes`/`loadTheme`.\n */\nexport function themesFeatureCore<C>(ui: ThemesUi<C>): EditorFeature<C> {\n return {\n id: 'themes',\n setup: ctx => (ui.lifetime ? ui.lifetime(ctx, () => startObsidianTheme(ctx)) : startObsidianTheme(ctx)),\n settingsTabs: ctx => [{ id: 'appearance', label: ctx.t('themes.tab'), icon: 'i-lucide-palette', component: ui.settingsTab }],\n }\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,kBAAqB,IAAmC;CACtE,OAAO;EACL,IAAI;EACJ,QAAO,QAAQ,GAAG,WAAW,GAAG,SAAS,WAAW,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,GAAG;EACrG,eAAc,QAAO,CAAC;GAAE,IAAI;GAAc,OAAO,IAAI,EAAE,YAAY;GAAG,MAAM;GAAoB,WAAW,GAAG;EAAY,CAAC;CAC7H;AACF"}
1
+ {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/themes/feature.ts"],"sourcesContent":["import type { EditorFeature, FeatureContext } from '../../core'\nimport { startObsidianTheme } from './theme-runtime'\n\n/** What a framework binding supplies to the themes feature. */\nexport interface ThemesUi<C> {\n /** The „Darstellung“ settings tab component of the framework. */\n settingsTab: C\n /**\n * Decides how long the theme runtime lives. Receives the feature context and `start` (starts the shared, reference-counted\n * runtime and returns its idempotent release); its return value is the feature's `setup` result (the teardown run when\n * the surface unmounts). Default: `start()` itself, i.e. the runtime lives as long as the surface is mounted.\n */\n lifetime?: (ctx: FeatureContext, start: () => () => void) => void | (() => void)\n}\n\n/**\n * Site-wide Obsidian themes: the theme named by the setting `appearance.theme` (`null` = the standard look) is\n * fetched from `GET /api/editor/themes/:id`, scoped to `.obsidian-theme` and injected as one `<style data-tiptap-theme>`\n * in the document head. `<html>` carries `obsidian-theme theme-dark|theme-light` following the app's colour mode\n * (`html.dark`, else the system preference), so the bridge stylesheet `styles/themes.css` maps Obsidian's variables onto\n * every UI token (the whole app) and onto the editor content. Needs an adapter with `listThemes`/`loadTheme`.\n */\nexport function themesFeatureCore<C>(ui: ThemesUi<C>): EditorFeature<C> {\n return {\n id: 'themes',\n dimension: 'interaction',\n backend: ['themes'],\n setup: ctx => (ui.lifetime ? ui.lifetime(ctx, () => startObsidianTheme(ctx)) : startObsidianTheme(ctx)),\n settingsTabs: ctx => [{ id: 'appearance', label: ctx.t('themes.tab'), icon: 'i-lucide-palette', component: ui.settingsTab }],\n }\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,kBAAqB,IAAmC;CACtE,OAAO;EACL,IAAI;EACJ,WAAW;EACX,SAAS,CAAC,QAAQ;EAClB,QAAO,QAAQ,GAAG,WAAW,GAAG,SAAS,WAAW,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,GAAG;EACrG,eAAc,QAAO,CAAC;GAAE,IAAI;GAAc,OAAO,IAAI,EAAE,YAAY;GAAG,MAAM;GAAoB,WAAW,GAAG;EAAY,CAAC;CAC7H;AACF"}
@@ -1,5 +1,6 @@
1
1
  import { THEME_ID } from "../../settings/index.js";
2
- import { MAX_THEME_BYTES, ThemeManifest, ThemeRecord, themeManifestSchema } from "./schema.js";
2
+ import { ThemeRecord } from "../../core/backend-types.js";
3
+ import { MAX_THEME_BYTES, ThemeManifest, themeManifestSchema } from "./schema.js";
3
4
  import { ThemesUi, themesFeatureCore } from "./feature.js";
4
5
  import { scopeThemeCss } from "./scope-css.js";
5
6
  import { THEME_SCOPE_CLASS, startObsidianTheme } from "./theme-runtime.js";