@ecsplico/editor 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +7 -7
  3. package/dist/adapters/anchors.d.ts +80 -0
  4. package/dist/adapters/anchors.js +270 -0
  5. package/dist/adapters/anchors.js.map +1 -0
  6. package/dist/adapters/comments/client.d.ts +64 -0
  7. package/dist/adapters/comments/client.js +536 -0
  8. package/dist/adapters/comments/client.js.map +1 -0
  9. package/dist/adapters/comments/drizzle.d.ts +430 -0
  10. package/dist/adapters/comments/drizzle.js +236 -0
  11. package/dist/adapters/comments/drizzle.js.map +1 -0
  12. package/dist/adapters/comments/errors.d.ts +9 -0
  13. package/dist/adapters/comments/errors.js +19 -0
  14. package/dist/adapters/comments/errors.js.map +1 -0
  15. package/dist/adapters/comments/json.d.ts +19 -0
  16. package/dist/adapters/comments/json.js +94 -0
  17. package/dist/adapters/comments/json.js.map +1 -0
  18. package/dist/adapters/comments/record.js +78 -0
  19. package/dist/adapters/comments/record.js.map +1 -0
  20. package/dist/adapters/comments/server.d.ts +23 -0
  21. package/dist/adapters/comments/server.js +102 -0
  22. package/dist/adapters/comments/server.js.map +1 -0
  23. package/dist/adapters/comments/types.d.ts +109 -0
  24. package/dist/adapters/documents.js +108 -0
  25. package/dist/adapters/documents.js.map +1 -0
  26. package/dist/adapters/drizzle.d.ts +293 -0
  27. package/dist/adapters/drizzle.js +157 -0
  28. package/dist/adapters/drizzle.js.map +1 -0
  29. package/dist/adapters/files.js +30 -0
  30. package/dist/adapters/files.js.map +1 -0
  31. package/dist/adapters/forge.d.ts +58 -0
  32. package/dist/adapters/forge.js +91 -0
  33. package/dist/adapters/forge.js.map +1 -0
  34. package/dist/adapters/index.d.ts +13 -0
  35. package/dist/adapters/index.js +0 -0
  36. package/dist/adapters/json.d.ts +24 -0
  37. package/dist/adapters/json.js +128 -0
  38. package/dist/adapters/json.js.map +1 -0
  39. package/dist/adapters/nitro.d.ts +61 -0
  40. package/dist/adapters/nitro.js +212 -0
  41. package/dist/adapters/nitro.js.map +1 -0
  42. package/dist/adapters/routes.js +16 -0
  43. package/dist/adapters/routes.js.map +1 -0
  44. package/dist/adapters/shared.d.ts +37 -0
  45. package/dist/adapters/shared.js +32 -0
  46. package/dist/adapters/shared.js.map +1 -0
  47. package/dist/adapters/vue.d.ts +67 -0
  48. package/dist/adapters/vue.js +187 -0
  49. package/dist/adapters/vue.js.map +1 -0
  50. package/dist/code/CodeSurface.vue.d.ts +3 -3
  51. package/dist/core/doc-anchors.d.ts +5 -0
  52. package/dist/core/doc-anchors.js +4 -3
  53. package/dist/core/doc-anchors.js.map +1 -1
  54. package/dist/core/editor-ui.js +57 -1
  55. package/dist/core/editor-ui.js.map +1 -1
  56. package/dist/core/notify.d.ts +1 -1
  57. package/dist/core/notify.js.map +1 -1
  58. package/dist/core/surface-session.js +139 -0
  59. package/dist/core/surface-session.js.map +1 -0
  60. package/dist/document/DocumentSurface.d.ts +1 -1
  61. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  62. package/dist/features/ai/agent-op-review.d.ts +2 -2
  63. package/dist/features/ai/agent-op-review.js.map +1 -1
  64. package/dist/features/ai/agent-ops.d.ts +1 -1
  65. package/dist/features/ai/agent-ops.js +9 -6
  66. package/dist/features/ai/agent-ops.js.map +1 -1
  67. package/dist/features/ai/ai-apply.d.ts +1 -1
  68. package/dist/features/ai/ai-review.js +10 -1
  69. package/dist/features/ai/ai-review.js.map +1 -1
  70. package/dist/features/ai/components/AiSelectionMenu.js +78 -252
  71. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
  72. package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +2 -1
  73. package/dist/features/ai/components/AiSidebar.js +2 -2
  74. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  75. package/dist/features/ai/components/DocumentChatOps.js +1 -0
  76. package/dist/features/ai/components/DocumentChatOps.js.map +1 -1
  77. package/dist/features/ai/components/ModelQuickSwitch.js +1 -1
  78. package/dist/features/ai/components/SettingsAiTab.js +1 -1
  79. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  80. package/dist/features/ai/document-chat.d.ts +3 -2
  81. package/dist/features/ai/document-chat.js +16 -19
  82. package/dist/features/ai/document-chat.js.map +1 -1
  83. package/dist/features/ai/index.d.ts +5 -3
  84. package/dist/features/ai/index.js +4 -2
  85. package/dist/features/ai/protocol/agent-ops.d.ts +22 -17
  86. package/dist/features/ai/protocol/agent-ops.js +65 -39
  87. package/dist/features/ai/protocol/agent-ops.js.map +1 -1
  88. package/dist/features/ai/protocol/block-lines.d.ts +89 -0
  89. package/dist/features/ai/protocol/block-lines.js +177 -0
  90. package/dist/features/ai/protocol/block-lines.js.map +1 -0
  91. package/dist/features/ai/protocol/chat.js +10 -10
  92. package/dist/features/ai/protocol/chat.js.map +1 -1
  93. package/dist/features/ai/protocol/index.d.ts +3 -2
  94. package/dist/features/ai/protocol/index.js +3 -2
  95. package/dist/features/ai/react/AiSelectionMenu.d.ts +3 -2
  96. package/dist/features/ai/react/AiSelectionMenu.js +64 -255
  97. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
  98. package/dist/features/ai/react/AiSidebar.js +2 -2
  99. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  100. package/dist/features/ai/react/DocumentChatOps.js +1 -0
  101. package/dist/features/ai/react/DocumentChatOps.js.map +1 -1
  102. package/dist/features/ai/react/ModelQuickSwitch.js +1 -1
  103. package/dist/features/ai/react/SettingsAiTab.js +1 -1
  104. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  105. package/dist/features/ai/react/use-ai-selection-flow.js +34 -0
  106. package/dist/features/ai/react/use-ai-selection-flow.js.map +1 -0
  107. package/dist/features/ai/selection-flow.d.ts +133 -0
  108. package/dist/features/ai/selection-flow.js +283 -0
  109. package/dist/features/ai/selection-flow.js.map +1 -0
  110. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  111. package/dist/features/ai/use-document-chat.d.ts +1 -1
  112. package/dist/features/authors/components/AuthorColorsPanel.js +1 -1
  113. package/dist/features/authors/feature.js +1 -1
  114. package/dist/features/authors/index.js +1 -1
  115. package/dist/features/authors/react/AuthorColorsPanel.js +1 -1
  116. package/dist/features/comments/components/CommentBubble.js +1 -1
  117. package/dist/features/comments/components/CommentComposer.js +1 -1
  118. package/dist/features/comments/components/CommentThreadView.js +5 -2
  119. package/dist/features/comments/components/CommentThreadView.js.map +1 -1
  120. package/dist/features/comments/components/CommentsMargin.js +108 -230
  121. package/dist/features/comments/components/CommentsMargin.js.map +1 -1
  122. package/dist/features/comments/components/CommentsOverlay.js +1 -1
  123. package/dist/features/comments/feature.js +1 -1
  124. package/dist/features/comments/index.d.ts +4 -2
  125. package/dist/features/comments/index.js +4 -2
  126. package/dist/features/comments/margin-controller.d.ts +44 -0
  127. package/dist/features/comments/margin-controller.js +132 -0
  128. package/dist/features/comments/margin-controller.js.map +1 -0
  129. package/dist/features/comments/margin-layout.d.ts +55 -0
  130. package/dist/features/comments/margin-layout.js +79 -0
  131. package/dist/features/comments/margin-layout.js.map +1 -0
  132. package/dist/features/comments/model.d.ts +8 -0
  133. package/dist/features/comments/model.js +60 -34
  134. package/dist/features/comments/model.js.map +1 -1
  135. package/dist/features/comments/react/CommentBubble.js +1 -1
  136. package/dist/features/comments/react/CommentComposer.js +1 -1
  137. package/dist/features/comments/react/CommentThreadView.js +2 -2
  138. package/dist/features/comments/react/CommentThreadView.js.map +1 -1
  139. package/dist/features/comments/react/CommentsMargin.d.ts +0 -5
  140. package/dist/features/comments/react/CommentsMargin.js +85 -251
  141. package/dist/features/comments/react/CommentsMargin.js.map +1 -1
  142. package/dist/features/comments/react/CommentsOverlay.js +1 -1
  143. package/dist/features/comments/react/use-comments.js +1 -1
  144. package/dist/features/comments/react/use-margin-layout.js +45 -0
  145. package/dist/features/comments/react/use-margin-layout.js.map +1 -0
  146. package/dist/features/comments/react.d.ts +1 -1
  147. package/dist/features/comments/schema.d.ts +23 -0
  148. package/dist/features/comments/schema.js.map +1 -1
  149. package/dist/features/comments/snapshot-equality.js +40 -0
  150. package/dist/features/comments/snapshot-equality.js.map +1 -0
  151. package/dist/features/comments/thread-anchor.js +33 -0
  152. package/dist/features/comments/thread-anchor.js.map +1 -0
  153. package/dist/features/comments/use-comments.d.ts +3 -1
  154. package/dist/features/comments/use-comments.js +1 -0
  155. package/dist/features/comments/use-comments.js.map +1 -1
  156. package/dist/features/comments/use-margin-layout.js +29 -0
  157. package/dist/features/comments/use-margin-layout.js.map +1 -0
  158. package/dist/features/comments/vue.d.ts +1 -1
  159. package/dist/features/composites/accepted-children.d.ts +21 -0
  160. package/dist/features/composites/accepted-children.js +126 -0
  161. package/dist/features/composites/accepted-children.js.map +1 -0
  162. package/dist/features/composites/components/CompositeDetails.js +1 -1
  163. package/dist/features/composites/components/CompositeInspector.js +38 -164
  164. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  165. package/dist/features/composites/feature.d.ts +2 -1
  166. package/dist/features/composites/feature.js +8 -2
  167. package/dist/features/composites/feature.js.map +1 -1
  168. package/dist/features/composites/index.d.ts +5 -3
  169. package/dist/features/composites/index.js +8 -6
  170. package/dist/features/composites/inspector-model.d.ts +97 -0
  171. package/dist/features/composites/inspector-model.js +255 -0
  172. package/dist/features/composites/inspector-model.js.map +1 -0
  173. package/dist/features/composites/kinds.d.ts +11 -0
  174. package/dist/features/composites/kinds.js +17 -2
  175. package/dist/features/composites/kinds.js.map +1 -1
  176. package/dist/features/composites/outline.d.ts +18 -5
  177. package/dist/features/composites/outline.js +44 -12
  178. package/dist/features/composites/outline.js.map +1 -1
  179. package/dist/features/composites/react/CompositeDetails.js +1 -1
  180. package/dist/features/composites/react/CompositeInspector.d.ts +1 -1
  181. package/dist/features/composites/react/CompositeInspector.js +21 -180
  182. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  183. package/dist/features/composites/react/CompositeStatusMenu.d.ts +1 -1
  184. package/dist/features/composites/react/CompositeView.js +1 -1
  185. package/dist/features/composites/react/use-composite-inspector.js +29 -0
  186. package/dist/features/composites/react/use-composite-inspector.js.map +1 -0
  187. package/dist/features/composites/schema.d.ts +2 -2
  188. package/dist/features/composites/schema.js +2 -2
  189. package/dist/features/composites/schema.js.map +1 -1
  190. package/dist/features/composites/use-composite-anchors.d.ts +1 -1
  191. package/dist/features/composites/use-composite-inspector.js +41 -0
  192. package/dist/features/composites/use-composite-inspector.js.map +1 -0
  193. package/dist/features/composites/use-composite-rows.d.ts +1 -1
  194. package/dist/features/media/index.d.ts +1 -1
  195. package/dist/features/media/vue.d.ts +1 -1
  196. package/dist/features/mentions/index.d.ts +3 -3
  197. package/dist/features/mentions/index.js +3 -3
  198. package/dist/features/mentions/schema.d.ts +1 -1
  199. package/dist/features/mentions/schema.js +1 -1
  200. package/dist/features/themes/index.d.ts +1 -1
  201. package/dist/features/themes/index.js +1 -1
  202. package/dist/features/transcript/feature.js +1 -1
  203. package/dist/features/transcript/index.js +2 -2
  204. package/dist/i18n/de/ai.js +2 -0
  205. package/dist/i18n/de/ai.js.map +1 -1
  206. package/dist/i18n/de/common.js +1 -2
  207. package/dist/i18n/de/common.js.map +1 -1
  208. package/dist/i18n/de/composites.js +8 -1
  209. package/dist/i18n/de/composites.js.map +1 -1
  210. package/dist/i18n/de/tree.js +14 -1
  211. package/dist/i18n/de/tree.js.map +1 -1
  212. package/dist/i18n/de.d.ts +22 -1
  213. package/dist/i18n/en/ai.js +2 -0
  214. package/dist/i18n/en/ai.js.map +1 -1
  215. package/dist/i18n/en/common.js +1 -2
  216. package/dist/i18n/en/common.js.map +1 -1
  217. package/dist/i18n/en/composites.js +8 -1
  218. package/dist/i18n/en/composites.js.map +1 -1
  219. package/dist/i18n/en/tree.js +14 -1
  220. package/dist/i18n/en/tree.js.map +1 -1
  221. package/dist/integrations/composite-ai/composite-fill.js +2 -2
  222. package/dist/integrations/composite-ai/composite-summary.js +2 -2
  223. package/dist/integrations/composite-ai/index.js +2 -2
  224. package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
  225. package/dist/markdown/MarkdownSurface.d.ts +1 -1
  226. package/dist/markdown/MarkdownSurface.js +2 -2
  227. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  228. package/dist/markdown/index.d.ts +2 -2
  229. package/dist/nuxt/module.d.ts +0 -6
  230. package/dist/nuxt/module.js +3 -5
  231. package/dist/nuxt/module.js.map +1 -1
  232. package/dist/nuxt/runtime/install-context.d.ts +2 -3
  233. package/dist/nuxt/runtime/install-context.js +2 -3
  234. package/dist/nuxt/runtime/install-context.js.map +1 -1
  235. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +1 -1
  236. package/dist/nuxt/runtime/server/routes/collab/[id].js +1 -1
  237. package/dist/react/EditorSurface.d.ts +1 -1
  238. package/dist/react/EditorSurface.js +43 -149
  239. package/dist/react/EditorSurface.js.map +1 -1
  240. package/dist/react/EditorToolbar.js +3 -40
  241. package/dist/react/EditorToolbar.js.map +1 -1
  242. package/dist/react/styles/ui-vars.css +2 -2
  243. package/dist/schema/advanced.js +2 -2
  244. package/dist/server/adapter.d.ts +2 -2
  245. package/dist/server/collab.js +1 -1
  246. package/dist/server/handlers/ai.d.ts +4 -2
  247. package/dist/server/handlers/ai.js +11 -9
  248. package/dist/server/handlers/ai.js.map +1 -1
  249. package/dist/server/handlers/context.d.ts +1 -1
  250. package/dist/server/handlers/editor.d.ts +1 -1
  251. package/dist/server/handlers/editor.js +2 -2
  252. package/dist/server/host.d.ts +1 -1
  253. package/dist/server/index.d.ts +2 -2
  254. package/dist/settings/index.d.ts +1 -1
  255. package/dist/styles/ai.css +1 -1
  256. package/dist/styles/authors.css +1 -1
  257. package/dist/styles/block-ui.css +1 -1
  258. package/dist/styles/collab.css +1 -1
  259. package/dist/styles/comments.css +1 -1
  260. package/dist/styles/core.css +2 -2
  261. package/dist/styles/media.css +1 -1
  262. package/dist/styles/mentions.css +1 -1
  263. package/dist/styles/themes.css +1 -1
  264. package/dist/styles/tokens.css +1 -1
  265. package/dist/styles/transcript.css +1 -1
  266. package/dist/tree/DocumentTree.js +159 -387
  267. package/dist/tree/DocumentTree.js.map +1 -1
  268. package/dist/tree/drop-rules.js +37 -0
  269. package/dist/tree/drop-rules.js.map +1 -0
  270. package/dist/tree/react/DocumentTree.d.ts +2 -2
  271. package/dist/tree/react/DocumentTree.js +135 -397
  272. package/dist/tree/react/DocumentTree.js.map +1 -1
  273. package/dist/tree/react/use-tree-model.js +129 -0
  274. package/dist/tree/react/use-tree-model.js.map +1 -0
  275. package/dist/tree/react.js +1 -1
  276. package/dist/tree/tree-host.js +100 -0
  277. package/dist/tree/tree-host.js.map +1 -0
  278. package/dist/tree/tree-items.js +22 -43
  279. package/dist/tree/tree-items.js.map +1 -1
  280. package/dist/tree/tree-model.js +580 -0
  281. package/dist/tree/tree-model.js.map +1 -0
  282. package/dist/tree/tree-rows.js +77 -0
  283. package/dist/tree/tree-rows.js.map +1 -0
  284. package/dist/tree/use-tree-model.js +94 -0
  285. package/dist/tree/use-tree-model.js.map +1 -0
  286. package/dist/tree/vue.js +1 -1
  287. package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
  288. package/dist/vue/SimpleEditor.vue.d.ts +2 -2
  289. package/dist/vue/components/EditorSurface.js +27 -87
  290. package/dist/vue/components/EditorSurface.js.map +1 -1
  291. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  292. package/dist/vue/components/EditorToolbar.js +7 -44
  293. package/dist/vue/components/EditorToolbar.js.map +1 -1
  294. package/dist/vue/context.d.ts +4 -4
  295. package/dist/vue/context.js +3 -2
  296. package/dist/vue/context.js.map +1 -1
  297. package/dist/vue/kit/types.d.ts +1 -1
  298. package/dist/vue/kit/use-ui-kit.d.ts +2 -2
  299. package/dist/vue/kit/use-ui-kit.js +3 -3
  300. package/dist/vue/kit/use-ui-kit.js.map +1 -1
  301. package/dist/vue/use-editor-ui.d.ts +1 -1
  302. package/dist/vue/use-notifier.d.ts +1 -1
  303. package/docs/integration.md +98 -5
  304. package/docs/llms.md +10 -2
  305. package/docs/usage.md +10 -5
  306. package/package.json +70 -23
  307. package/dist/nuxt/runtime/plugins/editor-context-primevue.client.ts +0 -8
  308. package/dist/vue/kit/primevue/Alert.js +0 -49
  309. package/dist/vue/kit/primevue/Alert.js.map +0 -1
  310. package/dist/vue/kit/primevue/Avatar.js +0 -54
  311. package/dist/vue/kit/primevue/Avatar.js.map +0 -1
  312. package/dist/vue/kit/primevue/Badge.js +0 -41
  313. package/dist/vue/kit/primevue/Badge.js.map +0 -1
  314. package/dist/vue/kit/primevue/Button.js +0 -112
  315. package/dist/vue/kit/primevue/Button.js.map +0 -1
  316. package/dist/vue/kit/primevue/Chip.js +0 -38
  317. package/dist/vue/kit/primevue/Chip.js.map +0 -1
  318. package/dist/vue/kit/primevue/DropdownMenu.js +0 -98
  319. package/dist/vue/kit/primevue/DropdownMenu.js.map +0 -1
  320. package/dist/vue/kit/primevue/FieldGroup.js +0 -22
  321. package/dist/vue/kit/primevue/FieldGroup.js.map +0 -1
  322. package/dist/vue/kit/primevue/FormField.js +0 -56
  323. package/dist/vue/kit/primevue/FormField.js.map +0 -1
  324. package/dist/vue/kit/primevue/Icon.js +0 -23
  325. package/dist/vue/kit/primevue/Icon.js.map +0 -1
  326. package/dist/vue/kit/primevue/Input.js +0 -96
  327. package/dist/vue/kit/primevue/Input.js.map +0 -1
  328. package/dist/vue/kit/primevue/InputMenu.js +0 -122
  329. package/dist/vue/kit/primevue/InputMenu.js.map +0 -1
  330. package/dist/vue/kit/primevue/Modal.js +0 -59
  331. package/dist/vue/kit/primevue/Modal.js.map +0 -1
  332. package/dist/vue/kit/primevue/Popover.js +0 -135
  333. package/dist/vue/kit/primevue/Popover.js.map +0 -1
  334. package/dist/vue/kit/primevue/RadioGroup.js +0 -58
  335. package/dist/vue/kit/primevue/RadioGroup.js.map +0 -1
  336. package/dist/vue/kit/primevue/Select.js +0 -68
  337. package/dist/vue/kit/primevue/Select.js.map +0 -1
  338. package/dist/vue/kit/primevue/SelectMenu.js +0 -94
  339. package/dist/vue/kit/primevue/SelectMenu.js.map +0 -1
  340. package/dist/vue/kit/primevue/Separator.js +0 -16
  341. package/dist/vue/kit/primevue/Separator.js.map +0 -1
  342. package/dist/vue/kit/primevue/Switch.js +0 -46
  343. package/dist/vue/kit/primevue/Switch.js.map +0 -1
  344. package/dist/vue/kit/primevue/Table.js +0 -54
  345. package/dist/vue/kit/primevue/Table.js.map +0 -1
  346. package/dist/vue/kit/primevue/Tabs.js +0 -49
  347. package/dist/vue/kit/primevue/Tabs.js.map +0 -1
  348. package/dist/vue/kit/primevue/Textarea.js +0 -55
  349. package/dist/vue/kit/primevue/Textarea.js.map +0 -1
  350. package/dist/vue/kit/primevue/Tooltip.js +0 -43
  351. package/dist/vue/kit/primevue/Tooltip.js.map +0 -1
  352. package/dist/vue/kit/primevue/Tree.js +0 -75
  353. package/dist/vue/kit/primevue/Tree.js.map +0 -1
  354. package/dist/vue/kit/primevue/select-options.js +0 -55
  355. package/dist/vue/kit/primevue/select-options.js.map +0 -1
  356. package/dist/vue/kit/primevue/shared.js +0 -49
  357. package/dist/vue/kit/primevue/shared.js.map +0 -1
  358. package/dist/vue/kit/primevue/trigger.js +0 -25
  359. package/dist/vue/kit/primevue/trigger.js.map +0 -1
  360. package/dist/vue/kit/primevue.css +0 -86
  361. package/dist/vue/kit/primevue.d.ts +0 -33
  362. package/dist/vue/kit/primevue.js +0 -96
  363. package/dist/vue/kit/primevue.js.map +0 -1
@@ -0,0 +1,177 @@
1
+ //#region src/features/ai/protocol/block-lines.ts
2
+ /**
3
+ * The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives
4
+ * `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie
5
+ * inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the
6
+ * prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.
7
+ *
8
+ * A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting
9
+ * of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a
10
+ * closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at
11
+ * the left edge, so their content can look like anything.
12
+ */
13
+ /** One level of nesting inside a Baustein or host block. */
14
+ const INDENT = " ";
15
+ /** The line shown instead of the rest of a document that did not fit. */
16
+ const TRUNCATED_LINE = "[…] (Rest des Dokuments gekürzt)";
17
+ /** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */
18
+ function blockLine(id, prefix, text) {
19
+ return `${id ? `[${id}] ` : ""}${prefix}${text}`;
20
+ }
21
+ /** `markdown` on one line: hard breaks and wrapped lines would start new "blocks". */
22
+ function singleBlockLine(markdown) {
23
+ return markdown.replace(/\s*\n\s*/g, " ");
24
+ }
25
+ /** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */
26
+ function nestedLinePrefix(prefix) {
27
+ return `${prefix}${INDENT}`;
28
+ }
29
+ /** The prefix of the blocks of a quote. */
30
+ function quoteLinePrefix(prefix) {
31
+ return `${prefix}> `;
32
+ }
33
+ /** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */
34
+ function listLinePrefix(prefix, ordered, number) {
35
+ return `${prefix}${ordered ? `${number}. ` : "- "}`;
36
+ }
37
+ /** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */
38
+ function continuedLinePrefix(prefix) {
39
+ return prefix.replace(/(?:- |\d+\. )$/, (marker) => " ".repeat(marker.length));
40
+ }
41
+ /** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */
42
+ function compositeChromePart(markdown) {
43
+ return singleBlockLine(markdown.replace(/^(?:#+|>) ?/, ""));
44
+ }
45
+ /**
46
+ * The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty
47
+ * chrome part (title, description, tags) separated by ` | `.
48
+ */
49
+ function compositeLineHeader(kind, meta, chrome) {
50
+ return [`::: ${kind}${meta && typeof meta === "object" && Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ""}`, ...chrome.filter(Boolean)].join(" | ");
51
+ }
52
+ /** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */
53
+ function compositeLineClose(prefix) {
54
+ return `${nestedLinePrefix(prefix)}:::`;
55
+ }
56
+ const ID_PREFIX = /^\[([^\]\s]+)\] /;
57
+ /** An opening code fence after optional list/quote markers and indentation: three or more backticks, no backtick in the info string. */
58
+ const FENCE_OPEN = /^(?:[ \t]|>|[-*+][ \t]|\d+[.)][ \t])*(`{3,})[^`]*$/;
59
+ const FENCE_CLOSE = /^[ \t]*(`{3,})[ \t]*$/;
60
+ /** Indentation and list/quote markers at the start of a line's content. */
61
+ const LINE_PREFIX = /^(?:[ \t]|>|[-*+][ \t]|\d+[.)][ \t])*/;
62
+ /**
63
+ * The fence length after `line` when a fence of `fence` backticks was open before it (0: none). A line of at least as
64
+ * many backticks closes the fence; `opens` says whether a line outside a fence may open one.
65
+ */
66
+ function fenceAfter(fence, line, opens) {
67
+ if (fence) {
68
+ const closing = FENCE_CLOSE.exec(line);
69
+ return closing && closing[1].length >= fence ? 0 : fence;
70
+ }
71
+ const opening = opens ? FENCE_OPEN.exec(line) : null;
72
+ return opening ? opening[1].length : 0;
73
+ }
74
+ const NOTHING = /* @__PURE__ */ new Set();
75
+ /**
76
+ * Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never
77
+ * block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence
78
+ * (a stray line of backticks inside the code cannot swallow what follows).
79
+ *
80
+ * Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its
81
+ * block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph
82
+ * and its nested list are siblings, nothing lies "inside" the paragraph. A block without lines of its own (a list, a
83
+ * quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not "inside".
84
+ */
85
+ function readBlockLines(text) {
86
+ const ids = /* @__PURE__ */ new Set();
87
+ const nested = /* @__PURE__ */ new Map();
88
+ const open = [];
89
+ let fence = 0;
90
+ for (const line of text.split("\n")) {
91
+ const id = ID_PREFIX.exec(line)?.[1];
92
+ if (fence) {
93
+ fence = fenceAfter(fence, line, false);
94
+ continue;
95
+ }
96
+ if (!id || line === "[…] (Rest des Dokuments gekürzt)") continue;
97
+ const content = line.slice(id.length + 3);
98
+ fence = fenceAfter(0, content, true);
99
+ ids.add(id);
100
+ const indent = /^[ \t]*/.exec(content)[0].length;
101
+ const listItem = /[-*+\d]/.test(LINE_PREFIX.exec(content)[0]);
102
+ while (open.length && open.at(-1).indent >= indent) open.pop();
103
+ for (const outer of open) {
104
+ if (!outer.head || outer.id === id) continue;
105
+ const inside = nested.get(outer.id);
106
+ if (inside) inside.add(id);
107
+ else nested.set(outer.id, /* @__PURE__ */ new Set([id]));
108
+ }
109
+ open.push({
110
+ id,
111
+ indent,
112
+ head: !listItem && (open.at(-1)?.head ?? true)
113
+ });
114
+ }
115
+ return {
116
+ ids,
117
+ below: (id) => nested.get(id) ?? NOTHING
118
+ };
119
+ }
120
+ /**
121
+ * Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the
122
+ * start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the
123
+ * indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper
124
+ * nested lines into indented code).
125
+ */
126
+ function stripBlockLineEcho(markdown, known) {
127
+ let fence = 0;
128
+ const stripped = markdown.split("\n").map((line) => {
129
+ if (fence) {
130
+ fence = fenceAfter(fence, line, false);
131
+ return line;
132
+ }
133
+ const bare = line.replace(/^\[([^\]\s]+)\] ?/, (prefix, id) => known(id) ? "" : prefix);
134
+ fence = fenceAfter(0, bare, true);
135
+ return bare;
136
+ });
137
+ const indents = stripped.filter((line) => line.trim()).map((line) => /^[ \t]*/.exec(line)[0].length);
138
+ const common = indents.length ? Math.min(...indents) : 0;
139
+ return (common ? stripped.map((line) => line.replace(new RegExp(`^[ \\t]{0,${common}}`), "")) : stripped).join("\n");
140
+ }
141
+ /**
142
+ * Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the
143
+ * start of each line of its Markdown.
144
+ */
145
+ function stripListMarkerEcho(markdown) {
146
+ return markdown.replace(/^(?:[ \t]*(?:[-*+]|\d+[.)])[ \t]+|[ \t]*>[ \t]?)+/gm, "");
147
+ }
148
+ /** The block ID of the prompt's example line (also the target of its citation example). */
149
+ const BLOCK_LINE_EXAMPLE_ID = "0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d";
150
+ /** The text of the prompt's example line: a speaker line. */
151
+ const BLOCK_LINE_EXAMPLE_TEXT = "**Anna:** Wir starten im Mai.";
152
+ /** The example line of the prompt, written by `blockLine` like every line of the document. */
153
+ const BLOCK_LINE_EXAMPLE = blockLine(BLOCK_LINE_EXAMPLE_ID, "", BLOCK_LINE_EXAMPLE_TEXT);
154
+ /** The label of the document in the user message. */
155
+ const BLOCK_LINES_LABEL = "Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)";
156
+ /** What the first lines of the system prompt say about the document's lines. */
157
+ const BLOCK_LINE_INTRO = `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \`${BLOCK_LINE_EXAMPLE}\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`;
158
+ /** The list and quote blocks with the characters their lines carry. */
159
+ const BLOCK_LINE_MARKERS = `Aufzählung („${listLinePrefix("", false, 1)}“), nummerierte Liste („${listLinePrefix("", true, 1)}“), Zitat („${quoteLinePrefix("")}“)`;
160
+ /** What the prompt says about lists and quotes in the document. */
161
+ const BLOCK_LINE_LIST_TEXT = "Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.";
162
+ /** What the prompt says about the lines of a host block with block content (a callout). */
163
+ const BLOCK_LINE_HOST_TEXT = `Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „${blockLine("ID", "", "<erste Zeile seiner Markdown-Form>")}“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt.`;
164
+ /** What the prompt says about the lines of a Baustein. */
165
+ const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopfzeile „${blockLine("ID", "", compositeLineHeader("Art", { feld: "wert" }, [
166
+ "Titel",
167
+ "Beschreibung",
168
+ "Tags: #a #b"
169
+ ]))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose("")}“ ohne ID schließt ihn.`;
170
+ /** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */
171
+ const BLOCK_LINE_ECHO_TEXT = "„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.";
172
+ /** What the retry message says about IDs. */
173
+ const BLOCK_LINE_IDS_HINT = "Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.";
174
+ //#endregion
175
+ export { BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho };
176
+
177
+ //# sourceMappingURL=block-lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-lines.js","names":[],"sources":["../../../../src/features/ai/protocol/block-lines.ts"],"sourcesContent":["// i18n-ignore-file: the document lines and the prompt texts describing them are model-facing (German; the model's language is not the editor's)\n/**\n * The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives\n * `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie\n * inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the\n * prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.\n *\n * A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting\n * of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a\n * closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at\n * the left edge, so their content can look like anything.\n */\n\n// ---------- Writing ----------------------------------------------------------\n\n/** One level of nesting inside a Baustein or host block. */\nconst INDENT = ' '\n\n/** The line shown instead of the rest of a document that did not fit. */\nexport const TRUNCATED_LINE = '[…] (Rest des Dokuments gekürzt)'\n\n/** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */\nexport function blockLine(id: string | null, prefix: string, text: string): string {\n return `${id ? `[${id}] ` : ''}${prefix}${text}`\n}\n\n/** `markdown` on one line: hard breaks and wrapped lines would start new \"blocks\". */\nexport function singleBlockLine(markdown: string): string {\n return markdown.replace(/\\s*\\n\\s*/g, ' ')\n}\n\n/** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */\nexport function nestedLinePrefix(prefix: string): string {\n return `${prefix}${INDENT}`\n}\n\n/** The prefix of the blocks of a quote. */\nexport function quoteLinePrefix(prefix: string): string {\n return `${prefix}> `\n}\n\n/** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */\nexport function listLinePrefix(prefix: string, ordered: boolean, number: number): string {\n return `${prefix}${ordered ? `${number}. ` : '- '}`\n}\n\n/** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */\nexport function continuedLinePrefix(prefix: string): string {\n return prefix.replace(/(?:- |\\d+\\. )$/, marker => ' '.repeat(marker.length))\n}\n\n/** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */\nexport function compositeChromePart(markdown: string): string {\n return singleBlockLine(markdown.replace(/^(?:#+|>) ?/, ''))\n}\n\n/**\n * The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty\n * chrome part (title, description, tags) separated by ` | `.\n */\nexport function compositeLineHeader(kind: string, meta: unknown, chrome: readonly string[]): string {\n const fields = meta && typeof meta === 'object' && Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ''\n return [`::: ${kind}${fields}`, ...chrome.filter(Boolean)].join(' | ')\n}\n\n/** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */\nexport function compositeLineClose(prefix: string): string {\n return `${nestedLinePrefix(prefix)}:::`\n}\n\n// ---------- Reading ----------------------------------------------------------\n\n/** The block IDs of a document text and which blocks lie inside which. */\nexport interface BlockLines {\n /** Every block ID that starts a line (lines inside fenced code do not count). */\n ids: ReadonlySet<string>\n /** The IDs of all blocks nested below block `id`: the lines indented under its header line (empty for a block without nested blocks). */\n below: (id: string) => ReadonlySet<string>\n}\n\nconst ID_PREFIX = /^\\[([^\\]\\s]+)\\] /\n/** An opening code fence after optional list/quote markers and indentation: three or more backticks, no backtick in the info string. */\nconst FENCE_OPEN = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*(`{3,})[^`]*$/\nconst FENCE_CLOSE = /^[ \\t]*(`{3,})[ \\t]*$/\n/** Indentation and list/quote markers at the start of a line's content. */\nconst LINE_PREFIX = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*/\n\n/**\n * The fence length after `line` when a fence of `fence` backticks was open before it (0: none). A line of at least as\n * many backticks closes the fence; `opens` says whether a line outside a fence may open one.\n */\nfunction fenceAfter(fence: number, line: string, opens: boolean): number {\n if (fence) {\n const closing = FENCE_CLOSE.exec(line)\n return closing && closing[1]!.length >= fence ? 0 : fence\n }\n const opening = opens ? FENCE_OPEN.exec(line) : null\n return opening ? opening[1]!.length : 0\n}\n\nconst NOTHING: ReadonlySet<string> = new Set()\n\n/**\n * Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never\n * block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence\n * (a stray line of backticks inside the code cannot swallow what follows).\n *\n * Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its\n * block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph\n * and its nested list are siblings, nothing lies \"inside\" the paragraph. A block without lines of its own (a list, a\n * quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not \"inside\".\n */\nexport function readBlockLines(text: string): BlockLines {\n const ids = new Set<string>()\n const nested = new Map<string, Set<string>>()\n // The blocks whose lines enclose the current one, outermost first; `head`: the block can have nested blocks.\n const open: { id: string, indent: number, head: boolean }[] = []\n let fence = 0\n\n for (const line of text.split('\\n')) {\n const id = ID_PREFIX.exec(line)?.[1]\n if (fence) {\n fence = fenceAfter(fence, line, false)\n continue\n }\n // The truncation notice reads like a line with the ID `…`.\n if (!id || line === TRUNCATED_LINE) continue\n const content = line.slice(id.length + 3)\n fence = fenceAfter(0, content, true)\n ids.add(id)\n\n const indent = /^[ \\t]*/.exec(content)![0].length\n const listItem = /[-*+\\d]/.test(LINE_PREFIX.exec(content)![0])\n while (open.length && open.at(-1)!.indent >= indent) open.pop()\n for (const outer of open) {\n if (!outer.head || outer.id === id) continue\n const inside = nested.get(outer.id)\n if (inside) inside.add(id)\n else nested.set(outer.id, new Set([id]))\n }\n open.push({ id, indent, head: !listItem && (open.at(-1)?.head ?? true) })\n }\n return { ids, below: id => nested.get(id) ?? NOTHING }\n}\n\n/**\n * Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the\n * start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the\n * indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper\n * nested lines into indented code).\n */\nexport function stripBlockLineEcho(markdown: string, known: (id: string) => boolean): string {\n let fence = 0\n const stripped = markdown.split('\\n').map((line) => {\n if (fence) {\n fence = fenceAfter(fence, line, false)\n return line\n }\n const bare = line.replace(/^\\[([^\\]\\s]+)\\] ?/, (prefix, id: string) => known(id) ? '' : prefix)\n fence = fenceAfter(0, bare, true)\n return bare\n })\n const indents = stripped.filter(line => line.trim()).map(line => /^[ \\t]*/.exec(line)![0].length)\n const common = indents.length ? Math.min(...indents) : 0\n return (common ? stripped.map(line => line.replace(new RegExp(`^[ \\\\t]{0,${common}}`), '')) : stripped).join('\\n')\n}\n\n/**\n * Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the\n * start of each line of its Markdown.\n */\nexport function stripListMarkerEcho(markdown: string): string {\n return markdown.replace(/^(?:[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+|[ \\t]*>[ \\t]?)+/gm, '')\n}\n\n// ---------- Prompt -----------------------------------------------------------\n\n/** The block ID of the prompt's example line (also the target of its citation example). */\nexport const BLOCK_LINE_EXAMPLE_ID = '0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d'\n\n/** The text of the prompt's example line: a speaker line. */\nexport const BLOCK_LINE_EXAMPLE_TEXT = '**Anna:** Wir starten im Mai.'\n\n/** The example line of the prompt, written by `blockLine` like every line of the document. */\nexport const BLOCK_LINE_EXAMPLE = blockLine(BLOCK_LINE_EXAMPLE_ID, '', BLOCK_LINE_EXAMPLE_TEXT)\n\n/** The label of the document in the user message. */\nexport const BLOCK_LINES_LABEL = 'Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)'\n\n/** What the first lines of the system prompt say about the document's lines. */\nexport const BLOCK_LINE_INTRO = `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \\`${BLOCK_LINE_EXAMPLE}\\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`\n\n/** The list and quote blocks with the characters their lines carry. */\nexport const BLOCK_LINE_MARKERS = `Aufzählung („${listLinePrefix('', false, 1)}“), nummerierte Liste („${listLinePrefix('', true, 1)}“), Zitat („${quoteLinePrefix('')}“)`\n\n/** What the prompt says about lists and quotes in the document. */\nexport const BLOCK_LINE_LIST_TEXT = 'Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.'\n\n/** What the prompt says about the lines of a host block with block content (a callout). */\nexport const BLOCK_LINE_HOST_TEXT = `Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „${blockLine('ID', '', '<erste Zeile seiner Markdown-Form>')}“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt.`\n\n/** What the prompt says about the lines of a Baustein. */\nexport const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopfzeile „${blockLine('ID', '', compositeLineHeader('Art', { feld: 'wert' }, ['Titel', 'Beschreibung', 'Tags: #a #b']))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose('')}“ ohne ID schließt ihn.`\n\n/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */\nexport const BLOCK_LINE_ECHO_TEXT = '„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.'\n\n/** What the retry message says about IDs. */\nexport const BLOCK_LINE_IDS_HINT = 'Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.'\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS } from "./block-lines.js";
1
2
  import { AGENT_TOOL, agentFeedbackText, agentShownMessage } from "./agent-ops.js";
2
3
  //#region src/features/ai/protocol/chat.ts
3
4
  /** Input limits (characters / messages) of `POST /api/ai/chat`; the client trims to fit before sending. */
@@ -16,7 +17,6 @@ function citationBlockId(href) {
16
17
  if (typeof href !== "string" || !href.startsWith("#")) return null;
17
18
  return (href.startsWith("#block-") ? href.slice(7) : href.slice(1)) || null;
18
19
  }
19
- const EXAMPLE_ID = "0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d";
20
20
  const FIELD_TYPES = {
21
21
  text: "Text",
22
22
  textarea: "Langtext",
@@ -37,11 +37,11 @@ function blockModelText({ kinds, blocks, targets, structure }) {
37
37
  const exampleMeta = exampleField ? ` {"${exampleField.key}":"${exampleField.options.at(-1).value}"}` : "";
38
38
  return [
39
39
  "Blockmodell:",
40
- "Blocktypen: Absatz, Überschrift (#, ##, ###), Aufzählung („- “), nummerierte Liste („1. “), Zitat („> “), Codeblock, Redebeitrag („**Name:** Text“) und Bausteine. Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.",
41
- ...blocks.length ? [`Eigene Blöcke: ${blocks.map((block) => `${block.id} (${block.label}): ${block.description} Markdown-Form: „${block.syntax.replace(/\n/g, "\\n")}“. Beispiel: „${block.example.replace(/\n/g, "\\n")}“`).join(" ")}`, "Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „[ID] <erste Zeile seiner Markdown-Form>“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt. „update“ auf seine ID mit vollständigem Markdown ersetzt den ganzen Block; „add“ nach einem seiner Inhaltsblöcke fügt innerhalb des Blocks ein, ein Block derselben Art danach."] : [],
40
+ `Blocktypen: Absatz, Überschrift (#, ##, ###), ${BLOCK_LINE_MARKERS}, Codeblock, Redebeitrag („**Name:** Text“) und Bausteine. ${BLOCK_LINE_LIST_TEXT}`,
41
+ ...blocks.length ? [`Eigene Blöcke: ${blocks.map((block) => `${block.id} (${block.label}): ${block.description} Markdown-Form: „${block.syntax.replace(/\n/g, "\\n")}“. Beispiel: „${block.example.replace(/\n/g, "\\n")}“`).join(" ")}`, `${BLOCK_LINE_HOST_TEXT} „update“ auf seine ID mit vollständigem Markdown ersetzt den ganzen Block; „add“ nach einem seiner Inhaltsblöcke fügt innerhalb des Blocks ein, ein Block derselben Art danach.`] : [],
42
42
  composites ? "Bevorzugte Gliederung: Bausteine. Neue Inhalte gliederst du mit Bausteinen statt mit Überschriften; Unterabschnitte sind verschachtelte Bausteine (Zaun im Zaun, jeder Zaun endet mit einer eigenen Zeile „:::“). Markdown-Überschriften schreibst du nur als Titelzeile eines Bausteins." : "Bevorzugte Gliederung: Überschriften. Neue Inhalte gliederst du mit Überschriften (##, ###) statt mit Bausteinen; einen Baustein schreibst du nur, wenn ausdrücklich ein Baustein (Abschnitt, Aufgabe, Notiz …) verlangt wird oder du einen bestehenden Baustein änderst.",
43
43
  ...kinds.length ? [
44
- `Bausteine sind Blöcke mit Titel, Beschreibung, Tags, Feldern und eigenen Inhaltsblöcken. Im Dokument beginnt ein Baustein mit der Kopfzeile „[ID] ::: Art {"feld":"wert"} | Titel | Beschreibung | Tags: #a #b“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „ :::“ ohne ID schließt ihn.`,
44
+ `Bausteine sind Blöcke mit Titel, Beschreibung, Tags, Feldern und eigenen Inhaltsblöcken. ${BLOCK_LINE_COMPOSITE_TEXT}`,
45
45
  `Arten von Bausteinen: ${kinds.map((kind) => `${kind.id} (${kind.label}${kind.purpose ? `: ${kind.purpose}` : ""}; ${kind.fields.length ? `Felder: ${kind.fields.map(fieldText).join(", ")}` : "keine Felder"})`).join("; ")}.`,
46
46
  `In „markdown“ schreibst du einen Baustein ohne IDs und ohne Einrückung als Zaun: die Zeile „::: Art {"feld":"wert"}“ (Felder als JSON, nur gesetzte), dann „## Titel“, „> Beschreibung“ und „Tags: #a #b“ (jeweils nur, wenn vorhanden), dann die Inhaltsblöcke als Markdown, zuletzt die Zeile „:::“. Beispiel: „::: ${example?.id ?? "note"}${exampleMeta}\\n## Angebot prüfen\\n- Preise vergleichen\\n- Rückmeldung an Anna\\n:::“.`,
47
47
  "„update“ auf einen Baustein mit Markdown ohne :::-Zaun ersetzt nur seinen Inhalt (Kinder); mit Zaun den ganzen Baustein (Art, Felder, Titel, Beschreibung, Tags und Inhalt)."
@@ -54,20 +54,20 @@ function blockModelText({ kinds, blocks, targets, structure }) {
54
54
  function chatSystem(blockModel) {
55
55
  return [
56
56
  "Du beantwortest Fragen zu einem Dokument, meist dem Transkript eines Gesprächs, und bearbeitest es auf Wunsch. Das Dokument steht in der letzten Nachricht der Nutzerin bzw. des Nutzers und ist der aktuelle Stand; frühere Nachrichten bezogen sich womöglich auf einen älteren Stand.",
57
- `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \`[${EXAMPLE_ID}] **Anna:** Wir starten im Mai.\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`,
57
+ BLOCK_LINE_INTRO,
58
58
  "",
59
59
  "Fragen:",
60
60
  `Belege deine Antworten mit wörtlichen Zitaten: ein zusammenhängender, unveränderter Ausschnitt aus einem einzigen Block. Jedes Zitat ist ein Markdown-Link, dessen Ziel „${CITATION_HREF_PREFIX}“ gefolgt von der vollständigen ID dieses Blocks ist: [„Zitat“](${CITATION_HREF_PREFIX}ID).`,
61
- `Beispiel zur Zeile oben: Anna kündigt den Start an: [„Wir starten im Mai.“](${CITATION_HREF_PREFIX}${EXAMPLE_ID})`,
61
+ `Beispiel zur Zeile oben: Anna kündigt den Start an: [„Wir starten im Mai.“](${CITATION_HREF_PREFIX}${BLOCK_LINE_EXAMPLE_ID})`,
62
62
  "Verwende nur IDs, die im Dokument vorkommen, und nenne IDs nie außerhalb solcher Links.",
63
63
  "Steht die Antwort nicht im Dokument, sag das ehrlich, statt etwas zu erfinden.",
64
64
  "Antworte knapp in Markdown (Absätze, Aufzählungen, **fett**) und in der Sprache der Frage.",
65
65
  "",
66
66
  "Änderungen:",
67
67
  `Bittet die Nutzerin bzw. der Nutzer darum, das Dokument zu ändern (kürzen, umformulieren, ergänzen, löschen, umwandeln …), rufe das Werkzeug ${AGENT_TOOL} auf, statt den neuen Text im Chat zu schreiben. Schreib davor höchstens ein bis zwei Sätze dazu, was du änderst. Bei Fragen rufst du das Werkzeug nicht auf.`,
68
- "Operationen: „update“ ersetzt den Inhalt des Blocks „id“ durch „markdown“; „delete“ löscht den Block „id“; „add“ fügt „markdown“ (ein oder mehrere Blöcke, durch Leerzeilen getrennt) nach dem Block „afterId“ ein, afterId null bedeutet am Dokumentanfang; „convert“ wandelt den Block „id“ in den Blocktyp „to“ um.",
69
- "„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile. Redebeiträge behalten den fett gesetzten Namen mit Doppelpunkt am Anfang; ein anderer Name wechselt die Sprecherin bzw. den Sprecher. Neue Redebeiträge schreibst du ebenso als „**Name:** Text“.",
70
- "Ändere nur die Blöcke, die die Bitte betrifft; lass unveränderte Blöcke weg. Jeder Block höchstens einmal. Verwende nur IDs aus dem Dokument. Nennt die Nachricht einen Bearbeitungsbereich, beziehen sich alle IDs und afterIds ausschließlich auf Blöcke daraus.",
68
+ "Operationen: „update“ ersetzt den Inhalt des Blocks „id“ durch „markdown“; „delete“ löscht den Block „id“; „add“ fügt „markdown“ (ein oder mehrere Blöcke, durch Leerzeilen getrennt) nach dem Block „afterId“ ein („afterId“ gehört bei „add“ immer dazu, für den Dokumentanfang schreibst du ausdrücklich null; mehrere „add“ nach demselben Block werden in der Reihenfolge eingefügt, in der du sie schreibst); „convert“ wandelt den Block „id“ in den Blocktyp „to“ um.",
69
+ `${BLOCK_LINE_ECHO_TEXT} Redebeiträge behalten den fett gesetzten Namen mit Doppelpunkt am Anfang; ein anderer Name wechselt die Sprecherin bzw. den Sprecher. Neue Redebeiträge schreibst du ebenso als „**Name:** Text“.`,
70
+ "Ändere nur die Blöcke, die die Bitte betrifft; lass unveränderte Blöcke weg. Jeder Block höchstens einmal; eine Operation auf einen Block innerhalb eines Blocks, den eine andere Operation ersetzt oder löscht, ist ungültig (schreibe die Änderung dann in das Markdown der äußeren Operation). Verwende nur IDs aus dem Dokument. Nennt die Nachricht einen Bearbeitungsbereich, beziehen sich alle IDs und afterIds ausschließlich auf Blöcke daraus.",
71
71
  "Jede Änderung wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt und einzeln übernommen oder abgelehnt. Die Entscheidungen erhältst du mit einer späteren Nachricht („Entscheidungen zu deinen letzten Vorschlägen: …“). Geh auf abgelehnte Vorschläge ein und wiederhole sie nicht unverändert.",
72
72
  "",
73
73
  ...blockModelText(blockModel)
@@ -132,7 +132,7 @@ function documentChatMessages(messages, document, scope, blockModel) {
132
132
  {
133
133
  role: "user",
134
134
  content: [
135
- `Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID):\n"""\n${document}\n"""`,
135
+ `${BLOCK_LINES_LABEL}:\n"""\n${document}\n"""`,
136
136
  range,
137
137
  ...decisions ? [decisions] : [],
138
138
  `Nachricht: ${last.content}`
@@ -1 +1 @@
1
- {"version":3,"file":"chat.js","names":[],"sources":["../../../../src/features/ai/protocol/chat.ts"],"sourcesContent":["// i18n-ignore-file: model-facing chat system prompt and messages (German prompts stay German whatever the editor's language)\nimport type { ModelMessage } from 'ai'\nimport type { AiBlock, AiContextKind } from '../../../core/feature'\nimport type { StructureMode } from '../../../settings'\nimport { AGENT_TOOL, type AgentOp, type AgentOpFeedback, agentFeedbackText, agentShownMessage } from './agent-ops'\n\n/**\n * One turn of the document chat; the conversation lives on the client and is sent in full with every message.\n * A user turn carries the decisions on earlier proposals made since the previous turn (`feedback`); an assistant turn\n * the edit operations it proposed (`ops`, possibly without any text) and how many invalid ones were dropped.\n */\nexport type DocumentChatMessage =\n | { role: 'user', content: string, feedback?: AgentOpFeedback[] }\n | { role: 'assistant', content: string, ops?: AgentOp[], dropped?: number }\n\n/** Input limits (characters / messages) of `POST /api/ai/chat`; the client trims to fit before sending. */\nexport const CHAT_LIMITS = { document: 120000, message: 8000, messages: 40 } as const\n\n/** Link target prefix of a quote citation: `[„Zitat“](#block-<id>)`. */\nexport const CITATION_HREF_PREFIX = '#block-'\n\n/**\n * The block ID a citation link points to, or `null` for any other href. Also accepts a bare `#<id>`, which models\n * write now and then despite the instructions; whether the ID exists is up to the caller.\n */\nexport function citationBlockId(href: unknown): string | null {\n if (typeof href !== 'string' || !href.startsWith('#')) return null\n return (href.startsWith(CITATION_HREF_PREFIX) ? href.slice(CITATION_HREF_PREFIX.length) : href.slice(1)) || null\n}\n\nconst EXAMPLE_ID = '0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d'\n\n/** A composite kind as the chat model learns it: the client sends the kinds of the editor's `aiContext` with every message. */\nexport type ChatCompositeKind = Pick<AiContextKind, 'id' | 'label' | 'fields' | 'purpose'>\ntype ChatField = ChatCompositeKind['fields'][number]\n\n/** A host-defined block as the chat model learns it: the editor's `aiContext.blocks`, sent with every message. */\nexport type ChatBlock = AiBlock\n\n/**\n * What the model may build and convert into: the editor's composite kinds and host blocks, the `convert` targets it\n * offers, and the user's preferred structure of new content (settings `blocks.structure`).\n */\nexport interface ChatBlockModel { kinds: readonly ChatCompositeKind[], blocks: readonly ChatBlock[], targets: readonly string[], structure: StructureMode }\n\nconst FIELD_TYPES: Record<NonNullable<ChatField['type']>, string> = { text: 'Text', textarea: 'Langtext', date: 'Datum JJJJ-MM-TT', number: 'Zahl', select: 'Auswahl' }\n\n/** `status (Status: open = Offen, doing = In Arbeit, done = Erledigt)`, `due (Fällig, Datum JJJJ-MM-TT)`. */\nfunction fieldText(field: ChatField): string {\n const values = field.options?.length ? `: ${field.options.map(option => `${option.value} = ${option.label}`).join(', ')}` : `, ${FIELD_TYPES[field.type ?? 'text']}`\n return `${field.key} (${field.label}${values})`\n}\n\n/** „Blockmodell“: the document's line format for composites and host blocks holding blocks, the fence the model writes composites in, and `convert`. */\nfunction blockModelText({ kinds, blocks, targets, structure }: ChatBlockModel): string[] {\n const composites = structure === 'composite' && kinds.length > 0\n const example = kinds.find(kind => kind.fields.some(field => field.options?.length)) ?? kinds[0]\n const exampleField = example?.fields.find(field => field.options?.length)\n const exampleMeta = exampleField ? ` {\"${exampleField.key}\":\"${exampleField.options!.at(-1)!.value}\"}` : ''\n return [\n 'Blockmodell:',\n 'Blocktypen: Absatz, Überschrift (#, ##, ###), Aufzählung („- “), nummerierte Liste („1. “), Zitat („> “), Codeblock, Redebeitrag („**Name:** Text“) und Bausteine. Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.',\n ...(blocks.length\n ? [\n `Eigene Blöcke: ${blocks.map(block => `${block.id} (${block.label}): ${block.description} Markdown-Form: „${block.syntax.replace(/\\n/g, '\\\\n')}“. Beispiel: „${block.example.replace(/\\n/g, '\\\\n')}“`).join(' ')}`,\n 'Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „[ID] <erste Zeile seiner Markdown-Form>“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt. „update“ auf seine ID mit vollständigem Markdown ersetzt den ganzen Block; „add“ nach einem seiner Inhaltsblöcke fügt innerhalb des Blocks ein, ein Block derselben Art danach.',\n ]\n : []),\n composites\n ? 'Bevorzugte Gliederung: Bausteine. Neue Inhalte gliederst du mit Bausteinen statt mit Überschriften; Unterabschnitte sind verschachtelte Bausteine (Zaun im Zaun, jeder Zaun endet mit einer eigenen Zeile „:::“). Markdown-Überschriften schreibst du nur als Titelzeile eines Bausteins.'\n : 'Bevorzugte Gliederung: Überschriften. Neue Inhalte gliederst du mit Überschriften (##, ###) statt mit Bausteinen; einen Baustein schreibst du nur, wenn ausdrücklich ein Baustein (Abschnitt, Aufgabe, Notiz …) verlangt wird oder du einen bestehenden Baustein änderst.',\n ...(kinds.length\n ? [\n `Bausteine sind Blöcke mit Titel, Beschreibung, Tags, Feldern und eigenen Inhaltsblöcken. Im Dokument beginnt ein Baustein mit der Kopfzeile „[ID] ::: Art {\"feld\":\"wert\"} | Titel | Beschreibung | Tags: #a #b“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „ :::“ ohne ID schließt ihn.`,\n `Arten von Bausteinen: ${kinds.map(kind => `${kind.id} (${kind.label}${kind.purpose ? `: ${kind.purpose}` : ''}; ${kind.fields.length ? `Felder: ${kind.fields.map(fieldText).join(', ')}` : 'keine Felder'})`).join('; ')}.`,\n `In „markdown“ schreibst du einen Baustein ohne IDs und ohne Einrückung als Zaun: die Zeile „::: Art {\"feld\":\"wert\"}“ (Felder als JSON, nur gesetzte), dann „## Titel“, „> Beschreibung“ und „Tags: #a #b“ (jeweils nur, wenn vorhanden), dann die Inhaltsblöcke als Markdown, zuletzt die Zeile „:::“. Beispiel: „::: ${example?.id ?? 'note'}${exampleMeta}\\\\n## Angebot prüfen\\\\n- Preise vergleichen\\\\n- Rückmeldung an Anna\\\\n:::“.`,\n '„update“ auf einen Baustein mit Markdown ohne :::-Zaun ersetzt nur seinen Inhalt (Kinder); mit Zaun den ganzen Baustein (Art, Felder, Titel, Beschreibung, Tags und Inhalt).',\n ]\n : []),\n `„convert“ wandelt den Block „id“ in den Blocktyp „to“ um und behält seinen Inhalt. Erlaubte Werte für „to“: ${targets.join(', ')}. Eine Überschrift, die zum Baustein wird, nimmt die folgenden Blöcke bis zur nächsten Überschrift gleicher oder höherer Ebene als Inhalt mit; ein Baustein, der zu einem anderen Blocktyp wird, behält Beschreibung, Tags und Felder als Textzeilen; wird er zur Überschrift, werden seine inneren Bausteine zu Unterüberschriften.`,\n 'Sollen sich Blocktyp und Inhalt oder Felder zugleich ändern (z. B. ein Absatz wird zu einer Aufgabe mit Status), nimm statt „convert“ ein „update“ mit dem vollständigen neuen Markdown samt :::-Zaun; jeder Block bekommt höchstens eine Operation.',\n ]\n}\n\n/** The system prompt: answering with citations, editing through `edit_document`, and the editor's block model. */\nfunction chatSystem(blockModel: ChatBlockModel): string {\n return [\n 'Du beantwortest Fragen zu einem Dokument, meist dem Transkript eines Gesprächs, und bearbeitest es auf Wunsch. Das Dokument steht in der letzten Nachricht der Nutzerin bzw. des Nutzers und ist der aktuelle Stand; frühere Nachrichten bezogen sich womöglich auf einen älteren Stand.',\n `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \\`[${EXAMPLE_ID}] **Anna:** Wir starten im Mai.\\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`,\n '',\n 'Fragen:',\n `Belege deine Antworten mit wörtlichen Zitaten: ein zusammenhängender, unveränderter Ausschnitt aus einem einzigen Block. Jedes Zitat ist ein Markdown-Link, dessen Ziel „${CITATION_HREF_PREFIX}“ gefolgt von der vollständigen ID dieses Blocks ist: [„Zitat“](${CITATION_HREF_PREFIX}ID).`,\n `Beispiel zur Zeile oben: Anna kündigt den Start an: [„Wir starten im Mai.“](${CITATION_HREF_PREFIX}${EXAMPLE_ID})`,\n 'Verwende nur IDs, die im Dokument vorkommen, und nenne IDs nie außerhalb solcher Links.',\n 'Steht die Antwort nicht im Dokument, sag das ehrlich, statt etwas zu erfinden.',\n 'Antworte knapp in Markdown (Absätze, Aufzählungen, **fett**) und in der Sprache der Frage.',\n '',\n 'Änderungen:',\n `Bittet die Nutzerin bzw. der Nutzer darum, das Dokument zu ändern (kürzen, umformulieren, ergänzen, löschen, umwandeln …), rufe das Werkzeug ${AGENT_TOOL} auf, statt den neuen Text im Chat zu schreiben. Schreib davor höchstens ein bis zwei Sätze dazu, was du änderst. Bei Fragen rufst du das Werkzeug nicht auf.`,\n 'Operationen: „update“ ersetzt den Inhalt des Blocks „id“ durch „markdown“; „delete“ löscht den Block „id“; „add“ fügt „markdown“ (ein oder mehrere Blöcke, durch Leerzeilen getrennt) nach dem Block „afterId“ ein, afterId null bedeutet am Dokumentanfang; „convert“ wandelt den Block „id“ in den Blocktyp „to“ um.',\n '„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile. Redebeiträge behalten den fett gesetzten Namen mit Doppelpunkt am Anfang; ein anderer Name wechselt die Sprecherin bzw. den Sprecher. Neue Redebeiträge schreibst du ebenso als „**Name:** Text“.',\n 'Ändere nur die Blöcke, die die Bitte betrifft; lass unveränderte Blöcke weg. Jeder Block höchstens einmal. Verwende nur IDs aus dem Dokument. Nennt die Nachricht einen Bearbeitungsbereich, beziehen sich alle IDs und afterIds ausschließlich auf Blöcke daraus.',\n 'Jede Änderung wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt und einzeln übernommen oder abgelehnt. Die Entscheidungen erhältst du mit einer späteren Nachricht („Entscheidungen zu deinen letzten Vorschlägen: …“). Geh auf abgelehnte Vorschläge ein und wiederhole sie nicht unverändert.',\n '',\n ...blockModelText(blockModel),\n ].join('\\n')\n}\n\n/** A user turn as the model reads it: decisions on earlier proposals first, then the message. */\nfunction userText(content: string, feedback: readonly AgentOpFeedback[] | undefined): string {\n const decisions = agentFeedbackText(feedback ?? [])\n return decisions ? `${decisions}\\n\\nNachricht: ${content}` : content\n}\n\n/**\n * Model messages for a chat turn: the history as sent (proposed operations as `edit_document` tool calls), with the\n * current document placed in the last (user) message next to the scope, the latest decisions and the message —\n * document content never goes into the system prompt (the editor's block model does). `scope`: IDs the edits are\n * limited to (null: whole document).\n */\nexport function documentChatMessages(messages: readonly DocumentChatMessage[], document: string, scope: readonly string[] | null, blockModel: ChatBlockModel): ModelMessage[] {\n const history = messages.slice(0, -1).flatMap((message, index): ModelMessage[] => {\n if (message.role === 'user') return [{ role: 'user', content: userText(message.content, message.feedback) }]\n if (!message.ops?.length && !message.dropped) return [{ role: 'assistant', content: message.content }]\n const ops = message.ops ?? []\n const toolCallId = `edit_${index}`\n return [\n {\n role: 'assistant',\n content: [\n ...(message.content ? [{ type: 'text' as const, text: message.content }] : []),\n { type: 'tool-call', toolCallId, toolName: AGENT_TOOL, input: { ops: ops.map(({ op: _op, ...rest }) => rest) } },\n ],\n },\n {\n role: 'tool',\n content: [{ type: 'tool-result', toolCallId, toolName: AGENT_TOOL, output: { type: 'text', value: agentShownMessage(ops, message.dropped ?? 0) } }],\n },\n ]\n })\n const last = messages.at(-1)! as Extract<DocumentChatMessage, { role: 'user' }>\n const range = scope\n ? `Bearbeitungsbereich: die Auswahl der Nutzerin bzw. des Nutzers. Änderungen (id und afterId) nur an diesen Blöcken: ${scope.join(', ')}`\n : 'Bearbeitungsbereich: das ganze Dokument.'\n const decisions = agentFeedbackText(last.feedback ?? [])\n return [\n { role: 'system', content: chatSystem(blockModel) },\n ...history,\n {\n role: 'user',\n content: [\n `Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID):\\n\"\"\"\\n${document}\\n\"\"\"`,\n range,\n ...(decisions ? [decisions] : []),\n `Nachricht: ${last.content}`,\n ].join('\\n\\n'),\n },\n ]\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chat.js","names":[],"sources":["../../../../src/features/ai/protocol/chat.ts"],"sourcesContent":["// i18n-ignore-file: model-facing chat system prompt and messages (German prompts stay German whatever the editor's language)\nimport type { ModelMessage } from 'ai'\nimport type { AiBlock, AiContextKind } from '../../../core/feature'\nimport type { StructureMode } from '../../../settings'\nimport { AGENT_TOOL, type AgentOp, type AgentOpFeedback, agentFeedbackText, agentShownMessage } from './agent-ops'\nimport { BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BLOCK_LINES_LABEL } from './block-lines'\n\n/**\n * One turn of the document chat; the conversation lives on the client and is sent in full with every message.\n * A user turn carries the decisions on earlier proposals made since the previous turn (`feedback`); an assistant turn\n * the edit operations it proposed (`ops`, possibly without any text) and how many invalid ones were dropped.\n */\nexport type DocumentChatMessage =\n | { role: 'user', content: string, feedback?: AgentOpFeedback[] }\n | { role: 'assistant', content: string, ops?: AgentOp[], dropped?: number }\n\n/** Input limits (characters / messages) of `POST /api/ai/chat`; the client trims to fit before sending. */\nexport const CHAT_LIMITS = { document: 120000, message: 8000, messages: 40 } as const\n\n/** Link target prefix of a quote citation: `[„Zitat“](#block-<id>)`. */\nexport const CITATION_HREF_PREFIX = '#block-'\n\n/**\n * The block ID a citation link points to, or `null` for any other href. Also accepts a bare `#<id>`, which models\n * write now and then despite the instructions; whether the ID exists is up to the caller.\n */\nexport function citationBlockId(href: unknown): string | null {\n if (typeof href !== 'string' || !href.startsWith('#')) return null\n return (href.startsWith(CITATION_HREF_PREFIX) ? href.slice(CITATION_HREF_PREFIX.length) : href.slice(1)) || null\n}\n\n/** A composite kind as the chat model learns it: the client sends the kinds of the editor's `aiContext` with every message. */\nexport type ChatCompositeKind = Pick<AiContextKind, 'id' | 'label' | 'fields' | 'purpose'>\ntype ChatField = ChatCompositeKind['fields'][number]\n\n/** A host-defined block as the chat model learns it: the editor's `aiContext.blocks`, sent with every message. */\nexport type ChatBlock = AiBlock\n\n/**\n * What the model may build and convert into: the editor's composite kinds and host blocks, the `convert` targets it\n * offers, and the user's preferred structure of new content (settings `blocks.structure`).\n */\nexport interface ChatBlockModel { kinds: readonly ChatCompositeKind[], blocks: readonly ChatBlock[], targets: readonly string[], structure: StructureMode }\n\nconst FIELD_TYPES: Record<NonNullable<ChatField['type']>, string> = { text: 'Text', textarea: 'Langtext', date: 'Datum JJJJ-MM-TT', number: 'Zahl', select: 'Auswahl' }\n\n/** `status (Status: open = Offen, doing = In Arbeit, done = Erledigt)`, `due (Fällig, Datum JJJJ-MM-TT)`. */\nfunction fieldText(field: ChatField): string {\n const values = field.options?.length ? `: ${field.options.map(option => `${option.value} = ${option.label}`).join(', ')}` : `, ${FIELD_TYPES[field.type ?? 'text']}`\n return `${field.key} (${field.label}${values})`\n}\n\n/** „Blockmodell“: the document's line format for composites and host blocks holding blocks, the fence the model writes composites in, and `convert`. */\nfunction blockModelText({ kinds, blocks, targets, structure }: ChatBlockModel): string[] {\n const composites = structure === 'composite' && kinds.length > 0\n const example = kinds.find(kind => kind.fields.some(field => field.options?.length)) ?? kinds[0]\n const exampleField = example?.fields.find(field => field.options?.length)\n const exampleMeta = exampleField ? ` {\"${exampleField.key}\":\"${exampleField.options!.at(-1)!.value}\"}` : ''\n return [\n 'Blockmodell:',\n `Blocktypen: Absatz, Überschrift (#, ##, ###), ${BLOCK_LINE_MARKERS}, Codeblock, Redebeitrag („**Name:** Text“) und Bausteine. ${BLOCK_LINE_LIST_TEXT}`,\n ...(blocks.length\n ? [\n `Eigene Blöcke: ${blocks.map(block => `${block.id} (${block.label}): ${block.description} Markdown-Form: „${block.syntax.replace(/\\n/g, '\\\\n')}“. Beispiel: „${block.example.replace(/\\n/g, '\\\\n')}“`).join(' ')}`,\n `${BLOCK_LINE_HOST_TEXT} „update“ auf seine ID mit vollständigem Markdown ersetzt den ganzen Block; „add“ nach einem seiner Inhaltsblöcke fügt innerhalb des Blocks ein, ein Block derselben Art danach.`,\n ]\n : []),\n composites\n ? 'Bevorzugte Gliederung: Bausteine. Neue Inhalte gliederst du mit Bausteinen statt mit Überschriften; Unterabschnitte sind verschachtelte Bausteine (Zaun im Zaun, jeder Zaun endet mit einer eigenen Zeile „:::“). Markdown-Überschriften schreibst du nur als Titelzeile eines Bausteins.'\n : 'Bevorzugte Gliederung: Überschriften. Neue Inhalte gliederst du mit Überschriften (##, ###) statt mit Bausteinen; einen Baustein schreibst du nur, wenn ausdrücklich ein Baustein (Abschnitt, Aufgabe, Notiz …) verlangt wird oder du einen bestehenden Baustein änderst.',\n ...(kinds.length\n ? [\n `Bausteine sind Blöcke mit Titel, Beschreibung, Tags, Feldern und eigenen Inhaltsblöcken. ${BLOCK_LINE_COMPOSITE_TEXT}`,\n `Arten von Bausteinen: ${kinds.map(kind => `${kind.id} (${kind.label}${kind.purpose ? `: ${kind.purpose}` : ''}; ${kind.fields.length ? `Felder: ${kind.fields.map(fieldText).join(', ')}` : 'keine Felder'})`).join('; ')}.`,\n `In „markdown“ schreibst du einen Baustein ohne IDs und ohne Einrückung als Zaun: die Zeile „::: Art {\"feld\":\"wert\"}“ (Felder als JSON, nur gesetzte), dann „## Titel“, „> Beschreibung“ und „Tags: #a #b“ (jeweils nur, wenn vorhanden), dann die Inhaltsblöcke als Markdown, zuletzt die Zeile „:::“. Beispiel: „::: ${example?.id ?? 'note'}${exampleMeta}\\\\n## Angebot prüfen\\\\n- Preise vergleichen\\\\n- Rückmeldung an Anna\\\\n:::“.`,\n '„update“ auf einen Baustein mit Markdown ohne :::-Zaun ersetzt nur seinen Inhalt (Kinder); mit Zaun den ganzen Baustein (Art, Felder, Titel, Beschreibung, Tags und Inhalt).',\n ]\n : []),\n `„convert“ wandelt den Block „id“ in den Blocktyp „to“ um und behält seinen Inhalt. Erlaubte Werte für „to“: ${targets.join(', ')}. Eine Überschrift, die zum Baustein wird, nimmt die folgenden Blöcke bis zur nächsten Überschrift gleicher oder höherer Ebene als Inhalt mit; ein Baustein, der zu einem anderen Blocktyp wird, behält Beschreibung, Tags und Felder als Textzeilen; wird er zur Überschrift, werden seine inneren Bausteine zu Unterüberschriften.`,\n 'Sollen sich Blocktyp und Inhalt oder Felder zugleich ändern (z. B. ein Absatz wird zu einer Aufgabe mit Status), nimm statt „convert“ ein „update“ mit dem vollständigen neuen Markdown samt :::-Zaun; jeder Block bekommt höchstens eine Operation.',\n ]\n}\n\n/** The system prompt: answering with citations, editing through `edit_document`, and the editor's block model. */\nfunction chatSystem(blockModel: ChatBlockModel): string {\n return [\n 'Du beantwortest Fragen zu einem Dokument, meist dem Transkript eines Gesprächs, und bearbeitest es auf Wunsch. Das Dokument steht in der letzten Nachricht der Nutzerin bzw. des Nutzers und ist der aktuelle Stand; frühere Nachrichten bezogen sich womöglich auf einen älteren Stand.',\n BLOCK_LINE_INTRO,\n '',\n 'Fragen:',\n `Belege deine Antworten mit wörtlichen Zitaten: ein zusammenhängender, unveränderter Ausschnitt aus einem einzigen Block. Jedes Zitat ist ein Markdown-Link, dessen Ziel „${CITATION_HREF_PREFIX}“ gefolgt von der vollständigen ID dieses Blocks ist: [„Zitat“](${CITATION_HREF_PREFIX}ID).`,\n `Beispiel zur Zeile oben: Anna kündigt den Start an: [„Wir starten im Mai.“](${CITATION_HREF_PREFIX}${BLOCK_LINE_EXAMPLE_ID})`,\n 'Verwende nur IDs, die im Dokument vorkommen, und nenne IDs nie außerhalb solcher Links.',\n 'Steht die Antwort nicht im Dokument, sag das ehrlich, statt etwas zu erfinden.',\n 'Antworte knapp in Markdown (Absätze, Aufzählungen, **fett**) und in der Sprache der Frage.',\n '',\n 'Änderungen:',\n `Bittet die Nutzerin bzw. der Nutzer darum, das Dokument zu ändern (kürzen, umformulieren, ergänzen, löschen, umwandeln …), rufe das Werkzeug ${AGENT_TOOL} auf, statt den neuen Text im Chat zu schreiben. Schreib davor höchstens ein bis zwei Sätze dazu, was du änderst. Bei Fragen rufst du das Werkzeug nicht auf.`,\n 'Operationen: „update“ ersetzt den Inhalt des Blocks „id“ durch „markdown“; „delete“ löscht den Block „id“; „add“ fügt „markdown“ (ein oder mehrere Blöcke, durch Leerzeilen getrennt) nach dem Block „afterId“ ein („afterId“ gehört bei „add“ immer dazu, für den Dokumentanfang schreibst du ausdrücklich null; mehrere „add“ nach demselben Block werden in der Reihenfolge eingefügt, in der du sie schreibst); „convert“ wandelt den Block „id“ in den Blocktyp „to“ um.',\n `${BLOCK_LINE_ECHO_TEXT} Redebeiträge behalten den fett gesetzten Namen mit Doppelpunkt am Anfang; ein anderer Name wechselt die Sprecherin bzw. den Sprecher. Neue Redebeiträge schreibst du ebenso als „**Name:** Text“.`,\n 'Ändere nur die Blöcke, die die Bitte betrifft; lass unveränderte Blöcke weg. Jeder Block höchstens einmal; eine Operation auf einen Block innerhalb eines Blocks, den eine andere Operation ersetzt oder löscht, ist ungültig (schreibe die Änderung dann in das Markdown der äußeren Operation). Verwende nur IDs aus dem Dokument. Nennt die Nachricht einen Bearbeitungsbereich, beziehen sich alle IDs und afterIds ausschließlich auf Blöcke daraus.',\n 'Jede Änderung wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt und einzeln übernommen oder abgelehnt. Die Entscheidungen erhältst du mit einer späteren Nachricht („Entscheidungen zu deinen letzten Vorschlägen: …“). Geh auf abgelehnte Vorschläge ein und wiederhole sie nicht unverändert.',\n '',\n ...blockModelText(blockModel),\n ].join('\\n')\n}\n\n/** A user turn as the model reads it: decisions on earlier proposals first, then the message. */\nfunction userText(content: string, feedback: readonly AgentOpFeedback[] | undefined): string {\n const decisions = agentFeedbackText(feedback ?? [])\n return decisions ? `${decisions}\\n\\nNachricht: ${content}` : content\n}\n\n/**\n * Model messages for a chat turn: the history as sent (proposed operations as `edit_document` tool calls), with the\n * current document placed in the last (user) message next to the scope, the latest decisions and the message —\n * document content never goes into the system prompt (the editor's block model does). `scope`: IDs the edits are\n * limited to (null: whole document).\n */\nexport function documentChatMessages(messages: readonly DocumentChatMessage[], document: string, scope: readonly string[] | null, blockModel: ChatBlockModel): ModelMessage[] {\n const history = messages.slice(0, -1).flatMap((message, index): ModelMessage[] => {\n if (message.role === 'user') return [{ role: 'user', content: userText(message.content, message.feedback) }]\n if (!message.ops?.length && !message.dropped) return [{ role: 'assistant', content: message.content }]\n const ops = message.ops ?? []\n const toolCallId = `edit_${index}`\n return [\n {\n role: 'assistant',\n content: [\n ...(message.content ? [{ type: 'text' as const, text: message.content }] : []),\n { type: 'tool-call', toolCallId, toolName: AGENT_TOOL, input: { ops: ops.map(({ op: _op, ...rest }) => rest) } },\n ],\n },\n {\n role: 'tool',\n content: [{ type: 'tool-result', toolCallId, toolName: AGENT_TOOL, output: { type: 'text', value: agentShownMessage(ops, message.dropped ?? 0) } }],\n },\n ]\n })\n const last = messages.at(-1)! as Extract<DocumentChatMessage, { role: 'user' }>\n const range = scope\n ? `Bearbeitungsbereich: die Auswahl der Nutzerin bzw. des Nutzers. Änderungen (id und afterId) nur an diesen Blöcken: ${scope.join(', ')}`\n : 'Bearbeitungsbereich: das ganze Dokument.'\n const decisions = agentFeedbackText(last.feedback ?? [])\n return [\n { role: 'system', content: chatSystem(blockModel) },\n ...history,\n {\n role: 'user',\n content: [\n `${BLOCK_LINES_LABEL}:\\n\"\"\"\\n${document}\\n\"\"\"`,\n range,\n ...(decisions ? [decisions] : []),\n `Nachricht: ${last.content}`,\n ].join('\\n\\n'),\n },\n ]\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
- import { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, ModelAgentOp, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps, documentBlockIds } from "./agent-ops.js";
1
+ import { BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho } from "./block-lines.js";
2
+ import { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, ModelAgentOp, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps } from "./agent-ops.js";
2
3
  import { CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, citationBlockId, documentChatMessages } from "./chat.js";
3
4
  import { AI_COMPLETE_LIMITS, AiCompleteMessage, cleanCompletion, completeMessages, completionReady } from "./complete.js";
4
5
  import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelProfile, ModelRef, UserModelProfile, modelRefSchema } from "./models.js";
5
6
  import { AI_ID_PATTERN, aiBlockInputSchema, aiBlockSchema } from "./schemas.js";
6
7
  import { AI_LIMITS, AI_TASKS, AI_TASK_IDS, AiApplyMode, AiBlockInput, AiInput, AiMessage, AiTask, AiTaskId, blockTasks, inBlockTasks, withHeading, withTaskHeading } from "./tasks.js";
7
- export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_TASKS, AI_TASK_IDS, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, AiApplyMode, AiBlockInput, AiCompleteMessage, AiInput, AiMessage, AiTask, AiTaskId, CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelAgentOp, ModelProfile, ModelRef, UserModelProfile, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiBlockInputSchema, aiBlockSchema, blockTasks, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionReady, documentBlockIds, documentChatMessages, inBlockTasks, modelRefSchema, withHeading, withTaskHeading };
8
+ export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_TASKS, AI_TASK_IDS, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, AiApplyMode, AiBlockInput, AiCompleteMessage, AiInput, AiMessage, AiTask, AiTaskId, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelAgentOp, ModelProfile, ModelRef, TRUNCATED_LINE, UserModelProfile, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiBlockInputSchema, aiBlockSchema, blockLine, blockTasks, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, documentChatMessages, inBlockTasks, listLinePrefix, modelRefSchema, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
@@ -1,7 +1,8 @@
1
- import { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps, documentBlockIds } from "./agent-ops.js";
1
+ import { BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho } from "./block-lines.js";
2
+ import { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps } from "./agent-ops.js";
2
3
  import { CHAT_LIMITS, CITATION_HREF_PREFIX, citationBlockId, documentChatMessages } from "./chat.js";
3
4
  import { AI_COMPLETE_LIMITS, cleanCompletion, completeMessages, completionReady } from "./complete.js";
4
5
  import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, modelRefSchema } from "./models.js";
5
6
  import { AI_LIMITS, AI_TASKS, AI_TASK_IDS, blockTasks, inBlockTasks, withHeading, withTaskHeading } from "./tasks.js";
6
7
  import { AI_ID_PATTERN, aiBlockInputSchema, aiBlockSchema } from "./schemas.js";
7
- export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_TASKS, AI_TASK_IDS, CHAT_LIMITS, CITATION_HREF_PREFIX, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiBlockInputSchema, aiBlockSchema, blockTasks, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionReady, documentBlockIds, documentChatMessages, inBlockTasks, modelRefSchema, withHeading, withTaskHeading };
8
+ export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_TASKS, AI_TASK_IDS, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, CHAT_LIMITS, CITATION_HREF_PREFIX, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, TRUNCATED_LINE, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiBlockInputSchema, aiBlockSchema, blockLine, blockTasks, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, documentChatMessages, inBlockTasks, listLinePrefix, modelRefSchema, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
@@ -5,10 +5,11 @@ import { Editor } from "@tiptap/core";
5
5
  * (`selectionMenu`), the result streams into a preview and is applied over the range captured when the menu opened.
6
6
  * Caret mode (`/ai`, see `openAiMenuAtCaret`): the same popover opens at the caret with the `caretMenu` tasks and
7
7
  * `generate` as free prompt; the result is inserted at the caret line (`insertAtCaretLine`).
8
- * Needs the AiSelectionHighlight extension on the editor.
8
+ * Needs the AiSelectionHighlight extension on the editor. The flow (`createAiSelectionFlow`) owns every step; this
9
+ * component renders its state, places the popover and shows the preview.
9
10
  */
10
11
  export declare function AiSelectionMenu({ editor }: {
11
12
  editor: Editor;
12
- }): import("react").JSX.Element;
13
+ }): import("react").JSX.Element | null;
13
14
  //#endregion
14
15
  //# sourceMappingURL=AiSelectionMenu.d.ts.map