@lexical/clipboard 0.13.1 → 0.14.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.
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { $generateHtmlFromNodes, $generateNodesFromDOM } from '@lexical/html';
8
+ import { $addNodeStyle, $cloneWithProperties, $sliceSelectedTextNodeContent } from '@lexical/selection';
9
+ import { objectKlassEquals } from '@lexical/utils';
10
+ import { $getSelection, $isRangeSelection, $createTabNode, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, $getRoot, $parseSerializedNode, $isTextNode, COPY_COMMAND, COMMAND_PRIORITY_CRITICAL, isSelectionWithinEditor, $isElementNode } from 'lexical';
11
+
12
+ /**
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ *
18
+ */
19
+
20
+ const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
21
+
22
+ /**
23
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
24
+ *
25
+ * This source code is licensed under the MIT license found in the
26
+ * LICENSE file in the root directory of this source tree.
27
+ *
28
+ */
29
+ const getDOMSelection = targetWindow => CAN_USE_DOM ? (targetWindow || window).getSelection() : null;
30
+
31
+ /**
32
+ * Returns the *currently selected* Lexical content as an HTML string, relying on the
33
+ * logic defined in the exportDOM methods on the LexicalNode classes. Note that
34
+ * this will not return the HTML content of the entire editor (unless all the content is included
35
+ * in the current selection).
36
+ *
37
+ * @param editor - LexicalEditor instance to get HTML content from
38
+ * @returns a string of HTML content
39
+ */
40
+ function $getHtmlContent(editor) {
41
+ const selection = $getSelection();
42
+ if (selection == null) {
43
+ {
44
+ throw Error(`Expected valid LexicalSelection`);
45
+ }
46
+ }
47
+
48
+ // If we haven't selected anything
49
+ if ($isRangeSelection(selection) && selection.isCollapsed() || selection.getNodes().length === 0) {
50
+ return '';
51
+ }
52
+ return $generateHtmlFromNodes(editor, selection);
53
+ }
54
+
55
+ /**
56
+ * Returns the *currently selected* Lexical content as a JSON string, relying on the
57
+ * logic defined in the exportJSON methods on the LexicalNode classes. Note that
58
+ * this will not return the JSON content of the entire editor (unless all the content is included
59
+ * in the current selection).
60
+ *
61
+ * @param editor - LexicalEditor instance to get the JSON content from
62
+ * @returns
63
+ */
64
+ function $getLexicalContent(editor) {
65
+ const selection = $getSelection();
66
+ if (selection == null) {
67
+ {
68
+ throw Error(`Expected valid LexicalSelection`);
69
+ }
70
+ }
71
+
72
+ // If we haven't selected anything
73
+ if ($isRangeSelection(selection) && selection.isCollapsed() || selection.getNodes().length === 0) {
74
+ return null;
75
+ }
76
+ return JSON.stringify($generateJSONFromSelectedNodes(editor, selection));
77
+ }
78
+
79
+ /**
80
+ * Attempts to insert content of the mime-types text/plain or text/uri-list from
81
+ * the provided DataTransfer object into the editor at the provided selection.
82
+ * text/uri-list is only used if text/plain is not also provided.
83
+ *
84
+ * @param dataTransfer an object conforming to the [DataTransfer interface] (https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface)
85
+ * @param selection the selection to use as the insertion point for the content in the DataTransfer object
86
+ */
87
+ function $insertDataTransferForPlainText(dataTransfer, selection) {
88
+ const text = dataTransfer.getData('text/plain') || dataTransfer.getData('text/uri-list');
89
+ if (text != null) {
90
+ selection.insertRawText(text);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Attempts to insert content of the mime-types application/x-lexical-editor, text/html,
96
+ * text/plain, or text/uri-list (in descending order of priority) from the provided DataTransfer
97
+ * object into the editor at the provided selection.
98
+ *
99
+ * @param dataTransfer an object conforming to the [DataTransfer interface] (https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface)
100
+ * @param selection the selection to use as the insertion point for the content in the DataTransfer object
101
+ * @param editor the LexicalEditor the content is being inserted into.
102
+ */
103
+ function $insertDataTransferForRichText(dataTransfer, selection, editor) {
104
+ const lexicalString = dataTransfer.getData('application/x-lexical-editor');
105
+ if (lexicalString) {
106
+ try {
107
+ const payload = JSON.parse(lexicalString);
108
+ if (payload.namespace === editor._config.namespace && Array.isArray(payload.nodes)) {
109
+ const nodes = $generateNodesFromSerializedNodes(payload.nodes);
110
+ return $insertGeneratedNodes(editor, nodes, selection);
111
+ }
112
+ } catch (_unused) {
113
+ // Fail silently.
114
+ }
115
+ }
116
+ const htmlString = dataTransfer.getData('text/html');
117
+ if (htmlString) {
118
+ try {
119
+ const parser = new DOMParser();
120
+ const dom = parser.parseFromString(htmlString, 'text/html');
121
+ const nodes = $generateNodesFromDOM(editor, dom);
122
+ return $insertGeneratedNodes(editor, nodes, selection);
123
+ } catch (_unused2) {
124
+ // Fail silently.
125
+ }
126
+ }
127
+
128
+ // Multi-line plain text in rich text mode pasted as separate paragraphs
129
+ // instead of single paragraph with linebreaks.
130
+ // Webkit-specific: Supports read 'text/uri-list' in clipboard.
131
+ const text = dataTransfer.getData('text/plain') || dataTransfer.getData('text/uri-list');
132
+ if (text != null) {
133
+ if ($isRangeSelection(selection)) {
134
+ const parts = text.split(/(\r?\n|\t)/);
135
+ if (parts[parts.length - 1] === '') {
136
+ parts.pop();
137
+ }
138
+ for (let i = 0; i < parts.length; i++) {
139
+ const part = parts[i];
140
+ if (part === '\n' || part === '\r\n') {
141
+ selection.insertParagraph();
142
+ } else if (part === '\t') {
143
+ selection.insertNodes([$createTabNode()]);
144
+ } else {
145
+ selection.insertText(part);
146
+ }
147
+ }
148
+ } else {
149
+ selection.insertRawText(text);
150
+ }
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Inserts Lexical nodes into the editor using different strategies depending on
156
+ * some simple selection-based heuristics. If you're looking for a generic way to
157
+ * to insert nodes into the editor at a specific selection point, you probably want
158
+ * {@link lexical.$insertNodes}
159
+ *
160
+ * @param editor LexicalEditor instance to insert the nodes into.
161
+ * @param nodes The nodes to insert.
162
+ * @param selection The selection to insert the nodes into.
163
+ */
164
+ function $insertGeneratedNodes(editor, nodes, selection) {
165
+ if (!editor.dispatchCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, {
166
+ nodes,
167
+ selection
168
+ })) {
169
+ selection.insertNodes(nodes);
170
+ }
171
+ return;
172
+ }
173
+ function exportNodeToJSON(node) {
174
+ const serializedNode = node.exportJSON();
175
+ const nodeClass = node.constructor;
176
+ if (serializedNode.type !== nodeClass.getType()) {
177
+ {
178
+ throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .exportJSON().`);
179
+ }
180
+ }
181
+ if ($isElementNode(node)) {
182
+ const serializedChildren = serializedNode.children;
183
+ if (!Array.isArray(serializedChildren)) {
184
+ {
185
+ throw Error(`LexicalNode: Node ${nodeClass.name} is an element but .exportJSON() does not have a children array.`);
186
+ }
187
+ }
188
+ }
189
+ return serializedNode;
190
+ }
191
+ function $appendNodesToJSON(editor, selection, currentNode, targetArray = []) {
192
+ let shouldInclude = selection !== null ? currentNode.isSelected(selection) : true;
193
+ const shouldExclude = $isElementNode(currentNode) && currentNode.excludeFromCopy('html');
194
+ let target = currentNode;
195
+ if (selection !== null) {
196
+ let clone = $cloneWithProperties(currentNode);
197
+ clone = $isTextNode(clone) && selection !== null ? $sliceSelectedTextNodeContent(selection, clone) : clone;
198
+ target = clone;
199
+ }
200
+ const children = $isElementNode(target) ? target.getChildren() : [];
201
+ const serializedNode = exportNodeToJSON(target);
202
+
203
+ // TODO: TextNode calls getTextContent() (NOT node.__text) within it's exportJSON method
204
+ // which uses getLatest() to get the text from the original node with the same key.
205
+ // This is a deeper issue with the word "clone" here, it's still a reference to the
206
+ // same node as far as the LexicalEditor is concerned since it shares a key.
207
+ // We need a way to create a clone of a Node in memory with it's own key, but
208
+ // until then this hack will work for the selected text extract use case.
209
+ if ($isTextNode(target)) {
210
+ const text = target.__text;
211
+ // If an uncollapsed selection ends or starts at the end of a line of specialized,
212
+ // TextNodes, such as code tokens, we will get a 'blank' TextNode here, i.e., one
213
+ // with text of length 0. We don't want this, it makes a confusing mess. Reset!
214
+ if (text.length > 0) {
215
+ serializedNode.text = text;
216
+ } else {
217
+ shouldInclude = false;
218
+ }
219
+ }
220
+ for (let i = 0; i < children.length; i++) {
221
+ const childNode = children[i];
222
+ const shouldIncludeChild = $appendNodesToJSON(editor, selection, childNode, serializedNode.children);
223
+ if (!shouldInclude && $isElementNode(currentNode) && shouldIncludeChild && currentNode.extractWithChild(childNode, selection, 'clone')) {
224
+ shouldInclude = true;
225
+ }
226
+ }
227
+ if (shouldInclude && !shouldExclude) {
228
+ targetArray.push(serializedNode);
229
+ } else if (Array.isArray(serializedNode.children)) {
230
+ for (let i = 0; i < serializedNode.children.length; i++) {
231
+ const serializedChildNode = serializedNode.children[i];
232
+ targetArray.push(serializedChildNode);
233
+ }
234
+ }
235
+ return shouldInclude;
236
+ }
237
+
238
+ // TODO why $ function with Editor instance?
239
+ /**
240
+ * Gets the Lexical JSON of the nodes inside the provided Selection.
241
+ *
242
+ * @param editor LexicalEditor to get the JSON content from.
243
+ * @param selection Selection to get the JSON content from.
244
+ * @returns an object with the editor namespace and a list of serializable nodes as JavaScript objects.
245
+ */
246
+ function $generateJSONFromSelectedNodes(editor, selection) {
247
+ const nodes = [];
248
+ const root = $getRoot();
249
+ const topLevelChildren = root.getChildren();
250
+ for (let i = 0; i < topLevelChildren.length; i++) {
251
+ const topLevelNode = topLevelChildren[i];
252
+ $appendNodesToJSON(editor, selection, topLevelNode, nodes);
253
+ }
254
+ return {
255
+ namespace: editor._config.namespace,
256
+ nodes
257
+ };
258
+ }
259
+
260
+ /**
261
+ * This method takes an array of objects conforming to the BaseSeralizedNode interface and returns
262
+ * an Array containing instances of the corresponding LexicalNode classes registered on the editor.
263
+ * Normally, you'd get an Array of BaseSerialized nodes from {@link $generateJSONFromSelectedNodes}
264
+ *
265
+ * @param serializedNodes an Array of objects conforming to the BaseSerializedNode interface.
266
+ * @returns an Array of Lexical Node objects.
267
+ */
268
+ function $generateNodesFromSerializedNodes(serializedNodes) {
269
+ const nodes = [];
270
+ for (let i = 0; i < serializedNodes.length; i++) {
271
+ const serializedNode = serializedNodes[i];
272
+ const node = $parseSerializedNode(serializedNode);
273
+ if ($isTextNode(node)) {
274
+ $addNodeStyle(node);
275
+ }
276
+ nodes.push(node);
277
+ }
278
+ return nodes;
279
+ }
280
+ const EVENT_LATENCY = 50;
281
+ let clipboardEventTimeout = null;
282
+
283
+ // TODO custom selection
284
+ // TODO potentially have a node customizable version for plain text
285
+ /**
286
+ * Copies the content of the current selection to the clipboard in
287
+ * text/plain, text/html, and application/x-lexical-editor (Lexical JSON)
288
+ * formats.
289
+ *
290
+ * @param editor the LexicalEditor instance to copy content from
291
+ * @param event the native browser ClipboardEvent to add the content to.
292
+ * @returns
293
+ */
294
+ async function copyToClipboard(editor, event) {
295
+ if (clipboardEventTimeout !== null) {
296
+ // Prevent weird race conditions that can happen when this function is run multiple times
297
+ // synchronously. In the future, we can do better, we can cancel/override the previously running job.
298
+ return false;
299
+ }
300
+ if (event !== null) {
301
+ return new Promise((resolve, reject) => {
302
+ editor.update(() => {
303
+ resolve($copyToClipboardEvent(editor, event));
304
+ });
305
+ });
306
+ }
307
+ const rootElement = editor.getRootElement();
308
+ const windowDocument = editor._window == null ? window.document : editor._window.document;
309
+ const domSelection = getDOMSelection(editor._window);
310
+ if (rootElement === null || domSelection === null) {
311
+ return false;
312
+ }
313
+ const element = windowDocument.createElement('span');
314
+ element.style.cssText = 'position: fixed; top: -1000px;';
315
+ element.append(windowDocument.createTextNode('#'));
316
+ rootElement.append(element);
317
+ const range = new Range();
318
+ range.setStart(element, 0);
319
+ range.setEnd(element, 1);
320
+ domSelection.removeAllRanges();
321
+ domSelection.addRange(range);
322
+ return new Promise((resolve, reject) => {
323
+ const removeListener = editor.registerCommand(COPY_COMMAND, secondEvent => {
324
+ if (objectKlassEquals(secondEvent, ClipboardEvent)) {
325
+ removeListener();
326
+ if (clipboardEventTimeout !== null) {
327
+ window.clearTimeout(clipboardEventTimeout);
328
+ clipboardEventTimeout = null;
329
+ }
330
+ resolve($copyToClipboardEvent(editor, secondEvent));
331
+ }
332
+ // Block the entire copy flow while we wait for the next ClipboardEvent
333
+ return true;
334
+ }, COMMAND_PRIORITY_CRITICAL);
335
+ // If the above hack execCommand hack works, this timeout code should never fire. Otherwise,
336
+ // the listener will be quickly freed so that the user can reuse it again
337
+ clipboardEventTimeout = window.setTimeout(() => {
338
+ removeListener();
339
+ clipboardEventTimeout = null;
340
+ resolve(false);
341
+ }, EVENT_LATENCY);
342
+ windowDocument.execCommand('copy');
343
+ element.remove();
344
+ });
345
+ }
346
+
347
+ // TODO shouldn't pass editor (pass namespace directly)
348
+ function $copyToClipboardEvent(editor, event) {
349
+ const domSelection = getDOMSelection(editor._window);
350
+ if (!domSelection) {
351
+ return false;
352
+ }
353
+ const anchorDOM = domSelection.anchorNode;
354
+ const focusDOM = domSelection.focusNode;
355
+ if (anchorDOM !== null && focusDOM !== null && !isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
356
+ return false;
357
+ }
358
+ event.preventDefault();
359
+ const clipboardData = event.clipboardData;
360
+ const selection = $getSelection();
361
+ if (clipboardData === null || selection === null) {
362
+ return false;
363
+ }
364
+ const htmlString = $getHtmlContent(editor);
365
+ const lexicalString = $getLexicalContent(editor);
366
+ let plainString = '';
367
+ if (selection !== null) {
368
+ plainString = selection.getTextContent();
369
+ }
370
+ if (htmlString !== null) {
371
+ clipboardData.setData('text/html', htmlString);
372
+ }
373
+ if (lexicalString !== null) {
374
+ clipboardData.setData('application/x-lexical-editor', lexicalString);
375
+ }
376
+ clipboardData.setData('text/plain', plainString);
377
+ return true;
378
+ }
379
+
380
+ export { $generateJSONFromSelectedNodes, $generateNodesFromSerializedNodes, $getHtmlContent, $getLexicalContent, $insertDataTransferForPlainText, $insertDataTransferForRichText, $insertGeneratedNodes, copyToClipboard };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import * as modDev from './LexicalClipboard.dev.esm.js';
8
+ import * as modProd from './LexicalClipboard.prod.esm.js';
9
+ const mod = process.env.NODE_ENV === 'development' ? modDev : modProd;
10
+ export const $generateJSONFromSelectedNodes = mod.$generateJSONFromSelectedNodes;
11
+ export const $generateNodesFromSerializedNodes = mod.$generateNodesFromSerializedNodes;
12
+ export const $getHtmlContent = mod.$getHtmlContent;
13
+ export const $getLexicalContent = mod.$getLexicalContent;
14
+ export const $insertDataTransferForPlainText = mod.$insertDataTransferForPlainText;
15
+ export const $insertDataTransferForRichText = mod.$insertDataTransferForRichText;
16
+ export const $insertGeneratedNodes = mod.$insertGeneratedNodes;
17
+ export const copyToClipboard = mod.copyToClipboard;
@@ -5,5 +5,5 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  'use strict'
8
- const LexicalClipboard = process.env.NODE_ENV === 'development' ? require('./LexicalClipboard.dev.js') : require('./LexicalClipboard.prod.js')
8
+ const LexicalClipboard = process.env.NODE_ENV === 'development' ? require('./LexicalClipboard.dev.js') : require('./LexicalClipboard.prod.js');
9
9
  module.exports = LexicalClipboard;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import{$generateHtmlFromNodes as e,$generateNodesFromDOM as t}from"@lexical/html";import{$addNodeStyle as n,$cloneWithProperties as o,$sliceSelectedTextNodeContent as l}from"@lexical/selection";import{objectKlassEquals as r}from"@lexical/utils";import{$getSelection as i,$isRangeSelection as a,$createTabNode as c,SELECTION_INSERT_CLIPBOARD_NODES_COMMAND as s,$getRoot as u,$parseSerializedNode as d,$isTextNode as f,COPY_COMMAND as p,COMMAND_PRIORITY_CRITICAL as m,isSelectionWithinEditor as h,$isElementNode as x}from"lexical";var g=function(e){const t=new URLSearchParams;t.append("code",e);for(let e=1;e<arguments.length;e++)t.append("v",arguments[e]);throw Error(`Minified Lexical error #${e}; visit https://lexical.dev/docs/error?${t} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)};const w="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,y=e=>w?(e||window).getSelection():null;function v(t){const n=i();if(null==n)throw Error("Expected valid LexicalSelection");return a(n)&&n.isCollapsed()||0===n.getNodes().length?"":e(t,n)}function D(e){const t=i();if(null==t)throw Error("Expected valid LexicalSelection");return a(t)&&t.isCollapsed()||0===t.getNodes().length?null:JSON.stringify(T(e,t))}function C(e,t){const n=e.getData("text/plain")||e.getData("text/uri-list");null!=n&&t.insertRawText(n)}function E(e,n,o){const l=e.getData("application/x-lexical-editor");if(l)try{const e=JSON.parse(l);if(e.namespace===o._config.namespace&&Array.isArray(e.nodes)){return N(o,_(e.nodes),n)}}catch(e){}const r=e.getData("text/html");if(r)try{const e=(new DOMParser).parseFromString(r,"text/html");return N(o,t(o,e),n)}catch(e){}const i=e.getData("text/plain")||e.getData("text/uri-list");if(null!=i)if(a(n)){const e=i.split(/(\r?\n|\t)/);""===e[e.length-1]&&e.pop();for(let t=0;t<e.length;t++){const o=e[t];"\n"===o||"\r\n"===o?n.insertParagraph():"\t"===o?n.insertNodes([c()]):n.insertText(o)}}else n.insertRawText(i)}function N(e,t,n){e.dispatchCommand(s,{nodes:t,selection:n})||n.insertNodes(t)}function S(e,t,n,r=[]){let i=null===t||n.isSelected(t);const a=x(n)&&n.excludeFromCopy("html");let c=n;if(null!==t){let e=o(n);e=f(e)&&null!==t?l(t,e):e,c=e}const s=x(c)?c.getChildren():[],u=function(e){const t=e.exportJSON(),n=e.constructor;if(t.type!==n.getType()&&g(58,n.name),x(e)){const e=t.children;Array.isArray(e)||g(59,n.name)}return t}(c);if(f(c)){const e=c.__text;e.length>0?u.text=e:i=!1}for(let o=0;o<s.length;o++){const l=s[o],r=S(e,t,l,u.children);!i&&x(n)&&r&&n.extractWithChild(l,t,"clone")&&(i=!0)}if(i&&!a)r.push(u);else if(Array.isArray(u.children))for(let e=0;e<u.children.length;e++){const t=u.children[e];r.push(t)}return i}function T(e,t){const n=[],o=u().getChildren();for(let l=0;l<o.length;l++){S(e,t,o[l],n)}return{namespace:e._config.namespace,nodes:n}}function _(e){const t=[];for(let o=0;o<e.length;o++){const l=e[o],r=d(l);f(r)&&n(r),t.push(r)}return t}let A=null;async function R(e,t){if(null!==A)return!1;if(null!==t)return new Promise(((n,o)=>{e.update((()=>{n(P(e,t))}))}));const n=e.getRootElement(),o=null==e._window?window.document:e._window.document,l=y(e._window);if(null===n||null===l)return!1;const i=o.createElement("span");i.style.cssText="position: fixed; top: -1000px;",i.append(o.createTextNode("#")),n.append(i);const a=new Range;return a.setStart(i,0),a.setEnd(i,1),l.removeAllRanges(),l.addRange(a),new Promise(((t,n)=>{const l=e.registerCommand(p,(n=>(r(n,ClipboardEvent)&&(l(),null!==A&&(window.clearTimeout(A),A=null),t(P(e,n))),!0)),m);A=window.setTimeout((()=>{l(),A=null,t(!1)}),50),o.execCommand("copy"),i.remove()}))}function P(e,t){const n=y(e._window);if(!n)return!1;const o=n.anchorNode,l=n.focusNode;if(null!==o&&null!==l&&!h(e,o,l))return!1;t.preventDefault();const r=t.clipboardData,a=i();if(null===r||null===a)return!1;const c=v(e),s=D(e);let u="";return null!==a&&(u=a.getTextContent()),null!==c&&r.setData("text/html",c),null!==s&&r.setData("application/x-lexical-editor",s),r.setData("text/plain",u),!0}export{T as $generateJSONFromSelectedNodes,_ as $generateNodesFromSerializedNodes,v as $getHtmlContent,D as $getLexicalContent,C as $insertDataTransferForPlainText,E as $insertDataTransferForRichText,N as $insertGeneratedNodes,R as copyToClipboard};
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
1
  # `@lexical/clipboard`
2
2
 
3
+ [![See API Documentation](https://lexical.dev/img/see-api-documentation.svg)](https://lexical.dev/docs/api/modules/lexical_clipboard)
4
+
3
5
  This package contains the functionality for the clipboard feature of Lexical.
package/package.json CHANGED
@@ -9,20 +9,22 @@
9
9
  "paste"
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "0.13.1",
12
+ "version": "0.14.0",
13
13
  "main": "LexicalClipboard.js",
14
14
  "peerDependencies": {
15
- "lexical": "0.13.1"
15
+ "lexical": "0.14.0"
16
16
  },
17
17
  "dependencies": {
18
- "@lexical/utils": "0.13.1",
19
- "@lexical/list": "0.13.1",
20
- "@lexical/selection": "0.13.1",
21
- "@lexical/html": "0.13.1"
18
+ "@lexical/utils": "0.14.0",
19
+ "@lexical/list": "0.14.0",
20
+ "@lexical/selection": "0.14.0",
21
+ "@lexical/html": "0.14.0"
22
22
  },
23
23
  "repository": {
24
24
  "type": "git",
25
25
  "url": "https://github.com/facebook/lexical",
26
26
  "directory": "packages/lexical-clipboard"
27
- }
27
+ },
28
+ "module": "LexicalClipboard.esm.js",
29
+ "sideEffects": false
28
30
  }