@mx-space/cli 0.7.8 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +30 -0
  2. package/ROADMAP.md +1 -0
  3. package/dist/{Lexical.dev-rcxzwpir.mjs → Lexical.dev-By5dOKBN.mjs} +1486 -356
  4. package/dist/{Lexical.node-BUsI9gWr.mjs → Lexical.node-fXM8Jp7p.mjs} +39 -17
  5. package/dist/{Lexical.prod-C8HtkSD6.mjs → Lexical.prod-BlCvXPGa.mjs} +2279 -1768
  6. package/dist/LexicalClipboard.dev--HxGoSkO.mjs +915 -0
  7. package/dist/{LexicalClipboard.node-CO-MCKx5.mjs → LexicalClipboard.node-C3mRbrmW.mjs} +10 -3
  8. package/dist/LexicalClipboard.prod-DNJzFMcT.mjs +371 -0
  9. package/dist/LexicalCodeCore.dev-DjSrd4A4.mjs +1109 -0
  10. package/dist/LexicalCodeCore.prod-DTDShkR3.mjs +642 -0
  11. package/dist/{LexicalDragon.dev-gBP8WSRS.mjs → LexicalDragon.dev-DwprRH0z.mjs} +3 -3
  12. package/dist/{LexicalDragon.node-OG6G55nl.mjs → LexicalDragon.node-BVhw4C7s.mjs} +2 -2
  13. package/dist/{LexicalDragon.prod-DP-_Q8cv.mjs → LexicalDragon.prod-nh4J-8vn.mjs} +3 -3
  14. package/dist/{LexicalExtension.dev-DVbjljrm.mjs → LexicalExtension.dev-BPNyvGqe.mjs} +335 -6
  15. package/dist/{LexicalExtension.node-CF3EAPN7.mjs → LexicalExtension.node-BVlcrgOx.mjs} +15 -8
  16. package/dist/{LexicalExtension.prod-DklHHErZ.mjs → LexicalExtension.prod-DFKbiNt-.mjs} +375 -265
  17. package/dist/{LexicalHeadless.dev-DdWLM7Nl.mjs → LexicalHeadless.dev-B-cj7Pr5.mjs} +2 -2
  18. package/dist/{LexicalHeadless.prod-CoiiTDzR.mjs → LexicalHeadless.prod-Byp1vNzT.mjs} +2 -2
  19. package/dist/LexicalHtml.dev-prBAwszN.mjs +2424 -0
  20. package/dist/LexicalHtml.node-BvNF96jZ.mjs +56 -0
  21. package/dist/LexicalHtml.prod-Sz3CW1ht.mjs +1207 -0
  22. package/dist/{LexicalLink.dev-BS1Bf-yD.mjs → LexicalLink.dev-NySTYhVR.mjs} +42 -8
  23. package/dist/{LexicalLink.prod-B_crkoHP.mjs → LexicalLink.prod-BuCM8gts.mjs} +209 -191
  24. package/dist/{LexicalList.dev-OMqmcnQ-.mjs → LexicalList.dev-DIMOvSFD.mjs} +252 -80
  25. package/dist/{LexicalList.prod-CpL1s8vS.mjs → LexicalList.prod-MVqfA6Vo.mjs} +364 -278
  26. package/dist/{LexicalMarkdown.dev-BHSdx_Gr.mjs → LexicalMarkdown.dev-C7aylvCG.mjs} +729 -552
  27. package/dist/LexicalMarkdown.prod-CfQSM406.mjs +868 -0
  28. package/dist/{LexicalRichText.dev-Bz9A_WbM.mjs → LexicalRichText.dev-DfICq6lU.mjs} +272 -77
  29. package/dist/{LexicalRichText.node-DDpwfE-P.mjs → LexicalRichText.node-Bg9jND3Q.mjs} +21 -8
  30. package/dist/LexicalRichText.prod-GsFzCK-o.mjs +550 -0
  31. package/dist/{LexicalSelection.dev-CQ380fnD.mjs → LexicalSelection.dev-DGjj12wW.mjs} +50 -38
  32. package/dist/{LexicalSelection.node-DesBMjIE.mjs → LexicalSelection.node-BU5L4193.mjs} +4 -4
  33. package/dist/{LexicalSelection.prod-D2jivCDB.mjs → LexicalSelection.prod-D-PwRbyK.mjs} +158 -140
  34. package/dist/{LexicalTable.dev-Ca7mQ7Jk.mjs → LexicalTable.dev-ChP7cugd.mjs} +215 -53
  35. package/dist/{LexicalTable.prod-DyPAcC7y.mjs → LexicalTable.prod-Bpr9YRXp.mjs} +668 -573
  36. package/dist/{LexicalUtils.dev-HxM8F0g6.mjs → LexicalUtils.dev-BiFU3789.mjs} +10 -47
  37. package/dist/{LexicalUtils.node-lCWjRjoG.mjs → LexicalUtils.node-j3Llm7Y3.mjs} +2 -2
  38. package/dist/{LexicalUtils.prod-Bti5w4ki.mjs → LexicalUtils.prod-DCvrA78p.mjs} +103 -105
  39. package/dist/bin/mxs.d.mts +1 -1
  40. package/dist/bin/mxs.mjs +1 -1
  41. package/dist/esm-By7trG9K.mjs +2 -0
  42. package/dist/{esm-CK9m5JKX.mjs → esm-Dg-WhPCe.mjs} +989 -989
  43. package/dist/index.d.mts +11 -2
  44. package/dist/index.mjs +1 -1
  45. package/dist/{mxs-BZt1cLW9.mjs → mxs-BR5Y5ash.mjs} +25141 -24672
  46. package/package.json +15 -14
  47. package/skills/commands-auth.md +1 -1
  48. package/skills/commands-category.md +1 -1
  49. package/skills/commands-comment.md +1 -1
  50. package/skills/commands-config.md +1 -1
  51. package/skills/commands-preview.md +1 -1
  52. package/skills/commands-profile.md +1 -1
  53. package/skills/commands-project.md +57 -0
  54. package/skills/commands-topic.md +1 -1
  55. package/skills/output-modes.md +1 -1
  56. package/skills/overview.md +2 -1
  57. package/skills/safety.md +10 -0
  58. package/dist/LexicalClipboard.dev-BpqRwTLP.mjs +0 -494
  59. package/dist/LexicalClipboard.prod-B_xdeiAC.mjs +0 -291
  60. package/dist/LexicalCodeCore.dev-7_cexm39.mjs +0 -537
  61. package/dist/LexicalCodeCore.prod-Bd6WFHcO.mjs +0 -381
  62. package/dist/LexicalHtml.dev-C3ZSWBFZ.mjs +0 -684
  63. package/dist/LexicalHtml.node-D0TD152E.mjs +0 -24
  64. package/dist/LexicalHtml.prod-CX47X0zl.mjs +0 -430
  65. package/dist/LexicalMarkdown.prod-C-S7Ii6-.mjs +0 -754
  66. package/dist/LexicalRichText.prod-C8qXACDL.mjs +0 -427
  67. package/dist/esm-DRbkkY3r.mjs +0 -2
  68. /package/dist/{mxs-CM8-6uAr.d.mts → mxs-iqJBopvE.d.mts} +0 -0
@@ -0,0 +1,915 @@
1
+ import { c as configExtension, d as getPeerDependencyFromEditor, r as $getPeerDependency } from "./LexicalExtension.node-BVlcrgOx.mjs";
2
+ import { $ as $isElementNode, A as $getChildCaretAtIndex, D as $getCaretRangeInDirection, Dt as $splitAtPointCaretNext, E as $getCaretRange, Er as isSelectionWithinEditor, F as $getEditor, Fr as shallowMergeConfig, H as $getRoot, K as $getTextPointCaret, L as $getNearestNodeFromDOMNode, Lt as COMMAND_PRIORITY_CRITICAL, M as $getCollapsedCaretRange, Rr as tokenizeRawText, Tt as $setSelectionFromCaretRange, U as $getSelection, Ut as COPY_COMMAND, _t as $parseSerializedNode, cr as defineExtension, ft as $isTextNode, g as $createTabNode, i as $caretRangeFromSelection, jr as safeCast, k as $getChildCaret, lr as getDOMSelection, pt as $isTextPointCaret, qn as SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, r as $caretFromPoint, s as $comparePointCaretNext, st as $isRangeSelection } from "./Lexical.node-fXM8Jp7p.mjs";
3
+ import { c as ImportSource, f as contextValue, i as $generateNodesFromDOMViaExtension, l as ImportSourceDataTransfer, n as $generateHtmlFromNodes, r as $generateNodesFromDOM } from "./LexicalHtml.node-BvNF96jZ.mjs";
4
+ import { i as $sliceSelectedTextNodeContent } from "./LexicalSelection.node-BU5L4193.mjs";
5
+ import { g as objectKlassEquals } from "./LexicalUtils.node-j3Llm7Y3.mjs";
6
+ //#region ../../node_modules/.pnpm/@lexical+clipboard@0.45.0/node_modules/@lexical/clipboard/dist/LexicalClipboard.dev.mjs
7
+ /**
8
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
9
+ *
10
+ * This source code is licensed under the MIT license found in the
11
+ * LICENSE file in the root directory of this source tree.
12
+ *
13
+ */
14
+ /**
15
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
16
+ *
17
+ * This source code is licensed under the MIT license found in the
18
+ * LICENSE file in the root directory of this source tree.
19
+ *
20
+ */
21
+ /** @internal */
22
+ function caretFromPoint(x, y) {
23
+ if (typeof document.caretRangeFromPoint !== "undefined") {
24
+ const range = document.caretRangeFromPoint(x, y);
25
+ if (range === null) return null;
26
+ return {
27
+ node: range.startContainer,
28
+ offset: range.startOffset
29
+ };
30
+ } else if (document.caretPositionFromPoint !== "undefined") {
31
+ const range = document.caretPositionFromPoint(x, y);
32
+ if (range === null) return null;
33
+ return {
34
+ node: range.offsetNode,
35
+ offset: range.offset
36
+ };
37
+ } else return null;
38
+ }
39
+ /**
40
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
41
+ *
42
+ * This source code is licensed under the MIT license found in the
43
+ * LICENSE file in the root directory of this source tree.
44
+ *
45
+ */
46
+ function formatDevErrorMessage(message) {
47
+ throw new Error(message);
48
+ }
49
+ /**
50
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
51
+ *
52
+ * This source code is licensed under the MIT license found in the
53
+ * LICENSE file in the root directory of this source tree.
54
+ *
55
+ */
56
+ /**
57
+ * A middleware function in a per-MIME-type clipboard-import stack. Mirrors
58
+ * the shape of {@link ExportMimeTypeFunction} on the export side.
59
+ *
60
+ * - `data` is the non-empty string returned by `DataTransfer.getData(mime)`
61
+ * for this MIME type.
62
+ * - `selection` is the current editor selection at the insertion point.
63
+ * - `$next` defers to the next-lower handler in the stack (i.e. the handler
64
+ * that was registered earlier). Returns `true` if that handler claimed
65
+ * the data; `false` if no handler accepted it.
66
+ * - `dataTransfer` is the full {@link DataTransfer} the paste/drop came
67
+ * from, so a handler can inspect companion MIME types or attached
68
+ * files in addition to the slot it was invoked for (e.g. peek at
69
+ * `'application/x-vscode-source'` while handling `'text/html'`). When
70
+ * threading through the new pipeline, pass this into
71
+ * `$generateNodesFromDOMViaExtension(dom, {
72
+ * context: [contextValue(ImportSourceDataTransfer, dataTransfer)],
73
+ * })` so rules and preprocessors can read it via
74
+ * `ctx.get(ImportSourceDataTransfer)`.
75
+ *
76
+ * The function should return `true` if it consumed the data (the caller
77
+ * stops trying further handlers for this MIME type and does not move on to
78
+ * the next MIME type). Return `$next()` to delegate. Return `false` if the
79
+ * function decided not to handle the data after inspecting it (e.g. the
80
+ * JSON namespace didn't match) so a lower-priority handler — or the next
81
+ * MIME type — gets a chance.
82
+ *
83
+ * @experimental
84
+ */
85
+ /**
86
+ * A mapping from MIME type to a stack of {@link ImportMimeTypeFunction}.
87
+ *
88
+ * Each entry is an ordered array; the function at the highest index runs
89
+ * first and may call `next()` to fall through to the function below it.
90
+ * The default config provides one handler each for
91
+ * `'application/x-lexical-editor'`, `'text/html'`, and `'text/plain'` that
92
+ * matches the legacy {@link $insertDataTransferForRichText} behavior.
93
+ *
94
+ * When {@link ClipboardImportExtension} merges a partial config, new
95
+ * functions are appended to the existing array for each MIME type, so
96
+ * later-registered handlers run before earlier ones (including the
97
+ * defaults) and may delegate to them via `next()`.
98
+ *
99
+ * @experimental
100
+ */
101
+ /**
102
+ * Per-MIME-type ordering weights. Lower numbers run first.
103
+ *
104
+ * Composable across extensions: each extension contributes weights for
105
+ * its MIME types without needing to coordinate. A partial config that
106
+ * sets `{'application/vnd.myapp+json': 5}` slots its type between the
107
+ * built-in `application/x-lexical-editor` (0) and `text/html` (10) — no
108
+ * need to enumerate the full ordering. mergeConfig spreads pairs (later
109
+ * keys override earlier ones for the same MIME type, so an extension
110
+ * can also re-rank a built-in by repeating its key with a new weight).
111
+ *
112
+ * Iteration: every MIME type that has a handler stack and is present in
113
+ * the dataTransfer (regardless of whether it has an explicit weight) is
114
+ * tried; MIME types with no explicit weight sort to the end, behind all
115
+ * weighted ones, in lexical order.
116
+ *
117
+ * @experimental
118
+ */
119
+ /**
120
+ * Configuration for {@link ClipboardImportExtension}.
121
+ *
122
+ * @experimental
123
+ */
124
+ /**
125
+ * Default per-MIME-type weights reproducing the legacy
126
+ * `$insertDataTransferForRichText` ordering:
127
+ *
128
+ * `application/x-lexical-editor` (0) → `text/html` (10) →
129
+ * `text/plain` (20) → `text/uri-list` (30).
130
+ *
131
+ * Gaps between weights let third-party MIME types slot in (e.g. weight
132
+ * 5 to run between lexical and html). Apps can also override built-in
133
+ * weights to demote them.
134
+ *
135
+ * @experimental
136
+ */
137
+ const DEFAULT_IMPORT_MIME_TYPE_PRIORITY = {
138
+ "application/x-lexical-editor": 0,
139
+ "text/html": 10,
140
+ "text/plain": 20,
141
+ "text/uri-list": 30
142
+ };
143
+ function trustHTML(html) {
144
+ if (window.trustedTypes && window.trustedTypes.createPolicy) return window.trustedTypes.createPolicy("lexical", { createHTML: (input) => input }).createHTML(html);
145
+ return html;
146
+ }
147
+ /**
148
+ * Default handler for `'application/x-lexical-editor'`: parse the JSON,
149
+ * verify the namespace, and insert the serialized nodes.
150
+ */
151
+ const $defaultLexicalEditorImporter = (data, selection, $next) => {
152
+ try {
153
+ const editor = $getEditor();
154
+ const payload = JSON.parse(data);
155
+ if (payload && payload.namespace === editor._config.namespace && Array.isArray(payload.nodes)) {
156
+ $insertGeneratedNodes(editor, $generateNodesFromSerializedNodes(payload.nodes), selection);
157
+ return true;
158
+ }
159
+ } catch (error) {
160
+ console.error(error);
161
+ }
162
+ return $next();
163
+ };
164
+ /**
165
+ * Default handler for `'text/html'`: parse the HTML and run the legacy
166
+ * `$generateNodesFromDOM`. Override (or stack a higher-priority handler
167
+ * on top) to route HTML pastes through {@link DOMImportExtension} or any
168
+ * custom pipeline. See {@link $generateNodesFromDOMViaExtension} for the
169
+ * built-in `DOMImportExtension` adapter.
170
+ */
171
+ const $defaultHtmlImporter = (data, selection, $next) => {
172
+ try {
173
+ const editor = $getEditor();
174
+ $insertGeneratedNodes(editor, $generateNodesFromDOM(editor, new DOMParser().parseFromString(trustHTML(data), "text/html")), selection);
175
+ return true;
176
+ } catch (error) {
177
+ console.error(error);
178
+ return $next();
179
+ }
180
+ };
181
+ /**
182
+ * Default handler for `'text/plain'`. On a RangeSelection, drive the
183
+ * insertion off {@link tokenizeRawText} so each `\n` becomes a real
184
+ * paragraph break via `insertParagraph` (preserving current text
185
+ * format / style on the surrounding `insertText` calls). For other
186
+ * selection types, defer to the selection's own `insertRawText`.
187
+ */
188
+ const $defaultPlainTextImporter = (data, selection) => {
189
+ if (!$isRangeSelection(selection)) {
190
+ selection.insertRawText(data);
191
+ return true;
192
+ }
193
+ const withCurrentRange = (fn) => {
194
+ const cur = $getSelection();
195
+ if ($isRangeSelection(cur)) fn(cur);
196
+ };
197
+ tokenizeRawText(data, {
198
+ linebreak: () => withCurrentRange((cur) => cur.insertParagraph()),
199
+ tab: () => withCurrentRange((cur) => cur.insertNodes([$createTabNode()])),
200
+ text: (part) => withCurrentRange((cur) => cur.insertText(part))
201
+ });
202
+ return true;
203
+ };
204
+ /**
205
+ * The default per-MIME-type handler stacks reproducing the legacy
206
+ * {@link $insertDataTransferForRichText} behavior exactly. Stacked
207
+ * extensions append on top of these.
208
+ *
209
+ * @experimental
210
+ */
211
+ const DEFAULT_IMPORT_MIME_TYPE = {
212
+ "application/x-lexical-editor": [$defaultLexicalEditorImporter],
213
+ "text/html": [$defaultHtmlImporter],
214
+ "text/plain": [$defaultPlainTextImporter],
215
+ "text/uri-list": [$defaultPlainTextImporter]
216
+ };
217
+ /**
218
+ * Output of {@link ClipboardImportExtension}: the merged configuration
219
+ * plus a self-contained {@link $insertDataTransfer} function that owns
220
+ * the entire paste-side iteration over the priority list. Apps look this
221
+ * up via peer-dependency and call it directly; {@link
222
+ * $insertDataTransferForRichText} delegates to it.
223
+ *
224
+ * @experimental
225
+ */
226
+ function $callImportMimeTypeFunctionStack(fns, data, selection, dataTransfer) {
227
+ if (!fns) return false;
228
+ const callAt = (i) => fns[i] ? fns[i](data, selection, callAt.bind(null, i - 1), dataTransfer) : false;
229
+ return callAt(fns.length - 1);
230
+ }
231
+ /**
232
+ * Sort the MIME types that have a registered handler stack by their
233
+ * configured priority weight (ascending). Types with no explicit weight
234
+ * sort after all weighted types, in lexical order, so unknown types
235
+ * remain reachable but never preempt a known one.
236
+ */
237
+ function orderedMimeTypes(config) {
238
+ return Object.keys(config.$importMimeType).filter((k) => config.$importMimeType[k] !== void 0).sort((a, b) => {
239
+ const wa = config.priority[a];
240
+ const wb = config.priority[b];
241
+ if (wa === void 0 && wb === void 0) return a < b ? -1 : a > b ? 1 : 0;
242
+ if (wa === void 0) return 1;
243
+ if (wb === void 0) return -1;
244
+ return wa - wb;
245
+ });
246
+ }
247
+ function $runImport(config, dataTransfer, selection) {
248
+ const plainString = dataTransfer.getData("text/plain");
249
+ for (const mime of orderedMimeTypes(config)) {
250
+ const data = dataTransfer.getData(mime);
251
+ if (!data) continue;
252
+ if (mime === "text/html" && data === plainString) continue;
253
+ if ($callImportMimeTypeFunctionStack(config.$importMimeType[mime], data, selection, dataTransfer)) return true;
254
+ }
255
+ return false;
256
+ }
257
+ const DEFAULT_OUTPUT = {
258
+ $importMimeType: DEFAULT_IMPORT_MIME_TYPE,
259
+ $insertDataTransfer: (dataTransfer, selection) => $runImport({
260
+ $importMimeType: DEFAULT_IMPORT_MIME_TYPE,
261
+ priority: DEFAULT_IMPORT_MIME_TYPE_PRIORITY
262
+ }, dataTransfer, selection),
263
+ priority: DEFAULT_IMPORT_MIME_TYPE_PRIORITY
264
+ };
265
+ /**
266
+ * @internal
267
+ *
268
+ * Look up the {@link ClipboardImportOutput} on the active editor. Returns
269
+ * a static default-backed output when no {@link ClipboardImportExtension}
270
+ * is configured, so callers can always invoke `output.$insertDataTransfer`
271
+ * regardless of whether the editor opted in.
272
+ */
273
+ function $getImportOutput() {
274
+ const dep = $getPeerDependency(ClipboardImportExtension.name);
275
+ return dep ? dep.output : DEFAULT_OUTPUT;
276
+ }
277
+ /**
278
+ * @experimental
279
+ *
280
+ * Mirror of {@link GetClipboardDataExtension} for the import direction.
281
+ * Holds a per-MIME-type stack of {@link ImportMimeTypeFunction}s.
282
+ *
283
+ * @example
284
+ * Route `text/html` pastes through {@link DOMImportExtension}, leaving the
285
+ * defaults for other MIME types untouched:
286
+ * ```ts
287
+ * import {configExtension, defineExtension, $getEditor} from 'lexical';
288
+ * import {
289
+ * ClipboardImportExtension,
290
+ * $insertGeneratedNodes,
291
+ * } from '@lexical/clipboard';
292
+ * import {
293
+ * contextValue,
294
+ * DOMImportExtension,
295
+ * ImportSource,
296
+ * ImportSourceDataTransfer,
297
+ * $generateNodesFromDOMViaExtension,
298
+ * } from '@lexical/html';
299
+ *
300
+ * defineExtension({
301
+ * name: 'app',
302
+ * dependencies: [
303
+ * DOMImportExtension,
304
+ * configExtension(ClipboardImportExtension, {
305
+ * $importMimeType: {
306
+ * 'text/html': [
307
+ * (html, selection, _$next, dataTransfer) => {
308
+ * const parser = new DOMParser();
309
+ * const dom = parser.parseFromString(html, 'text/html');
310
+ * const nodes = $generateNodesFromDOMViaExtension(dom, {
311
+ * context: [
312
+ * contextValue(ImportSource, 'paste'),
313
+ * contextValue(ImportSourceDataTransfer, dataTransfer),
314
+ * ],
315
+ * });
316
+ * $insertGeneratedNodes($getEditor(), nodes, selection);
317
+ * return true;
318
+ * },
319
+ * ],
320
+ * },
321
+ * }),
322
+ * ],
323
+ * });
324
+ * ```
325
+ */
326
+ const ClipboardImportExtension = defineExtension({
327
+ build: (_editor, config) => ({
328
+ $importMimeType: config.$importMimeType,
329
+ $insertDataTransfer: (dataTransfer, selection) => $runImport(config, dataTransfer, selection),
330
+ priority: config.priority
331
+ }),
332
+ config: safeCast({
333
+ $importMimeType: DEFAULT_IMPORT_MIME_TYPE,
334
+ priority: DEFAULT_IMPORT_MIME_TYPE_PRIORITY
335
+ }),
336
+ mergeConfig(config, partial) {
337
+ const merged = shallowMergeConfig(config, partial);
338
+ if (partial.$importMimeType) {
339
+ const $importMimeType = { ...config.$importMimeType };
340
+ for (const [k, v] of Object.entries(partial.$importMimeType)) if (v) {
341
+ const prev = $importMimeType[k];
342
+ $importMimeType[k] = prev ? [...prev, ...v] : v;
343
+ }
344
+ merged.$importMimeType = $importMimeType;
345
+ }
346
+ if (partial.priority) merged.priority = {
347
+ ...config.priority,
348
+ ...partial.priority
349
+ };
350
+ return merged;
351
+ },
352
+ name: "@lexical/clipboard/Import"
353
+ });
354
+ /**
355
+ * @experimental
356
+ *
357
+ * Drop-in extension that routes `text/html` clipboard pastes and drops
358
+ * through the {@link DOMImportExtension} pipeline (rules, schemas,
359
+ * preprocessors, overlays) instead of the legacy
360
+ * {@link $generateNodesFromDOM}. Add to your extension dependencies along
361
+ * with the per-package import extensions you want active
362
+ * ({@link CoreImportExtension}, {@link RichTextImportExtension}, etc.).
363
+ *
364
+ * The original {@link DataTransfer} and `'paste'` source kind are forwarded
365
+ * into the import context so rules and preprocessors can read them via
366
+ * `ctx.get(ImportSourceDataTransfer)` / `ctx.get(ImportSource)`.
367
+ *
368
+ * Equivalent to stacking this `text/html` handler manually via
369
+ * `configExtension(ClipboardImportExtension, {...})`.
370
+ *
371
+ * @example
372
+ * ```ts
373
+ * import {defineExtension} from 'lexical';
374
+ * import {ClipboardDOMImportExtension} from '@lexical/clipboard';
375
+ * import {CoreImportExtension, RichTextImportExtension} from '@lexical/html';
376
+ *
377
+ * defineExtension({
378
+ * name: 'app',
379
+ * dependencies: [
380
+ * CoreImportExtension,
381
+ * RichTextImportExtension,
382
+ * ClipboardDOMImportExtension,
383
+ * ],
384
+ * });
385
+ * ```
386
+ */
387
+ const ClipboardDOMImportExtension = defineExtension({
388
+ dependencies: [configExtension(ClipboardImportExtension, { $importMimeType: { "text/html": [(html, selection, _$next, dataTransfer) => {
389
+ const nodes = $generateNodesFromDOMViaExtension(new DOMParser().parseFromString(trustHTML(html), "text/html"), { context: [contextValue(ImportSource, "paste"), contextValue(ImportSourceDataTransfer, dataTransfer)] });
390
+ $insertGeneratedNodes($getEditor(), nodes, selection);
391
+ return true;
392
+ }] } })],
393
+ name: "@lexical/clipboard/DOMImport"
394
+ });
395
+ /**
396
+ * Returns the *currently selected* Lexical content as an HTML string, relying on the
397
+ * logic defined in the exportDOM methods on the LexicalNode classes. Note that
398
+ * this will not return the HTML content of the entire editor (unless all the content is included
399
+ * in the current selection).
400
+ *
401
+ * @param editor - LexicalEditor instance to get HTML content from
402
+ * @param selection - The selection to use (default is $getSelection())
403
+ * @returns a string of HTML content
404
+ */
405
+ function $getHtmlContent(editor, selection = $getSelection()) {
406
+ if (selection == null) formatDevErrorMessage(`Expected valid LexicalSelection`);
407
+ if ($isRangeSelection(selection) && selection.isCollapsed() || selection.getNodes().length === 0) return "";
408
+ return $generateHtmlFromNodes(editor, selection);
409
+ }
410
+ /**
411
+ * Returns the *currently selected* Lexical content as a JSON string, relying on the
412
+ * logic defined in the exportJSON methods on the LexicalNode classes. Note that
413
+ * this will not return the JSON content of the entire editor (unless all the content is included
414
+ * in the current selection).
415
+ *
416
+ * @param editor - LexicalEditor instance to get the JSON content from
417
+ * @param selection - The selection to use (default is $getSelection())
418
+ * @returns
419
+ */
420
+ function $getLexicalContent(editor, selection = $getSelection()) {
421
+ if (selection == null) formatDevErrorMessage(`Expected valid LexicalSelection`);
422
+ if ($isRangeSelection(selection) && selection.isCollapsed() || selection.getNodes().length === 0) return null;
423
+ return JSON.stringify($generateJSONFromSelectedNodes(editor, selection));
424
+ }
425
+ /**
426
+ * Attempts to insert content of the mime-types text/plain or text/uri-list from
427
+ * the provided DataTransfer object into the editor at the provided selection.
428
+ * text/uri-list is only used if text/plain is not also provided.
429
+ *
430
+ * @param dataTransfer an object conforming to the [DataTransfer interface] (https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface)
431
+ * @param selection the selection to use as the insertion point for the content in the DataTransfer object
432
+ */
433
+ function $insertDataTransferForPlainText(dataTransfer, selection) {
434
+ const text = dataTransfer.getData("text/plain") || dataTransfer.getData("text/uri-list");
435
+ if (text != null) selection.insertRawText(text);
436
+ }
437
+ /**
438
+ * Insert the contents of `dataTransfer` at `selection` using the rich-text
439
+ * import pipeline (`application/x-lexical-editor` → `text/html` → `text/plain`
440
+ * → `text/uri-list`, in descending order of priority).
441
+ *
442
+ * @param dataTransfer an object conforming to the [DataTransfer interface] (https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface)
443
+ * @param selection the selection to use as the insertion point for the content in the DataTransfer object
444
+ * @param _editor unused; retained for backwards compatibility. Safe to
445
+ * omit on new call sites.
446
+ */
447
+ function $insertDataTransferForRichText(dataTransfer, selection, _editor) {
448
+ $getImportOutput().$insertDataTransfer(dataTransfer, selection);
449
+ }
450
+ const LEXICAL_DRAG_MIME_TYPE = "application/x-lexical-drag";
451
+ /**
452
+ * Populate `dataTransfer` with a marker identifying the current editor as a
453
+ * drag source. Pair this with {@link $handleRichTextDrop} or
454
+ * {@link $handlePlainTextDrop} on the drop side to get cut-and-paste semantics
455
+ * for drags that end in a different editor.
456
+ *
457
+ * Only the source editor's key needs to round-trip — the source's
458
+ * RangeSelection itself is preserved on the source editor between drag start
459
+ * and drop (Lexical suppresses selectionchange during drag), so the drop
460
+ * handler reads it directly via `$getSelection()` on the resolved source
461
+ * editor.
462
+ *
463
+ * Callers typically invoke this from a DRAGSTART_COMMAND handler alongside
464
+ * {@link setLexicalClipboardDataTransfer} (so that the dragged content itself
465
+ * round-trips with full node fidelity).
466
+ */
467
+ function $writeDragSourceToDataTransfer(dataTransfer, editor) {
468
+ const marker = { editorKey: editor.getKey() };
469
+ dataTransfer.setData(LEXICAL_DRAG_MIME_TYPE, JSON.stringify(marker));
470
+ }
471
+ function isLexicalDragMarker(value) {
472
+ return value !== null && typeof value === "object" && "editorKey" in value && typeof value.editorKey === "string";
473
+ }
474
+ function readDragMarker(dataTransfer) {
475
+ const raw = dataTransfer.getData(LEXICAL_DRAG_MIME_TYPE);
476
+ if (!raw) return null;
477
+ let parsed;
478
+ try {
479
+ parsed = JSON.parse(raw);
480
+ } catch (_unused) {
481
+ return null;
482
+ }
483
+ return isLexicalDragMarker(parsed) ? parsed : null;
484
+ }
485
+ function findEditorRootByKey(key, doc) {
486
+ const elements = doc.querySelectorAll("[data-lexical-editor=\"true\"]");
487
+ for (const el of Array.from(elements)) {
488
+ const editor = el.__lexicalEditor;
489
+ if (editor && editor.getKey() === key) return el;
490
+ }
491
+ return null;
492
+ }
493
+ function $resolveDropPointCaret(event) {
494
+ const hit = caretFromPoint(event.clientX, event.clientY);
495
+ if (hit === null) return null;
496
+ const node = $getNearestNodeFromDOMNode(hit.node);
497
+ if (node === null) return null;
498
+ if ($isTextNode(node)) return $getTextPointCaret(node, "next", hit.offset);
499
+ if ($isElementNode(node)) return $getChildCaretAtIndex(node, hit.offset, "next");
500
+ const parent = node.getParent();
501
+ if (parent === null) return null;
502
+ return $getChildCaretAtIndex(parent, node.getIndexWithinParent() + 1, "next");
503
+ }
504
+ function $isDropCaretInsideSelection(dropCaret, selection) {
505
+ const { anchor: start, focus: end } = $getCaretRangeInDirection($caretRangeFromSelection(selection), "next");
506
+ return $comparePointCaretNext(start, dropCaret) < 0 && $comparePointCaretNext(dropCaret, end) < 0;
507
+ }
508
+ function $doDrop(event, editor, $insertDataTransfer) {
509
+ const dataTransfer = event.dataTransfer;
510
+ if (dataTransfer === null) return false;
511
+ const marker = readDragMarker(dataTransfer);
512
+ if (marker === null) return false;
513
+ const dropCaret = $resolveDropPointCaret(event);
514
+ if (dropCaret === null) return false;
515
+ const stableDropCaret = $splitAtPointCaretNext(dropCaret);
516
+ if (stableDropCaret === null) return false;
517
+ const isSameEditorDrag = marker.editorKey === editor.getKey();
518
+ const currentSelection = $getSelection();
519
+ if (isSameEditorDrag) {
520
+ if (!$isRangeSelection(currentSelection) || currentSelection.isCollapsed()) return false;
521
+ if ($isDropCaretInsideSelection(dropCaret, currentSelection)) {
522
+ event.preventDefault();
523
+ return true;
524
+ }
525
+ currentSelection.removeText();
526
+ }
527
+ if (!stableDropCaret.origin.isAttached()) {
528
+ event.preventDefault();
529
+ return true;
530
+ }
531
+ $insertDataTransfer(dataTransfer, $setSelectionFromCaretRange($getCollapsedCaretRange(stableDropCaret)), editor);
532
+ if (!isSameEditorDrag) {
533
+ const rootElement = editor.getRootElement();
534
+ const doc = rootElement ? rootElement.ownerDocument : null;
535
+ const sourceRoot = doc ? findEditorRootByKey(marker.editorKey, doc) : null;
536
+ if (sourceRoot !== null) sourceRoot.dispatchEvent(new InputEvent("beforeinput", {
537
+ bubbles: true,
538
+ cancelable: true,
539
+ inputType: "deleteByDrag"
540
+ }));
541
+ }
542
+ event.preventDefault();
543
+ return true;
544
+ }
545
+ /**
546
+ * Drop handler for rich-text editors. Inserts the DataTransfer payload via
547
+ * {@link $insertDataTransferForRichText} at the drop caret and, when the drag
548
+ * originated from a Lexical editor (marked via
549
+ * {@link $writeDragSourceToDataTransfer} on DRAGSTART), removes the source
550
+ * range — producing cut-and-paste semantics whether the drop is in the same
551
+ * editor or a different one on the same page.
552
+ */
553
+ function $handleRichTextDrop(event, editor) {
554
+ return $doDrop(event, editor, $insertDataTransferForRichText);
555
+ }
556
+ /**
557
+ * Drop handler for plain-text editors. Same semantics as
558
+ * {@link $handleRichTextDrop} but inserts via
559
+ * {@link $insertDataTransferForPlainText}.
560
+ */
561
+ function $handlePlainTextDrop(event, editor) {
562
+ return $doDrop(event, editor, (dataTransfer, selection) => $insertDataTransferForPlainText(dataTransfer, selection));
563
+ }
564
+ /**
565
+ * Inserts Lexical nodes into the editor using different strategies depending on
566
+ * some simple selection-based heuristics. If you're looking for a generic way to
567
+ * to insert nodes into the editor at a specific selection point, you probably want
568
+ * {@link lexical.$insertNodes}
569
+ *
570
+ * @param editor LexicalEditor instance to insert the nodes into.
571
+ * @param nodes The nodes to insert.
572
+ * @param selection The selection to insert the nodes into.
573
+ */
574
+ function $insertGeneratedNodes(editor, nodes, selection) {
575
+ if (!editor.dispatchCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, {
576
+ nodes,
577
+ selection
578
+ })) {
579
+ selection.insertNodes(nodes);
580
+ $updateSelectionOnInsert(selection);
581
+ }
582
+ }
583
+ function $updateSelectionOnInsert(selection) {
584
+ if ($isRangeSelection(selection) && selection.isCollapsed()) {
585
+ const anchor = selection.anchor;
586
+ let nodeToInspect = null;
587
+ const anchorCaret = $caretFromPoint(anchor, "previous");
588
+ if (anchorCaret) if ($isTextPointCaret(anchorCaret)) nodeToInspect = anchorCaret.origin;
589
+ else {
590
+ const range = $getCaretRange(anchorCaret, $getChildCaret($getRoot(), "next").getFlipped());
591
+ for (const caret of range) if ($isTextNode(caret.origin)) {
592
+ nodeToInspect = caret.origin;
593
+ break;
594
+ } else if ($isElementNode(caret.origin) && !caret.origin.isInline()) break;
595
+ }
596
+ if (nodeToInspect && $isTextNode(nodeToInspect)) {
597
+ const newFormat = nodeToInspect.getFormat();
598
+ const newStyle = nodeToInspect.getStyle();
599
+ if (selection.format !== newFormat || selection.style !== newStyle) {
600
+ selection.format = newFormat;
601
+ selection.style = newStyle;
602
+ selection.dirty = true;
603
+ }
604
+ }
605
+ }
606
+ }
607
+ function exportNodeToJSON(node) {
608
+ const serializedNode = node.exportJSON();
609
+ const nodeClass = node.constructor;
610
+ if (serializedNode.type !== nodeClass.getType()) formatDevErrorMessage(`LexicalNode: Node ${nodeClass.name} does not implement .exportJSON().`);
611
+ if ($isElementNode(node)) {
612
+ const serializedChildren = serializedNode.children;
613
+ if (!Array.isArray(serializedChildren)) formatDevErrorMessage(`LexicalNode: Node ${nodeClass.name} is an element but .exportJSON() does not have a children array.`);
614
+ }
615
+ return serializedNode;
616
+ }
617
+ function $appendNodesToJSON(editor, selection, currentNode, targetArray = []) {
618
+ let shouldInclude = selection !== null ? currentNode.isSelected(selection) : true;
619
+ const shouldExclude = $isElementNode(currentNode) && currentNode.excludeFromCopy("html");
620
+ let target = currentNode;
621
+ if (selection !== null && $isTextNode(target)) target = $sliceSelectedTextNodeContent(selection, target, "clone");
622
+ const children = $isElementNode(target) ? target.getChildren() : [];
623
+ const serializedNode = exportNodeToJSON(target);
624
+ if ($isTextNode(target) && target.getTextContentSize() === 0) shouldInclude = false;
625
+ for (let i = 0; i < children.length; i++) {
626
+ const childNode = children[i];
627
+ const shouldIncludeChild = $appendNodesToJSON(editor, selection, childNode, serializedNode.children);
628
+ if (!shouldInclude && $isElementNode(currentNode) && shouldIncludeChild && currentNode.extractWithChild(childNode, selection, "clone")) shouldInclude = true;
629
+ }
630
+ if (shouldInclude && !shouldExclude) targetArray.push(serializedNode);
631
+ else if (Array.isArray(serializedNode.children)) for (let i = 0; i < serializedNode.children.length; i++) {
632
+ const serializedChildNode = serializedNode.children[i];
633
+ targetArray.push(serializedChildNode);
634
+ }
635
+ return shouldInclude;
636
+ }
637
+ /**
638
+ * Gets the Lexical JSON of the nodes inside the provided Selection.
639
+ *
640
+ * @param editor LexicalEditor to get the JSON content from.
641
+ * @param selection Selection to get the JSON content from.
642
+ * @returns an object with the editor namespace and a list of serializable nodes as JavaScript objects.
643
+ */
644
+ function $generateJSONFromSelectedNodes(editor, selection) {
645
+ const nodes = [];
646
+ const topLevelChildren = $getRoot().getChildren();
647
+ for (let i = 0; i < topLevelChildren.length; i++) {
648
+ const topLevelNode = topLevelChildren[i];
649
+ $appendNodesToJSON(editor, selection, topLevelNode, nodes);
650
+ }
651
+ return {
652
+ namespace: editor._config.namespace,
653
+ nodes
654
+ };
655
+ }
656
+ /**
657
+ * This method takes an array of objects conforming to the BaseSerializedNode interface and returns
658
+ * an Array containing instances of the corresponding LexicalNode classes registered on the editor.
659
+ * Normally, you'd get an Array of BaseSerialized nodes from {@link $generateJSONFromSelectedNodes}
660
+ *
661
+ * @param serializedNodes an Array of objects conforming to the BaseSerializedNode interface.
662
+ * @returns an Array of Lexical Node objects.
663
+ */
664
+ function $generateNodesFromSerializedNodes(serializedNodes) {
665
+ const nodes = [];
666
+ for (const serializedNode of serializedNodes) nodes.push($parseSerializedNode(serializedNode));
667
+ return nodes;
668
+ }
669
+ const EVENT_LATENCY = 50;
670
+ let clipboardEventTimeout = null;
671
+ /**
672
+ * Copies the content of the current selection to the clipboard in
673
+ * text/plain, text/html, and application/x-lexical-editor (Lexical JSON)
674
+ * formats.
675
+ *
676
+ * @param editor the LexicalEditor instance to copy content from
677
+ * @param event the native browser ClipboardEvent to add the content to.
678
+ * @returns
679
+ */
680
+ async function copyToClipboard(editor, event, data) {
681
+ if (clipboardEventTimeout !== null) return false;
682
+ if (event !== null) return new Promise((resolve, reject) => {
683
+ editor.update(() => {
684
+ resolve($copyToClipboardEvent(editor, event, data));
685
+ });
686
+ });
687
+ const rootElement = editor.getRootElement();
688
+ const editorWindow = editor._window || window;
689
+ const windowDocument = editorWindow.document;
690
+ const domSelection = getDOMSelection(editorWindow);
691
+ if (rootElement === null || domSelection === null) return false;
692
+ const element = windowDocument.createElement("span");
693
+ element.style.position = "fixed";
694
+ element.style.top = "-1000px";
695
+ element.append(windowDocument.createTextNode("#"));
696
+ rootElement.append(element);
697
+ const range = new Range();
698
+ range.setStart(element, 0);
699
+ range.setEnd(element, 1);
700
+ domSelection.removeAllRanges();
701
+ domSelection.addRange(range);
702
+ return new Promise((resolve, reject) => {
703
+ const removeListener = editor.registerCommand(COPY_COMMAND, (secondEvent) => {
704
+ if (objectKlassEquals(secondEvent, ClipboardEvent)) {
705
+ removeListener();
706
+ if (clipboardEventTimeout !== null) {
707
+ editorWindow.clearTimeout(clipboardEventTimeout);
708
+ clipboardEventTimeout = null;
709
+ }
710
+ resolve($copyToClipboardEvent(editor, secondEvent, data));
711
+ }
712
+ return true;
713
+ }, COMMAND_PRIORITY_CRITICAL);
714
+ clipboardEventTimeout = editorWindow.setTimeout(() => {
715
+ removeListener();
716
+ clipboardEventTimeout = null;
717
+ resolve(false);
718
+ }, EVENT_LATENCY);
719
+ windowDocument.execCommand("copy");
720
+ element.remove();
721
+ });
722
+ }
723
+ function $copyToClipboardEvent(editor, event, data) {
724
+ if (data === void 0) {
725
+ const domSelection = getDOMSelection(editor._window);
726
+ const selection = $getSelection();
727
+ if (!selection || selection.isCollapsed()) return false;
728
+ if (!domSelection) return false;
729
+ const anchorDOM = domSelection.anchorNode;
730
+ const focusDOM = domSelection.focusNode;
731
+ if (anchorDOM !== null && focusDOM !== null && !isSelectionWithinEditor(editor, anchorDOM, focusDOM)) return false;
732
+ data = $getClipboardDataFromSelection(selection);
733
+ }
734
+ event.preventDefault();
735
+ const clipboardData = event.clipboardData;
736
+ if (clipboardData === null) return false;
737
+ setLexicalClipboardDataTransfer(clipboardData, data);
738
+ return true;
739
+ }
740
+ const clipboardDataFunctions = [["text/html", $getHtmlContent], ["application/x-lexical-editor", $getLexicalContent]];
741
+ /**
742
+ * Serialize the content of the current selection to strings in
743
+ * text/plain, text/html, and application/x-lexical-editor (Lexical JSON)
744
+ * formats (as available).
745
+ *
746
+ * @param selection the selection to serialize (defaults to $getSelection())
747
+ * @returns LexicalClipboardData
748
+ */
749
+ function $getClipboardDataFromSelection(selection = $getSelection()) {
750
+ return $getClipboardDataWithConfigFromSelection($getExportConfig(), selection);
751
+ }
752
+ /**
753
+ * Call setData on the given clipboardData for each MIME type present
754
+ * in the given data (from {@link $getClipboardDataFromSelection})
755
+ *
756
+ * @param clipboardData the event.clipboardData to populate from data
757
+ * @param data The lexical data
758
+ */
759
+ function setLexicalClipboardDataTransfer(clipboardData, data) {
760
+ for (const [k] of clipboardDataFunctions) if (data[k] === void 0) clipboardData.setData(k, "");
761
+ for (const k in data) {
762
+ const v = data[k];
763
+ if (v !== void 0) clipboardData.setData(k, v);
764
+ }
765
+ }
766
+ /**
767
+ * A function that produces the serialized representation of a selection for
768
+ * a single MIME type. Functions are arranged in a stack per MIME type (see
769
+ * {@link ExportMimeTypeConfig}); the function at the top of the stack is
770
+ * invoked first and may call `next()` to delegate to the previous function
771
+ * in the stack (typically the default Lexical serializer).
772
+ *
773
+ * Returning `null` from the top-most function omits that MIME type from the
774
+ * resulting {@link LexicalClipboardData}.
775
+ *
776
+ * @param selection - The selection to serialize, or `null` if there is none.
777
+ * @param next - Calls the previous handler in the stack and returns its
778
+ * result, or `null` if there is no previous handler.
779
+ * @returns The serialized string for this MIME type, or `null` to omit it.
780
+ */
781
+ /**
782
+ * Configuration for {@link GetClipboardDataExtension}.
783
+ */
784
+ /**
785
+ * A mapping from MIME type to a stack of {@link ExportMimeTypeFunction}.
786
+ *
787
+ * Each entry is an ordered array; the function at the highest index runs
788
+ * first and may call `next()` to fall through to the function below it.
789
+ * The default config provides a single fallback handler for
790
+ * `'application/x-lexical-editor'`, `'text/html'`, and `'text/plain'`.
791
+ *
792
+ * When {@link GetClipboardDataExtension} merges a partial config, new
793
+ * functions are appended to the existing array for each MIME type, so
794
+ * later-registered handlers run before earlier ones (including the
795
+ * defaults) and may delegate to them via `next()`. To register a brand new
796
+ * MIME type, supply a key not present in the default config; arbitrary
797
+ * string keys are accepted in addition to the keys of
798
+ * {@link LexicalClipboardData}.
799
+ */
800
+ function $getExportConfig(editor = $getEditor()) {
801
+ const dep = getPeerDependencyFromEditor(editor, GetClipboardDataExtension.name);
802
+ return dep ? dep.output : DEFAULT_EXPORT_MIME_TYPE;
803
+ }
804
+ const DEFAULT_EXPORT_MIME_TYPE = {
805
+ "application/x-lexical-editor": [(sel, next) => sel ? $getLexicalContent($getEditor(), sel) : next()],
806
+ "text/html": [(sel, next) => sel ? $getHtmlContent($getEditor(), sel) : next()],
807
+ "text/plain": [(sel, next) => sel ? sel.getTextContent() : next()]
808
+ };
809
+ function $getClipboardDataWithConfigFromSelection($exportMimeType, selection) {
810
+ const clipboardData = { "text/plain": "" };
811
+ for (const [k, fns] of Object.entries($exportMimeType)) if (fns) {
812
+ const v = callExportMimeTypeFunctionStack(fns, selection);
813
+ if (v !== null) clipboardData[k] = v;
814
+ }
815
+ return clipboardData;
816
+ }
817
+ function callExportMimeTypeFunctionStack(fns, selection) {
818
+ const callAt = (i) => fns[i] ? fns[i](selection, callAt.bind(null, i - 1)) : null;
819
+ return callAt(fns.length - 1);
820
+ }
821
+ /**
822
+ * Serialize the given selection for a single MIME type using the active
823
+ * editor's configured {@link ExportMimeTypeConfig}. The configured stack is
824
+ * read from {@link GetClipboardDataExtension} via the editor's peer
825
+ * dependency lookup; if the extension was not built into the editor, the
826
+ * default stack is used.
827
+ *
828
+ * Useful when only one MIME representation is needed rather than the full
829
+ * {@link LexicalClipboardData} produced by
830
+ * {@link $getClipboardDataFromSelection}.
831
+ *
832
+ * Must be called from within an editor update or read.
833
+ *
834
+ * @param mimeType - The MIME type to serialize, e.g. `'text/html'`,
835
+ * `'application/x-lexical-editor'`, `'text/plain'`, or any custom key
836
+ * registered in the {@link ExportMimeTypeConfig}.
837
+ * @param selection - The selection to serialize (defaults to
838
+ * `$getSelection()`).
839
+ * @returns The serialized string for the requested MIME type, or `null` if
840
+ * no handler is registered for it or every handler returned `null`.
841
+ */
842
+ function $exportMimeTypeFromSelection(mimeType, selection = $getSelection()) {
843
+ return callExportMimeTypeFunctionStack($getExportConfig()[mimeType] || [], selection);
844
+ }
845
+ /**
846
+ * Lexical extension that controls how the current selection is serialized
847
+ * into clipboard MIME types when copying or dragging out of the editor.
848
+ *
849
+ * The extension's config holds an {@link ExportMimeTypeConfig} — a stack of
850
+ * {@link ExportMimeTypeFunction} per MIME type. Out of the box it provides
851
+ * fallback serializers for `'application/x-lexical-editor'`, `'text/html'`,
852
+ * and `'text/plain'` that defer to {@link $getLexicalContent},
853
+ * {@link $getHtmlContent}, and `selection.getTextContent()` respectively.
854
+ *
855
+ * Apps can layer additional handlers on top to customize an existing
856
+ * payload (delegating to the default via `next()`) or to register an
857
+ * entirely new MIME type. Functions provided through `mergeConfig` are
858
+ * appended to the existing stack for each MIME type, so a newly registered
859
+ * handler runs first and may fall through to the previously registered
860
+ * handlers via its `next` argument.
861
+ *
862
+ * The extension's `output` is the resolved {@link ExportMimeTypeConfig},
863
+ * which {@link $getClipboardDataFromSelection} and
864
+ * {@link $exportMimeTypeFromSelection} read via the editor's peer
865
+ * dependency lookup.
866
+ *
867
+ * @example
868
+ * ```ts
869
+ * import {configExtension, defineExtension} from '@lexical/extension';
870
+ * import {GetClipboardDataExtension} from '@lexical/clipboard';
871
+ *
872
+ * const MyClipboardExtension = defineExtension({
873
+ * name: 'my-app/clipboard',
874
+ * dependencies: [
875
+ * configExtension(GetClipboardDataExtension, {
876
+ * $exportMimeType: {
877
+ * // Wrap the default HTML output with an app-specific marker.
878
+ * 'text/html': [
879
+ * (selection, next) => {
880
+ * const html = next();
881
+ * return html ? wrapWithMyAppMarker(html) : html;
882
+ * },
883
+ * ],
884
+ * // Add a brand-new MIME type.
885
+ * 'application/vnd.myapp+json': [
886
+ * (selection) =>
887
+ * selection ? exportMyAppFormat(selection) : null,
888
+ * ],
889
+ * },
890
+ * }),
891
+ * ],
892
+ * });
893
+ * ```
894
+ */
895
+ const GetClipboardDataExtension = defineExtension({
896
+ build(editor, config, state) {
897
+ return config.$exportMimeType;
898
+ },
899
+ config: safeCast({ $exportMimeType: DEFAULT_EXPORT_MIME_TYPE }),
900
+ mergeConfig(config, partial) {
901
+ const merged = shallowMergeConfig(config, partial);
902
+ if (partial.$exportMimeType) {
903
+ const $exportMimeType = { ...config.$exportMimeType };
904
+ for (const [k, v] of Object.entries(partial.$exportMimeType)) if (v) {
905
+ const prev = $exportMimeType[k];
906
+ $exportMimeType[k] = prev ? [...prev, ...v] : v;
907
+ }
908
+ merged.$exportMimeType = $exportMimeType;
909
+ }
910
+ return merged;
911
+ },
912
+ name: "@lexical/clipboard/GetClipboardData"
913
+ });
914
+ //#endregion
915
+ export { $exportMimeTypeFromSelection, $generateJSONFromSelectedNodes, $generateNodesFromSerializedNodes, $getClipboardDataFromSelection, $getHtmlContent, $getLexicalContent, $handlePlainTextDrop, $handleRichTextDrop, $insertDataTransferForPlainText, $insertDataTransferForRichText, $insertGeneratedNodes, $writeDragSourceToDataTransfer, ClipboardDOMImportExtension, ClipboardImportExtension, DEFAULT_IMPORT_MIME_TYPE, DEFAULT_IMPORT_MIME_TYPE_PRIORITY, GetClipboardDataExtension, caretFromPoint, copyToClipboard, setLexicalClipboardDataTransfer };