@blocknote/core 0.30.1 → 0.31.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.
- package/dist/blocknote.cjs +9 -9
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +2754 -2230
- 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/webpack-stats.json +1 -1
- package/package.json +4 -5
- 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 +88 -37
- 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 +75 -70
- package/src/extensions/FilePanel/FilePanelPlugin.ts +50 -49
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +56 -26
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +22 -21
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +45 -42
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +111 -108
- package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +179 -170
- package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +22 -19
- package/src/extensions/SideMenu/SideMenuPlugin.ts +19 -18
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +168 -168
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +4 -4
- package/src/extensions/Suggestions/SuggestionMarks.ts +175 -0
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +153 -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/removeBlocks/removeBlocks.d.ts +4 -0
- package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.d.ts +1 -0
- 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.d.ts +5 -0
- package/types/src/api/blockManipulation/transactions.test.d.ts +0 -0
- package/types/src/api/clipboard/clipboardExternal.test.d.ts +1 -0
- package/types/src/api/clipboard/clipboardInternal.test.d.ts +1 -0
- package/types/src/api/clipboard/testUtil.d.ts +541 -0
- package/types/src/api/exporters/html/htmlConversion.test.d.ts +1 -0
- package/types/src/api/exporters/markdown/markdownExporter.test.d.ts +1 -0
- package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
- package/types/src/api/nodeConversions/nodeToBlock.d.ts +39 -2
- package/types/src/api/parsers/html/parseHTML.test.d.ts +1 -0
- package/types/src/api/parsers/markdown/parseMarkdown.test.d.ts +1 -0
- package/types/src/api/pmUtil.d.ts +3 -3
- package/types/src/api/testUtil/cases/customBlocks.d.ts +670 -0
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +558 -0
- package/types/src/api/testUtil/cases/customStyles.d.ts +552 -0
- package/types/src/api/testUtil/cases/defaultSchema.d.ts +4 -0
- package/types/src/api/testUtil/index.d.ts +14 -0
- package/types/src/api/testUtil/partialBlockTestUtil.d.ts +9 -0
- package/types/src/api/testUtil/paste.d.ts +2 -0
- package/types/src/blocks/CodeBlockContent/defaultSupportedLanguages.d.ts +6 -0
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +9 -1
- package/types/src/editor/BlockNoteEditor.d.ts +55 -9
- 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/ForkYDocPlugin.test.d.ts +1 -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/Collaboration/createCollaborationExtensions.d.ts +17 -0
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +2 -4
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +3 -4
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -5
- package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +3 -4
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +2 -3
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +2 -3
- package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +2 -3
- package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +2 -3
- package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +3 -4
- package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +2 -4
- package/types/src/extensions/Suggestions/SuggestionMarks.d.ts +4 -0
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +5 -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/dist/tsconfig.tsbuildinfo +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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Mark, Node, Schema } from "@tiptap/pm/model";
|
|
2
|
-
|
|
1
|
+
import { Mark, Node, Schema, Slice } from "@tiptap/pm/model";
|
|
2
|
+
import type { Block } from "../../blocks/defaultBlocks.js";
|
|
3
3
|
import UniqueID from "../../extensions/UniqueID/UniqueID.js";
|
|
4
4
|
import type {
|
|
5
5
|
BlockSchema,
|
|
@@ -13,17 +13,18 @@ import type {
|
|
|
13
13
|
TableCell,
|
|
14
14
|
TableContent,
|
|
15
15
|
} from "../../schema/index.js";
|
|
16
|
-
import { getBlockInfoWithManualOffset } from "../getBlockInfoFromPos.js";
|
|
17
|
-
|
|
18
|
-
import type { Block } from "../../blocks/defaultBlocks.js";
|
|
19
16
|
import {
|
|
20
17
|
isLinkInlineContent,
|
|
21
18
|
isStyledTextInlineContent,
|
|
22
19
|
} from "../../schema/inlineContent/types.js";
|
|
23
20
|
import { UnreachableCaseError } from "../../util/typescript.js";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
21
|
+
import { getBlockInfoWithManualOffset } from "../getBlockInfoFromPos.js";
|
|
22
|
+
import {
|
|
23
|
+
getBlockCache,
|
|
24
|
+
getBlockSchema,
|
|
25
|
+
getInlineContentSchema,
|
|
26
|
+
getStyleSchema,
|
|
27
|
+
} from "../pmUtil.js";
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Converts an internal (prosemirror) table node contentto a BlockNote Tablecontent
|
|
@@ -492,3 +493,197 @@ export function nodeToBlock<
|
|
|
492
493
|
|
|
493
494
|
return block;
|
|
494
495
|
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Convert a Prosemirror document to a BlockNote document (array of blocks)
|
|
499
|
+
*/
|
|
500
|
+
export function docToBlocks<
|
|
501
|
+
BSchema extends BlockSchema,
|
|
502
|
+
I extends InlineContentSchema,
|
|
503
|
+
S extends StyleSchema,
|
|
504
|
+
>(
|
|
505
|
+
doc: Node,
|
|
506
|
+
schema: Schema,
|
|
507
|
+
blockSchema: BSchema = getBlockSchema(schema) as BSchema,
|
|
508
|
+
inlineContentSchema: I = getInlineContentSchema(schema) as I,
|
|
509
|
+
styleSchema: S = getStyleSchema(schema) as S,
|
|
510
|
+
blockCache = getBlockCache(schema),
|
|
511
|
+
) {
|
|
512
|
+
const blocks: Block<BSchema, I, S>[] = [];
|
|
513
|
+
doc.firstChild!.descendants((node) => {
|
|
514
|
+
blocks.push(
|
|
515
|
+
nodeToBlock(
|
|
516
|
+
node,
|
|
517
|
+
schema,
|
|
518
|
+
blockSchema,
|
|
519
|
+
inlineContentSchema,
|
|
520
|
+
styleSchema,
|
|
521
|
+
blockCache,
|
|
522
|
+
),
|
|
523
|
+
);
|
|
524
|
+
return false;
|
|
525
|
+
});
|
|
526
|
+
return blocks;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
*
|
|
531
|
+
* Parse a Prosemirror Slice into a BlockNote selection. The prosemirror schema looks like this:
|
|
532
|
+
*
|
|
533
|
+
* <blockGroup>
|
|
534
|
+
* <blockContainer> (main content of block)
|
|
535
|
+
* <p, heading, etc.>
|
|
536
|
+
* <blockGroup> (only if blocks has children)
|
|
537
|
+
* <blockContainer> (child block)
|
|
538
|
+
* <p, heading, etc.>
|
|
539
|
+
* </blockContainer>
|
|
540
|
+
* <blockContainer> (child block 2)
|
|
541
|
+
* <p, heading, etc.>
|
|
542
|
+
* </blockContainer>
|
|
543
|
+
* </blockContainer>
|
|
544
|
+
* </blockGroup>
|
|
545
|
+
* </blockGroup>
|
|
546
|
+
*
|
|
547
|
+
*/
|
|
548
|
+
export function prosemirrorSliceToSlicedBlocks<
|
|
549
|
+
BSchema extends BlockSchema,
|
|
550
|
+
I extends InlineContentSchema,
|
|
551
|
+
S extends StyleSchema,
|
|
552
|
+
>(
|
|
553
|
+
slice: Slice,
|
|
554
|
+
schema: Schema,
|
|
555
|
+
blockSchema: BSchema = getBlockSchema(schema) as BSchema,
|
|
556
|
+
inlineContentSchema: I = getInlineContentSchema(schema) as I,
|
|
557
|
+
styleSchema: S = getStyleSchema(schema) as S,
|
|
558
|
+
blockCache: WeakMap<Node, Block<BSchema, I, S>> = getBlockCache(schema),
|
|
559
|
+
): {
|
|
560
|
+
/**
|
|
561
|
+
* The blocks that are included in the selection.
|
|
562
|
+
*/
|
|
563
|
+
blocks: Block<BSchema, I, S>[];
|
|
564
|
+
/**
|
|
565
|
+
* If a block was "cut" at the start of the selection, this will be the id of the block that was cut.
|
|
566
|
+
*/
|
|
567
|
+
blockCutAtStart: string | undefined;
|
|
568
|
+
/**
|
|
569
|
+
* If a block was "cut" at the end of the selection, this will be the id of the block that was cut.
|
|
570
|
+
*/
|
|
571
|
+
blockCutAtEnd: string | undefined;
|
|
572
|
+
} {
|
|
573
|
+
// console.log(JSON.stringify(slice.toJSON()));
|
|
574
|
+
function processNode(
|
|
575
|
+
node: Node,
|
|
576
|
+
openStart: number,
|
|
577
|
+
openEnd: number,
|
|
578
|
+
): {
|
|
579
|
+
blocks: Block<BSchema, I, S>[];
|
|
580
|
+
blockCutAtStart: string | undefined;
|
|
581
|
+
blockCutAtEnd: string | undefined;
|
|
582
|
+
} {
|
|
583
|
+
if (node.type.name !== "blockGroup") {
|
|
584
|
+
throw new Error("unexpected");
|
|
585
|
+
}
|
|
586
|
+
const blocks: Block<BSchema, I, S>[] = [];
|
|
587
|
+
let blockCutAtStart: string | undefined;
|
|
588
|
+
let blockCutAtEnd: string | undefined;
|
|
589
|
+
|
|
590
|
+
node.forEach((blockContainer, _offset, index) => {
|
|
591
|
+
if (blockContainer.type.name !== "blockContainer") {
|
|
592
|
+
throw new Error("unexpected");
|
|
593
|
+
}
|
|
594
|
+
if (blockContainer.childCount === 0) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (blockContainer.childCount === 0 || blockContainer.childCount > 2) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
"unexpected, blockContainer.childCount: " + blockContainer.childCount,
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const isFirstBlock = index === 0;
|
|
604
|
+
const isLastBlock = index === node.childCount - 1;
|
|
605
|
+
|
|
606
|
+
if (blockContainer.firstChild!.type.name === "blockGroup") {
|
|
607
|
+
// this is the parent where a selection starts within one of its children,
|
|
608
|
+
// e.g.:
|
|
609
|
+
// A
|
|
610
|
+
// ├── B
|
|
611
|
+
// selection starts within B, then this blockContainer is A, but we don't care about A
|
|
612
|
+
// so let's descend into B and continue processing
|
|
613
|
+
if (!isFirstBlock) {
|
|
614
|
+
throw new Error("unexpected");
|
|
615
|
+
}
|
|
616
|
+
const ret = processNode(
|
|
617
|
+
blockContainer.firstChild!,
|
|
618
|
+
Math.max(0, openStart - 1),
|
|
619
|
+
isLastBlock ? Math.max(0, openEnd - 1) : 0,
|
|
620
|
+
);
|
|
621
|
+
blockCutAtStart = ret.blockCutAtStart;
|
|
622
|
+
if (isLastBlock) {
|
|
623
|
+
blockCutAtEnd = ret.blockCutAtEnd;
|
|
624
|
+
}
|
|
625
|
+
blocks.push(...ret.blocks);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const block = nodeToBlock(
|
|
630
|
+
blockContainer,
|
|
631
|
+
schema,
|
|
632
|
+
blockSchema,
|
|
633
|
+
inlineContentSchema,
|
|
634
|
+
styleSchema,
|
|
635
|
+
blockCache,
|
|
636
|
+
);
|
|
637
|
+
const childGroup =
|
|
638
|
+
blockContainer.childCount > 1 ? blockContainer.child(1) : undefined;
|
|
639
|
+
|
|
640
|
+
let childBlocks: Block<BSchema, I, S>[] = [];
|
|
641
|
+
if (childGroup) {
|
|
642
|
+
const ret = processNode(
|
|
643
|
+
childGroup,
|
|
644
|
+
0, // TODO: can this be anything other than 0?
|
|
645
|
+
isLastBlock ? Math.max(0, openEnd - 1) : 0,
|
|
646
|
+
);
|
|
647
|
+
childBlocks = ret.blocks;
|
|
648
|
+
if (isLastBlock) {
|
|
649
|
+
blockCutAtEnd = ret.blockCutAtEnd;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (isLastBlock && !childGroup && openEnd > 1) {
|
|
654
|
+
blockCutAtEnd = block.id;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (isFirstBlock && openStart > 1) {
|
|
658
|
+
blockCutAtStart = block.id;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
blocks.push({
|
|
662
|
+
...(block as any),
|
|
663
|
+
children: childBlocks,
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
return { blocks, blockCutAtStart, blockCutAtEnd };
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (slice.content.childCount === 0) {
|
|
671
|
+
return {
|
|
672
|
+
blocks: [],
|
|
673
|
+
blockCutAtStart: undefined,
|
|
674
|
+
blockCutAtEnd: undefined,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (slice.content.childCount !== 1) {
|
|
679
|
+
throw new Error(
|
|
680
|
+
"slice must be a single block, did you forget includeParents=true?",
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return processNode(
|
|
685
|
+
slice.content.firstChild!,
|
|
686
|
+
Math.max(slice.openStart - 1, 0),
|
|
687
|
+
Math.max(slice.openEnd - 1, 0),
|
|
688
|
+
);
|
|
689
|
+
}
|
package/src/api/pmUtil.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Node, Schema } from "prosemirror-model";
|
|
2
|
-
import
|
|
2
|
+
import { Transform } from "prosemirror-transform";
|
|
3
3
|
import type { BlockNoteEditor } from "../editor/BlockNoteEditor.js";
|
|
4
|
+
import { BlockNoteSchema } from "../editor/BlockNoteSchema.js";
|
|
4
5
|
import type { BlockSchema } from "../schema/blocks/types.js";
|
|
5
6
|
import type { InlineContentSchema } from "../schema/inlineContent/types.js";
|
|
6
7
|
import type { StyleSchema } from "../schema/styles/types.js";
|
|
7
|
-
import { BlockNoteSchema } from "../editor/BlockNoteSchema.js";
|
|
8
8
|
|
|
9
|
-
export function getPmSchema(trOrNode:
|
|
9
|
+
export function getPmSchema(trOrNode: Transform | Node) {
|
|
10
10
|
if ("doc" in trOrNode) {
|
|
11
11
|
return trOrNode.doc.type.schema;
|
|
12
12
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import type { HighlighterGeneric } from "@shikijs/types";
|
|
1
2
|
import { InputRule, isTextSelection } from "@tiptap/core";
|
|
2
3
|
import { TextSelection } from "@tiptap/pm/state";
|
|
3
|
-
import {
|
|
4
|
+
import { Parser, createHighlightPlugin } from "prosemirror-highlight";
|
|
4
5
|
import { createParser } from "prosemirror-highlight/shiki";
|
|
6
|
+
import { BlockNoteEditor } from "../../index.js";
|
|
5
7
|
import {
|
|
8
|
+
PropSchema,
|
|
6
9
|
createBlockSpecFromStronglyTypedTiptapNode,
|
|
7
10
|
createStronglyTypedTiptapNode,
|
|
8
|
-
PropSchema,
|
|
9
11
|
} from "../../schema/index.js";
|
|
10
12
|
import { createDefaultBlockDOMOutputSpec } from "../defaultBlockHelpers.js";
|
|
11
|
-
import type { HighlighterGeneric } from "@shikijs/types";
|
|
12
|
-
import { BlockNoteEditor } from "../../index.js";
|
|
13
13
|
|
|
14
14
|
export type CodeBlockOptions = {
|
|
15
15
|
/**
|
|
@@ -76,7 +76,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
76
76
|
name: "codeBlock",
|
|
77
77
|
content: "inline*",
|
|
78
78
|
group: "blockContent",
|
|
79
|
-
marks: "",
|
|
79
|
+
marks: "insertion deletion modification",
|
|
80
80
|
code: true,
|
|
81
81
|
defining: true,
|
|
82
82
|
addOptions() {
|
|
@@ -152,7 +152,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
152
152
|
// Parse from external HTML.
|
|
153
153
|
{
|
|
154
154
|
tag: "pre",
|
|
155
|
-
contentElement: "code",
|
|
155
|
+
// contentElement: "code",
|
|
156
156
|
preserveWhitespace: "full",
|
|
157
157
|
},
|
|
158
158
|
];
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { Node, mergeAttributes } from "@tiptap/core";
|
|
1
2
|
import { TableCell } from "@tiptap/extension-table-cell";
|
|
2
3
|
import { TableHeader } from "@tiptap/extension-table-header";
|
|
3
|
-
import { TableRow } from "@tiptap/extension-table-row";
|
|
4
4
|
import { DOMParser, Fragment, Node as PMNode, Schema } from "prosemirror-model";
|
|
5
5
|
import { TableView } from "prosemirror-tables";
|
|
6
|
-
|
|
7
6
|
import { NodeView } from "prosemirror-view";
|
|
8
7
|
import {
|
|
9
8
|
createBlockSpecFromStronglyTypedTiptapNode,
|
|
@@ -24,6 +23,7 @@ export const TableBlockContent = createStronglyTypedTiptapNode({
|
|
|
24
23
|
group: "blockContent",
|
|
25
24
|
tableRole: "table",
|
|
26
25
|
|
|
26
|
+
marks: "deletion insertion modification",
|
|
27
27
|
isolating: true,
|
|
28
28
|
|
|
29
29
|
parseHTML() {
|
|
@@ -152,6 +152,36 @@ const TableParagraph = createStronglyTypedTiptapNode({
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
+
* This extension allows you to create table rows.
|
|
156
|
+
* @see https://www.tiptap.dev/api/nodes/table-row
|
|
157
|
+
*/
|
|
158
|
+
export const TableRow = Node.create<{ HTMLAttributes: Record<string, any> }>({
|
|
159
|
+
name: "tableRow",
|
|
160
|
+
|
|
161
|
+
addOptions() {
|
|
162
|
+
return {
|
|
163
|
+
HTMLAttributes: {},
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
content: "(tableCell | tableHeader)+",
|
|
168
|
+
|
|
169
|
+
tableRole: "row",
|
|
170
|
+
marks: "deletion insertion modification",
|
|
171
|
+
parseHTML() {
|
|
172
|
+
return [{ tag: "tr" }];
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
renderHTML({ HTMLAttributes }) {
|
|
176
|
+
return [
|
|
177
|
+
"tr",
|
|
178
|
+
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
179
|
+
0,
|
|
180
|
+
];
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
/*
|
|
155
185
|
* This will flatten a node's content to fit into a table cell's paragraph.
|
|
156
186
|
*/
|
|
157
187
|
function parseTableContent(node: HTMLElement, schema: Schema) {
|
package/src/editor/Block.css
CHANGED
|
@@ -152,6 +152,11 @@ NESTED BLOCKS
|
|
|
152
152
|
|
|
153
153
|
.bn-block-outer:not([data-prev-type])
|
|
154
154
|
> .bn-block
|
|
155
|
+
> .bn-block-content[data-content-type="heading"],
|
|
156
|
+
.bn-block-outer:not([data-prev-type])
|
|
157
|
+
> .bn-block
|
|
158
|
+
> div[data-type="modification"]
|
|
159
|
+
> div[data-type="modification"]
|
|
155
160
|
> .bn-block-content[data-content-type="heading"] {
|
|
156
161
|
font-size: var(--level);
|
|
157
162
|
font-weight: bold;
|
|
@@ -195,6 +200,10 @@ NESTED BLOCKS
|
|
|
195
200
|
|
|
196
201
|
.bn-block-outer:not([data-prev-type])
|
|
197
202
|
> .bn-block
|
|
203
|
+
> .bn-block-content[data-content-type="numberedListItem"]::before,
|
|
204
|
+
.bn-block-outer:not([data-prev-type])
|
|
205
|
+
> .bn-block
|
|
206
|
+
> div[data-type="modification"]
|
|
198
207
|
> .bn-block-content[data-content-type="numberedListItem"]::before {
|
|
199
208
|
content: var(--index) ".";
|
|
200
209
|
}
|
|
@@ -239,6 +248,10 @@ NESTED BLOCKS
|
|
|
239
248
|
|
|
240
249
|
.bn-block-outer:not([data-prev-type])
|
|
241
250
|
> .bn-block
|
|
251
|
+
> .bn-block-content[data-content-type="bulletListItem"]::before,
|
|
252
|
+
.bn-block-outer:not([data-prev-type])
|
|
253
|
+
> .bn-block
|
|
254
|
+
> div[data-type="modification"]
|
|
242
255
|
> .bn-block-content[data-content-type="bulletListItem"]::before {
|
|
243
256
|
content: "•";
|
|
244
257
|
}
|
|
@@ -256,6 +269,12 @@ NESTED BLOCKS
|
|
|
256
269
|
~ .bn-block-group
|
|
257
270
|
> .bn-block-outer:not([data-prev-type])
|
|
258
271
|
> .bn-block
|
|
272
|
+
> .bn-block-content[data-content-type="bulletListItem"]::before,
|
|
273
|
+
[data-content-type="bulletListItem"]
|
|
274
|
+
~ .bn-block-group
|
|
275
|
+
> .bn-block-outer:not([data-prev-type])
|
|
276
|
+
> .bn-block
|
|
277
|
+
> div[data-type="modification"]
|
|
259
278
|
> .bn-block-content[data-content-type="bulletListItem"]::before {
|
|
260
279
|
content: "◦";
|
|
261
280
|
}
|
|
@@ -277,6 +296,14 @@ NESTED BLOCKS
|
|
|
277
296
|
~ .bn-block-group
|
|
278
297
|
> .bn-block-outer:not([data-prev-type])
|
|
279
298
|
> .bn-block
|
|
299
|
+
> .bn-block-content[data-content-type="bulletListItem"]::before,
|
|
300
|
+
[data-content-type="bulletListItem"]
|
|
301
|
+
~ .bn-block-group
|
|
302
|
+
[data-content-type="bulletListItem"]
|
|
303
|
+
~ .bn-block-group
|
|
304
|
+
> .bn-block-outer:not([data-prev-type])
|
|
305
|
+
> .bn-block
|
|
306
|
+
> div[data-type="modification"]
|
|
280
307
|
> .bn-block-content[data-content-type="bulletListItem"]::before {
|
|
281
308
|
content: "▪";
|
|
282
309
|
}
|
|
@@ -442,7 +469,6 @@ NESTED BLOCKS
|
|
|
442
469
|
position: absolute;
|
|
443
470
|
font-style: italic;
|
|
444
471
|
}
|
|
445
|
-
|
|
446
472
|
/* TODO: should this be here? */
|
|
447
473
|
|
|
448
474
|
/* TEXT COLORS */
|
|
@@ -75,6 +75,13 @@ it("adds id attribute when requested", async () => {
|
|
|
75
75
|
);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
+
it("updates block", () => {
|
|
79
|
+
const editor = BlockNoteEditor.create();
|
|
80
|
+
editor.updateBlock(editor.document[0], {
|
|
81
|
+
content: "hello",
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
78
85
|
it("block prop types", () => {
|
|
79
86
|
// this test checks whether the block props are correctly typed in typescript
|
|
80
87
|
const editor = BlockNoteEditor.create();
|
|
@@ -33,12 +33,13 @@ import {
|
|
|
33
33
|
import { insertContentAt } from "../api/blockManipulation/insertContentAt.js";
|
|
34
34
|
import {
|
|
35
35
|
getSelection,
|
|
36
|
+
getSelectionCutBlocks,
|
|
36
37
|
setSelection,
|
|
37
38
|
} from "../api/blockManipulation/selections/selection.js";
|
|
38
39
|
import {
|
|
39
40
|
getTextCursorPosition,
|
|
40
41
|
setTextCursorPosition,
|
|
41
|
-
} from "../api/blockManipulation/selections/textCursorPosition
|
|
42
|
+
} from "../api/blockManipulation/selections/textCursorPosition.js";
|
|
42
43
|
import { createExternalHTMLExporter } from "../api/exporters/html/externalHTMLExporter.js";
|
|
43
44
|
import { blocksToMarkdown } from "../api/exporters/markdown/markdownExporter.js";
|
|
44
45
|
import { HTMLToBlocks } from "../api/parsers/html/parseHTML.js";
|
|
@@ -93,6 +94,7 @@ import {
|
|
|
93
94
|
import { Dictionary } from "../i18n/dictionary.js";
|
|
94
95
|
import { en } from "../i18n/locales/index.js";
|
|
95
96
|
|
|
97
|
+
import { redo, undo } from "@tiptap/pm/history";
|
|
96
98
|
import {
|
|
97
99
|
TextSelection,
|
|
98
100
|
type Command,
|
|
@@ -101,11 +103,10 @@ import {
|
|
|
101
103
|
} from "@tiptap/pm/state";
|
|
102
104
|
import { dropCursor } from "prosemirror-dropcursor";
|
|
103
105
|
import { EditorView } from "prosemirror-view";
|
|
104
|
-
import {
|
|
105
|
-
import { undo, redo } from "@tiptap/pm/history";
|
|
106
|
+
import { redoCommand, undoCommand, ySyncPluginKey } from "y-prosemirror";
|
|
106
107
|
import { createInternalHTMLSerializer } from "../api/exporters/html/internalHTMLSerializer.js";
|
|
107
108
|
import { inlineContentToNodes } from "../api/nodeConversions/blockToNode.js";
|
|
108
|
-
import {
|
|
109
|
+
import { docToBlocks } from "../api/nodeConversions/nodeToBlock.js";
|
|
109
110
|
import {
|
|
110
111
|
BlocksChanged,
|
|
111
112
|
getBlocksChangedByTransaction,
|
|
@@ -113,20 +114,26 @@ import {
|
|
|
113
114
|
import { nestedListsToBlockNoteStructure } from "../api/parsers/html/util/nestedLists.js";
|
|
114
115
|
import { CodeBlockOptions } from "../blocks/CodeBlockContent/CodeBlockContent.js";
|
|
115
116
|
import type { ThreadStore, User } from "../comments/index.js";
|
|
116
|
-
import "../
|
|
117
|
+
import type { CursorPlugin } from "../extensions/Collaboration/CursorPlugin.js";
|
|
118
|
+
import type { ForkYDocPlugin } from "../extensions/Collaboration/ForkYDocPlugin.js";
|
|
117
119
|
import { EventEmitter } from "../util/EventEmitter.js";
|
|
118
|
-
import {
|
|
120
|
+
import { BlockNoteExtension } from "./BlockNoteExtension.js";
|
|
121
|
+
|
|
122
|
+
import "../style.css";
|
|
119
123
|
|
|
124
|
+
/**
|
|
125
|
+
* A factory function that returns a BlockNoteExtension
|
|
126
|
+
* This is useful so we can create extensions that require an editor instance
|
|
127
|
+
* in the constructor
|
|
128
|
+
*/
|
|
120
129
|
export type BlockNoteExtensionFactory = (
|
|
121
130
|
editor: BlockNoteEditor<any, any, any>,
|
|
122
131
|
) => BlockNoteExtension;
|
|
123
132
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
priority?: number;
|
|
129
|
-
};
|
|
133
|
+
/**
|
|
134
|
+
* We support Tiptap extensions and BlockNoteExtension based extensions
|
|
135
|
+
*/
|
|
136
|
+
export type SupportedExtension = AnyExtension | BlockNoteExtension;
|
|
130
137
|
|
|
131
138
|
export type BlockCache<
|
|
132
139
|
BSchema extends BlockSchema = any,
|
|
@@ -369,10 +376,17 @@ export type BlockNoteEditorOptions<
|
|
|
369
376
|
_tiptapOptions: Partial<EditorOptions>;
|
|
370
377
|
|
|
371
378
|
/**
|
|
372
|
-
* (experimental) add extra
|
|
379
|
+
* (experimental) add extra extensions to the editor
|
|
380
|
+
*
|
|
381
|
+
* @deprecated, should use `extensions` instead
|
|
373
382
|
*/
|
|
374
383
|
_extensions: Record<string, BlockNoteExtension | BlockNoteExtensionFactory>;
|
|
375
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Register
|
|
387
|
+
*/
|
|
388
|
+
extensions: Array<BlockNoteExtension | BlockNoteExtensionFactory>;
|
|
389
|
+
|
|
376
390
|
/**
|
|
377
391
|
* Boolean indicating whether the editor is in headless mode.
|
|
378
392
|
* Headless mode means we can use features like importing / exporting blocks,
|
|
@@ -404,7 +418,7 @@ export class BlockNoteEditor<
|
|
|
404
418
|
/**
|
|
405
419
|
* extensions that are added to the editor, can be tiptap extensions or prosemirror plugins
|
|
406
420
|
*/
|
|
407
|
-
public
|
|
421
|
+
public extensions: Record<string, SupportedExtension> = {};
|
|
408
422
|
|
|
409
423
|
/**
|
|
410
424
|
* Boolean indicating whether the editor is in headless mode.
|
|
@@ -473,8 +487,10 @@ export class BlockNoteEditor<
|
|
|
473
487
|
|
|
474
488
|
private readonly showSelectionPlugin: ShowSelectionPlugin;
|
|
475
489
|
|
|
476
|
-
|
|
477
|
-
|
|
490
|
+
/**
|
|
491
|
+
* The plugin for forking a document, only defined if in collaboration mode
|
|
492
|
+
*/
|
|
493
|
+
public readonly forkYDocPlugin?: ForkYDocPlugin;
|
|
478
494
|
/**
|
|
479
495
|
* The `uploadFile` method is what the editor uses when files need to be uploaded (for example when selecting an image to upload).
|
|
480
496
|
* This method should set when creating the editor as this is application-specific.
|
|
@@ -609,6 +625,16 @@ export class BlockNoteEditor<
|
|
|
609
625
|
});
|
|
610
626
|
|
|
611
627
|
// add extensions from options
|
|
628
|
+
for (let ext of newOptions.extensions || []) {
|
|
629
|
+
if (typeof ext === "function") {
|
|
630
|
+
// factory
|
|
631
|
+
ext = ext(this);
|
|
632
|
+
}
|
|
633
|
+
const key = (ext.constructor as any).name();
|
|
634
|
+
this.extensions[key] = ext;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// (when passed in via the deprecated `_extensions` option)
|
|
612
638
|
Object.entries(newOptions._extensions || {}).forEach(([key, ext]) => {
|
|
613
639
|
if (typeof ext === "function") {
|
|
614
640
|
// factory
|
|
@@ -625,7 +651,7 @@ export class BlockNoteEditor<
|
|
|
625
651
|
this.tableHandles = this.extensions["tableHandles"] as any;
|
|
626
652
|
this.comments = this.extensions["comments"] as any;
|
|
627
653
|
this.showSelectionPlugin = this.extensions["showSelection"] as any;
|
|
628
|
-
this.
|
|
654
|
+
this.forkYDocPlugin = this.extensions["forkYDocPlugin"] as any;
|
|
629
655
|
|
|
630
656
|
if (newOptions.uploadFile) {
|
|
631
657
|
const uploadFile = newOptions.uploadFile;
|
|
@@ -691,20 +717,19 @@ export class BlockNoteEditor<
|
|
|
691
717
|
return ext;
|
|
692
718
|
}
|
|
693
719
|
|
|
694
|
-
if (!ext.
|
|
695
|
-
|
|
696
|
-
"Extension should either be a TipTap extension or a ProseMirror plugin in a plugin property",
|
|
697
|
-
);
|
|
720
|
+
if (ext instanceof BlockNoteExtension && !ext.plugins.length) {
|
|
721
|
+
return undefined;
|
|
698
722
|
}
|
|
699
723
|
|
|
700
724
|
// "blocknote" extensions (prosemirror plugins)
|
|
701
725
|
return Extension.create({
|
|
702
726
|
name: key,
|
|
703
727
|
priority: ext.priority,
|
|
704
|
-
addProseMirrorPlugins: () =>
|
|
728
|
+
addProseMirrorPlugins: () => ext.plugins,
|
|
705
729
|
});
|
|
706
730
|
}),
|
|
707
|
-
];
|
|
731
|
+
].filter((ext): ext is Extension => ext !== undefined);
|
|
732
|
+
|
|
708
733
|
const tiptapOptions: BlockNoteTipTapEditorOptions = {
|
|
709
734
|
...blockNoteTipTapOptions,
|
|
710
735
|
...newOptions._tiptapOptions,
|
|
@@ -865,6 +890,26 @@ export class BlockNoteEditor<
|
|
|
865
890
|
}
|
|
866
891
|
}
|
|
867
892
|
|
|
893
|
+
// TO DISCUSS
|
|
894
|
+
/**
|
|
895
|
+
* Shorthand to get a typed extension from the editor, by
|
|
896
|
+
* just passing in the extension class.
|
|
897
|
+
*
|
|
898
|
+
* @param ext - The extension class to get
|
|
899
|
+
* @param key - optional, the key of the extension in the extensions object (defaults to the extension name)
|
|
900
|
+
* @returns The extension instance
|
|
901
|
+
*/
|
|
902
|
+
public extension<T extends BlockNoteExtension>(
|
|
903
|
+
ext: { new (...args: any[]): T } & typeof BlockNoteExtension,
|
|
904
|
+
key = ext.name(),
|
|
905
|
+
): T {
|
|
906
|
+
const extension = this.extensions[key] as T;
|
|
907
|
+
if (!extension) {
|
|
908
|
+
throw new Error(`Extension ${key} not found`);
|
|
909
|
+
}
|
|
910
|
+
return extension;
|
|
911
|
+
}
|
|
912
|
+
|
|
868
913
|
/**
|
|
869
914
|
* Mount the editor to a parent DOM element. Call mount(undefined) to clean up
|
|
870
915
|
*
|
|
@@ -946,15 +991,7 @@ export class BlockNoteEditor<
|
|
|
946
991
|
*/
|
|
947
992
|
public get document(): Block<BSchema, ISchema, SSchema>[] {
|
|
948
993
|
return this.transact((tr) => {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
tr.doc.firstChild!.descendants((node) => {
|
|
952
|
-
blocks.push(nodeToBlock(node, this.pmSchema));
|
|
953
|
-
|
|
954
|
-
return false;
|
|
955
|
-
});
|
|
956
|
-
|
|
957
|
-
return blocks;
|
|
994
|
+
return docToBlocks(tr.doc, this.pmSchema);
|
|
958
995
|
});
|
|
959
996
|
}
|
|
960
997
|
|
|
@@ -1099,12 +1136,26 @@ export class BlockNoteEditor<
|
|
|
1099
1136
|
}
|
|
1100
1137
|
|
|
1101
1138
|
/**
|
|
1102
|
-
* Gets a snapshot of the current selection.
|
|
1139
|
+
* Gets a snapshot of the current selection. This contains all blocks (included nested blocks)
|
|
1140
|
+
* that the selection spans across.
|
|
1141
|
+
*
|
|
1142
|
+
* If the selection starts / ends halfway through a block, the returned data will contain the entire block.
|
|
1103
1143
|
*/
|
|
1104
1144
|
public getSelection(): Selection<BSchema, ISchema, SSchema> | undefined {
|
|
1105
1145
|
return this.transact((tr) => getSelection(tr));
|
|
1106
1146
|
}
|
|
1107
1147
|
|
|
1148
|
+
/**
|
|
1149
|
+
* Gets a snapshot of the current selection. This contains all blocks (included nested blocks)
|
|
1150
|
+
* that the selection spans across.
|
|
1151
|
+
*
|
|
1152
|
+
* If the selection starts / ends halfway through a block, the returned block will be
|
|
1153
|
+
* only the part of the block that is included in the selection.
|
|
1154
|
+
*/
|
|
1155
|
+
public getSelectionCutBlocks() {
|
|
1156
|
+
return this.transact((tr) => getSelectionCutBlocks(tr));
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1108
1159
|
/**
|
|
1109
1160
|
* Sets the selection to a range of blocks.
|
|
1110
1161
|
* @param startBlock The identifier of the block that should be the start of the selection.
|
|
@@ -1500,7 +1551,7 @@ export class BlockNoteEditor<
|
|
|
1500
1551
|
);
|
|
1501
1552
|
}
|
|
1502
1553
|
|
|
1503
|
-
this.
|
|
1554
|
+
(this.extensions["yCursorPlugin"] as CursorPlugin).updateUser(user);
|
|
1504
1555
|
}
|
|
1505
1556
|
|
|
1506
1557
|
/**
|
|
@@ -1595,8 +1646,8 @@ export class BlockNoteEditor<
|
|
|
1595
1646
|
if (!this.prosemirrorView) {
|
|
1596
1647
|
return undefined;
|
|
1597
1648
|
}
|
|
1598
|
-
|
|
1599
|
-
const { selection } =
|
|
1649
|
+
|
|
1650
|
+
const { selection } = this.prosemirrorState;
|
|
1600
1651
|
|
|
1601
1652
|
// support for CellSelections
|
|
1602
1653
|
const { ranges } = selection;
|
|
@@ -1641,7 +1692,7 @@ export class BlockNoteEditor<
|
|
|
1641
1692
|
if (pluginState?.deleteTriggerCharacter) {
|
|
1642
1693
|
tr.insertText(triggerCharacter);
|
|
1643
1694
|
}
|
|
1644
|
-
tr.scrollIntoView().setMeta(this.suggestionMenus.
|
|
1695
|
+
tr.scrollIntoView().setMeta(this.suggestionMenus.plugins[0], {
|
|
1645
1696
|
triggerCharacter: triggerCharacter,
|
|
1646
1697
|
deleteTriggerCharacter: pluginState?.deleteTriggerCharacter || false,
|
|
1647
1698
|
ignoreQueryLength: pluginState?.ignoreQueryLength || false,
|