@tiptap/core 2.9.1 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/Editor.d.ts.map +1 -1
  2. package/dist/EventEmitter.d.ts +1 -0
  3. package/dist/EventEmitter.d.ts.map +1 -1
  4. package/dist/Extension.d.ts +2 -2
  5. package/dist/Extension.d.ts.map +1 -1
  6. package/dist/InputRule.d.ts.map +1 -1
  7. package/dist/Mark.d.ts +2 -2
  8. package/dist/Mark.d.ts.map +1 -1
  9. package/dist/Node.d.ts +18 -2
  10. package/dist/Node.d.ts.map +1 -1
  11. package/dist/PasteRule.d.ts +1 -1
  12. package/dist/PasteRule.d.ts.map +1 -1
  13. package/dist/commands/insertContent.d.ts +2 -2
  14. package/dist/commands/insertContent.d.ts.map +1 -1
  15. package/dist/commands/insertContentAt.d.ts +2 -2
  16. package/dist/commands/insertContentAt.d.ts.map +1 -1
  17. package/dist/commands/setContent.d.ts +2 -2
  18. package/dist/commands/setContent.d.ts.map +1 -1
  19. package/dist/commands/setNode.d.ts.map +1 -1
  20. package/dist/commands/updateAttributes.d.ts.map +1 -1
  21. package/dist/helpers/createDocument.d.ts +2 -2
  22. package/dist/helpers/createDocument.d.ts.map +1 -1
  23. package/dist/helpers/createNodeFromContent.d.ts +1 -1
  24. package/dist/helpers/createNodeFromContent.d.ts.map +1 -1
  25. package/dist/helpers/getMarkRange.d.ts +17 -1
  26. package/dist/helpers/getMarkRange.d.ts.map +1 -1
  27. package/dist/helpers/getSchemaByResolvedExtensions.d.ts.map +1 -1
  28. package/dist/index.cjs +158 -44
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.js +159 -45
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.umd.js +158 -44
  33. package/dist/index.umd.js.map +1 -1
  34. package/dist/inputRules/markInputRule.d.ts +1 -1
  35. package/dist/inputRules/nodeInputRule.d.ts +1 -1
  36. package/dist/inputRules/textInputRule.d.ts +1 -1
  37. package/dist/inputRules/textblockTypeInputRule.d.ts +1 -1
  38. package/dist/inputRules/wrappingInputRule.d.ts +1 -1
  39. package/dist/pasteRules/markPasteRule.d.ts +1 -1
  40. package/dist/pasteRules/nodePasteRule.d.ts +1 -1
  41. package/dist/pasteRules/textPasteRule.d.ts +1 -1
  42. package/package.json +2 -2
  43. package/src/Editor.ts +5 -8
  44. package/src/EventEmitter.ts +9 -0
  45. package/src/Extension.ts +2 -2
  46. package/src/InputRule.ts +45 -30
  47. package/src/Mark.ts +2 -2
  48. package/src/Node.ts +21 -2
  49. package/src/PasteRule.ts +67 -42
  50. package/src/commands/insertContent.ts +9 -9
  51. package/src/commands/insertContentAt.ts +11 -1
  52. package/src/commands/setContent.ts +10 -14
  53. package/src/commands/setNode.ts +9 -2
  54. package/src/commands/updateAttributes.ts +72 -12
  55. package/src/helpers/createDocument.ts +4 -2
  56. package/src/helpers/createNodeFromContent.ts +4 -1
  57. package/src/helpers/getMarkRange.ts +29 -5
  58. package/src/helpers/getSchemaByResolvedExtensions.ts +1 -0
  59. package/src/inputRules/markInputRule.ts +1 -1
  60. package/src/inputRules/nodeInputRule.ts +1 -1
  61. package/src/inputRules/textInputRule.ts +1 -1
  62. package/src/inputRules/textblockTypeInputRule.ts +1 -1
  63. package/src/inputRules/wrappingInputRule.ts +1 -1
  64. package/src/pasteRules/markPasteRule.ts +1 -1
  65. package/src/pasteRules/nodePasteRule.ts +1 -1
  66. package/src/pasteRules/textPasteRule.ts +1 -1
@@ -4,7 +4,7 @@ import { ExtendedRegExpMatchArray } from '../types.js';
4
4
  /**
5
5
  * Build an input rule that adds a mark when the
6
6
  * matched text is typed into it.
7
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
7
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
8
8
  */
9
9
  export declare function markInputRule(config: {
10
10
  find: InputRuleFinder;
@@ -4,7 +4,7 @@ import { ExtendedRegExpMatchArray } from '../types.js';
4
4
  /**
5
5
  * Build an input rule that adds a node when the
6
6
  * matched text is typed into it.
7
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
7
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
8
8
  */
9
9
  export declare function nodeInputRule(config: {
10
10
  /**
@@ -2,7 +2,7 @@ import { InputRule, InputRuleFinder } from '../InputRule.js';
2
2
  /**
3
3
  * Build an input rule that replaces text when the
4
4
  * matched text is typed into it.
5
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
5
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
6
6
  */
7
7
  export declare function textInputRule(config: {
8
8
  find: InputRuleFinder;
@@ -6,7 +6,7 @@ import { ExtendedRegExpMatchArray } from '../types.js';
6
6
  * matched text is typed into it. When using a regular expresion you’ll
7
7
  * probably want the regexp to start with `^`, so that the pattern can
8
8
  * only occur at the start of a textblock.
9
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
9
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
10
10
  */
11
11
  export declare function textblockTypeInputRule(config: {
12
12
  find: InputRuleFinder;
@@ -15,7 +15,7 @@ import { ExtendedRegExpMatchArray } from '../types.js';
15
15
  * two nodes. You can pass a join predicate, which takes a regular
16
16
  * expression match and the node before the wrapped node, and can
17
17
  * return a boolean to indicate whether a join should happen.
18
- * @see https://tiptap.dev/guide/custom-extensions/#input-rules
18
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules
19
19
  */
20
20
  export declare function wrappingInputRule(config: {
21
21
  find: InputRuleFinder;
@@ -4,7 +4,7 @@ import { ExtendedRegExpMatchArray } from '../types.js';
4
4
  /**
5
5
  * Build an paste rule that adds a mark when the
6
6
  * matched text is pasted into it.
7
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
7
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
8
8
  */
9
9
  export declare function markPasteRule(config: {
10
10
  find: PasteRuleFinder;
@@ -4,7 +4,7 @@ import { ExtendedRegExpMatchArray, JSONContent } from '../types.js';
4
4
  /**
5
5
  * Build an paste rule that adds a node when the
6
6
  * matched text is pasted into it.
7
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
7
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
8
8
  */
9
9
  export declare function nodePasteRule(config: {
10
10
  find: PasteRuleFinder;
@@ -2,7 +2,7 @@ import { PasteRule, PasteRuleFinder } from '../PasteRule.js';
2
2
  /**
3
3
  * Build an paste rule that replaces text when the
4
4
  * matched text is pasted into it.
5
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
5
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
6
6
  */
7
7
  export declare function textPasteRule(config: {
8
8
  find: PasteRuleFinder;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/core",
3
3
  "description": "headless rich text editor",
4
- "version": "2.9.1",
4
+ "version": "2.10.0",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -32,7 +32,7 @@
32
32
  "dist"
33
33
  ],
34
34
  "devDependencies": {
35
- "@tiptap/pm": "^2.9.1"
35
+ "@tiptap/pm": "^2.10.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@tiptap/pm": "^2.7.0"
package/src/Editor.ts CHANGED
@@ -360,6 +360,11 @@ export class Editor extends EventEmitter<EditorEvents> {
360
360
 
361
361
  this.view = new EditorView(this.options.element, {
362
362
  ...this.options.editorProps,
363
+ attributes: {
364
+ // add `role="textbox"` to the editor element
365
+ role: 'textbox',
366
+ ...this.options.editorProps?.attributes,
367
+ },
363
368
  dispatchTransaction: this.dispatchTransaction.bind(this),
364
369
  state: EditorState.create({
365
370
  doc,
@@ -367,14 +372,6 @@ export class Editor extends EventEmitter<EditorEvents> {
367
372
  }),
368
373
  })
369
374
 
370
- // add `role="textbox"` to the editor element
371
- this.view.dom.setAttribute('role', 'textbox')
372
-
373
- // add aria-label to the editor element
374
- if (!this.view.dom.getAttribute('aria-label')) {
375
- this.view.dom.setAttribute('aria-label', 'Rich-Text Editor')
376
- }
377
-
378
375
  // `editor.view` is not yet available at this time.
379
376
  // Therefore we will add all plugins and node views directly afterwards.
380
377
  const newState = this.state.reconfigure({
@@ -46,6 +46,15 @@ export class EventEmitter<T extends Record<string, any>> {
46
46
  return this
47
47
  }
48
48
 
49
+ public once<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this {
50
+ const onceFn = (...args: CallbackType<T, EventName>) => {
51
+ this.off(event, onceFn)
52
+ fn.apply(this, args)
53
+ }
54
+
55
+ return this.on(event, onceFn)
56
+ }
57
+
49
58
  public removeAllListeners(): void {
50
59
  this.callbacks = {}
51
60
  }
package/src/Extension.ts CHANGED
@@ -61,7 +61,7 @@ declare module '@tiptap/core' {
61
61
  */
62
62
  addOptions?: (this: {
63
63
  name: string
64
- parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addOptions'], undefined>
64
+ parent: ParentConfig<ExtensionConfig<Options, Storage>>['addOptions']
65
65
  }) => Options
66
66
 
67
67
  /**
@@ -76,7 +76,7 @@ declare module '@tiptap/core' {
76
76
  addStorage?: (this: {
77
77
  name: string
78
78
  options: Options
79
- parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addStorage'], undefined>
79
+ parent: ParentConfig<ExtensionConfig<Options, Storage>>['addStorage']
80
80
  }) => Storage
81
81
 
82
82
  /**
package/src/InputRule.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import { Fragment, Node as ProseMirrorNode } from '@tiptap/pm/model'
1
2
  import { EditorState, Plugin, TextSelection } from '@tiptap/pm/state'
2
3
 
3
4
  import { CommandManager } from './CommandManager.js'
4
5
  import { Editor } from './Editor.js'
5
6
  import { createChainableState } from './helpers/createChainableState.js'
7
+ import { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'
6
8
  import { getTextContentFromNodes } from './helpers/getTextContentFromNodes.js'
7
9
  import {
8
10
  CanCommands,
@@ -14,37 +16,37 @@ import {
14
16
  import { isRegExp } from './utilities/isRegExp.js'
15
17
 
16
18
  export type InputRuleMatch = {
17
- index: number
18
- text: string
19
- replaceWith?: string
20
- match?: RegExpMatchArray
21
- data?: Record<string, any>
22
- }
19
+ index: number;
20
+ text: string;
21
+ replaceWith?: string;
22
+ match?: RegExpMatchArray;
23
+ data?: Record<string, any>;
24
+ };
23
25
 
24
- export type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null)
26
+ export type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null);
25
27
 
26
28
  export class InputRule {
27
29
  find: InputRuleFinder
28
30
 
29
31
  handler: (props: {
30
- state: EditorState
31
- range: Range
32
- match: ExtendedRegExpMatchArray
33
- commands: SingleCommands
34
- chain: () => ChainedCommands
35
- can: () => CanCommands
32
+ state: EditorState;
33
+ range: Range;
34
+ match: ExtendedRegExpMatchArray;
35
+ commands: SingleCommands;
36
+ chain: () => ChainedCommands;
37
+ can: () => CanCommands;
36
38
  }) => void | null
37
39
 
38
40
  constructor(config: {
39
- find: InputRuleFinder
41
+ find: InputRuleFinder;
40
42
  handler: (props: {
41
- state: EditorState
42
- range: Range
43
- match: ExtendedRegExpMatchArray
44
- commands: SingleCommands
45
- chain: () => ChainedCommands
46
- can: () => CanCommands
47
- }) => void | null
43
+ state: EditorState;
44
+ range: Range;
45
+ match: ExtendedRegExpMatchArray;
46
+ commands: SingleCommands;
47
+ chain: () => ChainedCommands;
48
+ can: () => CanCommands;
49
+ }) => void | null;
48
50
  }) {
49
51
  this.find = config.find
50
52
  this.handler = config.handler
@@ -85,12 +87,12 @@ const inputRuleMatcherHandler = (
85
87
  }
86
88
 
87
89
  function run(config: {
88
- editor: Editor
89
- from: number
90
- to: number
91
- text: string
92
- rules: InputRule[]
93
- plugin: Plugin
90
+ editor: Editor;
91
+ from: number;
92
+ to: number;
93
+ text: string;
94
+ rules: InputRule[];
95
+ plugin: Plugin;
94
96
  }): boolean {
95
97
  const {
96
98
  editor, from, to, text, rules, plugin,
@@ -184,7 +186,7 @@ export function inputRulesPlugin(props: { editor: Editor; rules: InputRule[] }):
184
186
  init() {
185
187
  return null
186
188
  },
187
- apply(tr, prev) {
189
+ apply(tr, prev, state) {
188
190
  const stored = tr.getMeta(plugin)
189
191
 
190
192
  if (stored) {
@@ -192,12 +194,25 @@ export function inputRulesPlugin(props: { editor: Editor; rules: InputRule[] }):
192
194
  }
193
195
 
194
196
  // if InputRule is triggered by insertContent()
195
- const simulatedInputMeta = tr.getMeta('applyInputRules')
197
+ const simulatedInputMeta = tr.getMeta('applyInputRules') as
198
+ | undefined
199
+ | {
200
+ from: number;
201
+ text: string | ProseMirrorNode | Fragment;
202
+ }
196
203
  const isSimulatedInput = !!simulatedInputMeta
197
204
 
198
205
  if (isSimulatedInput) {
199
206
  setTimeout(() => {
200
- const { from, text } = simulatedInputMeta
207
+ let { text } = simulatedInputMeta
208
+
209
+ if (typeof text === 'string') {
210
+ text = text as string
211
+ } else {
212
+ text = getHTMLFromFragment(Fragment.from(text), state.schema)
213
+ }
214
+
215
+ const { from } = simulatedInputMeta
201
216
  const to = from + text.length
202
217
 
203
218
  run({
package/src/Mark.ts CHANGED
@@ -64,7 +64,7 @@ declare module '@tiptap/core' {
64
64
  */
65
65
  addOptions?: (this: {
66
66
  name: string
67
- parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addOptions'], undefined>
67
+ parent: ParentConfig<MarkConfig<Options, Storage>>['addOptions']
68
68
  }) => Options
69
69
 
70
70
  /**
@@ -79,7 +79,7 @@ declare module '@tiptap/core' {
79
79
  addStorage?: (this: {
80
80
  name: string
81
81
  options: Options
82
- parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addStorage'], undefined>
82
+ parent: ParentConfig<MarkConfig<Options, Storage>>['addStorage']
83
83
  }) => Storage
84
84
 
85
85
  /**
package/src/Node.ts CHANGED
@@ -65,7 +65,7 @@ declare module '@tiptap/core' {
65
65
  */
66
66
  addOptions?: (this: {
67
67
  name: string
68
- parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addOptions'], undefined>
68
+ parent: ParentConfig<NodeConfig<Options, Storage>>['addOptions']
69
69
  }) => Options
70
70
 
71
71
  /**
@@ -80,7 +80,7 @@ declare module '@tiptap/core' {
80
80
  addStorage?: (this: {
81
81
  name: string
82
82
  options: Options
83
- parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addStorage'], undefined>
83
+ parent: ParentConfig<NodeConfig<Options, Storage>>['addStorage']
84
84
  }) => Storage
85
85
 
86
86
  /**
@@ -595,6 +595,25 @@ declare module '@tiptap/core' {
595
595
  editor?: Editor
596
596
  }) => NodeSpec['whitespace'])
597
597
 
598
+ /**
599
+ * Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak).
600
+ * When converting between block types that have whitespace set to "pre"
601
+ * and don't support the linebreak node (e.g. codeBlock) and other block types
602
+ * that do support the linebreak node (e.g. paragraphs) - this node will be used
603
+ * as the linebreak instead of stripping the newline.
604
+ *
605
+ * See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement).
606
+ */
607
+ linebreakReplacement?:
608
+ | NodeSpec['linebreakReplacement']
609
+ | ((this: {
610
+ name: string
611
+ options: Options
612
+ storage: Storage
613
+ parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement']
614
+ editor?: Editor
615
+ }) => NodeSpec['linebreakReplacement'])
616
+
598
617
  /**
599
618
  * When enabled, enables both
600
619
  * [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and
package/src/PasteRule.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import { Fragment, Node as ProseMirrorNode } from '@tiptap/pm/model'
1
2
  import { EditorState, Plugin } from '@tiptap/pm/state'
2
3
 
3
4
  import { CommandManager } from './CommandManager.js'
4
5
  import { Editor } from './Editor.js'
5
6
  import { createChainableState } from './helpers/createChainableState.js'
7
+ import { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'
6
8
  import {
7
9
  CanCommands,
8
10
  ChainedCommands,
@@ -14,45 +16,47 @@ import { isNumber } from './utilities/isNumber.js'
14
16
  import { isRegExp } from './utilities/isRegExp.js'
15
17
 
16
18
  export type PasteRuleMatch = {
17
- index: number
18
- text: string
19
- replaceWith?: string
20
- match?: RegExpMatchArray
21
- data?: Record<string, any>
22
- }
19
+ index: number;
20
+ text: string;
21
+ replaceWith?: string;
22
+ match?: RegExpMatchArray;
23
+ data?: Record<string, any>;
24
+ };
23
25
 
24
- export type PasteRuleFinder = RegExp | ((text: string, event?: ClipboardEvent | null) => PasteRuleMatch[] | null | undefined)
26
+ export type PasteRuleFinder =
27
+ | RegExp
28
+ | ((text: string, event?: ClipboardEvent | null) => PasteRuleMatch[] | null | undefined);
25
29
 
26
30
  /**
27
31
  * Paste rules are used to react to pasted content.
28
- * @see https://tiptap.dev/guide/custom-extensions/#paste-rules
32
+ * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
29
33
  */
30
34
  export class PasteRule {
31
35
  find: PasteRuleFinder
32
36
 
33
37
  handler: (props: {
34
- state: EditorState
35
- range: Range
36
- match: ExtendedRegExpMatchArray
37
- commands: SingleCommands
38
- chain: () => ChainedCommands
39
- can: () => CanCommands
40
- pasteEvent: ClipboardEvent | null
41
- dropEvent: DragEvent | null
38
+ state: EditorState;
39
+ range: Range;
40
+ match: ExtendedRegExpMatchArray;
41
+ commands: SingleCommands;
42
+ chain: () => ChainedCommands;
43
+ can: () => CanCommands;
44
+ pasteEvent: ClipboardEvent | null;
45
+ dropEvent: DragEvent | null;
42
46
  }) => void | null
43
47
 
44
48
  constructor(config: {
45
- find: PasteRuleFinder
49
+ find: PasteRuleFinder;
46
50
  handler: (props: {
47
- can: () => CanCommands
48
- chain: () => ChainedCommands
49
- commands: SingleCommands
50
- dropEvent: DragEvent | null
51
- match: ExtendedRegExpMatchArray
52
- pasteEvent: ClipboardEvent | null
53
- range: Range
54
- state: EditorState
55
- }) => void | null
51
+ can: () => CanCommands;
52
+ chain: () => ChainedCommands;
53
+ commands: SingleCommands;
54
+ dropEvent: DragEvent | null;
55
+ match: ExtendedRegExpMatchArray;
56
+ pasteEvent: ClipboardEvent | null;
57
+ range: Range;
58
+ state: EditorState;
59
+ }) => void | null;
56
60
  }) {
57
61
  this.find = config.find
58
62
  this.handler = config.handler
@@ -96,13 +100,13 @@ const pasteRuleMatcherHandler = (
96
100
  }
97
101
 
98
102
  function run(config: {
99
- editor: Editor
100
- state: EditorState
101
- from: number
102
- to: number
103
- rule: PasteRule
104
- pasteEvent: ClipboardEvent | null
105
- dropEvent: DragEvent | null
103
+ editor: Editor;
104
+ state: EditorState;
105
+ from: number;
106
+ to: number;
107
+ rule: PasteRule;
108
+ pasteEvent: ClipboardEvent | null;
109
+ dropEvent: DragEvent | null;
106
110
  }): boolean {
107
111
  const {
108
112
  editor, state, from, to, rule, pasteEvent, dropEvent,
@@ -179,7 +183,13 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
179
183
  let isPastedFromProseMirror = false
180
184
  let isDroppedFromProseMirror = false
181
185
  let pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null
182
- let dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null
186
+ let dropEvent: DragEvent | null
187
+
188
+ try {
189
+ dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null
190
+ } catch (e) {
191
+ dropEvent = null
192
+ }
183
193
 
184
194
  const processEvent = ({
185
195
  state,
@@ -188,11 +198,11 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
188
198
  rule,
189
199
  pasteEvt,
190
200
  }: {
191
- state: EditorState
192
- from: number
193
- to: { b: number }
194
- rule: PasteRule
195
- pasteEvt: ClipboardEvent | null
201
+ state: EditorState;
202
+ from: number;
203
+ to: { b: number };
204
+ rule: PasteRule;
205
+ pasteEvt: ClipboardEvent | null;
196
206
  }) => {
197
207
  const tr = state.tr
198
208
  const chainableState = createChainableState({
@@ -214,7 +224,11 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
214
224
  return
215
225
  }
216
226
 
217
- dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null
227
+ try {
228
+ dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null
229
+ } catch (e) {
230
+ dropEvent = null
231
+ }
218
232
  pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null
219
233
 
220
234
  return tr
@@ -266,7 +280,9 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
266
280
  const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror
267
281
 
268
282
  // if PasteRule is triggered by insertContent()
269
- const simulatedPasteMeta = transaction.getMeta('applyPasteRules')
283
+ const simulatedPasteMeta = transaction.getMeta('applyPasteRules') as
284
+ | undefined
285
+ | { from: number; text: string | ProseMirrorNode | Fragment }
270
286
  const isSimulatedPaste = !!simulatedPasteMeta
271
287
 
272
288
  if (!isPaste && !isDrop && !isSimulatedPaste) {
@@ -275,8 +291,17 @@ export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }):
275
291
 
276
292
  // Handle simulated paste
277
293
  if (isSimulatedPaste) {
278
- const { from, text } = simulatedPasteMeta
294
+ let { text } = simulatedPasteMeta
295
+
296
+ if (typeof text === 'string') {
297
+ text = text as string
298
+ } else {
299
+ text = getHTMLFromFragment(Fragment.from(text), state.schema)
300
+ }
301
+
302
+ const { from } = simulatedPasteMeta
279
303
  const to = from + text.length
304
+
280
305
  const pasteEvt = createClipboardPasteEvent(text)
281
306
 
282
307
  return processEvent({
@@ -1,4 +1,4 @@
1
- import { ParseOptions } from '@tiptap/pm/model'
1
+ import { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'
2
2
 
3
3
  import { Content, RawCommands } from '../types.js'
4
4
 
@@ -14,7 +14,7 @@ declare module '@tiptap/core' {
14
14
  /**
15
15
  * The ProseMirror content to insert.
16
16
  */
17
- value: Content,
17
+ value: Content | ProseMirrorNode | Fragment,
18
18
 
19
19
  /**
20
20
  * Optional options
@@ -23,17 +23,17 @@ declare module '@tiptap/core' {
23
23
  /**
24
24
  * Options for parsing the content.
25
25
  */
26
- parseOptions?: ParseOptions
26
+ parseOptions?: ParseOptions;
27
27
 
28
28
  /**
29
29
  * Whether to update the selection after inserting the content.
30
30
  */
31
- updateSelection?: boolean
32
- applyInputRules?: boolean
33
- applyPasteRules?: boolean
34
- },
35
- ) => ReturnType
36
- }
31
+ updateSelection?: boolean;
32
+ applyInputRules?: boolean;
33
+ applyPasteRules?: boolean;
34
+ }
35
+ ) => ReturnType;
36
+ };
37
37
  }
38
38
  }
39
39
 
@@ -20,7 +20,7 @@ declare module '@tiptap/core' {
20
20
  /**
21
21
  * The ProseMirror content to insert.
22
22
  */
23
- value: Content,
23
+ value: Content | ProseMirrorNode | Fragment,
24
24
 
25
25
  /**
26
26
  * Optional options
@@ -132,6 +132,16 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
132
132
  // otherwise if it is an array, we have to join it
133
133
  if (Array.isArray(value)) {
134
134
  newContent = value.map(v => v.text || '').join('')
135
+ } else if (value instanceof Fragment) {
136
+ let text = ''
137
+
138
+ value.forEach(node => {
139
+ if (node.text) {
140
+ text += node.text
141
+ }
142
+ })
143
+
144
+ newContent = text
135
145
  } else if (typeof value === 'object' && !!value && !!value.text) {
136
146
  newContent = value.text
137
147
  } else {
@@ -1,4 +1,4 @@
1
- import { ParseOptions } from '@tiptap/pm/model'
1
+ import { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'
2
2
 
3
3
  import { createDocument } from '../helpers/createDocument.js'
4
4
  import { Content, RawCommands } from '../types.js'
@@ -17,7 +17,7 @@ declare module '@tiptap/core' {
17
17
  /**
18
18
  * The new content.
19
19
  */
20
- content: Content,
20
+ content: Content | Fragment | ProseMirrorNode,
21
21
 
22
22
  /**
23
23
  * Whether to emit an update event.
@@ -37,10 +37,10 @@ declare module '@tiptap/core' {
37
37
  /**
38
38
  * Whether to throw an error if the content is invalid.
39
39
  */
40
- errorOnInvalidContent?: boolean
41
- },
42
- ) => ReturnType
43
- }
40
+ errorOnInvalidContent?: boolean;
41
+ }
42
+ ) => ReturnType;
43
+ };
44
44
  }
45
45
  }
46
46
 
@@ -66,12 +66,8 @@ export const setContent: RawCommands['setContent'] = (content, emitUpdate = fals
66
66
  tr.setMeta('preventUpdate', !emitUpdate)
67
67
  }
68
68
 
69
- return commands.insertContentAt(
70
- { from: 0, to: doc.content.size },
71
- content,
72
- {
73
- parseOptions,
74
- errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,
75
- },
76
- )
69
+ return commands.insertContentAt({ from: 0, to: doc.content.size }, content, {
70
+ parseOptions,
71
+ errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,
72
+ })
77
73
  }