@ecsplico/editor 3.6.0 → 4.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 (593) hide show
  1. package/CHANGELOG.md +73 -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 -8
  268. package/dist/features/composites/merge.js +23 -22
  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 +150 -87
  374. package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
  375. package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +5 -1
  376. package/dist/features/image-studio/components/ImageStudioLibraryPanel.js +186 -0
  377. package/dist/features/image-studio/components/ImageStudioLibraryPanel.js.map +1 -0
  378. package/dist/features/image-studio/components/ImageStudioLibraryTile.js +134 -0
  379. package/dist/features/image-studio/components/ImageStudioLibraryTile.js.map +1 -0
  380. package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
  381. package/dist/features/image-studio/feature.d.ts +1 -1
  382. package/dist/features/image-studio/feature.js +1 -1
  383. package/dist/features/image-studio/feature.js.map +1 -1
  384. package/dist/features/image-studio/index.d.ts +3 -2
  385. package/dist/features/image-studio/index.js +2 -2
  386. package/dist/features/image-studio/library.d.ts +35 -0
  387. package/dist/features/image-studio/node-core.d.ts +3 -3
  388. package/dist/features/image-studio/node-core.js +1 -1
  389. package/dist/features/image-studio/node-core.js.map +1 -1
  390. package/dist/features/image-studio/node-store.d.ts +1 -1
  391. package/dist/features/image-studio/node-store.js +1 -1
  392. package/dist/features/image-studio/node-store.js.map +1 -1
  393. package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
  394. package/dist/features/image-studio/react/ImageStudioDialog.js +46 -5
  395. package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
  396. package/dist/features/image-studio/react/ImageStudioLibraryPanel.js +141 -0
  397. package/dist/features/image-studio/react/ImageStudioLibraryPanel.js.map +1 -0
  398. package/dist/features/image-studio/react/ImageStudioLibraryTile.js +105 -0
  399. package/dist/features/image-studio/react/ImageStudioLibraryTile.js.map +1 -0
  400. package/dist/features/image-studio/react/ImageStudioView.js +1 -1
  401. package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
  402. package/dist/features/image-studio/react/store.d.ts +8 -3
  403. package/dist/features/image-studio/react/store.js.map +1 -1
  404. package/dist/features/image-studio/react.d.ts +4 -3
  405. package/dist/features/image-studio/react.js +3 -3
  406. package/dist/features/image-studio/react.js.map +1 -1
  407. package/dist/features/image-studio/schema.d.ts +1 -1
  408. package/dist/features/image-studio/schema.js +1 -1
  409. package/dist/features/image-studio/schema.js.map +1 -1
  410. package/dist/features/image-studio/state.d.ts +8 -3
  411. package/dist/features/image-studio/state.js +11 -3
  412. package/dist/features/image-studio/state.js.map +1 -1
  413. package/dist/features/image-studio/store.d.ts +8 -3
  414. package/dist/features/image-studio/vue.d.ts +4 -3
  415. package/dist/features/image-studio/vue.js +3 -3
  416. package/dist/features/image-studio/vue.js.map +1 -1
  417. package/dist/features/mentions/tag.js +2 -1
  418. package/dist/features/mentions/tag.js.map +1 -1
  419. package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
  420. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
  421. package/dist/features/themes/feature.d.ts +1 -1
  422. package/dist/features/themes/feature.js.map +1 -1
  423. package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
  424. package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
  425. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
  426. package/dist/features/themes/theme-runtime.js +1 -1
  427. package/dist/features/themes/theme-runtime.js.map +1 -1
  428. package/dist/features/transcript/TranscriptSegmentView.js +1 -1
  429. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
  430. package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
  431. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
  432. package/dist/features/transcript/segment-node.js.map +1 -1
  433. package/dist/features/transfer/feature.js +1 -1
  434. package/dist/features/transfer/feature.js.map +1 -1
  435. package/dist/features/transfer/transforms.d.ts +4 -4
  436. package/dist/features/transfer/transforms.js +12 -12
  437. package/dist/features/transfer/transforms.js.map +1 -1
  438. package/dist/i18n/de/ai.js +4 -4
  439. package/dist/i18n/de/ai.js.map +1 -1
  440. package/dist/i18n/de/authors.js +1 -1
  441. package/dist/i18n/de/authors.js.map +1 -1
  442. package/dist/i18n/de/comments.js +1 -1
  443. package/dist/i18n/de/comments.js.map +1 -1
  444. package/dist/i18n/de/composites.js +62 -59
  445. package/dist/i18n/de/composites.js.map +1 -1
  446. package/dist/i18n/de/image-studio.js +11 -2
  447. package/dist/i18n/de/image-studio.js.map +1 -1
  448. package/dist/i18n/de/integrations.js +20 -20
  449. package/dist/i18n/de/integrations.js.map +1 -1
  450. package/dist/i18n/de/markdown.js +1 -1
  451. package/dist/i18n/de/markdown.js.map +1 -1
  452. package/dist/i18n/de/palette.js +1 -1
  453. package/dist/i18n/de/palette.js.map +1 -1
  454. package/dist/i18n/de/themes.js +3 -3
  455. package/dist/i18n/de/themes.js.map +1 -1
  456. package/dist/i18n/de/transfer.js +2 -2
  457. package/dist/i18n/de/transfer.js.map +1 -1
  458. package/dist/i18n/de/tree.js +6 -6
  459. package/dist/i18n/de/tree.js.map +1 -1
  460. package/dist/i18n/de.d.ts +110 -98
  461. package/dist/i18n/en/ai.js +2 -2
  462. package/dist/i18n/en/ai.js.map +1 -1
  463. package/dist/i18n/en/composites.js +64 -61
  464. package/dist/i18n/en/composites.js.map +1 -1
  465. package/dist/i18n/en/image-studio.js +9 -0
  466. package/dist/i18n/en/image-studio.js.map +1 -1
  467. package/dist/i18n/en/integrations.js +9 -9
  468. package/dist/i18n/en/integrations.js.map +1 -1
  469. package/dist/i18n/en/markdown.js +1 -1
  470. package/dist/i18n/en/markdown.js.map +1 -1
  471. package/dist/i18n/en/palette.js +1 -1
  472. package/dist/i18n/en/palette.js.map +1 -1
  473. package/dist/i18n/en/themes.js +4 -4
  474. package/dist/i18n/en/themes.js.map +1 -1
  475. package/dist/i18n/en/tree.js +5 -5
  476. package/dist/i18n/en/tree.js.map +1 -1
  477. package/dist/index.d.ts +4 -1
  478. package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
  479. package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
  480. package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
  481. package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
  482. package/dist/integrations/composite-ai/composite-fill.js +5 -5
  483. package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
  484. package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
  485. package/dist/integrations/composite-ai/composite-summary.js +5 -5
  486. package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
  487. package/dist/integrations/composite-ai/derive.d.ts +17 -17
  488. package/dist/integrations/composite-ai/derive.js +27 -26
  489. package/dist/integrations/composite-ai/derive.js.map +1 -1
  490. package/dist/integrations/composite-ai/index.d.ts +14 -15
  491. package/dist/integrations/composite-ai/index.js +98 -77
  492. package/dist/integrations/composite-ai/index.js.map +1 -1
  493. package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
  494. package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
  495. package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
  496. package/dist/integrations/composite-ai/rendition.d.ts +9 -9
  497. package/dist/integrations/composite-ai/rendition.js +27 -27
  498. package/dist/integrations/composite-ai/rendition.js.map +1 -1
  499. package/dist/integrations/composite-ai/split.d.ts +8 -8
  500. package/dist/integrations/composite-ai/split.js +12 -12
  501. package/dist/integrations/composite-ai/split.js.map +1 -1
  502. package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
  503. package/dist/integrations/composite-ai/transfer-transforms.js +11 -11
  504. package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
  505. package/dist/integrations/composite-media/index.d.ts +4 -3
  506. package/dist/integrations/composite-media/index.js +20 -14
  507. package/dist/integrations/composite-media/index.js.map +1 -1
  508. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  509. package/dist/markdown/document.js +1 -1
  510. package/dist/markdown/document.js.map +1 -1
  511. package/dist/nuxt/module.d.ts +48 -24
  512. package/dist/nuxt/module.js.map +1 -1
  513. package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
  514. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
  515. package/dist/presets/compose.d.ts +6 -6
  516. package/dist/presets/compose.js +7 -4
  517. package/dist/presets/compose.js.map +1 -1
  518. package/dist/react/AdvancedEditor.d.ts +2 -2
  519. package/dist/react/AdvancedEditor.js +1 -1
  520. package/dist/react/AdvancedEditor.js.map +1 -1
  521. package/dist/react/compose.d.ts +1 -1
  522. package/dist/react/compose.js +1 -1
  523. package/dist/react/compose.js.map +1 -1
  524. package/dist/react/composed-surface.d.ts +5 -5
  525. package/dist/react/composed-surface.js +2 -2
  526. package/dist/react/composed-surface.js.map +1 -1
  527. package/dist/server/ai.d.ts +7 -7
  528. package/dist/server/ai.js +1 -1
  529. package/dist/server/ai.js.map +1 -1
  530. package/dist/server/auth.d.ts +1 -1
  531. package/dist/server/collab-state.d.ts +1 -1
  532. package/dist/server/collab-state.js.map +1 -1
  533. package/dist/server/collab.d.ts +4 -4
  534. package/dist/server/collab.js +22 -17
  535. package/dist/server/collab.js.map +1 -1
  536. package/dist/server/handlers/ai.d.ts +9 -6
  537. package/dist/server/handlers/ai.js +13 -17
  538. package/dist/server/handlers/ai.js.map +1 -1
  539. package/dist/server/handlers/context.d.ts +4 -0
  540. package/dist/server/handlers/context.js.map +1 -1
  541. package/dist/server/handlers/editor.d.ts +33 -32
  542. package/dist/server/handlers/editor.js +13 -8
  543. package/dist/server/handlers/editor.js.map +1 -1
  544. package/dist/server/handlers/images.d.ts +1 -1
  545. package/dist/server/handlers/images.js +1 -1
  546. package/dist/server/handlers/images.js.map +1 -1
  547. package/dist/server/host.d.ts +4 -0
  548. package/dist/server/host.js +10 -2
  549. package/dist/server/host.js.map +1 -1
  550. package/dist/server/index.d.ts +3 -3
  551. package/dist/server/ports.d.ts +8 -8
  552. package/dist/server/ports.js.map +1 -1
  553. package/dist/settings/index.d.ts +20 -19
  554. package/dist/settings/index.js +25 -24
  555. package/dist/settings/index.js.map +1 -1
  556. package/dist/settings/migrate.d.ts +18 -0
  557. package/dist/settings/migrate.js +46 -0
  558. package/dist/settings/migrate.js.map +1 -0
  559. package/dist/settings/values.d.ts +12 -12
  560. package/dist/settings/values.js +11 -11
  561. package/dist/settings/values.js.map +1 -1
  562. package/dist/styles/composites.css +10 -10
  563. package/dist/tree/DocumentTree.js +12 -5
  564. package/dist/tree/DocumentTree.js.map +1 -1
  565. package/dist/tree/drop-rules.js +5 -5
  566. package/dist/tree/drop-rules.js.map +1 -1
  567. package/dist/tree/react/DocumentTree.js +14 -5
  568. package/dist/tree/react/DocumentTree.js.map +1 -1
  569. package/dist/tree/tree-host.js +5 -5
  570. package/dist/tree/tree-host.js.map +1 -1
  571. package/dist/tree/tree-items.js +34 -28
  572. package/dist/tree/tree-items.js.map +1 -1
  573. package/dist/tree/tree-model.js +16 -16
  574. package/dist/tree/tree-model.js.map +1 -1
  575. package/dist/tree/tree-rows.js +2 -2
  576. package/dist/tree/tree-rows.js.map +1 -1
  577. package/dist/tree/tree-view-model.d.ts +2 -2
  578. package/dist/tree/tree-view-model.js.map +1 -1
  579. package/dist/vue/AdvancedEditor.js +2 -2
  580. package/dist/vue/AdvancedEditor.js.map +1 -1
  581. package/dist/vue/AdvancedEditor.vue.d.ts +10 -10
  582. package/dist/vue/SimpleEditor.vue.d.ts +4 -4
  583. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  584. package/dist/vue/compose.d.ts +1 -1
  585. package/dist/vue/compose.js +1 -1
  586. package/dist/vue/compose.js.map +1 -1
  587. package/package.json +1 -1
  588. package/dist/features/composites/derivation.d.ts +0 -62
  589. package/dist/features/composites/derivation.js +0 -102
  590. package/dist/features/composites/derivation.js.map +0 -1
  591. package/dist/features/composites/kinds.d.ts +0 -89
  592. package/dist/features/composites/kinds.js +0 -285
  593. package/dist/features/composites/kinds.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptSegmentView.js","names":["selected","node","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createElementBlock","_withModifiers","_toDisplayString","_createCommentVNode","SuggestionList","_createVNode"],"sources":["../../../src/features/transcript/TranscriptSegmentView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewContent, NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../core'\nimport SuggestionList from '../../vue/components/SuggestionList.vue'\nimport { useEditorI18n } from '../../vue/use-editor-i18n'\nimport { formatSegmentTime } from './segment-node'\n\nconst props = defineProps(nodeViewProps)\nconst { t } = useEditorI18n()\n\nconst speaker = computed(() => String(props.node.attrs.speaker ?? ''))\n// Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n// speaker list is only extended with whole names, not with every prefix typed on the way.\nconst draft = ref(speaker.value)\nconst focused = ref(false)\nwatch(speaker, (value) => {\n if (!focused.value) draft.value = value\n})\n// `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\nconst readonly = ref(false)\n\nconst open = ref(false)\nconst items = ref<MetadataItem[]>([])\nconst list = useTemplateRef<InstanceType<typeof SuggestionList>>('list')\n\n// Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet pending: AbortController | undefined\nfunction scheduleSearch(query: string) {\n clearTimeout(timer)\n timer = setTimeout(async () => {\n pending?.abort()\n const controller = pending = new AbortController()\n try {\n const found = await metadataOf(props.editor).search(METADATA_LIST.speakers, { query, editor: props.editor, signal: controller.signal })\n if (!controller.signal.aborted) items.value = found.filter(item => item.label.trim())\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n}\nonBeforeUnmount(() => {\n clearTimeout(timer)\n pending?.abort()\n})\n\nfunction openList(query: string) {\n if (!props.editor.isEditable) return\n open.value = true\n scheduleSearch(query)\n}\n\nfunction closeList() {\n open.value = false\n clearTimeout(timer)\n pending?.abort()\n}\n\nfunction onFocus() {\n focused.value = true\n readonly.value = !props.editor.isEditable\n}\n\nfunction onInput(e: Event) {\n draft.value = (e.target as HTMLInputElement).value\n openList(draft.value)\n}\n\n/** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\nasync function commit(name = draft.value.trim()) {\n draft.value = name\n if (readonly.value || name === speaker.value) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(props.editor).search(METADATA_LIST.speakers, { query: name, editor: props.editor }) : null\n props.updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(props.editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n}\n\nfunction pick(item: unknown) {\n closeList()\n void commit((item as MetadataItem).label)\n}\n\nfunction onBlur() {\n focused.value = false\n closeList()\n void commit()\n}\n\n/** The speaker field of the segment `step` places before/after this one (document order), if any. */\nfunction neighbourField(input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined {\n const fields = [...props.editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n}\n\n// An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n// speaker and Enter continues in this segment's text.\nfunction onKeyDown(e: KeyboardEvent) {\n if (open.value && items.value.length && list.value?.onKeyDown(e)) {\n e.preventDefault()\n return\n }\n const input = e.target as HTMLInputElement\n if (e.key === 'Escape' && open.value) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n props.editor.commands.focus(props.getPos()! + props.node.nodeSize - 1)\n }\n}\n\n/** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */\nfunction seek() {\n const start = props.node.attrs.start\n const pos = props.getPos()\n // `seekSource` is contributed by the composites feature; without it the editor has no command and the click does nothing.\n if (typeof start === 'number' && pos !== undefined) props.editor.commands.seekSource?.(pos, start)\n}\n</script>\n\n<template>\n <NodeViewWrapper\n :class=\"['transcript-segment my-2', { 'is-selected rounded-md ring-1 ring-[var(--sk-primary)]': selected }]\"\n data-type=\"transcript-segment\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div class=\"transcript-segment__speaker relative pt-0.5\" contenteditable=\"false\">\n <input\n :value=\"draft\"\n :title=\"draft || undefined\"\n :placeholder=\"t('transcript.speaker')\"\n :aria-label=\"t('transcript.speaker')\"\n autocomplete=\"off\"\n role=\"combobox\"\n :aria-expanded=\"open && items.length > 0\"\n :readonly=\"readonly\"\n class=\"w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @mousedown=\"openList('')\"\n @input=\"onInput\"\n @keydown=\"onKeyDown\"\n >\n <button\n v-if=\"node.attrs.start != null\"\n type=\"button\"\n class=\"transcript-segment__time block rounded px-1 text-xs tabular-nums text-[var(--sk-muted)] hover:text-[var(--sk-primary)] hover:underline\"\n :title=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n :aria-label=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n @mousedown.prevent\n @click=\"seek\"\n >\n {{ formatSegmentTime(node.attrs.start) }}\n </button>\n <SuggestionList\n v-if=\"open && items.length\"\n ref=\"list\"\n class=\"absolute top-full left-0 z-50 mt-1\"\n :items=\"items\"\n :display=\"item => ({ label: (item as MetadataItem).label })\"\n :command=\"pick\"\n :loading=\"false\"\n loading-text=\"\"\n empty-text=\"\"\n />\n </div>\n <NodeViewContent class=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TranscriptSegmentView.js","names":["selected","node","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createElementBlock","_withModifiers","_toDisplayString","_createCommentVNode","SuggestionList","_createVNode"],"sources":["../../../src/features/transcript/TranscriptSegmentView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewContent, NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../core'\nimport SuggestionList from '../../vue/components/SuggestionList.vue'\nimport { useEditorI18n } from '../../vue/use-editor-i18n'\nimport { formatSegmentTime } from './segment-node'\n\nconst props = defineProps(nodeViewProps)\nconst { t } = useEditorI18n()\n\nconst speaker = computed(() => String(props.node.attrs.speaker ?? ''))\n// Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n// speaker list is only extended with whole names, not with every prefix typed on the way.\nconst draft = ref(speaker.value)\nconst focused = ref(false)\nwatch(speaker, (value) => {\n if (!focused.value) draft.value = value\n})\n// `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\nconst readonly = ref(false)\n\nconst open = ref(false)\nconst items = ref<MetadataItem[]>([])\nconst list = useTemplateRef<InstanceType<typeof SuggestionList>>('list')\n\n// Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet pending: AbortController | undefined\nfunction scheduleSearch(query: string) {\n clearTimeout(timer)\n timer = setTimeout(async () => {\n pending?.abort()\n const controller = pending = new AbortController()\n try {\n const found = await metadataOf(props.editor).search(METADATA_LIST.speakers, { query, editor: props.editor, signal: controller.signal })\n if (!controller.signal.aborted) items.value = found.filter(item => item.label.trim())\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n}\nonBeforeUnmount(() => {\n clearTimeout(timer)\n pending?.abort()\n})\n\nfunction openList(query: string) {\n if (!props.editor.isEditable) return\n open.value = true\n scheduleSearch(query)\n}\n\nfunction closeList() {\n open.value = false\n clearTimeout(timer)\n pending?.abort()\n}\n\nfunction onFocus() {\n focused.value = true\n readonly.value = !props.editor.isEditable\n}\n\nfunction onInput(e: Event) {\n draft.value = (e.target as HTMLInputElement).value\n openList(draft.value)\n}\n\n/** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\nasync function commit(name = draft.value.trim()) {\n draft.value = name\n if (readonly.value || name === speaker.value) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(props.editor).search(METADATA_LIST.speakers, { query: name, editor: props.editor }) : null\n props.updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(props.editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n}\n\nfunction pick(item: unknown) {\n closeList()\n void commit((item as MetadataItem).label)\n}\n\nfunction onBlur() {\n focused.value = false\n closeList()\n void commit()\n}\n\n/** The speaker field of the segment `step` places before/after this one (document order), if any. */\nfunction neighbourField(input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined {\n const fields = [...props.editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n}\n\n// An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n// speaker and Enter continues in this segment's text.\nfunction onKeyDown(e: KeyboardEvent) {\n if (open.value && items.value.length && list.value?.onKeyDown(e)) {\n e.preventDefault()\n return\n }\n const input = e.target as HTMLInputElement\n if (e.key === 'Escape' && open.value) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n props.editor.commands.focus(props.getPos()! + props.node.nodeSize - 1)\n }\n}\n\n/** Seeks and plays the source recording of the enclosing Typed block (a no-op without the composites feature or a source). */\nfunction seek() {\n const start = props.node.attrs.start\n const pos = props.getPos()\n // `seekSource` is contributed by the composites feature; without it the editor has no command and the click does nothing.\n if (typeof start === 'number' && pos !== undefined) props.editor.commands.seekSource?.(pos, start)\n}\n</script>\n\n<template>\n <NodeViewWrapper\n :class=\"['transcript-segment my-2', { 'is-selected rounded-md ring-1 ring-[var(--sk-primary)]': selected }]\"\n data-type=\"transcript-segment\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div class=\"transcript-segment__speaker relative pt-0.5\" contenteditable=\"false\">\n <input\n :value=\"draft\"\n :title=\"draft || undefined\"\n :placeholder=\"t('transcript.speaker')\"\n :aria-label=\"t('transcript.speaker')\"\n autocomplete=\"off\"\n role=\"combobox\"\n :aria-expanded=\"open && items.length > 0\"\n :readonly=\"readonly\"\n class=\"w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @mousedown=\"openList('')\"\n @input=\"onInput\"\n @keydown=\"onKeyDown\"\n >\n <button\n v-if=\"node.attrs.start != null\"\n type=\"button\"\n class=\"transcript-segment__time block rounded px-1 text-xs tabular-nums text-[var(--sk-muted)] hover:text-[var(--sk-primary)] hover:underline\"\n :title=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n :aria-label=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n @mousedown.prevent\n @click=\"seek\"\n >\n {{ formatSegmentTime(node.attrs.start) }}\n </button>\n <SuggestionList\n v-if=\"open && items.length\"\n ref=\"list\"\n class=\"absolute top-full left-0 z-50 mt-1\"\n :items=\"items\"\n :display=\"item => ({ label: (item as MetadataItem).label })\"\n :command=\"pick\"\n :loading=\"false\"\n loading-text=\"\"\n empty-text=\"\"\n />\n </div>\n <NodeViewContent class=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n</template>\n"],"mappings":"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"}
@@ -100,7 +100,7 @@ function TranscriptSegmentView({ node, editor, selected, updateAttributes, getPo
100
100
  editor.commands.focus(getPos() + node.nodeSize - 1);
101
101
  }
102
102
  };
103
- /** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */
103
+ /** Seeks and plays the source recording of the enclosing Typed block (a no-op without the composites feature or a source). */
104
104
  const seek = () => {
105
105
  const start = node.attrs.start;
106
106
  const pos = getPos();
@@ -1 +1 @@
1
- {"version":3,"file":"TranscriptSegmentView.js","names":[],"sources":["../../../../src/features/transcript/react/TranscriptSegmentView.tsx"],"sourcesContent":["import { NodeViewContent, NodeViewWrapper, type NodeViewProps } from '@tiptap/react'\nimport { type KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useRef, useState } from 'react'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../../core'\nimport { SuggestionList, type SuggestionListHandle } from '../../../react/suggestion-renderer'\nimport { cn } from '../../../react/ui/utils'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { formatSegmentTime } from '../segment-node'\n\n// Module-level so the list's memoised rows keep their identity across renders.\nconst displayItem = (item: unknown) => ({ label: (item as MetadataItem).label })\n\n/** The speaker segment: the speaker field (with a suggestion list of the `speakers` metadata) beside the editable text. */\nexport function TranscriptSegmentView({ node, editor, selected, updateAttributes, getPos }: NodeViewProps) {\n const { t } = useEditorI18n()\n const speaker = String(node.attrs.speaker ?? '')\n // Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n // speaker list is only extended with whole names, not with every prefix typed on the way.\n const [draft, setDraft] = useState(speaker)\n const focused = useRef(false)\n useEffect(() => {\n if (!focused.current) setDraft(speaker)\n }, [speaker])\n // `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\n const [readonly, setReadonly] = useState(false)\n\n const [open, setOpen] = useState(false)\n const [items, setItems] = useState<MetadataItem[]>([])\n const list = useRef<SuggestionListHandle>(null)\n\n // Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n const pending = useRef<AbortController>(undefined)\n const scheduleSearch = (query: string) => {\n clearTimeout(timer.current)\n timer.current = setTimeout(async () => {\n pending.current?.abort()\n const controller = pending.current = new AbortController()\n try {\n const found = await metadataOf(editor).search(METADATA_LIST.speakers, { query, editor, signal: controller.signal })\n if (!controller.signal.aborted) setItems(found.filter(item => item.label.trim()))\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n }\n useEffect(() => () => {\n clearTimeout(timer.current)\n pending.current?.abort()\n }, [])\n\n const openList = (query: string) => {\n if (!editor.isEditable) return\n setOpen(true)\n scheduleSearch(query)\n }\n\n const closeList = useCallback(() => {\n setOpen(false)\n clearTimeout(timer.current)\n pending.current?.abort()\n }, [])\n\n /** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\n const commit = async (name = draft.trim()) => {\n setDraft(name)\n if (readonly || name === speaker) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(editor).search(METADATA_LIST.speakers, { query: name, editor }) : null\n updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n }\n\n const pick = (item: unknown) => {\n closeList()\n void commit((item as MetadataItem).label)\n }\n\n /** The speaker field of the segment `step` places before/after this one (document order), if any. */\n const neighbourField = (input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined => {\n const fields = [...editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n }\n\n // An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n // speaker and Enter continues in this segment's text.\n const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (open && items.length && list.current?.onKeyDown(e.nativeEvent)) {\n e.preventDefault()\n return\n }\n const input = e.currentTarget\n if (e.key === 'Escape' && open) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n editor.commands.focus(getPos()! + node.nodeSize - 1)\n }\n }\n\n /** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */\n const seek = () => {\n const start = node.attrs.start\n const pos = getPos()\n // `seekSource` is contributed by the composites feature; without it the editor has no command and the click does nothing.\n if (typeof start === 'number' && pos !== undefined) editor.commands.seekSource?.(pos, start)\n }\n\n return (\n <NodeViewWrapper\n className={cn('transcript-segment my-2', selected && 'is-selected rounded-md ring-1 ring-primary')}\n data-type=\"transcript-segment\"\n data-id={node.attrs.id || undefined}\n >\n <div className=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div className=\"transcript-segment__speaker relative pt-0.5\" contentEditable={false}>\n <input\n value={draft}\n title={draft || undefined}\n placeholder={t('transcript.speaker')}\n aria-label={t('transcript.speaker')}\n autoComplete=\"off\"\n role=\"combobox\"\n aria-expanded={open && items.length > 0}\n readOnly={readonly}\n className=\"w-full truncate rounded bg-transparent px-1 text-sm text-muted-foreground outline-none placeholder:text-muted-foreground/60 focus:bg-muted focus:text-foreground\"\n onFocus={() => {\n focused.current = true\n setReadonly(!editor.isEditable)\n }}\n onBlur={() => {\n focused.current = false\n closeList()\n void commit()\n }}\n onMouseDown={() => openList('')}\n onChange={(e) => {\n setDraft(e.target.value)\n openList(e.target.value)\n }}\n onKeyDown={onKeyDown}\n />\n {node.attrs.start != null && (\n <button\n type=\"button\"\n className=\"transcript-segment__time block rounded px-1 text-xs tabular-nums text-muted-foreground hover:text-primary hover:underline\"\n title={t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })}\n aria-label={t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })}\n onMouseDown={e => e.preventDefault()}\n onClick={seek}\n >\n {formatSegmentTime(node.attrs.start)}\n </button>\n )}\n {open && items.length > 0 && (\n <div className=\"absolute top-full left-0 z-50 mt-1\">\n <SuggestionList ref={list} items={items} display={displayItem} command={pick} loading={false} loadingText=\"\" emptyText=\"\" />\n </div>\n )}\n </div>\n <NodeViewContent className=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TranscriptSegmentView.js","names":[],"sources":["../../../../src/features/transcript/react/TranscriptSegmentView.tsx"],"sourcesContent":["import { NodeViewContent, NodeViewWrapper, type NodeViewProps } from '@tiptap/react'\nimport { type KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useRef, useState } from 'react'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../../core'\nimport { SuggestionList, type SuggestionListHandle } from '../../../react/suggestion-renderer'\nimport { cn } from '../../../react/ui/utils'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { formatSegmentTime } from '../segment-node'\n\n// Module-level so the list's memoised rows keep their identity across renders.\nconst displayItem = (item: unknown) => ({ label: (item as MetadataItem).label })\n\n/** The speaker segment: the speaker field (with a suggestion list of the `speakers` metadata) beside the editable text. */\nexport function TranscriptSegmentView({ node, editor, selected, updateAttributes, getPos }: NodeViewProps) {\n const { t } = useEditorI18n()\n const speaker = String(node.attrs.speaker ?? '')\n // Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n // speaker list is only extended with whole names, not with every prefix typed on the way.\n const [draft, setDraft] = useState(speaker)\n const focused = useRef(false)\n useEffect(() => {\n if (!focused.current) setDraft(speaker)\n }, [speaker])\n // `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\n const [readonly, setReadonly] = useState(false)\n\n const [open, setOpen] = useState(false)\n const [items, setItems] = useState<MetadataItem[]>([])\n const list = useRef<SuggestionListHandle>(null)\n\n // Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n const pending = useRef<AbortController>(undefined)\n const scheduleSearch = (query: string) => {\n clearTimeout(timer.current)\n timer.current = setTimeout(async () => {\n pending.current?.abort()\n const controller = pending.current = new AbortController()\n try {\n const found = await metadataOf(editor).search(METADATA_LIST.speakers, { query, editor, signal: controller.signal })\n if (!controller.signal.aborted) setItems(found.filter(item => item.label.trim()))\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n }\n useEffect(() => () => {\n clearTimeout(timer.current)\n pending.current?.abort()\n }, [])\n\n const openList = (query: string) => {\n if (!editor.isEditable) return\n setOpen(true)\n scheduleSearch(query)\n }\n\n const closeList = useCallback(() => {\n setOpen(false)\n clearTimeout(timer.current)\n pending.current?.abort()\n }, [])\n\n /** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\n const commit = async (name = draft.trim()) => {\n setDraft(name)\n if (readonly || name === speaker) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(editor).search(METADATA_LIST.speakers, { query: name, editor }) : null\n updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n }\n\n const pick = (item: unknown) => {\n closeList()\n void commit((item as MetadataItem).label)\n }\n\n /** The speaker field of the segment `step` places before/after this one (document order), if any. */\n const neighbourField = (input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined => {\n const fields = [...editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n }\n\n // An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n // speaker and Enter continues in this segment's text.\n const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (open && items.length && list.current?.onKeyDown(e.nativeEvent)) {\n e.preventDefault()\n return\n }\n const input = e.currentTarget\n if (e.key === 'Escape' && open) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n editor.commands.focus(getPos()! + node.nodeSize - 1)\n }\n }\n\n /** Seeks and plays the source recording of the enclosing Typed block (a no-op without the composites feature or a source). */\n const seek = () => {\n const start = node.attrs.start\n const pos = getPos()\n // `seekSource` is contributed by the composites feature; without it the editor has no command and the click does nothing.\n if (typeof start === 'number' && pos !== undefined) editor.commands.seekSource?.(pos, start)\n }\n\n return (\n <NodeViewWrapper\n className={cn('transcript-segment my-2', selected && 'is-selected rounded-md ring-1 ring-primary')}\n data-type=\"transcript-segment\"\n data-id={node.attrs.id || undefined}\n >\n <div className=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div className=\"transcript-segment__speaker relative pt-0.5\" contentEditable={false}>\n <input\n value={draft}\n title={draft || undefined}\n placeholder={t('transcript.speaker')}\n aria-label={t('transcript.speaker')}\n autoComplete=\"off\"\n role=\"combobox\"\n aria-expanded={open && items.length > 0}\n readOnly={readonly}\n className=\"w-full truncate rounded bg-transparent px-1 text-sm text-muted-foreground outline-none placeholder:text-muted-foreground/60 focus:bg-muted focus:text-foreground\"\n onFocus={() => {\n focused.current = true\n setReadonly(!editor.isEditable)\n }}\n onBlur={() => {\n focused.current = false\n closeList()\n void commit()\n }}\n onMouseDown={() => openList('')}\n onChange={(e) => {\n setDraft(e.target.value)\n openList(e.target.value)\n }}\n onKeyDown={onKeyDown}\n />\n {node.attrs.start != null && (\n <button\n type=\"button\"\n className=\"transcript-segment__time block rounded px-1 text-xs tabular-nums text-muted-foreground hover:text-primary hover:underline\"\n title={t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })}\n aria-label={t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })}\n onMouseDown={e => e.preventDefault()}\n onClick={seek}\n >\n {formatSegmentTime(node.attrs.start)}\n </button>\n )}\n {open && items.length > 0 && (\n <div className=\"absolute top-full left-0 z-50 mt-1\">\n <SuggestionList ref={list} items={items} display={displayItem} command={pick} loading={false} loadingText=\"\" emptyText=\"\" />\n </div>\n )}\n </div>\n <NodeViewContent className=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"segment-node.js","names":[],"sources":["../../../src/features/transcript/segment-node.ts"],"sourcesContent":["import { type JSONContent, mergeAttributes, Node } from '@tiptap/core'\nimport { canSplit } from '@tiptap/pm/transform'\nimport { ABILITY } from '../../core/schema'\nimport { lastSpeaker } from './speakers'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n transcriptSegment: {\n /** Turn the current block into a speaker segment (or back into a paragraph when it already is one). */\n toggleTranscriptSegment: (attrs?: { speaker?: string, start?: number | null, end?: number | null }) => ReturnType\n }\n }\n}\n\n/** Markdown label for a segment without speaker; read back as an empty speaker so export → import round-trips. */\nconst UNNAMED_SPEAKER = 'Sprecher'\n/** Longer bold paragraph openers („**Bitte beachten Sie Folgendes:** …“) are not treated as speaker labels. */\nconst MAX_SPEAKER_LENGTH = 80\n\n/** A `data-start`/`data-end` attribute as seconds; null when missing or not a non-negative number. */\nfunction timeAttr(value: string | null): number | null {\n if (value === null || value.trim() === '') return null\n const seconds = Number(value)\n return Number.isFinite(seconds) && seconds >= 0 ? seconds : null\n}\n\n/** `mm:ss` of a segment time in seconds (minutes keep counting past 59: `75:03`); '' for null / not a number. */\nexport function formatSegmentTime(seconds: unknown): string {\n if (typeof seconds !== 'number' || !Number.isFinite(seconds) || seconds < 0) return ''\n const whole = Math.floor(seconds)\n return `${String(Math.floor(whole / 60)).padStart(2, '0')}:${String(whole % 60).padStart(2, '0')}`\n}\n\n// Schema-only node (no node view): shared by the client editor and the server-side Yjs seeding.\n// The client adds its node view in node-view.ts.\nexport const TranscriptSegmentNode = Node.create({\n name: 'transcriptSegment',\n // Above StarterKit's default (100) so this Enter / Mod-Shift-s keymap runs first,\n // below Paragraph (1000) so the paragraph stays the schema's default block.\n priority: 101,\n group: 'block',\n content: 'inline*',\n // A speaker turn is plain running text: no structural blocks or media inside it.\n abilities: {\n revokes: [ABILITY.headings, ABILITY.lists, ABILITY.blockquote, ABILITY.codeBlock, ABILITY.blockContent, ABILITY.composite, ABILITY.media, ABILITY.imageStudio, ABILITY.record],\n },\n defining: true,\n addAttributes: () => ({\n speaker: {\n default: '',\n parseHTML: (el) => el.getAttribute('data-speaker') ?? '',\n renderHTML: (attrs) => ({ 'data-speaker': attrs.speaker }),\n },\n // Seconds into the recording of the enclosing source-holding Baustein; null for a segment without timing.\n start: {\n default: null,\n parseHTML: el => timeAttr(el.getAttribute('data-start')),\n renderHTML: attrs => (attrs.start == null ? {} : { 'data-start': attrs.start }),\n },\n end: {\n default: null,\n parseHTML: el => timeAttr(el.getAttribute('data-end')),\n renderHTML: attrs => (attrs.end == null ? {} : { 'data-end': attrs.end }),\n },\n }),\n parseHTML: () => [{ tag: 'div[data-type=\"transcript-segment\"]' }],\n renderHTML: ({ HTMLAttributes }) => ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'transcript-segment' }), 0],\n // Markdown export (editor.getMarkdown(), AI context/selection) keeps the speaker visible to the model as\n // `**Name:** text`; `start`/`end` (seconds into the source recording) live in JSON/Yjs only and are not part of the\n // Markdown, so the Markdown output of a segment is the same with or without timestamps.\n // Import goes through speakerParagraphsToSegments() below, not parseMarkdown: marked yields\n // an ordinary paragraph token, and Paragraph (priority 1000) would always win that token over this node.\n // Backslash-escaped ASCII punctuation parses back to the literal character, so odd names survive the round-trip.\n // No trailing newline: the serializer separates blocks itself; extra blank lines re-import as empty paragraphs.\n renderMarkdown: (node, h) => `**${String(node.attrs?.speaker || UNNAMED_SPEAKER).replace(/[\\\\*_`[\\]<>~]/g, '\\\\$&')}:** ${h.renderChildren(node.content ?? [])}`,\n addCommands() {\n return {\n toggleTranscriptSegment: (attrs = {}) => ({ editor, commands }) =>\n editor.isActive(this.name)\n ? commands.setNode('paragraph')\n : commands.setNode(this.name, { speaker: attrs.speaker ?? lastSpeaker(editor.state.doc), start: attrs.start ?? null, end: attrs.end ?? null }),\n }\n },\n addKeyboardShortcuts() {\n return {\n // Enter inside a segment splits it into two segments with the same speaker — at the start, middle\n // or end alike. `splitBlock` is not used: it creates the default block (paragraph) at the end and\n // re-types the first half at the start. Shift-Enter keeps StarterKit's hard break.\n Enter: () => {\n const { $from } = this.editor.state.selection\n if ($from.parent.type.name !== this.name) return false\n const type = $from.parent.type\n const speaker = String($from.parent.attrs.speaker ?? '')\n return this.editor.commands.command(({ tr, state, dispatch }) => {\n const marks = state.storedMarks ?? ($from.parentOffset ? $from.marks() : null)\n const after = [{ type, attrs: { speaker } }]\n if (!canSplit(state.doc, state.selection.from, 1, after)) return false\n if (dispatch) {\n if (!tr.selection.empty) tr.deleteSelection()\n tr.split(tr.selection.from, 1, after)\n if (marks) tr.ensureMarks(marks)\n tr.scrollIntoView()\n }\n return true\n })\n },\n 'Mod-Shift-s': () => this.editor.commands.toggleTranscriptSegment(),\n }\n },\n})\n\n/**\n * Speaker name of a speaker line, i.e. a paragraph whose first inline node is bold-only text `Name:`\n * (Markdown `**Name:** text`), or null. Bold elsewhere in the paragraph does not count.\n */\nfunction speakerOf(block: JSONContent): string | null {\n const first = block.content?.[0]\n if (block.type !== 'paragraph' || first?.type !== 'text' || !first.text?.endsWith(':')) return null\n if (first.marks?.length !== 1 || first.marks[0]!.type !== 'bold') return null\n const name = first.text.slice(0, -1).trim()\n if (!name || name.length > MAX_SPEAKER_LENGTH || name.includes('\\n')) return null\n return name\n}\n\n/**\n * Turns top-level speaker-line paragraphs of parsed Markdown into transcriptSegment nodes (the inverse of\n * renderMarkdown above); every other block is returned unchanged. Applied to AI results on both apply paths.\n * A label without text (`**Aufgaben:**` above a list) is a bold pseudo-heading, not an empty turn, and stays a paragraph.\n */\nexport function speakerParagraphsToSegments(doc: JSONContent): JSONContent {\n if (!doc.content) return doc\n return {\n ...doc,\n content: doc.content.map((block) => {\n const name = speakerOf(block)\n if (name === null) return block\n const content = block.content!.slice(1)\n if (content.every(node => node.type === 'text' && !node.text?.trim())) return block\n const next = content[0]\n // renderMarkdown puts exactly one space between label and text; drop only that one.\n if (next?.type === 'text' && next.text && /^\\s/.test(next.text)) {\n if (next.text.length === 1) content.shift()\n else content[0] = { ...next, text: next.text.slice(1) }\n }\n return { type: 'transcriptSegment', attrs: { speaker: name === UNNAMED_SPEAKER ? '' : name }, content }\n }),\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"segment-node.js","names":[],"sources":["../../../src/features/transcript/segment-node.ts"],"sourcesContent":["import { type JSONContent, mergeAttributes, Node } from '@tiptap/core'\nimport { canSplit } from '@tiptap/pm/transform'\nimport { ABILITY } from '../../core/schema'\nimport { lastSpeaker } from './speakers'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n transcriptSegment: {\n /** Turn the current block into a speaker segment (or back into a paragraph when it already is one). */\n toggleTranscriptSegment: (attrs?: { speaker?: string, start?: number | null, end?: number | null }) => ReturnType\n }\n }\n}\n\n/** Markdown label for a segment without speaker; read back as an empty speaker so export → import round-trips. */\nconst UNNAMED_SPEAKER = 'Sprecher'\n/** Longer bold paragraph openers („**Bitte beachten Sie Folgendes:** …“) are not treated as speaker labels. */\nconst MAX_SPEAKER_LENGTH = 80\n\n/** A `data-start`/`data-end` attribute as seconds; null when missing or not a non-negative number. */\nfunction timeAttr(value: string | null): number | null {\n if (value === null || value.trim() === '') return null\n const seconds = Number(value)\n return Number.isFinite(seconds) && seconds >= 0 ? seconds : null\n}\n\n/** `mm:ss` of a segment time in seconds (minutes keep counting past 59: `75:03`); '' for null / not a number. */\nexport function formatSegmentTime(seconds: unknown): string {\n if (typeof seconds !== 'number' || !Number.isFinite(seconds) || seconds < 0) return ''\n const whole = Math.floor(seconds)\n return `${String(Math.floor(whole / 60)).padStart(2, '0')}:${String(whole % 60).padStart(2, '0')}`\n}\n\n// Schema-only node (no node view): shared by the client editor and the server-side Yjs seeding.\n// The client adds its node view in node-view.ts.\nexport const TranscriptSegmentNode = Node.create({\n name: 'transcriptSegment',\n // Above StarterKit's default (100) so this Enter / Mod-Shift-s keymap runs first,\n // below Paragraph (1000) so the paragraph stays the schema's default block.\n priority: 101,\n group: 'block',\n content: 'inline*',\n // A speaker turn is plain running text: no structural blocks or media inside it.\n abilities: {\n revokes: [ABILITY.headings, ABILITY.lists, ABILITY.blockquote, ABILITY.codeBlock, ABILITY.blockContent, ABILITY.composite, ABILITY.media, ABILITY.imageStudio, ABILITY.record],\n },\n defining: true,\n addAttributes: () => ({\n speaker: {\n default: '',\n parseHTML: (el) => el.getAttribute('data-speaker') ?? '',\n renderHTML: (attrs) => ({ 'data-speaker': attrs.speaker }),\n },\n // Seconds into the recording of the enclosing source-holding Typed block; null for a segment without timing.\n start: {\n default: null,\n parseHTML: el => timeAttr(el.getAttribute('data-start')),\n renderHTML: attrs => (attrs.start == null ? {} : { 'data-start': attrs.start }),\n },\n end: {\n default: null,\n parseHTML: el => timeAttr(el.getAttribute('data-end')),\n renderHTML: attrs => (attrs.end == null ? {} : { 'data-end': attrs.end }),\n },\n }),\n parseHTML: () => [{ tag: 'div[data-type=\"transcript-segment\"]' }],\n renderHTML: ({ HTMLAttributes }) => ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'transcript-segment' }), 0],\n // Markdown export (editor.getMarkdown(), AI context/selection) keeps the speaker visible to the model as\n // `**Name:** text`; `start`/`end` (seconds into the source recording) live in JSON/Yjs only and are not part of the\n // Markdown, so the Markdown output of a segment is the same with or without timestamps.\n // Import goes through speakerParagraphsToSegments() below, not parseMarkdown: marked yields\n // an ordinary paragraph token, and Paragraph (priority 1000) would always win that token over this node.\n // Backslash-escaped ASCII punctuation parses back to the literal character, so odd names survive the round-trip.\n // No trailing newline: the serializer separates blocks itself; extra blank lines re-import as empty paragraphs.\n renderMarkdown: (node, h) => `**${String(node.attrs?.speaker || UNNAMED_SPEAKER).replace(/[\\\\*_`[\\]<>~]/g, '\\\\$&')}:** ${h.renderChildren(node.content ?? [])}`,\n addCommands() {\n return {\n toggleTranscriptSegment: (attrs = {}) => ({ editor, commands }) =>\n editor.isActive(this.name)\n ? commands.setNode('paragraph')\n : commands.setNode(this.name, { speaker: attrs.speaker ?? lastSpeaker(editor.state.doc), start: attrs.start ?? null, end: attrs.end ?? null }),\n }\n },\n addKeyboardShortcuts() {\n return {\n // Enter inside a segment splits it into two segments with the same speaker — at the start, middle\n // or end alike. `splitBlock` is not used: it creates the default block (paragraph) at the end and\n // re-types the first half at the start. Shift-Enter keeps StarterKit's hard break.\n Enter: () => {\n const { $from } = this.editor.state.selection\n if ($from.parent.type.name !== this.name) return false\n const type = $from.parent.type\n const speaker = String($from.parent.attrs.speaker ?? '')\n return this.editor.commands.command(({ tr, state, dispatch }) => {\n const marks = state.storedMarks ?? ($from.parentOffset ? $from.marks() : null)\n const after = [{ type, attrs: { speaker } }]\n if (!canSplit(state.doc, state.selection.from, 1, after)) return false\n if (dispatch) {\n if (!tr.selection.empty) tr.deleteSelection()\n tr.split(tr.selection.from, 1, after)\n if (marks) tr.ensureMarks(marks)\n tr.scrollIntoView()\n }\n return true\n })\n },\n 'Mod-Shift-s': () => this.editor.commands.toggleTranscriptSegment(),\n }\n },\n})\n\n/**\n * Speaker name of a speaker line, i.e. a paragraph whose first inline node is bold-only text `Name:`\n * (Markdown `**Name:** text`), or null. Bold elsewhere in the paragraph does not count.\n */\nfunction speakerOf(block: JSONContent): string | null {\n const first = block.content?.[0]\n if (block.type !== 'paragraph' || first?.type !== 'text' || !first.text?.endsWith(':')) return null\n if (first.marks?.length !== 1 || first.marks[0]!.type !== 'bold') return null\n const name = first.text.slice(0, -1).trim()\n if (!name || name.length > MAX_SPEAKER_LENGTH || name.includes('\\n')) return null\n return name\n}\n\n/**\n * Turns top-level speaker-line paragraphs of parsed Markdown into transcriptSegment nodes (the inverse of\n * renderMarkdown above); every other block is returned unchanged. Applied to AI results on both apply paths.\n * A label without text (`**Aufgaben:**` above a list) is a bold pseudo-heading, not an empty turn, and stays a paragraph.\n */\nexport function speakerParagraphsToSegments(doc: JSONContent): JSONContent {\n if (!doc.content) return doc\n return {\n ...doc,\n content: doc.content.map((block) => {\n const name = speakerOf(block)\n if (name === null) return block\n const content = block.content!.slice(1)\n if (content.every(node => node.type === 'text' && !node.text?.trim())) return block\n const next = content[0]\n // renderMarkdown puts exactly one space between label and text; drop only that one.\n if (next?.type === 'text' && next.text && /^\\s/.test(next.text)) {\n if (next.text.length === 1) content.shift()\n else content[0] = { ...next, text: next.text.slice(1) }\n }\n return { type: 'transcriptSegment', attrs: { speaker: name === UNNAMED_SPEAKER ? '' : name }, content }\n }),\n }\n}\n"],"mappings":"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"}
@@ -39,7 +39,7 @@ const blockDropTargetKey = new PluginKey("blockDropTarget");
39
39
  /**
40
40
  * The slice dropping `payload` would insert, for `dropPoint`; `null` when this schema cannot hold the block. It is closed
41
41
  * (`openStart` and `openEnd` 0): an open slice would let `dropPoint` return a position inside a paragraph, where the
42
- * Baustein cannot be inserted.
42
+ * Typed block cannot be inserted.
43
43
  */
44
44
  function sliceOf(editor, payload) {
45
45
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/transfer/feature.ts"],"sourcesContent":["import { type AnyExtension, type Editor, Extension } from '@tiptap/core'\nimport { Fragment, type Node as PMNode, Slice } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { dropPoint } from '@tiptap/pm/transform'\nimport { BLOCK_MIME, type BlockTransfer, readBlockTransfer } from '../../core'\nimport type { EditorFeature, FeatureContext } from '../../core/feature'\nimport { findBlockById } from '../../core/schema'\nimport { createStore, type Store } from '../../core/store'\nimport { availableTransforms, type BlockTransform, DEFAULT_TRANSFORMS, runTransform, type TransferEnv } from './transforms'\n\n/** One drop of a block: the dragged block, where it lands (document position), whether the transform menu opens first and where it opens (viewport pixels). */\nexport interface TransferDrop {\n payload: BlockTransfer\n pos: number\n /** `true`: the menu opens at (`x`, `y`) and the user picks the transform; `false`: the default transform runs. */\n menu: boolean\n x: number\n y: number\n}\n\n/** A drop waiting for the user to pick a transform from the menu (`TransferDrop` with `menu` on). */\nexport type PendingDrop = TransferDrop\n\nexport interface BlockDropTargetStorage {\n /** The drop the transform menu is open for; `null` while no menu is open. */\n pending: Store<PendingDrop | null>\n /** The transforms the menu offers for `drop` (those whose `available` says yes). */\n available: (drop: PendingDrop) => BlockTransform[]\n /** Closes the menu and runs the transform `id` for `drop` (a refused insert shows its notice). */\n run: (id: string, drop: PendingDrop) => Promise<void>\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n /** Absent in an editor without the transfer feature. */\n blockDropTarget?: BlockDropTargetStorage\n }\n interface Commands<ReturnType> {\n blockTransfer: {\n /**\n * Puts the block of `payload` into the document at `pos` (a position where a block may be inserted): with `menu`\n * the transform menu opens at (`x`, `y`) and the transform the user picks runs, else the default transform runs.\n * Refuses a drop of a block onto its own position in the same document (notice). Used by the document tree's drop executor.\n */\n transferBlock: (options: { payload: BlockTransfer, pos: number, menu: boolean, x?: number, y?: number }) => ReturnType\n /** Closes the transform menu without inserting anything. */\n cancelTransfer: () => ReturnType\n }\n }\n}\n\n/**\n * Whether dropping `payload` at `pos` of `doc` lands inside the very block it was dragged from: the payload names a\n * block of this document (same `documentId`, a block with that id exists) and `pos` is strictly inside it.\n */\nexport function isSelfDrop(doc: PMNode, documentId: string | null | undefined, payload: BlockTransfer, pos: number): boolean {\n if (!payload.blockId || (payload.documentId ?? null) !== (documentId ?? null)) return false\n const found = findBlockById(doc, payload.blockId)\n return !!found && pos > found.pos && pos < found.pos + found.node.nodeSize\n}\n\n/**\n * The shared tail of a drop (DOM drop and `transferBlock`): refuses a self-drop, else opens the menu (`menu`) or runs\n * the default transform.\n */\nfunction dropBlock(env: TransferEnv, documentId: string | null, defaultId: string, pending: Store<PendingDrop | null>, drop: TransferDrop): void {\n const { payload, pos } = drop\n pending.set(null)\n if (isSelfDrop(env.editor.state.doc, documentId, payload, pos)) {\n env.notify({ kind: 'warning', title: env.t('transfer.selfDrop') })\n return\n }\n if (drop.menu) pending.set(drop)\n // The transform dispatches its own transaction, which must not collide with the one a tiptap command still holds (the command's transaction is dispatched after it returns).\n else queueMicrotask(() => { void runTransform(env, defaultId, payload, pos) })\n}\n\nconst blockDropTargetKey = new PluginKey('blockDropTarget')\n\n/**\n * The slice dropping `payload` would insert, for `dropPoint`; `null` when this schema cannot hold the block. It is closed\n * (`openStart` and `openEnd` 0): an open slice would let `dropPoint` return a position inside a paragraph, where the\n * Baustein cannot be inserted.\n */\nfunction sliceOf(editor: Editor, payload: BlockTransfer): Slice | null {\n try {\n return new Slice(Fragment.from(editor.schema.nodeFromJSON(payload.json)), 0, 0)\n }\n catch {\n return null\n }\n}\n\n/**\n * Takes blocks dragged out of another editor surface or document tree (`BLOCK_MIME`): a plain drop runs the default\n * transform, Shift opens the transform menu at the drop point. A drag that started in this editor stays ProseMirror's\n * native move. Also the commands `transferBlock` and `cancelTransfer`.\n */\nfunction blockDropTarget(ctx: FeatureContext, transforms: readonly BlockTransform[], defaultId: string): AnyExtension {\n const envOf = (editor: Editor): TransferEnv => ({ editor, transforms, ai: ctx.backend.ai, app: ctx.app, notify: ctx.notify, t: ctx.t })\n // The storage is built before the editor exists (`addStorage` has no `this.editor`); `onBeforeCreate` hands it over.\n let created: Editor | undefined\n const editorOf = (): Editor => {\n if (!created) throw new Error('blockDropTarget: the editor is not created yet')\n return created\n }\n return Extension.create<unknown, BlockDropTargetStorage>({\n name: 'blockDropTarget',\n onBeforeCreate() {\n created = this.editor\n },\n addStorage() {\n const pending = createStore<PendingDrop | null>(null)\n return {\n pending,\n available: ({ payload, pos }) => availableTransforms(envOf(editorOf()), payload, pos),\n run: async (id, { payload, pos }) => {\n pending.set(null)\n await runTransform(envOf(editorOf()), id, payload, pos)\n },\n }\n },\n addCommands() {\n return {\n transferBlock: ({ payload, pos, menu, x = 0, y = 0 }) => ({ editor, dispatch }) => {\n if (!dispatch) return true\n dropBlock(envOf(editor), ctx.documentId ?? null, defaultId, this.storage.pending, { payload, pos: Math.min(Math.max(pos, 0), editor.state.doc.content.size), menu, x, y })\n return true\n },\n cancelTransfer: () => () => {\n this.storage.pending.set(null)\n return true\n },\n }\n },\n addProseMirrorPlugins() {\n const editor = this.editor\n return [new Plugin({\n key: blockDropTargetKey,\n props: {\n handleDOMEvents: {\n dragover: (view, event) => {\n if (view.dragging || !view.editable || !event.dataTransfer?.types.includes(BLOCK_MIME)) return false\n event.preventDefault()\n event.dataTransfer.dropEffect = 'copy'\n return false\n },\n },\n handleDrop: (view, event) => {\n if (view.dragging || !view.editable || !event.dataTransfer) return false\n const payload = readBlockTransfer(event.dataTransfer)\n if (!payload) return false\n event.preventDefault()\n const env = envOf(editor)\n const slice = sliceOf(editor, payload)\n if (!slice) {\n env.notify({ kind: 'warning', title: env.t('transfer.refused') })\n return true\n }\n const at = view.posAtCoords({ left: event.clientX, top: event.clientY })?.pos ?? view.state.doc.content.size\n const pos = dropPoint(view.state.doc, at, slice) ?? view.state.doc.content.size\n dropBlock(env, ctx.documentId ?? null, defaultId, this.storage.pending, { payload, pos, menu: event.shiftKey, x: event.clientX, y: event.clientY })\n return true\n },\n },\n })]\n },\n })\n}\n\n/** Options of `transferFeature`. */\nexport interface TransferFeatureOptions {\n /** The transforms the menu offers and a drop can run; default `[copyTransform]` (`composeEditor` adds the AI transforms of `@ecsplico/editor/integrations/composite-ai` when `ai` is on). A host list replaces the default (add built-ins explicitly). */\n transforms?: readonly BlockTransform[]\n /** Id of the transform a plain drop (no Shift) runs; default `copy`. */\n defaultTransform?: string\n}\n\n/** What a framework binding supplies to `transferFeatureCore`. */\nexport interface TransferUi<C> {\n /** The transform menu (receives `{ editor }`), mounted as an overlay. */\n menu: C\n}\n\n/**\n * Block transfer between editor surfaces and document trees (the framework-free part of `transferFeature`): a block\n * dragged out of another surface lands as a copy with fresh ids and a `meta.derivation` pointing at its source, or,\n * with Shift held at the drop, as the result of a transform picked from a menu (the copy, and the AI transforms a host or `composeEditor` adds).\n * Insert guards apply; the commands `transferBlock` and `cancelTransfer` serve the document tree's drops.\n */\nexport function transferFeatureCore<C>(ui: TransferUi<C>, options: TransferFeatureOptions = {}): EditorFeature<C> {\n const transforms = options.transforms ?? DEFAULT_TRANSFORMS\n const defaultId = options.defaultTransform ?? 'copy'\n return {\n id: 'transfer',\n dimension: 'interaction',\n requires: ['composites'],\n extensions: ctx => [blockDropTarget(ctx, transforms, defaultId)],\n overlays: [ui.menu],\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/transfer/feature.ts"],"sourcesContent":["import { type AnyExtension, type Editor, Extension } from '@tiptap/core'\nimport { Fragment, type Node as PMNode, Slice } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { dropPoint } from '@tiptap/pm/transform'\nimport { BLOCK_MIME, type BlockTransfer, readBlockTransfer } from '../../core'\nimport type { EditorFeature, FeatureContext } from '../../core/feature'\nimport { findBlockById } from '../../core/schema'\nimport { createStore, type Store } from '../../core/store'\nimport { availableTransforms, type BlockTransform, DEFAULT_TRANSFORMS, runTransform, type TransferEnv } from './transforms'\n\n/** One drop of a block: the dragged block, where it lands (document position), whether the transform menu opens first and where it opens (viewport pixels). */\nexport interface TransferDrop {\n payload: BlockTransfer\n pos: number\n /** `true`: the menu opens at (`x`, `y`) and the user picks the transform; `false`: the default transform runs. */\n menu: boolean\n x: number\n y: number\n}\n\n/** A drop waiting for the user to pick a transform from the menu (`TransferDrop` with `menu` on). */\nexport type PendingDrop = TransferDrop\n\nexport interface BlockDropTargetStorage {\n /** The drop the transform menu is open for; `null` while no menu is open. */\n pending: Store<PendingDrop | null>\n /** The transforms the menu offers for `drop` (those whose `available` says yes). */\n available: (drop: PendingDrop) => BlockTransform[]\n /** Closes the menu and runs the transform `id` for `drop` (a refused insert shows its notice). */\n run: (id: string, drop: PendingDrop) => Promise<void>\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n /** Absent in an editor without the transfer feature. */\n blockDropTarget?: BlockDropTargetStorage\n }\n interface Commands<ReturnType> {\n blockTransfer: {\n /**\n * Puts the block of `payload` into the document at `pos` (a position where a block may be inserted): with `menu`\n * the transform menu opens at (`x`, `y`) and the transform the user picks runs, else the default transform runs.\n * Refuses a drop of a block onto its own position in the same document (notice). Used by the document tree's drop executor.\n */\n transferBlock: (options: { payload: BlockTransfer, pos: number, menu: boolean, x?: number, y?: number }) => ReturnType\n /** Closes the transform menu without inserting anything. */\n cancelTransfer: () => ReturnType\n }\n }\n}\n\n/**\n * Whether dropping `payload` at `pos` of `doc` lands inside the very block it was dragged from: the payload names a\n * block of this document (same `documentId`, a block with that id exists) and `pos` is strictly inside it.\n */\nexport function isSelfDrop(doc: PMNode, documentId: string | null | undefined, payload: BlockTransfer, pos: number): boolean {\n if (!payload.blockId || (payload.documentId ?? null) !== (documentId ?? null)) return false\n const found = findBlockById(doc, payload.blockId)\n return !!found && pos > found.pos && pos < found.pos + found.node.nodeSize\n}\n\n/**\n * The shared tail of a drop (DOM drop and `transferBlock`): refuses a self-drop, else opens the menu (`menu`) or runs\n * the default transform.\n */\nfunction dropBlock(env: TransferEnv, documentId: string | null, defaultId: string, pending: Store<PendingDrop | null>, drop: TransferDrop): void {\n const { payload, pos } = drop\n pending.set(null)\n if (isSelfDrop(env.editor.state.doc, documentId, payload, pos)) {\n env.notify({ kind: 'warning', title: env.t('transfer.selfDrop') })\n return\n }\n if (drop.menu) pending.set(drop)\n // The transform dispatches its own transaction, which must not collide with the one a tiptap command still holds (the command's transaction is dispatched after it returns).\n else queueMicrotask(() => { void runTransform(env, defaultId, payload, pos) })\n}\n\nconst blockDropTargetKey = new PluginKey('blockDropTarget')\n\n/**\n * The slice dropping `payload` would insert, for `dropPoint`; `null` when this schema cannot hold the block. It is closed\n * (`openStart` and `openEnd` 0): an open slice would let `dropPoint` return a position inside a paragraph, where the\n * Typed block cannot be inserted.\n */\nfunction sliceOf(editor: Editor, payload: BlockTransfer): Slice | null {\n try {\n return new Slice(Fragment.from(editor.schema.nodeFromJSON(payload.json)), 0, 0)\n }\n catch {\n return null\n }\n}\n\n/**\n * Takes blocks dragged out of another editor surface or document tree (`BLOCK_MIME`): a plain drop runs the default\n * transform, Shift opens the transform menu at the drop point. A drag that started in this editor stays ProseMirror's\n * native move. Also the commands `transferBlock` and `cancelTransfer`.\n */\nfunction blockDropTarget(ctx: FeatureContext, transforms: readonly BlockTransform[], defaultId: string): AnyExtension {\n const envOf = (editor: Editor): TransferEnv => ({ editor, transforms, ai: ctx.backend.ai, app: ctx.app, notify: ctx.notify, t: ctx.t })\n // The storage is built before the editor exists (`addStorage` has no `this.editor`); `onBeforeCreate` hands it over.\n let created: Editor | undefined\n const editorOf = (): Editor => {\n if (!created) throw new Error('blockDropTarget: the editor is not created yet')\n return created\n }\n return Extension.create<unknown, BlockDropTargetStorage>({\n name: 'blockDropTarget',\n onBeforeCreate() {\n created = this.editor\n },\n addStorage() {\n const pending = createStore<PendingDrop | null>(null)\n return {\n pending,\n available: ({ payload, pos }) => availableTransforms(envOf(editorOf()), payload, pos),\n run: async (id, { payload, pos }) => {\n pending.set(null)\n await runTransform(envOf(editorOf()), id, payload, pos)\n },\n }\n },\n addCommands() {\n return {\n transferBlock: ({ payload, pos, menu, x = 0, y = 0 }) => ({ editor, dispatch }) => {\n if (!dispatch) return true\n dropBlock(envOf(editor), ctx.documentId ?? null, defaultId, this.storage.pending, { payload, pos: Math.min(Math.max(pos, 0), editor.state.doc.content.size), menu, x, y })\n return true\n },\n cancelTransfer: () => () => {\n this.storage.pending.set(null)\n return true\n },\n }\n },\n addProseMirrorPlugins() {\n const editor = this.editor\n return [new Plugin({\n key: blockDropTargetKey,\n props: {\n handleDOMEvents: {\n dragover: (view, event) => {\n if (view.dragging || !view.editable || !event.dataTransfer?.types.includes(BLOCK_MIME)) return false\n event.preventDefault()\n event.dataTransfer.dropEffect = 'copy'\n return false\n },\n },\n handleDrop: (view, event) => {\n if (view.dragging || !view.editable || !event.dataTransfer) return false\n const payload = readBlockTransfer(event.dataTransfer)\n if (!payload) return false\n event.preventDefault()\n const env = envOf(editor)\n const slice = sliceOf(editor, payload)\n if (!slice) {\n env.notify({ kind: 'warning', title: env.t('transfer.refused') })\n return true\n }\n const at = view.posAtCoords({ left: event.clientX, top: event.clientY })?.pos ?? view.state.doc.content.size\n const pos = dropPoint(view.state.doc, at, slice) ?? view.state.doc.content.size\n dropBlock(env, ctx.documentId ?? null, defaultId, this.storage.pending, { payload, pos, menu: event.shiftKey, x: event.clientX, y: event.clientY })\n return true\n },\n },\n })]\n },\n })\n}\n\n/** Options of `transferFeature`. */\nexport interface TransferFeatureOptions {\n /** The transforms the menu offers and a drop can run; default `[copyTransform]` (`composeEditor` adds the AI transforms of `@ecsplico/editor/integrations/composite-ai` when `ai` is on). A host list replaces the default (add built-ins explicitly). */\n transforms?: readonly BlockTransform[]\n /** Id of the transform a plain drop (no Shift) runs; default `copy`. */\n defaultTransform?: string\n}\n\n/** What a framework binding supplies to `transferFeatureCore`. */\nexport interface TransferUi<C> {\n /** The transform menu (receives `{ editor }`), mounted as an overlay. */\n menu: C\n}\n\n/**\n * Block transfer between editor surfaces and document trees (the framework-free part of `transferFeature`): a block\n * dragged out of another surface lands as a copy with fresh ids and a `meta.derivation` pointing at its source, or,\n * with Shift held at the drop, as the result of a transform picked from a menu (the copy, and the AI transforms a host or `composeEditor` adds).\n * Insert guards apply; the commands `transferBlock` and `cancelTransfer` serve the document tree's drops.\n */\nexport function transferFeatureCore<C>(ui: TransferUi<C>, options: TransferFeatureOptions = {}): EditorFeature<C> {\n const transforms = options.transforms ?? DEFAULT_TRANSFORMS\n const defaultId = options.defaultTransform ?? 'copy'\n return {\n id: 'transfer',\n dimension: 'interaction',\n requires: ['composites'],\n extensions: ctx => [blockDropTarget(ctx, transforms, defaultId)],\n overlays: [ui.menu],\n }\n}\n"],"mappings":"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"}
@@ -44,15 +44,15 @@ export declare function withFreshBlockIds(editor: Editor, json: JSONContent): JS
44
44
  /** The `Creates` a node of `json` stands for in the insert guards. */
45
45
  export declare const createsOf: (json: JSONContent) => Creates;
46
46
  /**
47
- * The hash a derivation of the dragged block records as `inputHash`: the Markdown of a Baustein's body (the same text
47
+ * The hash a derivation of the dragged block records as `inputHash`: the Markdown of a Typed block's body (the same text
48
48
  * `derivationState` hashes for a source in the open document), else of the whole block. Without a Markdown serializer the
49
49
  * hash of the payload's Markdown.
50
50
  */
51
51
  export declare function transferSourceHash(editor: Editor, payload: BlockTransfer): string;
52
- /** The kind of Baustein the AI transforms insert: the setting `blocks.defaultKind` when the editor offers it, else its first kind. */
52
+ /** The Block type of the Typed block the AI transforms insert: the setting `blocks.defaultBlockType` when the editor offers it, else its first Block type. */
53
53
  export declare function transferKindOf(editor: Editor): string;
54
54
  /**
55
- * „Kopie einfügen“: inserts the dragged block with fresh block ids (the block and every block inside it). A Baustein
55
+ * „Kopie einfügen“: inserts the dragged block with fresh block ids (the block and every block inside it). A Typed block
56
56
  * that has a block id records its source in `meta.derivation` (`from: { block, document }`, method `copy`, the source
57
57
  * Markdown as `inputHash`, the hash of the copy's body as `outputHash`, so the badge shows „bearbeitet“ once the copy is edited).
58
58
  */
@@ -75,7 +75,7 @@ export declare function availableTransforms(env: TransferEnv, payload: BlockTran
75
75
  /**
76
76
  * Runs the transform `id` of `env` for dropping `payload` at `pos`. Nothing runs for an unknown or unavailable
77
77
  * transform. What the transform inserts (`BlockTransform.creates`) must pass the insert guards below `pos`'s parent and
78
- * the parent's content rules, else the warning „Hier lässt sich der Baustein nicht einfügen“ is shown and the document stays
78
+ * the parent's content rules, else the warning „Hier lässt sich der Block nicht einfügen“ is shown and the document stays
79
79
  * unchanged. The transform's own failure is its own to report; this never throws for a refusal.
80
80
  */
81
81
  export declare function runTransform(env: TransferEnv, id: string, payload: BlockTransfer, pos: number): Promise<void>;
@@ -1,11 +1,11 @@
1
1
  import { de } from "../../i18n/de.js";
2
+ import { newBlockId } from "../../core/schema/block-ids.js";
2
3
  import { editorEntryOf } from "../../core/editor-ui.js";
3
4
  import { guardsAccept } from "../../core/commands.js";
4
- import { newBlockId } from "../../core/schema/block-ids.js";
5
+ import { DERIVATION_KEY } from "../../core/derivation.js";
5
6
  import { sha256Hex } from "../../core/hash.js";
6
- import { DERIVATION_KEY } from "../composites/meta-keys.js";
7
- import { bodyMarkdown } from "../composites/derivation.js";
8
- import { compositeKindsOf } from "../composites/nodes.js";
7
+ import { blockTypesOf } from "../composites/nodes.js";
8
+ import { bodyMarkdown } from "../composites/source.js";
9
9
  //#region src/features/transfer/transforms.ts
10
10
  /** `json` with a fresh block id on every block that carries one (recursively); other nodes and attributes stay. */
11
11
  function withFreshBlockIds(editor, json) {
@@ -26,26 +26,26 @@ const createsOf = (json) => ({
26
26
  ...json.attrs && { attrs: json.attrs }
27
27
  });
28
28
  /**
29
- * The hash a derivation of the dragged block records as `inputHash`: the Markdown of a Baustein's body (the same text
29
+ * The hash a derivation of the dragged block records as `inputHash`: the Markdown of a Typed block's body (the same text
30
30
  * `derivationState` hashes for a source in the open document), else of the whole block. Without a Markdown serializer the
31
31
  * hash of the payload's Markdown.
32
32
  */
33
33
  function transferSourceHash(editor, payload) {
34
34
  const { json } = payload;
35
- if (!editor.markdown || json.type !== "composite") return sha256Hex(payload.markdown);
35
+ if (!editor.markdown || json.type !== "typedBlock") return sha256Hex(payload.markdown);
36
36
  return sha256Hex(editor.markdown.serialize({
37
37
  type: "doc",
38
38
  content: (json.content ?? []).slice(3)
39
39
  }));
40
40
  }
41
- /** The kind of Baustein the AI transforms insert: the setting `blocks.defaultKind` when the editor offers it, else its first kind. */
41
+ /** The Block type of the Typed block the AI transforms insert: the setting `blocks.defaultBlockType` when the editor offers it, else its first Block type. */
42
42
  function transferKindOf(editor) {
43
- const kinds = compositeKindsOf(editor);
44
- const wanted = editorEntryOf(editor)?.ctx.settings.get().blocks.defaultKind;
43
+ const kinds = blockTypesOf(editor);
44
+ const wanted = editorEntryOf(editor)?.ctx.settings.get().blocks.defaultBlockType;
45
45
  return kinds.find((kind) => kind.id === wanted)?.id ?? kinds[0]?.id ?? "section";
46
46
  }
47
47
  /**
48
- * „Kopie einfügen“: inserts the dragged block with fresh block ids (the block and every block inside it). A Baustein
48
+ * „Kopie einfügen“: inserts the dragged block with fresh block ids (the block and every block inside it). A Typed block
49
49
  * that has a block id records its source in `meta.derivation` (`from: { block, document }`, method `copy`, the source
50
50
  * Markdown as `inputHash`, the hash of the copy's body as `outputHash`, so the badge shows „bearbeitet“ once the copy is edited).
51
51
  */
@@ -58,7 +58,7 @@ const copyTransform = {
58
58
  run: async ({ editor, payload, pos }) => {
59
59
  const json = withFreshBlockIds(editor, payload.json);
60
60
  let node = editor.schema.nodeFromJSON(json);
61
- if (json.type === "composite" && payload.blockId) {
61
+ if (json.type === "typedBlock" && payload.blockId) {
62
62
  const derivation = {
63
63
  from: {
64
64
  block: payload.blockId,
@@ -106,7 +106,7 @@ function availableTransforms(env, payload, pos) {
106
106
  /**
107
107
  * Runs the transform `id` of `env` for dropping `payload` at `pos`. Nothing runs for an unknown or unavailable
108
108
  * transform. What the transform inserts (`BlockTransform.creates`) must pass the insert guards below `pos`'s parent and
109
- * the parent's content rules, else the warning „Hier lässt sich der Baustein nicht einfügen“ is shown and the document stays
109
+ * the parent's content rules, else the warning „Hier lässt sich der Block nicht einfügen“ is shown and the document stays
110
110
  * unchanged. The transform's own failure is its own to report; this never throws for a refusal.
111
111
  */
112
112
  async function runTransform(env, id, payload, pos) {
@@ -1 +1 @@
1
- {"version":3,"file":"transforms.js","names":[],"sources":["../../../src/features/transfer/transforms.ts"],"sourcesContent":["// Types only: brings in the `editor.markdown` augmentation of `@tiptap/markdown` for contexts that do not otherwise load it.\nimport type {} from '@tiptap/markdown'\nimport type { Editor, JSONContent } from '@tiptap/core'\nimport type { EditorBackend } from '../../core/backend'\nimport { type BlockTransfer, editorEntryOf, guardsAccept, sha256Hex } from '../../core'\nimport type { Creates } from '../../core/command-types'\nimport type { EditorContext } from '../../core/context'\nimport type { Notifier } from '../../core/notify'\nimport { newBlockId } from '../../core/schema'\nimport { de } from '../../i18n/de'\nimport type { MessageKey, Translate } from '../../i18n'\nimport { bodyMarkdown, COMPOSITE_CHROME_COUNT, compositeKindsOf, DERIVATION_KEY, type Derivation } from '../composites/schema'\n\n/** What a transform works with: the target editor, the dragged block, where it lands and the app services. */\nexport interface TransformContext {\n editor: Editor\n payload: BlockTransfer\n /** Document position the result is inserted at (a valid drop point of `editor.state.doc`). */\n pos: number\n /** The app's AI backend; absent when the app has none. */\n ai: EditorBackend['ai'] | undefined\n /** The app context the editor belongs to; transforms that need app services (the AI model profiles) read them from it. */\n app: EditorContext\n notify: Notifier\n t: Translate\n}\n\n/** One way to put a dragged block into a surface: offered by the transform menu and run by a plain drop (the default transform). */\nexport interface BlockTransform {\n id: string\n /** Menu text; shown as is unless `labelKey` is set. */\n label: string\n /** Message key of the label: the menu renders it in the editor's language instead of `label`. */\n labelKey?: MessageKey\n /** Iconify name (`i-lucide-…`). */\n icon: string\n /** Absent = always available. */\n available?: (ctx: TransformContext) => boolean\n /**\n * What `run` inserts at `ctx.pos`, so the insert guards and the target's content rules are checked before `run`; a\n * refused insert shows the warning „lässt sich nicht einfügen“ and runs nothing. Absent: the transform `copy` is\n * checked as inserting the dragged block itself, every other transform checks itself.\n */\n creates?: (ctx: TransformContext) => Creates | null\n run: (ctx: TransformContext) => Promise<void>\n}\n\n/** `json` with a fresh block id on every block that carries one (recursively); other nodes and attributes stay. */\nexport function withFreshBlockIds(editor: Editor, json: JSONContent): JSONContent {\n const visit = (node: JSONContent): JSONContent => {\n const next: JSONContent = { ...node }\n if (node.type && editor.schema.nodes[node.type]?.spec.editorBlock === true) next.attrs = { ...node.attrs, id: newBlockId() }\n if (node.content) next.content = node.content.map(visit)\n return next\n }\n return visit(json)\n}\n\n/** The `Creates` a node of `json` stands for in the insert guards. */\nexport const createsOf = (json: JSONContent): Creates => ({ type: json.type ?? '', ...(json.attrs && { attrs: json.attrs }) })\n\n/**\n * The hash a derivation of the dragged block records as `inputHash`: the Markdown of a Baustein's body (the same text\n * `derivationState` hashes for a source in the open document), else of the whole block. Without a Markdown serializer the\n * hash of the payload's Markdown.\n */\nexport function transferSourceHash(editor: Editor, payload: BlockTransfer): string {\n const { json } = payload\n if (!editor.markdown || json.type !== 'composite') return sha256Hex(payload.markdown)\n return sha256Hex(editor.markdown.serialize({ type: 'doc', content: (json.content ?? []).slice(COMPOSITE_CHROME_COUNT) }))\n}\n\n/** The kind of Baustein the AI transforms insert: the setting `blocks.defaultKind` when the editor offers it, else its first kind. */\nexport function transferKindOf(editor: Editor): string {\n const kinds = compositeKindsOf(editor)\n const wanted = editorEntryOf(editor)?.ctx.settings.get().blocks.defaultKind\n return kinds.find(kind => kind.id === wanted)?.id ?? kinds[0]?.id ?? 'section'\n}\n\n/**\n * „Kopie einfügen“: inserts the dragged block with fresh block ids (the block and every block inside it). A Baustein\n * that has a block id records its source in `meta.derivation` (`from: { block, document }`, method `copy`, the source\n * Markdown as `inputHash`, the hash of the copy's body as `outputHash`, so the badge shows „bearbeitet“ once the copy is edited).\n */\nexport const copyTransform: BlockTransform = {\n id: 'copy',\n label: de['transfer.copy'],\n labelKey: 'transfer.copy',\n icon: 'i-lucide-copy',\n creates: ctx => createsOf(ctx.payload.json),\n run: async ({ editor, payload, pos }) => {\n const json = withFreshBlockIds(editor, payload.json)\n let node = editor.schema.nodeFromJSON(json)\n if (json.type === 'composite' && payload.blockId) {\n const derivation: Derivation = {\n from: { block: payload.blockId, ...(payload.documentId && { document: payload.documentId }) },\n method: 'copy',\n createdAt: new Date().toISOString(),\n inputHash: transferSourceHash(editor, payload),\n outputHash: editor.markdown ? sha256Hex(bodyMarkdown(editor, node)) : transferSourceHash(editor, payload),\n }\n node = editor.schema.nodeFromJSON({ ...json, attrs: { ...json.attrs, meta: { ...json.attrs?.meta, [DERIVATION_KEY]: derivation } } })\n }\n editor.view.dispatch(editor.state.tr.insert(pos, node).scrollIntoView())\n },\n}\n\n/** The transforms `transferFeature` offers unless the host passes its own list: the copy. The AI transforms (`summaryTransform`, `bulletsTransform`) live in `@ecsplico/editor/integrations/composite-ai`; `composeEditor` adds them when `ai` is on. */\nexport const DEFAULT_TRANSFORMS: readonly BlockTransform[] = [copyTransform]\n\n/** What `runTransform` needs: the editor, the transform list and the app services. */\nexport interface TransferEnv {\n editor: Editor\n transforms: readonly BlockTransform[]\n ai: EditorBackend['ai'] | undefined\n app: EditorContext\n notify: Notifier\n t: Translate\n}\n\n/** The context a transform of `env` gets for dropping `payload` at `pos` (clamped into the document). */\nexport function transformContextOf(env: TransferEnv, payload: BlockTransfer, pos: number): TransformContext {\n const { editor, ai, app, notify, t } = env\n return { editor, payload, pos: Math.min(Math.max(pos, 0), editor.state.doc.content.size), ai, app, notify, t }\n}\n\n/** The transforms of `env` available for dropping `payload` at `pos`, in list order. */\nexport function availableTransforms(env: TransferEnv, payload: BlockTransfer, pos: number): BlockTransform[] {\n const ctx = transformContextOf(env, payload, pos)\n return env.transforms.filter(transform => transform.available?.(ctx) ?? true)\n}\n\n/**\n * Runs the transform `id` of `env` for dropping `payload` at `pos`. Nothing runs for an unknown or unavailable\n * transform. What the transform inserts (`BlockTransform.creates`) must pass the insert guards below `pos`'s parent and\n * the parent's content rules, else the warning „Hier lässt sich der Baustein nicht einfügen“ is shown and the document stays\n * unchanged. The transform's own failure is its own to report; this never throws for a refusal.\n */\nexport async function runTransform(env: TransferEnv, id: string, payload: BlockTransfer, pos: number): Promise<void> {\n const transform = env.transforms.find(candidate => candidate.id === id)\n if (!transform) return\n const ctx = transformContextOf(env, payload, pos)\n if (transform.available && !transform.available(ctx)) return\n const creates = transform.creates?.(ctx) ?? (transform.id === 'copy' ? createsOf(payload.json) : null)\n if (creates && !canInsertAt(env.editor, ctx.pos, creates)) {\n env.notify({ kind: 'warning', title: env.t('transfer.refused') })\n return\n }\n await transform.run(ctx)\n}\n\n/** Whether a node like `creates` fits the content rules below `pos`'s parent and passes every insert guard there. */\nfunction canInsertAt(editor: Editor, pos: number, creates: Creates): boolean {\n const type = editor.schema.nodes[creates.type]\n const $pos = editor.state.doc.resolve(pos)\n if (!type || !$pos.parent.canReplaceWith($pos.index(), $pos.index(), type)) return false\n return guardsAccept(editor, $pos, creates)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transforms.js","names":[],"sources":["../../../src/features/transfer/transforms.ts"],"sourcesContent":["// Types only: brings in the `editor.markdown` augmentation of `@tiptap/markdown` for contexts that do not otherwise load it.\nimport type {} from '@tiptap/markdown'\nimport type { Editor, JSONContent } from '@tiptap/core'\nimport type { EditorBackend } from '../../core/backend'\nimport { type BlockTransfer, DERIVATION_KEY, type Derivation, editorEntryOf, guardsAccept, sha256Hex } from '../../core'\nimport type { Creates } from '../../core/command-types'\nimport type { EditorContext } from '../../core/context'\nimport type { Notifier } from '../../core/notify'\nimport { newBlockId } from '../../core/schema'\nimport { de } from '../../i18n/de'\nimport type { MessageKey, Translate } from '../../i18n'\nimport { bodyMarkdown, COMPOSITE_CHROME_COUNT, blockTypesOf } from '../composites/schema'\n\n/** What a transform works with: the target editor, the dragged block, where it lands and the app services. */\nexport interface TransformContext {\n editor: Editor\n payload: BlockTransfer\n /** Document position the result is inserted at (a valid drop point of `editor.state.doc`). */\n pos: number\n /** The app's AI backend; absent when the app has none. */\n ai: EditorBackend['ai'] | undefined\n /** The app context the editor belongs to; transforms that need app services (the AI model profiles) read them from it. */\n app: EditorContext\n notify: Notifier\n t: Translate\n}\n\n/** One way to put a dragged block into a surface: offered by the transform menu and run by a plain drop (the default transform). */\nexport interface BlockTransform {\n id: string\n /** Menu text; shown as is unless `labelKey` is set. */\n label: string\n /** Message key of the label: the menu renders it in the editor's language instead of `label`. */\n labelKey?: MessageKey\n /** Iconify name (`i-lucide-…`). */\n icon: string\n /** Absent = always available. */\n available?: (ctx: TransformContext) => boolean\n /**\n * What `run` inserts at `ctx.pos`, so the insert guards and the target's content rules are checked before `run`; a\n * refused insert shows the warning „lässt sich nicht einfügen“ and runs nothing. Absent: the transform `copy` is\n * checked as inserting the dragged block itself, every other transform checks itself.\n */\n creates?: (ctx: TransformContext) => Creates | null\n run: (ctx: TransformContext) => Promise<void>\n}\n\n/** `json` with a fresh block id on every block that carries one (recursively); other nodes and attributes stay. */\nexport function withFreshBlockIds(editor: Editor, json: JSONContent): JSONContent {\n const visit = (node: JSONContent): JSONContent => {\n const next: JSONContent = { ...node }\n if (node.type && editor.schema.nodes[node.type]?.spec.editorBlock === true) next.attrs = { ...node.attrs, id: newBlockId() }\n if (node.content) next.content = node.content.map(visit)\n return next\n }\n return visit(json)\n}\n\n/** The `Creates` a node of `json` stands for in the insert guards. */\nexport const createsOf = (json: JSONContent): Creates => ({ type: json.type ?? '', ...(json.attrs && { attrs: json.attrs }) })\n\n/**\n * The hash a derivation of the dragged block records as `inputHash`: the Markdown of a Typed block's body (the same text\n * `derivationState` hashes for a source in the open document), else of the whole block. Without a Markdown serializer the\n * hash of the payload's Markdown.\n */\nexport function transferSourceHash(editor: Editor, payload: BlockTransfer): string {\n const { json } = payload\n if (!editor.markdown || json.type !== 'typedBlock') return sha256Hex(payload.markdown)\n return sha256Hex(editor.markdown.serialize({ type: 'doc', content: (json.content ?? []).slice(COMPOSITE_CHROME_COUNT) }))\n}\n\n/** The Block type of the Typed block the AI transforms insert: the setting `blocks.defaultBlockType` when the editor offers it, else its first Block type. */\nexport function transferKindOf(editor: Editor): string {\n const kinds = blockTypesOf(editor)\n const wanted = editorEntryOf(editor)?.ctx.settings.get().blocks.defaultBlockType\n return kinds.find(kind => kind.id === wanted)?.id ?? kinds[0]?.id ?? 'section'\n}\n\n/**\n * „Kopie einfügen“: inserts the dragged block with fresh block ids (the block and every block inside it). A Typed block\n * that has a block id records its source in `meta.derivation` (`from: { block, document }`, method `copy`, the source\n * Markdown as `inputHash`, the hash of the copy's body as `outputHash`, so the badge shows „bearbeitet“ once the copy is edited).\n */\nexport const copyTransform: BlockTransform = {\n id: 'copy',\n label: de['transfer.copy'],\n labelKey: 'transfer.copy',\n icon: 'i-lucide-copy',\n creates: ctx => createsOf(ctx.payload.json),\n run: async ({ editor, payload, pos }) => {\n const json = withFreshBlockIds(editor, payload.json)\n let node = editor.schema.nodeFromJSON(json)\n if (json.type === 'typedBlock' && payload.blockId) {\n const derivation: Derivation = {\n from: { block: payload.blockId, ...(payload.documentId && { document: payload.documentId }) },\n method: 'copy',\n createdAt: new Date().toISOString(),\n inputHash: transferSourceHash(editor, payload),\n outputHash: editor.markdown ? sha256Hex(bodyMarkdown(editor, node)) : transferSourceHash(editor, payload),\n }\n node = editor.schema.nodeFromJSON({ ...json, attrs: { ...json.attrs, meta: { ...json.attrs?.meta, [DERIVATION_KEY]: derivation } } })\n }\n editor.view.dispatch(editor.state.tr.insert(pos, node).scrollIntoView())\n },\n}\n\n/** The transforms `transferFeature` offers unless the host passes its own list: the copy. The AI transforms (`summaryTransform`, `bulletsTransform`) live in `@ecsplico/editor/integrations/composite-ai`; `composeEditor` adds them when `ai` is on. */\nexport const DEFAULT_TRANSFORMS: readonly BlockTransform[] = [copyTransform]\n\n/** What `runTransform` needs: the editor, the transform list and the app services. */\nexport interface TransferEnv {\n editor: Editor\n transforms: readonly BlockTransform[]\n ai: EditorBackend['ai'] | undefined\n app: EditorContext\n notify: Notifier\n t: Translate\n}\n\n/** The context a transform of `env` gets for dropping `payload` at `pos` (clamped into the document). */\nexport function transformContextOf(env: TransferEnv, payload: BlockTransfer, pos: number): TransformContext {\n const { editor, ai, app, notify, t } = env\n return { editor, payload, pos: Math.min(Math.max(pos, 0), editor.state.doc.content.size), ai, app, notify, t }\n}\n\n/** The transforms of `env` available for dropping `payload` at `pos`, in list order. */\nexport function availableTransforms(env: TransferEnv, payload: BlockTransfer, pos: number): BlockTransform[] {\n const ctx = transformContextOf(env, payload, pos)\n return env.transforms.filter(transform => transform.available?.(ctx) ?? true)\n}\n\n/**\n * Runs the transform `id` of `env` for dropping `payload` at `pos`. Nothing runs for an unknown or unavailable\n * transform. What the transform inserts (`BlockTransform.creates`) must pass the insert guards below `pos`'s parent and\n * the parent's content rules, else the warning „Hier lässt sich der Block nicht einfügen“ is shown and the document stays\n * unchanged. The transform's own failure is its own to report; this never throws for a refusal.\n */\nexport async function runTransform(env: TransferEnv, id: string, payload: BlockTransfer, pos: number): Promise<void> {\n const transform = env.transforms.find(candidate => candidate.id === id)\n if (!transform) return\n const ctx = transformContextOf(env, payload, pos)\n if (transform.available && !transform.available(ctx)) return\n const creates = transform.creates?.(ctx) ?? (transform.id === 'copy' ? createsOf(payload.json) : null)\n if (creates && !canInsertAt(env.editor, ctx.pos, creates)) {\n env.notify({ kind: 'warning', title: env.t('transfer.refused') })\n return\n }\n await transform.run(ctx)\n}\n\n/** Whether a node like `creates` fits the content rules below `pos`'s parent and passes every insert guard there. */\nfunction canInsertAt(editor: Editor, pos: number, creates: Creates): boolean {\n const type = editor.schema.nodes[creates.type]\n const $pos = editor.state.doc.resolve(pos)\n if (!type || !$pos.parent.canReplaceWith($pos.index(), $pos.index(), type)) return false\n return guardsAccept(editor, $pos, creates)\n}\n"],"mappings":"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"}
@@ -17,9 +17,9 @@ const de = {
17
17
  "ai.task.custom.label": "Eigene Anweisung",
18
18
  "ai.task.custom.promptLabel": "Anweisung an die KI, z. B. „als Aufzählung“",
19
19
  "ai.task.continue.label": "Weiterschreiben",
20
- "ai.task.fillBlock.label": "Baustein füllen",
20
+ "ai.task.fillBlock.label": "Block füllen",
21
21
  "ai.task.fillBlock.promptLabel": "Hinweis (optional)",
22
- "ai.task.splitBlock.label": "Baustein gliedern",
22
+ "ai.task.splitBlock.label": "Block gliedern",
23
23
  "ai.task.outlineSanitize.label": "Gliederung bereinigen",
24
24
  "ai.task.transferSummary.label": "Zusammenfassung (Übertragen)",
25
25
  "ai.task.transferBullets.label": "Stichpunkte (Übertragen)",
@@ -144,8 +144,8 @@ const de = {
144
144
  "ai.settings.autocomplete.description": "Grauer Text nach dem Cursor; Tab übernimmt, Esc verwirft.",
145
145
  "ai.settings.language.label": "Zielsprache beim Übersetzen",
146
146
  "ai.settings.language.description": "Vorausgewählt; im KI-Menü pro Aufruf änderbar.",
147
- "ai.settings.slots.title": "Modell je Aufgabe",
148
- "ai.settings.slots.description": "Welches Profil die einzelnen KI-Funktionen verwenden.",
147
+ "ai.settings.roles.title": "Modell je Aufgabe",
148
+ "ai.settings.roles.description": "Welches Profil die einzelnen KI-Funktionen verwenden.",
149
149
  "ai.settings.profiles.title": "Modellprofile",
150
150
  "ai.settings.profiles.description": "Server-Presets sind vorkonfiguriert. Eigene Profile werden nur in diesem Browser gespeichert; ihr API-Key wird mit jeder Anfrage gesendet und nicht auf dem Server abgelegt.",
151
151
  "ai.profiles.server": "Vom Server",
@@ -1 +1 @@
1
- {"version":3,"file":"ai.js","names":[],"sources":["../../../src/i18n/de/ai.ts"],"sourcesContent":["export const de = {\n // Tasks (menus, sidebar chips, prompt hints, result headings)\n 'ai.task.reformulate.label': 'Umformulieren',\n 'ai.task.reformulate.promptLabel': 'Hinweis (optional), z. B. „kürzer“ oder „förmlicher“',\n 'ai.task.generate.label': 'Text generieren',\n 'ai.task.generate.promptLabel': 'Was soll geschrieben werden?',\n 'ai.task.transcript.promptLabel': 'Hinweis (optional), z. B. „nur Beschlüsse“ oder „auf Englisch“',\n 'ai.task.transcriptSummary.heading': 'Zusammenfassung',\n 'ai.task.transcriptMinutes.heading': 'Protokoll',\n 'ai.task.transcriptActionItems.heading': 'Aufgaben',\n 'ai.task.fix.label': 'Korrigieren',\n 'ai.task.shorter.label': 'Kürzer',\n 'ai.task.formal.label': 'Förmlicher',\n 'ai.task.casual.label': 'Lockerer',\n 'ai.task.translate.label': 'Übersetzen',\n 'ai.task.summarize.label': 'Zusammenfassen',\n 'ai.task.custom.label': 'Eigene Anweisung',\n 'ai.task.custom.promptLabel': 'Anweisung an die KI, z. B. „als Aufzählung“',\n 'ai.task.continue.label': 'Weiterschreiben',\n 'ai.task.fillBlock.label': 'Baustein füllen',\n 'ai.task.fillBlock.promptLabel': 'Hinweis (optional)',\n 'ai.task.splitBlock.label': 'Baustein gliedern',\n 'ai.task.outlineSanitize.label': 'Gliederung bereinigen',\n 'ai.task.transferSummary.label': 'Zusammenfassung (Übertragen)',\n 'ai.task.transferBullets.label': 'Stichpunkte (Übertragen)',\n\n // Model slots and target languages\n 'ai.slot.complete.label': 'Vorschläge beim Tippen',\n 'ai.slot.complete.description': 'Schnelles Modell; muss Assistant-Prefill unterstützen.',\n 'ai.slot.rewrite.label': 'Text im Block',\n 'ai.slot.rewrite.description': 'Umformulieren, Korrigieren, Übersetzen, Zusammenfassen, Transkript bereinigen.',\n 'ai.slot.generate.label': 'Blöcke erzeugen & Chat',\n 'ai.slot.generate.description': 'Neue Blöcke, Protokolle, Chat-Agent (braucht Tool-Calling).',\n 'ai.slot.ariaLabel': 'Modell für „{slot}“',\n 'ai.slot.manage': 'Modelle verwalten…',\n 'ai.language.de': 'Deutsch',\n 'ai.language.en': 'Englisch',\n 'ai.language.fr': 'Französisch',\n 'ai.language.es': 'Spanisch',\n 'ai.language.it': 'Italienisch',\n\n // Feature entry points (toolbar, block action, slash command, settings tab)\n 'ai.tab': 'KI',\n 'ai.toolbar.sidebar': 'KI-Seitenleiste',\n 'ai.toolbar.autocomplete': 'KI-Vorschläge beim Tippen (Tab übernimmt, Esc verwirft)',\n 'ai.blockAction.label': 'Mit KI bearbeiten',\n 'ai.slash.label': 'KI',\n\n // Notices\n 'ai.rateLimited': 'Zu viele KI-Anfragen, bitte kurz warten',\n 'ai.sse.unknownError': 'Unbekannter Fehler',\n 'ai.sse.httpError': 'Fehler {status}',\n 'ai.sse.aborted': 'Verbindung abgebrochen',\n 'ai.chat.emptyScope': 'Die Auswahl enthält keine bearbeitbaren Blöcke.',\n 'ai.apply.missing.title': 'Textstelle nicht mehr gefunden',\n 'ai.apply.missing.description': 'Das Ergebnis wurde nicht übernommen.',\n 'ai.apply.empty.title': 'Leeres Ergebnis',\n 'ai.apply.empty.description': 'Das Ergebnis wurde nicht übernommen.',\n 'ai.apply.unchanged.title': 'Keine Änderungen',\n 'ai.apply.unchanged.description': 'Das Ergebnis entspricht dem vorhandenen Text.',\n 'ai.apply.overlap.title': 'Textstelle wird bereits geprüft',\n 'ai.apply.overlap.description': 'Erst die offenen KI-Änderungen übernehmen oder verwerfen.',\n\n // In-document review\n 'ai.review.accept': 'Übernehmen',\n 'ai.review.discard': 'Verwerfen',\n 'ai.review.acceptAll': 'Alle übernehmen',\n 'ai.review.rejectAll': 'Alle verwerfen',\n 'ai.review.barLabel': 'KI-Änderungen prüfen',\n 'ai.review.count.one': '1 KI-Änderung',\n 'ai.review.count.other': '{n} KI-Änderungen',\n 'ai.review.previous': 'Vorherige Änderung',\n 'ai.review.next': 'Nächste Änderung',\n 'ai.review.invalid.one': 'KI-Änderung verworfen',\n 'ai.review.invalid.other': '{n} KI-Änderungen verworfen',\n 'ai.review.invalid.description': 'Der ursprüngliche Text wurde inzwischen geändert.',\n\n // Selection / caret menu\n 'ai.menu.trigger': 'KI',\n 'ai.menu.model': 'Modell',\n 'ai.menu.targetLanguage': 'Zielsprache',\n 'ai.menu.sendInstruction': 'Anweisung senden',\n 'ai.menu.requestWithLanguage': '{task} – {language}',\n 'ai.menu.cancel': 'Abbrechen',\n 'ai.menu.discard': 'Verwerfen',\n 'ai.menu.retry': 'Erneut versuchen',\n 'ai.menu.insert': 'Einfügen',\n 'ai.menu.insertBelow': 'Darunter einfügen',\n 'ai.menu.replace': 'Ersetzen',\n 'ai.menu.missing.selection': 'Der markierte Text wurde gelöscht.',\n 'ai.menu.missing.line': 'Die Zeile wurde gelöscht.',\n 'ai.menu.missing.insert': 'Das Ergebnis wurde nicht eingefügt.',\n\n // Sidebar\n 'ai.sidebar.label': 'KI-Seitenleiste',\n 'ai.sidebar.title': 'KI',\n 'ai.sidebar.newConversation': 'Neues Gespräch',\n 'ai.sidebar.close': 'Schließen',\n 'ai.sidebar.selection.one': 'Auswahl: {n} Block',\n 'ai.sidebar.selection.other': 'Auswahl: {n} Blöcke',\n 'ai.sidebar.selection.title': 'Änderungen betreffen nur die markierten Blöcke',\n 'ai.sidebar.selection.clear': 'Auswahl aufheben',\n 'ai.sidebar.intro': 'Stelle eine Frage zum Inhalt, z. B. „Wer hat gesagt, dass das Budget nicht reicht?“, oder bitte um eine Änderung, z. B. „Kürze Abschnitt 2“. Mit einer Markierung im Text betreffen Änderungen nur die markierten Blöcke. Die Schnellaktionen unten erzeugen neue Blöcke, z. B. ein Protokoll.',\n 'ai.sidebar.stop': 'Stopp',\n 'ai.sidebar.discard': 'Verwerfen',\n 'ai.sidebar.apply': 'Übernehmen',\n 'ai.sidebar.generateGroup': 'Blöcke generieren',\n 'ai.sidebar.generate': 'Generieren',\n 'ai.sidebar.placeholder': 'Frage oder Änderungswunsch…',\n 'ai.sidebar.messageLabel': 'Nachricht zum Dokument',\n 'ai.sidebar.send': 'Senden',\n 'ai.sidebar.revealMissing': 'Textstelle nicht mehr im Dokument',\n\n // Chat edit operations\n 'ai.op.addAtStart': 'Einfügen am Dokumentanfang',\n 'ai.op.addAfter': 'Einfügen nach „{snippet}“',\n 'ai.op.convert': 'Umwandeln in {target}: „{snippet}“',\n 'ai.op.update': 'Ändern: „{snippet}“',\n 'ai.op.delete': 'Löschen: „{snippet}“',\n 'ai.op.accept': 'Übernehmen',\n 'ai.op.discard': 'Verwerfen',\n 'ai.op.problems.one': '1 Vorschlag verworfen',\n 'ai.op.problems.other': '{n} Vorschläge verworfen',\n 'ai.op.problems.description': 'Die KI hat auf Blöcke oder Blocktypen verwiesen, die es nicht gibt oder die außerhalb der Auswahl liegen.',\n 'ai.op.invalid.title': 'Änderung nicht mehr anwendbar',\n 'ai.op.invalid.description': 'Der Text wurde inzwischen anders geändert.',\n 'ai.op.snippet.documentStart': 'Dokumentanfang',\n 'ai.op.snippet.missing': 'Block nicht mehr vorhanden',\n 'ai.op.snippet.emptyBlock': 'Leerer Block',\n 'ai.op.snippet.image': 'Bild',\n 'ai.op.snippet.audio': 'Audio',\n 'ai.op.snippet.video': 'Video',\n 'ai.op.snippet.file': 'Datei',\n 'ai.opBadge.pending': 'Offen',\n 'ai.opBadge.accepted': 'Übernommen',\n 'ai.opBadge.rejected': 'Abgelehnt',\n 'ai.opBadge.partial': 'Teilweise',\n 'ai.opBadge.invalid': 'Verfallen',\n 'ai.opBadge.unchanged': 'Keine Änderung',\n 'ai.opBadge.missing': 'Block fehlt',\n 'ai.opBadge.stale': 'Block gelöscht',\n 'ai.opBadge.overlap': 'Stelle belegt',\n 'ai.opBadge.empty': 'Leer',\n 'ai.opBadge.blocked': 'Außerhalb der Auswahl',\n 'ai.opTitle.pending': 'Vorschlag noch nicht entschieden',\n 'ai.opTitle.accepted': 'Vorschlag übernommen',\n 'ai.opTitle.rejected': 'Vorschlag abgelehnt',\n 'ai.opTitle.partial': 'Vorschlag teilweise übernommen',\n 'ai.opTitle.invalid': 'Vorschlag verfallen, weil der Text inzwischen anders geändert wurde',\n 'ai.opTitle.unchanged': 'Vorschlag nicht angezeigt, weil er dem vorhandenen Text entspricht',\n 'ai.opTitle.missing': 'Vorschlag nicht angezeigt, weil der Block nicht mehr existiert',\n 'ai.opTitle.stale': 'Vorschlag nicht angezeigt: Block wurde inzwischen gelöscht',\n 'ai.opTitle.overlap': 'Vorschlag nicht angezeigt, weil an der Stelle noch andere KI-Änderungen offen sind',\n 'ai.opTitle.empty': 'Vorschlag nicht angezeigt, weil der Inhalt leer ist',\n 'ai.opTitle.blocked': 'Vorschlag blockiert, weil er außerhalb des erlaubten Bereichs liegt',\n\n // Settings tab\n 'ai.settings.writing.title': 'Beim Schreiben',\n 'ai.settings.autocomplete.label': 'KI-Vorschläge beim Tippen',\n 'ai.settings.autocomplete.description': 'Grauer Text nach dem Cursor; Tab übernimmt, Esc verwirft.',\n 'ai.settings.language.label': 'Zielsprache beim Übersetzen',\n 'ai.settings.language.description': 'Vorausgewählt; im KI-Menü pro Aufruf änderbar.',\n 'ai.settings.slots.title': 'Modell je Aufgabe',\n 'ai.settings.slots.description': 'Welches Profil die einzelnen KI-Funktionen verwenden.',\n 'ai.settings.profiles.title': 'Modellprofile',\n 'ai.settings.profiles.description': 'Server-Presets sind vorkonfiguriert. Eigene Profile werden nur in diesem Browser gespeichert; ihr API-Key wird mit jeder Anfrage gesendet und nicht auf dem Server abgelegt.',\n 'ai.profiles.server': 'Vom Server',\n 'ai.profiles.noPresets': 'Keine Presets geladen.',\n 'ai.profiles.serverBadge': 'Server',\n 'ai.profiles.own': 'Eigene Profile',\n 'ai.profiles.add': 'Profil hinzufügen',\n 'ai.profiles.edit': 'Bearbeiten',\n 'ai.profiles.delete': 'Löschen',\n 'ai.profiles.name': 'Name',\n 'ai.profiles.namePlaceholder': 'z. B. Lokal',\n 'ai.profiles.endpoint': 'Endpunkt',\n 'ai.profiles.endpointHelp': 'OpenAI-kompatible Basis-URL.',\n 'ai.profiles.endpointPlaceholder': 'https://openrouter.ai/api/v1',\n 'ai.profiles.model': 'Modell',\n 'ai.profiles.modelPlaceholder': 'google/gemini-2.5-flash',\n 'ai.profiles.apiKey': 'API-Key',\n 'ai.profiles.testOk': 'Verbindung funktioniert',\n 'ai.profiles.test': 'Testen',\n 'ai.profiles.cancel': 'Abbrechen',\n 'ai.profiles.save': 'Speichern',\n} as const\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai.js","names":[],"sources":["../../../src/i18n/de/ai.ts"],"sourcesContent":["export const de = {\n // Tasks (menus, sidebar chips, prompt hints, result headings)\n 'ai.task.reformulate.label': 'Umformulieren',\n 'ai.task.reformulate.promptLabel': 'Hinweis (optional), z. B. „kürzer“ oder „förmlicher“',\n 'ai.task.generate.label': 'Text generieren',\n 'ai.task.generate.promptLabel': 'Was soll geschrieben werden?',\n 'ai.task.transcript.promptLabel': 'Hinweis (optional), z. B. „nur Beschlüsse“ oder „auf Englisch“',\n 'ai.task.transcriptSummary.heading': 'Zusammenfassung',\n 'ai.task.transcriptMinutes.heading': 'Protokoll',\n 'ai.task.transcriptActionItems.heading': 'Aufgaben',\n 'ai.task.fix.label': 'Korrigieren',\n 'ai.task.shorter.label': 'Kürzer',\n 'ai.task.formal.label': 'Förmlicher',\n 'ai.task.casual.label': 'Lockerer',\n 'ai.task.translate.label': 'Übersetzen',\n 'ai.task.summarize.label': 'Zusammenfassen',\n 'ai.task.custom.label': 'Eigene Anweisung',\n 'ai.task.custom.promptLabel': 'Anweisung an die KI, z. B. „als Aufzählung“',\n 'ai.task.continue.label': 'Weiterschreiben',\n 'ai.task.fillBlock.label': 'Block füllen',\n 'ai.task.fillBlock.promptLabel': 'Hinweis (optional)',\n 'ai.task.splitBlock.label': 'Block gliedern',\n 'ai.task.outlineSanitize.label': 'Gliederung bereinigen',\n 'ai.task.transferSummary.label': 'Zusammenfassung (Übertragen)',\n 'ai.task.transferBullets.label': 'Stichpunkte (Übertragen)',\n\n // Model slots and target languages\n 'ai.slot.complete.label': 'Vorschläge beim Tippen',\n 'ai.slot.complete.description': 'Schnelles Modell; muss Assistant-Prefill unterstützen.',\n 'ai.slot.rewrite.label': 'Text im Block',\n 'ai.slot.rewrite.description': 'Umformulieren, Korrigieren, Übersetzen, Zusammenfassen, Transkript bereinigen.',\n 'ai.slot.generate.label': 'Blöcke erzeugen & Chat',\n 'ai.slot.generate.description': 'Neue Blöcke, Protokolle, Chat-Agent (braucht Tool-Calling).',\n 'ai.slot.ariaLabel': 'Modell für „{slot}“',\n 'ai.slot.manage': 'Modelle verwalten…',\n 'ai.language.de': 'Deutsch',\n 'ai.language.en': 'Englisch',\n 'ai.language.fr': 'Französisch',\n 'ai.language.es': 'Spanisch',\n 'ai.language.it': 'Italienisch',\n\n // Feature entry points (toolbar, block action, slash command, settings tab)\n 'ai.tab': 'KI',\n 'ai.toolbar.sidebar': 'KI-Seitenleiste',\n 'ai.toolbar.autocomplete': 'KI-Vorschläge beim Tippen (Tab übernimmt, Esc verwirft)',\n 'ai.blockAction.label': 'Mit KI bearbeiten',\n 'ai.slash.label': 'KI',\n\n // Notices\n 'ai.rateLimited': 'Zu viele KI-Anfragen, bitte kurz warten',\n 'ai.sse.unknownError': 'Unbekannter Fehler',\n 'ai.sse.httpError': 'Fehler {status}',\n 'ai.sse.aborted': 'Verbindung abgebrochen',\n 'ai.chat.emptyScope': 'Die Auswahl enthält keine bearbeitbaren Blöcke.',\n 'ai.apply.missing.title': 'Textstelle nicht mehr gefunden',\n 'ai.apply.missing.description': 'Das Ergebnis wurde nicht übernommen.',\n 'ai.apply.empty.title': 'Leeres Ergebnis',\n 'ai.apply.empty.description': 'Das Ergebnis wurde nicht übernommen.',\n 'ai.apply.unchanged.title': 'Keine Änderungen',\n 'ai.apply.unchanged.description': 'Das Ergebnis entspricht dem vorhandenen Text.',\n 'ai.apply.overlap.title': 'Textstelle wird bereits geprüft',\n 'ai.apply.overlap.description': 'Erst die offenen KI-Änderungen übernehmen oder verwerfen.',\n\n // In-document review\n 'ai.review.accept': 'Übernehmen',\n 'ai.review.discard': 'Verwerfen',\n 'ai.review.acceptAll': 'Alle übernehmen',\n 'ai.review.rejectAll': 'Alle verwerfen',\n 'ai.review.barLabel': 'KI-Änderungen prüfen',\n 'ai.review.count.one': '1 KI-Änderung',\n 'ai.review.count.other': '{n} KI-Änderungen',\n 'ai.review.previous': 'Vorherige Änderung',\n 'ai.review.next': 'Nächste Änderung',\n 'ai.review.invalid.one': 'KI-Änderung verworfen',\n 'ai.review.invalid.other': '{n} KI-Änderungen verworfen',\n 'ai.review.invalid.description': 'Der ursprüngliche Text wurde inzwischen geändert.',\n\n // Selection / caret menu\n 'ai.menu.trigger': 'KI',\n 'ai.menu.model': 'Modell',\n 'ai.menu.targetLanguage': 'Zielsprache',\n 'ai.menu.sendInstruction': 'Anweisung senden',\n 'ai.menu.requestWithLanguage': '{task} – {language}',\n 'ai.menu.cancel': 'Abbrechen',\n 'ai.menu.discard': 'Verwerfen',\n 'ai.menu.retry': 'Erneut versuchen',\n 'ai.menu.insert': 'Einfügen',\n 'ai.menu.insertBelow': 'Darunter einfügen',\n 'ai.menu.replace': 'Ersetzen',\n 'ai.menu.missing.selection': 'Der markierte Text wurde gelöscht.',\n 'ai.menu.missing.line': 'Die Zeile wurde gelöscht.',\n 'ai.menu.missing.insert': 'Das Ergebnis wurde nicht eingefügt.',\n\n // Sidebar\n 'ai.sidebar.label': 'KI-Seitenleiste',\n 'ai.sidebar.title': 'KI',\n 'ai.sidebar.newConversation': 'Neues Gespräch',\n 'ai.sidebar.close': 'Schließen',\n 'ai.sidebar.selection.one': 'Auswahl: {n} Block',\n 'ai.sidebar.selection.other': 'Auswahl: {n} Blöcke',\n 'ai.sidebar.selection.title': 'Änderungen betreffen nur die markierten Blöcke',\n 'ai.sidebar.selection.clear': 'Auswahl aufheben',\n 'ai.sidebar.intro': 'Stelle eine Frage zum Inhalt, z. B. „Wer hat gesagt, dass das Budget nicht reicht?“, oder bitte um eine Änderung, z. B. „Kürze Abschnitt 2“. Mit einer Markierung im Text betreffen Änderungen nur die markierten Blöcke. Die Schnellaktionen unten erzeugen neue Blöcke, z. B. ein Protokoll.',\n 'ai.sidebar.stop': 'Stopp',\n 'ai.sidebar.discard': 'Verwerfen',\n 'ai.sidebar.apply': 'Übernehmen',\n 'ai.sidebar.generateGroup': 'Blöcke generieren',\n 'ai.sidebar.generate': 'Generieren',\n 'ai.sidebar.placeholder': 'Frage oder Änderungswunsch…',\n 'ai.sidebar.messageLabel': 'Nachricht zum Dokument',\n 'ai.sidebar.send': 'Senden',\n 'ai.sidebar.revealMissing': 'Textstelle nicht mehr im Dokument',\n\n // Chat edit operations\n 'ai.op.addAtStart': 'Einfügen am Dokumentanfang',\n 'ai.op.addAfter': 'Einfügen nach „{snippet}“',\n 'ai.op.convert': 'Umwandeln in {target}: „{snippet}“',\n 'ai.op.update': 'Ändern: „{snippet}“',\n 'ai.op.delete': 'Löschen: „{snippet}“',\n 'ai.op.accept': 'Übernehmen',\n 'ai.op.discard': 'Verwerfen',\n 'ai.op.problems.one': '1 Vorschlag verworfen',\n 'ai.op.problems.other': '{n} Vorschläge verworfen',\n 'ai.op.problems.description': 'Die KI hat auf Blöcke oder Blocktypen verwiesen, die es nicht gibt oder die außerhalb der Auswahl liegen.',\n 'ai.op.invalid.title': 'Änderung nicht mehr anwendbar',\n 'ai.op.invalid.description': 'Der Text wurde inzwischen anders geändert.',\n 'ai.op.snippet.documentStart': 'Dokumentanfang',\n 'ai.op.snippet.missing': 'Block nicht mehr vorhanden',\n 'ai.op.snippet.emptyBlock': 'Leerer Block',\n 'ai.op.snippet.image': 'Bild',\n 'ai.op.snippet.audio': 'Audio',\n 'ai.op.snippet.video': 'Video',\n 'ai.op.snippet.file': 'Datei',\n 'ai.opBadge.pending': 'Offen',\n 'ai.opBadge.accepted': 'Übernommen',\n 'ai.opBadge.rejected': 'Abgelehnt',\n 'ai.opBadge.partial': 'Teilweise',\n 'ai.opBadge.invalid': 'Verfallen',\n 'ai.opBadge.unchanged': 'Keine Änderung',\n 'ai.opBadge.missing': 'Block fehlt',\n 'ai.opBadge.stale': 'Block gelöscht',\n 'ai.opBadge.overlap': 'Stelle belegt',\n 'ai.opBadge.empty': 'Leer',\n 'ai.opBadge.blocked': 'Außerhalb der Auswahl',\n 'ai.opTitle.pending': 'Vorschlag noch nicht entschieden',\n 'ai.opTitle.accepted': 'Vorschlag übernommen',\n 'ai.opTitle.rejected': 'Vorschlag abgelehnt',\n 'ai.opTitle.partial': 'Vorschlag teilweise übernommen',\n 'ai.opTitle.invalid': 'Vorschlag verfallen, weil der Text inzwischen anders geändert wurde',\n 'ai.opTitle.unchanged': 'Vorschlag nicht angezeigt, weil er dem vorhandenen Text entspricht',\n 'ai.opTitle.missing': 'Vorschlag nicht angezeigt, weil der Block nicht mehr existiert',\n 'ai.opTitle.stale': 'Vorschlag nicht angezeigt: Block wurde inzwischen gelöscht',\n 'ai.opTitle.overlap': 'Vorschlag nicht angezeigt, weil an der Stelle noch andere KI-Änderungen offen sind',\n 'ai.opTitle.empty': 'Vorschlag nicht angezeigt, weil der Inhalt leer ist',\n 'ai.opTitle.blocked': 'Vorschlag blockiert, weil er außerhalb des erlaubten Bereichs liegt',\n\n // Settings tab\n 'ai.settings.writing.title': 'Beim Schreiben',\n 'ai.settings.autocomplete.label': 'KI-Vorschläge beim Tippen',\n 'ai.settings.autocomplete.description': 'Grauer Text nach dem Cursor; Tab übernimmt, Esc verwirft.',\n 'ai.settings.language.label': 'Zielsprache beim Übersetzen',\n 'ai.settings.language.description': 'Vorausgewählt; im KI-Menü pro Aufruf änderbar.',\n 'ai.settings.roles.title': 'Modell je Aufgabe',\n 'ai.settings.roles.description': 'Welches Profil die einzelnen KI-Funktionen verwenden.',\n 'ai.settings.profiles.title': 'Modellprofile',\n 'ai.settings.profiles.description': 'Server-Presets sind vorkonfiguriert. Eigene Profile werden nur in diesem Browser gespeichert; ihr API-Key wird mit jeder Anfrage gesendet und nicht auf dem Server abgelegt.',\n 'ai.profiles.server': 'Vom Server',\n 'ai.profiles.noPresets': 'Keine Presets geladen.',\n 'ai.profiles.serverBadge': 'Server',\n 'ai.profiles.own': 'Eigene Profile',\n 'ai.profiles.add': 'Profil hinzufügen',\n 'ai.profiles.edit': 'Bearbeiten',\n 'ai.profiles.delete': 'Löschen',\n 'ai.profiles.name': 'Name',\n 'ai.profiles.namePlaceholder': 'z. B. Lokal',\n 'ai.profiles.endpoint': 'Endpunkt',\n 'ai.profiles.endpointHelp': 'OpenAI-kompatible Basis-URL.',\n 'ai.profiles.endpointPlaceholder': 'https://openrouter.ai/api/v1',\n 'ai.profiles.model': 'Modell',\n 'ai.profiles.modelPlaceholder': 'google/gemini-2.5-flash',\n 'ai.profiles.apiKey': 'API-Key',\n 'ai.profiles.testOk': 'Verbindung funktioniert',\n 'ai.profiles.test': 'Testen',\n 'ai.profiles.cancel': 'Abbrechen',\n 'ai.profiles.save': 'Speichern',\n} as const\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ const de = {
4
4
  "authors.title": "Autorenfarben",
5
5
  "authors.switch.label": "Text nach Autor einfärben",
6
6
  "authors.switch.description": "Zeigt, wer welchen Text geschrieben hat; die Farbe ist die Benutzerfarbe (wie bei Cursorn und Erwähnungen).",
7
- "authors.style.legend": "Darstellung der Autorenfarben",
7
+ "authors.style.legend": "Anzeige der Autorenfarben",
8
8
  "authors.style.background.label": "Hintergrund",
9
9
  "authors.style.background.description": "Text behält seine Farbe, farbig hinterlegt.",
10
10
  "authors.style.text.label": "Textfarbe",
@@ -1 +1 @@
1
- {"version":3,"file":"authors.js","names":[],"sources":["../../../src/i18n/de/authors.ts"],"sourcesContent":["export const de = {\n 'authors.tab': 'Autorenfarben',\n 'authors.title': 'Autorenfarben',\n 'authors.switch.label': 'Text nach Autor einfärben',\n 'authors.switch.description': 'Zeigt, wer welchen Text geschrieben hat; die Farbe ist die Benutzerfarbe (wie bei Cursorn und Erwähnungen).',\n 'authors.style.legend': 'Darstellung der Autorenfarben',\n 'authors.style.background.label': 'Hintergrund',\n 'authors.style.background.description': 'Text behält seine Farbe, farbig hinterlegt.',\n 'authors.style.text.label': 'Textfarbe',\n 'authors.style.text.description': 'Der Text selbst in der Autorenfarbe.',\n 'authors.panel.heading': 'Autoren in diesem Dokument',\n 'authors.panel.empty': 'Noch kein Text.',\n 'authors.panel.you': '{name} (du)',\n 'authors.panel.noAuthor': 'Ohne Autor',\n 'authors.panel.characters': '{n} Zeichen',\n} as const\n"],"mappings":";AAAA,MAAa,KAAK;CAChB,eAAe;CACf,iBAAiB;CACjB,wBAAwB;CACxB,8BAA8B;CAC9B,wBAAwB;CACxB,kCAAkC;CAClC,wCAAwC;CACxC,4BAA4B;CAC5B,kCAAkC;CAClC,yBAAyB;CACzB,uBAAuB;CACvB,qBAAqB;CACrB,0BAA0B;CAC1B,4BAA4B;AAC9B"}
1
+ {"version":3,"file":"authors.js","names":[],"sources":["../../../src/i18n/de/authors.ts"],"sourcesContent":["export const de = {\n 'authors.tab': 'Autorenfarben',\n 'authors.title': 'Autorenfarben',\n 'authors.switch.label': 'Text nach Autor einfärben',\n 'authors.switch.description': 'Zeigt, wer welchen Text geschrieben hat; die Farbe ist die Benutzerfarbe (wie bei Cursorn und Erwähnungen).',\n 'authors.style.legend': 'Anzeige der Autorenfarben',\n 'authors.style.background.label': 'Hintergrund',\n 'authors.style.background.description': 'Text behält seine Farbe, farbig hinterlegt.',\n 'authors.style.text.label': 'Textfarbe',\n 'authors.style.text.description': 'Der Text selbst in der Autorenfarbe.',\n 'authors.panel.heading': 'Autoren in diesem Dokument',\n 'authors.panel.empty': 'Noch kein Text.',\n 'authors.panel.you': '{name} (du)',\n 'authors.panel.noAuthor': 'Ohne Autor',\n 'authors.panel.characters': '{n} Zeichen',\n} as const\n"],"mappings":";AAAA,MAAa,KAAK;CAChB,eAAe;CACf,iBAAiB;CACjB,wBAAwB;CACxB,8BAA8B;CAC9B,wBAAwB;CACxB,kCAAkC;CAClC,wCAAwC;CACxC,4BAA4B;CAC5B,kCAAkC;CAClC,yBAAyB;CACzB,uBAAuB;CACvB,qBAAqB;CACrB,0BAA0B;CAC1B,4BAA4B;AAC9B"}
@@ -4,7 +4,7 @@ const de = {
4
4
  "comments.tab": "Kommentare",
5
5
  "comments.unavailable": "Kommentare gibt es nur in gemeinsam bearbeiteten Dokumenten.",
6
6
  "comments.empty": "Noch keine Kommentare.",
7
- "comments.layout.legend": "Darstellung der Kommentare",
7
+ "comments.layout.legend": "Anzeige der Kommentare",
8
8
  "comments.layout.margin.label": "Randspalte",
9
9
  "comments.layout.margin.description": "Karten neben dem Text, auf Höhe der kommentierten Stelle.",
10
10
  "comments.layout.inline.label": "Inline",
@@ -1 +1 @@
1
- {"version":3,"file":"comments.js","names":[],"sources":["../../../src/i18n/de/comments.ts"],"sourcesContent":["export const de = {\n 'comments.title': 'Kommentare',\n 'comments.tab': 'Kommentare',\n 'comments.unavailable': 'Kommentare gibt es nur in gemeinsam bearbeiteten Dokumenten.',\n 'comments.empty': 'Noch keine Kommentare.',\n 'comments.layout.legend': 'Darstellung der Kommentare',\n 'comments.layout.margin.label': 'Randspalte',\n 'comments.layout.margin.description': 'Karten neben dem Text, auf Höhe der kommentierten Stelle.',\n 'comments.layout.inline.label': 'Inline',\n 'comments.layout.inline.description': 'Kommentar öffnet sich direkt am Text; Liste aller Kommentare in der Werkzeugleiste.',\n 'comments.resolved': 'Erledigt',\n 'comments.resolvedCount': 'Erledigt ({n})',\n 'comments.reopen': 'Wieder öffnen',\n 'comments.repliesShort': '{n} Antw.',\n 'comments.orphan': 'Textstelle gelöscht',\n 'comments.bubble': 'Kommentar',\n 'comments.composer.placeholder': 'Kommentar…',\n 'comments.composer.label': 'Kommentar',\n 'comments.composer.submit': 'Kommentieren',\n 'comments.cancel': 'Abbrechen',\n 'comments.save': 'Speichern',\n 'comments.edit': 'Bearbeiten',\n 'comments.editLabel': 'Kommentar bearbeiten',\n 'comments.edited': '(bearbeitet)',\n 'comments.delete': 'Löschen',\n 'comments.actions': 'Aktionen',\n 'comments.reply.placeholder': 'Antworten…',\n 'comments.reply.label': 'Antwort',\n 'comments.reply.submit': 'Antworten',\n} as const\n"],"mappings":";AAAA,MAAa,KAAK;CAChB,kBAAkB;CAClB,gBAAgB;CAChB,wBAAwB;CACxB,kBAAkB;CAClB,0BAA0B;CAC1B,gCAAgC;CAChC,sCAAsC;CACtC,gCAAgC;CAChC,sCAAsC;CACtC,qBAAqB;CACrB,0BAA0B;CAC1B,mBAAmB;CACnB,yBAAyB;CACzB,mBAAmB;CACnB,mBAAmB;CACnB,iCAAiC;CACjC,2BAA2B;CAC3B,4BAA4B;CAC5B,mBAAmB;CACnB,iBAAiB;CACjB,iBAAiB;CACjB,sBAAsB;CACtB,mBAAmB;CACnB,mBAAmB;CACnB,oBAAoB;CACpB,8BAA8B;CAC9B,wBAAwB;CACxB,yBAAyB;AAC3B"}
1
+ {"version":3,"file":"comments.js","names":[],"sources":["../../../src/i18n/de/comments.ts"],"sourcesContent":["export const de = {\n 'comments.title': 'Kommentare',\n 'comments.tab': 'Kommentare',\n 'comments.unavailable': 'Kommentare gibt es nur in gemeinsam bearbeiteten Dokumenten.',\n 'comments.empty': 'Noch keine Kommentare.',\n 'comments.layout.legend': 'Anzeige der Kommentare',\n 'comments.layout.margin.label': 'Randspalte',\n 'comments.layout.margin.description': 'Karten neben dem Text, auf Höhe der kommentierten Stelle.',\n 'comments.layout.inline.label': 'Inline',\n 'comments.layout.inline.description': 'Kommentar öffnet sich direkt am Text; Liste aller Kommentare in der Werkzeugleiste.',\n 'comments.resolved': 'Erledigt',\n 'comments.resolvedCount': 'Erledigt ({n})',\n 'comments.reopen': 'Wieder öffnen',\n 'comments.repliesShort': '{n} Antw.',\n 'comments.orphan': 'Textstelle gelöscht',\n 'comments.bubble': 'Kommentar',\n 'comments.composer.placeholder': 'Kommentar…',\n 'comments.composer.label': 'Kommentar',\n 'comments.composer.submit': 'Kommentieren',\n 'comments.cancel': 'Abbrechen',\n 'comments.save': 'Speichern',\n 'comments.edit': 'Bearbeiten',\n 'comments.editLabel': 'Kommentar bearbeiten',\n 'comments.edited': '(bearbeitet)',\n 'comments.delete': 'Löschen',\n 'comments.actions': 'Aktionen',\n 'comments.reply.placeholder': 'Antworten…',\n 'comments.reply.label': 'Antwort',\n 'comments.reply.submit': 'Antworten',\n} as const\n"],"mappings":";AAAA,MAAa,KAAK;CAChB,kBAAkB;CAClB,gBAAgB;CAChB,wBAAwB;CACxB,kBAAkB;CAClB,0BAA0B;CAC1B,gCAAgC;CAChC,sCAAsC;CACtC,gCAAgC;CAChC,sCAAsC;CACtC,qBAAqB;CACrB,0BAA0B;CAC1B,mBAAmB;CACnB,yBAAyB;CACzB,mBAAmB;CACnB,mBAAmB;CACnB,iCAAiC;CACjC,2BAA2B;CAC3B,4BAA4B;CAC5B,mBAAmB;CACnB,iBAAiB;CACjB,iBAAiB;CACjB,sBAAsB;CACtB,mBAAmB;CACnB,mBAAmB;CACnB,oBAAoB;CACpB,8BAA8B;CAC9B,wBAAwB;CACxB,yBAAyB;AAC3B"}