@portabletext/editor 1.31.2 → 1.33.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 (74) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +4 -4
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +19 -11
  4. package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
  5. package/lib/_chunks-cjs/plugin.event-listener.cjs +1839 -1724
  6. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs +97 -0
  8. package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs.map +1 -0
  9. package/lib/_chunks-cjs/{parse-blocks.cjs → util.block-offsets-to-selection.cjs} +21 -2
  10. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -0
  11. package/lib/_chunks-cjs/util.reverse-selection.cjs +11 -0
  12. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +1 -1
  13. package/lib/_chunks-es/behavior.core.js +1 -1
  14. package/lib/_chunks-es/behavior.core.js.map +1 -1
  15. package/lib/_chunks-es/behavior.markdown.js +18 -11
  16. package/lib/_chunks-es/behavior.markdown.js.map +1 -1
  17. package/lib/_chunks-es/plugin.event-listener.js +1842 -1726
  18. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  19. package/lib/_chunks-es/selector.get-trimmed-selection.js +100 -0
  20. package/lib/_chunks-es/selector.get-trimmed-selection.js.map +1 -0
  21. package/lib/_chunks-es/{parse-blocks.js → util.block-offsets-to-selection.js} +21 -1
  22. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -0
  23. package/lib/_chunks-es/util.reverse-selection.js +11 -0
  24. package/lib/_chunks-es/util.reverse-selection.js.map +1 -1
  25. package/lib/behaviors/index.d.cts +27 -0
  26. package/lib/behaviors/index.d.ts +27 -0
  27. package/lib/index.d.cts +1128 -66
  28. package/lib/index.d.ts +1128 -66
  29. package/lib/plugins/index.cjs +295 -3
  30. package/lib/plugins/index.cjs.map +1 -1
  31. package/lib/plugins/index.d.cts +1142 -67
  32. package/lib/plugins/index.d.ts +1142 -67
  33. package/lib/plugins/index.js +300 -4
  34. package/lib/plugins/index.js.map +1 -1
  35. package/lib/selectors/index.cjs +51 -1
  36. package/lib/selectors/index.cjs.map +1 -1
  37. package/lib/selectors/index.d.cts +67 -0
  38. package/lib/selectors/index.d.ts +67 -0
  39. package/lib/selectors/index.js +53 -2
  40. package/lib/selectors/index.js.map +1 -1
  41. package/lib/utils/index.cjs +5 -4
  42. package/lib/utils/index.cjs.map +1 -1
  43. package/lib/utils/index.d.cts +16 -0
  44. package/lib/utils/index.d.ts +16 -0
  45. package/lib/utils/index.js +4 -3
  46. package/package.json +2 -2
  47. package/src/behavior-actions/behavior.action.decorator.add.ts +161 -0
  48. package/src/behavior-actions/behavior.action.delete.text.ts +54 -0
  49. package/src/behavior-actions/behavior.actions.ts +25 -43
  50. package/src/behaviors/behavior.markdown-emphasis.ts +395 -0
  51. package/src/behaviors/behavior.markdown.ts +11 -4
  52. package/src/behaviors/behavior.types.ts +7 -0
  53. package/src/editor/editor-machine.ts +80 -85
  54. package/src/editor/plugins/create-with-event-listeners.ts +51 -0
  55. package/src/editor/plugins/createWithEditableAPI.ts +18 -2
  56. package/src/editor/plugins/createWithPortableTextMarkModel.ts +2 -97
  57. package/src/editor/plugins/createWithUndoRedo.ts +132 -107
  58. package/src/editor/with-applying-behavior-actions.ts +27 -3
  59. package/src/plugins/plugin.markdown.tsx +11 -1
  60. package/src/selectors/index.ts +5 -0
  61. package/src/selectors/selector.get-anchor-block.ts +22 -0
  62. package/src/selectors/selector.get-anchor-child.ts +36 -0
  63. package/src/selectors/selector.get-anchor-span.ts +18 -0
  64. package/src/selectors/selector.get-anchor-text-block.ts +20 -0
  65. package/src/selectors/selector.get-trimmed-selection.test.ts +658 -0
  66. package/src/selectors/selector.get-trimmed-selection.ts +175 -0
  67. package/src/utils/index.ts +1 -0
  68. package/src/utils/util.block-offsets-to-selection.ts +36 -0
  69. package/lib/_chunks-cjs/parse-blocks.cjs.map +0 -1
  70. package/lib/_chunks-cjs/util.is-empty-text-block.cjs +0 -14
  71. package/lib/_chunks-cjs/util.is-empty-text-block.cjs.map +0 -1
  72. package/lib/_chunks-es/parse-blocks.js.map +0 -1
  73. package/lib/_chunks-es/util.is-empty-text-block.js +0 -15
  74. package/lib/_chunks-es/util.is-empty-text-block.js.map +0 -1
@@ -0,0 +1,54 @@
1
+ import {Transforms} from 'slate'
2
+ import {toSlateRange} from '../internal-utils/ranges'
3
+ import {fromSlateValue} from '../internal-utils/values'
4
+ import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
5
+ import * as selectors from '../selectors'
6
+ import * as utils from '../utils'
7
+ import type {BehaviorActionImplementation} from './behavior.actions'
8
+
9
+ export const deleteTextActionImplementation: BehaviorActionImplementation<
10
+ 'delete.text'
11
+ > = ({context, action}) => {
12
+ const value = fromSlateValue(
13
+ action.editor.children,
14
+ context.schema.block.name,
15
+ KEY_TO_VALUE_ELEMENT.get(action.editor),
16
+ )
17
+
18
+ const selection = utils.blockOffsetsToSelection({
19
+ value,
20
+ offsets: {
21
+ anchor: action.anchor,
22
+ focus: action.focus,
23
+ },
24
+ })
25
+
26
+ if (!selection) {
27
+ throw new Error('Unable to find selection from block offsets')
28
+ }
29
+
30
+ const trimmedSelection = selectors.getTrimmedSelection({
31
+ context: {
32
+ converters: [],
33
+ schema: context.schema,
34
+ keyGenerator: context.keyGenerator,
35
+ activeDecorators: [],
36
+ value,
37
+ selection,
38
+ },
39
+ })
40
+
41
+ if (!trimmedSelection) {
42
+ throw new Error('Unable to find trimmed selection')
43
+ }
44
+
45
+ const range = toSlateRange(trimmedSelection, action.editor)
46
+
47
+ if (!range) {
48
+ throw new Error('Unable to find Slate range from trimmed selection')
49
+ }
50
+
51
+ Transforms.delete(action.editor, {
52
+ at: range,
53
+ })
54
+ }
@@ -17,21 +17,24 @@ import {
17
17
  toggleAnnotationActionImplementation,
18
18
  } from '../editor/plugins/createWithEditableAPI'
19
19
  import {
20
- addDecoratorActionImplementation,
21
20
  removeDecoratorActionImplementation,
22
21
  toggleDecoratorActionImplementation,
23
22
  } from '../editor/plugins/createWithPortableTextMarkModel'
23
+ import {
24
+ historyRedoActionImplementation,
25
+ historyUndoActionImplementation,
26
+ } from '../editor/plugins/createWithUndoRedo'
24
27
  import {toSlatePath} from '../internal-utils/paths'
25
28
  import {toSlateRange} from '../internal-utils/ranges'
26
- import {fromSlateValue, toSlateValue} from '../internal-utils/values'
27
- import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
29
+ import {toSlateValue} from '../internal-utils/values'
28
30
  import type {PickFromUnion} from '../type-utils'
29
- import {blockOffsetToSpanSelectionPoint} from '../utils/util.block-offset'
30
31
  import {insertBlock} from './behavior.action-utils.insert-block'
31
32
  import {blockSetBehaviorActionImplementation} from './behavior.action.block.set'
32
33
  import {blockUnsetBehaviorActionImplementation} from './behavior.action.block.unset'
33
34
  import {dataTransferSetActionImplementation} from './behavior.action.data-transfer-set'
35
+ import {decoratorAddActionImplementation} from './behavior.action.decorator.add'
34
36
  import {deleteActionImplementation} from './behavior.action.delete'
37
+ import {deleteTextActionImplementation} from './behavior.action.delete.text'
35
38
  import {insertBlockObjectActionImplementation} from './behavior.action.insert-block-object'
36
39
  import {insertBlocksActionImplementation} from './behavior.action.insert-blocks'
37
40
  import {
@@ -84,7 +87,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
84
87
  ReactEditor.blur(action.editor)
85
88
  },
86
89
  'data transfer.set': dataTransferSetActionImplementation,
87
- 'decorator.add': addDecoratorActionImplementation,
90
+ 'decorator.add': decoratorAddActionImplementation,
88
91
  'decorator.remove': removeDecoratorActionImplementation,
89
92
  'decorator.toggle': toggleDecoratorActionImplementation,
90
93
  'focus': ({action}) => {
@@ -115,44 +118,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
115
118
  at: range,
116
119
  })
117
120
  },
118
- 'delete.text': ({context, action}) => {
119
- const value = fromSlateValue(
120
- action.editor.children,
121
- context.schema.block.name,
122
- KEY_TO_VALUE_ELEMENT.get(action.editor),
123
- )
124
-
125
- const anchor = blockOffsetToSpanSelectionPoint({
126
- value,
127
- blockOffset: action.anchor,
128
- })
129
- const focus = blockOffsetToSpanSelectionPoint({
130
- value,
131
- blockOffset: action.focus,
132
- })
133
-
134
- if (!anchor || !focus) {
135
- console.error('Unable to find anchor or focus selection point')
136
- return
137
- }
138
-
139
- const range = toSlateRange(
140
- {
141
- anchor,
142
- focus,
143
- },
144
- action.editor,
145
- )
146
-
147
- if (!range) {
148
- console.error('Unable to find Slate range from selection points')
149
- return
150
- }
151
-
152
- Transforms.delete(action.editor, {
153
- at: range,
154
- })
155
- },
121
+ 'delete.text': deleteTextActionImplementation,
156
122
  'deserialization.failure': ({action}) => {
157
123
  console.error(
158
124
  `Deserialization of ${action.mimeType} failed with reason ${action.reason}`,
@@ -168,6 +134,8 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
168
134
  },
169
135
  })
170
136
  },
137
+ 'history.redo': historyRedoActionImplementation,
138
+ 'history.undo': historyUndoActionImplementation,
171
139
  'insert.block': insertBlockActionImplementation,
172
140
  'insert.blocks': insertBlocksActionImplementation,
173
141
  'insert.block object': insertBlockObjectActionImplementation,
@@ -470,6 +438,20 @@ function performDefaultAction({
470
438
  })
471
439
  break
472
440
  }
441
+ case 'history.redo': {
442
+ behaviorActionImplementations['history.redo']({
443
+ context,
444
+ action,
445
+ })
446
+ break
447
+ }
448
+ case 'history.undo': {
449
+ behaviorActionImplementations['history.undo']({
450
+ context,
451
+ action,
452
+ })
453
+ break
454
+ }
473
455
  case 'insert.block': {
474
456
  behaviorActionImplementations['insert.block']({
475
457
  context,
@@ -0,0 +1,395 @@
1
+ import {useActorRef} from '@xstate/react'
2
+ import {isEqual} from 'lodash'
3
+ import {
4
+ assign,
5
+ fromCallback,
6
+ setup,
7
+ type AnyEventObject,
8
+ type CallbackLogicFunction,
9
+ } from 'xstate'
10
+ import type {Editor} from '../editor/create-editor'
11
+ import {useEditor} from '../editor/editor-provider'
12
+ import type {EditorSchema} from '../selectors'
13
+ import * as selectors from '../selectors'
14
+ import * as utils from '../utils'
15
+ import {defineBehavior} from './behavior.types'
16
+
17
+ /**
18
+ * @beta
19
+ */
20
+ export type MarkdownEmphasisBehaviorsConfig = {
21
+ boldDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
22
+ italicDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
23
+ }
24
+
25
+ /**
26
+ * @beta
27
+ */
28
+ export function useMarkdownEmphasisBehaviors(props: {
29
+ config: MarkdownEmphasisBehaviorsConfig
30
+ }) {
31
+ const editor = useEditor()
32
+
33
+ useActorRef(emphasisMachine, {
34
+ input: {
35
+ editor,
36
+ boldDecorator: props.config.boldDecorator?.({
37
+ schema: editor.getSnapshot().context.schema,
38
+ }),
39
+ italicDecorator: props.config.italicDecorator?.({
40
+ schema: editor.getSnapshot().context.schema,
41
+ }),
42
+ },
43
+ })
44
+ }
45
+
46
+ const italicRegex =
47
+ /(?<!\*)\*(?!\s)([^*\n]+?)(?<!\s)\*(?!\*)|(?<!_)_(?!\s)([^_\n]+?)(?<!\s)_(?!_)$/
48
+ const boldRegex =
49
+ /(?<!\*)\*\*(?!\s)([^*\n]+?)(?<!\s)\*\*(?!\*)|(?<!_)__(?!\s)([^_\n]+?)(?<!\s)__(?!_)$/
50
+
51
+ type MarkdownEmphasisEvent =
52
+ | {
53
+ type: 'emphasis.add'
54
+ blockOffset: utils.BlockOffset
55
+ }
56
+ | {
57
+ type: 'selection'
58
+ blockOffsets?: {
59
+ anchor: utils.BlockOffset
60
+ focus: utils.BlockOffset
61
+ }
62
+ }
63
+ | {
64
+ type: 'delete.backward'
65
+ }
66
+
67
+ const emphasisListener: CallbackLogicFunction<
68
+ AnyEventObject,
69
+ MarkdownEmphasisEvent,
70
+ {editor: Editor; boldDecorator?: string; italicDecorator?: string}
71
+ > = ({sendBack, input}) => {
72
+ const unregister = input.editor.registerBehavior({
73
+ behavior: defineBehavior({
74
+ on: 'insert.text',
75
+ guard: ({context, event}) => {
76
+ const boldDecorator = input.boldDecorator
77
+ const italicDecorator = input.italicDecorator
78
+
79
+ if (boldDecorator === undefined && italicDecorator === undefined) {
80
+ return false
81
+ }
82
+
83
+ if (event.text !== '*' && event.text !== '_') {
84
+ return false
85
+ }
86
+
87
+ const focusTextBlock = selectors.getFocusTextBlock({context})
88
+ const selectionStartPoint = selectors.getSelectionStartPoint({context})
89
+ const selectionStartOffset = selectionStartPoint
90
+ ? utils.spanSelectionPointToBlockOffset({
91
+ value: context.value,
92
+ selectionPoint: selectionStartPoint,
93
+ })
94
+ : undefined
95
+
96
+ if (!focusTextBlock || !selectionStartOffset) {
97
+ return false
98
+ }
99
+
100
+ const textBefore = selectors.getBlockTextBefore({context})
101
+
102
+ const textToItalic = `${textBefore}${event.text}`
103
+ .match(italicRegex)
104
+ ?.at(0)
105
+
106
+ if (textToItalic !== undefined && italicDecorator !== undefined) {
107
+ const prefixOffsets = {
108
+ anchor: {
109
+ path: focusTextBlock.path,
110
+ offset: textBefore.length - textToItalic.length + 1,
111
+ },
112
+ focus: {
113
+ path: focusTextBlock.path,
114
+ offset: textBefore.length - textToItalic.length + 1 + 1,
115
+ },
116
+ }
117
+ const suffixOffsets = {
118
+ anchor: {
119
+ path: focusTextBlock.path,
120
+ offset: selectionStartOffset.offset,
121
+ },
122
+ focus: {
123
+ path: focusTextBlock.path,
124
+ offset: selectionStartOffset.offset + 1,
125
+ },
126
+ }
127
+
128
+ const anchor = utils.blockOffsetToSpanSelectionPoint({
129
+ value: context.value,
130
+ blockOffset: prefixOffsets.focus,
131
+ })
132
+ const focus = utils.blockOffsetToSpanSelectionPoint({
133
+ value: context.value,
134
+ blockOffset: suffixOffsets.anchor,
135
+ })
136
+
137
+ if (!anchor || !focus) {
138
+ return false
139
+ }
140
+
141
+ return {
142
+ prefixOffsets,
143
+ suffixOffsets,
144
+ decorator: italicDecorator,
145
+ selection: {anchor, focus},
146
+ }
147
+ }
148
+
149
+ const textToBold = `${textBefore}${event.text}`.match(boldRegex)?.at(0)
150
+
151
+ if (textToBold !== undefined && boldDecorator !== undefined) {
152
+ const prefixOffsets = {
153
+ anchor: {
154
+ path: focusTextBlock.path,
155
+ offset: textBefore.length - textToBold.length + 1,
156
+ },
157
+ focus: {
158
+ path: focusTextBlock.path,
159
+ offset: textBefore.length - textToBold.length + 1 + 2,
160
+ },
161
+ }
162
+ const suffixOffsets = {
163
+ anchor: {
164
+ path: focusTextBlock.path,
165
+ offset: selectionStartOffset.offset - 1,
166
+ },
167
+ focus: {
168
+ path: focusTextBlock.path,
169
+ offset: selectionStartOffset.offset + 1,
170
+ },
171
+ }
172
+ const anchor = utils.blockOffsetToSpanSelectionPoint({
173
+ value: context.value,
174
+ blockOffset: prefixOffsets.focus,
175
+ })
176
+ const focus = utils.blockOffsetToSpanSelectionPoint({
177
+ value: context.value,
178
+ blockOffset: suffixOffsets.anchor,
179
+ })
180
+
181
+ if (!anchor || !focus) {
182
+ return false
183
+ }
184
+
185
+ return {
186
+ prefixOffsets,
187
+ suffixOffsets,
188
+ decorator: boldDecorator,
189
+ selection: {anchor, focus},
190
+ }
191
+ }
192
+
193
+ return false
194
+ },
195
+ actions: [
196
+ ({event}) => [event],
197
+ (_, {prefixOffsets, suffixOffsets, decorator, selection}) => [
198
+ {
199
+ type: 'decorator.add',
200
+ decorator,
201
+ selection,
202
+ },
203
+ {
204
+ type: 'delete.text',
205
+ ...suffixOffsets,
206
+ },
207
+ {
208
+ type: 'delete.text',
209
+ ...prefixOffsets,
210
+ },
211
+ {
212
+ type: 'effect',
213
+ effect: () => {
214
+ sendBack({
215
+ type: 'emphasis.add',
216
+ blockOffset: {
217
+ ...suffixOffsets.anchor,
218
+ offset:
219
+ suffixOffsets.anchor.offset -
220
+ (prefixOffsets.focus.offset - prefixOffsets.anchor.offset),
221
+ },
222
+ })
223
+ },
224
+ },
225
+ ],
226
+ ],
227
+ }),
228
+ })
229
+
230
+ return unregister
231
+ }
232
+
233
+ const selectionListenerCallback: CallbackLogicFunction<
234
+ AnyEventObject,
235
+ MarkdownEmphasisEvent,
236
+ {editor: Editor}
237
+ > = ({sendBack, input}) => {
238
+ const unregister = input.editor.registerBehavior({
239
+ behavior: defineBehavior({
240
+ on: 'select',
241
+ guard: ({context, event}) => {
242
+ if (!event.selection) {
243
+ return {blockOffsets: undefined}
244
+ }
245
+
246
+ const anchor = utils.spanSelectionPointToBlockOffset({
247
+ value: context.value,
248
+ selectionPoint: event.selection.anchor,
249
+ })
250
+ const focus = utils.spanSelectionPointToBlockOffset({
251
+ value: context.value,
252
+ selectionPoint: event.selection.focus,
253
+ })
254
+
255
+ if (!anchor || !focus) {
256
+ return {blockOffsets: undefined}
257
+ }
258
+
259
+ return {
260
+ blockOffsets: {
261
+ anchor,
262
+ focus,
263
+ },
264
+ }
265
+ },
266
+ actions: [
267
+ (_, {blockOffsets}) => [
268
+ {
269
+ type: 'effect',
270
+ effect: () => {
271
+ sendBack({type: 'selection', blockOffsets})
272
+ },
273
+ },
274
+ ],
275
+ ],
276
+ }),
277
+ })
278
+
279
+ return unregister
280
+ }
281
+
282
+ const deleteBackwardListenerCallback: CallbackLogicFunction<
283
+ AnyEventObject,
284
+ MarkdownEmphasisEvent,
285
+ {editor: Editor}
286
+ > = ({sendBack, input}) => {
287
+ const unregister = input.editor.registerBehavior({
288
+ behavior: defineBehavior({
289
+ on: 'delete.backward',
290
+ actions: [
291
+ () => [
292
+ {
293
+ type: 'history.undo',
294
+ },
295
+ {
296
+ type: 'effect',
297
+ effect: () => {
298
+ sendBack({type: 'delete.backward'})
299
+ },
300
+ },
301
+ ],
302
+ ],
303
+ }),
304
+ })
305
+
306
+ return unregister
307
+ }
308
+
309
+ const emphasisMachine = setup({
310
+ types: {
311
+ context: {} as {
312
+ boldDecorator?: string
313
+ italicDecorator?: string
314
+ offsetAfterEmphasis?: utils.BlockOffset
315
+ editor: Editor
316
+ },
317
+ input: {} as {
318
+ boldDecorator?: string
319
+ italicDecorator?: string
320
+ editor: Editor
321
+ },
322
+ events: {} as MarkdownEmphasisEvent,
323
+ },
324
+ actors: {
325
+ 'emphasis listener': fromCallback(emphasisListener),
326
+ 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),
327
+ 'selection listener': fromCallback(selectionListenerCallback),
328
+ },
329
+ }).createMachine({
330
+ id: 'emphasis',
331
+ context: ({input}) => ({
332
+ boldDecorator: input.boldDecorator,
333
+ italicDecorator: input.italicDecorator,
334
+ editor: input.editor,
335
+ }),
336
+ initial: 'idle',
337
+ states: {
338
+ 'idle': {
339
+ invoke: [
340
+ {
341
+ src: 'emphasis listener',
342
+ input: ({context}) => ({
343
+ editor: context.editor,
344
+ boldDecorator: context.boldDecorator,
345
+ italicDecorator: context.italicDecorator,
346
+ }),
347
+ },
348
+ ],
349
+ on: {
350
+ 'emphasis.add': {
351
+ target: 'emphasis added',
352
+ actions: assign({
353
+ offsetAfterEmphasis: ({event}) => event.blockOffset,
354
+ }),
355
+ },
356
+ },
357
+ },
358
+ 'emphasis added': {
359
+ exit: [
360
+ assign({
361
+ offsetAfterEmphasis: undefined,
362
+ }),
363
+ ],
364
+ invoke: [
365
+ {
366
+ src: 'selection listener',
367
+ input: ({context}) => ({editor: context.editor}),
368
+ },
369
+ {
370
+ src: 'delete.backward listener',
371
+ input: ({context}) => ({editor: context.editor}),
372
+ },
373
+ ],
374
+ on: {
375
+ 'selection': {
376
+ target: 'idle',
377
+ guard: ({context, event}) => {
378
+ const selectionChanged = !isEqual(
379
+ {
380
+ anchor: context.offsetAfterEmphasis,
381
+ focus: context.offsetAfterEmphasis,
382
+ },
383
+ event.blockOffsets,
384
+ )
385
+
386
+ return selectionChanged
387
+ },
388
+ },
389
+ 'delete.backward': {
390
+ target: 'idle',
391
+ },
392
+ },
393
+ },
394
+ },
395
+ })
@@ -86,6 +86,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
86
86
  return false
87
87
  }
88
88
 
89
+ const previousInlineObject = selectors.getPreviousInlineObject({context})
89
90
  const blockOffset = spanSelectionPointToBlockOffset({
90
91
  value: context.value,
91
92
  selectionPoint: {
@@ -98,7 +99,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
98
99
  },
99
100
  })
100
101
 
101
- if (!blockOffset) {
102
+ if (previousInlineObject || !blockOffset) {
102
103
  return false
103
104
  }
104
105
 
@@ -173,6 +174,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
173
174
  return false
174
175
  }
175
176
 
177
+ const previousInlineObject = selectors.getPreviousInlineObject({context})
176
178
  const textBefore = getBlockTextBefore({context})
177
179
  const hrBlockOffsets = {
178
180
  anchor: {
@@ -185,7 +187,10 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
185
187
  },
186
188
  }
187
189
 
188
- if (textBefore === `${hrCharacter}${hrCharacter}`) {
190
+ if (
191
+ !previousInlineObject &&
192
+ textBefore === `${hrCharacter}${hrCharacter}`
193
+ ) {
189
194
  return {hrObject, focusBlock, hrCharacter, hrBlockOffsets}
190
195
  }
191
196
 
@@ -290,6 +295,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
290
295
  return false
291
296
  }
292
297
 
298
+ const previousInlineObject = selectors.getPreviousInlineObject({context})
293
299
  const blockText = getTextBlockText(focusTextBlock.node)
294
300
  const markdownHeadingSearch = /^#+/.exec(blockText)
295
301
  const level = markdownHeadingSearch
@@ -297,7 +303,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
297
303
  : undefined
298
304
  const caretAtTheEndOfHeading = blockOffset.offset === level
299
305
 
300
- if (!caretAtTheEndOfHeading) {
306
+ if (previousInlineObject || !caretAtTheEndOfHeading) {
301
307
  return false
302
308
  }
303
309
 
@@ -397,6 +403,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
397
403
  return false
398
404
  }
399
405
 
406
+ const previousInlineObject = selectors.getPreviousInlineObject({context})
400
407
  const blockOffset = spanSelectionPointToBlockOffset({
401
408
  value: context.value,
402
409
  selectionPoint: {
@@ -409,7 +416,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
409
416
  },
410
417
  })
411
418
 
412
- if (!blockOffset) {
419
+ if (previousInlineObject || !blockOffset) {
413
420
  return false
414
421
  }
415
422
 
@@ -57,6 +57,7 @@ export type SyntheticBehaviorEvent =
57
57
  | {
58
58
  type: 'decorator.add'
59
59
  decorator: string
60
+ selection?: NonNullable<EditorSelection>
60
61
  }
61
62
  | {
62
63
  type: 'decorator.remove'
@@ -90,6 +91,12 @@ export type SyntheticBehaviorEvent =
90
91
  | {
91
92
  type: 'focus'
92
93
  }
94
+ | {
95
+ type: 'history.redo'
96
+ }
97
+ | {
98
+ type: 'history.undo'
99
+ }
93
100
  | {
94
101
  type: 'insert.blocks'
95
102
  blocks: Array<PortableTextBlock>