@ecsplico/editor 2.0.0 → 2.1.1

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 (278) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +4 -3
  3. package/dist/adapters/anchors.d.ts +80 -0
  4. package/dist/adapters/anchors.js +270 -0
  5. package/dist/adapters/anchors.js.map +1 -0
  6. package/dist/adapters/comments/client.d.ts +64 -0
  7. package/dist/adapters/comments/client.js +536 -0
  8. package/dist/adapters/comments/client.js.map +1 -0
  9. package/dist/adapters/comments/drizzle.d.ts +430 -0
  10. package/dist/adapters/comments/drizzle.js +236 -0
  11. package/dist/adapters/comments/drizzle.js.map +1 -0
  12. package/dist/adapters/comments/errors.d.ts +9 -0
  13. package/dist/adapters/comments/errors.js +19 -0
  14. package/dist/adapters/comments/errors.js.map +1 -0
  15. package/dist/adapters/comments/json.d.ts +19 -0
  16. package/dist/adapters/comments/json.js +94 -0
  17. package/dist/adapters/comments/json.js.map +1 -0
  18. package/dist/adapters/comments/record.js +78 -0
  19. package/dist/adapters/comments/record.js.map +1 -0
  20. package/dist/adapters/comments/server.d.ts +23 -0
  21. package/dist/adapters/comments/server.js +102 -0
  22. package/dist/adapters/comments/server.js.map +1 -0
  23. package/dist/adapters/comments/types.d.ts +109 -0
  24. package/dist/adapters/documents.js +108 -0
  25. package/dist/adapters/documents.js.map +1 -0
  26. package/dist/adapters/drizzle.d.ts +293 -0
  27. package/dist/adapters/drizzle.js +157 -0
  28. package/dist/adapters/drizzle.js.map +1 -0
  29. package/dist/adapters/files.js +30 -0
  30. package/dist/adapters/files.js.map +1 -0
  31. package/dist/adapters/forge.d.ts +58 -0
  32. package/dist/adapters/forge.js +91 -0
  33. package/dist/adapters/forge.js.map +1 -0
  34. package/dist/adapters/index.d.ts +13 -0
  35. package/dist/adapters/index.js +0 -0
  36. package/dist/adapters/json.d.ts +24 -0
  37. package/dist/adapters/json.js +128 -0
  38. package/dist/adapters/json.js.map +1 -0
  39. package/dist/adapters/nitro.d.ts +61 -0
  40. package/dist/adapters/nitro.js +212 -0
  41. package/dist/adapters/nitro.js.map +1 -0
  42. package/dist/adapters/routes.js +16 -0
  43. package/dist/adapters/routes.js.map +1 -0
  44. package/dist/adapters/shared.d.ts +37 -0
  45. package/dist/adapters/shared.js +32 -0
  46. package/dist/adapters/shared.js.map +1 -0
  47. package/dist/adapters/vue.d.ts +67 -0
  48. package/dist/adapters/vue.js +187 -0
  49. package/dist/adapters/vue.js.map +1 -0
  50. package/dist/code/CodeSurface.vue.d.ts +3 -3
  51. package/dist/core/doc-anchors.d.ts +5 -0
  52. package/dist/core/doc-anchors.js +4 -3
  53. package/dist/core/doc-anchors.js.map +1 -1
  54. package/dist/core/editor-ui.js +57 -1
  55. package/dist/core/editor-ui.js.map +1 -1
  56. package/dist/core/surface-session.js +139 -0
  57. package/dist/core/surface-session.js.map +1 -0
  58. package/dist/document/DocumentSurface.d.ts +1 -1
  59. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  60. package/dist/features/ai/agent-op-review.d.ts +2 -2
  61. package/dist/features/ai/agent-op-review.js.map +1 -1
  62. package/dist/features/ai/agent-ops.d.ts +1 -1
  63. package/dist/features/ai/agent-ops.js +9 -6
  64. package/dist/features/ai/agent-ops.js.map +1 -1
  65. package/dist/features/ai/ai-apply.d.ts +1 -1
  66. package/dist/features/ai/ai-review.js +10 -1
  67. package/dist/features/ai/ai-review.js.map +1 -1
  68. package/dist/features/ai/components/AiSelectionMenu.js +78 -252
  69. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
  70. package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +2 -1
  71. package/dist/features/ai/components/AiSidebar.js +2 -2
  72. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  73. package/dist/features/ai/components/DocumentChatOps.js +1 -0
  74. package/dist/features/ai/components/DocumentChatOps.js.map +1 -1
  75. package/dist/features/ai/components/ModelQuickSwitch.js +1 -1
  76. package/dist/features/ai/components/SettingsAiTab.js +1 -1
  77. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  78. package/dist/features/ai/document-chat.d.ts +3 -2
  79. package/dist/features/ai/document-chat.js +16 -19
  80. package/dist/features/ai/document-chat.js.map +1 -1
  81. package/dist/features/ai/index.d.ts +5 -3
  82. package/dist/features/ai/index.js +4 -2
  83. package/dist/features/ai/protocol/agent-ops.d.ts +22 -17
  84. package/dist/features/ai/protocol/agent-ops.js +65 -39
  85. package/dist/features/ai/protocol/agent-ops.js.map +1 -1
  86. package/dist/features/ai/protocol/block-lines.d.ts +89 -0
  87. package/dist/features/ai/protocol/block-lines.js +177 -0
  88. package/dist/features/ai/protocol/block-lines.js.map +1 -0
  89. package/dist/features/ai/protocol/chat.js +10 -10
  90. package/dist/features/ai/protocol/chat.js.map +1 -1
  91. package/dist/features/ai/protocol/index.d.ts +3 -2
  92. package/dist/features/ai/protocol/index.js +3 -2
  93. package/dist/features/ai/react/AiSelectionMenu.d.ts +3 -2
  94. package/dist/features/ai/react/AiSelectionMenu.js +64 -255
  95. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
  96. package/dist/features/ai/react/AiSidebar.js +2 -2
  97. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  98. package/dist/features/ai/react/DocumentChatOps.js +1 -0
  99. package/dist/features/ai/react/DocumentChatOps.js.map +1 -1
  100. package/dist/features/ai/react/ModelQuickSwitch.js +1 -1
  101. package/dist/features/ai/react/SettingsAiTab.js +1 -1
  102. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  103. package/dist/features/ai/react/use-ai-selection-flow.js +34 -0
  104. package/dist/features/ai/react/use-ai-selection-flow.js.map +1 -0
  105. package/dist/features/ai/selection-flow.d.ts +133 -0
  106. package/dist/features/ai/selection-flow.js +283 -0
  107. package/dist/features/ai/selection-flow.js.map +1 -0
  108. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  109. package/dist/features/ai/use-document-chat.d.ts +1 -1
  110. package/dist/features/authors/components/AuthorColorsPanel.js +1 -1
  111. package/dist/features/authors/feature.js +1 -1
  112. package/dist/features/authors/index.js +1 -1
  113. package/dist/features/authors/react/AuthorColorsPanel.js +1 -1
  114. package/dist/features/comments/components/CommentBubble.js +1 -1
  115. package/dist/features/comments/components/CommentComposer.js +1 -1
  116. package/dist/features/comments/components/CommentThreadView.js +5 -2
  117. package/dist/features/comments/components/CommentThreadView.js.map +1 -1
  118. package/dist/features/comments/components/CommentsMargin.js +108 -230
  119. package/dist/features/comments/components/CommentsMargin.js.map +1 -1
  120. package/dist/features/comments/components/CommentsOverlay.js +1 -1
  121. package/dist/features/comments/feature.js +1 -1
  122. package/dist/features/comments/index.d.ts +4 -2
  123. package/dist/features/comments/index.js +4 -2
  124. package/dist/features/comments/margin-controller.d.ts +44 -0
  125. package/dist/features/comments/margin-controller.js +132 -0
  126. package/dist/features/comments/margin-controller.js.map +1 -0
  127. package/dist/features/comments/margin-layout.d.ts +55 -0
  128. package/dist/features/comments/margin-layout.js +79 -0
  129. package/dist/features/comments/margin-layout.js.map +1 -0
  130. package/dist/features/comments/model.d.ts +8 -0
  131. package/dist/features/comments/model.js +60 -34
  132. package/dist/features/comments/model.js.map +1 -1
  133. package/dist/features/comments/react/CommentBubble.js +1 -1
  134. package/dist/features/comments/react/CommentComposer.js +1 -1
  135. package/dist/features/comments/react/CommentThreadView.js +2 -2
  136. package/dist/features/comments/react/CommentThreadView.js.map +1 -1
  137. package/dist/features/comments/react/CommentsMargin.d.ts +0 -5
  138. package/dist/features/comments/react/CommentsMargin.js +85 -251
  139. package/dist/features/comments/react/CommentsMargin.js.map +1 -1
  140. package/dist/features/comments/react/CommentsOverlay.js +1 -1
  141. package/dist/features/comments/react/use-comments.js +1 -1
  142. package/dist/features/comments/react/use-margin-layout.js +45 -0
  143. package/dist/features/comments/react/use-margin-layout.js.map +1 -0
  144. package/dist/features/comments/react.d.ts +1 -1
  145. package/dist/features/comments/schema.d.ts +23 -0
  146. package/dist/features/comments/schema.js.map +1 -1
  147. package/dist/features/comments/snapshot-equality.js +40 -0
  148. package/dist/features/comments/snapshot-equality.js.map +1 -0
  149. package/dist/features/comments/thread-anchor.js +33 -0
  150. package/dist/features/comments/thread-anchor.js.map +1 -0
  151. package/dist/features/comments/use-comments.d.ts +3 -1
  152. package/dist/features/comments/use-comments.js +1 -0
  153. package/dist/features/comments/use-comments.js.map +1 -1
  154. package/dist/features/comments/use-margin-layout.js +29 -0
  155. package/dist/features/comments/use-margin-layout.js.map +1 -0
  156. package/dist/features/comments/vue.d.ts +1 -1
  157. package/dist/features/composites/accepted-children.d.ts +21 -0
  158. package/dist/features/composites/accepted-children.js +126 -0
  159. package/dist/features/composites/accepted-children.js.map +1 -0
  160. package/dist/features/composites/components/CompositeDetails.js +1 -1
  161. package/dist/features/composites/components/CompositeInspector.js +38 -164
  162. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  163. package/dist/features/composites/feature.d.ts +2 -1
  164. package/dist/features/composites/feature.js +8 -2
  165. package/dist/features/composites/feature.js.map +1 -1
  166. package/dist/features/composites/index.d.ts +5 -3
  167. package/dist/features/composites/index.js +8 -6
  168. package/dist/features/composites/inspector-model.d.ts +97 -0
  169. package/dist/features/composites/inspector-model.js +255 -0
  170. package/dist/features/composites/inspector-model.js.map +1 -0
  171. package/dist/features/composites/kinds.d.ts +11 -0
  172. package/dist/features/composites/kinds.js +17 -2
  173. package/dist/features/composites/kinds.js.map +1 -1
  174. package/dist/features/composites/outline.d.ts +18 -5
  175. package/dist/features/composites/outline.js +44 -12
  176. package/dist/features/composites/outline.js.map +1 -1
  177. package/dist/features/composites/react/CompositeDetails.js +1 -1
  178. package/dist/features/composites/react/CompositeInspector.d.ts +1 -1
  179. package/dist/features/composites/react/CompositeInspector.js +21 -180
  180. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  181. package/dist/features/composites/react/CompositeStatusMenu.d.ts +1 -1
  182. package/dist/features/composites/react/CompositeView.js +1 -1
  183. package/dist/features/composites/react/use-composite-inspector.js +29 -0
  184. package/dist/features/composites/react/use-composite-inspector.js.map +1 -0
  185. package/dist/features/composites/schema.d.ts +2 -2
  186. package/dist/features/composites/schema.js +2 -2
  187. package/dist/features/composites/schema.js.map +1 -1
  188. package/dist/features/composites/use-composite-anchors.d.ts +1 -1
  189. package/dist/features/composites/use-composite-inspector.js +41 -0
  190. package/dist/features/composites/use-composite-inspector.js.map +1 -0
  191. package/dist/features/composites/use-composite-rows.d.ts +1 -1
  192. package/dist/features/media/index.d.ts +1 -1
  193. package/dist/features/media/vue.d.ts +1 -1
  194. package/dist/features/mentions/index.d.ts +3 -3
  195. package/dist/features/mentions/index.js +3 -3
  196. package/dist/features/mentions/schema.d.ts +1 -1
  197. package/dist/features/mentions/schema.js +1 -1
  198. package/dist/features/themes/index.d.ts +1 -1
  199. package/dist/features/themes/index.js +1 -1
  200. package/dist/features/transcript/feature.js +1 -1
  201. package/dist/features/transcript/index.js +2 -2
  202. package/dist/i18n/de/ai.js +2 -0
  203. package/dist/i18n/de/ai.js.map +1 -1
  204. package/dist/i18n/de/composites.js +8 -1
  205. package/dist/i18n/de/composites.js.map +1 -1
  206. package/dist/i18n/de/tree.js +14 -1
  207. package/dist/i18n/de/tree.js.map +1 -1
  208. package/dist/i18n/de.d.ts +22 -0
  209. package/dist/i18n/en/ai.js +2 -0
  210. package/dist/i18n/en/ai.js.map +1 -1
  211. package/dist/i18n/en/composites.js +8 -1
  212. package/dist/i18n/en/composites.js.map +1 -1
  213. package/dist/i18n/en/tree.js +14 -1
  214. package/dist/i18n/en/tree.js.map +1 -1
  215. package/dist/integrations/composite-ai/composite-fill.js +2 -2
  216. package/dist/integrations/composite-ai/composite-summary.js +2 -2
  217. package/dist/integrations/composite-ai/index.js +2 -2
  218. package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
  219. package/dist/markdown/MarkdownSurface.d.ts +1 -1
  220. package/dist/markdown/MarkdownSurface.js +2 -2
  221. package/dist/markdown/MarkdownSurface.vue.d.ts +9 -9
  222. package/dist/markdown/index.d.ts +2 -2
  223. package/dist/nuxt/runtime/server/routes/collab/[id].js +1 -1
  224. package/dist/react/EditorSurface.d.ts +1 -1
  225. package/dist/react/EditorSurface.js +43 -149
  226. package/dist/react/EditorSurface.js.map +1 -1
  227. package/dist/react/EditorToolbar.js +3 -40
  228. package/dist/react/EditorToolbar.js.map +1 -1
  229. package/dist/schema/advanced.js +2 -2
  230. package/dist/server/adapter.d.ts +2 -2
  231. package/dist/server/collab.js +1 -1
  232. package/dist/server/handlers/ai.d.ts +4 -2
  233. package/dist/server/handlers/ai.js +11 -9
  234. package/dist/server/handlers/ai.js.map +1 -1
  235. package/dist/server/handlers/context.d.ts +1 -1
  236. package/dist/server/handlers/editor.d.ts +1 -1
  237. package/dist/server/handlers/editor.js +2 -2
  238. package/dist/server/host.d.ts +1 -1
  239. package/dist/server/index.d.ts +2 -2
  240. package/dist/settings/index.d.ts +1 -1
  241. package/dist/styles/tokens.css +1 -1
  242. package/dist/tree/DocumentTree.js +159 -387
  243. package/dist/tree/DocumentTree.js.map +1 -1
  244. package/dist/tree/drop-rules.js +37 -0
  245. package/dist/tree/drop-rules.js.map +1 -0
  246. package/dist/tree/react/DocumentTree.d.ts +2 -2
  247. package/dist/tree/react/DocumentTree.js +135 -397
  248. package/dist/tree/react/DocumentTree.js.map +1 -1
  249. package/dist/tree/react/use-tree-model.js +129 -0
  250. package/dist/tree/react/use-tree-model.js.map +1 -0
  251. package/dist/tree/react.js +1 -1
  252. package/dist/tree/tree-host.js +100 -0
  253. package/dist/tree/tree-host.js.map +1 -0
  254. package/dist/tree/tree-items.js +22 -43
  255. package/dist/tree/tree-items.js.map +1 -1
  256. package/dist/tree/tree-model.js +580 -0
  257. package/dist/tree/tree-model.js.map +1 -0
  258. package/dist/tree/tree-rows.js +77 -0
  259. package/dist/tree/tree-rows.js.map +1 -0
  260. package/dist/tree/use-tree-model.js +94 -0
  261. package/dist/tree/use-tree-model.js.map +1 -0
  262. package/dist/tree/vue.js +1 -1
  263. package/dist/vue/AdvancedEditor.vue.d.ts +6 -6
  264. package/dist/vue/SimpleEditor.vue.d.ts +2 -2
  265. package/dist/vue/components/EditorSurface.js +27 -87
  266. package/dist/vue/components/EditorSurface.js.map +1 -1
  267. package/dist/vue/components/EditorSurface.vue.d.ts +6 -6
  268. package/dist/vue/components/EditorToolbar.js +7 -44
  269. package/dist/vue/components/EditorToolbar.js.map +1 -1
  270. package/dist/vue/context.d.ts +1 -1
  271. package/dist/vue/context.js +1 -1
  272. package/dist/vue/context.js.map +1 -1
  273. package/dist/vue/use-editor-ui.d.ts +1 -1
  274. package/dist/vue/use-notifier.d.ts +1 -1
  275. package/docs/integration.md +97 -4
  276. package/docs/llms.md +9 -1
  277. package/docs/usage.md +10 -5
  278. package/package.json +67 -5
@@ -0,0 +1,33 @@
1
+ import * as Y from "yjs";
2
+ //#region src/features/comments/thread-anchor.ts
3
+ /** The document position `anchor` points at now; null when it cannot be resolved (no Yjs session, or the text's block is gone for good). */
4
+ function resolveThreadAnchor(state, resolver, anchor) {
5
+ try {
6
+ const position = Y.createRelativePositionFromJSON(anchor);
7
+ return resolver.resolve(state, {
8
+ from: position,
9
+ to: position
10
+ })?.from ?? null;
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ /**
16
+ * The anchor of the text in `range` (its first character); null without a Yjs session or when the position does not
17
+ * resolve back to `range.from` (a document the editor has not rendered yet), so no wrong place is ever stored.
18
+ * `state` must match the Y document.
19
+ */
20
+ function captureThreadAnchor(state, resolver, range) {
21
+ try {
22
+ const start = resolver.capture(state, range, true)?.from;
23
+ if (!(start instanceof Y.RelativePosition)) return null;
24
+ const anchor = JSON.parse(JSON.stringify(Y.relativePositionToJSON(start)));
25
+ return resolveThreadAnchor(state, resolver, anchor) === range.from ? anchor : null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ //#endregion
31
+ export { captureThreadAnchor, resolveThreadAnchor };
32
+
33
+ //# sourceMappingURL=thread-anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-anchor.js","names":[],"sources":["../../../src/features/comments/thread-anchor.ts"],"sourcesContent":["import type { EditorState } from '@tiptap/pm/state'\nimport * as Y from 'yjs'\nimport type { AnchorResolver } from '../../core'\nimport type { CommentAnchor } from './schema'\n\n// The thread anchor: where a thread's text starts, as a Yjs relative position kept in the thread's Y.Map. The position\n// mapping is the document's `AnchorResolver` (collab feature), the same one the `DocAnchors` of AI flows use.\n\n/** The document position `anchor` points at now; null when it cannot be resolved (no Yjs session, or the text's block is gone for good). */\nexport function resolveThreadAnchor(state: EditorState, resolver: AnchorResolver, anchor: CommentAnchor): number | null {\n try {\n const position = Y.createRelativePositionFromJSON(anchor)\n return resolver.resolve(state, { from: position, to: position })?.from ?? null\n }\n catch {\n return null\n }\n}\n\n/**\n * The anchor of the text in `range` (its first character); null without a Yjs session or when the position does not\n * resolve back to `range.from` (a document the editor has not rendered yet), so no wrong place is ever stored.\n * `state` must match the Y document.\n */\nexport function captureThreadAnchor(state: EditorState, resolver: AnchorResolver, range: { from: number, to: number }): CommentAnchor | null {\n try {\n // The Yjs resolver's range token is `{ from, to }` of relative positions; the thread keeps the start.\n const start = (resolver.capture(state, range, true) as { from?: unknown } | null)?.from\n if (!(start instanceof Y.RelativePosition)) return null\n // Plain JSON (the Y.ID instances of the relative position become `{ client, clock }`).\n const anchor = JSON.parse(JSON.stringify(Y.relativePositionToJSON(start))) as CommentAnchor\n return resolveThreadAnchor(state, resolver, anchor) === range.from ? anchor : null\n }\n catch {\n return null\n }\n}\n"],"mappings":";;;AASA,SAAgB,oBAAoB,OAAoB,UAA0B,QAAsC;CACtH,IAAI;EACF,MAAM,WAAW,EAAE,+BAA+B,MAAM;EACxD,OAAO,SAAS,QAAQ,OAAO;GAAE,MAAM;GAAU,IAAI;EAAS,CAAC,CAAC,EAAE,QAAQ;CAC5E,QACM;EACJ,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,oBAAoB,OAAoB,UAA0B,OAA2D;CAC3I,IAAI;EAEF,MAAM,QAAS,SAAS,QAAQ,OAAO,OAAO,IAAI,CAAC,EAAgC;EACnF,IAAI,EAAE,iBAAiB,EAAE,mBAAmB,OAAO;EAEnD,MAAM,SAAS,KAAK,MAAM,KAAK,UAAU,EAAE,uBAAuB,KAAK,CAAC,CAAC;EACzE,OAAO,oBAAoB,OAAO,UAAU,MAAM,MAAM,MAAM,OAAO,SAAS;CAChF,QACM;EACJ,OAAO;CACT;AACF"}
@@ -2,8 +2,8 @@ import { AuthorIdentity } from "../../core/feature.js";
2
2
  import "../../core/index.js";
3
3
  import { CommentThread } from "./schema.js";
4
4
  import { CommentsActions, DocRange } from "./model.js";
5
- import { ComputedRef, MaybeRefOrGetter, Ref } from "vue";
6
5
  import { Editor } from "@tiptap/core";
6
+ import { ComputedRef, MaybeRefOrGetter, Ref } from "vue";
7
7
  //#region src/features/comments/use-comments.d.ts
8
8
  /** What `useComments` returns; layouts pass it on to `CommentThreadView`. */
9
9
  export interface CommentsStore extends CommentsActions {
@@ -16,6 +16,8 @@ export interface CommentsStore extends CommentsActions {
16
16
  threads: Readonly<Ref<readonly CommentThread[]>>;
17
17
  /** Current text range per thread id (threads without one are orphans). */
18
18
  ranges: ComputedRef<ReadonlyMap<string, DocRange>>;
19
+ /** Where the text of an orphaned open thread was (document position); absent for orphans without a usable anchor. */
20
+ orphanAt: ComputedRef<ReadonlyMap<string, number>>;
19
21
  active: ComputedRef<string | null>;
20
22
  /** Current range of the draft a new comment is being written for. */
21
23
  draft: ComputedRef<DocRange | null>;
@@ -22,6 +22,7 @@ function useComments(editor) {
22
22
  user: computed(() => snapshot.value.user),
23
23
  threads: computed(() => snapshot.value.threads),
24
24
  ranges: computed(() => snapshot.value.ranges),
25
+ orphanAt: computed(() => snapshot.value.orphanAt),
25
26
  active: computed(() => snapshot.value.active),
26
27
  draft: computed(() => snapshot.value.draft),
27
28
  draftQuote: computed(() => snapshot.value.draftQuote),
@@ -1 +1 @@
1
- {"version":3,"file":"use-comments.js","names":[],"sources":["../../../src/features/comments/use-comments.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { computed, type ComputedRef, getCurrentScope, type MaybeRefOrGetter, onScopeDispose, type Ref, toRaw, toValue, watch } from 'vue'\nimport type { AuthorIdentity } from '../../core'\nimport { useStoreRef } from '../../vue/store-ref'\nimport { type CommentsActions, createEditorCommentsModel, type DocRange } from './model'\nimport type { CommentThread } from './schema'\n\n/** What `useComments` returns; layouts pass it on to `CommentThreadView`. */\nexport interface CommentsStore extends CommentsActions {\n /** Comments need a collaborative editor whose `Comments` extension has a user. */\n available: ComputedRef<boolean>\n /** The user may compose a comment now: the default model needs an editable editor, a host model may allow readers. */\n canComment: ComputedRef<boolean>\n user: ComputedRef<AuthorIdentity | null>\n /** Plain snapshots of all threads in the Y.Doc, replaced on every change. */\n threads: Readonly<Ref<readonly CommentThread[]>>\n /** Current text range per thread id (threads without one are orphans). */\n ranges: ComputedRef<ReadonlyMap<string, DocRange>>\n active: ComputedRef<string | null>\n /** Current range of the draft a new comment is being written for. */\n draft: ComputedRef<DocRange | null>\n /** The draft's text as the new thread will keep it (`quote`). */\n draftQuote: ComputedRef<string>\n /** Unresolved threads in document order, orphans last; resolved ones newest first. */\n open: ComputedRef<readonly CommentThread[]>\n resolved: ComputedRef<readonly CommentThread[]>\n /** The thread's text was deleted (no `comment` mark left). */\n isOrphan: (id: string) => boolean\n}\n\n/**\n * The comment threads of an editor as Vue refs: a thin hook over the comments model of the editor's surface (the host's\n * `model` of `commentsFeature`, else `createCommentsModel`: see there for storage and rules). `editor` may be a ref or getter that changes or turns `undefined` (the hook is then idle: no Y.Doc\n * observer). Stops with the current effect scope.\n */\nexport function useComments(editor: MaybeRefOrGetter<Editor | undefined>): CommentsStore {\n // The surface registry is keyed by the raw editor instance, not by a reactive proxy of it.\n const model = createEditorCommentsModel(() => {\n const value = toValue(editor)\n return value && toRaw(value)\n })\n const snapshot = useStoreRef(model.state)\n if (getCurrentScope()) onScopeDispose(model.dispose)\n watch(() => toValue(editor), () => model.refresh(), { immediate: true })\n\n const { add, reply, edit, setResolved, remove, removeReply, activate, startDraft, cancelDraft } = model\n return {\n available: computed(() => snapshot.value.available),\n canComment: computed(() => snapshot.value.canComment),\n user: computed(() => snapshot.value.user),\n threads: computed(() => snapshot.value.threads),\n ranges: computed(() => snapshot.value.ranges),\n active: computed(() => snapshot.value.active),\n draft: computed(() => snapshot.value.draft),\n draftQuote: computed(() => snapshot.value.draftQuote),\n open: computed(() => snapshot.value.open),\n resolved: computed(() => snapshot.value.resolved),\n isOrphan: id => !snapshot.value.ranges.has(id),\n add,\n reply,\n edit,\n setResolved,\n remove,\n removeReply,\n activate,\n startDraft,\n cancelDraft,\n }\n}\n"],"mappings":";;;;;;;;;AAmCA,SAAgB,YAAY,QAA6D;CAEvF,MAAM,QAAQ,gCAAgC;EAC5C,MAAM,QAAQ,QAAQ,MAAM;EAC5B,OAAO,SAAS,MAAM,KAAK;CAC7B,CAAC;CACD,MAAM,WAAW,YAAY,MAAM,KAAK;CACxC,IAAI,gBAAgB,GAAG,eAAe,MAAM,OAAO;CACnD,YAAY,QAAQ,MAAM,SAAS,MAAM,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CAEvE,MAAM,EAAE,KAAK,OAAO,MAAM,aAAa,QAAQ,aAAa,UAAU,YAAY,gBAAgB;CAClG,OAAO;EACL,WAAW,eAAe,SAAS,MAAM,SAAS;EAClD,YAAY,eAAe,SAAS,MAAM,UAAU;EACpD,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,SAAS,eAAe,SAAS,MAAM,OAAO;EAC9C,QAAQ,eAAe,SAAS,MAAM,MAAM;EAC5C,QAAQ,eAAe,SAAS,MAAM,MAAM;EAC5C,OAAO,eAAe,SAAS,MAAM,KAAK;EAC1C,YAAY,eAAe,SAAS,MAAM,UAAU;EACpD,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,UAAU,eAAe,SAAS,MAAM,QAAQ;EAChD,WAAU,OAAM,CAAC,SAAS,MAAM,OAAO,IAAI,EAAE;EAC7C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"use-comments.js","names":[],"sources":["../../../src/features/comments/use-comments.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { computed, type ComputedRef, getCurrentScope, type MaybeRefOrGetter, onScopeDispose, type Ref, toRaw, toValue, watch } from 'vue'\nimport type { AuthorIdentity } from '../../core'\nimport { useStoreRef } from '../../vue/store-ref'\nimport { type CommentsActions, createEditorCommentsModel, type DocRange } from './model'\nimport type { CommentThread } from './schema'\n\n/** What `useComments` returns; layouts pass it on to `CommentThreadView`. */\nexport interface CommentsStore extends CommentsActions {\n /** Comments need a collaborative editor whose `Comments` extension has a user. */\n available: ComputedRef<boolean>\n /** The user may compose a comment now: the default model needs an editable editor, a host model may allow readers. */\n canComment: ComputedRef<boolean>\n user: ComputedRef<AuthorIdentity | null>\n /** Plain snapshots of all threads in the Y.Doc, replaced on every change. */\n threads: Readonly<Ref<readonly CommentThread[]>>\n /** Current text range per thread id (threads without one are orphans). */\n ranges: ComputedRef<ReadonlyMap<string, DocRange>>\n /** Where the text of an orphaned open thread was (document position); absent for orphans without a usable anchor. */\n orphanAt: ComputedRef<ReadonlyMap<string, number>>\n active: ComputedRef<string | null>\n /** Current range of the draft a new comment is being written for. */\n draft: ComputedRef<DocRange | null>\n /** The draft's text as the new thread will keep it (`quote`). */\n draftQuote: ComputedRef<string>\n /** Unresolved threads in document order, orphans last; resolved ones newest first. */\n open: ComputedRef<readonly CommentThread[]>\n resolved: ComputedRef<readonly CommentThread[]>\n /** The thread's text was deleted (no `comment` mark left). */\n isOrphan: (id: string) => boolean\n}\n\n/**\n * The comment threads of an editor as Vue refs: a thin hook over the comments model of the editor's surface (the host's\n * `model` of `commentsFeature`, else `createCommentsModel`: see there for storage and rules). `editor` may be a ref or getter that changes or turns `undefined` (the hook is then idle: no Y.Doc\n * observer). Stops with the current effect scope.\n */\nexport function useComments(editor: MaybeRefOrGetter<Editor | undefined>): CommentsStore {\n // The surface registry is keyed by the raw editor instance, not by a reactive proxy of it.\n const model = createEditorCommentsModel(() => {\n const value = toValue(editor)\n return value && toRaw(value)\n })\n const snapshot = useStoreRef(model.state)\n if (getCurrentScope()) onScopeDispose(model.dispose)\n watch(() => toValue(editor), () => model.refresh(), { immediate: true })\n\n const { add, reply, edit, setResolved, remove, removeReply, activate, startDraft, cancelDraft } = model\n return {\n available: computed(() => snapshot.value.available),\n canComment: computed(() => snapshot.value.canComment),\n user: computed(() => snapshot.value.user),\n threads: computed(() => snapshot.value.threads),\n ranges: computed(() => snapshot.value.ranges),\n orphanAt: computed(() => snapshot.value.orphanAt),\n active: computed(() => snapshot.value.active),\n draft: computed(() => snapshot.value.draft),\n draftQuote: computed(() => snapshot.value.draftQuote),\n open: computed(() => snapshot.value.open),\n resolved: computed(() => snapshot.value.resolved),\n isOrphan: id => !snapshot.value.ranges.has(id),\n add,\n reply,\n edit,\n setResolved,\n remove,\n removeReply,\n activate,\n startDraft,\n cancelDraft,\n }\n}\n"],"mappings":";;;;;;;;;AAqCA,SAAgB,YAAY,QAA6D;CAEvF,MAAM,QAAQ,gCAAgC;EAC5C,MAAM,QAAQ,QAAQ,MAAM;EAC5B,OAAO,SAAS,MAAM,KAAK;CAC7B,CAAC;CACD,MAAM,WAAW,YAAY,MAAM,KAAK;CACxC,IAAI,gBAAgB,GAAG,eAAe,MAAM,OAAO;CACnD,YAAY,QAAQ,MAAM,SAAS,MAAM,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CAEvE,MAAM,EAAE,KAAK,OAAO,MAAM,aAAa,QAAQ,aAAa,UAAU,YAAY,gBAAgB;CAClG,OAAO;EACL,WAAW,eAAe,SAAS,MAAM,SAAS;EAClD,YAAY,eAAe,SAAS,MAAM,UAAU;EACpD,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,SAAS,eAAe,SAAS,MAAM,OAAO;EAC9C,QAAQ,eAAe,SAAS,MAAM,MAAM;EAC5C,UAAU,eAAe,SAAS,MAAM,QAAQ;EAChD,QAAQ,eAAe,SAAS,MAAM,MAAM;EAC5C,OAAO,eAAe,SAAS,MAAM,KAAK;EAC1C,YAAY,eAAe,SAAS,MAAM,UAAU;EACpD,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,UAAU,eAAe,SAAS,MAAM,QAAQ;EAChD,WAAU,OAAM,CAAC,SAAS,MAAM,OAAO,IAAI,EAAE;EAC7C;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -0,0 +1,29 @@
1
+ import { createMarginController } from "./margin-controller.js";
2
+ import { useStoreRef } from "../../vue/store-ref.js";
3
+ import { nextTick, onBeforeUnmount, onMounted, toValue, watch } from "vue";
4
+ //#region src/features/comments/use-margin-layout.ts
5
+ /**
6
+ * The layout of the comments margin as Vue state: a thin hook over `createMarginController` (see there for the
7
+ * measuring). Hand the column, the card and the resolved section elements in through the returned `set*` functions
8
+ * (as function refs); `state` holds the measured tops. Re-measures when `source`'s cards or active card change.
9
+ * Call from `setup()`; it starts on mount and stops on unmount.
10
+ */
11
+ function useMarginLayout(options) {
12
+ const controller = createMarginController(options.source);
13
+ const state = useStoreRef(controller.state);
14
+ watch(() => toValue(options.editor), (editor) => controller.setEditor(editor), { immediate: true });
15
+ watch(() => toValue(options.container), (container) => controller.setContainer(container), { immediate: true });
16
+ watch([() => options.source.cards(), () => options.source.activeId()], () => nextTick(controller.schedule));
17
+ onMounted(controller.connect);
18
+ onBeforeUnmount(controller.disconnect);
19
+ return {
20
+ state,
21
+ setColumn: controller.setColumn,
22
+ setResolvedSection: controller.setResolvedSection,
23
+ setCard: controller.setCard
24
+ };
25
+ }
26
+ //#endregion
27
+ export { useMarginLayout };
28
+
29
+ //# sourceMappingURL=use-margin-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-margin-layout.js","names":[],"sources":["../../../src/features/comments/use-margin-layout.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type MaybeRefOrGetter, nextTick, onBeforeUnmount, onMounted, toValue, watch } from 'vue'\nimport { useStoreRef } from '../../vue/store-ref'\nimport { createMarginController, type MarginSource } from './margin-controller'\n\n/**\n * The layout of the comments margin as Vue state: a thin hook over `createMarginController` (see there for the\n * measuring). Hand the column, the card and the resolved section elements in through the returned `set*` functions\n * (as function refs); `state` holds the measured tops. Re-measures when `source`'s cards or active card change.\n * Call from `setup()`; it starts on mount and stops on unmount.\n */\nexport function useMarginLayout(options: {\n editor: MaybeRefOrGetter<Editor | undefined>\n /** The editor's scroll container, which the column sits in: its size changes re-run the layout. */\n container: MaybeRefOrGetter<HTMLElement | null>\n source: MarginSource\n}) {\n const controller = createMarginController(options.source)\n const state = useStoreRef(controller.state)\n\n watch(() => toValue(options.editor), editor => controller.setEditor(editor), { immediate: true })\n watch(() => toValue(options.container), container => controller.setContainer(container), { immediate: true })\n // Cards appearing, disappearing or expanding: measure once the DOM has them.\n watch([() => options.source.cards(), () => options.source.activeId()], () => nextTick(controller.schedule))\n\n onMounted(controller.connect)\n onBeforeUnmount(controller.disconnect)\n\n return { state, setColumn: controller.setColumn, setResolvedSection: controller.setResolvedSection, setCard: controller.setCard }\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAgB,gBAAgB,SAK7B;CACD,MAAM,aAAa,uBAAuB,QAAQ,MAAM;CACxD,MAAM,QAAQ,YAAY,WAAW,KAAK;CAE1C,YAAY,QAAQ,QAAQ,MAAM,IAAG,WAAU,WAAW,UAAU,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;CAChG,YAAY,QAAQ,QAAQ,SAAS,IAAG,cAAa,WAAW,aAAa,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;CAE5G,MAAM,OAAO,QAAQ,OAAO,MAAM,SAAS,QAAQ,OAAO,SAAS,CAAC,SAAS,SAAS,WAAW,QAAQ,CAAC;CAE1G,UAAU,WAAW,OAAO;CAC5B,gBAAgB,WAAW,UAAU;CAErC,OAAO;EAAE;EAAO,WAAW,WAAW;EAAW,oBAAoB,WAAW;EAAoB,SAAS,WAAW;CAAQ;AAClI"}
@@ -1,5 +1,5 @@
1
- import { VueFeature } from "../../vue/index.js";
2
1
  import { CommentsFeatureOptions } from "./feature.js";
2
+ import { VueFeature } from "../../vue/index.js";
3
3
  import _default from "./components/CommentBubble.vue.js";
4
4
  import _default$1 from "./components/CommentComposer.vue.js";
5
5
  import _default$3 from "./components/CommentsListMenu.vue.js";
@@ -0,0 +1,21 @@
1
+ import { Translate } from "../../i18n/index.js";
2
+ import { Notifier } from "../../core/notify.js";
3
+ import { Extension } from "@tiptap/core";
4
+ import { Node as Node$1 } from "@tiptap/pm/model";
5
+ //#region src/features/composites/accepted-children.d.ts
6
+ export interface AcceptedChildrenOptions {
7
+ /** Shows the notice of a refused edit (default: dropped). The composites feature passes the surface's `ctx.notify`. */
8
+ notify: Notifier;
9
+ /** The translator of the notice. The composites feature passes the surface's `ctx.t`. */
10
+ t: Translate;
11
+ }
12
+ /**
13
+ * Enforces the kinds' accepted children (`CompositeKind.accepts`) for every local edit at one place: a transaction that
14
+ * leaves the document with more Bausteine nested in a Baustein that does not accept them than before is refused — block
15
+ * drag, „In Baustein umwandeln“, paste, AI inserts and review acceptance, `moveBlockInto` — and the user is told why.
16
+ * Existing violations stay: transactions that add none (typing, undoing) pass, and so do collaboration replays, undo/redo
17
+ * and loading a whole document. Client-only, no schema: kinds are read from the editor (`compositeKindsOf`).
18
+ */
19
+ export declare const AcceptedChildren: Extension<AcceptedChildrenOptions, any>;
20
+ //#endregion
21
+ //# sourceMappingURL=accepted-children.d.ts.map
@@ -0,0 +1,126 @@
1
+ import { createTranslator } from "../../i18n/index.js";
2
+ import { compositeKind, kindAccepts } from "./kinds.js";
3
+ import { compositeKindsOf } from "./nodes.js";
4
+ import { Extension } from "@tiptap/core";
5
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
6
+ import { AddMarkStep, AttrStep, RemoveMarkStep, ReplaceStep } from "@tiptap/pm/transform";
7
+ //#region src/features/composites/accepted-children.ts
8
+ /** The short sentence for a refused nesting, naming both kinds by label (German: „Notiz nimmt keine Aufgabe auf.“). */
9
+ function nestingReason(kinds, parent, child, t) {
10
+ return t("composites.nesting.refused", {
11
+ parent: compositeKind(kinds, parent).label,
12
+ child: compositeKind(kinds, child).label
13
+ });
14
+ }
15
+ /**
16
+ * The Bausteine of `node`'s subtree, outermost first: `node` itself when it is a Baustein, otherwise the Bausteine
17
+ * directly inside it (not those nested in another Baustein).
18
+ */
19
+ function outermostComposites(node) {
20
+ if (node.type.name === "composite") return [node];
21
+ const found = [];
22
+ node.descendants((child) => {
23
+ if (child.type.name === "composite") {
24
+ found.push(child);
25
+ return false;
26
+ }
27
+ return !child.isTextblock;
28
+ });
29
+ return found;
30
+ }
31
+ /**
32
+ * Every Baustein in `doc` whose nearest Baustein ancestor (quotes and lists in between do not count) does not accept
33
+ * its kind, in document order. Top-level Bausteine have no parent and never violate.
34
+ */
35
+ function nestingViolations(doc, kinds) {
36
+ const found = [];
37
+ function walk(node, parentKind) {
38
+ node.forEach((child) => {
39
+ if (child.isTextblock || child.isLeaf) return;
40
+ if (child.type.name === "composite") {
41
+ const kind = String(child.attrs.kind);
42
+ if (parentKind !== null && !kindAccepts(kinds, parentKind, kind)) found.push({
43
+ parent: parentKind,
44
+ child: kind
45
+ });
46
+ walk(child, kind);
47
+ } else walk(child, parentKind);
48
+ });
49
+ }
50
+ walk(doc, null);
51
+ return found;
52
+ }
53
+ /** A step that cannot change which Baustein sits in which: marks, text typed or deleted inside one textblock, attributes other than `kind`. */
54
+ function keepsNesting(step, before) {
55
+ if (step instanceof AddMarkStep || step instanceof RemoveMarkStep) return true;
56
+ if (step instanceof AttrStep) return step.attr !== "kind";
57
+ if (step instanceof ReplaceStep) {
58
+ const { slice } = step;
59
+ if (slice.openStart !== 0 || slice.openEnd !== 0 || !slice.content.content.every((child) => child.isInline)) return false;
60
+ const $from = before.resolve(step.from);
61
+ return $from.parent.isTextblock && $from.sameParent(before.resolve(step.to));
62
+ }
63
+ return false;
64
+ }
65
+ /**
66
+ * Transactions that were not made by this user's edit: collaboration (y-sync marks remote changes and Yjs undo/redo
67
+ * with `isChangeOrigin`) and prosemirror-history's undo/redo. They replay what was allowed, or what others wrote.
68
+ */
69
+ function isReplay(tr) {
70
+ return tr.getMeta("y-sync$")?.isChangeOrigin === true || tr.getMeta("history$") !== void 0;
71
+ }
72
+ /** The pair of kinds that occurs more often in `after` than in `before`, if any. */
73
+ function addedViolation(before, after) {
74
+ const counts = /* @__PURE__ */ new Map();
75
+ const key = (v) => JSON.stringify([v.parent, v.child]);
76
+ for (const v of before) counts.set(key(v), (counts.get(key(v)) ?? 0) + 1);
77
+ for (const v of after) {
78
+ const left = counts.get(key(v)) ?? 0;
79
+ if (left === 0) return v;
80
+ counts.set(key(v), left - 1);
81
+ }
82
+ return after[0];
83
+ }
84
+ /**
85
+ * Enforces the kinds' accepted children (`CompositeKind.accepts`) for every local edit at one place: a transaction that
86
+ * leaves the document with more Bausteine nested in a Baustein that does not accept them than before is refused — block
87
+ * drag, „In Baustein umwandeln“, paste, AI inserts and review acceptance, `moveBlockInto` — and the user is told why.
88
+ * Existing violations stay: transactions that add none (typing, undoing) pass, and so do collaboration replays, undo/redo
89
+ * and loading a whole document. Client-only, no schema: kinds are read from the editor (`compositeKindsOf`).
90
+ */
91
+ const AcceptedChildren = Extension.create({
92
+ name: "acceptedChildren",
93
+ addOptions: () => ({
94
+ notify: () => {},
95
+ t: createTranslator()
96
+ }),
97
+ addProseMirrorPlugins() {
98
+ const { editor } = this;
99
+ const { notify, t } = this.options;
100
+ return [new Plugin({
101
+ key: new PluginKey("acceptedChildren"),
102
+ filterTransaction(tr, state) {
103
+ if (!tr.docChanged || isReplay(tr)) return true;
104
+ if (tr.steps.some((step, index) => step instanceof ReplaceStep && step.from === 0 && step.to === tr.docs[index].content.size)) return true;
105
+ if (tr.steps.every((step, index) => keepsNesting(step, tr.docs[index]))) return true;
106
+ const kinds = compositeKindsOf(editor);
107
+ const before = nestingViolations(state.doc, kinds);
108
+ const after = nestingViolations(tr.doc, kinds);
109
+ if (after.length <= before.length) return true;
110
+ const added = addedViolation(before, after);
111
+ notify({
112
+ id: "composites-accepted-children",
113
+ title: t("composites.nesting.title"),
114
+ ...added && { description: nestingReason(kinds, added.parent, added.child, t) },
115
+ kind: "warning",
116
+ icon: "i-lucide-ban"
117
+ });
118
+ return false;
119
+ }
120
+ })];
121
+ }
122
+ });
123
+ //#endregion
124
+ export { AcceptedChildren, nestingReason, nestingViolations, outermostComposites };
125
+
126
+ //# sourceMappingURL=accepted-children.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accepted-children.js","names":[],"sources":["../../../src/features/composites/accepted-children.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type Transaction } from '@tiptap/pm/state'\nimport { AddMarkStep, AttrStep, RemoveMarkStep, ReplaceStep, type Step } from '@tiptap/pm/transform'\nimport type { Notifier } from '../../core/notify'\nimport { createTranslator, type Translate } from '../../i18n'\nimport { type CompositeKind, compositeKind, kindAccepts } from './kinds'\nimport { compositeKindsOf } from './nodes'\n\n/** One Baustein nested in a Baustein that does not accept its kind (both kind ids). */\nexport interface NestingViolation {\n parent: string\n child: string\n}\n\n/** The short sentence for a refused nesting, naming both kinds by label (German: „Notiz nimmt keine Aufgabe auf.“). */\nexport function nestingReason(kinds: readonly CompositeKind[], parent: string, child: string, t: Translate): string {\n return t('composites.nesting.refused', { parent: compositeKind(kinds, parent).label, child: compositeKind(kinds, child).label })\n}\n\n/**\n * The Bausteine of `node`'s subtree, outermost first: `node` itself when it is a Baustein, otherwise the Bausteine\n * directly inside it (not those nested in another Baustein).\n */\nexport function outermostComposites(node: PMNode): PMNode[] {\n if (node.type.name === 'composite') return [node]\n const found: PMNode[] = []\n node.descendants((child) => {\n if (child.type.name === 'composite') {\n found.push(child)\n return false\n }\n return !child.isTextblock\n })\n return found\n}\n\n/**\n * Every Baustein in `doc` whose nearest Baustein ancestor (quotes and lists in between do not count) does not accept\n * its kind, in document order. Top-level Bausteine have no parent and never violate.\n */\nexport function nestingViolations(doc: PMNode, kinds: readonly CompositeKind[]): NestingViolation[] {\n const found: NestingViolation[] = []\n function walk(node: PMNode, parentKind: string | null) {\n node.forEach((child) => {\n if (child.isTextblock || child.isLeaf) return\n if (child.type.name === 'composite') {\n const kind = String(child.attrs.kind)\n if (parentKind !== null && !kindAccepts(kinds, parentKind, kind)) found.push({ parent: parentKind, child: kind })\n walk(child, kind)\n }\n else walk(child, parentKind)\n })\n }\n walk(doc, null)\n return found\n}\n\n/** A step that cannot change which Baustein sits in which: marks, text typed or deleted inside one textblock, attributes other than `kind`. */\nfunction keepsNesting(step: Step, before: PMNode): boolean {\n if (step instanceof AddMarkStep || step instanceof RemoveMarkStep) return true\n if (step instanceof AttrStep) return step.attr !== 'kind'\n if (step instanceof ReplaceStep) {\n const { slice } = step\n if (slice.openStart !== 0 || slice.openEnd !== 0 || !slice.content.content.every(child => child.isInline)) return false\n const $from = before.resolve(step.from)\n return $from.parent.isTextblock && $from.sameParent(before.resolve(step.to))\n }\n return false\n}\n\n/**\n * Transactions that were not made by this user's edit: collaboration (y-sync marks remote changes and Yjs undo/redo\n * with `isChangeOrigin`) and prosemirror-history's undo/redo. They replay what was allowed, or what others wrote.\n */\nfunction isReplay(tr: Transaction): boolean {\n const sync = tr.getMeta('y-sync$') as { isChangeOrigin?: boolean } | undefined\n return sync?.isChangeOrigin === true || tr.getMeta('history$') !== undefined\n}\n\n/** The pair of kinds that occurs more often in `after` than in `before`, if any. */\nfunction addedViolation(before: readonly NestingViolation[], after: readonly NestingViolation[]): NestingViolation | undefined {\n const counts = new Map<string, number>()\n const key = (v: NestingViolation) => JSON.stringify([v.parent, v.child])\n for (const v of before) counts.set(key(v), (counts.get(key(v)) ?? 0) + 1)\n for (const v of after) {\n const left = counts.get(key(v)) ?? 0\n if (left === 0) return v\n counts.set(key(v), left - 1)\n }\n return after[0]\n}\n\nexport interface AcceptedChildrenOptions {\n /** Shows the notice of a refused edit (default: dropped). The composites feature passes the surface's `ctx.notify`. */\n notify: Notifier\n /** The translator of the notice. The composites feature passes the surface's `ctx.t`. */\n t: Translate\n}\n\n/**\n * Enforces the kinds' accepted children (`CompositeKind.accepts`) for every local edit at one place: a transaction that\n * leaves the document with more Bausteine nested in a Baustein that does not accept them than before is refused — block\n * drag, „In Baustein umwandeln“, paste, AI inserts and review acceptance, `moveBlockInto` — and the user is told why.\n * Existing violations stay: transactions that add none (typing, undoing) pass, and so do collaboration replays, undo/redo\n * and loading a whole document. Client-only, no schema: kinds are read from the editor (`compositeKindsOf`).\n */\nexport const AcceptedChildren = Extension.create<AcceptedChildrenOptions>({\n name: 'acceptedChildren',\n addOptions: () => ({ notify: () => {}, t: createTranslator() }),\n addProseMirrorPlugins() {\n const { editor } = this\n const { notify, t } = this.options\n return [new Plugin({\n key: new PluginKey('acceptedChildren'),\n filterTransaction(tr, state) {\n if (!tr.docChanged || isReplay(tr)) return true\n // `setContent` loads a whole new document; that is not an edit of the existing one.\n if (tr.steps.some((step, index) => step instanceof ReplaceStep && step.from === 0 && step.to === tr.docs[index]!.content.size)) return true\n if (tr.steps.every((step, index) => keepsNesting(step, tr.docs[index]!))) return true\n const kinds = compositeKindsOf(editor)\n const before = nestingViolations(state.doc, kinds)\n const after = nestingViolations(tr.doc, kinds)\n if (after.length <= before.length) return true\n const added = addedViolation(before, after)\n notify({\n id: 'composites-accepted-children',\n title: t('composites.nesting.title'),\n ...(added && { description: nestingReason(kinds, added.parent, added.child, t) }),\n kind: 'warning',\n icon: 'i-lucide-ban',\n })\n return false\n },\n })]\n },\n})\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
+ import { compositeKindsOf } from "../nodes.js";
1
2
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
2
3
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
3
- import { compositeKindsOf } from "../nodes.js";
4
4
  import { compositeOfRow } from "../rows.js";
5
5
  import _sfc_main$1 from "./CompositeFieldEditor.js";
6
6
  import _sfc_main$2 from "./CompositeSummaryEditor.js";