@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,5 +1,5 @@
1
1
  import { Translate } from "../../i18n/index.js";
2
- import { $Fetch } from "ofetch";
2
+ import { EditorBackend } from "../../core/backend.js";
3
3
  //#region src/features/ai/ai-sse.d.ts
4
4
  /** How an AI SSE request ended. `rate-limited`: HTTP 429 before the stream started. */
5
5
  export type AiSseOutcome = {
@@ -13,12 +13,12 @@ export type AiSseOutcome = {
13
13
  status: 'aborted';
14
14
  };
15
15
  /**
16
- * POSTs `body` as JSON through `fetch` (the app's HTTP client: `FeatureContext.fetch`) to an AI streaming endpoint
17
- * (`/api/ai/generate`, `/api/ai/chat`: SSE events `delta` with `{ text }`, `done`, `error` with `{ message }`) and feeds
18
- * every text delta to `onDelta`; any other event goes with its parsed JSON data to `onEvent`. A failure without a
19
- * server message reads as `t` renders it. Never throws.
16
+ * Runs an AI streaming request through `ai.stream` (the backend's `generate` / `chat`: SSE events `delta` with
17
+ * `{ text }`, `done`, `error` with `{ message }`) and feeds every text delta to `onDelta`; any other event goes with its
18
+ * parsed JSON data to `onEvent`. A failure without a server message reads as `t` renders it; a backend without `ai`
19
+ * fails like a server without the route (501). Never throws.
20
20
  */
21
- export declare function postAiSse(fetch: $Fetch, url: string, body: unknown, opts: {
21
+ export declare function postAiSse(ai: EditorBackend['ai'], path: 'generate' | 'chat', body: object, opts: {
22
22
  signal: AbortSignal;
23
23
  t: Translate;
24
24
  onDelta: (text: string) => void;
@@ -1,78 +1,35 @@
1
1
  import { isRateLimited } from "./rate-limit.js";
2
2
  //#region src/features/ai/ai-sse.ts
3
3
  /**
4
- * POSTs `body` as JSON through `fetch` (the app's HTTP client: `FeatureContext.fetch`) to an AI streaming endpoint
5
- * (`/api/ai/generate`, `/api/ai/chat`: SSE events `delta` with `{ text }`, `done`, `error` with `{ message }`) and feeds
6
- * every text delta to `onDelta`; any other event goes with its parsed JSON data to `onEvent`. A failure without a
7
- * server message reads as `t` renders it. Never throws.
4
+ * Runs an AI streaming request through `ai.stream` (the backend's `generate` / `chat`: SSE events `delta` with
5
+ * `{ text }`, `done`, `error` with `{ message }`) and feeds every text delta to `onDelta`; any other event goes with its
6
+ * parsed JSON data to `onEvent`. A failure without a server message reads as `t` renders it; a backend without `ai`
7
+ * fails like a server without the route (501). Never throws.
8
8
  */
9
- async function postAiSse(fetch, url, body, opts) {
9
+ async function postAiSse(ai, path, body, opts) {
10
10
  const { signal, t, onDelta, onEvent } = opts;
11
11
  const failed = (message) => signal.aborted ? { status: "aborted" } : {
12
12
  status: "error",
13
13
  message
14
14
  };
15
- /** The outcome once the stream is finished (`done` or `error` event), otherwise `null`. */
16
- function handleEvent(raw) {
17
- let event = "message";
18
- let data = "";
19
- for (const line of raw.split("\n")) if (line.startsWith("event:")) event = line.slice(6).trim();
20
- else if (line.startsWith("data:")) data += line.slice(5).trimStart();
21
- let payload = {};
22
- if (data) try {
23
- payload = JSON.parse(data);
24
- } catch {
25
- payload = {};
26
- }
27
- if (event === "delta") {
28
- if (payload.text) onDelta(payload.text);
29
- return null;
30
- }
31
- if (event === "done") return { status: "done" };
32
- if (event === "error") return {
33
- status: "error",
34
- message: payload.message || t("ai.sse.unknownError")
35
- };
36
- onEvent?.(event, payload);
37
- return null;
38
- }
15
+ if (!ai) return failed(t("ai.sse.httpError", { status: 501 }));
39
16
  try {
40
- const res = await fetch.raw(url, {
41
- method: "POST",
42
- body,
43
- signal,
44
- responseType: "stream",
45
- ignoreResponseError: true
46
- });
47
- if (!res.ok || !res.body) {
48
- if (isRateLimited(res)) return { status: "rate-limited" };
49
- let message = t("ai.sse.httpError", { status: res.status });
50
- try {
51
- const json = await res.json();
52
- message = json.statusMessage || json.message || message;
53
- } catch {}
54
- return failed(message);
55
- }
56
- const reader = res.body.getReader();
57
- const decoder = new TextDecoder();
58
- let buffer = "";
59
- while (true) {
60
- const { value, done } = await reader.read();
61
- if (done) break;
62
- buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
63
- let index = buffer.indexOf("\n\n");
64
- while (index !== -1) {
65
- const outcome = handleEvent(buffer.slice(0, index));
66
- if (outcome) {
67
- await reader.cancel().catch(() => void 0);
68
- return outcome;
69
- }
70
- buffer = buffer.slice(index + 2);
71
- index = buffer.indexOf("\n\n");
72
- }
17
+ for await (const { event, data } of ai.stream(path, body, signal)) {
18
+ const payload = data ?? {};
19
+ if (event === "delta") {
20
+ if (payload.text) onDelta(payload.text);
21
+ } else if (event === "done") return { status: "done" };
22
+ else if (event === "error") return {
23
+ status: "error",
24
+ message: payload.message || t("ai.sse.unknownError")
25
+ };
26
+ else onEvent?.(event, payload);
73
27
  }
74
- return buffer.trim() && handleEvent(buffer) || failed(t("ai.sse.aborted"));
28
+ return failed(t("ai.sse.aborted"));
75
29
  } catch (err) {
30
+ if (isRateLimited(err)) return { status: "rate-limited" };
31
+ const status = err?.status;
32
+ if (typeof status === "number" && err instanceof Error && err.message === `HTTP ${status}`) return failed(t("ai.sse.httpError", { status }));
76
33
  return failed(err instanceof Error && err.message ? err.message : t("ai.sse.aborted"));
77
34
  }
78
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ai-sse.js","names":[],"sources":["../../../src/features/ai/ai-sse.ts"],"sourcesContent":["import type { $Fetch } from 'ofetch'\nimport type { Translate } from '../../i18n'\nimport { isRateLimited } from './rate-limit'\n\n/** How an AI SSE request ended. `rate-limited`: HTTP 429 before the stream started. */\nexport type AiSseOutcome =\n | { status: 'done' }\n | { status: 'error', message: string }\n | { status: 'rate-limited' }\n | { status: 'aborted' }\n\n/**\n * POSTs `body` as JSON through `fetch` (the app's HTTP client: `FeatureContext.fetch`) to an AI streaming endpoint\n * (`/api/ai/generate`, `/api/ai/chat`: SSE events `delta` with `{ text }`, `done`, `error` with `{ message }`) and feeds\n * every text delta to `onDelta`; any other event goes with its parsed JSON data to `onEvent`. A failure without a\n * server message reads as `t` renders it. Never throws.\n */\nexport async function postAiSse(fetch: $Fetch, url: string, body: unknown, opts: { signal: AbortSignal, t: Translate, onDelta: (text: string) => void, onEvent?: (event: string, data: unknown) => void }): Promise<AiSseOutcome> {\n const { signal, t, onDelta, onEvent } = opts\n const failed = (message: string): AiSseOutcome => signal.aborted ? { status: 'aborted' } : { status: 'error', message }\n\n /** The outcome once the stream is finished (`done` or `error` event), otherwise `null`. */\n function handleEvent(raw: string): AiSseOutcome | null {\n let event = 'message'\n let data = ''\n for (const line of raw.split('\\n')) {\n if (line.startsWith('event:')) event = line.slice(6).trim()\n else if (line.startsWith('data:')) data += line.slice(5).trimStart()\n }\n let payload: { text?: string, message?: string } = {}\n if (data) {\n try {\n payload = JSON.parse(data)\n }\n catch {\n payload = {}\n }\n }\n if (event === 'delta') {\n if (payload.text) onDelta(payload.text)\n return null\n }\n if (event === 'done') return { status: 'done' }\n if (event === 'error') return { status: 'error', message: payload.message || t('ai.sse.unknownError') }\n onEvent?.(event, payload)\n return null\n }\n\n try {\n const res = await fetch.raw<ReadableStream<Uint8Array>, 'stream'>(url, {\n method: 'POST',\n body: body as Record<string, unknown>,\n signal,\n responseType: 'stream',\n ignoreResponseError: true,\n })\n if (!res.ok || !res.body) {\n if (isRateLimited(res)) return { status: 'rate-limited' }\n let message = t('ai.sse.httpError', { status: res.status })\n try {\n const json = await res.json() as { statusMessage?: string, message?: string }\n message = json.statusMessage || json.message || message\n }\n catch { /* non-JSON error body */ }\n return failed(message)\n }\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true }).replace(/\\r\\n/g, '\\n')\n let index = buffer.indexOf('\\n\\n')\n while (index !== -1) {\n const outcome = handleEvent(buffer.slice(0, index))\n if (outcome) {\n await reader.cancel().catch(() => undefined)\n return outcome\n }\n buffer = buffer.slice(index + 2)\n index = buffer.indexOf('\\n\\n')\n }\n }\n return (buffer.trim() && handleEvent(buffer)) || failed(t('ai.sse.aborted'))\n }\n catch (err) {\n return failed(err instanceof Error && err.message ? err.message : t('ai.sse.aborted'))\n }\n}\n"],"mappings":";;;;;;;;AAiBA,eAAsB,UAAU,OAAe,KAAa,MAAe,MAAuJ;CAChO,MAAM,EAAE,QAAQ,GAAG,SAAS,YAAY;CACxC,MAAM,UAAU,YAAkC,OAAO,UAAU,EAAE,QAAQ,UAAU,IAAI;EAAE,QAAQ;EAAS;CAAQ;;CAGtH,SAAS,YAAY,KAAkC;EACrD,IAAI,QAAQ;EACZ,IAAI,OAAO;EACX,KAAK,MAAM,QAAQ,IAAI,MAAM,IAAI,GAC/B,IAAI,KAAK,WAAW,QAAQ,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK;OACrD,IAAI,KAAK,WAAW,OAAO,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU;EAErE,IAAI,UAA+C,CAAC;EACpD,IAAI,MACF,IAAI;GACF,UAAU,KAAK,MAAM,IAAI;EAC3B,QACM;GACJ,UAAU,CAAC;EACb;EAEF,IAAI,UAAU,SAAS;GACrB,IAAI,QAAQ,MAAM,QAAQ,QAAQ,IAAI;GACtC,OAAO;EACT;EACA,IAAI,UAAU,QAAQ,OAAO,EAAE,QAAQ,OAAO;EAC9C,IAAI,UAAU,SAAS,OAAO;GAAE,QAAQ;GAAS,SAAS,QAAQ,WAAW,EAAE,qBAAqB;EAAE;EACtG,UAAU,OAAO,OAAO;EACxB,OAAO;CACT;CAEA,IAAI;EACF,MAAM,MAAM,MAAM,MAAM,IAA0C,KAAK;GACrE,QAAQ;GACF;GACN;GACA,cAAc;GACd,qBAAqB;EACvB,CAAC;EACD,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM;GACxB,IAAI,cAAc,GAAG,GAAG,OAAO,EAAE,QAAQ,eAAe;GACxD,IAAI,UAAU,EAAE,oBAAoB,EAAE,QAAQ,IAAI,OAAO,CAAC;GAC1D,IAAI;IACF,MAAM,OAAO,MAAM,IAAI,KAAK;IAC5B,UAAU,KAAK,iBAAiB,KAAK,WAAW;GAClD,QACM,CAA4B;GAClC,OAAO,OAAO,OAAO;EACvB;EACA,MAAM,SAAS,IAAI,KAAK,UAAU;EAClC,MAAM,UAAU,IAAI,YAAY;EAChC,IAAI,SAAS;EACb,OAAO,MAAM;GACX,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC,QAAQ,SAAS,IAAI;GACvE,IAAI,QAAQ,OAAO,QAAQ,MAAM;GACjC,OAAO,UAAU,IAAI;IACnB,MAAM,UAAU,YAAY,OAAO,MAAM,GAAG,KAAK,CAAC;IAClD,IAAI,SAAS;KACX,MAAM,OAAO,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;KAC3C,OAAO;IACT;IACA,SAAS,OAAO,MAAM,QAAQ,CAAC;IAC/B,QAAQ,OAAO,QAAQ,MAAM;GAC/B;EACF;EACA,OAAQ,OAAO,KAAK,KAAK,YAAY,MAAM,KAAM,OAAO,EAAE,gBAAgB,CAAC;CAC7E,SACO,KAAK;EACV,OAAO,OAAO,eAAe,SAAS,IAAI,UAAU,IAAI,UAAU,EAAE,gBAAgB,CAAC;CACvF;AACF"}
1
+ {"version":3,"file":"ai-sse.js","names":[],"sources":["../../../src/features/ai/ai-sse.ts"],"sourcesContent":["import type { EditorBackend } from '../../core/backend'\nimport type { Translate } from '../../i18n'\nimport { isRateLimited } from './rate-limit'\n\n/** How an AI SSE request ended. `rate-limited`: HTTP 429 before the stream started. */\nexport type AiSseOutcome =\n | { status: 'done' }\n | { status: 'error', message: string }\n | { status: 'rate-limited' }\n | { status: 'aborted' }\n\n/**\n * Runs an AI streaming request through `ai.stream` (the backend's `generate` / `chat`: SSE events `delta` with\n * `{ text }`, `done`, `error` with `{ message }`) and feeds every text delta to `onDelta`; any other event goes with its\n * parsed JSON data to `onEvent`. A failure without a server message reads as `t` renders it; a backend without `ai`\n * fails like a server without the route (501). Never throws.\n */\nexport async function postAiSse(ai: EditorBackend['ai'], path: 'generate' | 'chat', body: object, opts: { signal: AbortSignal, t: Translate, onDelta: (text: string) => void, onEvent?: (event: string, data: unknown) => void }): Promise<AiSseOutcome> {\n const { signal, t, onDelta, onEvent } = opts\n const failed = (message: string): AiSseOutcome => signal.aborted ? { status: 'aborted' } : { status: 'error', message }\n if (!ai) return failed(t('ai.sse.httpError', { status: 501 }))\n\n try {\n for await (const { event, data } of ai.stream(path, body, signal)) {\n const payload = (data ?? {}) as { text?: string, message?: string }\n if (event === 'delta') {\n if (payload.text) onDelta(payload.text)\n }\n else if (event === 'done') return { status: 'done' }\n else if (event === 'error') return { status: 'error', message: payload.message || t('ai.sse.unknownError') }\n else onEvent?.(event, payload)\n }\n return failed(t('ai.sse.aborted'))\n }\n catch (err) {\n if (isRateLimited(err)) return { status: 'rate-limited' }\n // The backend words a non-2xx answer without a server message as `HTTP <status>`; `t` renders it instead.\n const status = (err as { status?: unknown } | null)?.status\n if (typeof status === 'number' && err instanceof Error && err.message === `HTTP ${status}`) return failed(t('ai.sse.httpError', { status }))\n return failed(err instanceof Error && err.message ? err.message : t('ai.sse.aborted'))\n }\n}\n"],"mappings":";;;;;;;;AAiBA,eAAsB,UAAU,IAAyB,MAA2B,MAAc,MAAuJ;CACvP,MAAM,EAAE,QAAQ,GAAG,SAAS,YAAY;CACxC,MAAM,UAAU,YAAkC,OAAO,UAAU,EAAE,QAAQ,UAAU,IAAI;EAAE,QAAQ;EAAS;CAAQ;CACtH,IAAI,CAAC,IAAI,OAAO,OAAO,EAAE,oBAAoB,EAAE,QAAQ,IAAI,CAAC,CAAC;CAE7D,IAAI;EACF,WAAW,MAAM,EAAE,OAAO,UAAU,GAAG,OAAO,MAAM,MAAM,MAAM,GAAG;GACjE,MAAM,UAAW,QAAQ,CAAC;GAC1B,IAAI,UAAU,SACR;QAAA,QAAQ,MAAM,QAAQ,QAAQ,IAAI;GAAA,OAEnC,IAAI,UAAU,QAAQ,OAAO,EAAE,QAAQ,OAAO;QAC9C,IAAI,UAAU,SAAS,OAAO;IAAE,QAAQ;IAAS,SAAS,QAAQ,WAAW,EAAE,qBAAqB;GAAE;QACtG,UAAU,OAAO,OAAO;EAC/B;EACA,OAAO,OAAO,EAAE,gBAAgB,CAAC;CACnC,SACO,KAAK;EACV,IAAI,cAAc,GAAG,GAAG,OAAO,EAAE,QAAQ,eAAe;EAExD,MAAM,SAAU,KAAqC;EACrD,IAAI,OAAO,WAAW,YAAY,eAAe,SAAS,IAAI,YAAY,QAAQ,UAAU,OAAO,OAAO,EAAE,oBAAoB,EAAE,OAAO,CAAC,CAAC;EAC3I,OAAO,OAAO,eAAe,SAAS,IAAI,UAAU,IAAI,UAAU,EAAE,gBAAgB,CAAC;CACvF;AACF"}
@@ -1,13 +1,13 @@
1
+ import { ReadonlyStore } from "../../core/store.js";
1
2
  import { Translate } from "../../i18n/index.js";
2
3
  import { AiLanguage, ModelSlot } from "../../settings/values.js";
3
4
  import "../../settings/index.js";
5
+ import { EditorBackend } from "../../core/backend.js";
4
6
  import { Notifier } from "../../core/notify.js";
5
- import { ReadonlyStore } from "../../core/store.js";
6
7
  import { AiBlock } from "../../core/feature.js";
7
- import { ModelRef } from "./protocol/models.js";
8
+ import { ModelRef } from "../../core/backend-types.js";
8
9
  import { AiBlockInput, AiTaskId } from "./protocol/tasks.js";
9
10
  import "./protocol/index.js";
10
- import { $Fetch } from "ofetch";
11
11
  //#region src/features/ai/assistant.d.ts
12
12
  export type AiStatus = 'idle' | 'streaming' | 'done' | 'error';
13
13
  export interface AiRunBody {
@@ -27,9 +27,10 @@ export interface AiAssistantState {
27
27
  output: string;
28
28
  error: string | null;
29
29
  }
30
- /** What `createAiAssistant` needs from the app: the fetcher, the model of a slot, the notifier and translator for the 429 notice and failures. */
30
+ /** What `createAiAssistant` needs from the app: the AI port, the model of a slot, the notifier and translator for the 429 notice and failures. */
31
31
  export interface AiAssistantOptions {
32
- fetch: $Fetch;
32
+ /** `EditorContext.backend.ai`; without it every run ends in the error state (501), like a server without AI. */
33
+ ai: EditorBackend['ai'];
33
34
  modelRef: (slot: ModelSlot) => ModelRef;
34
35
  /** Receives the 429 notice. */
35
36
  notify: Notifier;
@@ -47,7 +48,7 @@ export interface AiAssistant {
47
48
  /** Clears the error message; the status stays. */
48
49
  clearError: () => void;
49
50
  }
50
- /** Streams `/api/ai/generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */
51
- export declare function createAiAssistant({ fetch, modelRef, notify, t }: AiAssistantOptions): AiAssistant;
51
+ /** Streams the backend's `generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */
52
+ export declare function createAiAssistant({ ai, modelRef, notify, t }: AiAssistantOptions): AiAssistant;
52
53
  //#endregion
53
54
  //# sourceMappingURL=assistant.d.ts.map
@@ -8,8 +8,8 @@ const IDLE = {
8
8
  output: "",
9
9
  error: null
10
10
  };
11
- /** Streams `/api/ai/generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */
12
- function createAiAssistant({ fetch, modelRef, notify, t }) {
11
+ /** Streams the backend's `generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */
12
+ function createAiAssistant({ ai, modelRef, notify, t }) {
13
13
  const store = createStore(IDLE);
14
14
  let controller = null;
15
15
  function abort() {
@@ -36,7 +36,7 @@ function createAiAssistant({ fetch, modelRef, notify, t }) {
36
36
  output: "",
37
37
  error: null
38
38
  });
39
- const outcome = await postAiSse(fetch, "/api/ai/generate", {
39
+ const outcome = await postAiSse(ai, "generate", {
40
40
  ...body,
41
41
  model: modelRef(AI_TASKS[body.task].slot)
42
42
  }, {
@@ -1 +1 @@
1
- {"version":3,"file":"assistant.js","names":[],"sources":["../../../src/features/ai/assistant.ts"],"sourcesContent":["import type { $Fetch } from 'ofetch'\nimport type { AiBlock } from '../../core/feature'\nimport type { Notifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { AiLanguage, ModelSlot } from '../../settings'\nimport { postAiSse } from './ai-sse'\nimport { AI_TASKS, type AiBlockInput, type AiTaskId, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\nexport type AiStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface AiRunBody {\n task: AiTaskId\n prompt: string\n selection: string\n context: string\n language?: AiLanguage\n /** The editor's host blocks (`aiContextOf(editor).blocks`), listed in the task's system prompt. */\n blocks?: readonly AiBlock[]\n /** The block of a `needsBlock` task (`fill-block`): its brief and material. */\n block?: AiBlockInput\n}\n\n/** Snapshot of the running or last request: its status, the streamed text so far and the error message. */\nexport interface AiAssistantState {\n status: AiStatus\n output: string\n error: string | null\n}\n\n/** What `createAiAssistant` needs from the app: the fetcher, the model of a slot, the notifier and translator for the 429 notice and failures. */\nexport interface AiAssistantOptions {\n fetch: $Fetch\n modelRef: (slot: ModelSlot) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`). */\n t: Translate\n}\n\n/** The streaming assistant: observe `state`, `run` a task, `abort`/`reset` it. */\nexport interface AiAssistant {\n state: ReadonlyStore<AiAssistantState>\n run: (body: AiRunBody) => Promise<void>\n /** Stops the running request (its outcome is `idle`). */\n abort: () => void\n /** Aborts and returns to the empty `idle` state. */\n reset: () => void\n /** Clears the error message; the status stays. */\n clearError: () => void\n}\n\nconst IDLE: AiAssistantState = { status: 'idle', output: '', error: null }\n\n/** Streams `/api/ai/generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */\nexport function createAiAssistant({ fetch, modelRef, notify, t }: AiAssistantOptions): AiAssistant {\n const store = createStore<AiAssistantState>(IDLE)\n let controller: AbortController | null = null\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(IDLE)\n }\n\n function clearError() {\n const state = store.get()\n if (state.error !== null) store.set({ ...state, error: null })\n }\n\n async function run(body: AiRunBody): Promise<void> {\n reset()\n const own = new AbortController()\n controller = own\n store.set({ status: 'streaming', output: '', error: null })\n const outcome = await postAiSse(fetch, '/api/ai/generate', { ...body, model: modelRef(AI_TASKS[body.task].slot) }, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) store.set({ ...store.get(), output: store.get().output + text })\n },\n })\n if (controller !== own) return // superseded by reset()/another run\n controller = null\n if (outcome.status === 'done') {\n store.set({ ...store.get(), status: 'done' })\n }\n else if (outcome.status === 'error') {\n store.set({ ...store.get(), status: 'error', error: outcome.message })\n }\n else {\n // Notice instead of the error state on 429, so the caller stays ready for another try.\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n store.set({ ...store.get(), status: 'idle' })\n }\n }\n\n return { state: store, run, abort, reset, clearError }\n}\n"],"mappings":";;;;;AAqDA,MAAM,OAAyB;CAAE,QAAQ;CAAQ,QAAQ;CAAI,OAAO;AAAK;;AAGzE,SAAgB,kBAAkB,EAAE,OAAO,UAAU,QAAQ,KAAsC;CACjG,MAAM,QAAQ,YAA8B,IAAI;CAChD,IAAI,aAAqC;CAEzC,SAAS,QAAQ;EACf,YAAY,MAAM;CACpB;CAEA,SAAS,QAAQ;EACf,MAAM;EACN,aAAa;EACb,MAAM,IAAI,IAAI;CAChB;CAEA,SAAS,aAAa;EACpB,MAAM,QAAQ,MAAM,IAAI;EACxB,IAAI,MAAM,UAAU,MAAM,MAAM,IAAI;GAAE,GAAG;GAAO,OAAO;EAAK,CAAC;CAC/D;CAEA,eAAe,IAAI,MAAgC;EACjD,MAAM;EACN,MAAM,MAAM,IAAI,gBAAgB;EAChC,aAAa;EACb,MAAM,IAAI;GAAE,QAAQ;GAAa,QAAQ;GAAI,OAAO;EAAK,CAAC;EAC1D,MAAM,UAAU,MAAM,UAAU,OAAO,oBAAoB;GAAE,GAAG;GAAM,OAAO,SAAS,SAAS,KAAK,KAAK,CAAC,IAAI;EAAE,GAAG;GACjH,QAAQ,IAAI;GACZ;GACA,UAAU,SAAS;IACjB,IAAI,eAAe,KAAK,MAAM,IAAI;KAAE,GAAG,MAAM,IAAI;KAAG,QAAQ,MAAM,IAAI,CAAC,CAAC,SAAS;IAAK,CAAC;GACzF;EACF,CAAC;EACD,IAAI,eAAe,KAAK;EACxB,aAAa;EACb,IAAI,QAAQ,WAAW,QACrB,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;EAAO,CAAC;OAEzC,IAAI,QAAQ,WAAW,SAC1B,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;GAAS,OAAO,QAAQ;EAAQ,CAAC;OAElE;GAEH,IAAI,QAAQ,WAAW,gBAAgB,oBAAoB,QAAQ,CAAC;GACpE,MAAM,IAAI;IAAE,GAAG,MAAM,IAAI;IAAG,QAAQ;GAAO,CAAC;EAC9C;CACF;CAEA,OAAO;EAAE,OAAO;EAAO;EAAK;EAAO;EAAO;CAAW;AACvD"}
1
+ {"version":3,"file":"assistant.js","names":[],"sources":["../../../src/features/ai/assistant.ts"],"sourcesContent":["import type { EditorBackend } from '../../core/backend'\nimport type { AiBlock } from '../../core/feature'\nimport type { Notifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { AiLanguage, ModelSlot } from '../../settings'\nimport { postAiSse } from './ai-sse'\nimport { AI_TASKS, type AiBlockInput, type AiTaskId, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\nexport type AiStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface AiRunBody {\n task: AiTaskId\n prompt: string\n selection: string\n context: string\n language?: AiLanguage\n /** The editor's host blocks (`aiContextOf(editor).blocks`), listed in the task's system prompt. */\n blocks?: readonly AiBlock[]\n /** The block of a `needsBlock` task (`fill-block`): its brief and material. */\n block?: AiBlockInput\n}\n\n/** Snapshot of the running or last request: its status, the streamed text so far and the error message. */\nexport interface AiAssistantState {\n status: AiStatus\n output: string\n error: string | null\n}\n\n/** What `createAiAssistant` needs from the app: the AI port, the model of a slot, the notifier and translator for the 429 notice and failures. */\nexport interface AiAssistantOptions {\n /** `EditorContext.backend.ai`; without it every run ends in the error state (501), like a server without AI. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelSlot) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`). */\n t: Translate\n}\n\n/** The streaming assistant: observe `state`, `run` a task, `abort`/`reset` it. */\nexport interface AiAssistant {\n state: ReadonlyStore<AiAssistantState>\n run: (body: AiRunBody) => Promise<void>\n /** Stops the running request (its outcome is `idle`). */\n abort: () => void\n /** Aborts and returns to the empty `idle` state. */\n reset: () => void\n /** Clears the error message; the status stays. */\n clearError: () => void\n}\n\nconst IDLE: AiAssistantState = { status: 'idle', output: '', error: null }\n\n/** Streams the backend's `generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */\nexport function createAiAssistant({ ai, modelRef, notify, t }: AiAssistantOptions): AiAssistant {\n const store = createStore<AiAssistantState>(IDLE)\n let controller: AbortController | null = null\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(IDLE)\n }\n\n function clearError() {\n const state = store.get()\n if (state.error !== null) store.set({ ...state, error: null })\n }\n\n async function run(body: AiRunBody): Promise<void> {\n reset()\n const own = new AbortController()\n controller = own\n store.set({ status: 'streaming', output: '', error: null })\n const outcome = await postAiSse(ai, 'generate', { ...body, model: modelRef(AI_TASKS[body.task].slot) }, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) store.set({ ...store.get(), output: store.get().output + text })\n },\n })\n if (controller !== own) return // superseded by reset()/another run\n controller = null\n if (outcome.status === 'done') {\n store.set({ ...store.get(), status: 'done' })\n }\n else if (outcome.status === 'error') {\n store.set({ ...store.get(), status: 'error', error: outcome.message })\n }\n else {\n // Notice instead of the error state on 429, so the caller stays ready for another try.\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n store.set({ ...store.get(), status: 'idle' })\n }\n }\n\n return { state: store, run, abort, reset, clearError }\n}\n"],"mappings":";;;;;AAsDA,MAAM,OAAyB;CAAE,QAAQ;CAAQ,QAAQ;CAAI,OAAO;AAAK;;AAGzE,SAAgB,kBAAkB,EAAE,IAAI,UAAU,QAAQ,KAAsC;CAC9F,MAAM,QAAQ,YAA8B,IAAI;CAChD,IAAI,aAAqC;CAEzC,SAAS,QAAQ;EACf,YAAY,MAAM;CACpB;CAEA,SAAS,QAAQ;EACf,MAAM;EACN,aAAa;EACb,MAAM,IAAI,IAAI;CAChB;CAEA,SAAS,aAAa;EACpB,MAAM,QAAQ,MAAM,IAAI;EACxB,IAAI,MAAM,UAAU,MAAM,MAAM,IAAI;GAAE,GAAG;GAAO,OAAO;EAAK,CAAC;CAC/D;CAEA,eAAe,IAAI,MAAgC;EACjD,MAAM;EACN,MAAM,MAAM,IAAI,gBAAgB;EAChC,aAAa;EACb,MAAM,IAAI;GAAE,QAAQ;GAAa,QAAQ;GAAI,OAAO;EAAK,CAAC;EAC1D,MAAM,UAAU,MAAM,UAAU,IAAI,YAAY;GAAE,GAAG;GAAM,OAAO,SAAS,SAAS,KAAK,KAAK,CAAC,IAAI;EAAE,GAAG;GACtG,QAAQ,IAAI;GACZ;GACA,UAAU,SAAS;IACjB,IAAI,eAAe,KAAK,MAAM,IAAI;KAAE,GAAG,MAAM,IAAI;KAAG,QAAQ,MAAM,IAAI,CAAC,CAAC,SAAS;IAAK,CAAC;GACzF;EACF,CAAC;EACD,IAAI,eAAe,KAAK;EACxB,aAAa;EACb,IAAI,QAAQ,WAAW,QACrB,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;EAAO,CAAC;OAEzC,IAAI,QAAQ,WAAW,SAC1B,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;GAAS,OAAO,QAAQ;EAAQ,CAAC;OAElE;GAEH,IAAI,QAAQ,WAAW,gBAAgB,oBAAoB,QAAQ,CAAC;GACpE,MAAM,IAAI;IAAE,GAAG,MAAM,IAAI;IAAG,QAAQ;GAAO,CAAC;EAC9C;CACF;CAEA,OAAO;EAAE,OAAO;EAAO;EAAK;EAAO;EAAO;CAAW;AACvD"}
@@ -1,4 +1,5 @@
1
- import { BlockAction, BlockTarget, SlashCommandItem } from "../../core/extension-points.js";
1
+ import { CommandDef } from "../../core/command-types.js";
2
+ import { BlockAction, BlockTarget } from "../../core/extension-points.js";
2
3
  import { FeatureContext } from "../../core/feature.js";
3
4
  import "../../core/index.js";
4
5
  import { Editor } from "@tiptap/core";
@@ -15,6 +16,6 @@ export declare function blockTextRange(editor: Editor, { node, pos }: BlockTarge
15
16
  /** The hover action „Mit KI bearbeiten“: selects the block's text and opens the AI menu on it. */
16
17
  export declare const aiBlockAction: ({ t }: FeatureContext) => BlockAction;
17
18
  /** The `/ai` command: opens the AI menu at the caret. */
18
- export declare const aiSlashCommand: ({ t }: FeatureContext) => SlashCommandItem;
19
+ export declare const aiCommand: ({ t }: FeatureContext) => CommandDef<never>;
19
20
  //#endregion
20
21
  //# sourceMappingURL=block-actions.d.ts.map
@@ -42,14 +42,15 @@ const aiBlockAction = ({ t }) => ({
42
42
  }
43
43
  });
44
44
  /** The `/ai` command: opens the AI menu at the caret. */
45
- const aiSlashCommand = ({ t }) => ({
45
+ const aiCommand = ({ t }) => ({
46
46
  id: "ai",
47
+ slash: {},
47
48
  label: t("ai.slash.label"),
48
49
  icon: "i-lucide-sparkles",
49
50
  ability: ABILITY.aiGenerate,
50
51
  run: openAiMenuAtCaret
51
52
  });
52
53
  //#endregion
53
- export { aiBlockAction, aiSlashCommand, blockTextRange };
54
+ export { aiBlockAction, aiCommand, blockTextRange };
54
55
 
55
56
  //# sourceMappingURL=block-actions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"block-actions.js","names":[],"sources":["../../../src/features/ai/block-actions.ts"],"sourcesContent":["import { type Editor } from '@tiptap/core'\nimport { Selection } from '@tiptap/pm/state'\nimport type { BlockAction, BlockTarget, FeatureContext, SlashCommandItem } from '../../core'\nimport { ABILITY, isChromeNode } from '../../core/schema'\nimport { openAiMenuAtCaret, openAiMenuForSelection } from './ai-menu'\n\n/**\n * The text of a block as a selection range: a textblock's content; otherwise from the first to the last text position\n * inside it (a list, quote or table spans its paragraphs; a block with chrome - a composite's title, description and\n * tags - only its body). Null when the block holds no text position.\n */\nexport function blockTextRange(editor: Editor, { node, pos }: BlockTarget): { from: number, to: number } | null {\n if (node.isTextblock) return { from: pos + 1, to: pos + node.nodeSize - 1 }\n const { doc } = editor.state\n let start = pos + 1\n for (let i = 0; i < node.childCount && isChromeNode(node.child(i)); i++) start += node.child(i).nodeSize\n const end = pos + node.nodeSize - 1\n const first = Selection.findFrom(doc.resolve(start), 1, true)\n const last = Selection.findFrom(doc.resolve(end), -1, true)\n if (!first || !last || first.from < start || last.to > end || first.from >= last.to) return null\n return { from: first.from, to: last.to }\n}\n\n/** The hover action „Mit KI bearbeiten“: selects the block's text and opens the AI menu on it. */\nexport const aiBlockAction = ({ t }: FeatureContext): BlockAction => ({\n id: 'ai',\n label: t('ai.blockAction.label'),\n icon: 'i-lucide-sparkles',\n ability: ABILITY.aiRewrite,\n order: 10,\n isVisible: ctx => ctx.node.textContent.trim().length > 0 && blockTextRange(ctx.editor, ctx) !== null,\n run: (ctx) => {\n const range = blockTextRange(ctx.editor, ctx)\n if (!range) return\n ctx.editor.chain().focus().setTextSelection(range).run()\n openAiMenuForSelection(ctx.editor)\n },\n})\n\n/** The `/ai` command: opens the AI menu at the caret. */\nexport const aiSlashCommand = ({ t }: FeatureContext): SlashCommandItem => ({ id: 'ai', label: t('ai.slash.label'), icon: 'i-lucide-sparkles', ability: ABILITY.aiGenerate, run: openAiMenuAtCaret })\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,eAAe,QAAgB,EAAE,MAAM,OAAyD;CAC9G,IAAI,KAAK,aAAa,OAAO;EAAE,MAAM,MAAM;EAAG,IAAI,MAAM,KAAK,WAAW;CAAE;CAC1E,MAAM,EAAE,QAAQ,OAAO;CACvB,IAAI,QAAQ,MAAM;CAClB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,cAAc,aAAa,KAAK,MAAM,CAAC,CAAC,GAAG,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC;CAChG,MAAM,MAAM,MAAM,KAAK,WAAW;CAClC,MAAM,QAAQ,UAAU,SAAS,IAAI,QAAQ,KAAK,GAAG,GAAG,IAAI;CAC5D,MAAM,OAAO,UAAU,SAAS,IAAI,QAAQ,GAAG,GAAG,IAAI,IAAI;CAC1D,IAAI,CAAC,SAAS,CAAC,QAAQ,MAAM,OAAO,SAAS,KAAK,KAAK,OAAO,MAAM,QAAQ,KAAK,IAAI,OAAO;CAC5F,OAAO;EAAE,MAAM,MAAM;EAAM,IAAI,KAAK;CAAG;AACzC;;AAGA,MAAa,iBAAiB,EAAE,SAAsC;CACpE,IAAI;CACJ,OAAO,EAAE,sBAAsB;CAC/B,MAAM;CACN,SAAS,QAAQ;CACjB,OAAO;CACP,YAAW,QAAO,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,SAAS,KAAK,eAAe,IAAI,QAAQ,GAAG,MAAM;CAChG,MAAM,QAAQ;EACZ,MAAM,QAAQ,eAAe,IAAI,QAAQ,GAAG;EAC5C,IAAI,CAAC,OAAO;EACZ,IAAI,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,iBAAiB,KAAK,CAAC,CAAC,IAAI;EACvD,uBAAuB,IAAI,MAAM;CACnC;AACF;;AAGA,MAAa,kBAAkB,EAAE,SAA2C;CAAE,IAAI;CAAM,OAAO,EAAE,gBAAgB;CAAG,MAAM;CAAqB,SAAS,QAAQ;CAAY,KAAK;AAAkB"}
1
+ {"version":3,"file":"block-actions.js","names":[],"sources":["../../../src/features/ai/block-actions.ts"],"sourcesContent":["import { type Editor } from '@tiptap/core'\nimport { Selection } from '@tiptap/pm/state'\nimport type { BlockAction, BlockTarget, CommandDef, FeatureContext } from '../../core'\nimport { ABILITY, isChromeNode } from '../../core/schema'\nimport { openAiMenuAtCaret, openAiMenuForSelection } from './ai-menu'\n\n/**\n * The text of a block as a selection range: a textblock's content; otherwise from the first to the last text position\n * inside it (a list, quote or table spans its paragraphs; a block with chrome - a composite's title, description and\n * tags - only its body). Null when the block holds no text position.\n */\nexport function blockTextRange(editor: Editor, { node, pos }: BlockTarget): { from: number, to: number } | null {\n if (node.isTextblock) return { from: pos + 1, to: pos + node.nodeSize - 1 }\n const { doc } = editor.state\n let start = pos + 1\n for (let i = 0; i < node.childCount && isChromeNode(node.child(i)); i++) start += node.child(i).nodeSize\n const end = pos + node.nodeSize - 1\n const first = Selection.findFrom(doc.resolve(start), 1, true)\n const last = Selection.findFrom(doc.resolve(end), -1, true)\n if (!first || !last || first.from < start || last.to > end || first.from >= last.to) return null\n return { from: first.from, to: last.to }\n}\n\n/** The hover action „Mit KI bearbeiten“: selects the block's text and opens the AI menu on it. */\nexport const aiBlockAction = ({ t }: FeatureContext): BlockAction => ({\n id: 'ai',\n label: t('ai.blockAction.label'),\n icon: 'i-lucide-sparkles',\n ability: ABILITY.aiRewrite,\n order: 10,\n isVisible: ctx => ctx.node.textContent.trim().length > 0 && blockTextRange(ctx.editor, ctx) !== null,\n run: (ctx) => {\n const range = blockTextRange(ctx.editor, ctx)\n if (!range) return\n ctx.editor.chain().focus().setTextSelection(range).run()\n openAiMenuForSelection(ctx.editor)\n },\n})\n\n/** The `/ai` command: opens the AI menu at the caret. */\nexport const aiCommand = ({ t }: FeatureContext): CommandDef<never> => ({ id: 'ai', slash: {}, label: t('ai.slash.label'), icon: 'i-lucide-sparkles', ability: ABILITY.aiGenerate, run: openAiMenuAtCaret })\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,eAAe,QAAgB,EAAE,MAAM,OAAyD;CAC9G,IAAI,KAAK,aAAa,OAAO;EAAE,MAAM,MAAM;EAAG,IAAI,MAAM,KAAK,WAAW;CAAE;CAC1E,MAAM,EAAE,QAAQ,OAAO;CACvB,IAAI,QAAQ,MAAM;CAClB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,cAAc,aAAa,KAAK,MAAM,CAAC,CAAC,GAAG,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC;CAChG,MAAM,MAAM,MAAM,KAAK,WAAW;CAClC,MAAM,QAAQ,UAAU,SAAS,IAAI,QAAQ,KAAK,GAAG,GAAG,IAAI;CAC5D,MAAM,OAAO,UAAU,SAAS,IAAI,QAAQ,GAAG,GAAG,IAAI,IAAI;CAC1D,IAAI,CAAC,SAAS,CAAC,QAAQ,MAAM,OAAO,SAAS,KAAK,KAAK,OAAO,MAAM,QAAQ,KAAK,IAAI,OAAO;CAC5F,OAAO;EAAE,MAAM,MAAM;EAAM,IAAI,KAAK;CAAG;AACzC;;AAGA,MAAa,iBAAiB,EAAE,SAAsC;CACpE,IAAI;CACJ,OAAO,EAAE,sBAAsB;CAC/B,MAAM;CACN,SAAS,QAAQ;CACjB,OAAO;CACP,YAAW,QAAO,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,SAAS,KAAK,eAAe,IAAI,QAAQ,GAAG,MAAM;CAChG,MAAM,QAAQ;EACZ,MAAM,QAAQ,eAAe,IAAI,QAAQ,GAAG;EAC5C,IAAI,CAAC,OAAO;EACZ,IAAI,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,iBAAiB,KAAK,CAAC,CAAC,IAAI;EACvD,uBAAuB,IAAI,MAAM;CACnC;AACF;;AAGA,MAAa,aAAa,EAAE,SAA4C;CAAE,IAAI;CAAM,OAAO,CAAC;CAAG,OAAO,EAAE,gBAAgB;CAAG,MAAM;CAAqB,SAAS,QAAQ;CAAY,KAAK;AAAkB"}
@@ -6,8 +6,8 @@ import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
6
6
  import { useEditorSettings } from "../../../vue/settings.js";
7
7
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
8
8
  import { aiSelectionHighlightKey } from "../ai-selection-highlight.js";
9
- import { AI_SELECTION_MENU_KEY, createAiSelectionFlow } from "../selection-flow.js";
10
9
  import { previewExtensions } from "../preview-extensions.js";
10
+ import { AI_SELECTION_MENU_KEY, createAiSelectionFlow } from "../selection-flow.js";
11
11
  import { useNotifier } from "../../../vue/use-notifier.js";
12
12
  import { useStoreRef } from "../../../vue/store-ref.js";
13
13
  import { useAiAssistant } from "../use-ai-assistant.js";
@@ -1,7 +1,7 @@
1
1
  import { citationBlockId } from "../protocol/chat.js";
2
2
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
3
- import { documentChatAnswerContent } from "../document-chat.js";
4
3
  import { previewExtensions } from "../preview-extensions.js";
4
+ import { documentChatAnswerContent } from "../document-chat.js";
5
5
  import { useNotifier } from "../../../vue/use-notifier.js";
6
6
  import { createBlock, createCommentVNode, createElementBlock, defineComponent, onBeforeUnmount, openBlock, unref, watch } from "vue";
7
7
  import { EditorContent, useEditor } from "@tiptap/vue-3";
@@ -1,7 +1,7 @@
1
1
  import { ConversionProvider } from "../../../core/feature.js";
2
- import "../../../core/index.js";
3
2
  import { AgentOp, AgentOpProblem } from "../protocol/agent-ops.js";
4
3
  import "../protocol/index.js";
4
+ import "../../../core/index.js";
5
5
  import { AgentOpState } from "../agent-op-review.js";
6
6
  import "../use-document-chat.js";
7
7
  //#region src/features/ai/components/DocumentChatOps.vue.d.ts
@@ -1,6 +1,7 @@
1
1
  import { fetchErrorMessage } from "../../../core/fetch-error.js";
2
2
  import { modelRefSchema } from "../protocol/models.js";
3
3
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
4
+ import { useEditorContext } from "../../../vue/context.js";
4
5
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
5
6
  import { isRateLimited } from "../rate-limit.js";
6
7
  import { useModelProfiles } from "../use-model-profiles.js";
@@ -39,6 +40,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
39
40
  __name: "SettingsModelProfiles",
40
41
  setup(__props) {
41
42
  const { Alert: UAlert, Badge: UBadge, Button: UButton, FormField: UFormField, Icon: UIcon, Input: UInput } = useUiKit();
43
+ const app = useEditorContext();
42
44
  const { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles();
43
45
  const { t } = useEditorI18n();
44
46
  /** The profile being added or edited; null while the form is closed. */
@@ -77,20 +79,18 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
77
79
  removeUserProfile(id);
78
80
  if (draft.value?.id === id) draft.value = null;
79
81
  }
80
- /** One short completion through the profile's endpoint (`POST /api/ai/complete`). */
82
+ /** One short completion through the profile's endpoint (`ai.complete`). */
81
83
  async function runTest() {
82
84
  const model = draftRef.value;
83
85
  if (!model || !draftValid.value) return;
84
86
  const current = { state: "running" };
85
87
  test.value = current;
86
88
  try {
87
- await $fetch("/api/ai/complete", {
88
- method: "POST",
89
- body: {
90
- before: "Hallo",
91
- model
92
- }
93
- });
89
+ if (!app.backend.ai) throw new Error(t("ai.sse.httpError", { status: 501 }));
90
+ await app.backend.ai.complete({
91
+ before: "Hallo",
92
+ model
93
+ }, new AbortController().signal);
94
94
  if (test.value === current) test.value = { state: "ok" };
95
95
  } catch (error) {
96
96
  if (test.value === current) test.value = {
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsModelProfiles.js","names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_Fragment","_renderList","_createVNode","_createBlock","_createCommentVNode","_withModifiers","_withCtx"],"sources":["../../../../src/features/ai/components/SettingsModelProfiles.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { modelRefSchema, type UserModelProfile } from '../protocol'\nimport { isRateLimited } from '../rate-limit'\nimport { useModelProfiles } from '../use-model-profiles'\n\n/**\n * Section of the „KI“ tab: the server's presets and this browser's own profiles. Profiles are assigned to slots in the\n * section above.\n */\nconst { Alert: UAlert, Badge: UBadge, Button: UButton, FormField: UFormField, Icon: UIcon, Input: UInput } = useUiKit()\nconst { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles()\nconst { t } = useEditorI18n()\n\n/** The profile being added or edited; null while the form is closed. */\nconst draft = ref<UserModelProfile | null>(null)\n/** Result of „Testen“; replaced (not mutated) per run, so a stale run recognizes it was superseded. */\nconst test = shallowRef<{ state: 'running' } | { state: 'ok' } | { state: 'error', message: string } | null>(null)\n\nconst draftRef = computed(() => draft.value && { endpoint: draft.value.endpoint.trim(), model: draft.value.model.trim(), apiKey: draft.value.apiKey.trim() })\nconst draftValid = computed(() => !!draft.value?.label.trim() && modelRefSchema.safeParse(draftRef.value).success)\n// A test result belongs to the values it ran with.\nwatch(draftRef, () => { test.value = null })\n\nfunction edit(profile?: UserModelProfile) {\n draft.value = profile ? { ...profile } : { id: crypto.randomUUID(), label: '', endpoint: '', model: '', apiKey: '' }\n test.value = null\n}\n\nfunction save() {\n if (!draft.value || !draftRef.value || !draftValid.value) return\n saveUserProfile({ id: draft.value.id, label: draft.value.label.trim(), ...draftRef.value })\n draft.value = null\n}\n\nfunction remove(id: string) {\n removeUserProfile(id)\n if (draft.value?.id === id) draft.value = null\n}\n\n/** One short completion through the profile's endpoint (`POST /api/ai/complete`). */\nasync function runTest() {\n const model = draftRef.value\n if (!model || !draftValid.value) return\n const current = { state: 'running' } as const\n test.value = current\n try {\n await $fetch('/api/ai/complete', { method: 'POST', body: { before: 'Hallo', model } })\n if (test.value === current) test.value = { state: 'ok' }\n }\n catch (error) {\n if (test.value === current) test.value = { state: 'error', message: isRateLimited(error) ? t('ai.rateLimited') : fetchErrorMessage(error) }\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col divide-y divide-[var(--sk-border)]\">\n <div class=\"py-3 flex flex-col gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.server') }}\n </p>\n <p v-if=\"!presets.length\" class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('ai.profiles.noPresets') }}\n </p>\n <ul v-else class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"preset in presets\" :key=\"preset.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-server\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ preset.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ preset.model }} · {{ preset.endpoint }}\n </p>\n </div>\n <UBadge :label=\"t('ai.profiles.serverBadge')\" color=\"neutral\" variant=\"subtle\" size=\"sm\" />\n </li>\n </ul>\n </div>\n\n <div class=\"py-3 flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.own') }}\n </p>\n <UButton v-if=\"!draft\" :label=\"t('ai.profiles.add')\" icon=\"i-lucide-plus\" size=\"sm\" color=\"neutral\" variant=\"outline\" @click=\"edit()\" />\n </div>\n <ul v-if=\"userProfiles.length\" class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"profile in userProfiles\" :key=\"profile.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-key-round\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ profile.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ profile.model }} · {{ profile.endpoint }}\n </p>\n </div>\n <UButton icon=\"i-lucide-pencil\" size=\"sm\" color=\"neutral\" variant=\"ghost\" :aria-label=\"t('ai.profiles.edit')\" @click=\"edit(profile)\" />\n <UButton icon=\"i-lucide-trash-2\" size=\"sm\" color=\"error\" variant=\"ghost\" :aria-label=\"t('ai.profiles.delete')\" @click=\"remove(profile.id)\" />\n </li>\n </ul>\n\n <form v-if=\"draft\" class=\"flex flex-col gap-3 rounded-md border border-[var(--sk-border)] p-3\" @submit.prevent=\"save\">\n <UFormField :label=\"t('ai.profiles.name')\" required>\n <UInput v-model=\"draft.label\" :placeholder=\"t('ai.profiles.namePlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.endpoint')\" :description=\"t('ai.profiles.endpointHelp')\" required>\n <UInput v-model=\"draft.endpoint\" :placeholder=\"t('ai.profiles.endpointPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.model')\" required>\n <UInput v-model=\"draft.model\" :placeholder=\"t('ai.profiles.modelPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.apiKey')\" required>\n <UInput v-model=\"draft.apiKey\" type=\"password\" autocomplete=\"off\" class=\"w-full\" />\n </UFormField>\n <UAlert v-if=\"test?.state === 'ok'\" color=\"success\" variant=\"subtle\" icon=\"i-lucide-check\" :title=\"t('ai.profiles.testOk')\" />\n <UAlert v-else-if=\"test?.state === 'error'\" color=\"error\" variant=\"subtle\" icon=\"i-lucide-circle-alert\" :title=\"test.message\" />\n <div class=\"flex flex-wrap justify-end gap-2\">\n <UButton\n :label=\"t('ai.profiles.test')\"\n icon=\"i-lucide-plug-zap\"\n color=\"neutral\"\n variant=\"outline\"\n :loading=\"test?.state === 'running'\"\n :disabled=\"!draftValid\"\n @click=\"runTest\"\n />\n <UButton :label=\"t('ai.profiles.cancel')\" color=\"neutral\" variant=\"ghost\" @click=\"draft = null\" />\n <UButton type=\"submit\" :label=\"t('ai.profiles.save')\" :disabled=\"!draftValid\" />\n </div>\n </form>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"SettingsModelProfiles.js","names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_Fragment","_renderList","_createVNode","_createBlock","_createCommentVNode","_withModifiers","_withCtx"],"sources":["../../../../src/features/ai/components/SettingsModelProfiles.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { modelRefSchema, type UserModelProfile } from '../protocol'\nimport { isRateLimited } from '../rate-limit'\nimport { useModelProfiles } from '../use-model-profiles'\n\n/**\n * Section of the „KI“ tab: the server's presets and this browser's own profiles. Profiles are assigned to slots in the\n * section above.\n */\nconst { Alert: UAlert, Badge: UBadge, Button: UButton, FormField: UFormField, Icon: UIcon, Input: UInput } = useUiKit()\nconst app = useEditorContext()\nconst { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles()\nconst { t } = useEditorI18n()\n\n/** The profile being added or edited; null while the form is closed. */\nconst draft = ref<UserModelProfile | null>(null)\n/** Result of „Testen“; replaced (not mutated) per run, so a stale run recognizes it was superseded. */\nconst test = shallowRef<{ state: 'running' } | { state: 'ok' } | { state: 'error', message: string } | null>(null)\n\nconst draftRef = computed(() => draft.value && { endpoint: draft.value.endpoint.trim(), model: draft.value.model.trim(), apiKey: draft.value.apiKey.trim() })\nconst draftValid = computed(() => !!draft.value?.label.trim() && modelRefSchema.safeParse(draftRef.value).success)\n// A test result belongs to the values it ran with.\nwatch(draftRef, () => { test.value = null })\n\nfunction edit(profile?: UserModelProfile) {\n draft.value = profile ? { ...profile } : { id: crypto.randomUUID(), label: '', endpoint: '', model: '', apiKey: '' }\n test.value = null\n}\n\nfunction save() {\n if (!draft.value || !draftRef.value || !draftValid.value) return\n saveUserProfile({ id: draft.value.id, label: draft.value.label.trim(), ...draftRef.value })\n draft.value = null\n}\n\nfunction remove(id: string) {\n removeUserProfile(id)\n if (draft.value?.id === id) draft.value = null\n}\n\n/** One short completion through the profile's endpoint (`ai.complete`). */\nasync function runTest() {\n const model = draftRef.value\n if (!model || !draftValid.value) return\n const current = { state: 'running' } as const\n test.value = current\n try {\n if (!app.backend.ai) throw new Error(t('ai.sse.httpError', { status: 501 }))\n await app.backend.ai.complete({ before: 'Hallo', model }, new AbortController().signal)\n if (test.value === current) test.value = { state: 'ok' }\n }\n catch (error) {\n if (test.value === current) test.value = { state: 'error', message: isRateLimited(error) ? t('ai.rateLimited') : fetchErrorMessage(error) }\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col divide-y divide-[var(--sk-border)]\">\n <div class=\"py-3 flex flex-col gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.server') }}\n </p>\n <p v-if=\"!presets.length\" class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('ai.profiles.noPresets') }}\n </p>\n <ul v-else class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"preset in presets\" :key=\"preset.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-server\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ preset.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ preset.model }} · {{ preset.endpoint }}\n </p>\n </div>\n <UBadge :label=\"t('ai.profiles.serverBadge')\" color=\"neutral\" variant=\"subtle\" size=\"sm\" />\n </li>\n </ul>\n </div>\n\n <div class=\"py-3 flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.own') }}\n </p>\n <UButton v-if=\"!draft\" :label=\"t('ai.profiles.add')\" icon=\"i-lucide-plus\" size=\"sm\" color=\"neutral\" variant=\"outline\" @click=\"edit()\" />\n </div>\n <ul v-if=\"userProfiles.length\" class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"profile in userProfiles\" :key=\"profile.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-key-round\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ profile.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ profile.model }} · {{ profile.endpoint }}\n </p>\n </div>\n <UButton icon=\"i-lucide-pencil\" size=\"sm\" color=\"neutral\" variant=\"ghost\" :aria-label=\"t('ai.profiles.edit')\" @click=\"edit(profile)\" />\n <UButton icon=\"i-lucide-trash-2\" size=\"sm\" color=\"error\" variant=\"ghost\" :aria-label=\"t('ai.profiles.delete')\" @click=\"remove(profile.id)\" />\n </li>\n </ul>\n\n <form v-if=\"draft\" class=\"flex flex-col gap-3 rounded-md border border-[var(--sk-border)] p-3\" @submit.prevent=\"save\">\n <UFormField :label=\"t('ai.profiles.name')\" required>\n <UInput v-model=\"draft.label\" :placeholder=\"t('ai.profiles.namePlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.endpoint')\" :description=\"t('ai.profiles.endpointHelp')\" required>\n <UInput v-model=\"draft.endpoint\" :placeholder=\"t('ai.profiles.endpointPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.model')\" required>\n <UInput v-model=\"draft.model\" :placeholder=\"t('ai.profiles.modelPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.apiKey')\" required>\n <UInput v-model=\"draft.apiKey\" type=\"password\" autocomplete=\"off\" class=\"w-full\" />\n </UFormField>\n <UAlert v-if=\"test?.state === 'ok'\" color=\"success\" variant=\"subtle\" icon=\"i-lucide-check\" :title=\"t('ai.profiles.testOk')\" />\n <UAlert v-else-if=\"test?.state === 'error'\" color=\"error\" variant=\"subtle\" icon=\"i-lucide-circle-alert\" :title=\"test.message\" />\n <div class=\"flex flex-wrap justify-end gap-2\">\n <UButton\n :label=\"t('ai.profiles.test')\"\n icon=\"i-lucide-plug-zap\"\n color=\"neutral\"\n variant=\"outline\"\n :loading=\"test?.state === 'running'\"\n :disabled=\"!draftValid\"\n @click=\"runTest\"\n />\n <UButton :label=\"t('ai.profiles.cancel')\" color=\"neutral\" variant=\"ghost\" @click=\"draft = null\" />\n <UButton type=\"submit\" :label=\"t('ai.profiles.save')\" :disabled=\"!draftValid\" />\n </div>\n </form>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,22 +1,22 @@
1
+ import { ReadonlyStore } from "../../core/store.js";
1
2
  import { Translate } from "../../i18n/index.js";
2
3
  import { ModelSlot } from "../../settings/values.js";
3
4
  import "../../settings/index.js";
5
+ import { EditorBackend } from "../../core/backend.js";
4
6
  import { Notifier } from "../../core/notify.js";
5
- import { ReadonlyStore } from "../../core/store.js";
7
+ import { ModelRef } from "../../core/backend-types.js";
6
8
  import { AgentOp, AgentOpProblem } from "./protocol/agent-ops.js";
7
9
  import { DocumentChatMessage } from "./protocol/chat.js";
8
- import { ModelRef } from "./protocol/models.js";
9
10
  import { AiTask } from "./protocol/tasks.js";
10
11
  import "./protocol/index.js";
11
12
  import { AgentScope } from "./agent-ops.js";
12
13
  import { AgentOpReviewState } from "./agent-op-review.js";
13
14
  import { AiTarget } from "./ai-apply.js";
14
15
  import { Editor } from "@tiptap/core";
15
- import { $Fetch } from "ofetch";
16
16
  //#region src/features/ai/document-chat-model.d.ts
17
17
  /**
18
18
  * A block generation run from the AI sidebar's quick actions, shown as an assistant turn. Client only: it never goes
19
- * to `/api/ai/chat`. `markdown` is the streamed result without the task heading (shown with `withTaskHeading`, applied
19
+ * to the `chat` stream. `markdown` is the streamed result without the task heading (shown with `withTaskHeading`, applied
20
20
  * as `taskResultMarkdown`), `target` the apply target captured when it started; only a result that streamed to the end
21
21
  * (`done`) can be applied.
22
22
  */
@@ -49,7 +49,8 @@ export interface DocumentChatState {
49
49
  error: string | null;
50
50
  }
51
51
  export interface DocumentChatOptions {
52
- fetch: $Fetch;
52
+ /** `EditorContext.backend.ai`; without it every question ends in the error state (501), like a server without AI. */
53
+ ai: EditorBackend['ai'];
53
54
  modelRef: (slot: ModelSlot) => ModelRef;
54
55
  /** Receives the 429 notice. */
55
56
  notify: Notifier;
@@ -79,10 +80,10 @@ export interface DocumentChat {
79
80
  clearError: () => void;
80
81
  }
81
82
  /**
82
- * Document chat state: the conversation lives only here (client side) and goes to `/api/ai/chat` in full with every
83
+ * Document chat state: the conversation lives only here (client side) and goes to the backend's `chat` stream in full with every
83
84
  * message, together with the document as it is at that moment and the edit scope. Proposed edit operations open an
84
85
  * in-document review; their decisions go to the model with the next message.
85
86
  */
86
- export declare function createDocumentChat({ fetch, modelRef, notify, t }: DocumentChatOptions): DocumentChat;
87
+ export declare function createDocumentChat({ ai, modelRef, notify, t }: DocumentChatOptions): DocumentChat;
87
88
  //#endregion
88
89
  //# sourceMappingURL=document-chat-model.d.ts.map
@@ -1,13 +1,13 @@
1
- import { CORE_TARGET_IDS, convertTargetId } from "../../core/convert.js";
2
1
  import { aiContextOf, conversionsOf } from "../../core/pipeline.js";
2
+ import { CORE_TARGET_IDS, convertTargetId } from "../../core/convert.js";
3
3
  import { parseConversionTarget } from "../../core/block-convert.js";
4
4
  import { silentNotifier } from "../../core/notify.js";
5
5
  import { createStore } from "../../core/store.js";
6
6
  import { CHAT_LIMITS } from "./protocol/chat.js";
7
- import { NO_AGENT_OPS, createAgentOpReview } from "./agent-op-review.js";
8
7
  import { notifyAiRateLimited } from "./rate-limit.js";
9
8
  import { postAiSse } from "./ai-sse.js";
10
9
  import { documentChatText } from "./document-chat.js";
10
+ import { NO_AGENT_OPS, createAgentOpReview } from "./agent-op-review.js";
11
11
  //#region src/features/ai/document-chat-model.ts
12
12
  const EMPTY = {
13
13
  messages: [],
@@ -16,11 +16,11 @@ const EMPTY = {
16
16
  error: null
17
17
  };
18
18
  /**
19
- * Document chat state: the conversation lives only here (client side) and goes to `/api/ai/chat` in full with every
19
+ * Document chat state: the conversation lives only here (client side) and goes to the backend's `chat` stream in full with every
20
20
  * message, together with the document as it is at that moment and the edit scope. Proposed edit operations open an
21
21
  * in-document review; their decisions go to the model with the next message.
22
22
  */
23
- function createDocumentChat({ fetch, modelRef, notify, t }) {
23
+ function createDocumentChat({ ai, modelRef, notify, t }) {
24
24
  const store = createStore(EMPTY);
25
25
  const review = createAgentOpReview({
26
26
  notify: silentNotifier,
@@ -110,7 +110,7 @@ function createDocumentChat({ fetch, modelRef, notify, t }) {
110
110
  targets,
111
111
  structure
112
112
  };
113
- const outcome = await postAiSse(fetch, "/api/ai/chat", body, {
113
+ const outcome = await postAiSse(ai, "chat", body, {
114
114
  signal: own.signal,
115
115
  t,
116
116
  onDelta: (text) => {