@blocknote/core 0.30.1 → 0.31.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.
- package/dist/blocknote.cjs +9 -9
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +2793 -2213
- package/dist/blocknote.js.map +1 -1
- package/dist/{en-D4taoCs4.cjs → en-BXVKCwYt.cjs} +2 -2
- package/dist/en-BXVKCwYt.cjs.map +1 -0
- package/dist/{en-B7ycW7c8.js → en-qGo6sk9V.js} +2 -3
- package/dist/en-qGo6sk9V.js.map +1 -0
- package/dist/locales.cjs +1 -1
- package/dist/locales.cjs.map +1 -1
- package/dist/locales.js +20 -39
- package/dist/locales.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +5 -6
- package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +2 -3
- package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +1 -1
- package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +2816 -0
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +158 -0
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +87 -17
- package/src/api/blockManipulation/selections/selection.ts +48 -1
- package/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.ts → textCursorPosition.ts} +7 -7
- package/src/api/getBlockInfoFromPos.ts +1 -1
- package/src/api/nodeConversions/blockToNode.ts +5 -2
- package/src/api/nodeConversions/nodeToBlock.ts +203 -8
- package/src/api/pmUtil.ts +3 -3
- package/src/blocks/CodeBlockContent/CodeBlockContent.ts +6 -6
- package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +1 -1
- package/src/blocks/TableBlockContent/TableBlockContent.ts +32 -2
- package/src/editor/Block.css +27 -1
- package/src/editor/BlockNoteEditor.test.ts +7 -0
- package/src/editor/BlockNoteEditor.ts +124 -39
- package/src/editor/BlockNoteExtension.ts +26 -0
- package/src/editor/BlockNoteExtensions.ts +28 -12
- package/src/editor/BlockNoteTipTapEditor.ts +23 -2
- package/src/extensions/Collaboration/CursorPlugin.ts +13 -7
- package/src/extensions/Collaboration/ForkYDocPlugin.test.ts +166 -0
- package/src/extensions/Collaboration/ForkYDocPlugin.ts +174 -0
- package/src/extensions/Collaboration/SyncPlugin.ts +7 -4
- package/src/extensions/Collaboration/UndoPlugin.ts +7 -4
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +30 -0
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +30 -0
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -0
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -0
- package/src/extensions/Comments/CommentsPlugin.ts +79 -70
- package/src/extensions/FilePanel/FilePanelPlugin.ts +54 -49
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +60 -26
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +26 -21
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +49 -42
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +115 -108
- package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +183 -170
- package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +26 -19
- package/src/extensions/SideMenu/SideMenuPlugin.ts +23 -18
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +172 -168
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +4 -4
- package/src/extensions/Suggestions/SuggestionMarks.ts +175 -0
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +157 -150
- package/src/i18n/locales/ar.ts +0 -1
- package/src/i18n/locales/de.ts +0 -1
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/es.ts +0 -1
- package/src/i18n/locales/fr.ts +0 -1
- package/src/i18n/locales/hr.ts +0 -1
- package/src/i18n/locales/is.ts +0 -1
- package/src/i18n/locales/it.ts +0 -1
- package/src/i18n/locales/ja.ts +0 -1
- package/src/i18n/locales/ko.ts +0 -1
- package/src/i18n/locales/nl.ts +0 -1
- package/src/i18n/locales/no.ts +0 -1
- package/src/i18n/locales/pl.ts +0 -1
- package/src/i18n/locales/pt.ts +0 -1
- package/src/i18n/locales/ru.ts +0 -1
- package/src/i18n/locales/sk.ts +0 -1
- package/src/i18n/locales/uk.ts +0 -1
- package/src/i18n/locales/vi.ts +0 -1
- package/src/i18n/locales/zh-tw.ts +0 -1
- package/src/i18n/locales/zh.ts +0 -1
- package/src/index.ts +18 -8
- package/src/pm-nodes/BlockContainer.ts +1 -1
- package/src/pm-nodes/BlockGroup.ts +1 -1
- package/src/pm-nodes/Doc.ts +1 -0
- package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +1 -1
- package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +3 -1
- package/types/src/api/blockManipulation/selections/selection.d.ts +10 -0
- package/types/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.d.ts → textCursorPosition.d.ts} +2 -2
- package/types/src/api/nodeConversions/nodeToBlock.d.ts +39 -2
- package/types/src/api/pmUtil.d.ts +3 -3
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +9 -1
- package/types/src/editor/BlockNoteEditor.d.ts +62 -10
- package/types/src/editor/BlockNoteExtension.d.ts +9 -0
- package/types/src/editor/BlockNoteExtensions.d.ts +2 -2
- package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
- package/types/src/extensions/Collaboration/CursorPlugin.d.ts +3 -3
- package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +41 -0
- package/types/src/extensions/Collaboration/SyncPlugin.d.ts +3 -3
- package/types/src/extensions/Collaboration/UndoPlugin.d.ts +3 -3
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +3 -4
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +6 -5
- package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +4 -4
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +3 -3
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +3 -3
- package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +3 -3
- package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +3 -3
- package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +4 -4
- package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +3 -4
- package/types/src/extensions/Suggestions/SuggestionMarks.d.ts +4 -0
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +6 -6
- package/types/src/i18n/locales/en.d.ts +0 -1
- package/types/src/i18n/locales/sk.d.ts +0 -1
- package/types/src/index.d.ts +15 -8
- package/dist/en-B7ycW7c8.js.map +0 -1
- package/dist/en-D4taoCs4.cjs.map +0 -1
- package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +0 -844
- package/src/api/blockManipulation/selections/selection.test.ts +0 -72
- package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +0 -316
- package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +0 -74
- package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.d.ts +0 -1
- /package/types/src/{api/blockManipulation/selections/selection.test.d.ts → extensions/Collaboration/ForkYDocPlugin.test.d.ts} +0 -0
|
@@ -2,15 +2,15 @@ import { findParentNode } from "@tiptap/core";
|
|
|
2
2
|
import { EditorState, Plugin, PluginKey } from "prosemirror-state";
|
|
3
3
|
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
|
4
4
|
|
|
5
|
+
import { trackPosition } from "../../api/positionMapping.js";
|
|
5
6
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
7
|
+
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
6
8
|
import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
|
|
7
9
|
import {
|
|
8
10
|
BlockSchema,
|
|
9
11
|
InlineContentSchema,
|
|
10
12
|
StyleSchema,
|
|
11
13
|
} from "../../schema/index.js";
|
|
12
|
-
import { EventEmitter } from "../../util/EventEmitter.js";
|
|
13
|
-
import { trackPosition } from "../../api/positionMapping.js";
|
|
14
14
|
|
|
15
15
|
const findBlock = findParentNode((node) => node.type.name === "blockContainer");
|
|
16
16
|
|
|
@@ -166,190 +166,194 @@ export class SuggestionMenuProseMirrorPlugin<
|
|
|
166
166
|
BSchema extends BlockSchema,
|
|
167
167
|
I extends InlineContentSchema,
|
|
168
168
|
S extends StyleSchema,
|
|
169
|
-
> extends
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
> extends BlockNoteExtension {
|
|
170
|
+
public static key() {
|
|
171
|
+
return "suggestionMenu";
|
|
172
|
+
}
|
|
172
173
|
|
|
174
|
+
private view: SuggestionMenuView<BSchema, I, S> | undefined;
|
|
173
175
|
private triggerCharacters: string[] = [];
|
|
174
176
|
|
|
175
177
|
constructor(editor: BlockNoteEditor<BSchema, I, S>) {
|
|
176
178
|
super();
|
|
177
179
|
const triggerCharacters = this.triggerCharacters;
|
|
178
|
-
this.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
state: {
|
|
192
|
-
// Initialize the plugin's internal state.
|
|
193
|
-
init(): SuggestionPluginState {
|
|
194
|
-
return undefined;
|
|
180
|
+
this.addProsemirrorPlugin(
|
|
181
|
+
new Plugin({
|
|
182
|
+
key: suggestionMenuPluginKey,
|
|
183
|
+
|
|
184
|
+
view: () => {
|
|
185
|
+
this.view = new SuggestionMenuView<BSchema, I, S>(
|
|
186
|
+
editor,
|
|
187
|
+
(triggerCharacter, state) => {
|
|
188
|
+
this.emit(`update ${triggerCharacter}`, state);
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
return this.view;
|
|
195
192
|
},
|
|
196
193
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
prev,
|
|
201
|
-
_oldState,
|
|
202
|
-
newState,
|
|
203
|
-
): SuggestionPluginState => {
|
|
204
|
-
// TODO: More clearly define which transactions should be ignored.
|
|
205
|
-
if (transaction.getMeta("orderedListIndexing") !== undefined) {
|
|
206
|
-
return prev;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Ignore transactions in code blocks.
|
|
210
|
-
if (transaction.selection.$from.parent.type.spec.code) {
|
|
211
|
-
return prev;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Either contains the trigger character if the menu should be shown,
|
|
215
|
-
// or null if it should be hidden.
|
|
216
|
-
const suggestionPluginTransactionMeta: {
|
|
217
|
-
triggerCharacter: string;
|
|
218
|
-
deleteTriggerCharacter?: boolean;
|
|
219
|
-
ignoreQueryLength?: boolean;
|
|
220
|
-
} | null = transaction.getMeta(suggestionMenuPluginKey);
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
typeof suggestionPluginTransactionMeta === "object" &&
|
|
224
|
-
suggestionPluginTransactionMeta !== null
|
|
225
|
-
) {
|
|
226
|
-
if (prev) {
|
|
227
|
-
// Close the previous menu if it exists
|
|
228
|
-
this.closeMenu();
|
|
229
|
-
}
|
|
230
|
-
const trackedPosition = trackPosition(
|
|
231
|
-
editor,
|
|
232
|
-
newState.selection.from -
|
|
233
|
-
// Need to account for the trigger char that was inserted, so we offset the position by the length of the trigger character.
|
|
234
|
-
suggestionPluginTransactionMeta.triggerCharacter.length,
|
|
235
|
-
);
|
|
236
|
-
return {
|
|
237
|
-
triggerCharacter:
|
|
238
|
-
suggestionPluginTransactionMeta.triggerCharacter,
|
|
239
|
-
deleteTriggerCharacter:
|
|
240
|
-
suggestionPluginTransactionMeta.deleteTriggerCharacter !==
|
|
241
|
-
false,
|
|
242
|
-
// When reading the queryStartPos, we offset the result by the length of the trigger character, to make it easy on the caller
|
|
243
|
-
queryStartPos: () =>
|
|
244
|
-
trackedPosition() +
|
|
245
|
-
suggestionPluginTransactionMeta.triggerCharacter.length,
|
|
246
|
-
query: "",
|
|
247
|
-
decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,
|
|
248
|
-
ignoreQueryLength:
|
|
249
|
-
suggestionPluginTransactionMeta?.ignoreQueryLength,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Checks if the menu is hidden, in which case it doesn't need to be hidden or updated.
|
|
254
|
-
if (prev === undefined) {
|
|
255
|
-
return prev;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Checks if the menu should be hidden.
|
|
259
|
-
if (
|
|
260
|
-
// Highlighting text should hide the menu.
|
|
261
|
-
newState.selection.from !== newState.selection.to ||
|
|
262
|
-
// Transactions with plugin metadata should hide the menu.
|
|
263
|
-
suggestionPluginTransactionMeta === null ||
|
|
264
|
-
// Certain mouse events should hide the menu.
|
|
265
|
-
// TODO: Change to global mousedown listener.
|
|
266
|
-
transaction.getMeta("focus") ||
|
|
267
|
-
transaction.getMeta("blur") ||
|
|
268
|
-
transaction.getMeta("pointer") ||
|
|
269
|
-
// Moving the caret before the character which triggered the menu should hide it.
|
|
270
|
-
(prev.triggerCharacter !== undefined &&
|
|
271
|
-
newState.selection.from < prev.queryStartPos()) ||
|
|
272
|
-
// Moving the caret to a new block should hide the menu.
|
|
273
|
-
!newState.selection.$from.sameParent(
|
|
274
|
-
newState.doc.resolve(prev.queryStartPos()),
|
|
275
|
-
)
|
|
276
|
-
) {
|
|
194
|
+
state: {
|
|
195
|
+
// Initialize the plugin's internal state.
|
|
196
|
+
init(): SuggestionPluginState {
|
|
277
197
|
return undefined;
|
|
278
|
-
}
|
|
198
|
+
},
|
|
279
199
|
|
|
280
|
-
|
|
200
|
+
// Apply changes to the plugin state from an editor transaction.
|
|
201
|
+
apply: (
|
|
202
|
+
transaction,
|
|
203
|
+
prev,
|
|
204
|
+
_oldState,
|
|
205
|
+
newState,
|
|
206
|
+
): SuggestionPluginState => {
|
|
207
|
+
// TODO: More clearly define which transactions should be ignored.
|
|
208
|
+
if (transaction.getMeta("orderedListIndexing") !== undefined) {
|
|
209
|
+
return prev;
|
|
210
|
+
}
|
|
281
211
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
);
|
|
212
|
+
// Ignore transactions in code blocks.
|
|
213
|
+
if (transaction.selection.$from.parent.type.spec.code) {
|
|
214
|
+
return prev;
|
|
215
|
+
}
|
|
287
216
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
217
|
+
// Either contains the trigger character if the menu should be shown,
|
|
218
|
+
// or null if it should be hidden.
|
|
219
|
+
const suggestionPluginTransactionMeta: {
|
|
220
|
+
triggerCharacter: string;
|
|
221
|
+
deleteTriggerCharacter?: boolean;
|
|
222
|
+
ignoreQueryLength?: boolean;
|
|
223
|
+
} | null = transaction.getMeta(suggestionMenuPluginKey);
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
typeof suggestionPluginTransactionMeta === "object" &&
|
|
227
|
+
suggestionPluginTransactionMeta !== null
|
|
228
|
+
) {
|
|
229
|
+
if (prev) {
|
|
230
|
+
// Close the previous menu if it exists
|
|
231
|
+
this.closeMenu();
|
|
232
|
+
}
|
|
233
|
+
const trackedPosition = trackPosition(
|
|
234
|
+
editor,
|
|
235
|
+
newState.selection.from -
|
|
236
|
+
// Need to account for the trigger char that was inserted, so we offset the position by the length of the trigger character.
|
|
237
|
+
suggestionPluginTransactionMeta.triggerCharacter.length,
|
|
238
|
+
);
|
|
239
|
+
return {
|
|
240
|
+
triggerCharacter:
|
|
241
|
+
suggestionPluginTransactionMeta.triggerCharacter,
|
|
242
|
+
deleteTriggerCharacter:
|
|
243
|
+
suggestionPluginTransactionMeta.deleteTriggerCharacter !==
|
|
244
|
+
false,
|
|
245
|
+
// When reading the queryStartPos, we offset the result by the length of the trigger character, to make it easy on the caller
|
|
246
|
+
queryStartPos: () =>
|
|
247
|
+
trackedPosition() +
|
|
248
|
+
suggestionPluginTransactionMeta.triggerCharacter.length,
|
|
249
|
+
query: "",
|
|
250
|
+
decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,
|
|
251
|
+
ignoreQueryLength:
|
|
252
|
+
suggestionPluginTransactionMeta?.ignoreQueryLength,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Checks if the menu is hidden, in which case it doesn't need to be hidden or updated.
|
|
257
|
+
if (prev === undefined) {
|
|
258
|
+
return prev;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Checks if the menu should be hidden.
|
|
262
|
+
if (
|
|
263
|
+
// Highlighting text should hide the menu.
|
|
264
|
+
newState.selection.from !== newState.selection.to ||
|
|
265
|
+
// Transactions with plugin metadata should hide the menu.
|
|
266
|
+
suggestionPluginTransactionMeta === null ||
|
|
267
|
+
// Certain mouse events should hide the menu.
|
|
268
|
+
// TODO: Change to global mousedown listener.
|
|
269
|
+
transaction.getMeta("focus") ||
|
|
270
|
+
transaction.getMeta("blur") ||
|
|
271
|
+
transaction.getMeta("pointer") ||
|
|
272
|
+
// Moving the caret before the character which triggered the menu should hide it.
|
|
273
|
+
(prev.triggerCharacter !== undefined &&
|
|
274
|
+
newState.selection.from < prev.queryStartPos()) ||
|
|
275
|
+
// Moving the caret to a new block should hide the menu.
|
|
276
|
+
!newState.selection.$from.sameParent(
|
|
277
|
+
newState.doc.resolve(prev.queryStartPos()),
|
|
278
|
+
)
|
|
279
|
+
) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const next = { ...prev };
|
|
284
|
+
|
|
285
|
+
// Updates the current query.
|
|
286
|
+
next.query = newState.doc.textBetween(
|
|
287
|
+
prev.queryStartPos(),
|
|
288
|
+
newState.selection.from,
|
|
302
289
|
);
|
|
303
290
|
|
|
304
|
-
return
|
|
305
|
-
}
|
|
306
|
-
return false;
|
|
291
|
+
return next;
|
|
292
|
+
},
|
|
307
293
|
},
|
|
308
294
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const blockNode = findBlock(state.selection);
|
|
323
|
-
if (blockNode) {
|
|
324
|
-
return DecorationSet.create(state.doc, [
|
|
325
|
-
Decoration.node(
|
|
326
|
-
blockNode.pos,
|
|
327
|
-
blockNode.pos + blockNode.node.nodeSize,
|
|
328
|
-
{
|
|
329
|
-
nodeName: "span",
|
|
330
|
-
class: "bn-suggestion-decorator",
|
|
331
|
-
"data-decoration-id": suggestionPluginState.decorationId,
|
|
332
|
-
},
|
|
333
|
-
),
|
|
334
|
-
]);
|
|
295
|
+
props: {
|
|
296
|
+
handleTextInput(view, _from, _to, text) {
|
|
297
|
+
if (triggerCharacters.includes(text)) {
|
|
298
|
+
view.dispatch(view.state.tr.insertText(text));
|
|
299
|
+
view.dispatch(
|
|
300
|
+
view.state.tr
|
|
301
|
+
.setMeta(suggestionMenuPluginKey, {
|
|
302
|
+
triggerCharacter: text,
|
|
303
|
+
})
|
|
304
|
+
.scrollIntoView(),
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
return true;
|
|
335
308
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
309
|
+
return false;
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
// Setup decorator on the currently active suggestion.
|
|
313
|
+
decorations(state) {
|
|
314
|
+
const suggestionPluginState: SuggestionPluginState = (
|
|
315
|
+
this as Plugin
|
|
316
|
+
).getState(state);
|
|
317
|
+
|
|
318
|
+
if (suggestionPluginState === undefined) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// If the menu was opened programmatically by another extension, it may not use a trigger character. In this
|
|
323
|
+
// case, the decoration is set on the whole block instead, as the decoration range would otherwise be empty.
|
|
324
|
+
if (!suggestionPluginState.deleteTriggerCharacter) {
|
|
325
|
+
const blockNode = findBlock(state.selection);
|
|
326
|
+
if (blockNode) {
|
|
327
|
+
return DecorationSet.create(state.doc, [
|
|
328
|
+
Decoration.node(
|
|
329
|
+
blockNode.pos,
|
|
330
|
+
blockNode.pos + blockNode.node.nodeSize,
|
|
331
|
+
{
|
|
332
|
+
nodeName: "span",
|
|
333
|
+
class: "bn-suggestion-decorator",
|
|
334
|
+
"data-decoration-id": suggestionPluginState.decorationId,
|
|
335
|
+
},
|
|
336
|
+
),
|
|
337
|
+
]);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Creates an inline decoration around the trigger character.
|
|
341
|
+
return DecorationSet.create(state.doc, [
|
|
342
|
+
Decoration.inline(
|
|
343
|
+
suggestionPluginState.queryStartPos() -
|
|
344
|
+
suggestionPluginState.triggerCharacter!.length,
|
|
345
|
+
suggestionPluginState.queryStartPos(),
|
|
346
|
+
{
|
|
347
|
+
nodeName: "span",
|
|
348
|
+
class: "bn-suggestion-decorator",
|
|
349
|
+
"data-decoration-id": suggestionPluginState.decorationId,
|
|
350
|
+
},
|
|
351
|
+
),
|
|
352
|
+
]);
|
|
353
|
+
},
|
|
350
354
|
},
|
|
351
|
-
},
|
|
352
|
-
|
|
355
|
+
}),
|
|
356
|
+
);
|
|
353
357
|
}
|
|
354
358
|
|
|
355
359
|
public onUpdate(
|
|
@@ -235,7 +235,7 @@ export function getDefaultSlashMenuItems<
|
|
|
235
235
|
|
|
236
236
|
// Immediately open the file toolbar
|
|
237
237
|
editor.transact((tr) =>
|
|
238
|
-
tr.setMeta(editor.filePanel!.
|
|
238
|
+
tr.setMeta(editor.filePanel!.plugins[0], {
|
|
239
239
|
block: insertedBlock,
|
|
240
240
|
}),
|
|
241
241
|
);
|
|
@@ -254,7 +254,7 @@ export function getDefaultSlashMenuItems<
|
|
|
254
254
|
|
|
255
255
|
// Immediately open the file toolbar
|
|
256
256
|
editor.transact((tr) =>
|
|
257
|
-
tr.setMeta(editor.filePanel!.
|
|
257
|
+
tr.setMeta(editor.filePanel!.plugins[0], {
|
|
258
258
|
block: insertedBlock,
|
|
259
259
|
}),
|
|
260
260
|
);
|
|
@@ -273,7 +273,7 @@ export function getDefaultSlashMenuItems<
|
|
|
273
273
|
|
|
274
274
|
// Immediately open the file toolbar
|
|
275
275
|
editor.transact((tr) =>
|
|
276
|
-
tr.setMeta(editor.filePanel!.
|
|
276
|
+
tr.setMeta(editor.filePanel!.plugins[0], {
|
|
277
277
|
block: insertedBlock,
|
|
278
278
|
}),
|
|
279
279
|
);
|
|
@@ -292,7 +292,7 @@ export function getDefaultSlashMenuItems<
|
|
|
292
292
|
|
|
293
293
|
// Immediately open the file toolbar
|
|
294
294
|
editor.transact((tr) =>
|
|
295
|
-
tr.setMeta(editor.filePanel!.
|
|
295
|
+
tr.setMeta(editor.filePanel!.plugins[0], {
|
|
296
296
|
block: insertedBlock,
|
|
297
297
|
}),
|
|
298
298
|
);
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Mark } from "@tiptap/core";
|
|
2
|
+
import { MarkSpec } from "prosemirror-model";
|
|
3
|
+
|
|
4
|
+
// This copies the marks from @handlewithcare/prosemirror-suggest-changes,
|
|
5
|
+
// but uses the Tiptap Mark API instead so we can use them in BlockNote
|
|
6
|
+
|
|
7
|
+
// The ideal solution would be to not depend on tiptap nodes / marks, but be able to use prosemirror nodes / marks directly
|
|
8
|
+
// this way we could directly use the exported marks from @handlewithcare/prosemirror-suggest-changes
|
|
9
|
+
export const SuggestionAddMark = Mark.create({
|
|
10
|
+
name: "insertion",
|
|
11
|
+
inclusive: false,
|
|
12
|
+
excludes: "deletion modification insertion",
|
|
13
|
+
addAttributes() {
|
|
14
|
+
return {
|
|
15
|
+
id: { default: null, validate: "number" }, // note: validate is supported in prosemirror but not in tiptap, so this doesn't actually work (considered not critical)
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
extendMarkSchema(extension) {
|
|
19
|
+
if (extension.name !== "insertion") {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
blocknoteIgnore: true,
|
|
24
|
+
inclusive: false,
|
|
25
|
+
|
|
26
|
+
toDOM(mark, inline) {
|
|
27
|
+
return [
|
|
28
|
+
"ins",
|
|
29
|
+
{
|
|
30
|
+
"data-id": String(mark.attrs["id"]),
|
|
31
|
+
"data-inline": String(inline),
|
|
32
|
+
...(!inline && { style: "display: contents" }), // changed to "contents" to make this work for table rows
|
|
33
|
+
},
|
|
34
|
+
0,
|
|
35
|
+
];
|
|
36
|
+
},
|
|
37
|
+
parseDOM: [
|
|
38
|
+
{
|
|
39
|
+
tag: "ins",
|
|
40
|
+
getAttrs(node) {
|
|
41
|
+
if (!node.dataset["id"]) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
id: parseInt(node.dataset["id"], 10),
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
} satisfies MarkSpec;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const SuggestionDeleteMark = Mark.create({
|
|
55
|
+
name: "deletion",
|
|
56
|
+
inclusive: false,
|
|
57
|
+
excludes: "insertion modification deletion",
|
|
58
|
+
addAttributes() {
|
|
59
|
+
return {
|
|
60
|
+
id: { default: null, validate: "number" }, // note: validate is supported in prosemirror but not in tiptap
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
extendMarkSchema(extension) {
|
|
64
|
+
if (extension.name !== "deletion") {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
blocknoteIgnore: true,
|
|
69
|
+
inclusive: false,
|
|
70
|
+
|
|
71
|
+
// attrs: {
|
|
72
|
+
// id: { validate: "number" },
|
|
73
|
+
// },
|
|
74
|
+
toDOM(mark, inline) {
|
|
75
|
+
return [
|
|
76
|
+
"del",
|
|
77
|
+
{
|
|
78
|
+
"data-id": String(mark.attrs["id"]),
|
|
79
|
+
"data-inline": String(inline),
|
|
80
|
+
...(!inline && { style: "display: contents" }), // changed to "contents" to make this work for table rows
|
|
81
|
+
},
|
|
82
|
+
0,
|
|
83
|
+
];
|
|
84
|
+
},
|
|
85
|
+
parseDOM: [
|
|
86
|
+
{
|
|
87
|
+
tag: "del",
|
|
88
|
+
getAttrs(node) {
|
|
89
|
+
if (!node.dataset["id"]) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
id: parseInt(node.dataset["id"], 10),
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
} satisfies MarkSpec;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export const SuggestionModificationMark = Mark.create({
|
|
103
|
+
name: "modification",
|
|
104
|
+
inclusive: false,
|
|
105
|
+
excludes: "deletion insertion",
|
|
106
|
+
addAttributes() {
|
|
107
|
+
// note: validate is supported in prosemirror but not in tiptap
|
|
108
|
+
return {
|
|
109
|
+
id: { default: null, validate: "number" },
|
|
110
|
+
type: { validate: "string" },
|
|
111
|
+
attrName: { default: null, validate: "string|null" },
|
|
112
|
+
previousValue: { default: null },
|
|
113
|
+
newValue: { default: null },
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
extendMarkSchema(extension) {
|
|
117
|
+
if (extension.name !== "modification") {
|
|
118
|
+
return {};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
blocknoteIgnore: true,
|
|
122
|
+
inclusive: false,
|
|
123
|
+
// attrs: {
|
|
124
|
+
// id: { validate: "number" },
|
|
125
|
+
// type: { validate: "string" },
|
|
126
|
+
// attrName: { default: null, validate: "string|null" },
|
|
127
|
+
// previousValue: { default: null },
|
|
128
|
+
// newValue: { default: null },
|
|
129
|
+
// },
|
|
130
|
+
toDOM(mark, inline) {
|
|
131
|
+
return [
|
|
132
|
+
inline ? "span" : "div",
|
|
133
|
+
{
|
|
134
|
+
"data-type": "modification",
|
|
135
|
+
"data-id": String(mark.attrs["id"]),
|
|
136
|
+
"data-mod-type": mark.attrs["type"] as string,
|
|
137
|
+
"data-mod-prev-val": JSON.stringify(mark.attrs["previousValue"]),
|
|
138
|
+
// TODO: Try to serialize marks with toJSON?
|
|
139
|
+
"data-mod-new-val": JSON.stringify(mark.attrs["newValue"]),
|
|
140
|
+
},
|
|
141
|
+
0,
|
|
142
|
+
];
|
|
143
|
+
},
|
|
144
|
+
parseDOM: [
|
|
145
|
+
{
|
|
146
|
+
tag: "span[data-type='modification']",
|
|
147
|
+
getAttrs(node) {
|
|
148
|
+
if (!node.dataset["id"]) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
id: parseInt(node.dataset["id"], 10),
|
|
153
|
+
type: node.dataset["modType"],
|
|
154
|
+
previousValue: node.dataset["modPrevVal"],
|
|
155
|
+
newValue: node.dataset["modNewVal"],
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
tag: "div[data-type='modification']",
|
|
161
|
+
getAttrs(node) {
|
|
162
|
+
if (!node.dataset["id"]) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
id: parseInt(node.dataset["id"], 10),
|
|
167
|
+
type: node.dataset["modType"],
|
|
168
|
+
previousValue: node.dataset["modPrevVal"],
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
} satisfies MarkSpec;
|
|
174
|
+
},
|
|
175
|
+
});
|