@blocknote/core 0.42.2 → 0.43.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 (200) hide show
  1. package/dist/BlockNoteExtension-BWw0r8Gy.cjs +2 -0
  2. package/dist/BlockNoteExtension-BWw0r8Gy.cjs.map +1 -0
  3. package/dist/BlockNoteExtension-C2X7LW-V.js +25 -0
  4. package/dist/BlockNoteExtension-C2X7LW-V.js.map +1 -0
  5. package/dist/BlockNoteSchema-CbSavEwr.js +270 -0
  6. package/dist/BlockNoteSchema-CbSavEwr.js.map +1 -0
  7. package/dist/BlockNoteSchema-D8TyvlfU.cjs +2 -0
  8. package/dist/BlockNoteSchema-D8TyvlfU.cjs.map +1 -0
  9. package/dist/EventEmitter-CLwfmbqG.cjs +2 -0
  10. package/dist/EventEmitter-CLwfmbqG.cjs.map +1 -0
  11. package/dist/EventEmitter-CjSwpTbz.js +27 -0
  12. package/dist/EventEmitter-CjSwpTbz.js.map +1 -0
  13. package/dist/ShowSelection-BW37oJ6h.cjs +2 -0
  14. package/dist/ShowSelection-BW37oJ6h.cjs.map +1 -0
  15. package/dist/ShowSelection-Dz-NEase.js +43 -0
  16. package/dist/ShowSelection-Dz-NEase.js.map +1 -0
  17. package/dist/TrailingNode-BUhuMJrB.js +2096 -0
  18. package/dist/TrailingNode-BUhuMJrB.js.map +1 -0
  19. package/dist/TrailingNode-CaT_wbho.cjs +2 -0
  20. package/dist/TrailingNode-CaT_wbho.cjs.map +1 -0
  21. package/dist/{blockToNode-DIfPWLH8.js → blockToNode-DBNbhwwC.js} +33 -33
  22. package/dist/blockToNode-DBNbhwwC.js.map +1 -0
  23. package/dist/blockToNode-w7H99R6p.cjs.map +1 -1
  24. package/dist/blocknote.cjs +4 -4
  25. package/dist/blocknote.cjs.map +1 -1
  26. package/dist/blocknote.js +2401 -5592
  27. package/dist/blocknote.js.map +1 -1
  28. package/dist/blocks.cjs +1 -1
  29. package/dist/blocks.js +71 -70
  30. package/dist/blocks.js.map +1 -1
  31. package/dist/comments.cjs +1 -1
  32. package/dist/comments.cjs.map +1 -1
  33. package/dist/comments.js +451 -137
  34. package/dist/comments.js.map +1 -1
  35. package/dist/{BlockNoteSchema-Bi-eeHal.js → defaultBlocks-BJtxTOM2.js} +991 -1047
  36. package/dist/defaultBlocks-BJtxTOM2.js.map +1 -0
  37. package/dist/defaultBlocks-BxFclIGP.cjs +6 -0
  38. package/dist/defaultBlocks-BxFclIGP.cjs.map +1 -0
  39. package/dist/extensions.cjs +2 -0
  40. package/dist/extensions.cjs.map +1 -0
  41. package/dist/extensions.js +57 -0
  42. package/dist/extensions.js.map +1 -0
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/dist/webpack-stats.json +1 -1
  45. package/dist/yjs.js +1 -1
  46. package/package.json +12 -6
  47. package/src/api/nodeConversions/blockToNode.ts +1 -1
  48. package/src/api/nodeConversions/nodeToBlock.ts +1 -1
  49. package/src/blocks/Code/block.ts +4 -4
  50. package/src/blocks/Divider/block.ts +2 -2
  51. package/src/blocks/File/helpers/render/createAddFileButton.ts +7 -5
  52. package/src/blocks/Heading/block.ts +23 -20
  53. package/src/blocks/ListItem/BulletListItem/block.ts +2 -2
  54. package/src/blocks/ListItem/CheckListItem/block.ts +2 -2
  55. package/src/blocks/ListItem/NumberedListItem/block.ts +3 -3
  56. package/src/blocks/ListItem/ToggleListItem/block.ts +2 -2
  57. package/src/blocks/PageBreak/getPageBreakSlashMenuItems.ts +2 -2
  58. package/src/blocks/Paragraph/block.ts +2 -2
  59. package/src/blocks/Quote/block.ts +2 -2
  60. package/src/blocks/Table/block.ts +4 -3
  61. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +2 -1
  62. package/src/comments/extension.ts +353 -0
  63. package/src/comments/index.ts +2 -1
  64. package/src/comments/types.ts +8 -0
  65. package/src/{extensions/Comments → comments}/userstore/UserStore.ts +2 -2
  66. package/src/editor/BlockNoteEditor.test.ts +2 -23
  67. package/src/editor/BlockNoteEditor.ts +66 -453
  68. package/src/editor/BlockNoteExtension.test.ts +103 -0
  69. package/src/editor/BlockNoteExtension.ts +174 -56
  70. package/src/editor/managers/EventManager.ts +64 -35
  71. package/src/editor/managers/ExtensionManager/extensions.ts +214 -0
  72. package/src/editor/managers/ExtensionManager/index.ts +514 -0
  73. package/src/editor/managers/ExtensionManager/symbol.ts +6 -0
  74. package/src/editor/managers/SelectionManager.ts +5 -1
  75. package/src/editor/managers/StateManager.ts +29 -17
  76. package/src/editor/managers/index.ts +1 -5
  77. package/src/extensions/BlockChange/{BlockChangePlugin.ts → BlockChange.ts} +27 -29
  78. package/src/extensions/Collaboration/{ForkYDocPlugin.test.ts → ForkYDoc.test.ts} +6 -5
  79. package/src/extensions/Collaboration/ForkYDoc.ts +158 -0
  80. package/src/extensions/Collaboration/YCursorPlugin.ts +183 -0
  81. package/src/extensions/Collaboration/YSync.ts +16 -0
  82. package/src/extensions/Collaboration/YUndo.ts +12 -0
  83. package/src/extensions/Collaboration/schemaMigration/SchemaMigration.ts +59 -0
  84. package/src/extensions/DropCursor/DropCursor.ts +26 -0
  85. package/src/extensions/FilePanel/FilePanel.ts +41 -0
  86. package/src/extensions/FormattingToolbar/FormattingToolbar.ts +119 -0
  87. package/src/extensions/History/History.ts +11 -0
  88. package/src/extensions/LinkToolbar/LinkToolbar.ts +121 -0
  89. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.ts +74 -0
  90. package/src/extensions/Placeholder/Placeholder.ts +148 -0
  91. package/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.ts → PreviousBlockType.ts} +9 -13
  92. package/src/extensions/ShowSelection/{ShowSelectionPlugin.ts → ShowSelection.ts} +27 -33
  93. package/src/extensions/SideMenu/{SideMenuPlugin.ts → SideMenu.ts} +63 -83
  94. package/src/extensions/SuggestionMenu/{SuggestionPlugin.ts → SuggestionMenu.ts} +71 -77
  95. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +29 -44
  96. package/src/extensions/TableHandles/{TableHandlesPlugin.ts → TableHandles.ts} +416 -437
  97. package/src/extensions/TrailingNode/{TrailingNodeExtension.ts → TrailingNode.ts} +8 -17
  98. package/src/extensions/index.ts +24 -0
  99. package/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.ts +1 -1
  100. package/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.ts +21 -16
  101. package/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.ts +1 -1
  102. package/src/extensions/tiptap-extensions/index.ts +31 -0
  103. package/src/index.ts +1 -13
  104. package/src/schema/blocks/createSpec.ts +14 -11
  105. package/src/schema/blocks/internal.ts +2 -2
  106. package/src/schema/blocks/types.ts +8 -5
  107. package/src/schema/schema.ts +11 -36
  108. package/src/util/topo-sort.ts +46 -0
  109. package/types/src/comments/extension.d.ts +70 -0
  110. package/types/src/comments/index.d.ts +2 -1
  111. package/types/src/comments/types.d.ts +8 -0
  112. package/types/src/{extensions/Comments → comments}/userstore/UserStore.d.ts +2 -2
  113. package/types/src/editor/BlockNoteEditor.d.ts +34 -105
  114. package/types/src/editor/BlockNoteExtension.d.ts +87 -22
  115. package/types/src/editor/managers/EventManager.d.ts +25 -16
  116. package/types/src/editor/managers/ExtensionManager/extensions.d.ts +8 -0
  117. package/types/src/editor/managers/ExtensionManager/index.d.ts +83 -0
  118. package/types/src/editor/managers/ExtensionManager/symbol.d.ts +5 -0
  119. package/types/src/editor/managers/StateManager.d.ts +1 -12
  120. package/types/src/editor/managers/index.d.ts +1 -2
  121. package/types/src/extensions/BlockChange/BlockChange.d.ts +16 -0
  122. package/types/src/extensions/Collaboration/ForkYDoc.d.ts +34 -0
  123. package/types/src/extensions/Collaboration/ForkYDoc.test.d.ts +1 -0
  124. package/types/src/extensions/Collaboration/YCursorPlugin.d.ts +24 -0
  125. package/types/src/extensions/Collaboration/YSync.d.ts +8 -0
  126. package/types/src/extensions/Collaboration/YUndo.d.ts +12 -0
  127. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigration.d.ts +8 -0
  128. package/types/src/extensions/DropCursor/DropCursor.d.ts +5 -0
  129. package/types/src/extensions/FilePanel/FilePanel.d.ts +11 -0
  130. package/types/src/extensions/FormattingToolbar/FormattingToolbar.d.ts +9 -0
  131. package/types/src/extensions/History/History.d.ts +6 -0
  132. package/types/src/extensions/LinkToolbar/LinkToolbar.d.ts +24 -0
  133. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.d.ts +5 -0
  134. package/types/src/extensions/Placeholder/Placeholder.d.ts +6 -0
  135. package/types/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.d.ts → PreviousBlockType.d.ts} +9 -5
  136. package/types/src/extensions/ShowSelection/ShowSelection.d.ts +21 -0
  137. package/types/src/extensions/SideMenu/{SideMenuPlugin.d.ts → SideMenu.d.ts} +11 -15
  138. package/types/src/extensions/SuggestionMenu/SuggestionMenu.d.ts +54 -0
  139. package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +1 -1
  140. package/types/src/extensions/TableHandles/{TableHandlesPlugin.d.ts → TableHandles.d.ts} +28 -31
  141. package/types/src/extensions/TrailingNode/TrailingNode.d.ts +8 -0
  142. package/types/src/extensions/index.d.ts +24 -0
  143. package/types/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.d.ts +1 -1
  144. package/types/src/extensions/tiptap-extensions/index.d.ts +11 -0
  145. package/types/src/index.d.ts +1 -13
  146. package/types/src/schema/blocks/createSpec.d.ts +4 -4
  147. package/types/src/schema/blocks/internal.d.ts +2 -2
  148. package/types/src/schema/blocks/types.d.ts +5 -5
  149. package/types/src/util/topo-sort.d.ts +8 -0
  150. package/dist/BlockNoteSchema-Bi-eeHal.js.map +0 -1
  151. package/dist/BlockNoteSchema-DjDaA2C3.cjs +0 -6
  152. package/dist/BlockNoteSchema-DjDaA2C3.cjs.map +0 -1
  153. package/dist/blockToNode-DIfPWLH8.js.map +0 -1
  154. package/src/comments/models/User.ts +0 -8
  155. package/src/editor/BlockNoteExtensions.ts +0 -325
  156. package/src/editor/managers/CollaborationManager.ts +0 -212
  157. package/src/editor/managers/ExtensionManager.ts +0 -130
  158. package/src/extensions/Collaboration/CursorPlugin.ts +0 -189
  159. package/src/extensions/Collaboration/ForkYDocPlugin.ts +0 -192
  160. package/src/extensions/Collaboration/SyncPlugin.ts +0 -18
  161. package/src/extensions/Collaboration/UndoPlugin.ts +0 -18
  162. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +0 -59
  163. package/src/extensions/Comments/CommentsPlugin.ts +0 -392
  164. package/src/extensions/FilePanel/FilePanelPlugin.ts +0 -206
  165. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +0 -363
  166. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +0 -380
  167. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +0 -75
  168. package/src/extensions/Placeholder/PlaceholderPlugin.ts +0 -147
  169. package/types/src/comments/models/User.d.ts +0 -8
  170. package/types/src/editor/BlockNoteExtensions.d.ts +0 -43
  171. package/types/src/editor/managers/CollaborationManager.d.ts +0 -115
  172. package/types/src/editor/managers/ExtensionManager.d.ts +0 -68
  173. package/types/src/extensions/BlockChange/BlockChangePlugin.d.ts +0 -15
  174. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +0 -37
  175. package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +0 -41
  176. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +0 -7
  177. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +0 -9
  178. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +0 -7
  179. package/types/src/extensions/Comments/CommentsPlugin.d.ts +0 -66
  180. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +0 -31
  181. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +0 -41
  182. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +0 -42
  183. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +0 -5
  184. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +0 -6
  185. package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +0 -15
  186. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +0 -31
  187. package/types/src/extensions/TrailingNode/TrailingNodeExtension.d.ts +0 -13
  188. /package/src/{extensions/Comments/CommentMark.ts → comments/mark.ts} +0 -0
  189. /package/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.ts +0 -0
  190. /package/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.ts +0 -0
  191. /package/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.ts +0 -0
  192. /package/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.ts +0 -0
  193. /package/types/src/{extensions/Comments/CommentMark.d.ts → comments/mark.d.ts} +0 -0
  194. /package/types/src/{extensions/Collaboration/ForkYDocPlugin.test.d.ts → editor/BlockNoteExtension.test.d.ts} +0 -0
  195. /package/types/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.d.ts +0 -0
  196. /package/types/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.d.ts +0 -0
  197. /package/types/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.d.ts +0 -0
  198. /package/types/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.d.ts +0 -0
  199. /package/types/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.d.ts +0 -0
  200. /package/types/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.d.ts +0 -0
@@ -1,392 +0,0 @@
1
- import { Node } from "prosemirror-model";
2
- import { Plugin, PluginKey } from "prosemirror-state";
3
- import { Decoration, DecorationSet } from "prosemirror-view";
4
- import { getRelativeSelection, ySyncPluginKey } from "y-prosemirror";
5
- import type {
6
- CommentBody,
7
- ThreadData,
8
- ThreadStore,
9
- User,
10
- } from "../../comments/index.js";
11
- import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
12
- import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
13
- import { CustomBlockNoteSchema } from "../../schema/schema.js";
14
- import { UserStore } from "./userstore/UserStore.js";
15
- import { getMarkRange } from "@tiptap/core";
16
-
17
- const PLUGIN_KEY = new PluginKey(`blocknote-comments`);
18
- const SET_SELECTED_THREAD_ID = "SET_SELECTED_THREAD_ID";
19
-
20
- type CommentsPluginState = {
21
- /**
22
- * Decorations to be rendered, specifically to indicate the selected thread
23
- */
24
- decorations: DecorationSet;
25
- };
26
-
27
- /**
28
- * Calculate the thread positions from the current document state
29
- */
30
- function getUpdatedThreadPositions(doc: Node, markType: string) {
31
- const threadPositions = new Map<string, { from: number; to: number }>();
32
-
33
- // find all thread marks and store their position + create decoration for selected thread
34
- doc.descendants((node, pos) => {
35
- node.marks.forEach((mark) => {
36
- if (mark.type.name === markType) {
37
- const thisThreadId = (mark.attrs as { threadId: string | undefined })
38
- .threadId;
39
- if (!thisThreadId) {
40
- return;
41
- }
42
- const from = pos;
43
- const to = from + node.nodeSize;
44
-
45
- // FloatingThreads component uses "to" as the position, so always store the largest "to" found
46
- // AnchoredThreads component uses "from" as the position, so always store the smallest "from" found
47
- const currentPosition = threadPositions.get(thisThreadId) ?? {
48
- from: Infinity,
49
- to: 0,
50
- };
51
- threadPositions.set(thisThreadId, {
52
- from: Math.min(from, currentPosition.from),
53
- to: Math.max(to, currentPosition.to),
54
- });
55
- }
56
- });
57
- });
58
- return threadPositions;
59
- }
60
-
61
- export class CommentsPlugin extends BlockNoteExtension {
62
- public static key() {
63
- return "comments";
64
- }
65
-
66
- public readonly userStore: UserStore<User>;
67
-
68
- /**
69
- * Whether a comment is currently being composed
70
- */
71
- private pendingComment = false;
72
-
73
- /**
74
- * The currently selected thread id
75
- */
76
- private selectedThreadId: string | undefined;
77
-
78
- /**
79
- * Store the positions of all threads in the document.
80
- * this can be used later to implement a floating sidebar
81
- */
82
- private threadPositions: Map<string, { from: number; to: number }> =
83
- new Map();
84
-
85
- private emitStateUpdate() {
86
- this.emit("update", {
87
- selectedThreadId: this.selectedThreadId,
88
- pendingComment: this.pendingComment,
89
- threadPositions: this.threadPositions,
90
- });
91
- }
92
-
93
- /**
94
- * when a thread is resolved or deleted, we need to update the marks to reflect the new state
95
- */
96
- private updateMarksFromThreads = (threads: Map<string, ThreadData>) => {
97
- this.editor.transact((tr) => {
98
- tr.doc.descendants((node, pos) => {
99
- node.marks.forEach((mark) => {
100
- if (mark.type.name === this.markType) {
101
- const markType = mark.type;
102
- const markThreadId = mark.attrs.threadId;
103
- const thread = threads.get(markThreadId);
104
- const isOrphan = !!(!thread || thread.resolved || thread.deletedAt);
105
-
106
- if (isOrphan !== mark.attrs.orphan) {
107
- const trimmedFrom = Math.max(pos, 0);
108
- const trimmedTo = Math.min(
109
- pos + node.nodeSize,
110
- tr.doc.content.size - 1,
111
- tr.doc.content.size - 1,
112
- );
113
- tr.removeMark(trimmedFrom, trimmedTo, mark);
114
- tr.addMark(
115
- trimmedFrom,
116
- trimmedTo,
117
- markType.create({
118
- ...mark.attrs,
119
- orphan: isOrphan,
120
- }),
121
- );
122
-
123
- if (isOrphan && this.selectedThreadId === markThreadId) {
124
- // unselect
125
- this.selectedThreadId = undefined;
126
- this.emitStateUpdate();
127
- }
128
- }
129
- }
130
- });
131
- });
132
- });
133
- };
134
-
135
- constructor(
136
- private readonly editor: BlockNoteEditor<any, any, any>,
137
- public readonly threadStore: ThreadStore,
138
- private readonly markType: string,
139
- public readonly resolveUsers:
140
- | undefined
141
- | ((userIds: string[]) => Promise<User[]>),
142
- public readonly commentEditorSchema?: CustomBlockNoteSchema<any, any, any>,
143
- ) {
144
- super();
145
-
146
- if (!resolveUsers) {
147
- throw new Error("resolveUsers is required for comments");
148
- }
149
- this.userStore = new UserStore<User>(resolveUsers);
150
-
151
- // Note: Plugins are currently not destroyed when the editor is destroyed.
152
- // We should unsubscribe from the threadStore when the editor is destroyed.
153
- this.threadStore.subscribe(this.updateMarksFromThreads);
154
-
155
- editor.onCreate(() => {
156
- // Need to wait for TipTap editor state to be initialized
157
- this.updateMarksFromThreads(this.threadStore.getThreads());
158
- editor.onSelectionChange(() => {
159
- if (this.pendingComment) {
160
- this.pendingComment = false;
161
- this.emitStateUpdate();
162
- }
163
- });
164
- });
165
-
166
- // eslint-disable-next-line @typescript-eslint/no-this-alias
167
- const self = this;
168
-
169
- this.addProsemirrorPlugin(
170
- new Plugin<CommentsPluginState>({
171
- key: PLUGIN_KEY,
172
- state: {
173
- init() {
174
- return {
175
- decorations: DecorationSet.empty,
176
- };
177
- },
178
- apply(tr, state) {
179
- const action = tr.getMeta(PLUGIN_KEY);
180
-
181
- if (!tr.docChanged && !action) {
182
- return state;
183
- }
184
-
185
- // only update threadPositions if the doc changed
186
- const threadPositions = tr.docChanged
187
- ? getUpdatedThreadPositions(tr.doc, self.markType)
188
- : self.threadPositions;
189
-
190
- if (threadPositions.size > 0 || self.threadPositions.size > 0) {
191
- // small optimization; don't emit event if threadPositions before / after were both empty
192
- self.threadPositions = threadPositions;
193
- self.emitStateUpdate();
194
- }
195
-
196
- // update decorations if doc or selected thread changed
197
- const decorations = [];
198
-
199
- if (self.selectedThreadId) {
200
- const selectedThreadPosition = threadPositions.get(
201
- self.selectedThreadId,
202
- );
203
-
204
- if (selectedThreadPosition) {
205
- decorations.push(
206
- Decoration.inline(
207
- selectedThreadPosition.from,
208
- selectedThreadPosition.to,
209
- {
210
- class: "bn-thread-mark-selected",
211
- },
212
- ),
213
- );
214
- }
215
- }
216
-
217
- return {
218
- decorations: DecorationSet.create(tr.doc, decorations),
219
- };
220
- },
221
- },
222
- props: {
223
- decorations(state) {
224
- return (
225
- PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty
226
- );
227
- },
228
- /**
229
- * Handle click on a thread mark and mark it as selected
230
- */
231
- handleClick: (view, pos, event) => {
232
- if (event.button !== 0) {
233
- return;
234
- }
235
-
236
- const node = view.state.doc.nodeAt(pos);
237
-
238
- if (!node) {
239
- self.selectThread(undefined);
240
- return;
241
- }
242
-
243
- const markInSchema = view.state.schema.marks[markType];
244
- const resolvedPos = view.state.doc.resolve(pos);
245
- const commentMark = node.marks
246
- .filter(
247
- (mark) =>
248
- mark.type.name === markType && mark.attrs.orphan !== true,
249
- )
250
- .map((mark) => {
251
- // get the range of this mark within the document, to check how close it is to the click position
252
- const range = getMarkRange(
253
- resolvedPos,
254
- markInSchema,
255
- mark.attrs,
256
- )!;
257
-
258
- return {
259
- mark,
260
- // calculate how far the mark is from the click position
261
- distance:
262
- (Math.abs(range.from - pos) + Math.abs(range.to - pos)) / 2,
263
- // calculate the length of text the mark spans
264
- length: range.to - range.from,
265
- };
266
- })
267
- // This allows us to not have comments which are unreachable because they are completely overlapped by other comments (issue #2073)
268
- .sort((a, b) => {
269
- // Find the mark which is closest to the click position
270
- if (a.distance !== b.distance) {
271
- return a.distance - b.distance;
272
- }
273
- // Otherwise, select the mark which spans the smallest amount of text (most likely to be unreachable)
274
- return a.length - b.length;
275
- })[0]?.mark;
276
-
277
- const threadId = commentMark?.attrs.threadId as string | undefined;
278
- self.selectThread(threadId);
279
- },
280
- },
281
- }),
282
- );
283
- }
284
-
285
- /**
286
- * Subscribe to state updates
287
- */
288
- public onUpdate(
289
- callback: (state: {
290
- pendingComment: boolean;
291
- selectedThreadId: string | undefined;
292
- threadPositions: Map<string, { from: number; to: number }>;
293
- }) => void,
294
- ) {
295
- return this.on("update", callback);
296
- }
297
-
298
- /**
299
- * Set the selected thread
300
- */
301
- public selectThread(threadId: string | undefined) {
302
- if (this.selectedThreadId === threadId) {
303
- return;
304
- }
305
- this.selectedThreadId = threadId;
306
- this.emitStateUpdate();
307
- this.editor.transact((tr) =>
308
- tr.setMeta(PLUGIN_KEY, {
309
- name: SET_SELECTED_THREAD_ID,
310
- }),
311
- );
312
-
313
- if (threadId) {
314
- const selectedThreadPosition = this.threadPositions.get(threadId);
315
-
316
- if (!selectedThreadPosition) {
317
- return;
318
- }
319
-
320
- // When a new thread is selected, scrolls the page to its reference text in
321
- // the editor.
322
- (
323
- this.editor.prosemirrorView?.domAtPos(selectedThreadPosition.from)
324
- .node as Element | undefined
325
- )?.scrollIntoView({
326
- behavior: "smooth",
327
- block: "center",
328
- });
329
- }
330
- }
331
-
332
- /**
333
- * Start a pending comment (e.g.: when clicking the "Add comment" button)
334
- */
335
- public startPendingComment() {
336
- this.pendingComment = true;
337
- this.emitStateUpdate();
338
- }
339
-
340
- /**
341
- * Stop a pending comment (e.g.: user closes the comment composer)
342
- */
343
- public stopPendingComment() {
344
- this.pendingComment = false;
345
- this.emitStateUpdate();
346
- }
347
-
348
- /**
349
- * Create a thread at the current selection
350
- */
351
- public async createThread(options: {
352
- initialComment: {
353
- body: CommentBody;
354
- metadata?: any;
355
- };
356
- metadata?: any;
357
- }) {
358
- const thread = await this.threadStore.createThread(options);
359
-
360
- if (this.threadStore.addThreadToDocument) {
361
- // creating the mark is handled by the store
362
- // this is useful if we don't have write-access to the document.
363
- // We can then offload the responsibility of creating the mark to the server.
364
- // (e.g.: RESTYjsThreadStore)
365
- const view = this.editor.prosemirrorView!;
366
- const pmSelection = view.state.selection;
367
-
368
- const ystate = ySyncPluginKey.getState(view.state);
369
-
370
- const selection = {
371
- prosemirror: {
372
- head: pmSelection.head,
373
- anchor: pmSelection.anchor,
374
- },
375
- yjs: ystate
376
- ? getRelativeSelection(ystate.binding, view.state)
377
- : undefined, // if we're not using yjs
378
- };
379
-
380
- await this.threadStore.addThreadToDocument({
381
- threadId: thread.id,
382
- selection,
383
- });
384
- } else {
385
- // we create the mark directly in the document
386
- this.editor._tiptapEditor.commands.setMark(this.markType, {
387
- orphan: false,
388
- threadId: thread.id,
389
- });
390
- }
391
- }
392
- }
@@ -1,206 +0,0 @@
1
- import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
2
- import { EditorView } from "prosemirror-view";
3
-
4
- import { ySyncPluginKey } from "y-prosemirror";
5
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
6
- import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
7
- import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
8
- import type {
9
- BlockFromConfig,
10
- InlineContentSchema,
11
- StyleSchema,
12
- } from "../../schema/index.js";
13
-
14
- export type FilePanelState<
15
- I extends InlineContentSchema,
16
- S extends StyleSchema,
17
- > = UiElementPosition & {
18
- // TODO: This typing is not quite right (children should be from BSchema)
19
- block: BlockFromConfig<any, I, S>;
20
- };
21
-
22
- export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
23
- implements PluginView
24
- {
25
- public state?: FilePanelState<I, S>;
26
- public emitUpdate: () => void;
27
-
28
- constructor(
29
- private readonly editor: BlockNoteEditor<Record<string, any>, I, S>,
30
- private readonly pluginKey: PluginKey<FilePanelState<I, S>>,
31
- private readonly pmView: EditorView,
32
- emitUpdate: (state: FilePanelState<I, S>) => void,
33
- ) {
34
- this.emitUpdate = () => {
35
- if (!this.state) {
36
- throw new Error("Attempting to update uninitialized file panel");
37
- }
38
-
39
- emitUpdate(this.state);
40
- };
41
-
42
- pmView.dom.addEventListener("mousedown", this.mouseDownHandler);
43
- pmView.dom.addEventListener("dragstart", this.dragstartHandler);
44
-
45
- // Setting capture=true ensures that any parent container of the editor that
46
- // gets scrolled will trigger the scroll event. Scroll events do not bubble
47
- // and so won't propagate to the document by default.
48
- pmView.root.addEventListener("scroll", this.scrollHandler, true);
49
- }
50
-
51
- mouseDownHandler = () => {
52
- if (this.state?.show) {
53
- this.state.show = false;
54
- this.emitUpdate();
55
- }
56
- };
57
-
58
- // For dragging the whole editor.
59
- dragstartHandler = () => {
60
- if (this.state?.show) {
61
- this.state.show = false;
62
- this.emitUpdate();
63
- }
64
- };
65
-
66
- scrollHandler = () => {
67
- if (this.state?.show) {
68
- const blockElement = this.pmView.root.querySelector(
69
- `[data-node-type="blockContainer"][data-id="${this.state.block.id}"]`,
70
- );
71
- if (!blockElement) {
72
- return;
73
- }
74
- this.state.referencePos = blockElement.getBoundingClientRect();
75
- this.emitUpdate();
76
- }
77
- };
78
-
79
- update(view: EditorView, prevState: EditorState) {
80
- const pluginState = this.pluginKey.getState(view.state);
81
- const prevPluginState = this.pluginKey.getState(prevState);
82
-
83
- if (!this.state?.show && pluginState?.block && this.editor.isEditable) {
84
- const blockElement = this.pmView.root.querySelector(
85
- `[data-node-type="blockContainer"][data-id="${pluginState.block.id}"]`,
86
- );
87
- if (!blockElement) {
88
- return;
89
- }
90
- this.state = {
91
- show: true,
92
- referencePos: blockElement.getBoundingClientRect(),
93
- block: pluginState.block,
94
- };
95
-
96
- this.emitUpdate();
97
-
98
- return;
99
- }
100
-
101
- const isOpening = pluginState?.block && !prevPluginState?.block;
102
- const isClosing = !pluginState?.block && prevPluginState?.block;
103
- if (isOpening && this.state && !this.state.show) {
104
- this.state.show = true;
105
- this.emitUpdate();
106
- }
107
- if (isClosing && this.state?.show) {
108
- this.state.show = false;
109
- this.emitUpdate();
110
- }
111
- }
112
-
113
- closeMenu = () => {
114
- if (this.state?.show) {
115
- this.state.show = false;
116
- this.emitUpdate();
117
- }
118
- };
119
-
120
- destroy() {
121
- this.pmView.dom.removeEventListener("mousedown", this.mouseDownHandler);
122
-
123
- this.pmView.dom.removeEventListener("dragstart", this.dragstartHandler);
124
-
125
- this.pmView.root.removeEventListener("scroll", this.scrollHandler, true);
126
- }
127
- }
128
-
129
- const filePanelPluginKey = new PluginKey<FilePanelState<any, any>>(
130
- "FilePanelPlugin",
131
- );
132
-
133
- export class FilePanelProsemirrorPlugin<
134
- I extends InlineContentSchema,
135
- S extends StyleSchema,
136
- > extends BlockNoteExtension {
137
- public static key() {
138
- return "filePanel";
139
- }
140
-
141
- private view: FilePanelView<I, S> | undefined;
142
-
143
- constructor(editor: BlockNoteEditor<Record<string, any>, I, S>) {
144
- super();
145
- this.addProsemirrorPlugin(
146
- new Plugin<{
147
- block: BlockFromConfig<any, I, S> | undefined;
148
- }>({
149
- key: filePanelPluginKey,
150
- view: (editorView) => {
151
- this.view = new FilePanelView<I, S>(
152
- editor,
153
- filePanelPluginKey as any,
154
- editorView,
155
- (state) => {
156
- this.emit("update", state);
157
- },
158
- );
159
- return this.view;
160
- },
161
- props: {
162
- handleKeyDown: (_view, event: KeyboardEvent) => {
163
- if (event.key === "Escape" && this.shown) {
164
- this.view?.closeMenu();
165
- return true;
166
- }
167
- return false;
168
- },
169
- },
170
- state: {
171
- init: () => {
172
- return {
173
- block: undefined,
174
- };
175
- },
176
- apply: (transaction, prev) => {
177
- const state: FilePanelState<I, S> | undefined =
178
- transaction.getMeta(filePanelPluginKey);
179
-
180
- if (state) {
181
- return state;
182
- }
183
-
184
- if (
185
- !transaction.getMeta(ySyncPluginKey) &&
186
- (transaction.selectionSet || transaction.docChanged)
187
- ) {
188
- return { block: undefined };
189
- }
190
- return prev;
191
- },
192
- },
193
- }),
194
- );
195
- }
196
-
197
- public get shown() {
198
- return this.view?.state?.show || false;
199
- }
200
-
201
- public onUpdate(callback: (state: FilePanelState<I, S>) => void) {
202
- return this.on("update", callback);
203
- }
204
-
205
- public closeMenu = () => this.view?.closeMenu();
206
- }