@lexical/link 0.44.1-nightly.20260519.0 → 0.45.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.
@@ -1344,6 +1345,59 @@ const AutoLinkExtension = lexical.defineExtension({
1344
1345
  register: registerAutoLink
1345
1346
  });
1346
1347
 
1348
+ /**
1349
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1350
+ *
1351
+ * This source code is licensed under the MIT license found in the
1352
+ * LICENSE file in the root directory of this source tree.
1353
+ *
1354
+ */
1355
+
1356
+ const AnchorRule = html.defineImportRule({
1357
+ $import: (ctx, el) => {
1358
+ if (!el.textContent && el.children.length === 0) {
1359
+ return [];
1360
+ }
1361
+ // Use no schema here: when the `<a>` contains block descendants
1362
+ // (e.g. `<a><h1>x</h1><div>y</div></a>`), we want them lifted so each
1363
+ // block becomes a sibling at the link's level, with the link wrapping
1364
+ // its inline content. $distributeInlineWrapper handles both the
1365
+ // common all-inline case (single LinkNode wrapping the run) and the
1366
+ // mixed-block case (per-block recursion).
1367
+ const href = el.getAttribute('href') || '';
1368
+ const attrs = {
1369
+ rel: el.getAttribute('rel'),
1370
+ target: el.getAttribute('target'),
1371
+ title: el.getAttribute('title')
1372
+ };
1373
+ return html.$distributeInlineWrapper(ctx.$importChildren(el), () => $createLinkNode(href, attrs));
1374
+ },
1375
+ match: html.sel.tag('a'),
1376
+ name: '@lexical/link/a'
1377
+ });
1378
+
1379
+ /**
1380
+ * Import rules for {@link LinkNode}.
1381
+ *
1382
+ * @experimental
1383
+ */
1384
+ const LinkImportRules = [AnchorRule];
1385
+
1386
+ /**
1387
+ * Bundles {@link LinkImportRules} (plus {@link CoreImportExtension}) into
1388
+ * a single dependency. Equivalent to the legacy
1389
+ * `LinkNode.importDOM` registration on the new
1390
+ * {@link DOMImportExtension} pipeline.
1391
+ *
1392
+ * @experimental
1393
+ */
1394
+ const LinkImportExtension = lexical.defineExtension({
1395
+ dependencies: [html.CoreImportExtension, LinkExtension, lexical.configExtension(html.DOMImportExtension, {
1396
+ rules: LinkImportRules
1397
+ })],
1398
+ name: '@lexical/link/Import'
1399
+ });
1400
+
1347
1401
  /**
1348
1402
  * Copyright (c) Meta Platforms, Inc. and affiliates.
1349
1403
  *
@@ -1365,6 +1419,8 @@ exports.AutoLinkExtension = AutoLinkExtension;
1365
1419
  exports.AutoLinkNode = AutoLinkNode;
1366
1420
  exports.ClickableLinkExtension = ClickableLinkExtension;
1367
1421
  exports.LinkExtension = LinkExtension;
1422
+ exports.LinkImportExtension = LinkImportExtension;
1423
+ exports.LinkImportRules = LinkImportRules;
1368
1424
  exports.LinkNode = LinkNode;
1369
1425
  exports.TOGGLE_LINK_COMMAND = TOGGLE_LINK_COMMAND;
1370
1426
  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, COMMAND_PRIORITY_LOW, safeCast, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isTextNode, $isLineBreakNode, $createTextNode, configExtension } from 'lexical';
11
11
  import { namedSignals, effect } from '@lexical/extension';
12
+ import { CoreImportExtension, DOMImportExtension, defineImportRule, sel, $distributeInlineWrapper } from '@lexical/html';
12
13
 
13
14
  /**
14
15
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -1342,6 +1343,59 @@ const AutoLinkExtension = defineExtension({
1342
1343
  register: registerAutoLink
1343
1344
  });
1344
1345
 
1346
+ /**
1347
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1348
+ *
1349
+ * This source code is licensed under the MIT license found in the
1350
+ * LICENSE file in the root directory of this source tree.
1351
+ *
1352
+ */
1353
+
1354
+ const AnchorRule = defineImportRule({
1355
+ $import: (ctx, el) => {
1356
+ if (!el.textContent && el.children.length === 0) {
1357
+ return [];
1358
+ }
1359
+ // Use no schema here: when the `<a>` contains block descendants
1360
+ // (e.g. `<a><h1>x</h1><div>y</div></a>`), we want them lifted so each
1361
+ // block becomes a sibling at the link's level, with the link wrapping
1362
+ // its inline content. $distributeInlineWrapper handles both the
1363
+ // common all-inline case (single LinkNode wrapping the run) and the
1364
+ // mixed-block case (per-block recursion).
1365
+ const href = el.getAttribute('href') || '';
1366
+ const attrs = {
1367
+ rel: el.getAttribute('rel'),
1368
+ target: el.getAttribute('target'),
1369
+ title: el.getAttribute('title')
1370
+ };
1371
+ return $distributeInlineWrapper(ctx.$importChildren(el), () => $createLinkNode(href, attrs));
1372
+ },
1373
+ match: sel.tag('a'),
1374
+ name: '@lexical/link/a'
1375
+ });
1376
+
1377
+ /**
1378
+ * Import rules for {@link LinkNode}.
1379
+ *
1380
+ * @experimental
1381
+ */
1382
+ const LinkImportRules = [AnchorRule];
1383
+
1384
+ /**
1385
+ * Bundles {@link LinkImportRules} (plus {@link CoreImportExtension}) into
1386
+ * a single dependency. Equivalent to the legacy
1387
+ * `LinkNode.importDOM` registration on the new
1388
+ * {@link DOMImportExtension} pipeline.
1389
+ *
1390
+ * @experimental
1391
+ */
1392
+ const LinkImportExtension = defineExtension({
1393
+ dependencies: [CoreImportExtension, LinkExtension, configExtension(DOMImportExtension, {
1394
+ rules: LinkImportRules
1395
+ })],
1396
+ name: '@lexical/link/Import'
1397
+ });
1398
+
1345
1399
  /**
1346
1400
  * Copyright (c) Meta Platforms, Inc. and affiliates.
1347
1401
  *
@@ -1354,4 +1408,4 @@ const AutoLinkExtension = defineExtension({
1354
1408
  /** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
1355
1409
  const toggleLink = $toggleLink;
1356
1410
 
1357
- export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, AutoLinkExtension, AutoLinkNode, ClickableLinkExtension, LinkExtension, LinkNode, TOGGLE_LINK_COMMAND, createLinkMatcherWithRegExp, formatUrl, registerAutoLink, registerClickableLink, registerLink, toggleLink };
1411
+ 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");return!!n(o)&&(!l.getNodes().some(e=>t.$isElementNode(e))&&(r.dispatchCommand(p,{...s,url:o}),i.preventDefault(),!0))},t.COMMAND_PRIORITY_LOW)}))}const L=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 S(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 T=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:t.safeCast({disabled:!1,newTab:!1}),dependencies:[L],name:"@lexical/link/ClickableLink",register:(e,t,n)=>S(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:[L],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:[r.CoreImportExtension,L,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=T,exports.LinkExtension=L,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=S,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,COMMAND_PRIORITY_LOW as S,safeCast as O,isDOMNode as P,getNearestEditorFromDOMNode as y,$getNearestNodeFromDOMNode as A,TextNode as N,$isTextNode as R,$isLineBreakNode as D,$createTextNode as I,configExtension as w}from"lexical";import{namedSignals as E,effect as M}from"@lexical/extension";import{CoreImportExtension as F,DOMImportExtension as W,defineImportRule as J,sel as K,$distributeInlineWrapper as $}from"@lexical/html";const B=new Set(["http:","https:","mailto:","sms:","tel:"]);class z extends s{__url;__target;__rel;__title;static getType(){return"link"}static clone(t){return new z(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:Z,priority:1})}}static importJSON(t){return q().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=it(t);try{const e=new URL(it(t));if(!B.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 j(t){const e=x(t,"next");return[e,e.getFlipped()]}function H(t,e){for(const n of e)if(n.origin.isAttached()){const e=k(n);return void v(t,e)}}function G(t){const e=g();let n=null,i=null;function l(){u(e)&&(H(e.anchor,n),H(e.focus,i),d(e))}u(e)&&(n=j(e.anchor),i=j(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 Z(t){let n=null;if(e(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=q(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function q(t="",e){return a(new z(t,e))}function Q(t){return t instanceof z}class V extends z{__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 V(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 X().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=X(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,e),n}}function X(t="",e){return a(new V(t,e))}function Y(t){return t instanceof V}const tt=c("TOGGLE_LINK_COMMAND");function et(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 nt(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=>!Y(t)&&Q(t));e&&(e.insertBefore(t),0===e.getChildren().length&&e.remove())}else{const e=n(t,t=>!Y(t)&&Q(t));if(e)e.setURL(r),void 0!==i&&e.setTarget(i),void 0!==s&&e.setRel(s);else{const e=q(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=>!Y(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=>!Y(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=et(n.anchor,r?-1:0),l=et(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(!(Y(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=q(r,{rel:s,target:i,title:l}),e.insertAfter(t),t.append(e))}})}const rt=/^\+?[0-9\s()-]{5,}$/;function it(t){return t.match(/^[a-z][a-z0-9+.-]*:/i)||t.match(/^[/#.]/)?t:t.includes("@")?`mailto:${t}`:rt.test(t)?`tel:${t}`:`https://${t}`}function lt(t,e){return i(t.registerNodeTransform(z,G),t.registerCommand(tt,t=>{const n=e.validateUrl.peek(),r=e.attributes.peek();if(null===t)return nt(null),!0;if("string"==typeof t)return!(void 0!==n&&!n(t))&&(nt(t,r),!0);{const{url:e,target:n,rel:i,title:l}=t;return nt(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");return!!n(s)&&(!i.getNodes().some(t=>_(t))&&(t.dispatchCommand(tt,{...r,url:s}),e.preventDefault(),!0))},S)}))}const st=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:()=>[z],register:(t,e,n)=>lt(t,n.getOutput())});function ot(t,r,i={}){const l=i=>{const l=i.target;if(!P(l))return;const s=y(l);if(null===s)return;let o=null,a=null;if(s.update(()=>{const t=A(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 ut=U({build:(t,e,n)=>E(e),config:O({disabled:!1,newTab:!1}),dependencies:[st],name:"@lexical/link/ClickableLink",register:(t,e,n)=>ot(t,n.getOutput())});function at(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 ct(t,e){for(let n=0;n<e.length;n++){const r=e[n](t);if(r)return r}return null}const gt=/[.,;\s]/;function ft(t,e){return e.test(t)}function dt(t,e){return ft(t[t.length-1],e)}function ht(t,e){return ft(t[0],e)}function pt(t,e){let n=t.getPreviousSibling();return _(n)&&(n=n.getLastDescendant()),null===n||D(n)||R(n)&&dt(n.getTextContent(),e)}function _t(t,e){let n=t.getNextSibling();return _(n)&&(n=n.getFirstDescendant()),null===n||D(n)||R(n)&&ht(n.getTextContent(),e)}function mt(t,e,n,r,i){if(!(t>0?ft(r[t-1],n):pt(i[0],n)))return!1;return e<r.length?ft(r[e],n):_t(i[i.length-1],n)}function xt(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 kt(t,e,n,r){const i=X(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(R):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 bt(t,e,n,r){const i=t.getChildren(),l=i.length;for(let e=0;e<l;e++){const r=i[e];if(!R(r)||!r.isSimpleText())return vt(t),void n(null,t.getURL())}const s=t.getTextContent(),o=ct(s,e);if(null===o||o.text!==s)return vt(t),void n(null,t.getURL());if(!pt(t,r)||!_t(t,r))return vt(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 vt(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 Ut={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:gt};function Tt(t,e=Ut){const{matchers:n,changeHandlers:r,excludeParents:l,separatorRegex:s=gt}=e,o=(t,e)=>{for(const n of r)n(t,e)};return i(t.registerNodeTransform(N,t=>{const e=t.getParentOrThrow(),r=t.getPreviousSibling();if(Y(e))bt(e,n,o,s);else if(!Q(e)&&!l.some(t=>t(e))){if(t.isSimpleText()&&(ht(t.getTextContent(),s)||!Y(r))){const e=function(t){const e=[t];let n=t.getNextSibling();for(;null!==n&&R(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(Y(t)&&!t.getIsUnlinked())return}let i=[...t];const l=i.map(t=>t.getTextContent()).join("");let s,o=l,u=0;for(;(s=ct(o,e))&&null!==s;){const t=s.index,e=t+s.length;if(mt(u+t,u+e,r,l,i)){const[r,,l,a]=xt(i,u+t,u+e);let c=!1;for(const t of l){const e=t.getParent();if(Y(e)&&!e.getIsUnlinked()){c=!0;break}}if(c){u+=e,o=o.substring(e);continue}const g=kt(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(!Y(i)||i.getIsUnlinked()){if(Y(l)&&!l.getIsUnlinked()&&l.is(t.getPreviousSibling())&&t.getParent()===l.getParent()){if(!ht(o,r))return vt(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=ct(i,e);null!==s&&s.text===i&&(l.append(t),bt(l,e,n,r),n(null,l.getURL()))}}var u;!Y(s)||s.getIsUnlinked()||dt(o,r)||s.is(t.getNextSibling())&&t.getParent()===s.getParent()&&(vt(s),n(null,s.getURL()))}}(t,n,o,s)}}),t.registerCommand(tt,t=>{const e=g();if(null!==t||!u(e))return!1;return e.extract().forEach(t=>{const e=t.getParent();Y(e)&&(e.setIsUnlinked(!e.getIsUnlinked()),e.markDirty())}),!1},S))}const Ct=U({config:Ut,dependencies:[st],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:[V],register:Tt}),Lt=[J({$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 $(t.$importChildren(e),()=>q(n,r))},match:K.tag("a"),name:"@lexical/link/a"})],St=U({dependencies:[F,st,w(W,{rules:Lt})],name:"@lexical/link/Import"}),Ot=nt;export{X as $createAutoLinkNode,q as $createLinkNode,Y as $isAutoLinkNode,Q as $isLinkNode,nt as $toggleLink,Ct as AutoLinkExtension,V as AutoLinkNode,ut as ClickableLinkExtension,st as LinkExtension,St as LinkImportExtension,Lt as LinkImportRules,z as LinkNode,tt as TOGGLE_LINK_COMMAND,at as createLinkMatcherWithRegExp,it as formatUrl,Tt as registerAutoLink,ot as registerClickableLink,lt as registerLink,Ot as toggleLink};
@@ -0,0 +1,22 @@
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} (plus {@link CoreImportExtension}) into
16
+ * a single dependency. Equivalent to the legacy
17
+ * `LinkNode.importDOM` registration on the new
18
+ * {@link DOMImportExtension} pipeline.
19
+ *
20
+ * @experimental
21
+ */
22
+ 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.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/extension": "0.45.0",
16
+ "@lexical/html": "0.45.0",
17
+ "@lexical/internal": "0.45.0",
18
+ "@lexical/utils": "0.45.0",
19
+ "lexical": "0.45.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
+ });