@ecsplico/editor 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1286) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -2
  3. package/dist/code/CodeSurface.d.ts +24 -0
  4. package/dist/code/CodeSurface.js +57 -0
  5. package/dist/code/CodeSurface.js.map +1 -0
  6. package/dist/code/CodeSurface.vue.d.ts +29 -0
  7. package/dist/code/CodeSurface2.js +75 -0
  8. package/dist/code/CodeSurface2.js.map +1 -0
  9. package/dist/code/chrome-theme.js +76 -0
  10. package/dist/code/chrome-theme.js.map +1 -0
  11. package/dist/code/compartments.js +19 -0
  12. package/dist/code/compartments.js.map +1 -0
  13. package/dist/code/editor-factory.d.ts +41 -0
  14. package/dist/code/editor-factory.js +126 -0
  15. package/dist/code/editor-factory.js.map +1 -0
  16. package/dist/code/format-json.d.ts +27 -0
  17. package/dist/code/format-json.js +94 -0
  18. package/dist/code/format-json.js.map +1 -0
  19. package/dist/code/highlight-style.js +118 -0
  20. package/dist/code/highlight-style.js.map +1 -0
  21. package/dist/code/index.d.ts +6 -0
  22. package/dist/code/index.js +6 -0
  23. package/dist/code/jsonl-language.d.ts +6 -0
  24. package/dist/code/jsonl-language.js +33 -0
  25. package/dist/code/jsonl-language.js.map +1 -0
  26. package/dist/code/kind.d.ts +15 -0
  27. package/dist/code/kind.js +142 -0
  28. package/dist/code/kind.js.map +1 -0
  29. package/dist/code/lang-registry.d.ts +31 -0
  30. package/dist/code/lang-registry.js +54 -0
  31. package/dist/code/lang-registry.js.map +1 -0
  32. package/dist/code/react.d.ts +2 -0
  33. package/dist/code/react.js +1 -0
  34. package/dist/code/vue.d.ts +2 -0
  35. package/dist/code/vue.js +1 -0
  36. package/dist/core/abilities.d.ts +32 -0
  37. package/dist/core/abilities.js +160 -0
  38. package/dist/core/abilities.js.map +1 -0
  39. package/dist/core/block-convert.d.ts +59 -0
  40. package/dist/core/block-convert.js +164 -0
  41. package/dist/core/block-convert.js.map +1 -0
  42. package/dist/core/block-drag.d.ts +12 -0
  43. package/dist/core/block-drag.js +62 -0
  44. package/dist/core/block-drag.js.map +1 -0
  45. package/dist/core/block-flash.d.ts +19 -0
  46. package/dist/core/block-flash.js +66 -0
  47. package/dist/core/block-flash.js.map +1 -0
  48. package/dist/core/context.d.ts +63 -0
  49. package/dist/core/context.js +108 -0
  50. package/dist/core/context.js.map +1 -0
  51. package/dist/core/convert.d.ts +96 -0
  52. package/dist/core/convert.js +241 -0
  53. package/dist/core/convert.js.map +1 -0
  54. package/dist/core/core-feature.d.ts +17 -0
  55. package/dist/core/core-feature.js +100 -0
  56. package/dist/core/core-feature.js.map +1 -0
  57. package/dist/core/define-editor.d.ts +23 -0
  58. package/dist/core/define-editor.js +110 -0
  59. package/dist/core/define-editor.js.map +1 -0
  60. package/dist/core/doc-anchors.d.ts +67 -0
  61. package/dist/core/doc-anchors.js +112 -0
  62. package/dist/core/doc-anchors.js.map +1 -0
  63. package/dist/core/document-markdown.d.ts +13 -0
  64. package/dist/core/document-markdown.js +31 -0
  65. package/dist/core/document-markdown.js.map +1 -0
  66. package/dist/core/download.d.ts +5 -0
  67. package/dist/core/download.js +16 -0
  68. package/dist/core/download.js.map +1 -0
  69. package/dist/core/editor-ui.d.ts +45 -0
  70. package/dist/core/editor-ui.js +38 -0
  71. package/dist/core/editor-ui.js.map +1 -0
  72. package/dist/core/extension-points.d.ts +64 -0
  73. package/dist/core/feature-context.d.ts +32 -0
  74. package/dist/core/feature-context.js +46 -0
  75. package/dist/core/feature-context.js.map +1 -0
  76. package/dist/core/feature.d.ts +234 -0
  77. package/dist/core/feature.js +13 -0
  78. package/dist/core/feature.js.map +1 -0
  79. package/dist/core/fences.d.ts +16 -0
  80. package/dist/core/fences.js +105 -0
  81. package/dist/core/fences.js.map +1 -0
  82. package/dist/core/fetch-error.d.ts +5 -0
  83. package/dist/core/fetch-error.js +11 -0
  84. package/dist/core/fetch-error.js.map +1 -0
  85. package/dist/core/index.d.ts +28 -0
  86. package/dist/core/index.js +27 -0
  87. package/dist/core/metadata-types.d.ts +27 -0
  88. package/dist/core/metadata-types.js +10 -0
  89. package/dist/core/metadata-types.js.map +1 -0
  90. package/dist/core/metadata.d.ts +39 -0
  91. package/dist/core/metadata.js +76 -0
  92. package/dist/core/metadata.js.map +1 -0
  93. package/dist/core/notify.d.ts +31 -0
  94. package/dist/core/notify.js +7 -0
  95. package/dist/core/notify.js.map +1 -0
  96. package/dist/core/pipeline.d.ts +52 -0
  97. package/dist/core/pipeline.js +78 -0
  98. package/dist/core/pipeline.js.map +1 -0
  99. package/dist/core/schema/abilities.d.ts +55 -0
  100. package/dist/core/schema/abilities.js +113 -0
  101. package/dist/core/schema/abilities.js.map +1 -0
  102. package/dist/core/schema/block-ids.d.ts +29 -0
  103. package/dist/core/schema/block-ids.js +134 -0
  104. package/dist/core/schema/block-ids.js.map +1 -0
  105. package/dist/core/schema/hex-color.d.ts +5 -0
  106. package/dist/core/schema/hex-color.js +7 -0
  107. package/dist/core/schema/hex-color.js.map +1 -0
  108. package/dist/core/schema/index.d.ts +21 -0
  109. package/dist/core/schema/index.js +33 -0
  110. package/dist/core/schema/index.js.map +1 -0
  111. package/dist/core/schema/mark-flags.d.ts +12 -0
  112. package/dist/core/schema/node-flags.d.ts +23 -0
  113. package/dist/core/schema/node-flags.js +34 -0
  114. package/dist/core/schema/node-flags.js.map +1 -0
  115. package/dist/core/schema/opaque-fence.d.ts +11 -0
  116. package/dist/core/schema/opaque-fence.js +57 -0
  117. package/dist/core/schema/opaque-fence.js.map +1 -0
  118. package/dist/core/schema/strike.d.ts +4 -0
  119. package/dist/core/schema/strike.js +9 -0
  120. package/dist/core/schema/strike.js.map +1 -0
  121. package/dist/core/selection-range.d.ts +13 -0
  122. package/dist/core/selection-range.js +28 -0
  123. package/dist/core/selection-range.js.map +1 -0
  124. package/dist/core/slash.d.ts +24 -0
  125. package/dist/core/slash.js +60 -0
  126. package/dist/core/slash.js.map +1 -0
  127. package/dist/core/store.d.ts +22 -0
  128. package/dist/core/store.js +61 -0
  129. package/dist/core/store.js.map +1 -0
  130. package/dist/core/suggestion-popup.d.ts +63 -0
  131. package/dist/core/suggestion-popup.js +57 -0
  132. package/dist/core/suggestion-popup.js.map +1 -0
  133. package/dist/core/tag-name.js +11 -0
  134. package/dist/core/tag-name.js.map +1 -0
  135. package/dist/core/toolbar-items.d.ts +10 -0
  136. package/dist/core/toolbar-items.js +206 -0
  137. package/dist/core/toolbar-items.js.map +1 -0
  138. package/dist/core/toolbar.d.ts +74 -0
  139. package/dist/core/toolbar.js +45 -0
  140. package/dist/core/toolbar.js.map +1 -0
  141. package/dist/document/DocumentSurface.d.ts +38 -0
  142. package/dist/document/DocumentSurface.js +44 -0
  143. package/dist/document/DocumentSurface.js.map +1 -0
  144. package/dist/document/DocumentSurface.vue.d.ts +56 -0
  145. package/dist/document/DocumentSurface2.js +71 -0
  146. package/dist/document/DocumentSurface2.js.map +1 -0
  147. package/dist/document/react.d.ts +2 -0
  148. package/dist/document/react.js +1 -0
  149. package/dist/document/vue.d.ts +2 -0
  150. package/dist/document/vue.js +1 -0
  151. package/dist/features/ai/agent-op-review.d.ts +69 -0
  152. package/dist/features/ai/agent-op-review.js +182 -0
  153. package/dist/features/ai/agent-op-review.js.map +1 -0
  154. package/dist/features/ai/agent-ops.d.ts +15 -0
  155. package/dist/features/ai/agent-ops.js +228 -0
  156. package/dist/features/ai/agent-ops.js.map +1 -0
  157. package/dist/features/ai/ai-apply.d.ts +76 -0
  158. package/dist/features/ai/ai-apply.js +193 -0
  159. package/dist/features/ai/ai-apply.js.map +1 -0
  160. package/dist/features/ai/ai-menu.d.ts +38 -0
  161. package/dist/features/ai/ai-menu.js +20 -0
  162. package/dist/features/ai/ai-menu.js.map +1 -0
  163. package/dist/features/ai/ai-review.d.ts +136 -0
  164. package/dist/features/ai/ai-review.js +543 -0
  165. package/dist/features/ai/ai-review.js.map +1 -0
  166. package/dist/features/ai/ai-selection-highlight.d.ts +21 -0
  167. package/dist/features/ai/ai-selection-highlight.js +44 -0
  168. package/dist/features/ai/ai-selection-highlight.js.map +1 -0
  169. package/dist/features/ai/ai-sse.d.ts +28 -0
  170. package/dist/features/ai/ai-sse.js +82 -0
  171. package/dist/features/ai/ai-sse.js.map +1 -0
  172. package/dist/features/ai/assistant.d.ts +53 -0
  173. package/dist/features/ai/assistant.js +82 -0
  174. package/dist/features/ai/assistant.js.map +1 -0
  175. package/dist/features/ai/block-actions.d.ts +20 -0
  176. package/dist/features/ai/block-actions.js +55 -0
  177. package/dist/features/ai/block-actions.js.map +1 -0
  178. package/dist/features/ai/components/AiReviewBar.js +145 -0
  179. package/dist/features/ai/components/AiReviewBar.js.map +1 -0
  180. package/dist/features/ai/components/AiReviewBar.vue.d.ts +15 -0
  181. package/dist/features/ai/components/AiSelectionMenu.js +519 -0
  182. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -0
  183. package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +17 -0
  184. package/dist/features/ai/components/AiSidebar.js +378 -0
  185. package/dist/features/ai/components/AiSidebar.js.map +1 -0
  186. package/dist/features/ai/components/AiSidebar.vue.d.ts +26 -0
  187. package/dist/features/ai/components/DocumentChatAnswer.js +67 -0
  188. package/dist/features/ai/components/DocumentChatAnswer.js.map +1 -0
  189. package/dist/features/ai/components/DocumentChatAnswer.vue.d.ts +13 -0
  190. package/dist/features/ai/components/DocumentChatOps.js +113 -0
  191. package/dist/features/ai/components/DocumentChatOps.js.map +1 -0
  192. package/dist/features/ai/components/DocumentChatOps.vue.d.ts +27 -0
  193. package/dist/features/ai/components/ModelQuickSwitch.js +84 -0
  194. package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -0
  195. package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +23 -0
  196. package/dist/features/ai/components/SettingsAiTab.js +88 -0
  197. package/dist/features/ai/components/SettingsAiTab.js.map +1 -0
  198. package/dist/features/ai/components/SettingsAiTab.vue.d.ts +6 -0
  199. package/dist/features/ai/components/SettingsModelProfiles.js +260 -0
  200. package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -0
  201. package/dist/features/ai/components/SettingsModelProfiles.vue.d.ts +6 -0
  202. package/dist/features/ai/document-chat-model.d.ts +88 -0
  203. package/dist/features/ai/document-chat-model.js +190 -0
  204. package/dist/features/ai/document-chat-model.js.map +1 -0
  205. package/dist/features/ai/document-chat.d.ts +17 -0
  206. package/dist/features/ai/document-chat.js +112 -0
  207. package/dist/features/ai/document-chat.js.map +1 -0
  208. package/dist/features/ai/feature.d.ts +25 -0
  209. package/dist/features/ai/feature.js +89 -0
  210. package/dist/features/ai/feature.js.map +1 -0
  211. package/dist/features/ai/ghost-autocomplete.d.ts +47 -0
  212. package/dist/features/ai/ghost-autocomplete.js +204 -0
  213. package/dist/features/ai/ghost-autocomplete.js.map +1 -0
  214. package/dist/features/ai/index.d.ts +24 -0
  215. package/dist/features/ai/index.js +23 -0
  216. package/dist/features/ai/model-profiles.d.ts +36 -0
  217. package/dist/features/ai/model-profiles.js +111 -0
  218. package/dist/features/ai/model-profiles.js.map +1 -0
  219. package/dist/features/ai/preview-extensions.js +14 -0
  220. package/dist/features/ai/preview-extensions.js.map +1 -0
  221. package/dist/features/ai/protocol/agent-ops.d.ts +120 -0
  222. package/dist/features/ai/protocol/agent-ops.js +298 -0
  223. package/dist/features/ai/protocol/agent-ops.js.map +1 -0
  224. package/dist/features/ai/protocol/chat.d.ts +57 -0
  225. package/dist/features/ai/protocol/chat.js +146 -0
  226. package/dist/features/ai/protocol/chat.js.map +1 -0
  227. package/dist/features/ai/protocol/complete.d.ts +21 -0
  228. package/dist/features/ai/protocol/complete.js +81 -0
  229. package/dist/features/ai/protocol/complete.js.map +1 -0
  230. package/dist/features/ai/protocol/index.d.ts +7 -0
  231. package/dist/features/ai/protocol/index.js +7 -0
  232. package/dist/features/ai/protocol/models.d.ts +48 -0
  233. package/dist/features/ai/protocol/models.js +41 -0
  234. package/dist/features/ai/protocol/models.js.map +1 -0
  235. package/dist/features/ai/protocol/schemas.d.ts +36 -0
  236. package/dist/features/ai/protocol/schemas.js +40 -0
  237. package/dist/features/ai/protocol/schemas.js.map +1 -0
  238. package/dist/features/ai/protocol/tasks.d.ts +107 -0
  239. package/dist/features/ai/protocol/tasks.js +227 -0
  240. package/dist/features/ai/protocol/tasks.js.map +1 -0
  241. package/dist/features/ai/rate-limit.d.ts +9 -0
  242. package/dist/features/ai/rate-limit.js +20 -0
  243. package/dist/features/ai/rate-limit.js.map +1 -0
  244. package/dist/features/ai/react/AiReviewBar.d.ts +12 -0
  245. package/dist/features/ai/react/AiReviewBar.js +156 -0
  246. package/dist/features/ai/react/AiReviewBar.js.map +1 -0
  247. package/dist/features/ai/react/AiSelectionMenu.d.ts +14 -0
  248. package/dist/features/ai/react/AiSelectionMenu.js +513 -0
  249. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -0
  250. package/dist/features/ai/react/AiSidebar.d.ts +19 -0
  251. package/dist/features/ai/react/AiSidebar.js +339 -0
  252. package/dist/features/ai/react/AiSidebar.js.map +1 -0
  253. package/dist/features/ai/react/DocumentChatAnswer.d.ts +10 -0
  254. package/dist/features/ai/react/DocumentChatAnswer.js +70 -0
  255. package/dist/features/ai/react/DocumentChatAnswer.js.map +1 -0
  256. package/dist/features/ai/react/DocumentChatOps.d.ts +19 -0
  257. package/dist/features/ai/react/DocumentChatOps.js +111 -0
  258. package/dist/features/ai/react/DocumentChatOps.js.map +1 -0
  259. package/dist/features/ai/react/ModelQuickSwitch.d.ts +19 -0
  260. package/dist/features/ai/react/ModelQuickSwitch.js +89 -0
  261. package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -0
  262. package/dist/features/ai/react/SettingsAiTab.d.ts +5 -0
  263. package/dist/features/ai/react/SettingsAiTab.js +103 -0
  264. package/dist/features/ai/react/SettingsAiTab.js.map +1 -0
  265. package/dist/features/ai/react/SettingsModelProfiles.d.ts +8 -0
  266. package/dist/features/ai/react/SettingsModelProfiles.js +318 -0
  267. package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -0
  268. package/dist/features/ai/react/use-ai-assistant.d.ts +22 -0
  269. package/dist/features/ai/react/use-ai-assistant.js +36 -0
  270. package/dist/features/ai/react/use-ai-assistant.js.map +1 -0
  271. package/dist/features/ai/react/use-document-chat.d.ts +32 -0
  272. package/dist/features/ai/react/use-document-chat.js +43 -0
  273. package/dist/features/ai/react/use-document-chat.js.map +1 -0
  274. package/dist/features/ai/react/use-model-profiles.d.ts +20 -0
  275. package/dist/features/ai/react/use-model-profiles.js +33 -0
  276. package/dist/features/ai/react/use-model-profiles.js.map +1 -0
  277. package/dist/features/ai/react.d.ts +23 -0
  278. package/dist/features/ai/react.js +28 -0
  279. package/dist/features/ai/react.js.map +1 -0
  280. package/dist/features/ai/sidebar-flow.d.ts +61 -0
  281. package/dist/features/ai/sidebar-flow.js +125 -0
  282. package/dist/features/ai/sidebar-flow.js.map +1 -0
  283. package/dist/features/ai/use-ai-assistant.d.ts +22 -0
  284. package/dist/features/ai/use-ai-assistant.js +39 -0
  285. package/dist/features/ai/use-ai-assistant.js.map +1 -0
  286. package/dist/features/ai/use-document-chat.d.ts +33 -0
  287. package/dist/features/ai/use-document-chat.js +45 -0
  288. package/dist/features/ai/use-document-chat.js.map +1 -0
  289. package/dist/features/ai/use-model-profiles.d.ts +23 -0
  290. package/dist/features/ai/use-model-profiles.js +50 -0
  291. package/dist/features/ai/use-model-profiles.js.map +1 -0
  292. package/dist/features/ai/vue.d.ts +21 -0
  293. package/dist/features/ai/vue.js +28 -0
  294. package/dist/features/ai/vue.js.map +1 -0
  295. package/dist/features/authors/attribution.d.ts +36 -0
  296. package/dist/features/authors/attribution.js +94 -0
  297. package/dist/features/authors/attribution.js.map +1 -0
  298. package/dist/features/authors/author-color-styles.d.ts +12 -0
  299. package/dist/features/authors/author-color-styles.js +24 -0
  300. package/dist/features/authors/author-color-styles.js.map +1 -0
  301. package/dist/features/authors/components/AuthorColorsButton.js +50 -0
  302. package/dist/features/authors/components/AuthorColorsButton.js.map +1 -0
  303. package/dist/features/authors/components/AuthorColorsButton.vue.d.ts +11 -0
  304. package/dist/features/authors/components/AuthorColorsPanel.js +72 -0
  305. package/dist/features/authors/components/AuthorColorsPanel.js.map +1 -0
  306. package/dist/features/authors/components/AuthorColorsPanel.vue.d.ts +11 -0
  307. package/dist/features/authors/components/SettingsAuthorsTab.js +49 -0
  308. package/dist/features/authors/components/SettingsAuthorsTab.js.map +1 -0
  309. package/dist/features/authors/components/SettingsAuthorsTab.vue.d.ts +6 -0
  310. package/dist/features/authors/feature.d.ts +22 -0
  311. package/dist/features/authors/feature.js +47 -0
  312. package/dist/features/authors/feature.js.map +1 -0
  313. package/dist/features/authors/index.d.ts +7 -0
  314. package/dist/features/authors/index.js +6 -0
  315. package/dist/features/authors/react/AuthorColorsButton.d.ts +8 -0
  316. package/dist/features/authors/react/AuthorColorsButton.js +41 -0
  317. package/dist/features/authors/react/AuthorColorsButton.js.map +1 -0
  318. package/dist/features/authors/react/AuthorColorsPanel.d.ts +8 -0
  319. package/dist/features/authors/react/AuthorColorsPanel.js +125 -0
  320. package/dist/features/authors/react/AuthorColorsPanel.js.map +1 -0
  321. package/dist/features/authors/react/SettingsAuthorsTab.d.ts +5 -0
  322. package/dist/features/authors/react/SettingsAuthorsTab.js +92 -0
  323. package/dist/features/authors/react/SettingsAuthorsTab.js.map +1 -0
  324. package/dist/features/authors/react.d.ts +11 -0
  325. package/dist/features/authors/react.js +16 -0
  326. package/dist/features/authors/react.js.map +1 -0
  327. package/dist/features/authors/schema.d.ts +22 -0
  328. package/dist/features/authors/schema.js +70 -0
  329. package/dist/features/authors/schema.js.map +1 -0
  330. package/dist/features/authors/vue.d.ts +10 -0
  331. package/dist/features/authors/vue.js +16 -0
  332. package/dist/features/authors/vue.js.map +1 -0
  333. package/dist/features/block-ui/block-actions.d.ts +17 -0
  334. package/dist/features/block-ui/block-actions.js +37 -0
  335. package/dist/features/block-ui/block-actions.js.map +1 -0
  336. package/dist/features/block-ui/components/BlockActionsOverlay.js +142 -0
  337. package/dist/features/block-ui/components/BlockActionsOverlay.js.map +1 -0
  338. package/dist/features/block-ui/components/BlockActionsOverlay.vue.d.ts +25 -0
  339. package/dist/features/block-ui/components/BlockHandles.js +113 -0
  340. package/dist/features/block-ui/components/BlockHandles.js.map +1 -0
  341. package/dist/features/block-ui/components/BlockUi.js +181 -0
  342. package/dist/features/block-ui/components/BlockUi.js.map +1 -0
  343. package/dist/features/block-ui/components/BlockUi.vue.d.ts +17 -0
  344. package/dist/features/block-ui/feature.d.ts +21 -0
  345. package/dist/features/block-ui/feature.js +23 -0
  346. package/dist/features/block-ui/feature.js.map +1 -0
  347. package/dist/features/block-ui/handle-geometry.js +52 -0
  348. package/dist/features/block-ui/handle-geometry.js.map +1 -0
  349. package/dist/features/block-ui/index.d.ts +3 -0
  350. package/dist/features/block-ui/index.js +3 -0
  351. package/dist/features/block-ui/react/BlockActionsOverlay.d.ts +19 -0
  352. package/dist/features/block-ui/react/BlockActionsOverlay.js +142 -0
  353. package/dist/features/block-ui/react/BlockActionsOverlay.js.map +1 -0
  354. package/dist/features/block-ui/react/BlockHandles.d.ts +15 -0
  355. package/dist/features/block-ui/react/BlockHandles.js +104 -0
  356. package/dist/features/block-ui/react/BlockHandles.js.map +1 -0
  357. package/dist/features/block-ui/react/BlockUi.d.ts +14 -0
  358. package/dist/features/block-ui/react/BlockUi.js +174 -0
  359. package/dist/features/block-ui/react/BlockUi.js.map +1 -0
  360. package/dist/features/block-ui/react.d.ts +11 -0
  361. package/dist/features/block-ui/react.js +13 -0
  362. package/dist/features/block-ui/react.js.map +1 -0
  363. package/dist/features/block-ui/vue.d.ts +9 -0
  364. package/dist/features/block-ui/vue.js +12 -0
  365. package/dist/features/block-ui/vue.js.map +1 -0
  366. package/dist/features/collab/anchor-resolver.d.ts +7 -0
  367. package/dist/features/collab/anchor-resolver.js +92 -0
  368. package/dist/features/collab/anchor-resolver.js.map +1 -0
  369. package/dist/features/collab/feature.d.ts +22 -0
  370. package/dist/features/collab/feature.js +36 -0
  371. package/dist/features/collab/feature.js.map +1 -0
  372. package/dist/features/collab/index.d.ts +3 -0
  373. package/dist/features/collab/index.js +3 -0
  374. package/dist/features/collab/react.d.ts +7 -0
  375. package/dist/features/collab/react.js +10 -0
  376. package/dist/features/collab/react.js.map +1 -0
  377. package/dist/features/collab/vue.d.ts +6 -0
  378. package/dist/features/collab/vue.js +37 -0
  379. package/dist/features/collab/vue.js.map +1 -0
  380. package/dist/features/comments/comments-plugin.d.ts +71 -0
  381. package/dist/features/comments/comments-plugin.js +185 -0
  382. package/dist/features/comments/comments-plugin.js.map +1 -0
  383. package/dist/features/comments/components/CommentBubble.js +54 -0
  384. package/dist/features/comments/components/CommentBubble.js.map +1 -0
  385. package/dist/features/comments/components/CommentBubble.vue.d.ts +14 -0
  386. package/dist/features/comments/components/CommentComposer.js +87 -0
  387. package/dist/features/comments/components/CommentComposer.js.map +1 -0
  388. package/dist/features/comments/components/CommentComposer.vue.d.ts +21 -0
  389. package/dist/features/comments/components/CommentThreadView.js +255 -0
  390. package/dist/features/comments/components/CommentThreadView.js.map +1 -0
  391. package/dist/features/comments/components/CommentThreadView.vue.d.ts +29 -0
  392. package/dist/features/comments/components/CommentsListMenu.js +134 -0
  393. package/dist/features/comments/components/CommentsListMenu.js.map +1 -0
  394. package/dist/features/comments/components/CommentsListMenu.vue.d.ts +10 -0
  395. package/dist/features/comments/components/CommentsMargin.js +291 -0
  396. package/dist/features/comments/components/CommentsMargin.js.map +1 -0
  397. package/dist/features/comments/components/CommentsMargin.vue.d.ts +16 -0
  398. package/dist/features/comments/components/CommentsOverlay.js +39 -0
  399. package/dist/features/comments/components/CommentsOverlay.js.map +1 -0
  400. package/dist/features/comments/components/CommentsPopover.js +124 -0
  401. package/dist/features/comments/components/CommentsPopover.js.map +1 -0
  402. package/dist/features/comments/components/CommentsPopover.vue.d.ts +10 -0
  403. package/dist/features/comments/components/CommentsToolbarButton.js +61 -0
  404. package/dist/features/comments/components/CommentsToolbarButton.js.map +1 -0
  405. package/dist/features/comments/components/SettingsCommentsTab.js +46 -0
  406. package/dist/features/comments/components/SettingsCommentsTab.js.map +1 -0
  407. package/dist/features/comments/components/SettingsCommentsTab.vue.d.ts +6 -0
  408. package/dist/features/comments/feature.d.ts +39 -0
  409. package/dist/features/comments/feature.js +128 -0
  410. package/dist/features/comments/feature.js.map +1 -0
  411. package/dist/features/comments/index.d.ts +6 -0
  412. package/dist/features/comments/index.js +6 -0
  413. package/dist/features/comments/model.d.ts +87 -0
  414. package/dist/features/comments/model.js +390 -0
  415. package/dist/features/comments/model.js.map +1 -0
  416. package/dist/features/comments/react/CommentAvatar.js +24 -0
  417. package/dist/features/comments/react/CommentAvatar.js.map +1 -0
  418. package/dist/features/comments/react/CommentBubble.d.ts +11 -0
  419. package/dist/features/comments/react/CommentBubble.js +43 -0
  420. package/dist/features/comments/react/CommentBubble.js.map +1 -0
  421. package/dist/features/comments/react/CommentComposer.d.ts +15 -0
  422. package/dist/features/comments/react/CommentComposer.js +69 -0
  423. package/dist/features/comments/react/CommentComposer.js.map +1 -0
  424. package/dist/features/comments/react/CommentThreadView.d.ts +24 -0
  425. package/dist/features/comments/react/CommentThreadView.js +249 -0
  426. package/dist/features/comments/react/CommentThreadView.js.map +1 -0
  427. package/dist/features/comments/react/CommentsListMenu.d.ts +8 -0
  428. package/dist/features/comments/react/CommentsListMenu.js +115 -0
  429. package/dist/features/comments/react/CommentsListMenu.js.map +1 -0
  430. package/dist/features/comments/react/CommentsMargin.d.ts +16 -0
  431. package/dist/features/comments/react/CommentsMargin.js +294 -0
  432. package/dist/features/comments/react/CommentsMargin.js.map +1 -0
  433. package/dist/features/comments/react/CommentsOverlay.d.ts +11 -0
  434. package/dist/features/comments/react/CommentsOverlay.js +52 -0
  435. package/dist/features/comments/react/CommentsOverlay.js.map +1 -0
  436. package/dist/features/comments/react/CommentsPopover.d.ts +15 -0
  437. package/dist/features/comments/react/CommentsPopover.js +107 -0
  438. package/dist/features/comments/react/CommentsPopover.js.map +1 -0
  439. package/dist/features/comments/react/CommentsToolbarButton.d.ts +12 -0
  440. package/dist/features/comments/react/CommentsToolbarButton.js +52 -0
  441. package/dist/features/comments/react/CommentsToolbarButton.js.map +1 -0
  442. package/dist/features/comments/react/SettingsCommentsTab.d.ts +5 -0
  443. package/dist/features/comments/react/SettingsCommentsTab.js +77 -0
  444. package/dist/features/comments/react/SettingsCommentsTab.js.map +1 -0
  445. package/dist/features/comments/react/use-comments.d.ts +17 -0
  446. package/dist/features/comments/react/use-comments.js +37 -0
  447. package/dist/features/comments/react/use-comments.js.map +1 -0
  448. package/dist/features/comments/react.d.ts +23 -0
  449. package/dist/features/comments/react.js +28 -0
  450. package/dist/features/comments/react.js.map +1 -0
  451. package/dist/features/comments/schema.d.ts +51 -0
  452. package/dist/features/comments/schema.js +41 -0
  453. package/dist/features/comments/schema.js.map +1 -0
  454. package/dist/features/comments/shortcuts.js +34 -0
  455. package/dist/features/comments/shortcuts.js.map +1 -0
  456. package/dist/features/comments/use-comments.d.ts +37 -0
  457. package/dist/features/comments/use-comments.js +45 -0
  458. package/dist/features/comments/use-comments.js.map +1 -0
  459. package/dist/features/comments/vue.d.ts +20 -0
  460. package/dist/features/comments/vue.js +28 -0
  461. package/dist/features/comments/vue.js.map +1 -0
  462. package/dist/features/composites/ai-brief.d.ts +31 -0
  463. package/dist/features/composites/ai-brief.js +56 -0
  464. package/dist/features/composites/ai-brief.js.map +1 -0
  465. package/dist/features/composites/anchors.d.ts +47 -0
  466. package/dist/features/composites/anchors.js +137 -0
  467. package/dist/features/composites/anchors.js.map +1 -0
  468. package/dist/features/composites/components/CompositeChrome.js +243 -0
  469. package/dist/features/composites/components/CompositeChrome.js.map +1 -0
  470. package/dist/features/composites/components/CompositeChrome.vue.d.ts +19 -0
  471. package/dist/features/composites/components/CompositeDetails.js +158 -0
  472. package/dist/features/composites/components/CompositeDetails.js.map +1 -0
  473. package/dist/features/composites/components/CompositeDetails.vue.d.ts +19 -0
  474. package/dist/features/composites/components/CompositeFieldEditor.js +114 -0
  475. package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -0
  476. package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +23 -0
  477. package/dist/features/composites/components/CompositeInspector.js +439 -0
  478. package/dist/features/composites/components/CompositeInspector.js.map +1 -0
  479. package/dist/features/composites/components/CompositeInspector.vue.d.ts +32 -0
  480. package/dist/features/composites/components/CompositeStatusMenu.js +51 -0
  481. package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -0
  482. package/dist/features/composites/components/CompositeStatusMenu.vue.d.ts +28 -0
  483. package/dist/features/composites/components/CompositeSummaryEditor.js +79 -0
  484. package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -0
  485. package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +18 -0
  486. package/dist/features/composites/components/CompositeTable.js +348 -0
  487. package/dist/features/composites/components/CompositeTable.js.map +1 -0
  488. package/dist/features/composites/components/CompositeTable.vue.d.ts +15 -0
  489. package/dist/features/composites/components/CompositeTagsEditor.js +95 -0
  490. package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -0
  491. package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +13 -0
  492. package/dist/features/composites/components/CompositeView.js +46 -0
  493. package/dist/features/composites/components/CompositeView.js.map +1 -0
  494. package/dist/features/composites/components/CompositeView.vue.d.ts +96 -0
  495. package/dist/features/composites/components/SettingsEditingTab.js +119 -0
  496. package/dist/features/composites/components/SettingsEditingTab.js.map +1 -0
  497. package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +11 -0
  498. package/dist/features/composites/convert.d.ts +24 -0
  499. package/dist/features/composites/convert.js +145 -0
  500. package/dist/features/composites/convert.js.map +1 -0
  501. package/dist/features/composites/editing-settings-keys.js +47 -0
  502. package/dist/features/composites/editing-settings-keys.js.map +1 -0
  503. package/dist/features/composites/feature.d.ts +30 -0
  504. package/dist/features/composites/feature.js +305 -0
  505. package/dist/features/composites/feature.js.map +1 -0
  506. package/dist/features/composites/fences.d.ts +20 -0
  507. package/dist/features/composites/fences.js +86 -0
  508. package/dist/features/composites/fences.js.map +1 -0
  509. package/dist/features/composites/heading-composites.d.ts +36 -0
  510. package/dist/features/composites/heading-composites.js +140 -0
  511. package/dist/features/composites/heading-composites.js.map +1 -0
  512. package/dist/features/composites/index.d.ts +20 -0
  513. package/dist/features/composites/index.js +20 -0
  514. package/dist/features/composites/kinds.d.ts +49 -0
  515. package/dist/features/composites/kinds.js +131 -0
  516. package/dist/features/composites/kinds.js.map +1 -0
  517. package/dist/features/composites/legacy.d.ts +8 -0
  518. package/dist/features/composites/legacy.js +25 -0
  519. package/dist/features/composites/legacy.js.map +1 -0
  520. package/dist/features/composites/markdown-export.d.ts +17 -0
  521. package/dist/features/composites/markdown-export.js +49 -0
  522. package/dist/features/composites/markdown-export.js.map +1 -0
  523. package/dist/features/composites/meta-display.js +46 -0
  524. package/dist/features/composites/meta-display.js.map +1 -0
  525. package/dist/features/composites/node-views.d.ts +17 -0
  526. package/dist/features/composites/node-views.js +18 -0
  527. package/dist/features/composites/node-views.js.map +1 -0
  528. package/dist/features/composites/nodes.d.ts +56 -0
  529. package/dist/features/composites/nodes.js +480 -0
  530. package/dist/features/composites/nodes.js.map +1 -0
  531. package/dist/features/composites/outline.d.ts +35 -0
  532. package/dist/features/composites/outline.js +69 -0
  533. package/dist/features/composites/outline.js.map +1 -0
  534. package/dist/features/composites/react/CompositeChrome.d.ts +16 -0
  535. package/dist/features/composites/react/CompositeChrome.js +234 -0
  536. package/dist/features/composites/react/CompositeChrome.js.map +1 -0
  537. package/dist/features/composites/react/CompositeDetails.d.ts +16 -0
  538. package/dist/features/composites/react/CompositeDetails.js +149 -0
  539. package/dist/features/composites/react/CompositeDetails.js.map +1 -0
  540. package/dist/features/composites/react/CompositeFieldEditor.d.ts +18 -0
  541. package/dist/features/composites/react/CompositeFieldEditor.js +131 -0
  542. package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -0
  543. package/dist/features/composites/react/CompositeInspector.d.ts +23 -0
  544. package/dist/features/composites/react/CompositeInspector.js +506 -0
  545. package/dist/features/composites/react/CompositeInspector.js.map +1 -0
  546. package/dist/features/composites/react/CompositeStatusMenu.d.ts +15 -0
  547. package/dist/features/composites/react/CompositeStatusMenu.js +36 -0
  548. package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -0
  549. package/dist/features/composites/react/CompositeSummaryEditor.d.ts +15 -0
  550. package/dist/features/composites/react/CompositeSummaryEditor.js +85 -0
  551. package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -0
  552. package/dist/features/composites/react/CompositeTable.d.ts +11 -0
  553. package/dist/features/composites/react/CompositeTable.js +342 -0
  554. package/dist/features/composites/react/CompositeTable.js.map +1 -0
  555. package/dist/features/composites/react/CompositeTagsEditor.d.ts +10 -0
  556. package/dist/features/composites/react/CompositeTagsEditor.js +122 -0
  557. package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -0
  558. package/dist/features/composites/react/CompositeView.d.ts +6 -0
  559. package/dist/features/composites/react/CompositeView.js +30 -0
  560. package/dist/features/composites/react/CompositeView.js.map +1 -0
  561. package/dist/features/composites/react/SettingsEditingTab.d.ts +8 -0
  562. package/dist/features/composites/react/SettingsEditingTab.js +153 -0
  563. package/dist/features/composites/react/SettingsEditingTab.js.map +1 -0
  564. package/dist/features/composites/react/use-composite-anchors.d.ts +17 -0
  565. package/dist/features/composites/react/use-composite-anchors.js +46 -0
  566. package/dist/features/composites/react/use-composite-anchors.js.map +1 -0
  567. package/dist/features/composites/react/use-composite-rows.d.ts +11 -0
  568. package/dist/features/composites/react/use-composite-rows.js +26 -0
  569. package/dist/features/composites/react/use-composite-rows.js.map +1 -0
  570. package/dist/features/composites/react.d.ts +24 -0
  571. package/dist/features/composites/react.js +30 -0
  572. package/dist/features/composites/react.js.map +1 -0
  573. package/dist/features/composites/rows-model.d.ts +38 -0
  574. package/dist/features/composites/rows-model.js +92 -0
  575. package/dist/features/composites/rows-model.js.map +1 -0
  576. package/dist/features/composites/rows.d.ts +76 -0
  577. package/dist/features/composites/rows.js +159 -0
  578. package/dist/features/composites/rows.js.map +1 -0
  579. package/dist/features/composites/schema.d.ts +21 -0
  580. package/dist/features/composites/schema.js +26 -0
  581. package/dist/features/composites/schema.js.map +1 -0
  582. package/dist/features/composites/structure-mode.d.ts +11 -0
  583. package/dist/features/composites/structure-mode.js +16 -0
  584. package/dist/features/composites/structure-mode.js.map +1 -0
  585. package/dist/features/composites/structure.d.ts +19 -0
  586. package/dist/features/composites/structure.js +19 -0
  587. package/dist/features/composites/structure.js.map +1 -0
  588. package/dist/features/composites/table-model.js +117 -0
  589. package/dist/features/composites/table-model.js.map +1 -0
  590. package/dist/features/composites/tree-root.d.ts +11 -0
  591. package/dist/features/composites/tree-root.js +16 -0
  592. package/dist/features/composites/tree-root.js.map +1 -0
  593. package/dist/features/composites/use-composite-anchors.d.ts +18 -0
  594. package/dist/features/composites/use-composite-anchors.js +42 -0
  595. package/dist/features/composites/use-composite-anchors.js.map +1 -0
  596. package/dist/features/composites/use-composite-rows.d.ts +25 -0
  597. package/dist/features/composites/use-composite-rows.js +28 -0
  598. package/dist/features/composites/use-composite-rows.js.map +1 -0
  599. package/dist/features/composites/vue.d.ts +23 -0
  600. package/dist/features/composites/vue.js +30 -0
  601. package/dist/features/composites/vue.js.map +1 -0
  602. package/dist/features/media/assets.d.ts +30 -0
  603. package/dist/features/media/assets.js +26 -0
  604. package/dist/features/media/assets.js.map +1 -0
  605. package/dist/features/media/audio-recorder.d.ts +25 -0
  606. package/dist/features/media/audio-recorder.js +123 -0
  607. package/dist/features/media/audio-recorder.js.map +1 -0
  608. package/dist/features/media/components/AudioRecorderModal.js +152 -0
  609. package/dist/features/media/components/AudioRecorderModal.js.map +1 -0
  610. package/dist/features/media/components/AudioRecorderModal.vue.d.ts +15 -0
  611. package/dist/features/media/components/AudioView.js +46 -0
  612. package/dist/features/media/components/AudioView.js.map +1 -0
  613. package/dist/features/media/components/FileView.js +73 -0
  614. package/dist/features/media/components/FileView.js.map +1 -0
  615. package/dist/features/media/components/VideoView.js +47 -0
  616. package/dist/features/media/components/VideoView.js.map +1 -0
  617. package/dist/features/media/feature.d.ts +22 -0
  618. package/dist/features/media/feature.js +101 -0
  619. package/dist/features/media/feature.js.map +1 -0
  620. package/dist/features/media/file-import.d.ts +21 -0
  621. package/dist/features/media/file-import.js +76 -0
  622. package/dist/features/media/file-import.js.map +1 -0
  623. package/dist/features/media/index.d.ts +7 -0
  624. package/dist/features/media/index.js +7 -0
  625. package/dist/features/media/node-views.d.ts +28 -0
  626. package/dist/features/media/node-views.js +28 -0
  627. package/dist/features/media/node-views.js.map +1 -0
  628. package/dist/features/media/react/AudioRecorderModal.d.ts +12 -0
  629. package/dist/features/media/react/AudioRecorderModal.js +147 -0
  630. package/dist/features/media/react/AudioRecorderModal.js.map +1 -0
  631. package/dist/features/media/react/AudioView.js +32 -0
  632. package/dist/features/media/react/AudioView.js.map +1 -0
  633. package/dist/features/media/react/FileView.js +61 -0
  634. package/dist/features/media/react/FileView.js.map +1 -0
  635. package/dist/features/media/react/TitleField.js +30 -0
  636. package/dist/features/media/react/TitleField.js.map +1 -0
  637. package/dist/features/media/react/VideoView.js +33 -0
  638. package/dist/features/media/react/VideoView.js.map +1 -0
  639. package/dist/features/media/react/use-audio-recorder.d.ts +11 -0
  640. package/dist/features/media/react/use-audio-recorder.js +21 -0
  641. package/dist/features/media/react/use-audio-recorder.js.map +1 -0
  642. package/dist/features/media/react.d.ts +19 -0
  643. package/dist/features/media/react.js +37 -0
  644. package/dist/features/media/react.js.map +1 -0
  645. package/dist/features/media/schema.d.ts +59 -0
  646. package/dist/features/media/schema.js +194 -0
  647. package/dist/features/media/schema.js.map +1 -0
  648. package/dist/features/media/upload.d.ts +41 -0
  649. package/dist/features/media/upload.js +106 -0
  650. package/dist/features/media/upload.js.map +1 -0
  651. package/dist/features/media/use-audio-recorder.d.ts +12 -0
  652. package/dist/features/media/use-audio-recorder.js +21 -0
  653. package/dist/features/media/use-audio-recorder.js.map +1 -0
  654. package/dist/features/media/vue.d.ts +19 -0
  655. package/dist/features/media/vue.js +35 -0
  656. package/dist/features/media/vue.js.map +1 -0
  657. package/dist/features/mentions/index.d.ts +22 -0
  658. package/dist/features/mentions/index.js +34 -0
  659. package/dist/features/mentions/index.js.map +1 -0
  660. package/dist/features/mentions/mention-suggestion.d.ts +11 -0
  661. package/dist/features/mentions/mention-suggestion.js +34 -0
  662. package/dist/features/mentions/mention-suggestion.js.map +1 -0
  663. package/dist/features/mentions/mention-text.d.ts +18 -0
  664. package/dist/features/mentions/mention-text.js +75 -0
  665. package/dist/features/mentions/mention-text.js.map +1 -0
  666. package/dist/features/mentions/mention.d.ts +20 -0
  667. package/dist/features/mentions/mention.js +33 -0
  668. package/dist/features/mentions/mention.js.map +1 -0
  669. package/dist/features/mentions/metadata-defaults.d.ts +8 -0
  670. package/dist/features/mentions/metadata-defaults.js +30 -0
  671. package/dist/features/mentions/metadata-defaults.js.map +1 -0
  672. package/dist/features/mentions/normalize.d.ts +10 -0
  673. package/dist/features/mentions/normalize.js +31 -0
  674. package/dist/features/mentions/normalize.js.map +1 -0
  675. package/dist/features/mentions/schema.d.ts +10 -0
  676. package/dist/features/mentions/schema.js +10 -0
  677. package/dist/features/mentions/schema.js.map +1 -0
  678. package/dist/features/mentions/tag-suggestion.d.ts +11 -0
  679. package/dist/features/mentions/tag-suggestion.js +45 -0
  680. package/dist/features/mentions/tag-suggestion.js.map +1 -0
  681. package/dist/features/mentions/tag.d.ts +43 -0
  682. package/dist/features/mentions/tag.js +155 -0
  683. package/dist/features/mentions/tag.js.map +1 -0
  684. package/dist/features/tables/feature.d.ts +14 -0
  685. package/dist/features/tables/feature.js +100 -0
  686. package/dist/features/tables/feature.js.map +1 -0
  687. package/dist/features/tables/index.d.ts +3 -0
  688. package/dist/features/tables/index.js +3 -0
  689. package/dist/features/tables/schema.d.ts +6 -0
  690. package/dist/features/tables/schema.js +27 -0
  691. package/dist/features/tables/schema.js.map +1 -0
  692. package/dist/features/themes/components/SettingsAppearanceTab.js +227 -0
  693. package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -0
  694. package/dist/features/themes/components/SettingsAppearanceTab.vue.d.ts +6 -0
  695. package/dist/features/themes/feature.d.ts +24 -0
  696. package/dist/features/themes/feature.js +25 -0
  697. package/dist/features/themes/feature.js.map +1 -0
  698. package/dist/features/themes/index.d.ts +6 -0
  699. package/dist/features/themes/index.js +6 -0
  700. package/dist/features/themes/react/SettingsAppearanceTab.d.ts +5 -0
  701. package/dist/features/themes/react/SettingsAppearanceTab.js +221 -0
  702. package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -0
  703. package/dist/features/themes/react.d.ts +13 -0
  704. package/dist/features/themes/react.js +15 -0
  705. package/dist/features/themes/react.js.map +1 -0
  706. package/dist/features/themes/schema.d.ts +21 -0
  707. package/dist/features/themes/schema.js +16 -0
  708. package/dist/features/themes/schema.js.map +1 -0
  709. package/dist/features/themes/scope-css.d.ts +5 -0
  710. package/dist/features/themes/scope-css.js +142 -0
  711. package/dist/features/themes/scope-css.js.map +1 -0
  712. package/dist/features/themes/theme-runtime.d.ts +14 -0
  713. package/dist/features/themes/theme-runtime.js +147 -0
  714. package/dist/features/themes/theme-runtime.js.map +1 -0
  715. package/dist/features/themes/vue.d.ts +13 -0
  716. package/dist/features/themes/vue.js +29 -0
  717. package/dist/features/themes/vue.js.map +1 -0
  718. package/dist/features/transcript/TranscriptSegmentView.js +169 -0
  719. package/dist/features/transcript/TranscriptSegmentView.js.map +1 -0
  720. package/dist/features/transcript/TranscriptSegmentView.vue.d.ts +96 -0
  721. package/dist/features/transcript/convert.d.ts +11 -0
  722. package/dist/features/transcript/convert.js +40 -0
  723. package/dist/features/transcript/convert.js.map +1 -0
  724. package/dist/features/transcript/feature.d.ts +25 -0
  725. package/dist/features/transcript/feature.js +59 -0
  726. package/dist/features/transcript/feature.js.map +1 -0
  727. package/dist/features/transcript/index.d.ts +7 -0
  728. package/dist/features/transcript/index.js +7 -0
  729. package/dist/features/transcript/react/TranscriptSegmentView.d.ts +6 -0
  730. package/dist/features/transcript/react/TranscriptSegmentView.js +155 -0
  731. package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -0
  732. package/dist/features/transcript/react.d.ts +9 -0
  733. package/dist/features/transcript/react.js +12 -0
  734. package/dist/features/transcript/react.js.map +1 -0
  735. package/dist/features/transcript/schema.d.ts +9 -0
  736. package/dist/features/transcript/schema.js +9 -0
  737. package/dist/features/transcript/schema.js.map +1 -0
  738. package/dist/features/transcript/segment-node.d.ts +21 -0
  739. package/dist/features/transcript/segment-node.js +113 -0
  740. package/dist/features/transcript/segment-node.js.map +1 -0
  741. package/dist/features/transcript/segment.d.ts +10 -0
  742. package/dist/features/transcript/segment.js +26 -0
  743. package/dist/features/transcript/segment.js.map +1 -0
  744. package/dist/features/transcript/speakers.d.ts +8 -0
  745. package/dist/features/transcript/speakers.js +22 -0
  746. package/dist/features/transcript/speakers.js.map +1 -0
  747. package/dist/features/transcript/vue.d.ts +8 -0
  748. package/dist/features/transcript/vue.js +12 -0
  749. package/dist/features/transcript/vue.js.map +1 -0
  750. package/dist/i18n/de/ai.js +168 -0
  751. package/dist/i18n/de/ai.js.map +1 -0
  752. package/dist/i18n/de/authors.js +21 -0
  753. package/dist/i18n/de/authors.js.map +1 -0
  754. package/dist/i18n/de/block.js +16 -0
  755. package/dist/i18n/de/block.js.map +1 -0
  756. package/dist/i18n/de/comments.js +35 -0
  757. package/dist/i18n/de/comments.js.map +1 -0
  758. package/dist/i18n/de/common.js +11 -0
  759. package/dist/i18n/de/common.js.map +1 -0
  760. package/dist/i18n/de/composites.js +147 -0
  761. package/dist/i18n/de/composites.js.map +1 -0
  762. package/dist/i18n/de/integrations.js +42 -0
  763. package/dist/i18n/de/integrations.js.map +1 -0
  764. package/dist/i18n/de/markdown.js +16 -0
  765. package/dist/i18n/de/markdown.js.map +1 -0
  766. package/dist/i18n/de/media.js +27 -0
  767. package/dist/i18n/de/media.js.map +1 -0
  768. package/dist/i18n/de/mentions.js +10 -0
  769. package/dist/i18n/de/mentions.js.map +1 -0
  770. package/dist/i18n/de/outline.js +9 -0
  771. package/dist/i18n/de/outline.js.map +1 -0
  772. package/dist/i18n/de/settings.js +12 -0
  773. package/dist/i18n/de/settings.js.map +1 -0
  774. package/dist/i18n/de/tables.js +15 -0
  775. package/dist/i18n/de/tables.js.map +1 -0
  776. package/dist/i18n/de/themes.js +26 -0
  777. package/dist/i18n/de/themes.js.map +1 -0
  778. package/dist/i18n/de/toolbar.js +22 -0
  779. package/dist/i18n/de/toolbar.js.map +1 -0
  780. package/dist/i18n/de/transcript.js +10 -0
  781. package/dist/i18n/de/transcript.js.map +1 -0
  782. package/dist/i18n/de/tree.js +31 -0
  783. package/dist/i18n/de/tree.js.map +1 -0
  784. package/dist/i18n/de.d.ts +503 -0
  785. package/dist/i18n/de.js +42 -0
  786. package/dist/i18n/de.js.map +1 -0
  787. package/dist/i18n/en/ai.js +168 -0
  788. package/dist/i18n/en/ai.js.map +1 -0
  789. package/dist/i18n/en/authors.js +21 -0
  790. package/dist/i18n/en/authors.js.map +1 -0
  791. package/dist/i18n/en/block.js +16 -0
  792. package/dist/i18n/en/block.js.map +1 -0
  793. package/dist/i18n/en/comments.js +35 -0
  794. package/dist/i18n/en/comments.js.map +1 -0
  795. package/dist/i18n/en/common.js +11 -0
  796. package/dist/i18n/en/common.js.map +1 -0
  797. package/dist/i18n/en/composites.js +145 -0
  798. package/dist/i18n/en/composites.js.map +1 -0
  799. package/dist/i18n/en/integrations.js +42 -0
  800. package/dist/i18n/en/integrations.js.map +1 -0
  801. package/dist/i18n/en/markdown.js +16 -0
  802. package/dist/i18n/en/markdown.js.map +1 -0
  803. package/dist/i18n/en/media.js +27 -0
  804. package/dist/i18n/en/media.js.map +1 -0
  805. package/dist/i18n/en/mentions.js +10 -0
  806. package/dist/i18n/en/mentions.js.map +1 -0
  807. package/dist/i18n/en/outline.js +9 -0
  808. package/dist/i18n/en/outline.js.map +1 -0
  809. package/dist/i18n/en/settings.js +12 -0
  810. package/dist/i18n/en/settings.js.map +1 -0
  811. package/dist/i18n/en/tables.js +15 -0
  812. package/dist/i18n/en/tables.js.map +1 -0
  813. package/dist/i18n/en/themes.js +26 -0
  814. package/dist/i18n/en/themes.js.map +1 -0
  815. package/dist/i18n/en/toolbar.js +22 -0
  816. package/dist/i18n/en/toolbar.js.map +1 -0
  817. package/dist/i18n/en/transcript.js +10 -0
  818. package/dist/i18n/en/transcript.js.map +1 -0
  819. package/dist/i18n/en/tree.js +31 -0
  820. package/dist/i18n/en/tree.js.map +1 -0
  821. package/dist/i18n/en.js +42 -0
  822. package/dist/i18n/en.js.map +1 -0
  823. package/dist/i18n/index.d.ts +24 -0
  824. package/dist/i18n/index.js +31 -0
  825. package/dist/i18n/index.js.map +1 -0
  826. package/dist/index.d.ts +6 -0
  827. package/dist/index.js +0 -0
  828. package/dist/integrations/composite-ai/composite-fill.d.ts +17 -0
  829. package/dist/integrations/composite-ai/composite-fill.js +129 -0
  830. package/dist/integrations/composite-ai/composite-fill.js.map +1 -0
  831. package/dist/integrations/composite-ai/composite-summary.d.ts +28 -0
  832. package/dist/integrations/composite-ai/composite-summary.js +143 -0
  833. package/dist/integrations/composite-ai/composite-summary.js.map +1 -0
  834. package/dist/integrations/composite-ai/document-transcribe.d.ts +14 -0
  835. package/dist/integrations/composite-ai/document-transcribe.js +89 -0
  836. package/dist/integrations/composite-ai/document-transcribe.js.map +1 -0
  837. package/dist/integrations/composite-ai/index.d.ts +22 -0
  838. package/dist/integrations/composite-ai/index.js +80 -0
  839. package/dist/integrations/composite-ai/index.js.map +1 -0
  840. package/dist/integrations/composite-media/index.d.ts +19 -0
  841. package/dist/integrations/composite-media/index.js +72 -0
  842. package/dist/integrations/composite-media/index.js.map +1 -0
  843. package/dist/integrations/transcript-ai/index.d.ts +16 -0
  844. package/dist/integrations/transcript-ai/index.js +63 -0
  845. package/dist/integrations/transcript-ai/index.js.map +1 -0
  846. package/dist/integrations/transcript-ai/protocol.d.ts +68 -0
  847. package/dist/integrations/transcript-ai/protocol.js +132 -0
  848. package/dist/integrations/transcript-ai/protocol.js.map +1 -0
  849. package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +35 -0
  850. package/dist/integrations/transcript-ai/transcript-cleanup.js +169 -0
  851. package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -0
  852. package/dist/markdown/MarkdownSurface.d.ts +25 -0
  853. package/dist/markdown/MarkdownSurface.js +93 -0
  854. package/dist/markdown/MarkdownSurface.js.map +1 -0
  855. package/dist/markdown/MarkdownSurface.vue.d.ts +76 -0
  856. package/dist/markdown/MarkdownSurface2.js +237 -0
  857. package/dist/markdown/MarkdownSurface2.js.map +1 -0
  858. package/dist/markdown/clipboard.d.ts +17 -0
  859. package/dist/markdown/clipboard.js +125 -0
  860. package/dist/markdown/clipboard.js.map +1 -0
  861. package/dist/markdown/document.d.ts +36 -0
  862. package/dist/markdown/document.js +100 -0
  863. package/dist/markdown/document.js.map +1 -0
  864. package/dist/markdown/frontmatter.d.ts +18 -0
  865. package/dist/markdown/frontmatter.js +32 -0
  866. package/dist/markdown/frontmatter.js.map +1 -0
  867. package/dist/markdown/index.d.ts +6 -0
  868. package/dist/markdown/index.js +6 -0
  869. package/dist/markdown/policy.d.ts +34 -0
  870. package/dist/markdown/policy.js +78 -0
  871. package/dist/markdown/policy.js.map +1 -0
  872. package/dist/markdown/react.d.ts +2 -0
  873. package/dist/markdown/react.js +1 -0
  874. package/dist/markdown/source-mode.d.ts +49 -0
  875. package/dist/markdown/source-mode.js +86 -0
  876. package/dist/markdown/source-mode.js.map +1 -0
  877. package/dist/markdown/vue.d.ts +2 -0
  878. package/dist/markdown/vue.js +1 -0
  879. package/dist/nuxt/module.d.ts +68 -0
  880. package/dist/nuxt/module.js +103 -0
  881. package/dist/nuxt/module.js.map +1 -0
  882. package/dist/nuxt/runtime/install-context.d.ts +17 -0
  883. package/dist/nuxt/runtime/install-context.js +26 -0
  884. package/dist/nuxt/runtime/install-context.js.map +1 -0
  885. package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +8 -0
  886. package/dist/nuxt/runtime/plugins/editor-context-primevue.client.ts +8 -0
  887. package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +5 -0
  888. package/dist/nuxt/runtime/server/api/ai/chat.post.js +8 -0
  889. package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +1 -0
  890. package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +5 -0
  891. package/dist/nuxt/runtime/server/api/ai/complete.post.js +8 -0
  892. package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +1 -0
  893. package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +5 -0
  894. package/dist/nuxt/runtime/server/api/ai/generate.post.js +8 -0
  895. package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +1 -0
  896. package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +5 -0
  897. package/dist/nuxt/runtime/server/api/ai/models.get.js +8 -0
  898. package/dist/nuxt/runtime/server/api/ai/models.get.js.map +1 -0
  899. package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +5 -0
  900. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +8 -0
  901. package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +1 -0
  902. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +5 -0
  903. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +8 -0
  904. package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +1 -0
  905. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +5 -0
  906. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +8 -0
  907. package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +1 -0
  908. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +5 -0
  909. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +8 -0
  910. package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +1 -0
  911. package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +5 -0
  912. package/dist/nuxt/runtime/server/api/editor/settings.get.js +8 -0
  913. package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +1 -0
  914. package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +5 -0
  915. package/dist/nuxt/runtime/server/api/editor/settings.put.js +8 -0
  916. package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +1 -0
  917. package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +5 -0
  918. package/dist/nuxt/runtime/server/api/editor/tags.get.js +8 -0
  919. package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +1 -0
  920. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +5 -0
  921. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +8 -0
  922. package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +1 -0
  923. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +5 -0
  924. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +8 -0
  925. package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +1 -0
  926. package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +5 -0
  927. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +8 -0
  928. package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +1 -0
  929. package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +5 -0
  930. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +8 -0
  931. package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +1 -0
  932. package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +5 -0
  933. package/dist/nuxt/runtime/server/api/editor/users.get.js +8 -0
  934. package/dist/nuxt/runtime/server/api/editor/users.get.js.map +1 -0
  935. package/dist/nuxt/runtime/server/plugins/collab.d.ts +5 -0
  936. package/dist/nuxt/runtime/server/plugins/collab.js +10 -0
  937. package/dist/nuxt/runtime/server/plugins/collab.js.map +1 -0
  938. package/dist/nuxt/runtime/server/plugins/editor-server.d.ts +5 -0
  939. package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -0
  940. package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -0
  941. package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +5 -0
  942. package/dist/nuxt/runtime/server/routes/collab/[id].js +35 -0
  943. package/dist/nuxt/runtime/server/routes/collab/[id].js.map +1 -0
  944. package/dist/nuxt/runtime/server/utils/editor-route.d.ts +11 -0
  945. package/dist/nuxt/runtime/server/utils/editor-route.js +49 -0
  946. package/dist/nuxt/runtime/server/utils/editor-route.js.map +1 -0
  947. package/dist/outline/EditorOutline.js +124 -0
  948. package/dist/outline/EditorOutline.js.map +1 -0
  949. package/dist/outline/EditorOutline.vue.d.ts +15 -0
  950. package/dist/outline/heading-outline.d.ts +26 -0
  951. package/dist/outline/heading-outline.js +46 -0
  952. package/dist/outline/heading-outline.js.map +1 -0
  953. package/dist/outline/index.d.ts +2 -0
  954. package/dist/outline/index.js +1 -0
  955. package/dist/outline/react/EditorOutline.d.ts +12 -0
  956. package/dist/outline/react/EditorOutline.js +112 -0
  957. package/dist/outline/react/EditorOutline.js.map +1 -0
  958. package/dist/outline/react.d.ts +2 -0
  959. package/dist/outline/react.js +1 -0
  960. package/dist/outline/vue.d.ts +2 -0
  961. package/dist/outline/vue.js +1 -0
  962. package/dist/presets/advanced.d.ts +33 -0
  963. package/dist/presets/advanced.js +50 -0
  964. package/dist/presets/advanced.js.map +1 -0
  965. package/dist/presets/simple.d.ts +9 -0
  966. package/dist/presets/simple.js +12 -0
  967. package/dist/presets/simple.js.map +1 -0
  968. package/dist/react/AdvancedEditor.d.ts +23 -0
  969. package/dist/react/AdvancedEditor.js +24 -0
  970. package/dist/react/AdvancedEditor.js.map +1 -0
  971. package/dist/react/EditorSettingsModal.d.ts +14 -0
  972. package/dist/react/EditorSettingsModal.js +40 -0
  973. package/dist/react/EditorSettingsModal.js.map +1 -0
  974. package/dist/react/EditorSettingsPanel.d.ts +18 -0
  975. package/dist/react/EditorSettingsPanel.js +40 -0
  976. package/dist/react/EditorSettingsPanel.js.map +1 -0
  977. package/dist/react/EditorSurface.d.ts +61 -0
  978. package/dist/react/EditorSurface.js +277 -0
  979. package/dist/react/EditorSurface.js.map +1 -0
  980. package/dist/react/EditorToolbar.d.ts +15 -0
  981. package/dist/react/EditorToolbar.js +182 -0
  982. package/dist/react/EditorToolbar.js.map +1 -0
  983. package/dist/react/Icon.d.ts +13 -0
  984. package/dist/react/Icon.js +19 -0
  985. package/dist/react/Icon.js.map +1 -0
  986. package/dist/react/SettingsSection.d.ts +10 -0
  987. package/dist/react/SettingsSection.js +22 -0
  988. package/dist/react/SettingsSection.js.map +1 -0
  989. package/dist/react/SimpleEditor.d.ts +27 -0
  990. package/dist/react/SimpleEditor.js +22 -0
  991. package/dist/react/SimpleEditor.js.map +1 -0
  992. package/dist/react/advanced-preset.d.ts +11 -0
  993. package/dist/react/advanced-preset.js +31 -0
  994. package/dist/react/advanced-preset.js.map +1 -0
  995. package/dist/react/advanced.d.ts +4 -0
  996. package/dist/react/advanced.js +3 -0
  997. package/dist/react/context.d.ts +12 -0
  998. package/dist/react/context.js +21 -0
  999. package/dist/react/context.js.map +1 -0
  1000. package/dist/react/editor-ui.d.ts +14 -0
  1001. package/dist/react/editor-ui.js +20 -0
  1002. package/dist/react/editor-ui.js.map +1 -0
  1003. package/dist/react/index.d.ts +17 -0
  1004. package/dist/react/index.js +17 -0
  1005. package/dist/react/notify.d.ts +6 -0
  1006. package/dist/react/notify.js +25 -0
  1007. package/dist/react/notify.js.map +1 -0
  1008. package/dist/react/settings.d.ts +17 -0
  1009. package/dist/react/settings.js +27 -0
  1010. package/dist/react/settings.js.map +1 -0
  1011. package/dist/react/simple.d.ts +2 -0
  1012. package/dist/react/simple.js +1 -0
  1013. package/dist/react/styles/advanced.css +18 -0
  1014. package/dist/react/styles/themes-bridge.css +26 -0
  1015. package/dist/react/styles/ui-vars.css +20 -0
  1016. package/dist/react/suggestion-renderer.d.ts +12 -0
  1017. package/dist/react/suggestion-renderer.js +90 -0
  1018. package/dist/react/suggestion-renderer.js.map +1 -0
  1019. package/dist/react/ui/alert.js +38 -0
  1020. package/dist/react/ui/alert.js.map +1 -0
  1021. package/dist/react/ui/avatar.js +24 -0
  1022. package/dist/react/ui/avatar.js.map +1 -0
  1023. package/dist/react/ui/badge.js +30 -0
  1024. package/dist/react/ui/badge.js.map +1 -0
  1025. package/dist/react/ui/button.js +50 -0
  1026. package/dist/react/ui/button.js.map +1 -0
  1027. package/dist/react/ui/collapsible.js +19 -0
  1028. package/dist/react/ui/collapsible.js.map +1 -0
  1029. package/dist/react/ui/command.js +47 -0
  1030. package/dist/react/ui/command.js.map +1 -0
  1031. package/dist/react/ui/dialog.js +88 -0
  1032. package/dist/react/ui/dialog.js.map +1 -0
  1033. package/dist/react/ui/dropdown-menu.js +70 -0
  1034. package/dist/react/ui/dropdown-menu.js.map +1 -0
  1035. package/dist/react/ui/input-group.js +57 -0
  1036. package/dist/react/ui/input-group.js.map +1 -0
  1037. package/dist/react/ui/input.js +16 -0
  1038. package/dist/react/ui/input.js.map +1 -0
  1039. package/dist/react/ui/label.js +17 -0
  1040. package/dist/react/ui/label.js.map +1 -0
  1041. package/dist/react/ui/popover.js +36 -0
  1042. package/dist/react/ui/popover.js.map +1 -0
  1043. package/dist/react/ui/radio-group.js +29 -0
  1044. package/dist/react/ui/radio-group.js.map +1 -0
  1045. package/dist/react/ui/select.js +88 -0
  1046. package/dist/react/ui/select.js.map +1 -0
  1047. package/dist/react/ui/separator.js +18 -0
  1048. package/dist/react/ui/separator.js.map +1 -0
  1049. package/dist/react/ui/switch.js +21 -0
  1050. package/dist/react/ui/switch.js.map +1 -0
  1051. package/dist/react/ui/table.js +44 -0
  1052. package/dist/react/ui/table.js.map +1 -0
  1053. package/dist/react/ui/tabs.js +41 -0
  1054. package/dist/react/ui/tabs.js.map +1 -0
  1055. package/dist/react/ui/textarea.js +15 -0
  1056. package/dist/react/ui/textarea.js.map +1 -0
  1057. package/dist/react/ui/tooltip.js +37 -0
  1058. package/dist/react/ui/tooltip.js.map +1 -0
  1059. package/dist/react/ui/utils.d.ts +5 -0
  1060. package/dist/react/ui/utils.js +10 -0
  1061. package/dist/react/ui/utils.js.map +1 -0
  1062. package/dist/react/use-editor-i18n.d.ts +12 -0
  1063. package/dist/react/use-editor-i18n.js +17 -0
  1064. package/dist/react/use-editor-i18n.js.map +1 -0
  1065. package/dist/react/use-notifier.d.ts +11 -0
  1066. package/dist/react/use-notifier.js +25 -0
  1067. package/dist/react/use-notifier.js.map +1 -0
  1068. package/dist/react/use-store.d.ts +6 -0
  1069. package/dist/react/use-store.js +10 -0
  1070. package/dist/react/use-store.js.map +1 -0
  1071. package/dist/schema/advanced.d.ts +14 -0
  1072. package/dist/schema/advanced.js +43 -0
  1073. package/dist/schema/advanced.js.map +1 -0
  1074. package/dist/schema/simple.d.ts +10 -0
  1075. package/dist/schema/simple.js +13 -0
  1076. package/dist/schema/simple.js.map +1 -0
  1077. package/dist/server/adapter.d.ts +101 -0
  1078. package/dist/server/ai.d.ts +101 -0
  1079. package/dist/server/ai.js +105 -0
  1080. package/dist/server/ai.js.map +1 -0
  1081. package/dist/server/collab-state.d.ts +16 -0
  1082. package/dist/server/collab-state.js +43 -0
  1083. package/dist/server/collab-state.js.map +1 -0
  1084. package/dist/server/collab.d.ts +68 -0
  1085. package/dist/server/collab.js +187 -0
  1086. package/dist/server/collab.js.map +1 -0
  1087. package/dist/server/errors.d.ts +17 -0
  1088. package/dist/server/errors.js +26 -0
  1089. package/dist/server/errors.js.map +1 -0
  1090. package/dist/server/handlers/ai.d.ts +223 -0
  1091. package/dist/server/handlers/ai.js +422 -0
  1092. package/dist/server/handlers/ai.js.map +1 -0
  1093. package/dist/server/handlers/context.d.ts +64 -0
  1094. package/dist/server/handlers/context.js +85 -0
  1095. package/dist/server/handlers/context.js.map +1 -0
  1096. package/dist/server/handlers/editor.d.ts +107 -0
  1097. package/dist/server/handlers/editor.js +241 -0
  1098. package/dist/server/handlers/editor.js.map +1 -0
  1099. package/dist/server/handlers/index.d.ts +4 -0
  1100. package/dist/server/handlers/index.js +4 -0
  1101. package/dist/server/host.d.ts +61 -0
  1102. package/dist/server/host.js +105 -0
  1103. package/dist/server/host.js.map +1 -0
  1104. package/dist/server/index.d.ts +8 -0
  1105. package/dist/server/index.js +7 -0
  1106. package/dist/server/node.d.ts +24 -0
  1107. package/dist/server/node.js +54 -0
  1108. package/dist/server/node.js.map +1 -0
  1109. package/dist/server/rate-limit.d.ts +22 -0
  1110. package/dist/server/rate-limit.js +34 -0
  1111. package/dist/server/rate-limit.js.map +1 -0
  1112. package/dist/settings/index.d.ts +110 -0
  1113. package/dist/settings/index.js +114 -0
  1114. package/dist/settings/index.js.map +1 -0
  1115. package/dist/settings/values.d.ts +35 -0
  1116. package/dist/settings/values.js +41 -0
  1117. package/dist/settings/values.js.map +1 -0
  1118. package/dist/styles/ai.css +77 -0
  1119. package/dist/styles/authors.css +18 -0
  1120. package/dist/styles/block-ui.css +16 -0
  1121. package/dist/styles/collab.css +26 -0
  1122. package/dist/styles/comments.css +11 -0
  1123. package/dist/styles/composites.css +56 -0
  1124. package/dist/styles/core.css +99 -0
  1125. package/dist/styles/media.css +8 -0
  1126. package/dist/styles/mentions.css +24 -0
  1127. package/dist/styles/tables.css +25 -0
  1128. package/dist/styles/themes.css +622 -0
  1129. package/dist/styles/tokens.css +29 -0
  1130. package/dist/styles/transcript.css +13 -0
  1131. package/dist/tree/DocumentTree.js +595 -0
  1132. package/dist/tree/DocumentTree.js.map +1 -0
  1133. package/dist/tree/DocumentTree.vue.d.ts +36 -0
  1134. package/dist/tree/folder-paths.js +11 -0
  1135. package/dist/tree/folder-paths.js.map +1 -0
  1136. package/dist/tree/index.d.ts +4 -0
  1137. package/dist/tree/index.js +1 -0
  1138. package/dist/tree/react/DocumentTree.d.ts +24 -0
  1139. package/dist/tree/react/DocumentTree.js +579 -0
  1140. package/dist/tree/react/DocumentTree.js.map +1 -0
  1141. package/dist/tree/react/use-tree-view.d.ts +14 -0
  1142. package/dist/tree/react/use-tree-view.js +21 -0
  1143. package/dist/tree/react/use-tree-view.js.map +1 -0
  1144. package/dist/tree/react.d.ts +3 -0
  1145. package/dist/tree/react.js +3 -0
  1146. package/dist/tree/store.d.ts +45 -0
  1147. package/dist/tree/tree-items.js +221 -0
  1148. package/dist/tree/tree-items.js.map +1 -0
  1149. package/dist/tree/tree-view-model.d.ts +29 -0
  1150. package/dist/tree/tree-view-model.js +71 -0
  1151. package/dist/tree/tree-view-model.js.map +1 -0
  1152. package/dist/tree/tree-view.d.ts +15 -0
  1153. package/dist/tree/tree-view.js +21 -0
  1154. package/dist/tree/tree-view.js.map +1 -0
  1155. package/dist/tree/types.d.ts +23 -0
  1156. package/dist/tree/types.js +0 -0
  1157. package/dist/tree/vue.d.ts +3 -0
  1158. package/dist/tree/vue.js +3 -0
  1159. package/dist/vue/AdvancedEditor.js +100 -0
  1160. package/dist/vue/AdvancedEditor.js.map +1 -0
  1161. package/dist/vue/AdvancedEditor.vue.d.ts +71 -0
  1162. package/dist/vue/SimpleEditor.js +61 -0
  1163. package/dist/vue/SimpleEditor.js.map +1 -0
  1164. package/dist/vue/SimpleEditor.vue.d.ts +36 -0
  1165. package/dist/vue/advanced-preset.d.ts +13 -0
  1166. package/dist/vue/advanced-preset.js +33 -0
  1167. package/dist/vue/advanced-preset.js.map +1 -0
  1168. package/dist/vue/advanced.d.ts +3 -0
  1169. package/dist/vue/advanced.js +3 -0
  1170. package/dist/vue/components/EditorSettingsModal.js +69 -0
  1171. package/dist/vue/components/EditorSettingsModal.js.map +1 -0
  1172. package/dist/vue/components/EditorSettingsModal.vue.d.ts +24 -0
  1173. package/dist/vue/components/EditorSettingsPanel.js +58 -0
  1174. package/dist/vue/components/EditorSettingsPanel.js.map +1 -0
  1175. package/dist/vue/components/EditorSettingsPanel.vue.d.ts +29 -0
  1176. package/dist/vue/components/EditorSurface.js +229 -0
  1177. package/dist/vue/components/EditorSurface.js.map +1 -0
  1178. package/dist/vue/components/EditorSurface.vue.d.ts +68 -0
  1179. package/dist/vue/components/EditorToolbar.js +189 -0
  1180. package/dist/vue/components/EditorToolbar.js.map +1 -0
  1181. package/dist/vue/components/EditorToolbar.vue.d.ts +17 -0
  1182. package/dist/vue/components/SettingsSection.js +26 -0
  1183. package/dist/vue/components/SettingsSection.js.map +1 -0
  1184. package/dist/vue/components/SettingsSection.vue.d.ts +21 -0
  1185. package/dist/vue/components/SuggestionList.js +91 -0
  1186. package/dist/vue/components/SuggestionList.js.map +1 -0
  1187. package/dist/vue/components/SuggestionList.vue.d.ts +19 -0
  1188. package/dist/vue/context.d.ts +24 -0
  1189. package/dist/vue/context.js +41 -0
  1190. package/dist/vue/context.js.map +1 -0
  1191. package/dist/vue/index.d.ts +26 -0
  1192. package/dist/vue/index.js +16 -0
  1193. package/dist/vue/kit/index.d.ts +3 -0
  1194. package/dist/vue/kit/index.js +1 -0
  1195. package/dist/vue/kit/nuxt-ui.css +24 -0
  1196. package/dist/vue/kit/nuxt-ui.d.ts +31 -0
  1197. package/dist/vue/kit/nuxt-ui.js +101 -0
  1198. package/dist/vue/kit/nuxt-ui.js.map +1 -0
  1199. package/dist/vue/kit/primevue/Alert.js +49 -0
  1200. package/dist/vue/kit/primevue/Alert.js.map +1 -0
  1201. package/dist/vue/kit/primevue/Avatar.js +54 -0
  1202. package/dist/vue/kit/primevue/Avatar.js.map +1 -0
  1203. package/dist/vue/kit/primevue/Badge.js +41 -0
  1204. package/dist/vue/kit/primevue/Badge.js.map +1 -0
  1205. package/dist/vue/kit/primevue/Button.js +112 -0
  1206. package/dist/vue/kit/primevue/Button.js.map +1 -0
  1207. package/dist/vue/kit/primevue/Chip.js +38 -0
  1208. package/dist/vue/kit/primevue/Chip.js.map +1 -0
  1209. package/dist/vue/kit/primevue/DropdownMenu.js +98 -0
  1210. package/dist/vue/kit/primevue/DropdownMenu.js.map +1 -0
  1211. package/dist/vue/kit/primevue/FieldGroup.js +22 -0
  1212. package/dist/vue/kit/primevue/FieldGroup.js.map +1 -0
  1213. package/dist/vue/kit/primevue/FormField.js +56 -0
  1214. package/dist/vue/kit/primevue/FormField.js.map +1 -0
  1215. package/dist/vue/kit/primevue/Icon.js +23 -0
  1216. package/dist/vue/kit/primevue/Icon.js.map +1 -0
  1217. package/dist/vue/kit/primevue/Input.js +96 -0
  1218. package/dist/vue/kit/primevue/Input.js.map +1 -0
  1219. package/dist/vue/kit/primevue/InputMenu.js +122 -0
  1220. package/dist/vue/kit/primevue/InputMenu.js.map +1 -0
  1221. package/dist/vue/kit/primevue/Modal.js +59 -0
  1222. package/dist/vue/kit/primevue/Modal.js.map +1 -0
  1223. package/dist/vue/kit/primevue/Popover.js +135 -0
  1224. package/dist/vue/kit/primevue/Popover.js.map +1 -0
  1225. package/dist/vue/kit/primevue/RadioGroup.js +58 -0
  1226. package/dist/vue/kit/primevue/RadioGroup.js.map +1 -0
  1227. package/dist/vue/kit/primevue/Select.js +68 -0
  1228. package/dist/vue/kit/primevue/Select.js.map +1 -0
  1229. package/dist/vue/kit/primevue/SelectMenu.js +94 -0
  1230. package/dist/vue/kit/primevue/SelectMenu.js.map +1 -0
  1231. package/dist/vue/kit/primevue/Separator.js +16 -0
  1232. package/dist/vue/kit/primevue/Separator.js.map +1 -0
  1233. package/dist/vue/kit/primevue/Switch.js +46 -0
  1234. package/dist/vue/kit/primevue/Switch.js.map +1 -0
  1235. package/dist/vue/kit/primevue/Table.js +54 -0
  1236. package/dist/vue/kit/primevue/Table.js.map +1 -0
  1237. package/dist/vue/kit/primevue/Tabs.js +49 -0
  1238. package/dist/vue/kit/primevue/Tabs.js.map +1 -0
  1239. package/dist/vue/kit/primevue/Textarea.js +55 -0
  1240. package/dist/vue/kit/primevue/Textarea.js.map +1 -0
  1241. package/dist/vue/kit/primevue/Tooltip.js +43 -0
  1242. package/dist/vue/kit/primevue/Tooltip.js.map +1 -0
  1243. package/dist/vue/kit/primevue/Tree.js +75 -0
  1244. package/dist/vue/kit/primevue/Tree.js.map +1 -0
  1245. package/dist/vue/kit/primevue/select-options.js +55 -0
  1246. package/dist/vue/kit/primevue/select-options.js.map +1 -0
  1247. package/dist/vue/kit/primevue/shared.js +49 -0
  1248. package/dist/vue/kit/primevue/shared.js.map +1 -0
  1249. package/dist/vue/kit/primevue/trigger.js +25 -0
  1250. package/dist/vue/kit/primevue/trigger.js.map +1 -0
  1251. package/dist/vue/kit/primevue.css +86 -0
  1252. package/dist/vue/kit/primevue.d.ts +33 -0
  1253. package/dist/vue/kit/primevue.js +96 -0
  1254. package/dist/vue/kit/primevue.js.map +1 -0
  1255. package/dist/vue/kit/types.d.ts +370 -0
  1256. package/dist/vue/kit/use-ui-kit.d.ts +12 -0
  1257. package/dist/vue/kit/use-ui-kit.js +17 -0
  1258. package/dist/vue/kit/use-ui-kit.js.map +1 -0
  1259. package/dist/vue/notify.d.ts +12 -0
  1260. package/dist/vue/notify.js +27 -0
  1261. package/dist/vue/notify.js.map +1 -0
  1262. package/dist/vue/settings.d.ts +16 -0
  1263. package/dist/vue/settings.js +64 -0
  1264. package/dist/vue/settings.js.map +1 -0
  1265. package/dist/vue/simple.d.ts +2 -0
  1266. package/dist/vue/simple.js +1 -0
  1267. package/dist/vue/store-ref.d.ts +20 -0
  1268. package/dist/vue/store-ref.js +59 -0
  1269. package/dist/vue/store-ref.js.map +1 -0
  1270. package/dist/vue/suggestion-renderer.d.ts +6 -0
  1271. package/dist/vue/suggestion-renderer.js +20 -0
  1272. package/dist/vue/suggestion-renderer.js.map +1 -0
  1273. package/dist/vue/use-editor-i18n.d.ts +12 -0
  1274. package/dist/vue/use-editor-i18n.js +17 -0
  1275. package/dist/vue/use-editor-i18n.js.map +1 -0
  1276. package/dist/vue/use-editor-ui.d.ts +15 -0
  1277. package/dist/vue/use-editor-ui.js +23 -0
  1278. package/dist/vue/use-editor-ui.js.map +1 -0
  1279. package/dist/vue/use-notifier.d.ts +11 -0
  1280. package/dist/vue/use-notifier.js +19 -0
  1281. package/dist/vue/use-notifier.js.map +1 -0
  1282. package/docs/extending.md +538 -0
  1283. package/docs/integration.md +537 -0
  1284. package/docs/llms.md +375 -0
  1285. package/docs/usage.md +1071 -0
  1286. package/package.json +634 -4
package/docs/usage.md ADDED
@@ -0,0 +1,1071 @@
1
+ # Usage guide
2
+
3
+ Per-feature sections follow the order in which features entered the package. Every symbol below is exported from the named entry (see the [README](../README.md#public-exports)); every snippet uses public package imports only. Each feature has a framework-free entry (`@ecsplico/editor/<x>`: `<x>FeatureCore(ui)`, models), a Vue entry (`/<x>/vue`) and a React entry (`/<x>/react`); the snippets below show the Vue binding, the React binding has the same names ([integration guide §1](./integration.md#per-feature-entries-framework-free-vue-react)). The Vue examples need `<UApp>`, the React ones `EditorContextProvider` and a mounted `Toaster` ([Advanced editor in React](#advanced-editor-in-react)).
4
+
5
+ - [Simple editor in plain Vue](#simple-editor-in-plain-vue)
6
+ - [Simple editor in React](#simple-editor-in-react)
7
+ - [Advanced editor in React](#advanced-editor-in-react)
8
+ - [Media, mentions and transcript features](#media-mentions-and-transcript-features)
9
+ - [Authors and comments](#authors-and-comments)
10
+ - [Themes (Obsidian)](#themes-obsidian)
11
+ - [Composites and the settings UI](#composites-and-the-settings-ui)
12
+ - [File import into composites](#file-import-into-composites)
13
+ - [AI on a core editor](#ai-on-a-core-editor)
14
+ - [AI integrations: composite AI and transcript AI](#ai-integrations-composite-ai-and-transcript-ai)
15
+ - [Reference: AI surfaces, agent operations and composite fences](#reference-ai-surfaces-agent-operations-and-composite-fences)
16
+ - [File browser](#file-browser)
17
+ - [Advanced Nuxt example](#advanced-nuxt-example)
18
+
19
+ ---
20
+
21
+ ## Simple editor in plain Vue
22
+
23
+ `SimpleEditor` is the core feature alone: bold/italic/underline/strike/code/link, headings, lists, quotes, code blocks, horizontal rule, undo/redo. No backend requests, no Yjs, no feature code. The runnable reference is [`examples/vue-simple`](../../../examples/vue-simple); start it with `bun run --cwd examples/vue-simple dev` (port 3040). Its sample picker loads five demo documents — „Start“, „Formatierung“ (marks, lists, quote, code block, rule), „Gliederung“ (three outline levels), „Hinweise“ (the own „Hinweis“ block) and „Ungültig (Baustein-JSON)“ (advanced JSON, rejected through `contentError`) — beside the toolbar and outline, with the JSON and Markdown of the document next to the editor. The `/hinweis` entry and the callout behind it are an own block added through the `extensions` and `slashCommands` props; the [extending guide](./extending.md) shows how, and how to go further with a full feature.
24
+
25
+ ### Setup
26
+
27
+ Starting a new app from nothing? The files below plus these three are all that [`examples/vue-simple`](../../../examples/vue-simple) has:
28
+
29
+ - `package.json` dependencies: `@ecsplico/editor` (`workspace:*` inside this repository), `vue`, `@nuxt/ui`, `tailwindcss`, `@iconify-json/lucide`, `@tiptap/core`, `@tiptap/pm`, `@tiptap/vue-3`, `yjs`, `@hocuspocus/provider`; dev dependencies `vite`, `@vitejs/plugin-vue`, `typescript`, `vue-tsc`; scripts `dev` (`vite`), `build` (`vite build`), `typecheck` (`vue-tsc --noEmit -p tsconfig.json`). Use the exact version ranges of the example's [`package.json`](../../../examples/vue-simple/package.json).
30
+ - `index.html` with `<div id="app"></div>` and `<script type="module" src="/src/main.ts"></script>`.
31
+ - `tsconfig.json` with `moduleResolution: "Bundler"`, `types: ["vite/client"]`, `strict`, `verbatimModuleSyntax` and `include: ["src/**/*.ts", "src/**/*.vue", "vite.config.ts"]` (see the example's [`tsconfig.json`](../../../examples/vue-simple/tsconfig.json)).
32
+
33
+ Optional: Nuxt UI resolves `i-lucide-*` icons at build time, but a workspace library lies outside the Vite root it scans, so without the extra config the page fetches the toolbar's icons from the Iconify API at runtime. The example's [`vite.config.ts`](../../../examples/vue-simple/vite.config.ts) collects the library's icon names into `ui({ icon: { clientBundle: { scan: true, icons } } })` to bundle them.
34
+
35
+ `vite.config.ts` — Nuxt UI's Vite plugin without a router:
36
+
37
+ ```ts
38
+ import ui from '@nuxt/ui/vite'
39
+ import vue from '@vitejs/plugin-vue'
40
+ import { defineConfig } from 'vite'
41
+
42
+ export default defineConfig({ plugins: [vue(), ui({ router: false })] })
43
+ ```
44
+
45
+ `main.ts` — Nuxt UI and the editor context. `createEditor()` (from the Vue layer, `@ecsplico/editor/vue`) owns the settings of this app and is required by every editor component:
46
+
47
+ ```ts
48
+ import ui from '@nuxt/ui/vue-plugin'
49
+ import { createEditor } from '@ecsplico/editor/vue'
50
+ import { createApp } from 'vue'
51
+ import App from './App.vue'
52
+ import './style.css'
53
+
54
+ createApp(App).use(ui).use(createEditor()).mount('#app')
55
+ ```
56
+
57
+ `createEditor()` is local-only by default: the settings live in `localStorage` (`tiptap-editor:settings`), the settings endpoints are never called. `createEditor({ settings: { remote: true } })` additionally loads and saves them through `GET`/`PUT /api/editor/settings`; `createEditor({ fetch })` swaps the HTTP client of all features. `createEditor({ locale: 'en' })` switches every string the editor renders to English (default `'de'`), `createEditor({ messages: { 'toolbar.bold': 'Strong' } })` overrides single messages; see [Language](./llms.md#language). Components read `useEditorI18n()` (`{ t, locale }`); the Nuxt module takes the same two options as `tiptapEditor: { locale, messages }`.
58
+
59
+ `createEditor()` is a thin Vue plugin. The headless core (`@ecsplico/editor/core`) imports no Vue: `createEditorContext(options)` returns the framework-free context with the same options, whose `settings` is a store (`get()`, `set(value)`, `update(recipe)`, `subscribe(listener)`) and whose `ready` and `synced` are read-only stores. `createEditor()` creates that context, provides it to the app and disposes it on unmount; Vue components read the settings through `useEditorSettings()` (reactive refs) from `@ecsplico/editor/vue`.
60
+
61
+ `style.css` — Tailwind, Nuxt UI, then the core CSS, and let Tailwind scan the library:
62
+
63
+ ```css
64
+ @import "tailwindcss";
65
+ @import "@nuxt/ui";
66
+ @import "@ecsplico/editor/styles/core.css";
67
+ @source "../../../packages/editor/src";
68
+ ```
69
+
70
+ `App.vue` wraps everything in `<UApp>` (overlays, tooltips, toasts).
71
+
72
+ ### Mount the editor
73
+
74
+ ```vue
75
+ <script setup lang="ts">
76
+ import { ref, shallowRef } from 'vue'
77
+ import type { Editor, JSONContent } from '@tiptap/core'
78
+ import { SimpleEditor } from '@ecsplico/editor/vue/simple'
79
+ import { EditorToolbar } from '@ecsplico/editor/vue'
80
+ import { EditorOutline } from '@ecsplico/editor/outline/vue'
81
+
82
+ const editor = shallowRef<Editor>()
83
+ const content = ref<JSONContent>({ type: 'doc', content: [{ type: 'paragraph' }] })
84
+ </script>
85
+
86
+ <template>
87
+ <UApp>
88
+ <EditorToolbar :editor="editor" />
89
+ <SimpleEditor v-model="content" @ready="editor = $event" />
90
+ <EditorOutline :editor="editor" />
91
+ </UApp>
92
+ </template>
93
+ ```
94
+
95
+ ### Props and events of `SimpleEditor`
96
+
97
+ | Name | Kind | Meaning |
98
+ |---|---|---|
99
+ | `modelValue` / `update:modelValue` | prop / event | TipTap JSON document (`v-model`). Emitted after every user edit. |
100
+ | `editable` | prop, default `true` | Read-only mode when `false`; reactive. |
101
+ | `metadata` | prop | Host lists (`EditorMetadata`); reactive. The simple editor has no built-in lists. |
102
+ | `abilities` | prop | `Partial<AbilitiesOptions>`, deep-merged into the ability defaults; read once at setup. |
103
+ | `extensions` | prop | Extra TipTap extensions added after the built-in ones; read once at setup. An own block goes here, see the [extending guide](./extending.md#path-a-extensions-and-slashcommands). |
104
+ | `slashCommands` | prop | `(defaults: SlashCommandItem[]) => SlashCommandItem[]`; absent = no `/` menu. Read once at setup. |
105
+ | `ready` | event | `(editor: Editor)` once the instance exists. |
106
+ | `metadataCreate` | event | `(list: string, item: MetadataItem)` when the user creates a metadata item. |
107
+ | `contentError` | event | `(error: Error)` when JSON does not fit the simple schema. |
108
+ | `editor` | exposed | `ref` of the component exposes the live `Editor` (`simple.value?.editor`). |
109
+
110
+ `SimpleEditor` has `inheritAttrs: false` and accepts exactly these props. It does not take `blockActions`, `upload`, `documentId`, `collaboration`, `sidebarOpen` or `settingsUi`: those belong to features the simple editor does not contain.
111
+
112
+ ### JSON `v-model`, `ready` and the editor ref
113
+
114
+ The editor owns its document and undo history; `v-model` mirrors it as JSON. Keep the instance from `ready` in a `shallowRef` (a deep `ref` would wrap the TipTap instance in a Vue proxy). Alternatively use a template ref: `const simple = useTemplateRef('simple')` then `simple.value?.editor`.
115
+
116
+ ### External replacement
117
+
118
+ Assigning a new object to the model replaces the document **without an update event** (the editor calls `setContent(json, { emitUpdate: false })`), so `update:modelValue` does not fire and an edit counter does not move. The JSON the editor emitted itself and the host hands back unchanged is recognised and ignored, so the usual `v-model` round trip does not reset the caret or the undo history.
119
+
120
+ ```ts
121
+ content.value = sampleDocument // loads; no echo
122
+ ```
123
+
124
+ ### Rejected content: `contentError`
125
+
126
+ The simple schema is not a lossy view of advanced JSON. Initial and external JSON is checked against the installed schema; a document with unknown node or mark types (for example `{ type: 'doc', content: [{ type: 'composite' }] }`) is rejected: `contentError` fires with the reason and the **last valid document stays** (an empty document when the initial JSON was invalid). Nothing is flattened or silently dropped. The `v-model` value is the host's own state, so move it back when you want it to follow the editor:
127
+
128
+ ```ts
129
+ function onContentError(error: Error) {
130
+ message.value = error.message
131
+ content.value = lastValid // the editor kept it; make the model say so
132
+ }
133
+ ```
134
+
135
+ `examples/vue-simple/src/App.vue` does exactly this for its „Ungültig (Baustein-JSON)“ sample: the alert shows the reason, the model and the picker move back to the previous sample.
136
+
137
+ ### Metadata
138
+
139
+ `metadata` supplies named lists (`EditorMetadata`): a static array or a search function `(q: MetadataQuery) => items | Promise<items>`. The simple editor has no built-in `@`/`#` suggestion sources, so the lists are only read by extensions you add through `extensions` (they search the registry that `metadata` fills); `metadataCreate` reports items created there. Types: `MetadataItem`, `MetadataSource`, `MetadataQuery`, `EditorMetadata` (from `@ecsplico/editor/core`, also type-only from the package root).
140
+
141
+ ### Custom `/` commands via the transformer
142
+
143
+ The simple editor ships no slash commands and no `/` menu. Passing `slashCommands` turns the menu on with the commands you return:
144
+
145
+ ```vue
146
+ <script setup lang="ts">
147
+ import type { SlashCommandItem } from '@ecsplico/editor/core'
148
+
149
+ function slashCommands(defaults: SlashCommandItem[]): SlashCommandItem[] {
150
+ return [
151
+ ...defaults,
152
+ {
153
+ id: 'date',
154
+ label: 'Datum',
155
+ icon: 'i-lucide-calendar',
156
+ run: editor => editor.chain().focus().insertContent(new Date().toLocaleDateString('de-DE')).run(),
157
+ },
158
+ ]
159
+ }
160
+ </script>
161
+
162
+ <template>
163
+ <SimpleEditor v-model="content" :slash-commands="slashCommands" />
164
+ </template>
165
+ ```
166
+
167
+ `id` is what the user types after `/`; the query matches it or the label as a prefix, case-insensitively. `run` is called after the `/query` text is deleted. The playground in `examples/vue-simple/src/App.vue` registers two such commands: `/datum` (the snippet above with the id `datum`) and `/hinweis`, which wraps the paragraph at the caret in a callout with `run: editor => editor.chain().focus().setCallout('info').run()`. Entries without `ability` are listed wherever the `/` menu opens; give a command an `ability` to limit it ([extending guide](./extending.md#slashcommands)).
168
+
169
+ ### Toolbar and outline outside the editor
170
+
171
+ `EditorToolbar` and `EditorOutline` are siblings, not children: they work on exactly the editor you pass, never on a global “active” editor. Two editors on one page therefore need two `ready` handlers and two toolbars.
172
+
173
+ - `EditorToolbar` — props `editor?: Editor`, `preset?: EditorPreset`. Without an editor it renders the groups of `preset` disabled (pass `simpleEditor()` from `@ecsplico/editor/presets/simple` to see the toolbar before the editor is ready); with an editor it uses the preset registered for that instance.
174
+ - `EditorOutline` (`@ecsplico/editor/outline/vue`; the React version, same prop, is `@ecsplico/editor/outline/react`) — prop `editor?: Editor`. Nested headings in a tree; clicking one selects and reveals it and flashes the block (the document is never changed). A missing or destroyed editor renders an empty outline. The same extraction is available as `headingOutline(doc)` and `revealBlockInEditor` from `@ecsplico/editor/outline`.
175
+
176
+ ```vue
177
+ <EditorToolbar :editor="editorA" />
178
+ <SimpleEditor @ready="editorA = $event" />
179
+ <EditorOutline :editor="editorA" />
180
+
181
+ <EditorToolbar :editor="editorB" />
182
+ <SimpleEditor @ready="editorB = $event" />
183
+ <EditorOutline :editor="editorB" />
184
+ ```
185
+
186
+ ### Fixed at mount: remount with `key`
187
+
188
+ The preset (and with it the schema), `abilities`, `extensions` and `slashCommands` are read once when the component is set up. To change any of them, or to switch to a different document identity, remount the editor:
189
+
190
+ ```vue
191
+ <SimpleEditor :key="documentId" v-model="content" @ready="editor = $event" />
192
+ ```
193
+
194
+ `editable`, `metadata` and the model stay reactive without a remount. After a remount a new `ready` event delivers the new instance; the old one is destroyed by its surface, and the toolbar and outline detach from it when you pass the new editor.
195
+
196
+ ### Check the isolation yourself
197
+
198
+ `bun run check:editor-entries` proves the simple bundle contains no feature, AI SDK, Yjs, y-tiptap, Hocuspocus, Nuxt or server modules by inspecting the emitted chunks' module IDs. Fixtures live in `packages/editor/checks/fixtures/`; add a folder and a rule entry in `packages/editor/checks/entries.ts` to prove your own entry.
199
+
200
+ ---
201
+
202
+ ## Simple editor in React
203
+
204
+ The React layer (`@ecsplico/editor/react`, shadcn/ui on Radix and Tailwind 4) renders the same framework-free core as the Vue layer: the simple editor, `EditorToolbar`, the settings panel and modal, and the `@`, `#` and `/` suggestion popups. It uses no Vue, Nuxt UI or Nuxt module. The runnable reference is [`examples/react-simple`](../../../examples/react-simple); start it with `bun run --cwd examples/react-simple dev` (port 3041). It has the same sample picker, read-only toggle, host `/datum` command and JSON and Markdown panes as the Vue example, plus `@` mentions.
205
+
206
+ **More than the simple editor:** every advanced feature has a React binding as well (`./<x>/react`: media, transcript, composites, authors, comments, AI, collaboration, block UI, themes, the outline and the file browser), and `advancedEditor()` / `AdvancedEditor` from `@ecsplico/editor/react/advanced` compose them all; see [Advanced editor in React](#advanced-editor-in-react). `coreFeature()`, `mentionsFeature()`, `simpleEditor()` and the three integrations have no components and work in both layers; a React preset is `defineEditor({ features })` over features from `./<x>/react` and such component-free features.
207
+
208
+ ### Setup
209
+
210
+ - `package.json` dependencies: `@ecsplico/editor` (`workspace:*` inside this repository), `react`, `react-dom`, `@tiptap/core`, `@tiptap/pm`, `@tiptap/react`, `@iconify/react`, `@iconify-json/lucide`, `tailwindcss`, `tw-animate-css`, `yjs`, `@hocuspocus/provider` (the last two as in the [README](../README.md#prerequisites): source-shipped shared types reference them); dev dependencies `vite`, `@tailwindcss/vite`, `@vitejs/plugin-react`, `typescript`, `@types/react`, `@types/react-dom`. Use the exact version ranges of the example's [`package.json`](../../../examples/react-simple/package.json).
211
+ - `index.html` with `<div id="app"></div>` and `<script type="module" src="/src/main.tsx"></script>`.
212
+ - `tsconfig.json` with `jsx: "react-jsx"`, `moduleResolution: "Bundler"`, `strict` and `include: ["src"]` (see the example's [`tsconfig.json`](../../../examples/react-simple/tsconfig.json)).
213
+
214
+ `vite.config.ts`:
215
+
216
+ ```ts
217
+ import tailwindcss from '@tailwindcss/vite'
218
+ import react from '@vitejs/plugin-react'
219
+ import { defineConfig } from 'vite'
220
+
221
+ export default defineConfig({ plugins: [react(), tailwindcss()] })
222
+ ```
223
+
224
+ `index.css` — Tailwind, the animation utilities the shadcn dialog, dropdown and tooltip use, the library sheets, and a `@source` so Tailwind generates the utility classes the library's React sources use:
225
+
226
+ ```css
227
+ @import "tailwindcss";
228
+ @import "tw-animate-css";
229
+ /* ui-vars.css maps the shadcn tokens (--primary, --background, …) to the --ui-* variables the feature sheets read. */
230
+ @import "@ecsplico/editor/react/styles/ui-vars.css";
231
+ @import "@ecsplico/editor/styles/core.css";
232
+ @import "@ecsplico/editor/styles/mentions.css"; /* the sheet of each feature you use */
233
+
234
+ @source "../../../packages/editor/src/react"; /* relative path from this CSS file to the package's src/react */
235
+
236
+ /* The shadcn theme block follows: @custom-variant dark, @theme inline, :root and .dark tokens, @layer base.
237
+ `bunx shadcn@latest init` generates it; examples/react-simple/src/index.css has a complete copy. */
238
+ ```
239
+
240
+ `main.tsx` — icons and the editor context. The toolbar and menus render `i-lucide-*` names through `@iconify/react`; preloading the collection means the page makes no request to the Iconify API. The host owns the context from `createEditorContext()` (`@ecsplico/editor/core`) and every React component of the library reads it from `EditorContextProvider`; the provider never disposes it, call `context.dispose()` yourself when the context outlives your app. `createEditorContext` takes the same options as the Vue `createEditor` (local-only settings by default, `{ settings: { remote: true } }` for `GET`/`PUT /api/editor/settings`, `{ fetch }`):
241
+
242
+ ```tsx
243
+ import lucide from '@iconify-json/lucide/icons.json'
244
+ import { addCollection } from '@iconify/react'
245
+ import { createEditorContext } from '@ecsplico/editor/core'
246
+ import { EditorContextProvider } from '@ecsplico/editor/react'
247
+ import { StrictMode } from 'react'
248
+ import { createRoot } from 'react-dom/client'
249
+ import { App } from './App'
250
+ import './index.css'
251
+
252
+ addCollection(lucide)
253
+ const context = createEditorContext()
254
+
255
+ createRoot(document.getElementById('app')!).render(
256
+ <StrictMode>
257
+ <EditorContextProvider context={context}>
258
+ <App />
259
+ </EditorContextProvider>
260
+ </StrictMode>,
261
+ )
262
+ ```
263
+
264
+ Notices of app-level models (for example the AI model profiles) go through the context's notifier. The simple editor raises none, so `createEditorContext()` is enough here; hosts of features pass `createEditorContext({ notify: reactNotifier })` and mount sonner's `Toaster` once (both from `@ecsplico/editor/react`, see [Advanced editor in React](#advanced-editor-in-react)).
265
+
266
+ ### Mount the editor
267
+
268
+ `EditorSurface` takes a preset; `EditorToolbar` is a sibling that works on the editor you pass it (from `onReady`), never on a global active editor. The toolbar renders the groups of the preset registered for that editor; without an editor it shows `preset` disabled.
269
+
270
+ ```tsx
271
+ import { coreFeature, defineEditor } from '@ecsplico/editor/core'
272
+ import { mentionsFeature } from '@ecsplico/editor/mentions'
273
+ import { EditorSurface, EditorToolbar } from '@ecsplico/editor/react'
274
+ import type { Editor, JSONContent } from '@tiptap/core'
275
+ import { useState } from 'react'
276
+
277
+ // Module constants: the preset, `metadata`, `abilities`, `extensions` and `slashCommands` are read once at mount.
278
+ const preset = defineEditor({ features: [coreFeature(), mentionsFeature()] })
279
+ const metadata = { mentions: [{ id: 'u1', label: 'Ada Lovelace' }, { id: 'u2', label: 'Alan Turing' }] }
280
+
281
+ export function App() {
282
+ const [editor, setEditor] = useState<Editor | null>(null)
283
+ const [content, setContent] = useState<JSONContent>({ type: 'doc', content: [{ type: 'paragraph' }] })
284
+
285
+ return (
286
+ <>
287
+ <EditorToolbar editor={editor ?? undefined} preset={preset} />
288
+ <EditorSurface
289
+ preset={preset}
290
+ metadata={metadata}
291
+ value={content}
292
+ onChange={setContent}
293
+ onReady={setEditor}
294
+ onContentError={error => console.warn(error.message)}
295
+ />
296
+ </>
297
+ )
298
+ }
299
+ ```
300
+
301
+ Typing `@Ad` opens the mention popup with the `mentions` list. A list the host does not supply (here `hashtags`) comes from `/api/editor/tags` through the context's HTTP client, and only once the `#` popup is used. The simple editor itself ships no `/` commands; pass `slashCommands={defaults => [...defaults, ...]}` (a module function) to turn the `/` menu on.
302
+
303
+ ### The one-liner: `SimpleEditor`
304
+
305
+ Without own features, `SimpleEditor` from `@ecsplico/editor/react/simple` mounts the simple preset (core feature only) on an `EditorSurface`. The toolbar takes its groups from the editor it is given; `preset` only renders it disabled while there is no editor yet:
306
+
307
+ ```tsx
308
+ import { EditorToolbar } from '@ecsplico/editor/react'
309
+ import { SimpleEditor } from '@ecsplico/editor/react/simple'
310
+ import type { Editor } from '@tiptap/core'
311
+ import { useState } from 'react'
312
+
313
+ export function App() {
314
+ const [editor, setEditor] = useState<Editor | null>(null)
315
+ return (
316
+ <>
317
+ <EditorToolbar editor={editor ?? undefined} />
318
+ <SimpleEditor onReady={setEditor} />
319
+ </>
320
+ )
321
+ }
322
+ ```
323
+
324
+ ### Props of `EditorSurface` and `SimpleEditor`
325
+
326
+ | Name | Meaning |
327
+ |---|---|
328
+ | `preset` | `EditorSurface` only: `defineEditor({ features })` (a `ReactPreset`); fixed at mount, remount with `key` to change it. `SimpleEditor` has the simple preset built in. |
329
+ | `value` / `onChange` | TipTap JSON document and its edits. A new object replaces the document without an `onChange`; the JSON the editor emitted itself and the host hands back unchanged is ignored, so the round trip keeps caret and undo history. |
330
+ | `editable` | Read-only when `false` (default `true`); reactive. |
331
+ | `metadata` | Host lists (`EditorMetadata`); reactive, pass a constant. |
332
+ | `abilities`, `extensions`, `slashCommands` | As in the Vue props table above; read once at mount. |
333
+ | `onReady` | `(editor: Editor)` once the instance exists and its chrome is registered. |
334
+ | `onMetadataCreate` | `(list: string, item: MetadataItem)` when the user creates a metadata item. |
335
+ | `onContentError` | `(error: Error)` when JSON does not fit the schema; the last valid document stays. |
336
+ | `settingsUi` | `EditorSurface` only: `'internal'` (default) mounts the settings modal inside the surface; `'external'` calls `onSettingsOpen(tab?)` and the host renders `EditorSettingsPanel` or `EditorSettingsModal` (`preset`, `open`, `onOpenChange`, `tab`, `onTabChange`). Not shown when the preset has no settings tab. |
337
+ | ref | `{ editor }` (`SimpleEditor`) and `{ editor, openSettings(tab?) }` (`EditorSurface`, type `EditorSurfaceHandle`). |
338
+
339
+ `EditorSurface` also takes `collaboration`, `blockActions`, `documentId`, `upload`, `sidebarOpen`/`onSidebarOpenChange` and `commentsOpen`/`onCommentsOpenChange` for the features that use them (collaboration, block UI, media, AI, comments); the simple editor does not. A `content-error` and the settings tabs behave as in Vue; features contribute React tab components as `settingsTabs: [{ id, label, component }]`, rendered with `SettingsSection` from the same entry.
340
+
341
+ ### Settings in React
342
+
343
+ `useEditorSettings()` (from `@ecsplico/editor/react`) reads the app's settings from the context: `{ settings, update, set, ready, synced }`, where `settings` is the current `EditorSettings`, `update(draft => { … })` mutates a copy and stores it, and `set(value)` replaces it. `useStore(store)` subscribes a component to any `ReadonlyStore` of the core. Persistence is the same as in Vue: `localStorage` key `tiptap-editor:settings`, optionally synced per account.
344
+
345
+ ---
346
+
347
+ ## Advanced editor in React
348
+
349
+ `AdvancedEditor` from `@ecsplico/editor/react/advanced` is the React counterpart of the Vue `AdvancedEditor`: an `EditorSurface` over `advancedEditor()`, which lists every feature and integration of `ADVANCED_FEATURE_IDS` (`core`, `media`, `transcript`, `composites`, `mentions`, `authors`, `comments`, `ai`, `collab`, `block-ui`, `composite-media`, `composite-ai`, `transcript-ai`, `themes`) with its React binding, in the same schema order as the Vue surface. It uses no Vue, Nuxt UI or Nuxt module. The runnable reference is [`examples/react-advanced`](../../../examples/react-advanced) (`bun run --cwd examples/react-advanced dev`, port 3043): the React counterpart of the Nuxt document page. It reuses the Nuxt app's documents, session and collaboration socket through the Vite proxy (`/api`, `/collab`), so start the Nuxt app first (`bun run dev`, port 3031).
350
+
351
+ **Setup** is the [simple React setup](#setup) plus three things:
352
+
353
+ - **Styles.** Replace the three library imports by one: `@import "@ecsplico/editor/react/styles/advanced.css";` after `tailwindcss` and `tw-animate-css` (it imports `ui-vars.css`, `core.css`, every feature sheet and `themes.css` last, plus the bridge that lets an Obsidian theme restyle the shadcn tokens). Keep the `@source` for the library's `src` and the shadcn theme block; `examples/react-advanced/src/index.css` has a complete copy.
354
+ - **Notices.** Create the context with `createEditorContext({ notify: reactNotifier })` and render sonner's `<Toaster />` once inside the provider (both exported by `@ecsplico/editor/react`). Features send `Notice`s to the surface's notifier (import problems, AI rate limits, comment errors, „Dokument gelöscht“ with undo in the tree); without `<Toaster />` none is visible.
355
+ - **Dependencies.** The simple setup's list plus `sonner` (the `Toaster` and the notice seam), `yjs` and `@hocuspocus/provider` for collaboration; use the exact version ranges of the [example's `package.json`](../../../examples/react-advanced/package.json).
356
+
357
+ `main.tsx`:
358
+
359
+ ```tsx
360
+ import lucide from '@iconify-json/lucide/icons.json'
361
+ import { addCollection } from '@iconify/react'
362
+ import { createEditorContext } from '@ecsplico/editor/core'
363
+ import { EditorContextProvider, reactNotifier, Toaster } from '@ecsplico/editor/react'
364
+ import { StrictMode } from 'react'
365
+ import { createRoot } from 'react-dom/client'
366
+ import { App } from './App'
367
+ import './index.css'
368
+
369
+ addCollection(lucide)
370
+ // `remote: true` syncs the settings with the host's `/api/editor/settings`; drop it for local-only settings.
371
+ const context = createEditorContext({ settings: { remote: true }, notify: reactNotifier })
372
+
373
+ createRoot(document.getElementById('app')!).render(
374
+ <StrictMode>
375
+ <EditorContextProvider context={context}>
376
+ <App />
377
+ <Toaster />
378
+ </EditorContextProvider>
379
+ </StrictMode>,
380
+ )
381
+ ```
382
+
383
+ **The page.** Build the preset once with `advancedEditor()` and pass it to every chrome component beside the editor; the editor mounts its own, equal one. `AdvancedEditor` takes the surface's props except `preset`, plus `compositeKinds` (read once at mount):
384
+
385
+ ```tsx
386
+ import { AiSidebar } from '@ecsplico/editor/ai/react'
387
+ import { CommentsMargin } from '@ecsplico/editor/comments/react'
388
+ import { CompositeInspector } from '@ecsplico/editor/composites/react'
389
+ import type { CollaborationSession } from '@ecsplico/editor/core'
390
+ import { EditorSettingsModal, EditorToolbar } from '@ecsplico/editor/react'
391
+ import { AdvancedEditor, advancedEditor } from '@ecsplico/editor/react/advanced'
392
+ import type { Editor } from '@tiptap/core'
393
+ import { useState } from 'react'
394
+
395
+ export function Page({ id, session }: { id: string, session: CollaborationSession }) {
396
+ const [editor, setEditor] = useState<Editor>()
397
+ const [preset] = useState(() => advancedEditor())
398
+ const [scrollEl, setScrollEl] = useState<HTMLElement | null>(null)
399
+ const [settingsOpen, setSettingsOpen] = useState(false)
400
+ const [sidebarOpen, setSidebarOpen] = useState(false)
401
+ const [commentsOpen, setCommentsOpen] = useState(false)
402
+
403
+ return (
404
+ <>
405
+ <EditorToolbar editor={editor} preset={preset} />
406
+ <main ref={setScrollEl} className="relative flex min-h-0 flex-1 overflow-auto">
407
+ <AdvancedEditor
408
+ key={id}
409
+ collaboration={session}
410
+ documentId={id}
411
+ settingsUi="external"
412
+ sidebarOpen={sidebarOpen}
413
+ onSidebarOpenChange={setSidebarOpen}
414
+ commentsOpen={commentsOpen}
415
+ onCommentsOpenChange={setCommentsOpen}
416
+ onReady={setEditor}
417
+ onSettingsOpen={() => setSettingsOpen(true)}
418
+ />
419
+ <CommentsMargin editor={editor} container={scrollEl} open={commentsOpen} />
420
+ </main>
421
+ <CompositeInspector editor={editor} />
422
+ <AiSidebar editor={editor} open={sidebarOpen} onOpenChange={setSidebarOpen} />
423
+ <EditorSettingsModal preset={preset} open={settingsOpen} onOpenChange={setSettingsOpen} />
424
+ </>
425
+ )
426
+ }
427
+ ```
428
+
429
+ Notes:
430
+
431
+ - **Collaboration.** The `session` is `{ ydoc, provider, user }` (`CollaborationSession` from `@ecsplico/editor/core`): create the `Y.Doc` and the `HocuspocusProvider` in an effect (so StrictMode's double mount leaves exactly one provider), destroy the provider in its cleanup, and key the editor by document so a new document is a new Y.Doc ([integration guide §6](./integration.md#6-collaboration-on-the-client)). Without `collaboration` pass `value`/`onChange` for a standalone editor (the collaboration and comments features then contribute nothing).
432
+ - **Beside the editor.** `EditorToolbar`, `EditorSettingsModal`/`EditorSettingsPanel`, `CommentsMargin`, `CompositeTable`, `CompositeInspector`, `AiSidebar`, `EditorOutline` and `DocumentTree` are components you place; each takes the `editor` from `onReady` (or the `preset`). `useEditorUi(editor)` returns the chrome of a ready editor (`openSettings(tab?)`, `toggleSidebar()`, `toggleComments()`, `commentsOpen`) for your own buttons.
433
+ - **Own features.** Build on the same list: `defineEditor({ features: [...advancedEditor().features, myFeature()] })` for an `EditorSurface`, as in the Vue playground.
434
+ - **The file browser** is [`DocumentTree` from `@ecsplico/editor/tree/react`](#file-browser) over a `DocumentStore`; the example wires it to the host's `/api`.
435
+ - **Backend.** The same as for the Vue advanced editor: `/collab/:id`, the AI endpoints, assets, users/tags and themes of the Nuxt module and a host adapter ([integration guide](./integration.md)). A React app with its own server brings the same routes.
436
+
437
+ ---
438
+
439
+ ## Media, mentions and transcript features
440
+
441
+ Three independent features extend the core editor. Each is one factory you add to `defineEditor({ features })` next to `coreFeature()`; none needs composites, AI or Yjs, and none imports the others (`bun run check:editor-entries` proves it per feature).
442
+
443
+ ```vue
444
+ <script setup lang="ts">
445
+ import { coreFeature, defineEditor } from '@ecsplico/editor/core'
446
+ import { EditorSurface, EditorToolbar } from '@ecsplico/editor/vue'
447
+ import { mediaFeature } from '@ecsplico/editor/media/vue'
448
+ import { mentionsFeature } from '@ecsplico/editor/mentions'
449
+ import { transcriptFeature } from '@ecsplico/editor/transcript/vue'
450
+ import type { Editor } from '@tiptap/vue-3'
451
+ import { shallowRef } from 'vue'
452
+
453
+ const preset = defineEditor({ features: [coreFeature(), mediaFeature(), mentionsFeature(), transcriptFeature()] })
454
+ const editor = shallowRef<Editor>()
455
+ </script>
456
+
457
+ <template>
458
+ <EditorToolbar :editor="editor" />
459
+ <EditorSurface v-model="content" :preset="preset" @ready="editor = $event" />
460
+ </template>
461
+ ```
462
+
463
+ Import the feature CSS after `tailwindcss`, `@nuxt/ui` and `core.css`:
464
+
465
+ ```css
466
+ @import "@ecsplico/editor/styles/media.css";
467
+ @import "@ecsplico/editor/styles/mentions.css";
468
+ @import "@ecsplico/editor/styles/transcript.css";
469
+ ```
470
+
471
+ ### Dependency setup
472
+
473
+ Installing the package brings the feature dependencies; nothing else is required in the consumer beyond the peers listed in the [README](../README.md#prerequisites).
474
+
475
+ | Feature | Entry | Vue-free schema entry | Runtime dependencies it adds | Needs a backend |
476
+ |---|---|---|---|---|
477
+ | Media | `@ecsplico/editor/media` (framework-free), `/media/vue`, `/media/react` | `@ecsplico/editor/media/schema` | `@tiptap/extension-image`, `@tiptap/extension-file-handler` | only for uploads (see below); a `upload` function replaces it |
478
+ | Mentions | `@ecsplico/editor/mentions` | `@ecsplico/editor/mentions/schema` | `@tiptap/extension-mention`, `@tiptap/suggestion` | only for the built-in lists (see below); host lists replace them |
479
+ | Transcript | `@ecsplico/editor/transcript` (framework-free), `/transcript/vue`, `/transcript/react` | `@ecsplico/editor/transcript/schema` | none beyond core | no |
480
+
481
+ The `/schema` entries import no Vue, no node view and no feature factory. A server that validates or seeds documents builds the matching schema from them:
482
+
483
+ ```ts
484
+ import { getSchema } from '@tiptap/core'
485
+ import { coreSchemaExtensions } from '@ecsplico/editor/core/schema'
486
+ import { mediaSchemaExtensions } from '@ecsplico/editor/media/schema'
487
+ import { mentionsSchemaExtensions } from '@ecsplico/editor/mentions/schema'
488
+ import { transcriptSchemaExtensions } from '@ecsplico/editor/transcript/schema'
489
+
490
+ const schema = getSchema([...coreSchemaExtensions(), ...mediaSchemaExtensions(), ...mentionsSchemaExtensions(), ...transcriptSchemaExtensions()])
491
+ ```
492
+
493
+ The schema entries also export the helpers servers need: `MEDIA_TYPES`, `MAX_MEDIA_BYTES`, `mediaKindOf`, `assetUrl`, `assetRefOf`, `ASSET_NAME`, `ASSET_DOCUMENT_ID`, `SCRATCH_DOCUMENT_ID` (media), `mentionsFromText`, `mentionUsersIn`, `tagsFromText`, `collectTagNames`, `normalizeTagName` (mentions), `speakerParagraphsToSegments`, `lastSpeaker`, `speakersIn` (transcript).
494
+
495
+ ### Upload
496
+
497
+ `mediaFeature()` adds image, audio, video and file blocks, drop/paste of files, toolbar buttons (Bild, Audio-Datei, Video, Aufnahme), the matching `/` commands and the audio recorder modal. Every path ends in one function, `editor.storage.media.upload(file): Promise<{ url: string }>`, chosen when the surface is set up:
498
+
499
+ 1. the surface's `upload` prop (`(file: File) => Promise<{ url: string }>`), if given — use it to store files wherever you like (S3, a data URL, …); the returned `url` becomes the block's `src`;
500
+ 2. otherwise `POST /api/editor/assets/<documentId>` with a `multipart/form-data` body (field `file`) through the context's HTTP client; `documentId` is the surface's prop, default `scratch`. The response must be `{ url }`.
501
+
502
+ ```vue
503
+ <EditorSurface :preset="preset" :upload="file => storeSomewhere(file).then(url => ({ url }))" />
504
+ ```
505
+
506
+ Accepted types and size limits are `MEDIA_TYPES` and `MAX_MEDIA_BYTES` (Vue-free, from `@ecsplico/editor/media/schema`): images up to 5 MB (png, jpeg, gif, webp), audio 50 MB, video 200 MB, and any other type up to 50 MB as a downloadable file block. A server endpoint should validate against the same constants; the Nuxt module's `/api/editor/assets/*` routes do. A failed upload shows a toast (`Import fehlgeschlagen`) and inserts nothing.
507
+
508
+ **File import.** Drop, paste, the toolbar and `/` commands all call `editor.storage.media.importFiles(editor, files, pos?)`; `useFileImport()` (from `@ecsplico/editor/media/vue` or `/media/react`; call it in `setup` / while rendering: it binds the notifier of `editor`'s surface when you pass the editor, else the app's: a Nuxt UI toast on Vue and a sonner toast on React unless the host's `notify` says otherwise) returns the same `importFiles` for your own buttons; the framework-free model is `fileImport(notify, t)` from `@ecsplico/editor/media` (`t` is the editor context's translator). Text and Markdown files (`.md`, `.markdown`, `.txt`, at most 2 MB) are read and parsed through the editor's block pipeline (Markdown parse, then `normalize` — mention/tag chips —, `segment` — speaker lines —, `structure`); every other file is uploaded and appears as its media or file block. The resulting blocks pass through the pipeline's `wrapImport` stage, which is the identity unless another feature wraps imports, and are inserted as they are at the drop position (anchored while uploads run, so editing meanwhile does not shift it) or at the selection. Files are imported sequentially; a failed file is reported and skipped.
509
+
510
+ The recorder (`AudioRecorderModal`) is mounted by `EditorSurface` as a feature overlay. In a custom layout without `EditorSurface`, render `<AudioRecorderModal :editor="editor" />` yourself (exported from `@ecsplico/editor/media/vue`, or `/media/react`) and open it with `openAudioRecorder(editor)`. It records through `MediaRecorder` (needs HTTPS or localhost and microphone permission) and imports the result like any file. The recording model itself is framework-free: `createAudioRecorder()` from `@ecsplico/editor/media` (a store with `start`, `stop`, `cancel`, `dispose`).
511
+
512
+ ### Metadata
513
+
514
+ The mentions feature reads two named lists, the transcript feature one; hosts replace any of them through the `metadata` prop (`EditorMetadata`, see [Metadata](#metadata) above). Lists the host does not supply fall back to the feature's built-in source; the fallback is only searched when a suggestion popup is actually used.
515
+
516
+ | List | Used by | Built-in source when the host supplies none |
517
+ |---|---|---|
518
+ | `mentions` | `@` popup, `normalize` stage | `GET /api/editor/users?q=<query>` → `{ id, username, color }[]` (through the context's HTTP client) |
519
+ | `hashtags` | `#` popup | `GET /api/editor/tags?q=<normalized query>` → `string[]` |
520
+ | `speakers` | speaker field of a segment | speakers already in the document (no backend) |
521
+
522
+ A backend-free app passes its own lists and never calls those endpoints:
523
+
524
+ ```vue
525
+ <EditorSurface
526
+ :preset="preset"
527
+ :metadata="{
528
+ mentions: [{ id: 'u1', label: 'anna', color: '#e11d48' }],
529
+ hashtags: q => searchTags(q.query),
530
+ }"
531
+ @metadata-create="(list, item) => persist(list, item)"
532
+ />
533
+ ```
534
+
535
+ Mentions are `@username` chips tinted with the item's `color` (a `#rgb`/`#rrggbb` value; anything else is dropped). Tags are `#tag` chips; the typed name is normalized (`normalizeTagName`: lowercase, spaces → `-`, letters/digits/`-`/`_` only, at most 64 characters), and the popup offers „#name erstellen“ when it is not in the list — picking it emits `metadataCreate('hashtags', { id, label })`.
536
+
537
+ **Pipeline stages.** The mentions feature contributes the `normalize` stage: `@name` / `#tag` in Markdown that is imported from a file or written by AI turns into chips when the name matches a user already in the document or in the `mentions` list, or a tag already in the document. Without the feature installed, those stay plain text. The transcript feature contributes the `segment` stage: a paragraph that starts with a bold `**Name:**` followed by text becomes a speaker segment (a bare `**Aufgaben:**` label stays a paragraph). Both stages run for imported files and for AI results.
538
+
539
+ ### Transcript
540
+
541
+ `transcriptFeature()` adds the speaker segment block: a „Sprecherblock“ toolbar button (Mod-Shift-S), Enter inside a segment splits it keeping the speaker, a speaker field with suggestions from the `speakers` list, the „Sprecherzeile“ entry in the „Umwandeln in …“ menus (paragraphs, headings, lists, quotes and code blocks become segments carrying the last speaker; segments convert back into any core target), and Markdown `**Name:** text` export. A segment holds only inline text: headings, lists, quotes, code blocks, media and composites are revoked inside it.
542
+
543
+ ## Authors and comments
544
+
545
+ Both features belong to the advanced editor (not the simple one) and need a collaborative surface for the user's identity: `:collaboration="{ ydoc, provider, user: { id, name, color } }"` (`AuthorIdentity`). Entries: `@ecsplico/editor/authors/vue` or `/authors/react` (the framework-free `/authors` and `/authors/schema` hold the model), `@ecsplico/editor/comments/vue` or `/comments/react` (framework-free `/comments`, `/comments/schema`); CSS `styles/authors.css` and `styles/comments.css` (after `core.css`; the Nuxt module adds them).
546
+
547
+ ```ts
548
+ import { authorsFeature } from '@ecsplico/editor/authors/vue'
549
+ import { collabFeature } from '@ecsplico/editor/collab/vue'
550
+ import { commentsFeature } from '@ecsplico/editor/comments/vue'
551
+ const preset = defineEditor({ features: [coreFeature(), collabFeature(), authorsFeature(), commentsFeature()] })
552
+ ```
553
+
554
+ On React the same list comes from `/authors/react`, `/collab/react` and `/comments/react`, and `collaboration` is a prop of the same name.
555
+
556
+ **`authorsFeature()`** (no dependency, inert without a user)
557
+
558
+ - Every inserted text run carries an `author` mark `{ id, name, color }` (persisted, invisible, not in Markdown). Text typed inside someone else's run takes the typist's mark; remote edits and Yjs undo/redo keep the original author. `documentAuthors(doc)` lists the authors with their character counts; `currentAuthor(editor)` is the user the editor writes as.
559
+ - The colouring mode is a per-user preference: `editing.authorColors` (off by default) and `editing.authorColorStyle` (`'background'` tint or `'text'` colour). It is set in the „Autorenfarben“ settings tab and in the toolbar's palette button, whose popover also holds a legend of the document's authors (`AuthorColorsPanel`).
560
+
561
+ **`commentsFeature({ model? })`** (requires `collab` unless `model` is given)
562
+
563
+ - `model: (getEditor) => CommentsModel` backs the comments with the host's own data (REST threads, a database) instead of the Y.Doc: the feature then requires no `collab`, the `Comments` plugin reads the user, the thread ids and the ranges from `model.state` (a snapshot with `available`, `canComment`, `user`, `threads`, `ranges`, …), and `useComments(editor)` resolves the model of the editor's surface (`commentsModelOf(ctx)`; the factory runs once for the plugin and once per hook consumer, so share state outside it). `CommentsSnapshot.canComment` gates the selection bubble: the default model says `available` and editable, a host model may allow readers. `CommentsModel`, `CommentsSnapshot`, `CommentsActions`, `DocRange` are exported from `/comments`.
564
+
565
+ - Select text and choose „Kommentar“ in the selection bubble; others reply, anyone resolves or reopens a thread („Erledigt“ threads lose their highlight), authors edit or delete their own comments. A thread whose text was deleted stays as orphan („Textstelle gelöscht“). Threads live in the Y.Doc map `comments` (`createCommentsModel(getEditor)` is the framework-free model; `useComments(editor)` is the thin Vue hook, returning the `CommentsStore`, and the React binding has the same hook), the range is the `comment` mark (the schema mark is always registered, so stored marks round-trip even without the feature's UI).
566
+ - `editing.commentsLayout` (`'margin'` default, `'inline'`) is a per-user setting in the „Kommentare“ settings tab:
567
+ - `margin` („Randspalte“): cards beside the commented text. The host places `CommentsMargin` (props `editor`, `container`, `open`) inside the editor's scroll container (`relative flex`, editor wrapper `min-w-0 flex-1`) and binds `v-model:comments-open` on the surface (React: `commentsOpen`/`onCommentsOpenChange`). The toolbar's „Kommentare“ button and `Mod-Shift-m` toggle it; opening it closes the AI sidebar and vice versa.
568
+ - `inline`: `CommentsPopover` (mounted by the feature as an overlay) opens at the commented text; the toolbar's `CommentsListMenu` lists all threads with the open count. Esc closes the popover.
569
+ - Without collaboration the toolbar button is disabled with an explanation. Switching the layout closes any open thread or draft.
570
+
571
+ ```vue
572
+ <div ref="scrollEl" class="relative flex overflow-auto">
573
+ <div class="min-w-0 flex-1">
574
+ <AdvancedEditor v-model:comments-open="commentsOpen" :collaboration="{ ydoc, provider, user: { id, name, color } }" />
575
+ </div>
576
+ <CommentsMargin :editor="editor" :container="scrollEl" :open="commentsOpen" />
577
+ </div>
578
+ ```
579
+
580
+ React, same layout:
581
+
582
+ ```tsx
583
+ <main ref={setScrollEl} className="relative flex overflow-auto">
584
+ <div className="min-w-0 flex-1">
585
+ <AdvancedEditor commentsOpen={commentsOpen} onCommentsOpenChange={setCommentsOpen} collaboration={{ ydoc, provider, user: { id, name, color } }} />
586
+ </div>
587
+ <CommentsMargin editor={editor} container={scrollEl} open={commentsOpen} />
588
+ </main>
589
+ ```
590
+
591
+ `bun run check:editor-entries` proves the isolation with the `authors` and `comments` fixtures; the marks are part of the server schema snapshot.
592
+
593
+ ## Themes (Obsidian)
594
+
595
+ `themesFeature()` (`@ecsplico/editor/themes/vue` or `/themes/react`, part of `advancedEditor()`; the simple editor stays backend-free) renders an [Obsidian](https://obsidian.md) theme (`theme.css` plus `manifest.json`) on the **whole app**: dashboard, navbars, toolbars, the settings panel, modals and the editor content. `<html>` carries `obsidian-theme theme-dark` or `obsidian-theme theme-light` according to `html.dark` (Nuxt colour mode, or your own `dark` class on React; without a `dark`/`light` class the system preference), observed live, so toggling the mode switches the palette without a reload. Every Nuxt UI `--ui-*` token (on React the shadcn tokens, through `themes-bridge.css` in `advanced.css`) is bridged to the theme's variables, so components follow without per-component CSS; the chrome uses the theme's interface font (the page's root font size stays untouched) and the editor content its text font. „Standard“ is the plain Nuxt UI (or shadcn) look. Builtin themes: Nord, Things, Minimal, Catppuccin, Border, Blue Topaz and Tokyo Night. The runtime starts with the first mounted editor surface (the feature's `setup` hook), so a page without a surface is not themed.
596
+
597
+ **Choosing a theme.** `themesFeature()` contributes the settings tab **„Darstellung“** (`SettingsAppearanceTab`, id `appearance`, shown by `EditorSettingsPanel`/`EditorSettingsModal` like any tab; the simple editor has none). Its select lists „Standard“ plus every theme of `GET /api/editor/themes`, labelled `name – author`; the choice is `appearance.theme`, applies to the whole app at once and persists with the other settings (per browser, and to the server when remote settings are on). The app's navbars carry a `<UColorModeButton />` that toggles light/dark for the app and the theme together. Programmatically:
598
+
599
+ ```ts
600
+ // `PUT /api/editor/settings` with `appearance: { theme: 'nord' }`, or in the browser console (settings are stored per browser until synced):
601
+ const key = 'tiptap-editor:settings'
602
+ localStorage.setItem(key, JSON.stringify({ ...JSON.parse(localStorage.getItem(key) ?? '{}'), appearance: { theme: 'nord' } }))
603
+ // `null` (the default) is the standard look.
604
+ ```
605
+
606
+ Setup: `@ecsplico/editor/styles/themes.css` after `core.css` and the feature sheets (the Nuxt module adds it last; React hosts of the advanced editor get it, with the shadcn bridge, from `react/styles/advanced.css`), and an adapter with `listThemes`/`loadTheme` ([integration guide §3](./integration.md#3-editoradapter-auth-and-storage)) serving `GET /api/editor/themes` and `GET /api/editor/themes/:id`. A custom list of features uses `themesFeature()` from `/themes/vue` or `/themes/react` (call the Vue one during component setup); the framework-free `themesFeatureCore(ui)`, `startObsidianTheme(ctx)` (the runtime; returns its release), `scopeThemeCss(css, scope?)` and the Vue-free `ThemeRecord`/`THEME_ID`/`themeManifestSchema` (`/themes/schema`) are exported for hosts.
607
+
608
+ **How it works.** The runtime (one per app, shared by every editor on the page; started by the feature's `setup` hook and reference-counted, so it starts as soon as the first surface mounts; on Vue it then lives until the Vue app unmounts, also on pages without an editor, on React until the last surface that started it unmounts) reads `appearance.theme`, fetches the stylesheet through the app's `fetch` and scopes it with `scopeThemeCss`: `body`, `:root` and `html` rules become the scope element (`body.theme-dark` → `.obsidian-theme.theme-dark`), `.theme-dark`/`.theme-light` rules the scope element with that class, Obsidian's content-root classes (`.markdown-rendered`, `.markdown-preview-view`, `.markdown-source-view`, `.cm-s-obsidian`, …) `.tiptap`, everything else becomes a descendant selector; every emitted selector starts with `.obsidian-theme`. The scope element is `<html>`: the runtime toggles `obsidian-theme` and `theme-dark`/`theme-light` on `document.documentElement` (client side only; the first server-rendered paint shows the standard look). The result sits in one `<style data-tiptap-theme>` in `<head>` (removed again for `null`) and is cached per theme for the session. `@font-face`, `@keyframes`, `@media`, `@supports` and `@layer` survive.
609
+
610
+ **What is mapped.** `styles/themes.css` defines Obsidian's variables with defaults (a theme overrides them) and maps them onto the whole app and the editor content:
611
+
612
+ | Element | Obsidian variables |
613
+ |---|---|
614
+ | Root `<html>` (background, text colour, interface font; no font size) | `--background-primary`, `--text-normal`, `--font-interface` |
615
+ | `.tiptap` (text font and size, line height, caret) and `::selection` | `--font-text`, `--font-text-size`, `--line-height-normal`, `--caret-color`, `--text-selection` |
616
+ | `p`, `h1`–`h6` | `--p-spacing`, `--hN-size`/`-weight`/`-color`/`-line-height`/`-font`/`-variant`/`-style`, `--heading-spacing` |
617
+ | `strong`, `em`, `s`, `u` | `--font-weight` + `--bold-modifier`, `--bold-color`, `--italic-color` |
618
+ | Links | `--link-color`, `--link-color-hover`, `--link-external-color`, `--link-decoration` |
619
+ | `code`, `pre` | `--code-background`, `--code-normal`, `--code-size`, `--code-radius`, `--font-monospace`, `--code-white-space` |
620
+ | `blockquote`, `hr` | `--blockquote-*`, `--hr-color`, `--hr-thickness` |
621
+ | Lists, placeholder | `--list-indent`, `--list-marker-color`, `--list-spacing`, `--text-faint` |
622
+ | `.tag`, `.mention` | `--tag-*`, `--text-accent`, `--background-modifier-hover` |
623
+ | `.composite`, `.media-block`, `img` | `--embed-border-start`, `--embed-background`, `--radius-s` |
624
+ | Every package feature stylesheet (authors, comments, transcript, composites, media, mentions) | follows through the Nuxt UI tokens, which the bridge points at Obsidian's: `--ui-text` → `--text-normal`, `--ui-bg` → `--background-primary`, `--ui-border` → `--background-modifier-border`, `--ui-primary` → `--interactive-accent`, `--ui-warning`/`--ui-error`/`--ui-success`/`--ui-info` → `--color-orange`/`red`/`green`/`blue`, `--font-mono` → `--font-monospace` |
625
+ | Host callout (`.callout[data-tone]`, [`app/editor/callout/callout.css`](../../../app/editor/callout/callout.css)) | `--callout-info`, `--callout-warning`, … as RGB triplets (`rgba(var(--callout-color), 0.1)` background), `--callout-radius`, `--callout-padding`; the host owns this mapping for its own blocks |
626
+
627
+ Cascade: the bridge is part of `@layer components`; the scoped theme is injected **unlayered**, so its rules beat the bridge and `core.css`. Selecting „Standard“ (`null`) removes the style element and the three classes from `<html>`; the app then looks as before.
628
+
629
+ **Add a builtin theme (host).** In this repository a folder `server/themes/<id>/` with `manifest.json` (`name`, optional `version`, `author`, `authorUrl`; the folder name must match the `THEME_ID` pattern `^[a-z0-9][a-z0-9-]{0,63}$`), `theme.css` and the theme's `LICENSE` is a builtin: `server/utils/themes.ts` reads the folder once per process and `GET /api/editor/themes` marks it `builtin: true`. Seeded builtins (all MIT): Nord (`nord`), Things (`things`), Minimal (`minimal`), Catppuccin (`catppuccin`), Border (`border`), Blue Topaz (`blue-topaz`) and Tokyo Night (`tokyo-night`). Another host implements the adapter methods (`listThemes`/`loadTheme`, plus `storeTheme`/`removeTheme` for uploads) over its own storage.
630
+
631
+ **Upload and delete.** The „Darstellung“ tab has a row **„Theme hochladen“** with two file inputs, `theme.css` (required) and `manifest.json` (optional), and an upload button; below it the **Theme-Bibliothek** lists every theme with name, version and author. A successful upload joins the library for every user, is selected for the uploader and shows the toast „Theme „X“ hochgeladen“. Uploads carry a trash button (`DELETE`; deleting the selected theme resets `appearance.theme` to `null`, „Standard“); builtins carry the badge „mitgeliefert“ and no delete button. Failures appear as toasts with the server's German message.
632
+
633
+ | Route | Request | Answers |
634
+ |---|---|---|
635
+ | `POST /api/editor/themes` | multipart: `css` (File, required), `manifest` (File, optional), `name` (string, optional) | `201` + `ThemeRecord` (`builtin: false`); `400` missing `css`, „Ungültiges manifest.json“, or CSS that is not UTF-8 text or starts with markup (`<script`, `<?xml` within the first 512 bytes); `413` „Theme ist größer als 2 MB“ |
636
+ | `DELETE /api/editor/themes/:id` | – | `204`; `404` unknown id; `403` „Mitgelieferte Themes können nicht gelöscht werden“ for builtins; `400` for an id that does not fit `THEME_ID` |
637
+
638
+ Both routes authenticate like the read routes and answer `501` when the adapter lacks `storeTheme`/`removeTheme`. **Limits:** `theme.css` up to 2 MB (`MAX_THEME_BYTES`); the manifest is parsed with `themeManifestSchema` (`name` 1–120 characters; `version`, `author`, `authorUrl` optional). The name comes from the manifest, else the `name` field, else the CSS file's base name; the id is its slug (`[^a-z0-9]+` → `-`, at most 64 characters), made unique with `-2`, `-3` … against the library. In this repository uploads persist under `.data/themes/<id>/` (`manifest.json`, `theme.css`; Nitro storage mount `themes`) and survive restarts; the adapter methods `storeTheme(record, css)` and `removeTheme(id)` are the seam for another host's storage ([integration guide §3](./integration.md#3-editoradapter-auth-and-storage)).
639
+
640
+ **Failures.** A theme that cannot be loaded (deleted, network error, adapter without theme methods) falls back to the standard look for the session with a console warning (`tiptap-editor: Theme „<id>“ konnte nicht geladen werden …`); the stored setting is not changed. An id that does not fit `THEME_ID` is rejected by the settings schema.
641
+
642
+ **Limits.**
643
+
644
+ - Rules for Obsidian's own chrome (`.workspace*`, `.nav-*`, `.cm-line`, the sidebar, tabs, status bar) are scoped like the rest and match nothing in the app; they do no harm. The app chrome follows the theme through the Nuxt UI tokens only, not through an emulation of Obsidian's DOM.
645
+ - Obsidian sets body classes depending on settings and plugins (`is-mobile`, `is-translucent`, …); only `theme-dark`/`theme-light` are set on `<html>`, so rules that need other body classes do not apply (Catppuccin and Minimal fall back to their default palettes).
646
+ - `@import` is dropped (a theme never triggers remote requests); fonts must come as `@font-face` in the file, as the builtins do. Imported stylesheets and remote fonts are lost.
647
+ - Theme CSS is user-supplied and unlayered: a rule like `.obsidian-theme * { display: none }` breaks the app for whoever selected that theme. Themes are chosen per user.
648
+ - A theme's own `body { font-size }` rule lands on `<html>` after scoping and rescales `rem`, as Obsidian applies it to `body`; the bridge itself sets no root font size.
649
+ - Scoping needs `CSSStyleSheet.replaceSync` (Chrome 73+, Firefox 101+, Safari 16.4+). Selectors are rewritten from the browser's serialized `selectorText`, so quotes and spacing are normalised; the meaning is unchanged.
650
+
651
+ `bun run check:editor-entries` proves the isolation with the `themes` fixture.
652
+
653
+ ## Composites and the settings UI
654
+
655
+ `compositeFeature()` adds composites („Bausteine“): structured blocks with a title, description, tags, kind-specific meta fields and a body that may hold more blocks and composites. It needs no AI, media, transcript or Yjs code (`bun run check:editor-entries` proves it with the `composites` fixture), and it adds no dependency beyond the core peers.
656
+
657
+ ```vue
658
+ <script setup lang="ts">
659
+ import { compositeFeature } from '@ecsplico/editor/composites/vue'
660
+ import { coreFeature, defineEditor } from '@ecsplico/editor/core'
661
+ import { EditorSettingsPanel, EditorSurface, EditorToolbar } from '@ecsplico/editor/vue'
662
+ import type { Editor } from '@tiptap/vue-3'
663
+ import { shallowRef } from 'vue'
664
+
665
+ const preset = defineEditor({ features: [coreFeature(), compositeFeature()] })
666
+ const editor = shallowRef<Editor>()
667
+ const tab = shallowRef<string>()
668
+ </script>
669
+
670
+ <template>
671
+ <EditorToolbar :editor="editor" />
672
+ <EditorSurface v-model="content" :preset="preset" settings-ui="external" @ready="editor = $event" @settings-open="tab = $event ?? tab" />
673
+ <EditorSettingsPanel v-model:tab="tab" :preset="preset" />
674
+ </template>
675
+ ```
676
+
677
+ Import the feature CSS after `tailwindcss`, `@nuxt/ui` and `core.css`:
678
+
679
+ ```css
680
+ @import "@ecsplico/editor/styles/composites.css";
681
+ ```
682
+
683
+ On React the feature is `compositeFeature()` from `@ecsplico/editor/composites/react`, with the same components (`CompositeTable`, `CompositeInspector`, `CompositeView`, `CompositeDetails`, `CompositeFieldEditor`, ...) and hooks (`useCompositeRows`, `useCompositeAnchors`); the framework-free `@ecsplico/editor/composites` holds `compositeFeatureCore(ui, options)`, the nodes, conversions, fences and the models behind the hooks (`createCompositeRows`, `observeCompositeAnchors`). `EditorToolbar`, `EditorSurface` and the settings components come from `@ecsplico/editor/react` ([simple React setup](#simple-editor-in-react)).
684
+
685
+ ### What the feature contributes
686
+
687
+ - **Nodes and node views:** `composite` (attributes `kind`, `meta`) with its title, description and tags children. The title, description and tags are *chrome*: they cannot be selected, dragged, deleted or duplicated apart from their composite. Stored documents with the old `metaVisible` attribute still load; the attribute is dropped.
688
+ - **Toolbar group:** insert a composite of the default kind, „In Baustein umwandeln“ (kind menu for the block at the caret) and „Gliederung umwandeln“ (headings ↔ composites, the preferred structure's direction first; the last entry opens the settings when some feature contributes a tab).
689
+ - **`/` commands, block actions and overlay:** one insert command per kind, the heading ↔ composite conversions, the composite-specific hover action (export of a `document` composite) and the chip/details-popover overlay ([below](#chips-and-details-popover)).
690
+ - **Conversion providers** (below), **Markdown `:::` fences** for import (`::: kind {meta}`, `## title`, `> description`, `Tags: #a #b`, body, `:::`; nested fences close with the same bare `:::`; a fence of a kind that is not registered stays an `opaqueFence` and is written back verbatim) and export, **legacy migration** (`section` blocks of old documents become composites; also available as `migrateLegacyContent`), and the `structure` pipeline stage that turns Markdown headings into composites or composites into headings according to the preferred structure.
691
+ - **Settings tab `editing`:** the preferred structure (headings or composites), the scope of „Gliederung umwandeln“ without a selection, the root of a file browser's tree and the default kind of new composites. The values live in the shared settings model, so every panel and editor of the app sees the same choice.
692
+
693
+ Kinds are data. `compositeFeature({ kinds })` replaces the built-in `defaultCompositeKinds(ctx.t)` (section, task, ...; labelled in the context's language); a kind has `id`, `label`, `icon`, `titlePlaceholder`, `descriptionPlaceholder` and `fields` (`text`, `textarea`, `date`, `number`, `select`). `compositeKindsOf(editor)` returns the editor's own list and an unknown `kind` id falls back to the `section` presentation under its own label.
694
+
695
+ ### Chips and details popover
696
+
697
+ The feature mounts one overlay (`CompositeChrome`) beside the editor, so every composite looks the same in every host:
698
+
699
+ - **Chips on the title line.** The kind's `status` field (a `select`) shows as a status chip; its dropdown changes the value. Every tag is a `#tag` chip with a `×` that appears on hover or focus, and a `+` chip opens a tags editor (the same suggestions as the `#` list). Kinds without a status field show only the tags.
700
+ - **Details popover.** The kind icon on the composite's left border opens a popover (about 18rem wide, scaling and fading in from the icon within ~160 ms; no animation with `prefers-reduced-motion`). At the top is the node type as a dropdown of all kinds (icon and label, no label above it), below it every field of the kind except the status (the chip edits it) and the summary. `Escape` or a click outside closes it, unless a dropdown inside is open (that one takes its own `Escape` first).
701
+ - **The icon is the drag handle.** In an editable editor, dragging the kind icon moves the composite (the cursor is a grab hand): the composite becomes the selection and is dragged like with the standard drag handle, so dropping it elsewhere in the editor moves it with its content (`Alt` copies). A click without movement opens the popover. Composites show no standard drag handle; blocks without a kind icon keep theirs.
702
+ - **Icon visibility.** The settings value `blocks.kindIcon` is `'always'` (default) or `'hover'`. With `'hover'` the icon is invisible (120 ms fade) until the composite or its chips are hovered, its popover is open or the icon has keyboard focus; a nested composite's hover also reveals the icons of the composites around it. The invisible icon stays focusable and clickable, and dragging it works as soon as the pointer reaches it.
703
+ - **Changing the type** asks first. Choosing another kind shows an inline warning in the popover instead of applying it: „Art wirklich ändern? Beim Wechsel zu „<Kind>“ gehen diese Angaben verloren:“ followed by the field values the new kind does not declare (`setCompositeKind` keeps only the meta keys of the new kind), or just „Art wirklich ändern?“ when nothing would be lost. „Ändern“ runs `setCompositeKind` for the composite resolved at that moment, „Abbrechen“ keeps the kind.
704
+ - **Read-only editors.** Chips and popover render without edit affordances: no status menu, no `×`/`+`, the type as plain text instead of the dropdown, disabled fields, the summary as plain text. The icon still opens the popover but cannot be dragged.
705
+ - **Layout.** The chips on the title line run on to the editor's right edge; nothing reserves a margin column. The composite node view's own description and tags rows stay in the document as data, but `composites.css` hides them: chips and popover are their only editors. The description is the *summary* in the popover.
706
+
707
+ The hover bar of `BlockAction`s is right-aligned 8px inside the editor's right edge for every block and is hidden while the editor is read-only.
708
+
709
+ ### Headings ↔ composites
710
+
711
+ `convertHeadingsToComposites({ recursive, scope, kind })` and `convertCompositesToHeadings({ recursive, scope })` are editor commands; `convertBlockTo(target)` converts one block. Every conversion runs in a single transaction, so **one undo step** reverses it and redo restores it:
712
+
713
+ ```ts
714
+ editor.commands.convertHeadingsToComposites({ recursive: true, scope: 'document', kind: 'section' })
715
+ editor.commands.undo() // back to the headings, in one step
716
+ editor.commands.convertBlockTo({ type: 'composite', kind: 'task' }, pos)
717
+ editor.commands.convertCompositesToHeadings({ recursive: false, scope: 'heading' })
718
+ ```
719
+
720
+ Flattening keeps description, tags and meta fields as text below the heading, so nothing is lost silently; a document title above the headings stays as is.
721
+
722
+ ### Conversion providers
723
+
724
+ The „Umwandeln in …“ menus and `convertBlockTo` run over the conversion providers of the preset's features. A provider is inert data: `targets(editor, node)` (what it offers for `node`), `parseTarget(id)` (the target an id names, or `null`), `label(target)`, `convert(state, $start, target)` and optionally `handlesSource(node)`.
725
+
726
+ - The **owner** of a target is the first provider, in feature order, whose `parseTarget` parses its id. It supplies the label and, unless another provider claims the source block, executes the conversion.
727
+ - A provider that returns true from **`handlesSource(node)`** claims every conversion of such source blocks. The composite feature uses it for composites: converting a composite into a paragraph, heading, list, quote or code block is executed by the composite provider (it unwraps the composite), while `composite:<kind>` targets are owned by its second provider (wrap a block, a heading with its section or switch kind).
728
+ - `coreFeature()`'s provider declines every target that is not a core target, so features can add targets without interfering with it.
729
+ - A target id advertised by two providers for the same block throws (`the conversion target "paragraph" is advertised by more than one provider`) instead of silently picking one.
730
+
731
+ A feature of your own adds targets the same way: `defineEditor({ features: [coreFeature(), { id: 'mine', conversions: [myProvider] }] })`.
732
+
733
+ ### Vue-free schema entry
734
+
735
+ `@ecsplico/editor/composites/schema` imports no Vue, node view or feature factory. A server that validates or seeds documents builds its schema from it:
736
+
737
+ ```ts
738
+ import { getSchema } from '@tiptap/core'
739
+ import { coreSchemaExtensions } from '@ecsplico/editor/core/schema'
740
+ import { compositeSchemaExtensions } from '@ecsplico/editor/composites/schema'
741
+
742
+ const schema = getSchema([...coreSchemaExtensions(), ...compositeSchemaExtensions({ kinds })])
743
+ ```
744
+
745
+ It also exports the pure helpers: `defaultCompositeKinds`, `compositeKind`, `compositeJSON`, `compositeConversions`, `convertCompositeBlocks`, `blocksFromMarkdown`, `splitCompositeFences`, `compositeFence`, `groupHeadingsIntoComposites`, `flattenCompositesToHeadings`, `headingGroupRange`, `compositeChromeBlocks`, `structureCompositeBlocks` and `migrateLegacyContent`. `@ecsplico/editor/composites` (framework-free) re-exports them next to the node views (`Composite`, `CompositeTags`), `Structure`/`structureOf`, the framework-free rows and anchors models (`createCompositeRows`, `observeCompositeAnchors`), `compositeFeatureCore(ui)` and the helpers below. The bindings are `@ecsplico/editor/composites/vue` (`compositeFeature()`, `CompositeTable`, `CompositeInspector`, `SettingsEditingTab`, `useCompositeRows`, `useCompositeAnchors`, ...) and `@ecsplico/editor/composites/react` (the same names as React components and hooks; `CompositeTable` and `CompositeInspector` take `editor` and are placed beside the editor, `CompositeTable` has `onSelect`, `CompositeInspector` a `document` node and `documentFocused`).
746
+
747
+ ### Outline and Markdown export helpers
748
+
749
+ - `compositeOutline(doc, hiddenKinds)` reads the document as a tree of composites (`OutlineNode`: key, id, position, kind, title, summary flag, children); composites of a hidden kind drop out and their descendants move up. `canMoveBlockInto(editor, id, targetId)` and `moveBlockInto(editor, id, targetId)` move a block to the end of a composite's body (or the document with `null`) in one transaction.
750
+ - `headingsMarkdown(editor, blocks, level)` exports blocks as Markdown with every composite turned into a heading (the outermost at `level`) followed by its description, tags and body; `documentCompositeMarkdown(editor, node)` does the same for one composite as a document, `markdownFileName(node)` names the file and `downloadText(filename, text)` offers it as a download.
751
+
752
+ The heading-only outline of a plain editor stays `@ecsplico/editor/outline` (`headingOutline`, `revealBlockInEditor`); its component is `EditorOutline` from `/outline/vue` or `/outline/react`.
753
+
754
+ ### Metadata table and properties panel
755
+
756
+ Two components show the composites' metadata beside an editor. Both take the editor from the surface's `ready` event as a prop, follow it when it changes, and render their empty state without one.
757
+
758
+ - `CompositeTable` (prop `editor?: Editor`; event `select(row)`) lists every composite as a sortable, filterable table: Baustein (indented by nesting), Art, one column per meta field of the kinds in the document (select values show their option label, dates are formatted; `textarea` fields get no column), Tags and whether a summary exists. Above it: text search over title, summary and tags, kind and tag chips, one select per `select` field (for example Status), a reset and a row count. A header click sorts ascending, descending, then not at all. The table does not change the document; `select` hands the host the clicked row, so the host can switch back to the text and call `revealBlockInEditor(editor, row)` from `/outline`.
759
+ - `CompositeInspector` (props `editor?: Editor`, `documentFocused?: boolean`; slot `document`) is a side panel that shows one section at a time, chosen by where the caret is. The „Dokument“ section shows before the editor was ever focused, while the caret is in editor text outside any Baustein, and while the host's document input is focused; the „Baustein“ section shows while the caret is in a Baustein (also after opening a row from `CompositeTable`). Clicking into the panel never switches sections, and there is no empty state. Pass `documentFocused` as `true` while your document-level input (e.g. the title field) has the focus: the panel then switches to „Dokument“ and stays there after the input blurs, until the caret returns to the editor. A summary draft typed in the panel is committed when it switches away from „Baustein“. The „Dokument“ section starts with the host's `document` slot (the library knows nothing about titles, folders or dates of your storage), followed by what the document itself says: composites per kind and how many have a summary, all tags, all mentions. The „Baustein“ section edits the composite under the caret: kind, summary (as text; formatting on the unchanged text stays, a summary with mentions or tags is read-only there and edited in the document), tags with suggestions and creation, one input per meta field, and the summary state. Every edit is one editor transaction, so it reaches collaborators and undo like any other edit. A summary typed in the panel is written only while the document still holds the text the draft started from; when a collaborator changed it meanwhile, their text wins and is loaded into the panel. A read-only editor (`EditorSurface`'s `editable`) disables the controls. Give the panel its width and border through `class`. Host rows in the slot match its layout with `<dl class="grid grid-cols-[6.5rem_minmax(0,1fr)] items-start gap-x-3 gap-y-2">`, `dt.min-h-6.leading-6.text-muted` and `dd.min-h-6.leading-6.text-highlighted`.
760
+
761
+ ```vue
762
+ <CompositeTable v-if="view === 'table'" :editor="editor" @select="openRow" />
763
+ <CompositeInspector :editor="editor" class="w-80 border-l border-default">
764
+ <template #document>
765
+ <MyDocumentFacts />
766
+ </template>
767
+ </CompositeInspector>
768
+ ```
769
+
770
+ Own views read the same model: `useCompositeRows(editor)` returns `rows` (`CompositeRow`: key, id, position, kind with label and icon, title, summary text and whether it is text only, tags, `meta`, the kind's fields, depth), `tags`, `kinds` (with counts), `mentions` and `focused` (the composite under the caret), re-read once per frame after a document change. `setCompositeMetaField(editor, row, key, value)`, `setCompositeTags(editor, row, tags)` and `setCompositeDescription(editor, row, text, expected?)` write in one transaction and find the composite again by its block ID, so a row read before a remote change stays usable. `setCompositeDescription` stores `compositeDescriptionText(text)` (one line), replacing only the changed span so marks on the rest stay; it returns `false` instead of dropping a mention or tag in the summary, or, with `expected`, when the summary no longer holds that text. `compositeRows(doc, kinds)` is the same read without Vue.
771
+
772
+ ### Settings panel and modal
773
+
774
+ Features contribute settings *tabs* (`settingsTabs: ctx => [{ id, label: ctx.t('…'), icon?, component, props? }]`, a factory over the app context so labels follow its language); the UI that shows them is core and decoupled from any one feature:
775
+
776
+ - `EditorSettingsPanel` (props `preset`, `contentClass?`; `v-model:tab`) renders one tab per contributed tab, wherever you place it. With **no tab** it renders nothing.
777
+ - `EditorSettingsModal` (props `preset`; `v-model:open`, `v-model:tab`) is the same panel in a popup: a Nuxt UI modal titled „Einstellungen“ (`settings.title`; "Settings" with `locale: 'en'`) with a fixed width (`42rem` from the `sm` breakpoint), a blurred, dimmed backdrop, the panel in a scroll area of fixed height (`h-[32rem]`) and a „Schließen“ footer; the description says whether the values apply to the account or to this browser (`synced`). With no tab it renders nothing.
778
+ - `EditorSurface` takes **`settingsUi`**: `'internal'` (default) mounts the modal itself and opens it from the toolbar's settings entry; `'external'` mounts nothing and emits **`settingsOpen(tab?)`** instead, so the host opens its own settings page, drawer or panel, or places an `EditorSettingsModal` itself (the playground does; the example above sets the shown tab from the event). Without any contributed tab the surface neither opens nor emits, and the toolbar's settings entry is hidden.
779
+ - The values are shared app settings (`useEditorSettings()`): several panels and editors edit the same preferences, stored per account when a server is configured and per browser otherwise. Another feature's tab (for example the AI tab) appears in the same panel as soon as its feature is part of the preset.
780
+
781
+ ## File import into composites
782
+
783
+ `compositeMediaFeature()` from `@ecsplico/editor/integrations/composite-media` connects two features that know nothing of each other: with it, every imported file lands wrapped in a `document` composite. It `requires` `composites` and `media` (`defineEditor` throws when either is missing); neither of them imports it, so `core + media` and `core + composites` bundles stay free of it (`bun run check:editor-entries` proves it with the `media`, `composites` and `composite-media` fixtures).
784
+
785
+ ```ts
786
+ import { compositeFeature } from '@ecsplico/editor/composites/vue'
787
+ import { compositeMediaFeature } from '@ecsplico/editor/integrations/composite-media'
788
+ import { coreFeature, defineEditor } from '@ecsplico/editor/core'
789
+ import { mediaFeature } from '@ecsplico/editor/media/vue'
790
+
791
+ const preset = defineEditor({ features: [coreFeature(), mediaFeature(), compositeFeature(), compositeMediaFeature()] })
792
+ ```
793
+
794
+ Install it after `composites` and `media`; its CSS is the two feature stylesheets (`media.css`, `composites.css`).
795
+
796
+ - **Wrapping.** It contributes the `wrapImport` pipeline stage. The blocks of every imported file — the parsed text or Markdown, or the uploaded image, audio, video or file block — go into one `document` composite. The title is the file name without its last extension (the whole name for `.env`); the meta fields hold `filename`, `mimeType` (the file's, else `text/markdown` for text and `application/octet-stream`), `size` and the import date; the details popover shows them. Drop, paste, the toolbar's media buttons, the `/` commands and the audio recorder all import through `editor.storage.media.importFiles`, so they all wrap. Without the integration the blocks are inserted as they are.
797
+ - **Action.** It adds the `/` command „Datei importieren“ (`/datei`, available where composites may be inserted) that picks any files and imports them at the caret. Slash entries follow the feature order, so it is listed after the media entries when installed after `media`.
798
+ - **Not included.** Summaries and transcription of imported documents need AI and belong to the AI integrations, not here.
799
+
800
+ `wrapImport` allows one contributor: installing a second feature that contributes it throws, like every singleton pipeline hook.
801
+
802
+ ## AI on a core editor
803
+
804
+ `aiFeature()` adds the AI features to a core editor: selection menu (rewrite tasks such as shorter/formal/translate and free prompts), the `/ai` command and a „KI" toolbar group, in-document review of rewrites (accept/reject per change), ghost autocomplete while typing (Tab accepts, Esc dismisses), model profiles and the `ai` settings tab. The sidebar (document chat and block generation) and the quick model switch are components you place yourself. It needs no composites, transcript, media, mentions or Yjs; `bun run check:editor-entries` proves it with a `core + ai` fixture.
805
+
806
+ ```vue
807
+ <script setup lang="ts">
808
+ import { aiFeature, AiReviewBar, AiSidebar, ModelQuickSwitch } from '@ecsplico/editor/ai/vue'
809
+ import { coreFeature, defineEditor } from '@ecsplico/editor/core'
810
+ import { EditorSurface, EditorToolbar } from '@ecsplico/editor/vue'
811
+ import type { Editor } from '@tiptap/vue-3'
812
+ import { shallowRef } from 'vue'
813
+
814
+ const preset = defineEditor({ features: [coreFeature(), aiFeature()] })
815
+ const editor = shallowRef<Editor>()
816
+ const sidebarOpen = shallowRef(false)
817
+ </script>
818
+
819
+ <template>
820
+ <EditorToolbar :editor="editor" />
821
+ <div class="flex">
822
+ <EditorSurface v-model="content" v-model:sidebar-open="sidebarOpen" :preset="preset" class="flex-1" @ready="editor = $event" />
823
+ <AiSidebar v-model:open="sidebarOpen" :editor="editor" class="w-[380px]" />
824
+ </div>
825
+ </template>
826
+ ```
827
+
828
+ `aiFeature()` needs no component setup: the model profiles come from the app's editor context (`createEditor()` installed as a Vue plugin; `createEditorContext()` on React), and its notices (rate limits, import problems) go to the surface's notifier. The React binding is `aiFeature()` from `@ecsplico/editor/ai/react` with the same components and hooks (`useAiAssistant`, `useDocumentChat`, `useModelProfiles`); the framework-free `@ecsplico/editor/ai` holds `aiFeatureCore(ui)` and the models behind the hooks (`createAiAssistant`, `modelProfiles`). Import the CSS after `core.css`:
829
+
830
+ ```css
831
+ @import "@ecsplico/editor/styles/ai.css";
832
+ ```
833
+
834
+ Components: `AiSidebar` (`editor`; `v-model:open`), `ModelQuickSwitch` (`slot`, optional `size` and `editor`; the choice applies to the whole slot; `editor` makes „Modelle verwalten…" open that editor's settings when the switch sits outside an `EditorSurface`), `AiReviewBar` (`editor`). `AiSelectionMenu` and `AiReviewBar` are overlays the surface mounts for the feature; they are exported for hosts that render their own editor chrome.
835
+
836
+ ### Backend contract and configuration
837
+
838
+ The feature talks to four endpoints through the context's `fetch` (default `ofetch`, same origin). You provide them; the Nuxt app's server routes in `packages/editor/src/nuxt/runtime/server/api/ai` are the reference implementation and import the shared protocol from the package:
839
+
840
+ | Endpoint | Purpose |
841
+ |---|---|
842
+ | `GET /api/ai/models` | `{ presets: ModelProfile[], slots: Record<ModelSlot, string> }` — server model presets (no keys) and the default preset per slot (`complete`, `rewrite`, `generate`) |
843
+ | `POST /api/ai/generate` | SSE (`delta` / `done` / `error`) for selection tasks and block generation |
844
+ | `POST /api/ai/chat` | SSE for the sidebar's document chat with edit operations |
845
+ | `POST /api/ai/complete` | `{ text }` continuation for ghost autocomplete |
846
+
847
+ The framework-free protocol (`AI_TASKS`, `AiTask`, `AiInput`, `ModelRef`, `ModelProfile`, `modelRefSchema`, `aiBlockSchema`, `AGENT_OP_TYPES`, `checkAgentOps`, `CHAT_LIMITS`, `AI_COMPLETE_LIMITS`, `AI_LIMITS`, …) is exported from `@ecsplico/editor/ai` (and `/ai/protocol`), so a server may import the same constants and Zod schemas. A 429 from any endpoint shows one German „zu viele Anfragen" toast instead of an error.
848
+
849
+ Public AI configuration is inert data passed to `createEditor`:
850
+
851
+ ```ts
852
+ app.use(createEditor({ ai: { slots: { complete: 'flashLite', rewrite: 'sonnet', generate: 'sonnet' }, transcription: false } }))
853
+ ```
854
+
855
+ `slots` are the default preset ids per model slot until the user chooses one in the `ai` settings tab (stored in `settings.ai.slots`). API keys never reach the client: user-added profiles with their own endpoint/key are kept in this browser's `localStorage` only. In Nuxt the module's `tiptapEditor.ai` options (presets, slots, rate limit) and the env keys `NUXT_TIPTAP_EDITOR_AI_API_KEY` / `NUXT_TIPTAP_EDITOR_AI_PRESETS_<ID>_API_KEY` supply the server side.
856
+
857
+ ### Extending what AI knows about the document
858
+
859
+ By default the AI sees a flat document with no custom block kinds and no host blocks. Features adjust that through three optional hooks on `EditorFeature`:
860
+
861
+ - `aiContext: (editor) => ({ kinds?, blocks?, structure? })` — what the model may build and how. It is **additive**: any number of features may contribute. `defineEditor` merges the contributions, concatenating `kinds` and `blocks` in feature order; `structure` (the preferred structure for generated content, `'flat'` | `'composite'`) may be set by one feature only. A block `id` described by two features, or a second feature setting `structure`, throws when the merged context is read (`aiContextOf(editor)`, which returns `{ kinds, blocks, structure }`), not when `defineEditor` runs.
862
+ - `kinds` — the composite block kinds the model may build. The composites feature contributes them.
863
+ - `blocks` — host blocks as `AiBlock { id, label, description, syntax, example }` (exported from `@ecsplico/editor/core`): `id` is the node type name and the `convert` target id, `label` the German name, `description` its purpose and allowed content, `syntax` the Markdown form with placeholders in angle brackets, `example` one complete example. The document chat and the generate tasks (sidebar and selection menu) list them in the system prompt and send them with the request, so the model can write them; the chat offers a block as `convert` target when a registered conversion provider parses its id. See [Extending guide: AI](./extending.md#ai) for the callout's contribution.
864
+ - `formatAiResult: (editor, markdown, heading?) => string` — formats an AI result's Markdown (with the optional task heading) before the generic parse/normalize/structure pipeline. Without a contributor the result gets the normalized heading (`withHeading`); the `composite-ai` integration (below) contributes the composite-structure form. At most one feature may contribute it; `defineEditor` throws on a second one.
865
+ - `aiBlockData: ({ editor, node, pos, signal }) => AiBlockDatum[] | Promise<AiBlockDatum[]>` — material a feature supplies for one Baustein before „Baustein füllen (KI)“ asks the model (`AiBlockDatum { label, text }`; the three types are exported from `@ecsplico/editor/core`). It is **additive**: every contributor runs for the Baustein in parallel, and `defineEditor` concatenates their entries in feature order (`aiBlockDataOf(editor)` returns the merged provider; without contributors it resolves to no data). The library only collects: the fetch, the ranking and what the entries mean belong to your feature. `signal` aborts when the editor is destroyed. A contributor that throws or rejects cancels the fill with the notice „Daten für den Baustein fehlen“, and no generate request is sent. The playground's `epikriseFeature()` (`app/editor/epikrise/feature.ts`) fetches two example paragraphs from its own route for the Epikrise kind.
866
+
867
+ AI results are parsed with `parseBlocks(editor, markdown)`: inline replacements use the pre-segmentation `parsed` content, block insertion uses `blocks`.
868
+
869
+ ### Positions and collaboration
870
+
871
+ Selection menu, sidebar and review capture ranges as document anchors. Without an `anchorResolver` anchors map through local transactions only, and the review writes with a `closeHistory` step so rewriting does not merge into typing. A collaboration feature contributes an `AnchorResolver` that keeps anchors valid across remote edits and wraps the review's undo step; the AI feature imports no Yjs code either way.
872
+
873
+ ## AI integrations: composite AI and transcript AI
874
+
875
+ `aiFeature()` knows neither composites nor transcripts. Two integration features connect it to them; each imports only the features it names, no parent feature imports the integration, so `core + ai`, `core + composites` and `core + transcript` bundles stay free of them (`bun run check:editor-entries`: fixtures `composite-ai` and `transcript-ai` allow exactly their features plus the integration, and the `ai`, `composites` and `transcript` fixtures forbid any integration module).
876
+
877
+ | Factory (`@ecsplico/editor/integrations/…`) | `requires` | Contributes |
878
+ |---|---|---|
879
+ | `composite-ai` → `compositeAiFeature()` | `composites`, `ai`, `media` | block actions „Baustein füllen (KI)“ (kinds with `ai`), „Zusammenfassung erzeugen (KI)“ and „Transkribieren (KI)“; the single `formatAiResult` |
880
+ | `transcript-ai` → `transcriptAiFeature()` | `transcript`, `ai` | block action „Transkript bereinigen (KI)“ and the same row in the AI selection menu |
881
+
882
+ ```ts
883
+ import { aiFeature } from '@ecsplico/editor/ai/vue'
884
+ import { compositeFeature } from '@ecsplico/editor/composites/vue'
885
+ import { coreFeature, defineEditor } from '@ecsplico/editor/core'
886
+ import { compositeAiFeature } from '@ecsplico/editor/integrations/composite-ai'
887
+ import { compositeMediaFeature } from '@ecsplico/editor/integrations/composite-media'
888
+ import { transcriptAiFeature } from '@ecsplico/editor/integrations/transcript-ai'
889
+ import { mediaFeature } from '@ecsplico/editor/media/vue'
890
+ import { mentionsFeature } from '@ecsplico/editor/mentions'
891
+ import { transcriptFeature } from '@ecsplico/editor/transcript/vue'
892
+
893
+ // The integrations are framework-free and fit both surfaces; on React take the bindings from `/<x>/react`.
894
+ const preset = defineEditor({
895
+ features: [
896
+ coreFeature(), mediaFeature(), transcriptFeature(), compositeFeature(), mentionsFeature(), aiFeature(),
897
+ compositeMediaFeature(), compositeAiFeature(), transcriptAiFeature(),
898
+ ],
899
+ })
900
+ ```
901
+
902
+ Install each integration after the features it requires (the order of the list is the order of toolbar/menu entries; actions carry their own `order`). `defineEditor` throws `feature "composite-ai" requires "ai", which is not installed` (likewise for `composites`, `media`, and for `transcript-ai` without `transcript` or `ai`) — it never installs a missing dependency. `bun run check:editor-composition` loads the real modules and asserts every one of these rejections, the accepted full composition and the single-`formatAiResult` rule. CSS: the stylesheets of the features in the preset (`core.css`, `media.css`, `composites.css`, `transcript.css`, `ai.css`); the integrations bring none.
903
+
904
+ **`compositeAiFeature()`**
905
+
906
+ - **Fill a Baustein.** „Baustein füllen (KI)“ appears on a composite whose kind has an `ai` configuration (`CompositeKind.ai`). It sends the Baustein's brief (`compositeAiBrief(editor, pos)`: kind and `purpose`, titles of the enclosing composites as `path`, title, description, non-empty field values with `select` option labels, the instruction — a non-empty `meta[ai.instructionsField]` replaces `ai.instructions` — and the `skeleton` with `{key}` replaced by `meta[key]`, the current body as Markdown) plus the document as context to the task `fill-block` (`generate` slot, `POST /api/ai/generate`, rejected with 400 „Baustein fehlt“ without `block`). The result opens the in-document diff review over the Baustein's body; title and fields stay untouched. `fill-block` is offered nowhere else (`blockTasks()` skips tasks with `needsBlock`).
907
+ - **Material from features.** Before the request, `fillComposite` runs the preset's merged `aiBlockData` for the Baustein (above). The entries travel as `block.data` in order, at most 10, each `text` cut to `AI_LIMITS.block.data` (20000) characters; the task prompt prints each as `<label>:` followed by the quoted text.
908
+ - **Composite summary.** „Zusammenfassung erzeugen (KI)“ appears on a composite whose body has text (or, for an audio/video `document`, a `meta.transcript`). It streams a `summarize` task (`rewrite` model slot) from `POST /api/ai/generate` and replaces the composite's description with it; the details popover shows it as the summary. The description is anchored when the request starts, so edits made while it streams do not misplace the result.
909
+ - **Media transcription.** „Transkribieren (KI)“ appears on a `document` composite holding an audio/video asset, only when the app's public AI configuration has `transcription: true` (`createEditor({ ai: { transcription } })`). It calls `POST /api/ai/transcribe` with the asset reference (`{ documentId, name }`) through the context's `fetch` and stores the text in `meta.transcript`. Your server provides the endpoint; the Nuxt app's route in `packages/editor/src/nuxt/runtime/server/api/ai` is the reference.
910
+ - **Composite-structure AI results.** With the `composite` structure setting, an AI result that carries a heading (sidebar „block generation“, selection/menu tasks with a heading) is formatted as a `section` composite fence (`::: section` with the heading and the result inside), so the generic pipeline inserts it as a section composite. In flat structure, or without a heading, the result is unchanged from the base AI behaviour. The feature is the preset's only `formatAiResult` contributor — the base AI feature stays composite-free.
911
+ - **Exports** (`@ecsplico/editor/integrations/composite-ai`): `compositeAiFeature`, `fillComposite(editor, pos, fetch, modelRef, notify, t)`, `summarizeComposite(editor, pos, fetch, modelRef, notify, t)`, `transcribeDocument(editor, pos, fetch, notify, t)`, `compositeSourceText`, `mediaAssetOf`, `COMPOSITE_BODY_START`. Notices go through the surface's `notify` in the language of `t` (`ctx.t`), the model through `modelProfiles(app)`.
912
+
913
+ **`transcriptAiFeature()`**
914
+
915
+ - **Transcript cleanup.** „Transkript bereinigen (KI)“ is a block action on a speaker segment or a block (composite, quote, list) holding segments, and „Transkript bereinigen“ is a row of the AI selection menu over a selection that overlaps segments. It sends `{ id, speaker, text }` per segment to `POST /api/ai/transcript-cleanup` and opens the cleaned texts as an in-document AI review, one change per segment. The model returns only new texts keyed by block ID, so speakers, order and segment count cannot change; over-long, stale and unusable segments are skipped with a German notice. Without `transcriptAiFeature()` neither the action nor the menu row exists.
916
+ - **Server contract.** `@ecsplico/editor/integrations/transcript-ai/protocol` is Vue-free and server-importable: `TRANSCRIPT_CLEANUP_LIMITS`, `transcriptCleanupRequestSchema`, `transcriptCleanupMessages`, `transcriptCleanupChunks`, `parseTranscriptCleanup` and the request/response types. Your endpoint validates the request with the schema, cleans in chunks and answers `{ cleaned: [{ id, text }], failed }`.
917
+ - **Exports** (`@ecsplico/editor/integrations/transcript-ai`): `transcriptAiFeature` (its `setup` teardown aborts a running cleanup), `createTranscriptCleanup(modelRef, fetch, notify, t)` → `{ running, cleanup, abort }`, `hasTranscriptSegments`.
918
+ - Speaker-aware transformations that normalization already covers — `**Name:** text` lines becoming segments in imported or AI-written Markdown — stay with the `transcript` feature's `segment` stage; the integration adds only the cleanup.
919
+
920
+ ## Reference: AI surfaces, agent operations and composite fences
921
+
922
+ ### In-block AI versus block generation
923
+
924
+ - **In-block AI** (`rewrite` slot) works on text inside one block or a selection and is applied through the review diff: the selection tasks, the composite summary and transcript cleanup. Ghost autocomplete is in-block too but has its own `complete` slot.
925
+ - **Block generation** (`generate` slot) produces, replaces, converts or removes whole blocks: the tasks `generate`, `continue` and `transcript-*`, plus the chat agent operations. `fill-block` (block action „Baustein füllen (KI)“) is a `generate`-slot task too, but only reachable through that action.
926
+
927
+ | Surface | What | Slot | Route |
928
+ |---|---|---|---|
929
+ | Ghost autocomplete (`ai.autocomplete` setting) | Inline continuation while typing | `complete` | `POST /api/ai/complete` |
930
+ | `AiSelectionMenu`, selection mode | `reformulate`, `fix`, `shorter`, `formal`, `casual`, `translate`, `summarize` and the free prompt `custom` | `rewrite` | `POST /api/ai/generate` |
931
+ | `AiSelectionMenu`, caret mode (`/ai`) | `continue` („Weiterschreiben“) and the free prompt `generate` | `generate` | `POST /api/ai/generate` |
932
+ | `AiSidebar` quick actions | `blockTasks()` without the caret tasks: `generate`, `transcript-summary`, `transcript-minutes`, `transcript-action-items` | `generate` | `POST /api/ai/generate` |
933
+ | `AiSidebar` chat agent | Answers with citations, proposes operations (below) | `generate` | `POST /api/ai/chat` |
934
+ | Composite summary (block action, `composite-ai`) | Task `summarize` over the composite body → description | `rewrite` | `POST /api/ai/generate` |
935
+ | Fill a Baustein (block action, `composite-ai`) | Task `fill-block` over the Baustein's brief → body, as review | `generate` | `POST /api/ai/generate` |
936
+ | Transcript cleanup (block action, `transcript-ai`) | Cleans transcript segment texts | `rewrite` | `POST /api/ai/transcript-cleanup` |
937
+ | Document transcription (block action, `composite-ai`) | Speech-to-text of an audio/video `document` → `meta.transcript`; „Zusammenfassung erzeugen (KI)“ then summarises the transcript when the body holds no text | none (transcription endpoint) | `POST /api/ai/transcribe` |
938
+
939
+ A task's slot comes from `AiTask.slot` (`AI_TASKS`); `ModelQuickSwitch` in the selection menu and the sidebar changes the slot's profile in place. Inserted generation results (sidebar quick actions, `/ai` at the caret, agent `add` operations) go through the preset's `structure` stage: with the composites feature and the `blocks.structure` setting `'composite'`, headings are grouped into nested `section` composites, with `'flat'` composites are flattened into headings starting at `##`. A change of the setting applies to the next insertion. Rewrites (`replace-selection`, agent `update`) are never restructured.
940
+
941
+ ### Chat agent operations
942
+
943
+ The model proposes edits through the `edit_document` tool. The server checks them (`checkAgentOps`, `@ecsplico/editor/ai/protocol`) and streams the valid ones as an `ops` event: `{ ops: AgentOp[], dropped: AgentOpProblem[], retries }`. Each operation gets a conversation-wide `op: 'op<n>'` id.
944
+
945
+ | Type | Fields | Effect |
946
+ |---|---|---|
947
+ | `add` | `afterId: string \| null`, `markdown` | Insert block(s) after `afterId` (in the document or its composite body). `null` = document start, not allowed with a selection scope. |
948
+ | `update` | `id`, `markdown` | Replace block `id`. A single paragraph, or one block of the same type and attributes, is diffed within the block. On a composite, a `::: kind` fence (exactly one composite) replaces the whole composite; unfenced markdown replaces only its body. |
949
+ | `delete` | `id` | Remove the block (or the smallest list item, list or quote that would be invalid without it). |
950
+ | `convert` | `id`, `to` | Change the block type, keeping the content. `to` is one of the request's `targets`: the targets the installed conversion providers advertise (`paragraph`, `heading1`–`heading3`, `bulletList`, `orderedList`, `blockquote`, `codeBlock`, plus `transcriptSegment` and `composite:<kind>` with those features). |
951
+
952
+ Validation problems (`AgentOpProblemReason`): `unknown` (the id isn't in the document), `scope` (the id is outside the selection), `field` (a field is missing or invalid, e.g. empty `markdown` or more than `AGENT_LIMITS.markdown` = 8000 characters), `duplicate` (the same block is updated, converted or deleted twice), `limit` (more than `AGENT_LIMITS.ops` = 50 operations), `target` (`to` isn't among the request's `targets`). On problems the model is asked to retry, up to `AGENT_LIMITS.retries` = 2 times. Whatever is still invalid after that, or left out of a corrected list, is reported in `dropped`.
953
+
954
+ **Review flow.** The client re-checks each operation against the scope the request was sent with and shows valid ones as in-document review changes the user accepts or rejects. An operation that can't be shown gets an outcome instead: `blocked`, `missing`, `unchanged`, `overlap` or `empty`. Every operation's outcome (`accepted`, `rejected`, `partial`, `invalid`, `pending`, …) is sent back as `feedback` with the next user message.
955
+
956
+ **Document text** (`documentChatText(editor)`): one line per block, `[<id>] <markdown on one line>`. List and quote markers form the prefix (`- `, `1. `, `> `). A composite is a header line `[<id>] ::: kind {meta} | Title | Description | Tags: #a #b` (meta only when non-empty, empty parts omitted), followed by its body lines indented by two spaces and a closing line ` :::` without an id. The text is truncated at `CHAT_LIMITS.document` = 120000 characters.
957
+
958
+ ### Composite Markdown fence format
959
+
960
+ ```
961
+ ::: kind {meta-json}
962
+ ## Title
963
+ > Description
964
+ Tags: #a #b
965
+ body blocks…
966
+ :::
967
+ ```
968
+
969
+ - The meta JSON is written only when it is non-empty (`::: section`). The parser requires a JSON object; otherwise the line stays plain text.
970
+ - Fences nest. Every closer is a bare `:::` and closes the innermost open fence. An opener without a matching closer stays plain text.
971
+ - Inner text: the first `#`…`######` heading becomes the title, a blockquote directly after it the description, a `Tags:` line the tags (lower-cased, de-duplicated), the rest the body (recursively). A missing title gives an empty title; an empty body gives one empty paragraph.
972
+ - Known kind: `meta` keeps only that kind's field keys. Unknown kind: the id and meta are kept as given and the view falls back to the `section` presentation.
973
+
974
+ Used by `editor.getMarkdown()` (the composite nodes' `renderMarkdown`), by AI results applied through the composites feature's `parseMarkdown` stage (`blocksFromMarkdown`; task results with a heading are wrapped in a `section` fence in `'composite'` structure, else titled with `##`, by the `composite-ai` integration's `formatAiResult`) and by the agent's `add`/`update` Markdown and system prompt.
975
+
976
+ ```
977
+ ::: task {"status":"doing","due":"2026-11-01"}
978
+ ## Angebot schreiben
979
+ > Für Kunde Müller
980
+ Tags: #vertrieb
981
+ - Preise prüfen
982
+ - Entwurf an Ben
983
+ :::
984
+ ```
985
+
986
+ ### Stored settings
987
+
988
+ `EditorSettings` (`@ecsplico/editor/settings`) is one version-1 JSON object shared by all editors of an app: `editing.{headingConversionScope ('container'), authorColors (false), authorColorStyle ('background'), commentsLayout ('margin')}`, `ai.{autocomplete (false), slots ({}), translateLanguage ('Englisch')}`, `blocks.{defaultKind ('section'), structure ('composite'), treeRoot ('folders'), kindIcon ('always'; 'hover' shows the composites' kind icon only on hover, focus or open details), dragHandle ('hover'; 'always' shows the drag handle of blocks without kind icon permanently)}` and `appearance.{theme (null)}` (the Obsidian theme id, `null` = standard look; see [Themes (Obsidian)](#themes-obsidian)). `parseEditorSettings(input)` deep-merges any input over those defaults (unknown keys dropped, invalid values replaced; a stored `blocks.metaVisibleDefault` of an older version is just dropped) and migrates the legacy flat per-browser shape `{ headingConversionScope }`; `PUT /api/editor/settings` instead requires the complete, valid shape (`editorSettingsSchema`). Settings tabs are contributed by features (`editing` by composites, `authors` and `comments` by their features, `ai` by AI, `appearance` by themes), so a preset without a contributing feature has no settings UI. A tab only edits its own fields; the other sections are written back unchanged, which is why one account can use a simple and an advanced editor without erasing the other's preferences.
989
+
990
+ ## File browser
991
+
992
+ `DocumentTree` is the document drawer: folders and documents of *your* storage, with create, rename, move (drag and drop), delete with undo, per-item kind filters and the outline of the open document. It exists for both surfaces: `@ecsplico/editor/tree/vue` (Nuxt UI) and `@ecsplico/editor/tree/react` (shadcn/ui; needs `EditorContextProvider` and a mounted `Toaster`). The framework-free `@ecsplico/editor/tree` holds the `DocumentStore` contract and the view-state model (`createTreeView`, `treeView(context)`, `TreeViewModel`) that both components and their `useTreeView()` hooks sit on. It needs the settings context (`createEditor()` / `createEditorContext()`), the composites and media features' code (it reads the composite outline and imports files) and a `DocumentStore` that you implement over your storage and router; it does no HTTP or routing of its own.
993
+
994
+ ```vue
995
+ <script setup lang="ts">
996
+ import { DocumentTree } from '@ecsplico/editor/tree/vue'
997
+ import type { DocumentStore } from '@ecsplico/editor/tree'
998
+ import type { Editor } from '@tiptap/core'
999
+
1000
+ defineProps<{ store: DocumentStore, editor: Editor | undefined }>()
1001
+ const selectedFolder = defineModel<string>('selectedFolder', { default: '' })
1002
+ </script>
1003
+
1004
+ <template>
1005
+ <DocumentTree v-model:selected-folder="selectedFolder" :store="store" :editor="editor">
1006
+ <template #document-actions="{ id }"><!-- optional host buttons per document --></template>
1007
+ </DocumentTree>
1008
+ </template>
1009
+ ```
1010
+
1011
+ React, with the same `store` and a controlled folder:
1012
+
1013
+ ```tsx
1014
+ import type { DocumentStore } from '@ecsplico/editor/tree'
1015
+ import { DocumentTree } from '@ecsplico/editor/tree/react'
1016
+ import type { Editor } from '@tiptap/core'
1017
+ import { useState } from 'react'
1018
+
1019
+ export function Drawer({ store, editor }: { store: DocumentStore, editor: Editor | undefined }) {
1020
+ const [selectedFolder, setSelectedFolder] = useState('')
1021
+ return (
1022
+ <DocumentTree
1023
+ store={store}
1024
+ editor={editor}
1025
+ selectedFolder={selectedFolder}
1026
+ onSelectedFolderChange={setSelectedFolder}
1027
+ documentActions={({ id }) => null /* optional host buttons per document */}
1028
+ />
1029
+ )
1030
+ }
1031
+ ```
1032
+
1033
+ - `editor` is the open page's editor (`undefined` when no document is open); take it from your page through a shared composable, as `useActiveEditor()` does in the Nuxt app.
1034
+ - `v-model:selectedFolder` (React: `selectedFolder` / `onSelectedFolderChange`, uncontrolled when omitted) is where new documents, new folders and your own import actions go. Bind the same state in your import button (the Nuxt app binds it in the layout and passes it to `AppDocumentTree`). The Vue `document-actions` slot is `documentActions` on React.
1035
+ - Place `DocumentTree` anywhere (a sidebar, a drawer); several can coexist, sharing one filter state per app (`useTreeView()` from `/tree/vue` returns the refs `nodeFilters` and `expanded`, from `/tree/react` the plain values, both with the framework-free `model`).
1036
+ - The outline entries under the open document follow the settings: composites in the preferred structure (a *virtual root* when `blocks.treeRoot` is `'file'`), headings otherwise.
1037
+
1038
+ The full `DocumentStore` contract (loading states, mutation refresh, delete with undo, folder rules, name validation, who reports failures) and the runnable implementation are in the [integration guide](./integration.md#10-the-file-browser-and-documentstore); the host implementation is [`app/composables/useDocumentStore.ts`](../../../app/composables/useDocumentStore.ts).
1039
+
1040
+ ## Advanced Nuxt example
1041
+
1042
+ The repository's Nuxt app is the runnable advanced reference: documents with real-time collaboration, the file drawer, AI, and a playground with two independent editors. It has no login: a visitor acts as one of the demo users `anna`, `ben` and `bernd` (`shared/utils/demo-users.ts`), starts as `anna` and switches in the user menu top right („Benutzer wechseln“, which reloads the page under the new user). Start it with `bun run dev` (port 3031); it needs `NUXT_SESSION_PASSWORD` (at least 32 characters, for the `nuxt-auth-utils` session cookie) and, for AI, the API key variables below in `.env`.
1043
+
1044
+ **Walkthrough**
1045
+
1046
+ 1. Open `http://localhost:3031/`; you are `anna` and land on the home page: the file drawer on the left lists the documents and the folders, the main area reads „Kein Dokument ausgewählt“.
1047
+ 2. Press „Neues Dokument“ (in the main area, or „Hinzufügen“ → „Neues Dokument“ on a drawer row) to create a document; the app opens `/documents/<id>` and the new document appears in the drawer. In the drawer you can also create folders, drag documents and folders between folders, rename, filter and delete with undo ([File browser](#file-browser)); each document row has an export button that downloads a `.cdoc` package.
1048
+ 3. Type. Use the toolbar, the `/` menu, the drag handles and the hover block actions; the AI sidebar (the toolbar's AI button or `Mod-Shift-k`) and the settings cog in the header open their host-placed UI.
1049
+ 4. Open the same URL in a second browser tab, or as another user in a second browser (or a private window) where you switch to `ben` in the user menu; the session cookie is per browser, so tabs of one browser share the user. Text and carets appear in both within a moment and the header badge reads „Synchronisiert“; each tab keeps its own undo history. Text carries its author (switch the colouring on with the toolbar's palette button), and a selection can be commented („Kommentar“, `Mod-Shift-m` for the margin) with replies, resolve and reopen visible to both.
1050
+ 5. Optional: open `/playground` for two standalone editors with separate JSON and Markdown output, toolbars, sidebars and settings popups (`EditorSettingsModal`, opened from the pane's cog). The first select in the page header picks the editor that fills the page; the other one stays mounted, so its document, undo history and AI conversation survive the switch. Each starts from the first of its own samples; the second select in the page header („Beispiel“) loads another sample into the editor shown. **Editor A** (`AdvancedEditor` with host props) offers „Überblick“ (mention and tag chips, a task, a section with a nested note and a custom „Entscheidung“ composite, speaker segments, an image and a file block), „Gliederung“, „Transkript“ and „Leer“; hover a block for the host's „In Konsole loggen“ action and type `/datum` or `/decision`. **Editor B** (`EditorSurface` on the advanced features plus an own feature) starts with „Hinweise“: the toolbar has a „Hinweis“ button next to „Sprecherblock“ (`Mod-Shift-h`, also `/hinweis`), hovering a callout opens „Art des Hinweises“ to switch its tone, „Umwandeln in …“ offers „Hinweis“ on text blocks and turns a callout back into a core block, and „Markdown einfügen“ imports a heading, a `!!! warning` callout and a paragraph through the block pipeline. Open the settings popup and pick „Darstellung“ to try the themes: the whole app, header and drawer included, switches look.
1051
+
1052
+ **Where each part lives**
1053
+
1054
+ | Part | File |
1055
+ |---|---|
1056
+ | Collaborative page: `AdvancedEditor` with a `HocuspocusProvider` session, toolbar and `AiSidebar` placed by the page, editor-owned settings modal (`settingsUi` default `'internal'`), a „Text \| Tabelle“ switch to `CompositeTable` (a row returns to the text and reveals its Baustein) and a docked `CompositeInspector` with the page's own document facts in its `document` slot ([`DocumentProperties.vue`](../../../app/components/DocumentProperties.vue)) | [`app/pages/documents/[id].vue`](../../../app/pages/documents/[id].vue) |
1057
+ | Standalone page, Editor A: an `AdvancedEditor` with `settings-ui="external"` (the settings popup is placed by the pane below), custom composite kind, metadata, ability, block action and `/` command | [`app/pages/playground.vue`](../../../app/pages/playground.vue) |
1058
+ | Standalone page, Editor B: `defineEditor({ features: [...advancedEditor().features, calloutFeature()] })` mounted with `EditorSurface`, and the `abilities` prop `{ root: [...ROOT_ABILITIES, CALLOUT_ABILITY] }` that grants the callout's own ability | [`app/pages/playground.vue`](../../../app/pages/playground.vue) |
1059
+ | One playground pane, filling the page body (header with toolbar, switches and the `EditorSettingsModal` the editor's `settingsOpen` event opens, JSON and Markdown output; the AI sidebar comes in through a slot) and its state (`createPane`, `loadSample`, `showSettings`, `refreshMarkdown`) | [`app/components/PlaygroundPane.vue`](../../../app/components/PlaygroundPane.vue), [`app/editor/playground-pane.ts`](../../../app/editor/playground-pane.ts) |
1060
+ | Demo documents of both editors and the Markdown for „Markdown einfügen“ | [`app/editor/samples.ts`](../../../app/editor/samples.ts), assets [`public/demo/diagramm.svg`](../../../public/demo/diagramm.svg) and [`protokoll.txt`](../../../public/demo/protokoll.txt) |
1061
+ | The own block as a feature: node, node view, styles, feature (toolbar, `/`, hover menu, conversion) and conversion provider; walked through in the [extending guide](./extending.md) | [`app/editor/callout/`](../../../app/editor/callout): `node.ts`, `CalloutView.vue`, `index.ts`, `callout.css`, `feature.ts`, `convert.ts` |
1062
+ | Module registration (`@ecsplico/editor/nuxt`) | [`nuxt.config.ts`](../../../nuxt.config.ts) |
1063
+ | Adapter plugin: `setEditorAdapter` with sessions, document store, Yjs state, assets and per-user settings | [`server/plugins/editor-adapter.ts`](../../../server/plugins/editor-adapter.ts) |
1064
+ | Store adapter: documents, Yjs state, assets, settings, users on the file system | [`server/utils/documents.ts`](../../../server/utils/documents.ts), [`state.ts`](../../../server/utils/state.ts), [`assets.ts`](../../../server/utils/assets.ts), [`settings.ts`](../../../server/utils/settings.ts), [`users.ts`](../../../server/utils/users.ts) |
1065
+ | File drawer: `DocumentTree` over the host's `DocumentStore` (routes, `$fetch`, toasts, delete-with-undo), selected folder held by the layout | [`app/layouts/default.vue`](../../../app/layouts/default.vue), [`app/components/AppDocumentTree.vue`](../../../app/components/AppDocumentTree.vue), [`app/composables/useDocumentStore.ts`](../../../app/composables/useDocumentStore.ts) (the `.cdoc` export button comes in through the `document-actions` slot; the format stays in the host) |
1066
+
1067
+ The page and its components import only public entries: `AdvancedEditor` and `advancedEditor` from `@ecsplico/editor/vue/advanced`, `EditorToolbar`, `EditorSettingsModal`, `EditorSurface` and `useEditorUi` from `@ecsplico/editor/vue`, `defineEditor` and `parseBlocks` from `@ecsplico/editor/core`, `ROOT_ABILITIES` from `@ecsplico/editor/core/schema`, `AiSidebar` from `@ecsplico/editor/ai/vue`, `CommentsMargin` from `@ecsplico/editor/comments/vue`, `CompositeTable`/`CompositeInspector` from `@ecsplico/editor/composites/vue`, `defaultCompositeKinds` from `@ecsplico/editor/composites`, `DocumentTree` from `@ecsplico/editor/tree/vue` (the `DocumentStore` type from `@ecsplico/editor/tree`). The callout files add `commandItem`, `ConversionProvider` and the conversion helpers from `@ecsplico/editor/core`. The app-level editor context (settings sync, fetch, public AI configuration) comes from the Nuxt module, no per-page setup. The React counterpart of the document page is [`examples/react-advanced`](../../../examples/react-advanced) ([Advanced editor in React](#advanced-editor-in-react)).
1068
+
1069
+ **Optional AI configuration.** The editor works without AI credentials; AI requests then fail with the server's message. Keys come from the environment only (see [AI configuration and secrets](./integration.md#5-ai-configuration-and-secrets)): `NUXT_TIPTAP_EDITOR_AI_API_KEY` (shared fallback for every preset), `NUXT_TIPTAP_EDITOR_AI_PRESETS_<ID>_API_KEY` (one preset; `flashLite` → `NUXT_TIPTAP_EDITOR_AI_PRESETS_FLASH_LITE_API_KEY`) and `NUXT_TIPTAP_EDITOR_AI_TRANSCRIPTION_API_KEY` (only with `ai.transcription` configured).
1070
+
1071
+ **Two editors in one page.** Every component placed beside an editor names it: `EditorToolbar :editor`, `AiSidebar :editor`, `useEditorUi(() => editor)`. Each editor (`AdvancedEditor` or `EditorSurface`) registers its chrome (settings routing, sidebar toggle, block actions) under its own Tiptap instance, so editor B's toolbar never formats editor A and `openSettings` reaches the settings popup of the editor that asked. Only the values in the popups are shared; they are the app's settings, saved once for the account. External content replacement (picking a sample) emits no update event, so the page refreshes the Markdown output itself once the editor took the new content (`loadSample` in `app/editor/playground-pane.ts`).