lkb-fields-document 1.0.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 (112) hide show
  1. package/component-blocks/dist/lkb-fields-document-component-blocks.cjs.d.ts +2 -0
  2. package/component-blocks/dist/lkb-fields-document-component-blocks.cjs.js +306 -0
  3. package/component-blocks/dist/lkb-fields-document-component-blocks.esm.js +300 -0
  4. package/component-blocks/dist/lkb-fields-document-component-blocks.node.cjs.js +306 -0
  5. package/component-blocks/dist/lkb-fields-document-component-blocks.node.esm.js +300 -0
  6. package/component-blocks/package.json +4 -0
  7. package/dist/Cell-0ac0ac66.node.cjs.js +21 -0
  8. package/dist/Cell-242f7404.esm.js +17 -0
  9. package/dist/Cell-3103f73d.node.esm.js +17 -0
  10. package/dist/Cell-bfb56d74.cjs.js +21 -0
  11. package/dist/Field-0e0f75ed.node.cjs.js +1628 -0
  12. package/dist/Field-28177061.cjs.js +1628 -0
  13. package/dist/Field-35b79e6b.node.esm.js +1619 -0
  14. package/dist/Field-92d13205.esm.js +1619 -0
  15. package/dist/api-2f524611.esm.js +502 -0
  16. package/dist/api-73636987.cjs.js +506 -0
  17. package/dist/api-8e2b20b8.node.cjs.js +506 -0
  18. package/dist/api-c32e360e.node.esm.js +502 -0
  19. package/dist/callout-ui-2aded278.cjs.js +131 -0
  20. package/dist/callout-ui-3e5ca544.node.esm.js +126 -0
  21. package/dist/callout-ui-8b5f2376.esm.js +126 -0
  22. package/dist/callout-ui-ad50f301.node.cjs.js +131 -0
  23. package/dist/declarations/src/component-blocks.d.ts +4 -0
  24. package/dist/declarations/src/component-blocks.d.ts.map +1 -0
  25. package/dist/declarations/src/document-editor/component-blocks/api.d.ts +120 -0
  26. package/dist/declarations/src/document-editor/component-blocks/api.d.ts.map +1 -0
  27. package/dist/declarations/src/document-editor/component-blocks/types.d.ts +241 -0
  28. package/dist/declarations/src/document-editor/component-blocks/types.d.ts.map +1 -0
  29. package/dist/declarations/src/document-editor/toolset/relationship/relationship-shared.d.ts +10 -0
  30. package/dist/declarations/src/document-editor/toolset/relationship/relationship-shared.d.ts.map +1 -0
  31. package/dist/declarations/src/index.d.ts +7 -0
  32. package/dist/declarations/src/index.d.ts.map +1 -0
  33. package/dist/declarations/src/my-component-blocks/index.d.ts +46 -0
  34. package/dist/declarations/src/my-component-blocks/index.d.ts.map +1 -0
  35. package/dist/declarations/src/structure/Cell.d.ts +5 -0
  36. package/dist/declarations/src/structure/Cell.d.ts.map +1 -0
  37. package/dist/declarations/src/structure/Field.d.ts +5 -0
  38. package/dist/declarations/src/structure/Field.d.ts.map +1 -0
  39. package/dist/declarations/src/structure/controller.d.ts +10 -0
  40. package/dist/declarations/src/structure/controller.d.ts.map +1 -0
  41. package/dist/declarations/src/structure/structure.d.ts +4 -0
  42. package/dist/declarations/src/structure/structure.d.ts.map +1 -0
  43. package/dist/declarations/src/structure-views.d.ts +5 -0
  44. package/dist/declarations/src/structure-views.d.ts.map +1 -0
  45. package/dist/declarations/src/types/DocumentFeatures.d.ts +33 -0
  46. package/dist/declarations/src/types/DocumentFeatures.d.ts.map +1 -0
  47. package/dist/declarations/src/types/DocumentFieldConfig.d.ts +18 -0
  48. package/dist/declarations/src/types/DocumentFieldConfig.d.ts.map +1 -0
  49. package/dist/declarations/src/types/FormattingConfig.d.ts +28 -0
  50. package/dist/declarations/src/types/FormattingConfig.d.ts.map +1 -0
  51. package/dist/declarations/src/types/RelationshipsConfig.d.ts +9 -0
  52. package/dist/declarations/src/types/RelationshipsConfig.d.ts.map +1 -0
  53. package/dist/declarations/src/types/StructureFieldConfig.d.ts +10 -0
  54. package/dist/declarations/src/types/StructureFieldConfig.d.ts.map +1 -0
  55. package/dist/declarations/src/validation/structure-validation.d.ts +218 -0
  56. package/dist/declarations/src/validation/structure-validation.d.ts.map +1 -0
  57. package/dist/declarations/src/views/Cell.d.ts +5 -0
  58. package/dist/declarations/src/views/Cell.d.ts.map +1 -0
  59. package/dist/declarations/src/views/Field.d.ts +5 -0
  60. package/dist/declarations/src/views/Field.d.ts.map +1 -0
  61. package/dist/declarations/src/views/controller.d.ts +15 -0
  62. package/dist/declarations/src/views/controller.d.ts.map +1 -0
  63. package/dist/declarations/src/views/document.d.ts +4 -0
  64. package/dist/declarations/src/views/document.d.ts.map +1 -0
  65. package/dist/declarations/src/views.d.ts +7 -0
  66. package/dist/declarations/src/views.d.ts.map +1 -0
  67. package/dist/editor-shared-a6e340e6.node.esm.js +1993 -0
  68. package/dist/editor-shared-a997ae98.node.cjs.js +2007 -0
  69. package/dist/editor-shared-cc1293ed.cjs.js +2007 -0
  70. package/dist/editor-shared-da518ba3.esm.js +1993 -0
  71. package/dist/form-from-preview-2042b9ef.cjs.js +512 -0
  72. package/dist/form-from-preview-5df6e492.node.esm.js +508 -0
  73. package/dist/form-from-preview-9e501058.node.cjs.js +512 -0
  74. package/dist/form-from-preview-b3a66f37.esm.js +508 -0
  75. package/dist/index-06c36775.cjs.js +14 -0
  76. package/dist/index-586adb8f.node.esm.js +11 -0
  77. package/dist/index-67d52357.esm.js +11 -0
  78. package/dist/index-c3223fdc.node.cjs.js +14 -0
  79. package/dist/layouts-6412fa2a.esm.js +189 -0
  80. package/dist/layouts-a4a3cf0b.node.cjs.js +196 -0
  81. package/dist/layouts-ba9a558b.cjs.js +196 -0
  82. package/dist/layouts-e653b908.node.esm.js +189 -0
  83. package/dist/lkb-fields-document.cjs.d.ts +2 -0
  84. package/dist/lkb-fields-document.cjs.js +1167 -0
  85. package/dist/lkb-fields-document.esm.js +1162 -0
  86. package/dist/lkb-fields-document.node.cjs.js +1167 -0
  87. package/dist/lkb-fields-document.node.esm.js +1162 -0
  88. package/dist/shared-0533009e.cjs.js +594 -0
  89. package/dist/shared-4684cc24.node.cjs.js +594 -0
  90. package/dist/shared-5e864055.node.esm.js +579 -0
  91. package/dist/shared-aaba5901.esm.js +579 -0
  92. package/dist/toolbar-state-3359e2f3.cjs.js +994 -0
  93. package/dist/toolbar-state-945823b8.node.esm.js +971 -0
  94. package/dist/toolbar-state-9611743f.node.cjs.js +994 -0
  95. package/dist/toolbar-state-bc8fe661.esm.js +971 -0
  96. package/dist/utils-06bcddc4.node.cjs.js +747 -0
  97. package/dist/utils-200ff260.node.esm.js +722 -0
  98. package/dist/utils-6409f730.cjs.js +747 -0
  99. package/dist/utils-bc6a0b82.esm.js +722 -0
  100. package/package.json +118 -0
  101. package/structure-views/dist/lkb-fields-document-structure-views.cjs.d.ts +2 -0
  102. package/structure-views/dist/lkb-fields-document-structure-views.cjs.js +138 -0
  103. package/structure-views/dist/lkb-fields-document-structure-views.esm.js +131 -0
  104. package/structure-views/dist/lkb-fields-document-structure-views.node.cjs.js +138 -0
  105. package/structure-views/dist/lkb-fields-document-structure-views.node.esm.js +131 -0
  106. package/structure-views/package.json +4 -0
  107. package/views/dist/lkb-fields-document-views.cjs.d.ts +2 -0
  108. package/views/dist/lkb-fields-document-views.cjs.js +114 -0
  109. package/views/dist/lkb-fields-document-views.esm.js +95 -0
  110. package/views/dist/lkb-fields-document-views.node.cjs.js +114 -0
  111. package/views/dist/lkb-fields-document-views.node.esm.js +95 -0
  112. package/views/package.json +4 -0
@@ -0,0 +1,1993 @@
1
+ import { Range, Editor, Transforms, Node, Path, Text, Element, Point, createEditor } from 'slate';
2
+ import { withHistory } from 'slate-history';
3
+ import weakMemoize from '@emotion/weak-memoize';
4
+ import { b as getDocumentFeaturesForChildField, c as getAncestorSchemas, d as assert, e as getValueAtPropPath, m as moveChildren, f as getKeysForArrayValue, g as getInitialPropsValue, s as setKeysForArrayValue, r as replaceValueAtPropPath, h as findChildPropPaths, a as assertNever, t as traverseProps, i as getNewArrayElementKey, E as EditorAfterButIgnoringingPointsWithNoContent, j as isElementActive, k as insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading } from './utils-200ff260.node.esm.js';
5
+ import { a as areArraysEqual, n as normalizeTextBasedOnInlineMarksAndSoftBreaks, b as normalizeElementBasedOnDocumentFeatures, c as normalizeInlineBasedOnLinksAndRelationships, g as getAncestorComponentChildFieldDocumentFeatures, w as withList, d as withParagraphs, e as withLayouts, f as withDocumentFeaturesNormalization } from './shared-5e864055.node.esm.js';
6
+ import { i as isValidURL } from './index-586adb8f.node.esm.js';
7
+ import mdASTUtilFromMarkdown from 'mdast-util-from-markdown';
8
+ import autoLinkLiteralFromMarkdownExtension from 'mdast-util-gfm-autolink-literal/from-markdown';
9
+ import autoLinkLiteralMarkdownSyntax from 'micromark-extension-gfm-autolink-literal';
10
+ import gfmStrikethroughFromMarkdownExtension from 'mdast-util-gfm-strikethrough/from-markdown';
11
+ import gfmStrikethroughMarkdownSyntax from 'micromark-extension-gfm-strikethrough';
12
+
13
+ function getAncestorComponentBlock(editor) {
14
+ if (editor.selection) {
15
+ const ancestorEntry = Editor.above(editor, {
16
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node) && node.type !== 'paragraph'
17
+ });
18
+ if (ancestorEntry && (ancestorEntry[0].type === 'component-block-prop' || ancestorEntry[0].type === 'component-inline-prop')) {
19
+ return {
20
+ isInside: true,
21
+ componentBlock: Editor.parent(editor, ancestorEntry[1]),
22
+ prop: ancestorEntry
23
+ };
24
+ }
25
+ }
26
+ return {
27
+ isInside: false
28
+ };
29
+ }
30
+ const alreadyNormalizedThings = new WeakMap();
31
+ function normalizeNodeWithinComponentProp([node, path], editor, fieldOptions, relationships) {
32
+ let alreadyNormalizedNodes = alreadyNormalizedThings.get(fieldOptions);
33
+ if (!alreadyNormalizedNodes) {
34
+ alreadyNormalizedNodes = new WeakSet();
35
+ alreadyNormalizedThings.set(fieldOptions, alreadyNormalizedNodes);
36
+ }
37
+ if (alreadyNormalizedNodes.has(node)) return false;
38
+ let didNormalization = false;
39
+ if (fieldOptions.inlineMarks !== 'inherit' && Text.isText(node)) {
40
+ didNormalization = normalizeTextBasedOnInlineMarksAndSoftBreaks([node, path], editor, fieldOptions.inlineMarks, fieldOptions.softBreaks);
41
+ }
42
+ if (Element.isElement(node)) {
43
+ for (const [i, child] of node.children.entries()) {
44
+ if (normalizeNodeWithinComponentProp([child, [...path, i]], editor, fieldOptions, relationships)) {
45
+ return true;
46
+ }
47
+ }
48
+ if (fieldOptions.kind === 'block') {
49
+ if (node.type === 'component-block') {
50
+ if (!fieldOptions.componentBlocks) {
51
+ Transforms.unwrapNodes(editor, {
52
+ at: path
53
+ });
54
+ didNormalization = true;
55
+ }
56
+ } else {
57
+ didNormalization = normalizeElementBasedOnDocumentFeatures([node, path], editor, fieldOptions.documentFeatures, relationships);
58
+ }
59
+ } else {
60
+ didNormalization = normalizeInlineBasedOnLinksAndRelationships([node, path], editor, fieldOptions.documentFeatures.links, fieldOptions.documentFeatures.relationships, relationships);
61
+ }
62
+ }
63
+ if (didNormalization === false) {
64
+ alreadyNormalizedNodes.add(node);
65
+ }
66
+ return didNormalization;
67
+ }
68
+ function canSchemaContainChildField(rootSchema) {
69
+ const queue = new Set([rootSchema]);
70
+ for (const schema of queue) {
71
+ if (schema.kind === 'form' || schema.kind === 'relationship') continue;
72
+ if (schema.kind === 'child') return true;
73
+ if (schema.kind === 'array') {
74
+ queue.add(schema.element);
75
+ continue;
76
+ }
77
+ if (schema.kind === 'object') {
78
+ for (const innerProp of Object.values(schema.fields)) {
79
+ queue.add(innerProp);
80
+ }
81
+ continue;
82
+ }
83
+ if (schema.kind === 'conditional') {
84
+ for (const innerProp of Object.values(schema.values)) {
85
+ queue.add(innerProp);
86
+ }
87
+ continue;
88
+ }
89
+ assertNever(schema);
90
+ }
91
+ return false;
92
+ }
93
+ function doesSchemaOnlyEverContainASingleChildField(rootSchema) {
94
+ const queue = new Set([rootSchema]);
95
+ let hasFoundChildField = false;
96
+ for (const schema of queue) {
97
+ if (schema.kind === 'form' || schema.kind === 'relationship') continue;
98
+ if (schema.kind === 'child') {
99
+ if (hasFoundChildField) return false;
100
+ hasFoundChildField = true;
101
+ continue;
102
+ }
103
+ if (schema.kind === 'array') {
104
+ if (canSchemaContainChildField(schema.element)) return false;
105
+ continue;
106
+ }
107
+ if (schema.kind === 'object') {
108
+ for (const innerProp of Object.values(schema.fields)) {
109
+ queue.add(innerProp);
110
+ }
111
+ continue;
112
+ }
113
+ if (schema.kind === 'conditional') {
114
+ for (const innerProp of Object.values(schema.values)) {
115
+ queue.add(innerProp);
116
+ }
117
+ continue;
118
+ }
119
+ assertNever(schema);
120
+ }
121
+ return hasFoundChildField;
122
+ }
123
+ function findArrayFieldsWithSingleChildField(schema, value) {
124
+ const propPaths = [];
125
+ traverseProps(schema, value, (schema, value, path) => {
126
+ if (schema.kind === 'array' && doesSchemaOnlyEverContainASingleChildField(schema.element)) {
127
+ propPaths.push([path, schema]);
128
+ }
129
+ });
130
+ return propPaths;
131
+ }
132
+ function isEmptyChildFieldNode(element) {
133
+ const firstChild = element.children[0];
134
+ return element.children.length === 1 && (element.type === 'component-inline-prop' && firstChild.type === undefined && firstChild.text === '' || element.type === 'component-block-prop' && firstChild.type === 'paragraph' && firstChild.children.length === 1 && firstChild.children[0].type === undefined && firstChild.children[0].text === '');
135
+ }
136
+ function withComponentBlocks(blockComponents, editorDocumentFeatures, relationships, editor) {
137
+ // note that conflicts between the editor document features
138
+ // and the child field document features are dealt with elsewhere
139
+ const memoizedGetDocumentFeaturesForChildField = weakMemoize(options => {
140
+ return getDocumentFeaturesForChildField(editorDocumentFeatures, options);
141
+ });
142
+ const {
143
+ normalizeNode,
144
+ deleteBackward,
145
+ insertBreak
146
+ } = editor;
147
+ editor.deleteBackward = unit => {
148
+ if (editor.selection) {
149
+ const ancestorComponentBlock = getAncestorComponentBlock(editor);
150
+ if (ancestorComponentBlock.isInside && Range.isCollapsed(editor.selection) && Editor.isStart(editor, editor.selection.anchor, ancestorComponentBlock.prop[1]) && ancestorComponentBlock.prop[1][ancestorComponentBlock.prop[1].length - 1] === 0) {
151
+ Transforms.unwrapNodes(editor, {
152
+ at: ancestorComponentBlock.componentBlock[1]
153
+ });
154
+ return;
155
+ }
156
+ }
157
+ deleteBackward(unit);
158
+ };
159
+ editor.insertBreak = () => {
160
+ const ancestorComponentBlock = getAncestorComponentBlock(editor);
161
+ if (editor.selection && ancestorComponentBlock.isInside) {
162
+ const {
163
+ prop: [componentPropNode, componentPropPath],
164
+ componentBlock: [componentBlockNode, componentBlockPath]
165
+ } = ancestorComponentBlock;
166
+ const isLastProp = componentPropPath[componentPropPath.length - 1] === componentBlockNode.children.length - 1;
167
+ if (componentPropNode.type === 'component-block-prop') {
168
+ const [[paragraphNode, paragraphPath]] = Editor.nodes(editor, {
169
+ match: node => node.type === 'paragraph'
170
+ });
171
+ const isLastParagraph = paragraphPath[paragraphPath.length - 1] === componentPropNode.children.length - 1;
172
+ if (Node.string(paragraphNode) === '' && isLastParagraph) {
173
+ if (isLastProp) {
174
+ Transforms.moveNodes(editor, {
175
+ at: paragraphPath,
176
+ to: Path.next(ancestorComponentBlock.componentBlock[1])
177
+ });
178
+ } else {
179
+ Transforms.move(editor, {
180
+ distance: 1,
181
+ unit: 'line'
182
+ });
183
+ Transforms.removeNodes(editor, {
184
+ at: paragraphPath
185
+ });
186
+ }
187
+ return;
188
+ }
189
+ }
190
+ if (componentPropNode.type === 'component-inline-prop') {
191
+ Editor.withoutNormalizing(editor, () => {
192
+ const componentBlock = blockComponents[componentBlockNode.component];
193
+ if (componentPropNode.propPath !== undefined && componentBlock !== undefined) {
194
+ const rootSchema = {
195
+ kind: 'object',
196
+ fields: componentBlock.schema
197
+ };
198
+ const ancestorFields = getAncestorSchemas(rootSchema, componentPropNode.propPath, componentBlockNode.props);
199
+ const idx = [...ancestorFields].reverse().findIndex(item => item.kind === 'array');
200
+ if (idx !== -1) {
201
+ const arrayFieldIdx = ancestorFields.length - 1 - idx;
202
+ const arrayField = ancestorFields[arrayFieldIdx];
203
+ assert(arrayField.kind === 'array');
204
+ const val = getValueAtPropPath(componentBlockNode.props, componentPropNode.propPath.slice(0, arrayFieldIdx));
205
+ if (doesSchemaOnlyEverContainASingleChildField(arrayField.element)) {
206
+ if (Node.string(componentPropNode) === '' && val.length - 1 === componentPropNode.propPath[arrayFieldIdx]) {
207
+ Transforms.removeNodes(editor, {
208
+ at: componentPropPath
209
+ });
210
+ if (isLastProp) {
211
+ Transforms.insertNodes(editor, {
212
+ type: 'paragraph',
213
+ children: [{
214
+ text: ''
215
+ }]
216
+ }, {
217
+ at: Path.next(componentBlockPath)
218
+ });
219
+ Transforms.select(editor, Path.next(componentBlockPath));
220
+ } else {
221
+ Transforms.move(editor, {
222
+ distance: 1,
223
+ unit: 'line'
224
+ });
225
+ }
226
+ } else {
227
+ insertBreak();
228
+ }
229
+ return;
230
+ }
231
+ }
232
+ }
233
+ Transforms.splitNodes(editor, {
234
+ always: true
235
+ });
236
+ const splitNodePath = Path.next(componentPropPath);
237
+ if (isLastProp) {
238
+ Transforms.moveNodes(editor, {
239
+ at: splitNodePath,
240
+ to: Path.next(componentBlockPath)
241
+ });
242
+ } else {
243
+ moveChildren(editor, splitNodePath, [...Path.next(splitNodePath), 0]);
244
+ Transforms.removeNodes(editor, {
245
+ at: splitNodePath
246
+ });
247
+ }
248
+ });
249
+ return;
250
+ }
251
+ }
252
+ insertBreak();
253
+ };
254
+ editor.normalizeNode = entry => {
255
+ const [node, path] = entry;
256
+ if (node.type === 'component-inline-prop' && !node.propPath && (node.children.length !== 1 || !Text.isText(node.children[0]) || node.children[0].text !== '')) {
257
+ Transforms.removeNodes(editor, {
258
+ at: path
259
+ });
260
+ return;
261
+ }
262
+ if (node.type === 'component-block') {
263
+ const componentBlock = blockComponents[node.component];
264
+ if (componentBlock) {
265
+ const rootSchema = {
266
+ kind: 'object',
267
+ fields: componentBlock.schema
268
+ };
269
+ const updatedProps = addMissingFields(node.props, rootSchema);
270
+ if (updatedProps !== node.props) {
271
+ Transforms.setNodes(editor, {
272
+ props: updatedProps
273
+ }, {
274
+ at: path
275
+ });
276
+ return;
277
+ }
278
+ for (const [propPath, arrayField] of findArrayFieldsWithSingleChildField(rootSchema, node.props)) {
279
+ if (node.children.length === 1 && node.children[0].type === 'component-inline-prop' && node.children[0].propPath === undefined) {
280
+ break;
281
+ }
282
+ const nodesWithin = [];
283
+ for (const [idx, childNode] of node.children.entries()) {
284
+ if ((childNode.type === 'component-block-prop' || childNode.type === 'component-inline-prop') && childNode.propPath !== undefined) {
285
+ const subPath = childNode.propPath.concat();
286
+ while (subPath.length) {
287
+ if (typeof subPath.pop() === 'number') break;
288
+ }
289
+ if (areArraysEqual(propPath, subPath)) {
290
+ nodesWithin.push([idx, childNode]);
291
+ }
292
+ }
293
+ }
294
+ const arrVal = getValueAtPropPath(node.props, propPath);
295
+ const prevKeys = getKeysForArrayValue(arrVal);
296
+ const prevKeysSet = new Set(prevKeys);
297
+ const alreadyUsedIndicies = new Set();
298
+ const newVal = [];
299
+ const newKeys = [];
300
+ const getNewKey = () => {
301
+ let key = getNewArrayElementKey();
302
+ while (prevKeysSet.has(key)) {
303
+ key = getNewArrayElementKey();
304
+ }
305
+ return key;
306
+ };
307
+ for (const [, node] of nodesWithin) {
308
+ const idxFromValue = node.propPath[propPath.length];
309
+ assert(typeof idxFromValue === 'number');
310
+ if (arrVal.length <= idxFromValue || alreadyUsedIndicies.has(idxFromValue) && isEmptyChildFieldNode(node)) {
311
+ newVal.push(getInitialPropsValue(arrayField.element));
312
+ newKeys.push(getNewKey());
313
+ } else {
314
+ alreadyUsedIndicies.add(idxFromValue);
315
+ newVal.push(arrVal[idxFromValue]);
316
+ newKeys.push(alreadyUsedIndicies.has(idxFromValue) ? getNewKey() : prevKeys[idxFromValue]);
317
+ }
318
+ }
319
+ setKeysForArrayValue(newVal, newKeys);
320
+ if (!areArraysEqual(arrVal, newVal)) {
321
+ const transformedProps = replaceValueAtPropPath(rootSchema, node.props, newVal, propPath);
322
+ Transforms.setNodes(editor, {
323
+ props: transformedProps
324
+ }, {
325
+ at: path
326
+ });
327
+ for (const [idx, [idxInChildrenOfBlock, nodeWithin]] of nodesWithin.entries()) {
328
+ const newPropPath = [...nodeWithin.propPath];
329
+ newPropPath[propPath.length] = idx;
330
+ Transforms.setNodes(editor, {
331
+ propPath: newPropPath
332
+ }, {
333
+ at: [...path, idxInChildrenOfBlock]
334
+ });
335
+ }
336
+ return;
337
+ }
338
+ }
339
+ const missingKeys = new Map(findChildPropPaths(node.props, componentBlock.schema).map(x => [JSON.stringify(x.path), x.options.kind]));
340
+ node.children.forEach(node => {
341
+ assert(node.type === 'component-block-prop' || node.type === 'component-inline-prop');
342
+ missingKeys.delete(JSON.stringify(node.propPath));
343
+ });
344
+ if (missingKeys.size) {
345
+ Transforms.insertNodes(editor, [...missingKeys].map(([prop, kind]) => ({
346
+ type: `component-${kind}-prop`,
347
+ propPath: prop ? JSON.parse(prop) : prop,
348
+ children: [{
349
+ text: ''
350
+ }]
351
+ })), {
352
+ at: [...path, node.children.length]
353
+ });
354
+ return;
355
+ }
356
+ const foundProps = new Set();
357
+ const stringifiedInlinePropPaths = {};
358
+ findChildPropPaths(node.props, blockComponents[node.component].schema).forEach((x, index) => {
359
+ stringifiedInlinePropPaths[JSON.stringify(x.path)] = {
360
+ options: x.options,
361
+ index
362
+ };
363
+ });
364
+ for (const [index, childNode] of node.children.entries()) {
365
+ if (
366
+ // children that are not these will be handled by
367
+ // the generic allowedChildren normalization
368
+ childNode.type !== 'component-inline-prop' && childNode.type !== 'component-block-prop') {
369
+ continue;
370
+ }
371
+ const childPath = [...path, index];
372
+ const stringifiedPropPath = JSON.stringify(childNode.propPath);
373
+ if (stringifiedInlinePropPaths[stringifiedPropPath] === undefined) {
374
+ Transforms.removeNodes(editor, {
375
+ at: childPath
376
+ });
377
+ return;
378
+ }
379
+ if (foundProps.has(stringifiedPropPath)) {
380
+ Transforms.removeNodes(editor, {
381
+ at: childPath
382
+ });
383
+ return;
384
+ }
385
+ foundProps.add(stringifiedPropPath);
386
+ const propInfo = stringifiedInlinePropPaths[stringifiedPropPath];
387
+ const expectedIndex = propInfo.index;
388
+ if (index !== expectedIndex) {
389
+ Transforms.moveNodes(editor, {
390
+ at: childPath,
391
+ to: [...path, expectedIndex]
392
+ });
393
+ return;
394
+ }
395
+ const expectedChildNodeType = `component-${propInfo.options.kind}-prop`;
396
+ if (childNode.type !== expectedChildNodeType) {
397
+ Transforms.setNodes(editor, {
398
+ type: expectedChildNodeType
399
+ }, {
400
+ at: childPath
401
+ });
402
+ return;
403
+ }
404
+ const documentFeatures = memoizedGetDocumentFeaturesForChildField(propInfo.options);
405
+ if (normalizeNodeWithinComponentProp([childNode, childPath], editor, documentFeatures, relationships)) {
406
+ return;
407
+ }
408
+ }
409
+ }
410
+ }
411
+ normalizeNode(entry);
412
+ };
413
+ return editor;
414
+ }
415
+
416
+ // the only thing that this will fix is a new field being added to an object field, nothing else.
417
+ function addMissingFields(value, schema) {
418
+ if (schema.kind === 'child' || schema.kind === 'form' || schema.kind === 'relationship') {
419
+ return value;
420
+ }
421
+ if (schema.kind === 'conditional') {
422
+ const conditionalValue = value;
423
+ const updatedInnerValue = addMissingFields(conditionalValue.value, schema.values[conditionalValue.discriminant.toString()]);
424
+ if (updatedInnerValue === conditionalValue.value) {
425
+ return value;
426
+ }
427
+ return {
428
+ discriminant: conditionalValue.discriminant,
429
+ value: updatedInnerValue
430
+ };
431
+ }
432
+ if (schema.kind === 'array') {
433
+ const arrValue = value;
434
+ const newArrValue = arrValue.map(x => addMissingFields(x, schema.element));
435
+ if (areArraysEqual(arrValue, newArrValue)) {
436
+ return value;
437
+ }
438
+ return newArrValue;
439
+ }
440
+ if (schema.kind === 'object') {
441
+ const objectValue = value;
442
+ let hasChanged = false;
443
+ const newObjectValue = {};
444
+ for (const [key, innerSchema] of Object.entries(schema.fields)) {
445
+ const innerValue = objectValue[key];
446
+ if (innerValue === undefined) {
447
+ hasChanged = true;
448
+ newObjectValue[key] = getInitialPropsValue(innerSchema);
449
+ continue;
450
+ }
451
+ const newInnerValue = addMissingFields(innerValue, innerSchema);
452
+ if (newInnerValue !== innerValue) {
453
+ hasChanged = true;
454
+ }
455
+ newObjectValue[key] = newInnerValue;
456
+ }
457
+ if (hasChanged) {
458
+ return newObjectValue;
459
+ }
460
+ return value;
461
+ }
462
+ assertNever(schema);
463
+ }
464
+
465
+ const isLinkActive = editor => {
466
+ return isElementActive(editor, 'link');
467
+ };
468
+ function wrapLink(editor, url) {
469
+ if (isLinkActive(editor)) {
470
+ Transforms.unwrapNodes(editor, {
471
+ match: n => n.type === 'link'
472
+ });
473
+ return;
474
+ }
475
+ const {
476
+ selection
477
+ } = editor;
478
+ const isCollapsed = selection && Range.isCollapsed(selection);
479
+ if (isCollapsed) {
480
+ Transforms.insertNodes(editor, {
481
+ type: 'link',
482
+ href: url,
483
+ children: [{
484
+ text: url
485
+ }]
486
+ });
487
+ } else {
488
+ Transforms.wrapNodes(editor, {
489
+ type: 'link',
490
+ href: url,
491
+ children: [{
492
+ text: ''
493
+ }]
494
+ }, {
495
+ split: true
496
+ });
497
+ }
498
+ }
499
+ const markdownLinkPattern = /(^|\s)\[(.+?)\]\((\S+)\)$/;
500
+ function withLink(editorDocumentFeatures, componentBlocks, editor) {
501
+ const {
502
+ insertText,
503
+ isInline,
504
+ normalizeNode
505
+ } = editor;
506
+ editor.isInline = element => {
507
+ return element.type === 'link' ? true : isInline(element);
508
+ };
509
+ if (editorDocumentFeatures.links) {
510
+ editor.insertText = text => {
511
+ insertText(text);
512
+ if (text !== ')' || !editor.selection) return;
513
+ const startOfBlock = Editor.start(editor, Editor.above(editor, {
514
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
515
+ })[1]);
516
+ const startOfBlockToEndOfShortcutString = Editor.string(editor, {
517
+ anchor: editor.selection.anchor,
518
+ focus: startOfBlock
519
+ });
520
+ const match = markdownLinkPattern.exec(startOfBlockToEndOfShortcutString);
521
+ if (!match) return;
522
+ const ancestorComponentChildFieldDocumentFeatures = getAncestorComponentChildFieldDocumentFeatures(editor, editorDocumentFeatures, componentBlocks);
523
+ if ((ancestorComponentChildFieldDocumentFeatures === null || ancestorComponentChildFieldDocumentFeatures === void 0 ? void 0 : ancestorComponentChildFieldDocumentFeatures.documentFeatures.links) === false) {
524
+ return;
525
+ }
526
+ const [, maybeWhitespace, linkText, href] = match;
527
+ // by doing this, the insertText(')') above will happen in a different undo than the link replacement
528
+ // so that means that when someone does an undo after this
529
+ // it will undo to the state of "[content](link)" rather than "[content](link" (note the missing closing bracket)
530
+ editor.writeHistory('undos', {
531
+ operations: [],
532
+ selectionBefore: null
533
+ });
534
+ const startOfShortcut = match.index === 0 ? startOfBlock : EditorAfterButIgnoringingPointsWithNoContent(editor, startOfBlock, {
535
+ distance: match.index
536
+ });
537
+ const startOfLinkText = EditorAfterButIgnoringingPointsWithNoContent(editor, startOfShortcut, {
538
+ distance: maybeWhitespace === '' ? 1 : 2
539
+ });
540
+ const endOfLinkText = EditorAfterButIgnoringingPointsWithNoContent(editor, startOfLinkText, {
541
+ distance: linkText.length
542
+ });
543
+ Transforms.delete(editor, {
544
+ at: {
545
+ anchor: endOfLinkText,
546
+ focus: editor.selection.anchor
547
+ }
548
+ });
549
+ Transforms.delete(editor, {
550
+ at: {
551
+ anchor: startOfShortcut,
552
+ focus: startOfLinkText
553
+ }
554
+ });
555
+ Transforms.wrapNodes(editor, {
556
+ type: 'link',
557
+ href,
558
+ children: []
559
+ }, {
560
+ at: {
561
+ anchor: editor.selection.anchor,
562
+ focus: startOfShortcut
563
+ },
564
+ split: true
565
+ });
566
+ const nextNode = Editor.next(editor);
567
+ if (nextNode) {
568
+ Transforms.select(editor, nextNode[1]);
569
+ }
570
+ };
571
+ }
572
+ editor.normalizeNode = ([node, path]) => {
573
+ if (node.type === 'link') {
574
+ if (Node.string(node) === '') {
575
+ Transforms.unwrapNodes(editor, {
576
+ at: path
577
+ });
578
+ return;
579
+ }
580
+ for (const [idx, child] of node.children.entries()) {
581
+ if (child.type === 'link') {
582
+ // links cannot contain links
583
+ Transforms.unwrapNodes(editor, {
584
+ at: [...path, idx]
585
+ });
586
+ return;
587
+ }
588
+ }
589
+ }
590
+ if (isInlineContainer(node)) {
591
+ let lastMergableLink = null;
592
+ for (const [idx, child] of node.children.entries()) {
593
+ var _lastMergableLink;
594
+ if (child.type === 'link' && child.href === ((_lastMergableLink = lastMergableLink) === null || _lastMergableLink === void 0 ? void 0 : _lastMergableLink.node.href)) {
595
+ const firstLinkPath = [...path, lastMergableLink.index];
596
+ const secondLinkPath = [...path, idx];
597
+ const to = [...firstLinkPath, lastMergableLink.node.children.length];
598
+ // note this is going in reverse, js doesn't have double-ended iterators so it's a for(;;)
599
+ for (let i = child.children.length - 1; i >= 0; i--) {
600
+ const childPath = [...secondLinkPath, i];
601
+ Transforms.moveNodes(editor, {
602
+ at: childPath,
603
+ to
604
+ });
605
+ }
606
+ Transforms.removeNodes(editor, {
607
+ at: secondLinkPath
608
+ });
609
+ return;
610
+ }
611
+ if (!Text.isText(child) || child.text !== '') {
612
+ lastMergableLink = null;
613
+ }
614
+ if (child.type === 'link') {
615
+ lastMergableLink = {
616
+ index: idx,
617
+ node: child
618
+ };
619
+ }
620
+ }
621
+ }
622
+ normalizeNode([node, path]);
623
+ };
624
+ return editor;
625
+ }
626
+
627
+ function withHeading(editor) {
628
+ const {
629
+ insertBreak
630
+ } = editor;
631
+ editor.insertBreak = () => {
632
+ insertBreak();
633
+ const entry = Editor.above(editor, {
634
+ match: n => n.type === 'heading'
635
+ });
636
+ if (!entry || !editor.selection || !Range.isCollapsed(editor.selection)) return;
637
+ const path = entry[1];
638
+ if (
639
+ // we want to unwrap the heading when the user inserted a break at the end of the heading
640
+ // when the user inserts a break at the end of a heading, the new heading
641
+ // that we want to unwrap will be empty so the end will be equal to the selection
642
+ Point.equals(Editor.end(editor, path), editor.selection.anchor)) {
643
+ Transforms.unwrapNodes(editor, {
644
+ at: path
645
+ });
646
+ return;
647
+ }
648
+ // we also want to unwrap the _previous_ heading when the user inserted a break
649
+ // at the start of the heading, essentially just inserting an empty paragraph above the heading
650
+ if (!Path.hasPrevious(path)) return;
651
+ const previousPath = Path.previous(path);
652
+ const previousNode = Node.get(editor, previousPath);
653
+ if (previousNode.type === 'heading' && previousNode.children.length === 1 && Text.isText(previousNode.children[0]) && previousNode.children[0].text === '') {
654
+ Transforms.unwrapNodes(editor, {
655
+ at: previousPath
656
+ });
657
+ }
658
+ };
659
+ return editor;
660
+ }
661
+
662
+ function insertBlockquote(editor) {
663
+ const isActive = isElementActive(editor, 'blockquote');
664
+ if (isActive) {
665
+ Transforms.unwrapNodes(editor, {
666
+ match: node => node.type === 'blockquote'
667
+ });
668
+ } else {
669
+ Transforms.wrapNodes(editor, {
670
+ type: 'blockquote',
671
+ children: []
672
+ });
673
+ }
674
+ }
675
+ function getDirectBlockquoteParentFromSelection(editor) {
676
+ if (!editor.selection) return {
677
+ isInside: false
678
+ };
679
+ const [, parentPath] = Editor.parent(editor, editor.selection);
680
+ if (!parentPath.length) {
681
+ return {
682
+ isInside: false
683
+ };
684
+ }
685
+ const [maybeBlockquoteParent, maybeBlockquoteParentPath] = Editor.parent(editor, parentPath);
686
+ const isBlockquote = maybeBlockquoteParent.type === 'blockquote';
687
+ return isBlockquote ? {
688
+ isInside: true,
689
+ path: maybeBlockquoteParentPath
690
+ } : {
691
+ isInside: false
692
+ };
693
+ }
694
+ function withBlockquote(editor) {
695
+ const {
696
+ insertBreak,
697
+ deleteBackward
698
+ } = editor;
699
+ editor.deleteBackward = unit => {
700
+ if (editor.selection) {
701
+ const parentBlockquote = getDirectBlockquoteParentFromSelection(editor);
702
+ if (parentBlockquote.isInside && Range.isCollapsed(editor.selection) &&
703
+ // the selection is at the start of the paragraph
704
+ editor.selection.anchor.offset === 0 &&
705
+ // it's the first paragraph in the panel
706
+ editor.selection.anchor.path[editor.selection.anchor.path.length - 2] === 0) {
707
+ Transforms.unwrapNodes(editor, {
708
+ match: node => node.type === 'blockquote',
709
+ split: true
710
+ });
711
+ return;
712
+ }
713
+ }
714
+ deleteBackward(unit);
715
+ };
716
+ editor.insertBreak = () => {
717
+ const panel = getDirectBlockquoteParentFromSelection(editor);
718
+ if (editor.selection && panel.isInside) {
719
+ const [node, nodePath] = Editor.node(editor, editor.selection);
720
+ if (Path.isDescendant(nodePath, panel.path) && Node.string(node) === '') {
721
+ Transforms.unwrapNodes(editor, {
722
+ match: node => node.type === 'blockquote',
723
+ split: true
724
+ });
725
+ return;
726
+ }
727
+ }
728
+ insertBreak();
729
+ };
730
+ return editor;
731
+ }
732
+
733
+ function withRelationship(editor) {
734
+ const {
735
+ isVoid,
736
+ isInline
737
+ } = editor;
738
+ editor.isVoid = element => element.type === 'relationship' || isVoid(element);
739
+ editor.isInline = element => element.type === 'relationship' || isInline(element);
740
+ return editor;
741
+ }
742
+
743
+ function insertDivider(editor) {
744
+ insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading(editor, {
745
+ type: 'divider',
746
+ children: [{
747
+ text: ''
748
+ }]
749
+ });
750
+ Editor.insertNode(editor, {
751
+ type: 'paragraph',
752
+ children: [{
753
+ text: ''
754
+ }]
755
+ });
756
+ }
757
+ function withDivider(editor) {
758
+ const {
759
+ isVoid
760
+ } = editor;
761
+ editor.isVoid = node => {
762
+ return node.type === 'divider' || isVoid(node);
763
+ };
764
+ return editor;
765
+ }
766
+
767
+ function withCodeBlock(editor) {
768
+ const {
769
+ insertBreak,
770
+ normalizeNode
771
+ } = editor;
772
+ editor.insertBreak = () => {
773
+ const [node, path] = Editor.above(editor, {
774
+ match: n => Element.isElement(n) && Editor.isBlock(editor, n)
775
+ }) || [editor, []];
776
+ if (node.type === 'code' && Text.isText(node.children[0])) {
777
+ const text = node.children[0].text;
778
+ if (text[text.length - 1] === '\n' && editor.selection && Range.isCollapsed(editor.selection) && Point.equals(Editor.end(editor, path), editor.selection.anchor)) {
779
+ insertBreak();
780
+ Transforms.setNodes(editor, {
781
+ type: 'paragraph',
782
+ children: []
783
+ });
784
+ Transforms.delete(editor, {
785
+ distance: 1,
786
+ at: {
787
+ path: [...path, 0],
788
+ offset: text.length - 1
789
+ }
790
+ });
791
+ return;
792
+ }
793
+ editor.insertText('\n');
794
+ return;
795
+ }
796
+ insertBreak();
797
+ };
798
+ editor.normalizeNode = ([node, path]) => {
799
+ if (node.type === 'code' && Element.isElement(node)) {
800
+ for (const [index, childNode] of node.children.entries()) {
801
+ if (!Text.isText(childNode)) {
802
+ if (editor.isVoid(childNode)) {
803
+ Transforms.removeNodes(editor, {
804
+ at: [...path, index]
805
+ });
806
+ } else {
807
+ Transforms.unwrapNodes(editor, {
808
+ at: [...path, index]
809
+ });
810
+ }
811
+ return;
812
+ }
813
+ const marks = Object.keys(childNode).filter(x => x !== 'text');
814
+ if (marks.length) {
815
+ Transforms.unsetNodes(editor, marks, {
816
+ at: [...path, index]
817
+ });
818
+ return;
819
+ }
820
+ }
821
+ }
822
+ normalizeNode([node, path]);
823
+ };
824
+ return editor;
825
+ }
826
+
827
+ const allMarkdownShortcuts = {
828
+ bold: ['**', '__'],
829
+ italic: ['*', '_'],
830
+ strikethrough: ['~~'],
831
+ code: ['`']
832
+ };
833
+ function applyMark(editor, mark, shortcutText, startOfStartPoint) {
834
+ // so that this starts a new undo group
835
+ editor.writeHistory('undos', {
836
+ operations: [],
837
+ selectionBefore: null
838
+ });
839
+ const startPointRef = Editor.pointRef(editor, startOfStartPoint);
840
+ Transforms.delete(editor, {
841
+ at: editor.selection.anchor,
842
+ distance: shortcutText.length,
843
+ reverse: true
844
+ });
845
+ Transforms.delete(editor, {
846
+ at: startOfStartPoint,
847
+ distance: shortcutText.length
848
+ });
849
+ Transforms.setNodes(editor, {
850
+ [mark]: true
851
+ }, {
852
+ match: Text.isText,
853
+ split: true,
854
+ at: {
855
+ anchor: startPointRef.unref(),
856
+ focus: editor.selection.anchor
857
+ }
858
+ });
859
+ // once you've ended the shortcut, you're done with the mark
860
+ // so we need to remove it so the text you insert after doesn't have it
861
+ editor.removeMark(mark);
862
+ }
863
+ function withMarks(editorDocumentFeatures, componentBlocks, editor) {
864
+ const {
865
+ insertText,
866
+ insertBreak
867
+ } = editor;
868
+ editor.insertBreak = () => {
869
+ insertBreak();
870
+ const marksAfterInsertBreak = Editor.marks(editor);
871
+ if (!marksAfterInsertBreak || !editor.selection) return;
872
+ const parentBlock = Editor.above(editor, {
873
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
874
+ });
875
+ if (!parentBlock) return;
876
+ const point = EditorAfterButIgnoringingPointsWithNoContent(editor, editor.selection.anchor);
877
+ const marksAfterInsertBreakArr = Object.keys(marksAfterInsertBreak);
878
+ if (!point || !Path.isDescendant(point.path, parentBlock[1])) {
879
+ for (const mark of marksAfterInsertBreakArr) {
880
+ editor.removeMark(mark);
881
+ }
882
+ return;
883
+ }
884
+ const textNode = Node.get(editor, point.path);
885
+ for (const mark of marksAfterInsertBreakArr) {
886
+ if (!textNode[mark]) {
887
+ editor.removeMark(mark);
888
+ }
889
+ }
890
+ };
891
+ const selectedMarkdownShortcuts = {};
892
+ const enabledMarks = editorDocumentFeatures.formatting.inlineMarks;
893
+ Object.keys(allMarkdownShortcuts).forEach(mark => {
894
+ if (enabledMarks[mark]) {
895
+ selectedMarkdownShortcuts[mark] = allMarkdownShortcuts[mark];
896
+ }
897
+ });
898
+ if (Object.keys(selectedMarkdownShortcuts).length === 0) return editor;
899
+ editor.insertText = text => {
900
+ insertText(text);
901
+ if (editor.selection && Range.isCollapsed(editor.selection)) {
902
+ for (const [mark, shortcuts] of Object.entries(selectedMarkdownShortcuts)) {
903
+ for (const shortcutText of shortcuts) {
904
+ if (text === shortcutText[shortcutText.length - 1]) {
905
+ // this function is not inlined because
906
+ // https://github.com/swc-project/swc/issues/2622
907
+ const startOfBlock = getStartOfBlock(editor);
908
+ const startOfBlockToEndOfShortcutString = Editor.string(editor, {
909
+ anchor: editor.selection.anchor,
910
+ focus: startOfBlock
911
+ });
912
+ const hasWhitespaceBeforeEndOfShortcut = /\s/.test(startOfBlockToEndOfShortcutString.slice(-shortcutText.length - 1, -shortcutText.length));
913
+ const endOfShortcutContainsExpectedContent = shortcutText === startOfBlockToEndOfShortcutString.slice(-shortcutText.length);
914
+ if (hasWhitespaceBeforeEndOfShortcut || !endOfShortcutContainsExpectedContent) {
915
+ continue;
916
+ }
917
+ const strToMatchOn = startOfBlockToEndOfShortcutString.slice(0, -shortcutText.length - 1);
918
+ // TODO: use regex probs
919
+ for (const [offsetFromStartOfBlock] of [...strToMatchOn].reverse().entries()) {
920
+ const expectedShortcutText = strToMatchOn.slice(offsetFromStartOfBlock, offsetFromStartOfBlock + shortcutText.length);
921
+ if (expectedShortcutText !== shortcutText) {
922
+ continue;
923
+ }
924
+ const startOfStartOfShortcut = offsetFromStartOfBlock === 0 ? startOfBlock : EditorAfterButIgnoringingPointsWithNoContent(editor, startOfBlock, {
925
+ distance: offsetFromStartOfBlock
926
+ });
927
+ const endOfStartOfShortcut = Editor.after(editor, startOfStartOfShortcut, {
928
+ distance: shortcutText.length
929
+ });
930
+ if (offsetFromStartOfBlock !== 0 && !/\s/.test(Editor.string(editor, {
931
+ anchor: Editor.before(editor, startOfStartOfShortcut, {
932
+ unit: 'character'
933
+ }),
934
+ focus: startOfStartOfShortcut
935
+ }))) {
936
+ continue;
937
+ }
938
+ const contentBetweenShortcuts = Editor.string(editor, {
939
+ anchor: endOfStartOfShortcut,
940
+ focus: editor.selection.anchor
941
+ }).slice(0, -shortcutText.length);
942
+ if (contentBetweenShortcuts === '' || /\s/.test(contentBetweenShortcuts[0])) {
943
+ continue;
944
+ }
945
+
946
+ // this is a bit of a weird one
947
+ // let's say you had <text>__thing _<cursor /></text> and you insert `_`.
948
+ // without the below, that would turn into <text italic>_thing _<cursor /></text>
949
+ // but it's probably meant to be bold but it's not because of the space before the ending _
950
+ // there's probably a better way to do this but meh, this works
951
+ if (mark === 'italic' && (contentBetweenShortcuts[0] === '_' || contentBetweenShortcuts[0] === '*')) {
952
+ continue;
953
+ }
954
+ const ancestorComponentChildFieldDocumentFeatures = getAncestorComponentChildFieldDocumentFeatures(editor, editorDocumentFeatures, componentBlocks);
955
+ if (ancestorComponentChildFieldDocumentFeatures && ancestorComponentChildFieldDocumentFeatures.inlineMarks !== 'inherit' && ancestorComponentChildFieldDocumentFeatures.inlineMarks[mark] === false) {
956
+ continue;
957
+ }
958
+ applyMark(editor, mark, shortcutText, startOfStartOfShortcut);
959
+ return;
960
+ }
961
+ }
962
+ }
963
+ }
964
+ }
965
+ };
966
+ return editor;
967
+ }
968
+ function getStartOfBlock(editor) {
969
+ return Editor.start(editor, Editor.above(editor, {
970
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
971
+ })[1]);
972
+ }
973
+
974
+ function withSoftBreaks(editor) {
975
+ // TODO: should soft breaks only work in particular places
976
+ editor.insertSoftBreak = () => {
977
+ Transforms.insertText(editor, '\n');
978
+ };
979
+ return editor;
980
+ }
981
+
982
+ const shortcuts = {
983
+ '...': '…',
984
+ '-->': '→',
985
+ '->': '→',
986
+ '<-': '←',
987
+ '<--': '←',
988
+ '--': '–'
989
+ };
990
+ function withShortcuts(editor) {
991
+ const {
992
+ insertText
993
+ } = editor;
994
+ editor.insertText = text => {
995
+ insertText(text);
996
+ if (text === ' ' && editor.selection && Range.isCollapsed(editor.selection)) {
997
+ const selectionPoint = editor.selection.anchor;
998
+ const ancestorBlock = Editor.above(editor, {
999
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
1000
+ });
1001
+ if (ancestorBlock) {
1002
+ Object.keys(shortcuts).forEach(shortcut => {
1003
+ const pointBefore = Editor.before(editor, selectionPoint, {
1004
+ unit: 'character',
1005
+ distance: shortcut.length + 1
1006
+ });
1007
+ if (pointBefore && Path.isDescendant(pointBefore.path, ancestorBlock[1])) {
1008
+ const range = {
1009
+ anchor: selectionPoint,
1010
+ focus: pointBefore
1011
+ };
1012
+ const str = Editor.string(editor, range);
1013
+ if (str.slice(0, shortcut.length) === shortcut) {
1014
+ editor.writeHistory('undos', {
1015
+ operations: [],
1016
+ selectionBefore: null
1017
+ });
1018
+ Transforms.select(editor, range);
1019
+ editor.insertText(shortcuts[shortcut] + ' ');
1020
+ }
1021
+ }
1022
+ });
1023
+ }
1024
+ }
1025
+ };
1026
+ return editor;
1027
+ }
1028
+
1029
+ const nodeListsWithoutInsertMenu = new WeakSet();
1030
+ const nodesWithoutInsertMenu = new WeakSet();
1031
+ function findPathWithInsertMenu(node, path) {
1032
+ if (Text.isText(node)) return node.insertMenu ? path : undefined;
1033
+ if (nodeListsWithoutInsertMenu.has(node.children)) return;
1034
+ for (const [index, child] of node.children.entries()) {
1035
+ if (nodesWithoutInsertMenu.has(child)) continue;
1036
+ const maybePath = findPathWithInsertMenu(child, [...path, index]);
1037
+ if (maybePath) {
1038
+ return maybePath;
1039
+ }
1040
+ nodesWithoutInsertMenu.add(child);
1041
+ }
1042
+ nodeListsWithoutInsertMenu.add(node.children);
1043
+ }
1044
+ function removeInsertMenuMarkWhenOutsideOfSelection(editor) {
1045
+ var _Editor$marks;
1046
+ const path = findPathWithInsertMenu(editor, []);
1047
+ if (path && !((_Editor$marks = Editor.marks(editor)) !== null && _Editor$marks !== void 0 && _Editor$marks.insertMenu) && (!editor.selection || !Path.equals(editor.selection.anchor.path, path) || !Path.equals(editor.selection.focus.path, path))) {
1048
+ Transforms.unsetNodes(editor, 'insertMenu', {
1049
+ at: path
1050
+ });
1051
+ return true;
1052
+ }
1053
+ return false;
1054
+ }
1055
+ function withInsertMenu(editor) {
1056
+ const {
1057
+ normalizeNode,
1058
+ apply,
1059
+ insertText
1060
+ } = editor;
1061
+ editor.normalizeNode = ([node, path]) => {
1062
+ if (Text.isText(node) && node.insertMenu) {
1063
+ if (node.text[0] !== '/') {
1064
+ Transforms.unsetNodes(editor, 'insertMenu', {
1065
+ at: path
1066
+ });
1067
+ return;
1068
+ }
1069
+ const whitespaceMatch = /\s/.exec(node.text);
1070
+ if (whitespaceMatch) {
1071
+ Transforms.unsetNodes(editor, 'insertMenu', {
1072
+ at: {
1073
+ anchor: {
1074
+ path,
1075
+ offset: whitespaceMatch.index
1076
+ },
1077
+ focus: Editor.end(editor, path)
1078
+ },
1079
+ match: Text.isText,
1080
+ split: true
1081
+ });
1082
+ return;
1083
+ }
1084
+ }
1085
+ if (Editor.isEditor(editor) && removeInsertMenuMarkWhenOutsideOfSelection(editor)) {
1086
+ return;
1087
+ }
1088
+ normalizeNode([node, path]);
1089
+ };
1090
+ editor.apply = op => {
1091
+ apply(op);
1092
+ // we're calling this here AND in normalizeNode
1093
+ // because normalizeNode won't be called on selection changes
1094
+ // but apply will
1095
+ // we're still calling this from normalizeNode though because we want it to happen
1096
+ // when normalization happens
1097
+ if (op.type === 'set_selection') {
1098
+ removeInsertMenuMarkWhenOutsideOfSelection(editor);
1099
+ }
1100
+ };
1101
+ editor.insertText = text => {
1102
+ insertText(text);
1103
+ if (editor.selection && text === '/') {
1104
+ const startOfBlock = Editor.start(editor, Editor.above(editor, {
1105
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
1106
+ })[1]);
1107
+ const before = Editor.before(editor, editor.selection.anchor, {
1108
+ unit: 'character'
1109
+ });
1110
+ if (before && (Point.equals(startOfBlock, before) || before.offset !== 0 && /\s/.test(Node.get(editor, before.path).text[before.offset - 1]))) {
1111
+ Transforms.setNodes(editor, {
1112
+ insertMenu: true
1113
+ }, {
1114
+ at: {
1115
+ anchor: before,
1116
+ focus: editor.selection.anchor
1117
+ },
1118
+ match: Text.isText,
1119
+ split: true
1120
+ });
1121
+ }
1122
+ }
1123
+ };
1124
+ return editor;
1125
+ }
1126
+
1127
+ function withBlockMarkdownShortcuts(documentFeatures, componentBlocks, editor) {
1128
+ const {
1129
+ insertText
1130
+ } = editor;
1131
+ const shortcuts = Object.create(null);
1132
+ const editorDocumentFeaturesForNormalizationToCheck = {
1133
+ ...documentFeatures,
1134
+ relationships: true
1135
+ };
1136
+ const addShortcut = (text, insert, shouldBeEnabledInComponentBlock, type = 'paragraph') => {
1137
+ if (!shouldBeEnabledInComponentBlock(editorDocumentFeaturesForNormalizationToCheck)) return;
1138
+ const trigger = text[text.length - 1];
1139
+ if (!shortcuts[trigger]) {
1140
+ shortcuts[trigger] = Object.create(null);
1141
+ }
1142
+ shortcuts[trigger][text] = {
1143
+ insert,
1144
+ type,
1145
+ shouldBeEnabledInComponentBlock
1146
+ };
1147
+ };
1148
+ addShortcut('1. ', () => {
1149
+ Transforms.wrapNodes(editor, {
1150
+ type: 'ordered-list',
1151
+ children: []
1152
+ }, {
1153
+ match: n => Element.isElement(n) && Editor.isBlock(editor, n)
1154
+ });
1155
+ }, features => features.formatting.listTypes.ordered);
1156
+ addShortcut('- ', () => {
1157
+ Transforms.wrapNodes(editor, {
1158
+ type: 'unordered-list',
1159
+ children: []
1160
+ }, {
1161
+ match: n => Element.isElement(n) && Editor.isBlock(editor, n)
1162
+ });
1163
+ }, features => features.formatting.listTypes.unordered);
1164
+ addShortcut('* ', () => {
1165
+ Transforms.wrapNodes(editor, {
1166
+ type: 'unordered-list',
1167
+ children: []
1168
+ }, {
1169
+ match: n => Element.isElement(n) && Editor.isBlock(editor, n)
1170
+ });
1171
+ }, features => features.formatting.listTypes.unordered);
1172
+ documentFeatures.formatting.headingLevels.forEach(level => {
1173
+ addShortcut('#'.repeat(level) + ' ', () => {
1174
+ Transforms.setNodes(editor, {
1175
+ type: 'heading',
1176
+ level
1177
+ }, {
1178
+ match: node => node.type === 'paragraph' || node.type === 'heading'
1179
+ });
1180
+ }, features => features.formatting.headingLevels.includes(level), 'heading-or-paragraph');
1181
+ });
1182
+ addShortcut('> ', () => {
1183
+ Transforms.wrapNodes(editor, {
1184
+ type: 'blockquote',
1185
+ children: []
1186
+ }, {
1187
+ match: node => node.type === 'paragraph'
1188
+ });
1189
+ }, features => features.formatting.blockTypes.blockquote);
1190
+ addShortcut('```', () => {
1191
+ Transforms.wrapNodes(editor, {
1192
+ type: 'code',
1193
+ children: []
1194
+ }, {
1195
+ match: node => node.type === 'paragraph'
1196
+ });
1197
+ }, features => features.formatting.blockTypes.code);
1198
+ addShortcut('---', () => {
1199
+ insertDivider(editor);
1200
+ }, features => features.dividers);
1201
+ editor.insertText = text => {
1202
+ insertText(text);
1203
+ const shortcutsForTrigger = shortcuts[text];
1204
+ if (shortcutsForTrigger && editor.selection && Range.isCollapsed(editor.selection)) {
1205
+ const {
1206
+ anchor
1207
+ } = editor.selection;
1208
+ const block = Editor.above(editor, {
1209
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
1210
+ });
1211
+ if (!block || block[0].type !== 'paragraph' && block[0].type !== 'heading') return;
1212
+ const start = Editor.start(editor, block[1]);
1213
+ const range = {
1214
+ anchor,
1215
+ focus: start
1216
+ };
1217
+ const shortcutText = Editor.string(editor, range);
1218
+ const shortcut = shortcutsForTrigger[shortcutText];
1219
+ if (!shortcut || shortcut.type === 'paragraph' && block[0].type !== 'paragraph') {
1220
+ return;
1221
+ }
1222
+ const locationDocumentFeatures = getAncestorComponentChildFieldDocumentFeatures(editor, documentFeatures, componentBlocks);
1223
+ if (locationDocumentFeatures && (locationDocumentFeatures.kind === 'inline' || !shortcut.shouldBeEnabledInComponentBlock(locationDocumentFeatures.documentFeatures))) {
1224
+ return;
1225
+ }
1226
+ // so that this starts a new undo group
1227
+ editor.writeHistory('undos', {
1228
+ operations: [],
1229
+ selectionBefore: null
1230
+ });
1231
+ Transforms.select(editor, range);
1232
+ Transforms.delete(editor);
1233
+ shortcut.insert();
1234
+ }
1235
+ };
1236
+ return editor;
1237
+ }
1238
+
1239
+ // a v important note
1240
+ // marks in the markdown ast/html are represented quite differently to how they are in slate
1241
+ // if you had the markdown **something https://site.com something**
1242
+ // the bold node is the parent of the link node
1243
+ // but in slate, marks are only represented on text nodes
1244
+
1245
+ const currentlyActiveMarks = new Set();
1246
+ const currentlyDisabledMarks = new Set();
1247
+ let currentLink = null;
1248
+ function addMarkToChildren(mark, cb) {
1249
+ const wasPreviouslyActive = currentlyActiveMarks.has(mark);
1250
+ currentlyActiveMarks.add(mark);
1251
+ try {
1252
+ return cb();
1253
+ } finally {
1254
+ if (!wasPreviouslyActive) {
1255
+ currentlyActiveMarks.delete(mark);
1256
+ }
1257
+ }
1258
+ }
1259
+ function setLinkForChildren(href, cb) {
1260
+ // we'll only use the outer link
1261
+ if (currentLink !== null) {
1262
+ return cb();
1263
+ }
1264
+ currentLink = href;
1265
+ try {
1266
+ return cb();
1267
+ } finally {
1268
+ currentLink = null;
1269
+ }
1270
+ }
1271
+ function addMarksToChildren(marks, cb) {
1272
+ const marksToRemove = new Set();
1273
+ for (const mark of marks) {
1274
+ if (!currentlyActiveMarks.has(mark)) {
1275
+ marksToRemove.add(mark);
1276
+ }
1277
+ currentlyActiveMarks.add(mark);
1278
+ }
1279
+ try {
1280
+ return cb();
1281
+ } finally {
1282
+ for (const mark of marksToRemove) {
1283
+ currentlyActiveMarks.delete(mark);
1284
+ }
1285
+ }
1286
+ }
1287
+ function forceDisableMarkForChildren(mark, cb) {
1288
+ const wasPreviouslyDisabled = currentlyDisabledMarks.has(mark);
1289
+ currentlyDisabledMarks.add(mark);
1290
+ try {
1291
+ return cb();
1292
+ } finally {
1293
+ if (!wasPreviouslyDisabled) {
1294
+ currentlyDisabledMarks.delete(mark);
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ /**
1300
+ * This type is more strict than `Element & { type: 'link'; }` because `children`
1301
+ * is constrained to only contain Text nodes. This can't be assumed generally around the editor
1302
+ * (because of inline relationships or nested links(which are normalized away but the editor needs to not break if it happens))
1303
+ * but where this type is used, we're only going to allow links to contain Text and that's important
1304
+ * so that we know a block will never be inside an inline because Slate gets unhappy when that happens
1305
+ * (really the link inline should probably be a mark rather than an inline,
1306
+ * non-void inlines are probably always bad but that would imply changing the document
1307
+ * structure which would be such unnecessary breakage)
1308
+ */
1309
+
1310
+ // inline relationships are not here because we never create them from handling a paste from html or markdown
1311
+
1312
+ function getInlineNodes(text) {
1313
+ const node = {
1314
+ text
1315
+ };
1316
+ for (const mark of currentlyActiveMarks) {
1317
+ if (!currentlyDisabledMarks.has(mark)) {
1318
+ node[mark] = true;
1319
+ }
1320
+ }
1321
+ if (currentLink !== null) {
1322
+ return [{
1323
+ text: ''
1324
+ }, {
1325
+ type: 'link',
1326
+ href: currentLink,
1327
+ children: [node]
1328
+ }, {
1329
+ text: ''
1330
+ }];
1331
+ }
1332
+ return [node];
1333
+ }
1334
+
1335
+ // very loosely based on https://github.com/ianstormtaylor/slate/blob/d22c76ae1313fe82111317417912a2670e73f5c9/site/examples/paste-html.tsx
1336
+ function getAlignmentFromElement(element) {
1337
+ const parent = element.parentElement;
1338
+ // confluence
1339
+ const attribute = parent === null || parent === void 0 ? void 0 : parent.getAttribute('data-align');
1340
+ // note: we don't show html that confluence would parse as alignment
1341
+ // we could change that but meh
1342
+ // (they match on div.fabric-editor-block-mark with data-align)
1343
+ if (attribute === 'center' || attribute === 'end') {
1344
+ return attribute;
1345
+ }
1346
+ if (element instanceof HTMLElement) {
1347
+ // Google docs
1348
+ const textAlign = element.style.textAlign;
1349
+ if (textAlign === 'center') {
1350
+ return 'center';
1351
+ }
1352
+ // TODO: RTL things?
1353
+ if (textAlign === 'right' || textAlign === 'end') {
1354
+ return 'end';
1355
+ }
1356
+ }
1357
+ }
1358
+ const headings = {
1359
+ H1: 1,
1360
+ H2: 2,
1361
+ H3: 3,
1362
+ H4: 4,
1363
+ H5: 5,
1364
+ H6: 6
1365
+ };
1366
+ const TEXT_TAGS = {
1367
+ CODE: 'code',
1368
+ DEL: 'strikethrough',
1369
+ S: 'strikethrough',
1370
+ STRIKE: 'strikethrough',
1371
+ EM: 'italic',
1372
+ I: 'italic',
1373
+ STRONG: 'bold',
1374
+ U: 'underline',
1375
+ SUP: 'superscript',
1376
+ SUB: 'subscript',
1377
+ KBD: 'keyboard'
1378
+ };
1379
+ function marksFromElementAttributes(element) {
1380
+ const marks = new Set();
1381
+ const style = element.style;
1382
+ const {
1383
+ nodeName
1384
+ } = element;
1385
+ const markFromNodeName = TEXT_TAGS[nodeName];
1386
+ if (markFromNodeName) {
1387
+ marks.add(markFromNodeName);
1388
+ }
1389
+ const {
1390
+ fontWeight,
1391
+ textDecoration,
1392
+ verticalAlign
1393
+ } = style;
1394
+ if (textDecoration === 'underline') {
1395
+ marks.add('underline');
1396
+ } else if (textDecoration === 'line-through') {
1397
+ marks.add('strikethrough');
1398
+ }
1399
+ // confluence
1400
+ if (nodeName === 'SPAN' && element.classList.contains('code')) {
1401
+ marks.add('code');
1402
+ }
1403
+ // Google Docs does weird things with <b>
1404
+ if (nodeName === 'B' && fontWeight !== 'normal') {
1405
+ marks.add('bold');
1406
+ } else if (typeof fontWeight === 'string' && (fontWeight === 'bold' || fontWeight === 'bolder' || fontWeight === '1000' || /^[5-9]\d{2}$/.test(fontWeight))) {
1407
+ marks.add('bold');
1408
+ }
1409
+ if (style.fontStyle === 'italic') {
1410
+ marks.add('italic');
1411
+ }
1412
+ // Google Docs uses vertical align for subscript and superscript instead of <sup> and <sub>
1413
+ if (verticalAlign === 'super') {
1414
+ marks.add('superscript');
1415
+ } else if (verticalAlign === 'sub') {
1416
+ marks.add('subscript');
1417
+ }
1418
+ return marks;
1419
+ }
1420
+ function deserializeHTML(html) {
1421
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
1422
+ return fixNodesForBlockChildren(deserializeNodes(parsed.body.childNodes));
1423
+ }
1424
+ function deserializeHTMLNode(el) {
1425
+ if (!(el instanceof globalThis.HTMLElement)) {
1426
+ const text = el.textContent;
1427
+ if (!text) {
1428
+ return [];
1429
+ }
1430
+ return getInlineNodes(text);
1431
+ }
1432
+ if (el.nodeName === 'BR') {
1433
+ return getInlineNodes('\n');
1434
+ }
1435
+ if (el.nodeName === 'IMG') {
1436
+ const alt = el.getAttribute('alt');
1437
+ return getInlineNodes(alt !== null && alt !== void 0 ? alt : '');
1438
+ }
1439
+ if (el.nodeName === 'HR') {
1440
+ return [{
1441
+ type: 'divider',
1442
+ children: [{
1443
+ text: ''
1444
+ }]
1445
+ }];
1446
+ }
1447
+ const marks = marksFromElementAttributes(el);
1448
+
1449
+ // Dropbox Paper displays blockquotes as lists for some reason
1450
+ if (el.classList.contains('listtype-quote')) {
1451
+ marks.delete('italic');
1452
+ return addMarksToChildren(marks, () => [{
1453
+ type: 'blockquote',
1454
+ children: fixNodesForBlockChildren(deserializeNodes(el.childNodes))
1455
+ }]);
1456
+ }
1457
+ return addMarksToChildren(marks, () => {
1458
+ const {
1459
+ nodeName
1460
+ } = el;
1461
+ if (nodeName === 'A') {
1462
+ const href = el.getAttribute('href');
1463
+ if (href) {
1464
+ return setLinkForChildren(href, () => forceDisableMarkForChildren('underline', () => deserializeNodes(el.childNodes)));
1465
+ }
1466
+ }
1467
+ if (nodeName === 'PRE' && el.textContent) {
1468
+ return [{
1469
+ type: 'code',
1470
+ children: [{
1471
+ text: el.textContent || ''
1472
+ }]
1473
+ }];
1474
+ }
1475
+ const deserialized = deserializeNodes(el.childNodes);
1476
+ const children = fixNodesForBlockChildren(deserialized);
1477
+ if (nodeName === 'LI') {
1478
+ let nestedList;
1479
+ const listItemContent = {
1480
+ type: 'list-item-content',
1481
+ children: children.filter(node => {
1482
+ if (nestedList === undefined && (node.type === 'ordered-list' || node.type === 'unordered-list')) {
1483
+ nestedList = node;
1484
+ return false;
1485
+ }
1486
+ return true;
1487
+ })
1488
+ };
1489
+ const listItemChildren = nestedList ? [listItemContent, nestedList] : [listItemContent];
1490
+ return [{
1491
+ type: 'list-item',
1492
+ children: listItemChildren
1493
+ }];
1494
+ }
1495
+ if (nodeName === 'P') {
1496
+ return [{
1497
+ type: 'paragraph',
1498
+ textAlign: getAlignmentFromElement(el),
1499
+ children
1500
+ }];
1501
+ }
1502
+ const headingLevel = headings[nodeName];
1503
+ if (typeof headingLevel === 'number') {
1504
+ return [{
1505
+ type: 'heading',
1506
+ level: headingLevel,
1507
+ textAlign: getAlignmentFromElement(el),
1508
+ children
1509
+ }];
1510
+ }
1511
+ if (nodeName === 'BLOCKQUOTE') {
1512
+ return [{
1513
+ type: 'blockquote',
1514
+ children
1515
+ }];
1516
+ }
1517
+ if (nodeName === 'OL') {
1518
+ return [{
1519
+ type: 'ordered-list',
1520
+ children
1521
+ }];
1522
+ }
1523
+ if (nodeName === 'UL') {
1524
+ return [{
1525
+ type: 'unordered-list',
1526
+ children
1527
+ }];
1528
+ }
1529
+ if (nodeName === 'DIV' && !isBlock(children[0])) {
1530
+ return [{
1531
+ type: 'paragraph',
1532
+ children
1533
+ }];
1534
+ }
1535
+ return deserialized;
1536
+ });
1537
+ }
1538
+ function deserializeNodes(nodes) {
1539
+ const outputNodes = [];
1540
+ for (const node of nodes) {
1541
+ outputNodes.push(...deserializeHTMLNode(node));
1542
+ }
1543
+ return outputNodes;
1544
+ }
1545
+ function fixNodesForBlockChildren(deserializedNodes) {
1546
+ if (!deserializedNodes.length) {
1547
+ // Slate also gets unhappy if an element has no children
1548
+ // the empty text nodes will get normalized away if they're not needed
1549
+ return [{
1550
+ text: ''
1551
+ }];
1552
+ }
1553
+ if (deserializedNodes.some(isBlock)) {
1554
+ const result = [];
1555
+ let queuedInlines = [];
1556
+ const flushInlines = () => {
1557
+ if (queuedInlines.length) {
1558
+ result.push({
1559
+ type: 'paragraph',
1560
+ children: queuedInlines
1561
+ });
1562
+ queuedInlines = [];
1563
+ }
1564
+ };
1565
+ for (const node of deserializedNodes) {
1566
+ if (isBlock(node)) {
1567
+ flushInlines();
1568
+ result.push(node);
1569
+ continue;
1570
+ }
1571
+ // we want to ignore whitespace between block level elements
1572
+ // useful info about whitespace in html:
1573
+ // https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
1574
+ if (Node.string(node).trim() !== '') {
1575
+ queuedInlines.push(node);
1576
+ }
1577
+ }
1578
+ flushInlines();
1579
+ return result;
1580
+ }
1581
+ return deserializedNodes;
1582
+ }
1583
+
1584
+ const markdownConfig = {
1585
+ mdastExtensions: [autoLinkLiteralFromMarkdownExtension, gfmStrikethroughFromMarkdownExtension],
1586
+ extensions: [autoLinkLiteralMarkdownSyntax, gfmStrikethroughMarkdownSyntax()]
1587
+ };
1588
+ function deserializeMarkdown(markdown) {
1589
+ const root = mdASTUtilFromMarkdown(markdown, markdownConfig);
1590
+ let nodes = root.children;
1591
+ if (nodes.length === 1 && nodes[0].type === 'paragraph') {
1592
+ nodes = nodes[0].children;
1593
+ }
1594
+ return deserializeChildren(nodes, markdown);
1595
+ }
1596
+ function deserializeChildren(nodes, input) {
1597
+ const outputNodes = [];
1598
+ for (const node of nodes) {
1599
+ const result = deserializeMarkdownNode(node, input);
1600
+ if (result.length) {
1601
+ outputNodes.push(...result);
1602
+ }
1603
+ }
1604
+ if (!outputNodes.length) {
1605
+ outputNodes.push({
1606
+ text: ''
1607
+ });
1608
+ }
1609
+ return outputNodes;
1610
+ }
1611
+ function deserializeMarkdownNode(node, input) {
1612
+ switch (node.type) {
1613
+ case 'blockquote':
1614
+ return [{
1615
+ type: 'blockquote',
1616
+ children: deserializeChildren(node.children, input)
1617
+ }];
1618
+ case 'link':
1619
+ {
1620
+ // arguably this could just return a link node rather than use setLinkForChildren since the children _should_ only be inlines
1621
+ // but rather than relying on the markdown parser we use being correct in this way since it isn't nicely codified in types
1622
+ // let's be safe since we already have the code to do it the safer way because of html pasting
1623
+ return setLinkForChildren(node.url, () => deserializeChildren(node.children, input));
1624
+ }
1625
+ case 'code':
1626
+ return [{
1627
+ type: 'code',
1628
+ children: [{
1629
+ text: node.value
1630
+ }]
1631
+ }];
1632
+ case 'paragraph':
1633
+ return [{
1634
+ type: 'paragraph',
1635
+ children: deserializeChildren(node.children, input)
1636
+ }];
1637
+ case 'heading':
1638
+ {
1639
+ return [{
1640
+ type: 'heading',
1641
+ level: node.depth,
1642
+ children: deserializeChildren(node.children, input)
1643
+ }];
1644
+ }
1645
+ case 'list':
1646
+ {
1647
+ return [{
1648
+ type: node.ordered ? 'ordered-list' : 'unordered-list',
1649
+ children: deserializeChildren(node.children, input)
1650
+ }];
1651
+ }
1652
+ case 'listItem':
1653
+ return [{
1654
+ type: 'list-item',
1655
+ children: deserializeChildren(node.children, input)
1656
+ }];
1657
+ case 'thematicBreak':
1658
+ return [{
1659
+ type: 'divider',
1660
+ children: [{
1661
+ text: ''
1662
+ }]
1663
+ }];
1664
+ case 'break':
1665
+ return getInlineNodes('\n');
1666
+ case 'delete':
1667
+ return addMarkToChildren('strikethrough', () => deserializeChildren(node.children, input));
1668
+ case 'strong':
1669
+ return addMarkToChildren('bold', () => deserializeChildren(node.children, input));
1670
+ case 'emphasis':
1671
+ return addMarkToChildren('italic', () => deserializeChildren(node.children, input));
1672
+ case 'inlineCode':
1673
+ return addMarkToChildren('code', () => getInlineNodes(node.value));
1674
+ case 'text':
1675
+ return getInlineNodes(node.value);
1676
+ }
1677
+ return getInlineNodes(input.slice(node.position.start.offset, node.position.end.offset));
1678
+ }
1679
+
1680
+ const urlPattern = /https?:\/\//;
1681
+ function insertFragmentButDifferent(editor, nodes) {
1682
+ const firstNode = nodes[0];
1683
+ if (Element.isElement(firstNode) && Editor.isBlock(editor, firstNode)) {
1684
+ insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading(editor, nodes);
1685
+ } else {
1686
+ Transforms.insertFragment(editor, nodes);
1687
+ }
1688
+ }
1689
+ function withPasting(editor) {
1690
+ const {
1691
+ insertData,
1692
+ setFragmentData
1693
+ } = editor;
1694
+ editor.setFragmentData = data => {
1695
+ if (editor.selection) {
1696
+ data.setData('application/x-keystone-document-editor', 'true');
1697
+ }
1698
+ setFragmentData(data);
1699
+ };
1700
+ editor.insertData = data => {
1701
+ // this exists because behind the scenes, Slate sets the slate document
1702
+ // on the data transfer, this is great because it means when you copy and paste
1703
+ // something in the editor or between editors, it'll use the actual Slate data
1704
+ // rather than the serialized html so component blocks and etc. will work fine
1705
+ // we're setting application/x-keystone-document-editor
1706
+ // though so that we only accept slate data from Keystone's editor
1707
+ // because other editors will likely have a different structure
1708
+ // so we'll rely on the html deserialization instead
1709
+ // (note that yes, we do call insertData at the end of this function
1710
+ // which is where Slate's logic will run, it'll never do anything there though
1711
+ // since anything that will have slate data will also have text/html which we handle
1712
+ // before we call insertData)
1713
+ // TODO: handle the case of copying between editors with different components blocks
1714
+ // (right now, things will blow up in most cases)
1715
+ if (data.getData('application/x-keystone-document-editor') === 'true') {
1716
+ insertData(data);
1717
+ return;
1718
+ }
1719
+ const blockAbove = Editor.above(editor, {
1720
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node)
1721
+ });
1722
+ if ((blockAbove === null || blockAbove === void 0 ? void 0 : blockAbove[0].type) === 'code') {
1723
+ const plain = data.getData('text/plain');
1724
+ editor.insertText(plain);
1725
+ return;
1726
+ }
1727
+ const vsCodeEditorData = data.getData('vscode-editor-data');
1728
+ if (vsCodeEditorData) {
1729
+ try {
1730
+ const vsCodeData = JSON.parse(vsCodeEditorData);
1731
+ if ((vsCodeData === null || vsCodeData === void 0 ? void 0 : vsCodeData.mode) === 'markdown' || (vsCodeData === null || vsCodeData === void 0 ? void 0 : vsCodeData.mode) === 'mdx') {
1732
+ const plain = data.getData('text/plain');
1733
+ if (plain) {
1734
+ const fragment = deserializeMarkdown(plain);
1735
+ insertFragmentButDifferent(editor, fragment);
1736
+ return;
1737
+ }
1738
+ }
1739
+ } catch (err) {
1740
+ console.log(err);
1741
+ }
1742
+ }
1743
+ const plain = data.getData('text/plain');
1744
+ if (
1745
+ // isValidURL is a bit more permissive than a user might expect
1746
+ // so for pasting, we'll constrain it to starting with https:// or http://
1747
+ urlPattern.test(plain) && isValidURL(plain) && editor.selection && !Range.isCollapsed(editor.selection) &&
1748
+ // we only want to turn the selected text into a link if the selection is within the same block
1749
+ Editor.above(editor, {
1750
+ match: node => Element.isElement(node) && Editor.isBlock(editor, node) && !(Element.isElement(node.children[0]) && Editor.isBlock(editor, node.children[0]))
1751
+ }) &&
1752
+ // and there is only text(potentially with marks) in the selection
1753
+ // no other links or inline relationships
1754
+ Editor.nodes(editor, {
1755
+ match: node => Element.isElement(node) && Editor.isInline(editor, node)
1756
+ }).next().done) {
1757
+ Transforms.wrapNodes(editor, {
1758
+ type: 'link',
1759
+ href: plain,
1760
+ children: []
1761
+ }, {
1762
+ split: true
1763
+ });
1764
+ return;
1765
+ }
1766
+ const html = data.getData('text/html');
1767
+ if (html) {
1768
+ const fragment = deserializeHTML(html);
1769
+ insertFragmentButDifferent(editor, fragment);
1770
+ return;
1771
+ }
1772
+ if (plain) {
1773
+ const fragment = deserializeMarkdown(plain);
1774
+ insertFragmentButDifferent(editor, fragment);
1775
+ return;
1776
+ }
1777
+ insertData(data);
1778
+ };
1779
+ return editor;
1780
+ }
1781
+
1782
+ const blockquoteChildren = ['paragraph', 'code', 'heading', 'ordered-list', 'unordered-list', 'divider'];
1783
+ const paragraphLike = [...blockquoteChildren, 'blockquote'];
1784
+ const insideOfLayouts = [...paragraphLike, 'component-block'];
1785
+ const editorSchema = {
1786
+ editor: blockContainer({
1787
+ allowedChildren: [...insideOfLayouts, 'layout'],
1788
+ invalidPositionHandleMode: 'move'
1789
+ }),
1790
+ layout: blockContainer({
1791
+ allowedChildren: ['layout-area'],
1792
+ invalidPositionHandleMode: 'move'
1793
+ }),
1794
+ 'layout-area': blockContainer({
1795
+ allowedChildren: insideOfLayouts,
1796
+ invalidPositionHandleMode: 'unwrap'
1797
+ }),
1798
+ blockquote: blockContainer({
1799
+ allowedChildren: blockquoteChildren,
1800
+ invalidPositionHandleMode: 'move'
1801
+ }),
1802
+ paragraph: inlineContainer({
1803
+ invalidPositionHandleMode: 'unwrap'
1804
+ }),
1805
+ code: inlineContainer({
1806
+ invalidPositionHandleMode: 'move'
1807
+ }),
1808
+ divider: inlineContainer({
1809
+ invalidPositionHandleMode: 'move'
1810
+ }),
1811
+ heading: inlineContainer({
1812
+ invalidPositionHandleMode: 'unwrap'
1813
+ }),
1814
+ 'component-block': blockContainer({
1815
+ allowedChildren: ['component-block-prop', 'component-inline-prop'],
1816
+ invalidPositionHandleMode: 'move'
1817
+ }),
1818
+ 'component-inline-prop': inlineContainer({
1819
+ invalidPositionHandleMode: 'unwrap'
1820
+ }),
1821
+ 'component-block-prop': blockContainer({
1822
+ allowedChildren: insideOfLayouts,
1823
+ invalidPositionHandleMode: 'unwrap'
1824
+ }),
1825
+ 'ordered-list': blockContainer({
1826
+ allowedChildren: ['list-item'],
1827
+ invalidPositionHandleMode: 'move'
1828
+ }),
1829
+ 'unordered-list': blockContainer({
1830
+ allowedChildren: ['list-item'],
1831
+ invalidPositionHandleMode: 'move'
1832
+ }),
1833
+ 'list-item': blockContainer({
1834
+ allowedChildren: ['list-item-content', 'ordered-list', 'unordered-list'],
1835
+ invalidPositionHandleMode: 'unwrap'
1836
+ }),
1837
+ 'list-item-content': inlineContainer({
1838
+ invalidPositionHandleMode: 'unwrap'
1839
+ })
1840
+ };
1841
+ function inlineContainer(args) {
1842
+ return {
1843
+ kind: 'inlines',
1844
+ invalidPositionHandleMode: args.invalidPositionHandleMode
1845
+ };
1846
+ }
1847
+ const inlineContainerTypes = new Set(Object.entries(editorSchema).filter(([, value]) => value.kind === 'inlines').map(([type]) => type));
1848
+ function isInlineContainer(node) {
1849
+ return node.type !== undefined && inlineContainerTypes.has(node.type);
1850
+ }
1851
+ function createDocumentEditor(documentFeatures, componentBlocks, relationships, slate) {
1852
+ var _slate$withReact;
1853
+ return withPasting(withSoftBreaks(withBlocksSchema(withLink(documentFeatures, componentBlocks, withList(withHeading(withRelationship(withInsertMenu(withComponentBlocks(componentBlocks, documentFeatures, relationships, withParagraphs(withShortcuts(withDivider(withLayouts(withMarks(documentFeatures, componentBlocks, withCodeBlock(withBlockMarkdownShortcuts(documentFeatures, componentBlocks, withBlockquote(withDocumentFeaturesNormalization(documentFeatures, relationships, withHistory((_slate$withReact = slate === null || slate === void 0 ? void 0 : slate.withReact(createEditor())) !== null && _slate$withReact !== void 0 ? _slate$withReact : createEditor())))))))))))))))))));
1854
+ }
1855
+ function blockContainer(args) {
1856
+ return {
1857
+ kind: 'blocks',
1858
+ allowedChildren: new Set(args.allowedChildren),
1859
+ blockToWrapInlinesIn: args.allowedChildren[0],
1860
+ invalidPositionHandleMode: args.invalidPositionHandleMode
1861
+ };
1862
+ }
1863
+ const blockTypes = new Set(Object.keys(editorSchema).filter(x => x !== 'editor'));
1864
+ function isBlock(node) {
1865
+ return blockTypes.has(node.type);
1866
+ }
1867
+ function withBlocksSchema(editor) {
1868
+ const {
1869
+ normalizeNode
1870
+ } = editor;
1871
+ editor.normalizeNode = ([node, path]) => {
1872
+ if (!Text.isText(node) && node.type !== 'link' && node.type !== 'relationship') {
1873
+ const nodeType = Editor.isEditor(node) ? 'editor' : node.type;
1874
+ if (typeof nodeType !== 'string' || editorSchema[nodeType] === undefined) {
1875
+ Transforms.unwrapNodes(editor, {
1876
+ at: path
1877
+ });
1878
+ return;
1879
+ }
1880
+ const info = editorSchema[nodeType];
1881
+ if (info.kind === 'blocks' && node.children.length !== 0 && node.children.every(child => !(Element.isElement(child) && Editor.isBlock(editor, child)))) {
1882
+ Transforms.wrapNodes(editor, {
1883
+ type: info.blockToWrapInlinesIn,
1884
+ children: []
1885
+ }, {
1886
+ at: path,
1887
+ match: node => !(Element.isElement(node) && Editor.isBlock(editor, node))
1888
+ });
1889
+ return;
1890
+ }
1891
+ for (const [index, childNode] of node.children.entries()) {
1892
+ const childPath = [...path, index];
1893
+ if (info.kind === 'inlines') {
1894
+ if (!Text.isText(childNode) && !Editor.isInline(editor, childNode) &&
1895
+ // these checks are implicit in Editor.isBlock
1896
+ // but that isn't encoded in types so these will make TS happy
1897
+ childNode.type !== 'link' && childNode.type !== 'relationship') {
1898
+ handleNodeInInvalidPosition(editor, [childNode, childPath], path);
1899
+ return;
1900
+ }
1901
+ } else {
1902
+ if (!(Element.isElement(childNode) && Editor.isBlock(editor, childNode)) ||
1903
+ // these checks are implicit in Editor.isBlock
1904
+ // but that isn't encoded in types so these will make TS happy
1905
+ childNode.type === 'link' || childNode.type === 'relationship') {
1906
+ Transforms.wrapNodes(editor, {
1907
+ type: info.blockToWrapInlinesIn,
1908
+ children: []
1909
+ }, {
1910
+ at: childPath
1911
+ });
1912
+ return;
1913
+ }
1914
+ if (Element.isElement(childNode) && Editor.isBlock(editor, childNode) && !info.allowedChildren.has(childNode.type)) {
1915
+ handleNodeInInvalidPosition(editor, [childNode, childPath], path);
1916
+ return;
1917
+ }
1918
+ }
1919
+ }
1920
+ }
1921
+ normalizeNode([node, path]);
1922
+ };
1923
+ return editor;
1924
+ }
1925
+ function handleNodeInInvalidPosition(editor, [node, path], parentPath) {
1926
+ const nodeType = node.type;
1927
+ const childNodeInfo = editorSchema[nodeType];
1928
+ // the parent of a block will never be an inline so this casting is okay
1929
+ const parentNode = Node.get(editor, parentPath);
1930
+ const parentNodeType = Editor.isEditor(parentNode) ? 'editor' : parentNode.type;
1931
+ const parentNodeInfo = editorSchema[parentNodeType];
1932
+ if (!childNodeInfo || childNodeInfo.invalidPositionHandleMode === 'unwrap') {
1933
+ if (parentNodeInfo.kind === 'blocks' && parentNodeInfo.blockToWrapInlinesIn) {
1934
+ Transforms.setNodes(editor, {
1935
+ type: parentNodeInfo.blockToWrapInlinesIn,
1936
+ ...Object.fromEntries(Object.keys(node).filter(key => key !== 'type' && key !== 'children').map(key => [key, null])) // the Slate types don't understand that null is allowed and it will unset properties with setNodes
1937
+ }, {
1938
+ at: path
1939
+ });
1940
+ return;
1941
+ }
1942
+ Transforms.unwrapNodes(editor, {
1943
+ at: path
1944
+ });
1945
+ return;
1946
+ }
1947
+ const info = editorSchema[parentNode.type || 'editor'];
1948
+ if ((info === null || info === void 0 ? void 0 : info.kind) === 'blocks' && info.allowedChildren.has(nodeType)) {
1949
+ if (parentPath.length === 0) {
1950
+ Transforms.moveNodes(editor, {
1951
+ at: path,
1952
+ to: [path[0] + 1]
1953
+ });
1954
+ } else {
1955
+ Transforms.moveNodes(editor, {
1956
+ at: path,
1957
+ to: Path.next(parentPath)
1958
+ });
1959
+ }
1960
+ return;
1961
+ }
1962
+ if (Editor.isEditor(parentNode)) {
1963
+ Transforms.moveNodes(editor, {
1964
+ at: path,
1965
+ to: [path[0] + 1]
1966
+ });
1967
+ Transforms.unwrapNodes(editor, {
1968
+ at: [path[0] + 1]
1969
+ });
1970
+ return;
1971
+ }
1972
+ handleNodeInInvalidPosition(editor, [node, path], parentPath.slice(0, -1));
1973
+ }
1974
+
1975
+ // to print the editor schema in Graphviz if you want to visualize it
1976
+ // function printEditorSchema(editorSchema: EditorSchema) {
1977
+ // return `digraph G {
1978
+ // concentrate=true;
1979
+ // ${Object.keys(editorSchema)
1980
+ // .map(key => {
1981
+ // let val = editorSchema[key];
1982
+ // if (val.kind === 'inlines') {
1983
+ // return `"${key}" -> inlines`;
1984
+ // }
1985
+ // if (val.kind === 'blocks') {
1986
+ // return `"${key}" -> {${[...val.allowedChildren].map(x => JSON.stringify(x)).join(' ')}}`;
1987
+ // }
1988
+ // })
1989
+ // .join('\n ')}
1990
+ // }`;
1991
+ // }
1992
+
1993
+ export { insertDivider as a, createDocumentEditor as c, insertBlockquote as i, wrapLink as w };