@lofcz/platejs-core 52.3.4

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.
@@ -0,0 +1,2823 @@
1
+ import { ElementApi, IS_FIREFOX, NodeApi, OperationApi, PathApi, PointApi, RangeApi, TextApi, assignLegacyApi, assignLegacyTransforms, combineMatchOptions, createEditor, queryNode, syncLegacyMethods, withHistory } from "@platejs/slate";
2
+ import { nanoid } from "nanoid";
3
+ import { bindFirst, isDefined } from "@udecode/utils";
4
+ import merge from "lodash/merge.js";
5
+ import { createVanillaStore } from "zustand-x/vanilla";
6
+ import mergeWith from "lodash/mergeWith.js";
7
+ import defaults from "lodash/defaults.js";
8
+ import kebabCase from "lodash/kebabCase.js";
9
+ import pick from "lodash/pick.js";
10
+ import castArray from "lodash/castArray.js";
11
+ import { Path as Path$1 } from "slate";
12
+ import isEqual from "lodash/isEqual.js";
13
+ import isUndefined from "lodash/isUndefined.js";
14
+ import omitBy from "lodash/omitBy.js";
15
+ import { jsx } from "slate-hyperscript";
16
+ import cloneDeep from "lodash/cloneDeep.js";
17
+
18
+ //#region src/internal/utils/isFunction.ts
19
+ function isFunction(value) {
20
+ return typeof value === "function";
21
+ }
22
+
23
+ //#endregion
24
+ //#region src/internal/utils/mergePlugins.ts
25
+ function mergePlugins(basePlugin, ...sourcePlugins) {
26
+ return mergeWith({}, basePlugin, ...sourcePlugins, (objValue, srcValue, key) => {
27
+ if (Array.isArray(srcValue)) return srcValue;
28
+ if (key === "options") return {
29
+ ...objValue,
30
+ ...srcValue
31
+ };
32
+ });
33
+ }
34
+
35
+ //#endregion
36
+ //#region src/lib/plugin/createSlatePlugin.ts
37
+ /**
38
+ * Creates a new Plate plugin with the given configuration.
39
+ *
40
+ * @remarks
41
+ * - The plugin's key is required and specified by the K generic.
42
+ * - The `__extensions` array stores functions to be applied when `resolvePlugin`
43
+ * is called with an editor.
44
+ * - The `extend` method adds new extensions to be applied later.
45
+ * - The `extendPlugin` method extends an existing plugin (including nested
46
+ * plugins) or adds a new one if not found.
47
+ *
48
+ * @example
49
+ * const myPlugin = createSlatePlugin<
50
+ * 'myPlugin',
51
+ * MyOptions,
52
+ * MyApi,
53
+ * MyTransforms
54
+ * >({
55
+ * key: 'myPlugin',
56
+ * options: { someOption: true },
57
+ * transforms: { someTransform: () => {} },
58
+ * });
59
+ *
60
+ * const extendedPlugin = myPlugin.extend({
61
+ * options: { anotherOption: false },
62
+ * });
63
+ *
64
+ * const pluginWithNestedExtension = extendedPlugin.extendPlugin(
65
+ * nestedPlugin,
66
+ * { options: { nestedOption: true } }
67
+ * );
68
+ *
69
+ * @template K - The literal type of the plugin key.
70
+ * @template O - The type of the plugin options.
71
+ * @template A - The type of the plugin utilities.
72
+ * @template T - The type of the plugin transforms.
73
+ * @template S - The type of the plugin storage.
74
+ * @param {Partial<SlatePlugin<K, O, A, T, S>>} config - The configuration
75
+ * object for the plugin.
76
+ * @returns {SlatePlugin<K, O, A, T, S>} A new Plate plugin instance with the
77
+ * following properties and methods:
78
+ *
79
+ * - All properties from the input config, merged with default values.
80
+ * - `configure`: A method to create a new plugin instance with updated options.
81
+ * - `extend`: A method to create a new plugin instance with additional
82
+ * configuration.
83
+ * - `extendPlugin`: A method to extend an existing plugin (including nested
84
+ * plugins) or add a new one if not found.
85
+ */
86
+ function createSlatePlugin(config = {}) {
87
+ let baseConfig;
88
+ let initialExtension;
89
+ if (isFunction(config)) {
90
+ baseConfig = { key: "" };
91
+ initialExtension = (editor) => config(editor);
92
+ } else baseConfig = config;
93
+ const key = baseConfig.key ?? "";
94
+ const plugin = mergePlugins({
95
+ key,
96
+ __apiExtensions: [],
97
+ __configuration: null,
98
+ __extensions: initialExtension ? [initialExtension] : [],
99
+ __selectorExtensions: [],
100
+ api: {},
101
+ dependencies: [],
102
+ editor: {},
103
+ handlers: {},
104
+ inject: {},
105
+ node: { type: key },
106
+ options: {},
107
+ override: {},
108
+ parser: {},
109
+ parsers: {},
110
+ plugins: [],
111
+ priority: 100,
112
+ render: {},
113
+ rules: {},
114
+ shortcuts: {},
115
+ transforms: {}
116
+ }, config);
117
+ if (plugin.node.isLeaf && !isDefined(plugin.node.isDecoration)) plugin.node.isDecoration = true;
118
+ plugin.configure = (config$1) => {
119
+ const newPlugin = { ...plugin };
120
+ newPlugin.__configuration = (ctx) => isFunction(config$1) ? config$1(ctx) : config$1;
121
+ return createSlatePlugin(newPlugin);
122
+ };
123
+ plugin.configurePlugin = (p, config$1) => {
124
+ const newPlugin = { ...plugin };
125
+ const configureNestedPlugin = (plugins) => {
126
+ let found = false;
127
+ const updatedPlugins = plugins.map((nestedPlugin) => {
128
+ if (nestedPlugin.key === p.key) {
129
+ found = true;
130
+ return createSlatePlugin({
131
+ ...nestedPlugin,
132
+ __configuration: (ctx) => isFunction(config$1) ? config$1(ctx) : config$1
133
+ });
134
+ }
135
+ if (nestedPlugin.plugins && nestedPlugin.plugins.length > 0) {
136
+ const result = configureNestedPlugin(nestedPlugin.plugins);
137
+ if (result.found) {
138
+ found = true;
139
+ return {
140
+ ...nestedPlugin,
141
+ plugins: result.plugins
142
+ };
143
+ }
144
+ }
145
+ return nestedPlugin;
146
+ });
147
+ return {
148
+ found,
149
+ plugins: updatedPlugins
150
+ };
151
+ };
152
+ newPlugin.plugins = configureNestedPlugin(newPlugin.plugins).plugins;
153
+ return createSlatePlugin(newPlugin);
154
+ };
155
+ plugin.extendEditorApi = (extension) => {
156
+ const newPlugin = { ...plugin };
157
+ newPlugin.__apiExtensions = [...newPlugin.__apiExtensions, {
158
+ extension,
159
+ isPluginSpecific: false
160
+ }];
161
+ return createSlatePlugin(newPlugin);
162
+ };
163
+ plugin.extendSelectors = (extension) => {
164
+ const newPlugin = { ...plugin };
165
+ newPlugin.__selectorExtensions = [...newPlugin.__selectorExtensions, extension];
166
+ return createSlatePlugin(newPlugin);
167
+ };
168
+ plugin.extendApi = (extension) => {
169
+ const newPlugin = { ...plugin };
170
+ newPlugin.__apiExtensions = [...newPlugin.__apiExtensions, {
171
+ extension,
172
+ isPluginSpecific: true
173
+ }];
174
+ return createSlatePlugin(newPlugin);
175
+ };
176
+ plugin.extendEditorTransforms = (extension) => {
177
+ const newPlugin = { ...plugin };
178
+ newPlugin.__apiExtensions = [...newPlugin.__apiExtensions, {
179
+ extension,
180
+ isPluginSpecific: false,
181
+ isTransform: true
182
+ }];
183
+ return createSlatePlugin(newPlugin);
184
+ };
185
+ plugin.extendTransforms = (extension) => {
186
+ const newPlugin = { ...plugin };
187
+ newPlugin.__apiExtensions = [...newPlugin.__apiExtensions, {
188
+ extension,
189
+ isPluginSpecific: true,
190
+ isTransform: true
191
+ }];
192
+ return createSlatePlugin(newPlugin);
193
+ };
194
+ plugin.overrideEditor = (extension) => {
195
+ const newPlugin = { ...plugin };
196
+ newPlugin.__apiExtensions = [...newPlugin.__apiExtensions, {
197
+ extension,
198
+ isOverride: true,
199
+ isPluginSpecific: false,
200
+ isTransform: true
201
+ }];
202
+ return createSlatePlugin(newPlugin);
203
+ };
204
+ plugin.extend = (extendConfig) => {
205
+ let newPlugin = { ...plugin };
206
+ if (isFunction(extendConfig)) newPlugin.__extensions = [...newPlugin.__extensions, extendConfig];
207
+ else newPlugin = mergePlugins(newPlugin, extendConfig);
208
+ return createSlatePlugin(newPlugin);
209
+ };
210
+ plugin.clone = () => mergePlugins(plugin);
211
+ plugin.extendPlugin = (p, extendConfig) => {
212
+ const newPlugin = { ...plugin };
213
+ const extendNestedPlugin = (plugins) => {
214
+ let found = false;
215
+ const updatedPlugins = plugins.map((nestedPlugin) => {
216
+ if (nestedPlugin.key === p.key) {
217
+ found = true;
218
+ return createSlatePlugin({
219
+ ...nestedPlugin,
220
+ __extensions: [...nestedPlugin.__extensions, (ctx) => isFunction(extendConfig) ? extendConfig(ctx) : extendConfig]
221
+ });
222
+ }
223
+ if (nestedPlugin.plugins && nestedPlugin.plugins.length > 0) {
224
+ const result$1 = extendNestedPlugin(nestedPlugin.plugins);
225
+ if (result$1.found) {
226
+ found = true;
227
+ return {
228
+ ...nestedPlugin,
229
+ plugins: result$1.plugins
230
+ };
231
+ }
232
+ }
233
+ return nestedPlugin;
234
+ });
235
+ return {
236
+ found,
237
+ plugins: updatedPlugins
238
+ };
239
+ };
240
+ const result = extendNestedPlugin(newPlugin.plugins);
241
+ newPlugin.plugins = result.plugins;
242
+ if (!result.found) newPlugin.plugins.push(createSlatePlugin({
243
+ key: p.key,
244
+ __extensions: [(ctx) => isFunction(extendConfig) ? extendConfig(ctx) : extendConfig]
245
+ }));
246
+ return createSlatePlugin(newPlugin);
247
+ };
248
+ plugin.withComponent = (component) => plugin.extend({
249
+ node: { component },
250
+ render: { node: component }
251
+ });
252
+ return plugin;
253
+ }
254
+ /**
255
+ * Explicitly typed version of `createSlatePlugin`.
256
+ *
257
+ * @remarks
258
+ * While `createSlatePlugin` uses type inference, this function requires an
259
+ * explicit type parameter. Use this when you need precise control over the
260
+ * plugin's type structure or when type inference doesn't provide the desired
261
+ * result.
262
+ */
263
+ function createTSlatePlugin(config = {}) {
264
+ return createSlatePlugin(config);
265
+ }
266
+
267
+ //#endregion
268
+ //#region src/lib/plugin/getEditorPlugin.ts
269
+ function getEditorPlugin(editor, p) {
270
+ const plugin = editor.getPlugin(p);
271
+ return {
272
+ api: editor.api,
273
+ editor,
274
+ plugin,
275
+ setOption: ((keyOrOptions, value) => editor.setOption(plugin, keyOrOptions, value)),
276
+ setOptions: ((options) => editor.setOptions(plugin, options)),
277
+ tf: editor.transforms,
278
+ type: plugin.node.type,
279
+ getOption: (key, ...args) => editor.getOption(plugin, key, ...args),
280
+ getOptions: () => editor.getOptions(plugin)
281
+ };
282
+ }
283
+
284
+ //#endregion
285
+ //#region src/internal/plugin/resolvePlugin.ts
286
+ /**
287
+ * Resolves and finalizes a plugin configuration for use in a Plate editor.
288
+ *
289
+ * This function processes a given plugin configuration, applying any extensions
290
+ * and resolving nested plugins. It prepares the plugin for integration into the
291
+ * Plate editor system by:
292
+ *
293
+ * 1. Cloning the plugin to avoid mutating the original
294
+ * 2. Applying all stored extensions to the plugin
295
+ * 3. Clearing the extensions array after application
296
+ *
297
+ * @example
298
+ * const plugin = createSlatePlugin({ key: 'myPlugin', ...otherOptions }).extend(...);
299
+ * const resolvedPlugin = resolvePlugin(editor, plugin);
300
+ */
301
+ const resolvePlugin = (editor, _plugin) => {
302
+ let plugin = mergePlugins({}, _plugin);
303
+ plugin.__resolved = true;
304
+ if (plugin.__configuration) {
305
+ const configResult = plugin.__configuration(getEditorPlugin(editor, plugin));
306
+ plugin = mergePlugins(plugin, configResult);
307
+ plugin.__configuration = void 0;
308
+ }
309
+ if (plugin.__extensions && plugin.__extensions.length > 0) {
310
+ for (const extension of plugin.__extensions) plugin = mergePlugins(plugin, extension(getEditorPlugin(editor, plugin)));
311
+ plugin.__extensions = [];
312
+ }
313
+ const targetPluginToInject = plugin.inject?.targetPluginToInject;
314
+ const targetPlugins = plugin.inject?.targetPlugins;
315
+ if (targetPluginToInject && targetPlugins && targetPlugins.length > 0) {
316
+ plugin.inject = plugin.inject || {};
317
+ plugin.inject.plugins = merge({}, plugin.inject.plugins, Object.fromEntries(targetPlugins.map((targetPlugin) => {
318
+ return [targetPlugin, targetPluginToInject({
319
+ ...getEditorPlugin(editor, plugin),
320
+ targetPlugin
321
+ })];
322
+ })));
323
+ }
324
+ if (plugin.node?.component) plugin.render.node = plugin.node.component;
325
+ if (plugin.render?.node) plugin.node.component = plugin.render.node;
326
+ validatePlugin(editor, plugin);
327
+ return plugin;
328
+ };
329
+ const validatePlugin = (editor, plugin) => {
330
+ if (!plugin.__extensions) editor.api.debug.error(`Invalid plugin '${plugin.key}', you should use createSlatePlugin.`, "USE_CREATE_PLUGIN");
331
+ if (plugin.node.isElement && plugin.node.isLeaf) editor.api.debug.error(`Plugin ${plugin.key} cannot be both an element and a leaf.`, "PLUGIN_NODE_TYPE");
332
+ };
333
+
334
+ //#endregion
335
+ //#region src/lib/plugin/getSlatePlugin.ts
336
+ /** Get editor plugin by key or plugin object. */
337
+ function getSlatePlugin(editor, p) {
338
+ let plugin = p;
339
+ const editorPlugin = editor.plugins[p.key];
340
+ if (!editorPlugin) {
341
+ if (!plugin.node) plugin = createSlatePlugin(plugin);
342
+ return plugin.__resolved ? plugin : resolvePlugin(editor, plugin);
343
+ }
344
+ return editorPlugin;
345
+ }
346
+ /** Get editor plugin type by key or plugin object. */
347
+ function getPluginType(editor, key) {
348
+ const p = editor.getPlugin({ key });
349
+ return p.node.type ?? p.key ?? "";
350
+ }
351
+ /** Get editor plugin types by key. */
352
+ const getPluginTypes = (editor, keys) => keys.map((key) => editor.getType(key));
353
+ const getPluginKey = (editor, type) => editor.meta.pluginCache.node.types[type];
354
+ const getPluginKeys = (editor, types) => types.map((type) => {
355
+ return getPluginKey(editor, type) ?? type;
356
+ }).filter(Boolean);
357
+ const getPluginByType = (editor, type) => {
358
+ const key = getPluginKey(editor, type);
359
+ if (!key) return null;
360
+ return editor.getPlugin({ key });
361
+ };
362
+ const getContainerTypes = (editor) => getPluginTypes(editor, editor.meta.pluginCache.node.isContainer);
363
+
364
+ //#endregion
365
+ //#region src/internal/plugin/resolvePlugins.ts
366
+ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore) => {
367
+ editor.plugins = {};
368
+ editor.meta.pluginList = [];
369
+ editor.meta.shortcuts = {};
370
+ editor.meta.components = {};
371
+ editor.meta.pluginCache = {
372
+ decorate: [],
373
+ handlers: {
374
+ onChange: [],
375
+ onNodeChange: [],
376
+ onTextChange: []
377
+ },
378
+ inject: { nodeProps: [] },
379
+ node: {
380
+ isContainer: [],
381
+ isLeaf: [],
382
+ isText: [],
383
+ leafProps: [],
384
+ textProps: [],
385
+ types: {}
386
+ },
387
+ normalizeInitialValue: [],
388
+ render: {
389
+ aboveEditable: [],
390
+ aboveNodes: [],
391
+ aboveSlate: [],
392
+ afterContainer: [],
393
+ afterEditable: [],
394
+ beforeContainer: [],
395
+ beforeEditable: [],
396
+ belowNodes: [],
397
+ belowRootNodes: []
398
+ },
399
+ rules: { match: [] },
400
+ useHooks: []
401
+ };
402
+ const resolvedPlugins = resolveAndSortPlugins(editor, plugins);
403
+ applyPluginsToEditor(editor, resolvedPlugins);
404
+ resolvePluginOverrides(editor);
405
+ resolvePluginStores(editor, createStore);
406
+ editor.meta.pluginList.forEach((plugin) => {
407
+ if (plugin.extendEditor) {
408
+ editor = plugin.extendEditor(getEditorPlugin(editor, plugin));
409
+ syncLegacyMethods(editor);
410
+ }
411
+ resolvePluginMethods(editor, plugin);
412
+ if (plugin.node?.isContainer) editor.meta.pluginCache.node.isContainer.push(plugin.key);
413
+ editor.meta.pluginCache.node.types[plugin.node.type] = plugin.key;
414
+ if (plugin.inject?.nodeProps) editor.meta.pluginCache.inject.nodeProps.push(plugin.key);
415
+ if (plugin.render?.node) editor.meta.components[plugin.key] = plugin.render.node;
416
+ if (plugin.node?.isLeaf && (plugin.node?.isDecoration === true || plugin.render.leaf)) editor.meta.pluginCache.node.isLeaf.push(plugin.key);
417
+ if (plugin.node.isLeaf && plugin.node.isDecoration === false) editor.meta.pluginCache.node.isText.push(plugin.key);
418
+ if (plugin.node?.leafProps) editor.meta.pluginCache.node.leafProps.push(plugin.key);
419
+ if (plugin.node.textProps) editor.meta.pluginCache.node.textProps.push(plugin.key);
420
+ if (plugin.render.aboveEditable) editor.meta.pluginCache.render.aboveEditable.push(plugin.key);
421
+ if (plugin.render.aboveSlate) editor.meta.pluginCache.render.aboveSlate.push(plugin.key);
422
+ if (plugin.render.afterEditable) editor.meta.pluginCache.render.afterEditable.push(plugin.key);
423
+ if (plugin.render.beforeEditable) editor.meta.pluginCache.render.beforeEditable.push(plugin.key);
424
+ if (plugin.rules?.match) editor.meta.pluginCache.rules.match.push(plugin.key);
425
+ if (plugin.render.afterContainer) editor.meta.pluginCache.render.afterContainer.push(plugin.key);
426
+ if (plugin.render.beforeContainer) editor.meta.pluginCache.render.beforeContainer.push(plugin.key);
427
+ if (plugin.render.belowRootNodes) editor.meta.pluginCache.render.belowRootNodes.push(plugin.key);
428
+ if (plugin.normalizeInitialValue) editor.meta.pluginCache.normalizeInitialValue.push(plugin.key);
429
+ if (plugin.decorate) editor.meta.pluginCache.decorate.push(plugin.key);
430
+ if (plugin.render.aboveNodes) editor.meta.pluginCache.render.aboveNodes.push(plugin.key);
431
+ if (plugin.render.belowNodes) editor.meta.pluginCache.render.belowNodes.push(plugin.key);
432
+ if (plugin.useHooks) editor.meta.pluginCache.useHooks.push(plugin.key);
433
+ if (plugin.handlers?.onChange) editor.meta.pluginCache.handlers.onChange.push(plugin.key);
434
+ if (plugin.handlers?.onNodeChange) editor.meta.pluginCache.handlers.onNodeChange.push(plugin.key);
435
+ if (plugin.handlers?.onTextChange) editor.meta.pluginCache.handlers.onTextChange.push(plugin.key);
436
+ });
437
+ resolvePluginShortcuts(editor);
438
+ return editor;
439
+ };
440
+ const resolvePluginStores = (editor, createStore) => {
441
+ editor.meta.pluginList.forEach((plugin) => {
442
+ let store = createStore(plugin.options, {
443
+ mutative: true,
444
+ name: plugin.key
445
+ });
446
+ if (plugin.__selectorExtensions && plugin.__selectorExtensions.length > 0) plugin.__selectorExtensions.forEach((extension) => {
447
+ const extendedOptions = extension(getEditorPlugin(editor, plugin));
448
+ store = store.extendSelectors(() => extendedOptions);
449
+ });
450
+ plugin.optionsStore = store;
451
+ });
452
+ };
453
+ const resolvePluginMethods = (editor, plugin) => {
454
+ Object.entries(plugin.api).forEach(([apiKey, apiFunction]) => {
455
+ editor.api[apiKey] = apiFunction;
456
+ });
457
+ if (plugin.__apiExtensions && plugin.__apiExtensions.length > 0) {
458
+ plugin.__apiExtensions.forEach(({ extension, isOverride, isPluginSpecific, isTransform }) => {
459
+ const newExtensions = extension(getEditorPlugin(editor, plugin));
460
+ if (isOverride) {
461
+ if (newExtensions.api) {
462
+ merge(editor.api, newExtensions.api);
463
+ merge(plugin.api, newExtensions.api);
464
+ assignLegacyApi(editor, editor.api);
465
+ }
466
+ if (newExtensions.transforms) {
467
+ merge(editor.transforms, newExtensions.transforms);
468
+ merge(plugin.transforms, newExtensions.transforms);
469
+ assignLegacyTransforms(editor, newExtensions.transforms);
470
+ }
471
+ } else if (isTransform) if (isPluginSpecific) {
472
+ if (!editor.transforms[plugin.key]) editor.transforms[plugin.key] = {};
473
+ if (!plugin.transforms[plugin.key]) plugin.transforms[plugin.key] = {};
474
+ merge(editor.transforms[plugin.key], newExtensions);
475
+ merge(plugin.transforms[plugin.key], newExtensions);
476
+ } else {
477
+ merge(editor.transforms, newExtensions);
478
+ merge(plugin.transforms, newExtensions);
479
+ assignLegacyTransforms(editor, newExtensions);
480
+ }
481
+ else if (isPluginSpecific) {
482
+ if (!editor.api[plugin.key]) editor.api[plugin.key] = {};
483
+ if (!plugin.api[plugin.key]) plugin.api[plugin.key] = {};
484
+ merge(editor.api[plugin.key], newExtensions);
485
+ merge(plugin.api[plugin.key], newExtensions);
486
+ } else {
487
+ merge(editor.api, newExtensions);
488
+ merge(plugin.api, newExtensions);
489
+ assignLegacyApi(editor, editor.api);
490
+ }
491
+ });
492
+ plugin.__apiExtensions = void 0;
493
+ }
494
+ };
495
+ const resolvePluginShortcuts = (editor) => {
496
+ editor.meta.shortcuts = {};
497
+ editor.meta.pluginList.forEach((plugin) => {
498
+ Object.entries(plugin.shortcuts).forEach(([originalKey, hotkey]) => {
499
+ const namespacedKey = `${plugin.key}.${originalKey}`;
500
+ if (hotkey === null) delete editor.meta.shortcuts[namespacedKey];
501
+ else if (hotkey && typeof hotkey === "object") {
502
+ const resolvedHotkey = { ...hotkey };
503
+ if (!resolvedHotkey.handler) {
504
+ const pluginSpecificTransforms = plugin.transforms?.[plugin.key];
505
+ const pluginSpecificApi = plugin.api?.[plugin.key];
506
+ if (pluginSpecificTransforms?.[originalKey]) resolvedHotkey.handler = () => pluginSpecificTransforms[originalKey]();
507
+ else if (pluginSpecificApi?.[originalKey]) resolvedHotkey.handler = () => pluginSpecificApi[originalKey]();
508
+ }
509
+ resolvedHotkey.priority = resolvedHotkey.priority ?? plugin.priority;
510
+ editor.meta.shortcuts[namespacedKey] = resolvedHotkey;
511
+ }
512
+ });
513
+ });
514
+ };
515
+ const flattenAndResolvePlugins = (editor, plugins) => {
516
+ const pluginMap = /* @__PURE__ */ new Map();
517
+ const processPlugin = (plugin) => {
518
+ const resolvedPlugin = resolvePlugin(editor, plugin);
519
+ if (resolvedPlugin.key) {
520
+ const existingPlugin = pluginMap.get(resolvedPlugin.key);
521
+ if (existingPlugin) pluginMap.set(resolvedPlugin.key, mergePlugins(existingPlugin, resolvedPlugin));
522
+ else pluginMap.set(resolvedPlugin.key, resolvedPlugin);
523
+ }
524
+ if (resolvedPlugin.plugins && resolvedPlugin.plugins.length > 0) resolvedPlugin.plugins.forEach(processPlugin);
525
+ };
526
+ plugins.forEach(processPlugin);
527
+ return pluginMap;
528
+ };
529
+ const resolveAndSortPlugins = (editor, plugins) => {
530
+ const pluginMap = flattenAndResolvePlugins(editor, plugins);
531
+ const enabledPlugins = Array.from(pluginMap.values()).filter((plugin) => plugin.enabled !== false);
532
+ enabledPlugins.sort((a, b) => b.priority - a.priority);
533
+ const orderedPlugins = [];
534
+ const visited = /* @__PURE__ */ new Set();
535
+ const visit = (plugin) => {
536
+ if (visited.has(plugin.key)) return;
537
+ visited.add(plugin.key);
538
+ plugin.dependencies?.forEach((depKey) => {
539
+ const depPlugin = pluginMap.get(depKey);
540
+ if (depPlugin) visit(depPlugin);
541
+ else editor.api.debug.warn(`Plugin "${plugin.key}" depends on missing plugin "${depKey}"`, "PLUGIN_DEPENDENCY_MISSING");
542
+ });
543
+ orderedPlugins.push(plugin);
544
+ };
545
+ enabledPlugins.forEach(visit);
546
+ return orderedPlugins;
547
+ };
548
+ const applyPluginsToEditor = (editor, plugins) => {
549
+ editor.meta.pluginList = plugins;
550
+ editor.plugins = Object.fromEntries(plugins.map((plugin) => [plugin.key, plugin]));
551
+ };
552
+ const resolvePluginOverrides = (editor) => {
553
+ const applyOverrides = (plugins) => {
554
+ let overriddenPlugins = [...plugins];
555
+ const enabledOverrides = {};
556
+ const componentOverrides = {};
557
+ const pluginOverrides = {};
558
+ for (const plugin of plugins) {
559
+ if (plugin.override.enabled) Object.assign(enabledOverrides, plugin.override.enabled);
560
+ if (plugin.override.components) Object.entries(plugin.override.components).forEach(([key, component]) => {
561
+ if (!componentOverrides[key] || plugin.priority > componentOverrides[key].priority) componentOverrides[key] = {
562
+ component,
563
+ priority: plugin.priority
564
+ };
565
+ });
566
+ if (plugin.override.plugins) Object.entries(plugin.override.plugins).forEach(([key, value]) => {
567
+ pluginOverrides[key] = mergePlugins(pluginOverrides[key], value);
568
+ if (value.enabled !== void 0) enabledOverrides[key] = value.enabled;
569
+ });
570
+ }
571
+ overriddenPlugins = overriddenPlugins.map((p) => {
572
+ let updatedPlugin = { ...p };
573
+ if (pluginOverrides[p.key]) updatedPlugin = mergePlugins(updatedPlugin, pluginOverrides[p.key]);
574
+ if (componentOverrides[p.key] && (!p.render.node && !p.node.component || componentOverrides[p.key].priority > p.priority)) {
575
+ updatedPlugin.render.node = componentOverrides[p.key].component;
576
+ updatedPlugin.node.component = componentOverrides[p.key].component;
577
+ }
578
+ const enabled = enabledOverrides[p.key] ?? updatedPlugin.enabled;
579
+ if (isDefined(enabled)) updatedPlugin.enabled = enabled;
580
+ return updatedPlugin;
581
+ });
582
+ return overriddenPlugins.filter((p) => p.enabled !== false).map((plugin) => ({
583
+ ...plugin,
584
+ plugins: applyOverrides(plugin.plugins || [])
585
+ }));
586
+ };
587
+ editor.meta.pluginList = applyOverrides(editor.meta.pluginList);
588
+ editor.plugins = Object.fromEntries(editor.meta.pluginList.map((plugin) => [plugin.key, plugin]));
589
+ };
590
+
591
+ //#endregion
592
+ //#region src/lib/plugins/AstPlugin.ts
593
+ /**
594
+ * Enables support for deserializing inserted content from Slate Ast format to
595
+ * Slate format while apply a small bug fix.
596
+ */
597
+ const AstPlugin = createSlatePlugin({
598
+ key: "ast",
599
+ parser: {
600
+ format: "application/x-slate-fragment",
601
+ deserialize: ({ data }) => {
602
+ const decoded = decodeURIComponent(window.atob(data));
603
+ let parsed;
604
+ try {
605
+ parsed = JSON.parse(decoded);
606
+ } catch {}
607
+ return parsed;
608
+ }
609
+ }
610
+ });
611
+
612
+ //#endregion
613
+ //#region src/lib/plugins/HistoryPlugin.ts
614
+ const withPlateHistory = ({ editor }) => withHistory(editor);
615
+ /** @see {@link withHistory} */
616
+ const HistoryPlugin = createSlatePlugin({
617
+ key: "history",
618
+ extendEditor: withPlateHistory
619
+ });
620
+
621
+ //#endregion
622
+ //#region src/lib/plugins/paragraph/BaseParagraphPlugin.ts
623
+ const BaseParagraphPlugin = createSlatePlugin({
624
+ key: "p",
625
+ node: { isElement: true },
626
+ parsers: { html: { deserializer: {
627
+ rules: [{ validNodeName: "P" }],
628
+ query: ({ element }) => element.style.fontFamily !== "Consolas"
629
+ } } },
630
+ rules: { merge: { removeEmpty: true } }
631
+ });
632
+
633
+ //#endregion
634
+ //#region src/lib/plugins/override/withBreakRules.ts
635
+ const withBreakRules = (ctx) => {
636
+ const { editor, tf: { insertBreak } } = ctx;
637
+ const checkMatchRulesOverride = (rule, blockNode, blockPath) => {
638
+ const matchRulesKeys = editor.meta.pluginCache.rules.match;
639
+ for (const key of matchRulesKeys) {
640
+ const overridePlugin = editor.getPlugin({ key });
641
+ if (overridePlugin.rules?.break && overridePlugin.rules?.match?.({
642
+ ...ctx,
643
+ node: blockNode,
644
+ path: blockPath,
645
+ rule
646
+ })) return overridePlugin.rules.break;
647
+ }
648
+ return null;
649
+ };
650
+ const executeBreakAction = (action, blockPath) => {
651
+ if (action === "reset") {
652
+ editor.tf.resetBlock({ at: blockPath });
653
+ return true;
654
+ }
655
+ if (action === "exit") {
656
+ editor.tf.insertExitBreak();
657
+ return true;
658
+ }
659
+ if (action === "deleteExit") {
660
+ editor.tf.deleteBackward("character");
661
+ editor.tf.insertExitBreak();
662
+ return true;
663
+ }
664
+ if (action === "lineBreak") {
665
+ editor.tf.insertSoftBreak();
666
+ return true;
667
+ }
668
+ return false;
669
+ };
670
+ return { transforms: { insertBreak() {
671
+ if (editor.selection && editor.api.isCollapsed()) {
672
+ const block = editor.api.block();
673
+ if (block) {
674
+ const [blockNode, blockPath] = block;
675
+ const breakRules = getPluginByType(editor, blockNode.type)?.rules.break;
676
+ if (editor.api.isEmpty(editor.selection, { block: true })) {
677
+ const emptyAction = (checkMatchRulesOverride("break.empty", blockNode, blockPath) || breakRules)?.empty;
678
+ if (executeBreakAction(emptyAction, blockPath)) return;
679
+ }
680
+ if (!editor.api.isEmpty(editor.selection, { block: true }) && editor.api.isAt({ end: true })) {
681
+ const range = editor.api.range("before", editor.selection);
682
+ if (range) {
683
+ if (editor.api.string(range) === "\n") {
684
+ const emptyLineEndAction = (checkMatchRulesOverride("break.emptyLineEnd", blockNode, blockPath) || breakRules)?.emptyLineEnd;
685
+ if (executeBreakAction(emptyLineEndAction, blockPath)) return;
686
+ }
687
+ }
688
+ }
689
+ const defaultAction = (checkMatchRulesOverride("break.default", blockNode, blockPath) || breakRules)?.default;
690
+ if (executeBreakAction(defaultAction, blockPath)) return;
691
+ if (checkMatchRulesOverride("break.splitReset", blockNode, blockPath)?.splitReset ?? breakRules?.splitReset) {
692
+ const isAtStart = editor.api.isAt({ start: true });
693
+ insertBreak();
694
+ editor.tf.resetBlock({ at: isAtStart ? blockPath : PathApi.next(blockPath) });
695
+ return;
696
+ }
697
+ }
698
+ }
699
+ insertBreak();
700
+ } } };
701
+ };
702
+
703
+ //#endregion
704
+ //#region src/lib/plugins/override/withDeleteRules.ts
705
+ const withDeleteRules = (ctx) => {
706
+ const { editor, tf: { deleteBackward, deleteForward, deleteFragment } } = ctx;
707
+ const resetMarks = () => {
708
+ if (editor.api.isAt({ start: true })) editor.tf.removeMarks();
709
+ };
710
+ const checkMatchRulesOverride = (rule, blockNode, blockPath) => {
711
+ const matchRulesKeys = editor.meta.pluginCache.rules.match;
712
+ for (const key of matchRulesKeys) {
713
+ const overridePlugin = editor.getPlugin({ key });
714
+ if (overridePlugin.rules?.delete && overridePlugin.rules?.match?.({
715
+ ...ctx,
716
+ node: blockNode,
717
+ path: blockPath,
718
+ rule
719
+ })) return overridePlugin.rules.delete;
720
+ }
721
+ return null;
722
+ };
723
+ const executeDeleteAction = (action, blockPath) => {
724
+ if (action === "reset") {
725
+ editor.tf.resetBlock({ at: blockPath });
726
+ return true;
727
+ }
728
+ return false;
729
+ };
730
+ return { transforms: {
731
+ deleteBackward(unit) {
732
+ if (editor.selection && editor.api.isCollapsed()) {
733
+ const block = editor.api.block();
734
+ if (block) {
735
+ const [blockNode, blockPath] = block;
736
+ const deleteRules = getPluginByType(editor, blockNode.type)?.rules.delete;
737
+ if (editor.api.isAt({ start: true })) {
738
+ const startAction = (checkMatchRulesOverride("delete.start", blockNode, blockPath) || deleteRules)?.start;
739
+ if (executeDeleteAction(startAction, blockPath)) return;
740
+ }
741
+ if (editor.api.isEmpty(editor.selection, { block: true })) {
742
+ const emptyAction = (checkMatchRulesOverride("delete.empty", blockNode, blockPath) || deleteRules)?.empty;
743
+ if (executeDeleteAction(emptyAction, blockPath)) return;
744
+ }
745
+ }
746
+ if (PointApi.equals(editor.selection.anchor, editor.api.start([]))) {
747
+ editor.tf.resetBlock({ at: [0] });
748
+ return;
749
+ }
750
+ }
751
+ deleteBackward(unit);
752
+ resetMarks();
753
+ },
754
+ deleteForward(unit) {
755
+ deleteForward(unit);
756
+ resetMarks();
757
+ },
758
+ deleteFragment(options) {
759
+ if (editor.selection && RangeApi.equals(editor.selection, editor.api.range([]))) {
760
+ editor.tf.reset({
761
+ children: true,
762
+ select: true
763
+ });
764
+ return;
765
+ }
766
+ deleteFragment(options);
767
+ resetMarks();
768
+ }
769
+ } };
770
+ };
771
+
772
+ //#endregion
773
+ //#region src/lib/plugins/override/withMergeRules.ts
774
+ const withMergeRules = (ctx) => {
775
+ const { editor, tf: { removeNodes } } = ctx;
776
+ const checkMatchRulesOverride = (rule, blockNode, blockPath) => {
777
+ const matchRulesKeys = editor.meta.pluginCache.rules.match;
778
+ for (const key of matchRulesKeys) {
779
+ const overridePlugin = editor.getPlugin({ key });
780
+ if (overridePlugin.rules.merge && overridePlugin.rules?.match?.({
781
+ ...ctx,
782
+ node: blockNode,
783
+ path: blockPath,
784
+ rule
785
+ })) return overridePlugin.rules.merge;
786
+ }
787
+ return null;
788
+ };
789
+ return {
790
+ api: { shouldMergeNodes(prevNodeEntry, nextNodeEntry, { reverse } = {}) {
791
+ const [prevNode, prevPath] = prevNodeEntry;
792
+ const [, nextPath] = nextNodeEntry;
793
+ const [curNode, curPath] = reverse ? prevNodeEntry : nextNodeEntry;
794
+ const [targetNode, targetPath] = reverse ? nextNodeEntry : prevNodeEntry;
795
+ if (TextApi.isText(prevNode) && prevNode.text === "" && prevPath.at(-1) !== 0) {
796
+ editor.tf.removeNodes({ at: prevPath });
797
+ return false;
798
+ }
799
+ const shouldRemove = (node, path) => {
800
+ const plugin = getPluginByType(editor, node.type);
801
+ if (!plugin) return true;
802
+ if (!plugin.rules.merge?.removeEmpty) return false;
803
+ if (checkMatchRulesOverride("merge.removeEmpty", node, path)?.removeEmpty === false) return false;
804
+ return true;
805
+ };
806
+ if (ElementApi.isElement(targetNode) && editor.api.isVoid(targetNode)) {
807
+ if (shouldRemove(targetNode, targetPath)) editor.tf.removeNodes({ at: prevPath });
808
+ else if (ElementApi.isElement(curNode) && editor.api.isEmpty(curNode)) editor.tf.removeNodes({ at: curPath });
809
+ return false;
810
+ }
811
+ if (ElementApi.isElement(prevNode) && editor.api.isEmpty(prevNode) && PathApi.isSibling(prevPath, nextPath) && shouldRemove(prevNode, prevPath)) {
812
+ editor.tf.removeNodes({ at: prevPath });
813
+ return false;
814
+ }
815
+ return true;
816
+ } },
817
+ transforms: { removeNodes(options = {}) {
818
+ if (options.event?.type === "mergeNodes" && options.at) {
819
+ const nodeEntry = editor.api.node(options.at);
820
+ if (nodeEntry) {
821
+ const [node, path] = nodeEntry;
822
+ if (ElementApi.isElement(node)) {
823
+ const plugin = getPluginByType(editor, node.type);
824
+ if (plugin) {
825
+ const mergeRules = plugin.rules.merge;
826
+ if (checkMatchRulesOverride("merge.removeEmpty", node, path)?.removeEmpty === false || mergeRules?.removeEmpty === false) return;
827
+ }
828
+ }
829
+ }
830
+ }
831
+ removeNodes(options);
832
+ } }
833
+ };
834
+ };
835
+
836
+ //#endregion
837
+ //#region src/lib/plugins/override/withNormalizeRules.ts
838
+ const withNormalizeRules = (ctx) => {
839
+ const { editor, tf: { normalizeNode } } = ctx;
840
+ const checkMatchRulesOverride = (rule, node, path) => {
841
+ const matchRulesKeys = editor.meta.pluginCache.rules.match;
842
+ for (const key of matchRulesKeys) {
843
+ const overridePlugin = editor.getPlugin({ key });
844
+ if (overridePlugin.rules?.normalize && overridePlugin.rules?.match?.({
845
+ ...ctx,
846
+ node,
847
+ path,
848
+ rule
849
+ })) return overridePlugin.rules.normalize;
850
+ }
851
+ return null;
852
+ };
853
+ return { transforms: { normalizeNode([node, path]) {
854
+ if (ElementApi.isElement(node) && node.type) {
855
+ const normalizeRules = getPluginByType(editor, node.type)?.rules.normalize;
856
+ if ((checkMatchRulesOverride("normalize.removeEmpty", node, path) || normalizeRules)?.removeEmpty && editor.api.isEmpty(node)) {
857
+ editor.tf.removeNodes({ at: path });
858
+ return;
859
+ }
860
+ }
861
+ normalizeNode([node, path]);
862
+ } } };
863
+ };
864
+
865
+ //#endregion
866
+ //#region src/lib/plugins/override/OverridePlugin.ts
867
+ /**
868
+ * Merge and register all the inline types and void types from the plugins and
869
+ * options, using `editor.api.isInline`, `editor.api.markableVoid` and
870
+ * `editor.api.isVoid`
871
+ */
872
+ const withOverrides = ({ api: { isInline, isSelectable, isVoid, markableVoid }, editor }) => {
873
+ return { api: {
874
+ create: { block: (node) => ({
875
+ children: [{ text: "" }],
876
+ type: editor.getType(BaseParagraphPlugin.key),
877
+ ...node
878
+ }) },
879
+ isInline(element) {
880
+ return getPluginByType(editor, element.type)?.node.isInline ? true : isInline(element);
881
+ },
882
+ isSelectable(element) {
883
+ return getPluginByType(editor, element.type)?.node.isSelectable === false ? false : isSelectable(element);
884
+ },
885
+ isVoid(element) {
886
+ return getPluginByType(editor, element.type)?.node.isVoid ? true : isVoid(element);
887
+ },
888
+ markableVoid(element) {
889
+ return getPluginByType(editor, element.type)?.node.isMarkableVoid ? true : markableVoid(element);
890
+ }
891
+ } };
892
+ };
893
+ /** Override the editor api and transforms based on the plugins. */
894
+ const OverridePlugin = createSlatePlugin({ key: "override" }).overrideEditor(withOverrides).overrideEditor(withBreakRules).overrideEditor(withDeleteRules).overrideEditor(withMergeRules).overrideEditor(withNormalizeRules);
895
+
896
+ //#endregion
897
+ //#region src/internal/plugin/pipeInsertFragment.ts
898
+ /** Pipe preInsert then insertFragment. */
899
+ const pipeInsertFragment = (editor, injectedPlugins, { fragment, ...options }) => {
900
+ editor.tf.withoutNormalizing(() => {
901
+ injectedPlugins.some((p) => p.parser?.preInsert?.({
902
+ ...getEditorPlugin(editor, p),
903
+ fragment,
904
+ ...options
905
+ }) === true);
906
+ editor.tf.insertFragment(fragment);
907
+ });
908
+ };
909
+
910
+ //#endregion
911
+ //#region src/internal/plugin/pipeTransformData.ts
912
+ /** Pipe editor.tf.insertData.transformData */
913
+ const pipeTransformData = (editor, plugins, { data, ...options }) => {
914
+ plugins.forEach((p) => {
915
+ const transformData = p.parser?.transformData;
916
+ if (!transformData) return;
917
+ data = transformData({
918
+ ...getEditorPlugin(editor, p),
919
+ data,
920
+ ...options
921
+ });
922
+ });
923
+ return data;
924
+ };
925
+
926
+ //#endregion
927
+ //#region src/internal/plugin/pipeTransformFragment.ts
928
+ /** Pipe editor.tf.insertData.transformFragment */
929
+ const pipeTransformFragment = (editor, plugins, { fragment, ...options }) => {
930
+ plugins.forEach((p) => {
931
+ const transformFragment = p.parser?.transformFragment;
932
+ if (!transformFragment) return;
933
+ fragment = transformFragment({
934
+ fragment,
935
+ ...options,
936
+ ...getEditorPlugin(editor, p)
937
+ });
938
+ });
939
+ return fragment;
940
+ };
941
+
942
+ //#endregion
943
+ //#region src/lib/utils/applyDeepToNodes.ts
944
+ /** Recursively apply an operation to children nodes with a query. */
945
+ const applyDeepToNodes = ({ apply, node, path = [], query, source }) => {
946
+ if (queryNode([node, path], query)) if (typeof source === "function") apply(node, source());
947
+ else apply(node, source);
948
+ if (!NodeApi.isAncestor(node)) return;
949
+ node.children.forEach((child, index) => {
950
+ applyDeepToNodes({
951
+ apply,
952
+ node: child,
953
+ path: path.concat([index]),
954
+ query,
955
+ source
956
+ });
957
+ });
958
+ };
959
+
960
+ //#endregion
961
+ //#region src/lib/utils/checkUtils.ts
962
+ const isSlateVoid = (element) => element.dataset.slateVoid === "true";
963
+ const isSlateElement = (element) => element.dataset.slateNode === "element";
964
+ const isSlateText = (element) => element.dataset.slateNode === "text";
965
+ const isSlateString = (element) => element.dataset.slateString === "true";
966
+ const isSlateLeaf = (element) => element.dataset.slateLeaf === "true";
967
+ const isSlateEditor = (element) => element.dataset.slateEditor === "true";
968
+ const isSlateNode = (element) => isSlateLeaf(element) || isSlateElement(element) || isSlateVoid(element) || isSlateString(element) || isSlateText(element);
969
+ const isSlatePluginElement = (element, pluginKey) => element.dataset.slateNode === "element" && element.classList.contains(`slate-${pluginKey}`);
970
+ const isSlatePluginNode = (element, pluginKey) => element.classList.contains(`slate-${pluginKey}`);
971
+ const getSlateElements = (element) => Array.from(element.querySelectorAll("[data-slate-node=\"element\"]"));
972
+
973
+ //#endregion
974
+ //#region src/lib/utils/defaultsDeepToNodes.ts
975
+ /** Recursively merge a source object to children nodes with a query. */
976
+ const defaultsDeepToNodes = (options) => {
977
+ applyDeepToNodes({
978
+ ...options,
979
+ apply: defaults
980
+ });
981
+ };
982
+
983
+ //#endregion
984
+ //#region src/lib/utils/getInjectMatch.ts
985
+ const getInjectMatch = (editor, plugin) => {
986
+ return (node, path) => {
987
+ const { inject: { excludeBelowPlugins, excludePlugins, isBlock: _isBlock, isElement: _isElement, isLeaf, maxLevel, targetPlugins } } = plugin;
988
+ const element = ElementApi.isElement(node) ? node : void 0;
989
+ if (_isElement && !element) return false;
990
+ if (_isBlock && (!element || !editor.api.isBlock(element))) return false;
991
+ if (isLeaf && element) return false;
992
+ if (element?.type) {
993
+ if (excludePlugins?.includes(getPluginKey(editor, element.type))) return false;
994
+ if (targetPlugins && !targetPlugins.includes(getPluginKey(editor, element.type))) return false;
995
+ }
996
+ if (excludeBelowPlugins || maxLevel) {
997
+ if (maxLevel && path.length > maxLevel) return false;
998
+ if (excludeBelowPlugins) {
999
+ const excludeTypes = getPluginKeys(editor, excludeBelowPlugins);
1000
+ if (editor.api.above({
1001
+ at: path,
1002
+ match: (n) => ElementApi.isElement(n) && excludeTypes.includes(n.type)
1003
+ })) return false;
1004
+ }
1005
+ }
1006
+ return true;
1007
+ };
1008
+ };
1009
+
1010
+ //#endregion
1011
+ //#region src/lib/utils/getInjectedPlugins.ts
1012
+ /**
1013
+ * Get all plugins having a defined `inject.plugins[plugin.key]`. It includes
1014
+ * `plugin` itself.
1015
+ */
1016
+ const getInjectedPlugins = (editor, plugin) => {
1017
+ const injectedPlugins = [];
1018
+ [...editor.meta.pluginList].reverse().forEach((p) => {
1019
+ const injectedPlugin = p.inject.plugins?.[plugin.key];
1020
+ if (injectedPlugin) injectedPlugins.push(injectedPlugin);
1021
+ });
1022
+ return [plugin, ...injectedPlugins];
1023
+ };
1024
+
1025
+ //#endregion
1026
+ //#region src/lib/utils/getNodeDataAttributeKeys.ts
1027
+ const getNodeDataAttributeKeys = (node) => Object.keys(node).filter((key) => typeof node[key] !== "object" && (!TextApi.isText(node) || key !== "text")).map((key) => keyToDataAttribute(key));
1028
+ const keyToDataAttribute = (key) => `data-slate-${kebabCase(key)}`;
1029
+
1030
+ //#endregion
1031
+ //#region src/lib/utils/getPluginNodeProps.ts
1032
+ const getPluginNodeProps = ({ attributes: nodeAttributes, node, plugin, props }) => {
1033
+ const newProps = {
1034
+ ...props,
1035
+ attributes: { ...props.attributes }
1036
+ };
1037
+ if (plugin?.node.props) {
1038
+ const pluginNodeProps = (typeof plugin.node.props === "function" ? plugin.node.props(newProps) : plugin.node.props) ?? {};
1039
+ newProps.attributes = {
1040
+ ...newProps.attributes,
1041
+ ...pluginNodeProps
1042
+ };
1043
+ }
1044
+ if (nodeAttributes && plugin) newProps.attributes = {
1045
+ ...newProps.attributes,
1046
+ ...pick(
1047
+ nodeAttributes,
1048
+ /**
1049
+ * WARNING: Improper use of `dangerouslyAllowAttributes` WILL make your
1050
+ * application vulnerable to cross-site scripting (XSS) or information
1051
+ * exposure attacks.
1052
+ *
1053
+ * @see {@link BasePluginNode.dangerouslyAllowAttributes}
1054
+ */
1055
+ ...plugin.node.dangerouslyAllowAttributes ?? [],
1056
+ [...node ? getNodeDataAttributeKeys(node) : []]
1057
+ )
1058
+ };
1059
+ Object.keys(newProps.attributes).forEach((key) => {
1060
+ if (newProps.attributes?.[key] === void 0) delete newProps.attributes?.[key];
1061
+ });
1062
+ return newProps;
1063
+ };
1064
+
1065
+ //#endregion
1066
+ //#region src/lib/utils/getSlateClass.ts
1067
+ /** Get slate class name: slate-<type> */
1068
+ const getSlateClass = (type) => type ? `slate-${type}` : "";
1069
+
1070
+ //#endregion
1071
+ //#region src/lib/utils/mergeDeepToNodes.ts
1072
+ /** Recursively merge a source object to children nodes with a query. */
1073
+ const mergeDeepToNodes = (options) => {
1074
+ applyDeepToNodes({
1075
+ ...options,
1076
+ apply: merge
1077
+ });
1078
+ };
1079
+
1080
+ //#endregion
1081
+ //#region src/lib/plugins/affinity/queries/getEdgeNodes.ts
1082
+ /**
1083
+ * When the cursor is at a mark edge, this function returns the inward node and
1084
+ * the outward node (if any). If the cursor is at the start of the text, then
1085
+ * the node before the text is returned. If the cursor is at the end of the
1086
+ * text, then the node after the text is returned. Otherwise, null is returned.
1087
+ */
1088
+ const getEdgeNodes = (editor) => {
1089
+ if (!editor.api.isCollapsed()) return null;
1090
+ const cursor = editor.selection.anchor;
1091
+ const textRange = editor.api.range(cursor.path);
1092
+ if (!textRange) return null;
1093
+ const edge = editor.api.isStart(cursor, textRange) ? "start" : editor.api.isEnd(cursor, textRange) ? "end" : null;
1094
+ if (!edge) return null;
1095
+ const parent = NodeApi.parent(editor, cursor.path) ?? null;
1096
+ /** Inline elements */
1097
+ const isAffinityInlineElement = (() => {
1098
+ if (!parent || !ElementApi.isElement(parent)) return false;
1099
+ const parentAffinity = getPluginByType(editor, parent.type)?.rules.selection?.affinity;
1100
+ return parentAffinity === "hard" || parentAffinity === "directional";
1101
+ })();
1102
+ const nodeEntry = isAffinityInlineElement ? [parent, PathApi.parent(cursor.path)] : [NodeApi.get(editor, cursor.path), cursor.path];
1103
+ if (edge === "start" && cursor.path.at(-1) === 0 && !isAffinityInlineElement) return [null, nodeEntry];
1104
+ const siblingPath = edge === "end" ? Path$1.next(nodeEntry[1]) : Path$1.previous(nodeEntry[1]);
1105
+ const siblingNode = NodeApi.get(editor, siblingPath);
1106
+ const siblingEntry = siblingNode ? [siblingNode, siblingPath] : null;
1107
+ return edge === "end" ? [nodeEntry, siblingEntry] : [siblingEntry, nodeEntry];
1108
+ };
1109
+
1110
+ //#endregion
1111
+ //#region src/lib/plugins/affinity/queries/getMarkBoundaryAffinity.ts
1112
+ const getMarkBoundaryAffinity = (editor, markBoundary) => {
1113
+ const { marks, selection } = editor;
1114
+ if (!selection) return;
1115
+ const marksMatchLeaf = (leaf) => marks && isEqual(NodeApi.extractProps(leaf), marks) && Object.keys(marks).length > 1;
1116
+ const [backwardLeafEntry, forwardLeafEntry] = markBoundary;
1117
+ if (!backwardLeafEntry || !forwardLeafEntry) {
1118
+ const leafEntry = backwardLeafEntry || forwardLeafEntry;
1119
+ if (!marks || marksMatchLeaf(leafEntry[0])) return leafEntry === backwardLeafEntry ? "backward" : "forward";
1120
+ return;
1121
+ }
1122
+ const marksDirection = marks && (() => {
1123
+ if (backwardLeafEntry && marksMatchLeaf(backwardLeafEntry[0])) return "backward";
1124
+ if (forwardLeafEntry && marksMatchLeaf(forwardLeafEntry[0])) return "forward";
1125
+ return null;
1126
+ })();
1127
+ const selectionDirection = selection.anchor.offset === 0 ? "forward" : "backward";
1128
+ if (selectionDirection === "backward" && marksDirection === "forward") return "forward";
1129
+ if (IS_FIREFOX && selectionDirection === "forward" && marksDirection !== "backward") return "forward";
1130
+ return "backward";
1131
+ };
1132
+
1133
+ //#endregion
1134
+ //#region src/lib/plugins/affinity/queries/isNodeAffinity.ts
1135
+ const isNodeAffinity = (editor, node, affinity) => {
1136
+ const marks = Object.keys(NodeApi.extractProps(node));
1137
+ return (ElementApi.isElement(node) ? [node.type] : marks).some((type) => getPluginByType(editor, type)?.rules.selection?.affinity === affinity);
1138
+ };
1139
+ const isNodesAffinity = (editor, edgeNodes, affinity) => {
1140
+ const [backwardLeafEntry, forwardLeafEntry] = edgeNodes;
1141
+ return backwardLeafEntry && isNodeAffinity(editor, backwardLeafEntry[0], affinity) || forwardLeafEntry && isNodeAffinity(editor, forwardLeafEntry[0], affinity);
1142
+ };
1143
+
1144
+ //#endregion
1145
+ //#region src/lib/plugins/affinity/transforms/setAffinitySelection.ts
1146
+ const setAffinitySelection = (editor, edgeNodes, affinity) => {
1147
+ const setMarks = (marks) => {
1148
+ editor.marks = marks;
1149
+ editor.api.onChange();
1150
+ };
1151
+ const select = (point) => {
1152
+ editor.tf.setSelection({
1153
+ anchor: point,
1154
+ focus: point
1155
+ });
1156
+ };
1157
+ const [before, after] = edgeNodes;
1158
+ if (affinity === "backward") {
1159
+ if (before === null) {
1160
+ setMarks({});
1161
+ return;
1162
+ }
1163
+ const beforeEnd = editor.api.end(before[1]);
1164
+ if (beforeEnd) select(beforeEnd);
1165
+ if (ElementApi.isElement(before[0])) return;
1166
+ setMarks(null);
1167
+ return;
1168
+ }
1169
+ if (before === null) {
1170
+ setMarks(null);
1171
+ return;
1172
+ }
1173
+ if (after === null) {
1174
+ setMarks({});
1175
+ return;
1176
+ }
1177
+ select(editor.api.end(before[1]));
1178
+ if (ElementApi.isElement(after[0])) return;
1179
+ setMarks(NodeApi.extractProps(after[0]));
1180
+ };
1181
+
1182
+ //#endregion
1183
+ //#region src/lib/plugins/affinity/AffinityPlugin.ts
1184
+ const AffinityPlugin = createTSlatePlugin({ key: "affinity" }).overrideEditor(({ editor, tf: { deleteBackward, insertText, move } }) => ({ transforms: {
1185
+ deleteBackward: (unit) => {
1186
+ const apply = () => {
1187
+ if (unit === "character" && editor.api.isCollapsed()) {
1188
+ const [start] = getEdgeNodes(editor) ?? [null];
1189
+ const startText = start && (TextApi.isText(start[0]) ? start[0].text : NodeApi.string(start[0]));
1190
+ deleteBackward(unit);
1191
+ const edgeNodes = getEdgeNodes(editor);
1192
+ if (edgeNodes && isNodesAffinity(editor, edgeNodes, "directional") && !hasElement(edgeNodes)) setAffinitySelection(editor, edgeNodes, startText && startText.length > 1 ? "backward" : "forward");
1193
+ return true;
1194
+ }
1195
+ };
1196
+ if (apply()) return;
1197
+ deleteBackward(unit);
1198
+ },
1199
+ insertText(text, options) {
1200
+ /** This will be computed only for text nodes with marks. */
1201
+ const applyOutwardAffinity = () => {
1202
+ if (!editor.selection || editor.api.isExpanded()) return;
1203
+ const textPath = editor.selection.focus.path;
1204
+ const textNode = NodeApi.get(editor, textPath);
1205
+ if (!textNode) return;
1206
+ const outwardMarks = Object.keys(NodeApi.extractProps(textNode)).filter((type) => getPluginByType(editor, type)?.rules.selection?.affinity === "outward");
1207
+ if (!outwardMarks.length || !editor.api.isEnd(editor.selection.focus, textPath)) return;
1208
+ const nextPoint = editor.api.start(textPath, { next: true });
1209
+ const marksToRemove = [];
1210
+ let nextTextNode = null;
1211
+ if (nextPoint) {
1212
+ const nextTextPath = nextPoint.path;
1213
+ nextTextNode = NodeApi.get(editor, nextTextPath) || null;
1214
+ }
1215
+ for (const markKey of outwardMarks) {
1216
+ if (!textNode[markKey]) continue;
1217
+ if (!nextTextNode?.[markKey]) marksToRemove.push(markKey);
1218
+ }
1219
+ if (marksToRemove.length > 0) editor.tf.removeMarks(marksToRemove);
1220
+ };
1221
+ applyOutwardAffinity();
1222
+ return insertText(text, options);
1223
+ },
1224
+ move: (options) => {
1225
+ const apply = () => {
1226
+ const { distance = 1, reverse = false, unit = "character" } = options || {};
1227
+ if (unit === "character" && distance === 1 && editor.api.isCollapsed()) {
1228
+ const preEdgeNodes = getEdgeNodes(editor);
1229
+ if (preEdgeNodes && isNodesAffinity(editor, preEdgeNodes, "hard")) {
1230
+ if (preEdgeNodes && preEdgeNodes[reverse ? 0 : 1] === null && getMarkBoundaryAffinity(editor, preEdgeNodes) === (reverse ? "forward" : "backward")) {
1231
+ setAffinitySelection(editor, preEdgeNodes, reverse ? "backward" : "forward");
1232
+ return true;
1233
+ }
1234
+ move({
1235
+ ...options,
1236
+ unit: "offset"
1237
+ });
1238
+ return true;
1239
+ }
1240
+ move(options);
1241
+ const postEdgeNodes = getEdgeNodes(editor);
1242
+ /**
1243
+ * If the move places the cursor at a mark boundary, then the affinity
1244
+ * should be set to the direction the cursor came from.
1245
+ */
1246
+ if (postEdgeNodes && isNodesAffinity(editor, postEdgeNodes, "directional") && !hasElement(postEdgeNodes)) setAffinitySelection(editor, postEdgeNodes, reverse ? "forward" : "backward");
1247
+ return true;
1248
+ }
1249
+ };
1250
+ if (apply()) return;
1251
+ move(options);
1252
+ }
1253
+ } }));
1254
+ const hasElement = (edgeNodes) => {
1255
+ const [before, after] = edgeNodes;
1256
+ return before && ElementApi.isElement(before[0]) || after && ElementApi.isElement(after[0]);
1257
+ };
1258
+
1259
+ //#endregion
1260
+ //#region src/lib/plugins/chunking/withChunking.ts
1261
+ const withChunking = ({ editor, getOptions }) => {
1262
+ const { chunkSize, query } = getOptions();
1263
+ editor.getChunkSize = (ancestor) => query(ancestor) ? chunkSize : null;
1264
+ return {};
1265
+ };
1266
+
1267
+ //#endregion
1268
+ //#region src/lib/plugins/chunking/ChunkingPlugin.ts
1269
+ const ChunkingPlugin = createTSlatePlugin({
1270
+ key: "chunking",
1271
+ options: {
1272
+ chunkSize: 1e3,
1273
+ contentVisibilityAuto: true,
1274
+ query: NodeApi.isEditor
1275
+ }
1276
+ }).overrideEditor(withChunking);
1277
+
1278
+ //#endregion
1279
+ //#region src/lib/plugins/debug/DebugPlugin.ts
1280
+ var PlateError = class extends Error {
1281
+ type;
1282
+ constructor(message, type = "DEFAULT") {
1283
+ super(`[${type}] ${message}`);
1284
+ this.name = "PlateError";
1285
+ this.type = type;
1286
+ }
1287
+ };
1288
+ const DebugPlugin = createTSlatePlugin({
1289
+ key: "debug",
1290
+ options: {
1291
+ isProduction: process.env.NODE_ENV === "production",
1292
+ logger: {
1293
+ error: (message, type, details) => console.error(`${type ? `[${type}] ` : ""}${message}`, details),
1294
+ info: (message, type, details) => console.info(`${type ? `[${type}] ` : ""}${message}`, details),
1295
+ log: (message, type, details) => console.log(`${type ? `[${type}] ` : ""}${message}`, details),
1296
+ warn: (message, type, details) => console.warn(`${type ? `[${type}] ` : ""}${message}`, details)
1297
+ },
1298
+ logLevel: process.env.NODE_ENV === "production" ? "error" : "log",
1299
+ throwErrors: true
1300
+ }
1301
+ }).extendEditorApi(({ getOptions }) => {
1302
+ const logLevels = [
1303
+ "error",
1304
+ "warn",
1305
+ "info",
1306
+ "log"
1307
+ ];
1308
+ const log = (level, message, type, details) => {
1309
+ if (process.env.NODE_ENV === "production") return;
1310
+ const options = getOptions();
1311
+ if (options.isProduction && level === "log") return;
1312
+ if (logLevels.indexOf(level) <= logLevels.indexOf(options.logLevel)) {
1313
+ if (level === "error" && options.throwErrors) throw new PlateError(message, type);
1314
+ options.logger[level]?.(message, type, details);
1315
+ }
1316
+ };
1317
+ return { debug: {
1318
+ error: (message, type, details) => log("error", message, type, details),
1319
+ info: (message, type, details) => log("info", message, type, details),
1320
+ log: (message, type, details) => log("log", message, type, details),
1321
+ warn: (message, type, details) => log("warn", message, type, details)
1322
+ } };
1323
+ });
1324
+
1325
+ //#endregion
1326
+ //#region src/lib/plugins/dom/withScrolling.ts
1327
+ const withScrolling = (editor, fn, options) => {
1328
+ const prevOptions = editor.getOptions(DOMPlugin);
1329
+ const prevAutoScroll = AUTO_SCROLL.get(editor) ?? false;
1330
+ if (options) {
1331
+ const ops = {
1332
+ ...prevOptions,
1333
+ ...omitBy(options, isUndefined)
1334
+ };
1335
+ editor.setOptions(DOMPlugin, ops);
1336
+ }
1337
+ AUTO_SCROLL.set(editor, true);
1338
+ fn();
1339
+ AUTO_SCROLL.set(editor, prevAutoScroll);
1340
+ editor.setOptions(DOMPlugin, prevOptions);
1341
+ };
1342
+
1343
+ //#endregion
1344
+ //#region src/lib/plugins/dom/DOMPlugin.ts
1345
+ const AUTO_SCROLL = /* @__PURE__ */ new WeakMap();
1346
+ /**
1347
+ * Placeholder plugin for DOM interaction, that could be replaced with
1348
+ * ReactPlugin.
1349
+ */
1350
+ const DOMPlugin = createTSlatePlugin({
1351
+ key: "dom",
1352
+ options: {
1353
+ scrollMode: "last",
1354
+ scrollOperations: {
1355
+ insert_node: true,
1356
+ insert_text: true
1357
+ },
1358
+ scrollOptions: { scrollMode: "if-needed" }
1359
+ }
1360
+ }).extendEditorApi(({ editor }) => ({ isScrolling: () => AUTO_SCROLL.get(editor) ?? false })).extendEditorTransforms(({ editor }) => ({ withScrolling: bindFirst(withScrolling, editor) })).overrideEditor(({ api, editor, getOption, tf: { apply } }) => ({ transforms: { apply(operation) {
1361
+ if (api.isScrolling()) {
1362
+ apply(operation);
1363
+ const scrollOperations = getOption("scrollOperations");
1364
+ if (!scrollOperations[operation.type]) return;
1365
+ const matched = editor.operations.filter((op) => !!scrollOperations[op.type]);
1366
+ if (matched.length === 0) return;
1367
+ const targetOp = getOption("scrollMode") === "first" ? matched[0] : matched.at(-1);
1368
+ if (!targetOp) return;
1369
+ const { offset, path } = targetOp.path ? targetOp : {};
1370
+ if (!path) return;
1371
+ const scrollOptions = getOption("scrollOptions");
1372
+ const scrollTarget = {
1373
+ offset: offset ?? 0,
1374
+ path
1375
+ };
1376
+ api.scrollIntoView(scrollTarget, scrollOptions);
1377
+ return;
1378
+ }
1379
+ return apply(operation);
1380
+ } } })).overrideEditor(({ editor, tf: { apply } }) => ({ transforms: { apply(operation) {
1381
+ if (operation.type === "set_selection") {
1382
+ const { properties } = operation;
1383
+ editor.dom.prevSelection = properties;
1384
+ apply(operation);
1385
+ editor.dom.currentKeyboardEvent = null;
1386
+ return;
1387
+ }
1388
+ apply(operation);
1389
+ } } }));
1390
+
1391
+ //#endregion
1392
+ //#region src/lib/plugins/html/utils/isHtmlElement.ts
1393
+ const isHtmlElement = (node) => node.nodeType === Node.ELEMENT_NODE;
1394
+
1395
+ //#endregion
1396
+ //#region src/lib/plugins/html/utils/isHtmlText.ts
1397
+ const isHtmlText = (node) => node.nodeType === Node.TEXT_NODE;
1398
+
1399
+ //#endregion
1400
+ //#region src/lib/plugins/html/utils/inlineTagNames.ts
1401
+ /**
1402
+ * # Methodology
1403
+ *
1404
+ * ## Step 1. Get the list of all standard tag names
1405
+ *
1406
+ * Go to https://developer.mozilla.org/en-US/docs/Web/HTML/Element and run the
1407
+ * following in the console to generate a JSON array of tag names:
1408
+ *
1409
+ * ```js
1410
+ * JSON.stringify(
1411
+ * Array.from(document.querySelectorAll('article table td:first-child'))
1412
+ * .map((td) => {
1413
+ * const body = document.createElement('body');
1414
+ * body.innerHTML = td.textContent;
1415
+ * return body.firstChild?.tagName;
1416
+ * })
1417
+ * .filter((tagName) => tagName)
1418
+ * );
1419
+ * ```
1420
+ *
1421
+ * Output (as of 2023-11-06):
1422
+ *
1423
+ * ```json
1424
+ * '["BASE","LINK","META","STYLE","TITLE","ADDRESS","ARTICLE","ASIDE","FOOTER","HEADER","H1","HGROUP","MAIN","NAV","SECTION","SEARCH","BLOCKQUOTE","DD","DIV","DL","DT","FIGCAPTION","FIGURE","HR","LI","MENU","OL","P","PRE","UL","A","ABBR","B","BDI","BDO","BR","CITE","CODE","DATA","DFN","EM","I","KBD","MARK","Q","RP","RT","RUBY","S","SAMP","SMALL","SPAN","STRONG","SUB","SUP","TIME","U","VAR","WBR","AREA","AUDIO","IMG","MAP","TRACK","VIDEO","EMBED","IFRAME","OBJECT","PICTURE","PORTAL","SOURCE","svg","math","CANVAS","NOSCRIPT","SCRIPT","DEL","INS","TABLE","BUTTON","DATALIST","FIELDSET","FORM","INPUT","LABEL","LEGEND","METER","OPTGROUP","OPTION","OUTPUT","PROGRESS","SELECT","TEXTAREA","DETAILS","DIALOG","SUMMARY","SLOT","TEMPLATE","ACRONYM","BIG","CENTER","CONTENT","DIR","FONT","IMG","MARQUEE","MENUITEM","NOBR","NOEMBED","NOFRAMES","PARAM","PLAINTEXT","RB","RTC","SHADOW","STRIKE","TT","XMP"]'
1425
+ * ```
1426
+ *
1427
+ * ## Step 2. For each tag name, determine the default browser style
1428
+ *
1429
+ * Open an empty HTML file in the browser and run the following in the console:
1430
+ *
1431
+ * ```js
1432
+ * const tagNames = JSON.parse(<JSON string from step 1>);
1433
+ *
1434
+ * JSON.stringify(
1435
+ * tagNames.filter((tagName) => {
1436
+ * const element = document.createElement(tagName);
1437
+ * document.body.appendChild(element);
1438
+ * const display = window.getComputedStyle(element).display;
1439
+ * element.remove();
1440
+ * return display.startsWith('inline');
1441
+ * })
1442
+ * );
1443
+ * ```
1444
+ *
1445
+ * Place the result in the array below (accurate as of 2023-11-06).
1446
+ */
1447
+ const inlineTagNames = new Set([
1448
+ "A",
1449
+ "ABBR",
1450
+ "ACRONYM",
1451
+ "B",
1452
+ "BDI",
1453
+ "BDO",
1454
+ "BIG",
1455
+ "BR",
1456
+ "BUTTON",
1457
+ "CANVAS",
1458
+ "CITE",
1459
+ "CODE",
1460
+ "CONTENT",
1461
+ "DATA",
1462
+ "DEL",
1463
+ "DFN",
1464
+ "EM",
1465
+ "EMBED",
1466
+ "FONT",
1467
+ "I",
1468
+ "IFRAME",
1469
+ "IMG",
1470
+ "IMG",
1471
+ "INPUT",
1472
+ "INS",
1473
+ "KBD",
1474
+ "LABEL",
1475
+ "MAP",
1476
+ "MARK",
1477
+ "MARQUEE",
1478
+ "math",
1479
+ "MENUITEM",
1480
+ "METER",
1481
+ "NOBR",
1482
+ "OBJECT",
1483
+ "OUTPUT",
1484
+ "PICTURE",
1485
+ "PORTAL",
1486
+ "PROGRESS",
1487
+ "Q",
1488
+ "S",
1489
+ "SAMP",
1490
+ "SELECT",
1491
+ "SHADOW",
1492
+ "SMALL",
1493
+ "SOURCE",
1494
+ "SPAN",
1495
+ "STRIKE",
1496
+ "STRONG",
1497
+ "SUB",
1498
+ "SUP",
1499
+ "svg",
1500
+ "TEXTAREA",
1501
+ "TIME",
1502
+ "TRACK",
1503
+ "TT",
1504
+ "U",
1505
+ "VAR",
1506
+ "VIDEO",
1507
+ "WBR"
1508
+ ]);
1509
+
1510
+ //#endregion
1511
+ //#region src/lib/plugins/html/utils/isHtmlInlineElement.ts
1512
+ const isHtmlInlineElement = (node) => {
1513
+ if (!isHtmlElement(node)) return false;
1514
+ const element = node;
1515
+ const tagNameIsInline = inlineTagNames.has(element.tagName);
1516
+ /**
1517
+ * Valid display values include 'inline flow'. We only care about the first
1518
+ * part.
1519
+ */
1520
+ const displayProperty = element.style.display.split(" ")[0];
1521
+ if (displayProperty === "") return tagNameIsInline;
1522
+ if (displayProperty.startsWith("inline")) return true;
1523
+ if (displayProperty === "inherit" && element.parentElement) return isHtmlInlineElement(element.parentElement);
1524
+ /**
1525
+ * Handle all special values manually, so that any unhandled values can be
1526
+ * assumed to be block.
1527
+ *
1528
+ * Note: Ideally, content inside `display: none` elements should not be
1529
+ * parsed. However, if such elements are parsed, it's best for their inline or
1530
+ * block status to be left unchanged.
1531
+ */
1532
+ if ([
1533
+ "contents",
1534
+ "initial",
1535
+ "none",
1536
+ "revert",
1537
+ "revert-layer",
1538
+ "unset"
1539
+ ].includes(displayProperty)) return tagNameIsInline;
1540
+ return false;
1541
+ };
1542
+
1543
+ //#endregion
1544
+ //#region src/lib/plugins/html/utils/isHtmlBlockElement.ts
1545
+ const isHtmlBlockElement = (node) => {
1546
+ if (!isHtmlElement(node)) return false;
1547
+ return !isHtmlInlineElement(node);
1548
+ };
1549
+
1550
+ //#endregion
1551
+ //#region src/lib/plugins/html/utils/collapse-white-space/collapseString.ts
1552
+ const LEADING_WHITESPACE_REGEX = /^\s+/;
1553
+ const TRAILING_NEWLINE_REGEX = /\n$/;
1554
+ const collapseString = (text, { shouldCollapseWhiteSpace = true, trimEnd = "collapse", trimStart = "collapse", whiteSpaceIncludesNewlines = true } = {}) => {
1555
+ let result = text;
1556
+ if (trimStart === "all") result = result.replace(LEADING_WHITESPACE_REGEX, "");
1557
+ if (trimEnd === "single-newline") result = result.replace(TRAILING_NEWLINE_REGEX, "");
1558
+ if (shouldCollapseWhiteSpace) if (whiteSpaceIncludesNewlines) result = result.replaceAll(/\s+/g, " ");
1559
+ else {
1560
+ result = result.replaceAll(/[^\S\n\r]+/g, " ");
1561
+ /**
1562
+ * Trim horizontal whitespace from the start and end of lines (behavior of
1563
+ * pre-line).
1564
+ */
1565
+ result = result.replaceAll(/^[^\S\n\r]+/gm, "");
1566
+ result = result.replaceAll(/[^\S\n\r]+$/gm, "");
1567
+ }
1568
+ return result;
1569
+ };
1570
+
1571
+ //#endregion
1572
+ //#region src/lib/plugins/html/utils/collapse-white-space/isLastNonEmptyTextOfInlineFormattingContext.ts
1573
+ const isLastNonEmptyTextOfInlineFormattingContext = (initialText) => {
1574
+ let currentNode = initialText;
1575
+ while (true) {
1576
+ if (currentNode.nextSibling) currentNode = currentNode.nextSibling;
1577
+ else {
1578
+ currentNode = currentNode.parentElement;
1579
+ if (currentNode && isHtmlBlockElement(currentNode)) return true;
1580
+ currentNode = currentNode?.nextSibling || null;
1581
+ }
1582
+ if (!currentNode) return true;
1583
+ if (isHtmlBlockElement(currentNode)) return true;
1584
+ if ((currentNode.textContent || "").length > 0) return false;
1585
+ }
1586
+ };
1587
+
1588
+ //#endregion
1589
+ //#region src/lib/plugins/html/utils/collapse-white-space/stateTransforms.ts
1590
+ const upsertInlineFormattingContext = (state) => {
1591
+ if (state.inlineFormattingContext) state.inlineFormattingContext.atStart = false;
1592
+ else state.inlineFormattingContext = {
1593
+ atStart: true,
1594
+ lastHasTrailingWhiteSpace: false
1595
+ };
1596
+ };
1597
+ const endInlineFormattingContext = (state) => {
1598
+ state.inlineFormattingContext = null;
1599
+ };
1600
+
1601
+ //#endregion
1602
+ //#region src/lib/plugins/html/utils/collapse-white-space/collapseWhiteSpaceText.ts
1603
+ const collapseWhiteSpaceText = (text, state) => {
1604
+ const textContent = text.textContent || "";
1605
+ const isWhiteSpaceOnly = textContent.trim() === "";
1606
+ /**
1607
+ * Do not start an inline formatting context with a text node containing only
1608
+ * white space.
1609
+ */
1610
+ if (state.inlineFormattingContext || !isWhiteSpaceOnly) upsertInlineFormattingContext(state);
1611
+ const { whiteSpaceRule } = state;
1612
+ /**
1613
+ * Note: Due to the way HTML strings are parsed in htmlStringToDOMNode, up to
1614
+ * one newline is already trimmed from the start of text nodes inside <pre>
1615
+ * elements. If we do so again here, we may remove too many newlines. This
1616
+ * only applies to actual <pre> elements, not elements with the white-space
1617
+ * CSS property.
1618
+ */
1619
+ const trimStart = (() => {
1620
+ if (whiteSpaceRule !== "normal") return "collapse";
1621
+ if (!state.inlineFormattingContext || state.inlineFormattingContext.atStart || state.inlineFormattingContext.lastHasTrailingWhiteSpace) return "all";
1622
+ return "collapse";
1623
+ })();
1624
+ const trimEnd = (() => {
1625
+ if (whiteSpaceRule === "normal") return "collapse";
1626
+ if (isLastNonEmptyTextOfInlineFormattingContext(text)) return "single-newline";
1627
+ return "collapse";
1628
+ })();
1629
+ const shouldCollapseWhiteSpace = {
1630
+ normal: true,
1631
+ pre: false,
1632
+ "pre-line": true
1633
+ }[whiteSpaceRule];
1634
+ const collapsedTextContent = collapseString(textContent || "", {
1635
+ shouldCollapseWhiteSpace,
1636
+ trimEnd,
1637
+ trimStart,
1638
+ whiteSpaceIncludesNewlines: whiteSpaceRule !== "pre-line"
1639
+ });
1640
+ if (state.inlineFormattingContext && shouldCollapseWhiteSpace) state.inlineFormattingContext.lastHasTrailingWhiteSpace = collapsedTextContent.endsWith(" ");
1641
+ text.textContent = collapsedTextContent;
1642
+ };
1643
+
1644
+ //#endregion
1645
+ //#region src/lib/plugins/html/utils/collapse-white-space/collapseWhiteSpaceNode.ts
1646
+ const collapseWhiteSpaceNode = (node, state) => {
1647
+ if (isHtmlElement(node)) {
1648
+ collapseWhiteSpaceElement(node, state);
1649
+ return;
1650
+ }
1651
+ if (isHtmlText(node)) {
1652
+ collapseWhiteSpaceText(node, state);
1653
+ return;
1654
+ }
1655
+ collapseWhiteSpaceChildren(node, state);
1656
+ };
1657
+
1658
+ //#endregion
1659
+ //#region src/lib/plugins/html/utils/collapse-white-space/collapseWhiteSpaceChildren.ts
1660
+ const collapseWhiteSpaceChildren = (node, state) => {
1661
+ const childNodes = Array.from(node.childNodes);
1662
+ for (const childNode of childNodes) collapseWhiteSpaceNode(childNode, state);
1663
+ };
1664
+
1665
+ //#endregion
1666
+ //#region src/lib/plugins/html/utils/collapse-white-space/inferWhiteSpaceRule.ts
1667
+ const inferWhiteSpaceRule = (element) => {
1668
+ const whiteSpaceProperty = element.style.whiteSpace;
1669
+ switch (whiteSpaceProperty) {
1670
+ case "break-spaces":
1671
+ case "pre":
1672
+ case "pre-wrap": return "pre";
1673
+ case "normal":
1674
+ case "nowrap": return "normal";
1675
+ case "pre-line": return "pre-line";
1676
+ }
1677
+ if (element.tagName === "PRE") return "pre";
1678
+ if (whiteSpaceProperty === "initial") return "normal";
1679
+ return null;
1680
+ };
1681
+
1682
+ //#endregion
1683
+ //#region src/lib/plugins/html/utils/collapse-white-space/collapseWhiteSpaceElement.ts
1684
+ /**
1685
+ * Note: We do not want to start an inline formatting context until we encounter
1686
+ * a text node.
1687
+ */
1688
+ const collapseWhiteSpaceElement = (element, state) => {
1689
+ const isInlineElement = isHtmlInlineElement(element);
1690
+ const previousWhiteSpaceRule = state.whiteSpaceRule;
1691
+ const inferredWhiteSpaceRule = inferWhiteSpaceRule(element);
1692
+ if (inferredWhiteSpaceRule) state.whiteSpaceRule = inferredWhiteSpaceRule;
1693
+ if (!isInlineElement) endInlineFormattingContext(state);
1694
+ collapseWhiteSpaceChildren(element, state);
1695
+ if (!isInlineElement) endInlineFormattingContext(state);
1696
+ state.whiteSpaceRule = previousWhiteSpaceRule;
1697
+ };
1698
+
1699
+ //#endregion
1700
+ //#region src/lib/plugins/html/utils/collapse-white-space/collapseWhiteSpace.ts
1701
+ const collapseWhiteSpace = (element) => {
1702
+ const clonedElement = element.cloneNode(true);
1703
+ collapseWhiteSpaceElement(clonedElement, {
1704
+ inlineFormattingContext: null,
1705
+ whiteSpaceRule: "normal"
1706
+ });
1707
+ return clonedElement;
1708
+ };
1709
+
1710
+ //#endregion
1711
+ //#region src/lib/plugins/html/utils/deserializeHtmlNodeChildren.ts
1712
+ const deserializeHtmlNodeChildren = (editor, node, isSlateParent = false) => Array.from(node.childNodes).flatMap((child) => {
1713
+ if (child.nodeType === 1 && !isSlateNode(child) && isSlateParent) return deserializeHtmlNodeChildren(editor, child, isSlateParent);
1714
+ return deserializeHtmlNode(editor)(child);
1715
+ });
1716
+
1717
+ //#endregion
1718
+ //#region src/lib/plugins/html/utils/htmlBodyToFragment.ts
1719
+ /** Deserialize HTML body element to Fragment. */
1720
+ const htmlBodyToFragment = (editor, element) => {
1721
+ if (element.nodeName === "BODY") return jsx("fragment", {}, deserializeHtmlNodeChildren(editor, element));
1722
+ };
1723
+
1724
+ //#endregion
1725
+ //#region src/lib/plugins/html/utils/htmlBrToNewLine.ts
1726
+ /** Deserialize HTML to break line. */
1727
+ const htmlBrToNewLine = (node) => {
1728
+ if (node.nodeName === "BR") return "\n";
1729
+ };
1730
+
1731
+ //#endregion
1732
+ //#region src/lib/plugins/html/utils/getDataNodeProps.ts
1733
+ const getDefaultNodeProps = ({ element, type }) => {
1734
+ if (!isSlatePluginNode(element, type) && !isSlateLeaf(element)) return;
1735
+ const dataAttributes = {};
1736
+ Object.entries(element.dataset).forEach(([key, value]) => {
1737
+ if (key.startsWith("slate") && value && ![
1738
+ "slateInline",
1739
+ "slateLeaf",
1740
+ "slateNode",
1741
+ "slateVoid"
1742
+ ].includes(key)) {
1743
+ const attributeKey = key.slice(5).charAt(0).toLowerCase() + key.slice(6);
1744
+ if (value === void 0) return;
1745
+ let parsedValue = value;
1746
+ if (value === "true") parsedValue = true;
1747
+ else if (value === "false") parsedValue = false;
1748
+ else if (!Number.isNaN(Number(value))) parsedValue = Number(value);
1749
+ dataAttributes[attributeKey] = parsedValue;
1750
+ }
1751
+ });
1752
+ if (Object.keys(dataAttributes).length > 0) return dataAttributes;
1753
+ };
1754
+ const getDataNodeProps = ({ editor, element, plugin }) => {
1755
+ const toNodeProps = plugin.parsers.html?.deserializer?.toNodeProps;
1756
+ const defaultNodeProps = plugin.parsers.html?.deserializer?.disableDefaultNodeProps ?? false ? {} : getDefaultNodeProps({
1757
+ ...getEditorPlugin(editor, plugin),
1758
+ element
1759
+ });
1760
+ if (!toNodeProps) return defaultNodeProps;
1761
+ const customNodeProps = toNodeProps({
1762
+ ...getEditorPlugin(editor, plugin),
1763
+ element
1764
+ }) ?? {};
1765
+ return {
1766
+ ...defaultNodeProps,
1767
+ ...customNodeProps
1768
+ };
1769
+ };
1770
+
1771
+ //#endregion
1772
+ //#region src/lib/plugins/html/utils/pluginDeserializeHtml.ts
1773
+ /**
1774
+ * Get a deserializer and add default rules for deserializing plate static
1775
+ * elements
1776
+ */
1777
+ const getDeserializedWithStaticRules = (plugin) => {
1778
+ let deserializer = plugin.parsers?.html?.deserializer;
1779
+ const rules = deserializer?.rules ?? [];
1780
+ const staticRules = rules.some((rule) => rule.validClassName?.includes(`slate-${plugin.key}`)) ? rules : [{
1781
+ validClassName: `slate-${plugin.key}`,
1782
+ validNodeName: "*"
1783
+ }, ...rules];
1784
+ if (!deserializer) deserializer = { rules: staticRules };
1785
+ deserializer.rules = staticRules;
1786
+ return deserializer;
1787
+ };
1788
+ /** Get a deserializer by type, node names, class names and styles. */
1789
+ const pluginDeserializeHtml = (editor, plugin, { deserializeLeaf, element: el }) => {
1790
+ const { node: { isElement: isElementRoot, isLeaf: isLeafRoot } } = plugin;
1791
+ const deserializer = getDeserializedWithStaticRules(plugin);
1792
+ if (!deserializer) return;
1793
+ const { attributeNames, isElement: isElementRule, isLeaf: isLeafRule, query, rules } = deserializer;
1794
+ let { parse } = deserializer;
1795
+ const isElement = isElementRule || isElementRoot;
1796
+ const isLeaf = isLeafRule || isLeafRoot;
1797
+ if (!deserializeLeaf && !isElement) return;
1798
+ if (deserializeLeaf && !isLeaf) return;
1799
+ if (rules) {
1800
+ if (!rules.some(({ validAttribute, validClassName, validNodeName = "*", validStyle }) => {
1801
+ if (validNodeName) {
1802
+ const validNodeNames = castArray(validNodeName);
1803
+ if (validNodeNames.length > 0 && !validNodeNames.includes(el.nodeName) && validNodeName !== "*") return false;
1804
+ }
1805
+ if (validClassName && !el.classList.contains(validClassName)) return false;
1806
+ if (validStyle) for (const [key, value] of Object.entries(validStyle)) {
1807
+ if (!castArray(value).includes(el.style[key]) && value !== "*") return false;
1808
+ if (value === "*" && !el.style[key]) return false;
1809
+ const defaultNodeValue = plugin.inject.nodeProps?.defaultNodeValue;
1810
+ if (defaultNodeValue && defaultNodeValue === el.style[key]) return false;
1811
+ }
1812
+ if (validAttribute) if (typeof validAttribute === "string") {
1813
+ if (!el.getAttributeNames().includes(validAttribute)) return false;
1814
+ } else for (const [attributeName, attributeValue] of Object.entries(validAttribute)) {
1815
+ const attributeValues = castArray(attributeValue);
1816
+ const elAttribute = el.getAttribute(attributeName);
1817
+ if (!isDefined(elAttribute) || !attributeValues.includes(elAttribute)) return false;
1818
+ }
1819
+ return true;
1820
+ })) return;
1821
+ }
1822
+ if (query && !query({
1823
+ ...getEditorPlugin(editor, plugin),
1824
+ element: el
1825
+ })) return;
1826
+ if (!parse) if (isElement) parse = ({ type }) => ({ type });
1827
+ else if (isLeaf) parse = ({ type }) => ({ [type]: true });
1828
+ else return;
1829
+ const parsedNode = (() => {
1830
+ if (isSlateNode(el)) return {};
1831
+ return parse({
1832
+ ...getEditorPlugin(editor, plugin),
1833
+ element: el,
1834
+ node: {}
1835
+ }) ?? {};
1836
+ })();
1837
+ const dataNodeProps = getDataNodeProps({
1838
+ editor,
1839
+ element: el,
1840
+ plugin
1841
+ });
1842
+ let node = {
1843
+ ...parsedNode,
1844
+ ...dataNodeProps
1845
+ };
1846
+ if (Object.keys(node).length === 0) return;
1847
+ getInjectedPlugins(editor, plugin).forEach((injectedPlugin) => {
1848
+ const res = injectedPlugin.parsers?.html?.deserializer?.parse?.({
1849
+ ...getEditorPlugin(editor, plugin),
1850
+ element: el,
1851
+ node
1852
+ });
1853
+ if (res && !isSlateNode(el)) node = {
1854
+ ...node,
1855
+ ...res
1856
+ };
1857
+ });
1858
+ if (attributeNames) {
1859
+ const elementAttributes = {};
1860
+ const elementAttributeNames = el.getAttributeNames();
1861
+ for (const elementAttributeName of elementAttributeNames) if (attributeNames.includes(elementAttributeName)) elementAttributes[elementAttributeName] = el.getAttribute(elementAttributeName);
1862
+ if (Object.keys(elementAttributes).length > 0) node.attributes = elementAttributes;
1863
+ }
1864
+ return {
1865
+ ...deserializer,
1866
+ node
1867
+ };
1868
+ };
1869
+
1870
+ //#endregion
1871
+ //#region src/lib/plugins/html/utils/pipeDeserializeHtmlElement.ts
1872
+ const pipeDeserializeHtmlElement = (editor, element) => {
1873
+ let result;
1874
+ [...editor.meta.pluginList].reverse().some((plugin) => {
1875
+ result = pluginDeserializeHtml(editor, plugin, { element });
1876
+ return !!result;
1877
+ });
1878
+ return result;
1879
+ };
1880
+
1881
+ //#endregion
1882
+ //#region src/lib/plugins/html/utils/htmlElementToElement.ts
1883
+ /** Deserialize HTML to Element. */
1884
+ const htmlElementToElement = (editor, element, isSlate = false) => {
1885
+ const deserialized = pipeDeserializeHtmlElement(editor, element);
1886
+ if (deserialized) {
1887
+ const { node, withoutChildren } = deserialized;
1888
+ let descendants = node.children ?? deserializeHtmlNodeChildren(editor, element, isSlate);
1889
+ if (descendants.length === 0 || withoutChildren || isSlateVoid(element)) descendants = [{ text: "" }];
1890
+ return jsx("element", node, descendants);
1891
+ }
1892
+ };
1893
+
1894
+ //#endregion
1895
+ //#region src/lib/plugins/html/utils/pipeDeserializeHtmlLeaf.ts
1896
+ const pipeDeserializeHtmlLeaf = (editor, element) => {
1897
+ let node = {};
1898
+ [...editor.meta.pluginList].reverse().forEach((plugin) => {
1899
+ const deserialized = pluginDeserializeHtml(editor, plugin, {
1900
+ deserializeLeaf: true,
1901
+ element
1902
+ });
1903
+ if (!deserialized) return;
1904
+ node = {
1905
+ ...node,
1906
+ ...deserialized.node
1907
+ };
1908
+ });
1909
+ return node;
1910
+ };
1911
+
1912
+ //#endregion
1913
+ //#region src/lib/plugins/html/utils/htmlElementToLeaf.ts
1914
+ /**
1915
+ * Deserialize HTML to Descendant[] with marks on Text. Build the leaf from the
1916
+ * leaf deserializers of each plugin.
1917
+ */
1918
+ const htmlElementToLeaf = (editor, element) => {
1919
+ const node = pipeDeserializeHtmlLeaf(editor, element);
1920
+ return deserializeHtmlNodeChildren(editor, element).reduce((arr, child) => {
1921
+ if (!child) return arr;
1922
+ if (ElementApi.isElement(child)) {
1923
+ if (Object.keys(node).length > 0) mergeDeepToNodes({
1924
+ node: child,
1925
+ query: { filter: ([n]) => TextApi.isText(n) },
1926
+ source: node
1927
+ });
1928
+ arr.push(child);
1929
+ } else {
1930
+ const attributes = { ...node };
1931
+ if (TextApi.isText(child) && child.text) Object.keys(attributes).forEach((key) => {
1932
+ if (attributes[key] && child[key]) attributes[key] = child[key];
1933
+ });
1934
+ arr.push(jsx("text", attributes, child));
1935
+ }
1936
+ return arr;
1937
+ }, []);
1938
+ };
1939
+
1940
+ //#endregion
1941
+ //#region src/lib/plugins/html/utils/htmlTextNodeToString.ts
1942
+ /** Deserialize HTML text node to text. */
1943
+ const htmlTextNodeToString = (node) => {
1944
+ if (isHtmlText(node)) {
1945
+ if (node.parentElement?.dataset.platePreventDeserialization) return "";
1946
+ return node.textContent || "";
1947
+ }
1948
+ };
1949
+
1950
+ //#endregion
1951
+ //#region src/lib/plugins/html/utils/deserializeHtmlNode.ts
1952
+ /** Check if a BR tag should be converted to an empty paragraph. */
1953
+ const shouldBrBecomeEmptyParagraph = (node) => {
1954
+ if (node.nodeName !== "BR") return false;
1955
+ if (node.className === "Apple-interchange-newline") return false;
1956
+ const parent = node.parentElement;
1957
+ if (!parent) return false;
1958
+ if (parent.tagName === "P" || parent.tagName === "SPAN") return false;
1959
+ const hasAdjacentText = () => {
1960
+ let sibling = node.previousSibling;
1961
+ while (sibling) {
1962
+ if (sibling.nodeType === Node.TEXT_NODE && sibling.textContent?.trim()) return true;
1963
+ sibling = sibling.previousSibling;
1964
+ }
1965
+ sibling = node.nextSibling;
1966
+ while (sibling) {
1967
+ if (sibling.nodeType === Node.TEXT_NODE && sibling.textContent?.trim()) return true;
1968
+ sibling = sibling.nextSibling;
1969
+ }
1970
+ return false;
1971
+ };
1972
+ if (hasAdjacentText()) return false;
1973
+ return true;
1974
+ };
1975
+ /** Deserialize HTML element or child node. */
1976
+ const deserializeHtmlNode = (editor) => (node) => {
1977
+ const textNode = htmlTextNodeToString(node);
1978
+ if (textNode) return textNode;
1979
+ if (!isHtmlElement(node)) return null;
1980
+ if (shouldBrBecomeEmptyParagraph(node)) return {
1981
+ children: [{ text: "" }],
1982
+ type: editor.getType("p")
1983
+ };
1984
+ if (node.nodeName === "BR" && node.className === "Apple-interchange-newline") return null;
1985
+ const breakLine = htmlBrToNewLine(node);
1986
+ if (breakLine) return breakLine;
1987
+ const fragment = htmlBodyToFragment(editor, node);
1988
+ if (fragment) return fragment;
1989
+ const element = htmlElementToElement(editor, node, isSlateNode(node));
1990
+ if (element) return element;
1991
+ return htmlElementToLeaf(editor, node);
1992
+ };
1993
+
1994
+ //#endregion
1995
+ //#region src/lib/plugins/html/utils/deserializeHtmlElement.ts
1996
+ /** Deserialize HTML element to fragment. */
1997
+ const deserializeHtmlElement = (editor, element) => deserializeHtmlNode(editor)(element);
1998
+
1999
+ //#endregion
2000
+ //#region src/lib/plugins/html/utils/htmlStringToDOMNode.ts
2001
+ /** Convert HTML string into HTML element. */
2002
+ const htmlStringToDOMNode = (rawHtml) => {
2003
+ const node = document.createElement("body");
2004
+ node.innerHTML = rawHtml;
2005
+ return node;
2006
+ };
2007
+
2008
+ //#endregion
2009
+ //#region src/lib/plugins/html/utils/deserializeHtml.ts
2010
+ /** Deserialize HTML element to a valid document fragment. */
2011
+ const deserializeHtml = (editor, { collapseWhiteSpace: shouldCollapseWhiteSpace = true, defaultElementPlugin, element }) => {
2012
+ if (typeof element === "string") element = htmlStringToDOMNode(element);
2013
+ if (shouldCollapseWhiteSpace) element = collapseWhiteSpace(element);
2014
+ return normalizeDescendantsToDocumentFragment(editor, {
2015
+ defaultElementPlugin,
2016
+ descendants: deserializeHtmlElement(editor, element)
2017
+ });
2018
+ };
2019
+
2020
+ //#endregion
2021
+ //#region src/lib/plugins/html/utils/parseHtmlDocument.ts
2022
+ const parseHtmlDocument = (html) => new DOMParser().parseFromString(html, "text/html");
2023
+
2024
+ //#endregion
2025
+ //#region src/lib/plugins/html/HtmlPlugin.ts
2026
+ /**
2027
+ * Enables support for deserializing inserted content from HTML format to Slate
2028
+ * format and serializing Slate content to HTML format.
2029
+ */
2030
+ const HtmlPlugin = createSlatePlugin({ key: "html" }).extendApi(({ editor }) => ({ deserialize: bindFirst(deserializeHtml, editor) })).extend({ parser: {
2031
+ format: "text/html",
2032
+ deserialize: ({ api, data }) => {
2033
+ const document$1 = parseHtmlDocument(data);
2034
+ return api.html.deserialize({ element: document$1.body });
2035
+ }
2036
+ } });
2037
+
2038
+ //#endregion
2039
+ //#region src/lib/plugins/length/LengthPlugin.ts
2040
+ const LengthPlugin = createTSlatePlugin({ key: "length" }).overrideEditor(({ editor, getOptions, tf: { apply } }) => ({ transforms: { apply(operation) {
2041
+ editor.tf.withoutNormalizing(() => {
2042
+ apply(operation);
2043
+ const options = getOptions();
2044
+ if (options.maxLength) {
2045
+ const length = editor.api.string([]).length;
2046
+ if (length > options.maxLength) {
2047
+ const overflowLength = length - options.maxLength;
2048
+ editor.tf.delete({
2049
+ distance: overflowLength,
2050
+ reverse: true,
2051
+ unit: "character"
2052
+ });
2053
+ }
2054
+ }
2055
+ });
2056
+ } } }));
2057
+
2058
+ //#endregion
2059
+ //#region src/lib/plugins/node-id/withNodeId.ts
2060
+ /** Enables support for inserting nodes with an id key. */
2061
+ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes } }) => {
2062
+ const idPropsCreator = () => ({ [getOptions().idKey ?? ""]: getOptions().idCreator() });
2063
+ const filterNode = (nodeEntry) => {
2064
+ const { filter, filterText } = getOptions();
2065
+ return filter(nodeEntry) && (!filterText || nodeEntry[0]?.type !== void 0);
2066
+ };
2067
+ const removeIdFromNodeIfDuplicate = (node) => {
2068
+ const { idKey = "", reuseId } = getOptions();
2069
+ if (!reuseId && editor.api.some({
2070
+ at: [],
2071
+ match: { [idKey]: node[idKey] }
2072
+ })) delete node[idKey];
2073
+ };
2074
+ const overrideIdIfSet = (node) => {
2075
+ const { idKey = "" } = getOptions();
2076
+ if (isDefined(node._id)) {
2077
+ const id = node._id;
2078
+ node._id = void 0;
2079
+ if (!editor.api.some({
2080
+ at: [],
2081
+ match: { [idKey]: id }
2082
+ })) node[idKey] = id;
2083
+ }
2084
+ };
2085
+ return { transforms: {
2086
+ apply(operation) {
2087
+ const { allow, disableInsertOverrides, exclude, idCreator, idKey = "", reuseId } = getOptions();
2088
+ const query = {
2089
+ allow,
2090
+ exclude,
2091
+ filter: filterNode
2092
+ };
2093
+ if (operation.type === "insert_node") {
2094
+ const node = cloneDeep(operation.node);
2095
+ applyDeepToNodes({
2096
+ apply: removeIdFromNodeIfDuplicate,
2097
+ node,
2098
+ query,
2099
+ source: {}
2100
+ });
2101
+ defaultsDeepToNodes({
2102
+ node,
2103
+ path: operation.path,
2104
+ query,
2105
+ source: idPropsCreator
2106
+ });
2107
+ if (!disableInsertOverrides) applyDeepToNodes({
2108
+ apply: overrideIdIfSet,
2109
+ node,
2110
+ query,
2111
+ source: {}
2112
+ });
2113
+ return apply({
2114
+ ...operation,
2115
+ node
2116
+ });
2117
+ }
2118
+ if (operation.type === "split_node") {
2119
+ const node = operation.properties;
2120
+ let id = operation.properties[idKey];
2121
+ if (queryNode([node, operation.path], query)) {
2122
+ /**
2123
+ * Create a new id if:
2124
+ *
2125
+ * - The id in the new node is already being used in the editor or,
2126
+ * - The node has no id
2127
+ */
2128
+ if (!reuseId || id === void 0 || editor.api.some({
2129
+ at: [],
2130
+ match: { [idKey]: id }
2131
+ })) id = idCreator();
2132
+ return apply({
2133
+ ...operation,
2134
+ properties: {
2135
+ ...operation.properties,
2136
+ [idKey]: id
2137
+ }
2138
+ });
2139
+ }
2140
+ if (id) delete operation.properties[idKey];
2141
+ }
2142
+ return apply(operation);
2143
+ },
2144
+ insertNode(node) {
2145
+ const { disableInsertOverrides, idKey = "" } = getOptions();
2146
+ if (!disableInsertOverrides && node[idKey]) {
2147
+ if (!Object.isExtensible(node)) node = cloneDeep(node);
2148
+ node._id = node[idKey];
2149
+ }
2150
+ insertNode(node);
2151
+ },
2152
+ insertNodes(_nodes, options) {
2153
+ const nodes = castArray(_nodes).filter((node) => !!node);
2154
+ if (nodes.length === 0) return;
2155
+ const { disableInsertOverrides, idKey = "" } = getOptions();
2156
+ insertNodes(nodes.map((node) => {
2157
+ if (!disableInsertOverrides && node[idKey]) {
2158
+ if (!Object.isExtensible(node)) node = cloneDeep(node);
2159
+ node._id = node[idKey];
2160
+ }
2161
+ return node;
2162
+ }), options);
2163
+ }
2164
+ } };
2165
+ };
2166
+
2167
+ //#endregion
2168
+ //#region src/lib/plugins/node-id/NodeIdPlugin.ts
2169
+ /**
2170
+ * Normalize node IDs in a value without using editor operations. This is a pure
2171
+ * function that returns a new normalized value.
2172
+ */
2173
+ const normalizeNodeId = (value, options = {}) => {
2174
+ const { allow, exclude, filter = () => true, filterInline = true, filterText = true, idCreator = () => nanoid(10), idKey = "id" } = options;
2175
+ const normalizeNode = (node, path) => {
2176
+ const clonedNode = { ...node };
2177
+ if (!clonedNode[idKey] && queryNode([clonedNode, path], {
2178
+ allow,
2179
+ exclude,
2180
+ filter: (entry) => {
2181
+ const [node$1] = entry;
2182
+ if (filterText && !ElementApi.isElement(node$1)) return false;
2183
+ if (filterInline && ElementApi.isElement(node$1) && node$1.inline === true) return false;
2184
+ return filter(entry);
2185
+ }
2186
+ })) clonedNode[idKey] = idCreator();
2187
+ if (ElementApi.isElement(clonedNode)) clonedNode.children = clonedNode.children.map((child, index) => normalizeNode(child, [...path, index]));
2188
+ return clonedNode;
2189
+ };
2190
+ return value.map((node, index) => normalizeNode(node, [index]));
2191
+ };
2192
+ /** @see {@link withNodeId} */
2193
+ const NodeIdPlugin = createTSlatePlugin({
2194
+ key: "nodeId",
2195
+ options: {
2196
+ filterInline: true,
2197
+ filterText: true,
2198
+ idKey: "id",
2199
+ normalizeInitialValue: false,
2200
+ filter: () => true,
2201
+ idCreator: () => nanoid(10)
2202
+ }
2203
+ }).extendTransforms(({ editor, getOptions }) => ({ normalize() {
2204
+ const { allow, exclude, filter, filterInline, filterText, idKey } = getOptions();
2205
+ const addNodeId = (entry) => {
2206
+ const [node, path] = entry;
2207
+ if (!node[idKey] && queryNode([node, path], {
2208
+ allow,
2209
+ exclude,
2210
+ filter: (entry$1) => {
2211
+ const [node$1] = entry$1;
2212
+ if (filterText && !ElementApi.isElement(node$1)) return false;
2213
+ if (filterInline && ElementApi.isElement(node$1) && !editor.api.isBlock(node$1)) return false;
2214
+ return filter(entry$1);
2215
+ }
2216
+ })) {
2217
+ if (!editor.api.node(path)) return;
2218
+ editor.tf.withoutSaving(() => {
2219
+ editor.tf.setNodes({ [idKey]: getOptions().idCreator() }, { at: path });
2220
+ });
2221
+ }
2222
+ if (ElementApi.isElement(node)) node.children.forEach((child, index) => {
2223
+ addNodeId([child, [...path, index]]);
2224
+ });
2225
+ };
2226
+ editor.children.forEach((node, index) => {
2227
+ addNodeId([node, [index]]);
2228
+ });
2229
+ } })).extend({ normalizeInitialValue: ({ editor, getOptions, tf }) => {
2230
+ const { normalizeInitialValue } = getOptions();
2231
+ if (!normalizeInitialValue) {
2232
+ const firstNode = editor.children[0];
2233
+ const lastNode = editor.children.at(-1);
2234
+ if (firstNode?.id && lastNode?.id) return;
2235
+ }
2236
+ tf.nodeId.normalize();
2237
+ } }).overrideEditor(withNodeId);
2238
+
2239
+ //#endregion
2240
+ //#region src/lib/utils/pipeOnNodeChange.ts
2241
+ const pipeOnNodeChange = (editor, node, prevNode, operation) => {
2242
+ return editor.meta.pluginCache.handlers.onNodeChange.some((key) => {
2243
+ const plugin = editor.getPlugin({ key });
2244
+ if (!plugin || editor.dom?.readOnly) return false;
2245
+ const handler = plugin.handlers?.onNodeChange;
2246
+ if (!handler) return false;
2247
+ const shouldTreatEventAsHandled = handler({
2248
+ editor,
2249
+ node,
2250
+ operation,
2251
+ plugin,
2252
+ prevNode
2253
+ });
2254
+ if (shouldTreatEventAsHandled != null) return shouldTreatEventAsHandled;
2255
+ return false;
2256
+ });
2257
+ };
2258
+
2259
+ //#endregion
2260
+ //#region src/lib/utils/pipeOnTextChange.ts
2261
+ const pipeOnTextChange = (editor, node, text, prevText, operation) => {
2262
+ return editor.meta.pluginCache.handlers.onTextChange.some((key) => {
2263
+ const plugin = editor.getPlugin({ key });
2264
+ if (!plugin || editor.dom?.readOnly) return false;
2265
+ const handler = plugin.handlers?.onTextChange;
2266
+ if (!handler) return false;
2267
+ const shouldTreatEventAsHandled = handler({
2268
+ editor,
2269
+ node,
2270
+ operation,
2271
+ plugin,
2272
+ prevText,
2273
+ text
2274
+ });
2275
+ if (shouldTreatEventAsHandled != null) return shouldTreatEventAsHandled;
2276
+ return false;
2277
+ });
2278
+ };
2279
+
2280
+ //#endregion
2281
+ //#region src/internal/plugin/isEditOnlyDisabled.ts
2282
+ const DEFAULT = {
2283
+ handlers: true,
2284
+ inject: true,
2285
+ normalizeInitialValue: false,
2286
+ render: true
2287
+ };
2288
+ /**
2289
+ * Check if a plugin feature is disabled in read-only mode based on editOnly
2290
+ * configuration.
2291
+ *
2292
+ * @param plugin The plugin to check
2293
+ * @param isReadOnly Whether the editor is in read-only mode
2294
+ * @param feature The feature to check ('render' | 'handlers' | 'inject' |
2295
+ * 'normalizeInitialValue')
2296
+ * @returns True if the feature should be disabled
2297
+ */
2298
+ const isEditOnly = (readOnly, plugin, feature) => {
2299
+ if (!readOnly) return false;
2300
+ if (plugin.editOnly === true) return DEFAULT[feature];
2301
+ if (typeof plugin.editOnly === "object") return plugin.editOnly[feature] ?? DEFAULT[feature];
2302
+ return false;
2303
+ };
2304
+
2305
+ //#endregion
2306
+ //#region src/internal/plugin/pipeNormalizeInitialValue.ts
2307
+ /** Normalize initial value from editor plugins. Set into plate store if diff. */
2308
+ const pipeNormalizeInitialValue = (editor) => {
2309
+ const value = editor.meta.isNormalizing;
2310
+ editor.meta.isNormalizing = true;
2311
+ editor.meta.pluginCache.normalizeInitialValue.forEach((key) => {
2312
+ const p = editor.getPlugin({ key });
2313
+ if (isEditOnly(editor.dom.readOnly, p, "normalizeInitialValue")) return;
2314
+ p.normalizeInitialValue?.({
2315
+ ...getEditorPlugin(editor, p),
2316
+ value: editor.children
2317
+ });
2318
+ });
2319
+ editor.meta.isNormalizing = value;
2320
+ };
2321
+
2322
+ //#endregion
2323
+ //#region src/lib/plugins/slate-extension/transforms/init.ts
2324
+ const init = (editor, { autoSelect, selection, shouldNormalizeEditor, value, onReady }) => {
2325
+ const onValueLoaded = (isAsync = false) => {
2326
+ if (!editor.children || editor.children?.length === 0) editor.children = editor.api.create.value();
2327
+ if (selection) editor.selection = selection;
2328
+ else if (autoSelect) {
2329
+ const target = (autoSelect === "start" ? "start" : "end") === "start" ? editor.api.start([]) : editor.api.end([]);
2330
+ editor.tf.select(target);
2331
+ }
2332
+ if (editor.children.length > 0) pipeNormalizeInitialValue(editor);
2333
+ if (shouldNormalizeEditor) editor.tf.normalize({ force: true });
2334
+ if (onReady) onReady({
2335
+ editor,
2336
+ isAsync,
2337
+ value: editor.children
2338
+ });
2339
+ };
2340
+ if (value === null) onValueLoaded();
2341
+ else if (typeof value === "string") {
2342
+ editor.children = editor.api.html.deserialize({ element: value });
2343
+ onValueLoaded();
2344
+ } else if (typeof value === "function") {
2345
+ const result = value(editor);
2346
+ if (result && typeof result.then === "function") result.then((resolvedValue) => {
2347
+ editor.children = resolvedValue;
2348
+ onValueLoaded(true);
2349
+ });
2350
+ else {
2351
+ editor.children = result;
2352
+ onValueLoaded();
2353
+ }
2354
+ } else if (value) {
2355
+ editor.children = value;
2356
+ onValueLoaded();
2357
+ } else onValueLoaded();
2358
+ };
2359
+
2360
+ //#endregion
2361
+ //#region src/lib/plugins/slate-extension/transforms/insertExitBreak.ts
2362
+ /**
2363
+ * Exits the current block structure by creating a new block next to the
2364
+ * appropriate ancestor.
2365
+ *
2366
+ * This function automatically determines the exit point by finding the first
2367
+ * ancestor that doesn't have strict sibling constraints (`isStrictSiblings:
2368
+ * false`), allowing standard text blocks to be inserted as siblings.
2369
+ *
2370
+ * For example:
2371
+ *
2372
+ * - In `column_group > column > codeblock > codeline`, exits after `codeblock`,
2373
+ * then after `column_group`
2374
+ * - In `table > tr > td > p`, exits after `table`
2375
+ */
2376
+ const insertExitBreak = (editor, { match, reverse } = {}) => {
2377
+ if (!editor.selection || !editor.api.isCollapsed()) return;
2378
+ const block = editor.api.block();
2379
+ if (!block) return;
2380
+ const ancestorPath = editor.api.above({
2381
+ at: block[1],
2382
+ match: combineMatchOptions(editor, (n, p) => p.length === 1 || p.length > 1 && !!n.type && !getPluginByType(editor, n.type)?.node.isStrictSiblings, { match })
2383
+ })?.[1] ?? block[1];
2384
+ const targetPath = reverse ? ancestorPath : PathApi.next(ancestorPath);
2385
+ if (!targetPath) return;
2386
+ editor.tf.insertNodes(editor.api.create.block(), {
2387
+ at: targetPath,
2388
+ select: true
2389
+ });
2390
+ return true;
2391
+ };
2392
+
2393
+ //#endregion
2394
+ //#region src/lib/plugins/slate-extension/transforms/resetBlock.ts
2395
+ /**
2396
+ * Reset the current block to a paragraph, removing all properties except id and
2397
+ * type.
2398
+ */
2399
+ const resetBlock = (editor, { at } = {}) => {
2400
+ const entry = editor.api.block({ at });
2401
+ if (!entry?.[0]) return;
2402
+ const [block, path] = entry;
2403
+ editor.tf.withoutNormalizing(() => {
2404
+ const { id, type, ...otherProps } = NodeApi.extractProps(block);
2405
+ Object.keys(otherProps).forEach((key) => {
2406
+ editor.tf.unsetNodes(key, { at: path });
2407
+ });
2408
+ const paragraphType = editor.getType(BaseParagraphPlugin.key);
2409
+ if (block.type !== paragraphType) editor.tf.setNodes({ type: paragraphType }, { at: path });
2410
+ });
2411
+ return true;
2412
+ };
2413
+
2414
+ //#endregion
2415
+ //#region src/lib/plugins/slate-extension/transforms/setValue.ts
2416
+ const setValue = (editor, value) => {
2417
+ let children = value;
2418
+ if (typeof value === "string") children = editor.api.html.deserialize({ element: value });
2419
+ else if (!value || value.length === 0) children = editor.api.create.value();
2420
+ editor.tf.replaceNodes(children, {
2421
+ at: [],
2422
+ children: true
2423
+ });
2424
+ };
2425
+
2426
+ //#endregion
2427
+ //#region src/lib/plugins/slate-extension/SlateExtensionPlugin.ts
2428
+ /** Opinionated extension of slate default behavior. */
2429
+ const SlateExtensionPlugin = createTSlatePlugin({
2430
+ key: "slateExtension",
2431
+ options: {
2432
+ onNodeChange: () => {},
2433
+ onTextChange: () => {}
2434
+ }
2435
+ }).extendEditorTransforms(({ editor, getOption, tf: { apply } }) => ({
2436
+ init: bindFirst(init, editor),
2437
+ insertExitBreak: bindFirst(insertExitBreak, editor),
2438
+ resetBlock: bindFirst(resetBlock, editor),
2439
+ setValue: bindFirst(setValue, editor),
2440
+ apply(operation) {
2441
+ const noop = () => {};
2442
+ const hasNodeHandlers = editor.meta.pluginCache.handlers.onNodeChange.length > 0 || getOption("onNodeChange") !== noop;
2443
+ const hasTextHandlers = editor.meta.pluginCache.handlers.onTextChange.length > 0 || getOption("onTextChange") !== noop;
2444
+ if (!hasNodeHandlers && !hasTextHandlers) {
2445
+ apply(operation);
2446
+ return;
2447
+ }
2448
+ let prevNode;
2449
+ let node;
2450
+ let prevText;
2451
+ let text;
2452
+ let parentNode;
2453
+ if (OperationApi.isNodeOperation(operation) && hasNodeHandlers) switch (operation.type) {
2454
+ case "insert_node":
2455
+ prevNode = operation.node;
2456
+ node = operation.node;
2457
+ break;
2458
+ case "merge_node":
2459
+ case "move_node":
2460
+ case "set_node":
2461
+ case "split_node":
2462
+ prevNode = NodeApi.get(editor, operation.path);
2463
+ break;
2464
+ case "remove_node":
2465
+ prevNode = operation.node;
2466
+ node = operation.node;
2467
+ break;
2468
+ }
2469
+ else if (OperationApi.isTextOperation(operation) && hasTextHandlers) {
2470
+ const parentPath = PathApi.parent(operation.path);
2471
+ parentNode = NodeApi.get(editor, parentPath);
2472
+ prevText = NodeApi.get(editor, operation.path).text;
2473
+ }
2474
+ apply(operation);
2475
+ if (OperationApi.isNodeOperation(operation) && hasNodeHandlers) {
2476
+ switch (operation.type) {
2477
+ case "insert_node":
2478
+ case "remove_node": break;
2479
+ case "merge_node": {
2480
+ const prevPath = PathApi.previous(operation.path);
2481
+ if (prevPath) node = NodeApi.get(editor, prevPath);
2482
+ break;
2483
+ }
2484
+ case "move_node":
2485
+ node = NodeApi.get(editor, operation.newPath);
2486
+ break;
2487
+ case "set_node":
2488
+ node = NodeApi.get(editor, operation.path);
2489
+ break;
2490
+ case "split_node":
2491
+ node = NodeApi.get(editor, operation.path);
2492
+ break;
2493
+ }
2494
+ if (!node) node = prevNode;
2495
+ if (!pipeOnNodeChange(editor, node, prevNode, operation)) getOption("onNodeChange")({
2496
+ editor,
2497
+ node,
2498
+ operation,
2499
+ prevNode
2500
+ });
2501
+ }
2502
+ if (OperationApi.isTextOperation(operation) && hasTextHandlers) {
2503
+ const textNodeAfter = NodeApi.get(editor, operation.path);
2504
+ if (textNodeAfter) text = textNodeAfter.text;
2505
+ if (!pipeOnTextChange(editor, parentNode, text, prevText, operation)) getOption("onTextChange")({
2506
+ editor,
2507
+ node: parentNode,
2508
+ operation,
2509
+ prevText,
2510
+ text
2511
+ });
2512
+ }
2513
+ }
2514
+ }));
2515
+
2516
+ //#endregion
2517
+ //#region src/lib/utils/normalizeDescendantsToDocumentFragment.ts
2518
+ const isInlineNode = (editor) => (node) => TextApi.isText(node) || ElementApi.isElement(node) && editor.api.isInline(node);
2519
+ const makeBlockLazy = (type) => () => ({
2520
+ children: [],
2521
+ type
2522
+ });
2523
+ const hasDifferentChildNodes = (descendants, isInline) => descendants.some((descendant, index, arr) => {
2524
+ const prevDescendant = arr[index - 1];
2525
+ if (index !== 0) return isInline(descendant) !== isInline(prevDescendant);
2526
+ return false;
2527
+ });
2528
+ /**
2529
+ * Handles 3rd constraint: "Block nodes can only contain other blocks, or inline
2530
+ * and text nodes."
2531
+ */
2532
+ const normalizeDifferentNodeTypes = (descendants, isInline, makeDefaultBlock) => {
2533
+ const hasDifferentNodes = hasDifferentChildNodes(descendants, isInline);
2534
+ const { fragment } = descendants.reduce((memo, node) => {
2535
+ if (hasDifferentNodes && isInline(node)) {
2536
+ let block = memo.precedingBlock;
2537
+ if (!block) {
2538
+ block = makeDefaultBlock();
2539
+ memo.precedingBlock = block;
2540
+ memo.fragment.push(block);
2541
+ }
2542
+ block.children.push(node);
2543
+ } else {
2544
+ memo.fragment.push(node);
2545
+ memo.precedingBlock = null;
2546
+ }
2547
+ return memo;
2548
+ }, {
2549
+ fragment: [],
2550
+ precedingBlock: null
2551
+ });
2552
+ return fragment;
2553
+ };
2554
+ /**
2555
+ * Handles 1st constraint: "All Element nodes must contain at least one Text
2556
+ * descendant."
2557
+ */
2558
+ const normalizeEmptyChildren = (descendants) => {
2559
+ if (descendants.length === 0) return [{ text: "" }];
2560
+ return descendants;
2561
+ };
2562
+ const normalize = (descendants, isInline, makeDefaultBlock) => {
2563
+ descendants = normalizeEmptyChildren(descendants);
2564
+ descendants = normalizeDifferentNodeTypes(descendants, isInline, makeDefaultBlock);
2565
+ descendants = descendants.map((node) => {
2566
+ if (ElementApi.isElement(node)) return {
2567
+ ...node,
2568
+ children: normalize(node.children, isInline, makeDefaultBlock)
2569
+ };
2570
+ return node;
2571
+ });
2572
+ return descendants;
2573
+ };
2574
+ /** Normalize the descendants to a valid document fragment. */
2575
+ const normalizeDescendantsToDocumentFragment = (editor, { defaultElementPlugin = BaseParagraphPlugin, descendants }) => {
2576
+ return normalize(descendants, isInlineNode(editor), makeBlockLazy(editor.getType(defaultElementPlugin.key)));
2577
+ };
2578
+
2579
+ //#endregion
2580
+ //#region src/lib/utils/pipeInsertDataQuery.ts
2581
+ /** Is the plugin disabled by another plugin. */
2582
+ const pipeInsertDataQuery = (editor, plugins, options) => plugins.every((p) => {
2583
+ const query = p.parser?.query;
2584
+ return !query || query({
2585
+ ...getEditorPlugin(editor, p),
2586
+ ...options
2587
+ });
2588
+ });
2589
+
2590
+ //#endregion
2591
+ //#region src/lib/plugins/ParserPlugin.ts
2592
+ const ParserPlugin = createSlatePlugin({ key: "parser" }).overrideEditor(({ editor, tf: { insertData } }) => ({ transforms: { insertData(dataTransfer) {
2593
+ if ([...editor.meta.pluginList].reverse().some((plugin) => {
2594
+ const parser = plugin.parser;
2595
+ if (!parser) return false;
2596
+ const injectedPlugins = getInjectedPlugins(editor, plugin);
2597
+ const { deserialize, format, mimeTypes } = parser;
2598
+ if (!format && !mimeTypes) return false;
2599
+ const mimeTypeList = mimeTypes || (Array.isArray(format) ? format : format ? [format] : []).map((fmt) => fmt.includes("/") ? fmt : `text/${fmt}`);
2600
+ for (const mimeType of mimeTypeList) {
2601
+ let data = dataTransfer.getData(mimeType);
2602
+ if (mimeType !== "Files" && !data || mimeType === "Files" && dataTransfer.files.length === 0) continue;
2603
+ if (!pipeInsertDataQuery(editor, injectedPlugins, {
2604
+ data,
2605
+ dataTransfer,
2606
+ mimeType
2607
+ })) continue;
2608
+ data = pipeTransformData(editor, injectedPlugins, {
2609
+ data,
2610
+ dataTransfer,
2611
+ mimeType
2612
+ });
2613
+ let fragment = deserialize?.({
2614
+ ...getEditorPlugin(editor, plugin),
2615
+ data,
2616
+ dataTransfer,
2617
+ mimeType
2618
+ });
2619
+ if (!fragment?.length) continue;
2620
+ fragment = pipeTransformFragment(editor, injectedPlugins, {
2621
+ data,
2622
+ dataTransfer,
2623
+ fragment,
2624
+ mimeType
2625
+ });
2626
+ if (fragment.length === 0) continue;
2627
+ pipeInsertFragment(editor, injectedPlugins, {
2628
+ data,
2629
+ dataTransfer,
2630
+ fragment,
2631
+ mimeType
2632
+ });
2633
+ return true;
2634
+ }
2635
+ return false;
2636
+ })) return;
2637
+ insertData(dataTransfer);
2638
+ } } }));
2639
+
2640
+ //#endregion
2641
+ //#region src/lib/plugins/getCorePlugins.ts
2642
+ const getCorePlugins = ({ affinity, chunking, maxLength, nodeId, plugins = [] }) => {
2643
+ let resolvedNodeId = nodeId;
2644
+ if (process.env.NODE_ENV === "test" && nodeId === void 0) resolvedNodeId = false;
2645
+ let corePlugins = [
2646
+ DebugPlugin,
2647
+ SlateExtensionPlugin,
2648
+ DOMPlugin,
2649
+ HistoryPlugin,
2650
+ OverridePlugin,
2651
+ ParserPlugin,
2652
+ maxLength ? LengthPlugin.configure({ options: { maxLength } }) : LengthPlugin,
2653
+ HtmlPlugin,
2654
+ AstPlugin,
2655
+ NodeIdPlugin.configure({
2656
+ enabled: resolvedNodeId !== false,
2657
+ options: resolvedNodeId === false ? void 0 : resolvedNodeId
2658
+ }),
2659
+ AffinityPlugin.configure({ enabled: affinity }),
2660
+ BaseParagraphPlugin,
2661
+ ChunkingPlugin.configure({
2662
+ enabled: chunking !== false,
2663
+ options: typeof chunking === "boolean" ? void 0 : chunking
2664
+ })
2665
+ ];
2666
+ const customPluginsMap = new Map(plugins.map((plugin) => [plugin.key, plugin]));
2667
+ corePlugins = corePlugins.map((corePlugin) => {
2668
+ const customPlugin = customPluginsMap.get(corePlugin.key);
2669
+ if (customPlugin) {
2670
+ const index = plugins.findIndex((p) => p.key === corePlugin.key);
2671
+ if (index !== -1) plugins.splice(index, 1);
2672
+ return customPlugin;
2673
+ }
2674
+ return corePlugin;
2675
+ });
2676
+ return corePlugins;
2677
+ };
2678
+
2679
+ //#endregion
2680
+ //#region src/lib/editor/withSlate.ts
2681
+ /**
2682
+ * Applies Plate enhancements to an editor instance (non-React version).
2683
+ *
2684
+ * @remarks
2685
+ * This function supports server-side usage as it doesn't include React-specific
2686
+ * features like component rendering or hooks integration.
2687
+ * @see {@link createSlateEditor} for a higher-level non-React editor creation function.
2688
+ * @see {@link createPlateEditor} for a React-specific version of editor creation.
2689
+ * @see {@link usePlateEditor} for a memoized React version.
2690
+ * @see {@link withPlate} for the React-specific enhancement function.
2691
+ */
2692
+ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLength, nodeId, optionsStoreFactory, plugins = [], readOnly = false, rootPlugin, selection, shouldNormalizeEditor, skipInitialization, userId, value, onReady, ...pluginConfig } = {}) => {
2693
+ const editor = e;
2694
+ editor.id = id ?? editor.id ?? nanoid();
2695
+ editor.meta.key = editor.meta.key ?? nanoid();
2696
+ editor.meta.isFallback = false;
2697
+ editor.meta.userId = userId;
2698
+ editor.dom = {
2699
+ composing: false,
2700
+ currentKeyboardEvent: null,
2701
+ focused: false,
2702
+ prevSelection: null,
2703
+ readOnly
2704
+ };
2705
+ editor.getApi = () => editor.api;
2706
+ editor.getTransforms = () => editor.transforms;
2707
+ editor.getPlugin = (plugin) => getSlatePlugin(editor, plugin);
2708
+ editor.getType = (pluginKey) => getPluginType(editor, pluginKey);
2709
+ editor.getInjectProps = (plugin) => {
2710
+ const nodeProps = editor.getPlugin(plugin).inject?.nodeProps ?? {};
2711
+ nodeProps.nodeKey = nodeProps.nodeKey ?? editor.getType(plugin.key);
2712
+ nodeProps.styleKey = nodeProps.styleKey ?? nodeProps.nodeKey;
2713
+ return nodeProps;
2714
+ };
2715
+ editor.getOptionsStore = (plugin) => editor.getPlugin(plugin).optionsStore;
2716
+ editor.getOptions = (plugin) => {
2717
+ if (!editor.getOptionsStore(plugin)) return editor.getPlugin(plugin).options;
2718
+ return editor.getOptionsStore(plugin).get("state");
2719
+ };
2720
+ editor.getOption = (plugin, key, ...args) => {
2721
+ const store = editor.getOptionsStore(plugin);
2722
+ if (!store) return editor.getPlugin(plugin).options[key];
2723
+ if (!(key in store.get("state")) && !(key in store.selectors)) {
2724
+ editor.api.debug.error(`editor.getOption: ${key} option is not defined in plugin ${plugin.key}.`, "OPTION_UNDEFINED");
2725
+ return;
2726
+ }
2727
+ return store.get(key, ...args);
2728
+ };
2729
+ editor.setOption = (plugin, key, ...args) => {
2730
+ const store = editor.getOptionsStore(plugin);
2731
+ if (!store) return;
2732
+ if (!(key in store.get("state"))) {
2733
+ editor.api.debug.error(`editor.setOption: ${key} option is not defined in plugin ${plugin.key}.`, "OPTION_UNDEFINED");
2734
+ return;
2735
+ }
2736
+ store.set(key, ...args);
2737
+ };
2738
+ editor.setOptions = (plugin, options) => {
2739
+ const store = editor.getOptionsStore(plugin);
2740
+ if (!store) return;
2741
+ if (typeof options === "object") store.set("state", (draft) => {
2742
+ Object.assign(draft, options);
2743
+ });
2744
+ else if (typeof options === "function") store.set("state", options);
2745
+ };
2746
+ const corePlugins = getCorePlugins({
2747
+ affinity,
2748
+ chunking,
2749
+ maxLength,
2750
+ nodeId,
2751
+ plugins
2752
+ });
2753
+ let rootPluginInstance = createSlatePlugin({
2754
+ key: "root",
2755
+ priority: 1e4,
2756
+ ...pluginConfig,
2757
+ override: {
2758
+ ...pluginConfig.override,
2759
+ components: {
2760
+ ...pluginConfig.components,
2761
+ ...pluginConfig.override?.components
2762
+ }
2763
+ },
2764
+ plugins: [...corePlugins, ...plugins]
2765
+ });
2766
+ if (rootPlugin) rootPluginInstance = rootPlugin(rootPluginInstance);
2767
+ resolvePlugins(editor, [rootPluginInstance], optionsStoreFactory);
2768
+ /** Ignore normalizeNode overrides if shouldNormalizeNode returns false */
2769
+ const normalizeNode = editor.tf.normalizeNode;
2770
+ editor.tf.normalizeNode = (...args) => {
2771
+ if (!editor.api.shouldNormalizeNode(args[0])) return;
2772
+ return normalizeNode(...args);
2773
+ };
2774
+ editor.normalizeNode = editor.tf.normalizeNode;
2775
+ if (!skipInitialization) editor.tf.init({
2776
+ autoSelect,
2777
+ selection,
2778
+ shouldNormalizeEditor,
2779
+ value,
2780
+ onReady
2781
+ });
2782
+ return editor;
2783
+ };
2784
+ /**
2785
+ * Creates a Slate editor (non-React version).
2786
+ *
2787
+ * This function creates a fully configured Plate editor instance that can be
2788
+ * used in non-React environments or server-side contexts. It applies all the
2789
+ * specified plugins and configurations to create a functional editor.
2790
+ *
2791
+ * Examples:
2792
+ *
2793
+ * ```ts
2794
+ * const editor = createSlateEditor({
2795
+ * plugins: [ParagraphPlugin, HeadingPlugin],
2796
+ * value: [{ type: 'p', children: [{ text: 'Hello world!' }] }],
2797
+ * });
2798
+ *
2799
+ * // Editor with custom configuration
2800
+ * const editor = createSlateEditor({
2801
+ * plugins: [ParagraphPlugin],
2802
+ * maxLength: 1000,
2803
+ * nodeId: { idCreator: () => uuidv4() },
2804
+ * autoSelect: 'end',
2805
+ * });
2806
+ *
2807
+ * // Server-side editor
2808
+ * const editor = createSlateEditor({
2809
+ * plugins: [ParagraphPlugin],
2810
+ * value: '<p>HTML content</p>',
2811
+ * skipInitialization: true,
2812
+ * });
2813
+ * ```
2814
+ *
2815
+ * @see {@link createPlateEditor} for a React-specific version of editor creation.
2816
+ * @see {@link usePlateEditor} for a memoized React version.
2817
+ * @see {@link withSlate} for the underlying function that applies Slate enhancements to an editor.
2818
+ */
2819
+ const createSlateEditor = ({ editor = createEditor(), ...options } = {}) => withSlate(editor, options);
2820
+
2821
+ //#endregion
2822
+ export { DebugPlugin as $, pluginDeserializeHtml as A, withNormalizeRules as At, collapseWhiteSpaceText as B, getPluginKey as Bt, deserializeHtmlElement as C, isSlatePluginNode as Ct, pipeDeserializeHtmlLeaf as D, applyDeepToNodes as Dt, htmlElementToLeaf as E, isSlateVoid as Et, collapseWhiteSpace as F, HistoryPlugin as Ft, isHtmlBlockElement as G, getEditorPlugin as Gt, upsertInlineFormattingContext as H, getPluginType as Ht, collapseWhiteSpaceElement as I, withPlateHistory as It, isHtmlText as J, isHtmlInlineElement as K, createSlatePlugin as Kt, inferWhiteSpaceRule as L, AstPlugin as Lt, htmlBrToNewLine as M, withDeleteRules as Mt, htmlBodyToFragment as N, withBreakRules as Nt, htmlElementToElement as O, OverridePlugin as Ot, deserializeHtmlNodeChildren as P, BaseParagraphPlugin as Pt, withScrolling as Q, collapseWhiteSpaceChildren as R, getContainerTypes as Rt, htmlStringToDOMNode as S, isSlatePluginElement as St, htmlTextNodeToString as T, isSlateText as Tt, isLastNonEmptyTextOfInlineFormattingContext as U, getPluginTypes as Ut, endInlineFormattingContext as V, getPluginKeys as Vt, collapseString as W, getSlatePlugin as Wt, AUTO_SCROLL as X, isHtmlElement as Y, DOMPlugin as Z, withNodeId as _, getSlateElements as _t, pipeInsertDataQuery as a, isNodeAffinity as at, parseHtmlDocument as b, isSlateLeaf as bt, setValue as c, getEdgeNodes as ct, init as d, getPluginNodeProps as dt, PlateError as et, isEditOnly as f, getNodeDataAttributeKeys as ft, normalizeNodeId as g, defaultsDeepToNodes as gt, NodeIdPlugin as h, getInjectMatch as ht, ParserPlugin as i, setAffinitySelection as it, getDataNodeProps as j, withMergeRules as jt, pipeDeserializeHtmlElement as k, withOverrides as kt, resetBlock as l, mergeDeepToNodes as lt, pipeOnNodeChange as m, getInjectedPlugins as mt, withSlate as n, withChunking as nt, normalizeDescendantsToDocumentFragment as o, isNodesAffinity as ot, pipeOnTextChange as p, keyToDataAttribute as pt, inlineTagNames as q, createTSlatePlugin as qt, getCorePlugins as r, AffinityPlugin as rt, SlateExtensionPlugin as s, getMarkBoundaryAffinity as st, createSlateEditor as t, ChunkingPlugin as tt, insertExitBreak as u, getSlateClass as ut, LengthPlugin as v, isSlateEditor as vt, deserializeHtmlNode as w, isSlateString as wt, deserializeHtml as x, isSlateNode as xt, HtmlPlugin as y, isSlateElement as yt, collapseWhiteSpaceNode as z, getPluginByType as zt };
2823
+ //# sourceMappingURL=withSlate-1B0SfAWG.js.map