@portabletext/editor 1.24.0 → 1.26.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 (89) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +283 -64
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/selector.get-text-before.cjs +8 -8
  4. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  5. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +412 -0
  6. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -0
  7. package/lib/_chunks-cjs/util.is-empty-text-block.cjs +2 -2
  8. package/lib/_chunks-cjs/util.is-empty-text-block.cjs.map +1 -1
  9. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs +46 -0
  10. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs.map +1 -0
  11. package/lib/_chunks-cjs/util.reverse-selection.cjs +0 -16
  12. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +1 -1
  13. package/lib/_chunks-es/behavior.core.js +259 -40
  14. package/lib/_chunks-es/behavior.core.js.map +1 -1
  15. package/lib/_chunks-es/selector.get-text-before.js +2 -2
  16. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +414 -0
  17. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -0
  18. package/lib/_chunks-es/util.is-empty-text-block.js +1 -1
  19. package/lib/_chunks-es/util.is-equal-selection-points.js +47 -0
  20. package/lib/_chunks-es/util.is-equal-selection-points.js.map +1 -0
  21. package/lib/_chunks-es/util.reverse-selection.js +0 -16
  22. package/lib/_chunks-es/util.reverse-selection.js.map +1 -1
  23. package/lib/behaviors/index.cjs +27 -27
  24. package/lib/behaviors/index.cjs.map +1 -1
  25. package/lib/behaviors/index.d.cts +2208 -171
  26. package/lib/behaviors/index.d.ts +2208 -171
  27. package/lib/behaviors/index.js +1 -1
  28. package/lib/index.cjs +306 -298
  29. package/lib/index.cjs.map +1 -1
  30. package/lib/index.d.cts +10499 -521
  31. package/lib/index.d.ts +10499 -521
  32. package/lib/index.js +302 -294
  33. package/lib/index.js.map +1 -1
  34. package/lib/selectors/index.cjs +26 -171
  35. package/lib/selectors/index.cjs.map +1 -1
  36. package/lib/selectors/index.d.cts +16 -0
  37. package/lib/selectors/index.d.ts +16 -0
  38. package/lib/selectors/index.js +5 -151
  39. package/lib/selectors/index.js.map +1 -1
  40. package/lib/utils/index.cjs +5 -3
  41. package/lib/utils/index.cjs.map +1 -1
  42. package/lib/utils/index.d.cts +19 -0
  43. package/lib/utils/index.d.ts +19 -0
  44. package/lib/utils/index.js +4 -2
  45. package/package.json +6 -6
  46. package/src/behavior-actions/behavior.action-utils.insert-block.ts +3 -3
  47. package/src/behavior-actions/behavior.action.block.set.ts +23 -0
  48. package/src/behavior-actions/behavior.action.block.unset.ts +21 -0
  49. package/src/behavior-actions/behavior.action.insert-break.ts +2 -69
  50. package/src/behavior-actions/behavior.action.insert.block.ts +29 -0
  51. package/src/behavior-actions/behavior.actions.ts +116 -96
  52. package/src/behaviors/behavior.core.annotations.ts +29 -0
  53. package/src/behaviors/behavior.core.block-objects.ts +13 -13
  54. package/src/behaviors/behavior.core.decorators.ts +19 -0
  55. package/src/behaviors/behavior.core.insert-break.ts +122 -0
  56. package/src/behaviors/behavior.core.lists.ts +57 -23
  57. package/src/behaviors/behavior.core.style.ts +19 -0
  58. package/src/behaviors/behavior.core.ts +18 -2
  59. package/src/behaviors/behavior.types.ts +103 -88
  60. package/src/converters/converter.json.ts +4 -4
  61. package/src/converters/converter.portable-text.deserialize.test.ts +1 -1
  62. package/src/converters/converter.portable-text.ts +4 -4
  63. package/src/converters/converter.text-html.deserialize.test.ts +1 -1
  64. package/src/converters/converter.text-html.serialize.test.ts +1 -1
  65. package/src/converters/converter.text-html.ts +4 -4
  66. package/src/converters/converter.text-plain.test.ts +1 -1
  67. package/src/converters/converter.text-plain.ts +3 -3
  68. package/src/converters/{converter.ts → converter.types.ts} +6 -0
  69. package/src/editor/create-editor.ts +50 -7
  70. package/src/editor/editor-machine.ts +46 -3
  71. package/src/editor/editor-snapshot.ts +1 -1
  72. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +2 -2
  73. package/src/editor/plugins/create-with-event-listeners.ts +41 -106
  74. package/src/selectors/index.ts +2 -0
  75. package/src/selectors/selector.is-at-the-end-of-block.ts +22 -0
  76. package/src/selectors/selector.is-at-the-start-of-block.ts +25 -0
  77. package/src/selectors/selector.is-selection-collapsed.ts +6 -2
  78. package/src/utils/index.ts +2 -0
  79. package/src/utils/util.get-block-end-point.ts +34 -0
  80. package/src/utils/util.is-equal-selection-points.ts +13 -0
  81. package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs +0 -231
  82. package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +0 -1
  83. package/lib/_chunks-cjs/util.is-keyed-segment.cjs +0 -6
  84. package/lib/_chunks-cjs/util.is-keyed-segment.cjs.map +0 -1
  85. package/lib/_chunks-es/selector.is-selection-collapsed.js +0 -232
  86. package/lib/_chunks-es/selector.is-selection-collapsed.js.map +0 -1
  87. package/lib/_chunks-es/util.is-keyed-segment.js +0 -7
  88. package/lib/_chunks-es/util.is-keyed-segment.js.map +0 -1
  89. /package/src/converters/{converters.ts → converters.core.ts} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {htmlToBlocks} from '@portabletext/block-tools'
2
2
  import {isPortableTextTextBlock, type PortableTextBlock} from '@sanity/types'
3
3
  import {sliceBlocks} from '../utils'
4
- import type {Converter} from './converter'
4
+ import {defineConverter} from './converter.types'
5
5
 
6
- export const converterTextPlain: Converter<'text/plain'> = {
6
+ export const converterTextPlain = defineConverter({
7
7
  mimeType: 'text/plain',
8
8
  serialize: ({context, event}) => {
9
9
  if (!context.selection) {
@@ -73,7 +73,7 @@ export const converterTextPlain: Converter<'text/plain'> = {
73
73
  mimeType: 'text/plain',
74
74
  }
75
75
  },
76
- }
76
+ })
77
77
 
78
78
  const entityMap: Record<string, string> = {
79
79
  '&': '&amp;',
@@ -9,6 +9,12 @@ export type Converter<TMIMEType extends MIMEType = MIMEType> = {
9
9
  deserialize: Deserializer<TMIMEType>
10
10
  }
11
11
 
12
+ export function defineConverter<TMIMEType extends MIMEType>(
13
+ converter: Converter<TMIMEType>,
14
+ ): Converter<TMIMEType> {
15
+ return converter
16
+ }
17
+
12
18
  export type ConverterEvent<TMIMEType extends MIMEType = MIMEType> =
13
19
  | {
14
20
  type: 'serialize'
@@ -12,7 +12,7 @@ import {
12
12
  type Snapshot,
13
13
  } from 'xstate'
14
14
  import type {Behavior, CustomBehaviorEvent} from '../behaviors/behavior.types'
15
- import {coreConverters} from '../converters/converters'
15
+ import {coreConverters} from '../converters/converters.core'
16
16
  import {compileType} from '../internal-utils/schema'
17
17
  import type {PickFromUnion} from '../type-utils'
18
18
  import type {EditableAPI} from '../types/editor'
@@ -65,14 +65,40 @@ export type EditorEvent =
65
65
  'type',
66
66
  | 'annotation.add'
67
67
  | 'annotation.remove'
68
+ | 'annotation.toggle'
69
+ | 'block.set'
70
+ | 'block.unset'
68
71
  | 'blur'
72
+ | 'data transfer.set'
73
+ | 'decorator.add'
74
+ | 'decorator.remove'
69
75
  | 'decorator.toggle'
76
+ | 'delete.block'
77
+ | 'delete.text'
78
+ | 'deserialization.failure'
79
+ | 'deserialization.success'
70
80
  | 'focus'
81
+ | 'insert.block'
71
82
  | 'insert.block object'
72
83
  | 'insert.inline object'
84
+ | 'insert.span'
85
+ | 'insert.text block'
86
+ | 'list item.add'
87
+ | 'list item.remove'
73
88
  | 'list item.toggle'
89
+ | 'move.block'
90
+ | 'move.block down'
91
+ | 'move.block up'
74
92
  | 'select'
93
+ | 'select.next block'
94
+ | 'select.previous block'
95
+ | 'serialization.failure'
96
+ | 'serialization.success'
97
+ | 'style.add'
98
+ | 'style.remove'
75
99
  | 'style.toggle'
100
+ | 'text block.set'
101
+ | 'text block.unset'
76
102
  | 'patches'
77
103
  | 'update behaviors'
78
104
  | 'update key generator'
@@ -160,12 +186,29 @@ function createEditorFromActor(editorActor: EditorActor): Editor {
160
186
  send: (event) => {
161
187
  editorActor.send(event)
162
188
  },
163
- on: (event, listener) =>
164
- editorActor.on(
165
- event,
166
- // @ts-expect-error
167
- listener,
168
- ),
189
+ on: (event, listener) => {
190
+ const subscription = editorActor.on(event, (event) => {
191
+ switch (event.type) {
192
+ case 'blurred':
193
+ case 'done loading':
194
+ case 'editable':
195
+ case 'error':
196
+ case 'focused':
197
+ case 'invalid value':
198
+ case 'loading':
199
+ case 'mutation':
200
+ case 'patch':
201
+ case 'read only':
202
+ case 'ready':
203
+ case 'selection':
204
+ case 'value changed':
205
+ listener(event)
206
+ break
207
+ }
208
+ })
209
+
210
+ return subscription
211
+ },
169
212
  _internal: {
170
213
  editable,
171
214
  editorActor,
@@ -20,7 +20,7 @@ import {
20
20
  type NativeBehaviorEvent,
21
21
  type SyntheticBehaviorEvent,
22
22
  } from '../behaviors/behavior.types'
23
- import type {Converter} from '../converters/converter'
23
+ import type {Converter} from '../converters/converter.types'
24
24
  import type {OmitFromUnion, PickFromUnion} from '../type-utils'
25
25
  import type {
26
26
  EditorSelection,
@@ -172,7 +172,6 @@ export type InternalEditorEmittedEvent =
172
172
  description: string
173
173
  data: unknown
174
174
  }
175
- | {type: 'select'; selection: EditorSelection}
176
175
  | {type: 'selection'; selection: EditorSelection}
177
176
  | {type: 'blurred'; event: FocusEvent<HTMLDivElement, Element>}
178
177
  | {type: 'focused'; event: FocusEvent<HTMLDivElement, Element>}
@@ -185,13 +184,42 @@ export type InternalEditorEmittedEvent =
185
184
  'type',
186
185
  | 'annotation.add'
187
186
  | 'annotation.remove'
187
+ | 'annotation.toggle'
188
+ | 'block.set'
189
+ | 'block.unset'
188
190
  | 'blur'
191
+ | 'data transfer.set'
192
+ | 'decorator.add'
193
+ | 'decorator.remove'
189
194
  | 'decorator.toggle'
195
+ | 'delete.backward'
196
+ | 'delete.block'
197
+ | 'delete.forward'
198
+ | 'delete.text'
199
+ | 'deserialization.failure'
200
+ | 'deserialization.success'
201
+ | 'focus'
202
+ | 'insert.block'
190
203
  | 'insert.block object'
191
204
  | 'insert.inline object'
205
+ | 'insert.span'
206
+ | 'insert.text block'
207
+ | 'list item.add'
208
+ | 'list item.remove'
192
209
  | 'list item.toggle'
193
- | 'focus'
210
+ | 'move.block'
211
+ | 'move.block down'
212
+ | 'move.block up'
213
+ | 'select'
214
+ | 'select.next block'
215
+ | 'select.previous block'
216
+ | 'serialization.failure'
217
+ | 'serialization.success'
218
+ | 'style.add'
219
+ | 'style.remove'
194
220
  | 'style.toggle'
221
+ | 'text block.set'
222
+ | 'text block.unset'
195
223
  >
196
224
  | {
197
225
  type: 'custom.*'
@@ -571,6 +599,9 @@ export const editorMachine = setup({
571
599
  'annotation.*': {
572
600
  actions: emit(({event}) => event),
573
601
  },
602
+ 'block.*': {
603
+ actions: emit(({event}) => event),
604
+ },
574
605
  'blur': {
575
606
  actions: emit(({event}) => event),
576
607
  },
@@ -580,6 +611,9 @@ export const editorMachine = setup({
580
611
  'decorator.*': {
581
612
  actions: emit(({event}) => event),
582
613
  },
614
+ 'delete.*': {
615
+ actions: emit(({event}) => event),
616
+ },
583
617
  'focus': {
584
618
  actions: emit(({event}) => event),
585
619
  },
@@ -589,12 +623,21 @@ export const editorMachine = setup({
589
623
  'list item.*': {
590
624
  actions: emit(({event}) => event),
591
625
  },
626
+ 'move.*': {
627
+ actions: emit(({event}) => event),
628
+ },
592
629
  'select': {
593
630
  actions: emit(({event}) => event),
594
631
  },
632
+ 'select.*': {
633
+ actions: emit(({event}) => event),
634
+ },
595
635
  'style.*': {
596
636
  actions: emit(({event}) => event),
597
637
  },
638
+ 'text block.*': {
639
+ actions: emit(({event}) => event),
640
+ },
598
641
  },
599
642
  },
600
643
  },
@@ -1,5 +1,5 @@
1
1
  import type {PortableTextBlock} from '@sanity/types'
2
- import type {Converter} from '../converters/converter'
2
+ import type {Converter} from '../converters/converter.types'
3
3
  import {toPortableTextRange} from '../internal-utils/ranges'
4
4
  import {fromSlateValue} from '../internal-utils/values'
5
5
  import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
@@ -461,11 +461,11 @@ describe('plugin:withPortableTextMarksModel', () => {
461
461
  style: 'normal',
462
462
  },
463
463
  {
464
- _key: '3',
464
+ _key: '5',
465
465
  _type: 'myTestBlockType',
466
466
  children: [
467
467
  {
468
- _key: '2',
468
+ _key: '3',
469
469
  _type: 'span',
470
470
  marks: [],
471
471
  text: '',
@@ -17,123 +17,58 @@ export function createWithEventListeners(
17
17
  subscriptions.push(() => {
18
18
  const subscription = editorActor.on('*', (event) => {
19
19
  switch (event.type) {
20
- case 'annotation.add': {
21
- editorActor.send({
22
- type: 'behavior event',
23
- behaviorEvent: {
24
- type: 'annotation.add',
25
- annotation: event.annotation,
26
- },
27
- editor,
28
- })
29
- break
30
- }
31
- case 'annotation.remove': {
32
- editorActor.send({
33
- type: 'behavior event',
34
- behaviorEvent: {
35
- type: 'annotation.remove',
36
- annotation: event.annotation,
37
- },
38
- editor,
39
- })
40
- break
41
- }
42
- case 'blur': {
43
- editorActor.send({
44
- type: 'behavior event',
45
- behaviorEvent: {
46
- type: 'blur',
47
- },
48
- editor,
49
- })
50
- break
51
- }
52
- case 'custom.*': {
20
+ case 'custom.*':
53
21
  editorActor.send({
54
22
  type: 'custom behavior event',
55
23
  behaviorEvent: event.event,
56
24
  editor,
57
25
  })
58
26
  break
59
- }
60
- case 'decorator.toggle': {
61
- editorActor.send({
62
- type: 'behavior event',
63
- behaviorEvent: {
64
- type: 'decorator.toggle',
65
- decorator: event.decorator,
66
- },
67
- editor,
68
- })
69
- break
70
- }
71
- case 'focus': {
72
- editorActor.send({
73
- type: 'behavior event',
74
- behaviorEvent: {
75
- type: 'focus',
76
- },
77
- editor,
78
- })
79
- break
80
- }
81
- case 'insert.block object': {
82
- editorActor.send({
83
- type: 'behavior event',
84
- behaviorEvent: {
85
- type: 'insert.block object',
86
- placement: event.placement,
87
- blockObject: event.blockObject,
88
- },
89
- editor,
90
- })
91
- break
92
- }
93
- case 'insert.inline object': {
94
- editorActor.send({
95
- type: 'behavior event',
96
- behaviorEvent: {
97
- type: 'insert.inline object',
98
- inlineObject: event.inlineObject,
99
- },
100
- editor,
101
- })
102
- break
103
- }
104
- case 'list item.toggle': {
105
- editorActor.send({
106
- type: 'behavior event',
107
- behaviorEvent: {
108
- type: 'list item.toggle',
109
- listItem: event.listItem,
110
- },
111
- editor,
112
- })
113
- break
114
- }
115
- case 'select': {
116
- editorActor.send({
117
- type: 'behavior event',
118
- behaviorEvent: {
119
- type: 'select',
120
- selection: event.selection,
121
- },
122
- editor,
123
- })
124
- break
125
- }
126
- case 'style.toggle': {
27
+
28
+ case 'annotation.add':
29
+ case 'annotation.remove':
30
+ case 'annotation.toggle':
31
+ case 'block.set':
32
+ case 'block.unset':
33
+ case 'blur':
34
+ case 'data transfer.set':
35
+ case 'decorator.add':
36
+ case 'decorator.remove':
37
+ case 'decorator.toggle':
38
+ case 'delete.backward':
39
+ case 'delete.block':
40
+ case 'delete.forward':
41
+ case 'delete.text':
42
+ case 'deserialization.failure':
43
+ case 'deserialization.success':
44
+ case 'focus':
45
+ case 'insert.block':
46
+ case 'insert.block object':
47
+ case 'insert.inline object':
48
+ case 'insert.span':
49
+ case 'insert.text block':
50
+ case 'list item.add':
51
+ case 'list item.remove':
52
+ case 'list item.toggle':
53
+ case 'move.block':
54
+ case 'move.block down':
55
+ case 'move.block up':
56
+ case 'select':
57
+ case 'select.next block':
58
+ case 'select.previous block':
59
+ case 'serialization.failure':
60
+ case 'serialization.success':
61
+ case 'style.add':
62
+ case 'style.remove':
63
+ case 'style.toggle':
64
+ case 'text block.set':
65
+ case 'text block.unset':
127
66
  editorActor.send({
128
67
  type: 'behavior event',
129
- behaviorEvent: {
130
- type: 'style.toggle',
131
- style: event.style,
132
- },
68
+ behaviorEvent: event,
133
69
  editor,
134
70
  })
135
71
  break
136
- }
137
72
  }
138
73
  })
139
74
 
@@ -17,6 +17,8 @@ export {isActiveAnnotation} from './selector.is-active-annotation'
17
17
  export {isActiveDecorator} from './selector.is-active-decorator'
18
18
  export {isActiveListItem} from './selector.is-active-list-item'
19
19
  export {isActiveStyle} from './selector.is-active-style'
20
+ export {isAtTheEndOfBlock} from './selector.is-at-the-end-of-block'
21
+ export {isAtTheStartOfBlock} from './selector.is-at-the-start-of-block'
20
22
  export {isPointAfterSelection} from './selector.is-point-after-selection'
21
23
  export {isPointBeforeSelection} from './selector.is-point-before-selection'
22
24
  export {isSelectionCollapsed} from './selector.is-selection-collapsed'
@@ -0,0 +1,22 @@
1
+ import type {KeyedSegment, PortableTextBlock} from '@sanity/types'
2
+ import type {EditorSelector} from '../editor/editor-selector'
3
+ import * as utils from '../utils'
4
+ import {isSelectionCollapsed} from './selector.is-selection-collapsed'
5
+
6
+ /**
7
+ * @public
8
+ */
9
+ export function isAtTheEndOfBlock(block: {
10
+ node: PortableTextBlock
11
+ path: [KeyedSegment]
12
+ }): EditorSelector<boolean> {
13
+ return ({context}) => {
14
+ if (!context.selection || !isSelectionCollapsed({context})) {
15
+ return false
16
+ }
17
+
18
+ const blockEndPoint = utils.getBlockEndPoint(block)
19
+
20
+ return utils.isEqualSelectionPoints(context.selection.focus, blockEndPoint)
21
+ }
22
+ }
@@ -0,0 +1,25 @@
1
+ import type {KeyedSegment, PortableTextBlock} from '@sanity/types'
2
+ import type {EditorSelector} from '../editor/editor-selector'
3
+ import * as utils from '../utils'
4
+ import {isSelectionCollapsed} from './selector.is-selection-collapsed'
5
+
6
+ /**
7
+ * @public
8
+ */
9
+ export function isAtTheStartOfBlock(block: {
10
+ node: PortableTextBlock
11
+ path: [KeyedSegment]
12
+ }): EditorSelector<boolean> {
13
+ return ({context}) => {
14
+ if (!context.selection || !isSelectionCollapsed({context})) {
15
+ return false
16
+ }
17
+
18
+ const blockStartPoint = utils.getBlockStartPoint(block)
19
+
20
+ return utils.isEqualSelectionPoints(
21
+ context.selection.focus,
22
+ blockStartPoint,
23
+ )
24
+ }
25
+ }
@@ -4,9 +4,13 @@ import type {EditorSelector} from '../editor/editor-selector'
4
4
  * @public
5
5
  */
6
6
  export const isSelectionCollapsed: EditorSelector<boolean> = ({context}) => {
7
+ if (!context.selection) {
8
+ return false
9
+ }
10
+
7
11
  return (
8
- JSON.stringify(context.selection?.anchor.path) ===
9
- JSON.stringify(context.selection?.focus.path) &&
12
+ JSON.stringify(context.selection.anchor.path) ===
13
+ JSON.stringify(context.selection.focus.path) &&
10
14
  context.selection?.anchor.offset === context.selection?.focus.offset
11
15
  )
12
16
  }
@@ -4,9 +4,11 @@ export {
4
4
  blockOffsetToSpanSelectionPoint,
5
5
  spanSelectionPointToBlockOffset,
6
6
  } from './util.block-offset'
7
+ export {getBlockEndPoint} from './util.get-block-end-point'
7
8
  export {getBlockStartPoint} from './util.get-block-start-point'
8
9
  export {getTextBlockText} from './util.get-text-block-text'
9
10
  export {isEmptyTextBlock} from './util.is-empty-text-block'
11
+ export {isEqualSelectionPoints} from './util.is-equal-selection-points'
10
12
  export {isKeyedSegment} from './util.is-keyed-segment'
11
13
  export {reverseSelection} from './util.reverse-selection'
12
14
  export {sliceBlocks} from './util.slice-blocks'
@@ -0,0 +1,34 @@
1
+ import {
2
+ isPortableTextSpan,
3
+ isPortableTextTextBlock,
4
+ type KeyedSegment,
5
+ type PortableTextBlock,
6
+ } from '@sanity/types'
7
+ import type {EditorSelectionPoint} from '../types/editor'
8
+
9
+ /**
10
+ * @public
11
+ */
12
+ export function getBlockEndPoint({
13
+ node,
14
+ path,
15
+ }: {
16
+ node: PortableTextBlock
17
+ path: [KeyedSegment]
18
+ }): EditorSelectionPoint {
19
+ if (isPortableTextTextBlock(node)) {
20
+ const lastChild = node.children[node.children.length - 1]
21
+
22
+ if (lastChild) {
23
+ return {
24
+ path: [...path, 'children', {_key: lastChild._key}],
25
+ offset: isPortableTextSpan(lastChild) ? lastChild.text.length : 0,
26
+ }
27
+ }
28
+ }
29
+
30
+ return {
31
+ path,
32
+ offset: 0,
33
+ }
34
+ }
@@ -0,0 +1,13 @@
1
+ import type {EditorSelectionPoint} from '../types/editor'
2
+
3
+ /**
4
+ * @public
5
+ */
6
+ export function isEqualSelectionPoints(
7
+ a: EditorSelectionPoint,
8
+ b: EditorSelectionPoint,
9
+ ) {
10
+ return (
11
+ a.offset === b.offset && JSON.stringify(a.path) === JSON.stringify(b.path)
12
+ )
13
+ }