@lexical/link 0.44.1-nightly.20260519.0 → 0.45.1-dev.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.
@@ -11,6 +11,7 @@
11
11
  var utils = require('@lexical/utils');
12
12
  var lexical = require('lexical');
13
13
  var extension = require('@lexical/extension');
14
+ var html = require('@lexical/html');
14
15
 
15
16
  /**
16
17
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -787,8 +788,9 @@ function registerLink(editor, stores) {
787
788
  if (!validateUrl(clipboardText)) {
788
789
  return false;
789
790
  }
790
- // If we select nodes that are elements then avoid applying the link.
791
- if (!selection.getNodes().some(node => lexical.$isElementNode(node))) {
791
+ // Skip link wrapping for non-simple text nodes (e.g. code blocks).
792
+ const nodes = selection.getNodes();
793
+ if (!nodes.some(node => lexical.$isElementNode(node) || lexical.$isTextNode(node) && !node.isSimpleText())) {
792
794
  editor.dispatchCommand(TOGGLE_LINK_COMMAND, {
793
795
  ...attributes,
794
796
  url: clipboardText
@@ -1344,6 +1346,60 @@ const AutoLinkExtension = lexical.defineExtension({
1344
1346
  register: registerAutoLink
1345
1347
  });
1346
1348
 
1349
+ /**
1350
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1351
+ *
1352
+ * This source code is licensed under the MIT license found in the
1353
+ * LICENSE file in the root directory of this source tree.
1354
+ *
1355
+ */
1356
+
1357
+ const AnchorRule = html.defineImportRule({
1358
+ $import: (ctx, el) => {
1359
+ if (!el.textContent && el.children.length === 0) {
1360
+ return [];
1361
+ }
1362
+ // Use no schema here: when the `<a>` contains block descendants
1363
+ // (e.g. `<a><h1>x</h1><div>y</div></a>`), we want them lifted so each
1364
+ // block becomes a sibling at the link's level, with the link wrapping
1365
+ // its inline content. $distributeInlineWrapper handles both the
1366
+ // common all-inline case (single LinkNode wrapping the run) and the
1367
+ // mixed-block case (per-block recursion).
1368
+ const href = el.getAttribute('href') || '';
1369
+ const attrs = {
1370
+ rel: el.getAttribute('rel'),
1371
+ target: el.getAttribute('target'),
1372
+ title: el.getAttribute('title')
1373
+ };
1374
+ return html.$distributeInlineWrapper(ctx.$importChildren(el), () => $createLinkNode(href, attrs));
1375
+ },
1376
+ match: html.sel.tag('a'),
1377
+ name: '@lexical/link/a'
1378
+ });
1379
+
1380
+ /**
1381
+ * Import rules for {@link LinkNode}.
1382
+ *
1383
+ * @experimental
1384
+ */
1385
+ const LinkImportRules = [AnchorRule];
1386
+
1387
+ /**
1388
+ * Bundles {@link LinkImportRules} together with the runtime
1389
+ * {@link LinkExtension}. The application is expected to already have
1390
+ * `CoreImportExtension` (or some equivalent) in its dependency graph —
1391
+ * the core/text/paragraph/inline-format rules are a shared baseline,
1392
+ * not something this leaf importer should re-declare.
1393
+ *
1394
+ * @experimental
1395
+ */
1396
+ const LinkImportExtension = lexical.defineExtension({
1397
+ dependencies: [LinkExtension, lexical.configExtension(html.DOMImportExtension, {
1398
+ rules: LinkImportRules
1399
+ })],
1400
+ name: '@lexical/link/Import'
1401
+ });
1402
+
1347
1403
  /**
1348
1404
  * Copyright (c) Meta Platforms, Inc. and affiliates.
1349
1405
  *
@@ -1365,6 +1421,8 @@ exports.AutoLinkExtension = AutoLinkExtension;
1365
1421
  exports.AutoLinkNode = AutoLinkNode;
1366
1422
  exports.ClickableLinkExtension = ClickableLinkExtension;
1367
1423
  exports.LinkExtension = LinkExtension;
1424
+ exports.LinkImportExtension = LinkImportExtension;
1425
+ exports.LinkImportRules = LinkImportRules;
1368
1426
  exports.LinkNode = LinkNode;
1369
1427
  exports.TOGGLE_LINK_COMMAND = TOGGLE_LINK_COMMAND;
1370
1428
  exports.createLinkMatcherWithRegExp = createLinkMatcherWithRegExp;
@@ -7,8 +7,9 @@
7
7
  */
8
8
 
9
9
  import { addClassNamesToElement, isHTMLAnchorElement, $findMatchingParent, $insertNodeToNearestRootAtCaret, mergeRegister, objectKlassEquals } from '@lexical/utils';
10
- import { ElementNode, $copyNode, $isRangeSelection, $applyNodeReplacement, createCommand, $getSelection, $isNodeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, $getChildCaret, $isElementNode, $rewindSiblingCaret, $caretFromPoint, $normalizeCaret, $isSiblingCaret, $setPointFromCaret, defineExtension, shallowMergeConfig, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, COMMAND_PRIORITY_LOW, safeCast, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isTextNode, $isLineBreakNode, $createTextNode } from 'lexical';
10
+ import { ElementNode, $copyNode, $isRangeSelection, $applyNodeReplacement, createCommand, $getSelection, $isNodeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, $getChildCaret, $isElementNode, $rewindSiblingCaret, $caretFromPoint, $normalizeCaret, $isSiblingCaret, $setPointFromCaret, defineExtension, shallowMergeConfig, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, $isTextNode, COMMAND_PRIORITY_LOW, safeCast, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isLineBreakNode, $createTextNode, configExtension } from 'lexical';
11
11
  import { namedSignals, effect } from '@lexical/extension';
12
+ import { DOMImportExtension, defineImportRule, sel, $distributeInlineWrapper } from '@lexical/html';
12
13
 
13
14
  /**
14
15
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -785,8 +786,9 @@ function registerLink(editor, stores) {
785
786
  if (!validateUrl(clipboardText)) {
786
787
  return false;
787
788
  }
788
- // If we select nodes that are elements then avoid applying the link.
789
- if (!selection.getNodes().some(node => $isElementNode(node))) {
789
+ // Skip link wrapping for non-simple text nodes (e.g. code blocks).
790
+ const nodes = selection.getNodes();
791
+ if (!nodes.some(node => $isElementNode(node) || $isTextNode(node) && !node.isSimpleText())) {
790
792
  editor.dispatchCommand(TOGGLE_LINK_COMMAND, {
791
793
  ...attributes,
792
794
  url: clipboardText
@@ -1342,6 +1344,60 @@ const AutoLinkExtension = defineExtension({
1342
1344
  register: registerAutoLink
1343
1345
  });
1344
1346
 
1347
+ /**
1348
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1349
+ *
1350
+ * This source code is licensed under the MIT license found in the
1351
+ * LICENSE file in the root directory of this source tree.
1352
+ *
1353
+ */
1354
+
1355
+ const AnchorRule = defineImportRule({
1356
+ $import: (ctx, el) => {
1357
+ if (!el.textContent && el.children.length === 0) {
1358
+ return [];
1359
+ }
1360
+ // Use no schema here: when the `<a>` contains block descendants
1361
+ // (e.g. `<a><h1>x</h1><div>y</div></a>`), we want them lifted so each
1362
+ // block becomes a sibling at the link's level, with the link wrapping
1363
+ // its inline content. $distributeInlineWrapper handles both the
1364
+ // common all-inline case (single LinkNode wrapping the run) and the
1365
+ // mixed-block case (per-block recursion).
1366
+ const href = el.getAttribute('href') || '';
1367
+ const attrs = {
1368
+ rel: el.getAttribute('rel'),
1369
+ target: el.getAttribute('target'),
1370
+ title: el.getAttribute('title')
1371
+ };
1372
+ return $distributeInlineWrapper(ctx.$importChildren(el), () => $createLinkNode(href, attrs));
1373
+ },
1374
+ match: sel.tag('a'),
1375
+ name: '@lexical/link/a'
1376
+ });
1377
+
1378
+ /**
1379
+ * Import rules for {@link LinkNode}.
1380
+ *
1381
+ * @experimental
1382
+ */
1383
+ const LinkImportRules = [AnchorRule];
1384
+
1385
+ /**
1386
+ * Bundles {@link LinkImportRules} together with the runtime
1387
+ * {@link LinkExtension}. The application is expected to already have
1388
+ * `CoreImportExtension` (or some equivalent) in its dependency graph —
1389
+ * the core/text/paragraph/inline-format rules are a shared baseline,
1390
+ * not something this leaf importer should re-declare.
1391
+ *
1392
+ * @experimental
1393
+ */
1394
+ const LinkImportExtension = defineExtension({
1395
+ dependencies: [LinkExtension, configExtension(DOMImportExtension, {
1396
+ rules: LinkImportRules
1397
+ })],
1398
+ name: '@lexical/link/Import'
1399
+ });
1400
+
1345
1401
  /**
1346
1402
  * Copyright (c) Meta Platforms, Inc. and affiliates.
1347
1403
  *
@@ -1354,4 +1410,4 @@ const AutoLinkExtension = defineExtension({
1354
1410
  /** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
1355
1411
  const toggleLink = $toggleLink;
1356
1412
 
1357
- export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, AutoLinkExtension, AutoLinkNode, ClickableLinkExtension, LinkExtension, LinkNode, TOGGLE_LINK_COMMAND, createLinkMatcherWithRegExp, formatUrl, registerAutoLink, registerClickableLink, registerLink, toggleLink };
1413
+ export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, AutoLinkExtension, AutoLinkNode, ClickableLinkExtension, LinkExtension, LinkImportExtension, LinkImportRules, LinkNode, TOGGLE_LINK_COMMAND, createLinkMatcherWithRegExp, formatUrl, registerAutoLink, registerClickableLink, registerLink, toggleLink };
@@ -18,6 +18,8 @@ export const AutoLinkExtension = mod.AutoLinkExtension;
18
18
  export const AutoLinkNode = mod.AutoLinkNode;
19
19
  export const ClickableLinkExtension = mod.ClickableLinkExtension;
20
20
  export const LinkExtension = mod.LinkExtension;
21
+ export const LinkImportExtension = mod.LinkImportExtension;
22
+ export const LinkImportRules = mod.LinkImportRules;
21
23
  export const LinkNode = mod.LinkNode;
22
24
  export const TOGGLE_LINK_COMMAND = mod.TOGGLE_LINK_COMMAND;
23
25
  export const createLinkMatcherWithRegExp = mod.createLinkMatcherWithRegExp;
@@ -16,6 +16,8 @@ export const AutoLinkExtension = mod.AutoLinkExtension;
16
16
  export const AutoLinkNode = mod.AutoLinkNode;
17
17
  export const ClickableLinkExtension = mod.ClickableLinkExtension;
18
18
  export const LinkExtension = mod.LinkExtension;
19
+ export const LinkImportExtension = mod.LinkImportExtension;
20
+ export const LinkImportRules = mod.LinkImportRules;
19
21
  export const LinkNode = mod.LinkNode;
20
22
  export const TOGGLE_LINK_COMMAND = mod.TOGGLE_LINK_COMMAND;
21
23
  export const createLinkMatcherWithRegExp = mod.createLinkMatcherWithRegExp;
@@ -0,0 +1,9 @@
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
+ */
8
+
9
+ "use strict";var e=require("@lexical/utils"),t=require("lexical"),n=require("@lexical/extension"),r=require("@lexical/html");const i=new Set(["http:","https:","mailto:","sms:","tel:"]);class s extends t.ElementNode{__url;__target;__rel;__title;static getType(){return"link"}static clone(e){return new s(e.__url,{rel:e.__rel,target:e.__target,title:e.__title},e.__key)}constructor(e="",t={},n){super(n);const{target:r=null,rel:i=null,title:s=null}=t;this.__url=e,this.__target=r,this.__rel=i,this.__title=s}afterCloneFrom(e){super.afterCloneFrom(e),this.__url=e.__url,this.__rel=e.__rel,this.__target=e.__target,this.__title=e.__title}createDOM(t){const n=document.createElement("a");return this.updateLinkDOM(null,n,t),e.addClassNamesToElement(n,t.theme.link),n}updateLinkDOM(t,n,r){if(e.isHTMLAnchorElement(n)){t&&t.__url===this.__url||(n.href=this.sanitizeUrl(this.__url));for(const e of["target","rel","title"]){const r=`__${e}`,i=this[r];t&&t[r]===i||(i?n[e]=i:n.removeAttribute(e))}}}updateDOM(e,t,n){return this.updateLinkDOM(e,t,n),!1}static importDOM(){return{a:e=>({conversion:u,priority:1})}}static importJSON(e){return c().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setURL(e.url).setRel(e.rel||null).setTarget(e.target||null).setTitle(e.title||null)}sanitizeUrl(e){e=k(e);try{const t=new URL(k(e));if(!i.has(t.protocol))return"about:blank"}catch(t){return e}return e}exportJSON(){return{...super.exportJSON(),rel:this.getRel(),target:this.getTarget(),title:this.getTitle(),url:this.getURL()}}getURL(){return this.getLatest().__url}setURL(e){const t=this.getWritable();return t.__url=e,t}getTarget(){return this.getLatest().__target}setTarget(e){const t=this.getWritable();return t.__target=e,t}getRel(){return this.getLatest().__rel}setRel(e){const t=this.getWritable();return t.__rel=e,t}getTitle(){return this.getLatest().__title}setTitle(e){const t=this.getWritable();return t.__title=e,t}insertNewAfter(e,n=!0){const r=t.$copyNode(this);return this.insertAfter(r,n),r}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(e,n,r){if(!t.$isRangeSelection(n))return!1;const i=n.anchor.getNode(),s=n.focus.getNode();return this.isParentOf(i)&&this.isParentOf(s)&&n.getTextContent().length>0}isEmailURI(){return this.__url.startsWith("mailto:")}isWebSiteURI(){return this.__url.startsWith("https://")||this.__url.startsWith("http://")}shouldMergeAdjacentLink(e){return this.getType()===e.getType()&&this.__url===e.__url&&this.__target===e.__target&&this.__rel===e.__rel&&this.__title===e.__title}}function l(e){const n=t.$caretFromPoint(e,"next");return[n,n.getFlipped()]}function o(e,n){for(const r of n)if(r.origin.isAttached()){const n=t.$normalizeCaret(r);return void t.$setPointFromCaret(e,n)}}function a(n){const r=t.$getSelection();let i=null,s=null;function a(){t.$isRangeSelection(r)&&(o(r.anchor,i),o(r.focus,s),t.$normalizeSelection__EXPERIMENTAL(r))}t.$isRangeSelection(r)&&(i=l(r.anchor),s=l(r.focus));let u=!1;for(const r of t.$getChildCaret(n,"next")){const i=r.origin;if(t.$isElementNode(i)&&!i.isInline()){const s=i.getChildren();if(s.length>0){const e=t.$copyNode(n);e.append(...s),i.append(e),u=!0}e.$insertNodeToNearestRootAtCaret(i,t.$rewindSiblingCaret(r),{$shouldSplit:()=>!1})}}function c(e,n,r){const[i,s]=e,l=e=>t.$isSiblingCaret(e)&&e.origin.is(n);if(!l(i)&&!l(s))return e;const o=t.$normalizeCaret(t.$getChildCaret(r,"next"));return[o,o.getFlipped()]}if(n.isAttached()){const e=n.getPreviousSibling();if(g(e)&&e.shouldMergeAdjacentLink(n))return i&&(i=c(i,e,n)),s&&(s=c(s,e,n)),e.append(...n.getChildren()),n.remove(),void a();const t=n.getNextSibling();g(t)&&n.shouldMergeAdjacentLink(t)&&(i&&(i=c(i,n,t)),s&&(s=c(s,n,t)),n.append(...t.getChildren()),t.remove(),u=!0)}if(u){if(!n.canBeEmpty()&&n.isEmpty()){const e=n.getParent();n.remove(),e&&e.isEmpty()&&e.remove()}a()}}function u(t){let n=null;if(e.isHTMLAnchorElement(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=c(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function c(e="",n){return t.$applyNodeReplacement(new s(e,n))}function g(e){return e instanceof s}class d extends s{__isUnlinked;constructor(e="",t={},n){super(e,t,n),this.__isUnlinked=void 0!==t.isUnlinked&&null!==t.isUnlinked&&t.isUnlinked}afterCloneFrom(e){super.afterCloneFrom(e),this.__isUnlinked=e.__isUnlinked}static getType(){return"autolink"}static clone(e){return new d(e.__url,{isUnlinked:e.__isUnlinked,rel:e.__rel,target:e.__target,title:e.__title},e.__key)}shouldMergeAdjacentLink(e){return!1}getIsUnlinked(){return this.__isUnlinked}setIsUnlinked(e){const t=this.getWritable();return t.__isUnlinked=e,t}createDOM(e){return this.__isUnlinked?document.createElement("span"):super.createDOM(e)}updateDOM(e,t,n){return super.updateDOM(e,t,n)||e.__isUnlinked!==this.__isUnlinked}static importJSON(e){return f().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setIsUnlinked(e.isUnlinked||!1)}static importDOM(){return null}exportJSON(){return{...super.exportJSON(),isUnlinked:this.__isUnlinked}}insertNewAfter(e,t=!0){const n=f(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,t),n}}function f(e="",n){return t.$applyNodeReplacement(new d(e,n))}function h(e){return e instanceof d}const p=t.createCommand("TOGGLE_LINK_COMMAND");function _(e,n){if("element"===e.type){const r=e.getNode();t.$isElementNode(r)||function(e,...t){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",e);for(const e of t)r.append("v",e);throw n.search=r.toString(),Error(`Minified Lexical error #${e}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}(252);return r.getChildren()[e.offset+n]||null}return null}function m(n,r={}){let i;if(n&&"object"==typeof n){const{url:e,...t}=n;i=e,r={...t,...r}}else i=n;const{target:s,title:l}=r,o=void 0===r.rel?"noreferrer":r.rel,a=t.$getSelection();if(null===a||!t.$isRangeSelection(a)&&!t.$isNodeSelection(a))return;if(t.$isNodeSelection(a)){const t=a.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===i){const n=e.$findMatchingParent(t,e=>!h(e)&&g(e));n&&(n.insertBefore(t),0===n.getChildren().length&&n.remove())}else{const n=e.$findMatchingParent(t,e=>!h(e)&&g(e));if(n)n.setURL(i),void 0!==s&&n.setTarget(s),void 0!==o&&n.setRel(o);else{const e=c(i,{rel:o,target:s});t.insertBefore(e),e.append(t)}}})}if(a.isCollapsed()&&null===i)for(const t of a.getNodes()){const n=e.$findMatchingParent(t,e=>!h(e)&&g(e));return void(null!==n&&(n.getParentOrThrow().splice(n.getIndexWithinParent(),0,n.getChildren()),n.remove()))}const u=a.extract();if(null===i){const n=new Set;return void u.forEach(r=>{const i=e.$findMatchingParent(r,e=>!h(e)&&g(e));if(null!==i){const e=i.getKey();if(n.has(e))return;!function(e,n){const r=new Set(n.filter(t=>e.isParentOf(t)).map(e=>e.getKey())),i=e.getChildren(),s=i=>r.has(i.getKey())||t.$isElementNode(i)&&n.some(t=>e.isParentOf(t)&&i.isParentOf(t)),l=i.filter(s);if(l.length===i.length)return i.forEach(t=>e.insertBefore(t)),void e.remove();const o=i.findIndex(s),a=i.findLastIndex(s),u=0===o,c=a===i.length-1;if(u)l.forEach(t=>e.insertBefore(t));else if(c)for(let t=l.length-1;t>=0;t--)e.insertAfter(l[t]);else{for(let t=l.length-1;t>=0;t--)e.insertAfter(l[t]);const n=i.slice(a+1);if(n.length>0){const r=t.$copyNode(e);l[l.length-1].insertAfter(r),n.forEach(e=>r.append(e))}}}(i,u),n.add(e)}})}const d=new Set,f=e=>{d.has(e.getKey())||(d.add(e.getKey()),e.setURL(i),void 0!==s&&e.setTarget(s),void 0!==o&&e.setRel(o),void 0!==l&&e.setTitle(l))};if(1===u.length){const t=u[0],n=e.$findMatchingParent(t,g);if(null!==n)return f(n)}!function(e){const n=t.$getSelection();if(!t.$isRangeSelection(n))return e();const r=t.$normalizeSelection__EXPERIMENTAL(n),i=r.isBackward(),s=_(r.anchor,i?-1:0),l=_(r.focus,i?0:-1),o=e();if(s||l){const e=t.$getSelection();if(t.$isRangeSelection(e)){const n=e.clone();if(s){const e=s.getParent();e&&n.anchor.set(e.getKey(),s.getIndexWithinParent()+(i?1:0),"element")}if(l){const e=l.getParent();e&&n.focus.set(e.getKey(),l.getIndexWithinParent()+(i?0:1),"element")}t.$setSelection(t.$normalizeSelection__EXPERIMENTAL(n))}}}(()=>{let n=null;for(const r of u){if(!r.isAttached())continue;const a=e.$findMatchingParent(r,g);if(a){f(a);continue}if(t.$isElementNode(r)){if(!r.isInline())continue;if(g(r)){if(!(h(r)||null!==n&&n.getParentOrThrow().isParentOf(r))){f(r),n=r;continue}for(const e of r.getChildren())r.insertBefore(e);r.remove();continue}}const u=r.getPreviousSibling();g(u)&&u.is(n)?u.append(r):(n=c(i,{rel:o,target:s,title:l}),r.insertAfter(n),n.append(r))}})}const x=/^\+?[0-9\s()-]{5,}$/;function k(e){return e.match(/^[a-z][a-z0-9+.-]*:/i)||e.match(/^[/#.]/)?e:e.includes("@")?`mailto:${e}`:x.test(e)?`tel:${e}`:`https://${e}`}const N={attributes:void 0,validateUrl:void 0};function $(r,i){return e.mergeRegister(r.registerNodeTransform(s,a),r.registerCommand(p,e=>{const t=i.validateUrl.peek(),n=i.attributes.peek();if(null===e)return m(null),!0;if("string"==typeof e)return!(void 0!==t&&!t(e))&&(m(e,n),!0);{const{url:t,target:r,rel:i,title:s}=e;return m(t,{...n,rel:i,target:r,title:s}),!0}},t.COMMAND_PRIORITY_EDITOR),n.effect(()=>{const n=i.validateUrl.value;if(!n)return;const s=i.attributes.value;return r.registerCommand(t.PASTE_COMMAND,i=>{const l=t.$getSelection();if(!t.$isRangeSelection(l)||l.isCollapsed()||!e.objectKlassEquals(i,ClipboardEvent))return!1;if(null===i.clipboardData)return!1;const o=i.clipboardData.getData("text");if(!n(o))return!1;return!l.getNodes().some(e=>t.$isElementNode(e)||t.$isTextNode(e)&&!e.isSimpleText())&&(r.dispatchCommand(p,{...s,url:o}),i.preventDefault(),!0)},t.COMMAND_PRIORITY_LOW)}))}const T=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:N,mergeConfig(e,n){const r=t.shallowMergeConfig(e,n);return e.attributes&&(r.attributes=t.shallowMergeConfig(e.attributes,r.attributes)),r},name:"@lexical/link/Link",nodes:()=>[s],register:(e,t,n)=>$(e,n.getOutput())});function L(n,r,i={}){const s=i=>{const s=i.target;if(!t.isDOMNode(s))return;const l=t.getNearestEditorFromDOMNode(s);if(null===l)return;let o=null,a=null;if(l.update(()=>{const n=t.$getNearestNodeFromDOMNode(s);if(null!==n){const i=e.$findMatchingParent(n,t.$isElementNode);if(!r.disabled.peek())if(g(i))o=i.sanitizeUrl(i.getURL()),a=i.getTarget();else{const t=function(e,t){let n=e;for(;null!=n;){if(t(n))return n;n=n.parentNode}return null}(s,e.isHTMLAnchorElement);null!==t&&(o=t.href,a=t.target)}}}),null===o||""===o)return;const u=n.getEditorState().read(t.$getSelection,{editor:n});if(t.$isRangeSelection(u)&&!u.isCollapsed())return void i.preventDefault();const c="auxclick"===i.type&&1===i.button;window.open(o,r.newTab.peek()||c||i.metaKey||i.ctrlKey||"_blank"===a?"_blank":"_self"),i.preventDefault()},l=e=>{1===e.button&&s(e)};return n.registerRootListener(e=>{if(e)return e.addEventListener("click",s,i),e.addEventListener("mouseup",l,i),()=>{e.removeEventListener("click",s),e.removeEventListener("mouseup",l)}})}const S=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:t.safeCast({disabled:!1,newTab:!1}),dependencies:[T],name:"@lexical/link/ClickableLink",register:(e,t,n)=>L(e,n.getOutput())});function b(e,t){for(let n=0;n<t.length;n++){const r=t[n](e);if(r)return r}return null}const C=/[.,;\s]/;function v(e,t){return t.test(e)}function E(e,t){return v(e[e.length-1],t)}function R(e,t){return v(e[0],t)}function U(e,n){let r=e.getPreviousSibling();return t.$isElementNode(r)&&(r=r.getLastDescendant()),null===r||t.$isLineBreakNode(r)||t.$isTextNode(r)&&E(r.getTextContent(),n)}function O(e,n){let r=e.getNextSibling();return t.$isElementNode(r)&&(r=r.getFirstDescendant()),null===r||t.$isLineBreakNode(r)||t.$isTextNode(r)&&R(r.getTextContent(),n)}function A(e,t,n,r,i){if(!(e>0?v(r[e-1],n):U(i[0],n)))return!1;return t<r.length?v(r[t],n):O(i[i.length-1],n)}function M(e,t,n){const r=[],i=[],s=[];let l=0,o=0;const a=[...e];for(;a.length>0;){const e=a[0],u=e.getTextContent().length,c=o;o+u<=t?(r.push(e),l+=u):c>=n?s.push(e):i.push(e),o+=u,a.shift()}return[l,r,i,s]}function P(e,n,r,i){const s=f(i.url,i.attributes);if(1===e.length){let l,o=e[0];0===n?[l,o]=o.splitText(r):[,l,o]=o.splitText(n,r);const a=t.$createTextNode(i.text);return a.setFormat(l.getFormat()),a.setDetail(l.getDetail()),a.setStyle(l.getStyle()),s.append(a),l.replace(s),o}if(e.length>1){const i=e[0];let l,o=i.getTextContent().length;0===n?l=i:[,l]=i.splitText(n);const a=[];let u;for(let t=1;t<e.length;t++){const n=e[t],i=n.getTextContent().length,s=o;if(s<r)if(o+i<=r)a.push(n);else{const[e,t]=n.splitText(r-s);a.push(e),u=t}o+=i}const c=t.$getSelection(),g=c?c.getNodes().find(t.$isTextNode):void 0,d=t.$createTextNode(l.getTextContent());return d.setFormat(l.getFormat()),d.setDetail(l.getDetail()),d.setStyle(l.getStyle()),s.append(d,...a),g&&g===l&&(t.$isRangeSelection(c)?d.select(c.anchor.offset,c.focus.offset):t.$isNodeSelection(c)&&d.select(0,d.getTextContent().length)),l.replace(s),u}}function I(e,n,r,i){const s=e.getChildren(),l=s.length;for(let n=0;n<l;n++){const i=s[n];if(!t.$isTextNode(i)||!i.isSimpleText())return y(e),void r(null,e.getURL())}const o=e.getTextContent(),a=b(o,n);if(null===a||a.text!==o)return y(e),void r(null,e.getURL());if(!U(e,i)||!O(e,i))return y(e),void r(null,e.getURL());const u=e.getURL();if(u!==a.url&&(e.setURL(a.url),r(a.url,u)),a.attributes){const t=e.getRel();t!==a.attributes.rel&&(e.setRel(a.attributes.rel||null),r(a.attributes.rel||null,t));const n=e.getTarget();n!==a.attributes.target&&(e.setTarget(a.attributes.target||null),r(a.attributes.target||null,n))}}function y(e){const t=e.getChildren();for(let n=t.length-1;n>=0;n--)e.insertAfter(t[n]);return e.remove(),t.map(e=>e.getLatest())}const D={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:C};function w(n,r=D){const{matchers:i,changeHandlers:s,excludeParents:l,separatorRegex:o=C}=r,a=(e,t)=>{for(const n of s)n(e,t)};return e.mergeRegister(n.registerNodeTransform(t.TextNode,e=>{const n=e.getParentOrThrow(),r=e.getPreviousSibling();if(h(n))I(n,i,a,o);else if(!g(n)&&!l.some(e=>e(n))){if(e.isSimpleText()&&(R(e.getTextContent(),o)||!h(r))){const n=function(e){const n=[e];let r=e.getNextSibling();for(;null!==r&&t.$isTextNode(r)&&r.isSimpleText()&&(n.push(r),!/[\s]/.test(r.getTextContent()));)r=r.getNextSibling();return n}(e);!function(e,t,n,r){for(const t of e){const e=t.getParent();if(h(e)&&!e.getIsUnlinked())return}let i=[...e];const s=i.map(e=>e.getTextContent()).join("");let l,o=s,a=0;for(;(l=b(o,t))&&null!==l;){const e=l.index,t=e+l.length;if(A(a+e,a+t,r,s,i)){const[r,,s,u]=M(i,a+e,a+t);let c=!1;for(const e of s){const t=e.getParent();if(h(t)&&!t.getIsUnlinked()){c=!0;break}}if(c){a+=t,o=o.substring(t);continue}const g=P(s,a+e-r,a+t-r,l);i=g?[g,...u]:u,n(l.url,null),a=0}else a+=t;o=o.substring(t)}}(n,i,a,o)}!function(e,t,n,r){const i=e.getParent(),s=e.getPreviousSibling(),l=e.getNextSibling(),o=e.getTextContent();if(!h(i)||i.getIsUnlinked()){if(h(s)&&!s.getIsUnlinked()&&s.is(e.getPreviousSibling())&&e.getParent()===s.getParent()){if(!R(o,r))return y(s),void n(null,s.getURL());if(a=o,s.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(a):/^\.[a-zA-Z0-9]{1,}/.test(a)){const i=s.getTextContent()+o,l=b(i,t);null!==l&&l.text===i&&(s.append(e),I(s,t,n,r),n(null,s.getURL()))}}var a;!h(l)||l.getIsUnlinked()||E(o,r)||l.is(e.getNextSibling())&&e.getParent()===l.getParent()&&(y(l),n(null,l.getURL()))}}(e,i,a,o)}}),n.registerCommand(p,e=>{const n=t.$getSelection();if(null!==e||!t.$isRangeSelection(n))return!1;return n.extract().forEach(e=>{const t=e.getParent();h(t)&&(t.setIsUnlinked(!t.getIsUnlinked()),t.markDirty())}),!1},t.COMMAND_PRIORITY_LOW))}const F=t.defineExtension({config:D,dependencies:[T],mergeConfig(e,n){const r=t.shallowMergeConfig(e,n);for(const t of["matchers","changeHandlers","excludeParents"]){const i=n[t];Array.isArray(i)&&(r[t]=[...e[t],...i])}return r},name:"@lexical/link/AutoLink",nodes:[d],register:w}),W=[r.defineImportRule({$import:(e,t)=>{if(!t.textContent&&0===t.children.length)return[];const n=t.getAttribute("href")||"",i={rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")};return r.$distributeInlineWrapper(e.$importChildren(t),()=>c(n,i))},match:r.sel.tag("a"),name:"@lexical/link/a"})],z=t.defineExtension({dependencies:[T,t.configExtension(r.DOMImportExtension,{rules:W})],name:"@lexical/link/Import"}),J=m;exports.$createAutoLinkNode=f,exports.$createLinkNode=c,exports.$isAutoLinkNode=h,exports.$isLinkNode=g,exports.$toggleLink=m,exports.AutoLinkExtension=F,exports.AutoLinkNode=d,exports.ClickableLinkExtension=S,exports.LinkExtension=T,exports.LinkImportExtension=z,exports.LinkImportRules=W,exports.LinkNode=s,exports.TOGGLE_LINK_COMMAND=p,exports.createLinkMatcherWithRegExp=function(e,t=e=>e){return n=>{const r=e.exec(n);return null===r?null:{index:r.index,length:r[0].length,text:r[0],url:t(r[0])}}},exports.formatUrl=k,exports.registerAutoLink=w,exports.registerClickableLink=L,exports.registerLink=$,exports.toggleLink=J;
@@ -0,0 +1,9 @@
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
+ */
8
+
9
+ import{addClassNamesToElement as t,isHTMLAnchorElement as e,$findMatchingParent as n,$insertNodeToNearestRootAtCaret as r,mergeRegister as i,objectKlassEquals as l}from"@lexical/utils";import{ElementNode as s,$copyNode as o,$isRangeSelection as u,$applyNodeReplacement as a,createCommand as c,$getSelection as g,$isNodeSelection as f,$normalizeSelection__EXPERIMENTAL as d,$setSelection as h,$getChildCaret as p,$isElementNode as _,$rewindSiblingCaret as m,$caretFromPoint as x,$normalizeCaret as k,$isSiblingCaret as b,$setPointFromCaret as v,defineExtension as U,shallowMergeConfig as T,COMMAND_PRIORITY_EDITOR as C,PASTE_COMMAND as L,$isTextNode as S,COMMAND_PRIORITY_LOW as O,safeCast as P,isDOMNode as y,getNearestEditorFromDOMNode as A,$getNearestNodeFromDOMNode as N,TextNode as R,$isLineBreakNode as D,$createTextNode as I,configExtension as w}from"lexical";import{namedSignals as E,effect as M}from"@lexical/extension";import{DOMImportExtension as F,defineImportRule as W,sel as J,$distributeInlineWrapper as K}from"@lexical/html";const $=new Set(["http:","https:","mailto:","sms:","tel:"]);class B extends s{__url;__target;__rel;__title;static getType(){return"link"}static clone(t){return new B(t.__url,{rel:t.__rel,target:t.__target,title:t.__title},t.__key)}constructor(t="",e={},n){super(n);const{target:r=null,rel:i=null,title:l=null}=e;this.__url=t,this.__target=r,this.__rel=i,this.__title=l}afterCloneFrom(t){super.afterCloneFrom(t),this.__url=t.__url,this.__rel=t.__rel,this.__target=t.__target,this.__title=t.__title}createDOM(e){const n=document.createElement("a");return this.updateLinkDOM(null,n,e),t(n,e.theme.link),n}updateLinkDOM(t,n,r){if(e(n)){t&&t.__url===this.__url||(n.href=this.sanitizeUrl(this.__url));for(const e of["target","rel","title"]){const r=`__${e}`,i=this[r];t&&t[r]===i||(i?n[e]=i:n.removeAttribute(e))}}}updateDOM(t,e,n){return this.updateLinkDOM(t,e,n),!1}static importDOM(){return{a:t=>({conversion:G,priority:1})}}static importJSON(t){return Z().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setURL(t.url).setRel(t.rel||null).setTarget(t.target||null).setTitle(t.title||null)}sanitizeUrl(t){t=rt(t);try{const e=new URL(rt(t));if(!$.has(e.protocol))return"about:blank"}catch(e){return t}return t}exportJSON(){return{...super.exportJSON(),rel:this.getRel(),target:this.getTarget(),title:this.getTitle(),url:this.getURL()}}getURL(){return this.getLatest().__url}setURL(t){const e=this.getWritable();return e.__url=t,e}getTarget(){return this.getLatest().__target}setTarget(t){const e=this.getWritable();return e.__target=t,e}getRel(){return this.getLatest().__rel}setRel(t){const e=this.getWritable();return e.__rel=t,e}getTitle(){return this.getLatest().__title}setTitle(t){const e=this.getWritable();return e.__title=t,e}insertNewAfter(t,e=!0){const n=o(this);return this.insertAfter(n,e),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(t,e,n){if(!u(e))return!1;const r=e.anchor.getNode(),i=e.focus.getNode();return this.isParentOf(r)&&this.isParentOf(i)&&e.getTextContent().length>0}isEmailURI(){return this.__url.startsWith("mailto:")}isWebSiteURI(){return this.__url.startsWith("https://")||this.__url.startsWith("http://")}shouldMergeAdjacentLink(t){return this.getType()===t.getType()&&this.__url===t.__url&&this.__target===t.__target&&this.__rel===t.__rel&&this.__title===t.__title}}function z(t){const e=x(t,"next");return[e,e.getFlipped()]}function j(t,e){for(const n of e)if(n.origin.isAttached()){const e=k(n);return void v(t,e)}}function H(t){const e=g();let n=null,i=null;function l(){u(e)&&(j(e.anchor,n),j(e.focus,i),d(e))}u(e)&&(n=z(e.anchor),i=z(e.focus));let s=!1;for(const e of p(t,"next")){const n=e.origin;if(_(n)&&!n.isInline()){const i=n.getChildren();if(i.length>0){const e=o(t);e.append(...i),n.append(e),s=!0}r(n,m(e),{$shouldSplit:()=>!1})}}function a(t,e,n){const[r,i]=t,l=t=>b(t)&&t.origin.is(e);if(!l(r)&&!l(i))return t;const s=k(p(n,"next"));return[s,s.getFlipped()]}if(t.isAttached()){const e=t.getPreviousSibling();if(q(e)&&e.shouldMergeAdjacentLink(t))return n&&(n=a(n,e,t)),i&&(i=a(i,e,t)),e.append(...t.getChildren()),t.remove(),void l();const r=t.getNextSibling();q(r)&&t.shouldMergeAdjacentLink(r)&&(n&&(n=a(n,t,r)),i&&(i=a(i,t,r)),t.append(...r.getChildren()),r.remove(),s=!0)}if(s){if(!t.canBeEmpty()&&t.isEmpty()){const e=t.getParent();t.remove(),e&&e.isEmpty()&&e.remove()}l()}}function G(t){let n=null;if(e(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=Z(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function Z(t="",e){return a(new B(t,e))}function q(t){return t instanceof B}class Q extends B{__isUnlinked;constructor(t="",e={},n){super(t,e,n),this.__isUnlinked=void 0!==e.isUnlinked&&null!==e.isUnlinked&&e.isUnlinked}afterCloneFrom(t){super.afterCloneFrom(t),this.__isUnlinked=t.__isUnlinked}static getType(){return"autolink"}static clone(t){return new Q(t.__url,{isUnlinked:t.__isUnlinked,rel:t.__rel,target:t.__target,title:t.__title},t.__key)}shouldMergeAdjacentLink(t){return!1}getIsUnlinked(){return this.__isUnlinked}setIsUnlinked(t){const e=this.getWritable();return e.__isUnlinked=t,e}createDOM(t){return this.__isUnlinked?document.createElement("span"):super.createDOM(t)}updateDOM(t,e,n){return super.updateDOM(t,e,n)||t.__isUnlinked!==this.__isUnlinked}static importJSON(t){return V().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setIsUnlinked(t.isUnlinked||!1)}static importDOM(){return null}exportJSON(){return{...super.exportJSON(),isUnlinked:this.__isUnlinked}}insertNewAfter(t,e=!0){const n=V(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,e),n}}function V(t="",e){return a(new Q(t,e))}function X(t){return t instanceof Q}const Y=c("TOGGLE_LINK_COMMAND");function tt(t,e){if("element"===t.type){const n=t.getNode();_(n)||function(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);throw n.search=r.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}(252);return n.getChildren()[t.offset+e]||null}return null}function et(t,e={}){let r;if(t&&"object"==typeof t){const{url:n,...i}=t;r=n,e={...i,...e}}else r=t;const{target:i,title:l}=e,s=void 0===e.rel?"noreferrer":e.rel,a=g();if(null===a||!u(a)&&!f(a))return;if(f(a)){const t=a.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===r){const e=n(t,t=>!X(t)&&q(t));e&&(e.insertBefore(t),0===e.getChildren().length&&e.remove())}else{const e=n(t,t=>!X(t)&&q(t));if(e)e.setURL(r),void 0!==i&&e.setTarget(i),void 0!==s&&e.setRel(s);else{const e=Z(r,{rel:s,target:i});t.insertBefore(e),e.append(t)}}})}if(a.isCollapsed()&&null===r)for(const t of a.getNodes()){const e=n(t,t=>!X(t)&&q(t));return void(null!==e&&(e.getParentOrThrow().splice(e.getIndexWithinParent(),0,e.getChildren()),e.remove()))}const c=a.extract();if(null===r){const t=new Set;return void c.forEach(e=>{const r=n(e,t=>!X(t)&&q(t));if(null!==r){const e=r.getKey();if(t.has(e))return;!function(t,e){const n=new Set(e.filter(e=>t.isParentOf(e)).map(t=>t.getKey())),r=t.getChildren(),i=r=>n.has(r.getKey())||_(r)&&e.some(e=>t.isParentOf(e)&&r.isParentOf(e)),l=r.filter(i);if(l.length===r.length)return r.forEach(e=>t.insertBefore(e)),void t.remove();const s=r.findIndex(i),u=r.findLastIndex(i),a=0===s,c=u===r.length-1;if(a)l.forEach(e=>t.insertBefore(e));else if(c)for(let e=l.length-1;e>=0;e--)t.insertAfter(l[e]);else{for(let e=l.length-1;e>=0;e--)t.insertAfter(l[e]);const e=r.slice(u+1);if(e.length>0){const n=o(t);l[l.length-1].insertAfter(n),e.forEach(t=>n.append(t))}}}(r,c),t.add(e)}})}const p=new Set,m=t=>{p.has(t.getKey())||(p.add(t.getKey()),t.setURL(r),void 0!==i&&t.setTarget(i),void 0!==s&&t.setRel(s),void 0!==l&&t.setTitle(l))};if(1===c.length){const t=c[0],e=n(t,q);if(null!==e)return m(e)}!function(t){const e=g();if(!u(e))return t();const n=d(e),r=n.isBackward(),i=tt(n.anchor,r?-1:0),l=tt(n.focus,r?0:-1),s=t();if(i||l){const t=g();if(u(t)){const e=t.clone();if(i){const t=i.getParent();t&&e.anchor.set(t.getKey(),i.getIndexWithinParent()+(r?1:0),"element")}if(l){const t=l.getParent();t&&e.focus.set(t.getKey(),l.getIndexWithinParent()+(r?0:1),"element")}h(d(e))}}}(()=>{let t=null;for(const e of c){if(!e.isAttached())continue;const o=n(e,q);if(o){m(o);continue}if(_(e)){if(!e.isInline())continue;if(q(e)){if(!(X(e)||null!==t&&t.getParentOrThrow().isParentOf(e))){m(e),t=e;continue}for(const t of e.getChildren())e.insertBefore(t);e.remove();continue}}const u=e.getPreviousSibling();q(u)&&u.is(t)?u.append(e):(t=Z(r,{rel:s,target:i,title:l}),e.insertAfter(t),t.append(e))}})}const nt=/^\+?[0-9\s()-]{5,}$/;function rt(t){return t.match(/^[a-z][a-z0-9+.-]*:/i)||t.match(/^[/#.]/)?t:t.includes("@")?`mailto:${t}`:nt.test(t)?`tel:${t}`:`https://${t}`}function it(t,e){return i(t.registerNodeTransform(B,H),t.registerCommand(Y,t=>{const n=e.validateUrl.peek(),r=e.attributes.peek();if(null===t)return et(null),!0;if("string"==typeof t)return!(void 0!==n&&!n(t))&&(et(t,r),!0);{const{url:e,target:n,rel:i,title:l}=t;return et(e,{...r,rel:i,target:n,title:l}),!0}},C),M(()=>{const n=e.validateUrl.value;if(!n)return;const r=e.attributes.value;return t.registerCommand(L,e=>{const i=g();if(!u(i)||i.isCollapsed()||!l(e,ClipboardEvent))return!1;if(null===e.clipboardData)return!1;const s=e.clipboardData.getData("text");if(!n(s))return!1;return!i.getNodes().some(t=>_(t)||S(t)&&!t.isSimpleText())&&(t.dispatchCommand(Y,{...r,url:s}),e.preventDefault(),!0)},O)}))}const lt=U({build:(t,e,n)=>E(e),config:{attributes:void 0,validateUrl:void 0},mergeConfig(t,e){const n=T(t,e);return t.attributes&&(n.attributes=T(t.attributes,n.attributes)),n},name:"@lexical/link/Link",nodes:()=>[B],register:(t,e,n)=>it(t,n.getOutput())});function st(t,r,i={}){const l=i=>{const l=i.target;if(!y(l))return;const s=A(l);if(null===s)return;let o=null,a=null;if(s.update(()=>{const t=N(l);if(null!==t){const i=n(t,_);if(!r.disabled.peek())if(q(i))o=i.sanitizeUrl(i.getURL()),a=i.getTarget();else{const t=function(t,e){let n=t;for(;null!=n;){if(e(n))return n;n=n.parentNode}return null}(l,e);null!==t&&(o=t.href,a=t.target)}}}),null===o||""===o)return;const c=t.getEditorState().read(g,{editor:t});if(u(c)&&!c.isCollapsed())return void i.preventDefault();const f="auxclick"===i.type&&1===i.button;window.open(o,r.newTab.peek()||f||i.metaKey||i.ctrlKey||"_blank"===a?"_blank":"_self"),i.preventDefault()},s=t=>{1===t.button&&l(t)};return t.registerRootListener(t=>{if(t)return t.addEventListener("click",l,i),t.addEventListener("mouseup",s,i),()=>{t.removeEventListener("click",l),t.removeEventListener("mouseup",s)}})}const ot=U({build:(t,e,n)=>E(e),config:P({disabled:!1,newTab:!1}),dependencies:[lt],name:"@lexical/link/ClickableLink",register:(t,e,n)=>st(t,n.getOutput())});function ut(t,e=t=>t){return n=>{const r=t.exec(n);return null===r?null:{index:r.index,length:r[0].length,text:r[0],url:e(r[0])}}}function at(t,e){for(let n=0;n<e.length;n++){const r=e[n](t);if(r)return r}return null}const ct=/[.,;\s]/;function gt(t,e){return e.test(t)}function ft(t,e){return gt(t[t.length-1],e)}function dt(t,e){return gt(t[0],e)}function ht(t,e){let n=t.getPreviousSibling();return _(n)&&(n=n.getLastDescendant()),null===n||D(n)||S(n)&&ft(n.getTextContent(),e)}function pt(t,e){let n=t.getNextSibling();return _(n)&&(n=n.getFirstDescendant()),null===n||D(n)||S(n)&&dt(n.getTextContent(),e)}function _t(t,e,n,r,i){if(!(t>0?gt(r[t-1],n):ht(i[0],n)))return!1;return e<r.length?gt(r[e],n):pt(i[i.length-1],n)}function mt(t,e,n){const r=[],i=[],l=[];let s=0,o=0;const u=[...t];for(;u.length>0;){const t=u[0],a=t.getTextContent().length,c=o;o+a<=e?(r.push(t),s+=a):c>=n?l.push(t):i.push(t),o+=a,u.shift()}return[s,r,i,l]}function xt(t,e,n,r){const i=V(r.url,r.attributes);if(1===t.length){let l,s=t[0];0===e?[l,s]=s.splitText(n):[,l,s]=s.splitText(e,n);const o=I(r.text);return o.setFormat(l.getFormat()),o.setDetail(l.getDetail()),o.setStyle(l.getStyle()),i.append(o),l.replace(i),s}if(t.length>1){const r=t[0];let l,s=r.getTextContent().length;0===e?l=r:[,l]=r.splitText(e);const o=[];let a;for(let e=1;e<t.length;e++){const r=t[e],i=r.getTextContent().length,l=s;if(l<n)if(s+i<=n)o.push(r);else{const[t,e]=r.splitText(n-l);o.push(t),a=e}s+=i}const c=g(),d=c?c.getNodes().find(S):void 0,h=I(l.getTextContent());return h.setFormat(l.getFormat()),h.setDetail(l.getDetail()),h.setStyle(l.getStyle()),i.append(h,...o),d&&d===l&&(u(c)?h.select(c.anchor.offset,c.focus.offset):f(c)&&h.select(0,h.getTextContent().length)),l.replace(i),a}}function kt(t,e,n,r){const i=t.getChildren(),l=i.length;for(let e=0;e<l;e++){const r=i[e];if(!S(r)||!r.isSimpleText())return bt(t),void n(null,t.getURL())}const s=t.getTextContent(),o=at(s,e);if(null===o||o.text!==s)return bt(t),void n(null,t.getURL());if(!ht(t,r)||!pt(t,r))return bt(t),void n(null,t.getURL());const u=t.getURL();if(u!==o.url&&(t.setURL(o.url),n(o.url,u)),o.attributes){const e=t.getRel();e!==o.attributes.rel&&(t.setRel(o.attributes.rel||null),n(o.attributes.rel||null,e));const r=t.getTarget();r!==o.attributes.target&&(t.setTarget(o.attributes.target||null),n(o.attributes.target||null,r))}}function bt(t){const e=t.getChildren();for(let n=e.length-1;n>=0;n--)t.insertAfter(e[n]);return t.remove(),e.map(t=>t.getLatest())}const vt={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:ct};function Ut(t,e=vt){const{matchers:n,changeHandlers:r,excludeParents:l,separatorRegex:s=ct}=e,o=(t,e)=>{for(const n of r)n(t,e)};return i(t.registerNodeTransform(R,t=>{const e=t.getParentOrThrow(),r=t.getPreviousSibling();if(X(e))kt(e,n,o,s);else if(!q(e)&&!l.some(t=>t(e))){if(t.isSimpleText()&&(dt(t.getTextContent(),s)||!X(r))){const e=function(t){const e=[t];let n=t.getNextSibling();for(;null!==n&&S(n)&&n.isSimpleText()&&(e.push(n),!/[\s]/.test(n.getTextContent()));)n=n.getNextSibling();return e}(t);!function(t,e,n,r){for(const e of t){const t=e.getParent();if(X(t)&&!t.getIsUnlinked())return}let i=[...t];const l=i.map(t=>t.getTextContent()).join("");let s,o=l,u=0;for(;(s=at(o,e))&&null!==s;){const t=s.index,e=t+s.length;if(_t(u+t,u+e,r,l,i)){const[r,,l,a]=mt(i,u+t,u+e);let c=!1;for(const t of l){const e=t.getParent();if(X(e)&&!e.getIsUnlinked()){c=!0;break}}if(c){u+=e,o=o.substring(e);continue}const g=xt(l,u+t-r,u+e-r,s);i=g?[g,...a]:a,n(s.url,null),u=0}else u+=e;o=o.substring(e)}}(e,n,o,s)}!function(t,e,n,r){const i=t.getParent(),l=t.getPreviousSibling(),s=t.getNextSibling(),o=t.getTextContent();if(!X(i)||i.getIsUnlinked()){if(X(l)&&!l.getIsUnlinked()&&l.is(t.getPreviousSibling())&&t.getParent()===l.getParent()){if(!dt(o,r))return bt(l),void n(null,l.getURL());if(u=o,l.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(u):/^\.[a-zA-Z0-9]{1,}/.test(u)){const i=l.getTextContent()+o,s=at(i,e);null!==s&&s.text===i&&(l.append(t),kt(l,e,n,r),n(null,l.getURL()))}}var u;!X(s)||s.getIsUnlinked()||ft(o,r)||s.is(t.getNextSibling())&&t.getParent()===s.getParent()&&(bt(s),n(null,s.getURL()))}}(t,n,o,s)}}),t.registerCommand(Y,t=>{const e=g();if(null!==t||!u(e))return!1;return e.extract().forEach(t=>{const e=t.getParent();X(e)&&(e.setIsUnlinked(!e.getIsUnlinked()),e.markDirty())}),!1},O))}const Tt=U({config:vt,dependencies:[lt],mergeConfig(t,e){const n=T(t,e);for(const r of["matchers","changeHandlers","excludeParents"]){const i=e[r];Array.isArray(i)&&(n[r]=[...t[r],...i])}return n},name:"@lexical/link/AutoLink",nodes:[Q],register:Ut}),Ct=[W({$import:(t,e)=>{if(!e.textContent&&0===e.children.length)return[];const n=e.getAttribute("href")||"",r={rel:e.getAttribute("rel"),target:e.getAttribute("target"),title:e.getAttribute("title")};return K(t.$importChildren(e),()=>Z(n,r))},match:J.tag("a"),name:"@lexical/link/a"})],Lt=U({dependencies:[lt,w(F,{rules:Ct})],name:"@lexical/link/Import"}),St=et;export{V as $createAutoLinkNode,Z as $createLinkNode,X as $isAutoLinkNode,q as $isLinkNode,et as $toggleLink,Tt as AutoLinkExtension,Q as AutoLinkNode,ot as ClickableLinkExtension,lt as LinkExtension,Lt as LinkImportExtension,Ct as LinkImportRules,B as LinkNode,Y as TOGGLE_LINK_COMMAND,ut as createLinkMatcherWithRegExp,rt as formatUrl,Ut as registerAutoLink,st as registerClickableLink,it as registerLink,St as toggleLink};
@@ -0,0 +1,23 @@
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
+ */
8
+ /**
9
+ * Import rules for {@link LinkNode}.
10
+ *
11
+ * @experimental
12
+ */
13
+ export declare const LinkImportRules: import("@lexical/html").DOMImportRule<import("@lexical/html").ElementSelectorBuilder<HTMLAnchorElement, Record<string, never>>>[];
14
+ /**
15
+ * Bundles {@link LinkImportRules} together with the runtime
16
+ * {@link LinkExtension}. The application is expected to already have
17
+ * `CoreImportExtension` (or some equivalent) in its dependency graph —
18
+ * the core/text/paragraph/inline-format rules are a shared baseline,
19
+ * not something this leaf importer should re-declare.
20
+ *
21
+ * @experimental
22
+ */
23
+ export declare const LinkImportExtension: import("lexical").LexicalExtension<import("lexical").ExtensionConfigBase, "@lexical/link/Import", unknown, unknown>;
@@ -10,5 +10,6 @@ export { type ClickableLinkConfig, ClickableLinkExtension, registerClickableLink
10
10
  export { type AutoLinkConfig, AutoLinkExtension, type ChangeHandler, createLinkMatcherWithRegExp, type LinkMatcher, registerAutoLink, } from './LexicalAutoLinkExtension';
11
11
  export { LinkExtension, registerLink } from './LexicalLinkExtension';
12
12
  export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, type AutoLinkAttributes, AutoLinkNode, formatUrl, type LinkAttributes, LinkNode, type SerializedAutoLinkNode, type SerializedLinkNode, TOGGLE_LINK_COMMAND, } from './LexicalLinkNode';
13
+ export { LinkImportExtension, LinkImportRules } from './LinkImportExtension';
13
14
  /** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
14
15
  export declare const toggleLink: typeof $toggleLink;
package/package.json CHANGED
@@ -8,36 +8,52 @@
8
8
  "link"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.44.1-nightly.20260519.0",
12
- "main": "LexicalLink.js",
13
- "types": "index.d.ts",
11
+ "version": "0.45.1-dev.0",
12
+ "main": "./dist/LexicalLink.js",
13
+ "types": "./dist/index.d.ts",
14
14
  "dependencies": {
15
- "@lexical/extension": "0.44.1-nightly.20260519.0",
16
- "@lexical/utils": "0.44.1-nightly.20260519.0",
17
- "lexical": "0.44.1-nightly.20260519.0"
15
+ "@lexical/html": "0.45.1-dev.0",
16
+ "@lexical/internal": "0.45.1-dev.0",
17
+ "@lexical/extension": "0.45.1-dev.0",
18
+ "@lexical/utils": "0.45.1-dev.0",
19
+ "lexical": "0.45.1-dev.0"
18
20
  },
19
21
  "repository": {
20
22
  "type": "git",
21
23
  "url": "git+https://github.com/facebook/lexical.git",
22
24
  "directory": "packages/lexical-link"
23
25
  },
24
- "module": "LexicalLink.mjs",
26
+ "module": "./dist/LexicalLink.mjs",
25
27
  "sideEffects": false,
26
28
  "exports": {
27
29
  ".": {
30
+ "source": "./src/index.ts",
28
31
  "import": {
29
- "types": "./index.d.ts",
30
- "development": "./LexicalLink.dev.mjs",
31
- "production": "./LexicalLink.prod.mjs",
32
- "node": "./LexicalLink.node.mjs",
33
- "default": "./LexicalLink.mjs"
32
+ "types": "./dist/index.d.ts",
33
+ "development": "./dist/LexicalLink.dev.mjs",
34
+ "production": "./dist/LexicalLink.prod.mjs",
35
+ "node": "./dist/LexicalLink.node.mjs",
36
+ "default": "./dist/LexicalLink.mjs"
34
37
  },
35
38
  "require": {
36
- "types": "./index.d.ts",
37
- "development": "./LexicalLink.dev.js",
38
- "production": "./LexicalLink.prod.js",
39
- "default": "./LexicalLink.js"
39
+ "types": "./dist/index.d.ts",
40
+ "development": "./dist/LexicalLink.dev.js",
41
+ "production": "./dist/LexicalLink.prod.js",
42
+ "default": "./dist/LexicalLink.js"
40
43
  }
41
44
  }
42
- }
45
+ },
46
+ "files": [
47
+ "dist",
48
+ "src",
49
+ "!src/__tests__",
50
+ "!src/__bench__",
51
+ "!src/__mocks__",
52
+ "!src/**/*.test.ts",
53
+ "!src/**/*.test.tsx",
54
+ "!src/**/*.bench.ts",
55
+ "!src/**/*.bench.tsx",
56
+ "README.md",
57
+ "LICENSE"
58
+ ]
43
59
  }
@@ -0,0 +1,142 @@
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
+ */
8
+
9
+ import {namedSignals, NamedSignalsOutput} from '@lexical/extension';
10
+ import {$findMatchingParent, isHTMLAnchorElement} from '@lexical/utils';
11
+ import {
12
+ $getNearestNodeFromDOMNode,
13
+ $getSelection,
14
+ $isElementNode,
15
+ $isRangeSelection,
16
+ defineExtension,
17
+ getNearestEditorFromDOMNode,
18
+ isDOMNode,
19
+ LexicalEditor,
20
+ safeCast,
21
+ } from 'lexical';
22
+
23
+ import {LinkExtension} from './LexicalLinkExtension';
24
+ import {$isLinkNode} from './LexicalLinkNode';
25
+
26
+ function findMatchingDOM<T extends Node>(
27
+ startNode: Node,
28
+ predicate: (node: Node) => node is T,
29
+ ): T | null {
30
+ let node: Node | null = startNode;
31
+ while (node != null) {
32
+ if (predicate(node)) {
33
+ return node;
34
+ }
35
+ node = node.parentNode;
36
+ }
37
+ return null;
38
+ }
39
+
40
+ export interface ClickableLinkConfig {
41
+ /** Open clicked links in a new tab when true (default false) */
42
+ newTab: boolean;
43
+ /** Disable this extension when true (default false) */
44
+ disabled: boolean;
45
+ }
46
+
47
+ export function registerClickableLink(
48
+ editor: LexicalEditor,
49
+ stores: NamedSignalsOutput<ClickableLinkConfig>,
50
+ eventOptions: Pick<AddEventListenerOptions, 'signal'> = {},
51
+ ): () => void {
52
+ const onClick = (event: MouseEvent) => {
53
+ const target = event.target;
54
+ if (!isDOMNode(target)) {
55
+ return;
56
+ }
57
+ const nearestEditor = getNearestEditorFromDOMNode(target);
58
+
59
+ if (nearestEditor === null) {
60
+ return;
61
+ }
62
+
63
+ let url = null;
64
+ let urlTarget = null;
65
+ nearestEditor.update(() => {
66
+ const clickedNode = $getNearestNodeFromDOMNode(target);
67
+ if (clickedNode !== null) {
68
+ const maybeLinkNode = $findMatchingParent(clickedNode, $isElementNode);
69
+ if (!stores.disabled.peek()) {
70
+ if ($isLinkNode(maybeLinkNode)) {
71
+ url = maybeLinkNode.sanitizeUrl(maybeLinkNode.getURL());
72
+ urlTarget = maybeLinkNode.getTarget();
73
+ } else {
74
+ const a = findMatchingDOM(target, isHTMLAnchorElement);
75
+ if (a !== null) {
76
+ url = a.href;
77
+ urlTarget = a.target;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ });
83
+
84
+ if (url === null || url === '') {
85
+ return;
86
+ }
87
+
88
+ // Allow user to select link text without following url
89
+ const selection = editor.getEditorState().read($getSelection, {editor});
90
+ if ($isRangeSelection(selection) && !selection.isCollapsed()) {
91
+ event.preventDefault();
92
+ return;
93
+ }
94
+
95
+ const isMiddle = event.type === 'auxclick' && event.button === 1;
96
+ window.open(
97
+ url,
98
+ stores.newTab.peek() ||
99
+ isMiddle ||
100
+ event.metaKey ||
101
+ event.ctrlKey ||
102
+ urlTarget === '_blank'
103
+ ? '_blank'
104
+ : '_self',
105
+ );
106
+ event.preventDefault();
107
+ };
108
+
109
+ const onMouseUp = (event: MouseEvent) => {
110
+ if (event.button === 1) {
111
+ onClick(event);
112
+ }
113
+ };
114
+
115
+ return editor.registerRootListener(rootElement => {
116
+ if (rootElement) {
117
+ rootElement.addEventListener('click', onClick, eventOptions);
118
+ rootElement.addEventListener('mouseup', onMouseUp, eventOptions);
119
+ return () => {
120
+ rootElement.removeEventListener('click', onClick);
121
+ rootElement.removeEventListener('mouseup', onMouseUp);
122
+ };
123
+ }
124
+ });
125
+ }
126
+
127
+ /**
128
+ * Normally in a Lexical editor the `CLICK_COMMAND` on a LinkNode will cause the
129
+ * selection to change instead of opening a link. This extension can be used to
130
+ * restore the default behavior, e.g. when the editor is not editable.
131
+ */
132
+ export const ClickableLinkExtension = defineExtension({
133
+ build(editor, config, state) {
134
+ return namedSignals(config);
135
+ },
136
+ config: safeCast<ClickableLinkConfig>({disabled: false, newTab: false}),
137
+ dependencies: [LinkExtension],
138
+ name: '@lexical/link/ClickableLink',
139
+ register(editor, config, state) {
140
+ return registerClickableLink(editor, state.getOutput());
141
+ },
142
+ });