@ecsplico/editor 2.1.2 → 3.1.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 (706) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/README.md +39 -156
  3. package/dist/adapters/comments/drizzle.d.ts +1 -1
  4. package/dist/adapters/comments/record.d.ts +15 -0
  5. package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
  6. package/dist/adapters/comments/rest-model.js.map +1 -0
  7. package/dist/adapters/comments/server.d.ts +2 -1
  8. package/dist/adapters/comments/server.js +2 -1
  9. package/dist/adapters/comments/server.js.map +1 -1
  10. package/dist/adapters/comments/types.d.ts +2 -2
  11. package/dist/adapters/document-state.d.ts +23 -0
  12. package/dist/adapters/document-state.js +66 -0
  13. package/dist/adapters/document-state.js.map +1 -0
  14. package/dist/adapters/documents.js +14 -11
  15. package/dist/adapters/documents.js.map +1 -1
  16. package/dist/adapters/drizzle.d.ts +2 -2
  17. package/dist/adapters/drizzle.js +97 -88
  18. package/dist/adapters/drizzle.js.map +1 -1
  19. package/dist/adapters/forge.d.ts +12 -7
  20. package/dist/adapters/forge.js +38 -45
  21. package/dist/adapters/forge.js.map +1 -1
  22. package/dist/adapters/http.d.ts +20 -0
  23. package/dist/adapters/http.js +68 -0
  24. package/dist/adapters/http.js.map +1 -0
  25. package/dist/adapters/index.d.ts +5 -5
  26. package/dist/adapters/json.d.ts +3 -3
  27. package/dist/adapters/json.js +58 -49
  28. package/dist/adapters/json.js.map +1 -1
  29. package/dist/adapters/memory.d.ts +21 -0
  30. package/dist/adapters/memory.js +53 -0
  31. package/dist/adapters/memory.js.map +1 -0
  32. package/dist/adapters/nitro.d.ts +27 -20
  33. package/dist/adapters/nitro.js +59 -163
  34. package/dist/adapters/nitro.js.map +1 -1
  35. package/dist/adapters/react.d.ts +55 -0
  36. package/dist/adapters/react.js +209 -0
  37. package/dist/adapters/react.js.map +1 -0
  38. package/dist/adapters/shared.d.ts +15 -21
  39. package/dist/adapters/shared.js +10 -8
  40. package/dist/adapters/shared.js.map +1 -1
  41. package/dist/adapters/vue.d.ts +15 -30
  42. package/dist/adapters/vue.js +36 -87
  43. package/dist/adapters/vue.js.map +1 -1
  44. package/dist/code/CodeSurface.vue.d.ts +2 -2
  45. package/dist/config/index.d.ts +105 -0
  46. package/dist/config/index.js +95 -0
  47. package/dist/config/index.js.map +1 -0
  48. package/dist/core/abilities.js +1 -0
  49. package/dist/core/abilities.js.map +1 -1
  50. package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
  51. package/dist/core/api-routes.js.map +1 -0
  52. package/dist/core/backend-capability.d.ts +7 -0
  53. package/dist/core/backend-capability.js +17 -0
  54. package/dist/core/backend-capability.js.map +1 -0
  55. package/dist/core/backend-types.d.ts +245 -0
  56. package/dist/core/backend-types.js +17 -0
  57. package/dist/core/backend-types.js.map +1 -0
  58. package/dist/core/backend.d.ts +108 -0
  59. package/dist/core/backend.js +14 -0
  60. package/dist/core/backend.js.map +1 -0
  61. package/dist/core/block-convert.d.ts +7 -5
  62. package/dist/core/block-convert.js +28 -5
  63. package/dist/core/block-convert.js.map +1 -1
  64. package/dist/core/command-types.d.ts +96 -0
  65. package/dist/core/commands.d.ts +40 -0
  66. package/dist/core/commands.js +151 -0
  67. package/dist/core/commands.js.map +1 -0
  68. package/dist/core/context.d.ts +17 -20
  69. package/dist/core/context.js +24 -21
  70. package/dist/core/context.js.map +1 -1
  71. package/dist/core/core-feature.d.ts +22 -7
  72. package/dist/core/core-feature.js +51 -18
  73. package/dist/core/core-feature.js.map +1 -1
  74. package/dist/core/define-editor.d.ts +4 -3
  75. package/dist/core/define-editor.js +14 -7
  76. package/dist/core/define-editor.js.map +1 -1
  77. package/dist/core/document-markdown.js +12 -3
  78. package/dist/core/document-markdown.js.map +1 -1
  79. package/dist/core/editor-ui.d.ts +8 -1
  80. package/dist/core/editor-ui.js +9 -8
  81. package/dist/core/editor-ui.js.map +1 -1
  82. package/dist/core/extension-points.d.ts +3 -15
  83. package/dist/core/feature-context.d.ts +8 -7
  84. package/dist/core/feature-context.js +12 -11
  85. package/dist/core/feature-context.js.map +1 -1
  86. package/dist/core/feature.d.ts +41 -19
  87. package/dist/core/feature.js.map +1 -1
  88. package/dist/core/fences.d.ts +2 -1
  89. package/dist/core/fences.js +3 -2
  90. package/dist/core/fences.js.map +1 -1
  91. package/dist/core/fetch-error.d.ts +1 -1
  92. package/dist/core/fetch-error.js +3 -1
  93. package/dist/core/fetch-error.js.map +1 -1
  94. package/dist/core/hash.d.ts +5 -0
  95. package/dist/core/hash.js +11 -0
  96. package/dist/core/hash.js.map +1 -0
  97. package/dist/core/http-backend.d.ts +11 -0
  98. package/dist/core/http-backend.js +195 -0
  99. package/dist/core/http-backend.js.map +1 -0
  100. package/dist/core/index.d.ts +15 -9
  101. package/dist/core/index.js +13 -9
  102. package/dist/core/palette.d.ts +48 -0
  103. package/dist/core/palette.js +95 -0
  104. package/dist/core/palette.js.map +1 -0
  105. package/dist/core/schema/abilities.d.ts +2 -1
  106. package/dist/core/schema/abilities.js +5 -1
  107. package/dist/core/schema/abilities.js.map +1 -1
  108. package/dist/core/schema/index.d.ts +11 -5
  109. package/dist/core/schema/index.js +13 -8
  110. package/dist/core/schema/index.js.map +1 -1
  111. package/dist/core/schema/mark-flags.d.ts +8 -1
  112. package/dist/core/schema/of-features.d.ts +13 -0
  113. package/dist/core/schema/of-features.js +14 -0
  114. package/dist/core/schema/of-features.js.map +1 -0
  115. package/dist/core/slash.d.ts +8 -6
  116. package/dist/core/slash.js +24 -16
  117. package/dist/core/slash.js.map +1 -1
  118. package/dist/core/surface-session.js +15 -5
  119. package/dist/core/surface-session.js.map +1 -1
  120. package/dist/core/toolbar-items.d.ts +10 -4
  121. package/dist/core/toolbar-items.js +269 -195
  122. package/dist/core/toolbar-items.js.map +1 -1
  123. package/dist/core/transaction-guards.d.ts +21 -0
  124. package/dist/core/transaction-guards.js +49 -0
  125. package/dist/core/transaction-guards.js.map +1 -0
  126. package/dist/core/util.js +11 -0
  127. package/dist/core/util.js.map +1 -0
  128. package/dist/document/DocumentSurface.d.ts +1 -1
  129. package/dist/document/DocumentSurface.vue.d.ts +2 -2
  130. package/dist/features/ai/agent-op-review.d.ts +1 -1
  131. package/dist/features/ai/agent-op-review.js +1 -1
  132. package/dist/features/ai/agent-ops.js +1 -1
  133. package/dist/features/ai/ai-apply.d.ts +2 -2
  134. package/dist/features/ai/ai-apply.js +1 -1
  135. package/dist/features/ai/ai-review.js +1 -1
  136. package/dist/features/ai/ai-sse.d.ts +6 -6
  137. package/dist/features/ai/ai-sse.js +20 -63
  138. package/dist/features/ai/ai-sse.js.map +1 -1
  139. package/dist/features/ai/assistant.d.ts +8 -7
  140. package/dist/features/ai/assistant.js +3 -3
  141. package/dist/features/ai/assistant.js.map +1 -1
  142. package/dist/features/ai/block-actions.d.ts +3 -2
  143. package/dist/features/ai/block-actions.js +3 -2
  144. package/dist/features/ai/block-actions.js.map +1 -1
  145. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  146. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  147. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
  148. package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
  149. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  150. package/dist/features/ai/document-chat-model.d.ts +8 -7
  151. package/dist/features/ai/document-chat-model.js +5 -5
  152. package/dist/features/ai/document-chat-model.js.map +1 -1
  153. package/dist/features/ai/document-chat.js +1 -1
  154. package/dist/features/ai/feature.js +27 -14
  155. package/dist/features/ai/feature.js.map +1 -1
  156. package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
  157. package/dist/features/ai/ghost-autocomplete.js +6 -9
  158. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  159. package/dist/features/ai/index.d.ts +4 -3
  160. package/dist/features/ai/index.js +10 -10
  161. package/dist/features/ai/model-profiles.d.ts +5 -4
  162. package/dist/features/ai/model-profiles.js +2 -2
  163. package/dist/features/ai/model-profiles.js.map +1 -1
  164. package/dist/features/ai/protocol/block-lines.js +1 -1
  165. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  166. package/dist/features/ai/protocol/chat.js +6 -2
  167. package/dist/features/ai/protocol/chat.js.map +1 -1
  168. package/dist/features/ai/protocol/index.d.ts +3 -2
  169. package/dist/features/ai/protocol/models.d.ts +2 -16
  170. package/dist/features/ai/protocol/models.js.map +1 -1
  171. package/dist/features/ai/rate-limit.d.ts +1 -1
  172. package/dist/features/ai/rate-limit.js +3 -1
  173. package/dist/features/ai/rate-limit.js.map +1 -1
  174. package/dist/features/ai/react/AiSelectionMenu.js +2 -2
  175. package/dist/features/ai/react/AiSidebar.js +4 -4
  176. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  177. package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
  178. package/dist/features/ai/react/DocumentChatOps.js +1 -1
  179. package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
  180. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  181. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  182. package/dist/features/ai/react/use-ai-assistant.js +3 -3
  183. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  184. package/dist/features/ai/react/use-document-chat.js +2 -2
  185. package/dist/features/ai/react/use-document-chat.js.map +1 -1
  186. package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
  187. package/dist/features/ai/selection-flow.d.ts +1 -1
  188. package/dist/features/ai/selection-flow.js +1 -1
  189. package/dist/features/ai/sidebar-flow.d.ts +1 -1
  190. package/dist/features/ai/sidebar-flow.js +1 -1
  191. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  192. package/dist/features/ai/use-ai-assistant.js +3 -3
  193. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  194. package/dist/features/ai/use-document-chat.js +2 -2
  195. package/dist/features/ai/use-document-chat.js.map +1 -1
  196. package/dist/features/ai/use-model-profiles.d.ts +2 -1
  197. package/dist/features/authors/attribution.d.ts +1 -1
  198. package/dist/features/authors/feature.js +12 -11
  199. package/dist/features/authors/feature.js.map +1 -1
  200. package/dist/features/block-ui/block-actions.d.ts +1 -1
  201. package/dist/features/block-ui/block-actions.js +1 -1
  202. package/dist/features/block-ui/block-actions.js.map +1 -1
  203. package/dist/features/block-ui/components/BlockUi.js +1 -1
  204. package/dist/features/block-ui/feature.js +1 -0
  205. package/dist/features/block-ui/feature.js.map +1 -1
  206. package/dist/features/block-ui/react/BlockUi.js +1 -1
  207. package/dist/features/collab/anchor-resolver.js +1 -1
  208. package/dist/features/collab/feature.js +2 -0
  209. package/dist/features/collab/feature.js.map +1 -1
  210. package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
  211. package/dist/features/comments/feature.js +12 -11
  212. package/dist/features/comments/feature.js.map +1 -1
  213. package/dist/features/comments/index.d.ts +3 -2
  214. package/dist/features/comments/index.js +1 -1
  215. package/dist/features/comments/margin-layout.d.ts +3 -2
  216. package/dist/features/comments/model.d.ts +3 -67
  217. package/dist/features/comments/model.js +2 -2
  218. package/dist/features/comments/model.js.map +1 -1
  219. package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
  220. package/dist/features/comments/react/use-comments.d.ts +2 -1
  221. package/dist/features/comments/react.d.ts +1 -1
  222. package/dist/features/comments/schema.d.ts +2 -52
  223. package/dist/features/comments/schema.js.map +1 -1
  224. package/dist/features/comments/snapshot-equality.js.map +1 -1
  225. package/dist/features/comments/use-comments.d.ts +3 -2
  226. package/dist/features/comments/vue.d.ts +1 -1
  227. package/dist/features/composites/accepted-children.d.ts +1 -0
  228. package/dist/features/composites/accepted-children.js +16 -1
  229. package/dist/features/composites/accepted-children.js.map +1 -1
  230. package/dist/features/composites/ai-brief.js +39 -0
  231. package/dist/features/composites/ai-brief.js.map +1 -1
  232. package/dist/features/composites/bibliography-command.d.ts +11 -0
  233. package/dist/features/composites/bibliography-form.js +90 -0
  234. package/dist/features/composites/bibliography-form.js.map +1 -0
  235. package/dist/features/composites/bibliography.d.ts +33 -0
  236. package/dist/features/composites/bibliography.js +120 -0
  237. package/dist/features/composites/bibliography.js.map +1 -0
  238. package/dist/features/composites/components/BibliographyEditor.js +261 -0
  239. package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
  240. package/dist/features/composites/components/CompositeChrome.js +78 -29
  241. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  242. package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
  243. package/dist/features/composites/components/CompositeDetails.js +46 -11
  244. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  245. package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
  246. package/dist/features/composites/components/CompositeFieldEditor.js +64 -5
  247. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  248. package/dist/features/composites/components/CompositeInspector.js +20 -7
  249. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  250. package/dist/features/composites/components/CompositeTable.js +13 -4
  251. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  252. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  253. package/dist/features/composites/components/CompositeView.js +11 -1
  254. package/dist/features/composites/components/CompositeView.js.map +1 -1
  255. package/dist/features/composites/components/DerivationBadge.js +103 -0
  256. package/dist/features/composites/components/DerivationBadge.js.map +1 -0
  257. package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
  258. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
  259. package/dist/features/composites/components/ReferenceView.js +122 -0
  260. package/dist/features/composites/components/ReferenceView.js.map +1 -0
  261. package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
  262. package/dist/features/composites/components/SettingsEditingTab.js +1 -1
  263. package/dist/features/composites/convert.js +2 -2
  264. package/dist/features/composites/convert.js.map +1 -1
  265. package/dist/features/composites/derivation.d.ts +61 -0
  266. package/dist/features/composites/derivation.js +98 -0
  267. package/dist/features/composites/derivation.js.map +1 -0
  268. package/dist/features/composites/derived-chrome.js +69 -0
  269. package/dist/features/composites/derived-chrome.js.map +1 -0
  270. package/dist/features/composites/feature.js +159 -109
  271. package/dist/features/composites/feature.js.map +1 -1
  272. package/dist/features/composites/fences.d.ts +4 -3
  273. package/dist/features/composites/fences.js +51 -7
  274. package/dist/features/composites/fences.js.map +1 -1
  275. package/dist/features/composites/index.d.ts +12 -3
  276. package/dist/features/composites/index.js +15 -6
  277. package/dist/features/composites/inspector-model.d.ts +4 -1
  278. package/dist/features/composites/inspector-model.js +8 -4
  279. package/dist/features/composites/inspector-model.js.map +1 -1
  280. package/dist/features/composites/kinds.d.ts +16 -2
  281. package/dist/features/composites/kinds.js +145 -6
  282. package/dist/features/composites/kinds.js.map +1 -1
  283. package/dist/features/composites/legacy.d.ts +4 -1
  284. package/dist/features/composites/legacy.js +56 -1
  285. package/dist/features/composites/legacy.js.map +1 -1
  286. package/dist/features/composites/meta-display.js +45 -2
  287. package/dist/features/composites/meta-display.js.map +1 -1
  288. package/dist/features/composites/meta-keys.d.ts +7 -0
  289. package/dist/features/composites/meta-keys.js +21 -0
  290. package/dist/features/composites/meta-keys.js.map +1 -0
  291. package/dist/features/composites/node-views.d.ts +6 -3
  292. package/dist/features/composites/node-views.js +7 -3
  293. package/dist/features/composites/node-views.js.map +1 -1
  294. package/dist/features/composites/nodes.d.ts +6 -1
  295. package/dist/features/composites/nodes.js +33 -7
  296. package/dist/features/composites/nodes.js.map +1 -1
  297. package/dist/features/composites/react/BibliographyEditor.js +229 -0
  298. package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
  299. package/dist/features/composites/react/CompositeChrome.js +67 -19
  300. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  301. package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
  302. package/dist/features/composites/react/CompositeDetails.js +45 -6
  303. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  304. package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
  305. package/dist/features/composites/react/CompositeFieldEditor.js +43 -1
  306. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  307. package/dist/features/composites/react/CompositeInspector.js +12 -6
  308. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  309. package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
  310. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  311. package/dist/features/composites/react/CompositeTable.js +16 -2
  312. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  313. package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
  314. package/dist/features/composites/react/CompositeView.js +6 -1
  315. package/dist/features/composites/react/CompositeView.js.map +1 -1
  316. package/dist/features/composites/react/DerivationBadge.js +116 -0
  317. package/dist/features/composites/react/DerivationBadge.js.map +1 -0
  318. package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
  319. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
  320. package/dist/features/composites/react/ReferenceView.d.ts +11 -0
  321. package/dist/features/composites/react/ReferenceView.js +115 -0
  322. package/dist/features/composites/react/ReferenceView.js.map +1 -0
  323. package/dist/features/composites/react/SettingsEditingTab.js +2 -2
  324. package/dist/features/composites/react.d.ts +2 -1
  325. package/dist/features/composites/react.js +7 -3
  326. package/dist/features/composites/react.js.map +1 -1
  327. package/dist/features/composites/reference-ai.js +16 -0
  328. package/dist/features/composites/reference-ai.js.map +1 -0
  329. package/dist/features/composites/reference-label.d.ts +19 -0
  330. package/dist/features/composites/reference-label.js +58 -0
  331. package/dist/features/composites/reference-label.js.map +1 -0
  332. package/dist/features/composites/reference-suggestion.d.ts +10 -0
  333. package/dist/features/composites/reference-suggestion.js +41 -0
  334. package/dist/features/composites/reference-suggestion.js.map +1 -0
  335. package/dist/features/composites/reference-text.d.ts +18 -0
  336. package/dist/features/composites/reference-text.js +89 -0
  337. package/dist/features/composites/reference-text.js.map +1 -0
  338. package/dist/features/composites/reference.d.ts +44 -0
  339. package/dist/features/composites/reference.js +148 -0
  340. package/dist/features/composites/reference.js.map +1 -0
  341. package/dist/features/composites/reveal-source.d.ts +32 -0
  342. package/dist/features/composites/reveal-source.js +89 -0
  343. package/dist/features/composites/reveal-source.js.map +1 -0
  344. package/dist/features/composites/rows.d.ts +25 -3
  345. package/dist/features/composites/rows.js +72 -10
  346. package/dist/features/composites/rows.js.map +1 -1
  347. package/dist/features/composites/schema.d.ts +9 -3
  348. package/dist/features/composites/schema.js +11 -4
  349. package/dist/features/composites/schema.js.map +1 -1
  350. package/dist/features/composites/table-model.js +12 -21
  351. package/dist/features/composites/table-model.js.map +1 -1
  352. package/dist/features/composites/vue.d.ts +3 -2
  353. package/dist/features/composites/vue.js +8 -4
  354. package/dist/features/composites/vue.js.map +1 -1
  355. package/dist/features/media/assets.d.ts +2 -0
  356. package/dist/features/media/assets.js +3 -2
  357. package/dist/features/media/assets.js.map +1 -1
  358. package/dist/features/media/components/FileView.js +13 -4
  359. package/dist/features/media/components/FileView.js.map +1 -1
  360. package/dist/features/media/feature.d.ts +3 -3
  361. package/dist/features/media/feature.js +21 -28
  362. package/dist/features/media/feature.js.map +1 -1
  363. package/dist/features/media/file-import.js +2 -2
  364. package/dist/features/media/file-import.js.map +1 -1
  365. package/dist/features/media/index.d.ts +2 -2
  366. package/dist/features/media/index.js +3 -3
  367. package/dist/features/media/react/AudioRecorderModal.js +1 -1
  368. package/dist/features/media/react/FileView.js +19 -2
  369. package/dist/features/media/react/FileView.js.map +1 -1
  370. package/dist/features/media/react.js +1 -1
  371. package/dist/features/media/schema.d.ts +8 -0
  372. package/dist/features/media/schema.js +126 -11
  373. package/dist/features/media/schema.js.map +1 -1
  374. package/dist/features/media/upload.d.ts +16 -14
  375. package/dist/features/media/upload.js +13 -19
  376. package/dist/features/media/upload.js.map +1 -1
  377. package/dist/features/mentions/index.d.ts +4 -3
  378. package/dist/features/mentions/index.js +4 -1
  379. package/dist/features/mentions/index.js.map +1 -1
  380. package/dist/features/mentions/mention-text.d.ts +1 -1
  381. package/dist/features/mentions/mention-text.js.map +1 -1
  382. package/dist/features/mentions/mention.d.ts +0 -6
  383. package/dist/features/mentions/mention.js.map +1 -1
  384. package/dist/features/mentions/metadata-defaults.d.ts +2 -2
  385. package/dist/features/mentions/metadata-defaults.js +3 -9
  386. package/dist/features/mentions/metadata-defaults.js.map +1 -1
  387. package/dist/features/mentions/schema.d.ts +2 -1
  388. package/dist/features/mentions/schema.js.map +1 -1
  389. package/dist/features/tables/feature.d.ts +2 -1
  390. package/dist/features/tables/feature.js +90 -70
  391. package/dist/features/tables/feature.js.map +1 -1
  392. package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
  393. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  394. package/dist/features/themes/feature.js +2 -0
  395. package/dist/features/themes/feature.js.map +1 -1
  396. package/dist/features/themes/index.d.ts +2 -1
  397. package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
  398. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  399. package/dist/features/themes/schema.d.ts +2 -6
  400. package/dist/features/themes/schema.js.map +1 -1
  401. package/dist/features/themes/theme-runtime.d.ts +1 -1
  402. package/dist/features/themes/theme-runtime.js +4 -2
  403. package/dist/features/themes/theme-runtime.js.map +1 -1
  404. package/dist/features/transcript/TranscriptSegmentView.js +48 -28
  405. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  406. package/dist/features/transcript/feature.js +21 -19
  407. package/dist/features/transcript/feature.js.map +1 -1
  408. package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
  409. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  410. package/dist/features/transcript/segment-node.d.ts +2 -0
  411. package/dist/features/transcript/segment-node.js +35 -7
  412. package/dist/features/transcript/segment-node.js.map +1 -1
  413. package/dist/i18n/de/composites.js +91 -1
  414. package/dist/i18n/de/composites.js.map +1 -1
  415. package/dist/i18n/de/integrations.js +49 -8
  416. package/dist/i18n/de/integrations.js.map +1 -1
  417. package/dist/i18n/de/media.js +0 -1
  418. package/dist/i18n/de/media.js.map +1 -1
  419. package/dist/i18n/de/palette.js +22 -0
  420. package/dist/i18n/de/palette.js.map +1 -0
  421. package/dist/i18n/de/tables.js +0 -1
  422. package/dist/i18n/de/tables.js.map +1 -1
  423. package/dist/i18n/de/transcript.js +2 -1
  424. package/dist/i18n/de/transcript.js.map +1 -1
  425. package/dist/i18n/de/tree.js +3 -0
  426. package/dist/i18n/de/tree.js.map +1 -1
  427. package/dist/i18n/de.d.ts +157 -11
  428. package/dist/i18n/de.js +3 -1
  429. package/dist/i18n/de.js.map +1 -1
  430. package/dist/i18n/en/composites.js +89 -1
  431. package/dist/i18n/en/composites.js.map +1 -1
  432. package/dist/i18n/en/integrations.js +49 -8
  433. package/dist/i18n/en/integrations.js.map +1 -1
  434. package/dist/i18n/en/media.js +0 -1
  435. package/dist/i18n/en/media.js.map +1 -1
  436. package/dist/i18n/en/palette.js +22 -0
  437. package/dist/i18n/en/palette.js.map +1 -0
  438. package/dist/i18n/en/tables.js +0 -1
  439. package/dist/i18n/en/tables.js.map +1 -1
  440. package/dist/i18n/en/transcript.js +2 -1
  441. package/dist/i18n/en/transcript.js.map +1 -1
  442. package/dist/i18n/en/tree.js +3 -0
  443. package/dist/i18n/en/tree.js.map +1 -1
  444. package/dist/i18n/en.js +3 -1
  445. package/dist/i18n/en.js.map +1 -1
  446. package/dist/index.d.ts +4 -3
  447. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
  448. package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
  449. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
  450. package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
  451. package/dist/integrations/composite-ai/composite-fill.js +97 -56
  452. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  453. package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
  454. package/dist/integrations/composite-ai/composite-summary.js +7 -26
  455. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  456. package/dist/integrations/composite-ai/derive.d.ts +29 -0
  457. package/dist/integrations/composite-ai/derive.js +173 -0
  458. package/dist/integrations/composite-ai/derive.js.map +1 -0
  459. package/dist/integrations/composite-ai/index.d.ts +11 -7
  460. package/dist/integrations/composite-ai/index.js +72 -14
  461. package/dist/integrations/composite-ai/index.js.map +1 -1
  462. package/dist/integrations/composite-ai/rendition.d.ts +45 -0
  463. package/dist/integrations/composite-ai/rendition.js +314 -0
  464. package/dist/integrations/composite-ai/rendition.js.map +1 -0
  465. package/dist/integrations/composite-media/index.d.ts +7 -5
  466. package/dist/integrations/composite-media/index.js +86 -13
  467. package/dist/integrations/composite-media/index.js.map +1 -1
  468. package/dist/integrations/transcript-ai/index.d.ts +1 -1
  469. package/dist/integrations/transcript-ai/index.js +5 -2
  470. package/dist/integrations/transcript-ai/index.js.map +1 -1
  471. package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
  472. package/dist/integrations/transcript-ai/protocol.js +0 -2
  473. package/dist/integrations/transcript-ai/protocol.js.map +1 -1
  474. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
  475. package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
  476. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
  477. package/dist/markdown/MarkdownSurface.vue.d.ts +12 -11
  478. package/dist/markdown/MarkdownSurface2.js +3 -6
  479. package/dist/markdown/MarkdownSurface2.js.map +1 -1
  480. package/dist/markdown/document.js +1 -1
  481. package/dist/markdown/source-mode.d.ts +1 -1
  482. package/dist/nuxt/module.d.ts +1402 -54
  483. package/dist/nuxt/module.js +6 -49
  484. package/dist/nuxt/module.js.map +1 -1
  485. package/dist/nuxt/runtime/install-context.d.ts +7 -3
  486. package/dist/nuxt/runtime/install-context.js +14 -8
  487. package/dist/nuxt/runtime/install-context.js.map +1 -1
  488. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
  489. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
  490. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  491. package/dist/presets/compose.d.ts +94 -0
  492. package/dist/presets/compose.js +164 -0
  493. package/dist/presets/compose.js.map +1 -0
  494. package/dist/react/AdvancedEditor.d.ts +15 -15
  495. package/dist/react/AdvancedEditor.js +15 -13
  496. package/dist/react/AdvancedEditor.js.map +1 -1
  497. package/dist/react/EditorCommandPalette.d.ts +13 -0
  498. package/dist/react/EditorCommandPalette.js +118 -0
  499. package/dist/react/EditorCommandPalette.js.map +1 -0
  500. package/dist/react/EditorSettingsModal.d.ts +5 -5
  501. package/dist/react/EditorSettingsModal.js +7 -4
  502. package/dist/react/EditorSettingsModal.js.map +1 -1
  503. package/dist/react/EditorSettingsPanel.d.ts +7 -7
  504. package/dist/react/EditorSettingsPanel.js +8 -5
  505. package/dist/react/EditorSettingsPanel.js.map +1 -1
  506. package/dist/react/EditorSurface.d.ts +7 -7
  507. package/dist/react/EditorSurface.js +10 -6
  508. package/dist/react/EditorSurface.js.map +1 -1
  509. package/dist/react/EditorToolbar.d.ts +4 -6
  510. package/dist/react/EditorToolbar.js +29 -37
  511. package/dist/react/EditorToolbar.js.map +1 -1
  512. package/dist/react/SimpleEditor.d.ts +22 -23
  513. package/dist/react/SimpleEditor.js +21 -7
  514. package/dist/react/SimpleEditor.js.map +1 -1
  515. package/dist/react/advanced.d.ts +4 -3
  516. package/dist/react/advanced.js +2 -2
  517. package/dist/react/compose.d.ts +20 -0
  518. package/dist/react/compose.js +49 -0
  519. package/dist/react/compose.js.map +1 -0
  520. package/dist/react/composed-surface.d.ts +39 -0
  521. package/dist/react/composed-surface.js +71 -0
  522. package/dist/react/composed-surface.js.map +1 -0
  523. package/dist/react/context.js +6 -2
  524. package/dist/react/context.js.map +1 -1
  525. package/dist/react/index.d.ts +2 -1
  526. package/dist/react/index.js +2 -1
  527. package/dist/react/key-labels.js +15 -0
  528. package/dist/react/key-labels.js.map +1 -0
  529. package/dist/react/settings.d.ts +1 -1
  530. package/dist/react/simple.d.ts +2 -2
  531. package/dist/react/ui/command.js +36 -1
  532. package/dist/react/ui/command.js.map +1 -1
  533. package/dist/schema/advanced.d.ts +6 -6
  534. package/dist/schema/advanced.js +26 -29
  535. package/dist/schema/advanced.js.map +1 -1
  536. package/dist/server/ai.d.ts +5 -16
  537. package/dist/server/ai.js.map +1 -1
  538. package/dist/server/auth.d.ts +9 -0
  539. package/dist/server/collab-state.d.ts +1 -1
  540. package/dist/server/collab-state.js.map +1 -1
  541. package/dist/server/collab.d.ts +13 -13
  542. package/dist/server/collab.js +34 -18
  543. package/dist/server/collab.js.map +1 -1
  544. package/dist/server/crossref.js +78 -0
  545. package/dist/server/crossref.js.map +1 -0
  546. package/dist/server/errors.d.ts +1 -1
  547. package/dist/server/errors.js +1 -1
  548. package/dist/server/errors.js.map +1 -1
  549. package/dist/server/extract.js +308 -0
  550. package/dist/server/extract.js.map +1 -0
  551. package/dist/server/h3.d.ts +8 -0
  552. package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
  553. package/dist/server/h3.js.map +1 -0
  554. package/dist/server/handlers/ai.d.ts +32 -10
  555. package/dist/server/handlers/ai.js +172 -37
  556. package/dist/server/handlers/ai.js.map +1 -1
  557. package/dist/server/handlers/context.d.ts +12 -5
  558. package/dist/server/handlers/context.js +10 -4
  559. package/dist/server/handlers/context.js.map +1 -1
  560. package/dist/server/handlers/editor.d.ts +19 -13
  561. package/dist/server/handlers/editor.js +49 -38
  562. package/dist/server/handlers/editor.js.map +1 -1
  563. package/dist/server/handlers/index.d.ts +4 -4
  564. package/dist/server/handlers/index.js +4 -4
  565. package/dist/server/host.d.ts +16 -25
  566. package/dist/server/host.js +58 -41
  567. package/dist/server/host.js.map +1 -1
  568. package/dist/server/index.d.ts +6 -4
  569. package/dist/server/index.js +3 -2
  570. package/dist/server/pdf.js +97 -0
  571. package/dist/server/pdf.js.map +1 -0
  572. package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
  573. package/dist/server/ports.js +29 -0
  574. package/dist/server/ports.js.map +1 -0
  575. package/dist/server/routes.d.ts +22 -0
  576. package/dist/server/routes.js +144 -0
  577. package/dist/server/routes.js.map +1 -0
  578. package/dist/settings/index.d.ts +1 -1
  579. package/dist/settings/index.js.map +1 -1
  580. package/dist/styles/composites.css +25 -0
  581. package/dist/tree/DocumentTree.js +56 -29
  582. package/dist/tree/DocumentTree.js.map +1 -1
  583. package/dist/tree/react/DocumentTree.js +23 -0
  584. package/dist/tree/react/DocumentTree.js.map +1 -1
  585. package/dist/tree/tree-host.js +2 -2
  586. package/dist/tree/tree-items.js +1 -1
  587. package/dist/vue/AdvancedEditor.js +30 -9
  588. package/dist/vue/AdvancedEditor.js.map +1 -1
  589. package/dist/vue/AdvancedEditor.vue.d.ts +32 -12
  590. package/dist/vue/SimpleEditor.js +62 -16
  591. package/dist/vue/SimpleEditor.js.map +1 -1
  592. package/dist/vue/SimpleEditor.vue.d.ts +39 -9
  593. package/dist/vue/advanced.d.ts +3 -2
  594. package/dist/vue/advanced.js +2 -2
  595. package/dist/vue/components/EditorCommandPalette.js +91 -0
  596. package/dist/vue/components/EditorCommandPalette.js.map +1 -0
  597. package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
  598. package/dist/vue/components/EditorSettingsModal.js +10 -6
  599. package/dist/vue/components/EditorSettingsModal.js.map +1 -1
  600. package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
  601. package/dist/vue/components/EditorSettingsPanel.js +10 -6
  602. package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
  603. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
  604. package/dist/vue/components/EditorSurface.js +11 -6
  605. package/dist/vue/components/EditorSurface.js.map +1 -1
  606. package/dist/vue/components/EditorSurface.vue.d.ts +11 -11
  607. package/dist/vue/components/EditorToolbar.js +41 -49
  608. package/dist/vue/components/EditorToolbar.js.map +1 -1
  609. package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
  610. package/dist/vue/compose.d.ts +17 -0
  611. package/dist/vue/compose.js +51 -0
  612. package/dist/vue/compose.js.map +1 -0
  613. package/dist/vue/context-key.d.ts +6 -0
  614. package/dist/vue/context-key.js +6 -0
  615. package/dist/vue/context-key.js.map +1 -0
  616. package/dist/vue/context.d.ts +8 -6
  617. package/dist/vue/context.js +6 -5
  618. package/dist/vue/context.js.map +1 -1
  619. package/dist/vue/index.d.ts +10 -8
  620. package/dist/vue/index.js +10 -8
  621. package/dist/vue/kit/index.d.ts +2 -2
  622. package/dist/vue/kit/nuxt-ui.js +23 -0
  623. package/dist/vue/kit/nuxt-ui.js.map +1 -1
  624. package/dist/vue/kit/types.d.ts +40 -1
  625. package/package.json +30 -16
  626. package/dist/adapters/comments/client.d.ts +0 -64
  627. package/dist/adapters/comments/client.js.map +0 -1
  628. package/dist/adapters/routes.js.map +0 -1
  629. package/dist/core/toolbar.d.ts +0 -74
  630. package/dist/core/toolbar.js +0 -45
  631. package/dist/core/toolbar.js.map +0 -1
  632. package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
  633. package/dist/integrations/composite-ai/document-transcribe.js +0 -89
  634. package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
  635. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
  636. package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
  637. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
  638. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
  639. package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
  640. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
  641. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
  642. package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
  643. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
  644. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
  645. package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
  646. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
  647. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
  648. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
  649. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
  650. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
  651. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
  652. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
  653. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
  654. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
  655. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
  656. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
  657. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
  658. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
  659. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
  660. package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
  661. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
  662. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
  663. package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
  664. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
  665. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
  666. package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
  667. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
  668. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
  669. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
  670. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
  671. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
  672. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
  673. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
  674. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
  675. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
  676. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
  677. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
  678. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
  679. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
  680. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
  681. package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
  682. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
  683. package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
  684. package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
  685. package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
  686. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
  687. package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
  688. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
  689. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
  690. package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
  691. package/dist/presets/advanced.d.ts +0 -33
  692. package/dist/presets/advanced.js +0 -50
  693. package/dist/presets/advanced.js.map +0 -1
  694. package/dist/presets/simple.d.ts +0 -9
  695. package/dist/presets/simple.js +0 -12
  696. package/dist/presets/simple.js.map +0 -1
  697. package/dist/react/advanced-preset.d.ts +0 -11
  698. package/dist/react/advanced-preset.js +0 -31
  699. package/dist/react/advanced-preset.js.map +0 -1
  700. package/dist/vue/advanced-preset.d.ts +0 -13
  701. package/dist/vue/advanced-preset.js +0 -33
  702. package/dist/vue/advanced-preset.js.map +0 -1
  703. package/docs/extending.md +0 -538
  704. package/docs/integration.md +0 -632
  705. package/docs/llms.md +0 -383
  706. package/docs/usage.md +0 -1076
@@ -1 +1 @@
1
- {"version":3,"file":"block-lines.js","names":[],"sources":["../../../../src/features/ai/protocol/block-lines.ts"],"sourcesContent":["// i18n-ignore-file: the document lines and the prompt texts describing them are model-facing (German; the model's language is not the editor's)\n/**\n * The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives\n * `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie\n * inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the\n * prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.\n *\n * A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting\n * of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a\n * closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at\n * the left edge, so their content can look like anything.\n */\n\n// ---------- Writing ----------------------------------------------------------\n\n/** One level of nesting inside a Baustein or host block. */\nconst INDENT = ' '\n\n/** The line shown instead of the rest of a document that did not fit. */\nexport const TRUNCATED_LINE = '[…] (Rest des Dokuments gekürzt)'\n\n/** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */\nexport function blockLine(id: string | null, prefix: string, text: string): string {\n return `${id ? `[${id}] ` : ''}${prefix}${text}`\n}\n\n/** `markdown` on one line: hard breaks and wrapped lines would start new \"blocks\". */\nexport function singleBlockLine(markdown: string): string {\n return markdown.replace(/\\s*\\n\\s*/g, ' ')\n}\n\n/** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */\nexport function nestedLinePrefix(prefix: string): string {\n return `${prefix}${INDENT}`\n}\n\n/** The prefix of the blocks of a quote. */\nexport function quoteLinePrefix(prefix: string): string {\n return `${prefix}> `\n}\n\n/** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */\nexport function listLinePrefix(prefix: string, ordered: boolean, number: number): string {\n return `${prefix}${ordered ? `${number}. ` : '- '}`\n}\n\n/** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */\nexport function continuedLinePrefix(prefix: string): string {\n return prefix.replace(/(?:- |\\d+\\. )$/, marker => ' '.repeat(marker.length))\n}\n\n/** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */\nexport function compositeChromePart(markdown: string): string {\n return singleBlockLine(markdown.replace(/^(?:#+|>) ?/, ''))\n}\n\n/**\n * The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty\n * chrome part (title, description, tags) separated by ` | `.\n */\nexport function compositeLineHeader(kind: string, meta: unknown, chrome: readonly string[]): string {\n const fields = meta && typeof meta === 'object' && Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ''\n return [`::: ${kind}${fields}`, ...chrome.filter(Boolean)].join(' | ')\n}\n\n/** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */\nexport function compositeLineClose(prefix: string): string {\n return `${nestedLinePrefix(prefix)}:::`\n}\n\n// ---------- Reading ----------------------------------------------------------\n\n/** The block IDs of a document text and which blocks lie inside which. */\nexport interface BlockLines {\n /** Every block ID that starts a line (lines inside fenced code do not count). */\n ids: ReadonlySet<string>\n /** The IDs of all blocks nested below block `id`: the lines indented under its header line (empty for a block without nested blocks). */\n below: (id: string) => ReadonlySet<string>\n}\n\nconst ID_PREFIX = /^\\[([^\\]\\s]+)\\] /\n/** An opening code fence after optional list/quote markers and indentation: three or more backticks, no backtick in the info string. */\nconst FENCE_OPEN = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*(`{3,})[^`]*$/\nconst FENCE_CLOSE = /^[ \\t]*(`{3,})[ \\t]*$/\n/** Indentation and list/quote markers at the start of a line's content. */\nconst LINE_PREFIX = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*/\n\n/**\n * The fence length after `line` when a fence of `fence` backticks was open before it (0: none). A line of at least as\n * many backticks closes the fence; `opens` says whether a line outside a fence may open one.\n */\nfunction fenceAfter(fence: number, line: string, opens: boolean): number {\n if (fence) {\n const closing = FENCE_CLOSE.exec(line)\n return closing && closing[1]!.length >= fence ? 0 : fence\n }\n const opening = opens ? FENCE_OPEN.exec(line) : null\n return opening ? opening[1]!.length : 0\n}\n\nconst NOTHING: ReadonlySet<string> = new Set()\n\n/**\n * Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never\n * block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence\n * (a stray line of backticks inside the code cannot swallow what follows).\n *\n * Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its\n * block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph\n * and its nested list are siblings, nothing lies \"inside\" the paragraph. A block without lines of its own (a list, a\n * quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not \"inside\".\n */\nexport function readBlockLines(text: string): BlockLines {\n const ids = new Set<string>()\n const nested = new Map<string, Set<string>>()\n // The blocks whose lines enclose the current one, outermost first; `head`: the block can have nested blocks.\n const open: { id: string, indent: number, head: boolean }[] = []\n let fence = 0\n\n for (const line of text.split('\\n')) {\n const id = ID_PREFIX.exec(line)?.[1]\n if (fence) {\n fence = fenceAfter(fence, line, false)\n continue\n }\n // The truncation notice reads like a line with the ID `…`.\n if (!id || line === TRUNCATED_LINE) continue\n const content = line.slice(id.length + 3)\n fence = fenceAfter(0, content, true)\n ids.add(id)\n\n const indent = /^[ \\t]*/.exec(content)![0].length\n const listItem = /[-*+\\d]/.test(LINE_PREFIX.exec(content)![0])\n while (open.length && open.at(-1)!.indent >= indent) open.pop()\n for (const outer of open) {\n if (!outer.head || outer.id === id) continue\n const inside = nested.get(outer.id)\n if (inside) inside.add(id)\n else nested.set(outer.id, new Set([id]))\n }\n open.push({ id, indent, head: !listItem && (open.at(-1)?.head ?? true) })\n }\n return { ids, below: id => nested.get(id) ?? NOTHING }\n}\n\n/**\n * Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the\n * start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the\n * indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper\n * nested lines into indented code).\n */\nexport function stripBlockLineEcho(markdown: string, known: (id: string) => boolean): string {\n let fence = 0\n const stripped = markdown.split('\\n').map((line) => {\n if (fence) {\n fence = fenceAfter(fence, line, false)\n return line\n }\n const bare = line.replace(/^\\[([^\\]\\s]+)\\] ?/, (prefix, id: string) => known(id) ? '' : prefix)\n fence = fenceAfter(0, bare, true)\n return bare\n })\n const indents = stripped.filter(line => line.trim()).map(line => /^[ \\t]*/.exec(line)![0].length)\n const common = indents.length ? Math.min(...indents) : 0\n return (common ? stripped.map(line => line.replace(new RegExp(`^[ \\\\t]{0,${common}}`), '')) : stripped).join('\\n')\n}\n\n/**\n * Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the\n * start of each line of its Markdown.\n */\nexport function stripListMarkerEcho(markdown: string): string {\n return markdown.replace(/^(?:[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+|[ \\t]*>[ \\t]?)+/gm, '')\n}\n\n// ---------- Prompt -----------------------------------------------------------\n\n/** The block ID of the prompt's example line (also the target of its citation example). */\nexport const BLOCK_LINE_EXAMPLE_ID = '0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d'\n\n/** The text of the prompt's example line: a speaker line. */\nexport const BLOCK_LINE_EXAMPLE_TEXT = '**Anna:** Wir starten im Mai.'\n\n/** The example line of the prompt, written by `blockLine` like every line of the document. */\nexport const BLOCK_LINE_EXAMPLE = blockLine(BLOCK_LINE_EXAMPLE_ID, '', BLOCK_LINE_EXAMPLE_TEXT)\n\n/** The label of the document in the user message. */\nexport const BLOCK_LINES_LABEL = 'Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)'\n\n/** What the first lines of the system prompt say about the document's lines. */\nexport const BLOCK_LINE_INTRO = `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \\`${BLOCK_LINE_EXAMPLE}\\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`\n\n/** The list and quote blocks with the characters their lines carry. */\nexport const BLOCK_LINE_MARKERS = `Aufzählung („${listLinePrefix('', false, 1)}“), nummerierte Liste („${listLinePrefix('', true, 1)}“), Zitat („${quoteLinePrefix('')}“)`\n\n/** What the prompt says about lists and quotes in the document. */\nexport const BLOCK_LINE_LIST_TEXT = 'Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.'\n\n/** What the prompt says about the lines of a host block with block content (a callout). */\nexport const BLOCK_LINE_HOST_TEXT = `Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „${blockLine('ID', '', '<erste Zeile seiner Markdown-Form>')}“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt.`\n\n/** What the prompt says about the lines of a Baustein. */\nexport const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopfzeile „${blockLine('ID', '', compositeLineHeader('Art', { feld: 'wert' }, ['Titel', 'Beschreibung', 'Tags: #a #b']))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose('')}“ ohne ID schließt ihn.`\n\n/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */\nexport const BLOCK_LINE_ECHO_TEXT = '„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.'\n\n/** What the retry message says about IDs. */\nexport const BLOCK_LINE_IDS_HINT = 'Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.'\n"],"mappings":"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"}
1
+ {"version":3,"file":"block-lines.js","names":[],"sources":["../../../../src/features/ai/protocol/block-lines.ts"],"sourcesContent":["// i18n-ignore-file: the document lines and the prompt texts describing them are model-facing (German; the model's language is not the editor's)\n/**\n * The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives\n * `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie\n * inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the\n * prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.\n *\n * A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting\n * of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a\n * closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at\n * the left edge, so their content can look like anything.\n */\n\n// ---------- Writing ----------------------------------------------------------\n\n/** One level of nesting inside a Baustein or host block. */\nconst INDENT = ' '\n\n/** The line shown instead of the rest of a document that did not fit. */\nexport const TRUNCATED_LINE = '[…] (Rest des Dokuments gekürzt)'\n\n/** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */\nexport function blockLine(id: string | null, prefix: string, text: string): string {\n return `${id ? `[${id}] ` : ''}${prefix}${text}`\n}\n\n/** `markdown` on one line: hard breaks and wrapped lines would start new \"blocks\". */\nexport function singleBlockLine(markdown: string): string {\n return markdown.replace(/\\s*\\n\\s*/g, ' ')\n}\n\n/** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */\nexport function nestedLinePrefix(prefix: string): string {\n return `${prefix}${INDENT}`\n}\n\n/** The prefix of the blocks of a quote. */\nexport function quoteLinePrefix(prefix: string): string {\n return `${prefix}> `\n}\n\n/** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */\nexport function listLinePrefix(prefix: string, ordered: boolean, number: number): string {\n return `${prefix}${ordered ? `${number}. ` : '- '}`\n}\n\n/** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */\nexport function continuedLinePrefix(prefix: string): string {\n return prefix.replace(/(?:- |\\d+\\. )$/, marker => ' '.repeat(marker.length))\n}\n\n/** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */\nexport function compositeChromePart(markdown: string): string {\n return singleBlockLine(markdown.replace(/^(?:#+|>) ?/, ''))\n}\n\n/**\n * The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty\n * chrome part (title, description, tags) separated by ` | `.\n */\nexport function compositeLineHeader(kind: string, meta: unknown, chrome: readonly string[]): string {\n const fields = meta && typeof meta === 'object' && Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ''\n return [`::: ${kind}${fields}`, ...chrome.filter(Boolean)].join(' | ')\n}\n\n/** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */\nexport function compositeLineClose(prefix: string): string {\n return `${nestedLinePrefix(prefix)}:::`\n}\n\n// ---------- Reading ----------------------------------------------------------\n\n/** The block IDs of a document text and which blocks lie inside which. */\nexport interface BlockLines {\n /** Every block ID that starts a line (lines inside fenced code do not count). */\n ids: ReadonlySet<string>\n /** The IDs of all blocks nested below block `id`: the lines indented under its header line (empty for a block without nested blocks). */\n below: (id: string) => ReadonlySet<string>\n}\n\nconst ID_PREFIX = /^\\[([^\\]\\s]+)\\] /\n/** An opening code fence after optional list/quote markers and indentation: three or more backticks, no backtick in the info string. */\nconst FENCE_OPEN = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*(`{3,})[^`]*$/\nconst FENCE_CLOSE = /^[ \\t]*(`{3,})[ \\t]*$/\n/** Indentation and list/quote markers at the start of a line's content. */\nconst LINE_PREFIX = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*/\n\n/**\n * The fence length after `line` when a fence of `fence` backticks was open before it (0: none). A line of at least as\n * many backticks closes the fence; `opens` says whether a line outside a fence may open one.\n */\nfunction fenceAfter(fence: number, line: string, opens: boolean): number {\n if (fence) {\n const closing = FENCE_CLOSE.exec(line)\n return closing && closing[1]!.length >= fence ? 0 : fence\n }\n const opening = opens ? FENCE_OPEN.exec(line) : null\n return opening ? opening[1]!.length : 0\n}\n\nconst NOTHING: ReadonlySet<string> = new Set()\n\n/**\n * Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never\n * block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence\n * (a stray line of backticks inside the code cannot swallow what follows).\n *\n * Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its\n * block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph\n * and its nested list are siblings, nothing lies \"inside\" the paragraph. A block without lines of its own (a list, a\n * quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not \"inside\".\n */\nexport function readBlockLines(text: string): BlockLines {\n const ids = new Set<string>()\n const nested = new Map<string, Set<string>>()\n // The blocks whose lines enclose the current one, outermost first; `head`: the block can have nested blocks.\n const open: { id: string, indent: number, head: boolean }[] = []\n let fence = 0\n\n for (const line of text.split('\\n')) {\n const id = ID_PREFIX.exec(line)?.[1]\n if (fence) {\n fence = fenceAfter(fence, line, false)\n continue\n }\n // The truncation notice reads like a line with the ID `…`.\n if (!id || line === TRUNCATED_LINE) continue\n const content = line.slice(id.length + 3)\n fence = fenceAfter(0, content, true)\n ids.add(id)\n\n const indent = /^[ \\t]*/.exec(content)![0].length\n const listItem = /[-*+\\d]/.test(LINE_PREFIX.exec(content)![0])\n while (open.length && open.at(-1)!.indent >= indent) open.pop()\n for (const outer of open) {\n if (!outer.head || outer.id === id) continue\n const inside = nested.get(outer.id)\n if (inside) inside.add(id)\n else nested.set(outer.id, new Set([id]))\n }\n open.push({ id, indent, head: !listItem && (open.at(-1)?.head ?? true) })\n }\n return { ids, below: id => nested.get(id) ?? NOTHING }\n}\n\n/**\n * Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the\n * start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the\n * indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper\n * nested lines into indented code).\n */\nexport function stripBlockLineEcho(markdown: string, known: (id: string) => boolean): string {\n let fence = 0\n const stripped = markdown.split('\\n').map((line) => {\n if (fence) {\n fence = fenceAfter(fence, line, false)\n return line\n }\n const bare = line.replace(/^\\[([^\\]\\s]+)\\] ?/, (prefix, id: string) => known(id) ? '' : prefix)\n fence = fenceAfter(0, bare, true)\n return bare\n })\n const indents = stripped.filter(line => line.trim()).map(line => /^[ \\t]*/.exec(line)![0].length)\n const common = indents.length ? Math.min(...indents) : 0\n return (common ? stripped.map(line => line.replace(new RegExp(`^[ \\\\t]{0,${common}}`), '')) : stripped).join('\\n')\n}\n\n/**\n * Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the\n * start of each line of its Markdown.\n */\nexport function stripListMarkerEcho(markdown: string): string {\n return markdown.replace(/^(?:[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+|[ \\t]*>[ \\t]?)+/gm, '')\n}\n\n// ---------- Prompt -----------------------------------------------------------\n\n/** The block ID of the prompt's example line (also the target of its citation example). */\nexport const BLOCK_LINE_EXAMPLE_ID = '0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d'\n\n/** The text of the prompt's example line: a speaker line. */\nexport const BLOCK_LINE_EXAMPLE_TEXT = '**Anna:** Wir starten im Mai.'\n\n/** The example line of the prompt, written by `blockLine` like every line of the document. */\nexport const BLOCK_LINE_EXAMPLE = blockLine(BLOCK_LINE_EXAMPLE_ID, '', BLOCK_LINE_EXAMPLE_TEXT)\n\n/** The label of the document in the user message. */\nexport const BLOCK_LINES_LABEL = 'Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)'\n\n/** What the first lines of the system prompt say about the document's lines. */\nexport const BLOCK_LINE_INTRO = `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \\`${BLOCK_LINE_EXAMPLE}\\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`\n\n/** The list and quote blocks with the characters their lines carry. */\nexport const BLOCK_LINE_MARKERS = `Aufzählung („${listLinePrefix('', false, 1)}“), nummerierte Liste („${listLinePrefix('', true, 1)}“), Zitat („${quoteLinePrefix('')}“)`\n\n/** What the prompt says about lists and quotes in the document. */\nexport const BLOCK_LINE_LIST_TEXT = 'Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.'\n\n/** What the prompt says about the lines of a host block with block content (a callout). */\nexport const BLOCK_LINE_HOST_TEXT = `Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „${blockLine('ID', '', '<erste Zeile seiner Markdown-Form>')}“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt.`\n\n/** What the prompt says about the lines of a Baustein. */\nexport const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopfzeile „${blockLine('ID', '', compositeLineHeader('Art', { feld: 'wert' }, ['Titel', 'Beschreibung', 'Tags: #a #b']))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose('')}“ ohne ID schließt ihn. Das Feld „bib“ ist eine Literaturangabe als CSL-JSON, „derivation“ beschreibt, woraus der Baustein erzeugt wurde. Ein Verweis auf einen Baustein des Dokuments steht im Text als „[@<Block-ID>]“, mit Fundstelle als „[@<Block-ID>, S. 12]“.`\n\n/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */\nexport const BLOCK_LINE_ECHO_TEXT = '„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.'\n\n/** What the retry message says about IDs. */\nexport const BLOCK_LINE_IDS_HINT = 'Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.'\n"],"mappings":"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"}
@@ -22,11 +22,15 @@ const FIELD_TYPES = {
22
22
  textarea: "Langtext",
23
23
  date: "Datum JJJJ-MM-TT",
24
24
  number: "Zahl",
25
- select: "Auswahl"
25
+ select: "Auswahl",
26
+ multiselect: "Mehrfachauswahl",
27
+ reference: "Block-ID eines Bausteins",
28
+ bibliography: "Literaturangabe als CSL-JSON-Objekt",
29
+ derivation: "Herkunft, nur lesen"
26
30
  };
27
31
  /** `status (Status: open = Offen, doing = In Arbeit, done = Erledigt)`, `due (Fällig, Datum JJJJ-MM-TT)`. */
28
32
  function fieldText(field) {
29
- const values = field.options?.length ? `: ${field.options.map((option) => `${option.value} = ${option.label}`).join(", ")}` : `, ${FIELD_TYPES[field.type ?? "text"]}`;
33
+ const values = field.options?.length ? `: ${field.options.map((option) => `${option.value} = ${option.label}`).join(", ")}${field.type === "multiselect" ? `; ${FIELD_TYPES.multiselect}, mehrere Werte als JSON-Liste` : ""}` : `, ${FIELD_TYPES[field.type ?? "text"]}`;
30
34
  return `${field.key} (${field.label}${values})`;
31
35
  }
32
36
  /** „Blockmodell“: the document's line format for composites and host blocks holding blocks, the fence the model writes composites in, and `convert`. */
@@ -1 +1 @@
1
- {"version":3,"file":"chat.js","names":[],"sources":["../../../../src/features/ai/protocol/chat.ts"],"sourcesContent":["// i18n-ignore-file: model-facing chat system prompt and messages (German prompts stay German whatever the editor's language)\nimport type { ModelMessage } from 'ai'\nimport type { AiBlock, AiContextKind } from '../../../core/feature'\nimport type { StructureMode } from '../../../settings'\nimport { AGENT_TOOL, type AgentOp, type AgentOpFeedback, agentFeedbackText, agentShownMessage } from './agent-ops'\nimport { BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BLOCK_LINES_LABEL } from './block-lines'\n\n/**\n * One turn of the document chat; the conversation lives on the client and is sent in full with every message.\n * A user turn carries the decisions on earlier proposals made since the previous turn (`feedback`); an assistant turn\n * the edit operations it proposed (`ops`, possibly without any text) and how many invalid ones were dropped.\n */\nexport type DocumentChatMessage =\n | { role: 'user', content: string, feedback?: AgentOpFeedback[] }\n | { role: 'assistant', content: string, ops?: AgentOp[], dropped?: number }\n\n/** Input limits (characters / messages) of `POST /api/ai/chat`; the client trims to fit before sending. */\nexport const CHAT_LIMITS = { document: 120000, message: 8000, messages: 40 } as const\n\n/** Link target prefix of a quote citation: `[„Zitat“](#block-<id>)`. */\nexport const CITATION_HREF_PREFIX = '#block-'\n\n/**\n * The block ID a citation link points to, or `null` for any other href. Also accepts a bare `#<id>`, which models\n * write now and then despite the instructions; whether the ID exists is up to the caller.\n */\nexport function citationBlockId(href: unknown): string | null {\n if (typeof href !== 'string' || !href.startsWith('#')) return null\n return (href.startsWith(CITATION_HREF_PREFIX) ? href.slice(CITATION_HREF_PREFIX.length) : href.slice(1)) || null\n}\n\n/** A composite kind as the chat model learns it: the client sends the kinds of the editor's `aiContext` with every message. */\nexport type ChatCompositeKind = Pick<AiContextKind, 'id' | 'label' | 'fields' | 'purpose'>\ntype ChatField = ChatCompositeKind['fields'][number]\n\n/** A host-defined block as the chat model learns it: the editor's `aiContext.blocks`, sent with every message. */\nexport type ChatBlock = AiBlock\n\n/**\n * What the model may build and convert into: the editor's composite kinds and host blocks, the `convert` targets it\n * offers, and the user's preferred structure of new content (settings `blocks.structure`).\n */\nexport interface ChatBlockModel { kinds: readonly ChatCompositeKind[], blocks: readonly ChatBlock[], targets: readonly string[], structure: StructureMode }\n\nconst FIELD_TYPES: Record<NonNullable<ChatField['type']>, string> = { text: 'Text', textarea: 'Langtext', date: 'Datum JJJJ-MM-TT', number: 'Zahl', select: 'Auswahl' }\n\n/** `status (Status: open = Offen, doing = In Arbeit, done = Erledigt)`, `due (Fällig, Datum JJJJ-MM-TT)`. */\nfunction fieldText(field: ChatField): string {\n const values = field.options?.length ? `: ${field.options.map(option => `${option.value} = ${option.label}`).join(', ')}` : `, ${FIELD_TYPES[field.type ?? 'text']}`\n return `${field.key} (${field.label}${values})`\n}\n\n/** „Blockmodell“: the document's line format for composites and host blocks holding blocks, the fence the model writes composites in, and `convert`. */\nfunction blockModelText({ kinds, blocks, targets, structure }: ChatBlockModel): string[] {\n const composites = structure === 'composite' && kinds.length > 0\n const example = kinds.find(kind => kind.fields.some(field => field.options?.length)) ?? kinds[0]\n const exampleField = example?.fields.find(field => field.options?.length)\n const exampleMeta = exampleField ? ` {\"${exampleField.key}\":\"${exampleField.options!.at(-1)!.value}\"}` : ''\n return [\n 'Blockmodell:',\n `Blocktypen: Absatz, Überschrift (#, ##, ###), ${BLOCK_LINE_MARKERS}, Codeblock, Redebeitrag („**Name:** Text“) und Bausteine. ${BLOCK_LINE_LIST_TEXT}`,\n ...(blocks.length\n ? [\n `Eigene Blöcke: ${blocks.map(block => `${block.id} (${block.label}): ${block.description} Markdown-Form: „${block.syntax.replace(/\\n/g, '\\\\n')}“. Beispiel: „${block.example.replace(/\\n/g, '\\\\n')}“`).join(' ')}`,\n `${BLOCK_LINE_HOST_TEXT} „update“ auf seine ID mit vollständigem Markdown ersetzt den ganzen Block; „add“ nach einem seiner Inhaltsblöcke fügt innerhalb des Blocks ein, ein Block derselben Art danach.`,\n ]\n : []),\n composites\n ? 'Bevorzugte Gliederung: Bausteine. Neue Inhalte gliederst du mit Bausteinen statt mit Überschriften; Unterabschnitte sind verschachtelte Bausteine (Zaun im Zaun, jeder Zaun endet mit einer eigenen Zeile „:::“). Markdown-Überschriften schreibst du nur als Titelzeile eines Bausteins.'\n : 'Bevorzugte Gliederung: Überschriften. Neue Inhalte gliederst du mit Überschriften (##, ###) statt mit Bausteinen; einen Baustein schreibst du nur, wenn ausdrücklich ein Baustein (Abschnitt, Aufgabe, Notiz …) verlangt wird oder du einen bestehenden Baustein änderst.',\n ...(kinds.length\n ? [\n `Bausteine sind Blöcke mit Titel, Beschreibung, Tags, Feldern und eigenen Inhaltsblöcken. ${BLOCK_LINE_COMPOSITE_TEXT}`,\n `Arten von Bausteinen: ${kinds.map(kind => `${kind.id} (${kind.label}${kind.purpose ? `: ${kind.purpose}` : ''}; ${kind.fields.length ? `Felder: ${kind.fields.map(fieldText).join(', ')}` : 'keine Felder'})`).join('; ')}.`,\n `In „markdown“ schreibst du einen Baustein ohne IDs und ohne Einrückung als Zaun: die Zeile „::: Art {\"feld\":\"wert\"}“ (Felder als JSON, nur gesetzte), dann „## Titel“, „> Beschreibung“ und „Tags: #a #b“ (jeweils nur, wenn vorhanden), dann die Inhaltsblöcke als Markdown, zuletzt die Zeile „:::“. Beispiel: „::: ${example?.id ?? 'note'}${exampleMeta}\\\\n## Angebot prüfen\\\\n- Preise vergleichen\\\\n- Rückmeldung an Anna\\\\n:::“.`,\n '„update“ auf einen Baustein mit Markdown ohne :::-Zaun ersetzt nur seinen Inhalt (Kinder); mit Zaun den ganzen Baustein (Art, Felder, Titel, Beschreibung, Tags und Inhalt).',\n ]\n : []),\n `„convert“ wandelt den Block „id“ in den Blocktyp „to“ um und behält seinen Inhalt. Erlaubte Werte für „to“: ${targets.join(', ')}. Eine Überschrift, die zum Baustein wird, nimmt die folgenden Blöcke bis zur nächsten Überschrift gleicher oder höherer Ebene als Inhalt mit; ein Baustein, der zu einem anderen Blocktyp wird, behält Beschreibung, Tags und Felder als Textzeilen; wird er zur Überschrift, werden seine inneren Bausteine zu Unterüberschriften.`,\n 'Sollen sich Blocktyp und Inhalt oder Felder zugleich ändern (z. B. ein Absatz wird zu einer Aufgabe mit Status), nimm statt „convert“ ein „update“ mit dem vollständigen neuen Markdown samt :::-Zaun; jeder Block bekommt höchstens eine Operation.',\n ]\n}\n\n/** The system prompt: answering with citations, editing through `edit_document`, and the editor's block model. */\nfunction chatSystem(blockModel: ChatBlockModel): string {\n return [\n 'Du beantwortest Fragen zu einem Dokument, meist dem Transkript eines Gesprächs, und bearbeitest es auf Wunsch. Das Dokument steht in der letzten Nachricht der Nutzerin bzw. des Nutzers und ist der aktuelle Stand; frühere Nachrichten bezogen sich womöglich auf einen älteren Stand.',\n BLOCK_LINE_INTRO,\n '',\n 'Fragen:',\n `Belege deine Antworten mit wörtlichen Zitaten: ein zusammenhängender, unveränderter Ausschnitt aus einem einzigen Block. Jedes Zitat ist ein Markdown-Link, dessen Ziel „${CITATION_HREF_PREFIX}“ gefolgt von der vollständigen ID dieses Blocks ist: [„Zitat“](${CITATION_HREF_PREFIX}ID).`,\n `Beispiel zur Zeile oben: Anna kündigt den Start an: [„Wir starten im Mai.“](${CITATION_HREF_PREFIX}${BLOCK_LINE_EXAMPLE_ID})`,\n 'Verwende nur IDs, die im Dokument vorkommen, und nenne IDs nie außerhalb solcher Links.',\n 'Steht die Antwort nicht im Dokument, sag das ehrlich, statt etwas zu erfinden.',\n 'Antworte knapp in Markdown (Absätze, Aufzählungen, **fett**) und in der Sprache der Frage.',\n '',\n 'Änderungen:',\n `Bittet die Nutzerin bzw. der Nutzer darum, das Dokument zu ändern (kürzen, umformulieren, ergänzen, löschen, umwandeln …), rufe das Werkzeug ${AGENT_TOOL} auf, statt den neuen Text im Chat zu schreiben. Schreib davor höchstens ein bis zwei Sätze dazu, was du änderst. Bei Fragen rufst du das Werkzeug nicht auf.`,\n 'Operationen: „update“ ersetzt den Inhalt des Blocks „id“ durch „markdown“; „delete“ löscht den Block „id“; „add“ fügt „markdown“ (ein oder mehrere Blöcke, durch Leerzeilen getrennt) nach dem Block „afterId“ ein („afterId“ gehört bei „add“ immer dazu, für den Dokumentanfang schreibst du ausdrücklich null; mehrere „add“ nach demselben Block werden in der Reihenfolge eingefügt, in der du sie schreibst); „convert“ wandelt den Block „id“ in den Blocktyp „to“ um.',\n `${BLOCK_LINE_ECHO_TEXT} Redebeiträge behalten den fett gesetzten Namen mit Doppelpunkt am Anfang; ein anderer Name wechselt die Sprecherin bzw. den Sprecher. Neue Redebeiträge schreibst du ebenso als „**Name:** Text“.`,\n 'Ändere nur die Blöcke, die die Bitte betrifft; lass unveränderte Blöcke weg. Jeder Block höchstens einmal; eine Operation auf einen Block innerhalb eines Blocks, den eine andere Operation ersetzt oder löscht, ist ungültig (schreibe die Änderung dann in das Markdown der äußeren Operation). Verwende nur IDs aus dem Dokument. Nennt die Nachricht einen Bearbeitungsbereich, beziehen sich alle IDs und afterIds ausschließlich auf Blöcke daraus.',\n 'Jede Änderung wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt und einzeln übernommen oder abgelehnt. Die Entscheidungen erhältst du mit einer späteren Nachricht („Entscheidungen zu deinen letzten Vorschlägen: …“). Geh auf abgelehnte Vorschläge ein und wiederhole sie nicht unverändert.',\n '',\n ...blockModelText(blockModel),\n ].join('\\n')\n}\n\n/** A user turn as the model reads it: decisions on earlier proposals first, then the message. */\nfunction userText(content: string, feedback: readonly AgentOpFeedback[] | undefined): string {\n const decisions = agentFeedbackText(feedback ?? [])\n return decisions ? `${decisions}\\n\\nNachricht: ${content}` : content\n}\n\n/**\n * Model messages for a chat turn: the history as sent (proposed operations as `edit_document` tool calls), with the\n * current document placed in the last (user) message next to the scope, the latest decisions and the message —\n * document content never goes into the system prompt (the editor's block model does). `scope`: IDs the edits are\n * limited to (null: whole document).\n */\nexport function documentChatMessages(messages: readonly DocumentChatMessage[], document: string, scope: readonly string[] | null, blockModel: ChatBlockModel): ModelMessage[] {\n const history = messages.slice(0, -1).flatMap((message, index): ModelMessage[] => {\n if (message.role === 'user') return [{ role: 'user', content: userText(message.content, message.feedback) }]\n if (!message.ops?.length && !message.dropped) return [{ role: 'assistant', content: message.content }]\n const ops = message.ops ?? []\n const toolCallId = `edit_${index}`\n return [\n {\n role: 'assistant',\n content: [\n ...(message.content ? [{ type: 'text' as const, text: message.content }] : []),\n { type: 'tool-call', toolCallId, toolName: AGENT_TOOL, input: { ops: ops.map(({ op: _op, ...rest }) => rest) } },\n ],\n },\n {\n role: 'tool',\n content: [{ type: 'tool-result', toolCallId, toolName: AGENT_TOOL, output: { type: 'text', value: agentShownMessage(ops, message.dropped ?? 0) } }],\n },\n ]\n })\n const last = messages.at(-1)! as Extract<DocumentChatMessage, { role: 'user' }>\n const range = scope\n ? `Bearbeitungsbereich: die Auswahl der Nutzerin bzw. des Nutzers. Änderungen (id und afterId) nur an diesen Blöcken: ${scope.join(', ')}`\n : 'Bearbeitungsbereich: das ganze Dokument.'\n const decisions = agentFeedbackText(last.feedback ?? [])\n return [\n { role: 'system', content: chatSystem(blockModel) },\n ...history,\n {\n role: 'user',\n content: [\n `${BLOCK_LINES_LABEL}:\\n\"\"\"\\n${document}\\n\"\"\"`,\n range,\n ...(decisions ? [decisions] : []),\n `Nachricht: ${last.content}`,\n ].join('\\n\\n'),\n },\n ]\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chat.js","names":[],"sources":["../../../../src/features/ai/protocol/chat.ts"],"sourcesContent":["// i18n-ignore-file: model-facing chat system prompt and messages (German prompts stay German whatever the editor's language)\nimport type { ModelMessage } from 'ai'\nimport type { AiBlock, AiContextKind } from '../../../core/feature'\nimport type { StructureMode } from '../../../settings'\nimport { AGENT_TOOL, type AgentOp, type AgentOpFeedback, agentFeedbackText, agentShownMessage } from './agent-ops'\nimport { BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BLOCK_LINES_LABEL } from './block-lines'\n\n/**\n * One turn of the document chat; the conversation lives on the client and is sent in full with every message.\n * A user turn carries the decisions on earlier proposals made since the previous turn (`feedback`); an assistant turn\n * the edit operations it proposed (`ops`, possibly without any text) and how many invalid ones were dropped.\n */\nexport type DocumentChatMessage =\n | { role: 'user', content: string, feedback?: AgentOpFeedback[] }\n | { role: 'assistant', content: string, ops?: AgentOp[], dropped?: number }\n\n/** Input limits (characters / messages) of `POST /api/ai/chat`; the client trims to fit before sending. */\nexport const CHAT_LIMITS = { document: 120000, message: 8000, messages: 40 } as const\n\n/** Link target prefix of a quote citation: `[„Zitat“](#block-<id>)`. */\nexport const CITATION_HREF_PREFIX = '#block-'\n\n/**\n * The block ID a citation link points to, or `null` for any other href. Also accepts a bare `#<id>`, which models\n * write now and then despite the instructions; whether the ID exists is up to the caller.\n */\nexport function citationBlockId(href: unknown): string | null {\n if (typeof href !== 'string' || !href.startsWith('#')) return null\n return (href.startsWith(CITATION_HREF_PREFIX) ? href.slice(CITATION_HREF_PREFIX.length) : href.slice(1)) || null\n}\n\n/** A composite kind as the chat model learns it: the client sends the kinds of the editor's `aiContext` with every message. */\nexport type ChatCompositeKind = Pick<AiContextKind, 'id' | 'label' | 'fields' | 'purpose'>\ntype ChatField = ChatCompositeKind['fields'][number]\n\n/** A host-defined block as the chat model learns it: the editor's `aiContext.blocks`, sent with every message. */\nexport type ChatBlock = AiBlock\n\n/**\n * What the model may build and convert into: the editor's composite kinds and host blocks, the `convert` targets it\n * offers, and the user's preferred structure of new content (settings `blocks.structure`).\n */\nexport interface ChatBlockModel { kinds: readonly ChatCompositeKind[], blocks: readonly ChatBlock[], targets: readonly string[], structure: StructureMode }\n\nconst FIELD_TYPES: Record<NonNullable<ChatField['type']>, string> = { text: 'Text', textarea: 'Langtext', date: 'Datum JJJJ-MM-TT', number: 'Zahl', select: 'Auswahl', multiselect: 'Mehrfachauswahl', reference: 'Block-ID eines Bausteins', bibliography: 'Literaturangabe als CSL-JSON-Objekt', derivation: 'Herkunft, nur lesen' }\n\n/** `status (Status: open = Offen, doing = In Arbeit, done = Erledigt)`, `due (Fällig, Datum JJJJ-MM-TT)`. */\nfunction fieldText(field: ChatField): string {\n const values = field.options?.length ? `: ${field.options.map(option => `${option.value} = ${option.label}`).join(', ')}${field.type === 'multiselect' ? `; ${FIELD_TYPES.multiselect}, mehrere Werte als JSON-Liste` : ''}` : `, ${FIELD_TYPES[field.type ?? 'text']}`\n return `${field.key} (${field.label}${values})`\n}\n\n/** „Blockmodell“: the document's line format for composites and host blocks holding blocks, the fence the model writes composites in, and `convert`. */\nfunction blockModelText({ kinds, blocks, targets, structure }: ChatBlockModel): string[] {\n const composites = structure === 'composite' && kinds.length > 0\n const example = kinds.find(kind => kind.fields.some(field => field.options?.length)) ?? kinds[0]\n const exampleField = example?.fields.find(field => field.options?.length)\n const exampleMeta = exampleField ? ` {\"${exampleField.key}\":\"${exampleField.options!.at(-1)!.value}\"}` : ''\n return [\n 'Blockmodell:',\n `Blocktypen: Absatz, Überschrift (#, ##, ###), ${BLOCK_LINE_MARKERS}, Codeblock, Redebeitrag („**Name:** Text“) und Bausteine. ${BLOCK_LINE_LIST_TEXT}`,\n ...(blocks.length\n ? [\n `Eigene Blöcke: ${blocks.map(block => `${block.id} (${block.label}): ${block.description} Markdown-Form: „${block.syntax.replace(/\\n/g, '\\\\n')}“. Beispiel: „${block.example.replace(/\\n/g, '\\\\n')}“`).join(' ')}`,\n `${BLOCK_LINE_HOST_TEXT} „update“ auf seine ID mit vollständigem Markdown ersetzt den ganzen Block; „add“ nach einem seiner Inhaltsblöcke fügt innerhalb des Blocks ein, ein Block derselben Art danach.`,\n ]\n : []),\n composites\n ? 'Bevorzugte Gliederung: Bausteine. Neue Inhalte gliederst du mit Bausteinen statt mit Überschriften; Unterabschnitte sind verschachtelte Bausteine (Zaun im Zaun, jeder Zaun endet mit einer eigenen Zeile „:::“). Markdown-Überschriften schreibst du nur als Titelzeile eines Bausteins.'\n : 'Bevorzugte Gliederung: Überschriften. Neue Inhalte gliederst du mit Überschriften (##, ###) statt mit Bausteinen; einen Baustein schreibst du nur, wenn ausdrücklich ein Baustein (Abschnitt, Aufgabe, Notiz …) verlangt wird oder du einen bestehenden Baustein änderst.',\n ...(kinds.length\n ? [\n `Bausteine sind Blöcke mit Titel, Beschreibung, Tags, Feldern und eigenen Inhaltsblöcken. ${BLOCK_LINE_COMPOSITE_TEXT}`,\n `Arten von Bausteinen: ${kinds.map(kind => `${kind.id} (${kind.label}${kind.purpose ? `: ${kind.purpose}` : ''}; ${kind.fields.length ? `Felder: ${kind.fields.map(fieldText).join(', ')}` : 'keine Felder'})`).join('; ')}.`,\n `In „markdown“ schreibst du einen Baustein ohne IDs und ohne Einrückung als Zaun: die Zeile „::: Art {\"feld\":\"wert\"}“ (Felder als JSON, nur gesetzte), dann „## Titel“, „> Beschreibung“ und „Tags: #a #b“ (jeweils nur, wenn vorhanden), dann die Inhaltsblöcke als Markdown, zuletzt die Zeile „:::“. Beispiel: „::: ${example?.id ?? 'note'}${exampleMeta}\\\\n## Angebot prüfen\\\\n- Preise vergleichen\\\\n- Rückmeldung an Anna\\\\n:::“.`,\n '„update“ auf einen Baustein mit Markdown ohne :::-Zaun ersetzt nur seinen Inhalt (Kinder); mit Zaun den ganzen Baustein (Art, Felder, Titel, Beschreibung, Tags und Inhalt).',\n ]\n : []),\n `„convert“ wandelt den Block „id“ in den Blocktyp „to“ um und behält seinen Inhalt. Erlaubte Werte für „to“: ${targets.join(', ')}. Eine Überschrift, die zum Baustein wird, nimmt die folgenden Blöcke bis zur nächsten Überschrift gleicher oder höherer Ebene als Inhalt mit; ein Baustein, der zu einem anderen Blocktyp wird, behält Beschreibung, Tags und Felder als Textzeilen; wird er zur Überschrift, werden seine inneren Bausteine zu Unterüberschriften.`,\n 'Sollen sich Blocktyp und Inhalt oder Felder zugleich ändern (z. B. ein Absatz wird zu einer Aufgabe mit Status), nimm statt „convert“ ein „update“ mit dem vollständigen neuen Markdown samt :::-Zaun; jeder Block bekommt höchstens eine Operation.',\n ]\n}\n\n/** The system prompt: answering with citations, editing through `edit_document`, and the editor's block model. */\nfunction chatSystem(blockModel: ChatBlockModel): string {\n return [\n 'Du beantwortest Fragen zu einem Dokument, meist dem Transkript eines Gesprächs, und bearbeitest es auf Wunsch. Das Dokument steht in der letzten Nachricht der Nutzerin bzw. des Nutzers und ist der aktuelle Stand; frühere Nachrichten bezogen sich womöglich auf einen älteren Stand.',\n BLOCK_LINE_INTRO,\n '',\n 'Fragen:',\n `Belege deine Antworten mit wörtlichen Zitaten: ein zusammenhängender, unveränderter Ausschnitt aus einem einzigen Block. Jedes Zitat ist ein Markdown-Link, dessen Ziel „${CITATION_HREF_PREFIX}“ gefolgt von der vollständigen ID dieses Blocks ist: [„Zitat“](${CITATION_HREF_PREFIX}ID).`,\n `Beispiel zur Zeile oben: Anna kündigt den Start an: [„Wir starten im Mai.“](${CITATION_HREF_PREFIX}${BLOCK_LINE_EXAMPLE_ID})`,\n 'Verwende nur IDs, die im Dokument vorkommen, und nenne IDs nie außerhalb solcher Links.',\n 'Steht die Antwort nicht im Dokument, sag das ehrlich, statt etwas zu erfinden.',\n 'Antworte knapp in Markdown (Absätze, Aufzählungen, **fett**) und in der Sprache der Frage.',\n '',\n 'Änderungen:',\n `Bittet die Nutzerin bzw. der Nutzer darum, das Dokument zu ändern (kürzen, umformulieren, ergänzen, löschen, umwandeln …), rufe das Werkzeug ${AGENT_TOOL} auf, statt den neuen Text im Chat zu schreiben. Schreib davor höchstens ein bis zwei Sätze dazu, was du änderst. Bei Fragen rufst du das Werkzeug nicht auf.`,\n 'Operationen: „update“ ersetzt den Inhalt des Blocks „id“ durch „markdown“; „delete“ löscht den Block „id“; „add“ fügt „markdown“ (ein oder mehrere Blöcke, durch Leerzeilen getrennt) nach dem Block „afterId“ ein („afterId“ gehört bei „add“ immer dazu, für den Dokumentanfang schreibst du ausdrücklich null; mehrere „add“ nach demselben Block werden in der Reihenfolge eingefügt, in der du sie schreibst); „convert“ wandelt den Block „id“ in den Blocktyp „to“ um.',\n `${BLOCK_LINE_ECHO_TEXT} Redebeiträge behalten den fett gesetzten Namen mit Doppelpunkt am Anfang; ein anderer Name wechselt die Sprecherin bzw. den Sprecher. Neue Redebeiträge schreibst du ebenso als „**Name:** Text“.`,\n 'Ändere nur die Blöcke, die die Bitte betrifft; lass unveränderte Blöcke weg. Jeder Block höchstens einmal; eine Operation auf einen Block innerhalb eines Blocks, den eine andere Operation ersetzt oder löscht, ist ungültig (schreibe die Änderung dann in das Markdown der äußeren Operation). Verwende nur IDs aus dem Dokument. Nennt die Nachricht einen Bearbeitungsbereich, beziehen sich alle IDs und afterIds ausschließlich auf Blöcke daraus.',\n 'Jede Änderung wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt und einzeln übernommen oder abgelehnt. Die Entscheidungen erhältst du mit einer späteren Nachricht („Entscheidungen zu deinen letzten Vorschlägen: …“). Geh auf abgelehnte Vorschläge ein und wiederhole sie nicht unverändert.',\n '',\n ...blockModelText(blockModel),\n ].join('\\n')\n}\n\n/** A user turn as the model reads it: decisions on earlier proposals first, then the message. */\nfunction userText(content: string, feedback: readonly AgentOpFeedback[] | undefined): string {\n const decisions = agentFeedbackText(feedback ?? [])\n return decisions ? `${decisions}\\n\\nNachricht: ${content}` : content\n}\n\n/**\n * Model messages for a chat turn: the history as sent (proposed operations as `edit_document` tool calls), with the\n * current document placed in the last (user) message next to the scope, the latest decisions and the message —\n * document content never goes into the system prompt (the editor's block model does). `scope`: IDs the edits are\n * limited to (null: whole document).\n */\nexport function documentChatMessages(messages: readonly DocumentChatMessage[], document: string, scope: readonly string[] | null, blockModel: ChatBlockModel): ModelMessage[] {\n const history = messages.slice(0, -1).flatMap((message, index): ModelMessage[] => {\n if (message.role === 'user') return [{ role: 'user', content: userText(message.content, message.feedback) }]\n if (!message.ops?.length && !message.dropped) return [{ role: 'assistant', content: message.content }]\n const ops = message.ops ?? []\n const toolCallId = `edit_${index}`\n return [\n {\n role: 'assistant',\n content: [\n ...(message.content ? [{ type: 'text' as const, text: message.content }] : []),\n { type: 'tool-call', toolCallId, toolName: AGENT_TOOL, input: { ops: ops.map(({ op: _op, ...rest }) => rest) } },\n ],\n },\n {\n role: 'tool',\n content: [{ type: 'tool-result', toolCallId, toolName: AGENT_TOOL, output: { type: 'text', value: agentShownMessage(ops, message.dropped ?? 0) } }],\n },\n ]\n })\n const last = messages.at(-1)! as Extract<DocumentChatMessage, { role: 'user' }>\n const range = scope\n ? `Bearbeitungsbereich: die Auswahl der Nutzerin bzw. des Nutzers. Änderungen (id und afterId) nur an diesen Blöcken: ${scope.join(', ')}`\n : 'Bearbeitungsbereich: das ganze Dokument.'\n const decisions = agentFeedbackText(last.feedback ?? [])\n return [\n { role: 'system', content: chatSystem(blockModel) },\n ...history,\n {\n role: 'user',\n content: [\n `${BLOCK_LINES_LABEL}:\\n\"\"\"\\n${document}\\n\"\"\"`,\n range,\n ...(decisions ? [decisions] : []),\n `Nachricht: ${last.content}`,\n ].join('\\n\\n'),\n },\n ]\n}\n"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
+ import { ModelProfile, ModelRef } from "../../../core/backend-types.js";
1
2
  import { BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho } from "./block-lines.js";
2
3
  import { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, ModelAgentOp, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps } from "./agent-ops.js";
3
4
  import { CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, citationBlockId, documentChatMessages } from "./chat.js";
4
5
  import { AI_COMPLETE_LIMITS, AiCompleteMessage, cleanCompletion, completeMessages, completionReady } from "./complete.js";
5
- import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelProfile, ModelRef, UserModelProfile, modelRefSchema } from "./models.js";
6
+ import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, UserModelProfile, modelRefSchema } from "./models.js";
6
7
  import { AI_ID_PATTERN, aiBlockInputSchema, aiBlockSchema } from "./schemas.js";
7
8
  import { AI_LIMITS, AI_TASKS, AI_TASK_IDS, AiApplyMode, AiBlockInput, AiInput, AiMessage, AiTask, AiTaskId, blockTasks, inBlockTasks, withHeading, withTaskHeading } from "./tasks.js";
8
- export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_TASKS, AI_TASK_IDS, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, AiApplyMode, AiBlockInput, AiCompleteMessage, AiInput, AiMessage, AiTask, AiTaskId, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelAgentOp, ModelProfile, ModelRef, TRUNCATED_LINE, UserModelProfile, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiBlockInputSchema, aiBlockSchema, blockLine, blockTasks, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, documentChatMessages, inBlockTasks, listLinePrefix, modelRefSchema, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
9
+ export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_TASKS, AI_TASK_IDS, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, AiApplyMode, AiBlockInput, AiCompleteMessage, AiInput, AiMessage, AiTask, AiTaskId, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelAgentOp, type ModelProfile, type ModelRef, TRUNCATED_LINE, UserModelProfile, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiBlockInputSchema, aiBlockSchema, blockLine, blockTasks, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, documentChatMessages, inBlockTasks, listLinePrefix, modelRefSchema, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
@@ -1,6 +1,7 @@
1
1
  import { MessageKey } from "../../../i18n/index.js";
2
2
  import { AiLanguage, ModelSlot } from "../../../settings/values.js";
3
3
  import "../../../settings/index.js";
4
+ import { ModelProfile, ModelRef } from "../../../core/backend-types.js";
4
5
  import { z } from "zod";
5
6
  //#region src/features/ai/protocol/models.d.ts
6
7
  /** Catalog keys of the label and description of each model slot for the settings UI. */
@@ -10,26 +11,10 @@ export declare const MODEL_SLOT_KEYS: Record<ModelSlot, {
10
11
  }>;
11
12
  /** Catalog keys of the display names of the stored target-language ids (`AI_LANGUAGES`); the stored id is what the model is told. */
12
13
  export declare const AI_LANGUAGE_KEYS: Record<AiLanguage, MessageKey>;
13
- /** Profile as the client sees it; presets never expose their key. */
14
- export interface ModelProfile {
15
- id: string;
16
- label: string;
17
- endpoint: string;
18
- model: string;
19
- source: 'preset' | 'user';
20
- }
21
14
  /** Browser-defined profile: its key is sent with each request and never stored on the server. */
22
15
  export interface UserModelProfile extends Omit<ModelProfile, 'source'> {
23
16
  apiKey: string;
24
17
  }
25
- /** Model an AI request runs on: a server preset by id, or an OpenAI-compatible endpoint with its own key. */
26
- export type ModelRef = {
27
- preset: string;
28
- } | {
29
- endpoint: string;
30
- model: string;
31
- apiKey: string;
32
- };
33
18
  /** Maximum lengths (characters) of the fields of a user `ModelRef`. */
34
19
  export declare const MODEL_REF_LIMITS: {
35
20
  readonly endpoint: 500;
@@ -45,4 +30,5 @@ export declare const modelRefSchema: z.ZodUnion<readonly [z.ZodObject<{
45
30
  apiKey: z.ZodString;
46
31
  }, z.core.$strip>]>;
47
32
  //#endregion
33
+ export type { ModelProfile, ModelRef };
48
34
  //# sourceMappingURL=models.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","names":[],"sources":["../../../../src/features/ai/protocol/models.ts"],"sourcesContent":["import type { MessageKey } from '../../../i18n'\nimport type { AiLanguage, ModelSlot } from '../../../settings'\nimport { z } from 'zod'\n\n/** Catalog keys of the label and description of each model slot for the settings UI. */\nexport const MODEL_SLOT_KEYS: Record<ModelSlot, { labelKey: MessageKey, descriptionKey: MessageKey }> = {\n complete: { labelKey: 'ai.slot.complete.label', descriptionKey: 'ai.slot.complete.description' },\n rewrite: { labelKey: 'ai.slot.rewrite.label', descriptionKey: 'ai.slot.rewrite.description' },\n generate: { labelKey: 'ai.slot.generate.label', descriptionKey: 'ai.slot.generate.description' },\n}\n\n/** Catalog keys of the display names of the stored target-language ids (`AI_LANGUAGES`); the stored id is what the model is told. */\nexport const AI_LANGUAGE_KEYS: Record<AiLanguage, MessageKey> = {\n Deutsch: 'ai.language.de',\n Englisch: 'ai.language.en',\n Französisch: 'ai.language.fr',\n Spanisch: 'ai.language.es',\n Italienisch: 'ai.language.it',\n}\n\n/** Profile as the client sees it; presets never expose their key. */\nexport interface ModelProfile { id: string, label: string, endpoint: string, model: string, source: 'preset' | 'user' }\n\n/** Browser-defined profile: its key is sent with each request and never stored on the server. */\nexport interface UserModelProfile extends Omit<ModelProfile, 'source'> { apiKey: string }\n\n/** Model an AI request runs on: a server preset by id, or an OpenAI-compatible endpoint with its own key. */\nexport type ModelRef = { preset: string } | { endpoint: string, model: string, apiKey: string }\n\n/** Maximum lengths (characters) of the fields of a user `ModelRef`. */\nexport const MODEL_REF_LIMITS = { endpoint: 500, model: 200, apiKey: 500 } as const\n\n/** Validates the `model` field every AI route requires in its body. */\nexport const modelRefSchema = z.union([\n z.object({ preset: z.string().min(1).max(100) }),\n z.object({\n endpoint: z.string().url().max(MODEL_REF_LIMITS.endpoint),\n model: z.string().min(1).max(MODEL_REF_LIMITS.model),\n apiKey: z.string().min(1).max(MODEL_REF_LIMITS.apiKey),\n }),\n])\n"],"mappings":";;;AAKA,MAAa,kBAA2F;CACtG,UAAU;EAAE,UAAU;EAA0B,gBAAgB;CAA+B;CAC/F,SAAS;EAAE,UAAU;EAAyB,gBAAgB;CAA8B;CAC5F,UAAU;EAAE,UAAU;EAA0B,gBAAgB;CAA+B;AACjG;;AAGA,MAAa,mBAAmD;CAC9D,SAAS;CACT,UAAU;CACV,aAAa;CACb,UAAU;CACV,aAAa;AACf;;AAYA,MAAa,mBAAmB;CAAE,UAAU;CAAK,OAAO;CAAK,QAAQ;AAAI;;AAGzE,MAAa,iBAAiB,EAAE,MAAM,CACpC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,GAC/C,EAAE,OAAO;CACP,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,iBAAiB,QAAQ;CACxD,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,KAAK;CACnD,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,MAAM;AACvD,CAAC,CACH,CAAC"}
1
+ {"version":3,"file":"models.js","names":[],"sources":["../../../../src/features/ai/protocol/models.ts"],"sourcesContent":["import type { ModelProfile, ModelRef } from '../../../core/backend-types'\nimport type { MessageKey } from '../../../i18n'\nimport type { AiLanguage, ModelSlot } from '../../../settings'\nimport { z } from 'zod'\n\n/** Catalog keys of the label and description of each model slot for the settings UI. */\nexport const MODEL_SLOT_KEYS: Record<ModelSlot, { labelKey: MessageKey, descriptionKey: MessageKey }> = {\n complete: { labelKey: 'ai.slot.complete.label', descriptionKey: 'ai.slot.complete.description' },\n rewrite: { labelKey: 'ai.slot.rewrite.label', descriptionKey: 'ai.slot.rewrite.description' },\n generate: { labelKey: 'ai.slot.generate.label', descriptionKey: 'ai.slot.generate.description' },\n}\n\n/** Catalog keys of the display names of the stored target-language ids (`AI_LANGUAGES`); the stored id is what the model is told. */\nexport const AI_LANGUAGE_KEYS: Record<AiLanguage, MessageKey> = {\n Deutsch: 'ai.language.de',\n Englisch: 'ai.language.en',\n Französisch: 'ai.language.fr',\n Spanisch: 'ai.language.es',\n Italienisch: 'ai.language.it',\n}\n\n// `ModelProfile` (a profile as the client sees it; presets never expose their key) and `ModelRef` (the model an AI\n// request runs on: a server preset by id, or an OpenAI-compatible endpoint with its own key) are core's, because\n// `EditorBackend.ai` names them; this entry exports them as before.\nexport type { ModelProfile, ModelRef }\n\n/** Browser-defined profile: its key is sent with each request and never stored on the server. */\nexport interface UserModelProfile extends Omit<ModelProfile, 'source'> { apiKey: string }\n\n/** Maximum lengths (characters) of the fields of a user `ModelRef`. */\nexport const MODEL_REF_LIMITS = { endpoint: 500, model: 200, apiKey: 500 } as const\n\n/** Validates the `model` field every AI route requires in its body. */\nexport const modelRefSchema = z.union([\n z.object({ preset: z.string().min(1).max(100) }),\n z.object({\n endpoint: z.string().url().max(MODEL_REF_LIMITS.endpoint),\n model: z.string().min(1).max(MODEL_REF_LIMITS.model),\n apiKey: z.string().min(1).max(MODEL_REF_LIMITS.apiKey),\n }),\n]) satisfies z.ZodType<ModelRef>\n"],"mappings":";;;AAMA,MAAa,kBAA2F;CACtG,UAAU;EAAE,UAAU;EAA0B,gBAAgB;CAA+B;CAC/F,SAAS;EAAE,UAAU;EAAyB,gBAAgB;CAA8B;CAC5F,UAAU;EAAE,UAAU;EAA0B,gBAAgB;CAA+B;AACjG;;AAGA,MAAa,mBAAmD;CAC9D,SAAS;CACT,UAAU;CACV,aAAa;CACb,UAAU;CACV,aAAa;AACf;;AAWA,MAAa,mBAAmB;CAAE,UAAU;CAAK,OAAO;CAAK,QAAQ;AAAI;;AAGzE,MAAa,iBAAiB,EAAE,MAAM,CACpC,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,GAC/C,EAAE,OAAO;CACP,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,iBAAiB,QAAQ;CACxD,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,KAAK;CACnD,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,iBAAiB,MAAM;AACvD,CAAC,CACH,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { Translate } from "../../i18n/index.js";
2
2
  import { Notifier } from "../../core/notify.js";
3
3
  //#region src/features/ai/rate-limit.d.ts
4
- /** True for an HTTP 429 from an AI endpoint: accepts a status code, a `fetch` Response or a `$fetch` error. */
4
+ /** True for an HTTP 429 from an AI endpoint: accepts a status code, a `BackendError`, a `fetch` Response or a `$fetch` error. */
5
5
  export declare function isRateLimited(source: unknown): boolean;
6
6
  /** Shows the "rate limited" warning notice (`ai.rateLimited`, in the language of `t`); repeated calls refresh one notice instead of stacking. */
7
7
  export declare function notifyAiRateLimited(notify: Notifier, t: Translate): void;
@@ -1,7 +1,9 @@
1
+ import { BackendError } from "../../core/backend.js";
1
2
  //#region src/features/ai/rate-limit.ts
2
- /** True for an HTTP 429 from an AI endpoint: accepts a status code, a `fetch` Response or a `$fetch` error. */
3
+ /** True for an HTTP 429 from an AI endpoint: accepts a status code, a `BackendError`, a `fetch` Response or a `$fetch` error. */
3
4
  function isRateLimited(source) {
4
5
  if (typeof source === "number") return source === 429;
6
+ if (source instanceof BackendError) return source.status === 429;
5
7
  const { status, statusCode } = source ?? {};
6
8
  return status === 429 || statusCode === 429;
7
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.js","names":[],"sources":["../../../src/features/ai/rate-limit.ts"],"sourcesContent":["import type { Translate } from '../../i18n'\nimport type { Notifier } from '../../core/notify'\n\n/** True for an HTTP 429 from an AI endpoint: accepts a status code, a `fetch` Response or a `$fetch` error. */\nexport function isRateLimited(source: unknown): boolean {\n if (typeof source === 'number') return source === 429\n const { status, statusCode } = (source ?? {}) as { status?: unknown, statusCode?: unknown }\n return status === 429 || statusCode === 429\n}\n\n/** Shows the \"rate limited\" warning notice (`ai.rateLimited`, in the language of `t`); repeated calls refresh one notice instead of stacking. */\nexport function notifyAiRateLimited(notify: Notifier, t: Translate): void {\n notify({ id: 'ai-rate-limited', title: t('ai.rateLimited'), kind: 'warning', icon: 'i-lucide-timer' })\n}\n"],"mappings":";;AAIA,SAAgB,cAAc,QAA0B;CACtD,IAAI,OAAO,WAAW,UAAU,OAAO,WAAW;CAClD,MAAM,EAAE,QAAQ,eAAgB,UAAU,CAAC;CAC3C,OAAO,WAAW,OAAO,eAAe;AAC1C;;AAGA,SAAgB,oBAAoB,QAAkB,GAAoB;CACxE,OAAO;EAAE,IAAI;EAAmB,OAAO,EAAE,gBAAgB;EAAG,MAAM;EAAW,MAAM;CAAiB,CAAC;AACvG"}
1
+ {"version":3,"file":"rate-limit.js","names":[],"sources":["../../../src/features/ai/rate-limit.ts"],"sourcesContent":["import { BackendError } from '../../core/backend'\nimport type { Translate } from '../../i18n'\nimport type { Notifier } from '../../core/notify'\n\n/** True for an HTTP 429 from an AI endpoint: accepts a status code, a `BackendError`, a `fetch` Response or a `$fetch` error. */\nexport function isRateLimited(source: unknown): boolean {\n if (typeof source === 'number') return source === 429\n if (source instanceof BackendError) return source.status === 429\n const { status, statusCode } = (source ?? {}) as { status?: unknown, statusCode?: unknown }\n return status === 429 || statusCode === 429\n}\n\n/** Shows the \"rate limited\" warning notice (`ai.rateLimited`, in the language of `t`); repeated calls refresh one notice instead of stacking. */\nexport function notifyAiRateLimited(notify: Notifier, t: Translate): void {\n notify({ id: 'ai-rate-limited', title: t('ai.rateLimited'), kind: 'warning', icon: 'i-lucide-timer' })\n}\n"],"mappings":";;;AAKA,SAAgB,cAAc,QAA0B;CACtD,IAAI,OAAO,WAAW,UAAU,OAAO,WAAW;CAClD,IAAI,kBAAkB,cAAc,OAAO,OAAO,WAAW;CAC7D,MAAM,EAAE,QAAQ,eAAgB,UAAU,CAAC;CAC3C,OAAO,WAAW,OAAO,eAAe;AAC1C;;AAGA,SAAgB,oBAAoB,QAAkB,GAAoB;CACxE,OAAO;EAAE,IAAI;EAAmB,OAAO,EAAE,gBAAgB;EAAG,MAAM;EAAW,MAAM;CAAiB,CAAC;AACvG"}
@@ -7,14 +7,14 @@ import { Icon } from "../../../react/Icon.js";
7
7
  import { useStore } from "../../../react/use-store.js";
8
8
  import { useEditorSettings } from "../../../react/settings.js";
9
9
  import { Button } from "../../../react/ui/button.js";
10
+ import { InputGroup, InputGroupAddon, InputGroupInput } from "../../../react/ui/input-group.js";
10
11
  import { aiSelectionHighlightKey } from "../ai-selection-highlight.js";
11
- import { AI_SELECTION_MENU_KEY } from "../selection-flow.js";
12
12
  import { Popover, PopoverAnchor, PopoverContent } from "../../../react/ui/popover.js";
13
13
  import { useNotifier } from "../../../react/use-notifier.js";
14
14
  import { Alert, AlertTitle } from "../../../react/ui/alert.js";
15
- import { InputGroup, InputGroupAddon, InputGroupInput } from "../../../react/ui/input-group.js";
16
15
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../react/ui/select.js";
17
16
  import { previewExtensions } from "../preview-extensions.js";
17
+ import { AI_SELECTION_MENU_KEY } from "../selection-flow.js";
18
18
  import { ModelQuickSwitch } from "./ModelQuickSwitch.js";
19
19
  import { useAiAssistant } from "./use-ai-assistant.js";
20
20
  import { useAiSelectionFlow } from "./use-ai-selection-flow.js";
@@ -6,16 +6,16 @@ import { cn } from "../../../react/ui/utils.js";
6
6
  import { Icon } from "../../../react/Icon.js";
7
7
  import { useStore } from "../../../react/use-store.js";
8
8
  import { Button } from "../../../react/ui/button.js";
9
- import { selectionScope } from "../agent-ops.js";
10
- import { createAiSidebarFlow } from "../sidebar-flow.js";
11
- import { useNotifier } from "../../../react/use-notifier.js";
12
- import { Alert, AlertTitle } from "../../../react/ui/alert.js";
13
9
  import { Input } from "../../../react/ui/input.js";
14
10
  import { Textarea } from "../../../react/ui/textarea.js";
11
+ import { useNotifier } from "../../../react/use-notifier.js";
12
+ import { Alert, AlertTitle } from "../../../react/ui/alert.js";
15
13
  import { ModelQuickSwitch } from "./ModelQuickSwitch.js";
16
14
  import { useAiAssistant } from "./use-ai-assistant.js";
17
15
  import { Badge } from "../../../react/ui/badge.js";
18
16
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../../react/ui/tooltip.js";
17
+ import { selectionScope } from "../agent-ops.js";
18
+ import { createAiSidebarFlow } from "../sidebar-flow.js";
19
19
  import { DocumentChatAnswer } from "./DocumentChatAnswer.js";
20
20
  import { DocumentChatOps } from "./DocumentChatOps.js";
21
21
  import { useDocumentChat } from "./use-document-chat.js";
@@ -1,8 +1,8 @@
1
1
  import { citationBlockId } from "../protocol/chat.js";
2
2
  import { useEditorI18n } from "../../../react/use-editor-i18n.js";
3
- import { documentChatAnswerContent } from "../document-chat.js";
4
3
  import { useNotifier } from "../../../react/use-notifier.js";
5
4
  import { previewExtensions } from "../preview-extensions.js";
5
+ import { documentChatAnswerContent } from "../document-chat.js";
6
6
  import { useEffect, useRef, useState } from "react";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { EditorContent, useEditor } from "@tiptap/react";
@@ -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
  //#region src/features/ai/react/DocumentChatOps.d.ts
7
7
  /** The edit operations of one chat answer with their review state; open ones can be decided right here. */
@@ -3,9 +3,9 @@ import { useEditorI18n } from "../../../react/use-editor-i18n.js";
3
3
  import { cn } from "../../../react/ui/utils.js";
4
4
  import { Icon } from "../../../react/Icon.js";
5
5
  import { Button } from "../../../react/ui/button.js";
6
- import { AGENT_OP_OUTCOME_KEYS } from "../agent-ops.js";
7
6
  import { Alert, AlertDescription, AlertTitle } from "../../../react/ui/alert.js";
8
7
  import { Badge } from "../../../react/ui/badge.js";
8
+ import { AGENT_OP_OUTCOME_KEYS } from "../agent-ops.js";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
10
  //#region src/features/ai/react/DocumentChatOps.tsx
11
11
  const ICONS = {
@@ -4,10 +4,10 @@ import { useEditorContext } from "../../../react/context.js";
4
4
  import { useEditorI18n } from "../../../react/use-editor-i18n.js";
5
5
  import { Icon } from "../../../react/Icon.js";
6
6
  import { Button } from "../../../react/ui/button.js";
7
- import { isRateLimited } from "../rate-limit.js";
8
- import { Alert, AlertTitle } from "../../../react/ui/alert.js";
9
7
  import { Input } from "../../../react/ui/input.js";
8
+ import { Alert, AlertTitle } from "../../../react/ui/alert.js";
10
9
  import { useModelProfiles } from "./use-model-profiles.js";
10
+ import { isRateLimited } from "../rate-limit.js";
11
11
  import { Label } from "../../../react/ui/label.js";
12
12
  import { Badge } from "../../../react/ui/badge.js";
13
13
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -86,20 +86,18 @@ function SettingsModelProfiles() {
86
86
  removeUserProfile(id);
87
87
  if (draft?.id === id) setDraft(null);
88
88
  }
89
- /** One short completion through the profile's endpoint (`POST /api/ai/complete`). */
89
+ /** One short completion through the profile's endpoint (`ai.complete`). */
90
90
  async function runTest() {
91
91
  if (!draftRef || !draftValid) return;
92
92
  const current = { state: "running" };
93
93
  latestTest.current = current;
94
94
  setTest(current);
95
95
  try {
96
- await app.fetch("/api/ai/complete", {
97
- method: "POST",
98
- body: {
99
- before: "Hallo",
100
- model: draftRef
101
- }
102
- });
96
+ if (!app.backend.ai) throw new Error(t("ai.sse.httpError", { status: 501 }));
97
+ await app.backend.ai.complete({
98
+ before: "Hallo",
99
+ model: draftRef
100
+ }, new AbortController().signal);
103
101
  if (latestTest.current === current) setTest({ state: "ok" });
104
102
  } catch (error) {
105
103
  if (latestTest.current === current) setTest({
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsModelProfiles.js","names":[],"sources":["../../../../src/features/ai/react/SettingsModelProfiles.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport { fetchErrorMessage } from '../../../core'\nimport { Icon } from '../../../react/Icon'\nimport { useEditorContext } from '../../../react/context'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { Alert, AlertTitle } from '../../../react/ui/alert'\nimport { Badge } from '../../../react/ui/badge'\nimport { Button } from '../../../react/ui/button'\nimport { Input } from '../../../react/ui/input'\nimport { Label } from '../../../react/ui/label'\nimport { modelRefSchema, type UserModelProfile } from '../protocol'\nimport { isRateLimited } from '../rate-limit'\nimport { useModelProfiles } from './use-model-profiles'\n\n/** Result of „Testen“; replaced (not mutated) per run, so a stale run recognizes it was superseded. */\ntype TestResult = { state: 'running' } | { state: 'ok' } | { state: 'error', message: string }\n\n/** A labelled form row (the counterpart of Nuxt UI's `UFormField`). */\nfunction Field({ label, description, required, htmlFor, children }: { label: string, description?: string, required?: boolean, htmlFor: string, children: React.ReactNode }) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={htmlFor}>\n {label}\n {required && <span className=\"text-destructive\">*</span>}\n </Label>\n {description && <p className=\"text-xs text-muted-foreground\">{description}</p>}\n {children}\n </div>\n )\n}\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 */\nexport function SettingsModelProfiles() {\n const app = useEditorContext()\n const { t } = useEditorI18n()\n const { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles()\n\n /** The profile being added or edited; null while the form is closed. */\n const [draft, setDraft] = useState<UserModelProfile | null>(null)\n const [test, setTest] = useState<TestResult | null>(null)\n // The result object of the latest run, so a stale run recognizes it was superseded.\n const latestTest = useRef<TestResult | null>(null)\n\n const draftRef = useMemo(() => draft && { endpoint: draft.endpoint.trim(), model: draft.model.trim(), apiKey: draft.apiKey.trim() }, [draft])\n const draftValid = !!draft?.label.trim() && modelRefSchema.safeParse(draftRef).success\n // A test result belongs to the values it ran with.\n const endpoint = draftRef?.endpoint\n const model = draftRef?.model\n const apiKey = draftRef?.apiKey\n useEffect(() => {\n latestTest.current = null\n setTest(null)\n }, [endpoint, model, apiKey])\n\n function edit(profile?: UserModelProfile) {\n setDraft(profile ? { ...profile } : { id: crypto.randomUUID(), label: '', endpoint: '', model: '', apiKey: '' })\n latestTest.current = null\n setTest(null)\n }\n\n function save() {\n if (!draft || !draftRef || !draftValid) return\n saveUserProfile({ id: draft.id, label: draft.label.trim(), ...draftRef })\n setDraft(null)\n }\n\n function remove(id: string) {\n removeUserProfile(id)\n if (draft?.id === id) setDraft(null)\n }\n\n /** One short completion through the profile's endpoint (`POST /api/ai/complete`). */\n async function runTest() {\n if (!draftRef || !draftValid) return\n const current: TestResult = { state: 'running' }\n latestTest.current = current\n setTest(current)\n try {\n // i18n-ignore: model-facing test prompt sent to the completion endpoint\n await app.fetch('/api/ai/complete', { method: 'POST', body: { before: 'Hallo', model: draftRef } })\n if (latestTest.current === current) setTest({ state: 'ok' })\n }\n catch (error) {\n if (latestTest.current === current) setTest({ state: 'error', message: isRateLimited(error) ? t('ai.rateLimited') : fetchErrorMessage(error) })\n }\n }\n\n const ids = { name: 'ai-profile-label', endpoint: 'ai-profile-endpoint', model: 'ai-profile-model', apiKey: 'ai-profile-key' }\n\n return (\n <div className=\"flex flex-col divide-y divide-border\">\n <div className=\"flex flex-col gap-2 py-3\">\n <p className=\"text-sm font-medium text-foreground\">{t('ai.profiles.server')}</p>\n {!presets.length\n ? <p className=\"text-sm text-muted-foreground\">{t('ai.profiles.noPresets')}</p>\n : (\n <ul className=\"flex flex-col divide-y divide-border rounded-md border border-border\">\n {presets.map(preset => (\n <li key={preset.id} className=\"flex items-center gap-3 px-3 py-2\">\n <Icon name=\"i-lucide-server\" className=\"text-muted-foreground\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{preset.label}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{preset.model} · {preset.endpoint}</p>\n </div>\n <Badge variant=\"secondary\">{t('ai.profiles.serverBadge')}</Badge>\n </li>\n ))}\n </ul>\n )}\n </div>\n\n <div className=\"flex flex-col gap-2 py-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-sm font-medium text-foreground\">{t('ai.profiles.own')}</p>\n {!draft && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => edit()}>\n <Icon name=\"i-lucide-plus\" />\n {t('ai.profiles.add')}\n </Button>\n )}\n </div>\n {userProfiles.length > 0 && (\n <ul className=\"flex flex-col divide-y divide-border rounded-md border border-border\">\n {userProfiles.map(profile => (\n <li key={profile.id} className=\"flex items-center gap-3 px-3 py-2\">\n <Icon name=\"i-lucide-key-round\" className=\"text-muted-foreground\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{profile.label}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{profile.model} · {profile.endpoint}</p>\n </div>\n <Button size=\"icon-sm\" variant=\"ghost\" aria-label={t('ai.profiles.edit')} onClick={() => edit(profile)}>\n <Icon name=\"i-lucide-pencil\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"ghost\" className=\"text-destructive\" aria-label={t('ai.profiles.delete')} onClick={() => remove(profile.id)}>\n <Icon name=\"i-lucide-trash-2\" />\n </Button>\n </li>\n ))}\n </ul>\n )}\n\n {draft && (\n <form\n className=\"flex flex-col gap-3 rounded-md border border-border p-3\"\n onSubmit={(event) => {\n event.preventDefault()\n save()\n }}\n >\n <Field label={t('ai.profiles.name')} required htmlFor={ids.name}>\n <Input id={ids.name} value={draft.label} placeholder={t('ai.profiles.namePlaceholder')} onChange={event => setDraft({ ...draft, label: event.target.value })} />\n </Field>\n <Field label={t('ai.profiles.endpoint')} description={t('ai.profiles.endpointHelp')} required htmlFor={ids.endpoint}>\n <Input id={ids.endpoint} value={draft.endpoint} placeholder={t('ai.profiles.endpointPlaceholder')} onChange={event => setDraft({ ...draft, endpoint: event.target.value })} />\n </Field>\n <Field label={t('ai.profiles.model')} required htmlFor={ids.model}>\n <Input id={ids.model} value={draft.model} placeholder={t('ai.profiles.modelPlaceholder')} onChange={event => setDraft({ ...draft, model: event.target.value })} />\n </Field>\n <Field label={t('ai.profiles.apiKey')} required htmlFor={ids.apiKey}>\n <Input id={ids.apiKey} type=\"password\" autoComplete=\"off\" value={draft.apiKey} onChange={event => setDraft({ ...draft, apiKey: event.target.value })} />\n </Field>\n {test?.state === 'ok' && (\n <Alert>\n <Icon name=\"i-lucide-check\" />\n <AlertTitle>{t('ai.profiles.testOk')}</AlertTitle>\n </Alert>\n )}\n {test?.state === 'error' && (\n <Alert variant=\"destructive\">\n <Icon name=\"i-lucide-circle-alert\" />\n <AlertTitle>{test.message}</AlertTitle>\n </Alert>\n )}\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button type=\"button\" variant=\"outline\" disabled={!draftValid || test?.state === 'running'} onClick={() => void runTest()}>\n <Icon name={test?.state === 'running' ? 'i-lucide-loader-circle' : 'i-lucide-plug-zap'} className={test?.state === 'running' ? 'animate-spin' : undefined} />\n {t('ai.profiles.test')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={() => setDraft(null)}>{t('ai.profiles.cancel')}</Button>\n <Button type=\"submit\" disabled={!draftValid}>{t('ai.profiles.save')}</Button>\n </div>\n </form>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SettingsModelProfiles.js","names":[],"sources":["../../../../src/features/ai/react/SettingsModelProfiles.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport { fetchErrorMessage } from '../../../core'\nimport { Icon } from '../../../react/Icon'\nimport { useEditorContext } from '../../../react/context'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { Alert, AlertTitle } from '../../../react/ui/alert'\nimport { Badge } from '../../../react/ui/badge'\nimport { Button } from '../../../react/ui/button'\nimport { Input } from '../../../react/ui/input'\nimport { Label } from '../../../react/ui/label'\nimport { modelRefSchema, type UserModelProfile } from '../protocol'\nimport { isRateLimited } from '../rate-limit'\nimport { useModelProfiles } from './use-model-profiles'\n\n/** Result of „Testen“; replaced (not mutated) per run, so a stale run recognizes it was superseded. */\ntype TestResult = { state: 'running' } | { state: 'ok' } | { state: 'error', message: string }\n\n/** A labelled form row (the counterpart of Nuxt UI's `UFormField`). */\nfunction Field({ label, description, required, htmlFor, children }: { label: string, description?: string, required?: boolean, htmlFor: string, children: React.ReactNode }) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={htmlFor}>\n {label}\n {required && <span className=\"text-destructive\">*</span>}\n </Label>\n {description && <p className=\"text-xs text-muted-foreground\">{description}</p>}\n {children}\n </div>\n )\n}\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 */\nexport function SettingsModelProfiles() {\n const app = useEditorContext()\n const { t } = useEditorI18n()\n const { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles()\n\n /** The profile being added or edited; null while the form is closed. */\n const [draft, setDraft] = useState<UserModelProfile | null>(null)\n const [test, setTest] = useState<TestResult | null>(null)\n // The result object of the latest run, so a stale run recognizes it was superseded.\n const latestTest = useRef<TestResult | null>(null)\n\n const draftRef = useMemo(() => draft && { endpoint: draft.endpoint.trim(), model: draft.model.trim(), apiKey: draft.apiKey.trim() }, [draft])\n const draftValid = !!draft?.label.trim() && modelRefSchema.safeParse(draftRef).success\n // A test result belongs to the values it ran with.\n const endpoint = draftRef?.endpoint\n const model = draftRef?.model\n const apiKey = draftRef?.apiKey\n useEffect(() => {\n latestTest.current = null\n setTest(null)\n }, [endpoint, model, apiKey])\n\n function edit(profile?: UserModelProfile) {\n setDraft(profile ? { ...profile } : { id: crypto.randomUUID(), label: '', endpoint: '', model: '', apiKey: '' })\n latestTest.current = null\n setTest(null)\n }\n\n function save() {\n if (!draft || !draftRef || !draftValid) return\n saveUserProfile({ id: draft.id, label: draft.label.trim(), ...draftRef })\n setDraft(null)\n }\n\n function remove(id: string) {\n removeUserProfile(id)\n if (draft?.id === id) setDraft(null)\n }\n\n /** One short completion through the profile's endpoint (`ai.complete`). */\n async function runTest() {\n if (!draftRef || !draftValid) return\n const current: TestResult = { state: 'running' }\n latestTest.current = current\n setTest(current)\n try {\n if (!app.backend.ai) throw new Error(t('ai.sse.httpError', { status: 501 }))\n // i18n-ignore: model-facing test prompt sent to the completion endpoint\n await app.backend.ai.complete({ before: 'Hallo', model: draftRef }, new AbortController().signal)\n if (latestTest.current === current) setTest({ state: 'ok' })\n }\n catch (error) {\n if (latestTest.current === current) setTest({ state: 'error', message: isRateLimited(error) ? t('ai.rateLimited') : fetchErrorMessage(error) })\n }\n }\n\n const ids = { name: 'ai-profile-label', endpoint: 'ai-profile-endpoint', model: 'ai-profile-model', apiKey: 'ai-profile-key' }\n\n return (\n <div className=\"flex flex-col divide-y divide-border\">\n <div className=\"flex flex-col gap-2 py-3\">\n <p className=\"text-sm font-medium text-foreground\">{t('ai.profiles.server')}</p>\n {!presets.length\n ? <p className=\"text-sm text-muted-foreground\">{t('ai.profiles.noPresets')}</p>\n : (\n <ul className=\"flex flex-col divide-y divide-border rounded-md border border-border\">\n {presets.map(preset => (\n <li key={preset.id} className=\"flex items-center gap-3 px-3 py-2\">\n <Icon name=\"i-lucide-server\" className=\"text-muted-foreground\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{preset.label}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{preset.model} · {preset.endpoint}</p>\n </div>\n <Badge variant=\"secondary\">{t('ai.profiles.serverBadge')}</Badge>\n </li>\n ))}\n </ul>\n )}\n </div>\n\n <div className=\"flex flex-col gap-2 py-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-sm font-medium text-foreground\">{t('ai.profiles.own')}</p>\n {!draft && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => edit()}>\n <Icon name=\"i-lucide-plus\" />\n {t('ai.profiles.add')}\n </Button>\n )}\n </div>\n {userProfiles.length > 0 && (\n <ul className=\"flex flex-col divide-y divide-border rounded-md border border-border\">\n {userProfiles.map(profile => (\n <li key={profile.id} className=\"flex items-center gap-3 px-3 py-2\">\n <Icon name=\"i-lucide-key-round\" className=\"text-muted-foreground\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium\">{profile.label}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{profile.model} · {profile.endpoint}</p>\n </div>\n <Button size=\"icon-sm\" variant=\"ghost\" aria-label={t('ai.profiles.edit')} onClick={() => edit(profile)}>\n <Icon name=\"i-lucide-pencil\" />\n </Button>\n <Button size=\"icon-sm\" variant=\"ghost\" className=\"text-destructive\" aria-label={t('ai.profiles.delete')} onClick={() => remove(profile.id)}>\n <Icon name=\"i-lucide-trash-2\" />\n </Button>\n </li>\n ))}\n </ul>\n )}\n\n {draft && (\n <form\n className=\"flex flex-col gap-3 rounded-md border border-border p-3\"\n onSubmit={(event) => {\n event.preventDefault()\n save()\n }}\n >\n <Field label={t('ai.profiles.name')} required htmlFor={ids.name}>\n <Input id={ids.name} value={draft.label} placeholder={t('ai.profiles.namePlaceholder')} onChange={event => setDraft({ ...draft, label: event.target.value })} />\n </Field>\n <Field label={t('ai.profiles.endpoint')} description={t('ai.profiles.endpointHelp')} required htmlFor={ids.endpoint}>\n <Input id={ids.endpoint} value={draft.endpoint} placeholder={t('ai.profiles.endpointPlaceholder')} onChange={event => setDraft({ ...draft, endpoint: event.target.value })} />\n </Field>\n <Field label={t('ai.profiles.model')} required htmlFor={ids.model}>\n <Input id={ids.model} value={draft.model} placeholder={t('ai.profiles.modelPlaceholder')} onChange={event => setDraft({ ...draft, model: event.target.value })} />\n </Field>\n <Field label={t('ai.profiles.apiKey')} required htmlFor={ids.apiKey}>\n <Input id={ids.apiKey} type=\"password\" autoComplete=\"off\" value={draft.apiKey} onChange={event => setDraft({ ...draft, apiKey: event.target.value })} />\n </Field>\n {test?.state === 'ok' && (\n <Alert>\n <Icon name=\"i-lucide-check\" />\n <AlertTitle>{t('ai.profiles.testOk')}</AlertTitle>\n </Alert>\n )}\n {test?.state === 'error' && (\n <Alert variant=\"destructive\">\n <Icon name=\"i-lucide-circle-alert\" />\n <AlertTitle>{test.message}</AlertTitle>\n </Alert>\n )}\n <div className=\"flex flex-wrap justify-end gap-2\">\n <Button type=\"button\" variant=\"outline\" disabled={!draftValid || test?.state === 'running'} onClick={() => void runTest()}>\n <Icon name={test?.state === 'running' ? 'i-lucide-loader-circle' : 'i-lucide-plug-zap'} className={test?.state === 'running' ? 'animate-spin' : undefined} />\n {t('ai.profiles.test')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={() => setDraft(null)}>{t('ai.profiles.cancel')}</Button>\n <Button type=\"submit\" disabled={!draftValid}>{t('ai.profiles.save')}</Button>\n </div>\n </form>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import "../index.js";
3
3
  import { Editor } from "@tiptap/core";
4
4
  //#region src/features/ai/react/use-ai-assistant.d.ts
5
5
  /**
6
- * Streams `/api/ai/generate` into component state, on the task's model slot (see `createAiAssistant`). A running
6
+ * Streams the backend's `generate` into component state, on the task's model slot (see `createAiAssistant`). A running
7
7
  * request is aborted when the component unmounts. The 429 notice goes to `editor`'s surface (else the app's notifier).
8
8
  */
9
9
  export declare function useAiAssistant(editor?: Editor): {
@@ -1,19 +1,19 @@
1
1
  import { useEditorContext } from "../../../react/context.js";
2
2
  import { useStore } from "../../../react/use-store.js";
3
- import { createAiAssistant } from "../assistant.js";
4
3
  import { modelProfiles } from "../model-profiles.js";
5
4
  import { useNotifier } from "../../../react/use-notifier.js";
5
+ import { createAiAssistant } from "../assistant.js";
6
6
  import { useEffect, useMemo } from "react";
7
7
  //#region src/features/ai/react/use-ai-assistant.ts
8
8
  /**
9
- * Streams `/api/ai/generate` into component state, on the task's model slot (see `createAiAssistant`). A running
9
+ * Streams the backend's `generate` into component state, on the task's model slot (see `createAiAssistant`). A running
10
10
  * request is aborted when the component unmounts. The 429 notice goes to `editor`'s surface (else the app's notifier).
11
11
  */
12
12
  function useAiAssistant(editor) {
13
13
  const app = useEditorContext();
14
14
  const notify = useNotifier(editor);
15
15
  const assistant = useMemo(() => createAiAssistant({
16
- fetch: app.fetch,
16
+ ai: app.backend.ai,
17
17
  modelRef: modelProfiles(app).modelRef,
18
18
  notify,
19
19
  t: app.t
@@ -1 +1 @@
1
- {"version":3,"file":"use-ai-assistant.js","names":[],"sources":["../../../../src/features/ai/react/use-ai-assistant.ts"],"sourcesContent":["import { useEffect, useMemo } from 'react'\nimport { useEditorContext } from '../../../react/context'\nimport { useStore } from '../../../react/use-store'\nimport { type AiRunBody, type AiStatus, createAiAssistant } from '../assistant'\nimport { modelProfiles } from '../model-profiles'\nimport { useNotifier } from '../../../react/use-notifier'\nimport type { Editor } from '@tiptap/core'\n\nexport type { AiRunBody, AiStatus }\n\n/**\n * Streams `/api/ai/generate` into component state, on the task's model slot (see `createAiAssistant`). A running\n * request is aborted when the component unmounts. The 429 notice goes to `editor`'s surface (else the app's notifier).\n */\nexport function useAiAssistant(editor?: Editor) {\n const app = useEditorContext()\n const notify = useNotifier(editor)\n const assistant = useMemo(\n () => createAiAssistant({ fetch: app.fetch, modelRef: modelProfiles(app).modelRef, notify, t: app.t }),\n [app, notify],\n )\n useEffect(() => () => assistant.abort(), [assistant])\n const state = useStore(assistant.state)\n /** `getState` reads the current state after an await (a render's `state` would be stale). */\n return { model: assistant, ...state, getState: assistant.state.get, run: assistant.run, abort: assistant.abort, reset: assistant.reset, clearError: assistant.clearError }\n}\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,eAAe,QAAiB;CAC9C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,SAAS,YAAY,MAAM;CACjC,MAAM,YAAY,cACV,kBAAkB;EAAE,OAAO,IAAI;EAAO,UAAU,cAAc,GAAG,CAAC,CAAC;EAAU;EAAQ,GAAG,IAAI;CAAE,CAAC,GACrG,CAAC,KAAK,MAAM,CACd;CACA,sBAAsB,UAAU,MAAM,GAAG,CAAC,SAAS,CAAC;;CAGpD,OAAO;EAAE,OAAO;EAAW,GAFb,SAAS,UAAU,KAEC;EAAG,UAAU,UAAU,MAAM;EAAK,KAAK,UAAU;EAAK,OAAO,UAAU;EAAO,OAAO,UAAU;EAAO,YAAY,UAAU;CAAW;AAC3K"}
1
+ {"version":3,"file":"use-ai-assistant.js","names":[],"sources":["../../../../src/features/ai/react/use-ai-assistant.ts"],"sourcesContent":["import { useEffect, useMemo } from 'react'\nimport { useEditorContext } from '../../../react/context'\nimport { useStore } from '../../../react/use-store'\nimport { type AiRunBody, type AiStatus, createAiAssistant } from '../assistant'\nimport { modelProfiles } from '../model-profiles'\nimport { useNotifier } from '../../../react/use-notifier'\nimport type { Editor } from '@tiptap/core'\n\nexport type { AiRunBody, AiStatus }\n\n/**\n * Streams the backend's `generate` into component state, on the task's model slot (see `createAiAssistant`). A running\n * request is aborted when the component unmounts. The 429 notice goes to `editor`'s surface (else the app's notifier).\n */\nexport function useAiAssistant(editor?: Editor) {\n const app = useEditorContext()\n const notify = useNotifier(editor)\n const assistant = useMemo(\n () => createAiAssistant({ ai: app.backend.ai, modelRef: modelProfiles(app).modelRef, notify, t: app.t }),\n [app, notify],\n )\n useEffect(() => () => assistant.abort(), [assistant])\n const state = useStore(assistant.state)\n /** `getState` reads the current state after an await (a render's `state` would be stale). */\n return { model: assistant, ...state, getState: assistant.state.get, run: assistant.run, abort: assistant.abort, reset: assistant.reset, clearError: assistant.clearError }\n}\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,eAAe,QAAiB;CAC9C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,SAAS,YAAY,MAAM;CACjC,MAAM,YAAY,cACV,kBAAkB;EAAE,IAAI,IAAI,QAAQ;EAAI,UAAU,cAAc,GAAG,CAAC,CAAC;EAAU;EAAQ,GAAG,IAAI;CAAE,CAAC,GACvG,CAAC,KAAK,MAAM,CACd;CACA,sBAAsB,UAAU,MAAM,GAAG,CAAC,SAAS,CAAC;;CAGpD,OAAO;EAAE,OAAO;EAAW,GAFb,SAAS,UAAU,KAEC;EAAG,UAAU,UAAU,MAAM;EAAK,KAAK,UAAU;EAAK,OAAO,UAAU;EAAO,OAAO,UAAU;EAAO,YAAY,UAAU;CAAW;AAC3K"}
@@ -1,8 +1,8 @@
1
1
  import { useEditorContext } from "../../../react/context.js";
2
2
  import { useStore } from "../../../react/use-store.js";
3
- import { createDocumentChat } from "../document-chat-model.js";
4
3
  import { modelProfiles } from "../model-profiles.js";
5
4
  import { useNotifier } from "../../../react/use-notifier.js";
5
+ import { createDocumentChat } from "../document-chat-model.js";
6
6
  import { useEffect, useMemo } from "react";
7
7
  //#region src/features/ai/react/use-document-chat.ts
8
8
  /**
@@ -14,7 +14,7 @@ function useDocumentChat(editor) {
14
14
  const app = useEditorContext();
15
15
  const notify = useNotifier(editor);
16
16
  const chat = useMemo(() => createDocumentChat({
17
- fetch: app.fetch,
17
+ ai: app.backend.ai,
18
18
  modelRef: modelProfiles(app).modelRef,
19
19
  notify,
20
20
  t: app.t
@@ -1 +1 @@
1
- {"version":3,"file":"use-document-chat.js","names":[],"sources":["../../../../src/features/ai/react/use-document-chat.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { useEffect, useMemo } from 'react'\nimport { useEditorContext } from '../../../react/context'\nimport { useStore } from '../../../react/use-store'\nimport { createDocumentChat } from '../document-chat-model'\nimport { modelProfiles } from '../model-profiles'\nimport { useNotifier } from '../../../react/use-notifier'\n\n/**\n * Document chat state for a component (see `createDocumentChat`): re-renders as the conversation streams in. A running\n * answer is aborted when the component unmounts; hosts that want the conversation to survive closing keep the\n * component mounted (hidden). The 429 notice goes to `editor`'s surface (else the app's notifier).\n */\nexport function useDocumentChat(editor?: Editor) {\n const app = useEditorContext()\n const notify = useNotifier(editor)\n const chat = useMemo(\n () => createDocumentChat({ fetch: app.fetch, modelRef: modelProfiles(app).modelRef, notify, t: app.t }),\n [app, notify],\n )\n useEffect(() => () => chat.abort(), [chat])\n const state = useStore(chat.state)\n /** `getState` reads the current state after an await (a render's `state` would be stale). */\n return {\n /** The underlying model, for flows that act on it (`createAiSidebarFlow`). */\n model: chat,\n ...state,\n getState: chat.state.get,\n ask: chat.ask,\n abort: chat.abort,\n reset: chat.reset,\n decide: chat.decide,\n reveal: chat.reveal,\n addGeneration: chat.addGeneration,\n updateGeneration: chat.updateGeneration,\n removeGeneration: chat.removeGeneration,\n clearError: chat.clearError,\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,gBAAgB,QAAiB;CAC/C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,SAAS,YAAY,MAAM;CACjC,MAAM,OAAO,cACL,mBAAmB;EAAE,OAAO,IAAI;EAAO,UAAU,cAAc,GAAG,CAAC,CAAC;EAAU;EAAQ,GAAG,IAAI;CAAE,CAAC,GACtG,CAAC,KAAK,MAAM,CACd;CACA,sBAAsB,KAAK,MAAM,GAAG,CAAC,IAAI,CAAC;;CAG1C,OAAO;;EAEL,OAAO;EACP,GALY,SAAS,KAAK,KAKnB;EACP,UAAU,KAAK,MAAM;EACrB,KAAK,KAAK;EACV,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,eAAe,KAAK;EACpB,kBAAkB,KAAK;EACvB,kBAAkB,KAAK;EACvB,YAAY,KAAK;CACnB;AACF"}
1
+ {"version":3,"file":"use-document-chat.js","names":[],"sources":["../../../../src/features/ai/react/use-document-chat.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { useEffect, useMemo } from 'react'\nimport { useEditorContext } from '../../../react/context'\nimport { useStore } from '../../../react/use-store'\nimport { createDocumentChat } from '../document-chat-model'\nimport { modelProfiles } from '../model-profiles'\nimport { useNotifier } from '../../../react/use-notifier'\n\n/**\n * Document chat state for a component (see `createDocumentChat`): re-renders as the conversation streams in. A running\n * answer is aborted when the component unmounts; hosts that want the conversation to survive closing keep the\n * component mounted (hidden). The 429 notice goes to `editor`'s surface (else the app's notifier).\n */\nexport function useDocumentChat(editor?: Editor) {\n const app = useEditorContext()\n const notify = useNotifier(editor)\n const chat = useMemo(\n () => createDocumentChat({ ai: app.backend.ai, modelRef: modelProfiles(app).modelRef, notify, t: app.t }),\n [app, notify],\n )\n useEffect(() => () => chat.abort(), [chat])\n const state = useStore(chat.state)\n /** `getState` reads the current state after an await (a render's `state` would be stale). */\n return {\n /** The underlying model, for flows that act on it (`createAiSidebarFlow`). */\n model: chat,\n ...state,\n getState: chat.state.get,\n ask: chat.ask,\n abort: chat.abort,\n reset: chat.reset,\n decide: chat.decide,\n reveal: chat.reveal,\n addGeneration: chat.addGeneration,\n updateGeneration: chat.updateGeneration,\n removeGeneration: chat.removeGeneration,\n clearError: chat.clearError,\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,gBAAgB,QAAiB;CAC/C,MAAM,MAAM,iBAAiB;CAC7B,MAAM,SAAS,YAAY,MAAM;CACjC,MAAM,OAAO,cACL,mBAAmB;EAAE,IAAI,IAAI,QAAQ;EAAI,UAAU,cAAc,GAAG,CAAC,CAAC;EAAU;EAAQ,GAAG,IAAI;CAAE,CAAC,GACxG,CAAC,KAAK,MAAM,CACd;CACA,sBAAsB,KAAK,MAAM,GAAG,CAAC,IAAI,CAAC;;CAG1C,OAAO;;EAEL,OAAO;EACP,GALY,SAAS,KAAK,KAKnB;EACP,UAAU,KAAK,MAAM;EACrB,KAAK,KAAK;EACV,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,eAAe,KAAK;EACpB,kBAAkB,KAAK;EACvB,kBAAkB,KAAK;EACvB,YAAY,KAAK;CACnB;AACF"}
@@ -1,6 +1,7 @@
1
1
  import { ModelSlot } from "../../../settings/values.js";
2
2
  import "../../../settings/index.js";
3
- import { ModelProfile, ModelRef, UserModelProfile } from "../protocol/models.js";
3
+ import { ModelProfile, ModelRef } from "../../../core/backend-types.js";
4
+ import { UserModelProfile } from "../protocol/models.js";
4
5
  import "../protocol/index.js";
5
6
  //#region src/features/ai/react/use-model-profiles.d.ts
6
7
  /**
@@ -1,8 +1,8 @@
1
+ import { ReadonlyStore } from "../../core/store.js";
1
2
  import { Translate } from "../../i18n/index.js";
2
3
  import { AiLanguage } from "../../settings/values.js";
3
4
  import "../../settings/index.js";
4
5
  import { Notifier } from "../../core/notify.js";
5
- import { ReadonlyStore } from "../../core/store.js";
6
6
  import { AiTask } from "./protocol/tasks.js";
7
7
  import "./protocol/index.js";
8
8
  import { AiResultAction } from "./ai-apply.js";