@blocknote/core 0.38.0 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/dist/BlockNoteSchema-DmZ6UQfY.cjs +11 -0
  2. package/dist/BlockNoteSchema-DmZ6UQfY.cjs.map +1 -0
  3. package/dist/BlockNoteSchema-oR047ACf.js +4275 -0
  4. package/dist/BlockNoteSchema-oR047ACf.js.map +1 -0
  5. package/dist/blocknote.cjs +4 -12
  6. package/dist/blocknote.cjs.map +1 -1
  7. package/dist/blocknote.js +3401 -7305
  8. package/dist/blocknote.js.map +1 -1
  9. package/dist/blocks.cjs +2 -0
  10. package/dist/blocks.cjs.map +1 -0
  11. package/dist/blocks.js +71 -0
  12. package/dist/blocks.js.map +1 -0
  13. package/dist/style.css +1 -1
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/webpack-stats.json +1 -1
  16. package/package.json +19 -16
  17. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +1 -1
  18. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +2 -2
  19. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +34 -25
  20. package/src/api/blockManipulation/setupTestEnv.ts +0 -1
  21. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +6 -10
  22. package/src/api/clipboard/fromClipboard/pasteExtension.ts +1 -1
  23. package/src/api/clipboard/toClipboard/copyExtension.ts +1 -1
  24. package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +128 -28
  25. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +101 -41
  26. package/src/api/pmUtil.ts +1 -1
  27. package/src/api/positionMapping.test.ts +58 -15
  28. package/src/api/positionMapping.ts +2 -4
  29. package/src/blocks/Audio/block.ts +174 -0
  30. package/src/blocks/BlockNoteSchema.ts +59 -0
  31. package/src/blocks/Code/block.ts +303 -0
  32. package/src/blocks/Code/shiki.ts +73 -0
  33. package/src/blocks/File/block.ts +98 -0
  34. package/src/blocks/{FileBlockContent → File}/helpers/render/createAddFileButton.ts +5 -2
  35. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileBlockWrapper.ts +15 -6
  36. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.ts +15 -2
  37. package/src/blocks/{FileBlockContent → File}/helpers/render/createResizableFileBlockWrapper.ts +21 -2
  38. package/src/blocks/Heading/block.ts +138 -0
  39. package/src/blocks/Image/block.ts +190 -0
  40. package/src/blocks/ListItem/BulletListItem/block.ts +116 -0
  41. package/src/blocks/ListItem/CheckListItem/block.ts +175 -0
  42. package/src/blocks/ListItem/NumberedListItem/IndexingPlugin.ts +173 -0
  43. package/src/blocks/ListItem/NumberedListItem/block.ts +133 -0
  44. package/src/blocks/ListItem/ToggleListItem/block.ts +78 -0
  45. package/src/blocks/PageBreak/block.ts +72 -0
  46. package/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.ts +9 -7
  47. package/src/blocks/Paragraph/block.ts +80 -0
  48. package/src/blocks/Quote/block.ts +90 -0
  49. package/src/blocks/{TableBlockContent/TableBlockContent.ts → Table/block.ts} +169 -51
  50. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +1 -1
  51. package/src/blocks/Video/block.ts +143 -0
  52. package/src/blocks/defaultBlockHelpers.ts +2 -2
  53. package/src/blocks/defaultBlockTypeGuards.ts +143 -174
  54. package/src/blocks/defaultBlocks.ts +107 -35
  55. package/src/blocks/defaultProps.ts +145 -4
  56. package/src/blocks/index.ts +26 -0
  57. package/src/blocks/utils/listItemEnterHandler.ts +42 -0
  58. package/src/editor/Block.css +54 -18
  59. package/src/editor/BlockNoteEditor.ts +256 -211
  60. package/src/editor/BlockNoteExtension.ts +92 -0
  61. package/src/editor/BlockNoteExtensions.ts +18 -17
  62. package/src/editor/defaultColors.ts +2 -2
  63. package/src/exporter/Exporter.ts +1 -1
  64. package/src/exporter/mapping.ts +1 -1
  65. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +3 -20
  66. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +6 -8
  67. package/src/extensions/BlockChange/BlockChangePlugin.ts +2 -1
  68. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +2 -2
  69. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +2 -2
  70. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -1
  71. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -1
  72. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +52 -0
  73. package/src/extensions/Collaboration/schemaMigration/migrationRules/index.ts +4 -0
  74. package/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.ts +4 -0
  75. package/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.ts +78 -0
  76. package/src/extensions/Comments/CommentsPlugin.ts +1 -1
  77. package/src/extensions/FilePanel/FilePanelPlugin.ts +5 -10
  78. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +1 -1
  79. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +4 -3
  80. package/src/extensions/Placeholder/PlaceholderPlugin.ts +6 -6
  81. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +1 -23
  82. package/src/extensions/SideMenu/SideMenuPlugin.ts +1 -3
  83. package/src/extensions/SideMenu/dragging.ts +2 -2
  84. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +4 -7
  85. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +6 -2
  86. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +24 -17
  87. package/src/extensions/TableHandles/TableHandlesPlugin.ts +8 -8
  88. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +5 -11
  89. package/src/extensions/TextColor/TextColorExtension.ts +3 -17
  90. package/src/extensions/TextColor/TextColorMark.ts +4 -9
  91. package/src/extensions/UniqueID/UniqueID.ts +6 -13
  92. package/src/index.ts +2 -28
  93. package/src/schema/blocks/createSpec.ts +342 -169
  94. package/src/schema/blocks/internal.ts +77 -138
  95. package/src/schema/blocks/types.ts +264 -94
  96. package/src/schema/index.ts +1 -0
  97. package/src/schema/inlineContent/createSpec.ts +99 -21
  98. package/src/schema/inlineContent/internal.ts +16 -7
  99. package/src/schema/inlineContent/types.ts +24 -2
  100. package/src/schema/propTypes.ts +15 -9
  101. package/src/schema/schema.ts +209 -0
  102. package/src/schema/styles/createSpec.ts +79 -31
  103. package/src/schema/styles/internal.ts +61 -2
  104. package/src/schema/styles/types.ts +17 -3
  105. package/src/util/topo-sort.test.ts +125 -0
  106. package/src/util/topo-sort.ts +160 -0
  107. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.d.ts +2 -1
  108. package/types/src/api/blockManipulation/selections/selection.d.ts +1 -1
  109. package/types/src/api/blockManipulation/setupTestEnv.d.ts +29 -543
  110. package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +1 -1
  111. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
  112. package/types/src/api/pmUtil.d.ts +1 -1
  113. package/types/src/blocks/Audio/block.d.ts +58 -0
  114. package/types/src/blocks/BlockNoteSchema.d.ts +18 -0
  115. package/types/src/blocks/{CodeBlockContent/CodeBlockContent.d.ts → Code/block.d.ts} +25 -26
  116. package/types/src/blocks/Code/shiki.d.ts +4 -0
  117. package/types/src/blocks/File/block.d.ts +37 -0
  118. package/types/src/blocks/File/helpers/render/createAddFileButton.d.ts +6 -0
  119. package/types/src/blocks/File/helpers/render/createFileBlockWrapper.d.ts +25 -0
  120. package/types/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.d.ts +6 -2
  121. package/types/src/blocks/File/helpers/render/createResizableFileBlockWrapper.d.ts +31 -0
  122. package/types/src/blocks/Heading/block.d.ts +71 -0
  123. package/types/src/blocks/Image/block.d.ts +102 -0
  124. package/types/src/blocks/ListItem/BulletListItem/block.d.ts +25 -0
  125. package/types/src/blocks/ListItem/CheckListItem/block.d.ts +33 -0
  126. package/types/src/blocks/ListItem/NumberedListItem/IndexingPlugin.d.ts +8 -0
  127. package/types/src/blocks/ListItem/NumberedListItem/block.d.ts +33 -0
  128. package/types/src/blocks/ListItem/ToggleListItem/block.d.ts +25 -0
  129. package/types/src/blocks/PageBreak/block.d.ts +11 -0
  130. package/types/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.d.ts +4 -2
  131. package/types/src/blocks/Paragraph/block.d.ts +25 -0
  132. package/types/src/blocks/Quote/block.d.ts +17 -0
  133. package/types/src/blocks/Table/block.d.ts +21 -0
  134. package/types/src/blocks/Video/block.d.ts +67 -0
  135. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -1
  136. package/types/src/blocks/defaultBlockTypeGuards.d.ts +15 -36
  137. package/types/src/blocks/defaultBlocks.d.ts +221 -1060
  138. package/types/src/blocks/defaultProps.d.ts +17 -1
  139. package/types/src/blocks/index.d.ts +24 -0
  140. package/types/src/blocks/utils/listItemEnterHandler.d.ts +2 -0
  141. package/types/src/editor/BlockNoteEditor.d.ts +36 -67
  142. package/types/src/editor/BlockNoteExtension.d.ts +67 -0
  143. package/types/src/editor/BlockNoteExtensions.d.ts +1 -1
  144. package/types/src/editor/defaultColors.d.ts +8 -76
  145. package/types/src/exporter/Exporter.d.ts +1 -1
  146. package/types/src/exporter/mapping.d.ts +1 -1
  147. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +4 -1
  148. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +7 -0
  149. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/index.d.ts +3 -0
  150. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.d.ts +3 -0
  151. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.d.ts +2 -0
  152. package/types/src/extensions/Comments/CommentsPlugin.d.ts +1 -1
  153. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  154. package/types/src/extensions/TextColor/TextColorMark.d.ts +4 -1
  155. package/types/src/index.d.ts +2 -25
  156. package/types/src/schema/blocks/createSpec.d.ts +16 -36
  157. package/types/src/schema/blocks/internal.d.ts +11 -33
  158. package/types/src/schema/blocks/types.d.ts +181 -57
  159. package/types/src/schema/index.d.ts +1 -0
  160. package/types/src/schema/inlineContent/createSpec.d.ts +36 -2
  161. package/types/src/schema/inlineContent/internal.d.ts +7 -15
  162. package/types/src/schema/inlineContent/types.d.ts +15 -1
  163. package/types/src/schema/propTypes.d.ts +4 -4
  164. package/types/src/schema/schema.d.ts +40 -0
  165. package/types/src/schema/styles/createSpec.d.ts +6 -4
  166. package/types/src/schema/styles/internal.d.ts +6 -3
  167. package/types/src/schema/styles/types.d.ts +11 -2
  168. package/types/src/util/topo-sort.d.ts +18 -0
  169. package/types/src/util/topo-sort.test.d.ts +1 -0
  170. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +0 -144
  171. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +0 -445
  172. package/src/blocks/FileBlockContent/FileBlockContent.ts +0 -100
  173. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +0 -159
  174. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +0 -159
  175. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +0 -134
  176. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +0 -299
  177. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +0 -86
  178. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +0 -172
  179. package/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.ts +0 -104
  180. package/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts +0 -49
  181. package/src/blocks/PageBreakBlockContent/schema.ts +0 -40
  182. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +0 -78
  183. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +0 -121
  184. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +0 -158
  185. package/src/editor/BlockNoteSchema.ts +0 -107
  186. package/src/editor/BlockNoteTipTapEditor.ts +0 -335
  187. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +0 -99
  188. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +0 -90
  189. package/types/src/blocks/FileBlockContent/helpers/render/createAddFileButton.d.ts +0 -6
  190. package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +0 -9
  191. package/types/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.d.ts +0 -9
  192. package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +0 -67
  193. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +0 -131
  194. package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +0 -46
  195. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +0 -55
  196. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +0 -2
  197. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +0 -58
  198. package/types/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.d.ts +0 -46
  199. package/types/src/blocks/PageBreakBlockContent/PageBreakBlockContent.d.ts +0 -31
  200. package/types/src/blocks/PageBreakBlockContent/schema.d.ts +0 -86
  201. package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +0 -52
  202. package/types/src/blocks/QuoteBlockContent/QuoteBlockContent.d.ts +0 -52
  203. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +0 -39
  204. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +0 -131
  205. package/types/src/editor/BlockNoteSchema.d.ts +0 -34
  206. package/types/src/editor/BlockNoteTipTapEditor.d.ts +0 -43
  207. /package/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.ts +0 -0
  208. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.ts +0 -0
  209. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.ts +0 -0
  210. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.ts +0 -0
  211. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.ts +0 -0
  212. /package/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
  213. /package/src/blocks/{ImageBlockContent → Image}/parseImageElement.ts +0 -0
  214. /package/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.ts +0 -0
  215. /package/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.ts +0 -0
  216. /package/src/blocks/{TableBlockContent → Table}/TableExtension.ts +0 -0
  217. /package/src/blocks/{VideoBlockContent → Video}/parseVideoElement.ts +0 -0
  218. /package/types/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.d.ts +0 -0
  219. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.d.ts +0 -0
  220. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.d.ts +0 -0
  221. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.d.ts +0 -0
  222. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.d.ts +0 -0
  223. /package/types/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
  224. /package/types/src/blocks/{ImageBlockContent → Image}/parseImageElement.d.ts +0 -0
  225. /package/types/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.d.ts +0 -0
  226. /package/types/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.d.ts +0 -0
  227. /package/types/src/blocks/{TableBlockContent → Table}/TableExtension.d.ts +0 -0
  228. /package/types/src/blocks/{VideoBlockContent → Video}/parseVideoElement.d.ts +0 -0
@@ -1,69 +1,21 @@
1
- import { Editor } from "@tiptap/core";
2
- import { TagParseRule } from "@tiptap/pm/model";
3
- import { NodeView, ViewMutationRecord } from "@tiptap/pm/view";
4
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
5
- import { InlineContentSchema } from "../inlineContent/types.js";
6
- import { StyleSchema } from "../styles/types.js";
1
+ import { Editor, Node } from "@tiptap/core";
2
+ import { DOMParser, Fragment, TagParseRule } from "@tiptap/pm/model";
3
+ import { NodeView } from "@tiptap/pm/view";
4
+ import { mergeParagraphs } from "../../blocks/defaultBlockHelpers.js";
5
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
6
+ import { PropSchema } from "../propTypes.js";
7
7
  import {
8
- createInternalBlockSpec,
9
- createStronglyTypedTiptapNode,
10
8
  getBlockFromPos,
11
9
  propsToAttributes,
12
10
  wrapInBlockStructure,
13
11
  } from "./internal.js";
14
12
  import {
15
13
  BlockConfig,
16
- BlockFromConfig,
17
- BlockSchemaWithBlock,
18
- PartialBlockFromConfig,
14
+ BlockImplementation,
15
+ BlockSpec,
16
+ LooseBlockSpec,
19
17
  } from "./types.js";
20
18
 
21
- // restrict content to "inline" and "none" only
22
- export type CustomBlockConfig = BlockConfig & {
23
- content: "inline" | "none";
24
- };
25
-
26
- export type CustomBlockImplementation<
27
- T extends CustomBlockConfig,
28
- I extends InlineContentSchema,
29
- S extends StyleSchema,
30
- > = {
31
- render: (
32
- /**
33
- * The custom block to render
34
- */
35
- block: BlockFromConfig<T, I, S>,
36
- /**
37
- * The BlockNote editor instance
38
- * This is typed generically. If you want an editor with your custom schema, you need to
39
- * cast it manually, e.g.: `const e = editor as BlockNoteEditor<typeof mySchema>;`
40
- */
41
- editor: BlockNoteEditor<BlockSchemaWithBlock<T["type"], T>, I, S>,
42
- // (note) if we want to fix the manual cast, we need to prevent circular references and separate block definition and render implementations
43
- // or allow manually passing <BSchema>, but that's not possible without passing the other generics because Typescript doesn't support partial inferred generics
44
- ) => {
45
- dom: HTMLElement;
46
- contentDOM?: HTMLElement;
47
- ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
48
- destroy?: () => void;
49
- };
50
- // Exports block to external HTML. If not defined, the output will be the same
51
- // as `render(...).dom`. Used to create clipboard data when pasting outside
52
- // BlockNote.
53
- // TODO: Maybe can return undefined to ignore when serializing?
54
- toExternalHTML?: (
55
- block: BlockFromConfig<T, I, S>,
56
- editor: BlockNoteEditor<BlockSchemaWithBlock<T["type"], T>, I, S>,
57
- ) => {
58
- dom: HTMLElement;
59
- contentDOM?: HTMLElement;
60
- };
61
-
62
- parse?: (
63
- el: HTMLElement,
64
- ) => PartialBlockFromConfig<T, I, S>["props"] | undefined;
65
- };
66
-
67
19
  // Function that causes events within non-selectable blocks to be handled by the
68
20
  // browser instead of the editor.
69
21
  export function applyNonSelectableBlockFix(nodeView: NodeView, editor: Editor) {
@@ -84,9 +36,13 @@ export function applyNonSelectableBlockFix(nodeView: NodeView, editor: Editor) {
84
36
  // Function that uses the 'parse' function of a blockConfig to create a
85
37
  // TipTap node's `parseHTML` property. This is only used for parsing content
86
38
  // from the clipboard.
87
- export function getParseRules(
88
- config: BlockConfig,
89
- customParseFunction: CustomBlockImplementation<any, any, any>["parse"],
39
+ export function getParseRules<
40
+ TName extends string,
41
+ TProps extends PropSchema,
42
+ TContent extends "inline" | "none" | "table",
43
+ >(
44
+ config: BlockConfig<TName, TProps, TContent>,
45
+ implementation: BlockImplementation<TName, TProps, TContent>,
90
46
  ) {
91
47
  const rules: TagParseRule[] = [
92
48
  {
@@ -95,7 +51,7 @@ export function getParseRules(
95
51
  },
96
52
  ];
97
53
 
98
- if (customParseFunction) {
54
+ if (implementation.parse) {
99
55
  rules.push({
100
56
  tag: "*",
101
57
  getAttrs(node: string | HTMLElement) {
@@ -103,7 +59,7 @@ export function getParseRules(
103
59
  return false;
104
60
  }
105
61
 
106
- const props = customParseFunction?.(node);
62
+ const props = implementation.parse?.(node);
107
63
 
108
64
  if (props === undefined) {
109
65
  return false;
@@ -111,6 +67,40 @@ export function getParseRules(
111
67
 
112
68
  return props;
113
69
  },
70
+ getContent:
71
+ config.content === "inline" || config.content === "none"
72
+ ? (node, schema) => {
73
+ if (implementation.parseContent) {
74
+ return implementation.parseContent({
75
+ el: node as HTMLElement,
76
+ schema,
77
+ });
78
+ }
79
+
80
+ if (config.content === "inline") {
81
+ // Parse the inline content if it exists
82
+ const element = node as HTMLElement;
83
+
84
+ // Clone to avoid modifying the original
85
+ const clone = element.cloneNode(true) as HTMLElement;
86
+
87
+ // Merge multiple paragraphs into one with line breaks
88
+ mergeParagraphs(
89
+ clone,
90
+ implementation.meta?.code ? "\n" : "<br>",
91
+ );
92
+
93
+ // Parse the content directly as a paragraph to extract inline content
94
+ const parser = DOMParser.fromSchema(schema);
95
+ const parsed = parser.parse(clone, {
96
+ topNode: schema.nodes.paragraph.create(),
97
+ });
98
+
99
+ return parsed.content;
100
+ }
101
+ return Fragment.empty;
102
+ }
103
+ : undefined,
114
104
  });
115
105
  }
116
106
  // getContent(node, schema) {
@@ -134,84 +124,90 @@ export function getParseRules(
134
124
 
135
125
  // A function to create custom block for API consumers
136
126
  // we want to hide the tiptap node from API consumers and provide a simpler API surface instead
137
- export function createBlockSpec<
138
- T extends CustomBlockConfig,
139
- I extends InlineContentSchema,
140
- S extends StyleSchema,
127
+ export function addNodeAndExtensionsToSpec<
128
+ TName extends string,
129
+ TProps extends PropSchema,
130
+ TContent extends "inline" | "none" | "table",
141
131
  >(
142
- blockConfig: T,
143
- blockImplementation: CustomBlockImplementation<NoInfer<T>, I, S>,
144
- ) {
145
- const node = createStronglyTypedTiptapNode({
146
- name: blockConfig.type as T["type"],
147
- content: (blockConfig.content === "inline"
148
- ? "inline*"
149
- : "") as T["content"] extends "inline" ? "inline*" : "",
150
- group: "blockContent",
151
- selectable: blockConfig.isSelectable ?? true,
152
- isolating: true,
153
- addAttributes() {
154
- return propsToAttributes(blockConfig.propSchema);
155
- },
156
-
157
- parseHTML() {
158
- return getParseRules(blockConfig, blockImplementation.parse);
159
- },
132
+ blockConfig: BlockConfig<TName, TProps, TContent>,
133
+ blockImplementation: BlockImplementation<TName, TProps, TContent>,
134
+ extensions?: BlockNoteExtension<any>[],
135
+ priority?: number,
136
+ ): LooseBlockSpec<TName, TProps, TContent> {
137
+ const node =
138
+ ((blockImplementation as any).node as Node) ||
139
+ Node.create({
140
+ name: blockConfig.type,
141
+ content: (blockConfig.content === "inline"
142
+ ? "inline*"
143
+ : blockConfig.content === "none"
144
+ ? ""
145
+ : blockConfig.content) as TContent extends "inline" ? "inline*" : "",
146
+ group: "blockContent",
147
+ selectable: blockImplementation.meta?.selectable ?? true,
148
+ isolating: blockImplementation.meta?.isolating ?? true,
149
+ code: blockImplementation.meta?.code ?? false,
150
+ defining: blockImplementation.meta?.defining ?? true,
151
+ priority,
152
+ addAttributes() {
153
+ return propsToAttributes(blockConfig.propSchema);
154
+ },
160
155
 
161
- renderHTML({ HTMLAttributes }) {
162
- // renderHTML is used for copy/pasting content from the editor back into
163
- // the editor, so we need to make sure the `blockContent` element is
164
- // structured correctly as this is what's used for parsing blocks. We
165
- // just render a placeholder div inside as the `blockContent` element
166
- // already has all the information needed for proper parsing.
167
- const div = document.createElement("div");
168
- return wrapInBlockStructure(
169
- {
170
- dom: div,
171
- contentDOM: blockConfig.content === "inline" ? div : undefined,
172
- },
173
- blockConfig.type,
174
- {},
175
- blockConfig.propSchema,
176
- blockConfig.isFileBlock,
177
- HTMLAttributes,
178
- );
179
- },
156
+ parseHTML() {
157
+ return getParseRules(blockConfig, blockImplementation);
158
+ },
180
159
 
181
- addNodeView() {
182
- return ({ getPos }) => {
183
- // Gets the BlockNote editor instance
184
- const editor = this.options.editor;
185
- // Gets the block
186
- const block = getBlockFromPos(
187
- getPos,
188
- editor,
189
- this.editor,
160
+ renderHTML({ HTMLAttributes }) {
161
+ // renderHTML is used for copy/pasting content from the editor back into
162
+ // the editor, so we need to make sure the `blockContent` element is
163
+ // structured correctly as this is what's used for parsing blocks. We
164
+ // just render a placeholder div inside as the `blockContent` element
165
+ // already has all the information needed for proper parsing.
166
+ const div = document.createElement("div");
167
+ return wrapInBlockStructure(
168
+ {
169
+ dom: div,
170
+ contentDOM: blockConfig.content === "inline" ? div : undefined,
171
+ },
190
172
  blockConfig.type,
173
+ {},
174
+ blockConfig.propSchema,
175
+ blockImplementation.meta?.fileBlockAccept !== undefined,
176
+ HTMLAttributes,
191
177
  );
192
- // Gets the custom HTML attributes for `blockContent` nodes
193
- const blockContentDOMAttributes =
194
- this.options.domAttributes?.blockContent || {};
178
+ },
195
179
 
196
- const output = blockImplementation.render(block as any, editor);
180
+ addNodeView() {
181
+ return (props) => {
182
+ // Gets the BlockNote editor instance
183
+ const editor = this.options.editor;
184
+ // Gets the block
185
+ const block = getBlockFromPos(
186
+ props.getPos,
187
+ editor,
188
+ this.editor,
189
+ blockConfig.type,
190
+ );
191
+ // Gets the custom HTML attributes for `blockContent` nodes
192
+ const blockContentDOMAttributes =
193
+ this.options.domAttributes?.blockContent || {};
197
194
 
198
- const nodeView: NodeView = wrapInBlockStructure(
199
- output,
200
- block.type,
201
- block.props,
202
- blockConfig.propSchema,
203
- blockConfig.isFileBlock,
204
- blockContentDOMAttributes,
205
- );
195
+ const nodeView = blockImplementation.render.call(
196
+ { blockContentDOMAttributes, props, renderType: "nodeView" },
197
+ block as any,
198
+ editor as any,
199
+ );
206
200
 
207
- if (blockConfig.isSelectable === false) {
208
- applyNonSelectableBlockFix(nodeView, this.editor);
209
- }
201
+ if (blockImplementation.meta?.selectable === false) {
202
+ applyNonSelectableBlockFix(nodeView, this.editor);
203
+ }
210
204
 
211
- return nodeView;
212
- };
213
- },
214
- });
205
+ // See explanation for why `update` is not implemented for NodeViews
206
+ // https://github.com/TypeCellOS/BlockNote/pull/1904#discussion_r2313461464
207
+ return nodeView;
208
+ };
209
+ },
210
+ });
215
211
 
216
212
  if (node.name !== blockConfig.type) {
217
213
  throw new Error(
@@ -219,43 +215,220 @@ export function createBlockSpec<
219
215
  );
220
216
  }
221
217
 
222
- return createInternalBlockSpec(blockConfig, {
223
- node,
224
- toInternalHTML: (block, editor) => {
225
- const blockContentDOMAttributes =
226
- node.options.domAttributes?.blockContent || {};
227
-
228
- const output = blockImplementation.render(block as any, editor as any);
229
-
230
- return wrapInBlockStructure(
231
- output,
232
- block.type,
233
- block.props,
234
- blockConfig.propSchema,
235
- blockConfig.isFileBlock,
236
- blockContentDOMAttributes,
237
- );
238
- },
239
- // TODO: this should not have wrapInBlockStructure and generally be a lot simpler
240
- // post-processing in externalHTMLExporter should not be necessary
241
- toExternalHTML: (block, editor) => {
242
- const blockContentDOMAttributes =
243
- node.options.domAttributes?.blockContent || {};
244
-
245
- let output = blockImplementation.toExternalHTML?.(
246
- block as any,
247
- editor as any,
248
- );
249
- if (output === undefined) {
250
- output = blockImplementation.render(block as any, editor as any);
251
- }
252
- return wrapInBlockStructure(
253
- output,
254
- block.type,
255
- block.props,
256
- blockConfig.propSchema,
257
- blockContentDOMAttributes,
258
- );
218
+ return {
219
+ config: blockConfig,
220
+ implementation: {
221
+ node,
222
+ render(block, editor) {
223
+ const blockContentDOMAttributes =
224
+ node.options.domAttributes?.blockContent || {};
225
+
226
+ return blockImplementation.render.call(
227
+ {
228
+ blockContentDOMAttributes,
229
+ props: undefined,
230
+ renderType: "dom",
231
+ },
232
+ block as any,
233
+ editor as any,
234
+ );
235
+ },
236
+ // TODO: this should not have wrapInBlockStructure and generally be a lot simpler
237
+ // post-processing in externalHTMLExporter should not be necessary
238
+ toExternalHTML: (block, editor) => {
239
+ const blockContentDOMAttributes =
240
+ node.options.domAttributes?.blockContent || {};
241
+
242
+ return (
243
+ blockImplementation.toExternalHTML?.call(
244
+ { blockContentDOMAttributes },
245
+ block as any,
246
+ editor as any,
247
+ ) ??
248
+ blockImplementation.render.call(
249
+ { blockContentDOMAttributes, renderType: "dom", props: undefined },
250
+ block as any,
251
+ editor as any,
252
+ )
253
+ );
254
+ },
259
255
  },
260
- });
256
+ extensions,
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Helper function to create a block config.
262
+ */
263
+ export function createBlockConfig<
264
+ TCallback extends (
265
+ options: Partial<Record<string, any>>,
266
+ ) => BlockConfig<any, any, any>,
267
+ TOptions extends Parameters<TCallback>[0],
268
+ TName extends ReturnType<TCallback>["type"],
269
+ TProps extends ReturnType<TCallback>["propSchema"],
270
+ TContent extends ReturnType<TCallback>["content"],
271
+ >(
272
+ callback: TCallback,
273
+ ): TOptions extends undefined
274
+ ? () => BlockConfig<TName, TProps, TContent>
275
+ : (options: TOptions) => BlockConfig<TName, TProps, TContent> {
276
+ return callback as any;
277
+ }
278
+
279
+ /**
280
+ * Helper function to create a block definition.
281
+ * Can accept either functions that return the required objects, or the objects directly.
282
+ */
283
+ export function createBlockSpec<
284
+ const TName extends string,
285
+ const TProps extends PropSchema,
286
+ const TContent extends "inline" | "none",
287
+ const TOptions extends Partial<Record<string, any>> | undefined = undefined,
288
+ >(
289
+ blockConfigOrCreator: BlockConfig<TName, TProps, TContent>,
290
+ blockImplementationOrCreator:
291
+ | BlockImplementation<TName, TProps, TContent>
292
+ | (TOptions extends undefined
293
+ ? () => BlockImplementation<TName, TProps, TContent>
294
+ : (
295
+ options: Partial<TOptions>,
296
+ ) => BlockImplementation<TName, TProps, TContent>),
297
+ extensionsOrCreator?:
298
+ | BlockNoteExtension<any>[]
299
+ | (TOptions extends undefined
300
+ ? () => BlockNoteExtension<any>[]
301
+ : (options: Partial<TOptions>) => BlockNoteExtension<any>[]),
302
+ ): (options?: Partial<TOptions>) => BlockSpec<TName, TProps, TContent>;
303
+ export function createBlockSpec<
304
+ const TName extends string,
305
+ const TProps extends PropSchema,
306
+ const TContent extends "inline" | "none",
307
+ const BlockConf extends BlockConfig<TName, TProps, TContent>,
308
+ const TOptions extends Partial<Record<string, any>>,
309
+ >(
310
+ blockCreator: (options: Partial<TOptions>) => BlockConf,
311
+ blockImplementationOrCreator:
312
+ | BlockImplementation<
313
+ BlockConf["type"],
314
+ BlockConf["propSchema"],
315
+ BlockConf["content"]
316
+ >
317
+ | (TOptions extends undefined
318
+ ? () => BlockImplementation<
319
+ BlockConf["type"],
320
+ BlockConf["propSchema"],
321
+ BlockConf["content"]
322
+ >
323
+ : (
324
+ options: Partial<TOptions>,
325
+ ) => BlockImplementation<
326
+ BlockConf["type"],
327
+ BlockConf["propSchema"],
328
+ BlockConf["content"]
329
+ >),
330
+ extensionsOrCreator?:
331
+ | BlockNoteExtension<any>[]
332
+ | (TOptions extends undefined
333
+ ? () => BlockNoteExtension<any>[]
334
+ : (options: Partial<TOptions>) => BlockNoteExtension<any>[]),
335
+ ): (
336
+ options?: Partial<TOptions>,
337
+ ) => BlockSpec<
338
+ BlockConf["type"],
339
+ BlockConf["propSchema"],
340
+ BlockConf["content"]
341
+ >;
342
+ export function createBlockSpec<
343
+ const TName extends string,
344
+ const TProps extends PropSchema,
345
+ const TContent extends "inline" | "none",
346
+ const TOptions extends Partial<Record<string, any>> | undefined = undefined,
347
+ >(
348
+ blockConfigOrCreator:
349
+ | BlockConfig<TName, TProps, TContent>
350
+ | (TOptions extends undefined
351
+ ? () => BlockConfig<TName, TProps, TContent>
352
+ : (options: Partial<TOptions>) => BlockConfig<TName, TProps, TContent>),
353
+ blockImplementationOrCreator:
354
+ | BlockImplementation<TName, TProps, TContent>
355
+ | (TOptions extends undefined
356
+ ? () => BlockImplementation<TName, TProps, TContent>
357
+ : (
358
+ options: Partial<TOptions>,
359
+ ) => BlockImplementation<TName, TProps, TContent>),
360
+ extensionsOrCreator?:
361
+ | BlockNoteExtension<any>[]
362
+ | (TOptions extends undefined
363
+ ? () => BlockNoteExtension<any>[]
364
+ : (options: Partial<TOptions>) => BlockNoteExtension<any>[]),
365
+ ): (options?: Partial<TOptions>) => BlockSpec<TName, TProps, TContent> {
366
+ return (options = {} as TOptions) => {
367
+ const blockConfig =
368
+ typeof blockConfigOrCreator === "function"
369
+ ? blockConfigOrCreator(options as any)
370
+ : blockConfigOrCreator;
371
+
372
+ const blockImplementation =
373
+ typeof blockImplementationOrCreator === "function"
374
+ ? blockImplementationOrCreator(options as any)
375
+ : blockImplementationOrCreator;
376
+
377
+ const extensions = extensionsOrCreator
378
+ ? typeof extensionsOrCreator === "function"
379
+ ? extensionsOrCreator(options as any)
380
+ : extensionsOrCreator
381
+ : undefined;
382
+
383
+ return {
384
+ config: blockConfig,
385
+ implementation: {
386
+ ...blockImplementation,
387
+ // TODO: this should not have wrapInBlockStructure and generally be a lot simpler
388
+ // post-processing in externalHTMLExporter should not be necessary
389
+ toExternalHTML(block, editor) {
390
+ const output = blockImplementation.toExternalHTML?.call(
391
+ { blockContentDOMAttributes: this.blockContentDOMAttributes },
392
+ block as any,
393
+ editor as any,
394
+ );
395
+
396
+ if (output === undefined) {
397
+ return undefined;
398
+ }
399
+
400
+ return wrapInBlockStructure(
401
+ output,
402
+ block.type,
403
+ block.props,
404
+ blockConfig.propSchema,
405
+ blockImplementation.meta?.fileBlockAccept !== undefined,
406
+ );
407
+ },
408
+ render(block, editor) {
409
+ const output = blockImplementation.render.call(
410
+ {
411
+ blockContentDOMAttributes: this.blockContentDOMAttributes,
412
+ renderType: this.renderType,
413
+ props: this.props as any,
414
+ },
415
+ block as any,
416
+ editor as any,
417
+ );
418
+
419
+ const nodeView = wrapInBlockStructure(
420
+ output,
421
+ block.type,
422
+ block.props,
423
+ blockConfig.propSchema,
424
+ blockImplementation.meta?.fileBlockAccept !== undefined,
425
+ this.blockContentDOMAttributes,
426
+ ) satisfies NodeView;
427
+
428
+ return nodeView;
429
+ },
430
+ },
431
+ extensions: extensions,
432
+ };
433
+ };
261
434
  }