@ecsplico/editor 3.5.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (583) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/dist/adapters/direct-ai.d.ts +4 -4
  3. package/dist/adapters/direct-ai.js +2 -2
  4. package/dist/adapters/direct-ai.js.map +1 -1
  5. package/dist/adapters/document-state.d.ts +1 -1
  6. package/dist/adapters/document-state.js.map +1 -1
  7. package/dist/adapters/forge.d.ts +2 -2
  8. package/dist/adapters/forge.js.map +1 -1
  9. package/dist/adapters/nitro.d.ts +11 -3
  10. package/dist/adapters/nitro.js +3 -1
  11. package/dist/adapters/nitro.js.map +1 -1
  12. package/dist/adapters/shared.d.ts +6 -6
  13. package/dist/adapters/shared.js.map +1 -1
  14. package/dist/code/CodeSurface.vue.d.ts +3 -3
  15. package/dist/config/index.d.ts +14 -14
  16. package/dist/config/index.js +8 -8
  17. package/dist/config/index.js.map +1 -1
  18. package/dist/core/backend-types.d.ts +3 -3
  19. package/dist/core/backend-types.js.map +1 -1
  20. package/dist/core/backend.d.ts +4 -4
  21. package/dist/core/backend.js.map +1 -1
  22. package/dist/core/block-convert.d.ts +1 -1
  23. package/dist/core/block-convert.js +5 -5
  24. package/dist/core/block-convert.js.map +1 -1
  25. package/dist/core/block-transfer.d.ts +8 -4
  26. package/dist/core/block-transfer.js +23 -14
  27. package/dist/core/block-transfer.js.map +1 -1
  28. package/dist/core/capabilities.d.ts +113 -0
  29. package/dist/core/capabilities.js +155 -0
  30. package/dist/core/capabilities.js.map +1 -0
  31. package/dist/core/command-types.d.ts +1 -1
  32. package/dist/core/context.js.map +1 -1
  33. package/dist/core/convert.d.ts +5 -5
  34. package/dist/core/convert.js +5 -5
  35. package/dist/core/convert.js.map +1 -1
  36. package/dist/core/core-feature.js +1 -1
  37. package/dist/core/define-editor.d.ts +2 -2
  38. package/dist/core/define-editor.js +34 -6
  39. package/dist/core/define-editor.js.map +1 -1
  40. package/dist/core/derivation.d.ts +119 -0
  41. package/dist/core/derivation.js +187 -0
  42. package/dist/core/derivation.js.map +1 -0
  43. package/dist/core/derive.d.ts +61 -0
  44. package/dist/core/derive.js +69 -0
  45. package/dist/core/derive.js.map +1 -0
  46. package/dist/core/extension-points.d.ts +16 -0
  47. package/dist/core/feature-context.d.ts +5 -1
  48. package/dist/core/feature-context.js +10 -3
  49. package/dist/core/feature-context.js.map +1 -1
  50. package/dist/core/feature.d.ts +41 -19
  51. package/dist/core/feature.js.map +1 -1
  52. package/dist/core/fences.js +1 -1
  53. package/dist/core/fences.js.map +1 -1
  54. package/dist/core/index.d.ts +6 -2
  55. package/dist/core/index.js +5 -1
  56. package/dist/core/pipeline.d.ts +14 -5
  57. package/dist/core/pipeline.js +19 -7
  58. package/dist/core/pipeline.js.map +1 -1
  59. package/dist/core/properties.d.ts +22 -0
  60. package/dist/core/properties.js +17 -0
  61. package/dist/core/properties.js.map +1 -0
  62. package/dist/core/schema/block-ids.js +5 -5
  63. package/dist/core/schema/block-ids.js.map +1 -1
  64. package/dist/core/schema/index.d.ts +2 -1
  65. package/dist/core/schema/index.js +2 -1
  66. package/dist/core/schema/index.js.map +1 -1
  67. package/dist/core/schema/opaque-fence.d.ts +4 -4
  68. package/dist/core/schema/opaque-fence.js +7 -7
  69. package/dist/core/schema/opaque-fence.js.map +1 -1
  70. package/dist/core/schema/persisted-names.d.ts +58 -0
  71. package/dist/core/schema/persisted-names.js +96 -0
  72. package/dist/core/schema/persisted-names.js.map +1 -0
  73. package/dist/core/toolbar-items.js +1 -1
  74. package/dist/core/toolbar-items.js.map +1 -1
  75. package/dist/core/util.js +5 -1
  76. package/dist/core/util.js.map +1 -1
  77. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  78. package/dist/features/ai/agent-ops.js +1 -1
  79. package/dist/features/ai/ai-review.js +1 -1
  80. package/dist/features/ai/assistant.d.ts +4 -4
  81. package/dist/features/ai/assistant.js +1 -1
  82. package/dist/features/ai/assistant.js.map +1 -1
  83. package/dist/features/ai/components/AiSelectionMenu.js +1 -1
  84. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
  85. package/dist/features/ai/components/ModelQuickSwitch.js +7 -7
  86. package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -1
  87. package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +4 -4
  88. package/dist/features/ai/components/SettingsAiTab.js +8 -8
  89. package/dist/features/ai/components/SettingsAiTab.js.map +1 -1
  90. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  91. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
  92. package/dist/features/ai/document-chat-model.d.ts +2 -2
  93. package/dist/features/ai/document-chat-model.js +5 -5
  94. package/dist/features/ai/document-chat-model.js.map +1 -1
  95. package/dist/features/ai/document-chat.js +2 -2
  96. package/dist/features/ai/document-chat.js.map +1 -1
  97. package/dist/features/ai/feature.d.ts +2 -2
  98. package/dist/features/ai/feature.js +2 -2
  99. package/dist/features/ai/feature.js.map +1 -1
  100. package/dist/features/ai/ghost-autocomplete.d.ts +4 -4
  101. package/dist/features/ai/ghost-autocomplete.js +1 -1
  102. package/dist/features/ai/ghost-autocomplete.js.map +1 -1
  103. package/dist/features/ai/index.d.ts +3 -3
  104. package/dist/features/ai/index.js +2 -2
  105. package/dist/features/ai/model-profiles.d.ts +9 -9
  106. package/dist/features/ai/model-profiles.js +13 -13
  107. package/dist/features/ai/model-profiles.js.map +1 -1
  108. package/dist/features/ai/protocol/agent-ops.d.ts +2 -2
  109. package/dist/features/ai/protocol/agent-ops.js +1 -1
  110. package/dist/features/ai/protocol/agent-ops.js.map +1 -1
  111. package/dist/features/ai/protocol/block-lines.d.ts +7 -7
  112. package/dist/features/ai/protocol/block-lines.js +10 -10
  113. package/dist/features/ai/protocol/block-lines.js.map +1 -1
  114. package/dist/features/ai/protocol/chat.d.ts +5 -5
  115. package/dist/features/ai/protocol/chat.js +12 -12
  116. package/dist/features/ai/protocol/chat.js.map +1 -1
  117. package/dist/features/ai/protocol/generate.d.ts +1 -1
  118. package/dist/features/ai/protocol/generate.js +1 -1
  119. package/dist/features/ai/protocol/generate.js.map +1 -1
  120. package/dist/features/ai/protocol/index.d.ts +3 -3
  121. package/dist/features/ai/protocol/index.js +2 -2
  122. package/dist/features/ai/protocol/models.d.ts +3 -3
  123. package/dist/features/ai/protocol/models.js +3 -3
  124. package/dist/features/ai/protocol/models.js.map +1 -1
  125. package/dist/features/ai/protocol/schemas.d.ts +1 -1
  126. package/dist/features/ai/protocol/schemas.js +1 -1
  127. package/dist/features/ai/protocol/schemas.js.map +1 -1
  128. package/dist/features/ai/protocol/tasks.d.ts +5 -5
  129. package/dist/features/ai/protocol/tasks.js +12 -12
  130. package/dist/features/ai/protocol/tasks.js.map +1 -1
  131. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
  132. package/dist/features/ai/react/ModelQuickSwitch.d.ts +4 -4
  133. package/dist/features/ai/react/ModelQuickSwitch.js +6 -6
  134. package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -1
  135. package/dist/features/ai/react/SettingsAiTab.d.ts +1 -1
  136. package/dist/features/ai/react/SettingsAiTab.js +8 -8
  137. package/dist/features/ai/react/SettingsAiTab.js.map +1 -1
  138. package/dist/features/ai/react/SettingsModelProfiles.d.ts +1 -1
  139. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  140. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
  141. package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
  142. package/dist/features/ai/react/use-ai-assistant.js +1 -1
  143. package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
  144. package/dist/features/ai/react/use-model-profiles.d.ts +5 -5
  145. package/dist/features/ai/react/use-model-profiles.js +2 -2
  146. package/dist/features/ai/react/use-model-profiles.js.map +1 -1
  147. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  148. package/dist/features/ai/use-ai-assistant.js +1 -1
  149. package/dist/features/ai/use-ai-assistant.js.map +1 -1
  150. package/dist/features/ai/use-model-profiles.d.ts +4 -4
  151. package/dist/features/ai/use-model-profiles.js +2 -2
  152. package/dist/features/ai/use-model-profiles.js.map +1 -1
  153. package/dist/features/block-ui/block-actions.d.ts +8 -4
  154. package/dist/features/block-ui/block-actions.js +38 -22
  155. package/dist/features/block-ui/block-actions.js.map +1 -1
  156. package/dist/features/block-ui/block-target.d.ts +29 -0
  157. package/dist/features/block-ui/block-target.js +63 -0
  158. package/dist/features/block-ui/block-target.js.map +1 -0
  159. package/dist/features/block-ui/components/BlockHandles.js +3 -3
  160. package/dist/features/block-ui/components/BlockHandles.js.map +1 -1
  161. package/dist/features/block-ui/components/BlockUi.js +11 -23
  162. package/dist/features/block-ui/components/BlockUi.js.map +1 -1
  163. package/dist/features/block-ui/feature.d.ts +3 -2
  164. package/dist/features/block-ui/feature.js +3 -2
  165. package/dist/features/block-ui/feature.js.map +1 -1
  166. package/dist/features/block-ui/handle-geometry.js +12 -23
  167. package/dist/features/block-ui/handle-geometry.js.map +1 -1
  168. package/dist/features/block-ui/index.d.ts +2 -1
  169. package/dist/features/block-ui/index.js +2 -1
  170. package/dist/features/block-ui/react/BlockHandles.d.ts +1 -1
  171. package/dist/features/block-ui/react/BlockHandles.js +4 -4
  172. package/dist/features/block-ui/react/BlockHandles.js.map +1 -1
  173. package/dist/features/block-ui/react/BlockUi.js +13 -25
  174. package/dist/features/block-ui/react/BlockUi.js.map +1 -1
  175. package/dist/features/comments/model.js +1 -1
  176. package/dist/features/composites/accepted-children.d.ts +5 -4
  177. package/dist/features/composites/accepted-children.js +26 -26
  178. package/dist/features/composites/accepted-children.js.map +1 -1
  179. package/dist/features/composites/ai-brief.d.ts +4 -4
  180. package/dist/features/composites/ai-brief.js +14 -12
  181. package/dist/features/composites/ai-brief.js.map +1 -1
  182. package/dist/features/composites/anchors.d.ts +1 -1
  183. package/dist/features/composites/anchors.js.map +1 -1
  184. package/dist/features/composites/baskets.d.ts +1 -1
  185. package/dist/features/composites/baskets.js.map +1 -1
  186. package/dist/features/composites/bibliography-command.d.ts +1 -1
  187. package/dist/features/composites/bibliography-form.js +1 -1
  188. package/dist/features/composites/bibliography-form.js.map +1 -1
  189. package/dist/features/composites/bibliography.js.map +1 -1
  190. package/dist/features/composites/block-types-json.d.ts +38 -0
  191. package/dist/features/composites/block-types-json.js +122 -0
  192. package/dist/features/composites/block-types-json.js.map +1 -0
  193. package/dist/features/composites/block-types.d.ts +119 -0
  194. package/dist/features/composites/block-types.js +343 -0
  195. package/dist/features/composites/block-types.js.map +1 -0
  196. package/dist/features/composites/capabilities.js +109 -0
  197. package/dist/features/composites/capabilities.js.map +1 -0
  198. package/dist/features/composites/components/BibliographyEditor.js +5 -5
  199. package/dist/features/composites/components/BibliographyEditor.js.map +1 -1
  200. package/dist/features/composites/components/CompositeBasketMenu.js +2 -2
  201. package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -1
  202. package/dist/features/composites/components/CompositeChrome.js +18 -8
  203. package/dist/features/composites/components/CompositeChrome.js.map +1 -1
  204. package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
  205. package/dist/features/composites/components/CompositeDetails.js +11 -10
  206. package/dist/features/composites/components/CompositeDetails.js.map +1 -1
  207. package/dist/features/composites/components/CompositeDetails.vue.d.ts +1 -1
  208. package/dist/features/composites/components/CompositeFieldEditor.js +7 -7
  209. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
  210. package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +5 -5
  211. package/dist/features/composites/components/CompositeInspector.js +136 -113
  212. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  213. package/dist/features/composites/components/CompositeInspector.vue.d.ts +3 -3
  214. package/dist/features/composites/components/CompositeStatusMenu.js +2 -2
  215. package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -1
  216. package/dist/features/composites/components/CompositeSummaryEditor.js +1 -1
  217. package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -1
  218. package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +1 -1
  219. package/dist/features/composites/components/CompositeTable.js +2 -2
  220. package/dist/features/composites/components/CompositeTable.js.map +1 -1
  221. package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
  222. package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -1
  223. package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +1 -1
  224. package/dist/features/composites/components/CompositeView.js +19 -17
  225. package/dist/features/composites/components/CompositeView.js.map +1 -1
  226. package/dist/features/composites/components/DerivationBadge.js +15 -6
  227. package/dist/features/composites/components/DerivationBadge.js.map +1 -1
  228. package/dist/features/composites/components/FocusChip.js +1 -1
  229. package/dist/features/composites/components/FocusChip.js.map +1 -1
  230. package/dist/features/composites/components/FocusChip.vue.d.ts +1 -1
  231. package/dist/features/composites/components/ReferenceFieldSelect.js +3 -3
  232. package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -1
  233. package/dist/features/composites/components/ReferenceView.js +1 -1
  234. package/dist/features/composites/components/ReferenceView.js.map +1 -1
  235. package/dist/features/composites/components/SettingsEditingTab.js +18 -18
  236. package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
  237. package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +3 -3
  238. package/dist/features/composites/convert.d.ts +4 -4
  239. package/dist/features/composites/convert.js +27 -28
  240. package/dist/features/composites/convert.js.map +1 -1
  241. package/dist/features/composites/derived-chrome.js +34 -28
  242. package/dist/features/composites/derived-chrome.js.map +1 -1
  243. package/dist/features/composites/editing-settings-keys.js +6 -6
  244. package/dist/features/composites/editing-settings-keys.js.map +1 -1
  245. package/dist/features/composites/feature.d.ts +10 -10
  246. package/dist/features/composites/feature.js +58 -46
  247. package/dist/features/composites/feature.js.map +1 -1
  248. package/dist/features/composites/fences.d.ts +3 -3
  249. package/dist/features/composites/fences.js +18 -13
  250. package/dist/features/composites/fences.js.map +1 -1
  251. package/dist/features/composites/focus.d.ts +10 -10
  252. package/dist/features/composites/focus.js +11 -11
  253. package/dist/features/composites/focus.js.map +1 -1
  254. package/dist/features/composites/heading-composites.d.ts +5 -5
  255. package/dist/features/composites/heading-composites.js +13 -16
  256. package/dist/features/composites/heading-composites.js.map +1 -1
  257. package/dist/features/composites/index.d.ts +20 -18
  258. package/dist/features/composites/index.js +9 -7
  259. package/dist/features/composites/inspector-model.d.ts +45 -22
  260. package/dist/features/composites/inspector-model.js +53 -14
  261. package/dist/features/composites/inspector-model.js.map +1 -1
  262. package/dist/features/composites/legacy.d.ts +5 -4
  263. package/dist/features/composites/legacy.js +16 -23
  264. package/dist/features/composites/legacy.js.map +1 -1
  265. package/dist/features/composites/markdown-export.js +3 -3
  266. package/dist/features/composites/markdown-export.js.map +1 -1
  267. package/dist/features/composites/merge.d.ts +8 -7
  268. package/dist/features/composites/merge.js +39 -12
  269. package/dist/features/composites/merge.js.map +1 -1
  270. package/dist/features/composites/meta-display.js +5 -5
  271. package/dist/features/composites/meta-display.js.map +1 -1
  272. package/dist/features/composites/meta-keys.d.ts +5 -5
  273. package/dist/features/composites/meta-keys.js +8 -8
  274. package/dist/features/composites/meta-keys.js.map +1 -1
  275. package/dist/features/composites/node-views.d.ts +3 -3
  276. package/dist/features/composites/node-views.js +2 -2
  277. package/dist/features/composites/node-views.js.map +1 -1
  278. package/dist/features/composites/nodes.d.ts +13 -13
  279. package/dist/features/composites/nodes.js +100 -77
  280. package/dist/features/composites/nodes.js.map +1 -1
  281. package/dist/features/composites/outline.d.ts +15 -12
  282. package/dist/features/composites/outline.js +24 -21
  283. package/dist/features/composites/outline.js.map +1 -1
  284. package/dist/features/composites/properties.d.ts +51 -0
  285. package/dist/features/composites/properties.js +89 -0
  286. package/dist/features/composites/properties.js.map +1 -0
  287. package/dist/features/composites/react/BibliographyEditor.js +5 -5
  288. package/dist/features/composites/react/BibliographyEditor.js.map +1 -1
  289. package/dist/features/composites/react/CompositeBasketMenu.js +2 -2
  290. package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -1
  291. package/dist/features/composites/react/CompositeChrome.d.ts +1 -1
  292. package/dist/features/composites/react/CompositeChrome.js +15 -6
  293. package/dist/features/composites/react/CompositeChrome.js.map +1 -1
  294. package/dist/features/composites/react/CompositeDetails.d.ts +1 -1
  295. package/dist/features/composites/react/CompositeDetails.js +11 -10
  296. package/dist/features/composites/react/CompositeDetails.js.map +1 -1
  297. package/dist/features/composites/react/CompositeFieldEditor.d.ts +5 -5
  298. package/dist/features/composites/react/CompositeFieldEditor.js +6 -6
  299. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
  300. package/dist/features/composites/react/CompositeInspector.d.ts +3 -3
  301. package/dist/features/composites/react/CompositeInspector.js +142 -128
  302. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  303. package/dist/features/composites/react/CompositeStatusMenu.js +2 -2
  304. package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -1
  305. package/dist/features/composites/react/CompositeSummaryEditor.d.ts +1 -1
  306. package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
  307. package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -1
  308. package/dist/features/composites/react/CompositeTable.d.ts +1 -1
  309. package/dist/features/composites/react/CompositeTable.js +3 -3
  310. package/dist/features/composites/react/CompositeTable.js.map +1 -1
  311. package/dist/features/composites/react/CompositeTagsEditor.d.ts +1 -1
  312. package/dist/features/composites/react/CompositeTagsEditor.js +1 -1
  313. package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -1
  314. package/dist/features/composites/react/CompositeView.js +17 -15
  315. package/dist/features/composites/react/CompositeView.js.map +1 -1
  316. package/dist/features/composites/react/DerivationBadge.js +13 -4
  317. package/dist/features/composites/react/DerivationBadge.js.map +1 -1
  318. package/dist/features/composites/react/FocusChip.d.ts +1 -1
  319. package/dist/features/composites/react/FocusChip.js +1 -1
  320. package/dist/features/composites/react/FocusChip.js.map +1 -1
  321. package/dist/features/composites/react/ReferenceFieldSelect.js +3 -3
  322. package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -1
  323. package/dist/features/composites/react/ReferenceView.d.ts +1 -1
  324. package/dist/features/composites/react/ReferenceView.js +1 -1
  325. package/dist/features/composites/react/ReferenceView.js.map +1 -1
  326. package/dist/features/composites/react/SettingsEditingTab.d.ts +4 -4
  327. package/dist/features/composites/react/SettingsEditingTab.js +20 -20
  328. package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
  329. package/dist/features/composites/react.js +1 -1
  330. package/dist/features/composites/react.js.map +1 -1
  331. package/dist/features/composites/reference-ai.js +1 -1
  332. package/dist/features/composites/reference-ai.js.map +1 -1
  333. package/dist/features/composites/reference-label.d.ts +3 -3
  334. package/dist/features/composites/reference-label.js +6 -6
  335. package/dist/features/composites/reference-label.js.map +1 -1
  336. package/dist/features/composites/reference-suggestion.d.ts +1 -1
  337. package/dist/features/composites/reference-suggestion.js +3 -3
  338. package/dist/features/composites/reference-suggestion.js.map +1 -1
  339. package/dist/features/composites/reference-text.d.ts +2 -2
  340. package/dist/features/composites/reference-text.js +4 -4
  341. package/dist/features/composites/reference-text.js.map +1 -1
  342. package/dist/features/composites/reference.d.ts +3 -3
  343. package/dist/features/composites/reference.js +1 -1
  344. package/dist/features/composites/reference.js.map +1 -1
  345. package/dist/features/composites/reveal-source.d.ts +3 -3
  346. package/dist/features/composites/reveal-source.js +6 -6
  347. package/dist/features/composites/reveal-source.js.map +1 -1
  348. package/dist/features/composites/rows-model.d.ts +2 -2
  349. package/dist/features/composites/rows-model.js +4 -3
  350. package/dist/features/composites/rows-model.js.map +1 -1
  351. package/dist/features/composites/rows.d.ts +16 -28
  352. package/dist/features/composites/rows.js +20 -54
  353. package/dist/features/composites/rows.js.map +1 -1
  354. package/dist/features/composites/schema.d.ts +12 -11
  355. package/dist/features/composites/schema.js +10 -9
  356. package/dist/features/composites/schema.js.map +1 -1
  357. package/dist/features/composites/source.d.ts +20 -0
  358. package/dist/features/composites/source.js +40 -0
  359. package/dist/features/composites/source.js.map +1 -0
  360. package/dist/features/composites/split.d.ts +11 -11
  361. package/dist/features/composites/split.js +20 -20
  362. package/dist/features/composites/split.js.map +1 -1
  363. package/dist/features/composites/structure-mode.js +3 -3
  364. package/dist/features/composites/structure-mode.js.map +1 -1
  365. package/dist/features/composites/table-model.js +2 -2
  366. package/dist/features/composites/table-model.js.map +1 -1
  367. package/dist/features/composites/use-composite-inspector.js +3 -1
  368. package/dist/features/composites/use-composite-inspector.js.map +1 -1
  369. package/dist/features/composites/use-composite-rows.d.ts +2 -2
  370. package/dist/features/composites/use-composite-rows.js.map +1 -1
  371. package/dist/features/composites/vue.js +1 -1
  372. package/dist/features/composites/vue.js.map +1 -1
  373. package/dist/features/image-studio/components/ImageStudioDialog.js +1 -1
  374. package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
  375. package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +1 -1
  376. package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
  377. package/dist/features/image-studio/feature.d.ts +1 -1
  378. package/dist/features/image-studio/feature.js +1 -1
  379. package/dist/features/image-studio/feature.js.map +1 -1
  380. package/dist/features/image-studio/node-core.d.ts +3 -3
  381. package/dist/features/image-studio/node-core.js +1 -1
  382. package/dist/features/image-studio/node-core.js.map +1 -1
  383. package/dist/features/image-studio/node-store.d.ts +1 -1
  384. package/dist/features/image-studio/node-store.js +1 -1
  385. package/dist/features/image-studio/node-store.js.map +1 -1
  386. package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
  387. package/dist/features/image-studio/react/ImageStudioDialog.js +1 -1
  388. package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
  389. package/dist/features/image-studio/react/ImageStudioView.js +1 -1
  390. package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
  391. package/dist/features/image-studio/react/store.d.ts +3 -3
  392. package/dist/features/image-studio/react/store.js.map +1 -1
  393. package/dist/features/image-studio/react.d.ts +1 -1
  394. package/dist/features/image-studio/react.js +1 -1
  395. package/dist/features/image-studio/react.js.map +1 -1
  396. package/dist/features/image-studio/schema.d.ts +1 -1
  397. package/dist/features/image-studio/schema.js +1 -1
  398. package/dist/features/image-studio/schema.js.map +1 -1
  399. package/dist/features/image-studio/state.d.ts +3 -3
  400. package/dist/features/image-studio/state.js +2 -2
  401. package/dist/features/image-studio/state.js.map +1 -1
  402. package/dist/features/image-studio/store.d.ts +3 -3
  403. package/dist/features/image-studio/vue.d.ts +1 -1
  404. package/dist/features/image-studio/vue.js +1 -1
  405. package/dist/features/image-studio/vue.js.map +1 -1
  406. package/dist/features/media/index.js +1 -1
  407. package/dist/features/media/react/AudioRecorderModal.js +1 -1
  408. package/dist/features/media/react.js +1 -1
  409. package/dist/features/mentions/tag.js +2 -1
  410. package/dist/features/mentions/tag.js.map +1 -1
  411. package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
  412. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  413. package/dist/features/themes/feature.d.ts +1 -1
  414. package/dist/features/themes/feature.js.map +1 -1
  415. package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
  416. package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
  417. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  418. package/dist/features/themes/theme-runtime.js +1 -1
  419. package/dist/features/themes/theme-runtime.js.map +1 -1
  420. package/dist/features/transcript/TranscriptSegmentView.js +1 -1
  421. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  422. package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
  423. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  424. package/dist/features/transcript/segment-node.js.map +1 -1
  425. package/dist/features/transfer/feature.js +1 -1
  426. package/dist/features/transfer/feature.js.map +1 -1
  427. package/dist/features/transfer/transforms.d.ts +4 -4
  428. package/dist/features/transfer/transforms.js +12 -12
  429. package/dist/features/transfer/transforms.js.map +1 -1
  430. package/dist/i18n/de/ai.js +4 -4
  431. package/dist/i18n/de/ai.js.map +1 -1
  432. package/dist/i18n/de/authors.js +1 -1
  433. package/dist/i18n/de/authors.js.map +1 -1
  434. package/dist/i18n/de/comments.js +1 -1
  435. package/dist/i18n/de/comments.js.map +1 -1
  436. package/dist/i18n/de/composites.js +62 -59
  437. package/dist/i18n/de/composites.js.map +1 -1
  438. package/dist/i18n/de/image-studio.js +2 -2
  439. package/dist/i18n/de/image-studio.js.map +1 -1
  440. package/dist/i18n/de/integrations.js +20 -20
  441. package/dist/i18n/de/integrations.js.map +1 -1
  442. package/dist/i18n/de/markdown.js +1 -1
  443. package/dist/i18n/de/markdown.js.map +1 -1
  444. package/dist/i18n/de/palette.js +1 -1
  445. package/dist/i18n/de/palette.js.map +1 -1
  446. package/dist/i18n/de/themes.js +3 -3
  447. package/dist/i18n/de/themes.js.map +1 -1
  448. package/dist/i18n/de/transfer.js +2 -2
  449. package/dist/i18n/de/transfer.js.map +1 -1
  450. package/dist/i18n/de/tree.js +6 -6
  451. package/dist/i18n/de/tree.js.map +1 -1
  452. package/dist/i18n/de.d.ts +101 -98
  453. package/dist/i18n/en/ai.js +2 -2
  454. package/dist/i18n/en/ai.js.map +1 -1
  455. package/dist/i18n/en/composites.js +64 -61
  456. package/dist/i18n/en/composites.js.map +1 -1
  457. package/dist/i18n/en/integrations.js +9 -9
  458. package/dist/i18n/en/integrations.js.map +1 -1
  459. package/dist/i18n/en/markdown.js +1 -1
  460. package/dist/i18n/en/markdown.js.map +1 -1
  461. package/dist/i18n/en/palette.js +1 -1
  462. package/dist/i18n/en/palette.js.map +1 -1
  463. package/dist/i18n/en/themes.js +4 -4
  464. package/dist/i18n/en/themes.js.map +1 -1
  465. package/dist/i18n/en/tree.js +5 -5
  466. package/dist/i18n/en/tree.js.map +1 -1
  467. package/dist/index.d.ts +4 -1
  468. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
  469. package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
  470. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
  471. package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
  472. package/dist/integrations/composite-ai/composite-fill.js +5 -5
  473. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  474. package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
  475. package/dist/integrations/composite-ai/composite-summary.js +5 -5
  476. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  477. package/dist/integrations/composite-ai/derive.d.ts +17 -17
  478. package/dist/integrations/composite-ai/derive.js +27 -26
  479. package/dist/integrations/composite-ai/derive.js.map +1 -1
  480. package/dist/integrations/composite-ai/index.d.ts +14 -15
  481. package/dist/integrations/composite-ai/index.js +98 -77
  482. package/dist/integrations/composite-ai/index.js.map +1 -1
  483. package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
  484. package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
  485. package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
  486. package/dist/integrations/composite-ai/rendition.d.ts +9 -9
  487. package/dist/integrations/composite-ai/rendition.js +27 -27
  488. package/dist/integrations/composite-ai/rendition.js.map +1 -1
  489. package/dist/integrations/composite-ai/split.d.ts +8 -8
  490. package/dist/integrations/composite-ai/split.js +12 -12
  491. package/dist/integrations/composite-ai/split.js.map +1 -1
  492. package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
  493. package/dist/integrations/composite-ai/transfer-transforms.js +12 -12
  494. package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
  495. package/dist/integrations/composite-media/index.d.ts +4 -3
  496. package/dist/integrations/composite-media/index.js +20 -14
  497. package/dist/integrations/composite-media/index.js.map +1 -1
  498. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  499. package/dist/markdown/document.js +1 -1
  500. package/dist/markdown/document.js.map +1 -1
  501. package/dist/nuxt/module.d.ts +30 -24
  502. package/dist/nuxt/module.js.map +1 -1
  503. package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
  504. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  505. package/dist/presets/compose.d.ts +6 -6
  506. package/dist/presets/compose.js +8 -5
  507. package/dist/presets/compose.js.map +1 -1
  508. package/dist/react/AdvancedEditor.d.ts +2 -2
  509. package/dist/react/AdvancedEditor.js +1 -1
  510. package/dist/react/AdvancedEditor.js.map +1 -1
  511. package/dist/react/compose.d.ts +1 -1
  512. package/dist/react/compose.js +2 -2
  513. package/dist/react/compose.js.map +1 -1
  514. package/dist/react/composed-surface.d.ts +5 -5
  515. package/dist/react/composed-surface.js +2 -2
  516. package/dist/react/composed-surface.js.map +1 -1
  517. package/dist/server/ai.d.ts +7 -7
  518. package/dist/server/ai.js +1 -1
  519. package/dist/server/ai.js.map +1 -1
  520. package/dist/server/auth.d.ts +1 -1
  521. package/dist/server/collab-state.d.ts +1 -1
  522. package/dist/server/collab-state.js.map +1 -1
  523. package/dist/server/collab.d.ts +4 -4
  524. package/dist/server/collab.js +22 -17
  525. package/dist/server/collab.js.map +1 -1
  526. package/dist/server/handlers/ai.d.ts +9 -6
  527. package/dist/server/handlers/ai.js +13 -17
  528. package/dist/server/handlers/ai.js.map +1 -1
  529. package/dist/server/handlers/context.d.ts +4 -0
  530. package/dist/server/handlers/context.js.map +1 -1
  531. package/dist/server/handlers/editor.d.ts +33 -32
  532. package/dist/server/handlers/editor.js +13 -8
  533. package/dist/server/handlers/editor.js.map +1 -1
  534. package/dist/server/handlers/images.d.ts +1 -1
  535. package/dist/server/handlers/images.js +1 -1
  536. package/dist/server/handlers/images.js.map +1 -1
  537. package/dist/server/host.d.ts +4 -0
  538. package/dist/server/host.js +10 -2
  539. package/dist/server/host.js.map +1 -1
  540. package/dist/server/index.d.ts +3 -3
  541. package/dist/server/ports.d.ts +8 -8
  542. package/dist/server/ports.js.map +1 -1
  543. package/dist/settings/index.d.ts +20 -19
  544. package/dist/settings/index.js +25 -24
  545. package/dist/settings/index.js.map +1 -1
  546. package/dist/settings/migrate.d.ts +18 -0
  547. package/dist/settings/migrate.js +46 -0
  548. package/dist/settings/migrate.js.map +1 -0
  549. package/dist/settings/values.d.ts +12 -12
  550. package/dist/settings/values.js +11 -11
  551. package/dist/settings/values.js.map +1 -1
  552. package/dist/styles/composites.css +10 -10
  553. package/dist/tree/DocumentTree.js +12 -5
  554. package/dist/tree/DocumentTree.js.map +1 -1
  555. package/dist/tree/drop-rules.js +5 -5
  556. package/dist/tree/drop-rules.js.map +1 -1
  557. package/dist/tree/react/DocumentTree.js +14 -5
  558. package/dist/tree/react/DocumentTree.js.map +1 -1
  559. package/dist/tree/tree-host.js +6 -6
  560. package/dist/tree/tree-host.js.map +1 -1
  561. package/dist/tree/tree-items.js +34 -28
  562. package/dist/tree/tree-items.js.map +1 -1
  563. package/dist/tree/tree-model.js +16 -16
  564. package/dist/tree/tree-model.js.map +1 -1
  565. package/dist/tree/tree-rows.js +2 -2
  566. package/dist/tree/tree-rows.js.map +1 -1
  567. package/dist/tree/tree-view-model.d.ts +2 -2
  568. package/dist/tree/tree-view-model.js.map +1 -1
  569. package/dist/vue/AdvancedEditor.js +2 -2
  570. package/dist/vue/AdvancedEditor.js.map +1 -1
  571. package/dist/vue/AdvancedEditor.vue.d.ts +10 -10
  572. package/dist/vue/SimpleEditor.vue.d.ts +4 -4
  573. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  574. package/dist/vue/compose.d.ts +1 -1
  575. package/dist/vue/compose.js +2 -2
  576. package/dist/vue/compose.js.map +1 -1
  577. package/package.json +1 -1
  578. package/dist/features/composites/derivation.d.ts +0 -62
  579. package/dist/features/composites/derivation.js +0 -102
  580. package/dist/features/composites/derivation.js.map +0 -1
  581. package/dist/features/composites/kinds.d.ts +0 -83
  582. package/dist/features/composites/kinds.js +0 -285
  583. package/dist/features/composites/kinds.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"assistant.js","names":[],"sources":["../../../src/features/ai/assistant.ts"],"sourcesContent":["import type { EditorBackend } from '../../core/backend'\nimport type { AiBlock } from '../../core/feature'\nimport type { Notifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { AiLanguage, ModelSlot } from '../../settings'\nimport { postAiSse } from './ai-sse'\nimport { AI_TASKS, type AiBlockInput, type AiTaskId, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\nexport type AiStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface AiRunBody {\n task: AiTaskId\n prompt: string\n selection: string\n context: string\n language?: AiLanguage\n /** The editor's host blocks (`aiContextOf(editor).blocks`), listed in the task's system prompt. */\n blocks?: readonly AiBlock[]\n /** The block of a `needsBlock` task (`fill-block`): its brief and material. */\n block?: AiBlockInput\n}\n\n/** Snapshot of the running or last request: its status, the streamed text so far and the error message. */\nexport interface AiAssistantState {\n status: AiStatus\n output: string\n error: string | null\n}\n\n/** What `createAiAssistant` needs from the app: the AI port, the model of a slot, the notifier and translator for the 429 notice and failures. */\nexport interface AiAssistantOptions {\n /** `EditorContext.backend.ai`; without it every run ends in the error state (501), like a server without AI. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelSlot) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`). */\n t: Translate\n}\n\n/** The streaming assistant: observe `state`, `run` a task, `abort`/`reset` it. */\nexport interface AiAssistant {\n state: ReadonlyStore<AiAssistantState>\n run: (body: AiRunBody) => Promise<void>\n /** Stops the running request (its outcome is `idle`). */\n abort: () => void\n /** Aborts and returns to the empty `idle` state. */\n reset: () => void\n /** Clears the error message; the status stays. */\n clearError: () => void\n}\n\nconst IDLE: AiAssistantState = { status: 'idle', output: '', error: null }\n\n/** Streams the backend's `generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's model slot. */\nexport function createAiAssistant({ ai, modelRef, notify, t }: AiAssistantOptions): AiAssistant {\n const store = createStore<AiAssistantState>(IDLE)\n let controller: AbortController | null = null\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(IDLE)\n }\n\n function clearError() {\n const state = store.get()\n if (state.error !== null) store.set({ ...state, error: null })\n }\n\n async function run(body: AiRunBody): Promise<void> {\n reset()\n const own = new AbortController()\n controller = own\n store.set({ status: 'streaming', output: '', error: null })\n const outcome = await postAiSse(ai, 'generate', { ...body, model: modelRef(AI_TASKS[body.task].slot) }, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) store.set({ ...store.get(), output: store.get().output + text })\n },\n })\n if (controller !== own) return // superseded by reset()/another run\n controller = null\n if (outcome.status === 'done') {\n store.set({ ...store.get(), status: 'done' })\n }\n else if (outcome.status === 'error') {\n store.set({ ...store.get(), status: 'error', error: outcome.message })\n }\n else {\n // Notice instead of the error state on 429, so the caller stays ready for another try.\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n store.set({ ...store.get(), status: 'idle' })\n }\n }\n\n return { state: store, run, abort, reset, clearError }\n}\n"],"mappings":";;;;;AAsDA,MAAM,OAAyB;CAAE,QAAQ;CAAQ,QAAQ;CAAI,OAAO;AAAK;;AAGzE,SAAgB,kBAAkB,EAAE,IAAI,UAAU,QAAQ,KAAsC;CAC9F,MAAM,QAAQ,YAA8B,IAAI;CAChD,IAAI,aAAqC;CAEzC,SAAS,QAAQ;EACf,YAAY,MAAM;CACpB;CAEA,SAAS,QAAQ;EACf,MAAM;EACN,aAAa;EACb,MAAM,IAAI,IAAI;CAChB;CAEA,SAAS,aAAa;EACpB,MAAM,QAAQ,MAAM,IAAI;EACxB,IAAI,MAAM,UAAU,MAAM,MAAM,IAAI;GAAE,GAAG;GAAO,OAAO;EAAK,CAAC;CAC/D;CAEA,eAAe,IAAI,MAAgC;EACjD,MAAM;EACN,MAAM,MAAM,IAAI,gBAAgB;EAChC,aAAa;EACb,MAAM,IAAI;GAAE,QAAQ;GAAa,QAAQ;GAAI,OAAO;EAAK,CAAC;EAC1D,MAAM,UAAU,MAAM,UAAU,IAAI,YAAY;GAAE,GAAG;GAAM,OAAO,SAAS,SAAS,KAAK,KAAK,CAAC,IAAI;EAAE,GAAG;GACtG,QAAQ,IAAI;GACZ;GACA,UAAU,SAAS;IACjB,IAAI,eAAe,KAAK,MAAM,IAAI;KAAE,GAAG,MAAM,IAAI;KAAG,QAAQ,MAAM,IAAI,CAAC,CAAC,SAAS;IAAK,CAAC;GACzF;EACF,CAAC;EACD,IAAI,eAAe,KAAK;EACxB,aAAa;EACb,IAAI,QAAQ,WAAW,QACrB,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;EAAO,CAAC;OAEzC,IAAI,QAAQ,WAAW,SAC1B,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;GAAS,OAAO,QAAQ;EAAQ,CAAC;OAElE;GAEH,IAAI,QAAQ,WAAW,gBAAgB,oBAAoB,QAAQ,CAAC;GACpE,MAAM,IAAI;IAAE,GAAG,MAAM,IAAI;IAAG,QAAQ;GAAO,CAAC;EAC9C;CACF;CAEA,OAAO;EAAE,OAAO;EAAO;EAAK;EAAO;EAAO;CAAW;AACvD"}
1
+ {"version":3,"file":"assistant.js","names":[],"sources":["../../../src/features/ai/assistant.ts"],"sourcesContent":["import type { EditorBackend } from '../../core/backend'\nimport type { AiBlock } from '../../core/feature'\nimport type { Notifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { AiLanguage, ModelRole } from '../../settings'\nimport { postAiSse } from './ai-sse'\nimport { AI_TASKS, type AiBlockInput, type AiTaskId, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\nexport type AiStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface AiRunBody {\n task: AiTaskId\n prompt: string\n selection: string\n context: string\n language?: AiLanguage\n /** The editor's host blocks (`aiContextOf(editor).blocks`), listed in the task's system prompt. */\n blocks?: readonly AiBlock[]\n /** The block of a `needsBlock` task (`fill-block`): its brief and material. */\n block?: AiBlockInput\n}\n\n/** Snapshot of the running or last request: its status, the streamed text so far and the error message. */\nexport interface AiAssistantState {\n status: AiStatus\n output: string\n error: string | null\n}\n\n/** What `createAiAssistant` needs from the app: the AI port, the model of a Model role, the notifier and translator for the 429 notice and failures. */\nexport interface AiAssistantOptions {\n /** `EditorContext.backend.ai`; without it every run ends in the error state (501), like a server without AI. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelRole) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`). */\n t: Translate\n}\n\n/** The streaming assistant: observe `state`, `run` a task, `abort`/`reset` it. */\nexport interface AiAssistant {\n state: ReadonlyStore<AiAssistantState>\n run: (body: AiRunBody) => Promise<void>\n /** Stops the running request (its outcome is `idle`). */\n abort: () => void\n /** Aborts and returns to the empty `idle` state. */\n reset: () => void\n /** Clears the error message; the status stays. */\n clearError: () => void\n}\n\nconst IDLE: AiAssistantState = { status: 'idle', output: '', error: null }\n\n/** Streams the backend's `generate` (SSE: `delta` / `done` / `error` events) into a store, on the task's Model role. */\nexport function createAiAssistant({ ai, modelRef, notify, t }: AiAssistantOptions): AiAssistant {\n const store = createStore<AiAssistantState>(IDLE)\n let controller: AbortController | null = null\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(IDLE)\n }\n\n function clearError() {\n const state = store.get()\n if (state.error !== null) store.set({ ...state, error: null })\n }\n\n async function run(body: AiRunBody): Promise<void> {\n reset()\n const own = new AbortController()\n controller = own\n store.set({ status: 'streaming', output: '', error: null })\n const outcome = await postAiSse(ai, 'generate', { ...body, model: modelRef(AI_TASKS[body.task].slot) }, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) store.set({ ...store.get(), output: store.get().output + text })\n },\n })\n if (controller !== own) return // superseded by reset()/another run\n controller = null\n if (outcome.status === 'done') {\n store.set({ ...store.get(), status: 'done' })\n }\n else if (outcome.status === 'error') {\n store.set({ ...store.get(), status: 'error', error: outcome.message })\n }\n else {\n // Notice instead of the error state on 429, so the caller stays ready for another try.\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n store.set({ ...store.get(), status: 'idle' })\n }\n }\n\n return { state: store, run, abort, reset, clearError }\n}\n"],"mappings":";;;;;AAsDA,MAAM,OAAyB;CAAE,QAAQ;CAAQ,QAAQ;CAAI,OAAO;AAAK;;AAGzE,SAAgB,kBAAkB,EAAE,IAAI,UAAU,QAAQ,KAAsC;CAC9F,MAAM,QAAQ,YAA8B,IAAI;CAChD,IAAI,aAAqC;CAEzC,SAAS,QAAQ;EACf,YAAY,MAAM;CACpB;CAEA,SAAS,QAAQ;EACf,MAAM;EACN,aAAa;EACb,MAAM,IAAI,IAAI;CAChB;CAEA,SAAS,aAAa;EACpB,MAAM,QAAQ,MAAM,IAAI;EACxB,IAAI,MAAM,UAAU,MAAM,MAAM,IAAI;GAAE,GAAG;GAAO,OAAO;EAAK,CAAC;CAC/D;CAEA,eAAe,IAAI,MAAgC;EACjD,MAAM;EACN,MAAM,MAAM,IAAI,gBAAgB;EAChC,aAAa;EACb,MAAM,IAAI;GAAE,QAAQ;GAAa,QAAQ;GAAI,OAAO;EAAK,CAAC;EAC1D,MAAM,UAAU,MAAM,UAAU,IAAI,YAAY;GAAE,GAAG;GAAM,OAAO,SAAS,SAAS,KAAK,KAAK,CAAC,IAAI;EAAE,GAAG;GACtG,QAAQ,IAAI;GACZ;GACA,UAAU,SAAS;IACjB,IAAI,eAAe,KAAK,MAAM,IAAI;KAAE,GAAG,MAAM,IAAI;KAAG,QAAQ,MAAM,IAAI,CAAC,CAAC,SAAS;IAAK,CAAC;GACzF;EACF,CAAC;EACD,IAAI,eAAe,KAAK;EACxB,aAAa;EACb,IAAI,QAAQ,WAAW,QACrB,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;EAAO,CAAC;OAEzC,IAAI,QAAQ,WAAW,SAC1B,MAAM,IAAI;GAAE,GAAG,MAAM,IAAI;GAAG,QAAQ;GAAS,OAAO,QAAQ;EAAQ,CAAC;OAElE;GAEH,IAAI,QAAQ,WAAW,gBAAgB,oBAAoB,QAAQ,CAAC;GACpE,MAAM,IAAI;IAAE,GAAG,MAAM,IAAI;IAAG,QAAQ;GAAO,CAAC;EAC9C;CACF;CAEA,OAAO;EAAE,OAAO;EAAO;EAAK;EAAO;EAAO;CAAW;AACvD"}
@@ -180,7 +180,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
180
180
  })
181
181
  }, {
182
182
  content: withCtx(() => [unref(state).picking ? (openBlock(), createElementBlock("div", _hoisted_2, [
183
- createCommentVNode(" Caret mode generates blocks, selection mode rewrites text in the block: each has its own model slot. "),
183
+ createCommentVNode(" Caret mode generates blocks, selection mode rewrites text in the block: each has its own Model role. "),
184
184
  createElementVNode("div", _hoisted_3, [createElementVNode("span", _hoisted_4, toDisplayString(unref(t)("ai.menu.model")), 1), createVNode(_sfc_main$1, {
185
185
  slot: unref(state).mode === "caret" ? "generate" : "rewrite",
186
186
  size: "xs",
@@ -1 +1 @@
1
- {"version":3,"file":"AiSelectionMenu.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","_unref","_withCtx","_createElementVNode","_createCommentVNode","_toDisplayString","ModelQuickSwitch","_renderList","_createBlock","_withModifiers"],"sources":["../../../../src/features/ai/components/AiSelectionMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'\nimport type { EditorState } from '@tiptap/pm/state'\nimport { type Editor, EditorContent, useEditor } from '@tiptap/vue-3'\nimport { BubbleMenu } from '@tiptap/vue-3/menus'\nimport { computed, onBeforeUnmount, useTemplateRef, watch } from 'vue'\nimport { parseBlocks, resolveRange } from '../../../core'\nimport { useEditorSettings } from '../../../vue/settings'\nimport type { InputExposed } from '../../../vue/kit/types'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useStoreRef } from '../../../vue/store-ref'\nimport { useNotifier } from '../../../vue/use-notifier'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { AI_LANGUAGES } from '../../../settings'\nimport { aiSelectionHighlightKey } from '../ai-selection-highlight'\nimport { previewExtensions } from '../preview-extensions'\nimport { AI_LANGUAGE_KEYS } from '../protocol'\nimport { AI_SELECTION_MENU_KEY, createAiSelectionFlow } from '../selection-flow'\nimport { useAiAssistant } from '../use-ai-assistant'\nimport ModelQuickSwitch from './ModelQuickSwitch.vue'\n\n/**\n * AI quick actions on a text selection: a bubble menu entry opens a popover with the selection tasks of the registry\n * (`selectionMenu`), the result streams into a preview and is applied over the range captured when the menu opened.\n * Caret mode (`/ai`, see `openAiMenuAtCaret`): the same popover opens at the caret with the `caretMenu` tasks and\n * `generate` as free prompt; the result is inserted at the caret line (`insertAtCaretLine`).\n * Needs the AiSelectionHighlight extension on the editor. The flow (`createAiSelectionFlow`) owns every step; this\n * component renders its state, places the popover and shows the preview.\n */\nconst props = defineProps<{ editor: Editor }>()\n\nconst { Alert: UAlert, Button: UButton, Icon: UIcon, Input: UInput, Popover: UPopover, Select: USelect } = useUiKit()\nconst notify = useNotifier(() => props.editor)\nconst { t } = useEditorI18n()\nconst { model: assistant } = useAiAssistant(() => props.editor)\nconst { settings } = useEditorSettings()\nconst flow = createAiSelectionFlow({ assistant, notify, t, editor: () => props.editor, language: () => settings.value.ai.translateLanguage })\nconst state = useStoreRef(flow.state)\n\nconst languageItems = computed(() => AI_LANGUAGES.map(l => ({ label: t(AI_LANGUAGE_KEYS[l]), value: l })))\nconst prompt = computed({ get: () => state.value.prompt, set: flow.setPrompt })\nconst language = computed({ get: () => state.value.language, set: flow.setLanguage })\nconst promptInput = useTemplateRef<InputExposed>('promptInput')\nconst requestLabel = computed(() => {\n const request = state.value.request\n if (!request) return undefined\n const task = t(request.task.labelKey)\n return request.language ? t('ai.menu.requestWithLanguage', { task, language: t(AI_LANGUAGE_KEYS[request.language]) }) : task\n})\n// Extra rows other features contribute (`aiMenuActions` of their extensions), offered while the selection qualifies.\nconst menuActions = computed(() => state.value.open ? flow.menuActions() : [])\n\nconst shouldShow: NonNullable<BubbleMenuPluginProps['shouldShow']> = ({ element, view }) =>\n (view.hasFocus() || element.contains(document.activeElement)) && flow.offersSelection()\n\n/** Caret mode: the prompt input gets the focus, so the instruction can be typed right away. */\nfunction onOpenAutoFocus(event: Event) {\n if (state.value.mode !== 'caret') return\n event.preventDefault()\n promptInput.value?.inputRef?.focus()\n}\n\n\n// Popover anchor: the highlighted range (its plugin state follows local and remote edits); in caret mode the caret.\nlet anchorRect = new DOMRect()\nconst reference = {\n getBoundingClientRect(): DOMRect {\n const { editor } = props\n if (editor.isDestroyed) return anchorRect\n if (state.value.mode === 'caret') return caretRect(editor)\n const highlight = aiSelectionHighlightKey.getState(editor.state)\n const highlighted = highlight && resolveRange(editor.state, highlight.anchor)\n if (!highlighted || highlighted.from >= highlighted.to) return anchorRect\n const { view } = editor\n const start = view.coordsAtPos(highlighted.from, 1)\n const end = view.coordsAtPos(highlighted.to, -1)\n // Across lines the range spans the editor's width.\n const bounds = view.dom.getBoundingClientRect()\n const oneLine = end.top < start.bottom\n const left = oneLine ? start.left : bounds.left\n const right = oneLine ? end.right : bounds.right\n anchorRect = new DOMRect(left, start.top, Math.max(right - left, 1), end.bottom - start.top)\n return anchorRect\n },\n get contextElement() {\n return props.editor.view.dom\n },\n}\n\n// The caret's position is resolved from its anchor once per editor state (the anchor is measured every frame).\nlet caretAt: { state: EditorState, pos: number | null } | null = null\nfunction caretRect(editor: Editor): DOMRect {\n if (caretAt?.state !== editor.state) caretAt = { state: editor.state, pos: flow.range()?.from ?? null }\n if (caretAt.pos === null) return anchorRect\n const coords = editor.view.coordsAtPos(caretAt.pos)\n anchorRect = new DOMRect(coords.left, coords.top, 1, coords.bottom - coords.top)\n return anchorRect\n}\n\nconst preview = useEditor({\n editable: false,\n extensions: previewExtensions(props.editor),\n editorProps: { attributes: { class: 'tiptap ai-selection-preview' } },\n})\n\n// Coalesce streamed deltas: re-parse the Markdown at most every 80 ms; speaker lines show as segments, as applied.\nlet previewTimer: ReturnType<typeof setTimeout> | undefined\nwatch(() => state.value.output, () => {\n if (previewTimer) return\n previewTimer = setTimeout(() => {\n previewTimer = undefined\n const instance = preview.value\n if (!instance) return\n instance.commands.setContent({ type: 'doc', content: parseBlocks(props.editor, state.value.output).blocks }, { emitUpdate: false })\n }, 80)\n})\n\nonBeforeUnmount(() => {\n clearTimeout(previewTimer)\n flow.dispose()\n preview.value?.destroy()\n})\n</script>\n\n<template>\n <BubbleMenu :editor=\"editor\" :plugin-key=\"AI_SELECTION_MENU_KEY\" :should-show=\"shouldShow\" :options=\"{ placement: 'top', offset: 8 }\" class=\"z-10\">\n <div class=\"flex rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] p-0.5 shadow-md\">\n <UButton size=\"sm\" color=\"neutral\" variant=\"ghost\" icon=\"i-lucide-sparkles\" :label=\"t('ai.menu.trigger')\" @click=\"flow.openForSelection()\" />\n </div>\n </BubbleMenu>\n\n <UPopover\n :open=\"state.open\"\n :reference=\"reference\"\n :dismissible=\"state.picking\"\n :content=\"{ side: 'bottom', align: 'start', updatePositionStrategy: 'always', onOpenAutoFocus }\"\n :ui=\"{ content: 'w-[30rem] max-w-[calc(100vw-2rem)]' }\"\n @update:open=\"(value: boolean) => { if (!value) flow.close(state.mode === 'caret') }\"\n >\n <template #content>\n <div v-if=\"state.picking\" class=\"flex flex-col gap-1 p-2\">\n <!-- Caret mode generates blocks, selection mode rewrites text in the block: each has its own model slot. -->\n <div class=\"flex items-center justify-end gap-2 px-1 pb-1\">\n <span class=\"text-xs text-[var(--sk-muted)]\">{{ t('ai.menu.model') }}</span>\n <ModelQuickSwitch :slot=\"state.mode === 'caret' ? 'generate' : 'rewrite'\" size=\"xs\" class=\"w-56\" />\n </div>\n <div v-for=\"task in state.actions\" :key=\"task.id\" class=\"flex items-center gap-1\">\n <UButton\n :icon=\"task.icon\"\n :label=\"t(task.labelKey)\"\n color=\"neutral\"\n variant=\"ghost\"\n class=\"flex-1 justify-start\"\n :disabled=\"state.mode === 'caret' && task.needsSelection && !state.hasTextBefore\"\n @click=\"flow.start(task)\"\n />\n <USelect v-if=\"task.needsLanguage\" v-model=\"language\" :items=\"languageItems\" size=\"sm\" class=\"w-36\" :aria-label=\"t('ai.menu.targetLanguage')\" />\n </div>\n <UButton\n v-for=\"action in menuActions\"\n :key=\"action.id\"\n :icon=\"action.icon\"\n :label=\"action.label\"\n color=\"neutral\"\n variant=\"ghost\"\n class=\"justify-start\"\n :loading=\"state.runningAction === action.id\"\n :disabled=\"state.runningAction !== null && state.runningAction !== action.id\"\n @click=\"flow.runAction(action)\"\n />\n <form class=\"mt-1 flex gap-1 border-t border-[var(--sk-border)] pt-2\" @submit.prevent=\"flow.submitPrompt()\">\n <UInput ref=\"promptInput\" v-model=\"prompt\" :icon=\"state.freePrompt.icon\" :placeholder=\"state.freePrompt.promptLabelKey && t(state.freePrompt.promptLabelKey)\" class=\"flex-1\" />\n <UButton type=\"submit\" icon=\"i-lucide-arrow-up\" :disabled=\"!state.prompt.trim()\" :aria-label=\"t('ai.menu.sendInstruction')\" />\n </form>\n </div>\n\n <div v-else class=\"flex flex-col gap-3 p-3\">\n <div class=\"flex items-center gap-2 text-sm font-medium\">\n <UIcon v-if=\"state.request\" :name=\"state.request.task.icon\" class=\"size-4\" />\n <span class=\"flex-1 truncate\">{{ requestLabel }}</span>\n <UIcon v-if=\"state.status === 'streaming'\" name=\"i-lucide-loader-circle\" class=\"size-4 animate-spin text-[var(--sk-muted)]\" />\n </div>\n <UAlert v-if=\"state.error\" color=\"error\" variant=\"subtle\" :title=\"state.error\" />\n <div class=\"max-h-72 min-h-16 overflow-auto rounded-md border border-[var(--sk-border)] p-3 text-sm\">\n <EditorContent v-if=\"preview\" :editor=\"preview\" />\n </div>\n <div class=\"flex flex-wrap justify-end gap-2\">\n <UButton v-if=\"state.status === 'streaming'\" size=\"sm\" color=\"neutral\" variant=\"ghost\" :label=\"t('ai.menu.cancel')\" @click=\"flow.abort()\" />\n <UButton size=\"sm\" color=\"neutral\" variant=\"ghost\" :label=\"t('ai.menu.discard')\" @click=\"flow.close(true)\" />\n <UButton\n size=\"sm\"\n color=\"neutral\"\n variant=\"outline\"\n icon=\"i-lucide-rotate-ccw\"\n :label=\"t('ai.menu.retry')\"\n :disabled=\"state.status === 'streaming'\"\n @click=\"flow.retry()\"\n />\n <UButton v-if=\"state.mode === 'caret'\" size=\"sm\" icon=\"i-lucide-text-cursor-input\" :label=\"t('ai.menu.insert')\" :disabled=\"!state.canApply\" @click=\"flow.insertAtCaret()\" />\n <template v-else>\n <UButton\n size=\"sm\"\n color=\"neutral\"\n variant=\"outline\"\n icon=\"i-lucide-list-plus\"\n :label=\"t('ai.menu.insertBelow')\"\n :disabled=\"!state.canApply\"\n @click=\"flow.apply('insert-below')\"\n />\n <UButton size=\"sm\" icon=\"i-lucide-replace\" :label=\"t('ai.menu.replace')\" :disabled=\"!state.canApply\" @click=\"flow.apply('replace')\" />\n </template>\n </div>\n </div>\n </template>\n </UPopover>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"AiSelectionMenu.js","names":["_openBlock","_createElementBlock","_Fragment","_createVNode","_unref","_withCtx","_createElementVNode","_createCommentVNode","_toDisplayString","ModelQuickSwitch","_renderList","_createBlock","_withModifiers"],"sources":["../../../../src/features/ai/components/AiSelectionMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'\nimport type { EditorState } from '@tiptap/pm/state'\nimport { type Editor, EditorContent, useEditor } from '@tiptap/vue-3'\nimport { BubbleMenu } from '@tiptap/vue-3/menus'\nimport { computed, onBeforeUnmount, useTemplateRef, watch } from 'vue'\nimport { parseBlocks, resolveRange } from '../../../core'\nimport { useEditorSettings } from '../../../vue/settings'\nimport type { InputExposed } from '../../../vue/kit/types'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useStoreRef } from '../../../vue/store-ref'\nimport { useNotifier } from '../../../vue/use-notifier'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { AI_LANGUAGES } from '../../../settings'\nimport { aiSelectionHighlightKey } from '../ai-selection-highlight'\nimport { previewExtensions } from '../preview-extensions'\nimport { AI_LANGUAGE_KEYS } from '../protocol'\nimport { AI_SELECTION_MENU_KEY, createAiSelectionFlow } from '../selection-flow'\nimport { useAiAssistant } from '../use-ai-assistant'\nimport ModelQuickSwitch from './ModelQuickSwitch.vue'\n\n/**\n * AI quick actions on a text selection: a bubble menu entry opens a popover with the selection tasks of the registry\n * (`selectionMenu`), the result streams into a preview and is applied over the range captured when the menu opened.\n * Caret mode (`/ai`, see `openAiMenuAtCaret`): the same popover opens at the caret with the `caretMenu` tasks and\n * `generate` as free prompt; the result is inserted at the caret line (`insertAtCaretLine`).\n * Needs the AiSelectionHighlight extension on the editor. The flow (`createAiSelectionFlow`) owns every step; this\n * component renders its state, places the popover and shows the preview.\n */\nconst props = defineProps<{ editor: Editor }>()\n\nconst { Alert: UAlert, Button: UButton, Icon: UIcon, Input: UInput, Popover: UPopover, Select: USelect } = useUiKit()\nconst notify = useNotifier(() => props.editor)\nconst { t } = useEditorI18n()\nconst { model: assistant } = useAiAssistant(() => props.editor)\nconst { settings } = useEditorSettings()\nconst flow = createAiSelectionFlow({ assistant, notify, t, editor: () => props.editor, language: () => settings.value.ai.translateLanguage })\nconst state = useStoreRef(flow.state)\n\nconst languageItems = computed(() => AI_LANGUAGES.map(l => ({ label: t(AI_LANGUAGE_KEYS[l]), value: l })))\nconst prompt = computed({ get: () => state.value.prompt, set: flow.setPrompt })\nconst language = computed({ get: () => state.value.language, set: flow.setLanguage })\nconst promptInput = useTemplateRef<InputExposed>('promptInput')\nconst requestLabel = computed(() => {\n const request = state.value.request\n if (!request) return undefined\n const task = t(request.task.labelKey)\n return request.language ? t('ai.menu.requestWithLanguage', { task, language: t(AI_LANGUAGE_KEYS[request.language]) }) : task\n})\n// Extra rows other features contribute (`aiMenuActions` of their extensions), offered while the selection qualifies.\nconst menuActions = computed(() => state.value.open ? flow.menuActions() : [])\n\nconst shouldShow: NonNullable<BubbleMenuPluginProps['shouldShow']> = ({ element, view }) =>\n (view.hasFocus() || element.contains(document.activeElement)) && flow.offersSelection()\n\n/** Caret mode: the prompt input gets the focus, so the instruction can be typed right away. */\nfunction onOpenAutoFocus(event: Event) {\n if (state.value.mode !== 'caret') return\n event.preventDefault()\n promptInput.value?.inputRef?.focus()\n}\n\n\n// Popover anchor: the highlighted range (its plugin state follows local and remote edits); in caret mode the caret.\nlet anchorRect = new DOMRect()\nconst reference = {\n getBoundingClientRect(): DOMRect {\n const { editor } = props\n if (editor.isDestroyed) return anchorRect\n if (state.value.mode === 'caret') return caretRect(editor)\n const highlight = aiSelectionHighlightKey.getState(editor.state)\n const highlighted = highlight && resolveRange(editor.state, highlight.anchor)\n if (!highlighted || highlighted.from >= highlighted.to) return anchorRect\n const { view } = editor\n const start = view.coordsAtPos(highlighted.from, 1)\n const end = view.coordsAtPos(highlighted.to, -1)\n // Across lines the range spans the editor's width.\n const bounds = view.dom.getBoundingClientRect()\n const oneLine = end.top < start.bottom\n const left = oneLine ? start.left : bounds.left\n const right = oneLine ? end.right : bounds.right\n anchorRect = new DOMRect(left, start.top, Math.max(right - left, 1), end.bottom - start.top)\n return anchorRect\n },\n get contextElement() {\n return props.editor.view.dom\n },\n}\n\n// The caret's position is resolved from its anchor once per editor state (the anchor is measured every frame).\nlet caretAt: { state: EditorState, pos: number | null } | null = null\nfunction caretRect(editor: Editor): DOMRect {\n if (caretAt?.state !== editor.state) caretAt = { state: editor.state, pos: flow.range()?.from ?? null }\n if (caretAt.pos === null) return anchorRect\n const coords = editor.view.coordsAtPos(caretAt.pos)\n anchorRect = new DOMRect(coords.left, coords.top, 1, coords.bottom - coords.top)\n return anchorRect\n}\n\nconst preview = useEditor({\n editable: false,\n extensions: previewExtensions(props.editor),\n editorProps: { attributes: { class: 'tiptap ai-selection-preview' } },\n})\n\n// Coalesce streamed deltas: re-parse the Markdown at most every 80 ms; speaker lines show as segments, as applied.\nlet previewTimer: ReturnType<typeof setTimeout> | undefined\nwatch(() => state.value.output, () => {\n if (previewTimer) return\n previewTimer = setTimeout(() => {\n previewTimer = undefined\n const instance = preview.value\n if (!instance) return\n instance.commands.setContent({ type: 'doc', content: parseBlocks(props.editor, state.value.output).blocks }, { emitUpdate: false })\n }, 80)\n})\n\nonBeforeUnmount(() => {\n clearTimeout(previewTimer)\n flow.dispose()\n preview.value?.destroy()\n})\n</script>\n\n<template>\n <BubbleMenu :editor=\"editor\" :plugin-key=\"AI_SELECTION_MENU_KEY\" :should-show=\"shouldShow\" :options=\"{ placement: 'top', offset: 8 }\" class=\"z-10\">\n <div class=\"flex rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] p-0.5 shadow-md\">\n <UButton size=\"sm\" color=\"neutral\" variant=\"ghost\" icon=\"i-lucide-sparkles\" :label=\"t('ai.menu.trigger')\" @click=\"flow.openForSelection()\" />\n </div>\n </BubbleMenu>\n\n <UPopover\n :open=\"state.open\"\n :reference=\"reference\"\n :dismissible=\"state.picking\"\n :content=\"{ side: 'bottom', align: 'start', updatePositionStrategy: 'always', onOpenAutoFocus }\"\n :ui=\"{ content: 'w-[30rem] max-w-[calc(100vw-2rem)]' }\"\n @update:open=\"(value: boolean) => { if (!value) flow.close(state.mode === 'caret') }\"\n >\n <template #content>\n <div v-if=\"state.picking\" class=\"flex flex-col gap-1 p-2\">\n <!-- Caret mode generates blocks, selection mode rewrites text in the block: each has its own Model role. -->\n <div class=\"flex items-center justify-end gap-2 px-1 pb-1\">\n <span class=\"text-xs text-[var(--sk-muted)]\">{{ t('ai.menu.model') }}</span>\n <ModelQuickSwitch :slot=\"state.mode === 'caret' ? 'generate' : 'rewrite'\" size=\"xs\" class=\"w-56\" />\n </div>\n <div v-for=\"task in state.actions\" :key=\"task.id\" class=\"flex items-center gap-1\">\n <UButton\n :icon=\"task.icon\"\n :label=\"t(task.labelKey)\"\n color=\"neutral\"\n variant=\"ghost\"\n class=\"flex-1 justify-start\"\n :disabled=\"state.mode === 'caret' && task.needsSelection && !state.hasTextBefore\"\n @click=\"flow.start(task)\"\n />\n <USelect v-if=\"task.needsLanguage\" v-model=\"language\" :items=\"languageItems\" size=\"sm\" class=\"w-36\" :aria-label=\"t('ai.menu.targetLanguage')\" />\n </div>\n <UButton\n v-for=\"action in menuActions\"\n :key=\"action.id\"\n :icon=\"action.icon\"\n :label=\"action.label\"\n color=\"neutral\"\n variant=\"ghost\"\n class=\"justify-start\"\n :loading=\"state.runningAction === action.id\"\n :disabled=\"state.runningAction !== null && state.runningAction !== action.id\"\n @click=\"flow.runAction(action)\"\n />\n <form class=\"mt-1 flex gap-1 border-t border-[var(--sk-border)] pt-2\" @submit.prevent=\"flow.submitPrompt()\">\n <UInput ref=\"promptInput\" v-model=\"prompt\" :icon=\"state.freePrompt.icon\" :placeholder=\"state.freePrompt.promptLabelKey && t(state.freePrompt.promptLabelKey)\" class=\"flex-1\" />\n <UButton type=\"submit\" icon=\"i-lucide-arrow-up\" :disabled=\"!state.prompt.trim()\" :aria-label=\"t('ai.menu.sendInstruction')\" />\n </form>\n </div>\n\n <div v-else class=\"flex flex-col gap-3 p-3\">\n <div class=\"flex items-center gap-2 text-sm font-medium\">\n <UIcon v-if=\"state.request\" :name=\"state.request.task.icon\" class=\"size-4\" />\n <span class=\"flex-1 truncate\">{{ requestLabel }}</span>\n <UIcon v-if=\"state.status === 'streaming'\" name=\"i-lucide-loader-circle\" class=\"size-4 animate-spin text-[var(--sk-muted)]\" />\n </div>\n <UAlert v-if=\"state.error\" color=\"error\" variant=\"subtle\" :title=\"state.error\" />\n <div class=\"max-h-72 min-h-16 overflow-auto rounded-md border border-[var(--sk-border)] p-3 text-sm\">\n <EditorContent v-if=\"preview\" :editor=\"preview\" />\n </div>\n <div class=\"flex flex-wrap justify-end gap-2\">\n <UButton v-if=\"state.status === 'streaming'\" size=\"sm\" color=\"neutral\" variant=\"ghost\" :label=\"t('ai.menu.cancel')\" @click=\"flow.abort()\" />\n <UButton size=\"sm\" color=\"neutral\" variant=\"ghost\" :label=\"t('ai.menu.discard')\" @click=\"flow.close(true)\" />\n <UButton\n size=\"sm\"\n color=\"neutral\"\n variant=\"outline\"\n icon=\"i-lucide-rotate-ccw\"\n :label=\"t('ai.menu.retry')\"\n :disabled=\"state.status === 'streaming'\"\n @click=\"flow.retry()\"\n />\n <UButton v-if=\"state.mode === 'caret'\" size=\"sm\" icon=\"i-lucide-text-cursor-input\" :label=\"t('ai.menu.insert')\" :disabled=\"!state.canApply\" @click=\"flow.insertAtCaret()\" />\n <template v-else>\n <UButton\n size=\"sm\"\n color=\"neutral\"\n variant=\"outline\"\n icon=\"i-lucide-list-plus\"\n :label=\"t('ai.menu.insertBelow')\"\n :disabled=\"!state.canApply\"\n @click=\"flow.apply('insert-below')\"\n />\n <UButton size=\"sm\" icon=\"i-lucide-replace\" :label=\"t('ai.menu.replace')\" :disabled=\"!state.canApply\" @click=\"flow.apply('replace')\" />\n </template>\n </div>\n </div>\n </template>\n </UPopover>\n</template>\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { MODEL_SLOT_KEYS } from "../protocol/models.js";
1
+ import { MODEL_ROLE_KEYS } from "../protocol/models.js";
2
2
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
3
3
  import { useEditorSettings } from "../../../vue/settings.js";
4
4
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
@@ -7,8 +7,8 @@ import { useModelProfiles } from "../use-model-profiles.js";
7
7
  import { computed, createBlock, defineComponent, openBlock, ref, unref } from "vue";
8
8
  //#region src/features/ai/components/ModelQuickSwitch.vue
9
9
  /**
10
- * Model profile an AI slot runs on. A quick switch *is* the slot setting (`settings.ai.slots[slot]`): the choice applies
11
- * to every feature of the slot, not just to the surrounding dialog. The trailing „Modelle verwalten…“ opens the
10
+ * Model profile an AI Model role runs on. A quick switch *is* the Model role setting (`settings.ai.roles[slot]`): the choice applies
11
+ * to every feature of the Model role, not just to the surrounding dialog. The trailing „Modelle verwalten…“ opens the
12
12
  * settings on the „KI“ tab. Inside an editor surface the chrome comes from its provide; mounted beside one, pass its
13
13
  * `editor`.
14
14
  */
@@ -22,7 +22,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
22
22
  },
23
23
  setup(__props) {
24
24
  const props = __props;
25
- /** Item value of „Modelle verwalten…“; profile ids are preset ids (camelCase) or UUIDs, so it never collides. */
25
+ /** Item value of „Modelle verwalten…“; profile ids are Model preset ids (camelCase) or UUIDs, so it never collides. */
26
26
  const { SelectMenu: USelectMenu } = useUiKit();
27
27
  const { settings } = useEditorSettings();
28
28
  const { profiles, slotProfile } = useModelProfiles();
@@ -44,7 +44,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
44
44
  const selected = computed({
45
45
  get: () => current.value?.id,
46
46
  set: (id) => {
47
- if (id && id !== MANAGE) settings.value.ai.slots[props.slot] = id;
47
+ if (id && id !== MANAGE) settings.value.ai.roles[props.slot] = id;
48
48
  }
49
49
  });
50
50
  function manage(event) {
@@ -63,8 +63,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
63
63
  size: props.size,
64
64
  "search-input": false,
65
65
  icon: current.value?.source === "user" ? "i-lucide-key-round" : "i-lucide-server",
66
- placeholder: unref(t)(unref(MODEL_SLOT_KEYS)[props.slot].labelKey),
67
- "aria-label": unref(t)("ai.slot.ariaLabel", { slot: unref(t)(unref(MODEL_SLOT_KEYS)[props.slot].labelKey) }),
66
+ placeholder: unref(t)(unref(MODEL_ROLE_KEYS)[props.slot].labelKey),
67
+ "aria-label": unref(t)("ai.slot.ariaLabel", { slot: unref(t)(unref(MODEL_ROLE_KEYS)[props.slot].labelKey) }),
68
68
  ui: { content: "min-w-72" }
69
69
  }, null, 8, [
70
70
  "modelValue",
@@ -1 +1 @@
1
- {"version":3,"file":"ModelQuickSwitch.js","names":["_openBlock","_createBlock","_unref"],"sources":["../../../../src/features/ai/components/ModelQuickSwitch.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Editor } from '@tiptap/core'\nimport { computed, ref } from 'vue'\nimport type { SelectItem } from '../../../vue/kit/types'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorUi } from '../../../vue/use-editor-ui'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ModelSlot } from '../../../settings'\nimport { MODEL_SLOT_KEYS } from '../protocol'\nimport { useModelProfiles } from '../use-model-profiles'\n\n/**\n * Model profile an AI slot runs on. A quick switch *is* the slot setting (`settings.ai.slots[slot]`): the choice applies\n * to every feature of the slot, not just to the surrounding dialog. The trailing „Modelle verwalten…“ opens the\n * settings on the „KI“ tab. Inside an editor surface the chrome comes from its provide; mounted beside one, pass its\n * `editor`.\n */\nconst props = withDefaults(defineProps<{\n slot: ModelSlot\n size?: 'xs' | 'sm'\n /** The editor whose settings modal „Modelle verwalten…“ opens, when not mounted inside its editor surface. */\n editor?: Editor\n}>(), { size: 'sm' })\n\n/** Item value of „Modelle verwalten…“; profile ids are preset ids (camelCase) or UUIDs, so it never collides. */\nconst MANAGE = '__manage__'\n\nconst { SelectMenu: USelectMenu } = useUiKit()\nconst { settings } = useEditorSettings()\nconst { profiles, slotProfile } = useModelProfiles()\nconst { t } = useEditorI18n()\nconst editorUi = useEditorUi(() => props.editor)\nconst open = ref(false)\n\nconst items = computed<SelectItem[]>(() => [\n ...profiles.value.map(profile => ({\n label: profile.label,\n description: profile.model,\n icon: profile.source === 'preset' ? 'i-lucide-server' : 'i-lucide-key-round',\n value: profile.id,\n })),\n ...(editorUi.value\n ? [{ type: 'separator' as const }, { label: t('ai.slot.manage'), icon: 'i-lucide-settings-2', value: MANAGE, onSelect: manage }]\n : []),\n])\n\nconst current = computed(() => slotProfile(props.slot))\nconst selected = computed<string | undefined>({\n get: () => current.value?.id,\n set: (id) => {\n if (id && id !== MANAGE) settings.value.ai.slots[props.slot] = id\n },\n})\n\nfunction manage(event: Event) {\n // Not a selection: keep the slot as it is.\n event.preventDefault()\n open.value = false\n editorUi.value?.openSettings('ai')\n}\n</script>\n\n<template>\n <USelectMenu\n v-model=\"selected\"\n v-model:open=\"open\"\n :items=\"items\"\n value-key=\"value\"\n :size=\"props.size\"\n :search-input=\"false\"\n :icon=\"current?.source === 'user' ? 'i-lucide-key-round' : 'i-lucide-server'\"\n :placeholder=\"t(MODEL_SLOT_KEYS[props.slot].labelKey)\"\n :aria-label=\"t('ai.slot.ariaLabel', { slot: t(MODEL_SLOT_KEYS[props.slot].labelKey) })\"\n :ui=\"{ content: 'min-w-72' }\"\n />\n</template>\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAM,SAAS;;;;;;;;;EARf,MAAM,QAAQ;;EAUd,MAAM,EAAE,YAAY,gBAAgB,SAAS;EAC7C,MAAM,EAAE,aAAa,kBAAkB;EACvC,MAAM,EAAE,UAAU,gBAAgB,iBAAiB;EACnD,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,WAAW,kBAAkB,MAAM,MAAM;EAC/C,MAAM,OAAO,IAAI,KAAK;EAEtB,MAAM,QAAQ,eAA6B,CACzC,GAAG,SAAS,MAAM,KAAI,aAAY;GAChC,OAAO,QAAQ;GACf,aAAa,QAAQ;GACrB,MAAM,QAAQ,WAAW,WAAW,oBAAoB;GACxD,OAAO,QAAQ;EACjB,EAAE,GACF,GAAI,SAAS,QACT,CAAC,EAAE,MAAM,YAAqB,GAAG;GAAE,OAAO,EAAE,gBAAgB;GAAG,MAAM;GAAuB,OAAO;GAAQ,UAAU;EAAO,CAAC,IAC7H,CAAC,CACP,CAAC;EAED,MAAM,UAAU,eAAe,YAAY,MAAM,IAAI,CAAC;EACtD,MAAM,WAAW,SAA6B;GAC5C,WAAW,QAAQ,OAAO;GAC1B,MAAM,OAAO;IACX,IAAI,MAAM,OAAO,QAAQ,SAAS,MAAM,GAAG,MAAM,MAAM,QAAQ;GACjE;EACF,CAAC;EAED,SAAS,OAAO,OAAc;GAE5B,MAAM,eAAe;GACrB,KAAK,QAAQ;GACb,SAAS,OAAO,aAAa,IAAI;EACnC;;GAIE,OAAAA,UAAA,GAAAC,YAWEC,MAAA,WAAA,GAAA;IAVS,YAAA,SAAA;IAAA,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,SAAQ,QAAA;IACT,MAAM,KAAA;IAAA,iBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,KAAI,QAAA;IACjB,OAAO,MAAA;IACR,aAAU;IACT,MAAM,MAAM;IACZ,gBAAc;IACd,MAAM,QAAA,OAAS,WAAM,SAAA,uBAAA;IACrB,aAAaA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,MAAM,KAAI,CAAE,QAAQ;IACnD,cAAYA,MAAA,CAAA,CAAC,CAAA,qBAAA,EAAA,MAA8BA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,MAAM,KAAI,CAAE,QAAQ,EAAA,CAAA;IACjF,IAAI,EAAA,SAAA,WAAA"}
1
+ {"version":3,"file":"ModelQuickSwitch.js","names":["_openBlock","_createBlock","_unref"],"sources":["../../../../src/features/ai/components/ModelQuickSwitch.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Editor } from '@tiptap/core'\nimport { computed, ref } from 'vue'\nimport type { SelectItem } from '../../../vue/kit/types'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorUi } from '../../../vue/use-editor-ui'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ModelRole } from '../../../settings'\nimport { MODEL_ROLE_KEYS } from '../protocol'\nimport { useModelProfiles } from '../use-model-profiles'\n\n/**\n * Model profile an AI Model role runs on. A quick switch *is* the Model role setting (`settings.ai.roles[slot]`): the choice applies\n * to every feature of the Model role, not just to the surrounding dialog. The trailing „Modelle verwalten…“ opens the\n * settings on the „KI“ tab. Inside an editor surface the chrome comes from its provide; mounted beside one, pass its\n * `editor`.\n */\nconst props = withDefaults(defineProps<{\n slot: ModelRole\n size?: 'xs' | 'sm'\n /** The editor whose settings modal „Modelle verwalten…“ opens, when not mounted inside its editor surface. */\n editor?: Editor\n}>(), { size: 'sm' })\n\n/** Item value of „Modelle verwalten…“; profile ids are Model preset ids (camelCase) or UUIDs, so it never collides. */\nconst MANAGE = '__manage__'\n\nconst { SelectMenu: USelectMenu } = useUiKit()\nconst { settings } = useEditorSettings()\nconst { profiles, slotProfile } = useModelProfiles()\nconst { t } = useEditorI18n()\nconst editorUi = useEditorUi(() => props.editor)\nconst open = ref(false)\n\nconst items = computed<SelectItem[]>(() => [\n ...profiles.value.map(profile => ({\n label: profile.label,\n description: profile.model,\n icon: profile.source === 'preset' ? 'i-lucide-server' : 'i-lucide-key-round',\n value: profile.id,\n })),\n ...(editorUi.value\n ? [{ type: 'separator' as const }, { label: t('ai.slot.manage'), icon: 'i-lucide-settings-2', value: MANAGE, onSelect: manage }]\n : []),\n])\n\nconst current = computed(() => slotProfile(props.slot))\nconst selected = computed<string | undefined>({\n get: () => current.value?.id,\n set: (id) => {\n if (id && id !== MANAGE) settings.value.ai.roles[props.slot] = id\n },\n})\n\nfunction manage(event: Event) {\n // Not a selection: keep the Model role as it is.\n event.preventDefault()\n open.value = false\n editorUi.value?.openSettings('ai')\n}\n</script>\n\n<template>\n <USelectMenu\n v-model=\"selected\"\n v-model:open=\"open\"\n :items=\"items\"\n value-key=\"value\"\n :size=\"props.size\"\n :search-input=\"false\"\n :icon=\"current?.source === 'user' ? 'i-lucide-key-round' : 'i-lucide-server'\"\n :placeholder=\"t(MODEL_ROLE_KEYS[props.slot].labelKey)\"\n :aria-label=\"t('ai.slot.ariaLabel', { slot: t(MODEL_ROLE_KEYS[props.slot].labelKey) })\"\n :ui=\"{ content: 'min-w-72' }\"\n />\n</template>\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAM,SAAS;;;;;;;;;EARf,MAAM,QAAQ;;EAUd,MAAM,EAAE,YAAY,gBAAgB,SAAS;EAC7C,MAAM,EAAE,aAAa,kBAAkB;EACvC,MAAM,EAAE,UAAU,gBAAgB,iBAAiB;EACnD,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,WAAW,kBAAkB,MAAM,MAAM;EAC/C,MAAM,OAAO,IAAI,KAAK;EAEtB,MAAM,QAAQ,eAA6B,CACzC,GAAG,SAAS,MAAM,KAAI,aAAY;GAChC,OAAO,QAAQ;GACf,aAAa,QAAQ;GACrB,MAAM,QAAQ,WAAW,WAAW,oBAAoB;GACxD,OAAO,QAAQ;EACjB,EAAE,GACF,GAAI,SAAS,QACT,CAAC,EAAE,MAAM,YAAqB,GAAG;GAAE,OAAO,EAAE,gBAAgB;GAAG,MAAM;GAAuB,OAAO;GAAQ,UAAU;EAAO,CAAC,IAC7H,CAAC,CACP,CAAC;EAED,MAAM,UAAU,eAAe,YAAY,MAAM,IAAI,CAAC;EACtD,MAAM,WAAW,SAA6B;GAC5C,WAAW,QAAQ,OAAO;GAC1B,MAAM,OAAO;IACX,IAAI,MAAM,OAAO,QAAQ,SAAS,MAAM,GAAG,MAAM,MAAM,QAAQ;GACjE;EACF,CAAC;EAED,SAAS,OAAO,OAAc;GAE5B,MAAM,eAAe;GACrB,KAAK,QAAQ;GACb,SAAS,OAAO,aAAa,IAAI;EACnC;;GAIE,OAAAA,UAAA,GAAAC,YAWEC,MAAA,WAAA,GAAA;IAVS,YAAA,SAAA;IAAA,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,SAAQ,QAAA;IACT,MAAM,KAAA;IAAA,iBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,KAAI,QAAA;IACjB,OAAO,MAAA;IACR,aAAU;IACT,MAAM,MAAM;IACZ,gBAAc;IACd,MAAM,QAAA,OAAS,WAAM,SAAA,uBAAA;IACrB,aAAaA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,MAAM,KAAI,CAAE,QAAQ;IACnD,cAAYA,MAAA,CAAA,CAAC,CAAA,qBAAA,EAAA,MAA8BA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,MAAM,KAAI,CAAE,QAAQ,EAAA,CAAA;IACjF,IAAI,EAAA,SAAA,WAAA"}
@@ -1,15 +1,15 @@
1
- import { ModelSlot } from "../../../settings/values.js";
1
+ import { ModelRole } from "../../../settings/values.js";
2
2
  import "../../../settings/index.js";
3
3
  import { Editor } from "@tiptap/core";
4
4
  //#region src/features/ai/components/ModelQuickSwitch.vue.d.ts
5
5
  /**
6
- * Model profile an AI slot runs on. A quick switch *is* the slot setting (`settings.ai.slots[slot]`): the choice applies
7
- * to every feature of the slot, not just to the surrounding dialog. The trailing „Modelle verwalten…“ opens the
6
+ * Model profile an AI Model role runs on. A quick switch *is* the Model role setting (`settings.ai.roles[slot]`): the choice applies
7
+ * to every feature of the Model role, not just to the surrounding dialog. The trailing „Modelle verwalten…“ opens the
8
8
  * settings on the „KI“ tab. Inside an editor surface the chrome comes from its provide; mounted beside one, pass its
9
9
  * `editor`.
10
10
  */
11
11
  type __VLS_Props = {
12
- slot: ModelSlot;
12
+ slot: ModelRole;
13
13
  size?: 'xs' | 'sm';
14
14
  /** The editor whose settings modal „Modelle verwalten…“ opens, when not mounted inside its editor surface. */
15
15
  editor?: Editor;
@@ -1,5 +1,5 @@
1
- import { AI_LANGUAGE_KEYS, MODEL_SLOT_KEYS } from "../protocol/models.js";
2
- import { AI_LANGUAGES, MODEL_SLOTS } from "../../../settings/values.js";
1
+ import { AI_LANGUAGE_KEYS, MODEL_ROLE_KEYS } from "../protocol/models.js";
2
+ import { AI_LANGUAGES, MODEL_ROLES } from "../../../settings/values.js";
3
3
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
4
4
  import { useEditorSettings } from "../../../vue/settings.js";
5
5
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
@@ -9,7 +9,7 @@ import _sfc_main$3 from "./SettingsModelProfiles.js";
9
9
  import { Fragment, createBlock, createElementBlock, createVNode, defineComponent, openBlock, renderList, unref, withCtx } from "vue";
10
10
  //#region src/features/ai/components/SettingsAiTab.vue
11
11
  const _hoisted_1 = { class: "flex flex-col gap-8" };
12
- /** „KI“: writing aids, the model profile per slot, and model profiles (server presets and own profiles). */
12
+ /** „KI“: writing aids, the model profile per Model role, and model profiles (Model presets and own profiles). */
13
13
  const _sfc_main = /*@__PURE__*/ defineComponent({
14
14
  __name: "SettingsAiTab",
15
15
  setup(__props) {
@@ -51,16 +51,16 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
51
51
  _: 1
52
52
  }, 8, ["title"]),
53
53
  createVNode(_sfc_main$2, {
54
- title: unref(t)("ai.settings.slots.title"),
55
- description: unref(t)("ai.settings.slots.description")
54
+ title: unref(t)("ai.settings.roles.title"),
55
+ description: unref(t)("ai.settings.roles.description")
56
56
  }, {
57
- default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(MODEL_SLOTS), (slot) => {
57
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(MODEL_ROLES), (slot) => {
58
58
  return openBlock(), createBlock(unref(UFormField), {
59
59
  key: slot,
60
60
  orientation: "horizontal",
61
61
  class: "py-3",
62
- label: unref(t)(unref(MODEL_SLOT_KEYS)[slot].labelKey),
63
- description: unref(t)(unref(MODEL_SLOT_KEYS)[slot].descriptionKey)
62
+ label: unref(t)(unref(MODEL_ROLE_KEYS)[slot].labelKey),
63
+ description: unref(t)(unref(MODEL_ROLE_KEYS)[slot].descriptionKey)
64
64
  }, {
65
65
  default: withCtx(() => [createVNode(_sfc_main$1, {
66
66
  slot,
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsAiTab.js","names":["_openBlock","_createElementBlock","_createVNode","SettingsSection","_unref","_withCtx","_Fragment","_renderList","_createBlock","ModelQuickSwitch","SettingsModelProfiles"],"sources":["../../../../src/features/ai/components/SettingsAiTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport SettingsSection from '../../../vue/components/SettingsSection.vue'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { AI_LANGUAGES, MODEL_SLOTS } from '../../../settings'\nimport { AI_LANGUAGE_KEYS, MODEL_SLOT_KEYS } from '../protocol'\nimport ModelQuickSwitch from './ModelQuickSwitch.vue'\nimport SettingsModelProfiles from './SettingsModelProfiles.vue'\n\n/** „KI“: writing aids, the model profile per slot, and model profiles (server presets and own profiles). */\nconst { FormField: UFormField, Select: USelect, Switch: USwitch } = useUiKit()\nconst { settings } = useEditorSettings()\nconst { t } = useEditorI18n()\n\nconst languageItems = AI_LANGUAGES.map(l => ({ label: t(AI_LANGUAGE_KEYS[l]), value: l }))\n</script>\n\n<template>\n <div class=\"flex flex-col gap-8\">\n <SettingsSection :title=\"t('ai.settings.writing.title')\">\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('ai.settings.autocomplete.label')\"\n :description=\"t('ai.settings.autocomplete.description')\"\n >\n <USwitch v-model=\"settings.ai.autocomplete\" />\n </UFormField>\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('ai.settings.language.label')\"\n :description=\"t('ai.settings.language.description')\"\n >\n <USelect v-model=\"settings.ai.translateLanguage\" :items=\"languageItems\" class=\"w-64\" />\n </UFormField>\n </SettingsSection>\n\n <SettingsSection :title=\"t('ai.settings.slots.title')\" :description=\"t('ai.settings.slots.description')\">\n <UFormField\n v-for=\"slot in MODEL_SLOTS\"\n :key=\"slot\"\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t(MODEL_SLOT_KEYS[slot].labelKey)\"\n :description=\"t(MODEL_SLOT_KEYS[slot].descriptionKey)\"\n >\n <ModelQuickSwitch :slot=\"slot\" class=\"w-64\" />\n </UFormField>\n </SettingsSection>\n\n <SettingsSection\n :title=\"t('ai.settings.profiles.title')\"\n :description=\"t('ai.settings.profiles.description')\"\n >\n <SettingsModelProfiles />\n </SettingsSection>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;EAWA,MAAM,EAAE,WAAW,YAAY,QAAQ,SAAS,QAAQ,YAAY,SAAS;EAC7E,MAAM,EAAE,aAAa,kBAAkB;EACvC,MAAM,EAAE,MAAM,cAAc;EAE5B,MAAM,gBAAgB,aAAa,KAAI,OAAM;GAAE,OAAO,EAAE,iBAAiB,EAAE;GAAG,OAAO;EAAE,EAAE;;GAIvF,OAAAA,UAAA,GAAAC,mBAuCM,OAvCN,YAuCM;IAtCJC,YAiBkBC,aAAA,EAjBA,OAAOC,MAAA,CAAA,CAAC,CAAA,2BAAA,EAAA,GAAA;KACxB,SAAAC,cAOa,CAPbH,YAOaE,MAAA,UAAA,GAAA;MANX,aAAY;MACZ,OAAM;MACL,OAAOA,MAAA,CAAA,CAAC,CAAA,gCAAA;MACR,aAAaA,MAAA,CAAA,CAAC,CAAA,sCAAA;;MAEf,SAAAC,cAA8C,CAA9CH,YAA8CE,MAAA,OAAA,GAAA;OAA5B,YAAAA,MAAA,QAAA,CAAQ,CAAC,GAAG;OAAZ,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,QAAA,CAAQ,CAAC,GAAG,eAAY;;;KAE5C,GAAA,GAAA,CAAA,SAAA,aAAA,CAAA,GAAAF,YAOaE,MAAA,UAAA,GAAA;MANX,aAAY;MACZ,OAAM;MACL,OAAOA,MAAA,CAAA,CAAC,CAAA,4BAAA;MACR,aAAaA,MAAA,CAAA,CAAC,CAAA,kCAAA;;MAEf,SAAAC,cAAuF,CAAvFH,YAAuFE,MAAA,OAAA,GAAA;OAArE,YAAAA,MAAA,QAAA,CAAQ,CAAC,GAAG;OAAZ,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,QAAA,CAAQ,CAAC,GAAG,oBAAiB;OAAG,OAAOA,MAAA,aAAA;OAAe,OAAM;;;;;;IAIlFF,YAWkBC,aAAA;KAXA,OAAOC,MAAA,CAAA,CAAC,CAAA,yBAAA;KAA8B,aAAaA,MAAA,CAAA,CAAC,CAAA,+BAAA;;KAElE,SAAAC,cAA2B,EAD7BL,UAAA,IAAA,GAAAC,mBASaK,UAAA,MAAAC,WARIH,MAAA,WAAA,IAAR,SAAI;MADb,OAAAJ,UAAA,GAAAQ,YASaJ,MAAA,UAAA,GAAA;OAPV,KAAK;OACN,aAAY;OACZ,OAAM;OACL,OAAOA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,KAAI,CAAE,QAAQ;OACvC,aAAaA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,KAAI,CAAE,cAAc;;OAEpD,SAAAC,cAA8C,CAA9CH,YAA8CO,aAAA;QAArB;QAAM,OAAM;;;;;;;IAIzCP,YAKkBC,aAAA;KAJf,OAAOC,MAAA,CAAA,CAAC,CAAA,4BAAA;KACR,aAAaA,MAAA,CAAA,CAAC,CAAA,kCAAA;;KAEf,SAAAC,cAAyB,CAAzBH,YAAyBQ,WAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"SettingsAiTab.js","names":["_openBlock","_createElementBlock","_createVNode","SettingsSection","_unref","_withCtx","_Fragment","_renderList","_createBlock","ModelQuickSwitch","SettingsModelProfiles"],"sources":["../../../../src/features/ai/components/SettingsAiTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport SettingsSection from '../../../vue/components/SettingsSection.vue'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { AI_LANGUAGES, MODEL_ROLES } from '../../../settings'\nimport { AI_LANGUAGE_KEYS, MODEL_ROLE_KEYS } from '../protocol'\nimport ModelQuickSwitch from './ModelQuickSwitch.vue'\nimport SettingsModelProfiles from './SettingsModelProfiles.vue'\n\n/** „KI“: writing aids, the model profile per Model role, and model profiles (Model presets and own profiles). */\nconst { FormField: UFormField, Select: USelect, Switch: USwitch } = useUiKit()\nconst { settings } = useEditorSettings()\nconst { t } = useEditorI18n()\n\nconst languageItems = AI_LANGUAGES.map(l => ({ label: t(AI_LANGUAGE_KEYS[l]), value: l }))\n</script>\n\n<template>\n <div class=\"flex flex-col gap-8\">\n <SettingsSection :title=\"t('ai.settings.writing.title')\">\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('ai.settings.autocomplete.label')\"\n :description=\"t('ai.settings.autocomplete.description')\"\n >\n <USwitch v-model=\"settings.ai.autocomplete\" />\n </UFormField>\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('ai.settings.language.label')\"\n :description=\"t('ai.settings.language.description')\"\n >\n <USelect v-model=\"settings.ai.translateLanguage\" :items=\"languageItems\" class=\"w-64\" />\n </UFormField>\n </SettingsSection>\n\n <SettingsSection :title=\"t('ai.settings.roles.title')\" :description=\"t('ai.settings.roles.description')\">\n <UFormField\n v-for=\"slot in MODEL_ROLES\"\n :key=\"slot\"\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t(MODEL_ROLE_KEYS[slot].labelKey)\"\n :description=\"t(MODEL_ROLE_KEYS[slot].descriptionKey)\"\n >\n <ModelQuickSwitch :slot=\"slot\" class=\"w-64\" />\n </UFormField>\n </SettingsSection>\n\n <SettingsSection\n :title=\"t('ai.settings.profiles.title')\"\n :description=\"t('ai.settings.profiles.description')\"\n >\n <SettingsModelProfiles />\n </SettingsSection>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;EAWA,MAAM,EAAE,WAAW,YAAY,QAAQ,SAAS,QAAQ,YAAY,SAAS;EAC7E,MAAM,EAAE,aAAa,kBAAkB;EACvC,MAAM,EAAE,MAAM,cAAc;EAE5B,MAAM,gBAAgB,aAAa,KAAI,OAAM;GAAE,OAAO,EAAE,iBAAiB,EAAE;GAAG,OAAO;EAAE,EAAE;;GAIvF,OAAAA,UAAA,GAAAC,mBAuCM,OAvCN,YAuCM;IAtCJC,YAiBkBC,aAAA,EAjBA,OAAOC,MAAA,CAAA,CAAC,CAAA,2BAAA,EAAA,GAAA;KACxB,SAAAC,cAOa,CAPbH,YAOaE,MAAA,UAAA,GAAA;MANX,aAAY;MACZ,OAAM;MACL,OAAOA,MAAA,CAAA,CAAC,CAAA,gCAAA;MACR,aAAaA,MAAA,CAAA,CAAC,CAAA,sCAAA;;MAEf,SAAAC,cAA8C,CAA9CH,YAA8CE,MAAA,OAAA,GAAA;OAA5B,YAAAA,MAAA,QAAA,CAAQ,CAAC,GAAG;OAAZ,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,QAAA,CAAQ,CAAC,GAAG,eAAY;;;KAE5C,GAAA,GAAA,CAAA,SAAA,aAAA,CAAA,GAAAF,YAOaE,MAAA,UAAA,GAAA;MANX,aAAY;MACZ,OAAM;MACL,OAAOA,MAAA,CAAA,CAAC,CAAA,4BAAA;MACR,aAAaA,MAAA,CAAA,CAAC,CAAA,kCAAA;;MAEf,SAAAC,cAAuF,CAAvFH,YAAuFE,MAAA,OAAA,GAAA;OAArE,YAAAA,MAAA,QAAA,CAAQ,CAAC,GAAG;OAAZ,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,QAAA,CAAQ,CAAC,GAAG,oBAAiB;OAAG,OAAOA,MAAA,aAAA;OAAe,OAAM;;;;;;IAIlFF,YAWkBC,aAAA;KAXA,OAAOC,MAAA,CAAA,CAAC,CAAA,yBAAA;KAA8B,aAAaA,MAAA,CAAA,CAAC,CAAA,+BAAA;;KAElE,SAAAC,cAA2B,EAD7BL,UAAA,IAAA,GAAAC,mBASaK,UAAA,MAAAC,WARIH,MAAA,WAAA,IAAR,SAAI;MADb,OAAAJ,UAAA,GAAAQ,YASaJ,MAAA,UAAA,GAAA;OAPV,KAAK;OACN,aAAY;OACZ,OAAM;OACL,OAAOA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,KAAI,CAAE,QAAQ;OACvC,aAAaA,MAAA,CAAA,CAAC,CAACA,MAAA,eAAA,CAAe,CAAC,KAAI,CAAE,cAAc;;OAEpD,SAAAC,cAA8C,CAA9CH,YAA8CO,aAAA;QAArB;QAAM,OAAM;;;;;;;IAIzCP,YAKkBC,aAAA;KAJf,OAAOC,MAAA,CAAA,CAAC,CAAA,4BAAA;KACR,aAAaA,MAAA,CAAA,CAAC,CAAA,kCAAA;;KAEf,SAAAC,cAAyB,CAAzBH,YAAyBQ,WAAA,CAAA,CAAA"}
@@ -33,7 +33,7 @@ const _hoisted_14 = { class: "truncate text-sm font-medium" };
33
33
  const _hoisted_15 = { class: "truncate text-xs text-[var(--sk-muted)]" };
34
34
  const _hoisted_16 = { class: "flex flex-wrap justify-end gap-2" };
35
35
  /**
36
- * Section of the „KI“ tab: the server's presets and this browser's own profiles. Profiles are assigned to slots in the
36
+ * Section of the „KI“ tab: the server's Model presets and this browser's own profiles. Profiles are assigned to Model roles in the
37
37
  * section above.
38
38
  */
39
39
  const _sfc_main = /*@__PURE__*/ defineComponent({
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsModelProfiles.js","names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_Fragment","_renderList","_createVNode","_createBlock","_createCommentVNode","_withModifiers","_withCtx"],"sources":["../../../../src/features/ai/components/SettingsModelProfiles.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { modelRefSchema, type UserModelProfile } from '../protocol'\nimport { isRateLimited } from '../rate-limit'\nimport { useModelProfiles } from '../use-model-profiles'\n\n/**\n * Section of the „KI“ tab: the server's presets and this browser's own profiles. Profiles are assigned to slots in the\n * section above.\n */\nconst { Alert: UAlert, Badge: UBadge, Button: UButton, FormField: UFormField, Icon: UIcon, Input: UInput } = useUiKit()\nconst app = useEditorContext()\nconst { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles()\nconst { t } = useEditorI18n()\n\n/** The profile being added or edited; null while the form is closed. */\nconst draft = ref<UserModelProfile | null>(null)\n/** Result of „Testen“; replaced (not mutated) per run, so a stale run recognizes it was superseded. */\nconst test = shallowRef<{ state: 'running' } | { state: 'ok' } | { state: 'error', message: string } | null>(null)\n\nconst draftRef = computed(() => draft.value && { endpoint: draft.value.endpoint.trim(), model: draft.value.model.trim(), apiKey: draft.value.apiKey.trim() })\nconst draftValid = computed(() => !!draft.value?.label.trim() && modelRefSchema.safeParse(draftRef.value).success)\n// A test result belongs to the values it ran with.\nwatch(draftRef, () => { test.value = null })\n\nfunction edit(profile?: UserModelProfile) {\n draft.value = profile ? { ...profile } : { id: crypto.randomUUID(), label: '', endpoint: '', model: '', apiKey: '' }\n test.value = null\n}\n\nfunction save() {\n if (!draft.value || !draftRef.value || !draftValid.value) return\n saveUserProfile({ id: draft.value.id, label: draft.value.label.trim(), ...draftRef.value })\n draft.value = null\n}\n\nfunction remove(id: string) {\n removeUserProfile(id)\n if (draft.value?.id === id) draft.value = null\n}\n\n/** One short completion through the profile's endpoint (`ai.complete`). */\nasync function runTest() {\n const model = draftRef.value\n if (!model || !draftValid.value) return\n const current = { state: 'running' } as const\n test.value = current\n try {\n if (!app.backend.ai) throw new Error(t('ai.sse.httpError', { status: 501 }))\n await app.backend.ai.complete({ before: 'Hallo', model }, new AbortController().signal)\n if (test.value === current) test.value = { state: 'ok' }\n }\n catch (error) {\n if (test.value === current) test.value = { state: 'error', message: isRateLimited(error) ? t('ai.rateLimited') : fetchErrorMessage(error) }\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col divide-y divide-[var(--sk-border)]\">\n <div class=\"py-3 flex flex-col gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.server') }}\n </p>\n <p v-if=\"!presets.length\" class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('ai.profiles.noPresets') }}\n </p>\n <ul v-else class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"preset in presets\" :key=\"preset.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-server\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ preset.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ preset.model }} · {{ preset.endpoint }}\n </p>\n </div>\n <UBadge :label=\"t('ai.profiles.serverBadge')\" color=\"neutral\" variant=\"subtle\" size=\"sm\" />\n </li>\n </ul>\n </div>\n\n <div class=\"py-3 flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.own') }}\n </p>\n <UButton v-if=\"!draft\" :label=\"t('ai.profiles.add')\" icon=\"i-lucide-plus\" size=\"sm\" color=\"neutral\" variant=\"outline\" @click=\"edit()\" />\n </div>\n <ul v-if=\"userProfiles.length\" class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"profile in userProfiles\" :key=\"profile.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-key-round\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ profile.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ profile.model }} · {{ profile.endpoint }}\n </p>\n </div>\n <UButton icon=\"i-lucide-pencil\" size=\"sm\" color=\"neutral\" variant=\"ghost\" :aria-label=\"t('ai.profiles.edit')\" @click=\"edit(profile)\" />\n <UButton icon=\"i-lucide-trash-2\" size=\"sm\" color=\"error\" variant=\"ghost\" :aria-label=\"t('ai.profiles.delete')\" @click=\"remove(profile.id)\" />\n </li>\n </ul>\n\n <form v-if=\"draft\" class=\"flex flex-col gap-3 rounded-md border border-[var(--sk-border)] p-3\" @submit.prevent=\"save\">\n <UFormField :label=\"t('ai.profiles.name')\" required>\n <UInput v-model=\"draft.label\" :placeholder=\"t('ai.profiles.namePlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.endpoint')\" :description=\"t('ai.profiles.endpointHelp')\" required>\n <UInput v-model=\"draft.endpoint\" :placeholder=\"t('ai.profiles.endpointPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.model')\" required>\n <UInput v-model=\"draft.model\" :placeholder=\"t('ai.profiles.modelPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.apiKey')\" required>\n <UInput v-model=\"draft.apiKey\" type=\"password\" autocomplete=\"off\" class=\"w-full\" />\n </UFormField>\n <UAlert v-if=\"test?.state === 'ok'\" color=\"success\" variant=\"subtle\" icon=\"i-lucide-check\" :title=\"t('ai.profiles.testOk')\" />\n <UAlert v-else-if=\"test?.state === 'error'\" color=\"error\" variant=\"subtle\" icon=\"i-lucide-circle-alert\" :title=\"test.message\" />\n <div class=\"flex flex-wrap justify-end gap-2\">\n <UButton\n :label=\"t('ai.profiles.test')\"\n icon=\"i-lucide-plug-zap\"\n color=\"neutral\"\n variant=\"outline\"\n :loading=\"test?.state === 'running'\"\n :disabled=\"!draftValid\"\n @click=\"runTest\"\n />\n <UButton :label=\"t('ai.profiles.cancel')\" color=\"neutral\" variant=\"ghost\" @click=\"draft = null\" />\n <UButton type=\"submit\" :label=\"t('ai.profiles.save')\" :disabled=\"!draftValid\" />\n </div>\n </form>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"SettingsModelProfiles.js","names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_unref","_Fragment","_renderList","_createVNode","_createBlock","_createCommentVNode","_withModifiers","_withCtx"],"sources":["../../../../src/features/ai/components/SettingsModelProfiles.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, shallowRef, watch } from 'vue'\nimport { fetchErrorMessage } from '../../../core'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { modelRefSchema, type UserModelProfile } from '../protocol'\nimport { isRateLimited } from '../rate-limit'\nimport { useModelProfiles } from '../use-model-profiles'\n\n/**\n * Section of the „KI“ tab: the server's Model presets and this browser's own profiles. Profiles are assigned to Model roles in the\n * section above.\n */\nconst { Alert: UAlert, Badge: UBadge, Button: UButton, FormField: UFormField, Icon: UIcon, Input: UInput } = useUiKit()\nconst app = useEditorContext()\nconst { presets, userProfiles, saveUserProfile, removeUserProfile } = useModelProfiles()\nconst { t } = useEditorI18n()\n\n/** The profile being added or edited; null while the form is closed. */\nconst draft = ref<UserModelProfile | null>(null)\n/** Result of „Testen“; replaced (not mutated) per run, so a stale run recognizes it was superseded. */\nconst test = shallowRef<{ state: 'running' } | { state: 'ok' } | { state: 'error', message: string } | null>(null)\n\nconst draftRef = computed(() => draft.value && { endpoint: draft.value.endpoint.trim(), model: draft.value.model.trim(), apiKey: draft.value.apiKey.trim() })\nconst draftValid = computed(() => !!draft.value?.label.trim() && modelRefSchema.safeParse(draftRef.value).success)\n// A test result belongs to the values it ran with.\nwatch(draftRef, () => { test.value = null })\n\nfunction edit(profile?: UserModelProfile) {\n draft.value = profile ? { ...profile } : { id: crypto.randomUUID(), label: '', endpoint: '', model: '', apiKey: '' }\n test.value = null\n}\n\nfunction save() {\n if (!draft.value || !draftRef.value || !draftValid.value) return\n saveUserProfile({ id: draft.value.id, label: draft.value.label.trim(), ...draftRef.value })\n draft.value = null\n}\n\nfunction remove(id: string) {\n removeUserProfile(id)\n if (draft.value?.id === id) draft.value = null\n}\n\n/** One short completion through the profile's endpoint (`ai.complete`). */\nasync function runTest() {\n const model = draftRef.value\n if (!model || !draftValid.value) return\n const current = { state: 'running' } as const\n test.value = current\n try {\n if (!app.backend.ai) throw new Error(t('ai.sse.httpError', { status: 501 }))\n await app.backend.ai.complete({ before: 'Hallo', model }, new AbortController().signal)\n if (test.value === current) test.value = { state: 'ok' }\n }\n catch (error) {\n if (test.value === current) test.value = { state: 'error', message: isRateLimited(error) ? t('ai.rateLimited') : fetchErrorMessage(error) }\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col divide-y divide-[var(--sk-border)]\">\n <div class=\"py-3 flex flex-col gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.server') }}\n </p>\n <p v-if=\"!presets.length\" class=\"text-sm text-[var(--sk-muted)]\">\n {{ t('ai.profiles.noPresets') }}\n </p>\n <ul v-else class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"preset in presets\" :key=\"preset.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-server\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ preset.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ preset.model }} · {{ preset.endpoint }}\n </p>\n </div>\n <UBadge :label=\"t('ai.profiles.serverBadge')\" color=\"neutral\" variant=\"subtle\" size=\"sm\" />\n </li>\n </ul>\n </div>\n\n <div class=\"py-3 flex flex-col gap-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <p class=\"text-sm font-medium text-[var(--sk-fg)]\">\n {{ t('ai.profiles.own') }}\n </p>\n <UButton v-if=\"!draft\" :label=\"t('ai.profiles.add')\" icon=\"i-lucide-plus\" size=\"sm\" color=\"neutral\" variant=\"outline\" @click=\"edit()\" />\n </div>\n <ul v-if=\"userProfiles.length\" class=\"flex flex-col divide-y divide-[var(--sk-border)] rounded-md border border-[var(--sk-border)]\">\n <li v-for=\"profile in userProfiles\" :key=\"profile.id\" class=\"flex items-center gap-3 px-3 py-2\">\n <UIcon name=\"i-lucide-key-round\" class=\"size-4 shrink-0 text-[var(--sk-muted)]\" />\n <div class=\"min-w-0 flex-1\">\n <p class=\"truncate text-sm font-medium\">\n {{ profile.label }}\n </p>\n <p class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ profile.model }} · {{ profile.endpoint }}\n </p>\n </div>\n <UButton icon=\"i-lucide-pencil\" size=\"sm\" color=\"neutral\" variant=\"ghost\" :aria-label=\"t('ai.profiles.edit')\" @click=\"edit(profile)\" />\n <UButton icon=\"i-lucide-trash-2\" size=\"sm\" color=\"error\" variant=\"ghost\" :aria-label=\"t('ai.profiles.delete')\" @click=\"remove(profile.id)\" />\n </li>\n </ul>\n\n <form v-if=\"draft\" class=\"flex flex-col gap-3 rounded-md border border-[var(--sk-border)] p-3\" @submit.prevent=\"save\">\n <UFormField :label=\"t('ai.profiles.name')\" required>\n <UInput v-model=\"draft.label\" :placeholder=\"t('ai.profiles.namePlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.endpoint')\" :description=\"t('ai.profiles.endpointHelp')\" required>\n <UInput v-model=\"draft.endpoint\" :placeholder=\"t('ai.profiles.endpointPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.model')\" required>\n <UInput v-model=\"draft.model\" :placeholder=\"t('ai.profiles.modelPlaceholder')\" class=\"w-full\" />\n </UFormField>\n <UFormField :label=\"t('ai.profiles.apiKey')\" required>\n <UInput v-model=\"draft.apiKey\" type=\"password\" autocomplete=\"off\" class=\"w-full\" />\n </UFormField>\n <UAlert v-if=\"test?.state === 'ok'\" color=\"success\" variant=\"subtle\" icon=\"i-lucide-check\" :title=\"t('ai.profiles.testOk')\" />\n <UAlert v-else-if=\"test?.state === 'error'\" color=\"error\" variant=\"subtle\" icon=\"i-lucide-circle-alert\" :title=\"test.message\" />\n <div class=\"flex flex-wrap justify-end gap-2\">\n <UButton\n :label=\"t('ai.profiles.test')\"\n icon=\"i-lucide-plug-zap\"\n color=\"neutral\"\n variant=\"outline\"\n :loading=\"test?.state === 'running'\"\n :disabled=\"!draftValid\"\n @click=\"runTest\"\n />\n <UButton :label=\"t('ai.profiles.cancel')\" color=\"neutral\" variant=\"ghost\" @click=\"draft = null\" />\n <UButton type=\"submit\" :label=\"t('ai.profiles.save')\" :disabled=\"!draftValid\" />\n </div>\n </form>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcA,MAAM,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,SAAS,WAAW,YAAY,MAAM,OAAO,OAAO,WAAW,SAAS;EACtH,MAAM,MAAM,iBAAiB;EAC7B,MAAM,EAAE,SAAS,cAAc,iBAAiB,sBAAsB,iBAAiB;EACvF,MAAM,EAAE,MAAM,cAAc;;EAG5B,MAAM,QAAQ,IAA6B,IAAI;;EAE/C,MAAM,OAAO,WAAgG,IAAI;EAEjH,MAAM,WAAW,eAAe,MAAM,SAAS;GAAE,UAAU,MAAM,MAAM,SAAS,KAAK;GAAG,OAAO,MAAM,MAAM,MAAM,KAAK;GAAG,QAAQ,MAAM,MAAM,OAAO,KAAK;EAAE,CAAC;EAC5J,MAAM,aAAa,eAAe,CAAC,CAAC,MAAM,OAAO,MAAM,KAAK,KAAK,eAAe,UAAU,SAAS,KAAK,CAAC,CAAC,OAAO;EAEjH,MAAM,gBAAgB;GAAE,KAAK,QAAQ;EAAK,CAAC;EAE3C,SAAS,KAAK,SAA4B;GACxC,MAAM,QAAQ,UAAU,EAAE,GAAG,QAAQ,IAAI;IAAE,IAAI,OAAO,WAAW;IAAG,OAAO;IAAI,UAAU;IAAI,OAAO;IAAI,QAAQ;GAAG;GACnH,KAAK,QAAQ;EACf;EAEA,SAAS,OAAO;GACd,IAAI,CAAC,MAAM,SAAS,CAAC,SAAS,SAAS,CAAC,WAAW,OAAO;GAC1D,gBAAgB;IAAE,IAAI,MAAM,MAAM;IAAI,OAAO,MAAM,MAAM,MAAM,KAAK;IAAG,GAAG,SAAS;GAAM,CAAC;GAC1F,MAAM,QAAQ;EAChB;EAEA,SAAS,OAAO,IAAY;GAC1B,kBAAkB,EAAE;GACpB,IAAI,MAAM,OAAO,OAAO,IAAI,MAAM,QAAQ;EAC5C;;EAGA,eAAe,UAAU;GACvB,MAAM,QAAQ,SAAS;GACvB,IAAI,CAAC,SAAS,CAAC,WAAW,OAAO;GACjC,MAAM,UAAU,EAAE,OAAO,UAAU;GACnC,KAAK,QAAQ;GACb,IAAI;IACF,IAAI,CAAC,IAAI,QAAQ,IAAI,MAAM,IAAI,MAAM,EAAE,oBAAoB,EAAE,QAAQ,IAAI,CAAC,CAAC;IAC3E,MAAM,IAAI,QAAQ,GAAG,SAAS;KAAE,QAAQ;KAAS;IAAM,GAAG,IAAI,gBAAgB,CAAC,CAAC,MAAM;IACtF,IAAI,KAAK,UAAU,SAAS,KAAK,QAAQ,EAAE,OAAO,KAAK;GACzD,SACO,OAAO;IACZ,IAAI,KAAK,UAAU,SAAS,KAAK,QAAQ;KAAE,OAAO;KAAS,SAAS,cAAc,KAAK,IAAI,EAAE,gBAAgB,IAAI,kBAAkB,KAAK;IAAE;GAC5I;EACF;;GAIE,OAAAA,UAAA,GAAAC,mBA6EM,OA7EN,YA6EM,CA5EJC,mBAqBM,OArBN,YAqBM,CApBJA,mBAEI,KAFJ,YAEIC,gBADCC,MAAA,CAAA,CAAC,CAAA,oBAAA,CAAA,GAAA,CAAA,GAEI,CAAAA,MAAA,OAAA,CAAO,CAAC,UAAlBJ,UAAA,GAAAC,mBAEI,KAFJ,YAEIE,gBADCC,MAAA,CAAA,CAAC,CAAA,uBAAA,CAAA,GAAA,CAAA,MAENJ,UAAA,GAAAC,mBAaK,MAbL,YAaK,EAZHD,UAAA,IAAA,GAAAC,mBAWKI,UAAA,MAAAC,WAXgBF,MAAA,OAAA,IAAV,WAAM;IAAjB,OAAAJ,UAAA,GAAAC,mBAWK,MAAA;KAX0B,KAAK,OAAO;KAAI,OAAM;;KACnDM,YAA+EH,MAAA,KAAA,GAAA;MAAxE,MAAK;MAAkB,OAAM;;KACpCF,mBAOM,OAPN,YAOM,CANJA,mBAEI,KAFJ,YAEIC,gBADC,OAAO,KAAK,GAAA,CAAA,GAEjBD,mBAEI,KAFJ,YAEIC,gBADC,OAAO,KAAK,IAAG,QAAGA,gBAAG,OAAO,QAAQ,GAAA,CAAA,CAAA,CAAA;KAG3CI,YAA2FH,MAAA,MAAA,GAAA;MAAlF,OAAOA,MAAA,CAAA,CAAC,CAAA,yBAAA;MAA6B,OAAM;MAAU,SAAQ;MAAS,MAAK;;;GAK1F,CAAA,GAAA,GAAA,EAAA,CAAA,EAAA,CAAA,GAAAF,mBAoDM,OApDN,YAoDM;IAnDJA,mBAKM,OALN,aAKM,CAJJA,mBAEI,KAFJ,aAEIC,gBADCC,MAAA,CAAA,CAAC,CAAA,iBAAA,CAAA,GAAA,CAAA,GAEU,CAAA,MAAA,SAAhBJ,UAAA,GAAAQ,YAAwIJ,MAAA,OAAA,GAAA;;KAAhH,OAAOA,MAAA,CAAA,CAAC,CAAA,iBAAA;KAAqB,MAAK;KAAgB,MAAK;KAAK,OAAM;KAAU,SAAQ;KAAW,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI;;IAE1HA,MAAA,YAAA,CAAY,CAAC,UAAvBJ,UAAA,GAAAC,mBAcK,MAdL,aAcK,EAbHD,UAAA,IAAA,GAAAC,mBAYKI,UAAA,MAAAC,WAZiBF,MAAA,YAAA,IAAX,YAAO;KAAlB,OAAAJ,UAAA,GAAAC,mBAYK,MAAA;MAZgC,KAAK,QAAQ;MAAI,OAAM;;MAC1DM,YAAkFH,MAAA,KAAA,GAAA;OAA3E,MAAK;OAAqB,OAAM;;MACvCF,mBAOM,OAPN,aAOM,CANJA,mBAEI,KAFJ,aAEIC,gBADC,QAAQ,KAAK,GAAA,CAAA,GAElBD,mBAEI,KAFJ,aAEIC,gBADC,QAAQ,KAAK,IAAG,QAAGA,gBAAG,QAAQ,QAAQ,GAAA,CAAA,CAAA,CAAA;MAG7CI,YAAuIH,MAAA,OAAA,GAAA;OAA9H,MAAK;OAAkB,MAAK;OAAK,OAAM;OAAU,SAAQ;OAAS,cAAYA,MAAA,CAAA,CAAC,CAAA,kBAAA;OAAuB,UAAK,WAAE,KAAK,OAAO;;MAClIG,YAA6IH,MAAA,OAAA,GAAA;OAApI,MAAK;OAAmB,MAAK;OAAK,OAAM;OAAQ,SAAQ;OAAS,cAAYA,MAAA,CAAA,CAAC,CAAA,oBAAA;OAAyB,UAAK,WAAE,OAAO,QAAQ,EAAE;;;;IAIhI,MAAA,SAAZJ,UAAA,GAAAC,mBA4BO,QAAA;;KA5BY,OAAM;KAAuE,UAAMS,cAAU,MAAI,CAAA,SAAA,CAAA;;KAClHH,YAEaH,MAAA,UAAA,GAAA;MAFA,OAAOA,MAAA,CAAA,CAAC,CAAA,kBAAA;MAAsB,UAAA;;MACzC,SAAAO,cAA+F,CAA/FJ,YAA+FH,MAAA,MAAA,GAAA;OAA9E,YAAA,MAAA,MAAM;OAAN,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,MAAM,QAAK;OAAG,aAAaA,MAAA,CAAA,CAAC,CAAA,6BAAA;OAAiC,OAAM;;;;KAEtFG,YAEaH,MAAA,UAAA,GAAA;MAFA,OAAOA,MAAA,CAAA,CAAC,CAAA,sBAAA;MAA2B,aAAaA,MAAA,CAAA,CAAC,CAAA,0BAAA;MAA8B,UAAA;;MAC1F,SAAAO,cAAsG,CAAtGJ,YAAsGH,MAAA,MAAA,GAAA;OAArF,YAAA,MAAA,MAAM;OAAN,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,MAAM,WAAQ;OAAG,aAAaA,MAAA,CAAA,CAAC,CAAA,iCAAA;OAAqC,OAAM;;;;KAE7FG,YAEaH,MAAA,UAAA,GAAA;MAFA,OAAOA,MAAA,CAAA,CAAC,CAAA,mBAAA;MAAuB,UAAA;;MAC1C,SAAAO,cAAgG,CAAhGJ,YAAgGH,MAAA,MAAA,GAAA;OAA/E,YAAA,MAAA,MAAM;OAAN,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,MAAM,QAAK;OAAG,aAAaA,MAAA,CAAA,CAAC,CAAA,8BAAA;OAAkC,OAAM;;;;KAEvFG,YAEaH,MAAA,UAAA,GAAA;MAFA,OAAOA,MAAA,CAAA,CAAC,CAAA,oBAAA;MAAwB,UAAA;;MAC3C,SAAAO,cAAmF,CAAnFJ,YAAmFH,MAAA,MAAA,GAAA;OAAlE,YAAA,MAAA,MAAM;OAAN,uBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,MAAM,SAAM;OAAE,MAAK;OAAW,cAAa;OAAM,OAAM;;;;KAE5D,KAAA,OAAM,UAAK,QAAzBJ,UAAA,GAAAQ,YAA8HJ,MAAA,MAAA,GAAA;;MAA1F,OAAM;MAAU,SAAQ;MAAS,MAAK;MAAkB,OAAOA,MAAA,CAAA,CAAC,CAAA,oBAAA;KACjF,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,KAAA,KAAA,OAAM,UAAK,WAA9BJ,UAAA,GAAAQ,YAAgIJ,MAAA,MAAA,GAAA;;MAApF,OAAM;MAAQ,SAAQ;MAAS,MAAK;MAAyB,OAAO,KAAA,MAAK;;KACrHF,mBAYM,OAZN,aAYM;MAXJK,YAQEH,MAAA,OAAA,GAAA;OAPC,OAAOA,MAAA,CAAA,CAAC,CAAA,kBAAA;OACT,MAAK;OACL,OAAM;OACN,SAAQ;OACP,SAAS,KAAA,OAAM,UAAK;OACpB,UAAQ,CAAG,WAAA;OACX,SAAO;;;;;;MAEVG,YAAkGH,MAAA,OAAA,GAAA;OAAxF,OAAOA,MAAA,CAAA,CAAC,CAAA,oBAAA;OAAwB,OAAM;OAAU,SAAQ;OAAS,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,QAAK;;MACvFG,YAAgFH,MAAA,OAAA,GAAA;OAAvE,MAAK;OAAU,OAAOA,MAAA,CAAA,CAAC,CAAA,kBAAA;OAAuB,UAAQ,CAAG,WAAA"}
@@ -1,6 +1,6 @@
1
1
  import { ReadonlyStore } from "../../core/store.js";
2
2
  import { Translate } from "../../i18n/index.js";
3
- import { ModelSlot } from "../../settings/values.js";
3
+ import { ModelRole } from "../../settings/values.js";
4
4
  import "../../settings/index.js";
5
5
  import { EditorBackend } from "../../core/backend.js";
6
6
  import { Notifier } from "../../core/notify.js";
@@ -51,7 +51,7 @@ export interface DocumentChatState {
51
51
  export interface DocumentChatOptions {
52
52
  /** `EditorContext.backend.ai`; without it every question ends in the error state (501), like a server without AI. */
53
53
  ai: EditorBackend['ai'];
54
- modelRef: (slot: ModelSlot) => ModelRef;
54
+ modelRef: (slot: ModelRole) => ModelRef;
55
55
  /** Receives the 429 notice. */
56
56
  notify: Notifier;
57
57
  /** The app's translator (`EditorContext.t`): error messages, the 429 notice and the snippets of proposed operations. */
@@ -56,13 +56,13 @@ function createDocumentChat({ ai, modelRef, notify, t }) {
56
56
  patch({ error: t("ai.chat.emptyScope") });
57
57
  return false;
58
58
  }
59
- const { kinds, blocks, structure } = aiContextOf(editor);
59
+ const { blockTypes, blocks, structure } = aiContextOf(editor);
60
60
  const providers = conversionsOf(editor);
61
61
  const targets = [
62
62
  ...CORE_TARGET_IDS,
63
- ...kinds.map((kind) => convertTargetId({
64
- type: "composite",
65
- kind: kind.id
63
+ ...blockTypes.map((blockType) => convertTargetId({
64
+ type: "typedBlock",
65
+ blockType: blockType.id
66
66
  })),
67
67
  ...blocks.filter((block) => parseConversionTarget(providers, block.id) !== null).map((block) => block.id)
68
68
  ];
@@ -105,7 +105,7 @@ function createDocumentChat({ ai, modelRef, notify, t }) {
105
105
  scope: scope?.ids ?? null,
106
106
  nextOp: review.nextOp(),
107
107
  model: modelRef("generate"),
108
- kinds,
108
+ blockTypes,
109
109
  blocks,
110
110
  targets,
111
111
  structure
@@ -1 +1 @@
1
- {"version":3,"file":"document-chat-model.js","names":[],"sources":["../../../src/features/ai/document-chat-model.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorBackend } from '../../core/backend'\nimport { aiContextOf, CORE_TARGET_IDS, convertTargetId, conversionsOf, parseConversionTarget } from '../../core'\nimport { type Notifier, silentNotifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { ModelSlot } from '../../settings'\nimport { type AgentOpReviewState, createAgentOpReview, NO_AGENT_OPS } from './agent-op-review'\nimport type { AgentScope } from './agent-ops'\nimport type { AiTarget } from './ai-apply'\nimport { postAiSse } from './ai-sse'\nimport { documentChatText } from './document-chat'\nimport { type AgentOp, type AgentOpProblem, CHAT_LIMITS, type AiTask, type DocumentChatMessage, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\n/**\n * A block generation run from the AI sidebar's quick actions, shown as an assistant turn. Client only: it never goes\n * to the `chat` stream. `markdown` is the streamed result without the task heading (shown with `withTaskHeading`, applied\n * as `taskResultMarkdown`), `target` the apply target captured when it started; only a result that streamed to the end\n * (`done`) can be applied.\n */\nexport interface DocumentChatGeneration {\n task: AiTask\n /** The instruction of a task with `needsPrompt` (empty otherwise). */\n prompt: string\n markdown: string\n target: AiTarget\n done: boolean\n}\n\n/**\n * A chat turn as shown: an assistant turn also keeps the problems of the operations the server dropped, or is a block\n * generation of the sidebar (`generation`).\n */\nexport type DocumentChatTurn =\n | Extract<DocumentChatMessage, { role: 'user' }>\n | (Extract<DocumentChatMessage, { role: 'assistant' }> & { problems?: AgentOpProblem[], generation?: DocumentChatGeneration })\n\n/** Snapshot of a conversation; every change replaces `messages`/`ops` (never mutated in place). */\nexport interface DocumentChatState {\n messages: readonly DocumentChatTurn[]\n /** Operation states by operation id. */\n ops: AgentOpReviewState['ops']\n streaming: boolean\n error: string | null\n}\n\nexport interface DocumentChatOptions {\n /** `EditorContext.backend.ai`; without it every question ends in the error state (501), like a server without AI. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelSlot) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`): error messages, the 429 notice and the snippets of proposed operations. */\n t: Translate\n}\n\nexport interface DocumentChat {\n state: ReadonlyStore<DocumentChatState>\n /**\n * Sends `message` about the editor's current document; `scope` limits proposed edits to the selected blocks (null:\n * whole document). Returns false when it was not answered (error, rate limit, cancelled before any answer): it is\n * then removed again so the caller can restore the input.\n */\n ask: (message: string, editor: Editor, scope: AgentScope | null) => Promise<boolean>\n abort: () => void\n reset: () => void\n /** Accepts or rejects the open hunks of operation `id`. */\n decide: (editor: Editor, id: string, accept: boolean) => void\n /** Scrolls to operation `op`: its first open hunk (selected for accept/reject), else its target block. */\n reveal: (editor: Editor, op: AgentOp) => boolean\n /** Appends the assistant turn of a block generation. `generation` keeps its identity in `state`. */\n addGeneration: (generation: DocumentChatGeneration) => void\n /** Updates `generation` in place (same object) and publishes a new `messages` array. */\n updateGeneration: (generation: DocumentChatGeneration, patch: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) => void\n /** Removes the turn of `generation`. */\n removeGeneration: (generation: DocumentChatGeneration) => void\n clearError: () => void\n}\n\nconst EMPTY: DocumentChatState = { messages: [], ops: NO_AGENT_OPS, streaming: false, error: null }\n\n/**\n * Document chat state: the conversation lives only here (client side) and goes to the backend's `chat` stream in full with every\n * message, together with the document as it is at that moment and the edit scope. Proposed edit operations open an\n * in-document review; their decisions go to the model with the next message.\n */\nexport function createDocumentChat({ ai, modelRef, notify, t }: DocumentChatOptions): DocumentChat {\n const store = createStore<DocumentChatState>(EMPTY)\n const review = createAgentOpReview({ notify: silentNotifier, t })\n let controller: AbortController | null = null\n\n const patch = (changes: Partial<DocumentChatState>) => store.set({ ...store.get(), ...changes })\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(EMPTY)\n review.reset()\n }\n\n /** Replaces the turn `turn` (by identity) with `next`; returns `next`. */\n function replaceTurn<T extends DocumentChatTurn>(turn: DocumentChatTurn, next: T): T {\n const { messages } = store.get()\n const index = messages.indexOf(turn)\n if (index !== -1) patch({ messages: messages.map((m, i) => (i === index ? next : m)) })\n return next\n }\n\n // The proposed operations live in the review (the chat shows their outcomes inline, so it needs no extra notices); its state is the chat's `ops`.\n review.state.subscribe(({ ops }) => {\n if (ops !== store.get().ops) patch({ ops })\n })\n\n async function ask(message: string, editor: Editor, scope: AgentScope | null): Promise<boolean> {\n if (store.get().streaming || !message.trim()) return false\n if (scope && !scope.ids.length) {\n patch({ error: t('ai.chat.emptyScope') })\n return false\n }\n // The block model of the system prompt: the editor's `aiContext` kinds and host blocks, and what `convert` may turn blocks into.\n // Read before any state changes: a merge error in the contributions throws here and must not leave the chat streaming.\n const { kinds, blocks, structure } = aiContextOf(editor)\n const providers = conversionsOf(editor)\n const targets = [\n ...CORE_TARGET_IDS,\n ...kinds.map(kind => convertTargetId({ type: 'composite', kind: kind.id })),\n ...blocks.filter(block => parseConversionTarget(providers, block.id) !== null).map(block => block.id),\n ]\n const own = new AbortController()\n controller = own\n const feedback = review.unreported()\n const userTurn: DocumentChatTurn = { role: 'user', content: message.trim(), ...(feedback.length ? { feedback } : {}) }\n let answer: Extract<DocumentChatTurn, { role: 'assistant' }> = { role: 'assistant', content: '' }\n // The newest chat turns (block generations are the sidebar's own); the request must start with a user message.\n const sent: DocumentChatMessage[] = [...store.get().messages, userTurn].filter(turn => turn.role === 'user' || !turn.generation).slice(-CHAT_LIMITS.messages).map(turn => turn.role === 'user'\n ? { role: 'user', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.feedback && { feedback: turn.feedback }) }\n : { role: 'assistant', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.ops?.length ? { ops: turn.ops } : {}), ...(turn.dropped ? { dropped: turn.dropped } : {}) })\n if (sent[0]?.role === 'assistant') sent.shift()\n patch({ error: null, streaming: true, messages: [...store.get().messages, userTurn, answer] })\n const received: { proposal?: { ops: AgentOp[], dropped: AgentOpProblem[] } } = {}\n const body = { messages: sent, document: documentChatText(editor), scope: scope?.ids ?? null, nextOp: review.nextOp(), model: modelRef('generate'), kinds, blocks, targets, structure }\n const outcome = await postAiSse(ai, 'chat', body, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) answer = replaceTurn(answer, { ...answer, content: answer.content + text })\n },\n onEvent: (event, data) => {\n if (event === 'ops' && controller === own) received.proposal = data as typeof received.proposal\n },\n })\n if (controller !== own) return false // superseded by reset()\n controller = null\n patch({ streaming: false })\n const { ops: proposed = [], dropped = [] } = outcome.status === 'done' ? received.proposal ?? {} : {}\n if (outcome.status === 'done' || (outcome.status === 'aborted' && answer.content)) {\n review.markReported(feedback.filter(({ outcome: sentOutcome }) => sentOutcome !== 'pending').map(({ op }) => op))\n if (proposed.length || dropped.length) {\n answer = replaceTurn(answer, { ...answer, ops: proposed, dropped: dropped.length, problems: dropped })\n review.advanceOp(proposed.length)\n if (!editor.isDestroyed) review.propose(editor, proposed, scope)\n }\n return true\n }\n // Drop the unanswered turn: a later message should not carry a dangling one in its history.\n patch({ messages: store.get().messages.slice(0, -2) })\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n else if (outcome.status === 'error') patch({ error: outcome.message })\n return false\n }\n\n const decide = review.decide\n const reveal = review.reveal\n\n function addGeneration(generation: DocumentChatGeneration) {\n patch({ error: null, messages: [...store.get().messages, { role: 'assistant', content: '', generation }] })\n }\n\n function updateGeneration(generation: DocumentChatGeneration, changes: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) {\n Object.assign(generation, changes)\n const { messages } = store.get()\n patch({ messages: messages.map(turn => turn.role === 'assistant' && turn.generation === generation ? { ...turn } : turn) })\n }\n\n function removeGeneration(generation: DocumentChatGeneration) {\n const { messages } = store.get()\n const index = messages.findIndex(turn => turn.role === 'assistant' && turn.generation === generation)\n if (index !== -1) patch({ messages: messages.filter((_, i) => i !== index) })\n }\n\n function clearError() {\n if (store.get().error !== null) patch({ error: null })\n }\n\n return { state: store, ask, abort, reset, decide, reveal, addGeneration, updateGeneration, removeGeneration, clearError }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"document-chat-model.js","names":[],"sources":["../../../src/features/ai/document-chat-model.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorBackend } from '../../core/backend'\nimport { aiContextOf, CORE_TARGET_IDS, convertTargetId, conversionsOf, parseConversionTarget } from '../../core'\nimport { type Notifier, silentNotifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { ModelRole } from '../../settings'\nimport { type AgentOpReviewState, createAgentOpReview, NO_AGENT_OPS } from './agent-op-review'\nimport type { AgentScope } from './agent-ops'\nimport type { AiTarget } from './ai-apply'\nimport { postAiSse } from './ai-sse'\nimport { documentChatText } from './document-chat'\nimport { type AgentOp, type AgentOpProblem, CHAT_LIMITS, type AiTask, type DocumentChatMessage, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\n/**\n * A block generation run from the AI sidebar's quick actions, shown as an assistant turn. Client only: it never goes\n * to the `chat` stream. `markdown` is the streamed result without the task heading (shown with `withTaskHeading`, applied\n * as `taskResultMarkdown`), `target` the apply target captured when it started; only a result that streamed to the end\n * (`done`) can be applied.\n */\nexport interface DocumentChatGeneration {\n task: AiTask\n /** The instruction of a task with `needsPrompt` (empty otherwise). */\n prompt: string\n markdown: string\n target: AiTarget\n done: boolean\n}\n\n/**\n * A chat turn as shown: an assistant turn also keeps the problems of the operations the server dropped, or is a block\n * generation of the sidebar (`generation`).\n */\nexport type DocumentChatTurn =\n | Extract<DocumentChatMessage, { role: 'user' }>\n | (Extract<DocumentChatMessage, { role: 'assistant' }> & { problems?: AgentOpProblem[], generation?: DocumentChatGeneration })\n\n/** Snapshot of a conversation; every change replaces `messages`/`ops` (never mutated in place). */\nexport interface DocumentChatState {\n messages: readonly DocumentChatTurn[]\n /** Operation states by operation id. */\n ops: AgentOpReviewState['ops']\n streaming: boolean\n error: string | null\n}\n\nexport interface DocumentChatOptions {\n /** `EditorContext.backend.ai`; without it every question ends in the error state (501), like a server without AI. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelRole) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`): error messages, the 429 notice and the snippets of proposed operations. */\n t: Translate\n}\n\nexport interface DocumentChat {\n state: ReadonlyStore<DocumentChatState>\n /**\n * Sends `message` about the editor's current document; `scope` limits proposed edits to the selected blocks (null:\n * whole document). Returns false when it was not answered (error, rate limit, cancelled before any answer): it is\n * then removed again so the caller can restore the input.\n */\n ask: (message: string, editor: Editor, scope: AgentScope | null) => Promise<boolean>\n abort: () => void\n reset: () => void\n /** Accepts or rejects the open hunks of operation `id`. */\n decide: (editor: Editor, id: string, accept: boolean) => void\n /** Scrolls to operation `op`: its first open hunk (selected for accept/reject), else its target block. */\n reveal: (editor: Editor, op: AgentOp) => boolean\n /** Appends the assistant turn of a block generation. `generation` keeps its identity in `state`. */\n addGeneration: (generation: DocumentChatGeneration) => void\n /** Updates `generation` in place (same object) and publishes a new `messages` array. */\n updateGeneration: (generation: DocumentChatGeneration, patch: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) => void\n /** Removes the turn of `generation`. */\n removeGeneration: (generation: DocumentChatGeneration) => void\n clearError: () => void\n}\n\nconst EMPTY: DocumentChatState = { messages: [], ops: NO_AGENT_OPS, streaming: false, error: null }\n\n/**\n * Document chat state: the conversation lives only here (client side) and goes to the backend's `chat` stream in full with every\n * message, together with the document as it is at that moment and the edit scope. Proposed edit operations open an\n * in-document review; their decisions go to the model with the next message.\n */\nexport function createDocumentChat({ ai, modelRef, notify, t }: DocumentChatOptions): DocumentChat {\n const store = createStore<DocumentChatState>(EMPTY)\n const review = createAgentOpReview({ notify: silentNotifier, t })\n let controller: AbortController | null = null\n\n const patch = (changes: Partial<DocumentChatState>) => store.set({ ...store.get(), ...changes })\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(EMPTY)\n review.reset()\n }\n\n /** Replaces the turn `turn` (by identity) with `next`; returns `next`. */\n function replaceTurn<T extends DocumentChatTurn>(turn: DocumentChatTurn, next: T): T {\n const { messages } = store.get()\n const index = messages.indexOf(turn)\n if (index !== -1) patch({ messages: messages.map((m, i) => (i === index ? next : m)) })\n return next\n }\n\n // The proposed operations live in the review (the chat shows their outcomes inline, so it needs no extra notices); its state is the chat's `ops`.\n review.state.subscribe(({ ops }) => {\n if (ops !== store.get().ops) patch({ ops })\n })\n\n async function ask(message: string, editor: Editor, scope: AgentScope | null): Promise<boolean> {\n if (store.get().streaming || !message.trim()) return false\n if (scope && !scope.ids.length) {\n patch({ error: t('ai.chat.emptyScope') })\n return false\n }\n // The block model of the system prompt: the editor's `aiContext` Block types and host blocks, and what `convert` may turn blocks into.\n // Read before any state changes: a merge error in the contributions throws here and must not leave the chat streaming.\n const { blockTypes, blocks, structure } = aiContextOf(editor)\n const providers = conversionsOf(editor)\n const targets = [\n ...CORE_TARGET_IDS,\n ...blockTypes.map(blockType => convertTargetId({ type: 'typedBlock', blockType: blockType.id })),\n ...blocks.filter(block => parseConversionTarget(providers, block.id) !== null).map(block => block.id),\n ]\n const own = new AbortController()\n controller = own\n const feedback = review.unreported()\n const userTurn: DocumentChatTurn = { role: 'user', content: message.trim(), ...(feedback.length ? { feedback } : {}) }\n let answer: Extract<DocumentChatTurn, { role: 'assistant' }> = { role: 'assistant', content: '' }\n // The newest chat turns (block generations are the sidebar's own); the request must start with a user message.\n const sent: DocumentChatMessage[] = [...store.get().messages, userTurn].filter(turn => turn.role === 'user' || !turn.generation).slice(-CHAT_LIMITS.messages).map(turn => turn.role === 'user'\n ? { role: 'user', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.feedback && { feedback: turn.feedback }) }\n : { role: 'assistant', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.ops?.length ? { ops: turn.ops } : {}), ...(turn.dropped ? { dropped: turn.dropped } : {}) })\n if (sent[0]?.role === 'assistant') sent.shift()\n patch({ error: null, streaming: true, messages: [...store.get().messages, userTurn, answer] })\n const received: { proposal?: { ops: AgentOp[], dropped: AgentOpProblem[] } } = {}\n const body = { messages: sent, document: documentChatText(editor), scope: scope?.ids ?? null, nextOp: review.nextOp(), model: modelRef('generate'), blockTypes, blocks, targets, structure }\n const outcome = await postAiSse(ai, 'chat', body, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) answer = replaceTurn(answer, { ...answer, content: answer.content + text })\n },\n onEvent: (event, data) => {\n if (event === 'ops' && controller === own) received.proposal = data as typeof received.proposal\n },\n })\n if (controller !== own) return false // superseded by reset()\n controller = null\n patch({ streaming: false })\n const { ops: proposed = [], dropped = [] } = outcome.status === 'done' ? received.proposal ?? {} : {}\n if (outcome.status === 'done' || (outcome.status === 'aborted' && answer.content)) {\n review.markReported(feedback.filter(({ outcome: sentOutcome }) => sentOutcome !== 'pending').map(({ op }) => op))\n if (proposed.length || dropped.length) {\n answer = replaceTurn(answer, { ...answer, ops: proposed, dropped: dropped.length, problems: dropped })\n review.advanceOp(proposed.length)\n if (!editor.isDestroyed) review.propose(editor, proposed, scope)\n }\n return true\n }\n // Drop the unanswered turn: a later message should not carry a dangling one in its history.\n patch({ messages: store.get().messages.slice(0, -2) })\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n else if (outcome.status === 'error') patch({ error: outcome.message })\n return false\n }\n\n const decide = review.decide\n const reveal = review.reveal\n\n function addGeneration(generation: DocumentChatGeneration) {\n patch({ error: null, messages: [...store.get().messages, { role: 'assistant', content: '', generation }] })\n }\n\n function updateGeneration(generation: DocumentChatGeneration, changes: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) {\n Object.assign(generation, changes)\n const { messages } = store.get()\n patch({ messages: messages.map(turn => turn.role === 'assistant' && turn.generation === generation ? { ...turn } : turn) })\n }\n\n function removeGeneration(generation: DocumentChatGeneration) {\n const { messages } = store.get()\n const index = messages.findIndex(turn => turn.role === 'assistant' && turn.generation === generation)\n if (index !== -1) patch({ messages: messages.filter((_, i) => i !== index) })\n }\n\n function clearError() {\n if (store.get().error !== null) patch({ error: null })\n }\n\n return { state: store, ask, abort, reset, decide, reveal, addGeneration, updateGeneration, removeGeneration, clearError }\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, singleBlockLine } from "./protocol/block-lines.js";
2
2
  import { CHAT_LIMITS, CITATION_HREF_PREFIX, citationBlockId } from "./protocol/chat.js";
3
- import { parseBlocks } from "../../core/pipeline.js";
4
3
  import { blockIdOf, findBlockById } from "../../core/schema/block-ids.js";
5
4
  import { isBlockContainer, isChromeNode } from "../../core/schema/node-flags.js";
5
+ import { parseBlocks } from "../../core/pipeline.js";
6
6
  import { isWrapperBlock } from "../../core/convert.js";
7
7
  import { Fragment } from "@tiptap/pm/model";
8
8
  //#region src/features/ai/document-chat.ts
@@ -44,7 +44,7 @@ function documentChatText(editor) {
44
44
  node.forEach((child) => {
45
45
  if (isChromeNode(child)) chrome.push(compositeChromePart(markdownOf(child)));
46
46
  });
47
- push(blockLine(id, prefix, compositeLineHeader(String(node.attrs.kind), node.attrs.meta, chrome)));
47
+ push(blockLine(id, prefix, compositeLineHeader(String(node.attrs.blockType), node.attrs.meta, chrome)));
48
48
  visit(node, nestedLinePrefix(prefix), id);
49
49
  push(compositeLineClose(prefix));
50
50
  };
@@ -1 +1 @@
1
- {"version":3,"file":"document-chat.js","names":[],"sources":["../../../src/features/ai/document-chat.ts"],"sourcesContent":["import type { Editor, JSONContent } from '@tiptap/core'\nimport { Fragment, type Node as PMNode } from '@tiptap/pm/model'\nimport { isWrapperBlock, parseBlocks } from '../../core'\nimport { blockIdOf, findBlockById, isBlockContainer, isChromeNode } from '../../core/schema'\nimport { blockLine, CHAT_LIMITS, CITATION_HREF_PREFIX, citationBlockId, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, singleBlockLine, TRUNCATED_LINE } from './protocol'\n\n/**\n * The document as sent to the chat, in the line format of `protocol/block-lines.ts` (which also reads and describes\n * it): one line per text block, `[<block id>] <Markdown>` (e.g. `[…] **Anna:** Text`), with list markers and quote\n * prefixes after the ID. Nested blocks carry their own IDs, so quotes from list items or\n * block quotes link to exactly that paragraph. A block container below the document (a composite: the schema flags it\n * `editorBlockContainer`, its title/description/tags rows `editorChrome`) is a header line `[<id>] ::: <kind> {meta} |\n * <title> | <description> | Tags: #a` (empty parts left out), its body blocks two spaces further in, and a closing\n * `:::` line without ID at that indent. Any other non-list block with block content (a host block such as a callout) is\n * a header line `[<id>] <first line of its Markdown form without content>` (`!!! info`) with its body blocks two spaces\n * further in, each with its own ID and no closing line; if that header is empty, only the body blocks appear. Cut at\n * whole lines to fit `CHAT_LIMITS.document`.\n */\nexport function documentChatText(editor: Editor): string {\n const lines: string[] = []\n let size = 0\n let full = false\n\n const push = (line: string) => {\n if (full || size + line.length + 1 > CHAT_LIMITS.document - TRUNCATED_LINE.length - 1) {\n full = true\n return\n }\n lines.push(line)\n size += line.length + 1\n }\n const markdownOf = (node: PMNode) => editor.markdown!.serialize({ type: 'doc', content: [node.toJSON()] }).trim()\n\n const emit = (node: PMNode, prefix: string, id: string | null) => {\n const markdown = markdownOf(node)\n if (!markdown) return\n // Hard breaks and wrapped lines would start new \"blocks\"; code keeps its lines.\n push(blockLine(id, prefix, node.type.name === 'codeBlock' ? markdown : singleBlockLine(markdown)))\n }\n\n const container = (node: PMNode, prefix: string, id: string | null) => {\n const chrome: string[] = []\n // The chrome's own Markdown (`## title`, `> description`, `Tags: #a`) without the block markers.\n node.forEach((child) => {\n if (isChromeNode(child)) chrome.push(compositeChromePart(markdownOf(child)))\n })\n push(blockLine(id, prefix, compositeLineHeader(String(node.attrs.kind), node.attrs.meta, chrome)))\n visit(node, nestedLinePrefix(prefix), id)\n push(compositeLineClose(prefix))\n }\n\n // A host block with block content (a callout): its own Markdown form without content as the header line, the body\n // blocks two spaces further in, each with its own ID, no closing line (the form has none).\n const wrapper = (node: PMNode, prefix: string, id: string | null) => {\n const header = markdownOf(node.copy(Fragment.empty)).split('\\n')[0] ?? ''\n if (header) push(blockLine(id, prefix, header))\n visit(node, header ? nestedLinePrefix(prefix) : prefix, id)\n }\n\n const visit = (node: PMNode, prefix: string, parentId: string | null) => {\n let current = prefix\n node.forEach((child, _offset, index) => {\n if (full || isChromeNode(child)) return\n const id = blockIdOf(child) ?? parentId\n if (child.isTextblock || child.isLeaf) emit(child, current, id)\n else if (isBlockContainer(child)) container(child, current, id)\n else if (child.type.name === 'blockquote') visit(child, quoteLinePrefix(current), id)\n else if (child.type.name === 'bulletList' || child.type.name === 'orderedList') {\n const start = Number(child.attrs.start ?? 1)\n child.forEach((item, _o, i) => visit(item, listLinePrefix(current, child.type.name === 'orderedList', start + i), id))\n }\n else if (isWrapperBlock(child)) wrapper(child, current, id)\n else visit(child, current, id)\n // Later blocks of the same list item continue under its marker instead of repeating it.\n if (index === 0) current = continuedLinePrefix(prefix)\n })\n }\n\n visit(editor.state.doc, '', null)\n if (full) lines.push(TRUNCATED_LINE)\n return lines.join('\\n')\n}\n\n/**\n * Chat answer Markdown parsed for display with the pipeline of `editor` (speaker lines become segments where a feature\n * contributes them): quote links whose block exists in `doc` point to `#block-<id>`, and quote links to unknown blocks\n * lose the link (plain text).\n */\nexport function documentChatAnswerContent(editor: Editor, markdown: string, doc: PMNode | undefined): JSONContent {\n const resolve = (node: JSONContent): JSONContent => {\n const marks = node.marks?.flatMap((mark) => {\n if (mark.type !== 'link') return [mark]\n const id = citationBlockId(mark.attrs?.href)\n if (id === null) return [mark]\n return doc && findBlockById(doc, id) ? [{ ...mark, attrs: { ...mark.attrs, href: `${CITATION_HREF_PREFIX}${id}` } }] : []\n })\n return { ...node, ...(marks && { marks }), ...(node.content && { content: node.content.map(resolve) }) }\n }\n return resolve({ type: 'doc', content: parseBlocks(editor, markdown).blocks })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"document-chat.js","names":[],"sources":["../../../src/features/ai/document-chat.ts"],"sourcesContent":["import type { Editor, JSONContent } from '@tiptap/core'\nimport { Fragment, type Node as PMNode } from '@tiptap/pm/model'\nimport { isWrapperBlock, parseBlocks } from '../../core'\nimport { blockIdOf, findBlockById, isBlockContainer, isChromeNode } from '../../core/schema'\nimport { blockLine, CHAT_LIMITS, CITATION_HREF_PREFIX, citationBlockId, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, singleBlockLine, TRUNCATED_LINE } from './protocol'\n\n/**\n * The document as sent to the chat, in the line format of `protocol/block-lines.ts` (which also reads and describes\n * it): one line per text block, `[<block id>] <Markdown>` (e.g. `[…] **Anna:** Text`), with list markers and quote\n * prefixes after the ID. Nested blocks carry their own IDs, so quotes from list items or\n * block quotes link to exactly that paragraph. A block container below the document (a composite: the schema flags it\n * `editorBlockContainer`, its title/description/tags rows `editorChrome`) is a header line `[<id>] ::: <kind> {meta} |\n * <title> | <description> | Tags: #a` (empty parts left out), its body blocks two spaces further in, and a closing\n * `:::` line without ID at that indent. Any other non-list block with block content (a host block such as a callout) is\n * a header line `[<id>] <first line of its Markdown form without content>` (`!!! info`) with its body blocks two spaces\n * further in, each with its own ID and no closing line; if that header is empty, only the body blocks appear. Cut at\n * whole lines to fit `CHAT_LIMITS.document`.\n */\nexport function documentChatText(editor: Editor): string {\n const lines: string[] = []\n let size = 0\n let full = false\n\n const push = (line: string) => {\n if (full || size + line.length + 1 > CHAT_LIMITS.document - TRUNCATED_LINE.length - 1) {\n full = true\n return\n }\n lines.push(line)\n size += line.length + 1\n }\n const markdownOf = (node: PMNode) => editor.markdown!.serialize({ type: 'doc', content: [node.toJSON()] }).trim()\n\n const emit = (node: PMNode, prefix: string, id: string | null) => {\n const markdown = markdownOf(node)\n if (!markdown) return\n // Hard breaks and wrapped lines would start new \"blocks\"; code keeps its lines.\n push(blockLine(id, prefix, node.type.name === 'codeBlock' ? markdown : singleBlockLine(markdown)))\n }\n\n const container = (node: PMNode, prefix: string, id: string | null) => {\n const chrome: string[] = []\n // The chrome's own Markdown (`## title`, `> description`, `Tags: #a`) without the block markers.\n node.forEach((child) => {\n if (isChromeNode(child)) chrome.push(compositeChromePart(markdownOf(child)))\n })\n push(blockLine(id, prefix, compositeLineHeader(String(node.attrs.blockType), node.attrs.meta, chrome)))\n visit(node, nestedLinePrefix(prefix), id)\n push(compositeLineClose(prefix))\n }\n\n // A host block with block content (a callout): its own Markdown form without content as the header line, the body\n // blocks two spaces further in, each with its own ID, no closing line (the form has none).\n const wrapper = (node: PMNode, prefix: string, id: string | null) => {\n const header = markdownOf(node.copy(Fragment.empty)).split('\\n')[0] ?? ''\n if (header) push(blockLine(id, prefix, header))\n visit(node, header ? nestedLinePrefix(prefix) : prefix, id)\n }\n\n const visit = (node: PMNode, prefix: string, parentId: string | null) => {\n let current = prefix\n node.forEach((child, _offset, index) => {\n if (full || isChromeNode(child)) return\n const id = blockIdOf(child) ?? parentId\n if (child.isTextblock || child.isLeaf) emit(child, current, id)\n else if (isBlockContainer(child)) container(child, current, id)\n else if (child.type.name === 'blockquote') visit(child, quoteLinePrefix(current), id)\n else if (child.type.name === 'bulletList' || child.type.name === 'orderedList') {\n const start = Number(child.attrs.start ?? 1)\n child.forEach((item, _o, i) => visit(item, listLinePrefix(current, child.type.name === 'orderedList', start + i), id))\n }\n else if (isWrapperBlock(child)) wrapper(child, current, id)\n else visit(child, current, id)\n // Later blocks of the same list item continue under its marker instead of repeating it.\n if (index === 0) current = continuedLinePrefix(prefix)\n })\n }\n\n visit(editor.state.doc, '', null)\n if (full) lines.push(TRUNCATED_LINE)\n return lines.join('\\n')\n}\n\n/**\n * Chat answer Markdown parsed for display with the pipeline of `editor` (speaker lines become segments where a feature\n * contributes them): quote links whose block exists in `doc` point to `#block-<id>`, and quote links to unknown blocks\n * lose the link (plain text).\n */\nexport function documentChatAnswerContent(editor: Editor, markdown: string, doc: PMNode | undefined): JSONContent {\n const resolve = (node: JSONContent): JSONContent => {\n const marks = node.marks?.flatMap((mark) => {\n if (mark.type !== 'link') return [mark]\n const id = citationBlockId(mark.attrs?.href)\n if (id === null) return [mark]\n return doc && findBlockById(doc, id) ? [{ ...mark, attrs: { ...mark.attrs, href: `${CITATION_HREF_PREFIX}${id}` } }] : []\n })\n return { ...node, ...(marks && { marks }), ...(node.content && { content: node.content.map(resolve) }) }\n }\n return resolve({ type: 'doc', content: parseBlocks(editor, markdown).blocks })\n}\n"],"mappings":"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"}
@@ -14,9 +14,9 @@ export interface AiUi<C> {
14
14
  * AI on a core editor: the selection menu and `/ai` (rewrite and generate at the caret), in-document review of
15
15
  * rewrites, ghost autocomplete, the model profiles and the `ai` settings tab, the „Mit KI bearbeiten“ block action, the
16
16
  * toolbar group (sidebar toggle, autocomplete switch) and Mod-Shift-k. The sidebar (`AiSidebar`) and the model switch
17
- * are components the host places. Reads the document model through the merged `aiContext` (kinds, host blocks,
17
+ * are components the host places. Reads the document model through the merged `aiContext` (Block types, host blocks,
18
18
  * structure) and the `formatAiResult` contribution of other
19
- * features (defaults: no kinds, no blocks, flat structure) and maps positions through the preset's anchor resolver, so it imports
19
+ * features (defaults: no Block types, no blocks, flat structure) and maps positions through the preset's anchor resolver, so it imports
20
20
  * no composites, transcript, media, mentions, collaboration or Yjs code. The model profiles come from the app context
21
21
  * (`modelProfiles(ctx.app)`), shared by all editors of the app.
22
22
  */
@@ -60,9 +60,9 @@ function aiCommands(ctx) {
60
60
  * AI on a core editor: the selection menu and `/ai` (rewrite and generate at the caret), in-document review of
61
61
  * rewrites, ghost autocomplete, the model profiles and the `ai` settings tab, the „Mit KI bearbeiten“ block action, the
62
62
  * toolbar group (sidebar toggle, autocomplete switch) and Mod-Shift-k. The sidebar (`AiSidebar`) and the model switch
63
- * are components the host places. Reads the document model through the merged `aiContext` (kinds, host blocks,
63
+ * are components the host places. Reads the document model through the merged `aiContext` (Block types, host blocks,
64
64
  * structure) and the `formatAiResult` contribution of other
65
- * features (defaults: no kinds, no blocks, flat structure) and maps positions through the preset's anchor resolver, so it imports
65
+ * features (defaults: no Block types, no blocks, flat structure) and maps positions through the preset's anchor resolver, so it imports
66
66
  * no composites, transcript, media, mentions, collaboration or Yjs code. The model profiles come from the app context
67
67
  * (`modelProfiles(ctx.app)`), shared by all editors of the app.
68
68
  */